Browse Source

more stuff

Frank-Rainer Grahl 7 months ago
parent
commit
2af0185cfa
73 changed files with 63015 additions and 679 deletions
  1. 0 145
      frg/work-js/mozilla-release/patches/0926-Bug-1456494-Initialize-Zone-helperThreadUse_-first-t.patch
  2. 5 5
      frg/work-js/mozilla-release/patches/1033916-1-63a1.patch
  3. 2 2
      frg/work-js/mozilla-release/patches/1040316-63a1.patch
  4. 268 0
      frg/work-js/mozilla-release/patches/1052582-1-63a1.patch
  5. 134 0
      frg/work-js/mozilla-release/patches/1052582-2-63a1.patch
  6. 0 26
      frg/work-js/mozilla-release/patches/1244-Bug-1471371-OOM-handling-in-RegExp-construction.-r-j.patch
  7. 732 0
      frg/work-js/mozilla-release/patches/1309552-63a1.patch
  8. 43 0
      frg/work-js/mozilla-release/patches/1440648-63a1.patch
  9. 58 0
      frg/work-js/mozilla-release/patches/1460057-62a1.patch
  10. 0 0
      frg/work-js/mozilla-release/patches/1462261-1-62a1.patch
  11. 0 0
      frg/work-js/mozilla-release/patches/1462261-2-62a1.patch
  12. 53 0
      frg/work-js/mozilla-release/patches/1462261-3-25319.patch
  13. 14 14
      frg/work-js/mozilla-release/patches/1465585-3-std-62a1.patch
  14. 13 13
      frg/work-js/mozilla-release/patches/1466168-PARTIAL-js-62a1.patch
  15. 145 0
      frg/work-js/mozilla-release/patches/1468524-63a1.patch
  16. 487 0
      frg/work-js/mozilla-release/patches/1469044-1-63a1.patch
  17. 1343 0
      frg/work-js/mozilla-release/patches/1469044-2-63a1.patch
  18. 969 0
      frg/work-js/mozilla-release/patches/1469044-3-63a1.patch
  19. 61 0
      frg/work-js/mozilla-release/patches/1473024-63a1.patch
  20. 853 0
      frg/work-js/mozilla-release/patches/1475228-1-63a1.patch
  21. 504 0
      frg/work-js/mozilla-release/patches/1475228-2-63a1.patch
  22. 96 0
      frg/work-js/mozilla-release/patches/1475228-3-63a1.patch
  23. 162 0
      frg/work-js/mozilla-release/patches/1475228-4-63a1.patch
  24. 156 0
      frg/work-js/mozilla-release/patches/1475228-5-63a1.patch
  25. 214 0
      frg/work-js/mozilla-release/patches/1475228-6-63a1.patch
  26. 221 0
      frg/work-js/mozilla-release/patches/1475228-7-63a1.patch
  27. 96 0
      frg/work-js/mozilla-release/patches/1476921-63a1.patch
  28. 551 0
      frg/work-js/mozilla-release/patches/1478982-63a1.patch
  29. 32 0
      frg/work-js/mozilla-release/patches/1479673-1-63a1.patch
  30. 53 0
      frg/work-js/mozilla-release/patches/1479673-2-63a1.patch
  31. 103 0
      frg/work-js/mozilla-release/patches/1479793-63a1.patch
  32. 46 45
      frg/work-js/mozilla-release/patches/1480720-63a1.patch
  33. 199 0
      frg/work-js/mozilla-release/patches/1480966-63a1.patch
  34. 41 0
      frg/work-js/mozilla-release/patches/1481005-63a1.patch
  35. 14 14
      frg/work-js/mozilla-release/patches/1482153-64a1.patch
  36. 553 0
      frg/work-js/mozilla-release/patches/1482931-1-63a1.patch
  37. 208 0
      frg/work-js/mozilla-release/patches/1482931-2-63a1.patch
  38. 187 0
      frg/work-js/mozilla-release/patches/1483374-63a1.patch
  39. 681 0
      frg/work-js/mozilla-release/patches/1484385-63a1.patch
  40. 138 0
      frg/work-js/mozilla-release/patches/1484386-63a1.patch
  41. 477 0
      frg/work-js/mozilla-release/patches/1484420-63a1.patch
  42. 491 0
      frg/work-js/mozilla-release/patches/1484421-63a1.patch
  43. 151 0
      frg/work-js/mozilla-release/patches/1484943-63a1.patch
  44. 2031 0
      frg/work-js/mozilla-release/patches/1485347-3-64a1.patch
  45. 385 0
      frg/work-js/mozilla-release/patches/1486444-63a1.patch
  46. 0 0
      frg/work-js/mozilla-release/patches/1488698-7-64a1.patch
  47. 15106 0
      frg/work-js/mozilla-release/patches/1488698-7-WIP-64a1.patch
  48. 26 26
      frg/work-js/mozilla-release/patches/1499140-1-64a1.patch
  49. 6 3
      frg/work-js/mozilla-release/patches/1504727-65a1.patch
  50. 84 50
      frg/work-js/mozilla-release/patches/1539780-70a1.patch
  51. 8 7
      frg/work-js/mozilla-release/patches/1586165-71a1.patch
  52. 73 52
      frg/work-js/mozilla-release/patches/1590907-5-72a1.patch
  53. 9 9
      frg/work-js/mozilla-release/patches/1770048-9110.patch
  54. 0 232
      frg/work-js/mozilla-release/patches/L-1459220-62a1.patch
  55. 29 0
      frg/work-js/mozilla-release/patches/NOBUG-20180712-typetraits-63a1.patch
  56. 105 0
      frg/work-js/mozilla-release/patches/NOBUG-20180809-utf8-63a1.patch
  57. 10 10
      frg/work-js/mozilla-release/patches/TOP-NOBUG-REGEXP-03-1537978-68a1-25318.patch
  58. 14 14
      frg/work-js/mozilla-release/patches/TOP-NOBUG-REGEXP-04-1539690-68a1-25318.patch
  59. 198 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478476.patch
  60. 8467 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478477.patch
  61. 2677 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478478.patch
  62. 7724 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478479.patch
  63. 3868 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478480.patch
  64. 7464 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478481.patch
  65. 1100 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478482.patch
  66. 1388 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478483.patch
  67. 616 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478484.patch
  68. 551 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478485.patch
  69. 64 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478486.patch
  70. 308 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478487.patch
  71. 51 0
      frg/work-js/mozilla-release/patches/mozilla-esr68-push_478488.patch
  72. 58 9
      frg/work-js/mozilla-release/patches/series
  73. 41 3
      frg/work-js/mozilla-release/patches/series-test

+ 0 - 145
frg/work-js/mozilla-release/patches/0926-Bug-1456494-Initialize-Zone-helperThreadUse_-first-t.patch

@@ -1,145 +0,0 @@
-From ee91c74d0e2a202815166466f5d59fca2792fa68 Mon Sep 17 00:00:00 2001
-From: Jan de Mooij <jdemooij@mozilla.com>
-Date: Thu, 26 Apr 2018 09:57:15 +0200
-Subject: [PATCH 0926/1328] Bug 1456494 - Initialize Zone::helperThreadUse_
- first to avoid accessing uninitialized memory in debug builds.
-
----
- js/src/gc/Zone.cpp |  8 +++--
- js/src/gc/Zone.h   | 80 ++++++++++++++++++++++------------------------
- 2 files changed, 43 insertions(+), 45 deletions(-)
-
-diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp
-index 697e777b51e5..d63332274d35 100644
---- a/js/src/gc/Zone.cpp
-+++ b/js/src/gc/Zone.cpp
-@@ -25,6 +25,10 @@ Zone * const Zone::NotOnList = reinterpret_cast<Zone*>(1);
- 
- JS::Zone::Zone(JSRuntime* rt)
-   : JS::shadow::Zone(rt, &rt->gc.marker),
-+    // Note: don't use |this| before initializing helperThreadUse_!
-+    // ProtectedData checks in CheckZone::check may read this field.
-+    helperThreadUse_(HelperThreadUse::None),
-+    helperThreadOwnerContext_(nullptr),
-     debuggers(this, nullptr),
-     uniqueIds_(this),
-     suppressAllocationMetadataBuilder(this, false),
-@@ -52,8 +56,6 @@ JS::Zone::Zone(JSRuntime* rt)
-     nurseryShapes_(this),
-     data(this, nullptr),
-     isSystem(this, false),
--    helperThreadOwnerContext_(nullptr),
--    helperThreadUse(HelperThreadUse::None),
- #ifdef DEBUG
-     gcLastSweepGroupIndex(this, 0),
- #endif
-@@ -75,7 +77,7 @@ JS::Zone::Zone(JSRuntime* rt)
- 
- Zone::~Zone()
- {
--    MOZ_ASSERT(helperThreadUse == HelperThreadUse::None);
-+    MOZ_ASSERT(helperThreadUse_ == HelperThreadUse::None);
- 
-     JSRuntime* rt = runtimeFromAnyThread();
-     if (this == rt->gc.systemZone)
-diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h
-index 50d96e3875e0..1dcbaf373107 100644
---- a/js/src/gc/Zone.h
-+++ b/js/src/gc/Zone.h
-@@ -188,6 +188,44 @@ struct Zone : public JS::shadow::Zone,
-     MOZ_MUST_USE bool init(bool isSystem);
-     void destroy(js::FreeOp *fop);
- 
-+  private:
-+    enum class HelperThreadUse : uint32_t {
-+        None,
-+        Pending,
-+        Active
-+    };
-+    mozilla::Atomic<HelperThreadUse> helperThreadUse_;
-+
-+    // The helper thread context with exclusive access to this zone, if
-+    // usedByHelperThread(), or nullptr when on the main thread.
-+    js::UnprotectedData<JSContext*> helperThreadOwnerContext_;
-+
-+  public:
-+    bool ownedByCurrentHelperThread();
-+    void setHelperThreadOwnerContext(JSContext* cx);
-+
-+    // Whether this zone was created for use by a helper thread.
-+    bool createdForHelperThread() const {
-+        return helperThreadUse_ != HelperThreadUse::None;
-+    }
-+    // Whether this zone is currently in use by a helper thread.
-+    bool usedByHelperThread() {
-+        MOZ_ASSERT_IF(isAtomsZone(), helperThreadUse_ == HelperThreadUse::None);
-+        return helperThreadUse_ == HelperThreadUse::Active;
-+    }
-+    void setCreatedForHelperThread() {
-+        MOZ_ASSERT(helperThreadUse_ == HelperThreadUse::None);
-+        helperThreadUse_ = HelperThreadUse::Pending;
-+    }
-+    void setUsedByHelperThread() {
-+        MOZ_ASSERT(helperThreadUse_ == HelperThreadUse::Pending);
-+        helperThreadUse_ = HelperThreadUse::Active;
-+    }
-+    void clearUsedByHelperThread() {
-+        MOZ_ASSERT(helperThreadUse_ != HelperThreadUse::None);
-+        helperThreadUse_ = HelperThreadUse::None;
-+    }
-+
-     void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
- 
-     void discardJitCode(js::FreeOp* fop, bool discardBaselineCode = true);
-@@ -520,48 +558,6 @@ struct Zone : public JS::shadow::Zone,
- 
-     js::ZoneData<bool> isSystem;
- 
--  private:
--    // The helper thread context with exclusive access to this zone, if
--    // usedByHelperThread(), or nullptr when on the main thread.
--    js::UnprotectedData<JSContext*> helperThreadOwnerContext_;
--
--  public:
--    bool ownedByCurrentHelperThread();
--    void setHelperThreadOwnerContext(JSContext* cx);
--
--  private:
--    enum class HelperThreadUse : uint32_t
--    {
--        None,
--        Pending,
--        Active
--    };
--
--    mozilla::Atomic<HelperThreadUse> helperThreadUse;
--
--  public:
--    // Whether this zone was created for use by a helper thread.
--    bool createdForHelperThread() const {
--        return helperThreadUse != HelperThreadUse::None;
--    }
--    // Whether this zone is currently in use by a helper thread.
--    bool usedByHelperThread() {
--        MOZ_ASSERT_IF(isAtomsZone(), helperThreadUse == HelperThreadUse::None);
--        return helperThreadUse == HelperThreadUse::Active;
--    }
--    void setCreatedForHelperThread() {
--        MOZ_ASSERT(helperThreadUse == HelperThreadUse::None);
--        helperThreadUse = HelperThreadUse::Pending;
--    }
--    void setUsedByHelperThread() {
--        MOZ_ASSERT(helperThreadUse == HelperThreadUse::Pending);
--        helperThreadUse = HelperThreadUse::Active;
--    }
--    void clearUsedByHelperThread() {
--        MOZ_ASSERT(helperThreadUse != HelperThreadUse::None);
--        helperThreadUse = HelperThreadUse::None;
--    }
--
- #ifdef DEBUG
-     js::ZoneData<unsigned> gcLastSweepGroupIndex;
- #endif
--- 
-2.33.0.windows.2
-

+ 5 - 5
frg/work-js/mozilla-release/patches/1033916-1-63a1.patch

@@ -2,7 +2,7 @@
 # User Jeff Walden <jwalden@mit.edu>
 # User Jeff Walden <jwalden@mit.edu>
 # Date 1534776368 25200
 # Date 1534776368 25200
 # Node ID 67d5039dcbc2522e187bcf3dbec1c6e92bd32167
 # Node ID 67d5039dcbc2522e187bcf3dbec1c6e92bd32167
-# Parent  81bc6d1242a341dfe9a236395077b926de987c23
+# Parent  f65f1ea274cb12ceac5d801a299ea0c500b67ebc
 Bug 1033916 - Move JSAutoByteString out of jsapi.h into js/public/AutoByteString.h, incidentally breaking the jsfriendapi.h -> jsapi.h dependency.  r=jandem
 Bug 1033916 - Move JSAutoByteString out of jsapi.h into js/public/AutoByteString.h, incidentally breaking the jsfriendapi.h -> jsapi.h dependency.  r=jandem
 
 
 diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp
 diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp
@@ -510,7 +510,7 @@ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
 diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 --- a/js/src/jsapi.h
 --- a/js/src/jsapi.h
 +++ b/js/src/jsapi.h
 +++ b/js/src/jsapi.h
-@@ -4597,29 +4597,16 @@ JS_ConcatStrings(JSContext* cx, JS::Hand
+@@ -4552,29 +4552,16 @@ JS_ConcatStrings(JSContext* cx, JS::Hand
   * NB: This function does not store an additional zero byte or char16_t after the
   * NB: This function does not store an additional zero byte or char16_t after the
   * transcoded string.
   * transcoded string.
   */
   */
@@ -540,7 +540,7 @@ diff --git a/js/src/jsapi.h b/js/src/jsapi.h
  JS_GetStringEncodingLength(JSContext* cx, JSString* str);
  JS_GetStringEncodingLength(JSContext* cx, JSString* str);
  
  
  /**
  /**
-@@ -4628,85 +4615,16 @@ JS_GetStringEncodingLength(JSContext* cx
+@@ -4583,85 +4570,16 @@ JS_GetStringEncodingLength(JSContext* cx
   * encoded into bytes with no error reported. Otherwise it returns the number
   * encoded into bytes with no error reported. Otherwise it returns the number
   * of bytes that are necessary to encode the string. If that exceeds the
   * of bytes that are necessary to encode the string. If that exceeds the
   * length parameter, the string will be cut and only length bytes will be
   * length parameter, the string will be cut and only length bytes will be
@@ -709,11 +709,11 @@ diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
  #include "js/CallArgs.h"
  #include "js/CallArgs.h"
  #include "js/CallNonGenericMethod.h"
  #include "js/CallNonGenericMethod.h"
  #include "js/Class.h"
  #include "js/Class.h"
+ #include "js/ErrorReport.h"
  #include "js/HeapAPI.h"
  #include "js/HeapAPI.h"
  #include "js/StableStringChars.h"
  #include "js/StableStringChars.h"
  #include "js/TypeDecls.h"
  #include "js/TypeDecls.h"
  #include "js/Utility.h"
  #include "js/Utility.h"
- 
 diff --git a/js/src/moz.build b/js/src/moz.build
 diff --git a/js/src/moz.build b/js/src/moz.build
 --- a/js/src/moz.build
 --- a/js/src/moz.build
 +++ b/js/src/moz.build
 +++ b/js/src/moz.build
@@ -734,7 +734,7 @@ diff --git a/js/src/moz.build b/js/src/moz.build
      '../public/Conversions.h',
      '../public/Conversions.h',
      '../public/Date.h',
      '../public/Date.h',
      '../public/Debug.h',
      '../public/Debug.h',
-     '../public/GCAnnotations.h',
+     '../public/ErrorReport.h',
 diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp
 diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp
 --- a/js/src/proxy/ScriptedProxyHandler.cpp
 --- a/js/src/proxy/ScriptedProxyHandler.cpp
 +++ b/js/src/proxy/ScriptedProxyHandler.cpp
 +++ b/js/src/proxy/ScriptedProxyHandler.cpp

+ 2 - 2
frg/work-js/mozilla-release/patches/1040316-63a1.patch

@@ -2,7 +2,7 @@
 # User Jeff Walden <jwalden@mit.edu>
 # User Jeff Walden <jwalden@mit.edu>
 # Date 1534776284 25200
 # Date 1534776284 25200
 # Node ID 6d10eda7f12de64044246e544d581537f30f8998
 # Node ID 6d10eda7f12de64044246e544d581537f30f8998
-# Parent  57808f6b27e05dfdb40f8248d8833f7eee3f7109
+# Parent  0a9abf5e7427be47f60196e5ac873fd8986d76fb
 Bug 1040316 - Move AutoStableStringChars out of friendapi into public API.  r=jandem
 Bug 1040316 - Move AutoStableStringChars out of friendapi into public API.  r=jandem
 
 
 diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h
 diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h
@@ -563,7 +563,7 @@ diff --git a/js/src/builtin/intl/NumberFormat.cpp b/js/src/builtin/intl/NumberFo
  using namespace js;
  using namespace js;
 @@ -35,16 +36,18 @@ using mozilla::IsFinite;
 @@ -35,16 +36,18 @@ using mozilla::IsFinite;
  using mozilla::IsNaN;
  using mozilla::IsNaN;
- using mozilla::IsNegativeZero;
+ using mozilla::IsNegative;
  
  
  using js::intl::CallICU;
  using js::intl::CallICU;
  using js::intl::DateTimeFormatOptions;
  using js::intl::DateTimeFormatOptions;

+ 268 - 0
frg/work-js/mozilla-release/patches/1052582-1-63a1.patch

@@ -0,0 +1,268 @@
+# HG changeset patch
+# User Matt Howell <mhowell@mozilla.com>
+# Date 1530302774 25200
+# Node ID b6b0d0fc267d21a63a63feceaa6447440f265731
+# Parent  3b472c96b7d81ff70a0abd9c3ef612eb3fc6cae5
+Bug 1052582 Part 1 - Support an arena parameter for js_pod_malloc and friends. r=sfink
+
+This patch adds new functions taking the arena parameter rather than overloading
+existing functions, because there are already overloads of calloc that take
+two size_t parameters (which arena_id_t is an alias for), so it couldn't have
+been done that way, and malloc and realloc needed to be consistent with calloc.
+
+MozReview-Commit-ID: 1MUXoCUgJWO
+
+diff --git a/js/public/Utility.h b/js/public/Utility.h
+--- a/js/public/Utility.h
++++ b/js/public/Utility.h
+@@ -376,43 +376,57 @@ namespace js {
+ 
+ extern JS_PUBLIC_DATA(arena_id_t) MallocArena;
+ 
+ extern void InitMallocAllocator();
+ extern void ShutDownMallocAllocator();
+ 
+ } /* namespace js */
+ 
++static inline void* js_arena_malloc(arena_id_t arena, size_t bytes)
++{
++    JS_OOM_POSSIBLY_FAIL();
++    return moz_arena_malloc(arena, bytes);
++}
++
+ static inline void* js_malloc(size_t bytes)
+ {
++    return js_arena_malloc(js::MallocArena, bytes);
++}
++
++static inline void* js_arena_calloc(arena_id_t arena, size_t nmemb, size_t size)
++{
+     JS_OOM_POSSIBLY_FAIL();
+-    return moz_arena_malloc(js::MallocArena, bytes);
++    return moz_arena_calloc(arena, nmemb, size);
+ }
+ 
+ static inline void* js_calloc(size_t bytes)
+ {
+-    JS_OOM_POSSIBLY_FAIL();
+-    return moz_arena_calloc(js::MallocArena, bytes, 1);
++    return js_arena_calloc(js::MallocArena, bytes, 1);
+ }
+ 
+ static inline void* js_calloc(size_t nmemb, size_t size)
+ {
+-    JS_OOM_POSSIBLY_FAIL();
+-    return moz_arena_calloc(js::MallocArena, nmemb, size);
++    return js_arena_calloc(js::MallocArena, nmemb, size);
+ }
+ 
+-static inline void* js_realloc(void* p, size_t bytes)
++static inline void* js_arena_realloc(arena_id_t arena, void* p, size_t bytes)
+ {
+     // realloc() with zero size is not portable, as some implementations may
+     // return nullptr on success and free |p| for this.  We assume nullptr
+     // indicates failure and that |p| is still valid.
+     MOZ_ASSERT(bytes != 0);
+ 
+     JS_OOM_POSSIBLY_FAIL();
+-    return moz_arena_realloc(js::MallocArena, p, bytes);
++    return moz_arena_realloc(arena, p, bytes);
++}
++
++static inline void* js_realloc(void* p, size_t bytes)
++{
++    return js_arena_realloc(js::MallocArena, p, bytes);
+ }
+ 
+ static inline void js_free(void* p)
+ {
+     // TODO: This should call |moz_arena_free(js::MallocArena, p)| but we
+     // currently can't enforce that all memory freed here was allocated by
+     // js_malloc().
+     free(p);
+@@ -557,46 +571,60 @@ js_delete_poison(const T* p)
+         p->~T();
+         memset(static_cast<void*>(const_cast<T*>(p)), 0x3B, sizeof(T));
+         js_free(const_cast<T*>(p));
+     }
+ }
+ 
+ template <class T>
+ static MOZ_ALWAYS_INLINE T*
++js_pod_arena_malloc(arena_id_t arena, size_t numElems)
++{
++  size_t bytes;
++  if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
++    return nullptr;
++  return static_cast<T*>(js_arena_malloc(arena, bytes));
++}
++
++template <class T>
++static MOZ_ALWAYS_INLINE T*
++js_pod_malloc(size_t numElems)
++{
++    return js_pod_arena_malloc<T>(js::MallocArena, numElems);
++}
++
++template <class T>
++static MOZ_ALWAYS_INLINE T*
+ js_pod_malloc()
+ {
+-    return static_cast<T*>(js_malloc(sizeof(T)));
++    return js_pod_malloc<T>(sizeof(T));
++}
++
++template <class T>
++static MOZ_ALWAYS_INLINE T*
++js_pod_arena_calloc(arena_id_t arena, size_t numElems)
++{
++    size_t bytes;
++    if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
++        return nullptr;
++    return static_cast<T*>(js_arena_calloc(arena, bytes, 1));
++}
++
++template <class T>
++static MOZ_ALWAYS_INLINE T*
++js_pod_calloc(size_t numElems)
++{
++    return js_pod_arena_calloc<T>(js::MallocArena, numElems);
+ }
+ 
+ template <class T>
+ static MOZ_ALWAYS_INLINE T*
+ js_pod_calloc()
+ {
+-    return static_cast<T*>(js_calloc(sizeof(T)));
+-}
+-
+-template <class T>
+-static MOZ_ALWAYS_INLINE T*
+-js_pod_malloc(size_t numElems)
+-{
+-    size_t bytes;
+-    if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
+-        return nullptr;
+-    return static_cast<T*>(js_malloc(bytes));
+-}
+-
+-template <class T>
+-static MOZ_ALWAYS_INLINE T*
+-js_pod_calloc(size_t numElems)
+-{
+-    size_t bytes;
+-    if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
+-        return nullptr;
+-    return static_cast<T*>(js_calloc(bytes));
++    return js_pod_calloc<T>(sizeof(T));
+ }
+ 
+ template <class T>
+ static MOZ_ALWAYS_INLINE T*
+ js_pod_realloc(T* prior, size_t oldSize, size_t newSize)
+ {
+     MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
+     size_t bytes;
+diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h
+--- a/js/src/gc/Zone.h
++++ b/js/src/gc/Zone.h
+@@ -711,18 +711,18 @@ struct Zone : public JS::shadow::Zone,
+     // Delete an empty compartment after its contents have been merged.
+     void deleteEmptyCompartment(JSCompartment* comp);
+ 
+     /*
+      * This variation of calloc will call the large-allocation-failure callback
+      * on OOM and retry the allocation.
+      */
+     template <typename T>
+-    T* pod_callocCanGC(size_t numElems) {
+-        T* p = pod_calloc<T>(numElems);
++    T* pod_callocCanGC(size_t numElems, arena_id_t arena = js::MallocArena) {
++        T* p = pod_calloc<T>(numElems, arena);
+         if (MOZ_LIKELY(!!p))
+             return p;
+         size_t bytes;
+         if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
+             reportAllocationOverflow();
+             return nullptr;
+         }
+         JSRuntime* rt = runtimeFromMainThread();
+diff --git a/js/src/vm/MallocProvider.h b/js/src/vm/MallocProvider.h
+--- a/js/src/vm/MallocProvider.h
++++ b/js/src/vm/MallocProvider.h
+@@ -53,18 +53,18 @@ struct MallocProvider
+     T* maybe_pod_malloc(size_t numElems) {
+         T* p = js_pod_malloc<T>(numElems);
+         if (MOZ_LIKELY(p))
+             client()->updateMallocCounter(numElems * sizeof(T));
+         return p;
+     }
+ 
+     template <class T>
+-    T* maybe_pod_calloc(size_t numElems) {
+-        T* p = js_pod_calloc<T>(numElems);
++    T* maybe_pod_calloc(size_t numElems, arena_id_t arena = js::MallocArena) {
++        T* p = js_pod_arena_calloc<T>(arena, numElems);
+         if (MOZ_LIKELY(p))
+             client()->updateMallocCounter(numElems * sizeof(T));
+         return p;
+     }
+ 
+     template <class T>
+     T* maybe_pod_realloc(T* prior, size_t oldSize, size_t newSize) {
+         T* p = js_pod_realloc(prior, oldSize, newSize);
+@@ -118,23 +118,18 @@ struct MallocProvider
+ 
+     template <class T>
+     UniquePtr<T[], JS::FreePolicy>
+     make_pod_array(size_t numElems) {
+         return UniquePtr<T[], JS::FreePolicy>(pod_malloc<T>(numElems));
+     }
+ 
+     template <class T>
+-    T* pod_calloc() {
+-        return pod_calloc<T>(1);
+-    }
+-
+-    template <class T>
+-    T* pod_calloc(size_t numElems) {
+-        T* p = maybe_pod_calloc<T>(numElems);
++    T* pod_calloc(size_t numElems = 1, arena_id_t arena = js::MallocArena) {
++        T* p = maybe_pod_calloc<T>(numElems, arena);
+         if (MOZ_LIKELY(p))
+             return p;
+         size_t bytes;
+         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
+             client()->reportAllocationOverflow();
+             return nullptr;
+         }
+         p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, bytes);
+diff --git a/testing/mochitest/leaks.py b/testing/mochitest/leaks.py
+--- a/testing/mochitest/leaks.py
++++ b/testing/mochitest/leaks.py
+@@ -178,19 +178,22 @@ class LSANLeaks(object):
+         self.foundFrames = set([])
+         self.recordMoreFrames = None
+         self.currStack = None
+         self.maxNumRecordedFrames = 4
+ 
+         # Don't various allocation-related stack frames, as they do not help much to
+         # distinguish different leaks.
+         unescapedSkipList = [
+-            "malloc", "js_malloc", "malloc_", "__interceptor_malloc", "moz_xmalloc",
+-            "calloc", "js_calloc", "calloc_", "__interceptor_calloc", "moz_xcalloc",
+-            "realloc", "js_realloc", "realloc_", "__interceptor_realloc", "moz_xrealloc",
++            "malloc", "js_malloc", "js_arena_malloc", "malloc_",
++            "__interceptor_malloc", "moz_xmalloc",
++            "calloc", "js_calloc", "js_arena_calloc", "calloc_",
++            "__interceptor_calloc", "moz_xcalloc",
++            "realloc", "js_realloc", "js_arena_realloc", "realloc_",
++            "__interceptor_realloc", "moz_xrealloc",
+             "new",
+             "js::MallocProvider",
+         ]
+         self.skipListRegExp = re.compile(
+             "^" + "|".join([re.escape(f) for f in unescapedSkipList]) + "$")
+ 
+         self.startRegExp = re.compile(
+             "==\d+==ERROR: LeakSanitizer: detected memory leaks")

+ 134 - 0
frg/work-js/mozilla-release/patches/1052582-2-63a1.patch

@@ -0,0 +1,134 @@
+# HG changeset patch
+# User Matt Howell <mhowell@mozilla.com>
+# Date 1527112662 25200
+# Node ID 1f5426580dec836eaae2dbb7512461ffccfaf4b6
+# Parent  40f7d1046d5d1c4536d3683c5b0b2c71aee451f0
+Bug 1052582 Part 2 - Create and use a separate malloc arena for ArrayBuffer contents. r=sfink
+
+MozReview-Commit-ID: 7IlFvr3hoA8
+
+diff --git a/dom/network/TCPSocketChild.cpp b/dom/network/TCPSocketChild.cpp
+--- a/dom/network/TCPSocketChild.cpp
++++ b/dom/network/TCPSocketChild.cpp
+@@ -21,17 +21,18 @@ using mozilla::net::gNeckoChild;
+ 
+ namespace IPC {
+ 
+ bool
+ DeserializeArrayBuffer(JSContext* cx,
+                        const InfallibleTArray<uint8_t>& aBuffer,
+                        JS::MutableHandle<JS::Value> aVal)
+ {
+-  mozilla::UniquePtr<uint8_t[], JS::FreePolicy> data(js_pod_malloc<uint8_t>(aBuffer.Length()));
++  mozilla::UniquePtr<uint8_t[], JS::FreePolicy> data(
++    js_pod_arena_malloc<uint8_t>(js::ArrayBufferContentsArena, aBuffer.Length()));
+   if (!data)
+       return false;
+   memcpy(data.get(), aBuffer.Elements(), aBuffer.Length());
+ 
+   JSObject* obj = JS_NewArrayBufferWithContents(cx, aBuffer.Length(), data.get());
+   if (!obj)
+       return false;
+   // If JS_NewArrayBufferWithContents returns non-null, the ownership of
+diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp
+--- a/dom/workers/FileReaderSync.cpp
++++ b/dom/workers/FileReaderSync.cpp
+@@ -60,17 +60,18 @@ FileReaderSync::ReadAsArrayBuffer(JSCont
+                                   JS::MutableHandle<JSObject*> aRetval,
+                                   ErrorResult& aRv)
+ {
+   uint64_t blobSize = aBlob.GetSize(aRv);
+   if (NS_WARN_IF(aRv.Failed())) {
+     return;
+   }
+ 
+-  UniquePtr<char[], JS::FreePolicy> bufferData(js_pod_malloc<char>(blobSize));
++  UniquePtr<char[], JS::FreePolicy> bufferData(
++    js_pod_arena_malloc<char>(js::ArrayBufferContentsArena, blobSize));
+   if (!bufferData) {
+     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+     return;
+   }
+ 
+   nsCOMPtr<nsIInputStream> stream;
+   aBlob.CreateInputStream(getter_AddRefs(stream), aRv);
+   if (NS_WARN_IF(aRv.Failed())) {
+diff --git a/js/public/Utility.h b/js/public/Utility.h
+--- a/js/public/Utility.h
++++ b/js/public/Utility.h
+@@ -370,16 +370,17 @@ struct MOZ_RAII JS_PUBLIC_DATA(AutoEnter
+ 
+ } /* namespace js */
+ 
+ // Malloc allocation.
+ 
+ namespace js {
+ 
+ extern JS_PUBLIC_DATA(arena_id_t) MallocArena;
++extern JS_PUBLIC_DATA(arena_id_t) ArrayBufferContentsArena;
+ 
+ extern void InitMallocAllocator();
+ extern void ShutDownMallocAllocator();
+ 
+ } /* namespace js */
+ 
+ static inline void* js_arena_malloc(arena_id_t arena, size_t bytes)
+ {
+diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp
+--- a/js/src/jsutil.cpp
++++ b/js/src/jsutil.cpp
+@@ -160,28 +160,31 @@ ResetSimulatedInterrupt()
+     interruptCheckFailAlways = false;
+ }
+ 
+ } // namespace oom
+ } // namespace js
+ #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+ 
+ JS_PUBLIC_DATA(arena_id_t) js::MallocArena;
++JS_PUBLIC_DATA(arena_id_t) js::ArrayBufferContentsArena;
+ 
+ void
+ js::InitMallocAllocator()
+ {
+     MallocArena = moz_create_arena();
++    ArrayBufferContentsArena = moz_create_arena();
+ }
+ 
+ void
+ js::ShutDownMallocAllocator()
+ {
+     // Until Bug 1364359 is fixed it is unsafe to call moz_dispose_arena.
+     // moz_dispose_arena(MallocArena);
++    // moz_dispose_arena(ArrayBufferContentsArena);
+ }
+ 
+ JS_PUBLIC_API(void)
+ JS_Assert(const char* s, const char* file, int ln)
+ {
+     MOZ_ReportAssertionFailure(s, file, ln);
+     MOZ_CRASH();
+ }
+diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp
+--- a/js/src/vm/ArrayBufferObject.cpp
++++ b/js/src/vm/ArrayBufferObject.cpp
+@@ -454,17 +454,18 @@ ArrayBufferObject::class_constructor(JSC
+         return false;
+     args.rval().setObject(*bufobj);
+     return true;
+ }
+ 
+ static ArrayBufferObject::BufferContents
+ AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes)
+ {
+-    uint8_t* p = cx->zone()->pod_callocCanGC<uint8_t>(nbytes);
++    uint8_t* p = cx->zone()->pod_callocCanGC<uint8_t>(nbytes,
++                                                      js::ArrayBufferContentsArena);
+     if (!p)
+         ReportOutOfMemory(cx);
+ 
+     return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
+ }
+ 
+ static void
+ NoteViewBufferWasDetached(ArrayBufferViewObject* view,

+ 0 - 26
frg/work-js/mozilla-release/patches/1244-Bug-1471371-OOM-handling-in-RegExp-construction.-r-j.patch

@@ -1,26 +0,0 @@
-From 9eeca7ab2dffc777c1a89c47c84ad5fac487b846 Mon Sep 17 00:00:00 2001
-From: Ashley Hauck <khyperia@mozilla.com>
-Date: Tue, 14 Aug 2018 08:24:57 -0700
-Subject: [PATCH 1244/1328] Bug 1471371 - OOM handling in RegExp construction.
- r=jorendorff
-
----
- js/src/vm/RegExpObject.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
-index 9560747ea8c4..dbea4efff5c2 100644
---- a/js/src/vm/RegExpObject.cpp
-+++ b/js/src/vm/RegExpObject.cpp
-@@ -1725,7 +1725,7 @@ js::ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlags* flagsOut)
- 
-     if (!ok) {
-         TwoByteChars range(&invalidFlag, 1);
--        UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, range).c_str());
-+        UniqueChars utf8(JS::CharsToNewUTF8CharsZ(cx, range).c_str());
-         if (!utf8)
-             return false;
-         JS_ReportErrorFlagsAndNumberUTF8(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
--- 
-2.33.0.windows.2
-

+ 732 - 0
frg/work-js/mozilla-release/patches/1309552-63a1.patch

@@ -0,0 +1,732 @@
+# HG changeset patch
+# User Brian Hackett <bhackett1024@gmail.com>
+# Date 1532131114 0
+# Node ID 5c8c2d8a6003d2729497a2cf93c9afc89d2bf4f4
+# Parent  a6a74e774e1c619755bf362f930cc913fca5897b
+Bug 1309552 - Specify buffer size when freeing data in AllocPolicy, r=waldo.
+
+diff --git a/js/public/AllocPolicy.h b/js/public/AllocPolicy.h
+--- a/js/public/AllocPolicy.h
++++ b/js/public/AllocPolicy.h
+@@ -35,17 +35,17 @@ class SystemAllocPolicy
+     template <typename T> T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
+         return js_pod_realloc<T>(p, oldSize, newSize);
+     }
+     template <typename T> T* pod_malloc(size_t numElems) { return maybe_pod_malloc<T>(numElems); }
+     template <typename T> T* pod_calloc(size_t numElems) { return maybe_pod_calloc<T>(numElems); }
+     template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
+         return maybe_pod_realloc<T>(p, oldSize, newSize);
+     }
+-    void free_(void* p) { js_free(p); }
++    template <typename T> void free_(T* p, size_t numElems = 0) { js_free(p); }
+     void reportAllocOverflow() const {}
+     bool checkSimulatedOOM() const {
+         return !js::oom::ShouldFailWithOOM();
+     }
+ };
+ 
+ MOZ_COLD JS_FRIEND_API(void) ReportOutOfMemory(JSContext* cx);
+ 
+@@ -114,17 +114,18 @@ class TempAllocPolicy
+     template <typename T>
+     T* pod_realloc(T* prior, size_t oldSize, size_t newSize) {
+         T* p2 = maybe_pod_realloc<T>(prior, oldSize, newSize);
+         if (MOZ_UNLIKELY(!p2))
+             p2 = onOutOfMemoryTyped<T>(AllocFunction::Realloc, newSize, prior);
+         return p2;
+     }
+ 
+-    void free_(void* p) {
++    template <typename T>
++    void free_(T* p, size_t numElems = 0) {
+         js_free(p);
+     }
+ 
+     JS_FRIEND_API(void) reportAllocOverflow() const;
+ 
+     bool checkSimulatedOOM() const {
+         if (js::oom::ShouldFailWithOOM()) {
+             ReportOutOfMemory(cx_);
+@@ -156,17 +157,17 @@ class ZoneAllocPolicy
+     // These methods are defined in gc/Zone.h.
+     template <typename T> inline T* maybe_pod_malloc(size_t numElems);
+     template <typename T> inline T* maybe_pod_calloc(size_t numElems);
+     template <typename T> inline T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize);
+     template <typename T> inline T* pod_malloc(size_t numElems);
+     template <typename T> inline T* pod_calloc(size_t numElems);
+     template <typename T> inline T* pod_realloc(T* p, size_t oldSize, size_t newSize);
+ 
+-    void free_(void* p) { js_free(p); }
++    template <typename T> void free_(T* p, size_t numElems = 0) { js_free(p); }
+     void reportAllocOverflow() const {}
+ 
+     MOZ_MUST_USE bool checkSimulatedOOM() const {
+         return !js::oom::ShouldFailWithOOM();
+     }
+ };
+ 
+ } /* namespace js */
+diff --git a/js/public/HashTable.h b/js/public/HashTable.h
+--- a/js/public/HashTable.h
++++ b/js/public/HashTable.h
+@@ -1314,17 +1314,17 @@ class HashTable : private AllocPolicy
+         return table;
+     }
+ 
+     static void destroyTable(AllocPolicy& alloc, Entry* oldTable, uint32_t capacity)
+     {
+         Entry* end = oldTable + capacity;
+         for (Entry* e = oldTable; e < end; ++e)
+             e->~Entry();
+-        alloc.free_(oldTable);
++        alloc.free_(oldTable, capacity);
+     }
+ 
+   public:
+     explicit HashTable(AllocPolicy ap)
+       : AllocPolicy(ap)
+       , gen(0)
+       , hashShift(sHashBits)
+       , table(nullptr)
+@@ -1582,17 +1582,17 @@ class HashTable : private AllocPolicy
+                 findFreeEntry(hn).setLive(
+                     hn, std::move(const_cast<typename Entry::NonConstT&>(src->get())));
+             }
+ 
+             src->~Entry();
+         }
+ 
+         // All entries have been destroyed, no need to destroyTable.
+-        this->free_(oldTable);
++        this->free_(oldTable, oldCap);
+         return Rehashed;
+     }
+ 
+     bool shouldCompressTable()
+     {
+         // Compress if a quarter or more of all entries are removed.
+         return removedCount >= (capacity() >> 2);
+     }
+diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h
+--- a/js/src/ds/LifoAlloc.h
++++ b/js/src/ds/LifoAlloc.h
+@@ -943,17 +943,18 @@ class LifoAllocPolicy
+     template <typename T>
+     T* pod_calloc(size_t numElems) {
+         return maybe_pod_calloc<T>(numElems);
+     }
+     template <typename T>
+     T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
+         return maybe_pod_realloc<T>(p, oldSize, newSize);
+     }
+-    void free_(void* p) {
++    template <typename T>
++    void free_(T* p, size_t numElems) {
+     }
+     void reportAllocOverflow() const {
+     }
+     MOZ_MUST_USE bool checkSimulatedOOM() const {
+         return fb == Infallible || !js::oom::ShouldFailWithOOM();
+     }
+ };
+ 
+diff --git a/js/src/ds/OrderedHashTable.h b/js/src/ds/OrderedHashTable.h
+--- a/js/src/ds/OrderedHashTable.h
++++ b/js/src/ds/OrderedHashTable.h
+@@ -119,17 +119,17 @@ class OrderedHashTable
+         if (!tableAlloc)
+             return false;
+         for (uint32_t i = 0; i < buckets; i++)
+             tableAlloc[i] = nullptr;
+ 
+         uint32_t capacity = uint32_t(buckets * fillFactor());
+         Data* dataAlloc = alloc.template pod_malloc<Data>(capacity);
+         if (!dataAlloc) {
+-            alloc.free_(tableAlloc);
++            alloc.free_(tableAlloc, buckets);
+             return false;
+         }
+ 
+         // clear() requires that members are assigned only after all allocation
+         // has succeeded, and that this->ranges is left untouched.
+         hashTable = tableAlloc;
+         data = dataAlloc;
+         dataLength = 0;
+@@ -137,18 +137,18 @@ class OrderedHashTable
+         liveCount = 0;
+         hashShift = HashNumberSizeBits - initialBucketsLog2();
+         MOZ_ASSERT(hashBuckets() == buckets);
+         return true;
+     }
+ 
+     ~OrderedHashTable() {
+         forEachRange<Range::onTableDestroyed>();
+-        alloc.free_(hashTable);
+-        freeData(data, dataLength);
++        alloc.free_(hashTable, hashBuckets());
++        freeData(data, dataLength, dataCapacity);
+     }
+ 
+     /* Return the number of elements in the table. */
+     uint32_t count() const { return liveCount; }
+ 
+     /* True if any element matches l. */
+     bool has(const Lookup& l) const {
+         return lookup(l) != nullptr;
+@@ -243,27 +243,29 @@ class OrderedHashTable
+      * The effect on live Ranges is the same as removing all entries; in
+      * particular, those Ranges are still live and will see any entries added
+      * after a successful clear().
+      */
+     MOZ_MUST_USE bool clear() {
+         if (dataLength != 0) {
+             Data** oldHashTable = hashTable;
+             Data* oldData = data;
++            uint32_t oldHashBuckets = hashBuckets();
+             uint32_t oldDataLength = dataLength;
++            uint32_t oldDataCapacity = dataCapacity;
+ 
+             hashTable = nullptr;
+             if (!init()) {
+                 // init() only mutates members on success; see comment above.
+                 hashTable = oldHashTable;
+                 return false;
+             }
+ 
+-            alloc.free_(oldHashTable);
+-            freeData(oldData, oldDataLength);
++            alloc.free_(oldHashTable, oldHashBuckets);
++            freeData(oldData, oldDataLength, oldDataCapacity);
+             forEachRange<&Range::onClear>();
+         }
+ 
+         MOZ_ASSERT(hashTable);
+         MOZ_ASSERT(data);
+         MOZ_ASSERT(dataLength == 0);
+         MOZ_ASSERT(liveCount == 0);
+         return true;
+@@ -625,19 +627,19 @@ class OrderedHashTable
+         return 1 << (HashNumberSizeBits - hashShift);
+     }
+ 
+     static void destroyData(Data* data, uint32_t length) {
+         for (Data* p = data + length; p != data; )
+             (--p)->~Data();
+     }
+ 
+-    void freeData(Data* data, uint32_t length) {
++    void freeData(Data* data, uint32_t length, uint32_t capacity) {
+         destroyData(data, length);
+-        alloc.free_(data);
++        alloc.free_(data, capacity);
+     }
+ 
+     Data* lookup(const Lookup& l, HashNumber h) {
+         for (Data* e = hashTable[h >> hashShift]; e; e = e->chain) {
+             if (Ops::match(Ops::getKey(e->element), l))
+                 return e;
+         }
+         return nullptr;
+@@ -699,34 +701,34 @@ class OrderedHashTable
+         if (!newHashTable)
+             return false;
+         for (uint32_t i = 0; i < newHashBuckets; i++)
+             newHashTable[i] = nullptr;
+ 
+         uint32_t newCapacity = uint32_t(newHashBuckets * fillFactor());
+         Data* newData = alloc.template pod_malloc<Data>(newCapacity);
+         if (!newData) {
+-            alloc.free_(newHashTable);
++            alloc.free_(newHashTable, newHashBuckets);
+             return false;
+         }
+ 
+         Data* wp = newData;
+         Data* end = data + dataLength;
+         for (Data* p = data; p != end; p++) {
+             if (!Ops::isEmpty(Ops::getKey(p->element))) {
+                 HashNumber h = prepareHash(Ops::getKey(p->element)) >> newHashShift;
+                 new (wp) Data(std::move(p->element), newHashTable[h]);
+                 newHashTable[h] = wp;
+                 wp++;
+             }
+         }
+         MOZ_ASSERT(wp == newData + liveCount);
+ 
+-        alloc.free_(hashTable);
+-        freeData(data, dataLength);
++        alloc.free_(hashTable, hashBuckets());
++        freeData(data, dataLength, dataCapacity);
+ 
+         hashTable = newHashTable;
+         data = newData;
+         dataLength = liveCount;
+         dataCapacity = newCapacity;
+         hashShift = newHashShift;
+         MOZ_ASSERT(hashBuckets() == newHashBuckets);
+ 
+diff --git a/js/src/jit/JitAllocPolicy.h b/js/src/jit/JitAllocPolicy.h
+--- a/js/src/jit/JitAllocPolicy.h
++++ b/js/src/jit/JitAllocPolicy.h
+@@ -119,17 +119,18 @@ class JitAllocPolicy
+     template <typename T>
+     T* pod_calloc(size_t numElems) {
+         return maybe_pod_calloc<T>(numElems);
+     }
+     template <typename T>
+     T* pod_realloc(T* ptr, size_t oldSize, size_t newSize) {
+         return maybe_pod_realloc<T>(ptr, oldSize, newSize);
+     }
+-    void free_(void* p) {
++    template <typename T>
++    void free_(T* p, size_t numElems = 0) {
+     }
+     void reportAllocOverflow() const {
+     }
+     MOZ_MUST_USE bool checkSimulatedOOM() const {
+         return !js::oom::ShouldFailWithOOM();
+     }
+ };
+ 
+diff --git a/layout/style/nsNthIndexCache.h b/layout/style/nsNthIndexCache.h
+--- a/layout/style/nsNthIndexCache.h
++++ b/layout/style/nsNthIndexCache.h
+@@ -69,17 +69,17 @@ private:
+     }
+ 
+     template <typename T>
+     T *pod_calloc(size_t numElems) {
+       return maybe_pod_calloc<T>(numElems);
+     }
+ 
+     void *realloc_(void *p, size_t bytes) { return ::realloc(p, bytes); }
+-    void free_(void *p) { ::free(p); }
++    void free_(void *p, size_t bytes) { ::free(p); }
+     void reportAllocOverflow() const {}
+     bool checkSimulatedOOM() const { return true; }
+   };
+ 
+   typedef js::HashMap<nsIContent*, CacheEntry, js::DefaultHasher<nsIContent*>,
+                       SystemAllocPolicy> Cache;
+ 
+   /**
+diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h
+--- a/memory/mozalloc/mozalloc.h
++++ b/memory/mozalloc/mozalloc.h
+@@ -259,17 +259,18 @@ public:
+     T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
+     {
+         if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
+             reportAllocOverflow();
+         }
+         return static_cast<T*>(moz_xrealloc(aPtr, aNewSize * sizeof(T)));
+     }
+ 
+-    void free_(void* aPtr)
++    template <typename T>
++    void free_(T* aPtr, size_t aNumElems = 0)
+     {
+         free_impl(aPtr);
+     }
+ 
+     void reportAllocOverflow() const
+     {
+         mozalloc_abort("alloc overflow");
+     }
+diff --git a/memory/replace/dmd/DMD.cpp b/memory/replace/dmd/DMD.cpp
+--- a/memory/replace/dmd/DMD.cpp
++++ b/memory/replace/dmd/DMD.cpp
+@@ -181,17 +181,18 @@ public:
+ 
+   static void* memalign_(size_t aAlignment, size_t aSize)
+   {
+     void* p = gMallocTable.memalign(aAlignment, aSize);
+     ExitOnFailure(p);
+     return p;
+   }
+ 
+-  static void free_(void* aPtr) { gMallocTable.free(aPtr); }
++  template <typename T>
++  static void free_(T* aPtr, size_t aSize = 0) { gMallocTable.free(aPtr); }
+ 
+   static char* strdup_(const char* aStr)
+   {
+     char* s = (char*) InfallibleAllocPolicy::malloc_(strlen(aStr) + 1);
+     strcpy(s, aStr);
+     return s;
+   }
+ 
+diff --git a/mfbt/AllocPolicy.h b/mfbt/AllocPolicy.h
+--- a/mfbt/AllocPolicy.h
++++ b/mfbt/AllocPolicy.h
+@@ -37,17 +37,21 @@ namespace mozilla {
+  *      size is passed in, in addition to the new allocation size requested.
+  *  - template <typename T> T* pod_malloc(size_t)
+  *      Responsible for OOM reporting when null is returned.
+  *  - template <typename T> T* pod_calloc(size_t)
+  *      Responsible for OOM reporting when null is returned.
+  *  - template <typename T> T* pod_realloc(T*, size_t, size_t)
+  *      Responsible for OOM reporting when null is returned.  The old allocation
+  *      size is passed in, in addition to the new allocation size requested.
+- *  - void free_(void*)
++ *  - template <typename T> void free_(T*, size_t)
++ *      The capacity passed in must match the old allocation size.
++ *  - template <typename T> void free_(T*)
++ *      Frees a buffer without knowing its allocated size. This might not be
++ *      implemented by allocation policies that need the allocation size.
+  *  - void reportAllocOverflow() const
+  *      Called on allocation overflow (that is, an allocation implicitly tried
+  *      to allocate more than the available memory space -- think allocating an
+  *      array of large-size objects, where N * size overflows) before null is
+  *      returned.
+  *  - bool checkSimulatedOOM() const
+  *      Some clients generally allocate memory yet in some circumstances won't
+  *      need to do so. For example, appending to a vector with a small amount of
+@@ -109,17 +113,18 @@ public:
+   }
+ 
+   template <typename T>
+   T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
+   {
+     return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
+   }
+ 
+-  void free_(void* aPtr)
++  template <typename T>
++  void free_(T* aPtr, size_t aNumElems = 0)
+   {
+     free(aPtr);
+   }
+ 
+   void reportAllocOverflow() const
+   {
+   }
+ 
+@@ -170,17 +175,18 @@ public:
+   }
+ 
+   template <typename T>
+   T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
+   {
+     MOZ_CRASH("NeverAllocPolicy::pod_realloc");
+   }
+ 
+-  void free_(void* aPtr)
++  template <typename T>
++  void free_(T* aPtr, size_t aNumElems = 0)
+   {
+     MOZ_CRASH("NeverAllocPolicy::free_");
+   }
+ 
+   void reportAllocOverflow() const
+   {
+   }
+ 
+diff --git a/mfbt/BufferList.h b/mfbt/BufferList.h
+--- a/mfbt/BufferList.h
++++ b/mfbt/BufferList.h
+@@ -146,17 +146,17 @@ class BufferList : private AllocPolicy
+     }
+     return size;
+   }
+ 
+   void Clear()
+   {
+     if (mOwning) {
+       for (Segment& segment : mSegments) {
+-        this->free_(segment.mData);
++        this->free_(segment.mData, segment.mCapacity);
+       }
+     }
+     mSegments.clear();
+ 
+     mSize = 0;
+   }
+ 
+   // Iterates over bytes in the segments. You can advance it by as many bytes as
+@@ -346,17 +346,17 @@ class BufferList : private AllocPolicy
+   // This takes ownership of the data
+   void* WriteBytesZeroCopy(char *aData, size_t aSize, size_t aCapacity)
+   {
+     MOZ_ASSERT(aCapacity != 0);
+     MOZ_ASSERT(aSize <= aCapacity);
+     MOZ_ASSERT(mOwning);
+ 
+     if (!mSegments.append(Segment(aData, aSize, aCapacity))) {
+-      this->free_(aData);
++      this->free_(aData, aCapacity);
+       return nullptr;
+     }
+     mSize += aSize;
+     return aData;
+   }
+ 
+ private:
+   explicit BufferList(AllocPolicy aAP)
+@@ -373,17 +373,17 @@ private:
+     MOZ_ASSERT(aCapacity != 0);
+     MOZ_ASSERT(aSize <= aCapacity);
+ 
+     char* data = this->template pod_malloc<char>(aCapacity);
+     if (!data) {
+       return nullptr;
+     }
+     if (!mSegments.append(Segment(data, aSize, aCapacity))) {
+-      this->free_(data);
++      this->free_(data, aCapacity);
+       return nullptr;
+     }
+     mSize += aSize;
+     return data;
+   }
+ 
+   bool mOwning;
+   Vector<Segment, 1, AllocPolicy> mSegments;
+diff --git a/mfbt/SegmentedVector.h b/mfbt/SegmentedVector.h
+--- a/mfbt/SegmentedVector.h
++++ b/mfbt/SegmentedVector.h
+@@ -188,17 +188,17 @@ public:
+     MOZ_RELEASE_ASSERT(ok);
+   }
+ 
+   void Clear()
+   {
+     Segment* segment;
+     while ((segment = mSegments.popFirst())) {
+       segment->~Segment();
+-      this->free_(segment);
++      this->free_(segment, 1);
+     }
+   }
+ 
+   T& GetLast()
+   {
+     MOZ_ASSERT(!IsEmpty());
+     Segment* last = mSegments.getLast();
+     return (*last)[last->Length() - 1];
+@@ -214,17 +214,17 @@ public:
+   void PopLast()
+   {
+     MOZ_ASSERT(!IsEmpty());
+     Segment* last = mSegments.getLast();
+     last->PopLast();
+     if (!last->Length()) {
+       mSegments.popLast();
+       last->~Segment();
+-      this->free_(last);
++      this->free_(last, 1);
+     }
+   }
+ 
+   // Equivalent to calling |PopLast| |aNumElements| times, but potentially
+   // more efficient.
+   void PopLastN(uint32_t aNumElements)
+   {
+     MOZ_ASSERT(aNumElements <= Length());
+@@ -247,17 +247,17 @@ public:
+       uint32_t segmentLen = last->Length();
+       if (segmentLen > aNumElements) {
+         break;
+       }
+ 
+       // Destroying the segment destroys all elements contained therein.
+       mSegments.popLast();
+       last->~Segment();
+-      this->free_(last);
++      this->free_(last, 1);
+ 
+       MOZ_ASSERT(aNumElements >= segmentLen);
+       aNumElements -= segmentLen;
+       if (aNumElements == 0) {
+         return;
+       }
+     } while (true);
+ 
+diff --git a/mfbt/Vector.h b/mfbt/Vector.h
+--- a/mfbt/Vector.h
++++ b/mfbt/Vector.h
+@@ -139,17 +139,17 @@ struct VectorImpl
+       return false;
+     }
+     T* dst = newbuf;
+     T* src = aV.beginNoCheck();
+     for (; src < aV.endNoCheck(); ++dst, ++src) {
+       new_(dst, std::move(*src));
+     }
+     VectorImpl::destroy(aV.beginNoCheck(), aV.endNoCheck());
+-    aV.free_(aV.mBegin);
++    aV.free_(aV.mBegin, aV.mTail.mCapacity);
+     aV.mBegin = newbuf;
+     /* aV.mLength is unchanged. */
+     aV.mTail.mCapacity = aNewCap;
+     return true;
+   }
+ };
+ 
+ /*
+@@ -239,17 +239,17 @@ struct VectorImpl<T, N, AP, true>
+ 
+   static inline void
+   podResizeToFit(Vector<T, N, AP>& aV)
+   {
+     if (aV.usingInlineStorage() || aV.mLength == aV.mTail.mCapacity) {
+       return;
+     }
+     if (!aV.mLength) {
+-      aV.free_(aV.mBegin);
++      aV.free_(aV.mBegin, aV.mTail.mCapacity);
+       aV.mBegin = aV.inlineStorage();
+       aV.mTail.mCapacity = aV.kInlineCapacity;
+ #ifdef DEBUG
+       aV.mTail.mReserved = 0;
+ #endif
+       return;
+     }
+     T* newbuf =
+@@ -922,17 +922,17 @@ Vector<T, N, AP>::operator=(Vector&& aRh
+ 
+ template<typename T, size_t N, class AP>
+ MOZ_ALWAYS_INLINE
+ Vector<T, N, AP>::~Vector()
+ {
+   MOZ_REENTRANCY_GUARD_ET_AL;
+   Impl::destroy(beginNoCheck(), endNoCheck());
+   if (!usingInlineStorage()) {
+-    this->free_(beginNoCheck());
++    this->free_(beginNoCheck(), mTail.mCapacity);
+   }
+ }
+ 
+ template<typename T, size_t N, class AP>
+ MOZ_ALWAYS_INLINE void
+ Vector<T, N, AP>::reverse() {
+   MOZ_REENTRANCY_GUARD_ET_AL;
+   T* elems = mBegin;
+@@ -1233,17 +1233,17 @@ template<typename T, size_t N, class AP>
+ inline void
+ Vector<T, N, AP>::clearAndFree()
+ {
+   clear();
+ 
+   if (usingInlineStorage()) {
+     return;
+   }
+-  this->free_(beginNoCheck());
++  this->free_(beginNoCheck(), mTail.mCapacity);
+   mBegin = inlineStorage();
+   mTail.mCapacity = kInlineCapacity;
+ #ifdef DEBUG
+   mTail.mReserved = 0;
+ #endif
+ }
+ 
+ template<typename T, size_t N, class AP>
+@@ -1506,32 +1506,32 @@ template<typename T, size_t N, class AP>
+ inline void
+ Vector<T, N, AP>::replaceRawBuffer(T* aP, size_t aLength, size_t aCapacity)
+ {
+   MOZ_REENTRANCY_GUARD_ET_AL;
+ 
+   /* Destroy what we have. */
+   Impl::destroy(beginNoCheck(), endNoCheck());
+   if (!usingInlineStorage()) {
+-    this->free_(beginNoCheck());
++    this->free_(beginNoCheck(), mTail.mCapacity);
+   }
+ 
+   /* Take in the new buffer. */
+   if (aCapacity <= kInlineCapacity) {
+     /*
+      * We convert to inline storage if possible, even though aP might
+      * otherwise be acceptable.  Maybe this behaviour should be
+      * specifiable with an argument to this function.
+      */
+     mBegin = inlineStorage();
+     mLength = aLength;
+     mTail.mCapacity = kInlineCapacity;
+     Impl::moveConstruct(mBegin, aP, aP + aLength);
+     Impl::destroy(aP, aP + aLength);
+-    this->free_(aP);
++    this->free_(aP, aCapacity);
+   } else {
+     mBegin = aP;
+     mLength = aLength;
+     mTail.mCapacity = aCapacity;
+   }
+ #ifdef DEBUG
+   mTail.mReserved = aCapacity;
+ #endif
+diff --git a/mfbt/tests/TestBufferList.cpp b/mfbt/tests/TestBufferList.cpp
+--- a/mfbt/tests/TestBufferList.cpp
++++ b/mfbt/tests/TestBufferList.cpp
+@@ -21,17 +21,18 @@ public:
+     }
+     T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
+     if (!rv) {
+       MOZ_CRASH("TestBufferList.cpp: out of memory");
+     }
+     return rv;
+   }
+ 
+-  void free_(void* aPtr) { free(aPtr); }
++  template <typename T>
++  void free_(T* aPtr, size_t aNumElems = 0) { free(aPtr); }
+ 
+   void reportAllocOverflow() const {}
+ 
+   bool checkSimulatedOOM() const { return true; }
+ };
+ 
+ typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
+ 
+diff --git a/mfbt/tests/TestSegmentedVector.cpp b/mfbt/tests/TestSegmentedVector.cpp
+--- a/mfbt/tests/TestSegmentedVector.cpp
++++ b/mfbt/tests/TestSegmentedVector.cpp
+@@ -26,17 +26,18 @@ public:
+     }
+     T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
+     if (!rv) {
+       MOZ_CRASH("TestSegmentedVector.cpp: out of memory");
+     }
+     return rv;
+   }
+ 
+-  void free_(void* aPtr) { free(aPtr); }
++  template <typename T>
++  void free_(T* aPtr, size_t aNumElems = 0) { free(aPtr); }
+ };
+ 
+ // We want to test Append(), which is fallible and marked with
+ // MOZ_MUST_USE. But we're using an infallible alloc policy, and so
+ // don't really need to check the result. Casting to |void| works with clang
+ // but not GCC, so we instead use this dummy variable which works with both
+ // compilers.
+ static int gDummy;
+diff --git a/mozglue/misc/Printf.h b/mozglue/misc/Printf.h
+--- a/mozglue/misc/Printf.h
++++ b/mozglue/misc/Printf.h
+@@ -160,21 +160,23 @@ class MOZ_STACK_CLASS SprintfState final
+         ptrdiff_t off;
+         char* newbase;
+         size_t newlen;
+ 
+         off = mCur - mBase;
+         if (off + len >= mMaxlen) {
+             /* Grow the buffer */
+             newlen = mMaxlen + ((len > 32) ? len : 32);
+-            newbase = static_cast<char*>(this->maybe_pod_realloc(mBase, mMaxlen, newlen));
++            newbase = this->template maybe_pod_malloc<char>(newlen);
+             if (!newbase) {
+                 /* Ran out of memory */
+                 return false;
+             }
++            memcpy(newbase, mBase, mMaxlen);
++            this->free_(mBase);
+             mBase = newbase;
+             mMaxlen = newlen;
+             mCur = mBase + off;
+         }
+ 
+         /* Copy data */
+         memcpy(mCur, sp, len);
+         mCur += len;

+ 43 - 0
frg/work-js/mozilla-release/patches/1440648-63a1.patch

@@ -0,0 +1,43 @@
+# HG changeset patch
+# User Ashley Hauck <khyperia@mozilla.com>
+# Date 1534752600 -10800
+# Node ID 03537843fab253117d5df0a1a3f45cd1e5fa4d52
+# Parent  2fa2975f97e3e81f501aa982ab224ac470683f51
+Bug 1440648 - Add JSTRY_FOR_OF to JSScript::hasLoops, and guard against new JSTRY values. r=jandem
+
+diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
+--- a/js/src/vm/JSScript.cpp
++++ b/js/src/vm/JSScript.cpp
+@@ -4443,18 +4443,30 @@ JSScript::updateJitCodeRaw(JSRuntime* rt
+ bool
+ JSScript::hasLoops()
+ {
+     if (!hasTrynotes())
+         return false;
+     JSTryNote* tn = trynotes()->vector;
+     JSTryNote* tnlimit = tn + trynotes()->length;
+     for (; tn < tnlimit; tn++) {
+-        if (tn->kind == JSTRY_FOR_IN || tn->kind == JSTRY_LOOP)
++        switch (tn->kind) {
++          case JSTRY_FOR_IN:
++          case JSTRY_FOR_OF:
++          case JSTRY_LOOP:
+             return true;
++          case JSTRY_CATCH:
++          case JSTRY_FINALLY:
++          case JSTRY_FOR_OF_ITERCLOSE:
++          case JSTRY_DESTRUCTURING_ITERCLOSE:
++            break;
++          default:
++            MOZ_ASSERT(false, "Add new try note type to JSScript::hasLoops");
++            break;
++        }
+     }
+     return false;
+ }
+ 
+ bool
+ JSScript::mayReadFrameArgsDirectly()
+ {
+     return argumentsHasVarBinding() || hasRest();
+

+ 58 - 0
frg/work-js/mozilla-release/patches/1460057-62a1.patch

@@ -0,0 +1,58 @@
+# HG changeset patch
+# User Aaron Klotz <aklotz@mozilla.com>
+# Date 1525735491 21600
+# Node ID f4eb4ea90aca4747ae8d6786312608c4887a3d15
+# Parent  74cb61ae46eec86a761dfd8b245dff06bbf14634
+Bug 1460057: Update WindowsVersion with latest Windows 10 build numbers; r=froydnj
+
+diff --git a/mfbt/WindowsVersion.h b/mfbt/WindowsVersion.h
+--- a/mfbt/WindowsVersion.h
++++ b/mfbt/WindowsVersion.h
+@@ -143,28 +143,46 @@ IsWin8Point1OrLater()
+ 
+ MOZ_ALWAYS_INLINE bool
+ IsWin10OrLater()
+ {
+   return IsWindowsVersionOrLater(0x0a000000ul);
+ }
+ 
+ MOZ_ALWAYS_INLINE bool
++IsWin10November2015UpdateOrLater()
++{
++  return IsWindows10BuildOrLater(10586);
++}
++
++MOZ_ALWAYS_INLINE bool
++IsWin10AnniversaryUpdateOrLater()
++{
++  return IsWindows10BuildOrLater(14393);
++}
++
++MOZ_ALWAYS_INLINE bool
+ IsWin10CreatorsUpdateOrLater()
+ {
+   return IsWindows10BuildOrLater(15063);
+ }
+ 
+ MOZ_ALWAYS_INLINE bool
+ IsWin10FallCreatorsUpdateOrLater()
+ {
+   return IsWindows10BuildOrLater(16299);
+ }
+ 
+ MOZ_ALWAYS_INLINE bool
++IsWin10April2018UpdateOrLater()
++{
++  return IsWindows10BuildOrLater(17134);
++}
++
++MOZ_ALWAYS_INLINE bool
+ IsNotWin7PreRTM()
+ {
+   return IsWin7SP1OrLater() || IsWindowsBuildOrLater(7600);
+ }
+ 
+ inline bool
+ IsWin7AndPre2000Compatible() {
+   /*
+

+ 0 - 0
frg/work-js/mozilla-release/patches/L-1462261-1-62a1.patch → frg/work-js/mozilla-release/patches/1462261-1-62a1.patch


+ 0 - 0
frg/work-js/mozilla-release/patches/L-1462261-2-62a1.patch → frg/work-js/mozilla-release/patches/1462261-2-62a1.patch


+ 53 - 0
frg/work-js/mozilla-release/patches/1462261-3-25319.patch

@@ -0,0 +1,53 @@
+# HG changeset patch
+# User Frank-Rainer Grahl <frgrahl@gmx.net>
+# Date 1709841075 -3600
+# Parent  92dd00fd9e0ea43288b4c9b303f1b09ea0c8753d
+Bug 1462261 - Move nsNthIndexCache over to js::SystemAllocPolicy. r=me a=me
+
+and hope that I am right here and it is ok.
+
+SeaMonkey release branch only.
+
+diff --git a/layout/style/nsNthIndexCache.h b/layout/style/nsNthIndexCache.h
+--- a/layout/style/nsNthIndexCache.h
++++ b/layout/style/nsNthIndexCache.h
+@@ -54,38 +54,18 @@ private:
+                                     bool aIsOfType);
+ 
+   // This node's index for this cache.
+   // If -2, needs to be computed.
+   // If -1, needs to be computed but known not to be 1.
+   // If 0, the node is not at any index in its parent.
+   typedef int32_t CacheEntry;
+ 
+-  class SystemAllocPolicy {
+-  public:
+-    void *malloc_(size_t bytes) { return ::malloc(bytes); }
+-
+-    template <typename T>
+-    T *maybe_pod_calloc(size_t numElems) {
+-      return static_cast<T *>(::calloc(numElems, sizeof(T)));
+-    }
+-
+-    template <typename T>
+-    T *pod_calloc(size_t numElems) {
+-      return maybe_pod_calloc<T>(numElems);
+-    }
+-
+-    void *realloc_(void *p, size_t bytes) { return ::realloc(p, bytes); }
+-    void free_(void *p, size_t bytes) { ::free(p); }
+-    void reportAllocOverflow() const {}
+-    bool checkSimulatedOOM() const { return true; }
+-  };
+-
+   typedef js::HashMap<nsIContent*, CacheEntry, js::DefaultHasher<nsIContent*>,
+-                      SystemAllocPolicy> Cache;
++                      js::SystemAllocPolicy> Cache;
+ 
+   /**
+    * Returns true if aResult has been set to the correct value for aChild and
+    * no more work needs to be done.  Returns false otherwise.
+    *
+    * aResult is an inout parameter.  The in value is the number of elements
+    * that are in the half-open range (aSibling, aChild] (so including aChild
+    * but not including aSibling) that match aChild.  The out value is the

+ 14 - 14
frg/work-js/mozilla-release/patches/1465585-3-std-62a1.patch

@@ -3,7 +3,7 @@
 # Date 1527707735 -7200
 # Date 1527707735 -7200
 #      Wed May 30 21:15:35 2018 +0200
 #      Wed May 30 21:15:35 2018 +0200
 # Node ID b54db66223586b4e04f5cb926fccdacf8a176b91
 # Node ID b54db66223586b4e04f5cb926fccdacf8a176b91
-# Parent  88cd1961e21ed41edbc37429c8ad5ea4b1e0accb
+# Parent  212905ef07ce8d8b6ff377aba93247eece3bc39d
 Bug 1465585: Switch from mozilla::Move to std::move. r=froydnj
 Bug 1465585: Switch from mozilla::Move to std::move. r=froydnj
 
 
 This was done automatically replacing:
 This was done automatically replacing:
@@ -30333,7 +30333,7 @@ diff --git a/js/public/GCVector.h b/js/public/GCVector.h
 diff --git a/js/public/HashTable.h b/js/public/HashTable.h
 diff --git a/js/public/HashTable.h b/js/public/HashTable.h
 --- a/js/public/HashTable.h
 --- a/js/public/HashTable.h
 +++ b/js/public/HashTable.h
 +++ b/js/public/HashTable.h
-@@ -289,20 +289,20 @@ class HashMap
+@@ -288,20 +288,20 @@ class HashMap
          if (Ptr p = lookup(old_lookup)) {
          if (Ptr p = lookup(old_lookup)) {
              impl.rekeyAndMaybeRehash(p, new_lookup, new_key);
              impl.rekeyAndMaybeRehash(p, new_lookup, new_key);
              return true;
              return true;
@@ -30356,7 +30356,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      HashMap& operator=(const HashMap& hm) = delete;
      HashMap& operator=(const HashMap& hm) = delete;
  
  
      friend class Impl::Enum;
      friend class Impl::Enum;
-@@ -537,20 +537,20 @@ class HashSet
+@@ -536,20 +536,20 @@ class HashSet
          MOZ_ASSERT(p.found());
          MOZ_ASSERT(p.found());
          MOZ_ASSERT(*p != new_value);
          MOZ_ASSERT(*p != new_value);
          MOZ_ASSERT(HashPolicy::hash(*p) == HashPolicy::hash(new_value));
          MOZ_ASSERT(HashPolicy::hash(*p) == HashPolicy::hash(new_value));
@@ -30379,7 +30379,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      HashSet& operator=(const HashSet& hs) = delete;
      HashSet& operator=(const HashSet& hs) = delete;
  
  
      friend class Impl::Enum;
      friend class Impl::Enum;
-@@ -639,17 +639,17 @@ struct DefaultHasher<mozilla::UniquePtr<
+@@ -638,17 +638,17 @@ struct DefaultHasher<mozilla::UniquePtr<
  
  
      static HashNumber hash(const Lookup& l) {
      static HashNumber hash(const Lookup& l) {
          return PtrHasher::hash(l.get());
          return PtrHasher::hash(l.get());
@@ -30398,7 +30398,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
  struct DefaultHasher<double>
  struct DefaultHasher<double>
  {
  {
      typedef double Lookup;
      typedef double Lookup;
-@@ -742,23 +742,23 @@ class HashMapEntry
+@@ -741,23 +741,23 @@ class HashMapEntry
    public:
    public:
      template<typename KeyInput, typename ValueInput>
      template<typename KeyInput, typename ValueInput>
      HashMapEntry(KeyInput&& k, ValueInput&& v)
      HashMapEntry(KeyInput&& k, ValueInput&& v)
@@ -30426,17 +30426,17 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      const Key& key() const { return key_; }
      const Key& key() const { return key_; }
      Key& mutableKey() { return key_; }
      Key& mutableKey() { return key_; }
      const Value& value() const { return value_; }
      const Value& value() const { return value_; }
-@@ -832,17 +832,17 @@ class HashTableEntry
+@@ -841,17 +841,17 @@ class HashTableEntry
  
  
      void swap(HashTableEntry* other) {
      void swap(HashTableEntry* other) {
          if (this == other)
          if (this == other)
              return;
              return;
          MOZ_ASSERT(isLive());
          MOZ_ASSERT(isLive());
          if (other->isLive()) {
          if (other->isLive()) {
-             mozilla::Swap(*mem.addr(), *other->mem.addr());
+             mozilla::Swap(*valuePtr(), *other->valuePtr());
          } else {
          } else {
--            *other->mem.addr() = mozilla::Move(*mem.addr());
-+            *other->mem.addr() = std::move(*mem.addr());
+-            *other->valuePtr() = mozilla::Move(*valuePtr());
++            *other->valuePtr() = std::move(*valuePtr());
              destroy();
              destroy();
          }
          }
          mozilla::Swap(keyHash, other->keyHash);
          mozilla::Swap(keyHash, other->keyHash);
@@ -30444,8 +30444,8 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
  
  
      T& get() {
      T& get() {
          MOZ_ASSERT(isLive());
          MOZ_ASSERT(isLive());
-         return *mem.addr();
-@@ -1561,17 +1561,17 @@ class HashTable : private AllocPolicy
+         return *valuePtr();
+@@ -1575,17 +1575,17 @@ class HashTable : private AllocPolicy
          table = newTable;
          table = newTable;
  
  
          // Copy only live entries, leaving removed ones behind.
          // Copy only live entries, leaving removed ones behind.
@@ -30456,15 +30456,15 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
                  findFreeEntry(hn).setLive(
                  findFreeEntry(hn).setLive(
 -                    hn, mozilla::Move(const_cast<typename Entry::NonConstT&>(src->get())));
 -                    hn, mozilla::Move(const_cast<typename Entry::NonConstT&>(src->get())));
 +                    hn, std::move(const_cast<typename Entry::NonConstT&>(src->get())));
 +                    hn, std::move(const_cast<typename Entry::NonConstT&>(src->get())));
-                 src->destroy();
              }
              }
+ 
+             src->~Entry();
          }
          }
  
  
          // All entries have been destroyed, no need to destroyTable.
          // All entries have been destroyed, no need to destroyTable.
          this->free_(oldTable);
          this->free_(oldTable);
          return Rehashed;
          return Rehashed;
-     }
-@@ -1935,20 +1935,20 @@ class HashTable : private AllocPolicy
+@@ -1950,20 +1950,20 @@ class HashTable : private AllocPolicy
      }
      }
  
  
      void rekeyWithoutRehash(Ptr p, const Lookup& l, const Key& k)
      void rekeyWithoutRehash(Ptr p, const Lookup& l, const Key& k)

+ 13 - 13
frg/work-js/mozilla-release/patches/1466168-PARTIAL-js-62a1.patch

@@ -3,7 +3,7 @@
 # Date 1527870630 -7200
 # Date 1527870630 -7200
 #      Fri Jun 01 18:30:30 2018 +0200
 #      Fri Jun 01 18:30:30 2018 +0200
 # Node ID bb85c5ee5afc151be0d07ecc48318dc69cfef446
 # Node ID bb85c5ee5afc151be0d07ecc48318dc69cfef446
-# Parent  d22dbd952dd71ed92bda5e07ebbdd1bd456bbfc6
+# Parent  afda83ba6eb320885f28e5bb5702e3690d841c58
 Bug 1466168: Remove mozilla::Forward in favor of std::forward. r=froydnj
 Bug 1466168: Remove mozilla::Forward in favor of std::forward. r=froydnj
 
 
 Same approach as the other bug, mostly replacing automatically by removing
 Same approach as the other bug, mostly replacing automatically by removing
@@ -334,7 +334,7 @@ diff --git a/js/public/GCVector.h b/js/public/GCVector.h
 diff --git a/js/public/HashTable.h b/js/public/HashTable.h
 diff --git a/js/public/HashTable.h b/js/public/HashTable.h
 --- a/js/public/HashTable.h
 --- a/js/public/HashTable.h
 +++ b/js/public/HashTable.h
 +++ b/js/public/HashTable.h
-@@ -151,30 +151,30 @@ class HashMap
+@@ -150,30 +150,30 @@ class HashMap
      typedef typename Impl::AddPtr AddPtr;
      typedef typename Impl::AddPtr AddPtr;
      MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const {
      MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const {
          return impl.lookupForAdd(l);
          return impl.lookupForAdd(l);
@@ -370,7 +370,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      //   HM h;
      //   HM h;
      //   for (HM::Range r = h.all(); !r.empty(); r.popFront())
      //   for (HM::Range r = h.all(); !r.empty(); r.popFront())
      //     char c = r.front().value();
      //     char c = r.front().value();
-@@ -237,32 +237,32 @@ class HashMap
+@@ -236,32 +236,32 @@ class HashMap
          return impl.lookup(l).found();
          return impl.lookup(l).found();
      }
      }
  
  
@@ -407,7 +407,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
          if (p)
          if (p)
              return p;
              return p;
          bool ok = add(p, k, defaultValue);
          bool ok = add(p, k, defaultValue);
-@@ -406,22 +406,22 @@ class HashSet
+@@ -405,22 +405,22 @@ class HashSet
      // entry |t|, where the caller ensures match(l,t).
      // entry |t|, where the caller ensures match(l,t).
      typedef typename Impl::AddPtr AddPtr;
      typedef typename Impl::AddPtr AddPtr;
      MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const {
      MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const {
@@ -432,7 +432,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      //   HS h;
      //   HS h;
      //   for (HS::Range r = h.all(); !r.empty(); r.popFront())
      //   for (HS::Range r = h.all(); !r.empty(); r.popFront())
      //     int i = r.front();
      //     int i = r.front();
-@@ -483,34 +483,34 @@ class HashSet
+@@ -482,34 +482,34 @@ class HashSet
      bool has(const Lookup& l) const {
      bool has(const Lookup& l) const {
          return impl.lookup(l).found();
          return impl.lookup(l).found();
      }
      }
@@ -471,7 +471,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      }
      }
  
  
      // Infallibly rekey one entry, if present.
      // Infallibly rekey one entry, if present.
-@@ -708,23 +708,23 @@ struct FallibleHashMethods
+@@ -707,23 +707,23 @@ struct FallibleHashMethods
      // Fallible method to ensure a hashcode exists for its argument and create
      // Fallible method to ensure a hashcode exists for its argument and create
      // one if not.  Returns false on error, e.g. out of memory.
      // one if not.  Returns false on error, e.g. out of memory.
      template <typename Lookup> static bool ensureHash(Lookup&& l) { return true; }
      template <typename Lookup> static bool ensureHash(Lookup&& l) { return true; }
@@ -497,7 +497,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
  // more heavily parameterized than the other two. This leaves HashTable gnarly
  // more heavily parameterized than the other two. This leaves HashTable gnarly
  // and extremely coupled to HashMap and HashSet; thus code should not use
  // and extremely coupled to HashMap and HashSet; thus code should not use
  // HashTable directly.
  // HashTable directly.
-@@ -737,18 +737,18 @@ class HashMapEntry
+@@ -736,18 +736,18 @@ class HashMapEntry
  
  
      template <class, class, class> friend class detail::HashTable;
      template <class, class, class> friend class detail::HashTable;
      template <class> friend class detail::HashTableEntry;
      template <class> friend class detail::HashTableEntry;
@@ -518,7 +518,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      {}
      {}
  
  
      void operator=(HashMapEntry&& rhs) {
      void operator=(HashMapEntry&& rhs) {
-@@ -906,17 +906,17 @@ class HashTableEntry
+@@ -915,17 +915,17 @@ class HashTableEntry
          return keyHash & ~sCollisionBit;
          return keyHash & ~sCollisionBit;
      }
      }
  
  
@@ -527,8 +527,8 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      {
      {
          MOZ_ASSERT(!isLive());
          MOZ_ASSERT(!isLive());
          keyHash = hn;
          keyHash = hn;
--        new(mem.addr()) T(mozilla::Forward<Args>(args)...);
-+        new(mem.addr()) T(std::forward<Args>(args)...);
+-        new (valuePtr()) T(mozilla::Forward<Args>(args)...);
++        new (valuePtr()) T(std::forward<Args>(args)...);
          MOZ_ASSERT(isLive());
          MOZ_ASSERT(isLive());
      }
      }
  };
  };
@@ -537,7 +537,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
  class HashTable : private AllocPolicy
  class HashTable : private AllocPolicy
  {
  {
      friend class mozilla::ReentrancyGuard;
      friend class mozilla::ReentrancyGuard;
-@@ -1709,17 +1709,17 @@ class HashTable : private AllocPolicy
+@@ -1724,17 +1724,17 @@ class HashTable : private AllocPolicy
          MOZ_ASSERT(entry);
          MOZ_ASSERT(entry);
  
  
          if (entry->isRemoved()) {
          if (entry->isRemoved()) {
@@ -556,7 +556,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
  
  
    public:
    public:
      void clear()
      void clear()
-@@ -1860,51 +1860,51 @@ class HashTable : private AllocPolicy
+@@ -1875,51 +1875,51 @@ class HashTable : private AllocPolicy
              if (status == RehashFailed)
              if (status == RehashFailed)
                  return false;
                  return false;
              if (status == NotOverloaded && !this->checkSimulatedOOM())
              if (status == NotOverloaded && !this->checkSimulatedOOM())
@@ -611,7 +611,7 @@ diff --git a/js/public/HashTable.h b/js/public/HashTable.h
      template <typename... Args>
      template <typename... Args>
      MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, const Lookup& l, Args&&... args)
      MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, const Lookup& l, Args&&... args)
      {
      {
-@@ -1916,17 +1916,17 @@ class HashTable : private AllocPolicy
+@@ -1931,17 +1931,17 @@ class HashTable : private AllocPolicy
          p.generation = generation();
          p.generation = generation();
          p.mutationCount = mutationCount;
          p.mutationCount = mutationCount;
  #endif
  #endif

+ 145 - 0
frg/work-js/mozilla-release/patches/1468524-63a1.patch

@@ -0,0 +1,145 @@
+# HG changeset patch
+# User Lars T Hansen <lhansen@mozilla.com>
+# Date 1533026906 -7200
+# Node ID 3858eb122b9fb0868140e7c553ec99a49cc82611
+# Parent  61310ac63cfd3f5fdd04420311c8273e20b2b27d
+Bug 1468524 - Fix instanceof for TypedObject 'struct' instances. r=till
+
+Struct types need to appear to be callable, or some paths through the
+engine will not think that they are constructors (since constructors
+are callable).
+
+They are not *actually* callable however; the call implementation
+always throws.
+
+diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp
+--- a/js/src/builtin/TypedObject.cpp
++++ b/js/src/builtin/TypedObject.cpp
+@@ -678,17 +678,17 @@ js::IsTypedObjectArray(JSObject& obj)
+ static const ClassOps StructTypeDescrClassOps = {
+     nullptr, /* addProperty */
+     nullptr, /* delProperty */
+     nullptr, /* enumerate */
+     nullptr, /* newEnumerate */
+     nullptr, /* resolve */
+     nullptr, /* mayResolve */
+     TypeDescr::finalize,
+-    nullptr, /* call */
++    StructTypeDescr::call,
+     nullptr, /* hasInstance */
+     TypedObject::construct
+ };
+ 
+ const Class StructTypeDescr::class_ = {
+     "StructType",
+     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+     &StructTypeDescrClassOps
+@@ -1044,16 +1044,25 @@ StructTypeDescr::fieldOffset(size_t inde
+ TypeDescr&
+ StructTypeDescr::fieldDescr(size_t index) const
+ {
+     ArrayObject& fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
+     MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
+     return fieldDescrs.getDenseElement(index).toObject().as<TypeDescr>();
+ }
+ 
++bool
++StructTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
++{
++    // A structure type is a constructor and hence callable, but at present the
++    // call always throws.
++    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_STRUCTTYPE_NOT_CALLABLE);
++    return false;
++}
++
+ /******************************************************************************
+  * Creating the TypedObject "module"
+  *
+  * We create one global, `TypedObject`, which contains the following
+  * members:
+  *
+  * 1. uint8, uint16, etc
+  * 2. ArrayType
+diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h
+--- a/js/src/builtin/TypedObject.h
++++ b/js/src/builtin/TypedObject.h
+@@ -471,16 +471,18 @@ class StructTypeDescr : public ComplexTy
+     JSAtom& fieldName(size_t index) const;
+ 
+     // Return the type descr of the field at index `index`.
+     TypeDescr& fieldDescr(size_t index) const;
+ 
+     // Return the offset of the field at index `index`.
+     size_t fieldOffset(size_t index) const;
+ 
++    static bool call(JSContext* cx, unsigned argc, Value* vp);
++
+   private:
+     ArrayObject& fieldInfoObject(size_t slot) const {
+         return getReservedSlot(slot).toObject().as<ArrayObject>();
+     }
+ };
+ 
+ typedef Handle<StructTypeDescr*> HandleStructTypeDescr;
+ 
+diff --git a/js/src/js.msg b/js/src/js.msg
+--- a/js/src/js.msg
++++ b/js/src/js.msg
+@@ -536,16 +536,17 @@ MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR,    0
+ MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1, JSEXN_ERR, "No such property on self-hosted object: {0}")
+ 
+ // Typed object
+ MSG_DEF(JSMSG_INVALID_PROTOTYPE,       0, JSEXN_TYPEERR, "prototype field is not an object")
+ MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
+ MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
+ MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
+ MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
++MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_NOT_CALLABLE, 0, JSEXN_TYPEERR, "not callable")
+ MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG,     0, JSEXN_ERR, "Type is too large to allocate")
+ 
+ // Array
+ MSG_DEF(JSMSG_TOO_LONG_ARRAY,         0, JSEXN_TYPEERR, "Too long array")
+ 
+ // Typed array
+ MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
+ MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
+diff --git a/js/src/tests/non262/TypedObject/structtypereflection.js b/js/src/tests/non262/TypedObject/structtypereflection.js
+--- a/js/src/tests/non262/TypedObject/structtypereflection.js
++++ b/js/src/tests/non262/TypedObject/structtypereflection.js
+@@ -13,32 +13,35 @@ var int16 = TypedObject.int16;
+ var int32 = TypedObject.int32;
+ var float32 = TypedObject.float32;
+ var float64 = TypedObject.float64;
+ 
+ function runTests() {
+     print(BUGNUMBER + ": " + summary);
+ 
+     var S = new StructType({x: int32, y: uint8, z: float64});
++    var T = new StructType({x: int32, y: uint8, z: float64});
+     assertEq(S.__proto__, StructType.prototype);
+     assertEq(S.prototype.__proto__, StructType.prototype.prototype);
+     assertEq(S.toSource(), "new StructType({x: int32, y: uint8, z: float64})");
+     assertEq(S.byteLength, 16);
+     assertEq(S.byteAlignment, 8);
+     var fieldNames = Object.getOwnPropertyNames(S.fieldTypes);
+     assertEq(fieldNames[0], "x");
+     assertEq(fieldNames[1], "y");
+     assertEq(fieldNames[2], "z");
+     assertEq(fieldNames.length, 3);
+     assertEq(S.fieldTypes.x, int32);
+     assertEq(S.fieldTypes.y, uint8);
+     assertEq(S.fieldTypes.z, float64);
+     assertEq(S.fieldOffsets.x, 0);
+     assertEq(S.fieldOffsets.y, 4);
+     assertEq(S.fieldOffsets.z, 8);
++    assertEq((new S({x:10, y:20, z:30})) instanceof S, true);
++    assertEq((new S({x:10, y:20, z:30})) instanceof T, false);
+ 
+     // fieldTypes and fieldOffsets should be frozen
+     assertEq(Object.isFrozen(S.fieldTypes), true);
+     assertEq(Object.isFrozen(S.fieldOffsets), true);
+ 
+     if (typeof reportCompare === "function")
+         reportCompare(true, true);
+     print("Tests complete");
+

+ 487 - 0
frg/work-js/mozilla-release/patches/1469044-1-63a1.patch

@@ -0,0 +1,487 @@
+# HG changeset patch
+# User Matthew Gaudet <mgaudet@mozilla.com>
+# Date 1531166127 14400
+# Node ID 58e93d58965c4f57f20625929d33e9cafb6e0777
+# Parent  13efb4f58614a7bb20ca3149b0ca50cf92c7a13d
+Bug 1469044: [Part 1] Remove caching from math_function helper r=jandem
+
+diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp
+--- a/js/src/jsmath.cpp
++++ b/js/src/jsmath.cpp
+@@ -140,33 +140,29 @@ MathCache::MathCache() {
+ }
+ 
+ size_t
+ MathCache::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
+ {
+     return mallocSizeOf(this);
+ }
+ 
+-typedef double (*UnaryMathFunctionType)(MathCache* cache, double);
++typedef double (*UnaryMathFunctionType)(double);
+ 
+ template <UnaryMathFunctionType F>
+ static bool
+ math_function(JSContext* cx, HandleValue val, MutableHandleValue res)
+ {
+     double x;
+     if (!ToNumber(cx, val, &x))
+         return false;
+ 
+-    MathCache* mathCache = cx->caches().getMathCache(cx);
+-    if (!mathCache)
+-        return false;
+-
+     // NB: Always stored as a double so the math function can be inlined
+     // through MMathFunction.
+-    double z = F(mathCache, x);
++    double z = F(x);
+     res.setDouble(z);
+     return true;
+ }
+ 
+ template <UnaryMathFunctionType F>
+ static bool
+ math_function(JSContext* cx, unsigned argc, Value* vp)
+ {
+@@ -222,17 +218,17 @@ js::math_acos_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::acos(x);
+ }
+ 
+ bool
+ js::math_acos(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_acos_impl>(cx, argc, vp);
++    return math_function<math_acos_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_asin_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::asin, x, MathCache::Asin);
+ }
+@@ -242,17 +238,17 @@ js::math_asin_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::asin(x);
+ }
+ 
+ bool
+ js::math_asin(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_asin_impl>(cx, argc, vp);
++    return math_function<math_asin_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_atan_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::atan, x, MathCache::Atan);
+ }
+@@ -262,17 +258,17 @@ js::math_atan_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::atan(x);
+ }
+ 
+ bool
+ js::math_atan(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_atan_impl>(cx, argc, vp);
++    return math_function<math_atan_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::ecmaAtan2(double y, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::atan2(y, x);
+ }
+@@ -368,17 +364,17 @@ js::math_cos_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cos(x);
+ }
+ 
+ bool
+ js::math_cos(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_cos_impl>(cx, argc, vp);
++    return math_function<math_cos_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_exp_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::exp, x, MathCache::Exp);
+ }
+@@ -388,17 +384,17 @@ js::math_exp_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::exp(x);
+ }
+ 
+ bool
+ js::math_exp(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_exp_impl>(cx, argc, vp);
++    return math_function<math_exp_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_floor_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::floor(x);
+ }
+@@ -496,23 +492,23 @@ js::math_log_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log(x);
+ }
+ 
+ bool
+ js::math_log_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
+ {
+-    return math_function<math_log_impl>(cx, val, res);
++    return math_function<math_log_uncached>(cx, val, res);
+ }
+ 
+ bool
+ js::math_log(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_log_impl>(cx, argc, vp);
++    return math_function<math_log_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_max_impl(double x, double y)
+ {
+     AutoUnsafeCallWithABI unsafe;
+ 
+     // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
+@@ -822,23 +818,23 @@ js::math_sin_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return sin(x);
+ }
+ 
+ bool
+ js::math_sin_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
+ {
+-    return math_function<math_sin_impl>(cx, val, res);
++    return math_function<math_sin_uncached>(cx, val, res);
+ }
+ 
+ bool
+ js::math_sin(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_sin_impl>(cx, argc, vp);
++    return math_function<math_sin_uncached>(cx, argc, vp);
+ }
+ 
+ void
+ js::math_sincos_uncached(double x, double *sin, double *cos)
+ {
+     AutoUnsafeCallWithABI unsafe;
+ #if defined(HAVE_SINCOS)
+     sincos(x, sin, cos);
+@@ -868,32 +864,39 @@ js::math_sincos_impl(MathCache* mathCach
+     if (!hasSin)
+         *sin = js::math_sin_impl(mathCache, x);
+ 
+     if (!hasCos)
+         *cos = js::math_cos_impl(mathCache, x);
+ }
+ 
+ double
++js::math_sqrt_uncached(double x)
++{
++    AutoUnsafeCallWithABI unsafe;
++    return sqrt(x);
++}
++
++double
+ js::math_sqrt_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(sqrt, x, MathCache::Sqrt);
+ }
+ 
+ bool
+ js::math_sqrt_handle(JSContext* cx, HandleValue number, MutableHandleValue result)
+ {
+-    return math_function<math_sqrt_impl>(cx, number, result);
++    return math_function<math_sqrt_uncached>(cx, number, result);
+ }
+ 
+ bool
+ js::math_sqrt(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_sqrt_impl>(cx, argc, vp);
++    return math_function<math_sqrt_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_tan_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(tan, x, MathCache::Tan);
+ }
+@@ -903,17 +906,17 @@ js::math_tan_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return tan(x);
+ }
+ 
+ bool
+ js::math_tan(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_tan_impl>(cx, argc, vp);
++    return math_function<math_tan_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_log10_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::log10, x, MathCache::Log10);
+ }
+@@ -923,17 +926,17 @@ js::math_log10_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log10(x);
+ }
+ 
+ bool
+ js::math_log10(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_log10_impl>(cx, argc, vp);
++    return math_function<math_log10_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_log2_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::log2, x, MathCache::Log2);
+ }
+@@ -943,17 +946,17 @@ js::math_log2_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log2(x);
+ }
+ 
+ bool
+ js::math_log2(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_log2_impl>(cx, argc, vp);
++    return math_function<math_log2_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_log1p_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::log1p, x, MathCache::Log1p);
+ }
+@@ -963,17 +966,17 @@ js::math_log1p_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log1p(x);
+ }
+ 
+ bool
+ js::math_log1p(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_log1p_impl>(cx, argc, vp);
++    return math_function<math_log1p_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_expm1_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::expm1, x, MathCache::Expm1);
+ }
+@@ -983,17 +986,17 @@ js::math_expm1_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::expm1(x);
+ }
+ 
+ bool
+ js::math_expm1(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_expm1_impl>(cx, argc, vp);
++    return math_function<math_expm1_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_cosh_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::cosh, x, MathCache::Cosh);
+ }
+@@ -1003,17 +1006,17 @@ js::math_cosh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::cosh(x);
+ }
+ 
+ bool
+ js::math_cosh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_cosh_impl>(cx, argc, vp);
++    return math_function<math_cosh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_sinh_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::sinh, x, MathCache::Sinh);
+ }
+@@ -1023,17 +1026,17 @@ js::math_sinh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::sinh(x);
+ }
+ 
+ bool
+ js::math_sinh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_sinh_impl>(cx, argc, vp);
++    return math_function<math_sinh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_tanh_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::tanh, x, MathCache::Tanh);
+ }
+@@ -1043,17 +1046,17 @@ js::math_tanh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::tanh(x);
+ }
+ 
+ bool
+ js::math_tanh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_tanh_impl>(cx, argc, vp);
++    return math_function<math_tanh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_acosh_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::acosh, x, MathCache::Acosh);
+ }
+@@ -1063,17 +1066,17 @@ js::math_acosh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::acosh(x);
+ }
+ 
+ bool
+ js::math_acosh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_acosh_impl>(cx, argc, vp);
++    return math_function<math_acosh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_asinh_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::asinh, x, MathCache::Asinh);
+ }
+@@ -1083,17 +1086,17 @@ js::math_asinh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::asinh(x);
+ }
+ 
+ bool
+ js::math_asinh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_asinh_impl>(cx, argc, vp);
++    return math_function<math_asinh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_atanh_impl(MathCache* cache, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cache->lookup(fdlibm::atanh, x, MathCache::Atanh);
+ }
+@@ -1103,17 +1106,17 @@ js::math_atanh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::atanh(x);
+ }
+ 
+ bool
+ js::math_atanh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_atanh_impl>(cx, argc, vp);
++    return math_function<math_atanh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+ js::ecmaHypot(double x, double y)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::hypot(x, y);
+ }
+@@ -1297,17 +1300,17 @@ js::math_cbrt_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::cbrt(x);
+ }
+ 
+ bool
+ js::math_cbrt(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_cbrt_impl>(cx, argc, vp);
++    return math_function<math_cbrt_uncached>(cx, argc, vp);
+ }
+ 
+ static bool
+ math_toSource(JSContext* cx, unsigned argc, Value* vp)
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+     args.rval().setString(cx->names().Math);
+     return true;
+diff --git a/js/src/jsmath.h b/js/src/jsmath.h
+--- a/js/src/jsmath.h
++++ b/js/src/jsmath.h
+@@ -119,16 +119,19 @@ math_max(JSContext* cx, unsigned argc, j
+ 
+ extern double
+ math_min_impl(double x, double y);
+ 
+ extern bool
+ math_min(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
++math_sqrt_uncached(double x);
++
++extern double
+ math_sqrt_impl(MathCache* cache, double x);
+ 
+ extern bool
+ math_sqrt_handle(JSContext* cx, js::HandleValue number, js::MutableHandleValue result);
+ 
+ extern bool
+ math_sqrt(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+

+ 1343 - 0
frg/work-js/mozilla-release/patches/1469044-2-63a1.patch

@@ -0,0 +1,1343 @@
+# HG changeset patch
+# User Matthew Gaudet <mgaudet@mozilla.com>
+# Date 1531169536 14400
+# Node ID 76ed6d31be85c63eab788714d65b8ae787660700
+# Parent  58e93d58965c4f57f20625929d33e9cafb6e0777
+Bug 1469044: [Part 2] Remove remainder of MathCache r=jandem
+
+diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h
+--- a/js/public/MemoryMetrics.h
++++ b/js/public/MemoryMetrics.h
+@@ -555,17 +555,16 @@ struct RuntimeSizes
+ {
+ #define FOR_EACH_SIZE(macro) \
+     macro(_, MallocHeap, object) \
+     macro(_, MallocHeap, atomsTable) \
+     macro(_, MallocHeap, atomsMarkBitmaps) \
+     macro(_, MallocHeap, contexts) \
+     macro(_, MallocHeap, temporary) \
+     macro(_, MallocHeap, interpreterStack) \
+-    macro(_, MallocHeap, mathCache) \
+     macro(_, MallocHeap, sharedImmutableStringsCache) \
+     macro(_, MallocHeap, sharedIntlData) \
+     macro(_, MallocHeap, uncompressedSourceCache) \
+     macro(_, MallocHeap, scriptData) \
+     macro(_, MallocHeap, tracelogger) \
+     macro(_, MallocHeap, wasmRuntime) \
+     macro(_, MallocHeap, jitLazyLink)
+ 
+diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
+--- a/js/src/jit/CodeGenerator.cpp
++++ b/js/src/jit/CodeGenerator.cpp
+@@ -7584,86 +7584,79 @@ void
+ CodeGenerator::visitMathFunctionD(LMathFunctionD* ins)
+ {
+     Register temp = ToRegister(ins->temp());
+     FloatRegister input = ToFloatRegister(ins->input());
+     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+ 
+     masm.setupUnalignedABICall(temp);
+ 
+-    const MathCache* mathCache = ins->mir()->cache();
+-    if (mathCache) {
+-        masm.movePtr(ImmPtr(mathCache), temp);
+-        masm.passABIArg(temp);
+-    }
+     masm.passABIArg(input, MoveOp::DOUBLE);
+ 
+-#   define MAYBE_CACHED(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached)
+-
+     void* funptr = nullptr;
+     switch (ins->mir()->function()) {
+       case MMathFunction::Log:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_log));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log_uncached);
+         break;
+       case MMathFunction::Sin:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_sin));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_sin_uncached);
+         break;
+       case MMathFunction::Cos:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_cos));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cos_uncached);
+         break;
+       case MMathFunction::Exp:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_exp));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_exp_uncached);
+         break;
+       case MMathFunction::Tan:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_tan));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_tan_uncached);
+         break;
+       case MMathFunction::ATan:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_atan));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_atan_uncached);
+         break;
+       case MMathFunction::ASin:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_asin));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_asin_uncached);
+         break;
+       case MMathFunction::ACos:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_acos));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_acos_uncached);
+         break;
+       case MMathFunction::Log10:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_log10));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log10_uncached);
+         break;
+       case MMathFunction::Log2:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_log2));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log2_uncached);
+         break;
+       case MMathFunction::Log1P:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_log1p));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log1p_uncached);
+         break;
+       case MMathFunction::ExpM1:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_expm1));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_expm1_uncached);
+         break;
+       case MMathFunction::CosH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_cosh));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cosh_uncached);
+         break;
+       case MMathFunction::SinH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_sinh));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_sinh_uncached);
+         break;
+       case MMathFunction::TanH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_tanh));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_tanh_uncached);
+         break;
+       case MMathFunction::ACosH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_acosh));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_acosh_uncached);
+         break;
+       case MMathFunction::ASinH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_asinh));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_asinh_uncached);
+         break;
+       case MMathFunction::ATanH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_atanh));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_atanh_uncached);
+         break;
+       case MMathFunction::Trunc:
+         funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_trunc_uncached);
+         break;
+       case MMathFunction::Cbrt:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED(js::math_cbrt));
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cbrt_uncached);
+         break;
+       case MMathFunction::Floor:
+         funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_floor_impl);
+         break;
+       case MMathFunction::Ceil:
+         funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_ceil_impl);
+         break;
+       case MMathFunction::Round:
+@@ -8964,34 +8957,25 @@ CodeGenerator::visitSinCos(LSinCos *lir)
+     Register params = ToRegister(lir->temp2());
+     FloatRegister input = ToFloatRegister(lir->input());
+     FloatRegister outputSin = ToFloatRegister(lir->outputSin());
+     FloatRegister outputCos = ToFloatRegister(lir->outputCos());
+ 
+     masm.reserveStack(sizeof(double) * 2);
+     masm.moveStackPtrTo(params);
+ 
+-    const MathCache* mathCache = lir->mir()->cache();
+-
+     masm.setupUnalignedABICall(temp);
+-    if (mathCache) {
+-        masm.movePtr(ImmPtr(mathCache), temp);
+-        masm.passABIArg(temp);
+-    }
+-
+-#define MAYBE_CACHED_(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached)
+ 
+     masm.passABIArg(input, MoveOp::DOUBLE);
+     masm.passABIArg(MoveOperand(params, sizeof(double), MoveOperand::EFFECTIVE_ADDRESS),
+                                 MoveOp::GENERAL);
+     masm.passABIArg(MoveOperand(params, 0, MoveOperand::EFFECTIVE_ADDRESS),
+                                 MoveOp::GENERAL);
+ 
+-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED_(js::math_sincos)));
+-#undef MAYBE_CACHED_
++    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::math_sincos_uncached));
+ 
+     masm.loadDouble(Address(masm.getStackPointer(), 0), outputCos);
+     masm.loadDouble(Address(masm.getStackPointer(), sizeof(double)), outputSin);
+     masm.freeStack(sizeof(double) * 2);
+ }
+ 
+ typedef ArrayObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t);
+ static const VMFunction StringSplitInfo =
+diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
+--- a/js/src/jit/Ion.cpp
++++ b/js/src/jit/Ion.cpp
+@@ -1314,18 +1314,17 @@ OptimizeSinCos(MIRGraph &graph)
+                 continue;
+             }
+ 
+             JitSpew(JitSpew_Sincos, "Found, at least, a pair sin/cos. Adding sincos in block %d",
+                     block->id());
+             // Adding the MSinCos and replacing the parameters of the
+             // sin(x)/cos(x) to sin(sincos(x))/cos(sincos(x)).
+             MSinCos *insSinCos = MSinCos::New(graph.alloc(),
+-                                              insFunc->input(),
+-                                              insFunc->toMathFunction()->cache());
++                                              insFunc->input());
+             insSinCos->setImplicitlyUsedUnchecked();
+             block->insertBefore(insFunc, insSinCos);
+             for (MUseDefIterator uses(insFunc->input()); uses; )
+             {
+                 MDefinition* def = uses.def();
+                 uses++;
+                 if (!def->isInstruction())
+                     continue;
+diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
+--- a/js/src/jit/MCallOptimize.cpp
++++ b/js/src/jit/MCallOptimize.cpp
+@@ -507,22 +507,20 @@ IonBuilder::inlineMathFunction(CallInfo&
+     if (callInfo.argc() != 1)
+         return InliningStatus_NotInlined;
+ 
+     if (getInlineReturnType() != MIRType::Double)
+         return InliningStatus_NotInlined;
+     if (!IsNumberType(callInfo.getArg(0)->type()))
+         return InliningStatus_NotInlined;
+ 
+-    const MathCache* cache = TlsContext.get()->caches().maybeGetMathCache();
+-
+     callInfo.fun()->setImplicitlyUsedUnchecked();
+     callInfo.thisArg()->setImplicitlyUsedUnchecked();
+ 
+-    MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), function, cache);
++    MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), function);
+     current->add(ins);
+     current->push(ins);
+     return InliningStatus_Inlined;
+ }
+ 
+ IonBuilder::InliningResult
+ IonBuilder::inlineArray(CallInfo& callInfo)
+ {
+@@ -1150,18 +1148,17 @@ IonBuilder::inlineMathFloor(CallInfo& ca
+ 
+         if (returnType == MIRType::Double) {
+             callInfo.setImplicitlyUsedUnchecked();
+ 
+             MInstruction* ins = nullptr;
+             if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) {
+                 ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType, RoundingMode::Down);
+             } else {
+-                ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor,
+-                                         /* cache */ nullptr);
++                ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor);
+             }
+ 
+             current->add(ins);
+             current->push(ins);
+             return InliningStatus_Inlined;
+         }
+     }
+ 
+@@ -1204,18 +1201,17 @@ IonBuilder::inlineMathCeil(CallInfo& cal
+ 
+         if (returnType == MIRType::Double) {
+             callInfo.setImplicitlyUsedUnchecked();
+ 
+             MInstruction* ins = nullptr;
+             if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) {
+                 ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType, RoundingMode::Up);
+             } else {
+-                ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil,
+-                                         /* cache */ nullptr);
++                ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil);
+             }
+ 
+             current->add(ins);
+             current->push(ins);
+             return InliningStatus_Inlined;
+         }
+     }
+ 
+@@ -1276,18 +1272,17 @@ IonBuilder::inlineMathRound(CallInfo& ca
+         MRound* ins = MRound::New(alloc(), callInfo.getArg(0));
+         current->add(ins);
+         current->push(ins);
+         return InliningStatus_Inlined;
+     }
+ 
+     if (IsFloatingPointType(argType) && returnType == MIRType::Double) {
+         callInfo.setImplicitlyUsedUnchecked();
+-        MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round,
+-                                                /* cache */ nullptr);
++        MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round);
+         current->add(ins);
+         current->push(ins);
+         return InliningStatus_Inlined;
+     }
+ 
+     return InliningStatus_NotInlined;
+ }
+ 
+@@ -1521,18 +1516,17 @@ IonBuilder::inlineMathTrunc(CallInfo& ca
+         if (returnType == MIRType::Double) {
+             callInfo.setImplicitlyUsedUnchecked();
+ 
+             MInstruction* ins = nullptr;
+             if (MNearbyInt::HasAssemblerSupport(RoundingMode::TowardsZero)) {
+                 ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType,
+                                       RoundingMode::TowardsZero);
+             } else {
+-                ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Trunc,
+-                                         /* cache */ nullptr);
++                ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Trunc);
+             }
+ 
+             current->add(ins);
+             current->push(ins);
+             return InliningStatus_Inlined;
+         }
+     }
+ 
+diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
+--- a/js/src/jit/MIR.h
++++ b/js/src/jit/MIR.h
+@@ -7130,37 +7130,34 @@ class MMathFunction
+         Cbrt,
+         Floor,
+         Ceil,
+         Round
+     };
+ 
+   private:
+     Function function_;
+-    const MathCache* cache_;
+ 
+     // A nullptr cache means this function will neither access nor update the cache.
+-    MMathFunction(MDefinition* input, Function function, const MathCache* cache)
+-      : MUnaryInstruction(classOpcode, input), function_(function), cache_(cache)
++    MMathFunction(MDefinition* input, Function function)
++      : MUnaryInstruction(classOpcode, input), function_(function)
+     {
+         setResultType(MIRType::Double);
+         specialization_ = MIRType::Double;
+         setMovable();
+     }
+ 
+   public:
+     INSTRUCTION_HEADER(MathFunction)
+     TRIVIAL_NEW_WRAPPERS
+ 
+     Function function() const {
+         return function_;
+     }
+-    const MathCache* cache() const {
+-        return cache_;
+-    }
++
+     bool congruentTo(const MDefinition* ins) const override {
+         if (!ins->isMathFunction())
+             return false;
+         if (ins->toMathFunction()->function() != function())
+             return false;
+         return congruentIfOperandsEqual(ins);
+     }
+ 
+@@ -7810,46 +7807,41 @@ class MStringConvertCase
+         return mode_;
+     }
+ };
+ 
+ class MSinCos
+   : public MUnaryInstruction,
+     public FloatingPointPolicy<0>::Data
+ {
+-    const MathCache* cache_;
+-
+-    MSinCos(MDefinition *input, const MathCache *cache)
+-      : MUnaryInstruction(classOpcode, input),
+-        cache_(cache)
++
++    explicit MSinCos(MDefinition *input)
++      : MUnaryInstruction(classOpcode, input)
+     {
+         setResultType(MIRType::SinCosDouble);
+         specialization_ = MIRType::Double;
+         setMovable();
+     }
+ 
+   public:
+     INSTRUCTION_HEADER(SinCos)
+ 
+-    static MSinCos *New(TempAllocator &alloc, MDefinition *input, const MathCache *cache)
+-    {
+-        return new (alloc) MSinCos(input, cache);
++    static MSinCos *New(TempAllocator &alloc, MDefinition *input)
++    {
++        return new (alloc) MSinCos(input);
+     }
+     AliasSet getAliasSet() const override {
+         return AliasSet::None();
+     }
+     bool congruentTo(const MDefinition *ins) const override {
+         return congruentIfOperandsEqual(ins);
+     }
+     bool possiblyCalls() const override {
+         return true;
+     }
+-    const MathCache* cache() const {
+-        return cache_;
+-    }
+ };
+ 
+ class MStringSplit
+   : public MBinaryInstruction,
+     public MixPolicy<StringPolicy<0>, StringPolicy<1> >::Data
+ {
+     CompilerObjectGroup group_;
+ 
+diff --git a/js/src/jsapi-tests/testJitRangeAnalysis.cpp b/js/src/jsapi-tests/testJitRangeAnalysis.cpp
+--- a/js/src/jsapi-tests/testJitRangeAnalysis.cpp
++++ b/js/src/jsapi-tests/testJitRangeAnalysis.cpp
+@@ -106,17 +106,16 @@ BEGIN_TEST(testJitRangeAnalysis_MathSign
+ 
+     return true;
+ }
+ END_TEST(testJitRangeAnalysis_MathSign)
+ 
+ BEGIN_TEST(testJitRangeAnalysis_MathSignBeta)
+ {
+     MinimalFunc func;
+-    MathCache cache;
+ 
+     MBasicBlock* entry = func.createEntryBlock();
+     MBasicBlock* thenBlock = func.createBlock(entry);
+     MBasicBlock* elseBlock = func.createBlock(entry);
+     MBasicBlock* elseThenBlock = func.createBlock(elseBlock);
+     MBasicBlock* elseElseBlock = func.createBlock(elseBlock);
+ 
+     // if (p < 0)
+diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp
+--- a/js/src/jsmath.cpp
++++ b/js/src/jsmath.cpp
+@@ -125,31 +125,16 @@ static const JSConstDoubleSpec math_cons
+     {"LN2"    ,  M_LN2     },
+     {"LN10"   ,  M_LN10    },
+     {"PI"     ,  M_PI      },
+     {"SQRT2"  ,  M_SQRT2   },
+     {"SQRT1_2",  M_SQRT1_2 },
+     {nullptr  ,  0         }
+ };
+ 
+-MathCache::MathCache() {
+-    memset(table, 0, sizeof(table));
+-
+-    /* See comments in lookup(). */
+-    MOZ_ASSERT(IsNegativeZero(-0.0));
+-    MOZ_ASSERT(!IsNegativeZero(+0.0));
+-    MOZ_ASSERT(hash(-0.0, MathCache::Sin) != hash(+0.0, MathCache::Sin));
+-}
+-
+-size_t
+-MathCache::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
+-{
+-    return mallocSizeOf(this);
+-}
+-
+ typedef double (*UnaryMathFunctionType)(double);
+ 
+ template <UnaryMathFunctionType F>
+ static bool
+ math_function(JSContext* cx, HandleValue val, MutableHandleValue res)
+ {
+     double x;
+     if (!ToNumber(cx, val, &x))
+@@ -202,63 +187,42 @@ js::math_abs(JSContext* cx, unsigned arg
+         args.rval().setNaN();
+         return true;
+     }
+ 
+     return math_abs_handle(cx, args[0], args.rval());
+ }
+ 
+ double
+-js::math_acos_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::acos, x, MathCache::Acos);
+-}
+-
+-double
+ js::math_acos_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::acos(x);
+ }
+ 
+ bool
+ js::math_acos(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_acos_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_asin_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::asin, x, MathCache::Asin);
+-}
+-
+-double
+ js::math_asin_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::asin(x);
+ }
+ 
+ bool
+ js::math_asin(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_asin_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_atan_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::atan, x, MathCache::Atan);
+-}
+-
+-double
+ js::math_atan_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::atan(x);
+ }
+ 
+ bool
+ js::math_atan(JSContext* cx, unsigned argc, Value* vp)
+@@ -348,43 +312,29 @@ js::math_clz32(JSContext* cx, unsigned a
+         return true;
+     }
+ 
+     args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
+     return true;
+ }
+ 
+ double
+-js::math_cos_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(cos, x, MathCache::Cos);
+-}
+-
+-double
+ js::math_cos_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cos(x);
+ }
+ 
+ bool
+ js::math_cos(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_cos_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_exp_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::exp, x, MathCache::Exp);
+-}
+-
+-double
+ js::math_exp_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::exp(x);
+ }
+ 
+ bool
+ js::math_exp(JSContext* cx, unsigned argc, Value* vp)
+@@ -475,22 +425,16 @@ js::math_fround(JSContext* cx, unsigned 
+     if (args.length() == 0) {
+         args.rval().setNaN();
+         return true;
+     }
+ 
+     return RoundFloat32(cx, args[0], args.rval());
+ }
+ 
+-double
+-js::math_log_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::log, x, MathCache::Log);
+-}
+ 
+ double
+ js::math_log_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log(x);
+ }
+ 
+@@ -802,23 +746,16 @@ js::math_round(JSContext* cx, unsigned a
+         args.rval().setNaN();
+         return true;
+     }
+ 
+     return math_round_handle(cx, args[0], args.rval());
+ }
+ 
+ double
+-js::math_sin_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(sin, x, MathCache::Sin);
+-}
+-
+-double
+ js::math_sin_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return sin(x);
+ }
+ 
+ bool
+ js::math_sin_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
+@@ -841,272 +778,167 @@ js::math_sincos_uncached(double x, doubl
+ #elif defined(HAVE___SINCOS)
+     __sincos(x, sin, cos);
+ #else
+     *sin = js::math_sin_uncached(x);
+     *cos = js::math_cos_uncached(x);
+ #endif
+ }
+ 
+-void
+-js::math_sincos_impl(MathCache* mathCache, double x, double *sin, double *cos)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    unsigned indexSin;
+-    unsigned indexCos;
+-    bool hasSin = mathCache->isCached(x, MathCache::Sin, sin, &indexSin);
+-    bool hasCos = mathCache->isCached(x, MathCache::Cos, cos, &indexCos);
+-    if (!(hasSin || hasCos)) {
+-        js::math_sincos_uncached(x, sin, cos);
+-        mathCache->store(MathCache::Sin, x, *sin, indexSin);
+-        mathCache->store(MathCache::Cos, x, *cos, indexCos);
+-        return;
+-    }
+-
+-    if (!hasSin)
+-        *sin = js::math_sin_impl(mathCache, x);
+-
+-    if (!hasCos)
+-        *cos = js::math_cos_impl(mathCache, x);
+-}
+-
+ double
+ js::math_sqrt_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return sqrt(x);
+ }
+ 
+-double
+-js::math_sqrt_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(sqrt, x, MathCache::Sqrt);
+-}
+-
+ bool
+ js::math_sqrt_handle(JSContext* cx, HandleValue number, MutableHandleValue result)
+ {
+     return math_function<math_sqrt_uncached>(cx, number, result);
+ }
+ 
+ bool
+ js::math_sqrt(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_sqrt_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_tan_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(tan, x, MathCache::Tan);
+-}
+-
+-double
+ js::math_tan_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return tan(x);
+ }
+ 
+ bool
+ js::math_tan(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_tan_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_log10_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::log10, x, MathCache::Log10);
+-}
+-
+-double
+ js::math_log10_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log10(x);
+ }
+ 
+ bool
+ js::math_log10(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_log10_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_log2_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::log2, x, MathCache::Log2);
+-}
+-
+-double
+ js::math_log2_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log2(x);
+ }
+ 
+ bool
+ js::math_log2(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_log2_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_log1p_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::log1p, x, MathCache::Log1p);
+-}
+-
+-double
+ js::math_log1p_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log1p(x);
+ }
+ 
+ bool
+ js::math_log1p(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_log1p_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_expm1_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::expm1, x, MathCache::Expm1);
+-}
+-
+-double
+ js::math_expm1_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::expm1(x);
+ }
+ 
+ bool
+ js::math_expm1(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_expm1_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_cosh_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::cosh, x, MathCache::Cosh);
+-}
+-
+-double
+ js::math_cosh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::cosh(x);
+ }
+ 
+ bool
+ js::math_cosh(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_cosh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_sinh_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::sinh, x, MathCache::Sinh);
+-}
+-
+-double
+ js::math_sinh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::sinh(x);
+ }
+ 
+ bool
+ js::math_sinh(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_sinh_uncached>(cx, argc, vp);
+ }
+ 
+-double
+-js::math_tanh_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::tanh, x, MathCache::Tanh);
+-}
+ 
+ double
+ js::math_tanh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::tanh(x);
+ }
+ 
+ bool
+ js::math_tanh(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_tanh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_acosh_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::acosh, x, MathCache::Acosh);
+-}
+-
+-double
+ js::math_acosh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::acosh(x);
+ }
+ 
+ bool
+ js::math_acosh(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_acosh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_asinh_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::asinh, x, MathCache::Asinh);
+-}
+-
+-double
+ js::math_asinh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::asinh(x);
+ }
+ 
+ bool
+ js::math_asinh(JSContext* cx, unsigned argc, Value* vp)
+ {
+     return math_function<math_asinh_uncached>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_atanh_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::atanh, x, MathCache::Atanh);
+-}
+-
+-double
+ js::math_atanh_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::atanh(x);
+ }
+ 
+ bool
+ js::math_atanh(JSContext* cx, unsigned argc, Value* vp)
+@@ -1284,23 +1116,16 @@ js::math_sign(JSContext* cx, unsigned ar
+         args.rval().setNaN();
+         return true;
+     }
+ 
+     return math_sign_handle(cx, args[0], args.rval());
+ }
+ 
+ double
+-js::math_cbrt_impl(MathCache* cache, double x)
+-{
+-    AutoUnsafeCallWithABI unsafe;
+-    return cache->lookup(fdlibm::cbrt, x, MathCache::Cbrt);
+-}
+-
+-double
+ js::math_cbrt_uncached(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::cbrt(x);
+ }
+ 
+ bool
+ js::math_cbrt(JSContext* cx, unsigned argc, Value* vp)
+diff --git a/js/src/jsmath.h b/js/src/jsmath.h
+--- a/js/src/jsmath.h
++++ b/js/src/jsmath.h
+@@ -15,78 +15,16 @@
+ 
+ namespace js {
+ 
+ struct Class;
+ class GlobalObject;
+ 
+ typedef double (*UnaryFunType)(double);
+ 
+-class MathCache
+-{
+-  public:
+-    enum MathFuncId {
+-        Zero,
+-        Sin, Cos, Tan, Sinh, Cosh, Tanh, Asin, Acos, Atan, Asinh, Acosh, Atanh,
+-        Sqrt, Log, Log10, Log2, Log1p, Exp, Expm1, Cbrt
+-    };
+-
+-  private:
+-    static const unsigned SizeLog2 = 12;
+-    static const unsigned Size = 1 << SizeLog2;
+-    struct Entry { double in; MathFuncId id; double out; };
+-    Entry table[Size];
+-
+-  public:
+-    MathCache();
+-
+-    unsigned hash(double x, MathFuncId id) {
+-        union { double d; struct { uint32_t one, two; } s; } u = { x };
+-        uint32_t hash32 = u.s.one ^ u.s.two;
+-        hash32 += uint32_t(id) << 8;
+-        uint16_t hash16 = uint16_t(hash32 ^ (hash32 >> 16));
+-        return (hash16 & (Size - 1)) ^ (hash16 >> (16 - SizeLog2));
+-    }
+-
+-    /*
+-     * N.B. lookup uses double-equality. This is only safe if hash() maps +0
+-     * and -0 to different table entries, which is asserted in MathCache().
+-     */
+-    double lookup(UnaryFunType f, double x, MathFuncId id) {
+-        unsigned index = hash(x, id);
+-        Entry& e = table[index];
+-        if (e.in == x && e.id == id)
+-            return e.out;
+-        e.in = x;
+-        e.id = id;
+-        return e.out = f(x);
+-    }
+-
+-    bool isCached(double x, MathFuncId id, double *r, unsigned *index) {
+-        *index = hash(x, id);
+-        Entry& e = table[*index];
+-        if (e.in == x && e.id == id) {
+-            *r = e.out;
+-            return true;
+-        }
+-        return false;
+-    }
+-
+-    void store(MathFuncId id, double x, double v, unsigned index) {
+-        Entry &e = table[index];
+-        if (e.in == x && e.id == id)
+-            return;
+-        e.in = x;
+-        e.id = id;
+-        e.out = v;
+-    }
+-
+-    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+-};
+-
+ /*
+  * JS math functions.
+  */
+ 
+ extern const Class MathClass;
+ 
+ extern JSObject*
+ InitMathClass(JSContext* cx, Handle<GlobalObject*> global);
+@@ -121,38 +59,32 @@ extern double
+ math_min_impl(double x, double y);
+ 
+ extern bool
+ math_min(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+ math_sqrt_uncached(double x);
+ 
+-extern double
+-math_sqrt_impl(MathCache* cache, double x);
+-
+ extern bool
+ math_sqrt_handle(JSContext* cx, js::HandleValue number, js::MutableHandleValue result);
+ 
+ extern bool
+ math_sqrt(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ math_pow(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ minmax_impl(JSContext* cx, bool max, js::HandleValue a, js::HandleValue b,
+             js::MutableHandleValue res);
+ 
+ extern void
+ math_sincos_uncached(double x, double *sin, double *cos);
+ 
+-extern void
+-math_sincos_impl(MathCache* mathCache, double x, double *sin, double *cos);
+-
+ extern bool
+ math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res);
+ 
+ extern bool
+ math_imul(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ RoundFloat32(JSContext* cx, HandleValue v, float* out);
+@@ -162,61 +94,46 @@ RoundFloat32(JSContext* cx, HandleValue 
+ 
+ extern bool
+ math_fround(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ math_log(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_log_impl(MathCache* cache, double x);
+-
+-extern double
+ math_log_uncached(double x);
+ 
+ extern bool
+ math_log_handle(JSContext* cx, HandleValue val, MutableHandleValue res);
+ 
+ extern bool
+ math_sin(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_sin_impl(MathCache* cache, double x);
+-
+-extern double
+ math_sin_uncached(double x);
+ 
+ extern bool
+ math_sin_handle(JSContext* cx, HandleValue val, MutableHandleValue res);
+ 
+ extern bool
+ math_cos(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_cos_impl(MathCache* cache, double x);
+-
+-extern double
+ math_cos_uncached(double x);
+ 
+ extern bool
+ math_exp(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_exp_impl(MathCache* cache, double x);
+-
+-extern double
+ math_exp_uncached(double x);
+ 
+ extern bool
+ math_tan(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_tan_impl(MathCache* cache, double x);
+-
+-extern double
+ math_tan_uncached(double x);
+ 
+ extern bool
+ math_log10(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ math_log2(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+@@ -282,37 +199,28 @@ math_atan2_handle(JSContext* cx, HandleV
+ 
+ extern bool
+ math_atan2(JSContext* cx, unsigned argc, Value* vp);
+ 
+ extern double
+ ecmaAtan2(double x, double y);
+ 
+ extern double
+-math_atan_impl(MathCache* cache, double x);
+-
+-extern double
+ math_atan_uncached(double x);
+ 
+ extern bool
+ math_atan(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_asin_impl(MathCache* cache, double x);
+-
+-extern double
+ math_asin_uncached(double x);
+ 
+ extern bool
+ math_asin(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_acos_impl(MathCache* cache, double x);
+-
+-extern double
+ math_acos_uncached(double x);
+ 
+ extern bool
+ math_acos(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ math_ceil_handle(JSContext* cx, HandleValue value, MutableHandleValue res);
+ 
+@@ -351,73 +259,43 @@ math_roundf_impl(float x);
+ 
+ extern double
+ powi(double x, int32_t y);
+ 
+ extern double
+ ecmaPow(double x, double y);
+ 
+ extern double
+-math_log10_impl(MathCache* cache, double x);
+-
+-extern double
+ math_log10_uncached(double x);
+ 
+ extern double
+-math_log2_impl(MathCache* cache, double x);
+-
+-extern double
+ math_log2_uncached(double x);
+ 
+ extern double
+-math_log1p_impl(MathCache* cache, double x);
+-
+-extern double
+ math_log1p_uncached(double x);
+ 
+ extern double
+-math_expm1_impl(MathCache* cache, double x);
+-
+-extern double
+ math_expm1_uncached(double x);
+ 
+ extern double
+-math_cosh_impl(MathCache* cache, double x);
+-
+-extern double
+ math_cosh_uncached(double x);
+ 
+ extern double
+-math_sinh_impl(MathCache* cache, double x);
+-
+-extern double
+ math_sinh_uncached(double x);
+ 
+ extern double
+-math_tanh_impl(MathCache* cache, double x);
+-
+-extern double
+ math_tanh_uncached(double x);
+ 
+ extern double
+-math_acosh_impl(MathCache* cache, double x);
+-
+-extern double
+ math_acosh_uncached(double x);
+ 
+ extern double
+-math_asinh_impl(MathCache* cache, double x);
+-
+-extern double
+ math_asinh_uncached(double x);
+ 
+ extern double
+-math_atanh_impl(MathCache* cache, double x);
+-
+-extern double
+ math_atanh_uncached(double x);
+ 
+ extern double
+ math_trunc_uncached(double x);
+ 
+ extern float
+ math_truncf_impl(float x);
+ 
+@@ -426,16 +304,13 @@ math_trunc_handle(JSContext* cx, HandleV
+ 
+ extern double
+ math_sign_uncached(double x);
+ 
+ extern bool
+ math_sign_handle(JSContext* cx, HandleValue v, MutableHandleValue r);
+ 
+ extern double
+-math_cbrt_impl(MathCache* cache, double x);
+-
+-extern double
+ math_cbrt_uncached(double x);
+ 
+ } /* namespace js */
+ 
+ #endif /* jsmath_h */
+diff --git a/js/src/vm/Caches.cpp b/js/src/vm/Caches.cpp
+--- a/js/src/vm/Caches.cpp
++++ b/js/src/vm/Caches.cpp
+@@ -7,31 +7,16 @@
+ #include "vm/Caches-inl.h"
+ 
+ #include "mozilla/PodOperations.h"
+ 
+ using namespace js;
+ 
+ using mozilla::PodZero;
+ 
+-MathCache*
+-RuntimeCaches::createMathCache(JSContext* cx)
+-{
+-    MOZ_ASSERT(!mathCache_);
+-
+-    auto newMathCache = MakeUnique<MathCache>();
+-    if (!newMathCache) {
+-        ReportOutOfMemory(cx);
+-        return nullptr;
+-    }
+-
+-    mathCache_ = std::move(newMathCache);
+-    return mathCache_.get();
+-}
+-
+ bool
+ RuntimeCaches::init()
+ {
+     if (!evalCache.init())
+         return false;
+ 
+     return true;
+ }
+diff --git a/js/src/vm/Caches.h b/js/src/vm/Caches.h
+--- a/js/src/vm/Caches.h
++++ b/js/src/vm/Caches.h
+@@ -235,36 +235,25 @@ class NewObjectCache
+         // Initialize with barriers
+         dst->initGroup(src->group());
+         dst->initShape(src->shape());
+     }
+ };
+ 
+ class RuntimeCaches
+ {
+-    UniquePtr<js::MathCache> mathCache_;
+-
+-    js::MathCache* createMathCache(JSContext* cx);
+-
+   public:
+     js::GSNCache gsnCache;
+     js::EnvironmentCoordinateNameCache envCoordinateNameCache;
+     js::NewObjectCache newObjectCache;
+     js::UncompressedSourceCache uncompressedSourceCache;
+     js::EvalCache evalCache;
+ 
+     bool init();
+ 
+-    js::MathCache* getMathCache(JSContext* cx) {
+-        return mathCache_ ? mathCache_.get() : createMathCache(cx);
+-    }
+-    js::MathCache* maybeGetMathCache() {
+-        return mathCache_.get();
+-    }
+-
+     void purgeForMinorGC(JSRuntime* rt) {
+         newObjectCache.clearNurseryObjects(rt);
+         evalCache.sweep();
+     }
+ 
+     void purgeForCompaction() {
+         newObjectCache.purge();
+         if (evalCache.initialized())
+diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
+--- a/js/src/vm/Runtime.cpp
++++ b/js/src/vm/Runtime.cpp
+@@ -370,19 +370,16 @@ JSRuntime::addSizeOfIncludingThis(mozill
+     rtSizes->contexts += cx->sizeOfExcludingThis(mallocSizeOf);
+     rtSizes->temporary += cx->tempLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
+     rtSizes->interpreterStack += cx->interpreterStack().sizeOfExcludingThis(mallocSizeOf);
+ #ifdef JS_TRACE_LOGGING
+     if (cx->traceLogger)
+         rtSizes->tracelogger += cx->traceLogger->sizeOfIncludingThis(mallocSizeOf);
+ #endif
+ 
+-    if (MathCache* cache = caches().maybeGetMathCache())
+-        rtSizes->mathCache += cache->sizeOfIncludingThis(mallocSizeOf);
+-
+     rtSizes->uncompressedSourceCache +=
+         caches().uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf);
+ 
+     rtSizes->gc.nurseryCommitted += gc.nursery().sizeOfHeapCommitted();
+     rtSizes->gc.nurseryMallocedBuffers += gc.nursery().sizeOfMallocedBuffers(mallocSizeOf);
+     gc.storeBuffer().addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
+ 
+     if (sharedImmutableStrings_) {
+diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp
+--- a/js/xpconnect/src/XPCJSRuntime.cpp
++++ b/js/xpconnect/src/XPCJSRuntime.cpp
+@@ -1882,20 +1882,16 @@ ReportJSRuntimeExplicitTreeStats(const J
+         KIND_HEAP, rtStats.runtime.temporary,
+         "Transient data (mostly parse nodes) held by the JSRuntime during "
+         "compilation.");
+ 
+     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"),
+         KIND_HEAP, rtStats.runtime.interpreterStack,
+         "JS interpreter frames.");
+ 
+-    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"),
+-        KIND_HEAP, rtStats.runtime.mathCache,
+-        "The math cache.");
+-
+     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/shared-immutable-strings-cache"),
+         KIND_HEAP, rtStats.runtime.sharedImmutableStringsCache,
+         "Immutable strings (such as JS scripts' source text) shared across all JSRuntimes.");
+ 
+     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/shared-intl-data"),
+         KIND_HEAP, rtStats.runtime.sharedIntlData,
+         "Shared internationalization data.");
+ 
+

+ 969 - 0
frg/work-js/mozilla-release/patches/1469044-3-63a1.patch

@@ -0,0 +1,969 @@
+# HG changeset patch
+# User Matthew Gaudet <mgaudet@mozilla.com>
+# Date 1531171258 14400
+# Node ID c980318f4f82e2175a1f23c6c4d301f01dbd4ed1
+# Parent  76ed6d31be85c63eab788714d65b8ae787660700
+Bug 1469044: [Part 3] Rename 'uncached' to 'impl' r=jandem
+
+diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
+--- a/js/src/jit/CodeGenerator.cpp
++++ b/js/src/jit/CodeGenerator.cpp
+@@ -7589,74 +7589,74 @@ CodeGenerator::visitMathFunctionD(LMathF
+ 
+     masm.setupUnalignedABICall(temp);
+ 
+     masm.passABIArg(input, MoveOp::DOUBLE);
+ 
+     void* funptr = nullptr;
+     switch (ins->mir()->function()) {
+       case MMathFunction::Log:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log_impl);
+         break;
+       case MMathFunction::Sin:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_sin_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_sin_impl);
+         break;
+       case MMathFunction::Cos:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cos_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cos_impl);
+         break;
+       case MMathFunction::Exp:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_exp_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_exp_impl);
+         break;
+       case MMathFunction::Tan:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_tan_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_tan_impl);
+         break;
+       case MMathFunction::ATan:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_atan_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_atan_impl);
+         break;
+       case MMathFunction::ASin:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_asin_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_asin_impl);
+         break;
+       case MMathFunction::ACos:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_acos_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_acos_impl);
+         break;
+       case MMathFunction::Log10:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log10_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log10_impl);
+         break;
+       case MMathFunction::Log2:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log2_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log2_impl);
+         break;
+       case MMathFunction::Log1P:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log1p_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_log1p_impl);
+         break;
+       case MMathFunction::ExpM1:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_expm1_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_expm1_impl);
+         break;
+       case MMathFunction::CosH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cosh_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cosh_impl);
+         break;
+       case MMathFunction::SinH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_sinh_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_sinh_impl);
+         break;
+       case MMathFunction::TanH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_tanh_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_tanh_impl);
+         break;
+       case MMathFunction::ACosH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_acosh_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_acosh_impl);
+         break;
+       case MMathFunction::ASinH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_asinh_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_asinh_impl);
+         break;
+       case MMathFunction::ATanH:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_atanh_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_atanh_impl);
+         break;
+       case MMathFunction::Trunc:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_trunc_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_trunc_impl);
+         break;
+       case MMathFunction::Cbrt:
+-        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cbrt_uncached);
++        funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_cbrt_impl);
+         break;
+       case MMathFunction::Floor:
+         funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_floor_impl);
+         break;
+       case MMathFunction::Ceil:
+         funptr = JS_FUNC_TO_DATA_PTR(void*, js::math_ceil_impl);
+         break;
+       case MMathFunction::Round:
+@@ -8965,17 +8965,17 @@ CodeGenerator::visitSinCos(LSinCos *lir)
+     masm.setupUnalignedABICall(temp);
+ 
+     masm.passABIArg(input, MoveOp::DOUBLE);
+     masm.passABIArg(MoveOperand(params, sizeof(double), MoveOperand::EFFECTIVE_ADDRESS),
+                                 MoveOp::GENERAL);
+     masm.passABIArg(MoveOperand(params, 0, MoveOperand::EFFECTIVE_ADDRESS),
+                                 MoveOp::GENERAL);
+ 
+-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::math_sincos_uncached));
++    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::math_sincos_impl));
+ 
+     masm.loadDouble(Address(masm.getStackPointer(), 0), outputCos);
+     masm.loadDouble(Address(masm.getStackPointer(), sizeof(double)), outputSin);
+     masm.freeStack(sizeof(double) * 2);
+ }
+ 
+ typedef ArrayObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t);
+ static const VMFunction StringSplitInfo =
+diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
+--- a/js/src/jit/MIR.cpp
++++ b/js/src/jit/MIR.cpp
+@@ -1894,17 +1894,17 @@ void MNearbyInt::printOpcode(GenericPrin
+ MDefinition*
+ MSign::foldsTo(TempAllocator& alloc)
+ {
+     MDefinition* input = getOperand(0);
+     if (!input->isConstant() || !input->toConstant()->isTypeRepresentableAsDouble())
+         return this;
+ 
+     double in = input->toConstant()->numberToDouble();
+-    double out = js::math_sign_uncached(in);
++    double out = js::math_sign_impl(in);
+ 
+     if (type() == MIRType::Int32) {
+         // Decline folding if this is an int32 operation, but the result type
+         // isn't an int32.
+         Value outValue = NumberValue(out);
+         if (!outValue.isInt32())
+             return this;
+ 
+@@ -1961,74 +1961,74 @@ MMathFunction::foldsTo(TempAllocator& al
+     MDefinition* input = getOperand(0);
+     if (!input->isConstant() || !input->toConstant()->isTypeRepresentableAsDouble())
+         return this;
+ 
+     double in = input->toConstant()->numberToDouble();
+     double out;
+     switch (function_) {
+       case Log:
+-        out = js::math_log_uncached(in);
++        out = js::math_log_impl(in);
+         break;
+       case Sin:
+-        out = js::math_sin_uncached(in);
++        out = js::math_sin_impl(in);
+         break;
+       case Cos:
+-        out = js::math_cos_uncached(in);
++        out = js::math_cos_impl(in);
+         break;
+       case Exp:
+-        out = js::math_exp_uncached(in);
++        out = js::math_exp_impl(in);
+         break;
+       case Tan:
+-        out = js::math_tan_uncached(in);
++        out = js::math_tan_impl(in);
+         break;
+       case ACos:
+-        out = js::math_acos_uncached(in);
++        out = js::math_acos_impl(in);
+         break;
+       case ASin:
+-        out = js::math_asin_uncached(in);
++        out = js::math_asin_impl(in);
+         break;
+       case ATan:
+-        out = js::math_atan_uncached(in);
++        out = js::math_atan_impl(in);
+         break;
+       case Log10:
+-        out = js::math_log10_uncached(in);
++        out = js::math_log10_impl(in);
+         break;
+       case Log2:
+-        out = js::math_log2_uncached(in);
++        out = js::math_log2_impl(in);
+         break;
+       case Log1P:
+-        out = js::math_log1p_uncached(in);
++        out = js::math_log1p_impl(in);
+         break;
+       case ExpM1:
+-        out = js::math_expm1_uncached(in);
++        out = js::math_expm1_impl(in);
+         break;
+       case CosH:
+-        out = js::math_cosh_uncached(in);
++        out = js::math_cosh_impl(in);
+         break;
+       case SinH:
+-        out = js::math_sinh_uncached(in);
++        out = js::math_sinh_impl(in);
+         break;
+       case TanH:
+-        out = js::math_tanh_uncached(in);
++        out = js::math_tanh_impl(in);
+         break;
+       case ACosH:
+-        out = js::math_acosh_uncached(in);
++        out = js::math_acosh_impl(in);
+         break;
+       case ASinH:
+-        out = js::math_asinh_uncached(in);
++        out = js::math_asinh_impl(in);
+         break;
+       case ATanH:
+-        out = js::math_atanh_uncached(in);
++        out = js::math_atanh_impl(in);
+         break;
+       case Trunc:
+-        out = js::math_trunc_uncached(in);
++        out = js::math_trunc_impl(in);
+         break;
+       case Cbrt:
+-        out = js::math_cbrt_uncached(in);
++        out = js::math_cbrt_impl(in);
+         break;
+       case Floor:
+         out = js::math_floor_impl(in);
+         break;
+       case Ceil:
+         out = js::math_ceil_impl(in);
+         break;
+       case Round:
+diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp
+--- a/js/src/jsmath.cpp
++++ b/js/src/jsmath.cpp
+@@ -187,52 +187,52 @@ js::math_abs(JSContext* cx, unsigned arg
+         args.rval().setNaN();
+         return true;
+     }
+ 
+     return math_abs_handle(cx, args[0], args.rval());
+ }
+ 
+ double
+-js::math_acos_uncached(double x)
++js::math_acos_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::acos(x);
+ }
+ 
+ bool
+ js::math_acos(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_acos_uncached>(cx, argc, vp);
++    return math_function<math_acos_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_asin_uncached(double x)
++js::math_asin_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::asin(x);
+ }
+ 
+ bool
+ js::math_asin(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_asin_uncached>(cx, argc, vp);
++    return math_function<math_asin_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_atan_uncached(double x)
++js::math_atan_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::atan(x);
+ }
+ 
+ bool
+ js::math_atan(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_atan_uncached>(cx, argc, vp);
++    return math_function<math_atan_impl>(cx, argc, vp);
+ }
+ 
+ double
+ js::ecmaAtan2(double y, double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::atan2(y, x);
+ }
+@@ -312,39 +312,39 @@ js::math_clz32(JSContext* cx, unsigned a
+         return true;
+     }
+ 
+     args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
+     return true;
+ }
+ 
+ double
+-js::math_cos_uncached(double x)
++js::math_cos_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return cos(x);
+ }
+ 
+ bool
+ js::math_cos(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_cos_uncached>(cx, argc, vp);
++    return math_function<math_cos_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_exp_uncached(double x)
++js::math_exp_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::exp(x);
+ }
+ 
+ bool
+ js::math_exp(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_exp_uncached>(cx, argc, vp);
++    return math_function<math_exp_impl>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_floor_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::floor(x);
+ }
+@@ -427,32 +427,32 @@ js::math_fround(JSContext* cx, unsigned 
+         return true;
+     }
+ 
+     return RoundFloat32(cx, args[0], args.rval());
+ }
+ 
+ 
+ double
+-js::math_log_uncached(double x)
++js::math_log_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log(x);
+ }
+ 
+ bool
+ js::math_log_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
+ {
+-    return math_function<math_log_uncached>(cx, val, res);
++    return math_function<math_log_impl>(cx, val, res);
+ }
+ 
+ bool
+ js::math_log(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_log_uncached>(cx, argc, vp);
++    return math_function<math_log_impl>(cx, argc, vp);
+ }
+ 
+ double
+ js::math_max_impl(double x, double y)
+ {
+     AutoUnsafeCallWithABI unsafe;
+ 
+     // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
+@@ -746,209 +746,209 @@ js::math_round(JSContext* cx, unsigned a
+         args.rval().setNaN();
+         return true;
+     }
+ 
+     return math_round_handle(cx, args[0], args.rval());
+ }
+ 
+ double
+-js::math_sin_uncached(double x)
++js::math_sin_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return sin(x);
+ }
+ 
+ bool
+ js::math_sin_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
+ {
+-    return math_function<math_sin_uncached>(cx, val, res);
++    return math_function<math_sin_impl>(cx, val, res);
+ }
+ 
+ bool
+ js::math_sin(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_sin_uncached>(cx, argc, vp);
++    return math_function<math_sin_impl>(cx, argc, vp);
+ }
+ 
+ void
+-js::math_sincos_uncached(double x, double *sin, double *cos)
++js::math_sincos_impl(double x, double *sin, double *cos)
+ {
+     AutoUnsafeCallWithABI unsafe;
+ #if defined(HAVE_SINCOS)
+     sincos(x, sin, cos);
+ #elif defined(HAVE___SINCOS)
+     __sincos(x, sin, cos);
+ #else
+-    *sin = js::math_sin_uncached(x);
+-    *cos = js::math_cos_uncached(x);
++    *sin = js::math_sin_impl(x);
++    *cos = js::math_cos_impl(x);
+ #endif
+ }
+ 
+ double
+-js::math_sqrt_uncached(double x)
++js::math_sqrt_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return sqrt(x);
+ }
+ 
+ bool
+ js::math_sqrt_handle(JSContext* cx, HandleValue number, MutableHandleValue result)
+ {
+-    return math_function<math_sqrt_uncached>(cx, number, result);
++    return math_function<math_sqrt_impl>(cx, number, result);
+ }
+ 
+ bool
+ js::math_sqrt(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_sqrt_uncached>(cx, argc, vp);
++    return math_function<math_sqrt_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_tan_uncached(double x)
++js::math_tan_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return tan(x);
+ }
+ 
+ bool
+ js::math_tan(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_tan_uncached>(cx, argc, vp);
++    return math_function<math_tan_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_log10_uncached(double x)
++js::math_log10_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log10(x);
+ }
+ 
+ bool
+ js::math_log10(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_log10_uncached>(cx, argc, vp);
++    return math_function<math_log10_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_log2_uncached(double x)
++js::math_log2_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log2(x);
+ }
+ 
+ bool
+ js::math_log2(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_log2_uncached>(cx, argc, vp);
++    return math_function<math_log2_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_log1p_uncached(double x)
++js::math_log1p_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::log1p(x);
+ }
+ 
+ bool
+ js::math_log1p(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_log1p_uncached>(cx, argc, vp);
++    return math_function<math_log1p_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_expm1_uncached(double x)
++js::math_expm1_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::expm1(x);
+ }
+ 
+ bool
+ js::math_expm1(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_expm1_uncached>(cx, argc, vp);
++    return math_function<math_expm1_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_cosh_uncached(double x)
++js::math_cosh_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::cosh(x);
+ }
+ 
+ bool
+ js::math_cosh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_cosh_uncached>(cx, argc, vp);
++    return math_function<math_cosh_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_sinh_uncached(double x)
++js::math_sinh_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::sinh(x);
+ }
+ 
+ bool
+ js::math_sinh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_sinh_uncached>(cx, argc, vp);
++    return math_function<math_sinh_impl>(cx, argc, vp);
+ }
+ 
+ 
+ double
+-js::math_tanh_uncached(double x)
++js::math_tanh_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::tanh(x);
+ }
+ 
+ bool
+ js::math_tanh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_tanh_uncached>(cx, argc, vp);
++    return math_function<math_tanh_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_acosh_uncached(double x)
++js::math_acosh_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::acosh(x);
+ }
+ 
+ bool
+ js::math_acosh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_acosh_uncached>(cx, argc, vp);
++    return math_function<math_acosh_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_asinh_uncached(double x)
++js::math_asinh_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::asinh(x);
+ }
+ 
+ bool
+ js::math_asinh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_asinh_uncached>(cx, argc, vp);
++    return math_function<math_asinh_impl>(cx, argc, vp);
+ }
+ 
+ double
+-js::math_atanh_uncached(double x)
++js::math_atanh_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::atanh(x);
+ }
+ 
+ bool
+ js::math_atanh(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_atanh_uncached>(cx, argc, vp);
++    return math_function<math_atanh_impl>(cx, argc, vp);
+ }
+ 
+ double
+ js::ecmaHypot(double x, double y)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::hypot(x, y);
+ }
+@@ -1045,17 +1045,17 @@ js::math_hypot_handle(JSContext* cx, Han
+     double result = isInfinite ? PositiveInfinity<double>() :
+                     isNaN ? GenericNaN() :
+                     scale * sqrt(sumsq);
+     res.setDouble(result);
+     return true;
+ }
+ 
+ double
+-js::math_trunc_uncached(double x)
++js::math_trunc_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::trunc(x);
+ }
+ 
+ float
+ js::math_truncf_impl(float x)
+ {
+@@ -1065,77 +1065,77 @@ js::math_truncf_impl(float x)
+ 
+ bool
+ js::math_trunc_handle(JSContext* cx, HandleValue v, MutableHandleValue r)
+ {
+     double x;
+     if (!ToNumber(cx, v, &x))
+         return false;
+ 
+-    r.setNumber(math_trunc_uncached(x));
++    r.setNumber(math_trunc_impl(x));
+     return true;
+ }
+ 
+ bool
+ js::math_trunc(JSContext* cx, unsigned argc, Value* vp)
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+     if (args.length() == 0) {
+         args.rval().setNaN();
+         return true;
+     }
+ 
+     return math_trunc_handle(cx, args[0], args.rval());
+ }
+ 
+ double
+-js::math_sign_uncached(double x)
++js::math_sign_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+ 
+     if (mozilla::IsNaN(x))
+         return GenericNaN();
+ 
+     return x == 0 ? x : x < 0 ? -1 : 1;
+ }
+ 
+ bool
+ js::math_sign_handle(JSContext* cx, HandleValue v, MutableHandleValue r)
+ {
+     double x;
+     if (!ToNumber(cx, v, &x))
+         return false;
+ 
+-    r.setNumber(math_sign_uncached(x));
++    r.setNumber(math_sign_impl(x));
+     return true;
+ }
+ 
+ bool
+ js::math_sign(JSContext* cx, unsigned argc, Value* vp)
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+     if (args.length() == 0) {
+         args.rval().setNaN();
+         return true;
+     }
+ 
+     return math_sign_handle(cx, args[0], args.rval());
+ }
+ 
+ double
+-js::math_cbrt_uncached(double x)
++js::math_cbrt_impl(double x)
+ {
+     AutoUnsafeCallWithABI unsafe;
+     return fdlibm::cbrt(x);
+ }
+ 
+ bool
+ js::math_cbrt(JSContext* cx, unsigned argc, Value* vp)
+ {
+-    return math_function<math_cbrt_uncached>(cx, argc, vp);
++    return math_function<math_cbrt_impl>(cx, argc, vp);
+ }
+ 
+ static bool
+ math_toSource(JSContext* cx, unsigned argc, Value* vp)
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+     args.rval().setString(cx->names().Math);
+     return true;
+diff --git a/js/src/jsmath.h b/js/src/jsmath.h
+--- a/js/src/jsmath.h
++++ b/js/src/jsmath.h
+@@ -57,33 +57,33 @@ math_max(JSContext* cx, unsigned argc, j
+ 
+ extern double
+ math_min_impl(double x, double y);
+ 
+ extern bool
+ math_min(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_sqrt_uncached(double x);
++math_sqrt_impl(double x);
+ 
+ extern bool
+ math_sqrt_handle(JSContext* cx, js::HandleValue number, js::MutableHandleValue result);
+ 
+ extern bool
+ math_sqrt(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ math_pow(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ minmax_impl(JSContext* cx, bool max, js::HandleValue a, js::HandleValue b,
+             js::MutableHandleValue res);
+ 
+ extern void
+-math_sincos_uncached(double x, double *sin, double *cos);
++math_sincos_impl(double x, double *sin, double *cos);
+ 
+ extern bool
+ math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue res);
+ 
+ extern bool
+ math_imul(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+@@ -94,47 +94,47 @@ RoundFloat32(JSContext* cx, HandleValue 
+ 
+ extern bool
+ math_fround(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ math_log(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_log_uncached(double x);
++math_log_impl(double x);
+ 
+ extern bool
+ math_log_handle(JSContext* cx, HandleValue val, MutableHandleValue res);
+ 
+ extern bool
+ math_sin(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_sin_uncached(double x);
++math_sin_impl(double x);
+ 
+ extern bool
+ math_sin_handle(JSContext* cx, HandleValue val, MutableHandleValue res);
+ 
+ extern bool
+ math_cos(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_cos_uncached(double x);
++math_cos_impl(double x);
+ 
+ extern bool
+ math_exp(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_exp_uncached(double x);
++math_exp_impl(double x);
+ 
+ extern bool
+ math_tan(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_tan_uncached(double x);
++math_tan_impl(double x);
+ 
+ extern bool
+ math_log10(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ math_log2(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+@@ -199,29 +199,29 @@ math_atan2_handle(JSContext* cx, HandleV
+ 
+ extern bool
+ math_atan2(JSContext* cx, unsigned argc, Value* vp);
+ 
+ extern double
+ ecmaAtan2(double x, double y);
+ 
+ extern double
+-math_atan_uncached(double x);
++math_atan_impl(double x);
+ 
+ extern bool
+ math_atan(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_asin_uncached(double x);
++math_asin_impl(double x);
+ 
+ extern bool
+ math_asin(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern double
+-math_acos_uncached(double x);
++math_acos_impl(double x);
+ 
+ extern bool
+ math_acos(JSContext* cx, unsigned argc, js::Value* vp);
+ 
+ extern bool
+ math_ceil_handle(JSContext* cx, HandleValue value, MutableHandleValue res);
+ 
+ extern bool
+@@ -259,58 +259,58 @@ math_roundf_impl(float x);
+ 
+ extern double
+ powi(double x, int32_t y);
+ 
+ extern double
+ ecmaPow(double x, double y);
+ 
+ extern double
+-math_log10_uncached(double x);
++math_log10_impl(double x);
+ 
+ extern double
+-math_log2_uncached(double x);
++math_log2_impl(double x);
+ 
+ extern double
+-math_log1p_uncached(double x);
++math_log1p_impl(double x);
+ 
+ extern double
+-math_expm1_uncached(double x);
++math_expm1_impl(double x);
+ 
+ extern double
+-math_cosh_uncached(double x);
++math_cosh_impl(double x);
+ 
+ extern double
+-math_sinh_uncached(double x);
++math_sinh_impl(double x);
+ 
+ extern double
+-math_tanh_uncached(double x);
++math_tanh_impl(double x);
+ 
+ extern double
+-math_acosh_uncached(double x);
++math_acosh_impl(double x);
+ 
+ extern double
+-math_asinh_uncached(double x);
++math_asinh_impl(double x);
+ 
+ extern double
+-math_atanh_uncached(double x);
++math_atanh_impl(double x);
+ 
+ extern double
+-math_trunc_uncached(double x);
++math_trunc_impl(double x);
+ 
+ extern float
+ math_truncf_impl(float x);
+ 
+ extern bool
+ math_trunc_handle(JSContext* cx, HandleValue v, MutableHandleValue r);
+ 
+ extern double
+-math_sign_uncached(double x);
++math_sign_impl(double x);
+ 
+ extern bool
+ math_sign_handle(JSContext* cx, HandleValue v, MutableHandleValue r);
+ 
+ extern double
+-math_cbrt_uncached(double x);
++math_cbrt_impl(double x);
+ 
+ } /* namespace js */
+ 
+ #endif /* jsmath_h */
+diff --git a/js/src/wasm/WasmBuiltins.cpp b/js/src/wasm/WasmBuiltins.cpp
+--- a/js/src/wasm/WasmBuiltins.cpp
++++ b/js/src/wasm/WasmBuiltins.cpp
+@@ -806,18 +806,18 @@ wasm::NeedsBuiltinThunk(SymbolicAddress 
+     _(math_cbrt, MathCbrt)
+ 
+ #define FOR_EACH_BINARY_NATIVE(_)  \
+     _(ecmaAtan2, MathATan2)        \
+     _(ecmaHypot, MathHypot)        \
+     _(ecmaPow, MathPow)            \
+ 
+ #define DEFINE_UNARY_FLOAT_WRAPPER(func, _)        \
+-    static float func##_uncached_f32(float x) {    \
+-        return float(func##_uncached(double(x)));  \
++    static float func##_impl_f32(float x) {    \
++        return float(func##_impl(double(x)));  \
+     }
+ 
+ #define DEFINE_BINARY_FLOAT_WRAPPER(func, _)       \
+     static float func##_f32(float x, float y) {    \
+         return float(func(double(x), double(y)));  \
+     }
+ 
+ FOR_EACH_UNARY_NATIVE(DEFINE_UNARY_FLOAT_WRAPPER)
+@@ -855,18 +855,18 @@ PopulateTypedNatives(TypedNativeToFuncPt
+         return false;
+ 
+ #define ADD_OVERLOAD(funcName, native, abiType)                                           \
+     if (!typedNatives->putNew(TypedNative(InlinableNative::native, abiType),              \
+                               FuncCast(funcName, abiType)))                               \
+         return false;
+ 
+ #define ADD_UNARY_OVERLOADS(funcName, native)                                             \
+-    ADD_OVERLOAD(funcName##_uncached, native, Args_Double_Double)                         \
+-    ADD_OVERLOAD(funcName##_uncached_f32, native, Args_Float32_Float32)
++    ADD_OVERLOAD(funcName##_impl, native, Args_Double_Double)                         \
++    ADD_OVERLOAD(funcName##_impl_f32, native, Args_Float32_Float32)
+ 
+ #define ADD_BINARY_OVERLOADS(funcName, native)                                            \
+     ADD_OVERLOAD(funcName, native, Args_Double_DoubleDouble)                              \
+     ADD_OVERLOAD(funcName##_f32, native, Args_Float32_Float32Float32)
+ 
+     FOR_EACH_UNARY_NATIVE(ADD_UNARY_OVERLOADS)
+     FOR_EACH_BINARY_NATIVE(ADD_BINARY_OVERLOADS)
+ 
+

+ 61 - 0
frg/work-js/mozilla-release/patches/1473024-63a1.patch

@@ -0,0 +1,61 @@
+# HG changeset patch
+# User Andrea Marchesini <amarchesini@mozilla.com>
+# Date 1530771664 -7200
+# Node ID 78d32cf0c528ae55319aa20ea69539230e0969cc
+# Parent  c33a3baeae16df0cf1be2e362fd5515e0b0509fa
+Bug 1473024 - CSP should throw EvalError when blocking eval(), r=ckerschb
+
+diff --git a/dom/workers/test/test_csp.js b/dom/workers/test/test_csp.js
+--- a/dom/workers/test/test_csp.js
++++ b/dom/workers/test/test_csp.js
+@@ -23,26 +23,26 @@ xhr = new XMLHttpRequest;
+ xhr.open("GET", "csp_worker.js");
+ xhr.responseType = "blob";
+ xhr.send();
+ xhr.onload = (e) => {
+   uri = URL.createObjectURL(e.target.response);
+   worker = new Worker(uri);
+   worker.postMessage({ do: "eval" })
+   worker.onmessage = function(event) {
+-    is(event.data, "Error: call to eval() blocked by CSP", "Eval threw");
++    is(event.data, "EvalError: call to eval() blocked by CSP", "Eval threw");
+     testDone();
+   }
+ }
+ 
+ xhr = new XMLHttpRequest;
+ xhr.open("GET", "csp_worker.js");
+ xhr.responseType = "blob";
+ xhr.send();
+ xhr.onload = (e) => {
+   uri = URL.createObjectURL(e.target.response);
+   worker = new Worker(uri);
+   worker.postMessage({ do: "nest", uri: uri, level: 3 })
+   worker.onmessage = function(event) {
+-    is(event.data, "Error: call to eval() blocked by CSP", "Eval threw in nested worker");
++    is(event.data, "EvalError: call to eval() blocked by CSP", "Eval threw in nested worker");
+     testDone();
+   }
+ }
+diff --git a/js/src/js.msg b/js/src/js.msg
+--- a/js/src/js.msg
++++ b/js/src/js.msg
+@@ -148,17 +148,17 @@ MSG_DEF(JSMSG_BAD_FORMAL,              0
+ MSG_DEF(JSMSG_CALLER_IS_STRICT,        0, JSEXN_TYPEERR, "access to strict mode caller function is censored")
+ MSG_DEF(JSMSG_DEPRECATED_USAGE,        1, JSEXN_REFERENCEERR, "deprecated {0} usage")
+ MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION,   1, JSEXN_TYPEERR, "{0} is not a scripted function")
+ MSG_DEF(JSMSG_NO_REST_NAME,            0, JSEXN_SYNTAXERR, "no parameter name after ...")
+ MSG_DEF(JSMSG_PARAMETER_AFTER_REST,    0, JSEXN_SYNTAXERR, "parameter after rest parameter")
+ MSG_DEF(JSMSG_TOO_MANY_ARGUMENTS,      0, JSEXN_RANGEERR, "too many arguments provided for a function call")
+ 
+ // CSP
+-MSG_DEF(JSMSG_CSP_BLOCKED_EVAL,        0, JSEXN_ERR, "call to eval() blocked by CSP")
++MSG_DEF(JSMSG_CSP_BLOCKED_EVAL,        0, JSEXN_EVALERR, "call to eval() blocked by CSP")
+ MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION,    0, JSEXN_ERR, "call to Function() blocked by CSP")
+ 
+ // Wrappers
+ MSG_DEF(JSMSG_ACCESSOR_DEF_DENIED,     1, JSEXN_ERR, "Permission denied to define accessor property {0}")
+ MSG_DEF(JSMSG_DEAD_OBJECT,             0, JSEXN_TYPEERR, "can't access dead object")
+ MSG_DEF(JSMSG_OBJECT_ACCESS_DENIED,    0, JSEXN_ERR, "Permission denied to access object")
+ MSG_DEF(JSMSG_PROPERTY_ACCESS_DENIED,  1, JSEXN_ERR, "Permission denied to access property {0}")
+ 

+ 853 - 0
frg/work-js/mozilla-release/patches/1475228-1-63a1.patch

@@ -0,0 +1,853 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1531834222 -3600
+#      Tue Jul 17 14:30:22 2018 +0100
+# Node ID 4204e7eaa385eb5aa8ea0aa57cf20c588019882b
+# Parent  3b0b2bf06138d71d83f3d7f2fbb29739fe851be2
+Bug 1475228 - Make synchronous compile APIs take SourceBufferHolders exclusively r=jandem r=fitzgen
+
+diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp
+--- a/dom/base/nsJSUtils.cpp
++++ b/dom/base/nsJSUtils.cpp
+@@ -107,21 +107,22 @@ nsJSUtils::CompileFunction(AutoJSAPI& js
+ 
+   // Do the junk Gecko is supposed to do before calling into JSAPI.
+   for (size_t i = 0; i < aScopeChain.length(); ++i) {
+     JS::ExposeObjectToActiveJS(aScopeChain[i]);
+   }
+ 
+   // Compile.
+   JS::Rooted<JSFunction*> fun(cx);
++  JS::SourceBufferHolder source(PromiseFlatString(aBody).get(), aBody.Length(),
++                                JS::SourceBufferHolder::NoOwnership);
+   if (!JS::CompileFunction(cx, aScopeChain, aOptions,
+                            PromiseFlatCString(aName).get(),
+                            aArgCount, aArgArray,
+-                           PromiseFlatString(aBody).get(),
+-                           aBody.Length(), &fun))
++                           source, &fun))
+   {
+     return NS_ERROR_FAILURE;
+   }
+ 
+   *aFunctionObject = JS_GetFunctionObject(fun);
+   return NS_OK;
+ }
+ 
+diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
+--- a/dom/workers/WorkerPrivate.cpp
++++ b/dom/workers/WorkerPrivate.cpp
+@@ -6734,18 +6734,19 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
+       uint32_t lineNo = 0, dummyColumn = 0;
+       info->mHandler->GetLocation(&filename, &lineNo, &dummyColumn);
+ 
+       JS::CompileOptions options(aes.cx());
+       options.setFileAndLine(filename, lineNo).setNoScriptRval(true);
+ 
+       JS::Rooted<JS::Value> unused(aes.cx());
+ 
+-      if (!JS::Evaluate(aes.cx(), options, script.BeginReading(),
+-                        script.Length(), &unused) &&
++      JS::SourceBufferHolder srcBuf(script.BeginReading(), script.Length(),
++                                    JS::SourceBufferHolder::NoOwnership);
++      if (!JS::Evaluate(aes.cx(), options, srcBuf, &unused) &&
+           !JS_IsExceptionPending(aCx)) {
+         retval = false;
+         break;
+       }
+     } else {
+       ErrorResult rv;
+       JS::Rooted<JS::Value> ignoredVal(aCx);
+       callback->Call(GlobalScope(), info->mHandler->GetArgs(), &ignoredVal, rv,
+diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp
+--- a/ipc/testshell/XPCShellEnvironment.cpp
++++ b/ipc/testshell/XPCShellEnvironment.cpp
+@@ -481,18 +481,19 @@ XPCShellEnvironment::EvaluateString(cons
+ {
+   AutoEntryScript aes(GetGlobalObject(),
+                       "ipc XPCShellEnvironment::EvaluateString");
+   JSContext* cx = aes.cx();
+ 
+   JS::CompileOptions options(cx);
+   options.setFileAndLine("typein", 0);
+   JS::Rooted<JSScript*> script(cx);
+-  if (!JS_CompileUCScript(cx, aString.get(), aString.Length(), options,
+-                          &script))
++  JS::SourceBufferHolder srcBuf(aString.get(), aString.Length(),
++                                JS::SourceBufferHolder::NoOwnership);
++  if (!JS_CompileUCScript(cx, srcBuf, options, &script))
+   {
+      return false;
+   }
+ 
+   if (aResult) {
+       aResult->Truncate();
+   }
+ 
+diff --git a/js/rust/src/rust.rs b/js/rust/src/rust.rs
+--- a/js/rust/src/rust.rs
++++ b/js/rust/src/rust.rs
+@@ -229,17 +229,22 @@ impl Runtime {
+         } else {
+             (script_utf16.as_ptr(), script_utf16.len() as c_uint)
+         };
+         assert!(!ptr.is_null());
+         unsafe {
+             let _ac = AutoCompartment::with_obj(self.cx(), glob.get());
+             let options = CompileOptionsWrapper::new(self.cx(), filename_cstr.as_ptr(), line_num);
+ 
+-            if !JS::Evaluate2(self.cx(), options.ptr, ptr as *const u16, len as _, rval) {
++            let mut srcBuf = JS::SourceBufferHolder {
++                data_: ptr,
++                length_: len as _,
++                ownsChars_: false
++            };
++            if !JS::Evaluate(self.cx(), options.ptr, &mut srcBuf, rval) {
+                 debug!("...err!");
+                 panic::maybe_resume_unwind();
+                 Err(())
+             } else {
+                 // we could return the script result but then we'd have
+                 // to root it and so forth and, really, who cares?
+                 debug!("...ok!");
+                 Ok(())
+diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
+--- a/js/src/builtin/TestingFunctions.cpp
++++ b/js/src/builtin/TestingFunctions.cpp
+@@ -5086,17 +5086,18 @@ js::TestingFunctionArgumentToScript(JSCo
+         size_t len = GetLinearStringLength(linearStr);
+         AutoStableStringChars linearChars(cx);
+         if (!linearChars.initTwoByte(cx, linearStr))
+             return nullptr;
+         const char16_t* chars = linearChars.twoByteRange().begin().get();
+ 
+         RootedScript script(cx);
+         CompileOptions options(cx);
+-        if (!JS::Compile(cx, options, chars, len, &script))
++        SourceBufferHolder source(chars, len, SourceBufferHolder::NoOwnership);
++        if (!JS::Compile(cx, options, source, &script))
+             return nullptr;
+         return script;
+     }
+ 
+     RootedFunction fun(cx, JS_ValueToFunction(cx, v));
+     if (!fun)
+         return nullptr;
+ 
+diff --git a/js/src/jsapi-tests/testCompileNonSyntactic.cpp b/js/src/jsapi-tests/testCompileNonSyntactic.cpp
+--- a/js/src/jsapi-tests/testCompileNonSyntactic.cpp
++++ b/js/src/jsapi-tests/testCompileNonSyntactic.cpp
+@@ -79,28 +79,34 @@ testCompile(bool nonSyntactic)
+     // Check explicit non-syntactic compilation first to make sure it doesn't
+     // modify our options object.
+     CHECK(CompileForNonSyntacticScope(cx, options, buf, &script));
+     CHECK_EQUAL(script->hasNonSyntacticScope(), true);
+ 
+     CHECK(CompileForNonSyntacticScope(cx, options, src, length, &script));
+     CHECK_EQUAL(script->hasNonSyntacticScope(), true);
+ 
+-    CHECK(CompileForNonSyntacticScope(cx, options, src_16, length, &script));
+-    CHECK_EQUAL(script->hasNonSyntacticScope(), true);
++    {
++        JS::SourceBufferHolder srcBuf(src_16, length, JS::SourceBufferHolder::NoOwnership);
++        CHECK(CompileForNonSyntacticScope(cx, options, srcBuf, &script));
++        CHECK_EQUAL(script->hasNonSyntacticScope(), true);
++    }
+ 
+ 
+     CHECK(Compile(cx, options, buf, &script));
+     CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
+ 
+     CHECK(Compile(cx, options, src, length, &script));
+     CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
+ 
+-    CHECK(Compile(cx, options, src_16, length, &script));
+-    CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
++    {
++        JS::SourceBufferHolder srcBuf(src_16, length, JS::SourceBufferHolder::NoOwnership);
++        CHECK(Compile(cx, options, srcBuf, &script));
++        CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
++    }
+ 
+ 
+     options.forceAsync = true;
+     OffThreadTask task;
+     OffThreadToken* token;
+ 
+     CHECK(CompileOffThread(cx, options, src_16, length,
+                            task.OffThreadCallback, &task));
+diff --git a/js/src/jsapi-tests/testErrorLineOfContext.cpp b/js/src/jsapi-tests/testErrorLineOfContext.cpp
+--- a/js/src/jsapi-tests/testErrorLineOfContext.cpp
++++ b/js/src/jsapi-tests/testErrorLineOfContext.cpp
+@@ -35,17 +35,18 @@ eval(const char16_t* chars, size_t len, 
+ {
+     JS::RealmOptions globalOptions;
+     JS::RootedObject global(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
+ 						   JS::FireOnNewGlobalHook, globalOptions));
+     CHECK(global);
+ 
+     JSAutoRealm ar(cx, global);
+     JS::CompileOptions options(cx);
+-    return JS::Evaluate(cx, options, chars, len, rval);
++    JS::SourceBufferHolder srcBuf(chars, len, JS::SourceBufferHolder::NoOwnership);
++    return JS::Evaluate(cx, options, srcBuf, rval);
+ }
+ 
+ template<size_t N>
+ bool
+ testLineOfContextHasNoLineTerminator(const char16_t (&chars)[N], char16_t expectedLast)
+ {
+     JS::RootedValue rval(cx);
+     CHECK(!eval(chars, N - 1, &rval));
+diff --git a/js/src/jsapi-tests/testJSEvaluateScript.cpp b/js/src/jsapi-tests/testJSEvaluateScript.cpp
+--- a/js/src/jsapi-tests/testJSEvaluateScript.cpp
++++ b/js/src/jsapi-tests/testJSEvaluateScript.cpp
+@@ -12,18 +12,19 @@ BEGIN_TEST(testJSEvaluateScript)
+     CHECK(obj);
+ 
+     static const char16_t src[] = u"var x = 5;";
+ 
+     JS::RootedValue retval(cx);
+     JS::CompileOptions opts(cx);
+     JS::AutoObjectVector scopeChain(cx);
+     CHECK(scopeChain.append(obj));
++    JS::SourceBufferHolder srcBuf(src, ArrayLength(src) - 1, JS::SourceBufferHolder::NoOwnership);
+     CHECK(JS::Evaluate(cx, scopeChain, opts.setFileAndLine(__FILE__, __LINE__),
+-                       src, ArrayLength(src) - 1, &retval));
++                       srcBuf, &retval));
+ 
+     bool hasProp = true;
+     CHECK(JS_AlreadyHasOwnProperty(cx, obj, "x", &hasProp));
+     CHECK(hasProp);
+ 
+     hasProp = false;
+     CHECK(JS_HasProperty(cx, global, "x", &hasProp));
+     CHECK(!hasProp);
+diff --git a/js/src/jsapi-tests/testMutedErrors.cpp b/js/src/jsapi-tests/testMutedErrors.cpp
+--- a/js/src/jsapi-tests/testMutedErrors.cpp
++++ b/js/src/jsapi-tests/testMutedErrors.cpp
+@@ -49,17 +49,18 @@ eval(const char* asciiChars, bool mutedE
+     JSAutoRealm ar(cx, global);
+     CHECK(JS_InitStandardClasses(cx, global));
+ 
+ 
+     JS::CompileOptions options(cx);
+     options.setMutedErrors(mutedErrors)
+            .setFileAndLine("", 0);
+ 
+-    return JS::Evaluate(cx, options, chars.get(), len, rval);
++    JS::SourceBufferHolder srcBuf(chars.get(), len, JS::SourceBufferHolder::NoOwnership);
++    return JS::Evaluate(cx, options, srcBuf, rval);
+ }
+ 
+ bool
+ testOuter(const char* asciiChars)
+ {
+     CHECK(testInner(asciiChars, false));
+     CHECK(testInner(asciiChars, true));
+     return true;
+diff --git a/js/src/jsapi-tests/testScriptObject.cpp b/js/src/jsapi-tests/testScriptObject.cpp
+--- a/js/src/jsapi-tests/testScriptObject.cpp
++++ b/js/src/jsapi-tests/testScriptObject.cpp
+@@ -67,37 +67,40 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, 
+ }
+ END_FIXTURE_TEST(ScriptObjectFixture, bug438633_CompileScriptForPrincipals)
+ 
+ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript)
+ {
+     JS::CompileOptions options(cx);
+     options.setFileAndLine(__FILE__, __LINE__);
+     JS::RootedScript script(cx);
+-    CHECK(JS_CompileUCScript(cx, uc_code, code_size, options, &script));
++    JS::SourceBufferHolder srcBuf(uc_code, code_size, JS::SourceBufferHolder::NoOwnership);
++    CHECK(JS_CompileUCScript(cx, srcBuf, options, &script));
+     return tryScript(script);
+ }
+ END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript)
+ 
+ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript_empty)
+ {
+     JS::CompileOptions options(cx);
+     options.setFileAndLine(__FILE__, __LINE__);
+     JS::RootedScript script(cx);
+-    CHECK(JS_CompileUCScript(cx, uc_code, 0, options, &script));
++    JS::SourceBufferHolder srcBuf(uc_code, 0, JS::SourceBufferHolder::NoOwnership);
++    CHECK(JS_CompileUCScript(cx, srcBuf, options, &script));
+     return tryScript(script);
+ }
+ END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript_empty)
+ 
+ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScriptForPrincipals)
+ {
+     JS::CompileOptions options(cx);
+     options.setFileAndLine(__FILE__, __LINE__);
+     JS::RootedScript script(cx);
+-    CHECK(JS_CompileUCScript(cx, uc_code, code_size, options, &script));
++    JS::SourceBufferHolder srcBuf(uc_code, code_size, JS::SourceBufferHolder::NoOwnership);
++    CHECK(JS_CompileUCScript(cx, srcBuf, options, &script));
+     return tryScript(script);
+ }
+ END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScriptForPrincipals)
+ 
+ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile)
+ {
+     TempFile tempScript;
+     static const char script_filename[] = "temp-bug438633_JS_CompileFile";
+diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
+--- a/js/src/jsapi.cpp
++++ b/js/src/jsapi.cpp
+@@ -4050,35 +4050,28 @@ Compile(JSContext* cx, const ReadOnlyCom
+     CHECK_REQUEST(cx);
+ 
+     script.set(frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options, srcBuf));
+     return !!script;
+ }
+ 
+ static bool
+ Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+-        const char16_t* chars, size_t length, MutableHandleScript script)
+-{
+-    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+-    return ::Compile(cx, options, srcBuf, script);
+-}
+-
+-static bool
+-Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+         const char* bytes, size_t length, MutableHandleScript script)
+ {
+-    UniqueTwoByteChars chars;
++    char16_t* chars;
+     if (options.utf8)
+-        chars.reset(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get());
++        chars = UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get();
+     else
+-        chars.reset(InflateString(cx, bytes, length));
++        chars = InflateString(cx, bytes, length);
+     if (!chars)
+         return false;
+ 
+-    return ::Compile(cx, options, chars.get(), length, script);
++    SourceBufferHolder source(chars, length, SourceBufferHolder::GiveOwnership);
++    return ::Compile(cx, options, source, script);
+ }
+ 
+ static bool
+ Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+         FILE* fp, MutableHandleScript script)
+ {
+     FileContents buffer(cx);
+     if (!ReadCompleteFile(cx, fp, buffer))
+@@ -4110,23 +4103,16 @@ bool
+ JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+             const char* bytes, size_t length, JS::MutableHandleScript script)
+ {
+     return ::Compile(cx, options, bytes, length, script);
+ }
+ 
+ bool
+ JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+-            const char16_t* chars, size_t length, JS::MutableHandleScript script)
+-{
+-    return ::Compile(cx, options, chars, length, script);
+-}
+-
+-bool
+-JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+             FILE* file, JS::MutableHandleScript script)
+ {
+     return ::Compile(cx, options, file, script);
+ }
+ 
+ bool
+ JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+             const char* filename, JS::MutableHandleScript script)
+@@ -4149,26 +4135,16 @@ JS::CompileForNonSyntacticScope(JSContex
+ {
+     CompileOptions options(cx, optionsArg);
+     options.setNonSyntacticScope(true);
+     return ::Compile(cx, options, bytes, length, script);
+ }
+ 
+ bool
+ JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
+-                                const char16_t* chars, size_t length,
+-                                JS::MutableHandleScript script)
+-{
+-    CompileOptions options(cx, optionsArg);
+-    options.setNonSyntacticScope(true);
+-    return ::Compile(cx, options, chars, length, script);
+-}
+-
+-bool
+-JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
+                                 FILE* file, JS::MutableHandleScript script)
+ {
+     CompileOptions options(cx, optionsArg);
+     options.setNonSyntacticScope(true);
+     return ::Compile(cx, options, file, script);
+ }
+ 
+ bool
+@@ -4344,20 +4320,20 @@ JS::CancelMultiOffThreadScriptsDecoder(J
+ JS_PUBLIC_API(bool)
+ JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
+                  const JS::CompileOptions& options, MutableHandleScript script)
+ {
+     return ::Compile(cx, options, ascii, length, script);
+ }
+ 
+ JS_PUBLIC_API(bool)
+-JS_CompileUCScript(JSContext* cx, const char16_t* chars, size_t length,
++JS_CompileUCScript(JSContext* cx, JS::SourceBufferHolder& srcBuf,
+                    const JS::CompileOptions& options, MutableHandleScript script)
+ {
+-    return ::Compile(cx, options, chars, length, script);
++    return ::Compile(cx, options, srcBuf, script);
+ }
+ 
+ JS_PUBLIC_API(bool)
+ JS_BufferIsCompilableUnit(JSContext* cx, HandleObject obj, const char* utf8, size_t length)
+ {
+     AssertHeapIsIdle();
+     CHECK_REQUEST(cx);
+     assertSameCompartment(cx, obj);
+@@ -4568,39 +4544,29 @@ JS::CompileFunction(JSContext* cx, AutoO
+     return CompileFunction(cx, options, nameAtom, isInvalidName, newSrcBuf, parameterListEnd, env,
+                            scope, fun);
+ }
+ 
+ JS_PUBLIC_API(bool)
+ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain,
+                     const ReadOnlyCompileOptions& options,
+                     const char* name, unsigned nargs, const char* const* argnames,
+-                    const char16_t* chars, size_t length, MutableHandleFunction fun)
+-{
+-    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
++                    const char* bytes, size_t length, MutableHandleFunction fun)
++{
++    char16_t* chars;
++    if (options.utf8)
++        chars = UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get();
++    else
++        chars = InflateString(cx, bytes, length);
++    if (!chars)
++        return false;
++
++    SourceBufferHolder source(chars, length, SourceBufferHolder::GiveOwnership);
+     return CompileFunction(cx, envChain, options, name, nargs, argnames,
+-                           srcBuf, fun);
+-}
+-
+-JS_PUBLIC_API(bool)
+-JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain,
+-                    const ReadOnlyCompileOptions& options,
+-                    const char* name, unsigned nargs, const char* const* argnames,
+-                    const char* bytes, size_t length, MutableHandleFunction fun)
+-{
+-    UniqueTwoByteChars chars;
+-    if (options.utf8)
+-        chars.reset(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get());
+-    else
+-        chars.reset(InflateString(cx, bytes, length));
+-    if (!chars)
+-        return false;
+-
+-    return CompileFunction(cx, envChain, options, name, nargs, argnames,
+-                           chars.get(), length, fun);
++                           source, fun);
+ }
+ 
+ JS_PUBLIC_API(bool)
+ JS::InitScriptSourceElement(JSContext* cx, HandleScript script,
+                             HandleObject element, HandleString elementAttrName)
+ {
+     MOZ_ASSERT(cx);
+     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
+@@ -4769,25 +4735,16 @@ Evaluate(JSContext* cx, AutoObjectVector
+ {
+     RootedObject env(cx);
+     RootedScope scope(cx);
+     if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope))
+         return false;
+     return ::Evaluate(cx, scope->kind(), env, optionsArg, srcBuf, rval);
+ }
+ 
+-static bool
+-Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
+-         const char16_t* chars, size_t length, MutableHandleValue rval)
+-{
+-  SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+-  RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
+-  return ::Evaluate(cx, ScopeKind::Global, globalLexical, optionsArg, srcBuf, rval);
+-}
+-
+ extern JS_PUBLIC_API(bool)
+ JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
+              const char* bytes, size_t length, MutableHandleValue rval)
+ {
+     char16_t* chars;
+     if (options.utf8)
+         chars = UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(bytes, length), &length).get();
+     else
+@@ -4829,31 +4786,16 @@ JS_PUBLIC_API(bool)
+ JS::Evaluate(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& optionsArg,
+              SourceBufferHolder& srcBuf, MutableHandleValue rval)
+ {
+     return ::Evaluate(cx, envChain, optionsArg, srcBuf, rval);
+ }
+ 
+ JS_PUBLIC_API(bool)
+ JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
+-             const char16_t* chars, size_t length, MutableHandleValue rval)
+-{
+-    return ::Evaluate(cx, optionsArg, chars, length, rval);
+-}
+-
+-JS_PUBLIC_API(bool)
+-JS::Evaluate(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& optionsArg,
+-             const char16_t* chars, size_t length, MutableHandleValue rval)
+-{
+-    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+-    return ::Evaluate(cx, envChain, optionsArg, srcBuf, rval);
+-}
+-
+-JS_PUBLIC_API(bool)
+-JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
+              const char* filename, MutableHandleValue rval)
+ {
+     return ::Evaluate(cx, optionsArg, filename, rval);
+ }
+ 
+ JS_PUBLIC_API(JS::ModuleResolveHook)
+ JS::GetModuleResolveHook(JSRuntime* rt)
+ {
+diff --git a/js/src/jsapi.h b/js/src/jsapi.h
+--- a/js/src/jsapi.h
++++ b/js/src/jsapi.h
+@@ -3184,17 +3184,17 @@ extern JS_PUBLIC_API(bool)
+ JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
+                  const JS::CompileOptions& options,
+                  JS::MutableHandleScript script);
+ 
+ /**
+  * |script| will always be set. On failure, it will be set to nullptr.
+  */
+ extern JS_PUBLIC_API(bool)
+-JS_CompileUCScript(JSContext* cx, const char16_t* chars, size_t length,
++JS_CompileUCScript(JSContext* cx, JS::SourceBufferHolder& srcBuf,
+                    const JS::CompileOptions& options,
+                    JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(JSObject*)
+ JS_GetGlobalFromScript(JSScript* script);
+ 
+ extern JS_PUBLIC_API(const char*)
+ JS_GetScriptFilename(JSScript* script);
+@@ -3595,40 +3595,32 @@ Compile(JSContext* cx, const ReadOnlyCom
+         SourceBufferHolder& srcBuf, JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(bool)
+ Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+         const char* bytes, size_t length, JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(bool)
+ Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+-        const char16_t* chars, size_t length, JS::MutableHandleScript script);
+-
+-extern JS_PUBLIC_API(bool)
+-Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+         FILE* file, JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(bool)
+ Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+         const char* filename, JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(bool)
+ CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
+                             SourceBufferHolder& srcBuf, JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(bool)
+ CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
+                             const char* bytes, size_t length, JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(bool)
+ CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                            const char16_t* chars, size_t length, JS::MutableHandleScript script);
+-
+-extern JS_PUBLIC_API(bool)
+-CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
+                             FILE* file, JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(bool)
+ CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
+                             const char* filename, JS::MutableHandleScript script);
+ 
+ extern JS_PUBLIC_API(bool)
+ CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
+@@ -3709,25 +3701,16 @@ CancelMultiOffThreadScriptsDecoder(JSCon
+  * scope chain used for the function will consist of With wrappers for those
+  * objects, followed by the current global of the compartment cx is in.  This
+  * global must not be explicitly included in the scope chain.
+  */
+ extern JS_PUBLIC_API(bool)
+ CompileFunction(JSContext* cx, AutoObjectVector& envChain,
+                 const ReadOnlyCompileOptions& options,
+                 const char* name, unsigned nargs, const char* const* argnames,
+-                const char16_t* chars, size_t length, JS::MutableHandleFunction fun);
+-
+-/**
+- * Same as above, but taking a SourceBufferHolder for the function body.
+- */
+-extern JS_PUBLIC_API(bool)
+-CompileFunction(JSContext* cx, AutoObjectVector& envChain,
+-                const ReadOnlyCompileOptions& options,
+-                const char* name, unsigned nargs, const char* const* argnames,
+                 SourceBufferHolder& srcBuf, JS::MutableHandleFunction fun);
+ 
+ /**
+  * Same as above, but taking a const char * for the function body.
+  */
+ extern JS_PUBLIC_API(bool)
+ CompileFunction(JSContext* cx, AutoObjectVector& envChain,
+                 const ReadOnlyCompileOptions& options,
+@@ -3832,32 +3815,16 @@ Evaluate(JSContext* cx, const ReadOnlyCo
+  * the global object on it; that's implicit.  It needs to contain the other
+  * objects that should end up on the script's scope chain.
+  */
+ extern JS_PUBLIC_API(bool)
+ Evaluate(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& options,
+          SourceBufferHolder& srcBuf, JS::MutableHandleValue rval);
+ 
+ /**
+- * Evaluate the given character buffer in the scope of the current global of cx.
+- */
+-extern JS_PUBLIC_API(bool)
+-Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
+-         const char16_t* chars, size_t length, JS::MutableHandleValue rval);
+-
+-/**
+- * As above, but providing an explicit scope chain.  envChain must not include
+- * the global object on it; that's implicit.  It needs to contain the other
+- * objects that should end up on the script's scope chain.
+- */
+-extern JS_PUBLIC_API(bool)
+-Evaluate(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& options,
+-         const char16_t* chars, size_t length, JS::MutableHandleValue rval);
+-
+-/**
+  * Evaluate the given byte buffer in the scope of the current global of cx.
+  */
+ extern JS_PUBLIC_API(bool)
+ Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
+          const char* bytes, size_t length, JS::MutableHandleValue rval);
+ 
+ /**
+  * Evaluate the given file in the scope of the current global of cx.
+diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
+--- a/js/src/shell/js.cpp
++++ b/js/src/shell/js.cpp
+@@ -1939,20 +1939,22 @@ Evaluate(JSContext* cx, unsigned argc, V
+             }
+ 
+             if (loadBytecode) {
+                 JS::TranscodeResult rv = JS::DecodeScript(cx, loadBuffer, &script);
+                 if (!ConvertTranscodeResultToJSException(cx, rv))
+                     return false;
+             } else {
+                 mozilla::Range<const char16_t> chars = codeChars.twoByteRange();
++                JS::SourceBufferHolder srcBuf(chars.begin().get(), chars.length(),
++                                              JS::SourceBufferHolder::NoOwnership);
+                 if (envChain.length() == 0) {
+-                    (void) JS::Compile(cx, options, chars.begin().get(), chars.length(), &script);
++                    (void) JS::Compile(cx, options, srcBuf, &script);
+                 } else {
+-                    (void) JS::CompileForNonSyntacticScope(cx, options, chars.begin().get(), chars.length(), &script);
++                    (void) JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script);
+                 }
+             }
+ 
+             if (!script)
+                 return false;
+         }
+ 
+         if (displayURL && !script->scriptSource()->hasDisplayURL()) {
+@@ -2141,33 +2143,33 @@ Run(JSContext* cx, unsigned argc, Value*
+     str = FileAsString(cx, str);
+     if (!str)
+         return false;
+ 
+     AutoStableStringChars chars(cx);
+     if (!chars.initTwoByte(cx, str))
+         return false;
+ 
+-    const char16_t* ucbuf = chars.twoByteRange().begin().get();
+-    size_t buflen = str->length();
++    JS::SourceBufferHolder srcBuf(chars.twoByteRange().begin().get(), str->length(),
++                                  JS::SourceBufferHolder::NoOwnership);
+ 
+     RootedScript script(cx);
+     int64_t startClock = PRMJ_Now();
+     {
+         /* FIXME: This should use UTF-8 (bug 987069). */
+         JSAutoByteString filename(cx, str);
+         if (!filename)
+             return false;
+ 
+         JS::CompileOptions options(cx);
+         options.setIntroductionType("js shell run")
+                .setFileAndLine(filename.ptr(), 1)
+                .setIsRunOnce(true)
+                .setNoScriptRval(true);
+-        if (!JS_CompileUCScript(cx, ucbuf, buflen, options, &script))
++        if (!JS_CompileUCScript(cx, srcBuf, options, &script))
+             return false;
+     }
+ 
+     if (!JS_ExecuteScript(cx, script))
+         return false;
+ 
+     int64_t endClock = PRMJ_Now();
+ 
+@@ -3484,17 +3486,18 @@ EvalInContext(JSContext* cx, unsigned ar
+         sobj = ToWindowIfWindowProxy(sobj);
+ 
+         if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
+             JS_ReportErrorASCII(cx, "Invalid scope argument to evalcx");
+             return false;
+         }
+         JS::CompileOptions opts(cx);
+         opts.setFileAndLine(filename.get(), lineno);
+-        if (!JS::Evaluate(cx, opts, src, srclen, args.rval())) {
++        JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
++        if (!JS::Evaluate(cx, opts, srcBuf, args.rval())) {
+             return false;
+         }
+     }
+ 
+     if (!cx->compartment()->wrap(cx, args.rval()))
+         return false;
+ 
+     return true;
+@@ -3587,17 +3590,19 @@ WorkerMain(WorkerInput* input)
+         JSAutoRealm ar(cx, global);
+ 
+         JS::CompileOptions options(cx);
+         options.setFileAndLine("<string>", 1)
+                .setIsRunOnce(true);
+ 
+         AutoReportException are(cx);
+         RootedScript script(cx);
+-        if (!JS::Compile(cx, options, input->chars.get(), input->length, &script))
++        JS::SourceBufferHolder srcBuf(input->chars.get(), input->length,
++                                      JS::SourceBufferHolder::NoOwnership);
++        if (!JS::Compile(cx, options, srcBuf, &script))
+             break;
+         RootedValue result(cx);
+         JS_ExecuteScript(cx, script, &result);
+     } while (0);
+ 
+     KillWatchdog(cx);
+     JS_SetGrayGCRootsTracer(cx, nullptr, nullptr);
+ }
+@@ -4173,18 +4178,20 @@ Compile(JSContext* cx, unsigned argc, Va
+         return false;
+ 
+     JS::CompileOptions options(cx);
+     options.setIntroductionType("js shell compile")
+            .setFileAndLine("<string>", 1)
+            .setIsRunOnce(true)
+            .setNoScriptRval(true);
+     RootedScript script(cx);
+-    const char16_t* chars = stableChars.twoByteRange().begin().get();
+-    bool ok = JS_CompileUCScript(cx, chars, scriptContents->length(), options, &script);
++    JS::SourceBufferHolder srcBuf(stableChars.twoByteRange().begin().get(),
++                                  scriptContents->length(),
++                                  JS::SourceBufferHolder::NoOwnership);
++    bool ok = JS_CompileUCScript(cx, srcBuf, options, &script);
+     args.rval().setUndefined();
+     return ok;
+ }
+ 
+ static bool
+ ParseModule(JSContext* cx, unsigned argc, Value* vp)
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+@@ -6473,25 +6480,26 @@ EntryPoints(JSContext* cx, unsigned argc
+         if (!code.isUndefined()) {
+             RootedString codeString(cx, ToString(cx, code));
+             if (!codeString || !codeString->ensureFlat(cx))
+                 return false;
+ 
+             AutoStableStringChars stableChars(cx);
+             if (!stableChars.initTwoByte(cx, codeString))
+                 return false;
+-            const char16_t* chars = stableChars.twoByteRange().begin().get();
+-            size_t length = codeString->length();
++            JS::SourceBufferHolder srcBuf(stableChars.twoByteRange().begin().get(),
++                                          codeString->length(),
++                                          JS::SourceBufferHolder::NoOwnership);
+ 
+             CompileOptions options(cx);
+             options.setIntroductionType("entryPoint eval")
+                    .setFileAndLine("entryPoint eval", 1);
+ 
+             js::shell::ShellAutoEntryMonitor sarep(cx);
+-            if (!JS::Evaluate(cx, options, chars, length, &dummy))
++            if (!JS::Evaluate(cx, options, srcBuf, &dummy))
+                 return false;
+             return sarep.buildResult(cx, args.rval());
+         }
+     }
+ 
+     JS_ReportErrorASCII(cx, "bad 'params' object");
+     return false;
+ }
+diff --git a/js/xpconnect/loader/ChromeScriptLoader.cpp b/js/xpconnect/loader/ChromeScriptLoader.cpp
+--- a/js/xpconnect/loader/ChromeScriptLoader.cpp
++++ b/js/xpconnect/loader/ChromeScriptLoader.cpp
+@@ -136,17 +136,19 @@ AsyncScriptCompiler::StartCompile(JSCont
+             return false;
+         }
+ 
+         NS_ADDREF(this);
+         return true;
+     }
+ 
+     Rooted<JSScript*> script(aCx);
+-    if (!JS::Compile(aCx, mOptions, mScriptText.get(), mScriptLength, &script)) {
++    JS::SourceBufferHolder srcBuf(mScriptText.get(), mScriptLength,
++                                  JS::SourceBufferHolder::NoOwnership);
++    if (!JS::Compile(aCx, mOptions, srcBuf, &script)) {
+         return false;
+     }
+ 
+     Finish(aCx, script);
+     return true;
+ }
+ 
+ NS_IMETHODIMP
+diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp
+--- a/js/xpconnect/src/Sandbox.cpp
++++ b/js/xpconnect/src/Sandbox.cpp
+@@ -1902,18 +1902,19 @@ xpc::EvalInSandbox(JSContext* cx, Handle
+         // This is clearly Gecko-specific and not in any spec.
+         mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation");
+         JSContext* sandcx = aes.cx();
+         JSAutoRealm ar(sandcx, sandbox);
+ 
+         JS::CompileOptions options(sandcx);
+         options.setFileAndLine(filenameBuf.get(), lineNo);
+         MOZ_ASSERT(JS_IsGlobalObject(sandbox));
+-        ok = JS::Evaluate(sandcx, options,
+-                          PromiseFlatString(source).get(), source.Length(), &v);
++        JS::SourceBufferHolder buffer(PromiseFlatString(source).get(), source.Length(),
++                                      JS::SourceBufferHolder::NoOwnership);
++        ok = JS::Evaluate(sandcx, options, buffer, &v);
+ 
+         // If the sandbox threw an exception, grab it off the context.
+         if (aes.HasException()) {
+             if (!aes.StealException(&exn)) {
+                 return NS_ERROR_OUT_OF_MEMORY;
+             }
+         }
+     }

+ 504 - 0
frg/work-js/mozilla-release/patches/1475228-2-63a1.patch

@@ -0,0 +1,504 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1531834223 -3600
+#      Tue Jul 17 14:30:23 2018 +0100
+# Node ID b280769277af509270952ba4690e7bbc8ae3c161
+# Parent  29471c082697ba6591819648188cfe1274e63e16
+Bug 1475228 - Make asynchronous compile APIs take SourceBufferHolders r=jandem
+
+diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
+--- a/dom/script/ScriptLoader.cpp
++++ b/dom/script/ScriptLoader.cpp
+@@ -1754,36 +1754,40 @@ ScriptLoader::AttemptAsyncScriptCompile(
+     }
+   }
+ 
+   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
+     new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
+ 
+   if (aRequest->IsModuleRequest()) {
+     MOZ_ASSERT(aRequest->IsTextSource());
++    JS::SourceBufferHolder srcBuf(aRequest->ScriptText().begin(),
++                                  aRequest->ScriptText().length(),
++                                  JS::SourceBufferHolder::NoOwnership);
+     if (!JS::CompileOffThreadModule(cx, options,
+-                                    aRequest->ScriptText().begin(),
+-                                    aRequest->ScriptText().length(),
++                                    srcBuf,
+                                     OffThreadScriptLoaderCallback,
+                                     static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   } else if (aRequest->IsBytecode()) {
+     if (!JS::DecodeOffThreadScript(cx, options,
+                                    aRequest->mScriptBytecode,
+                                    aRequest->mBytecodeOffset,
+                                    OffThreadScriptLoaderCallback,
+                                    static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   } else {
+     MOZ_ASSERT(aRequest->IsTextSource());
++    JS::SourceBufferHolder srcBuf(aRequest->ScriptText().begin(),
++                                  aRequest->ScriptText().length(),
++                                  JS::SourceBufferHolder::NoOwnership);
+     if (!JS::CompileOffThread(cx, options,
+-                              aRequest->ScriptText().begin(),
+-                              aRequest->ScriptText().length(),
++                              srcBuf,
+                               OffThreadScriptLoaderCallback,
+                               static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   }
+ 
+   mDocument->BlockOnload();
+ 
+diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp
+--- a/dom/xul/XULDocument.cpp
++++ b/dom/xul/XULDocument.cpp
+@@ -3355,25 +3355,20 @@ XULDocument::OnStreamComplete(nsIStreamL
+                                           mOffThreadCompileStringLength,
+                                           JS::SourceBufferHolder::GiveOwnership);
+             mOffThreadCompileStringBuf = nullptr;
+             mOffThreadCompileStringLength = 0;
+ 
+             rv = mCurrentScriptProto->Compile(srcBuf, uri, 1, this, this);
+             if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
+                 // We will be notified via OnOffThreadCompileComplete when the
+-                // compile finishes. Keep the contents of the compiled script
+-                // alive until the compilation finishes.
++                // compile finishes. The JS engine has taken ownership of the
++                // source buffer.
++                MOZ_RELEASE_ASSERT(!srcBuf.ownsChars());
+                 mOffThreadCompiling = true;
+-                // If the JS engine did not take the source buffer, then take
+-                // it back here to ensure it remains alive.
+-                mOffThreadCompileStringBuf = srcBuf.take();
+-                if (mOffThreadCompileStringBuf) {
+-                  mOffThreadCompileStringLength = srcBuf.length();
+-                }
+                 BlockOnload();
+                 return NS_OK;
+             }
+         }
+     }
+ 
+     return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
+ }
+diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp
+--- a/dom/xul/nsXULElement.cpp
++++ b/dom/xul/nsXULElement.cpp
+@@ -2737,17 +2737,17 @@ nsXULPrototypeScript::Compile(JS::Source
+     options.setSourceIsLazy(mOutOfLine);
+     JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
+     if (scope) {
+       JS::ExposeObjectToActiveJS(scope);
+     }
+ 
+     if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) {
+         if (!JS::CompileOffThread(cx, options,
+-                                  aSrcBuf.get(), aSrcBuf.length(),
++                                  aSrcBuf,
+                                   OffThreadScriptReceiverCallback,
+                                   static_cast<void*>(aOffThreadReceiver))) {
+             return NS_ERROR_OUT_OF_MEMORY;
+         }
+         NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver);
+     } else {
+         JS::Rooted<JSScript*> script(cx);
+         if (!JS::Compile(cx, options, aSrcBuf, &script))
+diff --git a/js/src/NamespaceImports.h b/js/src/NamespaceImports.h
+--- a/js/src/NamespaceImports.h
++++ b/js/src/NamespaceImports.h
+@@ -33,17 +33,17 @@ class UTF8CharsZ;
+ using AutoValueVector = AutoVector<Value>;
+ using AutoIdVector = AutoVector<jsid>;
+ using AutoObjectVector = AutoVector<JSObject*>;
+ 
+ using ValueVector = JS::GCVector<JS::Value>;
+ using IdVector = JS::GCVector<jsid>;
+ using ScriptVector = JS::GCVector<JSScript*>;
+ 
+-class MOZ_STACK_CLASS SourceBufferHolder;
++class SourceBufferHolder;
+ 
+ class HandleValueArray;
+ 
+ class ObjectOpResult;
+ class PropertyResult;
+ 
+ enum class SymbolCode: uint32_t;
+ 
+diff --git a/js/src/jsapi-tests/testCompileNonSyntactic.cpp b/js/src/jsapi-tests/testCompileNonSyntactic.cpp
+--- a/js/src/jsapi-tests/testCompileNonSyntactic.cpp
++++ b/js/src/jsapi-tests/testCompileNonSyntactic.cpp
+@@ -103,17 +103,17 @@ testCompile(bool nonSyntactic)
+         CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
+     }
+ 
+ 
+     options.forceAsync = true;
+     OffThreadTask task;
+     OffThreadToken* token;
+ 
+-    CHECK(CompileOffThread(cx, options, src_16, length,
+-                           task.OffThreadCallback, &task));
++    SourceBufferHolder srcBuf(src_16, length, SourceBufferHolder::NoOwnership);
++    CHECK(CompileOffThread(cx, options, srcBuf, task.OffThreadCallback, &task));
+     CHECK(token = task.waitUntilDone(cx));
+     CHECK(script = FinishOffThreadScript(cx, token));
+     CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
+ 
+     return true;
+ }
+ END_TEST(testCompileScript);
+diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
+--- a/js/src/jsapi.cpp
++++ b/js/src/jsapi.cpp
+@@ -4198,21 +4198,21 @@ JS::CanCompileOffThread(JSContext* cx, c
+ JS_PUBLIC_API(bool)
+ JS::CanDecodeOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length)
+ {
+     return CanDoOffThread(cx, options, length, OffThread::Decode);
+ }
+ 
+ JS_PUBLIC_API(bool)
+ JS::CompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                     const char16_t* chars, size_t length,
++                     JS::SourceBufferHolder& srcBuf,
+                      OffThreadCompileCallback callback, void* callbackData)
+ {
+-    MOZ_ASSERT(CanCompileOffThread(cx, options, length));
+-    return StartOffThreadParseScript(cx, options, chars, length, callback, callbackData);
++    MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
++    return StartOffThreadParseScript(cx, options, srcBuf, callback, callbackData);
+ }
+ 
+ JS_PUBLIC_API(JSScript*)
+ JS::FinishOffThreadScript(JSContext* cx, JS::OffThreadToken* token)
+ {
+     MOZ_ASSERT(cx);
+     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
+     return HelperThreadState().finishScriptParseTask(cx, token);
+@@ -4223,21 +4223,21 @@ JS::CancelOffThreadScript(JSContext* cx,
+ {
+     MOZ_ASSERT(cx);
+     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
+     HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::Script, token);
+ }
+ 
+ JS_PUBLIC_API(bool)
+ JS::CompileOffThreadModule(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                           const char16_t* chars, size_t length,
++                           JS::SourceBufferHolder& srcBuf,
+                            OffThreadCompileCallback callback, void* callbackData)
+ {
+-    MOZ_ASSERT(CanCompileOffThread(cx, options, length));
+-    return StartOffThreadParseModule(cx, options, chars, length, callback, callbackData);
++    MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
++    return StartOffThreadParseModule(cx, options, srcBuf, callback, callbackData);
+ }
+ 
+ JS_PUBLIC_API(JSObject*)
+ JS::FinishOffThreadModule(JSContext* cx, JS::OffThreadToken* token)
+ {
+     MOZ_ASSERT(cx);
+     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
+     return HelperThreadState().finishModuleParseTask(cx, token);
+diff --git a/js/src/jsapi.h b/js/src/jsapi.h
+--- a/js/src/jsapi.h
++++ b/js/src/jsapi.h
+@@ -365,17 +365,17 @@ namespace JS {
+  *
+  * Example use:
+  *
+  *    size_t length = 512;
+  *    char16_t* chars = js_pod_malloc<char16_t>(length);
+  *    JS::SourceBufferHolder srcBuf(chars, length, JS::SourceBufferHolder::GiveOwnership);
+  *    JS::Compile(cx, options, srcBuf);
+  */
+-class MOZ_STACK_CLASS SourceBufferHolder final
++class SourceBufferHolder final
+ {
+   public:
+     enum Ownership {
+       NoOwnership,
+       GiveOwnership
+     };
+ 
+     SourceBufferHolder(const char16_t* data, size_t dataLength, Ownership ownership)
+@@ -3642,28 +3642,28 @@ CanDecodeOffThread(JSContext* cx, const 
+  *
+  * The characters passed in to CompileOffThread must remain live until the
+  * callback is invoked, and the resulting script will be rooted until the call
+  * to FinishOffThreadScript.
+  */
+ 
+ extern JS_PUBLIC_API(bool)
+ CompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                 const char16_t* chars, size_t length,
++                 JS::SourceBufferHolder& srcBuf,
+                  OffThreadCompileCallback callback, void* callbackData);
+ 
+ extern JS_PUBLIC_API(JSScript*)
+ FinishOffThreadScript(JSContext* cx, OffThreadToken* token);
+ 
+ extern JS_PUBLIC_API(void)
+ CancelOffThreadScript(JSContext* cx, OffThreadToken* token);
+ 
+ extern JS_PUBLIC_API(bool)
+ CompileOffThreadModule(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                       const char16_t* chars, size_t length,
++                       JS::SourceBufferHolder& srcBuf,
+                        OffThreadCompileCallback callback, void* callbackData);
+ 
+ extern JS_PUBLIC_API(JSObject*)
+ FinishOffThreadModule(JSContext* cx, OffThreadToken* token);
+ 
+ extern JS_PUBLIC_API(void)
+ CancelOffThreadModule(JSContext* cx, OffThreadToken* token);
+ 
+diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
+--- a/js/src/shell/js.cpp
++++ b/js/src/shell/js.cpp
+@@ -4533,17 +4533,19 @@ OffThreadCompileScript(JSContext* cx, un
+         return false;
+     }
+ 
+     OffThreadJob* job = NewOffThreadJob(cx, ScriptKind::Script,
+                                         OffThreadJob::Source(std::move(ownedChars)));
+     if (!job)
+         return false;
+ 
+-    if (!JS::CompileOffThread(cx, options, job->sourceChars(), length,
++    JS::SourceBufferHolder srcBuf(job->sourceChars(), length,
++                                  JS::SourceBufferHolder::NoOwnership);
++    if (!JS::CompileOffThread(cx, options, srcBuf,
+                               OffThreadCompileScriptCallback, job))
+     {
+         job->cancel();
+         DeleteOffThreadJob(cx, job);
+         return false;
+     }
+ 
+     args.rval().setInt32(job->id);
+@@ -4619,17 +4621,19 @@ OffThreadCompileModule(JSContext* cx, un
+         return false;
+     }
+ 
+     OffThreadJob* job = NewOffThreadJob(cx, ScriptKind::Module,
+                                         OffThreadJob::Source(std::move(ownedChars)));
+     if (!job)
+         return false;
+ 
+-    if (!JS::CompileOffThreadModule(cx, options, job->sourceChars(), length,
++    JS::SourceBufferHolder srcBuf(job->sourceChars(), length,
++                                  JS::SourceBufferHolder::NoOwnership);
++    if (!JS::CompileOffThreadModule(cx, options, srcBuf,
+                                     OffThreadCompileScriptCallback, job))
+     {
+         job->cancel();
+         DeleteOffThreadJob(cx, job);
+         return false;
+     }
+ 
+     args.rval().setInt32(job->id);
+diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp
+--- a/js/src/vm/HelperThreads.cpp
++++ b/js/src/vm/HelperThreads.cpp
+@@ -460,52 +460,50 @@ ParseTask::trace(JSTracer* trc)
+ size_t
+ ParseTask::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+ {
+     return options.sizeOfExcludingThis(mallocSizeOf) +
+            alloc.sizeOfExcludingThis(mallocSizeOf) +
+            errors.sizeOfExcludingThis(mallocSizeOf);
+ }
+ 
+-ScriptParseTask::ScriptParseTask(JSContext* cx, const char16_t* chars, size_t length,
++ScriptParseTask::ScriptParseTask(JSContext* cx, JS::SourceBufferHolder& srcBuf,
+                                  JS::OffThreadCompileCallback callback, void* callbackData)
+   : ParseTask(ParseTaskKind::Script, cx, callback, callbackData),
+-    data(TwoByteChars(chars, length))
++    data(std::move(srcBuf))
+ {}
+ 
+ void
+ ScriptParseTask::parse(JSContext* cx)
+ {
+-    SourceBufferHolder srcBuf(data.begin().get(), data.length(), SourceBufferHolder::NoOwnership);
+     Rooted<ScriptSourceObject*> sourceObject(cx);
+ 
+     ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
+ 
+     JSScript* script = frontend::CompileGlobalScript(cx, alloc, scopeKind,
+-                                                     options, srcBuf,
++                                                     options, data,
+                                                      /* sourceObjectOut = */ &sourceObject.get());
+     if (script)
+         scripts.infallibleAppend(script);
+     if (sourceObject)
+         sourceObjects.infallibleAppend(sourceObject);
+ }
+ 
+-ModuleParseTask::ModuleParseTask(JSContext* cx, const char16_t* chars, size_t length,
++ModuleParseTask::ModuleParseTask(JSContext* cx, JS::SourceBufferHolder& srcBuf,
+                                  JS::OffThreadCompileCallback callback, void* callbackData)
+   : ParseTask(ParseTaskKind::Module, cx, callback, callbackData),
+-    data(TwoByteChars(chars, length))
++    data(std::move(srcBuf))
+ {}
+ 
+ void
+ ModuleParseTask::parse(JSContext* cx)
+ {
+-    SourceBufferHolder srcBuf(data.begin().get(), data.length(), SourceBufferHolder::NoOwnership);
+     Rooted<ScriptSourceObject*> sourceObject(cx);
+ 
+-    ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject.get());
++    ModuleObject* module = frontend::CompileModule(cx, options, data, alloc, &sourceObject.get());
+     if (module) {
+         scripts.infallibleAppend(module->script());
+         if (sourceObject)
+             sourceObjects.infallibleAppend(sourceObject);
+     }
+ }
+ 
+ ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, const JS::TranscodeRange& range,
+@@ -778,33 +776,33 @@ StartOffThreadParseTask(JSContext* cx, P
+         return false;
+ 
+     createdForHelper.forget();
+     return true;
+ }
+ 
+ bool
+ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                              const char16_t* chars, size_t length,
++                              JS::SourceBufferHolder& srcBuf,
+                               JS::OffThreadCompileCallback callback, void* callbackData)
+ {
+-    auto task = cx->make_unique<ScriptParseTask>(cx, chars, length, callback, callbackData);
++    auto task = cx->make_unique<ScriptParseTask>(cx, srcBuf, callback, callbackData);
+     if (!task || !StartOffThreadParseTask(cx, task.get(), options))
+         return false;
+ 
+     Unused << task.release();
+     return true;
+ }
+ 
+ bool
+ js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                              const char16_t* chars, size_t length,
++                              JS::SourceBufferHolder& srcBuf,
+                               JS::OffThreadCompileCallback callback, void* callbackData)
+ {
+-    auto task = cx->make_unique<ModuleParseTask>(cx, chars, length, callback, callbackData);
++    auto task = cx->make_unique<ModuleParseTask>(cx, srcBuf, callback, callbackData);
+     if (!task || !StartOffThreadParseTask(cx, task.get(), options))
+         return false;
+ 
+     Unused << task.release();
+     return true;
+ }
+ 
+ bool
+diff --git a/js/src/vm/HelperThreads.h b/js/src/vm/HelperThreads.h
+--- a/js/src/vm/HelperThreads.h
++++ b/js/src/vm/HelperThreads.h
+@@ -584,22 +584,22 @@ void
+ CancelOffThreadParses(JSRuntime* runtime);
+ 
+ /*
+  * Start a parse/emit cycle for a stream of source. The characters must stay
+  * alive until the compilation finishes.
+  */
+ bool
+ StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                          const char16_t* chars, size_t length,
++                          JS::SourceBufferHolder& srcBuf,
+                           JS::OffThreadCompileCallback callback, void* callbackData);
+ 
+ bool
+ StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
+-                          const char16_t* chars, size_t length,
++                          JS::SourceBufferHolder& srcBuf,
+                           JS::OffThreadCompileCallback callback, void* callbackData);
+ 
+ bool
+ StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+                            const JS::TranscodeRange& range,
+                            JS::OffThreadCompileCallback callback, void* callbackData);
+ 
+ bool
+@@ -706,28 +706,28 @@ struct ParseTask : public mozilla::Linke
+     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+     }
+ };
+ 
+ struct ScriptParseTask : public ParseTask
+ {
+-    JS::TwoByteChars data;
++    JS::SourceBufferHolder data;
+ 
+-    ScriptParseTask(JSContext* cx, const char16_t* chars, size_t length,
++    ScriptParseTask(JSContext* cx, JS::SourceBufferHolder& srcBuf,
+                     JS::OffThreadCompileCallback callback, void* callbackData);
+     void parse(JSContext* cx) override;
+ };
+ 
+ struct ModuleParseTask : public ParseTask
+ {
+-    JS::TwoByteChars data;
++    JS::SourceBufferHolder data;
+ 
+-    ModuleParseTask(JSContext* cx, const char16_t* chars, size_t length,
++    ModuleParseTask(JSContext* cx, JS::SourceBufferHolder& srcBuf,
+                     JS::OffThreadCompileCallback callback, void* callbackData);
+     void parse(JSContext* cx) override;
+ };
+ 
+ struct ScriptDecodeTask : public ParseTask
+ {
+     const JS::TranscodeRange range;
+ 
+diff --git a/js/xpconnect/loader/ChromeScriptLoader.cpp b/js/xpconnect/loader/ChromeScriptLoader.cpp
+--- a/js/xpconnect/loader/ChromeScriptLoader.cpp
++++ b/js/xpconnect/loader/ChromeScriptLoader.cpp
+@@ -124,30 +124,30 @@ OffThreadScriptLoaderCallback(JS::OffThr
+     SystemGroup::Dispatch(TaskCategory::Other, scriptCompiler.forget());
+ }
+ 
+ bool
+ AsyncScriptCompiler::StartCompile(JSContext* aCx)
+ {
+     Rooted<JSObject*> global(aCx, mGlobalObject->GetGlobalJSObject());
+ 
++    JS::SourceBufferHolder srcBuf(mScriptText.get(), mScriptLength,
++                                  JS::SourceBufferHolder::NoOwnership);
+     if (JS::CanCompileOffThread(aCx, mOptions, mScriptLength)) {
+-        if (!JS::CompileOffThread(aCx, mOptions, mScriptText.get(), mScriptLength,
++        if (!JS::CompileOffThread(aCx, mOptions, srcBuf,
+                                   OffThreadScriptLoaderCallback,
+                                   static_cast<void*>(this))) {
+             return false;
+         }
+ 
+         NS_ADDREF(this);
+         return true;
+     }
+ 
+     Rooted<JSScript*> script(aCx);
+-    JS::SourceBufferHolder srcBuf(mScriptText.get(), mScriptLength,
+-                                  JS::SourceBufferHolder::NoOwnership);
+     if (!JS::Compile(aCx, mOptions, srcBuf, &script)) {
+         return false;
+     }
+ 
+     Finish(aCx, script);
+     return true;
+ }
+ 

+ 96 - 0
frg/work-js/mozilla-release/patches/1475228-3-63a1.patch

@@ -0,0 +1,96 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1531834223 -3600
+#      Tue Jul 17 14:30:23 2018 +0100
+# Node ID f43d114999adfec10befc0f758d0097358f190f2
+# Parent  b280769277af509270952ba4690e7bbc8ae3c161
+Bug 1475228 - Allow construction of a SourceBufferHolder from a UniquePtr r=jandem r=kmag
+
+diff --git a/js/src/jsapi.h b/js/src/jsapi.h
+--- a/js/src/jsapi.h
++++ b/js/src/jsapi.h
+@@ -386,24 +386,25 @@ class SourceBufferHolder final
+       GiveOwnership
+     };
+ 
+     SourceBufferHolder(const char16_t* data, size_t dataLength, Ownership ownership)
+       : data_(data),
+         length_(dataLength),
+         ownsChars_(ownership == GiveOwnership)
+     {
+-        // Ensure that null buffers properly return an unowned, empty,
+-        // null-terminated string.
+-        static const char16_t NullChar_ = 0;
+-        if (!get()) {
+-            data_ = &NullChar_;
+-            length_ = 0;
+-            ownsChars_ = false;
+-        }
++        fixEmptyBuffer();
++    }
++
++    SourceBufferHolder(UniqueTwoByteChars&& data, size_t dataLength)
++      : data_(data.release()),
++        length_(dataLength),
++        ownsChars_(true)
++    {
++        fixEmptyBuffer();
+     }
+ 
+     SourceBufferHolder(SourceBufferHolder&& other)
+       : data_(other.data_),
+         length_(other.length_),
+         ownsChars_(other.ownsChars_)
+     {
+         other.data_ = nullptr;
+@@ -443,16 +444,27 @@ class SourceBufferHolder final
+         ownsChars_ = false;
+         return const_cast<char16_t*>(data_);
+     }
+ 
+   private:
+     SourceBufferHolder(SourceBufferHolder&) = delete;
+     SourceBufferHolder& operator=(SourceBufferHolder&) = delete;
+ 
++    void fixEmptyBuffer() {
++        // Ensure that null buffers properly return an unowned, empty,
++        // null-terminated string.
++        static const char16_t NullChar_ = 0;
++        if (!get()) {
++            data_ = &NullChar_;
++            length_ = 0;
++            ownsChars_ = false;
++        }
++    }
++
+     const char16_t* data_;
+     size_t length_;
+     bool ownsChars_;
+ };
+ 
+ struct TranscodeSource;
+ 
+ } /* namespace JS */
+diff --git a/js/xpconnect/loader/ChromeScriptLoader.cpp b/js/xpconnect/loader/ChromeScriptLoader.cpp
+--- a/js/xpconnect/loader/ChromeScriptLoader.cpp
++++ b/js/xpconnect/loader/ChromeScriptLoader.cpp
+@@ -126,18 +126,17 @@ OffThreadScriptLoaderCallback(JS::OffThr
+     SystemGroup::Dispatch(TaskCategory::Other, scriptCompiler.forget());
+ }
+ 
+ bool
+ AsyncScriptCompiler::StartCompile(JSContext* aCx)
+ {
+     Rooted<JSObject*> global(aCx, mGlobalObject->GetGlobalJSObject());
+ 
+-    JS::SourceBufferHolder srcBuf(mScriptText.get(), mScriptLength,
+-                                  JS::SourceBufferHolder::NoOwnership);
++    JS::SourceBufferHolder srcBuf(std::move(mScriptText), mScriptLength);
+     if (JS::CanCompileOffThread(aCx, mOptions, mScriptLength)) {
+         if (!JS::CompileOffThread(aCx, mOptions, srcBuf,
+                                   OffThreadScriptLoaderCallback,
+                                   static_cast<void*>(this))) {
+             return false;
+         }
+ 
+         NS_ADDREF(this);

+ 162 - 0
frg/work-js/mozilla-release/patches/1475228-4-63a1.patch

@@ -0,0 +1,162 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1531834223 -3600
+#      Tue Jul 17 14:30:23 2018 +0100
+# Node ID ce27ea285957f3a547859555135c0d1d78bd5be3
+# Parent  f43d114999adfec10befc0f758d0097358f190f2
+Bug 1475228 - Add JSMallocAllocPolicy to let gecko allocate data structures using the JS heap r=jandem
+
+diff --git a/js/public/AllocPolicy.h b/js/public/AllocPolicy.h
+--- a/js/public/AllocPolicy.h
++++ b/js/public/AllocPolicy.h
+@@ -21,31 +21,38 @@ extern MOZ_COLD JS_PUBLIC_API(void) JS_R
+ 
+ namespace js {
+ 
+ enum class AllocFunction {
+     Malloc,
+     Calloc,
+     Realloc
+ };
+-/* Policy for using system memory functions and doing no error reporting. */
+-class SystemAllocPolicy
++
++/* Base class allocation policies providing allocation methods. */
++class AllocPolicyBase
+ {
+   public:
+     template <typename T> T* maybe_pod_malloc(size_t numElems) { return js_pod_malloc<T>(numElems); }
+     template <typename T> T* maybe_pod_calloc(size_t numElems) { return js_pod_calloc<T>(numElems); }
+     template <typename T> T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
+         return js_pod_realloc<T>(p, oldSize, newSize);
+     }
+     template <typename T> T* pod_malloc(size_t numElems) { return maybe_pod_malloc<T>(numElems); }
+     template <typename T> T* pod_calloc(size_t numElems) { return maybe_pod_calloc<T>(numElems); }
+     template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
+         return maybe_pod_realloc<T>(p, oldSize, newSize);
+     }
+     template <typename T> void free_(T* p, size_t numElems = 0) { js_free(p); }
++};
++
++/* Policy for using system memory functions and doing no error reporting. */
++class SystemAllocPolicy : public AllocPolicyBase
++{
++  public:
+     void reportAllocOverflow() const {}
+     bool checkSimulatedOOM() const {
+         return !js::oom::ShouldFailWithOOM();
+     }
+ };
+ 
+ MOZ_COLD JS_FRIEND_API(void) ReportOutOfMemory(JSContext* cx);
+ 
+@@ -53,17 +60,17 @@ MOZ_COLD JS_FRIEND_API(void) ReportOutOf
+  * Allocation policy that calls the system memory functions and reports errors
+  * to the context. Since the JSContext given on construction is stored for
+  * the lifetime of the container, this policy may only be used for containers
+  * whose lifetime is a shorter than the given JSContext.
+  *
+  * FIXME bug 647103 - rewrite this in terms of temporary allocation functions,
+  * not the system ones.
+  */
+-class TempAllocPolicy
++class TempAllocPolicy : public AllocPolicyBase
+ {
+     JSContext* const cx_;
+ 
+     /*
+      * Non-inline helper to call JSRuntime::onOutOfMemory with minimal
+      * code bloat.
+      */
+     JS_FRIEND_API(void*) onOutOfMemory(AllocFunction allocFunc, size_t nbytes,
+@@ -76,49 +83,34 @@ class TempAllocPolicy
+             return nullptr;
+         return static_cast<T*>(onOutOfMemory(allocFunc, bytes, reallocPtr));
+     }
+ 
+   public:
+     MOZ_IMPLICIT TempAllocPolicy(JSContext* cx) : cx_(cx) {}
+ 
+     template <typename T>
+-    T* maybe_pod_malloc(size_t numElems) {
+-        return js_pod_malloc<T>(numElems);
+-    }
+-
+-    template <typename T>
+-    T* maybe_pod_calloc(size_t numElems) {
+-        return js_pod_calloc<T>(numElems);
+-    }
+-
+-    template <typename T>
+-    T* maybe_pod_realloc(T* prior, size_t oldSize, size_t newSize) {
+-        return js_pod_realloc<T>(prior, oldSize, newSize);
+-    }
+-
+-    template <typename T>
+     T* pod_malloc(size_t numElems) {
+-        T* p = maybe_pod_malloc<T>(numElems);
++        T* p = this->maybe_pod_malloc<T>(numElems);
+         if (MOZ_UNLIKELY(!p))
+             p = onOutOfMemoryTyped<T>(AllocFunction::Malloc, numElems);
+         return p;
+     }
+ 
+     template <typename T>
+     T* pod_calloc(size_t numElems) {
+-        T* p = maybe_pod_calloc<T>(numElems);
++        T* p = this->maybe_pod_calloc<T>(numElems);
+         if (MOZ_UNLIKELY(!p))
+             p = onOutOfMemoryTyped<T>(AllocFunction::Calloc, numElems);
+         return p;
+     }
+ 
+     template <typename T>
+     T* pod_realloc(T* prior, size_t oldSize, size_t newSize) {
+-        T* p2 = maybe_pod_realloc<T>(prior, oldSize, newSize);
++        T* p2 = this->maybe_pod_realloc<T>(prior, oldSize, newSize);
+         if (MOZ_UNLIKELY(!p2))
+             p2 = onOutOfMemoryTyped<T>(AllocFunction::Realloc, newSize, prior);
+         return p2;
+     }
+ 
+     template <typename T>
+     void free_(T* p, size_t numElems = 0) {
+         js_free(p);
+diff --git a/js/src/jsapi.h b/js/src/jsapi.h
+--- a/js/src/jsapi.h
++++ b/js/src/jsapi.h
+@@ -1324,16 +1324,34 @@ JS_free(JSContext* cx, void* p);
+  * performance optimization as specified by the given JSFreeOp instance.
+  */
+ extern JS_PUBLIC_API(void)
+ JS_freeop(JSFreeOp* fop, void* p);
+ 
+ extern JS_PUBLIC_API(void)
+ JS_updateMallocCounter(JSContext* cx, size_t nbytes);
+ 
++/*
++ * A replacement for MallocAllocPolicy that allocates in the JS heap and adds no
++ * extra behaviours.
++ *
++ * This is currently used for allocating source buffers for parsing. Since these
++ * are temporary and will not be freed by GC, the memory is not tracked by the
++ * usual accounting.
++ */
++class JS_PUBLIC_API(JSMallocAllocPolicy) : public js::AllocPolicyBase
++{
++public:
++    void reportAllocOverflow() const {}
++
++    MOZ_MUST_USE bool checkSimulatedOOM() const {
++        return true;
++    }
++};
++
+ /**
+  * Set the size of the native stack that should not be exceed. To disable
+  * stack size checking pass 0.
+  *
+  * SpiderMonkey allows for a distinction between system code (such as GCs, which
+  * may incidentally be triggered by script but are not strictly performed on
+  * behalf of such script), trusted script (as determined by JS_SetTrustedPrincipals),
+  * and untrusted script. Each kind of code may have a different stack quota,

+ 156 - 0
frg/work-js/mozilla-release/patches/1475228-5-63a1.patch

@@ -0,0 +1,156 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1531834223 -3600
+#      Tue Jul 17 14:30:23 2018 +0100
+# Node ID b1a05b0af0274777350fdee29416570102745326
+# Parent  1cffd39eacf13cd377ce2fb608c40a2e94a3f65f
+Bug 1475228 - Refactor ScriptLoader::GetScriptSource() to remove inline data argument r=baku
+
+diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
+--- a/dom/script/ScriptLoader.cpp
++++ b/dom/script/ScriptLoader.cpp
+@@ -437,18 +437,17 @@ ScriptLoader::CreateModuleScript(ModuleL
+       rv = module ? NS_OK : NS_ERROR_FAILURE;
+     } else {
+       JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
+ 
+       JS::CompileOptions options(cx);
+       rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
+ 
+       if (NS_SUCCEEDED(rv)) {
+-        nsAutoString inlineData;
+-        SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
++        SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+         rv = nsJSUtils::CompileModule(cx, srcBuf, global, options, &module);
+       }
+     }
+ 
+     MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
+ 
+     RefPtr<ModuleScript> moduleScript = new ModuleScript(this, aRequest->mBaseURL);
+     aRequest->mModuleScript = moduleScript;
+@@ -1754,38 +1753,34 @@ ScriptLoader::AttemptAsyncScriptCompile(
+     }
+   }
+ 
+   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
+     new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
+ 
+   if (aRequest->IsModuleRequest()) {
+     MOZ_ASSERT(aRequest->IsTextSource());
+-    JS::SourceBufferHolder srcBuf(aRequest->ScriptText().begin(),
+-                                  aRequest->ScriptText().length(),
+-                                  JS::SourceBufferHolder::NoOwnership);
++    JS::SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+     if (!JS::CompileOffThreadModule(cx, options,
+                                     srcBuf,
+                                     OffThreadScriptLoaderCallback,
+                                     static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   } else if (aRequest->IsBytecode()) {
+     if (!JS::DecodeOffThreadScript(cx, options,
+                                    aRequest->mScriptBytecode,
+                                    aRequest->mBytecodeOffset,
+                                    OffThreadScriptLoaderCallback,
+                                    static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   } else {
+     MOZ_ASSERT(aRequest->IsTextSource());
+-    JS::SourceBufferHolder srcBuf(aRequest->ScriptText().begin(),
+-                                  aRequest->ScriptText().length(),
+-                                  JS::SourceBufferHolder::NoOwnership);
++    JS::SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+     if (!JS::CompileOffThread(cx, options,
+                               srcBuf,
+                               OffThreadScriptLoaderCallback,
+                               static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   }
+ 
+@@ -1813,29 +1808,32 @@ ScriptLoader::CompileOffThreadOrProcessR
+   if (NS_SUCCEEDED(rv)) {
+     return rv;
+   }
+ 
+   return ProcessRequest(aRequest);
+ }
+ 
+ SourceBufferHolder
+-ScriptLoader::GetScriptSource(ScriptLoadRequest* aRequest, nsAutoString& inlineData)
++ScriptLoader::GetScriptSource(JSContext* aCx, ScriptLoadRequest* aRequest)
+ {
+   // Return a SourceBufferHolder object holding the script's source text.
+-  // |inlineData| is used to hold the text for inline objects.
+ 
+   // If there's no script text, we try to get it from the element
+   if (aRequest->mIsInline) {
+-    // XXX This is inefficient - GetText makes multiple
+-    // copies.
++    nsAutoString inlineData;
+     aRequest->mElement->GetScriptText(inlineData);
+-    return SourceBufferHolder(inlineData.get(),
+-                              inlineData.Length(),
+-                              SourceBufferHolder::NoOwnership);
++
++    // Copy string to JS allocated buffer and transfer ownership to
++    // SourceBufferHolder result.
++    size_t nbytes = inlineData.Length() * sizeof(char16_t);
++    JS::UniqueTwoByteChars chars(static_cast<char16_t*>(JS_malloc(aCx, nbytes)));
++    MOZ_RELEASE_ASSERT(chars);
++    memcpy(chars.get(), inlineData.get(), nbytes);
++    return SourceBufferHolder(std::move(chars), inlineData.Length());
+   }
+ 
+   return SourceBufferHolder(aRequest->ScriptText().begin(),
+                             aRequest->ScriptText().length(),
+                             SourceBufferHolder::NoOwnership);
+ }
+ 
+ nsresult
+@@ -2259,18 +2257,17 @@ ScriptLoader::EvaluateScript(ScriptLoadR
+               LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute",
+                    aRequest));
+               MOZ_ASSERT(aRequest->IsTextSource());
+               rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
+             } else {
+               // Main thread parsing (inline and small scripts)
+               LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
+               MOZ_ASSERT(aRequest->IsTextSource());
+-              nsAutoString inlineData;
+-              SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
++              SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+               rv = exec.CompileAndExec(options, srcBuf, &script);
+             }
+           }
+ 
+           // Queue the current script load request to later save the bytecode.
+           if (script && encodeBytecode) {
+             aRequest->mScript = script;
+             HoldJSObjects(aRequest);
+diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h
+--- a/dom/script/ScriptLoader.h
++++ b/dom/script/ScriptLoader.h
+@@ -492,18 +492,18 @@ private:
+                                 nsresult aStatus);
+ 
+   void AddDeferRequest(ScriptLoadRequest* aRequest);
+   void AddAsyncRequest(ScriptLoadRequest* aRequest);
+   bool MaybeRemovedDeferRequests();
+ 
+   void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest);
+ 
+-  JS::SourceBufferHolder GetScriptSource(ScriptLoadRequest* aRequest,
+-                                         nsAutoString& inlineData);
++  JS::SourceBufferHolder GetScriptSource(JSContext* aCx,
++                                         ScriptLoadRequest* aRequest);
+ 
+   void SetModuleFetchStarted(ModuleLoadRequest *aRequest);
+   void SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest* aRequest,
+                                                       nsresult aResult);
+ 
+   bool IsFetchingModule(ModuleLoadRequest* aRequest) const;
+ 
+   bool ModuleMapContainsURL(nsIURI* aURL) const;

+ 214 - 0
frg/work-js/mozilla-release/patches/1475228-6-63a1.patch

@@ -0,0 +1,214 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1531834223 -3600
+#      Tue Jul 17 14:30:23 2018 +0100
+# Node ID 0c94a41970e21db5398970b168e6dd1c6a922ce8
+# Parent  94cd04dc8b31203062e875b0344b48258409ee74
+Bug 1475228 - Don't ignore errors returned from ScriptLoader::AttemptAsyncScriptCompile() r=baku
+
+diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
+--- a/dom/script/ScriptLoader.cpp
++++ b/dom/script/ScriptLoader.cpp
+@@ -1707,24 +1707,26 @@ OffThreadScriptLoaderCallback(JS::OffThr
+ {
+   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> aRunnable =
+     dont_AddRef(static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData));
+   aRunnable->SetToken(aToken);
+   NotifyOffThreadScriptLoadCompletedRunnable::Dispatch(aRunnable.forget());
+ }
+ 
+ nsresult
+-ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest)
++ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
++                                        bool* aCouldCompileOut)
+ {
+   MOZ_ASSERT_IF(!aRequest->IsModuleRequest(), aRequest->IsReadyToRun());
+   MOZ_ASSERT(!aRequest->mWasCompiledOMT);
++  MOZ_ASSERT(aCouldCompileOut && !*aCouldCompileOut);
+ 
+   // Don't off-thread compile inline scripts.
+   if (aRequest->mIsInline) {
+-    return NS_ERROR_FAILURE;
++    return NS_OK;
+   }
+ 
+   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
+   if (!globalObject) {
+     return NS_ERROR_FAILURE;
+   }
+ 
+   AutoJSAPI jsapi;
+@@ -1738,82 +1740,89 @@ ScriptLoader::AttemptAsyncScriptCompile(
+ 
+   nsresult rv = FillCompileOptionsForRequest(jsapi, aRequest, global, &options);
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+     return rv;
+   }
+ 
+   if (aRequest->IsTextSource()) {
+     if (!JS::CanCompileOffThread(cx, options, aRequest->ScriptText().length())) {
+-      return NS_ERROR_FAILURE;
++      return NS_OK;
+     }
+   } else {
+     MOZ_ASSERT(aRequest->IsBytecode());
+     size_t length = aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset;
+     if (!JS::CanDecodeOffThread(cx, options, length)) {
+-      return NS_ERROR_FAILURE;
++      return NS_OK;
+     }
+   }
+ 
+   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
+     new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
+ 
+   if (aRequest->IsModuleRequest()) {
+     MOZ_ASSERT(aRequest->IsTextSource());
+-    JS::SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
++    SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+     if (!JS::CompileOffThreadModule(cx, options,
+                                     srcBuf,
+                                     OffThreadScriptLoaderCallback,
+                                     static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   } else if (aRequest->IsBytecode()) {
+     if (!JS::DecodeOffThreadScript(cx, options,
+                                    aRequest->mScriptBytecode,
+                                    aRequest->mBytecodeOffset,
+                                    OffThreadScriptLoaderCallback,
+                                    static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   } else {
+     MOZ_ASSERT(aRequest->IsTextSource());
+-    JS::SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
++    SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+     if (!JS::CompileOffThread(cx, options,
+                               srcBuf,
+                               OffThreadScriptLoaderCallback,
+                               static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   }
+ 
+   mDocument->BlockOnload();
+ 
+   // Once the compilation is finished, an event would be added to the event loop
+   // to call ScriptLoader::ProcessOffThreadRequest with the same request.
+   aRequest->mProgress = ScriptLoadRequest::Progress::eCompiling;
+ 
++  *aCouldCompileOut = true;
+   Unused << runnable.forget();
+   return NS_OK;
+ }
+ 
+ nsresult
+ ScriptLoader::CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest)
+ {
+   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
+                "Processing requests when running scripts is unsafe.");
+   NS_ASSERTION(!aRequest->mOffThreadToken,
+                "Candidate for off-thread compile is already parsed off-thread");
+   NS_ASSERTION(!aRequest->InCompilingStage(),
+                "Candidate for off-thread compile is already in compiling stage.");
+ 
+-  nsresult rv = AttemptAsyncScriptCompile(aRequest);
+-  if (NS_SUCCEEDED(rv)) {
++  bool couldCompile = false;
++  nsresult rv = AttemptAsyncScriptCompile(aRequest, &couldCompile);
++  if (NS_FAILED(rv)) {
++    HandleLoadError(aRequest, rv);
+     return rv;
+   }
+ 
++  if (couldCompile) {
++    return NS_OK;
++  }
++
+   return ProcessRequest(aRequest);
+ }
+ 
+ SourceBufferHolder
+ ScriptLoader::GetScriptSource(JSContext* aCx, ScriptLoadRequest* aRequest)
+ {
+   // Return a SourceBufferHolder object holding the script's source text.
+ 
+@@ -3089,45 +3098,43 @@ ScriptLoader::PrepareLoadedRequest(Scrip
+     // Fixup moz-extension: and resource: URIs, because the channel URI will
+     // point to file:, which won't be allowed to load.
+     if (uri && IsInternalURIScheme(uri)) {
+       request->mBaseURL = uri;
+     } else {
+       channel->GetURI(getter_AddRefs(request->mBaseURL));
+     }
+ 
+-
+     // Attempt to compile off main thread.
+-    rv = AttemptAsyncScriptCompile(request);
+-    if (NS_SUCCEEDED(rv)) {
+-      return rv;
++    bool couldCompile = false;
++    rv = AttemptAsyncScriptCompile(request, &couldCompile);
++    NS_ENSURE_SUCCESS(rv, rv);
++    if (couldCompile) {
++      return NS_OK;
+     }
+ 
+     // Otherwise compile it right away and start fetching descendents.
+     return ProcessFetchedModuleSource(request);
+   }
+ 
+   // The script is now loaded and ready to run.
+   aRequest->SetReady();
+ 
+   // If this is currently blocking the parser, attempt to compile it off-main-thread.
+   if (aRequest == mParserBlockingRequest && NumberOfProcessors() > 1) {
+     MOZ_ASSERT(!aRequest->IsModuleRequest());
+-    nsresult rv = AttemptAsyncScriptCompile(aRequest);
+-    if (rv == NS_OK) {
++    bool couldCompile = false;
++    nsresult rv = AttemptAsyncScriptCompile(aRequest, &couldCompile);
++    NS_ENSURE_SUCCESS(rv, rv);
++    if (couldCompile) {
+       MOZ_ASSERT(aRequest->mProgress == ScriptLoadRequest::Progress::eCompiling,
+                  "Request should be off-thread compiling now.");
+       return NS_OK;
+     }
+ 
+-    // If off-thread compile errored, return the error.
+-    if (rv != NS_ERROR_FAILURE) {
+-      return rv;
+-    }
+-
+     // If off-thread compile was rejected, continue with regular processing.
+   }
+ 
+   MaybeMoveToLoadedList(aRequest);
+ 
+   return NS_OK;
+ }
+ 
+diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h
+--- a/dom/script/ScriptLoader.h
++++ b/dom/script/ScriptLoader.h
+@@ -442,17 +442,18 @@ private:
+                      nsresult aSRIStatus,
+                      SRICheckDataVerifier* aSRIDataVerifier) const;
+ 
+   nsresult SaveSRIHash(ScriptLoadRequest *aRequest,
+                        SRICheckDataVerifier* aSRIDataVerifier) const;
+ 
+   void ReportErrorToConsole(ScriptLoadRequest *aRequest, nsresult aResult) const;
+ 
+-  nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest);
++  nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
++                                     bool* aCouldCompileOut);
+   nsresult ProcessRequest(ScriptLoadRequest* aRequest);
+   nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
+   void FireScriptAvailable(nsresult aResult,
+                            ScriptLoadRequest* aRequest);
+   void FireScriptEvaluated(nsresult aResult,
+                            ScriptLoadRequest* aRequest);
+   nsresult EvaluateScript(ScriptLoadRequest* aRequest);
+ 

+ 221 - 0
frg/work-js/mozilla-release/patches/1475228-7-63a1.patch

@@ -0,0 +1,221 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1531834224 -3600
+#      Tue Jul 17 14:30:24 2018 +0100
+# Node ID 771c40992e319e091a6b39cfd793ccd7b696bee0
+# Parent  bc5b8700635ada1d7caa76ad750227f4365763dd
+Bug 1475228 - Allocate script loader source buffers on the JS heap and pass ownership when compiling r=baku
+
+diff --git a/dom/script/ScriptLoadHandler.cpp b/dom/script/ScriptLoadHandler.cpp
+--- a/dom/script/ScriptLoadHandler.cpp
++++ b/dom/script/ScriptLoadHandler.cpp
+@@ -102,41 +102,45 @@ ScriptLoadHandler::DecodeRawData(const u
+                                  uint32_t aDataLength,
+                                  bool aEndOfStream)
+ {
+   CheckedInt<size_t> needed = mDecoder->MaxUTF16BufferLength(aDataLength);
+   if (!needed.isValid()) {
+     return NS_ERROR_OUT_OF_MEMORY;
+   }
+ 
+-  uint32_t haveRead = mRequest->ScriptText().length();
++  // Reference to the script source buffer which we will update.
++  ScriptLoadRequest::ScriptTextBuffer& scriptText = mRequest->ScriptText();
++
++  uint32_t haveRead = scriptText.length();
+ 
+   CheckedInt<uint32_t> capacity = haveRead;
+   capacity += needed.value();
+ 
+-  if (!capacity.isValid() || !mRequest->ScriptText().reserve(capacity.value())) {
++  if (!capacity.isValid() || !scriptText.resize(capacity.value())) {
+     return NS_ERROR_OUT_OF_MEMORY;
+   }
+ 
+   uint32_t result;
+   size_t read;
+   size_t written;
+   bool hadErrors;
+   Tie(result, read, written, hadErrors) = mDecoder->DecodeToUTF16(
+     MakeSpan(aData, aDataLength),
+-    MakeSpan(mRequest->ScriptText().begin() + haveRead, needed.value()),
++    MakeSpan(scriptText.begin() + haveRead, needed.value()),
+     aEndOfStream);
+   MOZ_ASSERT(result == kInputEmpty);
+   MOZ_ASSERT(read == aDataLength);
+   MOZ_ASSERT(written <= needed.value());
+   Unused << hadErrors;
+ 
+   haveRead += written;
+   MOZ_ASSERT(haveRead <= capacity.value(), "mDecoder produced more data than expected");
+-  MOZ_ALWAYS_TRUE(mRequest->ScriptText().resizeUninitialized(haveRead));
++  MOZ_ALWAYS_TRUE(scriptText.resize(haveRead));
++  mRequest->mScriptTextLength = scriptText.length();
+ 
+   return NS_OK;
+ }
+ 
+ bool
+ ScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader* aLoader,
+                                  const uint8_t* aData,
+                                  uint32_t aDataLength,
+diff --git a/dom/script/ScriptLoadRequest.cpp b/dom/script/ScriptLoadRequest.cpp
+--- a/dom/script/ScriptLoadRequest.cpp
++++ b/dom/script/ScriptLoadRequest.cpp
+@@ -61,16 +61,17 @@ ScriptLoadRequest::ScriptLoadRequest(Scr
+   , mInDeferList(false)
+   , mInAsyncList(false)
+   , mIsNonAsyncScriptInserted(false)
+   , mIsXSLT(false)
+   , mIsCanceled(false)
+   , mWasCompiledOMT(false)
+   , mIsTracking(false)
+   , mOffThreadToken(nullptr)
++  , mScriptTextLength(0)
+   , mScriptBytecode()
+   , mBytecodeOffset(0)
+   , mURI(aURI)
+   , mLineNo(1)
+   , mCORSMode(aCORSMode)
+   , mIntegrity(aIntegrity)
+   , mReferrer(aReferrer)
+   , mReferrerPolicy(aReferrerPolicy)
+@@ -161,17 +162,17 @@ ScriptLoadRequest::SetUnknownDataType()
+   mScriptData.reset();
+ }
+ 
+ void
+ ScriptLoadRequest::SetTextSource()
+ {
+   MOZ_ASSERT(IsUnknownDataType());
+   mDataType = DataType::eTextSource;
+-  mScriptData.emplace(VariantType<Vector<char16_t>>());
++  mScriptData.emplace(VariantType<ScriptTextBuffer>());
+ }
+ 
+ void
+ ScriptLoadRequest::SetBytecode()
+ {
+   MOZ_ASSERT(IsUnknownDataType());
+   mDataType = DataType::eBytecode;
+ }
+diff --git a/dom/script/ScriptLoadRequest.h b/dom/script/ScriptLoadRequest.h
+--- a/dom/script/ScriptLoadRequest.h
++++ b/dom/script/ScriptLoadRequest.h
+@@ -156,23 +156,27 @@ public:
+   {
+     return mDataType == DataType::eBytecode;
+   }
+ 
+   void SetUnknownDataType();
+   void SetTextSource();
+   void SetBytecode();
+ 
+-  const Vector<char16_t>& ScriptText() const {
++  using ScriptTextBuffer = Vector<char16_t, 0, JSMallocAllocPolicy>;
++
++  const ScriptTextBuffer& ScriptText() const
++  {
+     MOZ_ASSERT(IsTextSource());
+-    return mScriptData->as<Vector<char16_t>>();
++    return mScriptData->as<ScriptTextBuffer>();
+   }
+-  Vector<char16_t>& ScriptText() {
++  ScriptTextBuffer& ScriptText()
++  {
+     MOZ_ASSERT(IsTextSource());
+-    return mScriptData->as<Vector<char16_t>>();
++    return mScriptData->as<ScriptTextBuffer>();
+   }
+ 
+   enum class ScriptMode : uint8_t {
+     eBlocking,
+     eDeferred,
+     eAsync
+   };
+ 
+@@ -227,17 +231,21 @@ public:
+ 
+   // Holds the top-level JSScript that corresponds to the current source, once
+   // it is parsed, and planned to be saved in the bytecode cache.
+   JS::Heap<JSScript*> mScript;
+ 
+   // Holds script source data for non-inline scripts. Don't use nsString so we
+   // can give ownership to jsapi. Holds either char16_t source text characters
+   // or nothing depending on mSourceEncoding.
+-  Maybe<Variant<Vector<char16_t>, Vector<uint8_t>>> mScriptData;
++  Maybe<Variant<ScriptTextBuffer>> mScriptData;
++
++  // The length of script source text, set when reading completes. This is used
++  // since mScriptData is cleared when the source is passed to the JS engine.
++  size_t mScriptTextLength;
+ 
+   // Holds the SRI serialized hash and the script bytecode for non-inline
+   // scripts.
+   mozilla::Vector<uint8_t> mScriptBytecode;
+   uint32_t mBytecodeOffset; // Offset of the bytecode in mScriptBytecode
+ 
+   const nsCOMPtr<nsIURI> mURI;
+   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
+diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
+--- a/dom/script/ScriptLoader.cpp
++++ b/dom/script/ScriptLoader.cpp
+@@ -1820,34 +1820,34 @@ ScriptLoader::CompileOffThreadOrProcessR
+ 
+   return ProcessRequest(aRequest);
+ }
+ 
+ SourceBufferHolder
+ ScriptLoader::GetScriptSource(JSContext* aCx, ScriptLoadRequest* aRequest)
+ {
+   // Return a SourceBufferHolder object holding the script's source text.
++  // Ownership of the buffer is transferred to the resulting SourceBufferHolder.
+ 
+   // If there's no script text, we try to get it from the element
+   if (aRequest->mIsInline) {
+     nsAutoString inlineData;
+     aRequest->mElement->GetScriptText(inlineData);
+ 
+-    // Copy string to JS allocated buffer and transfer ownership to
+-    // SourceBufferHolder result.
+     size_t nbytes = inlineData.Length() * sizeof(char16_t);
+     JS::UniqueTwoByteChars chars(static_cast<char16_t*>(JS_malloc(aCx, nbytes)));
+     MOZ_RELEASE_ASSERT(chars);
+     memcpy(chars.get(), inlineData.get(), nbytes);
+     return SourceBufferHolder(std::move(chars), inlineData.Length());
+   }
+ 
+-  return SourceBufferHolder(aRequest->ScriptText().begin(),
+-                            aRequest->ScriptText().length(),
+-                            SourceBufferHolder::NoOwnership);
++  size_t length = aRequest->ScriptText().length();
++  return SourceBufferHolder(aRequest->ScriptText().extractOrCopyRawBuffer(),
++                            length,
++                            SourceBufferHolder::GiveOwnership);
+ }
+ 
+ nsresult
+ ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest)
+ {
+   LOG(("ScriptLoadRequest (%p): Process request", aRequest));
+ 
+   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
+@@ -2108,17 +2108,17 @@ ScriptLoader::ShouldCacheBytecode(Script
+ 
+   // If the script is too small/large, do not attempt at creating a bytecode
+   // cache for this script, as the overhead of parsing it might not be worth the
+   // effort.
+   if (hasSourceLengthMin) {
+     size_t sourceLength;
+     size_t minLength;
+ 
+-    sourceLength = aRequest->ScriptText().length();
++    sourceLength = aRequest->mScriptTextLength;
+     minLength = sourceLengthMin;
+ 
+     if (sourceLength < minLength) {
+       LOG(("ScriptLoadRequest (%p): Bytecode-cache: Script is too small.", aRequest));
+       return false;
+     }
+   }
+ 

+ 96 - 0
frg/work-js/mozilla-release/patches/1476921-63a1.patch

@@ -0,0 +1,96 @@
+# HG changeset patch
+# User Ashley Hauck <khyperia@mozilla.com>
+# Date 1535454420 -10800
+# Node ID 432ffee537201b7225944a73ab538e2a228976f5
+# Parent  c63c833fdd7bb10c12f7db711d2f62bebfd4028e
+Bug 1476921 - Don't throw an error in GetModuleNamespace for errored modules. r=jonco
+
+diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js
+--- a/js/src/builtin/Module.js
++++ b/js/src/builtin/Module.js
+@@ -183,42 +183,37 @@ function IsResolvedBinding(resolution)
+ }
+ 
+ // 15.2.1.18 GetModuleNamespace(module)
+ function GetModuleNamespace(module)
+ {
+     // Step 1
+     assert(IsObject(module) && IsModule(module), "GetModuleNamespace called with non-module");
+ 
+-    // Until issue https://github.com/tc39/ecma262/issues/1155 is resolved,
+-    // violate the spec here and throw if called on an errored module.
+-    if (module.status === MODULE_STATUS_EVALUATED_ERROR)
+-        throw GetModuleEvaluationError(module);
+-
+-    // Steps 2-3
++    // Step 2
+     assert(module.status !== MODULE_STATUS_UNINSTANTIATED,
+            "Bad module state in GetModuleNamespace");
+ 
+-    // Step 4
++    // Step 3
+     let namespace = module.namespace;
+ 
+-    // Step 3
++    // Step 4
+     if (typeof namespace === "undefined") {
+         let exportedNames = callFunction(module.getExportedNames, module);
+         let unambiguousNames = [];
+         for (let i = 0; i < exportedNames.length; i++) {
+             let name = exportedNames[i];
+             let resolution = callFunction(module.resolveExport, module, name);
+             if (IsResolvedBinding(resolution))
+                 _DefineDataProperty(unambiguousNames, unambiguousNames.length, name);
+         }
+         namespace = ModuleNamespaceCreate(module, unambiguousNames);
+     }
+ 
+-    // Step 4
++    // Step 5
+     return namespace;
+ }
+ 
+ // 9.4.6.13 ModuleNamespaceCreate(module, exports)
+ function ModuleNamespaceCreate(module, exports)
+ {
+     callFunction(ArraySort, exports);
+ 
+diff --git a/js/src/jit-test/tests/modules/bug-1476921.js b/js/src/jit-test/tests/modules/bug-1476921.js
+new file mode 100644
+--- /dev/null
++++ b/js/src/jit-test/tests/modules/bug-1476921.js
+@@ -0,0 +1,19 @@
++"use strict";
++
++load(libdir + "asserts.js");
++load(libdir + "dummyModuleResolveHook.js");
++
++class UniqueError extends Error {}
++
++let a = moduleRepo['a'] = parseModule(`
++    throw new UniqueError();
++`);
++
++let b = moduleRepo['b'] = parseModule(`
++    import * as ns0 from "a";
++`);
++
++instantiateModule(a);
++assertThrowsInstanceOf(() => evaluateModule(a), UniqueError);
++instantiateModule(b);
++assertThrowsInstanceOf(() => evaluateModule(b), UniqueError);
+diff --git a/js/src/jit-test/tests/modules/bug1449153.js b/js/src/jit-test/tests/modules/bug1449153.js
+--- a/js/src/jit-test/tests/modules/bug1449153.js
++++ b/js/src/jit-test/tests/modules/bug1449153.js
+@@ -27,9 +27,10 @@ let c = moduleRepo["c"] = parseModule(`
+     import "a";
+ `);
+ c.declarationInstantiation();
+ assertThrowsMyError(() => c.evaluation());
+ 
+ let b = moduleRepo['b'] = parseModule(`
+     import * as ns0 from 'a'
+ `);
+-assertThrowsMyError(() => b.declarationInstantiation());
++b.declarationInstantiation();
++assertThrowsMyError(() => b.evaluation(b));

+ 551 - 0
frg/work-js/mozilla-release/patches/1478982-63a1.patch

@@ -0,0 +1,551 @@
+# HG changeset patch
+# User Lars T Hansen <lhansen@mozilla.com>
+# Date 1532691224 -7200
+# Node ID 3e4eec1a2feee53119135913a33a2fa62c7b4ceb
+# Parent  3858eb122b9fb0868140e7c553ec99a49cc82611
+Bug 1478982 - Allow TypedObject fields to be flagged immutable. r=till
+
+This allows internal clients (notably Wasm) to flag TO fields as
+immutable; we need this both to provide immutability for fields that
+are declared immutable in wasm structs, and to temporarily avoid the
+need for type constraints on assignments to Ref-typed pointer fields.
+
+diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp
+--- a/js/src/builtin/TypedObject.cpp
++++ b/js/src/builtin/TypedObject.cpp
+@@ -542,16 +542,17 @@ ArrayMetaTypeDescr::create(JSContext* cx
+ 
+     obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(ArrayTypeDescr::Kind));
+     obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
+     obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment()));
+     obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
+     obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque()));
+     obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE, ObjectValue(*elementType));
+     obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH, Int32Value(length));
++    obj->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(0));
+ 
+     RootedValue elementTypeVal(cx, ObjectValue(*elementType));
+     if (!DefineDataProperty(cx, obj, cx->names().elementType, elementTypeVal,
+                             JSPROP_READONLY | JSPROP_PERMANENT))
+     {
+         return nullptr;
+     }
+ 
+@@ -762,16 +763,18 @@ StructMetaTypeDescr::create(JSContext* c
+         return nullptr;
+ 
+     // Iterate through each field. Collect values for the various
+     // vectors below and also track total size and alignment. Be wary
+     // of overflow!
+     AutoValueVector fieldTypeObjs(cx); // Type descriptor of each field.
+     bool opaque = false;               // Opacity of struct.
+ 
++    Vector<bool> fieldMutabilities(cx);
++
+     RootedValue fieldTypeVal(cx);
+     RootedId id(cx);
+     Rooted<TypeDescr*> fieldType(cx);
+ 
+     for (unsigned int i = 0; i < ids.length(); i++) {
+         id = ids[i];
+ 
+         // Check that all the property names are non-numeric strings.
+@@ -791,38 +794,44 @@ StructMetaTypeDescr::create(JSContext* c
+             ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
+             return nullptr;
+         }
+ 
+         // Collect field type object
+         if (!fieldTypeObjs.append(ObjectValue(*fieldType)))
+             return nullptr;
+ 
++        // Along this path everything is mutable
++        if (!fieldMutabilities.append(true))
++            return nullptr;
++
+         // Struct is opaque if any field is opaque
+         if (fieldType->opaque())
+             opaque = true;
+     }
+ 
+     RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
+     if (!structTypePrototype)
+         return nullptr;
+ 
+-    return createFromArrays(cx, structTypePrototype, opaque, ids, fieldTypeObjs);
++    return createFromArrays(cx, structTypePrototype, opaque, ids, fieldTypeObjs, fieldMutabilities);
+ }
+ 
+ /* static */ StructTypeDescr*
+ StructMetaTypeDescr::createFromArrays(JSContext* cx,
+                                       HandleObject structTypePrototype,
+                                       bool opaque,
+                                       AutoIdVector& ids,
+-                                      AutoValueVector& fieldTypeObjs)
++                                      AutoValueVector& fieldTypeObjs,
++                                      Vector<bool>& fieldMutabilities)
+ {
+     StringBuffer stringBuffer(cx);     // Canonical string repr
+     AutoValueVector fieldNames(cx);    // Name of each field.
+     AutoValueVector fieldOffsets(cx);  // Offset of each field field.
++    AutoValueVector fieldMuts(cx);     // Mutability of each field.
+     RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
+     RootedObject userFieldTypes(cx);   // User-exposed {f:descr} object.
+     Layout layout;                     // Field offsetter
+ 
+     userFieldOffsets = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
+     if (!userFieldOffsets)
+         return nullptr;
+ 
+@@ -867,16 +876,19 @@ StructMetaTypeDescr::createFromArrays(JS
+         if (!offset.isValid()) {
+             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG);
+             return nullptr;
+         }
+         MOZ_ASSERT(offset.value() >= 0);
+         if (!fieldOffsets.append(Int32Value(offset.value())))
+             return nullptr;
+ 
++        if (!fieldMuts.append(BooleanValue(fieldMutabilities[i])))
++            return nullptr;
++
+         // userFieldOffsets[id] = offset
+         RootedValue offsetValue(cx, Int32Value(offset.value()));
+         if (!DefineDataProperty(cx, userFieldOffsets, id, offsetValue,
+                                 JSPROP_READONLY | JSPROP_PERMANENT))
+         {
+             return nullptr;
+         }
+     }
+@@ -903,16 +915,17 @@ StructMetaTypeDescr::createFromArrays(JS
+     if (!descr)
+         return nullptr;
+ 
+     descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Struct));
+     descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
+     descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(AssertedCast<int32_t>(alignment)));
+     descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value()));
+     descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(opaque));
++    descr->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(0));
+ 
+     // Construct for internal use an array with the name for each field.
+     {
+         RootedObject fieldNamesVec(cx);
+         fieldNamesVec = NewDenseCopiedArray(cx, fieldNames.length(),
+                                             fieldNames.begin(), nullptr,
+                                             TenuredObject);
+         if (!fieldNamesVec)
+@@ -935,17 +948,29 @@ StructMetaTypeDescr::createFromArrays(JS
+         fieldOffsetsVec = NewDenseCopiedArray(cx, fieldOffsets.length(),
+                                               fieldOffsets.begin(), nullptr,
+                                               TenuredObject);
+         if (!fieldOffsetsVec)
+             return nullptr;
+         descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS, ObjectValue(*fieldOffsetsVec));
+     }
+ 
++    // Construct for internal use an array with the mutability for each field.
++    {
++        RootedObject fieldMutsVec(cx);
++        fieldMutsVec = NewDenseCopiedArray(cx, fieldMuts.length(),
++                                           fieldMuts.begin(), nullptr,
++                                           TenuredObject);
++        if (!fieldMutsVec)
++            return nullptr;
++        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_MUTS, ObjectValue(*fieldMutsVec));
++    }
++
+     // Create data properties fieldOffsets and fieldTypes
++    // TODO: Probably also want to track mutability here, but not important yet.
+     if (!FreezeObject(cx, userFieldOffsets))
+         return nullptr;
+     if (!FreezeObject(cx, userFieldTypes))
+         return nullptr;
+     RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
+     if (!DefineDataProperty(cx, descr, cx->names().fieldOffsets, userFieldOffsetsValue,
+                             JSPROP_READONLY | JSPROP_PERMANENT))
+     {
+@@ -1036,16 +1061,24 @@ StructTypeDescr::fieldName(size_t index)
+ size_t
+ StructTypeDescr::fieldOffset(size_t index) const
+ {
+     ArrayObject& fieldOffsets = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
+     MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
+     return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
+ }
+ 
++bool
++StructTypeDescr::fieldIsMutable(size_t index) const
++{
++    ArrayObject& fieldMuts = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_MUTS);
++    MOZ_ASSERT(index < fieldMuts.getDenseInitializedLength());
++    return fieldMuts.getDenseElement(index).toBoolean();
++}
++
+ TypeDescr&
+ StructTypeDescr::fieldDescr(size_t index) const
+ {
+     ArrayObject& fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
+     MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
+     return fieldDescrs.getDenseElement(index).toObject().as<TypeDescr>();
+ }
+ 
+@@ -1128,16 +1161,17 @@ DefineSimpleTypeDescr(JSContext* cx,
+         return false;
+ 
+     descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
+     descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
+     descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(T::alignment(type)));
+     descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(AssertedCast<int32_t>(T::size(type))));
+     descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque));
+     descr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(int32_t(type)));
++    descr->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(0));
+ 
+     if (!CreateUserSizeAndAlignmentProperties(cx, descr))
+         return false;
+ 
+     if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods))
+         return false;
+ 
+     // Create the typed prototype for the scalar type. This winds up
+@@ -1869,16 +1903,22 @@ TypedObject::obj_setProperty(JSContext* 
+ 
+       case type::Struct: {
+         Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
+ 
+         size_t fieldIndex;
+         if (!descr->fieldIndex(id, &fieldIndex))
+             break;
+ 
++        if (!descr->fieldIsMutable(fieldIndex)) {
++            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
++                                      JSMSG_TYPEDOBJECT_SETTING_IMMUTABLE);
++            return false;
++        }
++
+         if (!receiver.isObject() || obj != &receiver.toObject())
+             return SetPropertyByDefining(cx, id, v, receiver, result);
+ 
+         size_t offset = descr->fieldOffset(fieldIndex);
+         Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
+         RootedAtom fieldName(cx, &descr->fieldName(fieldIndex));
+         if (!ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, v))
+             return false;
+diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h
+--- a/js/src/builtin/TypedObject.h
++++ b/js/src/builtin/TypedObject.h
+@@ -412,17 +412,18 @@ class StructMetaTypeDescr : public Nativ
+   public:
+     // The prototype cannot be null.
+     // The names in `ids` must all be non-numeric.
+     // The type objects in `fieldTypeObjs` must all be TypeDescr objects.
+     static StructTypeDescr* createFromArrays(JSContext* cx,
+                                              HandleObject structTypePrototype,
+                                              bool opaque,
+                                              AutoIdVector& ids,
+-                                             AutoValueVector& fieldTypeObjs);
++                                             AutoValueVector& fieldTypeObjs,
++                                             Vector<bool>& fieldMutabilities);
+ 
+     // Properties and methods to be installed on StructType.prototype,
+     // and hence inherited by all struct type objects:
+     static const JSPropertySpec typeObjectProperties[];
+     static const JSFunctionSpec typeObjectMethods[];
+ 
+     // Properties and methods to be installed on StructType.prototype.prototype,
+     // and hence inherited by all struct *typed* objects:
+@@ -471,16 +472,19 @@ class StructTypeDescr : public ComplexTy
+     JSAtom& fieldName(size_t index) const;
+ 
+     // Return the type descr of the field at index `index`.
+     TypeDescr& fieldDescr(size_t index) const;
+ 
+     // Return the offset of the field at index `index`.
+     size_t fieldOffset(size_t index) const;
+ 
++    // Return the mutability of the field at index `index`.
++    bool fieldIsMutable(size_t index) const;
++
+     static bool call(JSContext* cx, unsigned argc, Value* vp);
+ 
+   private:
+     ArrayObject& fieldInfoObject(size_t slot) const {
+         return getReservedSlot(slot).toObject().as<ArrayObject>();
+     }
+ };
+ 
+diff --git a/js/src/builtin/TypedObjectConstants.h b/js/src/builtin/TypedObjectConstants.h
+--- a/js/src/builtin/TypedObjectConstants.h
++++ b/js/src/builtin/TypedObjectConstants.h
+@@ -47,31 +47,33 @@
+ #define JS_DESCR_SLOT_KIND               0  // Atomized string representation
+ #define JS_DESCR_SLOT_STRING_REPR        1  // Atomized string representation
+ #define JS_DESCR_SLOT_ALIGNMENT          2  // Alignment in bytes
+ #define JS_DESCR_SLOT_SIZE               3  // Size in bytes, else 0
+ #define JS_DESCR_SLOT_OPAQUE             4  // Atomized string representation
+ #define JS_DESCR_SLOT_TYPROTO            5  // Prototype for instances, if any
+ #define JS_DESCR_SLOT_ARRAYPROTO         6  // Lazily created prototype for arrays
+ #define JS_DESCR_SLOT_TRACE_LIST         7  // List of references for use in tracing
++#define JS_DESCR_SLOT_FLAGS              8  // int32 bitvector of JS_DESCR_FLAG_*
+ 
+ // Slots on scalars, references
+-#define JS_DESCR_SLOT_TYPE               8  // Type code
++#define JS_DESCR_SLOT_TYPE               9  // Type code
+ 
+ // Slots on array descriptors
+-#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE    8
+-#define JS_DESCR_SLOT_ARRAY_LENGTH       9
++#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE    9
++#define JS_DESCR_SLOT_ARRAY_LENGTH       10
+ 
+ // Slots on struct type objects
+-#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 8
+-#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 9
+-#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 10
++#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 9
++#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 10
++#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 11
++#define JS_DESCR_SLOT_STRUCT_FIELD_MUTS  12
+ 
+ // Maximum number of slots for any descriptor
+-#define JS_DESCR_SLOTS                   11
++#define JS_DESCR_SLOTS                   13
+ 
+ // These constants are for use exclusively in JS code. In C++ code,
+ // prefer TypeRepresentation::Scalar etc, which allows you to
+ // write a switch which will receive a warning if you omit a case.
+ #define JS_TYPEREPR_SCALAR_KIND         1
+ #define JS_TYPEREPR_REFERENCE_KIND      2
+ #define JS_TYPEREPR_STRUCT_KIND         3
+ #define JS_TYPEREPR_ARRAY_KIND          4
+diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
+--- a/js/src/jit/IonBuilder.cpp
++++ b/js/src/jit/IonBuilder.cpp
+@@ -10569,17 +10569,18 @@ IonBuilder::getPropTryNotDefined(bool* e
+ AbortReasonOr<Ok>
+ IonBuilder::getPropTryTypedObject(bool* emitted,
+                                   MDefinition* obj,
+                                   PropertyName* name)
+ {
+     TypedObjectPrediction fieldPrediction;
+     size_t fieldOffset;
+     size_t fieldIndex;
+-    if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
++    bool fieldMutable;
++    if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex, &fieldMutable))
+         return Ok();
+ 
+     switch (fieldPrediction.kind()) {
+       case type::Struct:
+       case type::Array:
+         return getPropTryComplexPropOfTypedObject(emitted,
+                                                   obj,
+                                                   fieldOffset,
+@@ -11711,17 +11712,21 @@ IonBuilder::setPropTryCommonDOMSetter(bo
+ 
+ AbortReasonOr<Ok>
+ IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj,
+                                   PropertyName* name, MDefinition* value)
+ {
+     TypedObjectPrediction fieldPrediction;
+     size_t fieldOffset;
+     size_t fieldIndex;
+-    if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
++    bool fieldMutable;
++    if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex, &fieldMutable))
++        return Ok();
++
++    if (!fieldMutable)
+         return Ok();
+ 
+     switch (fieldPrediction.kind()) {
+       case type::Reference:
+         return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
+                                                     value, fieldPrediction, name);
+ 
+       case type::Scalar:
+@@ -13481,33 +13486,34 @@ IonBuilder::loadTypedObjectElements(MDef
+ // set `objTypes` of the field owner. If a field is found, returns true
+ // and sets *fieldOffset, *fieldPrediction, and *fieldIndex. Returns false
+ // otherwise. Infallible.
+ bool
+ IonBuilder::typedObjectHasField(MDefinition* typedObj,
+                                 PropertyName* name,
+                                 size_t* fieldOffset,
+                                 TypedObjectPrediction* fieldPrediction,
+-                                size_t* fieldIndex)
++                                size_t* fieldIndex,
++                                bool* fieldMutable)
+ {
+     TypedObjectPrediction objPrediction = typedObjectPrediction(typedObj);
+     if (objPrediction.isUseless()) {
+         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
+         return false;
+     }
+ 
+     // Must be accessing a struct.
+     if (objPrediction.kind() != type::Struct) {
+         trackOptimizationOutcome(TrackedOutcome::NotStruct);
+         return false;
+     }
+ 
+     // Determine the type/offset of the field `name`, if any.
+     if (!objPrediction.hasFieldNamed(NameToId(name), fieldOffset,
+-                                     fieldPrediction, fieldIndex))
++                                     fieldPrediction, fieldIndex, fieldMutable))
+     {
+         trackOptimizationOutcome(TrackedOutcome::StructNoField);
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
+--- a/js/src/jit/IonBuilder.h
++++ b/js/src/jit/IonBuilder.h
+@@ -352,17 +352,18 @@ class IonBuilder
+ 
+     // binary data lookup helpers.
+     TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
+     TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
+     bool typedObjectHasField(MDefinition* typedObj,
+                              PropertyName* name,
+                              size_t* fieldOffset,
+                              TypedObjectPrediction* fieldTypeReprs,
+-                             size_t* fieldIndex);
++                             size_t* fieldIndex,
++                             bool* fieldMutable);
+     MDefinition* loadTypedObjectType(MDefinition* value);
+     AbortReasonOr<Ok> loadTypedObjectData(MDefinition* typedObj,
+                                           MDefinition** owner,
+                                           LinearSum* ownerOffset);
+     AbortReasonOr<Ok> loadTypedObjectElements(MDefinition* typedObj,
+                                               const LinearSum& byteOffset,
+                                               uint32_t scale,
+                                               MDefinition** ownerElements,
+diff --git a/js/src/jit/TypedObjectPrediction.cpp b/js/src/jit/TypedObjectPrediction.cpp
+--- a/js/src/jit/TypedObjectPrediction.cpp
++++ b/js/src/jit/TypedObjectPrediction.cpp
+@@ -251,51 +251,54 @@ TypedObjectPrediction::arrayElementType(
+ }
+ 
+ bool
+ TypedObjectPrediction::hasFieldNamedPrefix(const StructTypeDescr& descr,
+                                            size_t fieldCount,
+                                            jsid id,
+                                            size_t* fieldOffset,
+                                            TypedObjectPrediction* out,
+-                                           size_t* index) const
++                                           size_t* index,
++                                           bool* isMutable) const
+ {
+     // Find the index of the field |id| if any.
+     if (!descr.fieldIndex(id, index))
+         return false;
+ 
+     // Check whether the index falls within our known safe prefix.
+     if (*index >= fieldCount)
+         return false;
+ 
+     // Load the offset and type.
+     *fieldOffset = descr.fieldOffset(*index);
+     *out = TypedObjectPrediction(descr.fieldDescr(*index));
++    *isMutable = descr.fieldIsMutable(*index);
+     return true;
+ }
+ 
+ bool
+ TypedObjectPrediction::hasFieldNamed(jsid id,
+                                      size_t* fieldOffset,
+                                      TypedObjectPrediction* fieldType,
+-                                     size_t* fieldIndex) const
++                                     size_t* fieldIndex,
++                                     bool* fieldMutable) const
+ {
+     MOZ_ASSERT(kind() == type::Struct);
+ 
+     switch (predictionKind()) {
+       case TypedObjectPrediction::Empty:
+       case TypedObjectPrediction::Inconsistent:
+         return false;
+ 
+       case TypedObjectPrediction::Descr:
+         return hasFieldNamedPrefix(
+             descr().as<StructTypeDescr>(), ALL_FIELDS,
+-            id, fieldOffset, fieldType, fieldIndex);
++            id, fieldOffset, fieldType, fieldIndex, fieldMutable);
+ 
+       case TypedObjectPrediction::Prefix:
+         return hasFieldNamedPrefix(
+             *prefix().descr, prefix().fields,
+-            id, fieldOffset, fieldType, fieldIndex);
++            id, fieldOffset, fieldType, fieldIndex, fieldMutable);
+ 
+       default:
+         MOZ_CRASH("Bad prediction kind");
+     }
+ }
+diff --git a/js/src/jit/TypedObjectPrediction.h b/js/src/jit/TypedObjectPrediction.h
+--- a/js/src/jit/TypedObjectPrediction.h
++++ b/js/src/jit/TypedObjectPrediction.h
+@@ -105,17 +105,18 @@ class TypedObjectPrediction {
+     template<typename T>
+     typename T::Type extractType() const;
+ 
+     bool hasFieldNamedPrefix(const StructTypeDescr& descr,
+                              size_t fieldCount,
+                              jsid id,
+                              size_t* fieldOffset,
+                              TypedObjectPrediction* out,
+-                             size_t* index) const;
++                             size_t* index,
++                             bool* isMutable) const;
+ 
+   public:
+ 
+     ///////////////////////////////////////////////////////////////////////////
+     // Constructing a prediction. Generally, you start with an empty
+     // prediction and invoke addDescr() repeatedly.
+ 
+     TypedObjectPrediction()
+@@ -187,15 +188,16 @@ class TypedObjectPrediction {
+ 
+     // Returns true if the predicted type includes a field named |id|
+     // and sets |*fieldOffset|, |*fieldType|, and |*fieldIndex| with
+     // the offset (in bytes), type, and index of the field
+     // respectively.  Otherwise returns false.
+     bool hasFieldNamed(jsid id,
+                        size_t* fieldOffset,
+                        TypedObjectPrediction* fieldType,
+-                       size_t* fieldIndex) const;
++                       size_t* fieldIndex,
++                       bool* fieldMutable) const;
+ };
+ 
+ } // namespace jit
+ } // namespace js
+ 
+ #endif
+diff --git a/js/src/js.msg b/js/src/js.msg
+--- a/js/src/js.msg
++++ b/js/src/js.msg
+@@ -538,16 +538,17 @@ MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1
+ // Typed object
+ MSG_DEF(JSMSG_INVALID_PROTOTYPE,       0, JSEXN_TYPEERR, "prototype field is not an object")
+ MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
+ MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
+ MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
+ MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
+ MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_NOT_CALLABLE, 0, JSEXN_TYPEERR, "not callable")
+ MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG,     0, JSEXN_ERR, "Type is too large to allocate")
++MSG_DEF(JSMSG_TYPEDOBJECT_SETTING_IMMUTABLE, 0, JSEXN_ERR, "setting immutable field")
+ 
+ // Array
+ MSG_DEF(JSMSG_TOO_LONG_ARRAY,         0, JSEXN_TYPEERR, "Too long array")
+ 
+ // Typed array
+ MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
+ MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
+ MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")
+

+ 32 - 0
frg/work-js/mozilla-release/patches/1479673-1-63a1.patch

@@ -0,0 +1,32 @@
+# HG changeset patch
+# User Steve Fink <sfink@mozilla.com>
+# Date 1531353503 25200
+# Node ID 12ddac0d1af4c9b0a24eef7dea6f10604c410697
+# Parent  9345ad05c0c590d9dae02c0d2e3b9903fc92985f
+Bug 1479673 - Trivial return value change, r=jonco
+
+JS::ubi::Concrete<T> inherits from JS::ubi::Base but not JS::ubi::Node, so the bare 'Size' in the class declaration refers to Base::Size, not Node::Size. (Note that they're the same underlying type.)
+
+diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
+--- a/js/src/vm/JSScript.cpp
++++ b/js/src/vm/JSScript.cpp
+@@ -4486,17 +4486,17 @@ JSScript::AutoDelazify::dropScript()
+ {
+     // Don't touch script_ if it's in the self-hosting realm, see the comment
+     // in holdScript.
+     if (script_ && !script_->realm()->isSelfHostingRealm())
+         script_->setDoNotRelazify(oldDoNotRelazify_);
+     script_ = nullptr;
+ }
+ 
+-JS::ubi::Node::Size
++JS::ubi::Base::Size
+ JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
+ {
+     Size size = Arena::thingSize(get().asTenured().getAllocKind());
+ 
+     size += get().sizeOfData(mallocSizeOf);
+     size += get().sizeOfTypeScript(mallocSizeOf);
+ 
+     size_t baselineSize = 0;
+

+ 53 - 0
frg/work-js/mozilla-release/patches/1479673-2-63a1.patch

@@ -0,0 +1,53 @@
+# HG changeset patch
+# User Steve Fink <sfink@mozilla.com>
+# Date 1534182556 25200
+# Node ID aedd937e5d6a64b48b1458e408e2124376c15409
+# Parent  b172a92546705094faecce98afb44eec59837c5e
+Bug 1479673 - Disable the analysis while calling function pointers during tracing, r=me.
+
+Fixes a hazard introduced by allowing the analysis to correctly see through more of the callgraph.
+
+diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp
+--- a/js/src/gc/RootMarking.cpp
++++ b/js/src/gc/RootMarking.cpp
+@@ -111,16 +111,20 @@ void
+ JSRuntime::tracePersistentRoots(JSTracer* trc)
+ {
+ #define TRACE_ROOTS(name, type, _) \
+     TracePersistentRootedList<type*>(trc, heapRoots.ref()[JS::RootKind::name], "persistent-" #name);
+ JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
+ #undef TRACE_ROOTS
+     TracePersistentRootedList<jsid>(trc, heapRoots.ref()[JS::RootKind::Id], "persistent-id");
+     TracePersistentRootedList<Value>(trc, heapRoots.ref()[JS::RootKind::Value], "persistent-value");
++
++    // ConcreteTraceable calls through a function pointer.
++    JS::AutoSuppressGCAnalysis nogc;
++
+     TracePersistentRootedList<ConcreteTraceable>(
+         trc, heapRoots.ref()[JS::RootKind::Traceable], "persistent-traceable");
+ }
+ 
+ static void
+ TracePersistentRooted(JSRuntime* rt, JSTracer* trc)
+ {
+     rt->tracePersistentRoots(trc);
+@@ -381,16 +385,19 @@ js::gc::GCRuntime::traceRuntimeCommon(JS
+ 
+     // Trace helper thread roots.
+     HelperThreadState().trace(trc, session);
+ 
+     // Trace the embedding's black and gray roots.
+     if (!JS::RuntimeHeapIsMinorCollecting()) {
+         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_EMBEDDING);
+ 
++        // The analysis doesn't like the function pointers below.
++        JS::AutoSuppressGCAnalysis nogc;
++
+         /*
+          * The embedding can register additional roots here.
+          *
+          * We don't need to trace these in a minor GC because all pointers into
+          * the nursery should be in the store buffer, and we want to avoid the
+          * time taken to trace all these roots.
+          */
+         for (size_t i = 0; i < blackRootTracers.ref().length(); i++) {

+ 103 - 0
frg/work-js/mozilla-release/patches/1479793-63a1.patch

@@ -0,0 +1,103 @@
+# HG changeset patch
+# User Boris Zbarsky <bzbarsky@mit.edu>
+# Date 1533920698 0
+# Node ID 5d5bb48ca0b9dc79f6f0c62f3f11b97c6f25cd2d
+# Parent  17e718b11b3f541813298d71381706bbd72408b1
+Bug 1479793.  Throw when someone tries to define an accessor property with an integer name on a DOM proxy with an indexed setter.  r=qdot,jorendorff
+
+Differential Revision: https://phabricator.services.mozilla.com/D2571
+
+diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
+--- a/dom/bindings/Codegen.py
++++ b/dom/bindings/Codegen.py
+@@ -11928,16 +11928,22 @@ class CGDOMJSProxyHandler_defineProperty
+ 
+         indexedSetter = self.descriptor.operations['IndexedSetter']
+         if indexedSetter:
+             set += fill(
+                 """
+                 uint32_t index = GetArrayIndexFromId(cx, id);
+                 if (IsArrayIndex(index)) {
+                   *defined = true;
++                  // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
++                  // Step 1.1.  The no-indexed-setter case is handled by step 1.2.
++                  if (!desc.isDataDescriptor()) {
++                    return opresult.failNotDataDescriptor();
++                  }
++
+                   $*{callSetter}
+                   return opresult.succeed();
+                 }
+                 """,
+                 callSetter=CGProxyIndexedSetter(self.descriptor).define())
+         elif self.descriptor.supportsIndexedProperties():
+             # We allow untrusted content to prevent Xrays from setting a
+             # property if that property is an indexed property and we have no
+diff --git a/js/public/Class.h b/js/public/Class.h
+--- a/js/public/Class.h
++++ b/js/public/Class.h
+@@ -197,16 +197,17 @@ class ObjectOpResult
+     JS_PUBLIC_API(bool) failCantSetInterposed();
+     JS_PUBLIC_API(bool) failCantDefineWindowElement();
+     JS_PUBLIC_API(bool) failCantDeleteWindowElement();
+     JS_PUBLIC_API(bool) failCantDeleteWindowNamedProperty();
+     JS_PUBLIC_API(bool) failCantPreventExtensions();
+     JS_PUBLIC_API(bool) failCantSetProto();
+     JS_PUBLIC_API(bool) failNoNamedSetter();
+     JS_PUBLIC_API(bool) failNoIndexedSetter();
++    JS_PUBLIC_API(bool) failNotDataDescriptor();
+ 
+     uint32_t failureCode() const {
+         MOZ_ASSERT(!ok());
+         return uint32_t(code_);
+     }
+ 
+     /*
+      * Report an error or warning if necessary; return true to proceed and
+diff --git a/js/src/js.msg b/js/src/js.msg
+--- a/js/src/js.msg
++++ b/js/src/js.msg
+@@ -584,16 +584,17 @@ MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED, 
+ // XPConnect wrappers and DOM bindings
+ MSG_DEF(JSMSG_CANT_SET_INTERPOSED,       1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
+ MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't define elements on a Window object")
+ MSG_DEF(JSMSG_CANT_DELETE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't delete elements from a Window object")
+ MSG_DEF(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't delete property {0} from window's named properties object")
+ MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS,   0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
+ MSG_DEF(JSMSG_NO_NAMED_SETTER,           2, JSEXN_TYPEERR, "{0} doesn't have a named property setter for '{1}'")
+ MSG_DEF(JSMSG_NO_INDEXED_SETTER,         2, JSEXN_TYPEERR, "{0} doesn't have an indexed property setter for '{1}'")
++MSG_DEF(JSMSG_NOT_DATA_DESCRIPTOR,       2, JSEXN_TYPEERR, "can't define a getter/setter for element '{1}' of {0} object")
+ 
+ // Super
+ MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
+ MSG_DEF(JSMSG_REINIT_THIS,       0, JSEXN_REFERENCEERR, "super() called twice in derived class constructor")
+ 
+ // Modules
+ MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT,        0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
+ MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT,   0, JSEXN_SYNTAXERR, "indirect export not found")
+diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
+--- a/js/src/jsapi.cpp
++++ b/js/src/jsapi.cpp
+@@ -273,16 +273,22 @@ JS::ObjectOpResult::failNoNamedSetter()
+ }
+ 
+ JS_PUBLIC_API(bool)
+ JS::ObjectOpResult::failNoIndexedSetter()
+ {
+     return fail(JSMSG_NO_INDEXED_SETTER);
+ }
+ 
++JS_PUBLIC_API(bool)
++JS::ObjectOpResult::failNotDataDescriptor()
++{
++    return fail(JSMSG_NOT_DATA_DESCRIPTOR);
++}
++
+ JS_PUBLIC_API(int64_t)
+ JS_Now()
+ {
+     return PRMJ_Now();
+ }
+ 
+ JS_PUBLIC_API(Value)
+ JS_GetNaNValue(JSContext* cx)

+ 46 - 45
frg/work-js/mozilla-release/patches/1480720-63a1.patch

@@ -2,7 +2,7 @@
 # User Jon Coppeard <jcoppeard@mozilla.com>
 # User Jon Coppeard <jcoppeard@mozilla.com>
 # Date 1533549268 -3600
 # Date 1533549268 -3600
 # Node ID 5f9c8d7612a7e44685a7c64815d86f5299f1b8b3
 # Node ID 5f9c8d7612a7e44685a7c64815d86f5299f1b8b3
-# Parent  4200cf09bbcc542eaf84eb18087c42f461b91ef2
+# Parent  7d9946c59d5963256ba7c8636707387024fc878b
 Bug 1480720 - Factor out script fetch options from script load request classes r=baku
 Bug 1480720 - Factor out script fetch options from script load request classes r=baku
 
 
 diff --git a/dom/script/ModuleLoadRequest.cpp b/dom/script/ModuleLoadRequest.cpp
 diff --git a/dom/script/ModuleLoadRequest.cpp b/dom/script/ModuleLoadRequest.cpp
@@ -95,7 +95,7 @@ diff --git a/dom/script/ModuleLoadRequest.h b/dom/script/ModuleLoadRequest.h
 diff --git a/dom/script/ScriptLoadHandler.cpp b/dom/script/ScriptLoadHandler.cpp
 diff --git a/dom/script/ScriptLoadHandler.cpp b/dom/script/ScriptLoadHandler.cpp
 --- a/dom/script/ScriptLoadHandler.cpp
 --- a/dom/script/ScriptLoadHandler.cpp
 +++ b/dom/script/ScriptLoadHandler.cpp
 +++ b/dom/script/ScriptLoadHandler.cpp
-@@ -202,17 +202,17 @@ ScriptLoadHandler::EnsureDecoder(nsIIncr
+@@ -206,17 +206,17 @@ ScriptLoadHandler::EnsureDecoder(nsIIncr
        return true;
        return true;
      }
      }
    }
    }
@@ -114,7 +114,7 @@ diff --git a/dom/script/ScriptLoadHandler.cpp b/dom/script/ScriptLoadHandler.cpp
      NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex,
      NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex,
                   "Incorrect preload bookkeeping");
                   "Incorrect preload bookkeeping");
      hintCharset = mScriptLoader->mPreloads[i].mCharset;
      hintCharset = mScriptLoader->mPreloads[i].mCharset;
-@@ -274,35 +274,35 @@ ScriptLoadHandler::EnsureKnownDataType(n
+@@ -278,35 +278,35 @@ ScriptLoadHandler::EnsureKnownDataType(n
  
  
    nsCOMPtr<nsIRequest> req;
    nsCOMPtr<nsIRequest> req;
    nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
    nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
@@ -157,7 +157,7 @@ diff --git a/dom/script/ScriptLoadHandler.cpp b/dom/script/ScriptLoadHandler.cpp
 diff --git a/dom/script/ScriptLoadRequest.cpp b/dom/script/ScriptLoadRequest.cpp
 diff --git a/dom/script/ScriptLoadRequest.cpp b/dom/script/ScriptLoadRequest.cpp
 --- a/dom/script/ScriptLoadRequest.cpp
 --- a/dom/script/ScriptLoadRequest.cpp
 +++ b/dom/script/ScriptLoadRequest.cpp
 +++ b/dom/script/ScriptLoadRequest.cpp
-@@ -12,74 +12,97 @@
+@@ -12,75 +12,98 @@
  #include "nsICacheInfoChannel.h"
  #include "nsICacheInfoChannel.h"
  #include "ScriptLoadRequest.h"
  #include "ScriptLoadRequest.h"
  #include "ScriptSettings.h"
  #include "ScriptSettings.h"
@@ -249,6 +249,7 @@ diff --git a/dom/script/ScriptLoadRequest.cpp b/dom/script/ScriptLoadRequest.cpp
    , mIsTracking(false)
    , mIsTracking(false)
 +  , mFetchOptions(aFetchOptions)
 +  , mFetchOptions(aFetchOptions)
    , mOffThreadToken(nullptr)
    , mOffThreadToken(nullptr)
+   , mScriptTextLength(0)
    , mScriptBytecode()
    , mScriptBytecode()
    , mBytecodeOffset(0)
    , mBytecodeOffset(0)
    , mURI(aURI)
    , mURI(aURI)
@@ -366,7 +367,7 @@ diff --git a/dom/script/ScriptLoadRequest.h b/dom/script/ScriptLoadRequest.h
    {
    {
      return mIsCanceled;
      return mIsCanceled;
    }
    }
-@@ -194,39 +219,67 @@ public:
+@@ -198,39 +223,67 @@ public:
    }
    }
  
  
    virtual bool IsTopLevel() const
    virtual bool IsTopLevel() const
@@ -438,8 +439,8 @@ diff --git a/dom/script/ScriptLoadRequest.h b/dom/script/ScriptLoadRequest.h
    JS::Heap<JSScript*> mScript;
    JS::Heap<JSScript*> mScript;
  
  
    // Holds script source data for non-inline scripts. Don't use nsString so we
    // Holds script source data for non-inline scripts. Don't use nsString so we
-@@ -235,24 +288,21 @@ public:
-   Maybe<Variant<Vector<char16_t>, Vector<uint8_t>>> mScriptData;
+@@ -243,24 +296,21 @@ public:
+   size_t mScriptTextLength;
  
  
    // Holds the SRI serialized hash and the script bytecode for non-inline
    // Holds the SRI serialized hash and the script bytecode for non-inline
    // scripts.
    // scripts.
@@ -466,7 +467,7 @@ diff --git a/dom/script/ScriptLoadRequest.h b/dom/script/ScriptLoadRequest.h
 diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
 diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
 --- a/dom/script/ScriptLoader.cpp
 --- a/dom/script/ScriptLoader.cpp
 +++ b/dom/script/ScriptLoader.cpp
 +++ b/dom/script/ScriptLoader.cpp
-@@ -817,17 +817,17 @@ public:
+@@ -816,17 +816,17 @@ public:
  
  
  void
  void
  ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest)
  ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest)
@@ -485,7 +486,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
        MaybeMoveToLoadedList(aRequest);
        MaybeMoveToLoadedList(aRequest);
        ProcessPendingRequests();
        ProcessPendingRequests();
      }
      }
-@@ -907,17 +907,17 @@ ScriptLoader::InstantiateModuleTree(Modu
+@@ -906,17 +906,17 @@ ScriptLoader::InstantiateModuleTree(Modu
    return true;
    return true;
  }
  }
  
  
@@ -504,7 +505,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    if (NS_FAILED(rv)) {
    if (NS_FAILED(rv)) {
      return rv;
      return rv;
    }
    }
-@@ -958,60 +958,60 @@ ScriptLoader::StartLoad(ScriptLoadReques
+@@ -957,60 +957,60 @@ ScriptLoader::StartLoad(ScriptLoadReques
        return NS_OK;
        return NS_OK;
      }
      }
    }
    }
@@ -574,7 +575,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
        nsIChannel::LOAD_CLASSIFY_URI);
        nsIChannel::LOAD_CLASSIFY_URI);
  
  
    NS_ENSURE_SUCCESS(rv, rv);
    NS_ENSURE_SUCCESS(rv, rv);
-@@ -1079,17 +1079,17 @@ ScriptLoader::StartLoad(ScriptLoadReques
+@@ -1078,17 +1078,17 @@ ScriptLoader::StartLoad(ScriptLoadReques
    if (httpChannel) {
    if (httpChannel) {
      // HTTP content negotation has little value in this context.
      // HTTP content negotation has little value in this context.
      nsAutoCString acceptTypes("*/*");
      nsAutoCString acceptTypes("*/*");
@@ -593,7 +594,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
        MOZ_ASSERT(NS_SUCCEEDED(rv));
        MOZ_ASSERT(NS_SUCCEEDED(rv));
      }
      }
    }
    }
-@@ -1170,30 +1170,31 @@ CSPAllowsInlineScript(nsIScriptElement* 
+@@ -1169,30 +1169,31 @@ CSPAllowsInlineScript(nsIScriptElement* 
                              &allowInlineScript);
                              &allowInlineScript);
    return allowInlineScript;
    return allowInlineScript;
  }
  }
@@ -629,7 +630,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    // We need a document to evaluate scripts.
    // We need a document to evaluate scripts.
    NS_ENSURE_TRUE(mDocument, false);
    NS_ENSURE_TRUE(mDocument, false);
  
  
-@@ -1313,19 +1314,18 @@ ScriptLoader::ProcessExternalScript(nsIS
+@@ -1312,19 +1313,18 @@ ScriptLoader::ProcessExternalScript(nsIS
  
  
      nsCOMPtr<nsIPrincipal> principal = aElement->GetScriptURITriggeringPrincipal();
      nsCOMPtr<nsIPrincipal> principal = aElement->GetScriptURITriggeringPrincipal();
      if (!principal) {
      if (!principal) {
@@ -650,7 +651,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
  
  
      LOG(("ScriptLoadRequest (%p): Created request for external script",
      LOG(("ScriptLoadRequest (%p): Created request for external script",
           request.get()));
           request.get()));
-@@ -1454,25 +1454,25 @@ ScriptLoader::ProcessInlineScript(nsIScr
+@@ -1453,25 +1453,25 @@ ScriptLoader::ProcessInlineScript(nsIScr
    // Inline classic scripts ignore their CORS mode and are always CORS_NONE.
    // Inline classic scripts ignore their CORS mode and are always CORS_NONE.
    CORSMode corsMode = CORS_NONE;
    CORSMode corsMode = CORS_NONE;
    if (aScriptKind == ScriptKind::eModule) {
    if (aScriptKind == ScriptKind::eModule) {
@@ -678,7 +679,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    request->SetScriptMode(false, aElement->GetScriptAsync());
    request->SetScriptMode(false, aElement->GetScriptAsync());
  
  
    LOG(("ScriptLoadRequest (%p): Created request for inline script",
    LOG(("ScriptLoadRequest (%p): Created request for inline script",
-@@ -1546,27 +1546,27 @@ ScriptLoader::LookupPreloadRequest(nsISc
+@@ -1545,27 +1545,27 @@ ScriptLoader::LookupPreloadRequest(nsISc
      mPreloads.IndexOf(aElement->GetScriptURI(), 0, PreloadURIComparator());
      mPreloads.IndexOf(aElement->GetScriptURI(), 0, PreloadURIComparator());
    if (i == nsTArray<PreloadInfo>::NoIndex) {
    if (i == nsTArray<PreloadInfo>::NoIndex) {
      return nullptr;
      return nullptr;
@@ -709,26 +710,26 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
  
  
    return request;
    return request;
  }
  }
-@@ -1811,17 +1811,17 @@ ScriptLoader::GetScriptSource(ScriptLoad
+@@ -1818,17 +1818,17 @@ SourceBufferHolder
+ ScriptLoader::GetScriptSource(JSContext* aCx, ScriptLoadRequest* aRequest)
  {
  {
    // Return a SourceBufferHolder object holding the script's source text.
    // Return a SourceBufferHolder object holding the script's source text.
-   // |inlineData| is used to hold the text for inline objects.
+   // Ownership of the buffer is transferred to the resulting SourceBufferHolder.
  
  
    // If there's no script text, we try to get it from the element
    // If there's no script text, we try to get it from the element
    if (aRequest->mIsInline) {
    if (aRequest->mIsInline) {
-     // XXX This is inefficient - GetText makes multiple
-     // copies.
+     nsAutoString inlineData;
 -    aRequest->mElement->GetScriptText(inlineData);
 -    aRequest->mElement->GetScriptText(inlineData);
 +    aRequest->Element()->GetScriptText(inlineData);
 +    aRequest->Element()->GetScriptText(inlineData);
-     return SourceBufferHolder(inlineData.get(),
-                               inlineData.Length(),
-                               SourceBufferHolder::NoOwnership);
+ 
+     size_t nbytes = inlineData.Length() * sizeof(char16_t);
+     JS::UniqueTwoByteChars chars(static_cast<char16_t*>(JS_malloc(aCx, nbytes)));
+     MOZ_RELEASE_ASSERT(chars);
+     memcpy(chars.get(), inlineData.get(), nbytes);
+     return SourceBufferHolder(std::move(chars), inlineData.Length());
    }
    }
  
  
-   return SourceBufferHolder(aRequest->ScriptText().begin(),
-                             aRequest->ScriptText().length(),
-                             SourceBufferHolder::NoOwnership);
-@@ -1850,31 +1850,31 @@ ScriptLoader::ProcessRequest(ScriptLoadR
+@@ -1861,31 +1861,31 @@ ScriptLoader::ProcessRequest(ScriptLoadR
      if (!request->mModuleScript) {
      if (!request->mModuleScript) {
        // There was an error fetching a module script.  Nothing to do here.
        // There was an error fetching a module script.  Nothing to do here.
        LOG(("ScriptLoadRequest (%p):   Error loading request, firing error", aRequest));
        LOG(("ScriptLoadRequest (%p):   Error loading request, firing error", aRequest));
@@ -764,7 +765,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
  
  
    {
    {
      // Try to perform a microtask checkpoint
      // Try to perform a microtask checkpoint
-@@ -1909,17 +1909,17 @@ ScriptLoader::ProcessRequest(ScriptLoadR
+@@ -1920,17 +1920,17 @@ ScriptLoader::ProcessRequest(ScriptLoadR
      nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
      nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
                                           scriptElem,
                                           scriptElem,
                                           NS_LITERAL_STRING("afterscriptexecute"),
                                           NS_LITERAL_STRING("afterscriptexecute"),
@@ -783,7 +784,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    if (aRequest->mOffThreadToken) {
    if (aRequest->mOffThreadToken) {
      // The request was parsed off-main-thread, but the result of the off
      // The request was parsed off-main-thread, but the result of the off
      // thread parse was not actually needed to process the request
      // thread parse was not actually needed to process the request
-@@ -1943,31 +1943,31 @@ ScriptLoader::ProcessRequest(ScriptLoadR
+@@ -1954,31 +1954,31 @@ ScriptLoader::ProcessRequest(ScriptLoadR
  }
  }
  
  
  void
  void
@@ -817,7 +818,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
  
  
  already_AddRefed<nsIScriptGlobalObject>
  already_AddRefed<nsIScriptGlobalObject>
  ScriptLoader::GetScriptGlobalObject()
  ScriptLoader::GetScriptGlobalObject()
-@@ -2028,18 +2028,18 @@ ScriptLoader::FillCompileOptionsForReque
+@@ -2039,18 +2039,18 @@ ScriptLoader::FillCompileOptionsForReque
      aOptions->setMutedErrors(!subsumes);
      aOptions->setMutedErrors(!subsumes);
    }
    }
  
  
@@ -838,7 +839,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    }
    }
  
  
    return NS_OK;
    return NS_OK;
-@@ -2136,17 +2136,17 @@ ScriptLoader::EvaluateScript(ScriptLoadR
+@@ -2147,17 +2147,17 @@ ScriptLoader::EvaluateScript(ScriptLoadR
    using namespace mozilla::Telemetry;
    using namespace mozilla::Telemetry;
    MOZ_ASSERT(aRequest->IsReadyToRun());
    MOZ_ASSERT(aRequest->IsReadyToRun());
  
  
@@ -857,7 +858,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
  
  
    // Get the script-type to be used by this element.
    // Get the script-type to be used by this element.
    NS_ASSERTION(scriptContent, "no content - what is default script-type?");
    NS_ASSERTION(scriptContent, "no content - what is default script-type?");
-@@ -2197,17 +2197,17 @@ ScriptLoader::EvaluateScript(ScriptLoadR
+@@ -2208,17 +2208,17 @@ ScriptLoader::EvaluateScript(ScriptLoadR
          JS_SetPendingException(cx, error);
          JS_SetPendingException(cx, error);
          return NS_OK; // An error is reported by AutoEntryScript.
          return NS_OK; // An error is reported by AutoEntryScript.
        }
        }
@@ -876,7 +877,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
          JS::ExposeScriptToDebugger(cx, script);
          JS::ExposeScriptToDebugger(cx, script);
        }
        }
  
  
-@@ -2216,24 +2216,24 @@ ScriptLoader::EvaluateScript(ScriptLoadR
+@@ -2227,24 +2227,24 @@ ScriptLoader::EvaluateScript(ScriptLoadR
        if (NS_FAILED(rv)) {
        if (NS_FAILED(rv)) {
          LOG(("ScriptLoadRequest (%p):   evaluation failed", aRequest));
          LOG(("ScriptLoadRequest (%p):   evaluation failed", aRequest));
          rv = NS_OK; // An error is reported by AutoEntryScript.
          rv = NS_OK; // An error is reported by AutoEntryScript.
@@ -903,7 +904,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
              LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute", aRequest));
              LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute", aRequest));
              rv = exec.DecodeAndExec(options, aRequest->mScriptBytecode,
              rv = exec.DecodeAndExec(options, aRequest->mScriptBytecode,
                                      aRequest->mBytecodeOffset);
                                      aRequest->mBytecodeOffset);
-@@ -2244,17 +2244,17 @@ ScriptLoader::EvaluateScript(ScriptLoadR
+@@ -2255,17 +2255,17 @@ ScriptLoader::EvaluateScript(ScriptLoadR
          } else {
          } else {
            MOZ_ASSERT(aRequest->IsSource());
            MOZ_ASSERT(aRequest->IsSource());
            JS::Rooted<JSScript*> script(cx);
            JS::Rooted<JSScript*> script(cx);
@@ -922,7 +923,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
                rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
                rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
              } else {
              } else {
                // Main thread parsing (inline and small scripts)
                // Main thread parsing (inline and small scripts)
-@@ -2265,23 +2265,23 @@ ScriptLoader::EvaluateScript(ScriptLoadR
+@@ -2275,23 +2275,23 @@ ScriptLoader::EvaluateScript(ScriptLoadR
                rv = exec.CompileAndExec(options, srcBuf, &script);
                rv = exec.CompileAndExec(options, srcBuf, &script);
              }
              }
            }
            }
@@ -948,7 +949,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
  
  
      // Even if we are not saving the bytecode of the current script, we have
      // Even if we are not saving the bytecode of the current script, we have
      // to trigger the encoding of the bytecode, as the current script can
      // to trigger the encoding of the bytecode, as the current script can
-@@ -2392,17 +2392,17 @@ ScriptLoader::EncodeBytecode()
+@@ -2402,17 +2402,17 @@ ScriptLoader::EncodeBytecode()
  
  
  void
  void
  ScriptLoader::EncodeRequestBytecode(JSContext* aCx, ScriptLoadRequest* aRequest)
  ScriptLoader::EncodeRequestBytecode(JSContext* aCx, ScriptLoadRequest* aRequest)
@@ -967,7 +968,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
           aRequest));
           aRequest));
      return;
      return;
    }
    }
-@@ -2436,17 +2436,17 @@ ScriptLoader::EncodeRequestBytecode(JSCo
+@@ -2446,17 +2446,17 @@ ScriptLoader::EncodeRequestBytecode(JSCo
                       aRequest->mScriptBytecode.length(), &n);
                       aRequest->mScriptBytecode.length(), &n);
    LOG(("ScriptLoadRequest (%p): Write bytecode cache (rv = %X, length = %u, written = %u)",
    LOG(("ScriptLoadRequest (%p): Write bytecode cache (rv = %X, length = %u, written = %u)",
         aRequest, unsigned(rv), unsigned(aRequest->mScriptBytecode.length()), n));
         aRequest, unsigned(rv), unsigned(aRequest->mScriptBytecode.length()), n));
@@ -986,7 +987,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    // If the document went away prematurely, we still want to set this, in order
    // If the document went away prematurely, we still want to set this, in order
    // to avoid queuing more scripts.
    // to avoid queuing more scripts.
    mGiveUpEncoding = true;
    mGiveUpEncoding = true;
-@@ -2463,17 +2463,17 @@ ScriptLoader::GiveUpBytecodeEncoding()
+@@ -2473,17 +2473,17 @@ ScriptLoader::GiveUpBytecodeEncoding()
      if (context) {
      if (context) {
        aes.emplace(globalObject, "give-up bytecode encoding", true);
        aes.emplace(globalObject, "give-up bytecode encoding", true);
      }
      }
@@ -1005,7 +1006,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
      }
      }
  
  
      request->mScriptBytecode.clearAndFree();
      request->mScriptBytecode.clearAndFree();
-@@ -2781,17 +2781,18 @@ ScriptLoader::VerifySRI(ScriptLoadReques
+@@ -2791,17 +2791,18 @@ ScriptLoader::VerifySRI(ScriptLoadReques
  
  
      if (loadInfo && loadInfo->GetEnforceSRI()) {
      if (loadInfo && loadInfo->GetEnforceSRI()) {
        MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
        MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
@@ -1025,7 +1026,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
      }
      }
    }
    }
  
  
-@@ -2845,17 +2846,17 @@ ScriptLoader::SaveSRIHash(ScriptLoadRequ
+@@ -2855,17 +2856,17 @@ ScriptLoader::SaveSRIHash(ScriptLoadRequ
  }
  }
  
  
  void
  void
@@ -1044,7 +1045,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    if (aResult == NS_ERROR_MALFORMED_URI) {
    if (aResult == NS_ERROR_MALFORMED_URI) {
      message =
      message =
        isScript ? "ScriptSourceMalformed" : "ModuleSourceMalformed";
        isScript ? "ScriptSourceMalformed" : "ModuleSourceMalformed";
-@@ -2866,17 +2867,18 @@ ScriptLoader::ReportErrorToConsole(Scrip
+@@ -2876,17 +2877,18 @@ ScriptLoader::ReportErrorToConsole(Scrip
    } else {
    } else {
      message =
      message =
        isScript ? "ScriptSourceLoadFailed" : "ModuleSourceLoadFailed";
        isScript ? "ScriptSourceLoadFailed" : "ModuleSourceLoadFailed";
@@ -1064,7 +1065,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
                                    EmptyString(), lineNo);
                                    EmptyString(), lineNo);
  }
  }
  
  
-@@ -2884,17 +2886,17 @@ void
+@@ -2894,17 +2896,17 @@ void
  ScriptLoader::HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult)
  ScriptLoader::HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult)
  {
  {
    /*
    /*
@@ -1083,7 +1084,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
      SetModuleFetchFinishedAndResumeWaitingRequests(request, aResult);
      SetModuleFetchFinishedAndResumeWaitingRequests(request, aResult);
    }
    }
  
  
-@@ -2929,22 +2931,22 @@ ScriptLoader::HandleLoadError(ScriptLoad
+@@ -2939,22 +2941,22 @@ ScriptLoader::HandleLoadError(ScriptLoad
      MOZ_ASSERT(!modReq->isInList());
      MOZ_ASSERT(!modReq->isInList());
      modReq->Cancel();
      modReq->Cancel();
      // A single error is fired for the top level module.
      // A single error is fired for the top level module.
@@ -1109,7 +1110,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
        aRequest->Cancel();
        aRequest->Cancel();
      }
      }
      if (aRequest->IsTopLevel()) {
      if (aRequest->IsTopLevel()) {
-@@ -2956,23 +2958,23 @@ ScriptLoader::HandleLoadError(ScriptLoad
+@@ -2966,23 +2968,23 @@ ScriptLoader::HandleLoadError(ScriptLoad
      MOZ_ASSERT(aRequest->IsCanceled());
      MOZ_ASSERT(aRequest->IsCanceled());
      MOZ_ASSERT(!aRequest->isInList());
      MOZ_ASSERT(!aRequest->isInList());
    }
    }
@@ -1135,7 +1136,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    if (mNumberOfProcessors > 0)
    if (mNumberOfProcessors > 0)
      return mNumberOfProcessors;
      return mNumberOfProcessors;
  
  
-@@ -3042,17 +3044,17 @@ ScriptLoader::PrepareLoadedRequest(Scrip
+@@ -3052,17 +3054,17 @@ ScriptLoader::PrepareLoadedRequest(Scrip
        aRequest->SetIsTracking();
        aRequest->SetIsTracking();
      }
      }
    }
    }
@@ -1154,7 +1155,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
    // This assertion could fire errorously if we ran out of memory when
    // This assertion could fire errorously if we ran out of memory when
    // inserting the request in the array. However it's an unlikely case
    // inserting the request in the array. However it's an unlikely case
    // so if you see this assertion it is likely something else that is
    // so if you see this assertion it is likely something else that is
-@@ -3198,19 +3200,19 @@ ScriptLoader::PreloadURI(nsIURI* aURI,
+@@ -3206,19 +3208,19 @@ ScriptLoader::PreloadURI(nsIURI* aURI,
      return;
      return;
    }
    }
  
  

+ 199 - 0
frg/work-js/mozilla-release/patches/1480966-63a1.patch

@@ -0,0 +1,199 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1533721203 -3600
+# Node ID 08d651dba0f60e2028ce7a4b891e809ffb7ff16b
+# Parent  464770088080c8f0bcd7fac0c905561e13c96917
+Bug 1480966 - Make ScriptLoader::GetScriptSource faillible on OOM r=baku
+
+diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
+--- a/dom/script/ScriptLoader.cpp
++++ b/dom/script/ScriptLoader.cpp
+@@ -437,18 +437,22 @@ ScriptLoader::CreateModuleScript(ModuleL
+       rv = module ? NS_OK : NS_ERROR_FAILURE;
+     } else {
+       JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
+ 
+       JS::CompileOptions options(cx);
+       rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
+ 
+       if (NS_SUCCEEDED(rv)) {
+-        SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+-        rv = nsJSUtils::CompileModule(cx, srcBuf, global, options, &module);
++        auto srcBuf = GetScriptSource(cx, aRequest);
++        if (srcBuf) {
++          rv = nsJSUtils::CompileModule(cx, *srcBuf, global, options, &module);
++        } else {
++          rv = NS_ERROR_OUT_OF_MEMORY;
++        }
+       }
+     }
+ 
+     MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
+ 
+     RefPtr<ModuleScript> moduleScript = new ModuleScript(this, aRequest->mBaseURL);
+     aRequest->mModuleScript = moduleScript;
+ 
+@@ -1748,38 +1752,38 @@ ScriptLoader::AttemptAsyncScriptCompile(
+     }
+   }
+ 
+   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
+     new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
+ 
+   if (aRequest->IsModuleRequest()) {
+     MOZ_ASSERT(aRequest->IsTextSource());
+-    SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+-    if (!JS::CompileOffThreadModule(cx, options,
+-                                    srcBuf,
+-                                    OffThreadScriptLoaderCallback,
+-                                    static_cast<void*>(runnable))) {
++    auto srcBuf = GetScriptSource(cx, aRequest);
++    if (!srcBuf || !JS::CompileOffThreadModule(cx, options,
++                                               *srcBuf,
++                                               OffThreadScriptLoaderCallback,
++                                               static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   } else if (aRequest->IsBytecode()) {
+     if (!JS::DecodeOffThreadScript(cx, options,
+                                    aRequest->mScriptBytecode,
+                                    aRequest->mBytecodeOffset,
+                                    OffThreadScriptLoaderCallback,
+                                    static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   } else {
+     MOZ_ASSERT(aRequest->IsTextSource());
+-    SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+-    if (!JS::CompileOffThread(cx, options,
+-                              srcBuf,
+-                              OffThreadScriptLoaderCallback,
+-                              static_cast<void*>(runnable))) {
++    auto srcBuf = GetScriptSource(cx, aRequest);
++    if (!srcBuf || !JS::CompileOffThread(cx, options,
++                                         *srcBuf,
++                                         OffThreadScriptLoaderCallback,
++                                         static_cast<void*>(runnable))) {
+       return NS_ERROR_OUT_OF_MEMORY;
+     }
+   }
+ 
+   mDocument->BlockOnload();
+ 
+   // Once the compilation is finished, an event would be added to the event loop
+   // to call ScriptLoader::ProcessOffThreadRequest with the same request.
+@@ -1809,38 +1813,40 @@ ScriptLoader::CompileOffThreadOrProcessR
+ 
+   if (couldCompile) {
+     return NS_OK;
+   }
+ 
+   return ProcessRequest(aRequest);
+ }
+ 
+-SourceBufferHolder
++mozilla::Maybe<SourceBufferHolder>
+ ScriptLoader::GetScriptSource(JSContext* aCx, ScriptLoadRequest* aRequest)
+ {
+   // Return a SourceBufferHolder object holding the script's source text.
+   // Ownership of the buffer is transferred to the resulting SourceBufferHolder.
+ 
+   // If there's no script text, we try to get it from the element
+   if (aRequest->mIsInline) {
+     nsAutoString inlineData;
+     aRequest->Element()->GetScriptText(inlineData);
+ 
+     size_t nbytes = inlineData.Length() * sizeof(char16_t);
+     JS::UniqueTwoByteChars chars(static_cast<char16_t*>(JS_malloc(aCx, nbytes)));
+-    MOZ_RELEASE_ASSERT(chars);
++    if (!chars) {
++      return Nothing();
++    }
++
+     memcpy(chars.get(), inlineData.get(), nbytes);
+-    return SourceBufferHolder(std::move(chars), inlineData.Length());
++    return Some(SourceBufferHolder(std::move(chars), inlineData.Length()));
+   }
+ 
+   size_t length = aRequest->ScriptText().length();
+-  return SourceBufferHolder(aRequest->ScriptText().extractOrCopyRawBuffer(),
+-                            length,
+-                            SourceBufferHolder::GiveOwnership);
++  JS::UniqueTwoByteChars chars(aRequest->ScriptText().extractOrCopyRawBuffer());
++  return Some(SourceBufferHolder(std::move(chars), length));
+ }
+ 
+ nsresult
+ ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest)
+ {
+   LOG(("ScriptLoadRequest (%p): Process request", aRequest));
+ 
+   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
+@@ -2266,18 +2272,23 @@ ScriptLoader::EvaluateScript(ScriptLoadR
+               LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute",
+                    aRequest));
+               MOZ_ASSERT(aRequest->IsTextSource());
+               rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
+             } else {
+               // Main thread parsing (inline and small scripts)
+               LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
+               MOZ_ASSERT(aRequest->IsTextSource());
+-              SourceBufferHolder srcBuf = GetScriptSource(cx, aRequest);
+-              rv = exec.CompileAndExec(options, srcBuf, &script);
++              auto srcBuf = GetScriptSource(cx, aRequest);
++
++              if (srcBuf) {
++                rv = exec.CompileAndExec(options, *srcBuf, &script);
++              } else {
++                rv = NS_ERROR_OUT_OF_MEMORY;
++              }
+             }
+           }
+ 
+           // Queue the current script load request to later save the bytecode.
+           if (script && encodeBytecode) {
+             aRequest->mScript = script;
+             HoldJSObjects(aRequest);
+             TRACE_FOR_TEST(aRequest->Element(), "scriptloader_encode");
+diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h
+--- a/dom/script/ScriptLoader.h
++++ b/dom/script/ScriptLoader.h
+@@ -18,16 +18,17 @@
+ #include "nsICacheInfoChannel.h"
+ #include "nsIDocument.h"
+ #include "nsIIncrementalStreamLoader.h"
+ #include "nsURIHashKey.h"
+ #include "mozilla/CORSMode.h"
+ #include "mozilla/dom/ScriptLoadRequest.h"
+ #include "mozilla/dom/SRIMetadata.h"
+ #include "mozilla/dom/SRICheck.h"
++#include "mozilla/Maybe.h"
+ #include "mozilla/MozPromise.h"
+ #include "mozilla/net/ReferrerPolicy.h"
+ #include "mozilla/Vector.h"
+ 
+ class nsIURI;
+ 
+ namespace JS {
+   class SourceBufferHolder;
+@@ -494,18 +495,18 @@ private:
+                                 nsresult aStatus);
+ 
+   void AddDeferRequest(ScriptLoadRequest* aRequest);
+   void AddAsyncRequest(ScriptLoadRequest* aRequest);
+   bool MaybeRemovedDeferRequests();
+ 
+   void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest);
+ 
+-  JS::SourceBufferHolder GetScriptSource(JSContext* aCx,
+-                                         ScriptLoadRequest* aRequest);
++  mozilla::Maybe<JS::SourceBufferHolder> GetScriptSource(JSContext* aCx,
++                                                         ScriptLoadRequest* aRequest);
+ 
+   void SetModuleFetchStarted(ModuleLoadRequest *aRequest);
+   void SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest* aRequest,
+                                                       nsresult aResult);
+ 
+   bool IsFetchingModule(ModuleLoadRequest* aRequest) const;
+ 
+   bool ModuleMapContainsURL(nsIURI* aURL) const;

+ 41 - 0
frg/work-js/mozilla-release/patches/1481005-63a1.patch

@@ -0,0 +1,41 @@
+# HG changeset patch
+# User Bryce Van Dyk <bvandyk@mozilla.com>
+# Date 1533664719 0
+# Node ID 76baf773b83e93b2b4c4457521671e93dbe07caa
+# Parent  7645fd7dc1a4da14cb59c065f1589e6fb29cf497
+Bug 1481005 - Explicitly delete dtor of AlignmentFinder::Alinger to avoid MSVC warning breaking debug builds. r=froydnj
+
+Bug 1480624 added new code that results in a warning on MSVC debug builds. This
+warning is treated as an error and makes such builds unhappy. The warning is
+due to implicit deletion of a dtor, this changeset makes that deletion
+explicit to avoid the fatal warning.
+
+Differential Revision: https://phabricator.services.mozilla.com/D2865
+
+diff --git a/mfbt/Alignment.h b/mfbt/Alignment.h
+--- a/mfbt/Alignment.h
++++ b/mfbt/Alignment.h
+@@ -21,16 +21,22 @@ namespace mozilla {
+  */
+ template<typename T>
+ class AlignmentFinder
+ {
+   struct Aligner
+   {
+     char mChar;
+     T mT;
++
++    // Aligner may be used to check alignment of types with deleted dtors. This
++    // results in such specializations having implicitly deleted dtors, which
++    // causes fatal warnings on MSVC (see bug 1481005). As we don't create
++    // Aligners, we can avoid this warning by explicitly deleting the dtor.
++    ~Aligner() = delete;
+   };
+ 
+ public:
+   static const size_t alignment = sizeof(Aligner) - sizeof(T);
+ };
+ 
+ #define MOZ_ALIGNOF(T) mozilla::AlignmentFinder<T>::alignment
+ 
+

+ 14 - 14
frg/work-js/mozilla-release/patches/1482153-64a1.patch

@@ -2,7 +2,7 @@
 # User Jon Coppeard <jcoppeard@mozilla.com>
 # User Jon Coppeard <jcoppeard@mozilla.com>
 # Date 1539693852 -3600
 # Date 1539693852 -3600
 # Node ID 180eb0ea89bcf02d511d4e05f493583d125177ea
 # Node ID 180eb0ea89bcf02d511d4e05f493583d125177ea
-# Parent  6d8994f35977ffb61f7d785e8a7b480c767f8203
+# Parent  49ba92489f7999da88079465778f20d7f7323d46
 Bug 1482153 - Provide a way of associating a private value with a script or module r=jandem rs=hsivonen
 Bug 1482153 - Provide a way of associating a private value with a script or module r=jandem rs=hsivonen
 
 
 diff --git a/dom/script/ModuleScript.cpp b/dom/script/ModuleScript.cpp
 diff --git a/dom/script/ModuleScript.cpp b/dom/script/ModuleScript.cpp
@@ -52,7 +52,7 @@ diff --git a/dom/script/ModuleScript.cpp b/dom/script/ModuleScript.cpp
 diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
 diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
 --- a/dom/script/ScriptLoader.cpp
 --- a/dom/script/ScriptLoader.cpp
 +++ b/dom/script/ScriptLoader.cpp
 +++ b/dom/script/ScriptLoader.cpp
-@@ -699,23 +699,23 @@ ScriptLoader::StartFetchingModuleAndDepe
+@@ -702,23 +702,23 @@ ScriptLoader::StartFetchingModuleAndDepe
      return ready;
      return ready;
    }
    }
  
  
@@ -80,7 +80,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
      return nullptr;
      return nullptr;
    }
    }
  
  
-@@ -732,29 +732,22 @@ HostResolveImportedModule(JSContext* aCx
+@@ -735,29 +735,22 @@ HostResolveImportedModule(JSContext* aCx
  
  
    MOZ_ASSERT(!ms->HasParseError());
    MOZ_ASSERT(!ms->HasParseError());
    MOZ_ASSERT(ms->ModuleRecord());
    MOZ_ASSERT(ms->ModuleRecord());
@@ -117,7 +117,7 @@ diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
 diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h
 diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h
 --- a/dom/script/ScriptLoader.h
 --- a/dom/script/ScriptLoader.h
 +++ b/dom/script/ScriptLoader.h
 +++ b/dom/script/ScriptLoader.h
-@@ -507,18 +507,19 @@ private:
+@@ -509,18 +509,19 @@ private:
  
  
    bool IsFetchingModule(ModuleLoadRequest* aRequest) const;
    bool IsFetchingModule(ModuleLoadRequest* aRequest) const;
  
  
@@ -291,7 +291,7 @@ diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h
 diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
 diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
 --- a/js/src/jsapi.cpp
 --- a/js/src/jsapi.cpp
 +++ b/js/src/jsapi.cpp
 +++ b/js/src/jsapi.cpp
-@@ -4892,25 +4892,37 @@ JS::CompileModule(JSContext* cx, const R
+@@ -4844,25 +4844,37 @@ JS::CompileModule(JSContext* cx, const R
      AssertHeapIsIdle();
      AssertHeapIsIdle();
      CHECK_REQUEST(cx);
      CHECK_REQUEST(cx);
  
  
@@ -338,7 +338,7 @@ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
 diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 --- a/js/src/jsapi.h
 --- a/js/src/jsapi.h
 +++ b/js/src/jsapi.h
 +++ b/js/src/jsapi.h
-@@ -3861,31 +3861,31 @@ Evaluate(JSContext* cx, const ReadOnlyCo
+@@ -3800,31 +3800,31 @@ Evaluate(JSContext* cx, const ReadOnlyCo
  
  
  /**
  /**
   * Evaluate the given file in the scope of the current global of cx.
   * Evaluate the given file in the scope of the current global of cx.
@@ -372,7 +372,7 @@ diff --git a/js/src/jsapi.h b/js/src/jsapi.h
  GetModuleMetadataHook(JSRuntime* rt);
  GetModuleMetadataHook(JSRuntime* rt);
  
  
  /**
  /**
-@@ -3899,27 +3899,40 @@ SetModuleMetadataHook(JSRuntime* rt, Mod
+@@ -3838,27 +3838,40 @@ SetModuleMetadataHook(JSRuntime* rt, Mod
   * Parse the given source buffer as a module in the scope of the current global
   * Parse the given source buffer as a module in the scope of the current global
   * of cx and return a source text module record.
   * of cx and return a source text module record.
   */
   */
@@ -567,7 +567,7 @@ diff --git a/js/src/shell/ModuleLoader.js b/js/src/shell/ModuleLoader.js
 diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 --- a/js/src/shell/js.cpp
 --- a/js/src/shell/js.cpp
 +++ b/js/src/shell/js.cpp
 +++ b/js/src/shell/js.cpp
-@@ -4284,28 +4284,28 @@ SetModuleResolveHook(JSContext* cx, unsi
+@@ -4300,28 +4300,28 @@ SetModuleResolveHook(JSContext* cx, unsi
      Handle<GlobalObject*> global = cx->global();
      Handle<GlobalObject*> global = cx->global();
      global->setReservedSlot(GlobalAppSlotModuleResolveHook, args[0]);
      global->setReservedSlot(GlobalAppSlotModuleResolveHook, args[0]);
  
  
@@ -598,7 +598,7 @@ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
  
  
      if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
      if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
           JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
           JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
-@@ -4334,35 +4334,82 @@ SetModuleMetadataHook(JSContext* cx, uns
+@@ -4350,35 +4350,82 @@ SetModuleMetadataHook(JSContext* cx, uns
      Handle<GlobalObject*> global = cx->global();
      Handle<GlobalObject*> global = cx->global();
      global->setReservedSlot(GlobalAppSlotModuleMetadataHook, args[0]);
      global->setReservedSlot(GlobalAppSlotModuleMetadataHook, args[0]);
  
  
@@ -683,7 +683,7 @@ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
      if (sc->moduleLoadPath) {
      if (sc->moduleLoadPath) {
          JSString* str = JS_NewStringCopyZ(cx, sc->moduleLoadPath.get());
          JSString* str = JS_NewStringCopyZ(cx, sc->moduleLoadPath.get());
          if (!str)
          if (!str)
-@@ -6849,16 +6896,24 @@ static const JSFunctionSpecWithHelp shel
+@@ -6870,16 +6917,24 @@ static const JSFunctionSpecWithHelp shel
  "  be implemented by the module loader."),
  "  be implemented by the module loader."),
  
  
      JS_FN_HELP("setModuleMetadataHook", SetModuleMetadataHook, 1, 0,
      JS_FN_HELP("setModuleMetadataHook", SetModuleMetadataHook, 1, 0,
@@ -711,12 +711,12 @@ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h
 diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h
 --- a/js/src/vm/JSScript.h
 --- a/js/src/vm/JSScript.h
 +++ b/js/src/vm/JSScript.h
 +++ b/js/src/vm/JSScript.h
-@@ -746,22 +746,32 @@ class ScriptSourceObject : public Native
-     }
+@@ -750,22 +750,32 @@ class ScriptSourceObject : public Native
      JSScript* introductionScript() const {
      JSScript* introductionScript() const {
          Value value = getReservedSlot(INTRODUCTION_SCRIPT_SLOT);
          Value value = getReservedSlot(INTRODUCTION_SCRIPT_SLOT);
-         if (value.isUndefined())
+         if (value.isUndefined()) {
              return nullptr;
              return nullptr;
+         }
          return value.toGCThing()->as<JSScript>();
          return value.toGCThing()->as<JSScript>();
      }
      }
  
  
@@ -752,7 +752,7 @@ diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h
 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 --- a/js/src/vm/SelfHosting.cpp
 --- a/js/src/vm/SelfHosting.cpp
 +++ b/js/src/vm/SelfHosting.cpp
 +++ b/js/src/vm/SelfHosting.cpp
-@@ -2158,19 +2158,21 @@ intrinsic_HostResolveImportedModule(JSCo
+@@ -2160,19 +2160,21 @@ intrinsic_HostResolveImportedModule(JSCo
  
  
      JS::ModuleResolveHook moduleResolveHook = cx->runtime()->moduleResolveHook;
      JS::ModuleResolveHook moduleResolveHook = cx->runtime()->moduleResolveHook;
      if (!moduleResolveHook) {
      if (!moduleResolveHook) {

+ 553 - 0
frg/work-js/mozilla-release/patches/1482931-1-63a1.patch

@@ -0,0 +1,553 @@
+# HG changeset patch
+# User Ted Campbell <tcampbell@mozilla.com>
+# Date 1534169521 14400
+# Node ID 490b0d605859e612832f6b655d4168e39a30ceb1
+# Parent  3b443d9bbb69ee797677759278e3c0b6a8178a99
+Bug 1482931 - Cleanup const-ness of statics in js/src. r=waldo
+
+Mark read-only global data as const. Replacing |const char* x[]| with
+|const char* const x[]|. Replacing |static const char* x = "..."| with
+|static const char x[] = "..."|.
+
+MozReview-Commit-ID: Ftv7ziIAWGh
+
+diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp
+--- a/js/src/gc/GC.cpp
++++ b/js/src/gc/GC.cpp
+@@ -1037,17 +1037,17 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
+ void
+ GCRuntime::getZealBits(uint32_t* zealBits, uint32_t* frequency, uint32_t* scheduled)
+ {
+     *zealBits = zealModeBits;
+     *frequency = zealFrequency;
+     *scheduled = nextScheduled;
+ }
+ 
+-const char* gc::ZealModeHelpText =
++const char gc::ZealModeHelpText[] =
+     "  Specifies how zealous the garbage collector should be. Some of these modes can\n"
+     "  be set simultaneously, by passing multiple level options, e.g. \"2;4\" will activate\n"
+     "  both modes 2 and 4. Modes can be specified by name or number.\n"
+     "  \n"
+     "  Values:\n"
+     "    0:  (None) Normal amount of collection (resets all modes)\n"
+     "    1:  (RootsChange) Collect when roots are added or removed\n"
+     "    2:  (Alloc) Collect when every N allocations (default: 100)\n"
+@@ -1245,17 +1245,17 @@ GCRuntime::parseAndSetZeal(const char* s
+     }
+ 
+     return true;
+ }
+ 
+ static const char*
+ AllocKindName(AllocKind kind)
+ {
+-    static const char* names[] = {
++    static const char* const names[] = {
+ #define EXPAND_THING_NAME(allocKind, _1, _2, _3, _4, _5) \
+         #allocKind,
+ FOR_EACH_ALLOCKIND(EXPAND_THING_NAME)
+ #undef EXPAND_THING_NAME
+     };
+     static_assert(ArrayLength(names) == size_t(AllocKind::LIMIT),
+                   "names array should have an entry for every AllocKind");
+ 
+diff --git a/js/src/gc/GC.h b/js/src/gc/GC.h
+--- a/js/src/gc/GC.h
++++ b/js/src/gc/GC.h
+@@ -135,17 +135,17 @@ void
+ MergeRealms(JS::Realm* source, JS::Realm* target);
+ 
+ enum VerifierType {
+     PreBarrierVerifier
+ };
+ 
+ #ifdef JS_GC_ZEAL
+ 
+-extern const char* ZealModeHelpText;
++extern const char ZealModeHelpText[];
+ 
+ /* Check that write barriers have been used correctly. See gc/Verifier.cpp. */
+ void
+ VerifyBarriers(JSRuntime* rt, VerifierType type);
+ 
+ void
+ MaybeVerifyBarriers(JSContext* cx, bool always = false);
+ 
+diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp
+--- a/js/src/gc/Nursery.cpp
++++ b/js/src/gc/Nursery.cpp
+@@ -611,17 +611,17 @@ js::Nursery::renderProfileJSON(JSONPrint
+     if (previousGC.nurseryLazyCapacity != previousGC.nurseryCapacity)
+         json.property("lazy_capacity", previousGC.nurseryLazyCapacity);
+     if (!timeInChunkAlloc_.IsZero())
+         json.property("chunk_alloc_us", timeInChunkAlloc_, json.MICROSECONDS);
+ 
+     json.beginObjectProperty("phase_times");
+ 
+ #define EXTRACT_NAME(name, text) #name,
+-    static const char* names[] = {
++    static const char* const names[] = {
+ FOR_EACH_NURSERY_PROFILE_TIME(EXTRACT_NAME)
+ #undef EXTRACT_NAME
+     "" };
+ 
+     size_t i = 0;
+     for (auto time : profileDurations_)
+         json.property(names[i++], time, json.MICROSECONDS);
+ 
+diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp
+--- a/js/src/jit/CacheIR.cpp
++++ b/js/src/jit/CacheIR.cpp
+@@ -21,17 +21,17 @@
+ #include "vm/UnboxedObject-inl.h"
+ 
+ using namespace js;
+ using namespace js::jit;
+ 
+ using mozilla::DebugOnly;
+ using mozilla::Maybe;
+ 
+-const char* js::jit::CacheKindNames[] = {
++const char* const js::jit::CacheKindNames[] = {
+ #define DEFINE_KIND(kind) #kind,
+     CACHE_IR_KINDS(DEFINE_KIND)
+ #undef DEFINE_KIND
+ };
+ 
+ void
+ CacheIRWriter::assertSameCompartment(JSObject* obj) {
+     assertSameCompartmentDebugOnly(cx_, obj);
+diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h
+--- a/js/src/jit/CacheIR.h
++++ b/js/src/jit/CacheIR.h
+@@ -170,17 +170,17 @@ class TypedOperandId : public OperandId
+ 
+ enum class CacheKind : uint8_t
+ {
+ #define DEFINE_KIND(kind) kind,
+     CACHE_IR_KINDS(DEFINE_KIND)
+ #undef DEFINE_KIND
+ };
+ 
+-extern const char* CacheKindNames[];
++extern const char* const CacheKindNames[];
+ 
+ #define CACHE_IR_OPS(_)                   \
+     _(GuardIsObject)                      \
+     _(GuardIsObjectOrNull)                \
+     _(GuardIsNullOrUndefined)             \
+     _(GuardIsString)                      \
+     _(GuardIsSymbol)                      \
+     _(GuardIsNumber)                      \
+diff --git a/js/src/jit/IonOptimizationLevels.cpp b/js/src/jit/IonOptimizationLevels.cpp
+--- a/js/src/jit/IonOptimizationLevels.cpp
++++ b/js/src/jit/IonOptimizationLevels.cpp
+@@ -10,17 +10,17 @@
+ #include "vm/JSScript.h"
+ 
+ using namespace js;
+ using namespace js::jit;
+ 
+ namespace js {
+ namespace jit {
+ 
+-OptimizationLevelInfo IonOptimizations;
++const OptimizationLevelInfo IonOptimizations;
+ 
+ const uint32_t OptimizationInfo::CompilerWarmupThreshold = 1000;
+ const uint32_t OptimizationInfo::CompilerSmallFunctionWarmupThreshold = CompilerWarmupThreshold;
+ 
+ void
+ OptimizationInfo::initNormalOptimizationInfo()
+ {
+     level_ = OptimizationLevel::Normal;
+diff --git a/js/src/jit/IonOptimizationLevels.h b/js/src/jit/IonOptimizationLevels.h
+--- a/js/src/jit/IonOptimizationLevels.h
++++ b/js/src/jit/IonOptimizationLevels.h
+@@ -303,14 +303,14 @@ class OptimizationLevelInfo
+     }
+ 
+     OptimizationLevel nextLevel(OptimizationLevel level) const;
+     OptimizationLevel firstLevel() const;
+     bool isLastLevel(OptimizationLevel level) const;
+     OptimizationLevel levelForScript(JSScript* script, jsbytecode* pc = nullptr) const;
+ };
+ 
+-extern OptimizationLevelInfo IonOptimizations;
++extern const OptimizationLevelInfo IonOptimizations;
+ 
+ } // namespace jit
+ } // namespace js
+ 
+ #endif /* jit_IonOptimizationLevels_h */
+diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp
+--- a/js/src/jit/JitSpewer.cpp
++++ b/js/src/jit/JitSpewer.cpp
+@@ -98,17 +98,17 @@ static size_t ChannelIndentLevel[] =
+ #define JITSPEW_CHANNEL(name) 0,
+     JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
+ #undef JITSPEW_CHANNEL
+ };
+ 
+ static bool
+ FilterContainsLocation(JSScript* function)
+ {
+-    static const char* filter = getenv("IONFILTER");
++    static const char* const filter = getenv("IONFILTER");
+ 
+     // If there is no filter we accept all outputs.
+     if (!filter || !filter[0])
+         return true;
+ 
+     // Disable wasm output when filter is set.
+     if (!function)
+         return false;
+diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp
+--- a/js/src/jit/ScalarReplacement.cpp
++++ b/js/src/jit/ScalarReplacement.cpp
+@@ -307,17 +307,17 @@ IsObjectEscaped(MInstruction* ins, JSObj
+     JitSpew(JitSpew_Escape, "Object is not escaped");
+     return false;
+ }
+ 
+ class ObjectMemoryView : public MDefinitionVisitorDefaultNoop
+ {
+   public:
+     typedef MObjectState BlockState;
+-    static const char* phaseName;
++    static const char phaseName[];
+ 
+   private:
+     TempAllocator& alloc_;
+     MConstant* undefinedVal_;
+     MInstruction* obj_;
+     MBasicBlock* startBlock_;
+     BlockState* state_;
+ 
+@@ -365,17 +365,18 @@ class ObjectMemoryView : public MDefinit
+     void visitLoadUnboxedString(MLoadUnboxedString* ins);
+ 
+   private:
+     void storeOffset(MInstruction* ins, size_t offset, MDefinition* value);
+     void loadOffset(MInstruction* ins, size_t offset);
+     void visitObjectGuard(MInstruction* ins, MDefinition* operand);
+ };
+ 
+-const char* ObjectMemoryView::phaseName = "Scalar Replacement of Object";
++/* static */ const char
++ObjectMemoryView::phaseName[] = "Scalar Replacement of Object";
+ 
+ ObjectMemoryView::ObjectMemoryView(TempAllocator& alloc, MInstruction* obj)
+   : alloc_(alloc),
+     undefinedVal_(nullptr),
+     obj_(obj),
+     startBlock_(obj->block()),
+     state_(nullptr),
+     lastResumePoint_(nullptr),
+diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
+--- a/js/src/jsnum.cpp
++++ b/js/src/jsnum.cpp
+@@ -1240,17 +1240,17 @@ js::InitNumberClass(JSContext* cx, Handl
+ 
+     if (!LinkConstructorAndPrototype(cx, ctor, numberProto))
+         return nullptr;
+ 
+     /*
+      * Our NaN must be one particular canonical value, because we rely on NaN
+      * encoding for our value representation.  See Value.h.
+      */
+-    static JSConstDoubleSpec number_constants[] = {
++    static const JSConstDoubleSpec number_constants[] = {
+         {"NaN",               GenericNaN()               },
+         {"POSITIVE_INFINITY", mozilla::PositiveInfinity<double>() },
+         {"NEGATIVE_INFINITY", mozilla::NegativeInfinity<double>() },
+         {"MAX_VALUE",         1.7976931348623157E+308    },
+         {"MIN_VALUE",         MinNumberValue<double>()   },
+         /* ES6 (April 2014 draft) 20.1.2.6 */
+         {"MAX_SAFE_INTEGER",  9007199254740991           },
+         /* ES6 (April 2014 draft) 20.1.2.10 */
+diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp
+--- a/js/src/proxy/ScriptedProxyHandler.cpp
++++ b/js/src/proxy/ScriptedProxyHandler.cpp
+@@ -30,17 +30,17 @@ IsCompatiblePropertyDescriptor(JSContext
+ {
+     // precondition:  we won't set details if checks pass, so it must be null here.
+     MOZ_ASSERT(*errorDetails == nullptr);
+ 
+     // Step 2.
+     if (!current.object()) {
+         // Step 2a-b,e.  As |O| is always undefined, steps 2c-d fall away.
+         if (!extensible) {
+-            static const char* DETAILS_NOT_EXTENSIBLE =
++            static const char DETAILS_NOT_EXTENSIBLE[] =
+                 "proxy can't report an extensible object as non-extensible";
+             *errorDetails = DETAILS_NOT_EXTENSIBLE;
+         }
+         return true;
+     }
+ 
+     // Step 3.
+     if (!desc.hasValue() && !desc.hasWritable() &&
+@@ -68,63 +68,63 @@ IsCompatiblePropertyDescriptor(JSContext
+         if (same)
+             return true;
+     }
+ 
+     // Step 5.
+     if (!current.configurable()) {
+         // Step 5a.
+         if (desc.hasConfigurable() && desc.configurable()) {
+-            static const char* DETAILS_CANT_REPORT_NC_AS_C =
++            static const char DETAILS_CANT_REPORT_NC_AS_C[] =
+                 "proxy can't report an existing non-configurable property as configurable";
+             *errorDetails = DETAILS_CANT_REPORT_NC_AS_C;
+             return true;
+         }
+ 
+         // Step 5b.
+         if (desc.hasEnumerable() && desc.enumerable() != current.enumerable()) {
+-            static const char* DETAILS_ENUM_DIFFERENT =
++            static const char DETAILS_ENUM_DIFFERENT[] =
+                 "proxy can't report a different 'enumerable' from target when target is not configurable";
+             *errorDetails = DETAILS_ENUM_DIFFERENT;
+             return true;
+         }
+     }
+ 
+     // Step 6.
+     if (desc.isGenericDescriptor())
+         return true;
+ 
+     // Step 7.
+     if (current.isDataDescriptor() != desc.isDataDescriptor()) {
+         // Steps 7a, 11.  As |O| is always undefined, steps 2b-c fall away.
+         if (!current.configurable()) {
+-            static const char* DETAILS_CURRENT_NC_DIFF_TYPE =
++            static const char DETAILS_CURRENT_NC_DIFF_TYPE[] =
+                 "proxy can't report a different descriptor type when target is not configurable";
+             *errorDetails = DETAILS_CURRENT_NC_DIFF_TYPE;
+         }
+         return true;
+     }
+ 
+     // Step 8.
+     if (current.isDataDescriptor()) {
+         MOZ_ASSERT(desc.isDataDescriptor()); // by step 7
+         if (!current.configurable() && !current.writable()) {
+             if (desc.hasWritable() && desc.writable()) {
+-                static const char* DETAILS_CANT_REPORT_NW_AS_W =
++                static const char DETAILS_CANT_REPORT_NW_AS_W[] =
+                     "proxy can't report a non-configurable, non-writable property as writable";
+                 *errorDetails = DETAILS_CANT_REPORT_NW_AS_W;
+                 return true;
+             }
+ 
+             if (desc.hasValue()) {
+                 bool same;
+                 if (!SameValue(cx, desc.value(), current.value(), &same))
+                     return false;
+                 if (!same) {
+-                    static const char* DETAILS_DIFFERENT_VALUE =
++                    static const char DETAILS_DIFFERENT_VALUE[] =
+                         "proxy must report the same value for the non-writable, non-configurable property";
+                     *errorDetails = DETAILS_DIFFERENT_VALUE;
+                     return true;
+                 }
+             }
+         }
+ 
+         return true;
+@@ -132,22 +132,22 @@ IsCompatiblePropertyDescriptor(JSContext
+ 
+     // Step 9.
+     MOZ_ASSERT(current.isAccessorDescriptor()); // by step 8
+     MOZ_ASSERT(desc.isAccessorDescriptor()); // by step 7
+ 
+     if (current.configurable())
+         return true;
+     if (desc.hasSetterObject() && (desc.setter() != current.setter())) {
+-        static const char* DETAILS_SETTERS_DIFFERENT =
++        static const char DETAILS_SETTERS_DIFFERENT[] =
+             "proxy can't report different setters for a currently non-configurable property";
+         *errorDetails = DETAILS_SETTERS_DIFFERENT;
+     }
+     else if (desc.hasGetterObject() && (desc.getter() != current.getter())) {
+-        static const char* DETAILS_GETTERS_DIFFERENT =
++        static const char DETAILS_GETTERS_DIFFERENT[] =
+             "proxy can't report different getters for a currently non-configurable property";
+         *errorDetails = DETAILS_GETTERS_DIFFERENT;
+     }
+     return true;
+ }
+ 
+ // Get the [[ProxyHandler]] of a scripted proxy.
+ /* static */ JSObject*
+@@ -651,17 +651,17 @@ ScriptedProxyHandler::defineProperty(JSC
+                                             &errorDetails))
+             return false;
+ 
+         if (errorDetails)
+             return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, errorDetails);
+ 
+         // Step 16b.
+         if (settingConfigFalse && targetDesc.configurable()) {
+-            static const char* DETAILS_CANT_REPORT_C_AS_NC =
++            static const char DETAILS_CANT_REPORT_C_AS_NC[] =
+                 "proxy can't define an existing configurable property as non-configurable";
+             return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, DETAILS_CANT_REPORT_C_AS_NC);
+         }
+     }
+ 
+     // Step 17.
+     return result.succeed();
+ }
+diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp
+--- a/js/src/vm/ArrayBufferObject.cpp
++++ b/js/src/vm/ArrayBufferObject.cpp
+@@ -1489,17 +1489,17 @@ ArrayBufferObject::addView(JSContext* cx
+     }
+     return ObjectRealm::get(this).innerViews.get().addView(cx, this, view);
+ }
+ 
+ /*
+  * InnerViewTable
+  */
+ 
+-static size_t VIEW_LIST_MAX_LENGTH = 500;
++constexpr size_t VIEW_LIST_MAX_LENGTH = 500;
+ 
+ bool
+ InnerViewTable::addView(JSContext* cx, ArrayBufferObject* buffer, ArrayBufferViewObject* view)
+ {
+     // ArrayBufferObject entries are only added when there are multiple views.
+     MOZ_ASSERT(buffer->firstView());
+ 
+     if (!map.initialized() && !map.init()) {
+diff --git a/js/src/vm/BytecodeUtil.cpp b/js/src/vm/BytecodeUtil.cpp
+--- a/js/src/vm/BytecodeUtil.cpp
++++ b/js/src/vm/BytecodeUtil.cpp
+@@ -71,27 +71,27 @@ const JSCodeSpec js::CodeSpec[] = {
+ };
+ 
+ const unsigned js::NumCodeSpecs = mozilla::ArrayLength(CodeSpec);
+ 
+ /*
+  * Each element of the array is either a source literal associated with JS
+  * bytecode or null.
+  */
+-static const char * const CodeToken[] = {
++static const char* const CodeToken[] = {
+ #define TOKEN(op, val, name, token, ...)  token,
+     FOR_EACH_OPCODE(TOKEN)
+ #undef TOKEN
+ };
+ 
+ /*
+  * Array of JS bytecode names used by PC count JSON, DEBUG-only Disassemble
+  * and JIT debug spew.
+  */
+-const char * const js::CodeName[] = {
++const char* const js::CodeName[] = {
+ #define OPNAME(op, val, name, ...)  name,
+     FOR_EACH_OPCODE(OPNAME)
+ #undef OPNAME
+ };
+ 
+ /************************************************************************/
+ 
+ static bool
+@@ -112,17 +112,18 @@ js::GetVariableBytecodeLength(jsbytecode
+         unsigned ncases = unsigned(high - low + 1);
+         return 1 + 3 * JUMP_OFFSET_LEN + ncases * JUMP_OFFSET_LEN;
+       }
+       default:
+         MOZ_CRASH("Unexpected op");
+     }
+ }
+ 
+-const char * PCCounts::numExecName = "interp";
++/* static */ const char
++PCCounts::numExecName[] = "interp";
+ 
+ static MOZ_MUST_USE bool
+ DumpIonScriptCounts(Sprinter* sp, HandleScript script, jit::IonScriptCounts* ionCounts)
+ {
+     if (!sp->jsprintf("IonScript [%zu blocks]:\n", ionCounts->numBlocks()))
+         return false;
+ 
+     for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
+diff --git a/js/src/vm/BytecodeUtil.h b/js/src/vm/BytecodeUtil.h
+--- a/js/src/vm/BytecodeUtil.h
++++ b/js/src/vm/BytecodeUtil.h
+@@ -822,17 +822,17 @@ class PCCounts
+ 
+     uint64_t& numExec() {
+         return numExec_;
+     }
+     uint64_t numExec() const {
+         return numExec_;
+     }
+ 
+-    static const char* numExecName;
++    static const char numExecName[];
+ };
+ 
+ static inline jsbytecode*
+ GetNextPc(jsbytecode* pc)
+ {
+     return pc + GetBytecodeLength(pc);
+ }
+ 
+diff --git a/js/src/vm/TraceLoggingGraph.cpp b/js/src/vm/TraceLoggingGraph.cpp
+--- a/js/src/vm/TraceLoggingGraph.cpp
++++ b/js/src/vm/TraceLoggingGraph.cpp
+@@ -51,17 +51,17 @@ TraceLoggerGraphState* traceLoggerGraphS
+ // Return a filename relative to the output directory. %u and %d substitutions
+ // are allowed, with %u standing for a full 32-bit number and %d standing for
+ // an up to 3-digit number.
+ static js::UniqueChars
+ MOZ_FORMAT_PRINTF(1, 2)
+ AllocTraceLogFilename(const char* pattern, ...) {
+     va_list ap;
+ 
+-    static const char* outdir = getenv("TLDIR") ? getenv("TLDIR") : DEFAULT_TRACE_LOG_DIR;
++    static const char* const outdir = getenv("TLDIR") ? getenv("TLDIR") : DEFAULT_TRACE_LOG_DIR;
+     size_t len = strlen(outdir) + 1; // "+ 1" is for the '/'
+ 
+     for (const char* p = pattern; *p; p++) {
+         if (*p == '%') {
+             p++;
+             if (*p == 'u')
+                 len += sizeof("4294967295") - 1;
+             else if (*p == 'd')
+diff --git a/js/src/wasm/WasmFrameIter.cpp b/js/src/wasm/WasmFrameIter.cpp
+--- a/js/src/wasm/WasmFrameIter.cpp
++++ b/js/src/wasm/WasmFrameIter.cpp
+@@ -1284,21 +1284,21 @@ ThunkedNativeToDescription(SymbolicAddre
+ const char*
+ ProfilingFrameIterator::label() const
+ {
+     MOZ_ASSERT(!done());
+ 
+     // Use the same string for both time inside and under so that the two
+     // entries will be coalesced by the profiler.
+     // Must be kept in sync with /tools/profiler/tests/test_asm.js
+-    static const char* importJitDescription = "fast exit trampoline (in wasm)";
+-    static const char* importInterpDescription = "slow exit trampoline (in wasm)";
+-    static const char* builtinNativeDescription = "fast exit trampoline to native (in wasm)";
+-    static const char* trapDescription = "trap handling (in wasm)";
+-    static const char* debugTrapDescription = "debug trap handling (in wasm)";
++    static const char importJitDescription[] = "fast exit trampoline (in wasm)";
++    static const char importInterpDescription[] = "slow exit trampoline (in wasm)";
++    static const char builtinNativeDescription[] = "fast exit trampoline to native (in wasm)";
++    static const char trapDescription[] = "trap handling (in wasm)";
++    static const char debugTrapDescription[] = "debug trap handling (in wasm)";
+ 
+     if (!exitReason_.isFixed())
+         return ThunkedNativeToDescription(exitReason_.symbolic());
+ 
+     switch (exitReason_.fixed()) {
+       case ExitReason::Fixed::None:
+         break;
+       case ExitReason::Fixed::ImportJit:

+ 208 - 0
frg/work-js/mozilla-release/patches/1482931-2-63a1.patch

@@ -0,0 +1,208 @@
+# HG changeset patch
+# User Ted Campbell <tcampbell@mozilla.com>
+# Date 1534172604 14400
+# Node ID a77d82cbe1cc5af755402e6c853c4ab36522f1e8
+# Parent  402fbf0289dcfd0cfd977b1315f70ca39a381558
+Bug 1482931 - Simplify some static initializers in js/src. r=jandem
+
+MozReview-Commit-ID: JLNqziLmGrx
+
+diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp
+--- a/js/src/jit/JitSpewer.cpp
++++ b/js/src/jit/JitSpewer.cpp
+@@ -81,49 +81,51 @@ class IonSpewer
+ // IonSpewer singleton.
+ static IonSpewer ionspewer;
+ 
+ static bool LoggingChecked = false;
+ static_assert(JitSpew_Terminator <= 64, "Increase the size of the LoggingBits global.");
+ static uint64_t LoggingBits = 0;
+ static mozilla::Atomic<uint32_t, mozilla::Relaxed> filteredOutCompilations(0);
+ 
+-static const char * const ChannelNames[] =
++static const char* const ChannelNames[] =
+ {
+ #define JITSPEW_CHANNEL(name) #name,
+     JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
+ #undef JITSPEW_CHANNEL
+ };
+ 
+ static size_t ChannelIndentLevel[] =
+ {
+ #define JITSPEW_CHANNEL(name) 0,
+     JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
+ #undef JITSPEW_CHANNEL
+ };
+ 
++// The IONFILTER environment variable specifies an expression to select only
++// certain functions for spewing to reduce amount of log data generated.
++static const char* gSpewFilter = nullptr;
++
+ static bool
+ FilterContainsLocation(JSScript* function)
+ {
+-    static const char* const filter = getenv("IONFILTER");
+-
+     // If there is no filter we accept all outputs.
+-    if (!filter || !filter[0])
++    if (!gSpewFilter || !gSpewFilter[0])
+         return true;
+ 
+     // Disable wasm output when filter is set.
+     if (!function)
+         return false;
+ 
+     const char* filename = function->filename();
+     const size_t line = function->lineno();
+     const size_t filelen = strlen(filename);
+-    const char* index = strstr(filter, filename);
++    const char* index = strstr(gSpewFilter, filename);
+     while (index) {
+-        if (index == filter || index[-1] == ',') {
++        if (index == gSpewFilter || index[-1] == ',') {
+             if (index[filelen] == 0 || index[filelen] == ',')
+                 return true;
+             if (index[filelen] == ':' && line != size_t(-1)) {
+                 size_t read_line = strtoul(&index[filelen + 1], nullptr, 10);
+                 if (read_line == line)
+                     return true;
+             }
+         }
+@@ -158,16 +160,19 @@ IonSpewer::release()
+ }
+ 
+ bool
+ IonSpewer::init()
+ {
+     if (inited_)
+         return true;
+ 
++    // Filter expression for spewing
++    gSpewFilter = getenv("IONFILTER");
++
+     const size_t bufferLength = 256;
+     char c1Buffer[bufferLength];
+     char jsonBuffer[bufferLength];
+     const char *c1Filename = JIT_SPEW_DIR "/ion.cfg";
+     const char *jsonFilename = JIT_SPEW_DIR "/ion.json";
+ 
+     const char* usePid = getenv("ION_SPEW_BY_PID");
+     if (usePid && *usePid != 0) {
+diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp
+--- a/js/src/jsfriendapi.cpp
++++ b/js/src/jsfriendapi.cpp
+@@ -1141,18 +1141,17 @@ JS::ForceLexicalInitialization(JSContext
+     }
+     return initializedAny;
+ }
+ 
+ extern JS_FRIEND_API(int)
+ JS::IsGCPoisoning()
+ {
+ #ifdef JS_GC_POISONING
+-    static bool disablePoison = bool(getenv("JSGC_DISABLE_POISONING"));
+-    return !disablePoison;
++    return !js::gDisablePoisoning;
+ #else
+     return false;
+ #endif
+ }
+ 
+ struct DumpHeapTracer : public JS::CallbackTracer, public WeakMapTracer
+ {
+     const char* prefix;
+diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp
+--- a/js/src/jsutil.cpp
++++ b/js/src/jsutil.cpp
+@@ -159,16 +159,18 @@ ResetSimulatedInterrupt()
+     maxInterruptChecks = UINT64_MAX;
+     interruptCheckFailAlways = false;
+ }
+ 
+ } // namespace oom
+ } // namespace js
+ #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+ 
++bool js::gDisablePoisoning = false;
++
+ JS_PUBLIC_DATA(arena_id_t) js::MallocArena;
+ JS_PUBLIC_DATA(arena_id_t) js::ArrayBufferContentsArena;
+ 
+ void
+ js::InitMallocAllocator()
+ {
+     MallocArena = moz_create_arena();
+     ArrayBufferContentsArena = moz_create_arena();
+diff --git a/js/src/jsutil.h b/js/src/jsutil.h
+--- a/js/src/jsutil.h
++++ b/js/src/jsutil.h
+@@ -338,21 +338,23 @@ AlwaysPoison(void* ptr, uint8_t value, s
+     }
+ #else // !DEBUG
+     memset(ptr, value, num);
+ #endif // !DEBUG
+ 
+     SetMemCheckKind(ptr, num, kind);
+ }
+ 
++// JSGC_DISABLE_POISONING environment variable
++extern bool gDisablePoisoning;
++
+ static inline void
+ Poison(void* ptr, uint8_t value, size_t num, MemCheckKind kind)
+ {
+-    static bool disablePoison = bool(getenv("JSGC_DISABLE_POISONING"));
+-    if (!disablePoison)
++    if (!js::gDisablePoisoning)
+         AlwaysPoison(ptr, value, num, kind);
+ }
+ 
+ } // namespace js
+ 
+ /* Crash diagnostics by default in debug and on nightly channel. */
+ #if defined(DEBUG) || defined(NIGHTLY_BUILD)
+ # define JS_CRASH_DIAGNOSTICS 1
+diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp
+--- a/js/src/vm/Initialization.cpp
++++ b/js/src/vm/Initialization.cpp
+@@ -99,16 +99,18 @@ JS::detail::InitWithFailureDiagnostic(bo
+ #endif
+ 
+     RETURN_IF_FAIL(js::TlsContext.init());
+ 
+ #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+     RETURN_IF_FAIL(js::oom::InitThreadType());
+ #endif
+ 
++    js::gDisablePoisoning = bool(getenv("JSGC_DISABLE_POISONING"));
++
+     js::InitMallocAllocator();
+ 
+     RETURN_IF_FAIL(js::Mutex::Init());
+ 
+     js::gc::InitMemorySubsystem(); // Ensure gc::SystemPageSize() works.
+ 
+     RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory());
+ 
+diff --git a/js/src/vm/TraceLoggingGraph.cpp b/js/src/vm/TraceLoggingGraph.cpp
+--- a/js/src/vm/TraceLoggingGraph.cpp
++++ b/js/src/vm/TraceLoggingGraph.cpp
+@@ -51,17 +51,17 @@ TraceLoggerGraphState* traceLoggerGraphS
+ // Return a filename relative to the output directory. %u and %d substitutions
+ // are allowed, with %u standing for a full 32-bit number and %d standing for
+ // an up to 3-digit number.
+ static js::UniqueChars
+ MOZ_FORMAT_PRINTF(1, 2)
+ AllocTraceLogFilename(const char* pattern, ...) {
+     va_list ap;
+ 
+-    static const char* const outdir = getenv("TLDIR") ? getenv("TLDIR") : DEFAULT_TRACE_LOG_DIR;
++    const char* outdir = getenv("TLDIR") ? getenv("TLDIR") : DEFAULT_TRACE_LOG_DIR;
+     size_t len = strlen(outdir) + 1; // "+ 1" is for the '/'
+ 
+     for (const char* p = pattern; *p; p++) {
+         if (*p == '%') {
+             p++;
+             if (*p == 'u')
+                 len += sizeof("4294967295") - 1;
+             else if (*p == 'd')

+ 187 - 0
frg/work-js/mozilla-release/patches/1483374-63a1.patch

@@ -0,0 +1,187 @@
+# HG changeset patch
+# User Jeff Walden <jwalden@mit.edu>
+# Date 1534453062 25200
+# Node ID e0e6b77fb800d5b81c5b68af3dde3c4beee68d7a
+# Parent  30e8eebe3b8ad87e259dcb99457dde6fbd5c13de
+Bug 1483374 - Intl.NumberFormat shouldn't deliberately conflate -0 with +0.  r=anba
+
+diff --git a/js/src/builtin/intl/NumberFormat.cpp b/js/src/builtin/intl/NumberFormat.cpp
+--- a/js/src/builtin/intl/NumberFormat.cpp
++++ b/js/src/builtin/intl/NumberFormat.cpp
+@@ -28,17 +28,17 @@
+ 
+ #include "vm/JSObject-inl.h"
+ 
+ using namespace js;
+ 
+ using mozilla::AssertedCast;
+ using mozilla::IsFinite;
+ using mozilla::IsNaN;
+-using mozilla::IsNegativeZero;
++using mozilla::IsNegative;
+ 
+ using js::intl::CallICU;
+ using js::intl::DateTimeFormatOptions;
+ using js::intl::GetAvailableLocales;
+ using js::intl::IcuLocale;
+ 
+ const ClassOps NumberFormatObject::classOps_ = {
+     nullptr, /* addProperty */
+@@ -368,34 +368,30 @@ NewUNumberFormat(JSContext* cx, Handle<N
+     }
+     unum_setAttribute(nf, UNUM_GROUPING_USED, uUseGrouping);
+     unum_setAttribute(nf, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
+ 
+     return toClose.forget();
+ }
+ 
+ static JSString*
+-PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double* x,
++PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double x,
+                        UFieldPositionIterator* fpositer)
+ {
+-    // PartitionNumberPattern doesn't consider -0.0 to be negative.
+-    if (IsNegativeZero(*x))
+-        *x = 0.0;
+-
+     return CallICU(cx, [nf, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
+-        return unum_formatDoubleForFields(nf, *x, chars, size, fpositer, status);
++        return unum_formatDoubleForFields(nf, x, chars, size, fpositer, status);
+     });
+ }
+ 
+ static bool
+ intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result)
+ {
+     // Passing null for |fpositer| will just not compute partition information,
+     // letting us common up all ICU number-formatting code.
+-    JSString* str = PartitionNumberPattern(cx, nf, &x, nullptr);
++    JSString* str = PartitionNumberPattern(cx, nf, x, nullptr);
+     if (!str)
+         return false;
+ 
+     result.setString(str);
+     return true;
+ }
+ 
+ using FieldType = ImmutablePropertyNamePtr JSAtomState::*;
+@@ -421,23 +417,20 @@ GetFieldTypeForNumberField(UNumberFormat
+ 
+       case UNUM_DECIMAL_SEPARATOR_FIELD:
+         return &JSAtomState::decimal;
+ 
+       case UNUM_FRACTION_FIELD:
+         return &JSAtomState::fraction;
+ 
+       case UNUM_SIGN_FIELD: {
+-        MOZ_ASSERT(!IsNegativeZero(d),
+-                   "-0 should have been excluded by PartitionNumberPattern");
+-
+         // Manual trawling through the ICU call graph appears to indicate that
+         // the basic formatting we request will never include a positive sign.
+         // But this analysis may be mistaken, so don't absolutely trust it.
+-        return d < 0 ? &JSAtomState::minusSign : &JSAtomState::plusSign;
++        return IsNegative(d) ? &JSAtomState::minusSign : &JSAtomState::plusSign;
+       }
+ 
+       case UNUM_PERCENT_FIELD:
+         return &JSAtomState::percentSign;
+ 
+       case UNUM_CURRENCY_FIELD:
+         return &JSAtomState::currency;
+ 
+@@ -477,17 +470,17 @@ intl_FormatNumberToParts(JSContext* cx, 
+     if (U_FAILURE(status)) {
+         intl::ReportInternalError(cx);
+         return false;
+     }
+ 
+     MOZ_ASSERT(fpositer);
+     ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer);
+ 
+-    RootedString overallResult(cx, PartitionNumberPattern(cx, nf, &x, fpositer));
++    RootedString overallResult(cx, PartitionNumberPattern(cx, nf, x, fpositer));
+     if (!overallResult)
+         return false;
+ 
+     RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
+     if (!partsArray)
+         return false;
+ 
+     // First, vacuum up fields in the overall formatted string.
+diff --git a/js/src/tests/jstests.list.1483374.later b/js/src/tests/jstests.list.1483374.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/tests/jstests.list.1483374.later
+@@ -0,0 +1,22 @@
++--- jstests.list
+++++ jstests.list
++@@ -469,19 +469,16 @@ skip script test262/intl402/RelativeTime
++ 
++ # https://bugzilla.mozilla.org/show_bug.cgi?id=1473228
++ skip script test262/intl402/Segmenter/prototype/toStringTag/toString.js
++ skip script test262/intl402/Segmenter/prototype/toStringTag/toStringTag.js
++ 
++ # https://bugzilla.mozilla.org/show_bug.cgi?id=1473229
++ skip include test262/intl402/RelativeTimeFormat/prototype/formatToParts/jstests.list
++ 
++-# https://bugzilla.mozilla.org/show_bug.cgi?id=1483374
++-skip script test262/intl402/NumberFormat/prototype/format/format-negative-numbers.js
++-
++ # https://bugzilla.mozilla.org/show_bug.cgi?id=1483545
++ skip script test262/intl402/RelativeTimeFormat/prototype/format/en-us-numeric-always.js
++ skip script test262/intl402/RelativeTimeFormat/prototype/format/en-us-numeric-auto.js
++ skip script test262/intl402/RelativeTimeFormat/prototype/format/en-us-style-short.js
++ skip script test262/intl402/RelativeTimeFormat/prototype/format/pl-pl-style-long.js
++ skip script test262/intl402/RelativeTimeFormat/prototype/format/pl-pl-style-narrow.js
++ 
++ # https://bugzilla.mozilla.org/show_bug.cgi?id=1473230
+diff --git a/js/src/tests/non262/Intl/NumberFormat/formatToParts.js b/js/src/tests/non262/Intl/NumberFormat/formatToParts.js
+--- a/js/src/tests/non262/Intl/NumberFormat/formatToParts.js
++++ b/js/src/tests/non262/Intl/NumberFormat/formatToParts.js
+@@ -48,16 +48,24 @@ function assertParts(nf, x, expected)
+   {
+     assertEq(parts[i].type, expected[i].type, "type mismatch at " + i);
+     assertEq(parts[i].value, expected[i].value, "value mismatch at " + i);
+   }
+ }
+ 
+ //-----------------------------------------------------------------------------
+ 
++// Test -0's partitioning now that it's not treated like +0.
++// https://github.com/tc39/ecma402/pull/232
++
++var deadSimpleFormatter = new Intl.NumberFormat("en-US");
++
++assertParts(deadSimpleFormatter, -0,
++            [MinusSign("-"), Integer("0")]);
++
+ // Test behavior of a currency with code formatting.
+ var usdCodeOptions =
+   {
+     style: "currency",
+     currency: "USD",
+     currencyDisplay: "code",
+     minimumFractionDigits: 0,
+     maximumFractionDigits: 0,
+@@ -143,18 +151,18 @@ var usdNameFractionOptions =
+     currency: "USD",
+     currencyDisplay: "name",
+     minimumFractionDigits: 2,
+     maximumFractionDigits: 2,
+   };
+ var usdNameFractionFormatter =
+   new Intl.NumberFormat("en-US", usdNameFractionOptions);
+ 
+-// The US national surplus (i.e. debt) as of October 18, 2016.
+-// (Replicating data from a comment in Intl.cpp.)
++// The US national surplus (i.e. debt) as of October 18, 2016.  (Replicating
++// data from a comment in builtin/Intl/NumberFormat.cpp.)
+ var usNationalSurplus = -19766580028249.41;
+ 
+ assertParts(usdNameFractionFormatter, usNationalSurplus,
+             [MinusSign("-"),
+              Integer("19"),
+              Group(","),
+              Integer("766"),
+              Group(","),

+ 681 - 0
frg/work-js/mozilla-release/patches/1484385-63a1.patch

@@ -0,0 +1,681 @@
+# HG changeset patch
+# User Jeff Walden <jwalden@mit.edu>
+# Date 1534776344 25200
+# Node ID fa2218b2f4f17c47f39243914e9987eec1d51d38
+# Parent  6d10eda7f12de64044246e544d581537f30f8998
+Bug 1484385 - Move various error-report-related structures and types into js/public/ErrorReport.h to minimize dependencies (and ultimately to make jsfriendapi.h not depend on jsapi.h).  r=jandem
+
+diff --git a/js/public/ErrorReport.h b/js/public/ErrorReport.h
+new file mode 100644
+--- /dev/null
++++ b/js/public/ErrorReport.h
+@@ -0,0 +1,294 @@
++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* 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/. */
++
++/*
++ * Error-reporting types and structures.
++ *
++ * Despite these types and structures existing in js/public, significant parts
++ * of their heritage date back to distant SpiderMonkey past, and they are not
++ * all universally well-thought-out as ideal, intended-to-be-permanent API.
++ * We may eventually replace this with something more consistent with
++ * ECMAScript the language and less consistent with '90s-era JSAPI inventions,
++ * but it's doubtful this will happen any time soon.
++ */
++
++#ifndef js_ErrorReport_h
++#define js_ErrorReport_h
++
++#include "mozilla/Assertions.h" // MOZ_ASSERT
++
++#include <iterator> // std::input_iterator_tag, std::iterator
++#include <stddef.h> // size_t
++#include <stdint.h> // int16_t, uint16_t
++#include <string.h> // strlen
++
++#include "jstypes.h" // JS_PUBLIC_API
++
++#include "js/AllocPolicy.h" // js::SystemAllocPolicy
++#include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
++#include "js/UniquePtr.h" // js::UniquePtr
++#include "js/Vector.h" // js::Vector
++
++struct JSContext;
++class JSString;
++
++/**
++ * Possible exception types. These types are part of a JSErrorFormatString
++ * structure. They define which error to throw in case of a runtime error.
++ *
++ * JSEXN_WARN is used for warnings in js.msg files (for instance because we
++ * don't want to prepend 'Error:' to warning messages). This value can go away
++ * if we ever decide to use an entirely separate mechanism for warnings.
++ */
++enum JSExnType
++{
++    JSEXN_ERR,
++    JSEXN_FIRST = JSEXN_ERR,
++        JSEXN_INTERNALERR,
++        JSEXN_EVALERR,
++        JSEXN_RANGEERR,
++        JSEXN_REFERENCEERR,
++        JSEXN_SYNTAXERR,
++        JSEXN_TYPEERR,
++        JSEXN_URIERR,
++        JSEXN_DEBUGGEEWOULDRUN,
++        JSEXN_WASMCOMPILEERROR,
++        JSEXN_WASMLINKERROR,
++        JSEXN_WASMRUNTIMEERROR,
++    JSEXN_ERROR_LIMIT,
++    JSEXN_WARN = JSEXN_ERROR_LIMIT,
++    JSEXN_NOTE,
++    JSEXN_LIMIT
++};
++
++struct JSErrorFormatString
++{
++     /** The error message name in ASCII. */
++    const char* name;
++
++    /** The error format string in ASCII. */
++    const char* format;
++
++    /** The number of arguments to expand in the formatted error message. */
++    uint16_t argCount;
++
++    /** One of the JSExnType constants above. */
++    int16_t exnType;
++};
++
++using JSErrorCallback =
++    const JSErrorFormatString* (*)(void* userRef, const unsigned errorNumber);
++
++/**
++ * Base class that implements parts shared by JSErrorReport and
++ * JSErrorNotes::Note.
++ */
++class JSErrorBase
++{
++  private:
++    // The (default) error message.
++    // If ownsMessage_ is true, the it is freed in destructor.
++    JS::ConstUTF8CharsZ message_;
++
++  public:
++    // Source file name, URL, etc., or null.
++    const char* filename;
++
++    // Source line number.
++    unsigned lineno;
++
++    // Zero-based column index in line.
++    unsigned column;
++
++    // the error number, e.g. see js.msg.
++    unsigned errorNumber;
++
++  private:
++    bool ownsMessage_ : 1;
++
++  public:
++    JSErrorBase()
++      : filename(nullptr), lineno(0), column(0),
++        errorNumber(0),
++        ownsMessage_(false)
++    {}
++
++    ~JSErrorBase() {
++        freeMessage();
++    }
++
++  public:
++    const JS::ConstUTF8CharsZ message() const {
++        return message_;
++    }
++
++    void initOwnedMessage(const char* messageArg) {
++        initBorrowedMessage(messageArg);
++        ownsMessage_ = true;
++    }
++    void initBorrowedMessage(const char* messageArg) {
++        MOZ_ASSERT(!message_);
++        message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
++    }
++
++    JSString* newMessageString(JSContext* cx);
++
++  private:
++    void freeMessage();
++};
++
++/**
++ * Notes associated with JSErrorReport.
++ */
++class JSErrorNotes
++{
++  public:
++    class Note final : public JSErrorBase
++    {};
++
++  private:
++    // Stores pointers to each note.
++    js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_;
++
++  public:
++    JSErrorNotes();
++    ~JSErrorNotes();
++
++    // Add an note to the given position.
++    bool addNoteASCII(JSContext* cx,
++                      const char* filename, unsigned lineno, unsigned column,
++                      JSErrorCallback errorCallback, void* userRef,
++                      const unsigned errorNumber, ...);
++    bool addNoteLatin1(JSContext* cx,
++                       const char* filename, unsigned lineno, unsigned column,
++                       JSErrorCallback errorCallback, void* userRef,
++                       const unsigned errorNumber, ...);
++    bool addNoteUTF8(JSContext* cx,
++                     const char* filename, unsigned lineno, unsigned column,
++                     JSErrorCallback errorCallback, void* userRef,
++                     const unsigned errorNumber, ...);
++
++    JS_PUBLIC_API(size_t) length();
++
++    // Create a deep copy of notes.
++    js::UniquePtr<JSErrorNotes> copy(JSContext* cx);
++
++    class iterator final
++      : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>>
++    {
++      private:
++        js::UniquePtr<Note>* note_;
++
++      public:
++        explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note)
++        {}
++
++        bool operator==(iterator other) const {
++            return note_ == other.note_;
++        }
++        bool operator!=(iterator other) const {
++            return !(*this == other);
++        }
++        iterator& operator++() {
++            note_++;
++            return *this;
++        }
++        reference operator*() {
++            return *note_;
++        }
++    };
++
++    JS_PUBLIC_API(iterator) begin();
++    JS_PUBLIC_API(iterator) end();
++};
++
++/**
++ * Describes a single error or warning that occurs in the execution of script.
++ */
++class JSErrorReport : public JSErrorBase
++{
++  private:
++    // Offending source line without final '\n'.
++    // If ownsLinebuf_ is true, the buffer is freed in destructor.
++    const char16_t* linebuf_;
++
++    // Number of chars in linebuf_. Does not include trailing '\0'.
++    size_t linebufLength_;
++
++    // The 0-based offset of error token in linebuf_.
++    size_t tokenOffset_;
++
++  public:
++    // Associated notes, or nullptr if there's no note.
++    js::UniquePtr<JSErrorNotes> notes;
++
++    // error/warning, etc.
++    unsigned flags;
++
++    // One of the JSExnType constants.
++    int16_t exnType;
++
++    // See the comment in TransitiveCompileOptions.
++    bool isMuted : 1;
++
++  private:
++    bool ownsLinebuf_ : 1;
++
++  public:
++    JSErrorReport()
++      : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0),
++        notes(nullptr),
++        flags(0), exnType(0), isMuted(false),
++        ownsLinebuf_(false)
++    {}
++
++    ~JSErrorReport() {
++        freeLinebuf();
++    }
++
++  public:
++    const char16_t* linebuf() const {
++        return linebuf_;
++    }
++    size_t linebufLength() const {
++        return linebufLength_;
++    }
++    size_t tokenOffset() const {
++        return tokenOffset_;
++    }
++    void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
++                          size_t tokenOffsetArg) {
++        initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg);
++        ownsLinebuf_ = true;
++    }
++    void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
++                             size_t tokenOffsetArg);
++
++  private:
++    void freeLinebuf();
++};
++
++/*
++ * JSErrorReport flag values.  These may be freely composed.
++ */
++#define JSREPORT_ERROR      0x0     /* pseudo-flag for default case */
++#define JSREPORT_WARNING    0x1     /* reported via JS_ReportWarning */
++#define JSREPORT_EXCEPTION  0x2     /* exception was thrown */
++#define JSREPORT_STRICT     0x4     /* error or warning due to strict option */
++
++#define JSREPORT_USER_1     0x8     /* user-defined flag */
++
++/*
++ * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception
++ * has been thrown for this runtime error, and the host should ignore it.
++ * Exception-aware hosts should also check for JS_IsExceptionPending if
++ * JS_ExecuteScript returns failure, and signal or propagate the exception, as
++ * appropriate.
++ */
++#define JSREPORT_IS_WARNING(flags)      (((flags) & JSREPORT_WARNING) != 0)
++#define JSREPORT_IS_EXCEPTION(flags)    (((flags) & JSREPORT_EXCEPTION) != 0)
++#define JSREPORT_IS_STRICT(flags)       (((flags) & JSREPORT_STRICT) != 0)
++
++#endif /* js_ErrorReport_h */
+diff --git a/js/src/jsapi.h b/js/src/jsapi.h
+--- a/js/src/jsapi.h
++++ b/js/src/jsapi.h
+@@ -12,28 +12,28 @@
+ #include "mozilla/AlreadyAddRefed.h"
+ #include "mozilla/FloatingPoint.h"
+ #include "mozilla/MemoryReporting.h"
+ #include "mozilla/Range.h"
+ #include "mozilla/RangedPtr.h"
+ #include "mozilla/RefPtr.h"
+ #include "mozilla/Variant.h"
+ 
+-#include <iterator>
+ #include <stdarg.h>
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <stdio.h>
+ 
+ #include "jspubtd.h"
+ 
+ #include "js/AllocPolicy.h"
+ #include "js/CallArgs.h"
+ #include "js/CharacterEncoding.h"
+ #include "js/Class.h"
++#include "js/ErrorReport.h"
+ #include "js/GCVector.h"
+ #include "js/HashTable.h"
+ #include "js/Id.h"
+ #include "js/Principals.h"
+ #include "js/Realm.h"
+ #include "js/RefCounted.h"
+ #include "js/RootingAPI.h"
+ #include "js/Stream.h"
+@@ -217,61 +217,16 @@ enum class PromiseRejectionHandlingState
+ 
+ } /* namespace JS */
+ 
+ typedef void
+ (* JSPromiseRejectionTrackerCallback)(JSContext* cx, JS::HandleObject promise,
+                                       JS::PromiseRejectionHandlingState state,
+                                       void* data);
+ 
+-/**
+- * Possible exception types. These types are part of a JSErrorFormatString
+- * structure. They define which error to throw in case of a runtime error.
+- *
+- * JSEXN_WARN is used for warnings in js.msg files (for instance because we
+- * don't want to prepend 'Error:' to warning messages). This value can go away
+- * if we ever decide to use an entirely separate mechanism for warnings.
+- */
+-typedef enum JSExnType {
+-    JSEXN_ERR,
+-    JSEXN_FIRST = JSEXN_ERR,
+-        JSEXN_INTERNALERR,
+-        JSEXN_EVALERR,
+-        JSEXN_RANGEERR,
+-        JSEXN_REFERENCEERR,
+-        JSEXN_SYNTAXERR,
+-        JSEXN_TYPEERR,
+-        JSEXN_URIERR,
+-        JSEXN_DEBUGGEEWOULDRUN,
+-        JSEXN_WASMCOMPILEERROR,
+-        JSEXN_WASMLINKERROR,
+-        JSEXN_WASMRUNTIMEERROR,
+-    JSEXN_ERROR_LIMIT,
+-    JSEXN_WARN = JSEXN_ERROR_LIMIT,
+-    JSEXN_NOTE,
+-    JSEXN_LIMIT
+-} JSExnType;
+-
+-struct JSErrorFormatString {
+-     /** The error message name in ASCII. */
+-    const char* name;
+-
+-    /** The error format string in ASCII. */
+-    const char* format;
+-
+-    /** The number of arguments to expand in the formatted error message. */
+-    uint16_t argCount;
+-
+-    /** One of the JSExnType constants above. */
+-    int16_t exnType;
+-};
+-
+-typedef const JSErrorFormatString*
+-(* JSErrorCallback)(void* userRef, const unsigned errorNumber);
+-
+ typedef bool
+ (* JSLocaleToUpperCase)(JSContext* cx, JS::HandleString src, JS::MutableHandleValue rval);
+ 
+ typedef bool
+ (* JSLocaleToLowerCase)(JSContext* cx, JS::HandleString src, JS::MutableHandleValue rval);
+ 
+ typedef bool
+ (* JSLocaleCompare)(JSContext* cx, JS::HandleString src1, JS::HandleString src2,
+@@ -5113,218 +5068,16 @@ extern MOZ_COLD JS_PUBLIC_API(void)
+ JS_ReportOutOfMemory(JSContext* cx);
+ 
+ /**
+  * Complain when an allocation size overflows the maximum supported limit.
+  */
+ extern JS_PUBLIC_API(void)
+ JS_ReportAllocationOverflow(JSContext* cx);
+ 
+-/**
+- * Base class that implements parts shared by JSErrorReport and
+- * JSErrorNotes::Note.
+- */
+-class JSErrorBase
+-{
+-    // The (default) error message.
+-    // If ownsMessage_ is true, the it is freed in destructor.
+-    JS::ConstUTF8CharsZ message_;
+-
+-  public:
+-    JSErrorBase()
+-      : filename(nullptr), lineno(0), column(0),
+-        errorNumber(0),
+-        ownsMessage_(false)
+-    {}
+-
+-    ~JSErrorBase() {
+-        freeMessage();
+-    }
+-
+-    // Source file name, URL, etc., or null.
+-    const char* filename;
+-
+-    // Source line number.
+-    unsigned lineno;
+-
+-    // Zero-based column index in line.
+-    unsigned column;
+-
+-    // the error number, e.g. see js.msg.
+-    unsigned errorNumber;
+-
+-  private:
+-    bool ownsMessage_ : 1;
+-
+-  public:
+-    const JS::ConstUTF8CharsZ message() const {
+-        return message_;
+-    }
+-
+-    void initOwnedMessage(const char* messageArg) {
+-        initBorrowedMessage(messageArg);
+-        ownsMessage_ = true;
+-    }
+-    void initBorrowedMessage(const char* messageArg) {
+-        MOZ_ASSERT(!message_);
+-        message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
+-    }
+-
+-    JSString* newMessageString(JSContext* cx);
+-
+-  private:
+-    void freeMessage();
+-};
+-
+-/**
+- * Notes associated with JSErrorReport.
+- */
+-class JSErrorNotes
+-{
+-  public:
+-    class Note : public JSErrorBase
+-    {};
+-
+-  private:
+-    // Stores pointers to each note.
+-    js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_;
+-
+-  public:
+-    JSErrorNotes();
+-    ~JSErrorNotes();
+-
+-    // Add an note to the given position.
+-    bool addNoteASCII(JSContext* cx,
+-                      const char* filename, unsigned lineno, unsigned column,
+-                      JSErrorCallback errorCallback, void* userRef,
+-                      const unsigned errorNumber, ...);
+-    bool addNoteLatin1(JSContext* cx,
+-                       const char* filename, unsigned lineno, unsigned column,
+-                       JSErrorCallback errorCallback, void* userRef,
+-                       const unsigned errorNumber, ...);
+-    bool addNoteUTF8(JSContext* cx,
+-                     const char* filename, unsigned lineno, unsigned column,
+-                     JSErrorCallback errorCallback, void* userRef,
+-                     const unsigned errorNumber, ...);
+-
+-    JS_PUBLIC_API(size_t) length();
+-
+-    // Create a deep copy of notes.
+-    js::UniquePtr<JSErrorNotes> copy(JSContext* cx);
+-
+-    class iterator : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>>
+-    {
+-        js::UniquePtr<Note>* note_;
+-      public:
+-        explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note)
+-        {}
+-
+-        bool operator==(iterator other) const {
+-            return note_ == other.note_;
+-        }
+-        bool operator!=(iterator other) const {
+-            return !(*this == other);
+-        }
+-        iterator& operator++() {
+-            note_++;
+-            return *this;
+-        }
+-        reference operator*() {
+-            return *note_;
+-        }
+-    };
+-    JS_PUBLIC_API(iterator) begin();
+-    JS_PUBLIC_API(iterator) end();
+-};
+-
+-/**
+- * Describes a single error or warning that occurs in the execution of script.
+- */
+-class JSErrorReport : public JSErrorBase
+-{
+-    // Offending source line without final '\n'.
+-    // If ownsLinebuf_ is true, the buffer is freed in destructor.
+-    const char16_t* linebuf_;
+-
+-    // Number of chars in linebuf_. Does not include trailing '\0'.
+-    size_t linebufLength_;
+-
+-    // The 0-based offset of error token in linebuf_.
+-    size_t tokenOffset_;
+-
+-  public:
+-    JSErrorReport()
+-      : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0),
+-        notes(nullptr),
+-        flags(0), exnType(0), isMuted(false),
+-        ownsLinebuf_(false)
+-    {}
+-
+-    ~JSErrorReport() {
+-        freeLinebuf();
+-    }
+-
+-    // Associated notes, or nullptr if there's no note.
+-    js::UniquePtr<JSErrorNotes> notes;
+-
+-    // error/warning, etc.
+-    unsigned flags;
+-
+-    // One of the JSExnType constants.
+-    int16_t exnType;
+-
+-    // See the comment in TransitiveCompileOptions.
+-    bool isMuted : 1;
+-
+-  private:
+-    bool ownsLinebuf_ : 1;
+-
+-  public:
+-    const char16_t* linebuf() const {
+-        return linebuf_;
+-    }
+-    size_t linebufLength() const {
+-        return linebufLength_;
+-    }
+-    size_t tokenOffset() const {
+-        return tokenOffset_;
+-    }
+-    void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+-                          size_t tokenOffsetArg) {
+-        initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg);
+-        ownsLinebuf_ = true;
+-    }
+-    void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+-                             size_t tokenOffsetArg);
+-
+-  private:
+-    void freeLinebuf();
+-};
+-
+-/*
+- * JSErrorReport flag values.  These may be freely composed.
+- */
+-#define JSREPORT_ERROR      0x0     /* pseudo-flag for default case */
+-#define JSREPORT_WARNING    0x1     /* reported via JS_ReportWarning */
+-#define JSREPORT_EXCEPTION  0x2     /* exception was thrown */
+-#define JSREPORT_STRICT     0x4     /* error or warning due to strict option */
+-
+-#define JSREPORT_USER_1     0x8     /* user-defined flag */
+-
+-/*
+- * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception
+- * has been thrown for this runtime error, and the host should ignore it.
+- * Exception-aware hosts should also check for JS_IsExceptionPending if
+- * JS_ExecuteScript returns failure, and signal or propagate the exception, as
+- * appropriate.
+- */
+-#define JSREPORT_IS_WARNING(flags)      (((flags) & JSREPORT_WARNING) != 0)
+-#define JSREPORT_IS_EXCEPTION(flags)    (((flags) & JSREPORT_EXCEPTION) != 0)
+-#define JSREPORT_IS_STRICT(flags)       (((flags) & JSREPORT_STRICT) != 0)
+-
+ namespace JS {
+ 
+ using WarningReporter = void (*)(JSContext* cx, JSErrorReport* report);
+ 
+ extern JS_PUBLIC_API(WarningReporter)
+ SetWarningReporter(JSContext* cx, WarningReporter reporter);
+ 
+ extern JS_PUBLIC_API(WarningReporter)
+diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
+--- a/js/src/jsfriendapi.h
++++ b/js/src/jsfriendapi.h
+@@ -14,16 +14,17 @@
+ #include "mozilla/UniquePtr.h"
+ 
+ #include "jsapi.h" // For JSAutoByteString.  See bug 1033916.
+ #include "jspubtd.h"
+ 
+ #include "js/CallArgs.h"
+ #include "js/CallNonGenericMethod.h"
+ #include "js/Class.h"
++#include "js/ErrorReport.h"
+ #include "js/HeapAPI.h"
+ #include "js/StableStringChars.h"
+ #include "js/TypeDecls.h"
+ #include "js/Utility.h"
+ 
+ #ifndef JS_STACK_GROWTH_DIRECTION
+ # ifdef __hppa
+ #  define JS_STACK_GROWTH_DIRECTION (1)
+@@ -35,17 +36,16 @@
+ #if JS_STACK_GROWTH_DIRECTION > 0
+ # define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) < (limit)))
+ #else
+ # define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) > (limit)))
+ #endif
+ 
+ struct JSErrorFormatString;
+ struct JSJitInfo;
+-class JSErrorReport;
+ 
+ namespace JS {
+ template <class T>
+ class Heap;
+ } /* namespace JS */
+ 
+ namespace js {
+ class JS_FRIEND_API(BaseProxyHandler);
+diff --git a/js/src/moz.build b/js/src/moz.build
+--- a/js/src/moz.build
++++ b/js/src/moz.build
+@@ -126,16 +126,17 @@ EXPORTS.js += [
+     '../public/AllocPolicy.h',
+     '../public/CallArgs.h',
+     '../public/CallNonGenericMethod.h',
+     '../public/CharacterEncoding.h',
+     '../public/Class.h',
+     '../public/Conversions.h',
+     '../public/Date.h',
+     '../public/Debug.h',
++    '../public/ErrorReport.h',
+     '../public/GCAnnotations.h',
+     '../public/GCAPI.h',
+     '../public/GCHashTable.h',
+     '../public/GCPolicyAPI.h',
+     '../public/GCVariant.h',
+     '../public/GCVector.h',
+     '../public/HashTable.h',
+     '../public/HeapAPI.h',
+

+ 138 - 0
frg/work-js/mozilla-release/patches/1484386-63a1.patch

@@ -0,0 +1,138 @@
+# HG changeset patch
+# User Jeff Walden <jwalden@mit.edu>
+# Date 1534776344 25200
+# Node ID cd604eaf33010f3a8b31b6f63669321c5fe0d92d
+# Parent  fa2218b2f4f17c47f39243914e9987eec1d51d38
+Bug 1484386 - Move various memory-allocation-related functions into js/public/MemoryFunctions.h to (ultimately) minimize dependencies required to use these functions in forthcoming public headers.  r=jandem
+
+diff --git a/js/public/MemoryFunctions.h b/js/public/MemoryFunctions.h
+new file mode 100644
+--- /dev/null
++++ b/js/public/MemoryFunctions.h
+@@ -0,0 +1,42 @@
++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* 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/. */
++
++/* Low-level memory-allocation functions. */
++
++#ifndef js_MemoryFunctions_h
++#define js_MemoryFunctions_h
++
++#include "mozilla/Attributes.h" // MOZ_MUST_USE
++
++#include <stddef.h> // size_t
++
++#include "jstypes.h" // JS_PUBLIC_API
++
++struct JSContext;
++
++extern JS_PUBLIC_API(void*)
++JS_malloc(JSContext* cx, size_t nbytes);
++
++extern JS_PUBLIC_API(void*)
++JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes);
++
++/**
++ * A wrapper for |js_free(p)| that may delay |js_free(p)| invocation as a
++ * performance optimization.  |cx| may be nullptr.
++ */
++extern JS_PUBLIC_API(void)
++JS_free(JSContext* cx, void* p);
++
++/**
++ * A wrapper for |js_free(p)| that may delay |js_free(p)| invocation as a
++ * performance optimization as specified by the given JSFreeOp instance.
++ */
++extern JS_PUBLIC_API(void)
++JS_freeop(JSFreeOp* fop, void* p);
++
++extern JS_PUBLIC_API(void)
++JS_updateMallocCounter(JSContext* cx, size_t nbytes);
++
++#endif /* js_MemoryFunctions_h */
+diff --git a/js/src/jsapi.h b/js/src/jsapi.h
+--- a/js/src/jsapi.h
++++ b/js/src/jsapi.h
+@@ -27,16 +27,17 @@
+ #include "js/AllocPolicy.h"
+ #include "js/CallArgs.h"
+ #include "js/CharacterEncoding.h"
+ #include "js/Class.h"
+ #include "js/ErrorReport.h"
+ #include "js/GCVector.h"
+ #include "js/HashTable.h"
+ #include "js/Id.h"
++#include "js/MemoryFunctions.h"
+ #include "js/Principals.h"
+ #include "js/Realm.h"
+ #include "js/RefCounted.h"
+ #include "js/RootingAPI.h"
+ #include "js/Stream.h"
+ #include "js/TracingAPI.h"
+ #include "js/UniquePtr.h"
+ #include "js/Utility.h"
+@@ -1261,40 +1262,16 @@ struct JSCTypesCallbacks {
+  * pointer to static data that exists for the lifetime of 'ctypesObj', but it
+  * may safely be altered after calling this function and without having
+  * to call this function again.
+  */
+ extern JS_PUBLIC_API(void)
+ JS_SetCTypesCallbacks(JSObject* ctypesObj, const JSCTypesCallbacks* callbacks);
+ #endif
+ 
+-extern JS_PUBLIC_API(void*)
+-JS_malloc(JSContext* cx, size_t nbytes);
+-
+-extern JS_PUBLIC_API(void*)
+-JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes);
+-
+-/**
+- * A wrapper for js_free(p) that may delay js_free(p) invocation as a
+- * performance optimization.
+- * cx may be nullptr.
+- */
+-extern JS_PUBLIC_API(void)
+-JS_free(JSContext* cx, void* p);
+-
+-/**
+- * A wrapper for js_free(p) that may delay js_free(p) invocation as a
+- * performance optimization as specified by the given JSFreeOp instance.
+- */
+-extern JS_PUBLIC_API(void)
+-JS_freeop(JSFreeOp* fop, void* p);
+-
+-extern JS_PUBLIC_API(void)
+-JS_updateMallocCounter(JSContext* cx, size_t nbytes);
+-
+ /*
+  * A replacement for MallocAllocPolicy that allocates in the JS heap and adds no
+  * extra behaviours.
+  *
+  * This is currently used for allocating source buffers for parsing. Since these
+  * are temporary and will not be freed by GC, the memory is not tracked by the
+  * usual accounting.
+  */
+diff --git a/js/src/moz.build b/js/src/moz.build
+--- a/js/src/moz.build
++++ b/js/src/moz.build
+@@ -137,16 +137,17 @@ EXPORTS.js += [
+     '../public/GCHashTable.h',
+     '../public/GCPolicyAPI.h',
+     '../public/GCVariant.h',
+     '../public/GCVector.h',
+     '../public/HashTable.h',
+     '../public/HeapAPI.h',
+     '../public/Id.h',
+     '../public/Initialization.h',
++    '../public/MemoryFunctions.h',
+     '../public/MemoryMetrics.h',
+     '../public/Principals.h',
+     '../public/Printf.h',
+     '../public/ProfilingFrameIterator.h',
+     '../public/ProfilingStack.h',
+     '../public/ProtoKey.h',
+     '../public/Proxy.h',
+     '../public/Realm.h',
+

+ 477 - 0
frg/work-js/mozilla-release/patches/1484420-63a1.patch

@@ -0,0 +1,477 @@
+# HG changeset patch
+# User Jeff Walden <jwalden@mit.edu>
+# Date 1534803092 18000
+# Node ID 9f92f288b60876c75fddfddb2eba8ff685ed2b78
+# Parent  121a6196a45108b09bf01b28fd8484cc95643cc6
+Bug 1484420 - Move locale-related functions into js/public/LocaleSensitive.h that isn't #include'd in jsapi.h.  r=anba
+
+diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
+--- a/dom/workers/RuntimeService.cpp
++++ b/dom/workers/RuntimeService.cpp
+@@ -23,16 +23,17 @@
+ #include "nsIURI.h"
+ #include "nsIXULRuntime.h"
+ #include "nsPIDOMWindow.h"
+ 
+ #include <algorithm>
+ #include "BackgroundChild.h"
+ #include "GeckoProfiler.h"
+ #include "jsfriendapi.h"
++#include "js/LocaleSensitive.h"
+ #include "mozilla/AbstractThread.h"
+ #include "mozilla/ArrayUtils.h"
+ #include "mozilla/AsyncEventDispatcher.h"
+ #include "mozilla/Atomics.h"
+ #include "mozilla/CycleCollectedJSContext.h"
+ #include "mozilla/CycleCollectedJSRuntime.h"
+ #include "mozilla/Telemetry.h"
+ #include "mozilla/TimeStamp.h"
+diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
+--- a/dom/workers/WorkerPrivate.cpp
++++ b/dom/workers/WorkerPrivate.cpp
+@@ -33,16 +33,17 @@
+ #include "nsIWorkerDebugger.h"
+ #include "nsIXPConnect.h"
+ #include "nsPIDOMWindow.h"
+ #include "nsGlobalWindow.h"
+ 
+ #include <algorithm>
+ #include "ImageContainer.h"
+ #include "jsfriendapi.h"
++#include "js/LocaleSensitive.h"
+ #include "js/MemoryMetrics.h"
+ #include "mozilla/Assertions.h"
+ #include "mozilla/Attributes.h"
+ #include "mozilla/ContentEvents.h"
+ #include "mozilla/EventDispatcher.h"
+ #include "mozilla/Likely.h"
+ #include "mozilla/LoadContext.h"
+ #include "mozilla/Move.h"
+diff --git a/js/public/LocaleSensitive.h b/js/public/LocaleSensitive.h
+new file mode 100644
+--- /dev/null
++++ b/js/public/LocaleSensitive.h
+@@ -0,0 +1,99 @@
++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* 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/. */
++
++/*
++ * Functions and structures related to locale-sensitive behavior, including
++ * exposure of the default locale (used by operations like toLocaleString).
++ */
++
++#ifndef js_LocaleSensitive_h
++#define js_LocaleSensitive_h
++
++#include "jstypes.h" // JS_PUBLIC_API
++
++#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
++#include "js/Utility.h" // JS::UniqueChars
++
++struct JSContext;
++struct JSRuntime;
++class JSString;
++
++namespace JS { union Value; }
++
++/**
++ * Set the default locale for the ECMAScript Internationalization API
++ * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat, and others that will
++ * arise as time passes).  (Note that the Internationalization API encourages
++ * clients to specify their own locales; this default locale is only used when
++ * no locale is specified, e.g. calling a toLocaleString function without
++ * passing a locale argument to it.)
++ *
++ * The locale string remains owned by the caller.
++ */
++extern JS_PUBLIC_API(bool)
++JS_SetDefaultLocale(JSRuntime* rt, const char* locale);
++
++/**
++ * Return a copy of the default locale for the ECMAScript Internationalization
++ * API (and for various ECMAScript functions that will invoke it).  The locale
++ * is retrieved from the |JSRuntime| that corresponds to |cx|.
++ *
++ * XXX Bug 1483961 means it's difficult to interpret the meaning of a null
++ *     return value for the time being, and we should fix this!
++ */
++extern JS_PUBLIC_API(JS::UniqueChars)
++JS_GetDefaultLocale(JSContext* cx);
++
++/** Reset the default locale to OS defaults. */
++extern JS_PUBLIC_API(void)
++JS_ResetDefaultLocale(JSRuntime* rt);
++
++using JSLocaleToUpperCase =
++    bool (*)(JSContext* cx, JS::Handle<JSString*> src, JS::MutableHandle<JS::Value> rval);
++
++using JSLocaleToLowerCase =
++    bool (*)(JSContext* cx, JS::Handle<JSString*> src, JS::MutableHandle<JS::Value> rval);
++
++using JSLocaleCompare =
++    bool (*)(JSContext* cx, JS::Handle<JSString*> src1, JS::Handle<JSString*> src2,
++             JS::MutableHandle<JS::Value> rval);
++
++using JSLocaleToUnicode =
++    bool (*)(JSContext* cx, const char* src, JS::MutableHandle<JS::Value> rval);
++
++/**
++ * A suite of locale-specific string conversion and error message callbacks
++ * used to implement locale-sensitive behaviors (such as those performed by
++ * the various toLocaleString and toLocale{Date,Time}String functions).
++ *
++ * If SpiderMonkey is compiled --with-intl-api, then #if EXPOSE_INTL_API.  In
++ * this case, SpiderMonkey itself will implement ECMA-402-compliant behavior by
++ * calling on ICU, and none of the fields in this struct will ever be used.
++ * (You'll still be able to call the get/set-callbacks functions; they just
++ * won't affect JavaScript semantics.)
++ */
++struct JSLocaleCallbacks
++{
++    JSLocaleToUpperCase localeToUpperCase;
++    JSLocaleToLowerCase localeToLowerCase;
++    JSLocaleCompare localeCompare;
++    JSLocaleToUnicode localeToUnicode;
++};
++
++/**
++ * Set locale callbacks to be used in builds not compiled --with-intl-api.
++ * |callbacks| must persist as long as the |JSRuntime|.  Pass |nullptr| to
++ * restore default behavior.
++ */
++extern JS_PUBLIC_API(void)
++JS_SetLocaleCallbacks(JSRuntime* rt, const JSLocaleCallbacks* callbacks);
++
++/**
++ * Return the current locale callbacks, which may be nullptr.
++ */
++extern JS_PUBLIC_API(const JSLocaleCallbacks*)
++JS_GetLocaleCallbacks(JSRuntime* rt);
++
++#endif /* js_LocaleSensitive_h */
+diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
+--- a/js/src/builtin/String.cpp
++++ b/js/src/builtin/String.cpp
+@@ -27,16 +27,19 @@
+ 
+ #include "builtin/Array.h"
+ #include "builtin/Boolean.h"
+ #include "builtin/intl/CommonFunctions.h"
+ #include "builtin/intl/ICUStubs.h"
+ #include "builtin/RegExp.h"
+ #include "jit/InlinableNatives.h"
+ #include "js/Conversions.h"
++#if !EXPOSE_INTL_API
++#include "js/LocaleSensitive.h"
++#endif
+ #include "js/StableStringChars.h"
+ #include "js/UniquePtr.h"
+ #if ENABLE_INTL_API
+ # include "unicode/uchar.h"
+ # include "unicode/unorm2.h"
+ #endif
+ #include "util/StringBuffer.h"
+ #include "util/Unicode.h"
+diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
+--- a/js/src/builtin/TestingFunctions.cpp
++++ b/js/src/builtin/TestingFunctions.cpp
+@@ -39,16 +39,17 @@
+ #endif
+ #include "gc/Heap.h"
+ #include "jit/BaselineJIT.h"
+ #include "jit/InlinableNatives.h"
+ #include "jit/JitRealm.h"
+ #include "js/AutoByteString.h"
+ #include "js/Debug.h"
+ #include "js/HashTable.h"
++#include "js/LocaleSensitive.h"
+ #include "js/StableStringChars.h"
+ #include "js/StructuredClone.h"
+ #include "js/UbiNode.h"
+ #include "js/UbiNodeBreadthFirst.h"
+ #include "js/UbiNodeShortestPaths.h"
+ #include "js/UniquePtr.h"
+ #include "js/Vector.h"
+ #include "js/Wrapper.h"
+diff --git a/js/src/jsapi-tests/testDateToLocaleString.cpp b/js/src/jsapi-tests/testDateToLocaleString.cpp
+--- a/js/src/jsapi-tests/testDateToLocaleString.cpp
++++ b/js/src/jsapi-tests/testDateToLocaleString.cpp
+@@ -1,15 +1,16 @@
+ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+  * vim: set ts=8 sts=4 et sw=4 tw=99:
+  */
+ /* 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/. */
+ 
++#include "js/LocaleSensitive.h"
+ #include "jsapi-tests/tests.h"
+ 
+ BEGIN_TEST(testDateToLocaleString)
+ {
+     JSRuntime* rt = JS_GetRuntime(cx);
+ 
+     // This test should only attempt to run if we have Intl support: necessary
+     // to properly assume that changes to the default locale will predictably
+diff --git a/js/src/jsapi-tests/testIntlAvailableLocales.cpp b/js/src/jsapi-tests/testIntlAvailableLocales.cpp
+--- a/js/src/jsapi-tests/testIntlAvailableLocales.cpp
++++ b/js/src/jsapi-tests/testIntlAvailableLocales.cpp
+@@ -1,15 +1,16 @@
+ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+  * vim: set ts=8 sts=4 et sw=4 tw=99:
+  */
+ /* 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/. */
+ 
++#include "js/LocaleSensitive.h"
+ #include "jsapi-tests/tests.h"
+ 
+ BEGIN_TEST(testIntlAvailableLocales)
+ {
+     JSRuntime* rt = JS_GetRuntime(cx);
+ 
+     // This test should only attempt to run if we have Intl support.
+     JS::Rooted<JS::Value> haveIntl(cx);
+diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
+--- a/js/src/jsapi.cpp
++++ b/js/src/jsapi.cpp
+@@ -56,16 +56,17 @@
+ #include "jit/JitCommon.h"
+ #include "jit/JitSpewer.h"
+ #include "js/AutoByteString.h"
+ #include "js/CharacterEncoding.h"
+ #include "js/Conversions.h"
+ #include "js/Date.h"
+ #include "js/Initialization.h"
+ #include "js/JSON.h"
++#include "js/LocaleSensitive.h"
+ #include "js/Proxy.h"
+ #include "js/SliceBudget.h"
+ #include "js/StableStringChars.h"
+ #include "js/StructuredClone.h"
+ #include "js/Utility.h"
+ #include "js/Wrapper.h"
+ #include "util/StringBuffer.h"
+ #include "util/Text.h"
+diff --git a/js/src/jsapi.h b/js/src/jsapi.h
+--- a/js/src/jsapi.h
++++ b/js/src/jsapi.h
+@@ -217,29 +217,16 @@ enum class PromiseRejectionHandlingState
+ 
+ } /* namespace JS */
+ 
+ typedef void
+ (* JSPromiseRejectionTrackerCallback)(JSContext* cx, JS::HandleObject promise,
+                                       JS::PromiseRejectionHandlingState state,
+                                       void* data);
+ 
+-typedef bool
+-(* JSLocaleToUpperCase)(JSContext* cx, JS::HandleString src, JS::MutableHandleValue rval);
+-
+-typedef bool
+-(* JSLocaleToLowerCase)(JSContext* cx, JS::HandleString src, JS::MutableHandleValue rval);
+-
+-typedef bool
+-(* JSLocaleCompare)(JSContext* cx, JS::HandleString src1, JS::HandleString src2,
+-                    JS::MutableHandleValue rval);
+-
+-typedef bool
+-(* JSLocaleToUnicode)(JSContext* cx, const char* src, JS::MutableHandleValue rval);
+-
+ /**
+  * Callback used to ask the embedding for the cross compartment wrapper handler
+  * that implements the desired prolicy for this kind of object in the
+  * destination compartment. |obj| is the object to be wrapped. If |existing| is
+  * non-nullptr, it will point to an existing wrapper object that should be
+  * re-used if possible. |existing| is guaranteed to be a cross-compartment
+  * wrapper with a lazily-defined prototype and the correct global. It is
+  * guaranteed not to wrap a function.
+@@ -4694,65 +4681,16 @@ PropertySpecNameEqualsId(const char* nam
+  */
+ JS_PUBLIC_API(bool)
+ PropertySpecNameToPermanentId(JSContext* cx, const char* name, jsid* idp);
+ 
+ } /* namespace JS */
+ 
+ /************************************************************************/
+ 
+-/**
+- * The default locale for the ECMAScript Internationalization API
+- * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
+- * Note that the Internationalization API encourages clients to
+- * specify their own locales.
+- * The locale string remains owned by the caller.
+- */
+-extern JS_PUBLIC_API(bool)
+-JS_SetDefaultLocale(JSRuntime* rt, const char* locale);
+-
+-/**
+- * Look up the default locale for the ECMAScript Internationalization API.
+- * NB: The locale information is retrieved from cx's runtime.
+- */
+-extern JS_PUBLIC_API(JS::UniqueChars)
+-JS_GetDefaultLocale(JSContext* cx);
+-
+-/**
+- * Reset the default locale to OS defaults.
+- */
+-extern JS_PUBLIC_API(void)
+-JS_ResetDefaultLocale(JSRuntime* rt);
+-
+-/**
+- * Locale specific string conversion and error message callbacks.
+- */
+-struct JSLocaleCallbacks {
+-    JSLocaleToUpperCase     localeToUpperCase; // not used #if EXPOSE_INTL_API
+-    JSLocaleToLowerCase     localeToLowerCase; // not used #if EXPOSE_INTL_API
+-    JSLocaleCompare         localeCompare; // not used #if EXPOSE_INTL_API
+-    JSLocaleToUnicode       localeToUnicode;
+-};
+-
+-/**
+- * Establish locale callbacks. The pointer must persist as long as the
+- * JSContext.  Passing nullptr restores the default behaviour.
+- */
+-extern JS_PUBLIC_API(void)
+-JS_SetLocaleCallbacks(JSRuntime* rt, const JSLocaleCallbacks* callbacks);
+-
+-/**
+- * Return the address of the current locale callbacks struct, which may
+- * be nullptr.
+- */
+-extern JS_PUBLIC_API(const JSLocaleCallbacks*)
+-JS_GetLocaleCallbacks(JSRuntime* rt);
+-
+-/************************************************************************/
+-
+ /*
+  * Error reporting.
+  *
+  * There are four encoding variants for the error reporting API:
+  *   UTF-8
+  *     JSAPI's default encoding for error handling.  Use this when the encoding
+  *     of the error message, format string, and arguments is UTF-8.
+  *   ASCII
+diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
+--- a/js/src/jsnum.cpp
++++ b/js/src/jsnum.cpp
+@@ -23,16 +23,19 @@
+ #include <math.h>
+ #include <string.h>
+ 
+ #include "jstypes.h"
+ 
+ #include "builtin/String.h"
+ #include "double-conversion/double-conversion.h"
+ #include "js/Conversions.h"
++#if !EXPOSE_INTL_API
++#include "js/LocaleSensitive.h"
++#endif
+ #include "util/DoubleToString.h"
+ #include "util/StringBuffer.h"
+ #ifdef ENABLE_BIGINT
+ #include "vm/BigIntType.h"
+ #endif
+ #include "vm/GlobalObject.h"
+ #include "vm/JSAtom.h"
+ #include "vm/JSContext.h"
+diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h
+--- a/js/src/jspubtd.h
++++ b/js/src/jspubtd.h
+@@ -63,17 +63,16 @@ enum JSProtoKey {
+     JSProto_LIMIT
+ };
+ 
+ /* Struct forward declarations. */
+ struct JSClass;
+ class JSErrorReport;
+ struct JSExceptionState;
+ struct JSFunctionSpec;
+-struct JSLocaleCallbacks;
+ struct JSPrincipals;
+ struct JSPropertySpec;
+ struct JSSecurityCallbacks;
+ struct JSStructuredCloneCallbacks;
+ struct JSStructuredCloneReader;
+ struct JSStructuredCloneWriter;
+ class JS_PUBLIC_API(JSTracer);
+ 
+diff --git a/js/src/moz.build b/js/src/moz.build
+--- a/js/src/moz.build
++++ b/js/src/moz.build
+@@ -138,16 +138,17 @@ EXPORTS.js += [
+     '../public/GCPolicyAPI.h',
+     '../public/GCVariant.h',
+     '../public/GCVector.h',
+     '../public/HashTable.h',
+     '../public/HeapAPI.h',
+     '../public/Id.h',
+     '../public/Initialization.h',
+     '../public/JSON.h',
++    '../public/LocaleSensitive.h',
+     '../public/MemoryMetrics.h',
+     '../public/Principals.h',
+     '../public/Printf.h',
+     '../public/ProfilingFrameIterator.h',
+     '../public/ProfilingStack.h',
+     '../public/ProtoKey.h',
+     '../public/Proxy.h',
+     '../public/Realm.h',
+diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
+--- a/js/src/vm/Runtime.h
++++ b/js/src/vm/Runtime.h
+@@ -58,16 +58,17 @@ class AutoKeepAtoms;
+ class EnterDebuggeeNoExecute;
+ #ifdef JS_TRACE_LOGGING
+ class TraceLoggerThread;
+ #endif
+ 
+ } // namespace js
+ 
+ struct DtoaState;
++struct JSLocaleCallbacks;
+ 
+ #ifdef JS_SIMULATOR_ARM64
+ namespace vixl {
+ class Simulator;
+ }
+ #endif
+ 
+ namespace js {
+diff --git a/js/xpconnect/src/XPCLocale.cpp b/js/xpconnect/src/XPCLocale.cpp
+--- a/js/xpconnect/src/XPCLocale.cpp
++++ b/js/xpconnect/src/XPCLocale.cpp
+@@ -1,29 +1,27 @@
+ /* -*- 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/. */
+ 
+ #include "mozilla/Assertions.h"
+ 
+-#include "jsapi.h"
++#include "js/LocaleSensitive.h"
+ 
+-#include "nsJSUtils.h"
+ #include "nsIObserver.h"
+ #include "nsComponentManagerUtils.h"
+ #include "nsServiceManagerUtils.h"
+ #include "mozilla/CycleCollectedJSContext.h"
+ #include "mozilla/intl/LocaleService.h"
+ #include "mozilla/Preferences.h"
+ 
+ #include "xpcpublic.h"
+ 
+-using namespace JS;
+ using namespace mozilla;
+ using mozilla::intl::LocaleService;
+ 
+ class XPCLocaleObserver : public nsIObserver
+ {
+ public:
+   NS_DECL_ISUPPORTS
+   NS_DECL_NSIOBSERVER

+ 491 - 0
frg/work-js/mozilla-release/patches/1484421-63a1.patch

@@ -0,0 +1,491 @@
+# HG changeset patch
+# User Jeff Walden <jwalden@mit.edu>
+# Date 1534776885 25200
+# Node ID ab5ac052f221b03e0700351ec6534160b38b106c
+# Parent  62d3590273210c7bdbdd3ce48a6ce01a90609313
+Bug 1484421 - Move JSON-related functionality into js/public/JSON.h that isn't #include'd in jsapi.h.  r=jandem
+
+diff --git a/dom/base/BodyUtil.cpp b/dom/base/BodyUtil.cpp
+--- a/dom/base/BodyUtil.cpp
++++ b/dom/base/BodyUtil.cpp
+@@ -13,16 +13,17 @@
+ 
+ #include "nsCharSeparatedTokenizer.h"
+ #include "nsDOMString.h"
+ #include "nsNetUtil.h"
+ #include "nsReadableUtils.h"
+ #include "nsStreamUtils.h"
+ #include "nsStringStream.h"
+ 
++#include "js/JSON.h"
+ #include "mozilla/ErrorResult.h"
+ #include "mozilla/dom/Exceptions.h"
+ #include "mozilla/dom/FetchUtil.h"
+ #include "mozilla/dom/File.h"
+ #include "mozilla/dom/FormData.h"
+ #include "mozilla/dom/Headers.h"
+ #include "mozilla/dom/Promise.h"
+ #include "mozilla/dom/URLSearchParams.h"
+diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
+--- a/dom/base/nsContentUtils.cpp
++++ b/dom/base/nsContentUtils.cpp
+@@ -15,16 +15,17 @@
+ #include "harfbuzz/hb.h"
+ #include "imgICache.h"
+ #include "imgIContainer.h"
+ #include "imgINotificationObserver.h"
+ #include "imgLoader.h"
+ #include "imgRequestProxy.h"
+ #include "jsapi.h"
+ #include "jsfriendapi.h"
++#include "js/JSON.h"
+ #include "js/Value.h"
+ #include "Layers.h"
+ #include "nsAppRunner.h"
+ #include "gfxDrawable.h"
+ #include "gfxPrefs.h"
+ #include "ImageOps.h"
+ #include "mozAutoDocUpdate.h"
+ #include "mozilla/ArrayUtils.h"
+diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp
+--- a/dom/base/nsFrameMessageManager.cpp
++++ b/dom/base/nsFrameMessageManager.cpp
+@@ -26,16 +26,17 @@
+ #include "nsIXULRuntime.h"
+ #include "nsIScriptError.h"
+ #include "nsIConsoleService.h"
+ #include "nsIMemoryReporter.h"
+ #include "nsIProtocolHandler.h"
+ #include "nsIScriptSecurityManager.h"
+ #include "nsIDOMClassInfo.h"
+ #include "xpcpublic.h"
++#include "js/JSON.h"
+ #include "mozilla/CycleCollectedJSContext.h"
+ #include "mozilla/IntentionalCrash.h"
+ #include "mozilla/Preferences.h"
+ #include "mozilla/ScriptPreloader.h"
+ #include "mozilla/Telemetry.h"
+ #include "mozilla/dom/File.h"
+ #include "mozilla/dom/MessagePort.h"
+ #include "mozilla/dom/ContentParent.h"
+diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp
+--- a/dom/base/nsINode.cpp
++++ b/dom/base/nsINode.cpp
+@@ -7,16 +7,17 @@
+ /*
+  * Base class for all DOM nodes.
+  */
+ 
+ #include "nsINode.h"
+ 
+ #include "AccessCheck.h"
+ #include "jsapi.h"
++#include "js/JSON.h"
+ #include "mozAutoDocUpdate.h"
+ #include "mozilla/AsyncEventDispatcher.h"
+ #include "mozilla/CORSMode.h"
+ #include "mozilla/EventDispatcher.h"
+ #include "mozilla/EventListenerManager.h"
+ #include "mozilla/HTMLEditor.h"
+ #include "mozilla/InternalMutationEvent.h"
+ #include "mozilla/Likely.h"
+diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
+--- a/dom/bindings/BindingUtils.cpp
++++ b/dom/bindings/BindingUtils.cpp
+@@ -12,16 +12,17 @@
+ #include "mozilla/Assertions.h"
+ #include "mozilla/DebugOnly.h"
+ #include "mozilla/FloatingPoint.h"
+ #include "mozilla/Preferences.h"
+ #include "mozilla/Unused.h"
+ #include "mozilla/UseCounter.h"
+ 
+ #include "AccessCheck.h"
++#include "js/JSON.h"
+ #include "js/StableStringChars.h"
+ #include "jsfriendapi.h"
+ #include "nsContentCreatorFunctions.h"
+ #include "nsContentUtils.h"
+ #include "nsGlobalWindow.h"
+ #include "nsHTMLTags.h"
+ #include "nsIDocShell.h"
+ #include "nsIDOMGlobalPropertyInitializer.h"
+diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp
+--- a/dom/ipc/TabChild.cpp
++++ b/dom/ipc/TabChild.cpp
+@@ -10,16 +10,17 @@
+ 
+ #include "gfxPrefs.h"
+ #ifdef ACCESSIBILITY
+ #include "mozilla/a11y/DocAccessibleChild.h"
+ #endif
+ #include "Layers.h"
+ #include "ContentChild.h"
+ #include "TabParent.h"
++#include "js/JSON.h"
+ #include "mozilla/Preferences.h"
+ #include "mozilla/BrowserElementParent.h"
+ #include "mozilla/ClearOnShutdown.h"
+ #include "mozilla/EventListenerManager.h"
+ #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
+ #include "mozilla/dom/PaymentRequestChild.h"
+ #include "mozilla/IMEStateManager.h"
+ #include "mozilla/ipc/URIUtils.h"
+diff --git a/dom/json/nsJSON.cpp b/dom/json/nsJSON.cpp
+--- a/dom/json/nsJSON.cpp
++++ b/dom/json/nsJSON.cpp
+@@ -3,16 +3,17 @@
+ /* 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/. */
+ 
+ #include "nsJSON.h"
+ 
+ #include "jsapi.h"
+ #include "js/CharacterEncoding.h"
++#include "js/JSON.h"
+ #include "nsIXPConnect.h"
+ #include "nsIXPCScriptable.h"
+ #include "nsStreamUtils.h"
+ #include "nsIInputStream.h"
+ #include "nsStringStream.h"
+ #include "nsNetUtil.h"
+ #include "nsIURI.h"
+ #include "nsComponentManagerUtils.h"
+diff --git a/dom/payments/PaymentRequestUtils.cpp b/dom/payments/PaymentRequestUtils.cpp
+--- a/dom/payments/PaymentRequestUtils.cpp
++++ b/dom/payments/PaymentRequestUtils.cpp
+@@ -1,14 +1,15 @@
+ /* -*- 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/. */
+ 
++#include "js/JSON.h"
+ #include "nsArrayUtils.h"
+ #include "PaymentRequestUtils.h"
+ #include "nsIMutableArray.h"
+ #include "nsISupportsPrimitives.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp
+--- a/dom/xhr/XMLHttpRequestMainThread.cpp
++++ b/dom/xhr/XMLHttpRequestMainThread.cpp
+@@ -71,16 +71,17 @@
+ #include "nsIPromptFactory.h"
+ #include "nsIWindowWatcher.h"
+ #include "nsIConsoleService.h"
+ #include "nsIContentSecurityPolicy.h"
+ #include "nsAsyncRedirectVerifyHelper.h"
+ #include "nsStringBuffer.h"
+ #include "nsIFileChannel.h"
+ #include "mozilla/Telemetry.h"
++#include "js/JSON.h"
+ #include "jsfriendapi.h"
+ #include "GeckoProfiler.h"
+ #include "mozilla/dom/XMLHttpRequestBinding.h"
+ #include "mozilla/Attributes.h"
+ #include "MultipartBlobImpl.h"
+ #include "nsIPermissionManager.h"
+ #include "nsMimeTypes.h"
+ #include "nsIHttpChannelInternal.h"
+diff --git a/js/public/JSON.h b/js/public/JSON.h
+new file mode 100644
+--- /dev/null
++++ b/js/public/JSON.h
+@@ -0,0 +1,91 @@
++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* 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/. */
++
++/*
++ * JSON serialization and deserialization operations.
++ */
++
++#ifndef js_JSON_h
++#define js_JSON_h
++
++#include <stdint.h> // uint32_t
++
++#include "jstypes.h" // JS_PUBLIC_API
++
++#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
++
++struct JSContext;
++class JSObject;
++class JSString;
++
++namespace JS { union Value; }
++
++using JSONWriteCallback = bool (*)(const char16_t* buf, uint32_t len, void* data);
++
++/**
++ * Performs the JSON.stringify operation, as specified by ECMAScript, except
++ * writing stringified data by repeated calls of |callback|, with each such
++ * call passed |data| as argument.
++ */
++extern JS_PUBLIC_API(bool)
++JS_Stringify(JSContext* cx, JS::MutableHandle<JS::Value> value, JS::Handle<JSObject*> replacer,
++             JS::Handle<JS::Value> space, JSONWriteCallback callback, void* data);
++
++namespace JS {
++
++/**
++ * An API akin to JS_Stringify but with the goal of not having observable
++ * side-effects when the stringification is performed.  This means it does not
++ * allow a replacer or a custom space and has the following constraints on its
++ * input:
++ *
++ * 1) The input must be a plain object or array, not an abitrary value.
++ * 2) Every value in the graph reached by the algorithm starting with this
++ *    object must be one of the following: null, undefined, a string (NOT a
++ *    string object!), a boolean, a finite number (i.e. no NaN or Infinity or
++ *    -Infinity), a plain object with no accessor properties, or an Array with
++ *    no holes.
++ *
++ * The actual behavior differs from JS_Stringify only in asserting the above and
++ * NOT attempting to get the "toJSON" property from things, since that could
++ * clearly have side-effects.
++ */
++extern JS_PUBLIC_API(bool)
++ToJSONMaybeSafely(JSContext* cx, JS::Handle<JSObject*> input,
++                  JSONWriteCallback callback, void* data);
++
++} /* namespace JS */
++
++/**
++ * Performs the JSON.parse operation as specified by ECMAScript.
++ */
++extern JS_PUBLIC_API(bool)
++JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, JS::MutableHandle<JS::Value> vp);
++
++/**
++ * Performs the JSON.parse operation as specified by ECMAScript.
++ */
++extern JS_PUBLIC_API(bool)
++JS_ParseJSON(JSContext* cx, JS::Handle<JSString*> str, JS::MutableHandle<JS::Value> vp);
++
++/**
++ * Performs the JSON.parse operation as specified by ECMAScript, using the
++ * given |reviver| argument as the corresponding optional argument to that
++ * function.
++ */
++extern JS_PUBLIC_API(bool)
++JS_ParseJSONWithReviver(JSContext* cx, const char16_t* chars, uint32_t len,
++                        JS::Handle<JS::Value> reviver, JS::MutableHandle<JS::Value> vp);
++
++/**
++ * Performs the JSON.parse operation as specified by ECMAScript, using the
++ * given |reviver| argument as the corresponding optional argument to that
++ * function.
++ */
++extern JS_PUBLIC_API(bool)
++JS_ParseJSONWithReviver(JSContext* cx, JS::Handle<JSString*> str, JS::Handle<JS::Value> reviver,
++                        JS::MutableHandle<JS::Value> vp);
++
++#endif /* js_JSON_h */
+diff --git a/js/src/jsapi-tests/testParseJSON.cpp b/js/src/jsapi-tests/testParseJSON.cpp
+--- a/js/src/jsapi-tests/testParseJSON.cpp
++++ b/js/src/jsapi-tests/testParseJSON.cpp
+@@ -5,16 +5,17 @@
+  * 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/. */
+ 
+ #include <limits>
+ #include <string.h>
+ 
+ #include "builtin/String.h"
+ 
++#include "js/JSON.h"
+ #include "js/Printf.h"
+ #include "jsapi-tests/tests.h"
+ 
+ using namespace js;
+ 
+ class AutoInflatedString {
+     JSContext * const cx;
+     char16_t* chars_;
+diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
+--- a/js/src/jsapi.cpp
++++ b/js/src/jsapi.cpp
+@@ -55,16 +55,17 @@
+ #include "gc/WeakMap.h"
+ #include "jit/JitCommon.h"
+ #include "jit/JitSpewer.h"
+ #include "js/AutoByteString.h"
+ #include "js/CharacterEncoding.h"
+ #include "js/Conversions.h"
+ #include "js/Date.h"
+ #include "js/Initialization.h"
++#include "js/JSON.h"
+ #include "js/Proxy.h"
+ #include "js/SliceBudget.h"
+ #include "js/StableStringChars.h"
+ #include "js/StructuredClone.h"
+ #include "js/Utility.h"
+ #include "js/Wrapper.h"
+ #include "util/StringBuffer.h"
+ #include "util/Text.h"
+diff --git a/js/src/jsapi.h b/js/src/jsapi.h
+--- a/js/src/jsapi.h
++++ b/js/src/jsapi.h
+@@ -4693,71 +4693,16 @@ PropertySpecNameEqualsId(const char* nam
+  * during GC marking.
+  */
+ JS_PUBLIC_API(bool)
+ PropertySpecNameToPermanentId(JSContext* cx, const char* name, jsid* idp);
+ 
+ } /* namespace JS */
+ 
+ /************************************************************************/
+-/*
+- * JSON functions
+- */
+-typedef bool (* JSONWriteCallback)(const char16_t* buf, uint32_t len, void* data);
+-
+-/**
+- * JSON.stringify as specified by ES5.
+- */
+-JS_PUBLIC_API(bool)
+-JS_Stringify(JSContext* cx, JS::MutableHandleValue value, JS::HandleObject replacer,
+-             JS::HandleValue space, JSONWriteCallback callback, void* data);
+-
+-namespace JS {
+-
+-/**
+- * An API akin to JS_Stringify but with the goal of not having observable
+- * side-effects when the stringification is performed.  This means it does not
+- * allow a replacer or a custom space, and has the following constraints on its
+- * input:
+- *
+- * 1) The input must be a plain object or array, not an abitrary value.
+- * 2) Every value in the graph reached by the algorithm starting with this
+- *    object must be one of the following: null, undefined, a string (NOT a
+- *    string object!), a boolean, a finite number (i.e. no NaN or Infinity or
+- *    -Infinity), a plain object with no accessor properties, or an Array with
+- *    no holes.
+- *
+- * The actual behavior differs from JS_Stringify only in asserting the above and
+- * NOT attempting to get the "toJSON" property from things, since that could
+- * clearly have side-effects.
+- */
+-JS_PUBLIC_API(bool)
+-ToJSONMaybeSafely(JSContext* cx, JS::HandleObject input,
+-                  JSONWriteCallback callback, void* data);
+-
+-} /* namespace JS */
+-
+-/**
+- * JSON.parse as specified by ES5.
+- */
+-JS_PUBLIC_API(bool)
+-JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, JS::MutableHandleValue vp);
+-
+-JS_PUBLIC_API(bool)
+-JS_ParseJSON(JSContext* cx, JS::HandleString str, JS::MutableHandleValue vp);
+-
+-JS_PUBLIC_API(bool)
+-JS_ParseJSONWithReviver(JSContext* cx, const char16_t* chars, uint32_t len, JS::HandleValue reviver,
+-                        JS::MutableHandleValue vp);
+-
+-JS_PUBLIC_API(bool)
+-JS_ParseJSONWithReviver(JSContext* cx, JS::HandleString str, JS::HandleValue reviver,
+-                        JS::MutableHandleValue vp);
+-
+-/************************************************************************/
+ 
+ /**
+  * The default locale for the ECMAScript Internationalization API
+  * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
+  * Note that the Internationalization API encourages clients to
+  * specify their own locales.
+  * The locale string remains owned by the caller.
+  */
+diff --git a/js/src/moz.build b/js/src/moz.build
+--- a/js/src/moz.build
++++ b/js/src/moz.build
+@@ -137,16 +137,17 @@ EXPORTS.js += [
+     '../public/GCHashTable.h',
+     '../public/GCPolicyAPI.h',
+     '../public/GCVariant.h',
+     '../public/GCVector.h',
+     '../public/HashTable.h',
+     '../public/HeapAPI.h',
+     '../public/Id.h',
+     '../public/Initialization.h',
++    '../public/JSON.h',
+     '../public/MemoryMetrics.h',
+     '../public/Principals.h',
+     '../public/Printf.h',
+     '../public/ProfilingFrameIterator.h',
+     '../public/ProfilingStack.h',
+     '../public/ProtoKey.h',
+     '../public/Proxy.h',
+     '../public/Realm.h',
+diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
+--- a/js/src/shell/js.cpp
++++ b/js/src/shell/js.cpp
+@@ -73,16 +73,17 @@
+ #include "jit/Ion.h"
+ #include "jit/JitcodeMap.h"
+ #include "jit/JitRealm.h"
+ #include "jit/shared/CodeGenerator-shared.h"
+ #include "js/AutoByteString.h"
+ #include "js/Debug.h"
+ #include "js/GCVector.h"
+ #include "js/Initialization.h"
++#include "js/JSON.h"
+ #include "js/Printf.h"
+ #include "js/StableStringChars.h"
+ #include "js/StructuredClone.h"
+ #include "js/SweepingAPI.h"
+ #include "js/Wrapper.h"
+ #include "perf/jsperf.h"
+ #include "shell/jsoptparse.h"
+ #include "shell/jsshell.h"
+diff --git a/toolkit/mozapps/extensions/AddonManagerStartup.cpp b/toolkit/mozapps/extensions/AddonManagerStartup.cpp
+--- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp
++++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp
+@@ -3,16 +3,17 @@
+  * 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/. */
+ 
+ #include "AddonManagerStartup.h"
+ #include "AddonManagerStartup-inlines.h"
+ 
+ #include "jsapi.h"
+ #include "jsfriendapi.h"
++#include "js/JSON.h"
+ #include "js/TracingAPI.h"
+ #include "xpcpublic.h"
+ 
+ #include "mozilla/ClearOnShutdown.h"
+ #include "mozilla/EndianUtils.h"
+ #include "mozilla/Compression.h"
+ #include "mozilla/LinkedList.h"
+ #include "mozilla/FileUtils.h"
+diff --git a/tools/profiler/gecko/nsProfiler.cpp b/tools/profiler/gecko/nsProfiler.cpp
+--- a/tools/profiler/gecko/nsProfiler.cpp
++++ b/tools/profiler/gecko/nsProfiler.cpp
+@@ -14,16 +14,17 @@
+ #include "nsString.h"
+ #include "mozilla/Services.h"
+ #include "nsIObserverService.h"
+ #include "nsIInterfaceRequestor.h"
+ #include "nsILoadContext.h"
+ #include "nsIWebNavigation.h"
+ #include "nsIInterfaceRequestorUtils.h"
+ #include "shared-libraries.h"
++#include "js/JSON.h"
+ #include "js/Value.h"
+ #include "mozilla/ErrorResult.h"
+ #include "mozilla/dom/Promise.h"
+ #include "mozilla/dom/TypedArray.h"
+ #include "nsLocalFile.h"
+ #include "nsThreadUtils.h"
+ #include "ProfilerParent.h"
+ #include "platform.h"

+ 151 - 0
frg/work-js/mozilla-release/patches/1484943-63a1.patch

@@ -0,0 +1,151 @@
+# HG changeset patch
+# User Jeff Walden <jwalden@mit.edu>
+# Date 1534880090 18000
+# Node ID 2de8069003dc62198d7744d029454aeb9774fe29
+# Parent  87509a363c9ee2a38998a2e4dacc16e577a877ec
+Bug 1484943 - Don't assert when trying to formatToParts a NaN value whose sign bit is set.  r=anba
+
+diff --git a/js/src/builtin/intl/NumberFormat.cpp b/js/src/builtin/intl/NumberFormat.cpp
+--- a/js/src/builtin/intl/NumberFormat.cpp
++++ b/js/src/builtin/intl/NumberFormat.cpp
+@@ -30,16 +30,17 @@
+ #include "vm/JSObject-inl.h"
+ 
+ using namespace js;
+ 
+ using mozilla::AssertedCast;
+ using mozilla::IsFinite;
+ using mozilla::IsNaN;
+ using mozilla::IsNegative;
++using mozilla::SpecificNaN;
+ 
+ using js::intl::CallICU;
+ using js::intl::DateTimeFormatOptions;
+ using js::intl::GetAvailableLocales;
+ using js::intl::IcuLocale;
+ 
+ using JS::AutoStableStringChars;
+ 
+@@ -371,30 +372,36 @@ NewUNumberFormat(JSContext* cx, Handle<N
+     }
+     unum_setAttribute(nf, UNUM_GROUPING_USED, uUseGrouping);
+     unum_setAttribute(nf, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
+ 
+     return toClose.forget();
+ }
+ 
+ static JSString*
+-PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double x,
++PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double* x,
+                        UFieldPositionIterator* fpositer)
+ {
+-    return CallICU(cx, [nf, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
+-        return unum_formatDoubleForFields(nf, x, chars, size, fpositer, status);
++    // ICU incorrectly formats NaN values with the sign bit set, as if they
++    // were negative.  Replace all NaNs with a single pattern with sign bit
++    // unset ("positive", that is) until ICU is fixed.
++    if (MOZ_UNLIKELY(IsNaN(*x)))
++        *x = SpecificNaN<double>(0, 1);
++
++    return CallICU(cx, [nf, d = *x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
++        return unum_formatDoubleForFields(nf, d, chars, size, fpositer, status);
+     });
+ }
+ 
+ static bool
+ intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result)
+ {
+     // Passing null for |fpositer| will just not compute partition information,
+     // letting us common up all ICU number-formatting code.
+-    JSString* str = PartitionNumberPattern(cx, nf, x, nullptr);
++    JSString* str = PartitionNumberPattern(cx, nf, &x, nullptr);
+     if (!str)
+         return false;
+ 
+     result.setString(str);
+     return true;
+ }
+ 
+ using FieldType = ImmutablePropertyNamePtr JSAtomState::*;
+@@ -423,16 +430,21 @@ GetFieldTypeForNumberField(UNumberFormat
+ 
+       case UNUM_FRACTION_FIELD:
+         return &JSAtomState::fraction;
+ 
+       case UNUM_SIGN_FIELD: {
+         // Manual trawling through the ICU call graph appears to indicate that
+         // the basic formatting we request will never include a positive sign.
+         // But this analysis may be mistaken, so don't absolutely trust it.
++        MOZ_ASSERT(!IsNaN(d),
++                   "ICU appearing not to produce positive-sign among fields, "
++                   "plus our coercing all NaNs to one with sign bit unset "
++                   "(i.e. \"positive\"), means we shouldn't reach here with a "
++                   "NaN value");
+         return IsNegative(d) ? &JSAtomState::minusSign : &JSAtomState::plusSign;
+       }
+ 
+       case UNUM_PERCENT_FIELD:
+         return &JSAtomState::percentSign;
+ 
+       case UNUM_CURRENCY_FIELD:
+         return &JSAtomState::currency;
+@@ -473,17 +485,17 @@ intl_FormatNumberToParts(JSContext* cx, 
+     if (U_FAILURE(status)) {
+         intl::ReportInternalError(cx);
+         return false;
+     }
+ 
+     MOZ_ASSERT(fpositer);
+     ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer);
+ 
+-    RootedString overallResult(cx, PartitionNumberPattern(cx, nf, x, fpositer));
++    RootedString overallResult(cx, PartitionNumberPattern(cx, nf, &x, fpositer));
+     if (!overallResult)
+         return false;
+ 
+     RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
+     if (!partsArray)
+         return false;
+ 
+     // First, vacuum up fields in the overall formatted string.
+diff --git a/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js b/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js
+new file mode 100644
+--- /dev/null
++++ b/js/src/tests/non262/Intl/NumberFormat/formatting-NaN.js
+@@ -0,0 +1,35 @@
++// |reftest| skip-if(!this.hasOwnProperty("Intl"))
++// Any copyright is dedicated to the Public Domain.
++// http://creativecommons.org/licenses/publicdomain/
++
++//-----------------------------------------------------------------------------
++var BUGNUMBER = 1484943;
++var summary = "Don't crash doing format/formatToParts on -NaN";
++
++print(BUGNUMBER + ": " + summary);
++
++//-----------------------------------------------------------------------------
++
++assertEq("formatToParts" in Intl.NumberFormat(), true);
++
++var nf = new Intl.NumberFormat("en-US");
++var parts;
++
++var values = [NaN, -NaN];
++
++for (var v of values)
++{
++  assertEq(nf.format(v), "NaN");
++
++  parts = nf.formatToParts(v);
++  assertEq(parts.length, 1);
++  assertEq(parts[0].type, "nan");
++  assertEq(parts[0].value, "NaN");
++}
++
++//-----------------------------------------------------------------------------
++
++if (typeof reportCompare === "function")
++  reportCompare(0, 0, 'ok');
++
++print("Tests complete");
+

+ 2031 - 0
frg/work-js/mozilla-release/patches/1485347-3-64a1.patch

@@ -0,0 +1,2031 @@
+# HG changeset patch
+# User Ted Campbell <tcampbell@mozilla.com>
+# Date 1534993363 14400
+# Node ID 78d60776b5be58408a15de4346b302d1df1f8c14
+# Parent  dc4ca2dbed1fbabaa968cc1b802ba31406e45941
+Bug 1485347 - Part 3: Use mozilla::Span for JSScript::data arrays. r=jandem
+
+Replace various custom data-types in JSScript interfaces with
+mozilla::Span. This abstracts implementation details and supports
+range-based for-loops. Underlying storage is unchanged, but this sets us
+up to be able to more easily change it.
+
+MozReview-Commit-ID: FDfIYsAxTA8
+
+
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -10,16 +10,17 @@
+ 
+ #include "frontend/BytecodeEmitter.h"
+ 
+ #include "mozilla/ArrayUtils.h"
+ #include "mozilla/DebugOnly.h"
+ #include "mozilla/FloatingPoint.h"
+ #include "mozilla/Maybe.h"
+ #include "mozilla/PodOperations.h"
++#include "mozilla/ReverseIterator.h"
+ 
+ #include <string.h>
+ 
+ #include "jsnum.h"
+ #include "jstypes.h"
+ #include "jsutil.h"
+ 
+ #include "ds/Nestable.h"
+@@ -9625,22 +9626,22 @@ BytecodeEmitter::copySrcNotes(jssrcnote*
+     unsigned mainCount = main.notes.length();
+     // nsrcnotes includes SN_MAKE_TERMINATOR in addition to main.notes.
+     MOZ_ASSERT(mainCount == nsrcnotes - 1);
+     PodCopy(destination, main.notes.begin(), mainCount);
+     SN_MAKE_TERMINATOR(&destination[mainCount]);
+ }
+ 
+ void
+-CGNumberList::finish(ConstArray* array)
+-{
+-    MOZ_ASSERT(length() == array->length);
++CGNumberList::finish(mozilla::Span<GCPtrValue> array)
++{
++    MOZ_ASSERT(length() == array.size());
+ 
+     for (unsigned i = 0; i < length(); i++) {
+-        array->vector[i] = DoubleValue(list[i]);
++        array[i].init(DoubleValue(list[i]));
+     }
+ }
+ 
+ /*
+  * Find the index of the given object for code generator.
+  *
+  * Since the emitter refers to each parsed object only once, for the index we
+  * use the number of already indexed objects. We also add the object to a list
+@@ -9652,42 +9653,41 @@ CGObjectList::add(ObjectBox* objbox)
+ {
+     MOZ_ASSERT(!objbox->emitLink);
+     objbox->emitLink = lastbox;
+     lastbox = objbox;
+     return length++;
+ }
+ 
+ void
+-CGObjectList::finish(ObjectArray* array)
++CGObjectList::finish(mozilla::Span<GCPtrObject> array)
+ {
+     MOZ_ASSERT(length <= INDEX_LIMIT);
+-    MOZ_ASSERT(length == array->length);
+-
+-    js::GCPtrObject* cursor = array->vector + array->length;
++    MOZ_ASSERT(length == array.size());
++
+     ObjectBox* objbox = lastbox;
+-    do {
+-        --cursor;
+-        MOZ_ASSERT(!*cursor);
++    for (GCPtrObject& obj : mozilla::Reversed(array)) {
++        MOZ_ASSERT(obj == nullptr);
+         MOZ_ASSERT(objbox->object->isTenured());
+         if (objbox->isFunctionBox()) {
+             objbox->asFunctionBox()->finish();
+         }
+-        *cursor = objbox->object;
+-    } while ((objbox = objbox->emitLink) != nullptr);
+-    MOZ_ASSERT(cursor == array->vector);
++        obj.init(objbox->object);
++        objbox = objbox->emitLink;
++    }
+ }
+ 
+ void
+-CGScopeList::finish(ScopeArray* array)
++CGScopeList::finish(mozilla::Span<GCPtrScope> array)
+ {
+     MOZ_ASSERT(length() <= INDEX_LIMIT);
+-    MOZ_ASSERT(length() == array->length);
++    MOZ_ASSERT(length() == array.size());
++
+     for (uint32_t i = 0; i < length(); i++) {
+-        array->vector[i].init(vector[i]);
++        array[i].init(vector[i]);
+     }
+ }
+ 
+ bool
+ CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end)
+ {
+     MOZ_ASSERT(start <= end);
+     MOZ_ASSERT(size_t(uint32_t(start)) == start);
+@@ -9698,22 +9698,22 @@ CGTryNoteList::append(JSTryNoteKind kind
+     note.stackDepth = stackDepth;
+     note.start = uint32_t(start);
+     note.length = uint32_t(end - start);
+ 
+     return list.append(note);
+ }
+ 
+ void
+-CGTryNoteList::finish(TryNoteArray* array)
+-{
+-    MOZ_ASSERT(length() == array->length);
++CGTryNoteList::finish(mozilla::Span<JSTryNote> array)
++{
++    MOZ_ASSERT(length() == array.size());
+ 
+     for (unsigned i = 0; i < length(); i++) {
+-        array->vector[i] = list[i];
++        array[i] = list[i];
+     }
+ }
+ 
+ bool
+ CGScopeNoteList::append(uint32_t scopeIndex, uint32_t offset, bool inPrologue,
+                         uint32_t parent)
+ {
+     CGScopeNote note;
+@@ -9732,37 +9732,37 @@ CGScopeNoteList::recordEnd(uint32_t inde
+ {
+     MOZ_ASSERT(index < length());
+     MOZ_ASSERT(list[index].length == 0);
+     list[index].end = offset;
+     list[index].endInPrologue = inPrologue;
+ }
+ 
+ void
+-CGScopeNoteList::finish(ScopeNoteArray* array, uint32_t prologueLength)
+-{
+-    MOZ_ASSERT(length() == array->length);
++CGScopeNoteList::finish(mozilla::Span<ScopeNote> array, uint32_t prologueLength)
++{
++    MOZ_ASSERT(length() == array.size());
+ 
+     for (unsigned i = 0; i < length(); i++) {
+         if (!list[i].startInPrologue) {
+             list[i].start += prologueLength;
+         }
+         if (!list[i].endInPrologue && list[i].end != UINT32_MAX) {
+             list[i].end += prologueLength;
+         }
+         MOZ_ASSERT(list[i].end >= list[i].start);
+         list[i].length = list[i].end - list[i].start;
+-        array->vector[i] = list[i];
++        array[i] = list[i];
+     }
+ }
+ 
+ void
+-CGYieldAndAwaitOffsetList::finish(YieldAndAwaitOffsetArray& array, uint32_t prologueLength)
+-{
+-    MOZ_ASSERT(length() == array.length());
++CGYieldAndAwaitOffsetList::finish(mozilla::Span<uint32_t> array, uint32_t prologueLength)
++{
++    MOZ_ASSERT(length() == array.size());
+ 
+     for (unsigned i = 0; i < length(); i++) {
+         array[i] = prologueLength + list[i];
+     }
+ }
+ 
+ const JSSrcNoteSpec js_SrcNoteSpec[] = {
+ #define DEFINE_SRC_NOTE_SPEC(sym, name, arity) { name, arity },
+diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
+--- a/js/src/frontend/BytecodeEmitter.h
++++ b/js/src/frontend/BytecodeEmitter.h
+@@ -5,16 +5,17 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* JS bytecode generation. */
+ 
+ #ifndef frontend_BytecodeEmitter_h
+ #define frontend_BytecodeEmitter_h
+ 
+ #include "mozilla/Attributes.h"
++#include "mozilla/Span.h"
+ 
+ #include "ds/InlineTable.h"
+ #include "frontend/BCEParserHandle.h"
+ #include "frontend/EitherParser.h"
+ #include "frontend/JumpList.h"
+ #include "frontend/NameFunctions.h"
+ #include "frontend/SharedContext.h"
+ #include "frontend/SourceNotes.h"
+@@ -29,48 +30,48 @@ class CGNumberList {
+     Vector<double> list;
+ 
+   public:
+     explicit CGNumberList(JSContext* cx) : list(cx) {}
+     MOZ_MUST_USE bool append(double v) {
+         return list.append(v);
+     }
+     size_t length() const { return list.length(); }
+-    void finish(ConstArray* array);
++    void finish(mozilla::Span<GCPtrValue> array);
+ };
+ 
+ struct CGObjectList {
+     uint32_t            length;     /* number of emitted so far objects */
+     ObjectBox*          lastbox;   /* last emitted object */
+ 
+     CGObjectList() : length(0), lastbox(nullptr) {}
+ 
+     unsigned add(ObjectBox* objbox);
+-    void finish(ObjectArray* array);
++    void finish(mozilla::Span<GCPtrObject> array);
+ };
+ 
+ struct MOZ_STACK_CLASS CGScopeList {
+     Rooted<GCVector<Scope*>> vector;
+ 
+     explicit CGScopeList(JSContext* cx)
+       : vector(cx, GCVector<Scope*>(cx))
+     { }
+ 
+     bool append(Scope* scope) { return vector.append(scope); }
+     uint32_t length() const { return vector.length(); }
+-    void finish(ScopeArray* array);
++    void finish(mozilla::Span<GCPtrScope> array);
+ };
+ 
+ struct CGTryNoteList {
+     Vector<JSTryNote> list;
+     explicit CGTryNoteList(JSContext* cx) : list(cx) {}
+ 
+     MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end);
+     size_t length() const { return list.length(); }
+-    void finish(TryNoteArray* array);
++    void finish(mozilla::Span<JSTryNote> array);
+ };
+ 
+ struct CGScopeNote : public ScopeNote
+ {
+     // The end offset. Used to compute the length; may need adjusting first if
+     // in the prologue.
+     uint32_t end;
+ 
+@@ -84,28 +85,28 @@ struct CGScopeNote : public ScopeNote
+ struct CGScopeNoteList {
+     Vector<CGScopeNote> list;
+     explicit CGScopeNoteList(JSContext* cx) : list(cx) {}
+ 
+     MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset, bool inPrologue,
+                              uint32_t parent);
+     void recordEnd(uint32_t index, uint32_t offset, bool inPrologue);
+     size_t length() const { return list.length(); }
+-    void finish(ScopeNoteArray* array, uint32_t prologueLength);
++    void finish(mozilla::Span<ScopeNote> array, uint32_t prologueLength);
+ };
+ 
+ struct CGYieldAndAwaitOffsetList {
+     Vector<uint32_t> list;
+     uint32_t numYields;
+     uint32_t numAwaits;
+     explicit CGYieldAndAwaitOffsetList(JSContext* cx) : list(cx), numYields(0), numAwaits(0) {}
+ 
+     MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
+     size_t length() const { return list.length(); }
+-    void finish(YieldAndAwaitOffsetArray& array, uint32_t prologueLength);
++    void finish(mozilla::Span<uint32_t> array, uint32_t prologueLength);
+ };
+ 
+ // Have a few inline elements, so as to avoid heap allocation for tiny
+ // sequences.  See bug 1390526.
+ typedef Vector<jsbytecode, 64> BytecodeVector;
+ typedef Vector<jssrcnote, 64> SrcNotesVector;
+ 
+ // Used to control whether JSOP_CALL_IGNORES_RV is emitted for function calls.
+diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp
+--- a/js/src/jit/BaselineBailouts.cpp
++++ b/js/src/jit/BaselineBailouts.cpp
+@@ -484,49 +484,48 @@ GetNextNonLoopEntryPc(jsbytecode* pc, js
+ 
+ static bool
+ HasLiveStackValueAtDepth(JSScript* script, jsbytecode* pc, uint32_t stackDepth)
+ {
+     if (!script->hasTrynotes()) {
+         return false;
+     }
+ 
+-    JSTryNote* tn = script->trynotes()->vector;
+-    JSTryNote* tnEnd = tn + script->trynotes()->length;
+     uint32_t pcOffset = uint32_t(pc - script->main());
+-    for (; tn != tnEnd; ++tn) {
+-        if (pcOffset < tn->start) {
++
++    for (const JSTryNote& tn : script->trynotes()) {
++        if (pcOffset < tn.start) {
+             continue;
+         }
+-        if (pcOffset >= tn->start + tn->length) {
++        if (pcOffset >= tn.start + tn.length) {
+             continue;
+         }
+ 
+-        switch (tn->kind) {
++        switch (tn.kind) {
+           case JSTRY_FOR_IN:
+             // For-in loops have only the iterator on stack.
+-            if (stackDepth == tn->stackDepth) {
++            if (stackDepth == tn.stackDepth) {
+                 return true;
+             }
+             break;
+ 
+           case JSTRY_FOR_OF:
+             // For-of loops have the iterator, its next method and the
+             // result.value on stack.
+             // The iterator is below the result.value, the next method below
+             // the iterator.
+-            if (stackDepth == tn->stackDepth - 1 || stackDepth == tn->stackDepth - 2) {
++            if (stackDepth == tn.stackDepth - 1 || stackDepth == tn.stackDepth - 2) {
+                 return true;
+             }
+             break;
+ 
+           case JSTRY_DESTRUCTURING_ITERCLOSE:
+             // Destructuring code that need to call IteratorClose have both
+             // the iterator and the "done" value on the stack.
+-            if (stackDepth == tn->stackDepth || stackDepth == tn->stackDepth - 1) {
++            if (stackDepth == tn.stackDepth || stackDepth == tn.stackDepth - 1) {
+                 return true;
+             }
+             break;
+ 
+           default:
+             break;
+         }
+     }
+diff --git a/js/src/jit/BytecodeAnalysis.cpp b/js/src/jit/BytecodeAnalysis.cpp
+--- a/js/src/jit/BytecodeAnalysis.cpp
++++ b/js/src/jit/BytecodeAnalysis.cpp
+@@ -112,24 +112,22 @@ BytecodeAnalysis::init(TempAllocator& al
+                     infos_[targetOffset].jumpTarget = true;
+                 }
+                 pc2 += JUMP_OFFSET_LEN;
+             }
+             break;
+           }
+ 
+           case JSOP_TRY: {
+-            JSTryNote* tn = script_->trynotes()->vector;
+-            JSTryNote* tnlimit = tn + script_->trynotes()->length;
+-            for (; tn < tnlimit; tn++) {
+-                unsigned startOffset = script_->mainOffset() + tn->start;
++            for (const JSTryNote& tn : script_->trynotes()) {
++                unsigned startOffset = script_->mainOffset() + tn.start;
+                 if (startOffset == offset + 1) {
+-                    unsigned catchOffset = startOffset + tn->length;
++                    unsigned catchOffset = startOffset + tn.length;
+ 
+-                    if (tn->kind != JSTRY_FOR_IN) {
++                    if (tn.kind != JSTRY_FOR_IN) {
+                         infos_[catchOffset].init(stackDepth);
+                         infos_[catchOffset].jumpTarget = true;
+                     }
+                 }
+             }
+ 
+             // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
+             // jump over the catch/finally blocks.
+diff --git a/js/src/jit/IonControlFlow.cpp b/js/src/jit/IonControlFlow.cpp
+--- a/js/src/jit/IonControlFlow.cpp
++++ b/js/src/jit/IonControlFlow.cpp
+@@ -559,20 +559,18 @@ ControlFlowGenerator::processLabelEnd(CF
+ 
+ ControlFlowGenerator::ControlStatus
+ ControlFlowGenerator::processTry()
+ {
+     MOZ_ASSERT(JSOp(*pc) == JSOP_TRY);
+ 
+     // Try-finally is not yet supported.
+     if (!checkedTryFinally_) {
+-        JSTryNote* tn = script->trynotes()->vector;
+-        JSTryNote* tnlimit = tn + script->trynotes()->length;
+-        for (; tn < tnlimit; tn++) {
+-            if (tn->kind == JSTRY_FINALLY) {
++        for (const JSTryNote& tn : script->trynotes()) {
++            if (tn.kind == JSTRY_FINALLY) {
+                 return ControlStatus::Abort;
+             }
+         }
+         checkedTryFinally_ = true;
+     }
+ 
+     jssrcnote* sn = GetSrcNote(gsn, script, pc);
+     MOZ_ASSERT(SN_TYPE(sn) == SRC_TRY);
+diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp
+--- a/js/src/jit/JitFrames.cpp
++++ b/js/src/jit/JitFrames.cpp
+@@ -100,17 +100,17 @@ ReadFrameBooleanSlot(JitFrameLayout* fp,
+ static uint32_t
+ NumArgAndLocalSlots(const InlineFrameIterator& frame)
+ {
+     JSScript* script = frame.script();
+     return CountArgSlots(script, frame.maybeCalleeTemplate()) + script->nfixed();
+ }
+ 
+ static void
+-CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, JSTryNote* tn)
++CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, const JSTryNote* tn)
+ {
+     MOZ_ASSERT(tn->kind == JSTRY_FOR_IN ||
+                tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE);
+ 
+     bool isDestructuring = tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE;
+     MOZ_ASSERT_IF(!isDestructuring, tn->stackDepth > 0);
+     MOZ_ASSERT_IF(isDestructuring, tn->stackDepth > 1);
+ 
+@@ -218,17 +218,17 @@ HandleExceptionIon(JSContext* cx, const 
+     RootedScript script(cx, frame.script());
+     if (!script->hasTrynotes()) {
+         return;
+     }
+ 
+     bool inForOfIterClose = false;
+ 
+     for (TryNoteIterIon tni(cx, frame); !tni.done(); ++tni) {
+-        JSTryNote* tn = *tni;
++        const JSTryNote* tn = *tni;
+ 
+         switch (tn->kind) {
+           case JSTRY_FOR_IN:
+           case JSTRY_DESTRUCTURING_ITERCLOSE:
+             // See corresponding comment in ProcessTryNotes.
+             if (inForOfIterClose) {
+                 break;
+             }
+@@ -300,27 +300,27 @@ OnLeaveBaselineFrame(JSContext* cx, cons
+ static inline void
+ ForcedReturn(JSContext* cx, const JSJitFrameIter& frame, jsbytecode* pc,
+              ResumeFromException* rfe)
+ {
+     OnLeaveBaselineFrame(cx, frame, pc, rfe, true);
+ }
+ 
+ static inline void
+-BaselineFrameAndStackPointersFromTryNote(JSTryNote* tn, const JSJitFrameIter& frame,
++BaselineFrameAndStackPointersFromTryNote(const JSTryNote* tn, const JSJitFrameIter& frame,
+                                          uint8_t** framePointer, uint8_t** stackPointer)
+ {
+     JSScript* script = frame.baselineFrame()->script();
+     *framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
+     *stackPointer = *framePointer - BaselineFrame::Size() -
+                     (script->nfixed() + tn->stackDepth) * sizeof(Value);
+ }
+ 
+ static void
+-SettleOnTryNote(JSContext* cx, JSTryNote* tn, const JSJitFrameIter& frame,
++SettleOnTryNote(JSContext* cx, const JSTryNote* tn, const JSJitFrameIter& frame,
+                 EnvironmentIter& ei, ResumeFromException* rfe, jsbytecode** pc)
+ {
+     RootedScript script(cx, frame.baselineFrame()->script());
+ 
+     // Unwind environment chain (pop block objects).
+     if (cx->isExceptionPending()) {
+         UnwindEnvironment(cx, ei, UnwindEnvironmentToTryPc(script, tn));
+     }
+@@ -371,17 +371,17 @@ class TryNoteIterBaseline : public TryNo
+ // Close all live iterators on a BaselineFrame due to exception unwinding. The
+ // pc parameter is updated to where the envs have been unwound to.
+ static void
+ CloseLiveIteratorsBaselineForUncatchableException(JSContext* cx, const JSJitFrameIter& frame,
+                                                   jsbytecode* pc)
+ {
+     bool inForOfIterClose = false;
+     for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), pc); !tni.done(); ++tni) {
+-        JSTryNote* tn = *tni;
++        const JSTryNote* tn = *tni;
+         switch (tn->kind) {
+           case JSTRY_FOR_IN: {
+             // See corresponding comment in ProcessTryNotes.
+             if (inForOfIterClose) {
+                 break;
+             }
+ 
+             uint8_t* framePointer;
+@@ -410,17 +410,17 @@ CloseLiveIteratorsBaselineForUncatchable
+ static bool
+ ProcessTryNotesBaseline(JSContext* cx, const JSJitFrameIter& frame, EnvironmentIter& ei,
+                         ResumeFromException* rfe, jsbytecode** pc)
+ {
+     RootedScript script(cx, frame.baselineFrame()->script());
+     bool inForOfIterClose = false;
+ 
+     for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), *pc); !tni.done(); ++tni) {
+-        JSTryNote* tn = *tni;
++        const JSTryNote* tn = *tni;
+ 
+         MOZ_ASSERT(cx->isExceptionPending());
+         switch (tn->kind) {
+           case JSTRY_CATCH: {
+             // If we're closing a legacy generator, we have to skip catch
+             // blocks.
+             if (cx->isClosingGenerator()) {
+                 break;
+diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
+--- a/js/src/shell/js.cpp
++++ b/js/src/shell/js.cpp
+@@ -3067,63 +3067,59 @@ TryNotes(JSContext* cx, HandleScript scr
+     if (!script->hasTrynotes()) {
+         return true;
+     }
+ 
+     if (!sp->put("\nException table:\nkind               stack    start      end\n")) {
+         return false;
+     }
+ 
+-    JSTryNote* tn = script->trynotes()->vector;
+-    JSTryNote* tnlimit = tn + script->trynotes()->length;
+-    do {
+-        uint32_t startOff = script->pcToOffset(script->main()) + tn->start;
++    for (const JSTryNote& tn : script->trynotes()) {
++        uint32_t startOff = script->pcToOffset(script->main()) + tn.start;
+         if (!sp->jsprintf(" %-16s %6u %8u %8u\n",
+-                          TryNoteName(static_cast<JSTryNoteKind>(tn->kind)),
+-                          tn->stackDepth, startOff, startOff + tn->length))
++                          TryNoteName(static_cast<JSTryNoteKind>(tn.kind)),
++                          tn.stackDepth, startOff, startOff + tn.length))
+         {
+             return false;
+         }
+-    } while (++tn != tnlimit);
++    }
+     return true;
+ }
+ 
+ static MOZ_MUST_USE bool
+ ScopeNotes(JSContext* cx, HandleScript script, Sprinter* sp)
+ {
+     if (!script->hasScopeNotes()) {
+         return true;
+     }
+ 
+     if (!sp->put("\nScope notes:\n   index   parent    start      end\n")) {
+         return false;
+     }
+ 
+-    ScopeNoteArray* notes = script->scopeNotes();
+-    for (uint32_t i = 0; i < notes->length; i++) {
+-        const ScopeNote* note = &notes->vector[i];
+-        if (note->index == ScopeNote::NoScopeIndex) {
++    for (const ScopeNote& note : script->scopeNotes()) {
++        if (note.index == ScopeNote::NoScopeIndex) {
+             if (!sp->jsprintf("%8s ", "(none)")) {
+                 return false;
+             }
+         } else {
+-            if (!sp->jsprintf("%8u ", note->index)) {
+-                return false;
+-            }
+-        }
+-        if (note->parent == ScopeNote::NoScopeIndex) {
++            if (!sp->jsprintf("%8u ", note.index)) {
++                return false;
++            }
++        }
++        if (note.parent == ScopeNote::NoScopeIndex) {
+             if (!sp->jsprintf("%8s ", "(none)")) {
+                 return false;
+             }
+         } else {
+-            if (!sp->jsprintf("%8u ", note->parent)) {
+-                return false;
+-            }
+-        }
+-        if (!sp->jsprintf("%8u %8u\n", note->start, note->start + note->length)) {
++            if (!sp->jsprintf("%8u ", note.parent)) {
++                return false;
++            }
++        }
++        if (!sp->jsprintf("%8u %8u\n", note.start, note.start + note.length)) {
+             return false;
+         }
+     }
+     return true;
+ }
+ 
+ static MOZ_MUST_USE bool
+ DisassembleScript(JSContext* cx, HandleScript script, HandleFunction fun,
+@@ -3184,19 +3180,17 @@ DisassembleScript(JSContext* cx, HandleS
+     if (!TryNotes(cx, script, sp)) {
+         return false;
+     }
+     if (!ScopeNotes(cx, script, sp)) {
+         return false;
+     }
+ 
+     if (recursive && script->hasObjects()) {
+-        ObjectArray* objects = script->objects();
+-        for (unsigned i = 0; i != objects->length; ++i) {
+-            JSObject* obj = objects->vector[i];
++        for (JSObject* obj : script->objects()) {
+             if (obj->is<JSFunction>()) {
+                 if (!sp->put("\n")) {
+                     return false;
+                 }
+ 
+                 RootedFunction fun(cx, &obj->as<JSFunction>());
+                 if (fun->isInterpreted()) {
+                     RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
+diff --git a/js/src/vm/BytecodeUtil.cpp b/js/src/vm/BytecodeUtil.cpp
+--- a/js/src/vm/BytecodeUtil.cpp
++++ b/js/src/vm/BytecodeUtil.cpp
+@@ -8,16 +8,17 @@
+  * JS bytecode descriptors, disassemblers, and (expression) decompilers.
+  */
+ 
+ #include "vm/BytecodeUtil-inl.h"
+ 
+ #define __STDC_FORMAT_MACROS
+ 
+ #include "mozilla/Attributes.h"
++#include "mozilla/ReverseIterator.h"
+ #include "mozilla/Sprintf.h"
+ #include "mozilla/Vector.h"
+ 
+ #include <algorithm>
+ #include <ctype.h>
+ #include <inttypes.h>
+ #include <stdio.h>
+ #include <string.h>
+@@ -940,29 +941,27 @@ BytecodeParser::parse()
+             break;
+           }
+ 
+           case JSOP_TRY: {
+             // Everything between a try and corresponding catch or finally is conditional.
+             // Note that there is no problem with code which is skipped by a thrown
+             // exception but is not caught by a later handler in the same function:
+             // no more code will execute, and it does not matter what is defined.
+-            JSTryNote* tn = script_->trynotes()->vector;
+-            JSTryNote* tnlimit = tn + script_->trynotes()->length;
+-            for (; tn < tnlimit; tn++) {
+-                uint32_t startOffset = script_->mainOffset() + tn->start;
++            for (const JSTryNote& tn : script_->trynotes()) {
++                uint32_t startOffset = script_->mainOffset() + tn.start;
+                 if (startOffset == offset + 1) {
+-                    uint32_t catchOffset = startOffset + tn->length;
+-                    if (tn->kind == JSTRY_CATCH) {
++                    uint32_t catchOffset = startOffset + tn.length;
++                    if (tn.kind == JSTRY_CATCH) {
+                         if (!addJump(catchOffset, &nextOffset, stackDepth, offsetStack,
+                                      pc, JumpKind::TryCatch))
+                         {
+                             return false;
+                         }
+-                    } else if (tn->kind == JSTRY_FINALLY) {
++                    } else if (tn.kind == JSTRY_FINALLY) {
+                         if (!addJump(catchOffset, &nextOffset, stackDepth, offsetStack,
+                                      pc, JumpKind::TryFinally))
+                         {
+                             return false;
+                         }
+                     }
+                 }
+             }
+@@ -1450,25 +1449,22 @@ Disassemble1(JSContext* cx, HandleScript
+     int i;
+     switch (JOF_TYPE(cs->format)) {
+       case JOF_BYTE:
+           // Scan the trynotes to find the associated catch block
+           // and make the try opcode look like a jump instruction
+           // with an offset. This simplifies code coverage analysis
+           // based on this disassembled output.
+           if (op == JSOP_TRY) {
+-              TryNoteArray* trynotes = script->trynotes();
+-              uint32_t i;
+               size_t mainOffset = script->mainOffset();
+-              for(i = 0; i < trynotes->length; i++) {
+-                  JSTryNote note = trynotes->vector[i];
+-                  if (note.kind == JSTRY_CATCH && note.start + mainOffset == loc + 1) {
++              for (const JSTryNote& tn : script->trynotes()) {
++                  if (tn.kind == JSTRY_CATCH && tn.start + mainOffset == loc + 1) {
+                       if (!sp->jsprintf(" %u (%+d)",
+-                                        unsigned(loc + note.length + 1),
+-                                        int(note.length + 1)))
++                                        unsigned(loc + tn.length + 1),
++                                        int(tn.length + 1)))
+                       {
+                           return 0;
+                       }
+                       break;
+                   }
+               }
+           }
+         break;
+@@ -3115,20 +3111,18 @@ GenerateLcovInfo(JSContext* cx, JS::Real
+ 
+             // Iterate from the last to the first object in order to have
+             // the functions them visited in the opposite order when popping
+             // elements from the stack of remaining scripts, such that the
+             // functions are more-less listed with increasing line numbers.
+             if (!script->hasObjects()) {
+                 continue;
+             }
+-            size_t idx = script->objects()->length;
+-            while (idx--) {
+-                JSObject* obj = script->getObject(idx);
+-
++            auto objects = script->objects();
++            for (JSObject* obj : mozilla::Reversed(objects)) {
+                 // Only continue on JSFunction objects.
+                 if (!obj->is<JSFunction>()) {
+                     continue;
+                 }
+                 fun = &obj->as<JSFunction>();
+ 
+                 // Let's skip wasm for now.
+                 if (!fun->isInterpreted()) {
+diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
+--- a/js/src/vm/Debugger.cpp
++++ b/js/src/vm/Debugger.cpp
+@@ -6233,22 +6233,20 @@ DebuggerScript_getChildScripts(JSContext
+     if (!result) {
+         return false;
+     }
+     if (script->hasObjects()) {
+         // script->savedCallerFun indicates that this is a direct eval script
+         // and the calling function is stored as script->objects()->vector[0].
+         // It is not really a child script of this script, so skip it using
+         // innerObjectsStart().
+-        ObjectArray* objects = script->objects();
+         RootedFunction fun(cx);
+         RootedScript funScript(cx);
+-        RootedObject obj(cx), s(cx);
+-        for (uint32_t i = 0; i < objects->length; i++) {
+-            obj = objects->vector[i];
++        RootedObject s(cx);
++        for (const GCPtrObject& obj : script->objects()) {
+             if (obj->is<JSFunction>()) {
+                 fun = &obj->as<JSFunction>();
+                 // The inner function could be a wasm native.
+                 if (fun->isNative()) {
+                     continue;
+                 }
+                 funScript = GetOrCreateFunctionScript(cx, fun);
+                 if (!funScript) {
+@@ -6424,23 +6422,21 @@ class FlowGraphSummary {
+                     pc += step;
+                 }
+             } else if (op == JSOP_TRY) {
+                 // As there is no literal incoming edge into the catch block, we
+                 // make a fake one by copying the JSOP_TRY location, as-if this
+                 // was an incoming edge of the catch block. This is needed
+                 // because we only report offsets of entry points which have
+                 // valid incoming edges.
+-                JSTryNote* tn = script->trynotes()->vector;
+-                JSTryNote* tnlimit = tn + script->trynotes()->length;
+-                for (; tn < tnlimit; tn++) {
+-                    uint32_t startOffset = script->mainOffset() + tn->start;
++                for (const JSTryNote& tn : script->trynotes()) {
++                    uint32_t startOffset = script->mainOffset() + tn.start;
+                     if (startOffset == r.frontOffset() + 1) {
+-                        uint32_t catchOffset = startOffset + tn->length;
+-                        if (tn->kind == JSTRY_CATCH || tn->kind == JSTRY_FINALLY) {
++                        uint32_t catchOffset = startOffset + tn.length;
++                        if (tn.kind == JSTRY_CATCH || tn.kind == JSTRY_FINALLY) {
+                             addEdge(lineno, column, catchOffset);
+                         }
+                     }
+                 }
+             }
+ 
+             prevLineno = lineno;
+             prevColumn = column;
+@@ -7450,27 +7446,24 @@ class DebuggerScriptIsInCatchScopeMatche
+             return false;
+         }
+ 
+         // Try note ranges are relative to the mainOffset of the script, so adjust
+         // offset accordingly.
+         size_t offset = offset_ - script->mainOffset();
+ 
+         if (script->hasTrynotes()) {
+-            JSTryNote* tnBegin = script->trynotes()->vector;
+-            JSTryNote* tnEnd = tnBegin + script->trynotes()->length;
+-            while (tnBegin != tnEnd) {
+-                if (tnBegin->start <= offset &&
+-                    offset <= tnBegin->start + tnBegin->length &&
+-                    tnBegin->kind == JSTRY_CATCH)
++            for (const JSTryNote& tn : script->trynotes()) {
++                if (tn.start <= offset &&
++                    offset <= tn.start + tn.length &&
++                    tn.kind == JSTRY_CATCH)
+                 {
+                     isInCatch_ = true;
+                     return true;
+                 }
+-                ++tnBegin;
+             }
+         }
+         isInCatch_ = false;
+         return true;
+     }
+     ReturnType match(Handle<LazyScript*> lazyScript) {
+         RootedScript script(cx_, DelazifyScript(cx_, lazyScript));
+         if (!script) {
+diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp
+--- a/js/src/vm/EnvironmentObject.cpp
++++ b/js/src/vm/EnvironmentObject.cpp
+@@ -3907,21 +3907,19 @@ RemoveReferencedNames(JSContext* cx, Han
+         }
+ 
+         if (name) {
+             remainingNames.remove(name);
+         }
+     }
+ 
+     if (script->hasObjects()) {
+-        ObjectArray* objects = script->objects();
+         RootedFunction fun(cx);
+         RootedScript innerScript(cx);
+-        for (size_t i = 0; i < objects->length; i++) {
+-            JSObject* obj = objects->vector[i];
++        for (JSObject* obj : script->objects()) {
+             if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
+                 fun = &obj->as<JSFunction>();
+                 innerScript = JSFunction::getOrCreateScript(cx, fun);
+                 if (!innerScript) {
+                     return false;
+                 }
+ 
+                 if (!RemoveReferencedNames(cx, innerScript, remainingNames)) {
+@@ -3979,21 +3977,19 @@ AnalyzeEntrainedVariablesInScript(JSCont
+             buf.printf(" ");
+             buf.putString(r.front());
+         }
+ 
+         printf("%s\n", buf.string());
+     }
+ 
+     if (innerScript->hasObjects()) {
+-        ObjectArray* objects = innerScript->objects();
+         RootedFunction fun(cx);
+         RootedScript innerInnerScript(cx);
+-        for (size_t i = 0; i < objects->length; i++) {
+-            JSObject* obj = objects->vector[i];
++        for (JSObject* obj : script->objects()) {
+             if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
+                 fun = &obj->as<JSFunction>();
+                 innerInnerScript = JSFunction::getOrCreateScript(cx, fun);
+                 if (!innerInnerScript ||
+                     !AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript))
+                 {
+                     return false;
+                 }
+@@ -4017,21 +4013,19 @@ AnalyzeEntrainedVariablesInScript(JSCont
+ // |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|.
+ bool
+ js::AnalyzeEntrainedVariables(JSContext* cx, HandleScript script)
+ {
+     if (!script->hasObjects()) {
+         return true;
+     }
+ 
+-    ObjectArray* objects = script->objects();
+     RootedFunction fun(cx);
+     RootedScript innerScript(cx);
+-    for (size_t i = 0; i < objects->length; i++) {
+-        JSObject* obj = objects->vector[i];
++    for (JSObject* obj : script->objects()) {
+         if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
+             fun = &obj->as<JSFunction>();
+             innerScript = JSFunction::getOrCreateScript(cx, fun);
+             if (!innerScript) {
+                 return false;
+             }
+ 
+             if (script->functionDelazifying() && script->functionDelazifying()->needsCallObject()) {
+diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
+--- a/js/src/vm/Interpreter.cpp
++++ b/js/src/vm/Interpreter.cpp
+@@ -1298,17 +1298,17 @@ js::UnwindAllEnvironmentsInFrame(JSConte
+ // block. We cannot unwind to *after* the JSOP_TRY, because that might be the
+ // first opcode of an inner scope, with the same problem as above. e.g.,
+ //
+ // try { { let x; } }
+ //
+ // will have no pc location distinguishing the try block scope from the inner
+ // let block scope.
+ jsbytecode*
+-js::UnwindEnvironmentToTryPc(JSScript* script, JSTryNote* tn)
++js::UnwindEnvironmentToTryPc(JSScript* script, const JSTryNote* tn)
+ {
+     jsbytecode* pc = script->main() + tn->start;
+     if (tn->kind == JSTRY_CATCH || tn->kind == JSTRY_FINALLY) {
+         pc -= JSOP_TRY_LENGTH;
+         MOZ_ASSERT(*pc == JSOP_TRY);
+     } else if (tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE) {
+         pc -= JSOP_TRY_DESTRUCTURING_ITERCLOSE_LENGTH;
+         MOZ_ASSERT(*pc == JSOP_TRY_DESTRUCTURING_ITERCLOSE);
+@@ -1322,17 +1322,17 @@ ForcedReturn(JSContext* cx, InterpreterR
+     bool ok = Debugger::onLeaveFrame(cx, regs.fp(), regs.pc, true);
+     // Point the frame to the end of the script, regardless of error. The
+     // caller must jump to the correct continuation depending on 'ok'.
+     regs.setToEndOfScript();
+     return ok;
+ }
+ 
+ static void
+-SettleOnTryNote(JSContext* cx, JSTryNote* tn, EnvironmentIter& ei, InterpreterRegs& regs)
++SettleOnTryNote(JSContext* cx, const JSTryNote* tn, EnvironmentIter& ei, InterpreterRegs& regs)
+ {
+     // Unwind the environment to the beginning of the JSOP_TRY.
+     UnwindEnvironment(cx, ei, UnwindEnvironmentToTryPc(regs.fp()->script(), tn));
+ 
+     // Set pc to the first bytecode after the the try note to point
+     // to the beginning of catch or finally.
+     regs.pc = regs.fp()->script()->main() + tn->start + tn->length;
+     regs.sp = regs.spForStackDepth(tn->stackDepth);
+@@ -1358,17 +1358,17 @@ class TryNoteIterInterpreter : public Tr
+ 
+ static void
+ UnwindIteratorsForUncatchableException(JSContext* cx, const InterpreterRegs& regs)
+ {
+     // c.f. the regular (catchable) TryNoteIterInterpreter loop in
+     // ProcessTryNotes.
+     bool inForOfIterClose = false;
+     for (TryNoteIterInterpreter tni(cx, regs); !tni.done(); ++tni) {
+-        JSTryNote* tn = *tni;
++        const JSTryNote* tn = *tni;
+         switch (tn->kind) {
+           case JSTRY_FOR_IN: {
+             // See corresponding comment in ProcessTryNotes.
+             if (inForOfIterClose) {
+                 break;
+             }
+ 
+             Value* sp = regs.spForStackDepth(tn->stackDepth);
+@@ -1398,17 +1398,17 @@ enum HandleErrorContinuation
+     FinallyContinuation
+ };
+ 
+ static HandleErrorContinuation
+ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs)
+ {
+     bool inForOfIterClose = false;
+     for (TryNoteIterInterpreter tni(cx, regs); !tni.done(); ++tni) {
+-        JSTryNote* tn = *tni;
++        const JSTryNote* tn = *tni;
+ 
+         switch (tn->kind) {
+           case JSTRY_CATCH:
+             /* Catch cannot intercept the closing of a generator. */
+             if (cx->isClosingGenerator()) {
+                 break;
+             }
+ 
+diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h
+--- a/js/src/vm/Interpreter.h
++++ b/js/src/vm/Interpreter.h
+@@ -340,27 +340,28 @@ UnwindEnvironment(JSContext* cx, Environ
+ 
+ // Unwind all environments.
+ extern void
+ UnwindAllEnvironmentsInFrame(JSContext* cx, EnvironmentIter& ei);
+ 
+ // Compute the pc needed to unwind the scope to the beginning of the block
+ // pointed to by the try note.
+ extern jsbytecode*
+-UnwindEnvironmentToTryPc(JSScript* script, JSTryNote* tn);
++UnwindEnvironmentToTryPc(JSScript* script, const JSTryNote* tn);
+ 
+ template <class StackDepthOp>
+ class MOZ_STACK_CLASS TryNoteIter
+ {
+     RootedScript script_;
+     uint32_t pcOffset_;
+-    JSTryNote* tn_;
+-    JSTryNote* tnEnd_;
+     StackDepthOp getStackDepth_;
+ 
++    const JSTryNote* tn_;
++    const JSTryNote* tnEnd_;
++
+     void settle() {
+         for (; tn_ != tnEnd_; ++tn_) {
+             /* If pc is out of range, try the next one. */
+             if (pcOffset_ - tn_->start >= tn_->length) {
+                 continue;
+             }
+ 
+             /*
+@@ -391,31 +392,34 @@ class MOZ_STACK_CLASS TryNoteIter
+   public:
+     TryNoteIter(JSContext* cx, JSScript* script, jsbytecode* pc,
+                 StackDepthOp getStackDepth)
+       : script_(cx, script),
+         pcOffset_(pc - script->main()),
+         getStackDepth_(getStackDepth)
+     {
+         if (script->hasTrynotes()) {
+-            tn_ = script->trynotes()->vector;
+-            tnEnd_ = tn_ + script->trynotes()->length;
++            // NOTE: The Span is a temporary so we can't use begin()/end()
++            // here or the iterator will outlive the span.
++            auto trynotes = script->trynotes();
++            tn_ = trynotes.data();
++            tnEnd_ = tn_ + trynotes.size();
+         } else {
+             tn_ = tnEnd_ = nullptr;
+         }
+         settle();
+     }
+ 
+     void operator++() {
+         ++tn_;
+         settle();
+     }
+ 
+     bool done() const { return tn_ == tnEnd_; }
+-    JSTryNote* operator*() const { return tn_; }
++    const JSTryNote* operator*() const { return tn_; }
+ };
+ 
+ bool
+ HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame, bool ok);
+ 
+ /************************************************************************/
+ 
+ bool
+diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
+--- a/js/src/vm/JSScript.cpp
++++ b/js/src/vm/JSScript.cpp
+@@ -299,21 +299,20 @@ XDRRelazificationInfo(XDRState<mode>* xd
+     // have any.
+ 
+     return Ok();
+ }
+ 
+ static inline uint32_t
+ FindScopeIndex(JSScript* script, Scope& scope)
+ {
+-    ScopeArray* scopes = script->scopes();
+-    GCPtrScope* vector = scopes->vector;
+-    unsigned length = scopes->length;
++    auto scopes = script->scopes();
++    unsigned length = scopes.size();
+     for (uint32_t i = 0; i < length; ++i) {
+-        if (vector[i] == &scope) {
++        if (scopes[i] == &scope) {
+             return i;
+         }
+     }
+ 
+     MOZ_CRASH("Scope not found");
+ }
+ 
+ enum XDRClassKind {
+@@ -353,17 +352,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+         HasNonSyntacticScope,
+         HasInnerFunctions,
+         NeedsHomeObject,
+         IsDerivedClassConstructor,
+         IsDefaultClassConstructor,
+     };
+ 
+     uint32_t length, lineno, column, nfixed, nslots;
+-    uint32_t natoms, nsrcnotes, i;
++    uint32_t natoms, nsrcnotes;
+     uint32_t nconsts, nobjects, nscopes, nregexps, ntrynotes, nscopenotes, nyieldoffsets;
+     uint32_t prologueLength;
+     uint32_t funLength = 0;
+     uint32_t nTypeSets = 0;
+     uint32_t scriptBits = 0;
+     uint32_t bodyScopeIndex = 0;
+ 
+     JSContext* cx = xdr->cx();
+@@ -403,30 +402,30 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+         nslots = script->nslots();
+ 
+         bodyScopeIndex = script->bodyScopeIndex();
+         natoms = script->natoms();
+ 
+         nsrcnotes = script->numNotes();
+ 
+         if (script->hasConsts()) {
+-            nconsts = script->consts()->length;
++            nconsts = script->consts().size();
+         }
+         if (script->hasObjects()) {
+-            nobjects = script->objects()->length;
++            nobjects = script->objects().size();
+         }
+-        nscopes = script->scopes()->length;
++        nscopes = script->scopes().size();
+         if (script->hasTrynotes()) {
+-            ntrynotes = script->trynotes()->length;
++            ntrynotes = script->trynotes().size();
+         }
+         if (script->hasScopeNotes()) {
+-            nscopenotes = script->scopeNotes()->length;
++            nscopenotes = script->scopeNotes().size();
+         }
+         if (script->hasYieldAndAwaitOffsets()) {
+-            nyieldoffsets = script->yieldAndAwaitOffsets().length();
++            nyieldoffsets = script->yieldAndAwaitOffsets().size();
+         }
+ 
+         nTypeSets = script->nTypeSets();
+         funLength = script->funLength();
+ 
+         if (script->noScriptRval()) {
+             scriptBits |= (1 << NoScriptRval);
+         }
+@@ -699,17 +698,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+             script->freeScriptData();
+         }
+     });
+ 
+     jsbytecode* code = script->code();
+     MOZ_TRY(xdr->codeBytes(code, length));
+     MOZ_TRY(xdr->codeBytes(code + length, nsrcnotes));
+ 
+-    for (i = 0; i != natoms; ++i) {
++    for (uint32_t i = 0; i != natoms; ++i) {
+         if (mode == XDR_DECODE) {
+             RootedAtom tmp(cx);
+             MOZ_TRY(XDRAtom(xdr, &tmp));
+             script->atoms()[i].init(tmp);
+         } else {
+             RootedAtom tmp(cx, script->atoms()[i]);
+             MOZ_TRY(XDRAtom(xdr, &tmp));
+         }
+@@ -718,37 +717,36 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+     scriptDataGuard.release();
+     if (mode == XDR_DECODE) {
+         if (!script->shareScriptData(cx)) {
+             return xdr->fail(JS::TranscodeResult_Throw);
+         }
+     }
+ 
+     if (nconsts) {
+-        GCPtrValue* vector = script->consts()->vector;
+         RootedValue val(cx);
+-        for (i = 0; i != nconsts; ++i) {
++        for (GCPtrValue& elem : script->consts()) {
+             if (mode == XDR_ENCODE) {
+-                val = vector[i];
++                val = elem.get();
+             }
+             MOZ_TRY(XDRScriptConst(xdr, &val));
+             if (mode == XDR_DECODE) {
+-                vector[i].init(val);
++                elem.init(val);
+             }
+         }
+     }
+ 
+     {
+         MOZ_ASSERT(nscopes != 0);
+-        GCPtrScope* vector = script->scopes()->vector;
++        GCPtrScope* vector = script->scopes().data();
+         RootedScope scope(cx);
+         RootedScope enclosing(cx);
+         ScopeKind scopeKind;
+         uint32_t enclosingScopeIndex = 0;
+-        for (i = 0; i != nscopes; ++i) {
++        for (uint32_t i = 0; i != nscopes; ++i) {
+             if (mode == XDR_ENCODE) {
+                 scope = vector[i];
+                 scopeKind = scope->kind();
+             } else {
+                 scope = nullptr;
+             }
+ 
+             MOZ_TRY(xdr->codeEnum32(&scopeKind));
+@@ -829,128 +827,133 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+         MOZ_TRY(xdr->codeMarker(0x48922BAB));
+     }
+ 
+     /*
+      * Here looping from 0-to-length to xdr objects is essential to ensure that
+      * all references to enclosing blocks (via FindScopeIndex below) happen
+      * after the enclosing block has been XDR'd.
+      */
+-    for (i = 0; i != nobjects; ++i) {
+-        GCPtrObject* objp = &script->objects()->vector[i];
+-        XDRClassKind classk;
+-
+-        if (mode == XDR_ENCODE) {
+-            JSObject* obj = *objp;
+-            if (obj->is<RegExpObject>()) {
+-                classk = CK_RegexpObject;
+-            } else if (obj->is<JSFunction>()) {
+-                classk = CK_JSFunction;
+-            } else if (obj->is<PlainObject>() || obj->is<ArrayObject>()) {
+-                classk = CK_JSObject;
+-            } else {
+-                MOZ_CRASH("Cannot encode this class of object.");
+-            }
+-        }
+-
+-        MOZ_TRY(xdr->codeEnum32(&classk));
+-
+-        switch (classk) {
+-          case CK_RegexpObject: {
+-            Rooted<RegExpObject*> regexp(cx);
+-            if (mode == XDR_ENCODE) {
+-                regexp = &(*objp)->as<RegExpObject>();
+-            }
+-            MOZ_TRY(XDRScriptRegExpObject(xdr, &regexp));
+-            if (mode == XDR_DECODE) {
+-                *objp = regexp;
+-            }
+-            break;
+-          }
+-
+-          case CK_JSFunction: {
+-            /* Code the nested function's enclosing scope. */
+-            uint32_t funEnclosingScopeIndex = 0;
+-            RootedScope funEnclosingScope(cx);
++    if (nobjects) {
++        for (GCPtrObject& elem : script->objects()) {
++            XDRClassKind classk;
++
+             if (mode == XDR_ENCODE) {
+-                RootedFunction function(cx, &(*objp)->as<JSFunction>());
+-
+-                if (function->isInterpretedLazy()) {
+-                    funEnclosingScope = function->lazyScript()->enclosingScope();
+-                } else if (function->isInterpreted()) {
+-                    funEnclosingScope = function->nonLazyScript()->enclosingScope();
++                JSObject* obj = elem.get();
++                if (obj->is<RegExpObject>()) {
++                    classk = CK_RegexpObject;
++                } else if (obj->is<JSFunction>()) {
++                    classk = CK_JSFunction;
++                } else if (obj->is<PlainObject>() || obj->is<ArrayObject>()) {
++                    classk = CK_JSObject;
+                 } else {
+-                    MOZ_ASSERT(function->isAsmJSNative());
+-                    return xdr->fail(JS::TranscodeResult_Failure_AsmJSNotSupported);
++                    MOZ_CRASH("Cannot encode this class of object.");
+                 }
+-
+-                funEnclosingScopeIndex = FindScopeIndex(script, *funEnclosingScope);
+-            }
+-
+-            MOZ_TRY(xdr->codeUint32(&funEnclosingScopeIndex));
+-
+-            if (mode == XDR_DECODE) {
+-                MOZ_ASSERT(funEnclosingScopeIndex < script->scopes()->length);
+-                funEnclosingScope = script->scopes()->vector[funEnclosingScopeIndex];
+             }
+ 
+-            // Code nested function and script.
+-            RootedFunction tmp(cx);
+-            if (mode == XDR_ENCODE) {
+-                tmp = &(*objp)->as<JSFunction>();
++            MOZ_TRY(xdr->codeEnum32(&classk));
++
++            switch (classk) {
++              case CK_RegexpObject: {
++                Rooted<RegExpObject*> regexp(cx);
++                if (mode == XDR_ENCODE) {
++                    regexp = &elem->as<RegExpObject>();
++                }
++                MOZ_TRY(XDRScriptRegExpObject(xdr, &regexp));
++                if (mode == XDR_DECODE) {
++                    elem.init(regexp);
++                }
++                break;
++              }
++
++              case CK_JSFunction: {
++                /* Code the nested function's enclosing scope. */
++                uint32_t funEnclosingScopeIndex = 0;
++                RootedScope funEnclosingScope(cx);
++                if (mode == XDR_ENCODE) {
++                    RootedFunction function(cx, &elem->as<JSFunction>());
++
++                    if (function->isInterpretedLazy()) {
++                        funEnclosingScope = function->lazyScript()->enclosingScope();
++                    } else if (function->isInterpreted()) {
++                        funEnclosingScope = function->nonLazyScript()->enclosingScope();
++                    } else {
++                        MOZ_ASSERT(function->isAsmJSNative());
++                        return xdr->fail(JS::TranscodeResult_Failure_AsmJSNotSupported);
++                    }
++
++                    funEnclosingScopeIndex = FindScopeIndex(script, *funEnclosingScope);
++                }
++
++                MOZ_TRY(xdr->codeUint32(&funEnclosingScopeIndex));
++
++                if (mode == XDR_DECODE) {
++                    funEnclosingScope = script->getScope(funEnclosingScopeIndex);
++                }
++
++                // Code nested function and script.
++                RootedFunction tmp(cx);
++                if (mode == XDR_ENCODE) {
++                    tmp = &elem->as<JSFunction>();
++                }
++                MOZ_TRY(XDRInterpretedFunction(xdr, funEnclosingScope, sourceObject, &tmp));
++                if (mode == XDR_DECODE) {
++                    elem.init(tmp);
++                }
++                break;
++              }
++
++              case CK_JSObject: {
++                /* Code object literal. */
++                RootedObject tmp(cx);
++                if (mode == XDR_ENCODE) {
++                    tmp = elem.get();
++                }
++                MOZ_TRY(XDRObjectLiteral(xdr, &tmp));
++                if (mode == XDR_DECODE) {
++                    elem.init(tmp);
++                }
++                break;
++              }
++
++              default: {
++                // Fail in debug, but only soft-fail in release
++                MOZ_ASSERT(false, "Bad XDR class kind");
++                return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
++              }
+             }
+-            MOZ_TRY(XDRInterpretedFunction(xdr, funEnclosingScope, sourceObject, &tmp));
+-            *objp = tmp;
+-            break;
+-          }
+-
+-          case CK_JSObject: {
+-            /* Code object literal. */
+-            RootedObject tmp(cx, *objp);
+-            MOZ_TRY(XDRObjectLiteral(xdr, &tmp));
+-            *objp = tmp;
+-            break;
+-          }
+-
+-          default: {
+-            // Fail in debug, but only soft-fail in release
+-            MOZ_ASSERT(false, "Bad XDR class kind");
+-            return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
+-          }
+         }
+     }
+ 
+     // Verify marker to detect data corruption after decoding object data. A
+     // mismatch here indicates we will almost certainly crash in release.
+     MOZ_TRY(xdr->codeMarker(0xF83B989A));
+ 
+-    if (ntrynotes != 0) {
+-        JSTryNote* tnfirst = script->trynotes()->vector;
+-        MOZ_ASSERT(script->trynotes()->length == ntrynotes);
+-        JSTryNote* tn = tnfirst + ntrynotes;
+-        do {
+-            --tn;
+-            MOZ_TRY(xdr->codeUint8(&tn->kind));
+-            MOZ_TRY(xdr->codeUint32(&tn->stackDepth));
+-            MOZ_TRY(xdr->codeUint32(&tn->start));
+-            MOZ_TRY(xdr->codeUint32(&tn->length));
+-        } while (tn != tnfirst);
+-    }
+-
+-    for (i = 0; i < nscopenotes; ++i) {
+-        ScopeNote* note = &script->scopeNotes()->vector[i];
+-        MOZ_TRY(xdr->codeUint32(&note->index));
+-        MOZ_TRY(xdr->codeUint32(&note->start));
+-        MOZ_TRY(xdr->codeUint32(&note->length));
+-        MOZ_TRY(xdr->codeUint32(&note->parent));
+-    }
+-
+-    for (i = 0; i < nyieldoffsets; ++i) {
+-        uint32_t* offset = &script->yieldAndAwaitOffsets()[i];
+-        MOZ_TRY(xdr->codeUint32(offset));
++    if (ntrynotes) {
++        for (JSTryNote& elem : script->trynotes()) {
++            MOZ_TRY(xdr->codeUint8(&elem.kind));
++            MOZ_TRY(xdr->codeUint32(&elem.stackDepth));
++            MOZ_TRY(xdr->codeUint32(&elem.start));
++            MOZ_TRY(xdr->codeUint32(&elem.length));
++        }
++    }
++
++    if (nscopenotes) {
++        for (ScopeNote& elem : script->scopeNotes()) {
++            MOZ_TRY(xdr->codeUint32(&elem.index));
++            MOZ_TRY(xdr->codeUint32(&elem.start));
++            MOZ_TRY(xdr->codeUint32(&elem.length));
++            MOZ_TRY(xdr->codeUint32(&elem.parent));
++        }
++    }
++
++    if (nyieldoffsets) {
++        for (uint32_t& elem : script->yieldAndAwaitOffsets()) {
++            MOZ_TRY(xdr->codeUint32(&elem));
++        }
+     }
+ 
+     if (scriptBits & (1 << HasLazyScript)) {
+         Rooted<LazyScript*> lazy(cx);
+         if (mode == XDR_ENCODE) {
+             lazy = script->maybeLazyScript();
+         }
+ 
+@@ -2962,54 +2965,54 @@ JSScript::partiallyInit(JSContext* cx, H
+     YieldAndAwaitOffsetArray* yieldAndAwaitOffsets = nullptr;
+     if (nyieldoffsets != 0) {
+         yieldAndAwaitOffsets = reinterpret_cast<YieldAndAwaitOffsetArray*>(cursor);
+         cursor += sizeof(YieldAndAwaitOffsetArray);
+     }
+ 
+     if (nconsts != 0) {
+         MOZ_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(JS::Value) == 0);
+-        script->consts()->length = nconsts;
+-        script->consts()->vector = (GCPtrValue*)cursor;
+-        cursor += nconsts * sizeof(script->consts()->vector[0]);
+-    }
+-
+-    script->scopes()->length = nscopes;
+-    script->scopes()->vector = (GCPtrScope*)cursor;
+-    cursor += nscopes * sizeof(script->scopes()->vector[0]);
++        script->constsRaw()->length = nconsts;
++        script->constsRaw()->vector = (GCPtrValue*)cursor;
++        cursor += nconsts * sizeof(script->constsRaw()->vector[0]);
++    }
++
++    script->scopesRaw()->length = nscopes;
++    script->scopesRaw()->vector = (GCPtrScope*)cursor;
++    cursor += nscopes * sizeof(script->scopesRaw()->vector[0]);
+ 
+     if (nobjects != 0) {
+-        script->objects()->length = nobjects;
+-        script->objects()->vector = (GCPtrObject*)cursor;
+-        cursor += nobjects * sizeof(script->objects()->vector[0]);
++        script->objectsRaw()->length = nobjects;
++        script->objectsRaw()->vector = (GCPtrObject*)cursor;
++        cursor += nobjects * sizeof(script->objectsRaw()->vector[0]);
+     }
+ 
+     if (ntrynotes != 0) {
+-        script->trynotes()->length = ntrynotes;
+-        script->trynotes()->vector = reinterpret_cast<JSTryNote*>(cursor);
+-        size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
++        script->trynotesRaw()->length = ntrynotes;
++        script->trynotesRaw()->vector = reinterpret_cast<JSTryNote*>(cursor);
++        size_t vectorSize = ntrynotes * sizeof(script->trynotesRaw()->vector[0]);
+ #ifdef DEBUG
+         memset(cursor, 0, vectorSize);
+ #endif
+         cursor += vectorSize;
+     }
+ 
+     if (nscopenotes != 0) {
+-        script->scopeNotes()->length = nscopenotes;
+-        script->scopeNotes()->vector = reinterpret_cast<ScopeNote*>(cursor);
+-        size_t vectorSize = nscopenotes * sizeof(script->scopeNotes()->vector[0]);
++        script->scopeNotesRaw()->length = nscopenotes;
++        script->scopeNotesRaw()->vector = reinterpret_cast<ScopeNote*>(cursor);
++        size_t vectorSize = nscopenotes * sizeof(script->scopeNotesRaw()->vector[0]);
+ #ifdef DEBUG
+         memset(cursor, 0, vectorSize);
+ #endif
+         cursor += vectorSize;
+     }
+ 
+     if (nyieldoffsets != 0) {
+         yieldAndAwaitOffsets->init(reinterpret_cast<uint32_t*>(cursor), nyieldoffsets);
+-        size_t vectorSize = nyieldoffsets * sizeof(script->yieldAndAwaitOffsets()[0]);
++        size_t vectorSize = nyieldoffsets * sizeof(script->yieldAndAwaitOffsetsRaw()[0]);
+ #ifdef DEBUG
+         memset(cursor, 0, vectorSize);
+ #endif
+         cursor += vectorSize;
+     }
+ 
+     MOZ_ASSERT(cursor == script->data + size);
+     return true;
+@@ -3034,17 +3037,17 @@ JSScript::initFunctionPrototype(JSContex
+     script->nTypeSets_ = 0;
+ 
+     RootedScope enclosing(cx, &cx->global()->emptyGlobalScope());
+     Scope* functionProtoScope = FunctionScope::create(cx, nullptr, false, false, functionProto,
+                                                       enclosing);
+     if (!functionProtoScope) {
+         return false;
+     }
+-    script->scopes()->vector[0].init(functionProtoScope);
++    script->scopesRaw()->vector[0].init(functionProtoScope);
+ 
+     uint32_t codeLength = 1;
+     uint32_t srcNotesLength = 1;
+     uint32_t numAtoms = 0;
+     if (!script->createScriptData(cx, codeLength, srcNotesLength, numAtoms)) {
+         return false;
+     }
+ 
+@@ -3267,27 +3270,25 @@ JSScript::assertValidJumpTargets() const
+                 MOZ_ASSERT_IF(off, mainEntry <= pc + off && pc + off < end);
+                 MOZ_ASSERT_IF(off, BytecodeIsJumpTarget(JSOp(*(pc + off))));
+             }
+         }
+     }
+ 
+     // Check catch/finally blocks as jump targets.
+     if (hasTrynotes()) {
+-        JSTryNote* tn = trynotes()->vector;
+-        JSTryNote* tnlimit = tn + trynotes()->length;
+-        for (; tn < tnlimit; tn++) {
+-            jsbytecode* tryStart = mainEntry + tn->start;
++        for (const JSTryNote& tn : trynotes()) {
++            jsbytecode* tryStart = mainEntry + tn.start;
+             jsbytecode* tryPc = tryStart - 1;
+-            if (tn->kind != JSTRY_CATCH && tn->kind != JSTRY_FINALLY) {
++            if (tn.kind != JSTRY_CATCH && tn.kind != JSTRY_FINALLY) {
+                 continue;
+             }
+ 
+             MOZ_ASSERT(JSOp(*tryPc) == JSOP_TRY);
+-            jsbytecode* tryTarget = tryStart + tn->length;
++            jsbytecode* tryTarget = tryStart + tn.length;
+             MOZ_ASSERT(mainEntry <= tryTarget && tryTarget < end);
+             MOZ_ASSERT(BytecodeIsJumpTarget(JSOp(*tryTarget)));
+         }
+     }
+ }
+ #endif
+ 
+ size_t
+@@ -3660,22 +3661,22 @@ js::detail::CopyScript(JSContext* cx, Ha
+         return false;
+     }
+ 
+     /* NB: Keep this in sync with XDRScript. */
+ 
+     /* Some embeddings are not careful to use ExposeObjectToActiveJS as needed. */
+     MOZ_ASSERT(!src->sourceObject()->isMarkedGray());
+ 
+-    uint32_t nconsts   = src->hasConsts()   ? src->consts()->length   : 0;
+-    uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
+-    uint32_t nscopes   = src->scopes()->length;
+-    uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
+-    uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes()->length : 0;
+-    uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().length() : 0;
++    uint32_t nconsts = src->hasConsts() ? src->consts().size() : 0;
++    uint32_t nobjects = src->hasObjects() ? src->objects().size() : 0;
++    uint32_t nscopes = src->scopes().size();
++    uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes().size() : 0;
++    uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes().size() : 0;
++    uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().size() : 0;
+ 
+     /* Script data */
+ 
+     size_t size = src->dataSize();
+     UniquePtr<uint8_t, JS::FreePolicy> data(AllocScriptData(cx, size));
+     if (size && !data) {
+         return false;
+     }
+@@ -3684,37 +3685,35 @@ js::detail::CopyScript(JSContext* cx, Ha
+ 
+     // The passed in scopes vector contains body scopes that needed to be
+     // cloned especially, depending on whether the script is a function or
+     // global scope. Starting at scopes.length() means we only deal with
+     // intra-body scopes.
+     {
+         MOZ_ASSERT(nscopes != 0);
+         MOZ_ASSERT(src->bodyScopeIndex() + 1 == scopes.length());
+-        GCPtrScope* vector = src->scopes()->vector;
+         RootedScope original(cx);
+         RootedScope clone(cx);
+-        for (uint32_t i = scopes.length(); i < nscopes; i++) {
+-            original = vector[i];
++        for (const GCPtrScope& elem : src->scopes().From(scopes.length())) {
++            original = elem.get();
+             clone = Scope::clone(cx, original, scopes[FindScopeIndex(src, *original->enclosing())]);
+             if (!clone || !scopes.append(clone)) {
+                 return false;
+             }
+         }
+     }
+ 
+     /* Objects */
+ 
+     AutoObjectVector objects(cx);
+     if (nobjects != 0) {
+-        GCPtrObject* vector = src->objects()->vector;
+         RootedObject obj(cx);
+         RootedObject clone(cx);
+-        for (unsigned i = 0; i < nobjects; i++) {
+-            obj = vector[i];
++        for (const GCPtrObject& elem : src->objects()) {
++            obj = elem.get();
+             clone = nullptr;
+             if (obj->is<RegExpObject>()) {
+                 clone = CloneScriptRegExpObject(cx, obj->as<RegExpObject>());
+             } else if (obj->is<JSFunction>()) {
+                 RootedFunction innerFun(cx, &obj->as<JSFunction>());
+                 if (innerFun->isNative()) {
+                     if (cx->compartment() != innerFun->compartment()) {
+                         MOZ_ASSERT(innerFun->isAsmJSNative());
+@@ -3791,45 +3790,45 @@ js::detail::CopyScript(JSContext* cx, Ha
+     dst->bitFields_.isDerivedClassConstructor_ = src->isDerivedClassConstructor();
+     dst->bitFields_.needsHomeObject_ = src->needsHomeObject();
+     dst->bitFields_.isDefaultClassConstructor_ = src->isDefaultClassConstructor();
+     dst->bitFields_.isAsync_ = src->bitFields_.isAsync_;
+     dst->bitFields_.hasRest_ = src->bitFields_.hasRest_;
+     dst->bitFields_.hideScriptFromDebugger_ = src->bitFields_.hideScriptFromDebugger_;
+ 
+     if (nconsts != 0) {
+-        GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
+-        dst->consts()->vector = vector;
++        GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->constsRaw()->vector);
++        dst->constsRaw()->vector = vector;
+         for (unsigned i = 0; i < nconsts; ++i) {
+             MOZ_ASSERT_IF(vector[i].isGCThing(), vector[i].toString()->isAtom());
+         }
+     }
+     if (nobjects != 0) {
+-        GCPtrObject* vector = Rebase<GCPtrObject>(dst, src, src->objects()->vector);
+-        dst->objects()->vector = vector;
++        GCPtrObject* vector = Rebase<GCPtrObject>(dst, src, src->objectsRaw()->vector);
++        dst->objectsRaw()->vector = vector;
+         for (unsigned i = 0; i < nobjects; ++i) {
+             vector[i].init(&objects[i]->as<NativeObject>());
+         }
+     }
+     {
+-        GCPtrScope* vector = Rebase<GCPtrScope>(dst, src, src->scopes()->vector);
+-        dst->scopes()->vector = vector;
++        GCPtrScope* vector = Rebase<GCPtrScope>(dst, src, src->scopesRaw()->vector);
++        dst->scopesRaw()->vector = vector;
+         for (uint32_t i = 0; i < nscopes; ++i) {
+             vector[i].init(scopes[i]);
+         }
+     }
+     if (ntrynotes != 0) {
+-        dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
++        dst->trynotesRaw()->vector = Rebase<JSTryNote>(dst, src, src->trynotesRaw()->vector);
+     }
+     if (nscopenotes != 0) {
+-        dst->scopeNotes()->vector = Rebase<ScopeNote>(dst, src, src->scopeNotes()->vector);
++        dst->scopeNotesRaw()->vector = Rebase<ScopeNote>(dst, src, src->scopeNotesRaw()->vector);
+     }
+     if (nyieldoffsets != 0) {
+-        dst->yieldAndAwaitOffsets().vector_ =
+-            Rebase<uint32_t>(dst, src, src->yieldAndAwaitOffsets().vector_);
++        dst->yieldAndAwaitOffsetsRaw().vector_ =
++            Rebase<uint32_t>(dst, src, src->yieldAndAwaitOffsetsRaw().vector_);
+     }
+ 
+     return true;
+ }
+ 
+ static JSScript*
+ CreateEmptyScriptForClone(JSContext* cx, HandleScript src)
+ {
+@@ -4168,28 +4167,29 @@ JSScript::traceChildren(JSTracer* trc)
+     MOZ_ASSERT_IF(trc->isMarkingTracer() &&
+                   GCMarker::fromTracer(trc)->shouldCheckCompartments(),
+                   zone()->isCollecting());
+ 
+     if (scriptData()) {
+         scriptData()->traceChildren(trc);
+     }
+ 
+-    if (ScopeArray* scopearray = scopes()) {
+-        TraceRange(trc, scopearray->length, scopearray->vector, "scopes");
++    if (data) {
++        auto array = scopes();
++        TraceRange(trc, array.size(), array.data(), "scopes");
+     }
+ 
+     if (hasConsts()) {
+-        ConstArray* constarray = consts();
+-        TraceRange(trc, constarray->length, constarray->vector, "consts");
++        auto array = consts();
++        TraceRange(trc, array.size(), array.data(), "consts");
+     }
+ 
+     if (hasObjects()) {
+-        ObjectArray* objarray = objects();
+-        TraceRange(trc, objarray->length, objarray->vector, "objects");
++        auto array = objects();
++        TraceRange(trc, array.size(), array.data(), "objects");
+     }
+ 
+     MOZ_ASSERT_IF(sourceObject(), MaybeForwarded(sourceObject())->compartment() == compartment());
+     TraceNullableEdge(trc, &sourceObject_, "sourceObject");
+ 
+     if (maybeLazyScript()) {
+         TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
+     }
+@@ -4247,35 +4247,35 @@ JSScript::lookupScope(jsbytecode* pc)
+     MOZ_ASSERT(containsPC(pc));
+ 
+     if (!hasScopeNotes()) {
+         return nullptr;
+     }
+ 
+     size_t offset = pc - code();
+ 
+-    ScopeNoteArray* notes = scopeNotes();
++    auto notes = scopeNotes();
+     Scope* scope = nullptr;
+ 
+     // Find the innermost block chain using a binary search.
+     size_t bottom = 0;
+-    size_t top = notes->length;
++    size_t top = notes.size();
+ 
+     while (bottom < top) {
+         size_t mid = bottom + (top - bottom) / 2;
+-        const ScopeNote* note = &notes->vector[mid];
++        const ScopeNote* note = &notes[mid];
+         if (note->start <= offset) {
+             // Block scopes are ordered in the list by their starting offset, and since
+             // blocks form a tree ones earlier in the list may cover the pc even if
+             // later blocks end before the pc. This only happens when the earlier block
+             // is a parent of the later block, so we need to check parents of |mid| in
+             // the searched range for coverage.
+             size_t check = mid;
+             while (check >= bottom) {
+-                const ScopeNote* checkNote = &notes->vector[check];
++                const ScopeNote* checkNote = &notes[check];
+                 MOZ_ASSERT(checkNote->start <= offset);
+                 if (offset < checkNote->start + checkNote->length) {
+                     // We found a matching block chain but there may be inner ones
+                     // at a higher block chain index than mid. Continue the binary search.
+                     if (checkNote->index == ScopeNote::NoScopeIndex) {
+                         scope = nullptr;
+                     } else {
+                         scope = getScope(checkNote->index);
+@@ -4719,20 +4719,18 @@ JSScript::updateJitCodeRaw(JSRuntime* rt
+ }
+ 
+ bool
+ JSScript::hasLoops()
+ {
+     if (!hasTrynotes()) {
+         return false;
+     }
+-    JSTryNote* tn = trynotes()->vector;
+-    JSTryNote* tnlimit = tn + trynotes()->length;
+-    for (; tn < tnlimit; tn++) {
+-        switch (tn->kind) {
++    for (const JSTryNote& tn : trynotes()) {
++        switch (tn.kind) {
+           case JSTRY_FOR_IN:
+           case JSTRY_FOR_OF:
+           case JSTRY_LOOP:
+             return true;
+           case JSTRY_CATCH:
+           case JSTRY_FINALLY:
+           case JSTRY_FOR_OF_ITERCLOSE:
+           case JSTRY_DESTRUCTURING_ITERCLOSE:
+diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h
+--- a/js/src/vm/JSScript.h
++++ b/js/src/vm/JSScript.h
+@@ -8,16 +8,17 @@
+ 
+ #ifndef vm_JSScript_h
+ #define vm_JSScript_h
+ 
+ #include "mozilla/ArrayUtils.h"
+ #include "mozilla/Atomics.h"
+ #include "mozilla/Maybe.h"
+ #include "mozilla/MemoryReporting.h"
++#include "mozilla/Span.h"
+ #include "mozilla/Variant.h"
+ 
+ #include "jstypes.h"
+ 
+ #include "frontend/NameAnalysisTypes.h"
+ #include "gc/Barrier.h"
+ #include "gc/Rooting.h"
+ #include "jit/IonCode.h"
+@@ -1820,28 +1821,26 @@ class JSScript : public js::gc::TenuredC
+ 
+     bool functionHasExtraBodyVarScope() const {
+         MOZ_ASSERT_IF(bitFields_.functionHasExtraBodyVarScope_, functionHasParameterExprs());
+         return bitFields_.functionHasExtraBodyVarScope_;
+     }
+ 
+     js::VarScope* functionExtraBodyVarScope() const {
+         MOZ_ASSERT(functionHasExtraBodyVarScope());
+-        for (uint32_t i = 0; i < scopes()->length; i++) {
+-            js::Scope* scope = getScope(i);
++        for (js::Scope* scope : scopes()) {
+             if (scope->kind() == js::ScopeKind::FunctionBodyVar) {
+                 return &scope->as<js::VarScope>();
+             }
+         }
+         MOZ_CRASH("Function extra body var scope not found");
+     }
+ 
+     bool needsBodyEnvironment() const {
+-        for (uint32_t i = 0; i < scopes()->length; i++) {
+-            js::Scope* scope = getScope(i);
++        for (js::Scope* scope : scopes()) {
+             if (ScopeKindIsInBody(scope->kind()) && scope->hasEnvironment()) {
+                 return true;
+             }
+         }
+         return false;
+     }
+ 
+     inline js::LexicalScope* maybeNamedLambdaScope() const;
+@@ -1926,46 +1925,80 @@ class JSScript : public js::gc::TenuredC
+     size_t yieldAndAwaitOffsetsOffset() const {
+         return OFF(scopeNotesOffset, hasScopeNotes, js::ScopeNoteArray);
+     }
+ 
+ #undef OFF
+ 
+     size_t dataSize() const { return dataSize_; }
+ 
+-    js::ConstArray* consts() {
++  private:
++
++    js::ConstArray* constsRaw() const {
+         MOZ_ASSERT(hasConsts());
+         return reinterpret_cast<js::ConstArray*>(data + constsOffset());
+     }
+ 
+-    js::ObjectArray* objects() {
++    js::ObjectArray* objectsRaw() const {
+         MOZ_ASSERT(hasObjects());
+         return reinterpret_cast<js::ObjectArray*>(data + objectsOffset());
+     }
+ 
+-    js::ScopeArray* scopes() const {
++    js::ScopeArray* scopesRaw() const {
+         return reinterpret_cast<js::ScopeArray*>(data + scopesOffset());
+     }
+ 
+-    js::TryNoteArray* trynotes() const {
++    js::TryNoteArray* trynotesRaw() const {
+         MOZ_ASSERT(hasTrynotes());
+         return reinterpret_cast<js::TryNoteArray*>(data + trynotesOffset());
+     }
+ 
+-    js::ScopeNoteArray* scopeNotes() {
++    js::ScopeNoteArray* scopeNotesRaw() const {
+         MOZ_ASSERT(hasScopeNotes());
+         return reinterpret_cast<js::ScopeNoteArray*>(data + scopeNotesOffset());
+     }
+ 
+-    js::YieldAndAwaitOffsetArray& yieldAndAwaitOffsets() {
++    js::YieldAndAwaitOffsetArray& yieldAndAwaitOffsetsRaw() const {
+         MOZ_ASSERT(hasYieldAndAwaitOffsets());
+         return *reinterpret_cast<js::YieldAndAwaitOffsetArray*>(data +
+                                                                 yieldAndAwaitOffsetsOffset());
+     }
+ 
++  public:
++
++    mozilla::Span<js::GCPtrValue> consts() const {
++        js::ConstArray* array = constsRaw();
++        return mozilla::MakeSpan(array->vector, array->length);
++    }
++
++    mozilla::Span<js::GCPtrObject> objects() const {
++        js::ObjectArray* array = objectsRaw();
++        return mozilla::MakeSpan(array->vector, array->length);
++    }
++
++    mozilla::Span<js::GCPtrScope> scopes() const {
++        js::ScopeArray* array = scopesRaw();
++        return mozilla::MakeSpan(array->vector, array->length);
++    }
++
++    mozilla::Span<JSTryNote> trynotes() const {
++        js::TryNoteArray* array = trynotesRaw();
++        return mozilla::MakeSpan(array->vector, array->length);
++    }
++
++    mozilla::Span<js::ScopeNote> scopeNotes() const {
++        js::ScopeNoteArray* array = scopeNotesRaw();
++        return mozilla::MakeSpan(array->vector, array->length);
++    }
++
++    mozilla::Span<uint32_t> yieldAndAwaitOffsets() const {
++        js::YieldAndAwaitOffsetArray& array = yieldAndAwaitOffsetsRaw();
++        return mozilla::MakeSpan(&array[0], array.length());
++    }
++
+     bool hasLoops();
+ 
+     uint32_t numNotes() const {
+         MOZ_ASSERT(scriptData_);
+         return scriptData_->numNotes();
+     }
+     jssrcnote* notes() const {
+         MOZ_ASSERT(scriptData_);
+@@ -1996,31 +2029,27 @@ class JSScript : public js::gc::TenuredC
+         return getAtom(index)->asPropertyName();
+     }
+ 
+     js::PropertyName* getName(jsbytecode* pc) const {
+         return getAtom(pc)->asPropertyName();
+     }
+ 
+     JSObject* getObject(size_t index) {
+-        js::ObjectArray* arr = objects();
+-        MOZ_ASSERT(index < arr->length);
+-        MOZ_ASSERT(arr->vector[index]->isTenured());
+-        return arr->vector[index];
++        MOZ_ASSERT(objects()[index]->isTenured());
++        return objects()[index];
+     }
+ 
+     JSObject* getObject(jsbytecode* pc) {
+         MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
+         return getObject(GET_UINT32_INDEX(pc));
+     }
+ 
+     js::Scope* getScope(size_t index) const {
+-        js::ScopeArray* array = scopes();
+-        MOZ_ASSERT(index < array->length);
+-        return array->vector[index];
++        return scopes()[index];
+     }
+ 
+     js::Scope* getScope(jsbytecode* pc) const {
+         // This method is used to get a scope directly using a JSOp with an
+         // index. To search through ScopeNotes to look for a Scope using pc,
+         // use lookupScope.
+         MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
+         MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPE,
+@@ -2035,19 +2064,17 @@ class JSScript : public js::gc::TenuredC
+         }
+         return nullptr;
+     }
+ 
+     inline js::RegExpObject* getRegExp(size_t index);
+     inline js::RegExpObject* getRegExp(jsbytecode* pc);
+ 
+     const js::Value& getConst(size_t index) {
+-        js::ConstArray* arr = consts();
+-        MOZ_ASSERT(index < arr->length);
+-        return arr->vector[index];
++        return consts()[index];
+     }
+ 
+     // The following 3 functions find the static scope just before the
+     // execution of the instruction pointed to by pc.
+ 
+     js::Scope* lookupScope(jsbytecode* pc);
+ 
+     js::Scope* innermostScope(jsbytecode* pc);
+diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp
+--- a/js/src/vm/ObjectGroup.cpp
++++ b/js/src/vm/ObjectGroup.cpp
+@@ -235,25 +235,23 @@ ObjectGroup::useSingletonForAllocationSi
+     // All loops in the script will have a try note indicating their boundary.
+ 
+     if (!script->hasTrynotes()) {
+         return SingletonObject;
+     }
+ 
+     unsigned offset = script->pcToOffset(pc);
+ 
+-    JSTryNote* tn = script->trynotes()->vector;
+-    JSTryNote* tnlimit = tn + script->trynotes()->length;
+-    for (; tn < tnlimit; tn++) {
+-        if (tn->kind != JSTRY_FOR_IN && tn->kind != JSTRY_FOR_OF && tn->kind != JSTRY_LOOP) {
++    for (const JSTryNote& tn : script->trynotes()) {
++        if (tn.kind != JSTRY_FOR_IN && tn.kind != JSTRY_FOR_OF && tn.kind != JSTRY_LOOP) {
+             continue;
+         }
+ 
+-        unsigned startOffset = script->mainOffset() + tn->start;
+-        unsigned endOffset = startOffset + tn->length;
++        unsigned startOffset = script->mainOffset() + tn.start;
++        unsigned endOffset = startOffset + tn.length;
+ 
+         if (offset >= startOffset && offset < endOffset) {
+             return GenericObject;
+         }
+     }
+ 
+     return SingletonObject;
+ }
+diff --git a/js/src/vm/JSCompartment.cpp b/js/src/vm/JSCompartment.cpp
+--- a/js/src/vm/JSCompartment.cpp
++++ b/js/src/vm/JSCompartment.cpp
+@@ -705,19 +705,17 @@ Realm::setNewObjectMetadata(JSContext* c
+ }
+ 
+ static bool
+ AddInnerLazyFunctionsFromScript(JSScript* script, AutoObjectVector& lazyFunctions)
+ {
+     if (!script->hasObjects()) {
+         return true;
+     }
+-    ObjectArray* objects = script->objects();
+-    for (size_t i = 0; i < objects->length; i++) {
+-        JSObject* obj = objects->vector[i];
++    for (JSObject* obj : script->objects()) {
+         if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
+             if (!lazyFunctions.append(obj)) {
+                 return false;
+             }
+         }
+     }
+     return true;
+ }
+

+ 385 - 0
frg/work-js/mozilla-release/patches/1486444-63a1.patch

@@ -0,0 +1,385 @@
+# HG changeset patch
+# User Jan de Mooij <jdemooij@mozilla.com>
+# Date 1535383696 -7200
+# Node ID c44d81b8909ab05e294a897ec76f02c04b729331
+# Parent  b8fdb9372c775f81cbe2f5da4e4c2257433980a2
+Bug 1486444 - Remove bogus/over-allocating no-arg versions of js_pod_malloc/js_pod_calloc. r=tcampbell
+
+These functions incorrectly passed sizeof(T) instead of 1, so we would allocate sizeof(T) * sizeof(T) bytes instead of sizeof(T) bytes. This was used for PcScriptCache where we would allocate a few extra megabytes due to this bug. The patch changes PcScriptCache to use UniquePtr + MakeUnique.
+
+Differential Revision: https://phabricator.services.mozilla.com/D4343
+
+diff --git a/js/public/Utility.h b/js/public/Utility.h
+--- a/js/public/Utility.h
++++ b/js/public/Utility.h
+@@ -588,23 +588,16 @@ template <class T>
+ static MOZ_ALWAYS_INLINE T*
+ js_pod_malloc(size_t numElems)
+ {
+     return js_pod_arena_malloc<T>(js::MallocArena, numElems);
+ }
+ 
+ template <class T>
+ static MOZ_ALWAYS_INLINE T*
+-js_pod_malloc()
+-{
+-    return js_pod_malloc<T>(sizeof(T));
+-}
+-
+-template <class T>
+-static MOZ_ALWAYS_INLINE T*
+ js_pod_arena_calloc(arena_id_t arena, size_t numElems)
+ {
+     size_t bytes;
+     if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
+         return nullptr;
+     return static_cast<T*>(js_arena_calloc(arena, bytes, 1));
+ }
+ 
+@@ -612,23 +605,16 @@ template <class T>
+ static MOZ_ALWAYS_INLINE T*
+ js_pod_calloc(size_t numElems)
+ {
+     return js_pod_arena_calloc<T>(js::MallocArena, numElems);
+ }
+ 
+ template <class T>
+ static MOZ_ALWAYS_INLINE T*
+-js_pod_calloc()
+-{
+-    return js_pod_calloc<T>(sizeof(T));
+-}
+-
+-template <class T>
+-static MOZ_ALWAYS_INLINE T*
+ js_pod_realloc(T* prior, size_t oldSize, size_t newSize)
+ {
+     MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
+     size_t bytes;
+     if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes)))
+         return nullptr;
+     return static_cast<T*>(js_realloc(prior, bytes));
+ }
+diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp
+--- a/js/src/jit/JitFrames.cpp
++++ b/js/src/jit/JitFrames.cpp
+@@ -1405,24 +1405,24 @@ GetPcScript(JSContext* cx, JSScript** sc
+         retAddr = it.frame().returnAddress();
+     }
+ 
+     uint32_t hash;
+     if (retAddr) {
+         hash = PcScriptCache::Hash(retAddr);
+ 
+         // Lazily initialize the cache. The allocation may safely fail and will not GC.
+-        if (MOZ_UNLIKELY(cx->ionPcScriptCache == nullptr)) {
+-            cx->ionPcScriptCache = js_pod_malloc<PcScriptCache>();
+-            if (cx->ionPcScriptCache)
+-                cx->ionPcScriptCache->clear(cx->runtime()->gc.gcNumber());
++        if (MOZ_UNLIKELY(cx->ionPcScriptCache == nullptr))
++            cx->ionPcScriptCache = MakeUnique<PcScriptCache>(cx->runtime()->gc.gcNumber());
++
++        if (cx->ionPcScriptCache.ref() &&
++            cx->ionPcScriptCache->get(cx->runtime(), hash, retAddr, scriptRes, pcRes))
++        {
++            return;
+         }
+-
+-        if (cx->ionPcScriptCache && cx->ionPcScriptCache->get(cx->runtime(), hash, retAddr, scriptRes, pcRes))
+-            return;
+     }
+ 
+     // Lookup failed: undertake expensive process to recover the innermost inlined frame.
+     jsbytecode* pc = nullptr;
+     if (it.frame().isIonJS() || it.frame().isBailoutJS()) {
+         InlineFrameIterator ifi(cx, &it.frame());
+         *scriptRes = ifi.script();
+         pc = ifi.pc();
+@@ -1430,17 +1430,17 @@ GetPcScript(JSContext* cx, JSScript** sc
+         MOZ_ASSERT(it.frame().isBaselineJS());
+         it.frame().baselineScriptAndPc(scriptRes, &pc);
+     }
+ 
+     if (pcRes)
+         *pcRes = pc;
+ 
+     // Add entry to cache.
+-    if (retAddr && cx->ionPcScriptCache)
++    if (retAddr && cx->ionPcScriptCache.ref())
+         cx->ionPcScriptCache->add(hash, retAddr, pc, *scriptRes);
+ }
+ 
+ uint32_t
+ OsiIndex::returnPointDisplacement() const
+ {
+     // In general, pointer arithmetic on code is bad, but in this case,
+     // getting the return address from a call instruction, stepping over pools
+diff --git a/js/src/jit/PcScriptCache.h b/js/src/jit/PcScriptCache.h
+--- a/js/src/jit/PcScriptCache.h
++++ b/js/src/jit/PcScriptCache.h
+@@ -20,26 +20,32 @@ struct PcScriptCacheEntry
+ {
+     uint8_t* returnAddress; // Key into the hash table.
+     jsbytecode* pc;         // Cached PC.
+     JSScript* script;       // Cached script.
+ };
+ 
+ struct PcScriptCache
+ {
++  private:
+     static const uint32_t Length = 73;
+ 
+     // GC number at the time the cache was filled or created.
+     // Storing and checking against this number allows us to not bother
+     // clearing this cache on every GC -- only when actually necessary.
+     uint64_t gcNumber;
+ 
+     // List of cache entries.
+     mozilla::Array<PcScriptCacheEntry, Length> entries;
+ 
++  public:
++    explicit PcScriptCache(uint64_t gcNumber) {
++        clear(gcNumber);
++    }
++
+     void clear(uint64_t gcNumber) {
+         for (uint32_t i = 0; i < Length; i++)
+             entries[i].returnAddress = nullptr;
+         this->gcNumber = gcNumber;
+     }
+ 
+     // Get a value from the cache. May perform lazy allocation.
+     MOZ_MUST_USE bool get(JSRuntime* rt, uint32_t hash, uint8_t* addr,
+diff --git a/js/src/jit/arm/Simulator-arm.cpp b/js/src/jit/arm/Simulator-arm.cpp
+--- a/js/src/jit/arm/Simulator-arm.cpp
++++ b/js/src/jit/arm/Simulator-arm.cpp
+@@ -1248,18 +1248,19 @@ class Redirection
+         Redirection* current = SimulatorProcess::redirection();
+         for (; current != nullptr; current = current->next_) {
+             if (current->nativeFunction_ == nativeFunction) {
+                 MOZ_ASSERT(current->type() == type);
+                 return current;
+             }
+         }
+ 
++        // Note: we can't use js_new here because the constructor is private.
+         AutoEnterOOMUnsafeRegion oomUnsafe;
+-        Redirection* redir = js_pod_malloc<Redirection>();
++        Redirection* redir = js_pod_malloc<Redirection>(1);
+         if (!redir)
+             oomUnsafe.crash("Simulator redirection");
+         new(redir) Redirection(nativeFunction, type);
+         return redir;
+     }
+ 
+     static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
+         uint8_t* addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction);
+diff --git a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
+--- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
++++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
+@@ -383,18 +383,19 @@ class Redirection
+     Redirection* current = SimulatorProcess::redirection();
+     for (; current != nullptr; current = current->next_) {
+       if (current->nativeFunction_ == nativeFunction) {
+         VIXL_ASSERT(current->type() == type);
+         return current;
+       }
+     }
+ 
++    // Note: we can't use js_new here because the constructor is private.
+     js::AutoEnterOOMUnsafeRegion oomUnsafe;
+-    Redirection* redir = js_pod_malloc<Redirection>();
++    Redirection* redir = js_pod_malloc<Redirection>(1);
+     if (!redir)
+         oomUnsafe.crash("Simulator redirection");
+     new(redir) Redirection(nativeFunction, type);
+     return redir;
+   }
+ 
+   static const Redirection* FromSvcInstruction(const Instruction* svcInstruction) {
+     const uint8_t* addrOfSvc = reinterpret_cast<const uint8_t*>(svcInstruction);
+diff --git a/js/src/jit/mips32/Simulator-mips32.cpp b/js/src/jit/mips32/Simulator-mips32.cpp
+--- a/js/src/jit/mips32/Simulator-mips32.cpp
++++ b/js/src/jit/mips32/Simulator-mips32.cpp
+@@ -1338,18 +1338,19 @@ class Redirection
+         Redirection* current = SimulatorProcess::redirection();
+         for (; current != nullptr; current = current->next_) {
+             if (current->nativeFunction_ == nativeFunction) {
+                 MOZ_ASSERT(current->type() == type);
+                 return current;
+             }
+         }
+ 
++        // Note: we can't use js_new here because the constructor is private.
+         AutoEnterOOMUnsafeRegion oomUnsafe;
+-        Redirection* redir = js_pod_malloc<Redirection>();
++        Redirection* redir = js_pod_malloc<Redirection>(1);
+         if (!redir) {
+             oomUnsafe.crash("Simulator redirection");
+         }
+         new(redir) Redirection(nativeFunction, type);
+         return redir;
+     }
+ 
+     static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
+diff --git a/js/src/jit/mips64/Simulator-mips64.cpp b/js/src/jit/mips64/Simulator-mips64.cpp
+--- a/js/src/jit/mips64/Simulator-mips64.cpp
++++ b/js/src/jit/mips64/Simulator-mips64.cpp
+@@ -1346,18 +1346,19 @@ class Redirection
+         Redirection* current = SimulatorProcess::redirection();
+         for (; current != nullptr; current = current->next_) {
+             if (current->nativeFunction_ == nativeFunction) {
+                 MOZ_ASSERT(current->type() == type);
+                 return current;
+             }
+         }
+ 
++        // Note: we can't use js_new here because the constructor is private.
+         AutoEnterOOMUnsafeRegion oomUnsafe;
+-        Redirection* redir = js_pod_malloc<Redirection>();
++        Redirection* redir = js_pod_malloc<Redirection>(1);
+         if (!redir) {
+             oomUnsafe.crash("Simulator redirection");
+         }
+         new(redir) Redirection(nativeFunction, type);
+         return redir;
+     }
+ 
+     static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
+diff --git a/js/src/jit/shared/Disassembler-shared.cpp b/js/src/jit/shared/Disassembler-shared.cpp
+--- a/js/src/jit/shared/Disassembler-shared.cpp
++++ b/js/src/jit/shared/Disassembler-shared.cpp
+@@ -232,17 +232,17 @@ DisassemblerSpew::lookup(const Label* ke
+         ;
+     return p;
+ }
+ 
+ DisassemblerSpew::Node*
+ DisassemblerSpew::add(const Label* key, uint32_t value)
+ {
+     MOZ_ASSERT(!lookup(key));
+-    Node* node = js_pod_malloc<Node>();
++    Node* node = js_new<Node>();
+     if (node) {
+         node->key = key;
+         node->value = value;
+         node->bound = false;
+         node->next = nodes_;
+         nodes_ = node;
+     }
+     return node;
+diff --git a/js/src/threading/ProtectedData.h b/js/src/threading/ProtectedData.h
+--- a/js/src/threading/ProtectedData.h
++++ b/js/src/threading/ProtectedData.h
+@@ -82,16 +82,19 @@ class ProtectedData
+     DECLARE_BOOL_OPERATORS(T)
+ 
+     operator const T&() const { return ref(); }
+     const T& operator->() const { return ref(); }
+ 
+     template <typename U>
+     ThisType& operator=(const U& p) { this->ref() = p; return *this; }
+ 
++    template <typename U>
++    ThisType& operator=(U&& p) { this->ref() = std::move(p); return *this; }
++
+     template <typename U> T& operator +=(const U& rhs) { return ref() += rhs; }
+     template <typename U> T& operator -=(const U& rhs) { return ref() -= rhs; }
+     template <typename U> T& operator *=(const U& rhs) { return ref() *= rhs; }
+     template <typename U> T& operator /=(const U& rhs) { return ref() /= rhs; }
+     template <typename U> T& operator &=(const U& rhs) { return ref() &= rhs; }
+     template <typename U> T& operator |=(const U& rhs) { return ref() |= rhs; }
+     T& operator ++() { return ++ref(); }
+     T& operator --() { return --ref(); }
+@@ -127,42 +130,40 @@ class ProtectedData
+     Check check;
+ #endif
+ };
+ 
+ // Intermediate class for protected data whose checks take no constructor arguments.
+ template <typename Check, typename T>
+ class ProtectedDataNoCheckArgs : public ProtectedData<Check, T>
+ {
+-    typedef ProtectedDataNoCheckArgs<Check, T> ThisType;
++    using Base = ProtectedData<Check, T>;
+ 
+   public:
+     template <typename... Args>
+     explicit ProtectedDataNoCheckArgs(Args&&... args)
+       : ProtectedData<Check, T>(Check(), std::forward<Args>(args)...)
+     {}
+ 
+-    template <typename U>
+-    ThisType& operator=(const U& p) { this->ref() = p; return *this; }
++    using Base::operator=;
+ };
+ 
+ // Intermediate class for protected data whose checks take a Zone constructor argument.
+ template <typename Check, typename T>
+ class ProtectedDataZoneArg : public ProtectedData<Check, T>
+ {
+-    typedef ProtectedDataZoneArg<Check, T> ThisType;
++    using Base = ProtectedData<Check, T>;
+ 
+   public:
+     template <typename... Args>
+     explicit ProtectedDataZoneArg(JS::Zone* zone, Args&&... args)
+       : ProtectedData<Check, T>(Check(zone), std::forward<Args>(args)...)
+     {}
+ 
+-    template <typename U>
+-    ThisType& operator=(const U& p) { this->ref() = p; return *this; }
++    using Base::operator=;
+ };
+ 
+ class CheckUnprotected
+ {
+ #ifdef JS_HAS_PROTECTED_DATA_CHECKS
+   public:
+     inline void check() const {}
+ #endif
+diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp
+--- a/js/src/vm/JSContext.cpp
++++ b/js/src/vm/JSContext.cpp
+@@ -1354,18 +1354,16 @@ JSContext::~JSContext()
+ {
+     // Clear the ContextKind first, so that ProtectedData checks will allow us to
+     // destroy this context even if the runtime is already gone.
+     kind_ = ContextKind::HelperThread;
+ 
+     /* Free the stuff hanging off of cx. */
+     MOZ_ASSERT(!resolvingList);
+ 
+-    js_delete(ionPcScriptCache.ref());
+-
+     if (dtoaState)
+         DestroyDtoaState(dtoaState);
+ 
+     fx.destroyInstance();
+     freeOsrTempData();
+ 
+ #ifdef JS_SIMULATOR
+     js::jit::Simulator::Destroy(simulator_);
+diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h
+--- a/js/src/vm/JSContext.h
++++ b/js/src/vm/JSContext.h
+@@ -651,17 +651,17 @@ struct JSContext : public JS::RootingCon
+     js::ThreadData<js::LifoAlloc> tempLifoAlloc_;
+   public:
+     js::LifoAlloc& tempLifoAlloc() { return tempLifoAlloc_.ref(); }
+     const js::LifoAlloc& tempLifoAlloc() const { return tempLifoAlloc_.ref(); }
+ 
+     js::ThreadData<uint32_t> debuggerMutations;
+ 
+     // Cache for jit::GetPcScript().
+-    js::ThreadData<js::jit::PcScriptCache*> ionPcScriptCache;
++    js::ThreadData<js::UniquePtr<js::jit::PcScriptCache>> ionPcScriptCache;
+ 
+   private:
+     /* Exception state -- the exception member is a GC root by definition. */
+     js::ThreadData<bool> throwing;            /* is there a pending exception? */
+     js::ThreadData<JS::PersistentRooted<JS::Value>> unwrappedException_; /* most-recently-thrown exception */
+ 
+     JS::Value& unwrappedException() {
+         if (!unwrappedException_.ref().initialized())
+

+ 0 - 0
frg/work-js/mozilla-release/patches/435732.patch → frg/work-js/mozilla-release/patches/1488698-7-64a1.patch


+ 15106 - 0
frg/work-js/mozilla-release/patches/1488698-7-WIP-64a1.patch

@@ -0,0 +1,15106 @@
+# HG changeset patch
+# User Jan de Mooij <jdemooij@mozilla.com>
+# Date 1536225329 -7200
+#      Thu Sep 06 11:15:29 2018 +0200
+# Node ID 3810b18e5e79dc6a5204a2df01bfaafc40f0573e
+# Parent  ea76f5cb481ce237f1205c31c9503b3a833e335f
+Bug 1488698 - Always use braces for if/for/while statements in js/src/vm, part 7. r=jorendorff
+
+diff --git a/js/src/vm/JSAtom-inl.h b/js/src/vm/JSAtom-inl.h
+--- a/js/src/vm/JSAtom-inl.h
++++ b/js/src/vm/JSAtom-inl.h
+@@ -20,55 +20,59 @@
+ namespace js {
+ 
+ inline jsid
+ AtomToId(JSAtom* atom)
+ {
+     JS_STATIC_ASSERT(JSID_INT_MIN == 0);
+ 
+     uint32_t index;
+-    if (atom->isIndex(&index) && index <= JSID_INT_MAX)
++    if (atom->isIndex(&index) && index <= JSID_INT_MAX) {
+         return INT_TO_JSID(int32_t(index));
++    }
+ 
+     return JSID_FROM_BITS(size_t(atom) | JSID_TYPE_STRING);
+ }
+ 
+ // Use the NameToId method instead!
+ inline jsid
+ AtomToId(PropertyName* name) = delete;
+ 
+ MOZ_ALWAYS_INLINE bool
+ ValueToIntId(const Value& v, jsid* id)
+ {
+     int32_t i;
+-    if (v.isInt32())
++    if (v.isInt32()) {
+         i = v.toInt32();
+-    else if (!v.isDouble() || !mozilla::NumberEqualsInt32(v.toDouble(), &i))
++    } else if (!v.isDouble() || !mozilla::NumberEqualsInt32(v.toDouble(), &i)) {
+         return false;
++    }
+ 
+-    if (!INT_FITS_IN_JSID(i))
++    if (!INT_FITS_IN_JSID(i)) {
+         return false;
++    }
+ 
+     *id = INT_TO_JSID(i);
+     return true;
+ }
+ 
+ inline bool
+ ValueToIdPure(const Value& v, jsid* id)
+ {
+     if (v.isString()) {
+         if (v.toString()->isAtom()) {
+             *id = AtomToId(&v.toString()->asAtom());
+             return true;
+         }
+         return false;
+     }
+ 
+-    if (ValueToIntId(v, id))
++    if (ValueToIntId(v, id)) {
+         return true;
++    }
+ 
+     if (v.isSymbol()) {
+         *id = SYMBOL_TO_JSID(v.toSymbol());
+         return true;
+     }
+ 
+     return false;
+ }
+@@ -79,28 +83,30 @@ ValueToId(JSContext* cx, typename MaybeR
+           typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
+ {
+     if (v.isString()) {
+         if (v.toString()->isAtom()) {
+             idp.set(AtomToId(&v.toString()->asAtom()));
+             return true;
+         }
+     } else {
+-        if (ValueToIntId(v, idp.address()))
++        if (ValueToIntId(v, idp.address())) {
+             return true;
++        }
+ 
+         if (v.isSymbol()) {
+             idp.set(SYMBOL_TO_JSID(v.toSymbol()));
+             return true;
+         }
+     }
+ 
+     JSAtom* atom = ToAtom<allowGC>(cx, v);
+-    if (!atom)
++    if (!atom) {
+         return false;
++    }
+ 
+     idp.set(AtomToId(atom));
+     return true;
+ }
+ 
+ /*
+  * Write out character representing |index| to the memory just before |end|.
+  * Thus |*end| is not touched, but |end[-1]| and earlier are modified as
+@@ -142,26 +148,29 @@ IndexToId(JSContext* cx, uint32_t index,
+     }
+ 
+     return IndexToIdSlow(cx, index, idp);
+ }
+ 
+ static MOZ_ALWAYS_INLINE JSFlatString*
+ IdToString(JSContext* cx, jsid id)
+ {
+-    if (JSID_IS_STRING(id))
++    if (JSID_IS_STRING(id)) {
+         return JSID_TO_ATOM(id);
++    }
+ 
+-    if (MOZ_LIKELY(JSID_IS_INT(id)))
++    if (MOZ_LIKELY(JSID_IS_INT(id))) {
+         return Int32ToString<CanGC>(cx, JSID_TO_INT(id));
++    }
+ 
+     RootedValue idv(cx, IdToValue(id));
+     JSString* str = ToStringSlow<CanGC>(cx, idv);
+-    if (!str)
++    if (!str) {
+         return nullptr;
++    }
+ 
+     return str->ensureFlat(cx);
+ }
+ 
+ inline Handle<PropertyName*>
+ TypeName(JSType type, const JSAtomState& names)
+ {
+     MOZ_ASSERT(type < JSTYPE_LIMIT);
+diff --git a/js/src/vm/JSAtom.cpp b/js/src/vm/JSAtom.cpp
+--- a/js/src/vm/JSAtom.cpp
++++ b/js/src/vm/JSAtom.cpp
+@@ -183,34 +183,37 @@ JSRuntime::initializeAtoms(JSContext* cx
+         JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
+ #undef COMMON_NAME_INFO
+ #define COMMON_NAME_INFO(name) { "Symbol." #name, sizeof("Symbol." #name) - 1 },
+         JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
+ #undef COMMON_NAME_INFO
+     };
+ 
+     commonNames = js_new<JSAtomState>();
+-    if (!commonNames)
++    if (!commonNames) {
+         return false;
++    }
+ 
+     ImmutablePropertyNamePtr* names = reinterpret_cast<ImmutablePropertyNamePtr*>(commonNames.ref());
+     for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
+         JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, PinAtom);
+-        if (!atom)
++        if (!atom) {
+             return false;
++        }
+         names->init(atom->asPropertyName());
+     }
+     MOZ_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
+ 
+     emptyString = commonNames->empty;
+ 
+     // Create the well-known symbols.
+     wellKnownSymbols = js_new<WellKnownSymbols>();
+-    if (!wellKnownSymbols)
++    if (!wellKnownSymbols) {
+         return false;
++    }
+ 
+     ImmutablePropertyNamePtr* descriptions = commonNames->wellKnownSymbolDescriptions();
+     ImmutableSymbolPtr* symbols = reinterpret_cast<ImmutableSymbolPtr*>(wellKnownSymbols.ref());
+     for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
+         JS::Symbol* symbol = JS::Symbol::new_(cx, JS::SymbolCode(i), descriptions[i]);
+         if (!symbol) {
+             ReportOutOfMemory(cx);
+             return false;
+@@ -349,18 +352,19 @@ AtomizeAndCopyCharsInner(JSContext* cx, 
+ 
+ /* |tbchars| must not point into an inline or short string. */
+ template <typename CharT>
+ MOZ_ALWAYS_INLINE
+ static JSAtom*
+ AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
+                     const Maybe<uint32_t>& indexValue)
+ {
+-    if (JSAtom* s = cx->staticStrings().lookup(tbchars, length))
++    if (JSAtom* s = cx->staticStrings().lookup(tbchars, length)) {
+         return s;
++    }
+ 
+     AtomHasher::Lookup lookup(tbchars, length);
+ 
+     // Try the per-Zone cache first. If we find the atom there we can avoid the
+     // atoms lock, the markAtom call, and the multiple HashSet lookups below.
+     // We don't use the per-Zone cache if we want a pinned atom: handling that
+     // is more complicated and pinning atoms is relatively uncommon.
+     Zone* zone = cx->zone();
+@@ -567,108 +571,120 @@ js::AtomizeUTF8Chars(JSContext* cx, cons
+     // This could be optimized to hand the char16_t's directly to the JSAtom
+     // instead of making a copy. UTF8CharsToNewTwoByteCharsZ should be
+     // refactored to take an JSContext so that this function could also.
+ 
+     UTF8Chars utf8(utf8Chars, utf8ByteLength);
+ 
+     size_t length;
+     UniqueTwoByteChars chars(JS::UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get());
+-    if (!chars)
++    if (!chars) {
+         return nullptr;
++    }
+ 
+     return AtomizeChars(cx, chars.get(), length);
+ }
+ 
+ bool
+ js::IndexToIdSlow(JSContext* cx, uint32_t index, MutableHandleId idp)
+ {
+     MOZ_ASSERT(index > JSID_INT_MAX);
+ 
+     char16_t buf[UINT32_CHAR_BUFFER_LENGTH];
+     RangedPtr<char16_t> end(ArrayEnd(buf), buf, ArrayEnd(buf));
+     RangedPtr<char16_t> start = BackfillIndexInCharBuffer(index, end);
+ 
+     JSAtom* atom = AtomizeChars(cx, start.get(), end - start);
+-    if (!atom)
++    if (!atom) {
+         return false;
++    }
+ 
+     idp.set(JSID_FROM_BITS((size_t)atom | JSID_TYPE_STRING));
+     return true;
+ }
+ 
+ template <AllowGC allowGC>
+ static JSAtom*
+ ToAtomSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
+ {
+     MOZ_ASSERT(!arg.isString());
+ 
+     Value v = arg;
+     if (!v.isPrimitive()) {
+         MOZ_ASSERT(!cx->helperThread());
+-        if (!allowGC)
++        if (!allowGC) {
+             return nullptr;
++        }
+         RootedValue v2(cx, v);
+-        if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
++        if (!ToPrimitive(cx, JSTYPE_STRING, &v2)) {
+             return nullptr;
++        }
+         v = v2;
+     }
+ 
+     if (v.isString()) {
+         JSAtom* atom = AtomizeString(cx, v.toString());
+-        if (!allowGC && !atom)
++        if (!allowGC && !atom) {
+             cx->recoverFromOutOfMemory();
++        }
+         return atom;
+     }
+     if (v.isInt32()) {
+         JSAtom* atom = Int32ToAtom(cx, v.toInt32());
+-        if (!allowGC && !atom)
++        if (!allowGC && !atom) {
+             cx->recoverFromOutOfMemory();
++        }
+         return atom;
+     }
+     if (v.isDouble()) {
+         JSAtom* atom = NumberToAtom(cx, v.toDouble());
+-        if (!allowGC && !atom)
++        if (!allowGC && !atom) {
+             cx->recoverFromOutOfMemory();
++        }
+         return atom;
+     }
+-    if (v.isBoolean())
++    if (v.isBoolean()) {
+         return v.toBoolean() ? cx->names().true_ : cx->names().false_;
+-    if (v.isNull())
++    }
++    if (v.isNull()) {
+         return cx->names().null;
++    }
+     if (v.isSymbol()) {
+         MOZ_ASSERT(!cx->helperThread());
+         if (allowGC) {
+             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                       JSMSG_SYMBOL_TO_STRING);
+         }
+         return nullptr;
+     }
+ #ifdef ENABLE_BIGINT
+     if (v.isBigInt()) {
+         JSAtom* atom = BigIntToAtom(cx, v.toBigInt());
+-        if (!allowGC && !atom)
++        if (!allowGC && !atom) {
+             cx->recoverFromOutOfMemory();
++        }
+         return atom;
+     }
+ #endif
+     MOZ_ASSERT(v.isUndefined());
+     return cx->names().undefined;
+ }
+ 
+ template <AllowGC allowGC>
+ JSAtom*
+ js::ToAtom(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v)
+ {
+-    if (!v.isString())
++    if (!v.isString()) {
+         return ToAtomSlow<allowGC>(cx, v);
++    }
+ 
+     JSString* str = v.toString();
+-    if (str->isAtom())
++    if (str->isAtom()) {
+         return &str->asAtom();
++    }
+ 
+     JSAtom* atom = AtomizeString(cx, str);
+     if (!atom && !allowGC) {
+         MOZ_ASSERT_IF(!cx->helperThread(), cx->isThrowingOutOfMemory());
+         cx->recoverFromOutOfMemory();
+     }
+     return atom;
+ }
+@@ -697,23 +713,25 @@ js::XDRAtom(XDRState<mode>* xdr, Mutable
+ 
+     if (mode == XDR_DECODE) {
+         length = lengthAndEncoding >> 1;
+         latin1 = lengthAndEncoding & 0x1;
+     }
+ 
+     // We need to align the string in the XDR buffer such that we can avoid
+     // non-align loads of 16bits characters.
+-    if (!latin1)
++    if (!latin1) {
+         MOZ_TRY(xdr->codeAlign(sizeof(char16_t)));
++    }
+ 
+     if (mode == XDR_ENCODE) {
+         JS::AutoCheckCannotGC nogc;
+-        if (latin1)
++        if (latin1) {
+             return xdr->codeChars(atomp->latin1Chars(nogc), length);
++        }
+         return xdr->codeChars(const_cast<char16_t*>(atomp->twoByteChars(nogc)), length);
+     }
+ 
+     MOZ_ASSERT(mode == XDR_DECODE);
+     /* Avoid JSString allocation for already existing atoms. See bug 321985. */
+     JSContext* cx = xdr->cx();
+     JSAtom* atom;
+     if (latin1) {
+@@ -750,29 +768,31 @@ js::XDRAtom(XDRState<mode>* xdr, Mutable
+             chars = stackChars;
+         } else {
+             /*
+              * This is very uncommon. Don't use the tempLifoAlloc arena for this as
+              * most allocations here will be bigger than tempLifoAlloc's default
+              * chunk size.
+              */
+             heapChars.reset(cx->pod_malloc<char16_t>(length));
+-            if (!heapChars)
++            if (!heapChars) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+ 
+             chars = heapChars.get();
+         }
+ 
+         MOZ_TRY(xdr->codeChars(chars, length));
+         atom = AtomizeChars(cx, chars, length);
+ #endif /* !MOZ_LITTLE_ENDIAN */
+     }
+ 
+-    if (!atom)
++    if (!atom) {
+         return xdr->fail(JS::TranscodeResult_Throw);
++    }
+     atomp.set(atom);
+     return Ok();
+ }
+ 
+ template XDRResult
+ js::XDRAtom(XDRState<XDR_ENCODE>* xdr, MutableHandleAtom atomp);
+ 
+ template XDRResult
+diff --git a/js/src/vm/JSAtom.cpp.1488698-7.later b/js/src/vm/JSAtom.cpp.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSAtom.cpp.1488698-7.later
+@@ -0,0 +1,557 @@
++--- JSAtom.cpp
+++++ JSAtom.cpp
++@@ -83,40 +83,45 @@ js::AtomHasher::hash(const Lookup& l)
++ {
++     return l.hash;
++ }
++ 
++ MOZ_ALWAYS_INLINE bool
++ js::AtomHasher::match(const AtomStateEntry& entry, const Lookup& lookup)
++ {
++     JSAtom* key = entry.asPtrUnbarriered();
++-    if (lookup.atom)
+++    if (lookup.atom) {
++         return lookup.atom == key;
++-    if (key->length() != lookup.length || key->hash() != lookup.hash)
+++    }
+++    if (key->length() != lookup.length || key->hash() != lookup.hash) {
++         return false;
+++    }
++ 
++     if (key->hasLatin1Chars()) {
++         const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
++-        if (lookup.isLatin1)
+++        if (lookup.isLatin1) {
++             return mozilla::ArrayEqual(keyChars, lookup.latin1Chars, lookup.length);
+++        }
++         return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
++     }
++ 
++     const char16_t* keyChars = key->twoByteChars(lookup.nogc);
++-    if (lookup.isLatin1)
+++    if (lookup.isLatin1) {
++         return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
+++    }
++     return mozilla::ArrayEqual(keyChars, lookup.twoByteChars, lookup.length);
++ }
++ 
++ inline JSAtom*
++ js::AtomStateEntry::asPtr(JSContext* cx) const
++ {
++     JSAtom* atom = asPtrUnbarriered();
++-    if (!cx->helperThread())
+++    if (!cx->helperThread()) {
++         JSString::readBarrier(atom);
+++    }
++     return atom;
++ }
++ 
++ UniqueChars
++ js::AtomToPrintableString(JSContext* cx, JSAtom* atom)
++ {
++     return QuoteString(cx, atom);
++ }
++@@ -156,29 +161,32 @@ JSRuntime::initializeAtoms(JSContext* cx
++         permanentAtoms_ = parentRuntime->permanentAtoms_;
++ 
++         staticStrings = parentRuntime->staticStrings;
++         commonNames = parentRuntime->commonNames;
++         emptyString = parentRuntime->emptyString;
++         wellKnownSymbols = parentRuntime->wellKnownSymbols;
++ 
++         atoms_ = js_new<AtomsTable>();
++-        if (!atoms_)
+++        if (!atoms_) {
++             return false;
+++        }
++ 
++         return atoms_->init();
++     }
++ 
++     permanentAtomsDuringInit_ = js_new<AtomSet>(JS_PERMANENT_ATOM_SIZE);
++-    if (!permanentAtomsDuringInit_)
+++    if (!permanentAtomsDuringInit_) {
++         return false;
+++    }
++ 
++     staticStrings = js_new<StaticStrings>();
++-    if (!staticStrings || !staticStrings->init(cx))
+++    if (!staticStrings || !staticStrings->init(cx)) {
++         return false;
+++    }
++ 
++     static const CommonNameInfo cachedNames[] = {
++ #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
++         FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
++ #undef COMMON_NAME_INFO
++ #define COMMON_NAME_INFO(name, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
++         JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
++ #undef COMMON_NAME_INFO
++@@ -254,69 +265,74 @@ class AtomsTable::AutoLock
++     MOZ_ALWAYS_INLINE explicit AutoLock(JSRuntime* rt, Mutex& aLock) {
++         if (rt->hasHelperThreadZones()) {
++             lock = &aLock;
++             lock->lock();
++         }
++     }
++ 
++     MOZ_ALWAYS_INLINE ~AutoLock() {
++-        if (lock)
+++        if (lock) {
++             lock->unlock();
+++        }
++     }
++ };
++ 
++ AtomsTable::Partition::Partition(uint32_t index)
++   : lock(MutexId { mutexid::AtomsTable.name, mutexid::AtomsTable.order + index }),
++     atoms(InitialTableSize),
++     atomsAddedWhileSweeping(nullptr)
++ {}
++ 
++ AtomsTable::Partition::~Partition()
++ {
++     MOZ_ASSERT(!atomsAddedWhileSweeping);
++ }
++ 
++ AtomsTable::~AtomsTable()
++ {
++-    for (size_t i = 0; i < PartitionCount; i++)
+++    for (size_t i = 0; i < PartitionCount; i++) {
++         js_delete(partitions[i]);
+++    }
++ }
++ 
++ bool
++ AtomsTable::init()
++ {
++     for (size_t i = 0; i < PartitionCount; i++) {
++         partitions[i] = js_new<Partition>(i);
++-        if (!partitions[i])
+++        if (!partitions[i]) {
++             return false;
+++        }
++     }
++     return true;
++ }
++ 
++ void
++ AtomsTable::lockAll()
++ {
++     MOZ_ASSERT(!allPartitionsLocked);
++ 
++-    for (size_t i = 0; i < PartitionCount; i++)
+++    for (size_t i = 0; i < PartitionCount; i++) {
++         partitions[i]->lock.lock();
+++    }
++ 
++ #ifdef DEBUG
++     allPartitionsLocked = true;
++ #endif
++ }
++ 
++ void
++ AtomsTable::unlockAll()
++ {
++     MOZ_ASSERT(allPartitionsLocked);
++ 
++-    for (size_t i = 0; i < PartitionCount; i++)
+++    for (size_t i = 0; i < PartitionCount; i++) {
++         partitions[PartitionCount - i - 1]->lock.unlock();
+++    }
++ 
++ #ifdef DEBUG
++     allPartitionsLocked = false;
++ #endif
++ }
++ 
++ MOZ_ALWAYS_INLINE size_t
++ AtomsTable::getPartitionIndex(const AtomHasher::Lookup& lookup)
++@@ -341,27 +357,29 @@ AtomsTable::tracePinnedAtomsInSet(JSTrac
++ }
++ 
++ void
++ AtomsTable::tracePinnedAtoms(JSTracer* trc, const AutoAccessAtomsZone& access)
++ {
++     for (size_t i = 0; i < PartitionCount; i++) {
++         Partition& part = *partitions[i];
++         tracePinnedAtomsInSet(trc, part.atoms);
++-        if (part.atomsAddedWhileSweeping)
+++        if (part.atomsAddedWhileSweeping) {
++             tracePinnedAtomsInSet(trc, *part.atomsAddedWhileSweeping);
+++        }
++     }
++ }
++ 
++ void
++ js::TraceAtoms(JSTracer* trc, const AutoAccessAtomsZone& access)
++ {
++     JSRuntime* rt = trc->runtime();
++-    if (rt->permanentAtomsPopulated())
+++    if (rt->permanentAtomsPopulated()) {
++         rt->atoms().tracePinnedAtoms(trc, access);
+++    }
++ }
++ 
++ static void
++ TracePermanentAtoms(JSTracer* trc, AtomSet::Range atoms)
++ {
++     for (; !atoms.empty(); atoms.popFront()) {
++         const AtomStateEntry& entry = atoms.front();
++         JSAtom* atom = entry.asPtrUnbarriered();
++@@ -369,54 +387,61 @@ TracePermanentAtoms(JSTracer* trc, AtomS
++         TraceProcessGlobalRoot(trc, atom, "permanent atom");
++     }
++ }
++ 
++ void
++ JSRuntime::tracePermanentAtoms(JSTracer* trc)
++ {
++     // Permanent atoms only need to be traced in the runtime which owns them.
++-    if (parentRuntime)
+++    if (parentRuntime) {
++         return;
+++    }
++ 
++     // Static strings are not included in the permanent atoms table.
++-    if (staticStrings)
+++    if (staticStrings) {
++         staticStrings->trace(trc);
+++    }
++ 
++-    if (permanentAtomsDuringInit_)
+++    if (permanentAtomsDuringInit_) {
++         TracePermanentAtoms(trc, permanentAtomsDuringInit_->all());
+++    }
++ 
++-    if (permanentAtoms_)
+++    if (permanentAtoms_) {
++         TracePermanentAtoms(trc, permanentAtoms_->all());
+++    }
++ }
++ 
++ void
++ js::TraceWellKnownSymbols(JSTracer* trc)
++ {
++     JSRuntime* rt = trc->runtime();
++ 
++-    if (rt->parentRuntime)
+++    if (rt->parentRuntime) {
++         return;
+++    }
++ 
++     if (WellKnownSymbols* wks = rt->wellKnownSymbols) {
++-        for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++)
+++        for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
++             TraceProcessGlobalRoot(trc, wks->get(i).get(), "well_known_symbol");
+++        }
++     }
++ }
++ 
++ void
++ AtomsTable::sweepAll(JSRuntime* rt)
++ {
++     for (size_t i = 0; i < PartitionCount; i++) {
++         AutoLock lock(rt, partitions[i]->lock);
++         AtomSet& atoms = partitions[i]->atoms;
++         for (AtomSet::Enum e(atoms); !e.empty(); e.popFront()) {
++             JSAtom* atom = e.front().asPtrUnbarriered();
++-            if (IsAboutToBeFinalizedUnbarriered(&atom))
+++            if (IsAboutToBeFinalizedUnbarriered(&atom)) {
++                 e.removeFront();
+++            }
++         }
++     }
++ }
++ 
++ AtomsTable::SweepIterator::SweepIterator(AtomsTable& atoms)
++   : atoms(atoms),
++     partitionIndex(0)
++ {
++@@ -441,18 +466,19 @@ AtomsTable::SweepIterator::finishSweepin
++ inline void
++ AtomsTable::SweepIterator::settle()
++ {
++     MOZ_ASSERT(!empty());
++ 
++     while (atomsIter->empty()) {
++         finishSweepingPartition();
++         partitionIndex++;
++-        if (empty())
+++        if (empty()) {
++             return;
+++        }
++         startSweepingPartition();
++     }
++ }
++ 
++ inline bool
++ AtomsTable::SweepIterator::empty() const
++ {
++     return partitionIndex == PartitionCount;
++@@ -517,40 +543,44 @@ AtomsTable::mergeAtomsAddedWhileSweeping
++     // the main table.
++ 
++     AutoEnterOOMUnsafeRegion oomUnsafe;
++ 
++     auto newAtoms = part.atomsAddedWhileSweeping;
++     part.atomsAddedWhileSweeping = nullptr;
++ 
++     for (auto r = newAtoms->all(); !r.empty(); r.popFront()) {
++-        if (!part.atoms.putNew(AtomHasher::Lookup(r.front().asPtrUnbarriered()), r.front()))
+++        if (!part.atoms.putNew(AtomHasher::Lookup(r.front().asPtrUnbarriered()), r.front())) {
++             oomUnsafe.crash("Adding atom from secondary table after sweep");
+++        }
++     }
++ 
++     js_delete(newAtoms);
++ }
++ 
++ bool
++ AtomsTable::sweepIncrementally(SweepIterator& atomsToSweep, SliceBudget& budget)
++ {
++     // Sweep the table incrementally until we run out of work or budget.
++     while (!atomsToSweep.empty()) {
++         budget.step();
++-        if (budget.isOverBudget())
+++        if (budget.isOverBudget()) {
++             return false;
+++        }
++ 
++         JSAtom* atom = atomsToSweep.front();
++-        if (IsAboutToBeFinalizedUnbarriered(&atom))
+++        if (IsAboutToBeFinalizedUnbarriered(&atom)) {
++             atomsToSweep.removeFront();
+++        }
++         atomsToSweep.popFront();
++     }
++ 
++-    for (size_t i = 0; i < PartitionCount; i++)
+++    for (size_t i = 0; i < PartitionCount; i++) {
++         MOZ_ASSERT(!partitions[i]->atomsAddedWhileSweeping);
+++    }
++ 
++     return true;
++ }
++ 
++ size_t
++ AtomsTable::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
++ {
++     size_t size = sizeof(AtomsTable);
++@@ -618,44 +649,47 @@ AtomizeAndCopyChars(JSContext* cx, const
++             MOZ_ASSERT(AtomIsMarked(zone, atom));
++             return atom;
++         }
++     }
++ 
++     // This function can be called during initialization, while the permanent
++     // atoms table is being created. In this case all atoms created are added to
++     // the permanent atoms table.
++-    if (!cx->permanentAtomsPopulated())
+++    if (!cx->permanentAtomsPopulated()) {
++         return PermanentlyAtomizeAndCopyChars(cx, zonePtr, tbchars, length, indexValue, lookup);
+++    }
++ 
++     AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
++     if (pp) {
++         JSAtom* atom = pp->asPtr(cx);
++         if (zonePtr &&
++             MOZ_UNLIKELY(!zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false))))
++         {
++             ReportOutOfMemory(cx);
++             return nullptr;
++         }
++ 
++         return atom;
++     }
++ 
++     // Validate the length before taking an atoms partition lock, as throwing an
++     // exception here may reenter this code.
++-    if (MOZ_UNLIKELY(!JSString::validateLength(cx, length)))
+++    if (MOZ_UNLIKELY(!JSString::validateLength(cx, length))) {
++         return nullptr;
+++    }
++ 
++     JSAtom* atom = cx->atoms().atomizeAndCopyChars(cx,
++                                                    tbchars, length,
++                                                    pin,
++                                                    indexValue,
++                                                    lookup);
++-    if (!atom)
+++    if (!atom) {
++         return nullptr;
+++    }
++ 
++     cx->atomMarking().inlinedMarkAtom(cx, atom);
++ 
++     if (zonePtr &&
++         MOZ_UNLIKELY(!zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false))))
++     {
++         ReportOutOfMemory(cx);
++         return nullptr;
++@@ -687,34 +721,36 @@ AtomsTable::atomizeAndCopyChars(JSContex
++         // be added to a secondary table. Check this first.
++         p = atomsAddedWhileSweeping->lookupForAdd(lookup);
++ 
++         // If that fails check the main table but check if any atom found there
++         // is dead.
++         if (!p) {
++             if (AtomSet::AddPtr p2 = atoms.lookupForAdd(lookup)) {
++                 JSAtom* atom = p2->asPtrUnbarriered();
++-                if (!IsAboutToBeFinalizedUnbarriered(&atom))
+++                if (!IsAboutToBeFinalizedUnbarriered(&atom)) {
++                     p = p2;
+++                }
++             }
++         }
++     }
++ 
++     if (p) {
++         JSAtom* atom = p->asPtr(cx);
++         if (pin && !atom->isPinned()) {
++             atom->setPinned();
++             p->setPinned(true);
++         }
++         return atom;
++     }
++ 
++     JSAtom* atom = AllocateNewAtom(cx, tbchars, length, pin, indexValue, lookup);
++-    if (!atom)
+++    if (!atom) {
++         return nullptr;
+++    }
++ 
++     // We have held the lock since looking up p, and the operations we've done
++     // since then can't GC; therefore the atoms table has not been modified and
++     // p is still valid.
++     AtomSet* addSet = part.atomsAddedWhileSweeping ? part.atomsAddedWhileSweeping : &atoms;
++     if (MOZ_UNLIKELY(!addSet->add(p, AtomStateEntry(atom, bool(pin))))) {
++         ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
++         return nullptr;
++@@ -740,22 +776,24 @@ PermanentlyAtomizeAndCopyChars(JSContext
++                                const AtomHasher::Lookup& lookup)
++ {
++     MOZ_ASSERT(!cx->permanentAtomsPopulated());
++     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
++ 
++     JSRuntime* rt = cx->runtime();
++     AtomSet& atoms = *rt->permanentAtomsDuringInit();
++     AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
++-    if (p)
+++    if (p) {
++         return p->asPtr(cx);
+++    }
++ 
++     JSAtom* atom = AllocateNewAtom(cx, tbchars, length, DoNotPinAtom, indexValue, lookup);
++-    if (!atom)
+++    if (!atom) {
++         return nullptr;
+++    }
++ 
++     atom->morphIntoPermanentAtom();
++ 
++     // We are single threaded at this point, and the operations we've done since
++     // then can't GC; therefore the atoms table has not been modified and p is
++     // still valid.
++     if (!atoms.add(p, AtomStateEntry(atom, true))) {
++         ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
++@@ -786,45 +824,50 @@ AllocateNewAtom(JSContext* cx, const Cha
++         // please also fix or comment the similar case in Symbol::new_.
++         ReportOutOfMemory(cx);
++         return nullptr;
++     }
++ 
++     JSAtom* atom = flat->morphAtomizedStringIntoAtom(lookup.hash);
++     MOZ_ASSERT(atom->hash() == lookup.hash);
++ 
++-    if (pin)
+++    if (pin) {
++         atom->setPinned();
+++    }
++ 
++-    if (indexValue)
+++    if (indexValue) {
++         atom->maybeInitializeIndex(*indexValue, true);
+++    }
++ 
++     return atom;
++ }
++ 
++ JSAtom*
++ js::AtomizeString(JSContext* cx, JSString* str,
++                   js::PinningBehavior pin /* = js::DoNotPinAtom */)
++ {
++     if (str->isAtom()) {
++         JSAtom& atom = str->asAtom();
++         /* N.B. static atoms are effectively always interned. */
++-        if (pin == PinAtom && !atom.isPinned())
+++        if (pin == PinAtom && !atom.isPinned()) {
++             cx->runtime()->atoms().pinExistingAtom(cx, &atom);
+++        }
++ 
++         return &atom;
++     }
++ 
++     JSLinearString* linear = str->ensureLinear(cx);
++-    if (!linear)
+++    if (!linear) {
++         return nullptr;
+++    }
++ 
++     Maybe<uint32_t> indexValue;
++-    if (str->hasIndexValue())
+++    if (str->hasIndexValue()) {
++         indexValue.emplace(str->getIndexValue());
+++    }
++ 
++     JS::AutoCheckCannotGC nogc;
++     return linear->hasLatin1Chars()
++            ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), pin, indexValue)
++            : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), pin, indexValue);
++ }
++ 
++ void
++@@ -833,18 +876,19 @@ AtomsTable::pinExistingAtom(JSContext* c
++     MOZ_ASSERT(atom);
++     MOZ_ASSERT(!atom->isPinned());
++ 
++     AtomHasher::Lookup lookup(atom);
++ 
++     AtomsTable::Partition& part = *partitions[getPartitionIndex(lookup)];
++     AtomsTable::AutoLock lock(cx->runtime(), part.lock);
++     AtomSet::Ptr p = part.atoms.lookup(lookup);
++-    if (!p && part.atomsAddedWhileSweeping)
+++    if (!p && part.atomsAddedWhileSweeping) {
++         p = part.atomsAddedWhileSweeping->lookup(lookup);
+++    }
++ 
++     MOZ_ASSERT(p); // Unpinned atoms must exist in atoms table.
++     MOZ_ASSERT(p->asPtrUnbarriered() == atom);
++ 
++     atom->setPinned();
++     p->setPinned(true);
++ }
++ 
++@@ -1094,18 +1154,20 @@ js::ClassName(JSProtoKey key, JSContext*
++ {
++     return ClassName(key, cx->names());
++ }
++ 
++ js::AutoLockAllAtoms::AutoLockAllAtoms(JSRuntime* rt)
++   : runtime(rt)
++ {
++     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
++-    if (runtime->hasHelperThreadZones())
+++    if (runtime->hasHelperThreadZones()) {
++         runtime->atoms().lockAll();
+++    }
++ }
++ 
++ js::AutoLockAllAtoms::~AutoLockAllAtoms()
++ {
++     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
++-    if (runtime->hasHelperThreadZones())
+++    if (runtime->hasHelperThreadZones()) {
++         runtime->atoms().unlockAll();
+++    }
++ }
+diff --git a/js/src/vm/JSContext-inl.h.1488698-7.later b/js/src/vm/JSContext-inl.h.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSContext-inl.h.1488698-7.later
+@@ -0,0 +1,399 @@
++--- JSContext-inl.h
+++++ JSContext-inl.h
++@@ -53,28 +53,31 @@ class ContextChecks
++                                 c1, c2, argIndex);
++     }
++     static void fail(JS::Zone* z1, JS::Zone* z2, int argIndex) {
++         MOZ_CRASH_UNSAFE_PRINTF("*** Zone mismatch %p vs. %p at argument %d\n",
++                                 z1, z2, argIndex);
++     }
++ 
++     void check(JS::Realm* r, int argIndex) {
++-        if (r && r != realm())
+++        if (r && r != realm()) {
++             fail(realm(), r, argIndex);
+++        }
++     }
++ 
++     void check(JS::Compartment* c, int argIndex) {
++-        if (c && c != compartment())
+++        if (c && c != compartment()) {
++             fail(compartment(), c, argIndex);
+++        }
++     }
++ 
++     void check(JS::Zone* z, int argIndex) {
++-        if (zone() && z != zone())
+++        if (zone() && z != zone()) {
++             fail(zone(), z, argIndex);
+++        }
++     }
++ 
++     void check(JSObject* obj, int argIndex) {
++         if (obj) {
++             MOZ_ASSERT(JS::ObjectIsNotGray(obj));
++             MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(&obj));
++             check(obj->compartment(), argIndex);
++         }
++@@ -95,82 +98,91 @@ class ContextChecks
++                                         zone(), argIndex);
++             }
++         }
++ #endif
++     }
++ 
++     void check(JSString* str, int argIndex) {
++         MOZ_ASSERT(JS::CellIsNotGray(str));
++-        if (str->isAtom())
+++        if (str->isAtom()) {
++             checkAtom(&str->asAtom(), argIndex);
++-        else
+++        } else {
++             check(str->zone(), argIndex);
+++        }
++     }
++ 
++     void check(JS::Symbol* symbol, int argIndex) {
++         checkAtom(symbol, argIndex);
++     }
++ 
++     void check(const js::Value& v, int argIndex) {
++-        if (v.isObject())
+++        if (v.isObject()) {
++             check(&v.toObject(), argIndex);
++-        else if (v.isString())
+++        } else if (v.isString()) {
++             check(v.toString(), argIndex);
++-        else if (v.isSymbol())
+++        } else if (v.isSymbol()) {
++             check(v.toSymbol(), argIndex);
+++        }
++     }
++ 
++     // Check the contents of any container class that supports the C++
++     // iteration protocol, eg GCVector<jsid>.
++     template <typename Container>
++     typename mozilla::EnableIf<
++         mozilla::IsSame<
++             decltype(((Container*)nullptr)->begin()),
++             decltype(((Container*)nullptr)->end())
++         >::value
++     >::Type
++     check(const Container& container, int argIndex) {
++-        for (auto i : container)
+++        for (auto i : container) {
++             check(i, argIndex);
+++        }
++     }
++ 
++     void check(const JS::HandleValueArray& arr, int argIndex) {
++-        for (size_t i = 0; i < arr.length(); i++)
+++        for (size_t i = 0; i < arr.length(); i++) {
++             check(arr[i], argIndex);
+++        }
++     }
++ 
++     void check(const CallArgs& args, int argIndex) {
++-        for (Value* p = args.base(); p != args.end(); ++p)
+++        for (Value* p = args.base(); p != args.end(); ++p) {
++             check(*p, argIndex);
+++        }
++     }
++ 
++     void check(jsid id, int argIndex) {
++-        if (JSID_IS_ATOM(id))
+++        if (JSID_IS_ATOM(id)) {
++             checkAtom(JSID_TO_ATOM(id), argIndex);
++-        else if (JSID_IS_SYMBOL(id))
+++        } else if (JSID_IS_SYMBOL(id)) {
++             checkAtom(JSID_TO_SYMBOL(id), argIndex);
++-        else
+++        } else {
++             MOZ_ASSERT(!JSID_IS_GCTHING(id));
+++        }
++     }
++ 
++     void check(JSScript* script, int argIndex) {
++         MOZ_ASSERT(JS::CellIsNotGray(script));
++-        if (script)
+++        if (script) {
++             check(script->realm(), argIndex);
+++        }
++     }
++ 
++     void check(AbstractFramePtr frame, int argIndex);
++ 
++     void check(Handle<PropertyDescriptor> desc, int argIndex) {
++         check(desc.object(), argIndex);
++-        if (desc.hasGetterObject())
+++        if (desc.hasGetterObject()) {
++             check(desc.getterObject(), argIndex);
++-        if (desc.hasSetterObject())
+++        }
+++        if (desc.hasSetterObject()) {
++             check(desc.setterObject(), argIndex);
+++        }
++         check(desc.value(), argIndex);
++     }
++ 
++     void check(TypeSet::Type type, int argIndex) {
++         check(type.maybeCompartment(), argIndex);
++     }
++ };
++ 
++@@ -182,34 +194,37 @@ JSContext::checkImpl(int argIndex, const
++     js::ContextChecks(this).check(head, argIndex);
++     checkImpl(argIndex + 1, tail...);
++ }
++ 
++ template <class... Args> inline void
++ JSContext::check(const Args&... args)
++ {
++ #ifdef JS_CRASH_DIAGNOSTICS
++-    if (contextChecksEnabled())
+++    if (contextChecksEnabled()) {
++         checkImpl(0, args...);
+++    }
++ #endif
++ }
++ 
++ template <class... Args> inline void
++ JSContext::releaseCheck(const Args&... args)
++ {
++-    if (contextChecksEnabled())
+++    if (contextChecksEnabled()) {
++         checkImpl(0, args...);
+++    }
++ }
++ 
++ template <class... Args> MOZ_ALWAYS_INLINE void
++ JSContext::debugOnlyCheck(const Args&... args)
++ {
++ #if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
++-    if (contextChecksEnabled())
+++    if (contextChecksEnabled()) {
++         checkImpl(0, args...);
+++    }
++ #endif
++ }
++ 
++ namespace js {
++ 
++ STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
++ MOZ_ALWAYS_INLINE bool
++ CallNativeImpl(JSContext* cx, NativeImpl impl, const CallArgs& args)
++@@ -225,69 +240,76 @@ CallNativeImpl(JSContext* cx, NativeImpl
++     }
++     return ok;
++ }
++ 
++ MOZ_ALWAYS_INLINE bool
++ CallJSGetterOp(JSContext* cx, GetterOp op, HandleObject obj, HandleId id,
++                MutableHandleValue vp)
++ {
++-    if (!CheckRecursionLimit(cx))
+++    if (!CheckRecursionLimit(cx)) {
++         return false;
+++    }
++ 
++     cx->check(obj, id, vp);
++     bool ok = op(cx, obj, id, vp);
++-    if (ok)
+++    if (ok) {
++         cx->check(vp);
+++    }
++     return ok;
++ }
++ 
++ MOZ_ALWAYS_INLINE bool
++ CallJSSetterOp(JSContext* cx, SetterOp op, HandleObject obj, HandleId id, HandleValue v,
++                ObjectOpResult& result)
++ {
++-    if (!CheckRecursionLimit(cx))
+++    if (!CheckRecursionLimit(cx)) {
++         return false;
+++    }
++ 
++     cx->check(obj, id, v);
++     return op(cx, obj, id, v, result);
++ }
++ 
++ inline bool
++ CallJSAddPropertyOp(JSContext* cx, JSAddPropertyOp op, HandleObject obj, HandleId id,
++                     HandleValue v)
++ {
++-    if (!CheckRecursionLimit(cx))
+++    if (!CheckRecursionLimit(cx)) {
++         return false;
+++    }
++ 
++     cx->check(obj, id, v);
++     return op(cx, obj, id, v);
++ }
++ 
++ inline bool
++ CallJSDeletePropertyOp(JSContext* cx, JSDeletePropertyOp op, HandleObject receiver, HandleId id,
++                        ObjectOpResult& result)
++ {
++-    if (!CheckRecursionLimit(cx))
+++    if (!CheckRecursionLimit(cx)) {
++         return false;
+++    }
++ 
++     cx->check(receiver, id);
++-    if (op)
+++    if (op) {
++         return op(cx, receiver, id, result);
+++    }
++     return result.succeed();
++ }
++ 
++ MOZ_ALWAYS_INLINE bool
++ CheckForInterrupt(JSContext* cx)
++ {
++     MOZ_ASSERT(!cx->isExceptionPending());
++     // Add an inline fast-path since we have to check for interrupts in some hot
++     // C++ loops of library builtins.
++-    if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt()))
+++    if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt())) {
++         return cx->handleInterrupt();
+++    }
++ 
++     JS_INTERRUPT_POSSIBLY_FAIL();
++ 
++     return true;
++ }
++ 
++ }  /* namespace js */
++ 
++@@ -312,22 +334,24 @@ JSContext::minorGC(JS::gcreason::Reason 
++ inline void
++ JSContext::setPendingException(JS::HandleValue v)
++ {
++ #if defined(NIGHTLY_BUILD)
++     do {
++         // Do not intercept exceptions if we are already
++         // in the exception interceptor. That would lead
++         // to infinite recursion.
++-        if (this->runtime()->errorInterception.isExecuting)
+++        if (this->runtime()->errorInterception.isExecuting) {
++             break;
+++        }
++ 
++         // Check whether we have an interceptor at all.
++-        if (!this->runtime()->errorInterception.interceptor)
+++        if (!this->runtime()->errorInterception.interceptor) {
++             break;
+++        }
++ 
++         // Make sure that we do not call the interceptor from within
++         // the interceptor.
++         this->runtime()->errorInterception.isExecuting = true;
++ 
++         // The interceptor must be infallible.
++         const mozilla::DebugOnly<bool> wasExceptionPending = this->isExceptionPending();
++         this->runtime()->errorInterception.interceptor->interceptError(this, v);
++@@ -365,18 +389,19 @@ JSContext::enterAtomsZone()
++ {
++     realm_ = nullptr;
++     setZone(runtime_->unsafeAtomsZone(), AtomsZone);
++ }
++ 
++ inline void
++ JSContext::setZone(js::Zone *zone, JSContext::IsAtomsZone isAtomsZone)
++ {
++-    if (zone_)
+++    if (zone_) {
++         zone_->addTenuredAllocsSinceMinorGC(allocsThisZoneSinceMinorGC_);
+++    }
++ 
++     allocsThisZoneSinceMinorGC_ = 0;
++ 
++     zone_ = zone;
++     if (zone == nullptr) {
++         freeLists_ = nullptr;
++         return;
++     }
++@@ -425,18 +450,19 @@ JSContext::leaveRealm(JS::Realm* oldReal
++     // Only call leave() after we've setRealm()-ed away from the current realm.
++     JS::Realm* startingRealm = realm_;
++ 
++     // The current realm should be marked as entered-from-C++ at this point.
++     MOZ_ASSERT_IF(startingRealm, startingRealm->hasBeenEnteredIgnoringJit());
++ 
++     setRealm(oldRealm);
++ 
++-    if (startingRealm)
+++    if (startingRealm) {
++         startingRealm->leave();
+++    }
++ }
++ 
++ inline void
++ JSContext::leaveAtomsZone(JS::Realm* oldRealm)
++ {
++     setRealm(oldRealm);
++ }
++ 
++@@ -461,49 +487,55 @@ JSContext::setRealmForJitExceptionHandle
++     // so we don't call realm->leave() here.
++     MOZ_ASSERT(realm->compartment() == compartment());
++     realm_ = realm;
++ }
++ 
++ inline JSScript*
++ JSContext::currentScript(jsbytecode** ppc, AllowCrossRealm allowCrossRealm) const
++ {
++-    if (ppc)
+++    if (ppc) {
++         *ppc = nullptr;
+++    }
++ 
++     js::Activation* act = activation();
++-    if (!act)
+++    if (!act) {
++         return nullptr;
+++    }
++ 
++     MOZ_ASSERT(act->cx() == this);
++ 
++     // Cross-compartment implies cross-realm.
++-    if (allowCrossRealm == AllowCrossRealm::DontAllow && act->compartment() != compartment())
+++    if (allowCrossRealm == AllowCrossRealm::DontAllow && act->compartment() != compartment()) {
++         return nullptr;
+++    }
++ 
++     JSScript* script = nullptr;
++     jsbytecode* pc = nullptr;
++     if (act->isJit()) {
++-        if (act->hasWasmExitFP())
+++        if (act->hasWasmExitFP()) {
++             return nullptr;
+++        }
++         js::jit::GetPcScript(const_cast<JSContext*>(this), &script, &pc);
++     } else {
++         js::InterpreterFrame* fp = act->asInterpreter()->current();
++         MOZ_ASSERT(!fp->runningInJit());
++         script = fp->script();
++         pc = act->asInterpreter()->regs().pc;
++     }
++ 
++     MOZ_ASSERT(script->containsPC(pc));
++ 
++-    if (allowCrossRealm == AllowCrossRealm::DontAllow && script->realm() != realm())
+++    if (allowCrossRealm == AllowCrossRealm::DontAllow && script->realm() != realm()) {
++         return nullptr;
+++    }
++ 
++-    if (ppc)
+++    if (ppc) {
++         *ppc = pc;
+++    }
++     return script;
++ }
++ 
++ inline js::RuntimeCaches&
++ JSContext::caches()
++ {
++     return runtime()->caches();
++ }
+diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp
+--- a/js/src/vm/JSContext.cpp
++++ b/js/src/vm/JSContext.cpp
+@@ -73,22 +73,24 @@ using mozilla::PodArrayZero;
+ bool
+ js::AutoCycleDetector::init()
+ {
+     MOZ_ASSERT(cyclic);
+ 
+     AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
+ 
+     for (JSObject* obj2 : vector) {
+-        if (MOZ_UNLIKELY(obj == obj2))
++        if (MOZ_UNLIKELY(obj == obj2)) {
+             return true;
++        }
+     }
+ 
+-    if (!vector.append(obj))
++    if (!vector.append(obj)) {
+         return false;
++    }
+ 
+     cyclic = false;
+     return true;
+ }
+ 
+ js::AutoCycleDetector::~AutoCycleDetector()
+ {
+     if (MOZ_LIKELY(!cyclic)) {
+@@ -139,18 +141,19 @@ js::NewContext(uint32_t maxBytes, uint32
+     MOZ_RELEASE_ASSERT(!TlsContext.get());
+ 
+ #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+     js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
+                                           : js::THREAD_TYPE_WORKER);
+ #endif
+ 
+     JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
+-    if (!runtime)
++    if (!runtime) {
+         return nullptr;
++    }
+ 
+     JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
+     if (!cx) {
+         js_delete(runtime);
+         return nullptr;
+     }
+ 
+     if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
+@@ -168,18 +171,19 @@ js::NewContext(uint32_t maxBytes, uint32
+     }
+ 
+     return cx;
+ }
+ 
+ static void
+ FreeJobQueueHandling(JSContext* cx)
+ {
+-    if (!cx->jobQueue)
++    if (!cx->jobQueue) {
+         return;
++    }
+ 
+     cx->jobQueue->reset();
+     FreeOp* fop = cx->defaultFreeOp();
+     fop->delete_(cx->jobQueue.ref());
+     cx->getIncumbentGlobalCallback = nullptr;
+     cx->enqueuePromiseJobCallback = nullptr;
+     cx->enqueuePromiseJobCallbackData = nullptr;
+ }
+@@ -208,30 +212,32 @@ js::DestroyContext(JSContext* cx)
+     cx->runtime()->destroyRuntime();
+     js_delete(cx->runtime());
+     js_delete_poison(cx);
+ }
+ 
+ void
+ JS::RootingContext::checkNoGCRooters() {
+ #ifdef DEBUG
+-    for (auto const& stackRootPtr : stackRoots_)
++    for (auto const& stackRootPtr : stackRoots_) {
+         MOZ_ASSERT(stackRootPtr == nullptr);
++    }
+ #endif
+ }
+ 
+ bool
+ AutoResolving::alreadyStartedSlow() const
+ {
+     MOZ_ASSERT(link);
+     AutoResolving* cursor = link;
+     do {
+         MOZ_ASSERT(this != cursor);
+-        if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
++        if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind) {
+             return true;
++        }
+     } while (!!(cursor = cursor->link));
+     return false;
+ }
+ 
+ static void
+ ReportError(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback,
+             void* userRef)
+ {
+@@ -259,26 +265,28 @@ ReportError(JSContext* cx, JSErrorReport
+ /*
+  * The given JSErrorReport object have been zeroed and must not outlive
+  * cx->fp() (otherwise owned fields may become invalid).
+  */
+ static void
+ PopulateReportBlame(JSContext* cx, JSErrorReport* report)
+ {
+     JS::Realm* realm = cx->realm();
+-    if (!realm)
++    if (!realm) {
+         return;
++    }
+ 
+     /*
+      * Walk stack until we find a frame that is associated with a non-builtin
+      * rather than a builtin frame and which we're allowed to know about.
+      */
+     NonBuiltinFrameIter iter(cx, realm->principals());
+-    if (iter.done())
++    if (iter.done()) {
+         return;
++    }
+ 
+     report->filename = iter.filename();
+     uint32_t column;
+     report->lineno = iter.computeLine(&column);
+     report->column = FixupColumnForDisplay(column);
+     report->isMuted = iter.mutedErrors();
+ }
+ 
+@@ -453,34 +461,39 @@ enum class PrintErrorKind {
+ 
+ static void
+ PrintErrorLine(FILE* file, const char* prefix, JSErrorReport* report)
+ {
+     if (const char16_t* linebuf = report->linebuf()) {
+         size_t n = report->linebufLength();
+ 
+         fputs(":\n", file);
+-        if (prefix)
++        if (prefix) {
+             fputs(prefix, file);
++        }
+ 
+-        for (size_t i = 0; i < n; i++)
++        for (size_t i = 0; i < n; i++) {
+             fputc(static_cast<char>(linebuf[i]), file);
++        }
+ 
+         // linebuf usually ends with a newline. If not, add one here.
+-        if (n == 0 || linebuf[n-1] != '\n')
++        if (n == 0 || linebuf[n-1] != '\n') {
+             fputc('\n', file);
++        }
+ 
+-        if (prefix)
++        if (prefix) {
+             fputs(prefix, file);
++        }
+ 
+         n = report->tokenOffset();
+         for (size_t i = 0, j = 0; i < n; i++) {
+             if (linebuf[i] == '\t') {
+-                for (size_t k = (j + 8) & ~7; j < k; j++)
++                for (size_t k = (j + 8) & ~7; j < k; j++) {
+                     fputc('.', file);
++                }
+                 continue;
+             }
+             fputc('.', file);
+             j++;
+         }
+         fputc('^', file);
+     }
+ }
+@@ -491,18 +504,19 @@ PrintErrorLine(FILE* file, const char* p
+ }
+ 
+ template <typename T>
+ static bool
+ PrintSingleError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
+                  T* report, PrintErrorKind kind)
+ {
+     UniqueChars prefix;
+-    if (report->filename)
++    if (report->filename) {
+         prefix = JS_smprintf("%s:", report->filename);
++    }
+ 
+     if (report->lineno) {
+         prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
+                                         report->column);
+     }
+ 
+     if (kind != PrintErrorKind::Error) {
+         const char* kindPrefix = nullptr;
+@@ -524,56 +538,61 @@ PrintSingleError(JSContext* cx, FILE* fi
+     }
+ 
+     const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str();
+ 
+     /* embedded newlines -- argh! */
+     const char* ctmp;
+     while ((ctmp = strchr(message, '\n')) != 0) {
+         ctmp++;
+-        if (prefix)
++        if (prefix) {
+             fputs(prefix.get(), file);
++        }
+         mozilla::Unused << fwrite(message, 1, ctmp - message, file);
+         message = ctmp;
+     }
+ 
+     /* If there were no filename or lineno, the prefix might be empty */
+-    if (prefix)
++    if (prefix) {
+         fputs(prefix.get(), file);
++    }
+     fputs(message, file);
+ 
+     PrintErrorLine(file, prefix.get(), report);
+     fputc('\n', file);
+ 
+     fflush(file);
+     return true;
+ }
+ 
+ bool
+ js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
+                JSErrorReport* report, bool reportWarnings)
+ {
+     MOZ_ASSERT(report);
+ 
+     /* Conditionally ignore reported warnings. */
+-    if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
++    if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) {
+         return false;
++    }
+ 
+     PrintErrorKind kind = PrintErrorKind::Error;
+     if (JSREPORT_IS_WARNING(report->flags)) {
+-        if (JSREPORT_IS_STRICT(report->flags))
++        if (JSREPORT_IS_STRICT(report->flags)) {
+             kind = PrintErrorKind::StrictWarning;
+-        else
++        } else {
+             kind = PrintErrorKind::Warning;
++        }
+     }
+     PrintSingleError(cx, file, toStringResult, report, kind);
+ 
+     if (report->notes) {
+-        for (auto&& note : *report->notes)
++        for (auto&& note : *report->notes) {
+             PrintSingleError(cx, file, JS::ConstUTF8CharsZ(), note.get(), PrintErrorKind::Note);
++        }
+     }
+ 
+     return true;
+ }
+ 
+ class MOZ_RAII AutoMessageArgs
+ {
+     size_t totalLength_;
+@@ -591,18 +610,19 @@ class MOZ_RAII AutoMessageArgs
+     }
+ 
+     ~AutoMessageArgs()
+     {
+         /* free the arguments only if we allocated them */
+         if (allocatedElements_) {
+             uint16_t i = 0;
+             while (i < count_) {
+-                if (args_[i])
++                if (args_[i]) {
+                     js_free((void*)args_[i]);
++                }
+                 i++;
+             }
+         }
+     }
+ 
+     const char* args(size_t i) const {
+         MOZ_ASSERT(i < count_);
+         return args_[i];
+@@ -639,31 +659,33 @@ class MOZ_RAII AutoMessageArgs
+                 break;
+               }
+               case ArgumentsAreLatin1: {
+                 MOZ_ASSERT(!argsArg);
+                 const Latin1Char* latin1 = va_arg(ap, Latin1Char*);
+                 size_t len = strlen(reinterpret_cast<const char*>(latin1));
+                 mozilla::Range<const Latin1Char> range(latin1, len);
+                 char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
+-                if (!utf8)
++                if (!utf8) {
+                     return false;
++                }
+ 
+                 args_[i] = utf8;
+                 lengths_[i] = strlen(utf8);
+                 allocatedElements_ = true;
+                 break;
+               }
+               case ArgumentsAreUnicode: {
+                 const char16_t* uc = argsArg ? argsArg[i] : va_arg(ap, char16_t*);
+                 size_t len = js_strlen(uc);
+                 mozilla::Range<const char16_t> range(uc, len);
+                 char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
+-                if (!utf8)
++                if (!utf8) {
+                     return false;
++                }
+ 
+                 args_[i] = utf8;
+                 lengths_[i] = strlen(utf8);
+                 allocatedElements_ = true;
+                 break;
+               }
+             }
+             totalLength_ += lengths_[i];
+@@ -730,30 +752,32 @@ ExpandErrorArgumentsHelper(JSContext* cx
+                 char* out;
+ #ifdef DEBUG
+                 int expandedArgs = 0;
+ #endif
+                 size_t expandedLength;
+                 size_t len = strlen(efs->format);
+ 
+                 AutoMessageArgs args;
+-                if (!args.init(cx, messageArgs, argCount, argumentsType, ap))
++                if (!args.init(cx, messageArgs, argCount, argumentsType, ap)) {
+                     return false;
++                }
+ 
+                 expandedLength = len
+                                  - (3 * args.count()) /* exclude the {n} */
+                                  + args.totalLength();
+ 
+                 /*
+                 * Note - the above calculation assumes that each argument
+                 * is used once and only once in the expansion !!!
+                 */
+                 char* utf8 = out = cx->pod_malloc<char>(expandedLength + 1);
+-                if (!out)
++                if (!out) {
+                     return false;
++                }
+ 
+                 fmt = efs->format;
+                 while (*fmt) {
+                     if (*fmt == '{') {
+                         if (isdigit(fmt[1])) {
+                             int d = JS7_UNDEC(fmt[1]);
+                             MOZ_RELEASE_ASSERT(d < args.count());
+                             strncpy(out, args.args(d), args.lengths(d));
+@@ -774,28 +798,30 @@ ExpandErrorArgumentsHelper(JSContext* cx
+             }
+         } else {
+             /* Non-null messageArgs should have at least one non-null arg. */
+             MOZ_ASSERT(!messageArgs);
+             /*
+              * Zero arguments: the format string (if it exists) is the
+              * entire message.
+              */
+-            if (efs->format)
++            if (efs->format) {
+                 reportp->initBorrowedMessage(efs->format);
++            }
+         }
+     }
+     if (!reportp->message()) {
+         /* where's the right place for this ??? */
+         const char* defaultErrorMessage
+             = "No error message available for error number %d";
+         size_t nbytes = strlen(defaultErrorMessage) + 16;
+         char* message = cx->pod_malloc<char>(nbytes);
+-        if (!message)
++        if (!message) {
+             return false;
++        }
+         snprintf(message, nbytes, defaultErrorMessage, errorNumber);
+         reportp->initOwnedMessage(message);
+     }
+     return true;
+ }
+ 
+ bool
+ js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
+@@ -822,18 +848,19 @@ js::ExpandErrorArgumentsVA(JSContext* cx
+ bool
+ js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
+                         void* userRef, const unsigned errorNumber,
+                         ErrorArgumentsType argumentsType, va_list ap)
+ {
+     JSErrorReport report;
+     bool warning;
+ 
+-    if (checkReportFlags(cx, &flags))
++    if (checkReportFlags(cx, &flags)) {
+         return true;
++    }
+     warning = JSREPORT_IS_WARNING(flags);
+ 
+     report.flags = flags;
+     report.errorNumber = errorNumber;
+     PopulateReportBlame(cx, &report);
+ 
+     if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
+                                 nullptr, argumentsType, &report, ap)) {
+@@ -860,18 +887,19 @@ ExpandErrorArguments(JSContext* cx, JSEr
+     return expanded;
+ }
+ 
+ bool
+ js::ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback,
+                              void* userRef, const unsigned errorNumber,
+                              const char16_t** args)
+ {
+-    if (checkReportFlags(cx, &flags))
++    if (checkReportFlags(cx, &flags)) {
+         return true;
++    }
+     bool warning = JSREPORT_IS_WARNING(flags);
+ 
+     JSErrorReport report;
+     report.flags = flags;
+     report.errorNumber = errorNumber;
+     PopulateReportBlame(cx, &report);
+ 
+     if (!ExpandErrorArguments(cx, callback, userRef, errorNumber,
+@@ -1049,18 +1077,19 @@ JS_FRIEND_API(bool)
+ js::UseInternalJobQueues(JSContext* cx)
+ {
+     // Internal job queue handling must be set up very early. Self-hosting
+     // initialization is as good a marker for that as any.
+     MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
+                        "js::UseInternalJobQueues must be called early during runtime startup.");
+     MOZ_ASSERT(!cx->jobQueue);
+     auto* queue = js_new<PersistentRooted<JobQueue>>(cx, JobQueue(SystemAllocPolicy()));
+-    if (!queue)
++    if (!queue) {
+         return false;
++    }
+ 
+     cx->jobQueue = queue;
+ 
+     cx->runtime()->offThreadPromiseState.ref().initInternalDispatchQueue();
+     MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
+ 
+     JS::SetEnqueuePromiseJobCallback(cx, InternalEnqueuePromiseJobCallback);
+ 
+@@ -1086,18 +1115,19 @@ js::StopDrainingJobQueue(JSContext* cx)
+     cx->stopDrainingJobQueue = true;
+ }
+ 
+ JS_FRIEND_API(void)
+ js::RunJobs(JSContext* cx)
+ {
+     MOZ_ASSERT(cx->jobQueue);
+ 
+-    if (cx->drainingJobQueue || cx->stopDrainingJobQueue)
++    if (cx->drainingJobQueue || cx->stopDrainingJobQueue) {
+         return;
++    }
+ 
+     while (true) {
+         cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);
+ 
+         // It doesn't make sense for job queue draining to be reentrant. At the
+         // same time we don't want to assert against it, because that'd make
+         // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
+         // so we simply ignore nested calls of drainJobQueue.
+@@ -1152,45 +1182,48 @@ js::RunJobs(JSContext* cx)
+         if (cx->stopDrainingJobQueue) {
+             cx->stopDrainingJobQueue = false;
+             break;
+         }
+ 
+         cx->jobQueue->clear();
+ 
+         // It's possible a job added a new off-thread promise task.
+-        if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending())
++        if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending()) {
+             break;
++        }
+     }
+ }
+ 
+ JS::Error JSContext::reportedError;
+ JS::OOM JSContext::reportedOOM;
+ 
+ mozilla::GenericErrorResult<OOM&>
+ JSContext::alreadyReportedOOM()
+ {
+ #ifdef DEBUG
+     if (helperThread()) {
+         // Keep in sync with addPendingOutOfMemory.
+-        if (ParseTask* task = helperThread()->parseTask())
++        if (ParseTask* task = helperThread()->parseTask()) {
+             MOZ_ASSERT(task->outOfMemory);
++        }
+     } else {
+         MOZ_ASSERT(isThrowingOutOfMemory());
+     }
+ #endif
+     return mozilla::Err(reportedOOM);
+ }
+ 
+ mozilla::GenericErrorResult<JS::Error&>
+ JSContext::alreadyReportedError()
+ {
+ #ifdef DEBUG
+-    if (!helperThread())
++    if (!helperThread()) {
+         MOZ_ASSERT(isExceptionPending());
++    }
+ #endif
+     return mozilla::Err(reportedError);
+ }
+ 
+ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
+   : runtime_(runtime),
+     kind_(ContextKind::HelperThread),
+     helperThread_(nullptr),
+@@ -1460,18 +1493,19 @@ JSContext::inAtomsZone() const
+ #endif
+ 
+ void
+ JSContext::trace(JSTracer* trc)
+ {
+     cycleDetectorVector().trace(trc);
+     geckoProfiler().trace(trc);
+ 
+-    if (trc->isMarkingTracer() && realm_)
++    if (trc->isMarkingTracer() && realm_) {
+         realm_->mark();
++    }
+ }
+ 
+ void*
+ JSContext::stackLimitAddressForJitCode(JS::StackKind kind)
+ {
+ #ifdef JS_SIMULATOR
+     return addressOfSimulatorStackLimit();
+ #else
+@@ -1571,18 +1605,19 @@ AutoEnterOOMUnsafeRegion::crash(const ch
+ AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback
+ AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback = nullptr;
+ 
+ void
+ AutoEnterOOMUnsafeRegion::crash(size_t size, const char* reason)
+ {
+     {
+         JS::AutoSuppressGCAnalysis suppress;
+-        if (annotateOOMSizeCallback)
++        if (annotateOOMSizeCallback) {
+             annotateOOMSizeCallback(size);
++        }
+     }
+     crash(reason);
+ }
+ 
+ #ifdef DEBUG
+ AutoUnsafeCallWithABI::AutoUnsafeCallWithABI()
+   : cx_(TlsContext.get()),
+     nested_(cx_->hasAutoUnsafeCallWithABI),
+diff --git a/js/src/vm/JSContext.cpp.1488698-7.later b/js/src/vm/JSContext.cpp.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSContext.cpp.1488698-7.later
+@@ -0,0 +1,573 @@
++--- JSContext.cpp
+++++ JSContext.cpp
++@@ -102,34 +104,39 @@ js::AutoCycleDetector::~AutoCycleDetecto
++     }
++ }
++ 
++ bool
++ JSContext::init(ContextKind kind)
++ {
++     // Skip most of the initialization if this thread will not be running JS.
++     if (kind == ContextKind::MainThread) {
++-        if (!regexpStack.ref().init())
+++        if (!regexpStack.ref().init()) {
++             return false;
+++        }
++ 
++-        if (!fx.initInstance())
+++        if (!fx.initInstance()) {
++             return false;
+++        }
++ 
++ #ifdef JS_SIMULATOR
++         simulator_ = jit::Simulator::Create(this);
++-        if (!simulator_)
+++        if (!simulator_) {
++             return false;
+++        }
++ #endif
++ 
++-        if (!wasm::EnsureSignalHandlers(this))
+++        if (!wasm::EnsureSignalHandlers(this)) {
++             return false;
+++        }
++     } else {
++         atomsZoneFreeLists_ = js_new<gc::FreeLists>();
++-        if (!atomsZoneFreeLists_)
+++        if (!atomsZoneFreeLists_) {
++             return false;
+++        }
++     }
++ 
++     // Set the ContextKind last, so that ProtectedData checks will allow us to
++     // initialize this context before it becomes the runtime's active context.
++     kind_ = kind;
++ 
++     return true;
++ }
++@@ -299,25 +312,27 @@ js::ReportOutOfMemory(JSContext* cx)
++      * OOMs are non-deterministic, especially across different execution modes
++      * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
++      * so that the fuzzers can detect this.
++      */
++     fprintf(stderr, "ReportOutOfMemory called\n");
++ #endif
++     mozilla::recordreplay::InvalidateRecording("OutOfMemory exception thrown");
++ 
++-    if (cx->helperThread())
+++    if (cx->helperThread()) {
++         return cx->addPendingOutOfMemory();
+++    }
++ 
++     cx->runtime()->hadOutOfMemory = true;
++     gc::AutoSuppressGC suppressGC(cx);
++ 
++     /* Report the oom. */
++-    if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback)
+++    if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback) {
++         oomCallback(cx, cx->runtime()->oomCallbackData);
+++    }
++ 
++     RootedValue oomMessage(cx, StringValue(cx->names().outOfMemory));
++     cx->setPendingException(oomMessage);
++ }
++ 
++ mozilla::GenericErrorResult<OOM&>
++ js::ReportOutOfMemoryResult(JSContext* cx)
++ {
++@@ -354,56 +369,61 @@ JS_FRIEND_API(void)
++ js::ReportOverRecursed(JSContext* maybecx)
++ {
++     ReportOverRecursed(maybecx, JSMSG_OVER_RECURSED);
++ }
++ 
++ void
++ js::ReportAllocationOverflow(JSContext* cx)
++ {
++-    if (!cx)
+++    if (!cx) {
++         return;
+++    }
++ 
++-    if (cx->helperThread())
+++    if (cx->helperThread()) {
++         return;
+++    }
++ 
++     gc::AutoSuppressGC suppressGC(cx);
++     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
++ }
++ 
++ /*
++  * Given flags and the state of cx, decide whether we should report an
++  * error, a warning, or just continue execution normally.  Return
++  * true if we should continue normally, without reporting anything;
++  * otherwise, adjust *flags as appropriate and return false.
++  */
++ static bool
++ checkReportFlags(JSContext* cx, unsigned* flags)
++ {
++     if (JSREPORT_IS_STRICT(*flags)) {
++         /* Warning/error only when JSOPTION_STRICT is set. */
++-        if (!cx->realm()->behaviors().extraWarnings(cx))
+++        if (!cx->realm()->behaviors().extraWarnings(cx)) {
++             return true;
+++        }
++     }
++ 
++     /* Warnings become errors when JSOPTION_WERROR is set. */
++-    if (JSREPORT_IS_WARNING(*flags) && cx->options().werror())
+++    if (JSREPORT_IS_WARNING(*flags) && cx->options().werror()) {
++         *flags &= ~JSREPORT_WARNING;
+++    }
++ 
++     return false;
++ }
++ 
++ bool
++ js::ReportErrorVA(JSContext* cx, unsigned flags, const char* format,
++                   ErrorArgumentsType argumentsType, va_list ap)
++ {
++     JSErrorReport report;
++ 
++-    if (checkReportFlags(cx, &flags))
+++    if (checkReportFlags(cx, &flags)) {
++         return true;
+++    }
++ 
++     UniqueChars message(JS_vsmprintf(format, ap));
++     if (!message) {
++         ReportOutOfMemory(cx);
++         return false;
++     }
++ 
++     MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(message.get()));
++@@ -411,43 +431,46 @@ js::ReportErrorVA(JSContext* cx, unsigne
++     report.flags = flags;
++     report.errorNumber = JSMSG_USER_DEFINED_ERROR;
++     if (argumentsType == ArgumentsAreASCII || argumentsType == ArgumentsAreUTF8) {
++         report.initOwnedMessage(message.release());
++     } else {
++         MOZ_ASSERT(argumentsType == ArgumentsAreLatin1);
++         Latin1Chars latin1(message.get(), strlen(message.get()));
++         UTF8CharsZ utf8(JS::CharsToNewUTF8CharsZ(cx, latin1));
++-        if (!utf8)
+++        if (!utf8) {
++             return false;
+++        }
++         report.initOwnedMessage(reinterpret_cast<const char*>(utf8.get()));
++     }
++     PopulateReportBlame(cx, &report);
++ 
++     bool warning = JSREPORT_IS_WARNING(report.flags);
++ 
++     ReportError(cx, &report, nullptr, nullptr);
++     return warning;
++ }
++ 
++ /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
++ void
++ js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg)
++ {
++     RootedValue usage(cx);
++-    if (!JS_GetProperty(cx, callee, "usage", &usage))
+++    if (!JS_GetProperty(cx, callee, "usage", &usage)) {
++         return;
+++    }
++ 
++     if (!usage.isString()) {
++         JS_ReportErrorASCII(cx, "%s", msg);
++     } else {
++         RootedString usageStr(cx, usage.toString());
++         UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
++-        if (!str)
+++        if (!str) {
++             return;
+++        }
++         JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.get());
++     }
++ }
++ 
++ enum class PrintErrorKind {
++     Error,
++     Warning,
++     StrictWarning,
++@@ -703,18 +740,19 @@ bool
++ ExpandErrorArgumentsHelper(JSContext* cx, JSErrorCallback callback,
++                            void* userRef, const unsigned errorNumber,
++                            const char16_t** messageArgs,
++                            ErrorArgumentsType argumentsType,
++                            T* reportp, va_list ap)
++ {
++     const JSErrorFormatString* efs;
++ 
++-    if (!callback)
+++    if (!callback) {
++         callback = GetErrorMessage;
+++    }
++ 
++     {
++         gc::AutoSuppressGC suppressGC(cx);
++         efs = callback(userRef, errorNumber);
++     }
++ 
++     if (efs) {
++         SetExnType(reportp, efs->exnType);
++@@ -886,18 +930,19 @@ js::ReportErrorNumberUCArray(JSContext* 
++     ReportError(cx, &report, callback, userRef);
++ 
++     return warning;
++ }
++ 
++ void
++ js::ReportIsNotDefined(JSContext* cx, HandleId id)
++ {
++-    if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier))
+++    if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) {
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.get());
+++    }
++ }
++ 
++ void
++ js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name)
++ {
++     RootedId id(cx, NameToId(name));
++     ReportIsNotDefined(cx, id);
++ }
++@@ -909,18 +954,19 @@ js::ReportIsNullOrUndefinedForPropertyAc
++ 
++     if (!reportScanStack) {
++         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
++                                   v.isNull() ? "null" : "undefined", "object");
++         return;
++     }
++ 
++     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
++-    if (!bytes)
+++    if (!bytes) {
++         return;
+++    }
++ 
++     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES, bytes.get());
++     } else if (v.isUndefined()) {
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, bytes.get(),
++                                  js_undefined_str);
++     } else {
++         MOZ_ASSERT(v.isNull());
++@@ -931,29 +977,31 @@ js::ReportIsNullOrUndefinedForPropertyAc
++ 
++ void
++ js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, HandleId key,
++                                              bool reportScanStack)
++ {
++     MOZ_ASSERT(v.isNullOrUndefined());
++ 
++     UniqueChars keyBytes = IdToPrintableUTF8(cx, key, IdToPrintableBehavior::IdIsPropertyKey);
++-    if (!keyBytes)
+++    if (!keyBytes) {
++         return;
+++    }
++ 
++     if (!reportScanStack) {
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
++                                  keyBytes.get(),
++                                  v.isUndefined() ? js_undefined_str : js_null_str);
++         return;
++     }
++ 
++     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
++-    if (!bytes)
+++    if (!bytes) {
++         return;
+++    }
++ 
++     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
++                                  keyBytes.get(), bytes.get());
++     } else if (v.isUndefined()) {
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
++                                  bytes.get(), js_undefined_str, keyBytes.get());
++     } else {
++@@ -968,106 +1016,120 @@ js::ReportMissingArg(JSContext* cx, Hand
++ {
++     char argbuf[11];
++     UniqueChars bytes;
++ 
++     SprintfLiteral(argbuf, "%u", arg);
++     if (IsFunctionObject(v)) {
++         RootedAtom name(cx, v.toObject().as<JSFunction>().explicitName());
++         bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, name);
++-        if (!bytes)
+++        if (!bytes) {
++             return;
+++        }
++     }
++     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_MISSING_FUN_ARG,
++                              argbuf, bytes ? bytes.get() : "");
++ }
++ 
++ bool
++ js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
++                           int spindex, HandleValue v, HandleString fallback,
++                           const char* arg1, const char* arg2)
++ {
++     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
++     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
++     UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
++-    if (!bytes)
+++    if (!bytes) {
++         return false;
+++    }
++ 
++     return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, errorNumber,
++                                             bytes.get(), arg1, arg2);
++ }
++ 
++ JSObject*
++ js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report)
++ {
++     RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
++-    if (!notesArray)
+++    if (!notesArray) {
++         return nullptr;
+++    }
++ 
++-    if (!report->notes)
+++    if (!report->notes) {
++         return notesArray;
+++    }
++ 
++     for (auto&& note : *report->notes) {
++         RootedPlainObject noteObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
++-        if (!noteObj)
+++        if (!noteObj) {
++             return nullptr;
+++        }
++ 
++         RootedString messageStr(cx, note->newMessageString(cx));
++-        if (!messageStr)
+++        if (!messageStr) {
++             return nullptr;
+++        }
++         RootedValue messageVal(cx, StringValue(messageStr));
++-        if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal))
+++        if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal)) {
++             return nullptr;
+++        }
++ 
++         RootedValue filenameVal(cx);
++         if (note->filename) {
++             RootedString filenameStr(cx, NewStringCopyZ<CanGC>(cx, note->filename));
++-            if (!filenameStr)
+++            if (!filenameStr) {
++                 return nullptr;
+++            }
++             filenameVal = StringValue(filenameStr);
++         }
++-        if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal))
+++        if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal)) {
++             return nullptr;
+++        }
++ 
++         RootedValue linenoVal(cx, Int32Value(note->lineno));
++-        if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal))
+++        if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) {
++             return nullptr;
+++        }
++         RootedValue columnVal(cx, Int32Value(note->column));
++-        if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal))
+++        if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal)) {
++             return nullptr;
+++        }
++ 
++-        if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj)))
+++        if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) {
++             return nullptr;
+++        }
++     }
++ 
++     return notesArray;
++ }
++ 
++ const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
++ #define MSG_DEF(name, count, exception, format) \
++     { #name, format, count, exception } ,
++ #include "js.msg"
++ #undef MSG_DEF
++ };
++ 
++ JS_FRIEND_API(const JSErrorFormatString*)
++ js::GetErrorMessage(void* userRef, const unsigned errorNumber)
++ {
++-    if (errorNumber > 0 && errorNumber < JSErr_Limit)
+++    if (errorNumber > 0 && errorNumber < JSErr_Limit) {
++         return &js_ErrorFormatString[errorNumber];
+++    }
++     return nullptr;
++ }
++ 
++ void
++ JSContext::recoverFromOutOfMemory()
++ {
++     if (helperThread()) {
++         // Keep in sync with addPendingOutOfMemory.
++-        if (ParseTask* task = helperThread()->parseTask())
+++        if (ParseTask* task = helperThread()->parseTask()) {
++             task->outOfMemory = false;
+++        }
++     } else {
++         if (isExceptionPending()) {
++             MOZ_ASSERT(isThrowingOutOfMemory());
++             clearPendingException();
++         }
++     }
++ }
++ 
++@@ -1149,41 +1213,45 @@ js::RunJobs(JSContext* cx)
++         RootedValue rval(cx);
++ 
++         // Execute jobs in a loop until we've reached the end of the queue.
++         // Since executing a job can trigger enqueuing of additional jobs,
++         // it's crucial to re-check the queue length during each iteration.
++         for (size_t i = 0; i < cx->jobQueue->length(); i++) {
++             // A previous job might have set this flag. E.g., the js shell
++             // sets it if the `quit` builtin function is called.
++-            if (cx->stopDrainingJobQueue)
+++            if (cx->stopDrainingJobQueue) {
++                 break;
+++            }
++ 
++             job = cx->jobQueue->get()[i];
++ 
++             // It's possible that queue draining was interrupted prematurely,
++             // leaving the queue partly processed. In that case, slots for
++             // already-executed entries will contain nullptrs, which we should
++             // just skip.
++-            if (!job)
+++            if (!job) {
++                 continue;
+++            }
++ 
++             cx->jobQueue->get()[i] = nullptr;
++ 
++             // If the next job is the last job in the job queue, allow
++             // skipping the standard job queuing behavior.
++-            if (i == cx->jobQueue->length() - 1)
+++            if (i == cx->jobQueue->length() - 1) {
++                 JS::JobQueueIsEmpty(cx);
+++            }
++ 
++             AutoRealm ar(cx, &job->as<JSFunction>());
++             {
++                 if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
++                     // Nothing we can do about uncatchable exceptions.
++-                    if (!cx->isExceptionPending())
+++                    if (!cx->isExceptionPending()) {
++                         continue;
+++                    }
++                     RootedValue exn(cx);
++                     if (cx->getPendingException(&exn)) {
++                         /*
++                          * Clear the exception, because
++                          * PrepareScriptEnvironmentAndInvoke will assert that we don't
++                          * have one.
++                          */
++                         cx->clearPendingException();
++@@ -1315,42 +1386,45 @@ JSContext::JSContext(JSRuntime* runtime,
++     promiseRejectionTrackerCallbackData(nullptr)
++ {
++     MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
++                JS::RootingContext::get(this));
++ 
++     MOZ_ASSERT(!TlsContext.get());
++     TlsContext.set(this);
++ 
++-    for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
+++    for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++) {
++         nativeStackQuota[i] = 0;
+++    }
++ }
++ 
++ JSContext::~JSContext()
++ {
++     // Clear the ContextKind first, so that ProtectedData checks will allow us to
++     // destroy this context even if the runtime is already gone.
++     kind_ = ContextKind::HelperThread;
++ 
++     /* Free the stuff hanging off of cx. */
++     MOZ_ASSERT(!resolvingList);
++ 
++-    if (dtoaState)
+++    if (dtoaState) {
++         DestroyDtoaState(dtoaState);
+++    }
++ 
++     fx.destroyInstance();
++     freeOsrTempData();
++ 
++ #ifdef JS_SIMULATOR
++     js::jit::Simulator::Destroy(simulator_);
++ #endif
++ 
++ #ifdef JS_TRACE_LOGGING
++-    if (traceLogger)
+++    if (traceLogger) {
++         DestroyTraceLogger(traceLogger);
+++    }
++ #endif
++ 
++     js_delete(atomsZoneFreeLists_.ref());
++ 
++     MOZ_ASSERT(TlsContext.get() == this);
++     TlsContext.set(nullptr);
++ }
++ 
++@@ -1366,22 +1440,24 @@ JSContext::setRuntime(JSRuntime* rt)
++     runtime_ = rt;
++ }
++ 
++ bool
++ JSContext::getPendingException(MutableHandleValue rval)
++ {
++     MOZ_ASSERT(throwing);
++     rval.set(unwrappedException());
++-    if (zone()->isAtomsZone())
+++    if (zone()->isAtomsZone()) {
++         return true;
+++    }
++     bool wasOverRecursed = overRecursed_;
++     clearPendingException();
++-    if (!compartment()->wrap(this, rval))
+++    if (!compartment()->wrap(this, rval)) {
++         return false;
+++    }
++     this->check(rval);
++     setPendingException(rval);
++     overRecursed_ = wasOverRecursed;
++     return true;
++ }
++ 
++ bool
++ JSContext::isThrowingOutOfMemory()
++@@ -1560,18 +1637,19 @@ JSContext::updateMallocCounter(size_t nb
++ 
++     zone()->updateMallocCounter(nbytes);
++ }
++ 
++ #ifdef JS_CRASH_DIAGNOSTICS
++ void
++ ContextChecks::check(AbstractFramePtr frame, int argIndex)
++ {
++-    if (frame)
+++    if (frame) {
++         check(frame.realm(), argIndex);
+++    }
++ }
++ #endif
++ 
++ void
++ AutoEnterOOMUnsafeRegion::crash(const char* reason)
++ {
++     char msgbuf[1024];
++     js::NoteIntentionalCrash();
+diff --git a/js/src/vm/JSContext.h b/js/src/vm/JSContext.h
+--- a/js/src/vm/JSContext.h
++++ b/js/src/vm/JSContext.h
+@@ -608,18 +608,19 @@ struct JSContext : public JS::RootingCon
+     js::ThreadData<js::UniquePtr<js::jit::PcScriptCache>> ionPcScriptCache;
+ 
+   private:
+     /* Exception state -- the exception member is a GC root by definition. */
+     js::ThreadData<bool> throwing;            /* is there a pending exception? */
+     js::ThreadData<JS::PersistentRooted<JS::Value>> unwrappedException_; /* most-recently-thrown exception */
+ 
+     JS::Value& unwrappedException() {
+-        if (!unwrappedException_.ref().initialized())
++        if (!unwrappedException_.ref().initialized()) {
+             unwrappedException_.ref().init(this);
++        }
+         return unwrappedException_.ref().get();
+     }
+ 
+     // True if the exception currently being thrown is by result of
+     // ReportOverRecursed. See Debugger::slowPathOnExceptionUnwind.
+     js::ThreadData<bool> overRecursed_;
+ 
+     // True if propagating a forced return from an interrupt handler during
+@@ -686,18 +687,19 @@ struct JSContext : public JS::RootingCon
+      *
+      * New activations will reset this to nullptr on construction after getting
+      * the current value, and will restore the previous value on destruction.
+      */
+     js::ThreadData<JS::PersistentRooted<js::SavedFrame*>> asyncStackForNewActivations_;
+   public:
+ 
+     js::SavedFrame*& asyncStackForNewActivations() {
+-        if (!asyncStackForNewActivations_.ref().initialized())
++        if (!asyncStackForNewActivations_.ref().initialized()) {
+             asyncStackForNewActivations_.ref().init(this);
++        }
+         return asyncStackForNewActivations_.ref().get();
+     }
+ 
+     /*
+      * Value of asyncCause to be attached to asyncStackForNewActivations.
+      */
+     js::ThreadData<const char*> asyncCauseForNewActivations;
+ 
+diff --git a/js/src/vm/JSContext.h.1488698-7.later b/js/src/vm/JSContext.h.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSContext.h.1488698-7.later
+@@ -0,0 +1,33 @@
++--- JSContext.h
+++++ JSContext.h
++@@ -178,26 +178,28 @@ struct JSContext : public JS::RootingCon
++ 
++     /*
++      * This variation of calloc will call the large-allocation-failure callback
++      * on OOM and retry the allocation.
++      */
++     template <typename T>
++     T* pod_callocCanGC(size_t numElems, arena_id_t arena = js::MallocArena) {
++         T* p = maybe_pod_calloc<T>(numElems, arena);
++-        if (MOZ_LIKELY(!!p))
+++        if (MOZ_LIKELY(!!p)) {
++             return p;
+++        }
++         size_t bytes;
++         if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
++             reportAllocationOverflow();
++             return nullptr;
++         }
++         p = static_cast<T*>(runtime()->onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
++-        if (!p)
+++        if (!p) {
++             return nullptr;
+++        }
++         updateMallocCounter(bytes);
++         return p;
++     }
++ 
++     void updateMallocCounter(size_t nbytes);
++ 
++     void reportAllocationOverflow() {
++         js::ReportAllocationOverflow(this);
+diff --git a/js/src/vm/JSFunction-inl.h b/js/src/vm/JSFunction-inl.h
+--- a/js/src/vm/JSFunction-inl.h
++++ b/js/src/vm/JSFunction-inl.h
+@@ -25,27 +25,30 @@ GetFunctionNameBytes(JSContext* cx, JSFu
+     if (JSAtom* name = fun->explicitName())
+         return bytes->encodeLatin1(cx, name);
+     return js_anonymous_str;
+ }
+ 
+ inline bool
+ CanReuseFunctionForClone(JSContext* cx, HandleFunction fun)
+ {
+-    if (!fun->isSingleton())
++    if (!fun->isSingleton()) {
+         return false;
++    }
+     if (fun->isInterpretedLazy()) {
+         LazyScript* lazy = fun->lazyScript();
+-        if (lazy->hasBeenCloned())
++        if (lazy->hasBeenCloned()) {
+             return false;
++        }
+         lazy->setHasBeenCloned();
+     } else {
+         JSScript* script = fun->nonLazyScript();
+-        if (script->hasBeenCloned())
++        if (script->hasBeenCloned()) {
+             return false;
++        }
+         script->setHasBeenCloned();
+     }
+     return true;
+ }
+ 
+ inline JSFunction*
+ CloneFunctionObjectIfNotSingleton(JSContext* cx, HandleFunction fun, HandleObject parent,
+                                   HandleObject proto = nullptr,
+@@ -104,18 +107,19 @@ JSFunction::create(JSContext* cx, js::gc
+     const js::Class* clasp = group->clasp();
+     MOZ_ASSERT(clasp->isJSFunction());
+ 
+     static constexpr size_t NumDynamicSlots = 0;
+     MOZ_ASSERT(dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp) ==
+                NumDynamicSlots);
+ 
+     JSObject* obj = js::Allocate<JSObject>(cx, kind, NumDynamicSlots, heap, clasp);
+-    if (!obj)
++    if (!obj) {
+         return cx->alreadyReportedOOM();
++    }
+ 
+     NativeObject* nobj = static_cast<NativeObject*>(obj);
+     nobj->initGroup(group);
+     nobj->initShape(shape);
+ 
+     nobj->initSlots(nullptr);
+     nobj->setEmptyElements();
+ 
+@@ -129,18 +133,19 @@ JSFunction::create(JSContext* cx, js::gc
+     // value to which we could sensibly initialize this.
+     MOZ_MAKE_MEM_UNDEFINED(&fun->u, sizeof(u));
+ 
+     // Safe: we're initializing for the very first time.
+     fun->atom_.unsafeSet(nullptr);
+ 
+     if (kind == js::gc::AllocKind::FUNCTION_EXTENDED) {
+         fun->setFlags(JSFunction::EXTENDED);
+-        for (js::GCPtrValue& extendedSlot : fun->toExtended()->extendedSlots)
++        for (js::GCPtrValue& extendedSlot : fun->toExtended()->extendedSlots) {
+             extendedSlot.unsafeSet(JS::DoubleValue(+0.0));
++        }
+     } else {
+         fun->setFlags(0);
+     }
+ 
+     MOZ_ASSERT(!clasp->shouldDelayMetadataBuilder(),
+                "Function has no extra data hanging off it, that wouldn't be "
+                "allocated at this point, that would require delaying the "
+                "building of metadata for it");
+diff --git a/js/src/vm/JSFunction-inl.h.1488698-7.later b/js/src/vm/JSFunction-inl.h.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSFunction-inl.h.1488698-7.later
+@@ -0,0 +1,46 @@
++--- JSFunction-inl.h
+++++ JSFunction-inl.h
++@@ -61,37 +64,40 @@ CloneFunctionObjectIfNotSingleton(JSCont
++      *
++      * For functions inner to run once lambda, it may be possible that
++      * the lambda runs multiple times and we repeatedly clone it. In these
++      * cases, fall through to CloneFunctionObject, which will deep clone
++      * the function's script.
++      */
++     if (CanReuseFunctionForClone(cx, fun)) {
++         ObjectOpResult succeeded;
++-        if (proto && !SetPrototype(cx, fun, proto, succeeded))
+++        if (proto && !SetPrototype(cx, fun, proto, succeeded)) {
++             return nullptr;
+++        }
++         MOZ_ASSERT(!proto || succeeded);
++         fun->setEnvironment(parent);
++         return fun;
++     }
++ 
++     // These intermediate variables are needed to avoid link errors on some
++     // platforms.  Sigh.
++     gc::AllocKind finalizeKind = gc::AllocKind::FUNCTION;
++     gc::AllocKind extendedFinalizeKind = gc::AllocKind::FUNCTION_EXTENDED;
++     gc::AllocKind kind = fun->isExtended()
++                          ? extendedFinalizeKind
++                          : finalizeKind;
++ 
++-    if (CanReuseScriptForClone(cx->realm(), fun, parent))
+++    if (CanReuseScriptForClone(cx->realm(), fun, parent)) {
++         return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto);
+++    }
++ 
++     RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
++-    if (!script)
+++    if (!script) {
++         return nullptr;
+++    }
++     RootedScope enclosingScope(cx, script->enclosingScope());
++     return CloneFunctionAndScript(cx, fun, parent, enclosingScope, kind, proto);
++ }
++ 
++ } /* namespace js */
++ 
++ /* static */ inline JS::Result<JSFunction*, JS::OOM&>
++ JSFunction::create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
+diff --git a/js/src/vm/JSFunction.cpp b/js/src/vm/JSFunction.cpp
+--- a/js/src/vm/JSFunction.cpp
++++ b/js/src/vm/JSFunction.cpp
+@@ -73,30 +73,33 @@ fun_enumerate(JSContext* cx, HandleObjec
+ {
+     MOZ_ASSERT(obj->is<JSFunction>());
+ 
+     RootedId id(cx);
+     bool found;
+ 
+     if (!obj->isBoundFunction() && !obj->as<JSFunction>().isArrow()) {
+         id = NameToId(cx->names().prototype);
+-        if (!HasOwnProperty(cx, obj, id, &found))
++        if (!HasOwnProperty(cx, obj, id, &found)) {
+             return false;
++        }
+     }
+ 
+     if (!obj->as<JSFunction>().hasResolvedLength()) {
+         id = NameToId(cx->names().length);
+-        if (!HasOwnProperty(cx, obj, id, &found))
++        if (!HasOwnProperty(cx, obj, id, &found)) {
+             return false;
++        }
+     }
+ 
+     if (!obj->as<JSFunction>().hasResolvedName()) {
+         id = NameToId(cx->names().name);
+-        if (!HasOwnProperty(cx, obj, id, &found))
++        if (!HasOwnProperty(cx, obj, id, &found)) {
+             return false;
++        }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ IsFunction(HandleValue v)
+ {
+@@ -104,48 +107,53 @@ IsFunction(HandleValue v)
+ }
+ 
+ static bool
+ AdvanceToActiveCallLinear(JSContext* cx, NonBuiltinScriptFrameIter& iter, HandleFunction fun)
+ {
+     MOZ_ASSERT(!fun->isBuiltin());
+ 
+     for (; !iter.done(); ++iter) {
+-        if (!iter.isFunctionFrame())
++        if (!iter.isFunctionFrame()) {
+             continue;
+-        if (iter.matchCallee(cx, fun))
++        }
++        if (iter.matchCallee(cx, fun)) {
+             return true;
++        }
+     }
+     return false;
+ }
+ 
+ void
+ js::ThrowTypeErrorBehavior(JSContext* cx)
+ {
+     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_THROW_TYPE_ERROR);
+ }
+ 
+ static bool
+ IsSloppyNormalFunction(JSFunction* fun)
+ {
+     // FunctionDeclaration or FunctionExpression in sloppy mode.
+     if (fun->kind() == JSFunction::NormalFunction) {
+-        if (fun->isBuiltin() || fun->isBoundFunction())
++        if (fun->isBuiltin() || fun->isBoundFunction()) {
+             return false;
+-
+-        if (fun->isGenerator() || fun->isAsync())
++        }
++
++        if (fun->isGenerator() || fun->isAsync()) {
+             return false;
++        }
+ 
+         MOZ_ASSERT(fun->isInterpreted());
+         return !fun->strict();
+     }
+ 
+     // Or asm.js function in sloppy mode.
+-    if (fun->kind() == JSFunction::AsmJS)
++    if (fun->kind() == JSFunction::AsmJS) {
+         return !IsAsmJSStrictModeModuleOrFunction(fun);
++    }
+ 
+     return false;
+ }
+ 
+ // Beware: this function can be invoked on *any* function! That includes
+ // natives, strict mode functions, bound functions, arrow functions,
+ // self-hosted functions and constructors, asm.js functions, functions with
+ // destructuring arguments and/or a rest argument, and probably a few more I
+@@ -174,29 +182,31 @@ ArgumentsRestrictions(JSContext* cx, Han
+ }
+ 
+ bool
+ ArgumentsGetterImpl(JSContext* cx, const CallArgs& args)
+ {
+     MOZ_ASSERT(IsFunction(args.thisv()));
+ 
+     RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
+-    if (!ArgumentsRestrictions(cx, fun))
++    if (!ArgumentsRestrictions(cx, fun)) {
+         return false;
++    }
+ 
+     // Return null if this function wasn't found on the stack.
+     NonBuiltinScriptFrameIter iter(cx);
+     if (!AdvanceToActiveCallLinear(cx, iter, fun)) {
+         args.rval().setNull();
+         return true;
+     }
+ 
+     Rooted<ArgumentsObject*> argsobj(cx, ArgumentsObject::createUnexpected(cx, iter));
+-    if (!argsobj)
++    if (!argsobj) {
+         return false;
++    }
+ 
+ #ifndef JS_CODEGEN_NONE
+     // Disabling compiling of this script in IonMonkey.  IonMonkey doesn't
+     // guarantee |f.arguments| can be fully recovered, so we try to mitigate
+     // observing this behavior by detecting its use early.
+     JSScript* script = iter.script();
+     jit::ForbidCompilation(cx, script);
+ #endif
+@@ -213,18 +223,19 @@ ArgumentsGetter(JSContext* cx, unsigned 
+ }
+ 
+ bool
+ ArgumentsSetterImpl(JSContext* cx, const CallArgs& args)
+ {
+     MOZ_ASSERT(IsFunction(args.thisv()));
+ 
+     RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
+-    if (!ArgumentsRestrictions(cx, fun))
++    if (!ArgumentsRestrictions(cx, fun)) {
+         return false;
++    }
+ 
+     // If the function passes the gauntlet, return |undefined|.
+     args.rval().setUndefined();
+     return true;
+ }
+ 
+ static bool
+ ArgumentsSetter(JSContext* cx, unsigned argc, Value* vp)
+@@ -266,56 +277,61 @@ CallerGetterImpl(JSContext* cx, const Ca
+ {
+     MOZ_ASSERT(IsFunction(args.thisv()));
+ 
+     // Beware!  This function can be invoked on *any* function!  It can't
+     // assume it'll never be invoked on natives, strict mode functions, bound
+     // functions, or anything else that ordinarily has immutable .caller
+     // defined with [[ThrowTypeError]].
+     RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
+-    if (!CallerRestrictions(cx, fun))
++    if (!CallerRestrictions(cx, fun)) {
+         return false;
++    }
+ 
+     // Also return null if this function wasn't found on the stack.
+     NonBuiltinScriptFrameIter iter(cx);
+     if (!AdvanceToActiveCallLinear(cx, iter, fun)) {
+         args.rval().setNull();
+         return true;
+     }
+ 
+     ++iter;
+-    while (!iter.done() && iter.isEvalFrame())
++    while (!iter.done() && iter.isEvalFrame()) {
+         ++iter;
++    }
+ 
+     if (iter.done() || !iter.isFunctionFrame()) {
+         args.rval().setNull();
+         return true;
+     }
+ 
+     RootedObject caller(cx, iter.callee(cx));
+-    if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync())
++    if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync()) {
+         caller = GetWrappedAsyncFunction(&caller->as<JSFunction>());
+-    if (!cx->compartment()->wrap(cx, &caller))
++    }
++    if (!cx->compartment()->wrap(cx, &caller)) {
+         return false;
++    }
+ 
+     // Censor the caller if we don't have full access to it.  If we do, but the
+     // caller is a function with strict mode code, throw a TypeError per ES5.
+     // If we pass these checks, we can return the computed caller.
+     {
+         JSObject* callerObj = CheckedUnwrap(caller);
+         if (!callerObj) {
+             args.rval().setNull();
+             return true;
+         }
+ 
+         JSFunction* callerFun = &callerObj->as<JSFunction>();
+-        if (IsWrappedAsyncFunction(callerFun))
++        if (IsWrappedAsyncFunction(callerFun)) {
+             callerFun = GetUnwrappedAsyncFunction(callerFun);
+-        else if (IsWrappedAsyncGenerator(callerFun))
++        } else if (IsWrappedAsyncGenerator(callerFun)) {
+             callerFun = GetUnwrappedAsyncGenerator(callerFun);
++        }
+         MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
+ 
+         if (callerFun->strict()) {
+             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CALLER_IS_STRICT);
+             return false;
+         }
+     }
+ 
+@@ -335,53 +351,58 @@ CallerSetterImpl(JSContext* cx, const Ca
+ {
+     MOZ_ASSERT(IsFunction(args.thisv()));
+ 
+     // Beware!  This function can be invoked on *any* function!  It can't
+     // assume it'll never be invoked on natives, strict mode functions, bound
+     // functions, or anything else that ordinarily has immutable .caller
+     // defined with [[ThrowTypeError]].
+     RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
+-    if (!CallerRestrictions(cx, fun))
++    if (!CallerRestrictions(cx, fun)) {
+         return false;
++    }
+ 
+     // Return |undefined| unless an error must be thrown.
+     args.rval().setUndefined();
+ 
+     // We can almost just return |undefined| here -- but if the caller function
+     // was strict mode code, we still have to throw a TypeError.  This requires
+     // computing the caller, checking that no security boundaries are crossed,
+     // and throwing a TypeError if the resulting caller is strict.
+ 
+     NonBuiltinScriptFrameIter iter(cx);
+-    if (!AdvanceToActiveCallLinear(cx, iter, fun))
++    if (!AdvanceToActiveCallLinear(cx, iter, fun)) {
+         return true;
++    }
+ 
+     ++iter;
+-    while (!iter.done() && iter.isEvalFrame())
++    while (!iter.done() && iter.isEvalFrame()) {
+         ++iter;
+-
+-    if (iter.done() || !iter.isFunctionFrame())
++    }
++
++    if (iter.done() || !iter.isFunctionFrame()) {
+         return true;
++    }
+ 
+     RootedObject caller(cx, iter.callee(cx));
+     // |caller| is only used for security access-checking and for its
+     // strictness.  An unwrapped async function has its wrapped async
+     // function's security access and strictness, so don't bother calling
+     // |GetUnwrappedAsyncFunction|.
+     if (!cx->compartment()->wrap(cx, &caller)) {
+         cx->clearPendingException();
+         return true;
+     }
+ 
+     // If we don't have full access to the caller, or the caller is not strict,
+     // return undefined.  Otherwise throw a TypeError.
+     JSObject* callerObj = CheckedUnwrap(caller);
+-    if (!callerObj)
++    if (!callerObj) {
+         return true;
++    }
+ 
+     JSFunction* callerFun = &callerObj->as<JSFunction>();
+     MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
+ 
+     if (callerFun->strict()) {
+         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CALLER_IS_STRICT);
+         return false;
+     }
+@@ -418,38 +439,42 @@ ResolveInterpretedFunctionPrototype(JSCo
+ 
+     // Make the prototype object an instance of Object with the same parent as
+     // the function object itself, unless the function is an ES6 generator.  In
+     // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is
+     // the GeneratorObjectPrototype singleton.
+     bool isGenerator = fun->isGenerator();
+     Rooted<GlobalObject*> global(cx, &fun->global());
+     RootedObject objProto(cx);
+-    if (isAsyncGenerator)
++    if (isAsyncGenerator) {
+         objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global);
+-    else if (isGenerator)
++    } else if (isGenerator) {
+         objProto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, global);
+-    else
++    } else {
+         objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
+-    if (!objProto)
++    }
++    if (!objProto) {
+         return false;
++    }
+ 
+     RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, objProto,
+                                                                      SingletonObject));
+-    if (!proto)
++    if (!proto) {
+         return false;
++    }
+ 
+     // Per ES5 13.2 the prototype's .constructor property is configurable,
+     // non-enumerable, and writable.  However, per the 15 July 2013 ES6 draft,
+     // section 15.19.3, the .prototype of a generator function does not link
+     // back with a .constructor.
+     if (!isGenerator && !isAsyncGenerator) {
+         RootedValue objVal(cx, ObjectValue(*fun));
+-        if (!DefineDataProperty(cx, proto, cx->names().constructor, objVal, 0))
++        if (!DefineDataProperty(cx, proto, cx->names().constructor, objVal, 0)) {
+             return false;
++        }
+     }
+ 
+     // Per ES5 15.3.5.2 a user-defined function's .prototype property is
+     // initially non-configurable, non-enumerable, and writable.
+     RootedValue protoVal(cx, ObjectValue(*proto));
+     return DefineDataProperty(cx, fun, id, protoVal, JSPROP_PERMANENT | JSPROP_RESOLVING);
+ }
+ 
+@@ -469,46 +494,51 @@ JSFunction::needsPrototypeProperty()
+      * Generators are not constructors, but they have a .prototype property anyway,
+      * according to errata to ES6. See bug 1191486.
+      *
+      * Thus all of the following don't get a .prototype property:
+      * - Methods (that are not class-constructors or generators)
+      * - Arrow functions
+      * - Function.prototype
+      */
+-    if (isBuiltin())
++    if (isBuiltin()) {
+         return IsWrappedAsyncGenerator(this);
++    }
+ 
+     return isConstructor() || isGenerator() || isAsync();
+ }
+ 
+ static bool
+ fun_mayResolve(const JSAtomState& names, jsid id, JSObject*)
+ {
+-    if (!JSID_IS_ATOM(id))
++    if (!JSID_IS_ATOM(id)) {
+         return false;
++    }
+ 
+     JSAtom* atom = JSID_TO_ATOM(id);
+     return atom == names.prototype || atom == names.length || atom == names.name;
+ }
+ 
+ static bool
+ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+ {
+-    if (!JSID_IS_ATOM(id))
++    if (!JSID_IS_ATOM(id)) {
+         return true;
++    }
+ 
+     RootedFunction fun(cx, &obj->as<JSFunction>());
+ 
+     if (JSID_IS_ATOM(id, cx->names().prototype)) {
+-        if (!fun->needsPrototypeProperty())
++        if (!fun->needsPrototypeProperty()) {
+             return true;
+-
+-        if (!ResolveInterpretedFunctionPrototype(cx, fun, id))
++        }
++
++        if (!ResolveInterpretedFunctionPrototype(cx, fun, id)) {
+             return false;
++        }
+ 
+         *resolvedp = true;
+         return true;
+     }
+ 
+     bool isLength = JSID_IS_ATOM(id, cx->names().length);
+     if (isLength || JSID_IS_ATOM(id, cx->names().name)) {
+         MOZ_ASSERT(!IsInternalFunctionObject(*obj));
+@@ -525,43 +555,50 @@ fun_resolve(JSContext* cx, HandleObject 
+         // Afterwards, asking for f.length or f.name again will cause this
+         // resolve hook to run again. Defining the property again the second
+         // time through would be a bug.
+         //     assertEq(f.length, 0);  // gets Function.prototype.length!
+         //     assertEq(f.name, "");  // gets Function.prototype.name!
+         // We use the RESOLVED_LENGTH and RESOLVED_NAME flags as a hack to prevent this
+         // bug.
+         if (isLength) {
+-            if (fun->hasResolvedLength())
++            if (fun->hasResolvedLength()) {
+                 return true;
+-
+-            if (!JSFunction::getUnresolvedLength(cx, fun, &v))
++            }
++
++            if (!JSFunction::getUnresolvedLength(cx, fun, &v)) {
+                 return false;
++            }
+         } else {
+-            if (fun->hasResolvedName())
++            if (fun->hasResolvedName()) {
+                 return true;
++            }
+ 
+             RootedString name(cx);
+-            if (!JSFunction::getUnresolvedName(cx, fun, &name))
++            if (!JSFunction::getUnresolvedName(cx, fun, &name)) {
+                 return false;
++            }
+ 
+             // Don't define an own .name property for unnamed functions.
+-            if (!name)
++            if (!name) {
+                 return true;
++            }
+ 
+             v.setString(name);
+         }
+ 
+-        if (!NativeDefineDataProperty(cx, fun, id, v, JSPROP_READONLY | JSPROP_RESOLVING))
++        if (!NativeDefineDataProperty(cx, fun, id, v, JSPROP_READONLY | JSPROP_RESOLVING)) {
+             return false;
+-
+-        if (isLength)
++        }
++
++        if (isLength) {
+             fun->setResolvedLength();
+-        else
++        } else {
+             fun->setResolvedName();
++        }
+ 
+         *resolvedp = true;
+         return true;
+     }
+ 
+     return true;
+ }
+ 
+@@ -584,36 +621,40 @@ js::XDRInterpretedFunction(XDRState<mode
+ 
+     JSContext* cx = xdr->cx();
+     RootedFunction fun(cx);
+     RootedScript script(cx);
+     Rooted<LazyScript*> lazy(cx);
+ 
+     if (mode == XDR_ENCODE) {
+         fun = objp;
+-        if (!fun->isInterpreted())
++        if (!fun->isInterpreted()) {
+             return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
+-
+-        if (fun->explicitName() || fun->hasInferredName() || fun->hasGuessedAtom())
++        }
++
++        if (fun->explicitName() || fun->hasInferredName() || fun->hasGuessedAtom()) {
+             firstword |= HasAtom;
+-
+-        if (fun->isGenerator() || fun->isAsync())
++        }
++
++        if (fun->isGenerator() || fun->isAsync()) {
+             firstword |= HasGeneratorProto;
++        }
+ 
+         if (fun->isInterpretedLazy()) {
+             // Encode a lazy script.
+             firstword |= IsLazy;
+             lazy = fun->lazyScript();
+         } else {
+             // Encode the script.
+             script = fun->nonLazyScript();
+         }
+ 
+-        if (fun->isSingleton())
++        if (fun->isSingleton()) {
+             firstword |= HasSingletonType;
++        }
+ 
+         atom = fun->displayAtom();
+         flagsword = (fun->nargs() << 16) |
+                     (fun->flags() & ~JSFunction::NO_XDR_FLAGS);
+ 
+         // The environment of any function which is not reused will always be
+         // null, it is later defined when a function is cloned or reused to
+         // mirror the scope chain.
+@@ -624,58 +665,64 @@ js::XDRInterpretedFunction(XDRState<mode
+ 
+     // Everything added below can substituted by the non-lazy-script version of
+     // this function later.
+     MOZ_TRY(xdr->codeAlign(sizeof(js::XDRAlignment)));
+     js::AutoXDRTree funTree(xdr, xdr->getTreeKey(fun));
+ 
+     MOZ_TRY(xdr->codeUint32(&firstword));
+ 
+-    if (firstword & HasAtom)
++    if (firstword & HasAtom) {
+         MOZ_TRY(XDRAtom(xdr, &atom));
++    }
+     MOZ_TRY(xdr->codeUint32(&flagsword));
+ 
+     if (mode == XDR_DECODE) {
+         RootedObject proto(cx);
+         if (firstword & HasGeneratorProto) {
+             proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
+-            if (!proto)
++            if (!proto) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+         }
+ 
+         gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
+-        if (uint16_t(flagsword) & JSFunction::EXTENDED)
++        if (uint16_t(flagsword) & JSFunction::EXTENDED) {
+             allocKind = gc::AllocKind::FUNCTION_EXTENDED;
++        }
+         fun = NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED,
+                                    /* enclosingDynamicScope = */ nullptr, nullptr, proto,
+                                    allocKind, TenuredObject);
+-        if (!fun)
++        if (!fun) {
+             return xdr->fail(JS::TranscodeResult_Throw);
++        }
+         script = nullptr;
+     }
+ 
+-    if (firstword & IsLazy)
++    if (firstword & IsLazy) {
+         MOZ_TRY(XDRLazyScript(xdr, enclosingScope, sourceObject, fun, &lazy));
+-    else
++    } else {
+         MOZ_TRY(XDRScript(xdr, enclosingScope, sourceObject, fun, &script));
++    }
+ 
+     if (mode == XDR_DECODE) {
+         fun->setArgCount(flagsword >> 16);
+         fun->setFlags(uint16_t(flagsword));
+         fun->initAtom(atom);
+         if (firstword & IsLazy) {
+             MOZ_ASSERT(fun->lazyScript() == lazy);
+         } else {
+             MOZ_ASSERT(fun->nonLazyScript() == script);
+             MOZ_ASSERT(fun->nargs() == script->numArgs());
+         }
+ 
+         bool singleton = firstword & HasSingletonType;
+-        if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
++        if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton)) {
+             return xdr->fail(JS::TranscodeResult_Throw);
++        }
+         objp.set(fun);
+     }
+ 
+     // Verify marker at end of function to detect buffer trunction.
+     MOZ_TRY(xdr->codeMarker(0x9E35CA1F));
+ 
+     // Required by AutoXDRTree to copy & paste snipet of sub-trees while keeping
+     // the alignment.
+@@ -712,18 +759,19 @@ js::fun_symbolHasInstance(JSContext* cx,
+         args.rval().setBoolean(false);
+         return true;
+     }
+ 
+     RootedObject obj(cx, &func.toObject());
+ 
+     /* Step 2. */
+     bool result;
+-    if (!OrdinaryHasInstance(cx, obj, args[0], &result))
++    if (!OrdinaryHasInstance(cx, obj, args[0], &result)) {
+         return false;
++    }
+ 
+     args.rval().setBoolean(result);
+     return true;
+ }
+ 
+ /*
+  * ES6 (4-25-16) 7.3.19 OrdinaryHasInstance
+  */
+@@ -788,23 +836,25 @@ JSFunction::trace(JSTracer* trc)
+     }
+ 
+     TraceNullableEdge(trc, &atom_, "atom");
+ 
+     if (isInterpreted()) {
+         // Functions can be be marked as interpreted despite having no script
+         // yet at some points when parsing, and can be lazy with no lazy script
+         // for self-hosted code.
+-        if (hasScript() && !hasUncompletedScript())
++        if (hasScript() && !hasUncompletedScript()) {
+             TraceManuallyBarrieredEdge(trc, &u.scripted.s.script_, "script");
+-        else if (isInterpretedLazy() && u.scripted.s.lazy_)
++        } else if (isInterpretedLazy() && u.scripted.s.lazy_) {
+             TraceManuallyBarrieredEdge(trc, &u.scripted.s.lazy_, "lazyScript");
+-
+-        if (u.scripted.env_)
++        }
++
++        if (u.scripted.env_) {
+             TraceManuallyBarrieredEdge(trc, &u.scripted.env_, "fun_environment");
++        }
+     }
+ }
+ 
+ static void
+ fun_trace(JSTracer* trc, JSObject* obj)
+ {
+     obj->as<JSFunction>().trace(trc);
+ }
+@@ -926,54 +976,60 @@ const Class JSFunction::class_ = {
+ };
+ 
+ const Class* const js::FunctionClassPtr = &JSFunction::class_;
+ 
+ JSString*
+ js::FunctionToStringCache::lookup(JSScript* script) const
+ {
+     for (size_t i = 0; i < NumEntries; i++) {
+-        if (entries_[i].script == script)
++        if (entries_[i].script == script) {
+             return entries_[i].string;
++        }
+     }
+     return nullptr;
+ }
+ 
+ void
+ js::FunctionToStringCache::put(JSScript* script, JSString* string)
+ {
+-    for (size_t i = NumEntries - 1; i > 0; i--)
++    for (size_t i = NumEntries - 1; i > 0; i--) {
+         entries_[i] = entries_[i - 1];
++    }
+ 
+     entries_[0].set(script, string);
+ }
+ 
+ JSString*
+ js::FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource)
+ {
+-    if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
++    if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun)) {
+         return nullptr;
+-
+-    if (IsAsmJSModule(fun))
++    }
++
++    if (IsAsmJSModule(fun)) {
+         return AsmJSModuleToString(cx, fun, isToSource);
+-    if (IsAsmJSFunction(fun))
++    }
++    if (IsAsmJSFunction(fun)) {
+         return AsmJSFunctionToString(cx, fun);
++    }
+ 
+     if (IsWrappedAsyncFunction(fun)) {
+         RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
+         return FunctionToString(cx, unwrapped, isToSource);
+     }
+     if (IsWrappedAsyncGenerator(fun)) {
+         RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
+         return FunctionToString(cx, unwrapped, isToSource);
+     }
+ 
+     RootedScript script(cx);
+-    if (fun->hasScript())
++    if (fun->hasScript()) {
+         script = fun->nonLazyScript();
++    }
+ 
+     // Default class constructors are self-hosted, but have their source
+     // objects overridden to refer to the span of the class statement or
+     // expression. Non-default class constructors are never self-hosted. So,
+     // all class constructors always have source.
+     bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
+                                                !fun->isSelfHostedBuiltin());
+ 
+@@ -985,66 +1041,76 @@ js::FunctionToString(JSContext* cx, Hand
+         !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
+     {
+         return nullptr;
+     }
+ 
+     // Fast path for the common case, to avoid StringBuffer overhead.
+     if (!addParentheses && haveSource) {
+         FunctionToStringCache& cache = cx->zone()->functionToStringCache();
+-        if (JSString* str = cache.lookup(script))
++        if (JSString* str = cache.lookup(script)) {
+             return str;
++        }
+ 
+         size_t start = script->toStringStart(), end = script->toStringEnd();
+         JSString* str = (end - start <= ScriptSource::SourceDeflateLimit)
+             ? script->scriptSource()->substring(cx, start, end)
+             : script->scriptSource()->substringDontDeflate(cx, start, end);
+-        if (!str)
++        if (!str) {
+             return nullptr;
++        }
+ 
+         cache.put(script, str);
+         return str;
+     }
+ 
+     StringBuffer out(cx);
+     if (addParentheses) {
+-        if (!out.append('('))
++        if (!out.append('(')) {
+             return nullptr;
++        }
+     }
+ 
+     if (haveSource) {
+-        if (!script->appendSourceDataForToString(cx, out))
++        if (!script->appendSourceDataForToString(cx, out)) {
+             return nullptr;
++        }
+     } else {
+         if (fun->isAsync()) {
+-            if (!out.append("async "))
++            if (!out.append("async ")) {
+                 return nullptr;
++            }
+         }
+ 
+         if (!fun->isArrow()) {
+-            if (!out.append("function"))
++            if (!out.append("function")) {
+                 return nullptr;
++            }
+ 
+             if (fun->isGenerator()) {
+-                if (!out.append('*'))
++                if (!out.append('*')) {
+                     return nullptr;
++                }
+             }
+         }
+ 
+         if (fun->explicitName()) {
+-            if (!out.append(' '))
++            if (!out.append(' ')) {
+                 return nullptr;
++            }
+ 
+             if (fun->isBoundFunction()) {
+                 JSLinearString* boundName = JSFunction::getBoundFunctionName(cx, fun);
+-                if (!boundName || !out.append(boundName))
++                if (!boundName || !out.append(boundName)) {
+                     return nullptr;
++                }
+             } else {
+-                if (!out.append(fun->explicitName()))
++                if (!out.append(fun->explicitName())) {
+                     return nullptr;
++                }
+             }
+         }
+ 
+         if (fun->isInterpreted() &&
+             (!fun->isSelfHostedBuiltin() ||
+              fun->infallibleIsDefaultClassConstructor(cx)))
+         {
+             // Default class constructors should always haveSource except;
+@@ -1053,38 +1119,42 @@ js::FunctionToString(JSContext* cx, Hand
+             //
+             // 2. The source is marked as "lazy", i.e., retrieved on demand, and
+             // the embedding has not provided a hook to retrieve sources.
+             MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx),
+                           !cx->runtime()->sourceHook.ref() ||
+                           !script->scriptSource()->sourceRetrievable() ||
+                           fun->realm()->behaviors().discardSource());
+ 
+-            if (!out.append("() {\n    [sourceless code]\n}"))
++            if (!out.append("() {\n    [sourceless code]\n}")) {
+                 return nullptr;
++            }
+         } else {
+-            if (!out.append("() {\n    [native code]\n}"))
++            if (!out.append("() {\n    [native code]\n}")) {
+                 return nullptr;
++            }
+         }
+     }
+ 
+     if (addParentheses) {
+-        if (!out.append(')'))
++        if (!out.append(')')) {
+             return nullptr;
++        }
+     }
+ 
+     return out.finishString();
+ }
+ 
+ JSString*
+ fun_toStringHelper(JSContext* cx, HandleObject obj, bool isToSource)
+ {
+     if (!obj->is<JSFunction>()) {
+-        if (JSFunToStringOp op = obj->getOpsFunToString())
++        if (JSFunToStringOp op = obj->getOpsFunToString()) {
+             return op(cx, obj, isToSource);
++        }
+ 
+         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                   JSMSG_INCOMPATIBLE_PROTO,
+                                   js_Function_str, js_toString_str, "object");
+         return nullptr;
+     }
+ 
+     RootedFunction fun(cx, &obj->as<JSFunction>());
+@@ -1092,59 +1162,65 @@ fun_toStringHelper(JSContext* cx, Handle
+ }
+ 
+ bool
+ js::FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols)
+ {
+     jsid id = SYMBOL_TO_JSID(symbols.hasInstance);
+     Shape* shape = fun->lookupPure(id);
+     if (shape) {
+-        if (!shape->isDataProperty())
++        if (!shape->isDataProperty()) {
+             return false;
++        }
+         const Value hasInstance = fun->as<NativeObject>().getSlot(shape->slot());
+         return IsNativeFunction(hasInstance, js::fun_symbolHasInstance);
+     }
+     return true;
+ }
+ 
+ bool
+ js::fun_toString(JSContext* cx, unsigned argc, Value* vp)
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+     MOZ_ASSERT(IsFunctionObject(args.calleev()));
+ 
+     RootedObject obj(cx, ToObject(cx, args.thisv()));
+-    if (!obj)
++    if (!obj) {
+         return false;
++    }
+ 
+     JSString* str = fun_toStringHelper(cx, obj, /* isToSource = */ false);
+-    if (!str)
++    if (!str) {
+         return false;
++    }
+ 
+     args.rval().setString(str);
+     return true;
+ }
+ 
+ static bool
+ fun_toSource(JSContext* cx, unsigned argc, Value* vp)
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+     MOZ_ASSERT(IsFunctionObject(args.calleev()));
+ 
+     RootedObject obj(cx, ToObject(cx, args.thisv()));
+-    if (!obj)
++    if (!obj) {
+         return false;
++    }
+ 
+     RootedString str(cx);
+-    if (obj->isCallable())
++    if (obj->isCallable()) {
+         str = fun_toStringHelper(cx, obj, /* isToSource = */ true);
+-    else
++    } else {
+         str = ObjectToSource(cx, obj);
+-    if (!str)
++    }
++    if (!str) {
+         return false;
++    }
+ 
+     args.rval().setString(str);
+     return true;
+ }
+ 
+ bool
+ js::fun_call(JSContext* cx, unsigned argc, Value* vp)
+ {
+@@ -1163,25 +1239,28 @@ js::fun_call(JSContext* cx, unsigned arg
+     // |Function.prototype.call| and would conclude, "Function.prototype.call
+     // is not a function".  Grotesque.)
+     if (!IsCallable(func)) {
+         ReportIncompatibleMethod(cx, args, &JSFunction::class_);
+         return false;
+     }
+ 
+     size_t argCount = args.length();
+-    if (argCount > 0)
++    if (argCount > 0) {
+         argCount--; // strip off provided |this|
++    }
+ 
+     InvokeArgs iargs(cx);
+-    if (!iargs.init(cx, argCount))
++    if (!iargs.init(cx, argCount)) {
+         return false;
+-
+-    for (size_t i = 0; i < argCount; i++)
++    }
++
++    for (size_t i = 0; i < argCount; i++) {
+         iargs[i].set(args[i + 1]);
++    }
+ 
+     return Call(cx, func, args.get(0), iargs, args.rval());
+ }
+ 
+ // ES5 15.3.4.3
+ bool
+ js::fun_apply(JSContext* cx, unsigned argc, Value* vp)
+ {
+@@ -1194,69 +1273,75 @@ js::fun_apply(JSContext* cx, unsigned ar
+     // have side effects or throw an exception.
+     HandleValue fval = args.thisv();
+     if (!IsCallable(fval)) {
+         ReportIncompatibleMethod(cx, args, &JSFunction::class_);
+         return false;
+     }
+ 
+     // Step 2.
+-    if (args.length() < 2 || args[1].isNullOrUndefined())
++    if (args.length() < 2 || args[1].isNullOrUndefined()) {
+         return fun_call(cx, (args.length() > 0) ? 1 : 0, vp);
++    }
+ 
+     InvokeArgs args2(cx);
+ 
+     // A JS_OPTIMIZED_ARGUMENTS magic value means that 'arguments' flows into
+     // this apply call from a scripted caller and, as an optimization, we've
+     // avoided creating it since apply can simply pull the argument values from
+     // the calling frame (which we must do now).
+     if (args[1].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
+         // Step 3-6.
+         ScriptFrameIter iter(cx);
+         MOZ_ASSERT(iter.numActualArgs() <= ARGS_LENGTH_MAX);
+-        if (!args2.init(cx, iter.numActualArgs()))
++        if (!args2.init(cx, iter.numActualArgs())) {
+             return false;
++        }
+ 
+         // Steps 7-8.
+         iter.unaliasedForEachActual(cx, CopyTo(args2.array()));
+     } else {
+         // Step 3.
+         if (!args[1].isObject()) {
+             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                       JSMSG_BAD_APPLY_ARGS, js_apply_str);
+             return false;
+         }
+ 
+         // Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
+         // original version of ES5).
+         RootedObject aobj(cx, &args[1].toObject());
+         uint32_t length;
+-        if (!GetLengthProperty(cx, aobj, &length))
++        if (!GetLengthProperty(cx, aobj, &length)) {
+             return false;
++        }
+ 
+         // Step 6.
+-        if (!args2.init(cx, length))
++        if (!args2.init(cx, length)) {
+             return false;
++        }
+ 
+         MOZ_ASSERT(length <= ARGS_LENGTH_MAX);
+ 
+         // Steps 7-8.
+-        if (!GetElements(cx, aobj, length, args2.array()))
++        if (!GetElements(cx, aobj, length, args2.array())) {
+             return false;
++        }
+     }
+ 
+     // Step 9.
+     return Call(cx, fval, args[0], args2, args.rval());
+ }
+ 
+ bool
+ JSFunction::infallibleIsDefaultClassConstructor(JSContext* cx) const
+ {
+-    if (!isSelfHostedBuiltin())
++    if (!isSelfHostedBuiltin()) {
+         return false;
++    }
+ 
+     bool isDefault = false;
+     if (isInterpretedLazy()) {
+         JSAtom* name = &getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom();
+         isDefault = name == cx->names().DefaultDerivedClassConstructor ||
+                     name == cx->names().DefaultBaseClassConstructor;
+     } else {
+         isDefault = nonLazyScript()->isDefaultClassConstructor();
+@@ -1290,18 +1375,19 @@ JSFunction::isDerivedClassConstructor()
+     MOZ_ASSERT_IF(derived, isClassConstructor());
+     return derived;
+ }
+ 
+ /* static */ bool
+ JSFunction::getLength(JSContext* cx, HandleFunction fun, uint16_t* length)
+ {
+     MOZ_ASSERT(!fun->isBoundFunction());
+-    if (fun->isInterpretedLazy() && !getOrCreateScript(cx, fun))
++    if (fun->isInterpretedLazy() && !getOrCreateScript(cx, fun)) {
+         return false;
++    }
+ 
+     *length = fun->isNative() ? fun->nargs() : fun->nonLazyScript()->funLength();
+     return true;
+ }
+ 
+ /* static */ bool
+ JSFunction::getUnresolvedLength(JSContext* cx, HandleFunction fun, MutableHandleValue v)
+ {
+@@ -1312,46 +1398,50 @@ JSFunction::getUnresolvedLength(JSContex
+     // they're handled differently from other functions.
+     if (fun->isBoundFunction()) {
+         MOZ_ASSERT(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber());
+         v.set(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT));
+         return true;
+     }
+ 
+     uint16_t length;
+-    if (!JSFunction::getLength(cx, fun, &length))
++    if (!JSFunction::getLength(cx, fun, &length)) {
+         return false;
++    }
+ 
+     v.setInt32(length);
+     return true;
+ }
+ 
+ JSAtom*
+ JSFunction::infallibleGetUnresolvedName(JSContext* cx)
+ {
+     MOZ_ASSERT(!IsInternalFunctionObject(*this));
+     MOZ_ASSERT(!hasResolvedName());
+ 
+-    if (JSAtom* name = explicitOrInferredName())
++    if (JSAtom* name = explicitOrInferredName()) {
+         return name;
++    }
+ 
+     // Unnamed class expressions should not get a .name property at all.
+-    if (isClassConstructor())
++    if (isClassConstructor()) {
+         return nullptr;
++    }
+ 
+     return cx->names().empty;
+ }
+ 
+ /* static */ bool
+ JSFunction::getUnresolvedName(JSContext* cx, HandleFunction fun, MutableHandleString v)
+ {
+     if (fun->isBoundFunction()) {
+         JSLinearString* name = JSFunction::getBoundFunctionName(cx, fun);
+-        if (!name)
++        if (!name) {
+             return false;
++        }
+ 
+         v.set(name);
+         return true;
+     }
+ 
+     v.set(fun->infallibleGetUnresolvedName(cx));
+     return true;
+ }
+@@ -1361,55 +1451,61 @@ JSFunction::getBoundFunctionName(JSConte
+ {
+     MOZ_ASSERT(fun->isBoundFunction());
+     JSAtom* name = fun->explicitName();
+ 
+     // Bound functions are never unnamed.
+     MOZ_ASSERT(name);
+ 
+     // If the bound function prefix is present, return the name as is.
+-    if (fun->hasBoundFunctionNamePrefix())
++    if (fun->hasBoundFunctionNamePrefix()) {
+         return name;
++    }
+ 
+     // Otherwise return "bound " * (number of bound function targets) + name.
+     size_t boundTargets = 0;
+     for (JSFunction* boundFn = fun; boundFn->isBoundFunction(); ) {
+         boundTargets++;
+ 
+         JSObject* target = boundFn->getBoundFunctionTarget();
+-        if (!target->is<JSFunction>())
++        if (!target->is<JSFunction>()) {
+             break;
++        }
+         boundFn = &target->as<JSFunction>();
+     }
+ 
+     // |function /*unnamed*/ (){...}.bind()| is a common case, handle it here.
+-    if (name->empty() && boundTargets == 1)
++    if (name->empty() && boundTargets == 1) {
+         return cx->names().boundWithSpace;
++    }
+ 
+     static constexpr char boundWithSpaceChars[] = "bound ";
+     static constexpr size_t boundWithSpaceCharsLength =
+         ArrayLength(boundWithSpaceChars) - 1; // No trailing '\0'.
+     MOZ_ASSERT(StringEqualsAscii(cx->names().boundWithSpace, boundWithSpaceChars));
+ 
+     StringBuffer sb(cx);
+-    if (name->hasTwoByteChars() && !sb.ensureTwoByteChars())
++    if (name->hasTwoByteChars() && !sb.ensureTwoByteChars()) {
+         return nullptr;
++    }
+ 
+     CheckedInt<size_t> len(boundTargets);
+     len *= boundWithSpaceCharsLength;
+     len += name->length();
+     if (!len.isValid()) {
+         ReportAllocationOverflow(cx);
+         return nullptr;
+     }
+-    if (!sb.reserve(len.value()))
++    if (!sb.reserve(len.value())) {
+         return nullptr;
+-
+-    while (boundTargets--)
++    }
++
++    while (boundTargets--) {
+         sb.infallibleAppend(boundWithSpaceChars, boundWithSpaceCharsLength);
++    }
+     sb.infallibleAppendSubstring(name, 0, name->length());
+ 
+     return sb.finishString();
+ }
+ 
+ static const js::Value&
+ BoundFunctionEnvironmentSlotValue(const JSFunction* fun, uint32_t slotIndex)
+ {
+@@ -1455,69 +1551,77 @@ JSFunction::getBoundFunctionArgumentCoun
+ 
+ static JSAtom*
+ AppendBoundFunctionPrefix(JSContext* cx, JSString* str)
+ {
+     static constexpr char boundWithSpaceChars[] = "bound ";
+     MOZ_ASSERT(StringEqualsAscii(cx->names().boundWithSpace, boundWithSpaceChars));
+ 
+     StringBuffer sb(cx);
+-    if (!sb.append(boundWithSpaceChars) || !sb.append(str))
++    if (!sb.append(boundWithSpaceChars) || !sb.append(str)) {
+         return nullptr;
++    }
+     return sb.finishAtom();
+ }
+ 
+ /* static */ bool
+ JSFunction::finishBoundFunctionInit(JSContext* cx, HandleFunction bound, HandleObject targetObj,
+                                     int32_t argCount)
+ {
+     bound->setIsBoundFunction();
+     MOZ_ASSERT(bound->getBoundFunctionTarget() == targetObj);
+ 
+     // 9.4.1.3 BoundFunctionCreate, steps 1, 3-5, 8-12 (Already performed).
+ 
+     // 9.4.1.3 BoundFunctionCreate, step 6.
+-    if (targetObj->isConstructor())
++    if (targetObj->isConstructor()) {
+         bound->setIsConstructor();
++    }
+ 
+     // 9.4.1.3 BoundFunctionCreate, step 2.
+     RootedObject proto(cx);
+-    if (!GetPrototype(cx, targetObj, &proto))
++    if (!GetPrototype(cx, targetObj, &proto)) {
+         return false;
++    }
+ 
+     // 9.4.1.3 BoundFunctionCreate, step 7.
+     if (bound->staticPrototype() != proto) {
+-        if (!SetPrototype(cx, bound, proto))
++        if (!SetPrototype(cx, bound, proto)) {
+             return false;
++        }
+     }
+ 
+     double length = 0.0;
+ 
+     // Try to avoid invoking the resolve hook.
+     if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedLength()) {
+         RootedValue targetLength(cx);
+-        if (!JSFunction::getUnresolvedLength(cx, targetObj.as<JSFunction>(), &targetLength))
++        if (!JSFunction::getUnresolvedLength(cx, targetObj.as<JSFunction>(), &targetLength)) {
+             return false;
++        }
+ 
+         length = Max(0.0, targetLength.toNumber() - argCount);
+     } else {
+         // 19.2.3.2 Function.prototype.bind, step 5.
+         bool hasLength;
+         RootedId idRoot(cx, NameToId(cx->names().length));
+-        if (!HasOwnProperty(cx, targetObj, idRoot, &hasLength))
++        if (!HasOwnProperty(cx, targetObj, idRoot, &hasLength)) {
+             return false;
++        }
+ 
+         // 19.2.3.2 Function.prototype.bind, step 6.
+         if (hasLength) {
+             RootedValue targetLength(cx);
+-            if (!GetProperty(cx, targetObj, targetObj, idRoot, &targetLength))
++            if (!GetProperty(cx, targetObj, targetObj, idRoot, &targetLength)) {
+                 return false;
+-
+-            if (targetLength.isNumber())
++            }
++
++            if (targetLength.isNumber()) {
+                 length = Max(0.0, JS::ToInteger(targetLength.toNumber()) - argCount);
++            }
+         }
+ 
+         // 19.2.3.2 Function.prototype.bind, step 7 (implicit).
+     }
+ 
+     // 19.2.3.2 Function.prototype.bind, step 8.
+     bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, NumberValue(length));
+ 
+@@ -1528,50 +1632,56 @@ JSFunction::finishBoundFunctionInit(JSCo
+     if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedName()) {
+         JSFunction* targetFn = &targetObj->as<JSFunction>();
+ 
+         // If the target is a bound function with a prefixed name, we can't
+         // lazily compute the full name in getBoundFunctionName(), therefore
+         // we need to append the bound function name prefix here.
+         if (targetFn->isBoundFunction() && targetFn->hasBoundFunctionNamePrefix()) {
+             name = AppendBoundFunctionPrefix(cx, targetFn->explicitName());
+-            if (!name)
++            if (!name) {
+                 return false;
++            }
+             bound->setPrefixedBoundFunctionName(name);
+         } else {
+             name = targetFn->infallibleGetUnresolvedName(cx);
+-            if (name)
++            if (name) {
+                 bound->setAtom(name);
++            }
+         }
+     }
+ 
+     // 19.2.3.2 Function.prototype.bind, steps 9-11.
+     if (!name) {
+         // 19.2.3.2 Function.prototype.bind, step 9.
+         RootedValue targetName(cx);
+-        if (!GetProperty(cx, targetObj, targetObj, cx->names().name, &targetName))
++        if (!GetProperty(cx, targetObj, targetObj, cx->names().name, &targetName)) {
+             return false;
++        }
+ 
+         // 19.2.3.2 Function.prototype.bind, step 10.
+-        if (!targetName.isString())
++        if (!targetName.isString()) {
+             targetName.setString(cx->names().empty);
++        }
+ 
+         // If the target itself is a bound function (with a resolved name), we
+         // can't compute the full name in getBoundFunctionName() based only on
+         // the number of bound target functions, therefore we need to store
+         // the complete prefixed name here.
+         if (targetObj->is<JSFunction>() && targetObj->as<JSFunction>().isBoundFunction()) {
+             name = AppendBoundFunctionPrefix(cx, targetName.toString());
+-            if (!name)
++            if (!name) {
+                 return false;
++            }
+             bound->setPrefixedBoundFunctionName(name);
+         } else {
+             name = AtomizeString(cx, targetName.toString());
+-            if (!name)
++            if (!name) {
+                 return false;
++            }
+             bound->setAtom(name);
+         }
+     }
+ 
+     return true;
+ }
+ 
+ /* static */ bool
+@@ -1593,63 +1703,68 @@ JSFunction::createScriptForLazilyInterpr
+         bool canRelazify = !lazy->numInnerFunctions() && !lazy->hasDirectEval();
+ 
+         if (script) {
+             // This function is non-canonical function, and the canonical
+             // function is already delazified.
+             fun->setUnlazifiedScript(script);
+             // Remember the lazy script on the compiled script, so it can be
+             // stored on the function again in case of re-lazification.
+-            if (canRelazify)
++            if (canRelazify) {
+                 script->setLazyScript(lazy);
++            }
+             return true;
+         }
+ 
+         if (fun != lazy->functionNonDelazifying()) {
+             // This function is non-canonical function, and the canonical
+             // function is lazy.
+             // Delazify the canonical function, which will result in calling
+             // this function again with the canonical function.
+-            if (!LazyScript::functionDelazifying(cx, lazy))
++            if (!LazyScript::functionDelazifying(cx, lazy)) {
+                 return false;
++            }
+             script = lazy->functionNonDelazifying()->nonLazyScript();
+-            if (!script)
++            if (!script) {
+                 return false;
++            }
+ 
+             fun->setUnlazifiedScript(script);
+             return true;
+         }
+ 
+         // This is lazy canonical-function.
+ 
+         MOZ_ASSERT(lazy->scriptSource()->hasSourceData());
+ 
+         // Parse and compile the script from source.
+         size_t lazyLength = lazy->sourceEnd() - lazy->sourceStart();
+         UncompressedSourceCache::AutoHoldEntry holder;
+         ScriptSource::PinnedChars chars(cx, lazy->scriptSource(), holder,
+                                         lazy->sourceStart(), lazyLength);
+-        if (!chars.get())
++        if (!chars.get()) {
+             return false;
++        }
+ 
+         if (!frontend::CompileLazyFunction(cx, lazy, chars.get(), lazyLength)) {
+             // The frontend shouldn't fail after linking the function and the
+             // non-lazy script together.
+             MOZ_ASSERT(fun->isInterpretedLazy());
+             MOZ_ASSERT(fun->lazyScript() == lazy);
+             MOZ_ASSERT(!lazy->hasScript());
+             return false;
+         }
+ 
+         script = fun->nonLazyScript();
+ 
+         // Remember the compiled script on the lazy script itself, in case
+         // there are clones of the function still pointing to the lazy script.
+-        if (!lazy->maybeScript())
++        if (!lazy->maybeScript()) {
+             lazy->initScript(script);
++        }
+ 
+         // Try to insert the newly compiled script into the lazy script cache.
+         if (canRelazify) {
+             // A script's starting column isn't set by the bytecode emitter, so
+             // specify this from the lazy script so that if an identical lazy
+             // script is encountered later a match can be determined.
+             script->setColumn(lazy->column());
+ 
+@@ -1994,63 +2109,70 @@ JSFunction::isBuiltinFunctionConstructor
+     return maybeNative() == Function || maybeNative() == Generator;
+ }
+ 
+ bool
+ JSFunction::needsExtraBodyVarEnvironment() const
+ {
+     MOZ_ASSERT(!isInterpretedLazy());
+ 
+-    if (isNative())
++    if (isNative()) {
+         return false;
+-
+-    if (!nonLazyScript()->functionHasExtraBodyVarScope())
++    }
++
++    if (!nonLazyScript()->functionHasExtraBodyVarScope()) {
+         return false;
++    }
+ 
+     return nonLazyScript()->functionExtraBodyVarScope()->hasEnvironment();
+ }
+ 
+ bool
+ JSFunction::needsNamedLambdaEnvironment() const
+ {
+     MOZ_ASSERT(!isInterpretedLazy());
+ 
+-    if (!isNamedLambda())
++    if (!isNamedLambda()) {
+         return false;
++    }
+ 
+     LexicalScope* scope = nonLazyScript()->maybeNamedLambdaScope();
+-    if (!scope)
++    if (!scope) {
+         return false;
++    }
+ 
+     return scope->hasEnvironment();
+ }
+ 
+ JSFunction*
+ js::NewScriptedFunction(JSContext* cx, unsigned nargs,
+                         JSFunction::Flags flags, HandleAtom atom,
+                         HandleObject proto /* = nullptr */,
+                         gc::AllocKind allocKind /* = AllocKind::FUNCTION */,
+                         NewObjectKind newKind /* = GenericObject */,
+                         HandleObject enclosingEnvArg /* = nullptr */)
+ {
+     RootedObject enclosingEnv(cx, enclosingEnvArg);
+-    if (!enclosingEnv)
++    if (!enclosingEnv) {
+         enclosingEnv = &cx->global()->lexicalEnvironment();
++    }
+     return NewFunctionWithProto(cx, nullptr, nargs, flags, enclosingEnv,
+                                 atom, proto, allocKind, newKind);
+ }
+ 
+ #ifdef DEBUG
+ static JSObject*
+ SkipEnvironmentObjects(JSObject* env)
+ {
+-    if (!env)
++    if (!env) {
+         return nullptr;
+-    while (env->is<EnvironmentObject>())
++    }
++    while (env->is<EnvironmentObject>()) {
+         env = &env->as<EnvironmentObject>().enclosingEnvironment();
++    }
+     return env;
+ }
+ 
+ static bool
+ NewFunctionEnvironmentIsWellFormed(JSContext* cx, HandleObject env)
+ {
+     // Assert that the terminating environment is null, global, or a debug
+     // scope proxy. All other cases of polluting global scope behavior are
+@@ -2373,24 +2495,26 @@ NameToFunctionName(JSContext* cx, Handle
+  */
+ JSAtom*
+ js::IdToFunctionName(JSContext* cx, HandleId id,
+                      FunctionPrefixKind prefixKind /* = FunctionPrefixKind::None */)
+ {
+     MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id) || JSID_IS_INT(id));
+ 
+     // No prefix fastpath.
+-    if (JSID_IS_ATOM(id) && prefixKind == FunctionPrefixKind::None)
++    if (JSID_IS_ATOM(id) && prefixKind == FunctionPrefixKind::None) {
+         return JSID_TO_ATOM(id);
++    }
+ 
+     // Step 3 (implicit).
+ 
+     // Step 4.
+-    if (JSID_IS_SYMBOL(id))
++    if (JSID_IS_SYMBOL(id)) {
+         return SymbolToFunctionName(cx, JSID_TO_SYMBOL(id), prefixKind);
++    }
+ 
+     // Step 5.
+     RootedValue idv(cx, IdToValue(id));
+     return NameToFunctionName(cx, idv, prefixKind);
+ }
+ 
+ bool
+ js::SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun, HandleValue name,
+@@ -2403,28 +2527,30 @@ js::SetFunctionNameIfNoOwnName(JSContext
+     // end up not adding a new inferred name if |fun| is a class constructor.
+     if (fun->hasInferredName()) {
+         MOZ_ASSERT(fun->isSingleton());
+         fun->clearInferredName();
+     }
+ 
+     if (fun->isClassConstructor()) {
+         // A class may have static 'name' method or accessor.
+-        if (fun->contains(cx, cx->names().name))
++        if (fun->contains(cx, cx->names().name)) {
+             return true;
++        }
+     } else {
+         // Anonymous function shouldn't have own 'name' property at this point.
+         MOZ_ASSERT(!fun->containsPure(cx->names().name));
+     }
+ 
+     JSAtom* funName = name.isSymbol()
+                       ? SymbolToFunctionName(cx, name.toSymbol(), prefixKind)
+                       : NameToFunctionName(cx, name, prefixKind);
+-    if (!funName)
++    if (!funName) {
+         return false;
++    }
+ 
+     // RESOLVED_NAME shouldn't yet be set, at least as long as we don't
+     // support the "static public fields" or "decorators" proposal.
+     // These two proposals allow to access class constructors before
+     // JSOP_SETFUNNAME is executed, which means user code may have set the
+     // RESOLVED_NAME flag when we reach this point.
+     MOZ_ASSERT(!fun->hasResolvedName());
+ 
+diff --git a/js/src/vm/JSFunction.cpp.1488698-7.later b/js/src/vm/JSFunction.cpp.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSFunction.cpp.1488698-7.later
+@@ -0,0 +1,884 @@
++--- JSFunction.cpp
+++++ JSFunction.cpp
++@@ -753,35 +801,37 @@ JS::OrdinaryHasInstance(JSContext* cx, H
++     /* Step 3. */
++     if (!v.isObject()) {
++         *bp = false;
++         return true;
++     }
++ 
++     /* Step 4. */
++     RootedValue pval(cx);
++-    if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval))
+++    if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval)) {
++         return false;
+++    }
++ 
++     /* Step 5. */
++     if (pval.isPrimitive()) {
++         /*
++          * Throw a runtime error if instanceof is called on a function that
++          * has a non-object as its .prototype value.
++          */
++         RootedValue val(cx, ObjectValue(*obj));
++         ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, val, nullptr);
++         return false;
++     }
++ 
++     /* Step 6. */
++     RootedObject pobj(cx, &pval.toObject());
++     bool isPrototype;
++-    if (!IsPrototypeOf(cx, pobj, &v.toObject(), &isPrototype))
+++    if (!IsPrototypeOf(cx, pobj, &v.toObject(), &isPrototype)) {
++         return false;
+++    }
++     *bp = isPrototype;
++     return true;
++ }
++ 
++ inline void
++ JSFunction::trace(JSTracer* trc)
++ {
++     if (isExtended()) {
++@@ -816,18 +868,19 @@ CreateFunctionConstructor(JSContext* cx,
++ {
++     Rooted<GlobalObject*> global(cx, cx->global());
++     RootedObject functionProto(cx, &global->getPrototype(JSProto_Function).toObject());
++ 
++     RootedObject functionCtor(cx,
++       NewFunctionWithProto(cx, Function, 1, JSFunction::NATIVE_CTOR,
++                            nullptr, HandlePropertyName(cx->names().Function),
++                            functionProto, gc::AllocKind::FUNCTION, SingletonObject));
++-    if (!functionCtor)
+++    if (!functionCtor) {
++         return nullptr;
+++    }
++ 
++     return functionCtor;
++ }
++ 
++ static JSObject*
++ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
++ {
++     Rooted<GlobalObject*> self(cx, cx->global());
++@@ -836,68 +889,77 @@ CreateFunctionPrototype(JSContext* cx, J
++     /*
++      * Bizarrely, |Function.prototype| must be an interpreted function, so
++      * give it the guts to be one.
++      */
++     RootedObject enclosingEnv(cx, &self->lexicalEnvironment());
++     RootedFunction functionProto(cx, NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED,
++                                                           enclosingEnv, nullptr, objectProto,
++                                                           gc::AllocKind::FUNCTION, SingletonObject));
++-    if (!functionProto)
+++    if (!functionProto) {
++     	return nullptr;
+++    }
++ 
++     const char* rawSource = "function () {\n}";
++     size_t sourceLen = strlen(rawSource);
++     size_t begin = 9;
++     MOZ_ASSERT(rawSource[begin] == '(');
++     UniqueTwoByteChars source(InflateString(cx, rawSource, sourceLen));
++-    if (!source)
+++    if (!source) {
++         return nullptr;
+++    }
++ 
++     ScriptSource* ss = cx->new_<ScriptSource>();
++-    if (!ss)
+++    if (!ss) {
++         return nullptr;
+++    }
++     ScriptSourceHolder ssHolder(ss);
++-    if (!ss->setSource(cx, std::move(source), sourceLen))
+++    if (!ss->setSource(cx, std::move(source), sourceLen)) {
++         return nullptr;
+++    }
++ 
++     CompileOptions options(cx);
++     options.setIntroductionType("Function.prototype")
++            .setNoScriptRval(true);
++-    if (!ss->initFromOptions(cx, options))
+++    if (!ss->initFromOptions(cx, options)) {
++         return nullptr;
+++    }
++     RootedScriptSourceObject sourceObject(cx, ScriptSourceObject::create(cx, ss));
++-    if (!sourceObject || !ScriptSourceObject::initFromOptions(cx, sourceObject, options))
+++    if (!sourceObject || !ScriptSourceObject::initFromOptions(cx, sourceObject, options)) {
++         return nullptr;
+++    }
++ 
++     RootedScript script(cx, JSScript::Create(cx,
++                                              options,
++                                              sourceObject,
++                                              begin,
++                                              ss->length(),
++                                              0,
++                                              ss->length()));
++-    if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto))
+++    if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto)) {
++         return nullptr;
+++    }
++ 
++     functionProto->initScript(script);
++     ObjectGroup* protoGroup = JSObject::getGroup(cx, functionProto);
++-    if (!protoGroup)
+++    if (!protoGroup) {
++         return nullptr;
+++    }
++ 
++     protoGroup->setInterpretedFunction(functionProto);
++ 
++     /*
++      * The default 'new' group of Function.prototype is required by type
++      * inference to have unknown properties, to simplify handling of e.g.
++      * NewFunctionClone.
++      */
++     ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
++-    if (!JSObject::setNewGroupUnknown(cx, realm, &JSFunction::class_, functionProto))
+++    if (!JSObject::setNewGroupUnknown(cx, realm, &JSFunction::class_, functionProto)) {
++         return nullptr;
+++    }
++ 
++     return functionProto;
++ }
++ 
++ static const ClassOps JSFunctionClassOps = {
++     nullptr,                 /* addProperty */
++     nullptr,                 /* delProperty */
++     fun_enumerate,
++@@ -1661,66 +1788,73 @@ JSFunction::createScriptForLazilyInterpr
++             // stored on the function again in case of re-lazification.
++             // Only functions without inner functions are re-lazified.
++             script->setLazyScript(lazy);
++         }
++ 
++         // XDR the newly delazified function.
++         if (script->scriptSource()->hasEncoder()) {
++             RootedScriptSourceObject sourceObject(cx, &lazy->sourceObject());
++-            if (!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
+++            if (!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject)) {
++                 return false;
+++            }
++         }
++ 
++         return true;
++     }
++ 
++     /* Lazily cloned self-hosted script. */
++     MOZ_ASSERT(fun->isSelfHostedBuiltin());
++     RootedAtom funAtom(cx, &fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom());
++-    if (!funAtom)
+++    if (!funAtom) {
++         return false;
+++    }
++     Rooted<PropertyName*> funName(cx, funAtom->asPropertyName());
++     return cx->runtime()->cloneSelfHostedFunctionScript(cx, funName, fun);
++ }
++ 
++ void
++ JSFunction::maybeRelazify(JSRuntime* rt)
++ {
++     // Try to relazify functions with a non-lazy script. Note: functions can be
++     // marked as interpreted despite having no script yet at some points when
++     // parsing.
++-    if (!hasScript() || !u.scripted.s.script_)
+++    if (!hasScript() || !u.scripted.s.script_) {
++         return;
+++    }
++ 
++     // Don't relazify functions in compartments that are active.
++     Realm* realm = this->realm();
++     if (!rt->allowRelazificationForTesting) {
++-        if (realm->compartment()->gcState.hasEnteredRealm)
+++        if (realm->compartment()->gcState.hasEnteredRealm) {
++             return;
+++        }
++ 
++         MOZ_ASSERT(!realm->hasBeenEnteredIgnoringJit());
++     }
++ 
++     // The caller should have checked we're not in the self-hosting zone (it's
++     // shared with worker runtimes so relazifying functions in it will race).
++     MOZ_ASSERT(!realm->isSelfHostingRealm());
++ 
++     // Don't relazify if the realm is being debugged.
++-    if (realm->isDebuggee())
+++    if (realm->isDebuggee()) {
++         return;
+++    }
++ 
++     // Don't relazify if the realm and/or runtime is instrumented to
++     // collect code coverage for analysis.
++-    if (realm->collectCoverageForDebug())
+++    if (realm->collectCoverageForDebug()) {
++         return;
+++    }
++ 
++     // Don't relazify functions with JIT code.
++-    if (!u.scripted.s.script_->isRelazifiable())
+++    if (!u.scripted.s.script_->isRelazifiable()) {
++         return;
+++    }
++ 
++     // To delazify self-hosted builtins we need the name of the function
++     // to clone. This name is stored in the first extended slot. Since
++     // that slot is sometimes also used for other purposes, make sure it
++     // contains a string.
++     if (isSelfHostedBuiltin() &&
++         (!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString()))
++     {
++@@ -1771,101 +1905,116 @@ CreateDynamicFunction(JSContext* cx, con
++     unsigned lineno;
++     bool mutedErrors;
++     uint32_t pcOffset;
++     DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
++                                          &mutedErrors);
++ 
++     const char* introductionType = "Function";
++     if (isAsync) {
++-        if (isGenerator)
+++        if (isGenerator) {
++             introductionType = "AsyncGenerator";
++-        else
+++        } else {
++             introductionType = "AsyncFunction";
+++        }
++     } else if (isGenerator) {
++         introductionType = "GeneratorFunction";
++     }
++ 
++     const char* introducerFilename = filename;
++-    if (maybeScript && maybeScript->scriptSource()->introducerFilename())
+++    if (maybeScript && maybeScript->scriptSource()->introducerFilename()) {
++         introducerFilename = maybeScript->scriptSource()->introducerFilename();
+++    }
++ 
++     CompileOptions options(cx);
++     options.setMutedErrors(mutedErrors)
++            .setFileAndLine(filename, 1)
++            .setNoScriptRval(false)
++            .setIntroductionInfo(introducerFilename, introductionType, lineno, maybeScript, pcOffset);
++ 
++     StringBuffer sb(cx);
++ 
++     if (isAsync) {
++-        if (!sb.append("async "))
+++        if (!sb.append("async ")) {
++             return false;
+++        }
++     }
++-    if (!sb.append("function"))
+++    if (!sb.append("function")) {
++          return false;
+++    }
++     if (isGenerator) {
++-        if (!sb.append('*'))
+++        if (!sb.append('*')) {
++             return false;
+++        }
++     }
++ 
++-    if (!sb.append(" anonymous("))
+++    if (!sb.append(" anonymous(")) {
++         return false;
+++    }
++ 
++     if (args.length() > 1) {
++         RootedString str(cx);
++ 
++         // Steps 10, 14.d.
++         unsigned n = args.length() - 1;
++ 
++         for (unsigned i = 0; i < n; i++) {
++             // Steps 14.a-b, 14.d.i-ii.
++             str = ToString<CanGC>(cx, args[i]);
++-            if (!str)
+++            if (!str) {
++                 return false;
+++            }
++ 
++             // Steps 14.b, 14.d.iii.
++-            if (!sb.append(str))
+++            if (!sb.append(str)) {
++                  return false;
+++            }
++ 
++             if (i < args.length() - 2) {
++                 // Step 14.d.iii.
++-                if (!sb.append(','))
+++                if (!sb.append(',')) {
++                     return false;
+++                }
++             }
++         }
++     }
++ 
++-    if (!sb.append('\n'))
+++    if (!sb.append('\n')) {
++         return false;
+++    }
++ 
++     // Remember the position of ")".
++     Maybe<uint32_t> parameterListEnd = Some(uint32_t(sb.length()));
++     MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')');
++ 
++-    if (!sb.append(FunctionConstructorMedialSigils))
+++    if (!sb.append(FunctionConstructorMedialSigils)) {
++         return false;
+++    }
++ 
++     if (args.length() > 0) {
++         // Steps 13, 14.e, 15.
++         RootedString body(cx, ToString<CanGC>(cx, args[args.length() - 1]));
++-        if (!body || !sb.append(body))
+++        if (!body || !sb.append(body)) {
++              return false;
+++        }
++      }
++ 
++-    if (!sb.append(FunctionConstructorFinalBrace))
+++    if (!sb.append(FunctionConstructorFinalBrace)) {
++         return false;
+++    }
++ 
++     // The parser only accepts two byte strings.
++-    if (!sb.ensureTwoByteChars())
+++    if (!sb.ensureTwoByteChars()) {
++         return false;
+++    }
++ 
++     RootedString functionText(cx, sb.finishString());
++-    if (!functionText)
+++    if (!functionText) {
++         return false;
+++    }
++ 
++     // Block this call if security callbacks forbid it.
++     Handle<GlobalObject*> global = cx->global();
++     RootedValue v(cx, StringValue(functionText));
++     if (!GlobalObject::isRuntimeCodeGenEnabled(cx, v, global)) {
++         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_FUNCTION);
++         return false;
++     }
++@@ -1880,92 +2029,103 @@ CreateDynamicFunction(JSContext* cx, con
++ 
++     // Initialize the function with the default prototype:
++     // Leave as nullptr to get the default from clasp for normal functions.
++     // Use %Generator% for generators and the unwrapped function of async
++     // functions and async generators.
++     RootedObject defaultProto(cx);
++     if (isGenerator || isAsync) {
++         defaultProto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, global);
++-        if (!defaultProto)
+++        if (!defaultProto) {
++             return false;
+++        }
++     }
++ 
++     // Step 30-37 (reordered).
++     RootedObject globalLexical(cx, &global->lexicalEnvironment());
++     JSFunction::Flags flags = (isGenerator || isAsync)
++                               ? JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC
++                               : JSFunction::INTERPRETED_LAMBDA;
++     gc::AllocKind allocKind = isAsync ? gc::AllocKind::FUNCTION_EXTENDED
++                                       : gc::AllocKind::FUNCTION;
++     RootedFunction fun(cx, NewFunctionWithProto(cx, nullptr, 0,
++                                                 flags, globalLexical,
++                                                 anonymousAtom, defaultProto,
++                                                 allocKind, TenuredObject));
++-    if (!fun)
+++    if (!fun) {
++         return false;
++-
++-    if (!JSFunction::setTypeForScriptedFunction(cx, fun))
+++    }
+++
+++    if (!JSFunction::setTypeForScriptedFunction(cx, fun)) {
++         return false;
+++    }
++ 
++     // Steps 7.a-b, 8.a-b, 9.a-b, 16-28.
++     AutoStableStringChars stableChars(cx);
++-    if (!stableChars.initTwoByte(cx, functionText))
+++    if (!stableChars.initTwoByte(cx, functionText)) {
++         return false;
+++    }
++ 
++     mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
++     SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
++                                               ? SourceBufferHolder::GiveOwnership
++                                               : SourceBufferHolder::NoOwnership;
++     SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership);
++     if (isAsync) {
++         if (isGenerator) {
++-            if (!CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf, parameterListEnd))
+++            if (!CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf, parameterListEnd)) {
++                 return false;
+++            }
++         } else {
++-            if (!CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd))
+++            if (!CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd)) {
++                 return false;
+++            }
++         }
++     } else {
++         if (isGenerator) {
++-            if (!CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd))
+++            if (!CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd)) {
++                 return false;
+++            }
++         } else {
++-            if (!CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd))
+++            if (!CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd)) {
++                 return false;
+++            }
++         }
++     }
++ 
++     // Steps 6, 29.
++     RootedObject proto(cx);
++-    if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
+++    if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
++         return false;
+++    }
++ 
++     if (isAsync) {
++         // Create the async function wrapper.
++         JSObject* wrapped;
++         if (isGenerator) {
++             wrapped = proto
++                       ? WrapAsyncGeneratorWithProto(cx, fun, proto)
++                       : WrapAsyncGenerator(cx, fun);
++         } else {
++             // Step 9.d, use %AsyncFunctionPrototype% as the fallback prototype.
++             wrapped = proto
++                       ? WrapAsyncFunctionWithProto(cx, fun, proto)
++                       : WrapAsyncFunction(cx, fun);
++         }
++-        if (!wrapped)
+++        if (!wrapped) {
++             return false;
+++        }
++ 
++         fun = &wrapped->as<JSFunction>();
++     } else {
++         // Steps 7.d, 8.d (implicit).
++         // Call SetPrototype if an explicit prototype was given.
++-        if (proto && !SetPrototype(cx, fun, proto))
+++        if (proto && !SetPrototype(cx, fun, proto)) {
++             return false;
+++        }
++     }
++ 
++     // Step 38.
++     args.rval().setObject(*fun);
++     return true;
++ }
++ 
++ bool
++@@ -2082,42 +2249,47 @@ js::NewFunctionWithProto(JSContext* cx, 
++                          NewObjectKind newKind /* = GenericObject */)
++ {
++     MOZ_ASSERT(allocKind == gc::AllocKind::FUNCTION ||
++                allocKind == gc::AllocKind::FUNCTION_EXTENDED);
++     MOZ_ASSERT_IF(native, !enclosingEnv);
++     MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
++ 
++     JSFunction* fun = NewObjectWithClassProto<JSFunction>(cx, proto, allocKind, newKind);
++-    if (!fun)
+++    if (!fun) {
++         return nullptr;
++-
++-    if (allocKind == gc::AllocKind::FUNCTION_EXTENDED)
+++    }
+++
+++    if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
++         flags = JSFunction::Flags(flags | JSFunction::EXTENDED);
+++    }
++ 
++     /* Initialize all function members. */
++     fun->setArgCount(uint16_t(nargs));
++     fun->setFlags(flags);
++     if (fun->isInterpreted()) {
++         MOZ_ASSERT(!native);
++-        if (fun->isInterpretedLazy())
+++        if (fun->isInterpretedLazy()) {
++             fun->initLazyScript(nullptr);
++-        else
+++        } else {
++             fun->initScript(nullptr);
+++        }
++         fun->initEnvironment(enclosingEnv);
++     } else {
++         MOZ_ASSERT(fun->isNative());
++         MOZ_ASSERT(native);
++-        if (fun->isWasmOptimized())
+++        if (fun->isWasmOptimized()) {
++             fun->initWasmNative(native);
++-        else
+++        } else {
++             fun->initNative(native, nullptr);
+++        }
++     }
++-    if (allocKind == gc::AllocKind::FUNCTION_EXTENDED)
+++    if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
++         fun->initializeExtended();
+++    }
++     fun->initAtom(atom);
++ 
++     return fun;
++ }
++ 
++ bool
++ js::CanReuseScriptForClone(JS::Realm* realm, HandleFunction fun,
++                            HandleObject newParent)
++@@ -2126,76 +2298,83 @@ js::CanReuseScriptForClone(JS::Realm* re
++ 
++     if (realm != fun->realm() ||
++         fun->isSingleton() ||
++         ObjectGroup::useSingletonForClone(fun))
++     {
++         return false;
++     }
++ 
++-    if (newParent->is<GlobalObject>())
+++    if (newParent->is<GlobalObject>()) {
++         return true;
+++    }
++ 
++     // Don't need to clone the script if newParent is a syntactic scope, since
++     // in that case we have some actual scope objects on our scope chain and
++     // whatnot; whoever put them there should be responsible for setting our
++     // script's flags appropriately.  We hit this case for JSOP_LAMBDA, for
++     // example.
++-    if (IsSyntacticEnvironment(newParent))
+++    if (IsSyntacticEnvironment(newParent)) {
++         return true;
+++    }
++ 
++     // We need to clone the script if we're not already marked as having a
++     // non-syntactic scope.
++     return fun->hasScript()
++         ? fun->nonLazyScript()->hasNonSyntacticScope()
++         : fun->lazyScript()->hasNonSyntacticScope();
++ }
++ 
++ static inline JSFunction*
++ NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind,
++                  gc::AllocKind allocKind, HandleObject proto)
++ {
++     RootedObject cloneProto(cx, proto);
++     if (!proto && (fun->isGenerator() || fun->isAsync())) {
++         cloneProto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
++-        if (!cloneProto)
+++        if (!cloneProto) {
++             return nullptr;
+++        }
++     }
++ 
++     RootedFunction clone(cx);
++     clone = NewObjectWithClassProto<JSFunction>(cx, cloneProto, allocKind, newKind);
++-    if (!clone)
+++    if (!clone) {
++         return nullptr;
+++    }
++ 
++     // JSFunction::HAS_INFERRED_NAME can be set at compile-time and at
++     // runtime. In the latter case we should actually clear the flag before
++     // cloning the function, but since we can't differentiate between both
++     // cases here, we'll end up with a momentarily incorrect function name.
++     // This will be fixed up in SetFunctionNameIfNoOwnName(), which should
++     // happen through JSOP_SETFUNNAME directly after JSOP_LAMBDA.
++     constexpr uint16_t NonCloneableFlags = JSFunction::EXTENDED |
++                                            JSFunction::RESOLVED_LENGTH |
++                                            JSFunction::RESOLVED_NAME;
++ 
++     uint16_t flags = fun->flags() & ~NonCloneableFlags;
++-    if (allocKind == gc::AllocKind::FUNCTION_EXTENDED)
+++    if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
++         flags |= JSFunction::EXTENDED;
+++    }
++ 
++     clone->setArgCount(fun->nargs());
++     clone->setFlags(flags);
++ 
++     JSAtom* atom = fun->displayAtom();
++-    if (atom)
+++    if (atom) {
++         cx->markAtom(atom);
+++    }
++     clone->initAtom(atom);
++ 
++     if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
++         if (fun->isExtended() && fun->compartment() == cx->compartment()) {
++-            for (unsigned i = 0; i < FunctionExtended::NUM_EXTENDED_SLOTS; i++)
+++            for (unsigned i = 0; i < FunctionExtended::NUM_EXTENDED_SLOTS; i++) {
++                 clone->initExtendedSlot(i, fun->getExtendedSlot(i));
+++            }
++         } else {
++             clone->initializeExtended();
++         }
++     }
++ 
++     return clone;
++ }
++ 
++@@ -2206,99 +2385,106 @@ js::CloneFunctionReuseScript(JSContext* 
++                              HandleObject proto /* = nullptr */)
++ {
++     MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
++     MOZ_ASSERT(fun->isInterpreted());
++     MOZ_ASSERT(!fun->isBoundFunction());
++     MOZ_ASSERT(CanReuseScriptForClone(cx->realm(), fun, enclosingEnv));
++ 
++     RootedFunction clone(cx, NewFunctionClone(cx, fun, newKind, allocKind, proto));
++-    if (!clone)
+++    if (!clone) {
++         return nullptr;
+++    }
++ 
++     if (fun->hasScript()) {
++         clone->initScript(fun->nonLazyScript());
++         clone->initEnvironment(enclosingEnv);
++     } else {
++         MOZ_ASSERT(fun->isInterpretedLazy());
++         MOZ_ASSERT(fun->compartment() == clone->compartment());
++         LazyScript* lazy = fun->lazyScriptOrNull();
++         clone->initLazyScript(lazy);
++         clone->initEnvironment(enclosingEnv);
++     }
++ 
++     /*
++      * Clone the function, reusing its script. We can use the same group as
++      * the original function provided that its prototype is correct.
++      */
++-    if (fun->staticPrototype() == clone->staticPrototype())
+++    if (fun->staticPrototype() == clone->staticPrototype()) {
++         clone->setGroup(fun->group());
+++    }
++     return clone;
++ }
++ 
++ JSFunction*
++ js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject enclosingEnv,
++                            HandleScope newScope, gc::AllocKind allocKind /* = FUNCTION */,
++                            HandleObject proto /* = nullptr */)
++ {
++     MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
++     MOZ_ASSERT(fun->isInterpreted());
++     MOZ_ASSERT(!fun->isBoundFunction());
++ 
++     JSScript::AutoDelazify funScript(cx, fun);
++-    if (!funScript)
+++    if (!funScript) {
++         return nullptr;
+++    }
++ 
++     RootedFunction clone(cx, NewFunctionClone(cx, fun, SingletonObject, allocKind, proto));
++-    if (!clone)
+++    if (!clone) {
++         return nullptr;
+++    }
++ 
++     clone->initScript(nullptr);
++     clone->initEnvironment(enclosingEnv);
++ 
++     /*
++      * Across compartments or if we have to introduce a non-syntactic scope we
++      * have to clone the script for interpreted functions. Cross-compartment
++      * cloning only happens via JSAPI (JS::CloneFunctionObject) which
++      * dynamically ensures that 'script' has no enclosing lexical scope (only
++      * the global scope or other non-lexical scope).
++      */
++ #ifdef DEBUG
++     RootedObject terminatingEnv(cx, enclosingEnv);
++-    while (IsSyntacticEnvironment(terminatingEnv))
+++    while (IsSyntacticEnvironment(terminatingEnv)) {
++         terminatingEnv = terminatingEnv->enclosingEnvironment();
+++    }
++     MOZ_ASSERT_IF(!terminatingEnv->is<GlobalObject>(),
++                   newScope->hasOnChain(ScopeKind::NonSyntactic));
++ #endif
++ 
++     RootedScript script(cx, fun->nonLazyScript());
++     MOZ_ASSERT(script->realm() == fun->realm());
++     MOZ_ASSERT(cx->compartment() == clone->compartment(),
++                "Otherwise we could relazify clone below!");
++ 
++     RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, newScope, clone, script));
++-    if (!clonedScript)
+++    if (!clonedScript) {
++         return nullptr;
+++    }
++     Debugger::onNewScript(cx, clonedScript);
++ 
++     return clone;
++ }
++ 
++ JSFunction*
++ js::CloneAsmJSModuleFunction(JSContext* cx, HandleFunction fun)
++ {
++     MOZ_ASSERT(fun->isNative());
++     MOZ_ASSERT(IsAsmJSModule(fun));
++     MOZ_ASSERT(fun->isExtended());
++     MOZ_ASSERT(cx->compartment() == fun->compartment());
++ 
++     JSFunction* clone = NewFunctionClone(cx, fun, GenericObject, gc::AllocKind::FUNCTION_EXTENDED,
++                                          /* proto = */ nullptr);
++-    if (!clone)
+++    if (!clone) {
++         return nullptr;
+++    }
++ 
++     MOZ_ASSERT(fun->native() == InstantiateAsmJS);
++     MOZ_ASSERT(!fun->hasJitInfo());
++     clone->initNative(InstantiateAsmJS, nullptr);
++ 
++     clone->setGroup(fun->group());
++     return clone;
++ }
++@@ -2308,74 +2494,84 @@ js::CloneSelfHostingIntrinsic(JSContext*
++ {
++     MOZ_ASSERT(fun->isNative());
++     MOZ_ASSERT(fun->realm()->isSelfHostingRealm());
++     MOZ_ASSERT(!fun->isExtended());
++     MOZ_ASSERT(cx->compartment() != fun->compartment());
++ 
++     JSFunction* clone = NewFunctionClone(cx, fun, SingletonObject, gc::AllocKind::FUNCTION,
++                                          /* proto = */ nullptr);
++-    if (!clone)
+++    if (!clone) {
++         return nullptr;
+++    }
++ 
++     clone->initNative(fun->native(), fun->hasJitInfo() ? fun->jitInfo() : nullptr);
++     return clone;
++ }
++ 
++ static JSAtom*
++ SymbolToFunctionName(JSContext* cx, JS::Symbol* symbol, FunctionPrefixKind prefixKind)
++ {
++     // Step 4.a.
++     JSAtom* desc = symbol->description();
++ 
++     // Step 4.b, no prefix fastpath.
++-    if (!desc && prefixKind == FunctionPrefixKind::None)
+++    if (!desc && prefixKind == FunctionPrefixKind::None) {
++         return cx->names().empty;
+++    }
++ 
++     // Step 5 (reordered).
++     StringBuffer sb(cx);
++     if (prefixKind == FunctionPrefixKind::Get) {
++-        if (!sb.append("get "))
+++        if (!sb.append("get ")) {
++             return nullptr;
+++        }
++     } else if (prefixKind == FunctionPrefixKind::Set) {
++-        if (!sb.append("set "))
+++        if (!sb.append("set ")) {
++             return nullptr;
+++        }
++     }
++ 
++     // Step 4.b.
++     if (desc) {
++         // Step 4.c.
++-        if (!sb.append('[') || !sb.append(desc) || !sb.append(']'))
+++        if (!sb.append('[') || !sb.append(desc) || !sb.append(']')) {
++             return nullptr;
+++        }
++     }
++     return sb.finishAtom();
++ }
++ 
++ static JSAtom*
++ NameToFunctionName(JSContext* cx, HandleValue name, FunctionPrefixKind prefixKind)
++ {
++     MOZ_ASSERT(name.isString() || name.isNumber());
++ 
++-    if (prefixKind == FunctionPrefixKind::None)
+++    if (prefixKind == FunctionPrefixKind::None) {
++         return ToAtom<CanGC>(cx, name);
+++    }
++ 
++     JSString* nameStr = ToString(cx, name);
++-    if (!nameStr)
+++    if (!nameStr) {
++         return nullptr;
+++    }
++ 
++     StringBuffer sb(cx);
++     if (prefixKind == FunctionPrefixKind::Get) {
++-        if (!sb.append("get "))
+++        if (!sb.append("get ")) {
++             return nullptr;
+++        }
++     } else {
++-        if (!sb.append("set "))
+++        if (!sb.append("set ")) {
++             return nullptr;
+++        }
++     }
++-    if (!sb.append(nameStr))
+++    if (!sb.append(nameStr)) {
++         return nullptr;
+++    }
++     return sb.finishAtom();
++ }
++ 
++ /*
++  * Return an atom for use as the name of a builtin method with the given
++  * property id.
++  *
++  * Function names are always strings. If id is the well-known @@iterator
++@@ -2446,36 +2646,40 @@ js::SetFunctionNameIfNoOwnName(JSContext
++     return true;
++ }
++ 
++ JSFunction*
++ js::DefineFunction(JSContext* cx, HandleObject obj, HandleId id, Native native,
++                    unsigned nargs, unsigned flags, gc::AllocKind allocKind /* = AllocKind::FUNCTION */)
++ {
++     RootedAtom atom(cx, IdToFunctionName(cx, id));
++-    if (!atom)
+++    if (!atom) {
++         return nullptr;
+++    }
++ 
++     RootedFunction fun(cx);
++-    if (!native)
+++    if (!native) {
++         fun = NewScriptedFunction(cx, nargs,
++                                   JSFunction::INTERPRETED_LAZY, atom,
++                                   /* proto = */ nullptr,
++                                   allocKind, GenericObject, obj);
++-    else if (flags & JSFUN_CONSTRUCTOR)
+++    } else if (flags & JSFUN_CONSTRUCTOR) {
++         fun = NewNativeConstructor(cx, native, nargs, atom, allocKind);
++-    else
+++    } else {
++         fun = NewNativeFunction(cx, native, nargs, atom, allocKind);
++-
++-    if (!fun)
+++    }
+++
+++    if (!fun) {
++         return nullptr;
+++    }
++ 
++     RootedValue funVal(cx, ObjectValue(*fun));
++-    if (!DefineDataProperty(cx, obj, id, funVal, flags & ~JSFUN_FLAGS_MASK))
+++    if (!DefineDataProperty(cx, obj, id, funVal, flags & ~JSFUN_FLAGS_MASK)) {
++         return nullptr;
+++    }
++ 
++     return fun;
++ }
++ 
++ void
++ js::ReportIncompatibleMethod(JSContext* cx, const CallArgs& args, const Class* clasp)
++ {
++     RootedValue thisv(cx, args.thisv());
+diff --git a/js/src/vm/JSFunction.h b/js/src/vm/JSFunction.h
+--- a/js/src/vm/JSFunction.h
++++ b/js/src/vm/JSFunction.h
+@@ -187,18 +187,19 @@ class JSFunction : public js::NativeObje
+     static inline JS::Result<JSFunction*, JS::OOM&>
+     create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
+            js::HandleShape shape, js::HandleObjectGroup group);
+ 
+     /* Call objects must be created for each invocation of this function. */
+     bool needsCallObject() const {
+         MOZ_ASSERT(!isInterpretedLazy());
+ 
+-        if (isNative())
++        if (isNative()) {
+             return false;
++        }
+ 
+         // Note: this should be kept in sync with
+         // FunctionBox::needsCallObjectRegardlessOfBindings().
+         MOZ_ASSERT_IF(nonLazyScript()->funHasExtensibleScope() ||
+                       nonLazyScript()->needsHomeObject()       ||
+                       nonLazyScript()->isDerivedClassConstructor() ||
+                       isGenerator() ||
+                       isAsync(),
+@@ -282,18 +283,19 @@ class JSFunction : public js::NativeObje
+     bool hasResolvedLength()        const { return flags() & RESOLVED_LENGTH; }
+     bool hasResolvedName()          const { return flags() & RESOLVED_NAME; }
+ 
+     bool isSelfHostedOrIntrinsic()  const { return flags() & SELF_HOSTED; }
+     bool isSelfHostedBuiltin()      const { return isSelfHostedOrIntrinsic() && !isNative(); }
+     bool isIntrinsic()              const { return isSelfHostedOrIntrinsic() && isNative(); }
+ 
+     bool hasJITCode() const {
+-        if (!hasScript())
++        if (!hasScript()) {
+             return false;
++        }
+ 
+         return nonLazyScript()->hasBaselineScript() || nonLazyScript()->hasIonScript();
+     }
+     bool hasJitEntry() const {
+         return hasScript() || isNativeWithJitEntry();
+     }
+ 
+     /* Compound attributes: */
+@@ -379,20 +381,21 @@ class JSFunction : public js::NativeObje
+     bool wasNewScriptCleared() const {
+         return flags_ & NEW_SCRIPT_CLEARED;
+     }
+     void setNewScriptCleared() {
+         flags_ |= NEW_SCRIPT_CLEARED;
+     }
+ 
+     void setAsyncKind(js::FunctionAsyncKind asyncKind) {
+-        if (isInterpretedLazy())
++        if (isInterpretedLazy()) {
+             lazyScript()->setAsyncKind(asyncKind);
+-        else
++        } else {
+             nonLazyScript()->setAsyncKind(asyncKind);
++        }
+     }
+ 
+     static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun,
+                                     js::MutableHandleValue v);
+ 
+     JSAtom* infallibleGetUnresolvedName(JSContext* cx);
+ 
+     static bool getUnresolvedName(JSContext* cx, js::HandleFunction fun,
+@@ -520,18 +523,19 @@ class JSFunction : public js::NativeObje
+     //   use existingScriptNonDelazifying().
+     //
+     // - For functions known to have a JSScript, nonLazyScript() will get it.
+ 
+     static JSScript* getOrCreateScript(JSContext* cx, js::HandleFunction fun) {
+         MOZ_ASSERT(fun->isInterpreted());
+         MOZ_ASSERT(cx);
+         if (fun->isInterpretedLazy()) {
+-            if (!createScriptForLazilyInterpretedFunction(cx, fun))
++            if (!createScriptForLazilyInterpretedFunction(cx, fun)) {
+                 return nullptr;
++            }
+             return fun->nonLazyScript();
+         }
+         return fun->nonLazyScript();
+     }
+ 
+     JSScript* existingScriptNonDelazifying() const {
+         MOZ_ASSERT(isInterpreted());
+         if (isInterpretedLazy()) {
+@@ -546,18 +550,19 @@ class JSFunction : public js::NativeObje
+             return fun->nonLazyScript();
+         }
+         return nonLazyScript();
+     }
+ 
+     JSScript* existingScript() {
+         MOZ_ASSERT(isInterpreted());
+         if (isInterpretedLazy()) {
+-            if (shadowZone()->needsIncrementalBarrier())
++            if (shadowZone()->needsIncrementalBarrier()) {
+                 js::LazyScript::writeBarrierPre(lazyScript());
++            }
+             JSScript* script = existingScriptNonDelazifying();
+             flags_ &= ~INTERPRETED_LAZY;
+             flags_ |= INTERPRETED;
+             initScript(script);
+         }
+         return nonLazyScript();
+     }
+ 
+@@ -585,55 +590,61 @@ class JSFunction : public js::NativeObje
+     }
+ 
+     js::LazyScript* lazyScriptOrNull() const {
+         MOZ_ASSERT(isInterpretedLazy());
+         return u.scripted.s.lazy_;
+     }
+ 
+     js::GeneratorKind generatorKind() const {
+-        if (!isInterpreted())
++        if (!isInterpreted()) {
+             return js::GeneratorKind::NotGenerator;
+-        if (hasScript())
++        }
++        if (hasScript()) {
+             return nonLazyScript()->generatorKind();
+-        if (js::LazyScript* lazy = lazyScriptOrNull())
++        }
++        if (js::LazyScript* lazy = lazyScriptOrNull()) {
+             return lazy->generatorKind();
++        }
+         MOZ_ASSERT(isSelfHostedBuiltin());
+         return js::GeneratorKind::NotGenerator;
+     }
+ 
+     bool isGenerator() const { return generatorKind() == js::GeneratorKind::Generator; }
+ 
+     js::FunctionAsyncKind asyncKind() const {
+         return isInterpretedLazy() ? lazyScript()->asyncKind() : nonLazyScript()->asyncKind();
+     }
+ 
+     bool isAsync() const {
+-        if (isInterpretedLazy())
++        if (isInterpretedLazy()) {
+             return lazyScript()->isAsync();
+-        if (hasScript())
++        }
++        if (hasScript()) {
+             return nonLazyScript()->isAsync();
++        }
+         return false;
+     }
+ 
+     void setScript(JSScript* script_) {
+         mutableScript() = script_;
+     }
+ 
+     void initScript(JSScript* script_) {
+         mutableScript().init(script_);
+     }
+ 
+     void setUnlazifiedScript(JSScript* script) {
+         MOZ_ASSERT(isInterpretedLazy());
+         if (lazyScriptOrNull()) {
+             // Trigger a pre barrier on the lazy script being overwritten.
+             js::LazyScript::writeBarrierPre(lazyScriptOrNull());
+-            if (!lazyScript()->maybeScript())
++            if (!lazyScript()->maybeScript()) {
+                 lazyScript()->initScript(script);
++            }
+         }
+         flags_ &= ~INTERPRETED_LAZY;
+         flags_ |= INTERPRETED;
+         initScript(script);
+     }
+ 
+     void initLazyScript(js::LazyScript* lazy) {
+         MOZ_ASSERT(isInterpreted());
+@@ -777,18 +788,19 @@ class JSFunction : public js::NativeObje
+ 
+     /* GC support. */
+     js::gc::AllocKind getAllocKind() const {
+         static_assert(js::gc::AllocKind::FUNCTION != js::gc::AllocKind::FUNCTION_EXTENDED,
+                       "extended/non-extended AllocKinds have to be different "
+                       "for getAllocKind() to have a reason to exist");
+ 
+         js::gc::AllocKind kind = js::gc::AllocKind::FUNCTION;
+-        if (isExtended())
++        if (isExtended()) {
+             kind = js::gc::AllocKind::FUNCTION_EXTENDED;
++        }
+         MOZ_ASSERT_IF(isTenured(), kind == asTenured().getAllocKind());
+         return kind;
+     }
+ };
+ 
+ static_assert(sizeof(JSFunction) == sizeof(js::shadow::Function),
+               "shadow interface must match actual interface");
+ 
+diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp
+--- a/js/src/vm/JSONParser.cpp
++++ b/js/src/vm/JSONParser.cpp
+@@ -132,61 +132,67 @@ JSONParser<CharT>::readString()
+     CharPtr start = current;
+     for (; current < end; current++) {
+         if (*current == '"') {
+             size_t length = current - start;
+             current++;
+             JSFlatString* str = (ST == JSONParser::PropertyName)
+                                 ? AtomizeChars(cx, start.get(), length)
+                                 : NewStringCopyN<CanGC>(cx, start.get(), length);
+-            if (!str)
++            if (!str) {
+                 return token(OOM);
++            }
+             return stringToken(str);
+         }
+ 
+-        if (*current == '\\')
++        if (*current == '\\') {
+             break;
++        }
+ 
+         if (*current <= 0x001F) {
+             error("bad control character in string literal");
+             return token(Error);
+         }
+     }
+ 
+     /*
+      * Slow case: string contains escaped characters.  Copy a maximal sequence
+      * of unescaped characters into a temporary buffer, then an escaped
+      * character, and repeat until the entire string is consumed.
+      */
+     StringBuffer buffer(cx);
+     do {
+-        if (start < current && !buffer.append(start.get(), current.get()))
++        if (start < current && !buffer.append(start.get(), current.get())) {
+             return token(OOM);
++        }
+ 
+-        if (current >= end)
++        if (current >= end) {
+             break;
++        }
+ 
+         char16_t c = *current++;
+         if (c == '"') {
+             JSFlatString* str = (ST == JSONParser::PropertyName)
+                                 ? buffer.finishAtom()
+                                 : buffer.finishString();
+-            if (!str)
++            if (!str) {
+                 return token(OOM);
++            }
+             return stringToken(str);
+         }
+ 
+         if (c != '\\') {
+             --current;
+             error("bad character in string literal");
+             return token(Error);
+         }
+ 
+-        if (current >= end)
++        if (current >= end) {
+             break;
++        }
+ 
+         switch (*current++) {
+           case '"':  c = '"';  break;
+           case '/':  c = '/';  break;
+           case '\\': c = '\\'; break;
+           case 'b':  c = '\b'; break;
+           case 'f':  c = '\f'; break;
+           case 'n':  c = '\n'; break;
+@@ -197,49 +203,52 @@ JSONParser<CharT>::readString()
+             if (end - current < 4 ||
+                 !(JS7_ISHEX(current[0]) &&
+                   JS7_ISHEX(current[1]) &&
+                   JS7_ISHEX(current[2]) &&
+                   JS7_ISHEX(current[3])))
+             {
+                 // Point to the first non-hexadecimal character (which may be
+                 // missing).
+-                if (current == end || !JS7_ISHEX(current[0]))
++                if (current == end || !JS7_ISHEX(current[0])) {
+                     ; // already at correct location
+-                else if (current + 1 == end || !JS7_ISHEX(current[1]))
++                } else if (current + 1 == end || !JS7_ISHEX(current[1])) {
+                     current += 1;
+-                else if (current + 2 == end || !JS7_ISHEX(current[2]))
++                } else if (current + 2 == end || !JS7_ISHEX(current[2])) {
+                     current += 2;
+-                else if (current + 3 == end || !JS7_ISHEX(current[3]))
++                } else if (current + 3 == end || !JS7_ISHEX(current[3])) {
+                     current += 3;
+-                else
++                } else {
+                     MOZ_CRASH("logic error determining first erroneous character");
++                }
+ 
+                 error("bad Unicode escape");
+                 return token(Error);
+             }
+             c = (JS7_UNHEX(current[0]) << 12)
+               | (JS7_UNHEX(current[1]) << 8)
+               | (JS7_UNHEX(current[2]) << 4)
+               | (JS7_UNHEX(current[3]));
+             current += 4;
+             break;
+ 
+           default:
+             current--;
+             error("bad escaped character");
+             return token(Error);
+         }
+-        if (!buffer.append(c))
++        if (!buffer.append(c)) {
+             return token(OOM);
++        }
+ 
+         start = current;
+         for (; current < end; current++) {
+-            if (*current == '"' || *current == '\\' || *current <= 0x001F)
++            if (*current == '"' || *current == '\\' || *current <= 0x001F) {
+                 break;
++            }
+         }
+     } while (current < end);
+ 
+     error("unterminated string");
+     return token(Error);
+ }
+ 
+ template <typename CharT>
+@@ -266,52 +275,55 @@ JSONParser<CharT>::readNumber()
+ 
+     /* 0|[1-9][0-9]+ */
+     if (!IsAsciiDigit(*current)) {
+         error("unexpected non-digit");
+         return token(Error);
+     }
+     if (*current++ != '0') {
+         for (; current < end; current++) {
+-            if (!IsAsciiDigit(*current))
++            if (!IsAsciiDigit(*current)) {
+                 break;
++            }
+         }
+     }
+ 
+     /* Fast path: no fractional or exponent part. */
+     if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) {
+         mozilla::Range<const CharT> chars(digitStart.get(), current - digitStart);
+         if (chars.length() < strlen("9007199254740992")) {
+             // If the decimal number is shorter than the length of 2**53, (the
+             // largest number a double can represent with integral precision),
+             // parse it using a decimal-only parser.  This comparison is
+             // conservative but faster than a fully-precise check.
+             double d = ParseDecimalNumber(chars);
+             return numberToken(negative ? -d : d);
+         }
+ 
+         double d;
+-        if (!GetFullInteger(cx, digitStart.get(), current.get(), 10, &d))
++        if (!GetFullInteger(cx, digitStart.get(), current.get(), 10, &d)) {
+             return token(OOM);
++        }
+         return numberToken(negative ? -d : d);
+     }
+ 
+     /* (\.[0-9]+)? */
+     if (current < end && *current == '.') {
+         if (++current == end) {
+             error("missing digits after decimal point");
+             return token(Error);
+         }
+         if (!IsAsciiDigit(*current)) {
+             error("unterminated fractional number");
+             return token(Error);
+         }
+         while (++current < end) {
+-            if (!IsAsciiDigit(*current))
++            if (!IsAsciiDigit(*current)) {
+                 break;
++            }
+         }
+     }
+ 
+     /* ([eE][\+\-]?[0-9]+)? */
+     if (current < end && (*current == 'e' || *current == 'E')) {
+         if (++current == end) {
+             error("missing digits after exponent indicator");
+             return token(Error);
+@@ -322,39 +334,42 @@ JSONParser<CharT>::readNumber()
+                 return token(Error);
+             }
+         }
+         if (!IsAsciiDigit(*current)) {
+             error("exponent part is missing a number");
+             return token(Error);
+         }
+         while (++current < end) {
+-            if (!IsAsciiDigit(*current))
++            if (!IsAsciiDigit(*current)) {
+                 break;
++            }
+         }
+     }
+ 
+     double d;
+-    if (!FullStringToDouble(cx, digitStart.get(), current.get(), &d))
++    if (!FullStringToDouble(cx, digitStart.get(), current.get(), &d)) {
+         return token(OOM);
++    }
+     return numberToken(negative ? -d : d);
+ }
+ 
+ static inline bool
+ IsJSONWhitespace(char16_t c)
+ {
+     return c == '\t' || c == '\r' || c == '\n' || c == ' ';
+ }
+ 
+ template <typename CharT>
+ JSONParserBase::Token
+ JSONParser<CharT>::advance()
+ {
+-    while (current < end && IsJSONWhitespace(*current))
++    while (current < end && IsJSONWhitespace(*current)) {
+         current++;
++    }
+     if (current >= end) {
+         error("unexpected end of data");
+         return token(Error);
+     }
+ 
+     switch (*current) {
+       case '"':
+         return readString<LiteralValue>();
+@@ -427,25 +442,27 @@ JSONParser<CharT>::advance()
+ }
+ 
+ template <typename CharT>
+ JSONParserBase::Token
+ JSONParser<CharT>::advanceAfterObjectOpen()
+ {
+     MOZ_ASSERT(current[-1] == '{');
+ 
+-    while (current < end && IsJSONWhitespace(*current))
++    while (current < end && IsJSONWhitespace(*current)) {
+         current++;
++    }
+     if (current >= end) {
+         error("end of data while reading object contents");
+         return token(Error);
+     }
+ 
+-    if (*current == '"')
++    if (*current == '"') {
+         return readString<PropertyName>();
++    }
+ 
+     if (*current == '}') {
+         current++;
+         return token(ObjectClose);
+     }
+ 
+     error("expected property name or '}'");
+     return token(Error);
+@@ -480,18 +497,19 @@ AssertPastValue(const RangedPtr<const Ch
+ }
+ 
+ template <typename CharT>
+ JSONParserBase::Token
+ JSONParser<CharT>::advanceAfterArrayElement()
+ {
+     AssertPastValue(current);
+ 
+-    while (current < end && IsJSONWhitespace(*current))
++    while (current < end && IsJSONWhitespace(*current)) {
+         current++;
++    }
+     if (current >= end) {
+         error("end of data when ',' or ']' was expected");
+         return token(Error);
+     }
+ 
+     if (*current == ',') {
+         current++;
+         return token(Comma);
+@@ -507,38 +525,41 @@ JSONParser<CharT>::advanceAfterArrayElem
+ }
+ 
+ template <typename CharT>
+ JSONParserBase::Token
+ JSONParser<CharT>::advancePropertyName()
+ {
+     MOZ_ASSERT(current[-1] == ',');
+ 
+-    while (current < end && IsJSONWhitespace(*current))
++    while (current < end && IsJSONWhitespace(*current)) {
+         current++;
++    }
+     if (current >= end) {
+         error("end of data when property name was expected");
+         return token(Error);
+     }
+ 
+-    if (*current == '"')
++    if (*current == '"') {
+         return readString<PropertyName>();
++    }
+ 
+     error("expected double-quoted property name");
+     return token(Error);
+ }
+ 
+ template <typename CharT>
+ JSONParserBase::Token
+ JSONParser<CharT>::advancePropertyColon()
+ {
+     MOZ_ASSERT(current[-1] == '"');
+ 
+-    while (current < end && IsJSONWhitespace(*current))
++    while (current < end && IsJSONWhitespace(*current)) {
+         current++;
++    }
+     if (current >= end) {
+         error("end of data after property name when ':' was expected");
+         return token(Error);
+     }
+ 
+     if (*current == ':') {
+         current++;
+         return token(Colon);
+@@ -549,18 +570,19 @@ JSONParser<CharT>::advancePropertyColon(
+ }
+ 
+ template <typename CharT>
+ JSONParserBase::Token
+ JSONParser<CharT>::advanceAfterProperty()
+ {
+     AssertPastValue(current);
+ 
+-    while (current < end && IsJSONWhitespace(*current))
++    while (current < end && IsJSONWhitespace(*current)) {
+         current++;
++    }
+     if (current >= end) {
+         error("end of data after property value in object");
+         return token(Error);
+     }
+ 
+     if (*current == ',') {
+         current++;
+         return token(Comma);
+@@ -576,52 +598,58 @@ JSONParser<CharT>::advanceAfterProperty(
+ }
+ 
+ inline bool
+ JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector& properties)
+ {
+     MOZ_ASSERT(&properties == &stack.back().properties());
+ 
+     JSObject* obj = ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), GenericObject);
+-    if (!obj)
++    if (!obj) {
+         return false;
++    }
+ 
+     vp.setObject(*obj);
+-    if (!freeProperties.append(&properties))
++    if (!freeProperties.append(&properties)) {
+         return false;
++    }
+     stack.popBack();
+ 
+     if (!stack.empty() && stack.back().state == FinishArrayElement) {
+         const ElementVector& elements = stack.back().elements();
+-        if (!CombinePlainObjectPropertyTypes(cx, obj, elements.begin(), elements.length()))
++        if (!CombinePlainObjectPropertyTypes(cx, obj, elements.begin(), elements.length())) {
+             return false;
++        }
+     }
+ 
+     return true;
+ }
+ 
+ inline bool
+ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements)
+ {
+     MOZ_ASSERT(&elements == &stack.back().elements());
+ 
+     ArrayObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(),
+                                                    GenericObject);
+-    if (!obj)
++    if (!obj) {
+         return false;
++    }
+ 
+     vp.setObject(*obj);
+-    if (!freeElements.append(&elements))
++    if (!freeElements.append(&elements)) {
+         return false;
++    }
+     stack.popBack();
+ 
+     if (!stack.empty() && stack.back().state == FinishArrayElement) {
+         const ElementVector& elements = stack.back().elements();
+-        if (!CombineArrayElementTypes(cx, obj, elements.begin(), elements.length()))
++        if (!CombineArrayElementTypes(cx, obj, elements.begin(), elements.length())) {
+             return false;
++        }
+     }
+ 
+     return true;
+ }
+ 
+ template <typename CharT>
+ bool
+ JSONParser<CharT>::parse(MutableHandleValue vp)
+@@ -636,60 +664,69 @@ JSONParser<CharT>::parse(MutableHandleVa
+     while (true) {
+         switch (state) {
+           case FinishObjectMember: {
+             PropertyVector& properties = stack.back().properties();
+             properties.back().value = value;
+ 
+             token = advanceAfterProperty();
+             if (token == ObjectClose) {
+-                if (!finishObject(&value, properties))
++                if (!finishObject(&value, properties)) {
+                     return false;
++                }
+                 break;
+             }
+             if (token != Comma) {
+-                if (token == OOM)
++                if (token == OOM) {
+                     return false;
+-                if (token != Error)
++                }
++                if (token != Error) {
+                     error("expected ',' or '}' after property-value pair in object literal");
++                }
+                 return errorReturn();
+             }
+             token = advancePropertyName();
+             /* FALL THROUGH */
+           }
+ 
+           JSONMember:
+             if (token == String) {
+                 jsid id = AtomToId(atomValue());
+                 PropertyVector& properties = stack.back().properties();
+-                if (!properties.append(IdValuePair(id)))
++                if (!properties.append(IdValuePair(id))) {
+                     return false;
++                }
+                 token = advancePropertyColon();
+                 if (token != Colon) {
+                     MOZ_ASSERT(token == Error);
+                     return errorReturn();
+                 }
+                 goto JSONValue;
+             }
+-            if (token == OOM)
++            if (token == OOM) {
+                 return false;
+-            if (token != Error)
++            }
++            if (token != Error) {
+                 error("property names must be double-quoted strings");
++            }
+             return errorReturn();
+ 
+           case FinishArrayElement: {
+             ElementVector& elements = stack.back().elements();
+-            if (!elements.append(value.get()))
++            if (!elements.append(value.get())) {
+                 return false;
++            }
+             token = advanceAfterArrayElement();
+-            if (token == Comma)
++            if (token == Comma) {
+                 goto JSONValue;
++            }
+             if (token == ArrayClose) {
+-                if (!finishArray(&value, elements))
++                if (!finishArray(&value, elements)) {
+                     return false;
++                }
+                 break;
+             }
+             MOZ_ASSERT(token == Error);
+             return errorReturn();
+           }
+ 
+           JSONValue:
+           case JSONValue:
+@@ -714,52 +751,56 @@ JSONParser<CharT>::parse(MutableHandleVa
+ 
+               case ArrayOpen: {
+                 ElementVector* elements;
+                 if (!freeElements.empty()) {
+                     elements = freeElements.popCopy();
+                     elements->clear();
+                 } else {
+                     elements = cx->new_<ElementVector>(cx);
+-                    if (!elements)
++                    if (!elements) {
+                         return false;
++                    }
+                 }
+                 if (!stack.append(elements)) {
+                     js_delete(elements);
+                     return false;
+                 }
+ 
+                 token = advance();
+                 if (token == ArrayClose) {
+-                    if (!finishArray(&value, *elements))
++                    if (!finishArray(&value, *elements)) {
+                         return false;
++                    }
+                     break;
+                 }
+                 goto JSONValueSwitch;
+               }
+ 
+               case ObjectOpen: {
+                 PropertyVector* properties;
+                 if (!freeProperties.empty()) {
+                     properties = freeProperties.popCopy();
+                     properties->clear();
+                 } else {
+                     properties = cx->new_<PropertyVector>(cx);
+-                    if (!properties)
++                    if (!properties) {
+                         return false;
++                    }
+                 }
+                 if (!stack.append(properties)) {
+                     js_delete(properties);
+                     return false;
+                 }
+ 
+                 token = advanceAfterObjectOpen();
+                 if (token == ObjectClose) {
+-                    if (!finishObject(&value, *properties))
++                    if (!finishObject(&value, *properties)) {
+                         return false;
++                    }
+                     break;
+                 }
+                 goto JSONMember;
+               }
+ 
+               case ArrayClose:
+               case ObjectClose:
+               case Colon:
+@@ -774,18 +815,19 @@ JSONParser<CharT>::parse(MutableHandleVa
+                 return false;
+ 
+               case Error:
+                 return errorReturn();
+             }
+             break;
+         }
+ 
+-        if (stack.empty())
++        if (stack.empty()) {
+             break;
++        }
+         state = stack.back().state;
+     }
+ 
+     for (; current < end; current++) {
+         if (!IsJSONWhitespace(*current)) {
+             error("unexpected non-whitespace character after JSON data");
+             return errorReturn();
+         }
+diff --git a/js/src/vm/JSONParser.cpp.1488698-7.later b/js/src/vm/JSONParser.cpp.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSONParser.cpp.1488698-7.later
+@@ -0,0 +1,69 @@
++--- JSONParser.cpp
+++++ JSONParser.cpp
++@@ -24,54 +24,59 @@
++ using namespace js;
++ 
++ using mozilla::IsAsciiDigit;
++ using mozilla::RangedPtr;
++ 
++ JSONParserBase::~JSONParserBase()
++ {
++     for (size_t i = 0; i < stack.length(); i++) {
++-        if (stack[i].state == FinishArrayElement)
+++        if (stack[i].state == FinishArrayElement) {
++             js_delete(&stack[i].elements());
++-        else
+++        } else {
++             js_delete(&stack[i].properties());
+++        }
++     }
++ 
++-    for (size_t i = 0; i < freeElements.length(); i++)
+++    for (size_t i = 0; i < freeElements.length(); i++) {
++         js_delete(freeElements[i]);
+++    }
++ 
++-    for (size_t i = 0; i < freeProperties.length(); i++)
+++    for (size_t i = 0; i < freeProperties.length(); i++) {
++         js_delete(freeProperties[i]);
+++    }
++ }
++ 
++ void
++ JSONParserBase::trace(JSTracer* trc)
++ {
++     for (auto& elem : stack) {
++-        if (elem.state == FinishArrayElement)
+++        if (elem.state == FinishArrayElement) {
++             elem.elements().trace(trc);
++-        else
+++        } else {
++             elem.properties().trace(trc);
+++        }
++     }
++ }
++ 
++ template <typename CharT>
++ void
++ JSONParser<CharT>::getTextPosition(uint32_t* column, uint32_t* line)
++ {
++     CharPtr ptr = begin;
++     uint32_t col = 1;
++     uint32_t row = 1;
++     for (; ptr < current; ptr++) {
++         if (*ptr == '\n' || *ptr == '\r') {
++             ++row;
++             col = 1;
++             // \r\n is treated as a single newline.
++-            if (ptr + 1 < current && *ptr == '\r' && *(ptr + 1) == '\n')
+++            if (ptr + 1 < current && *ptr == '\r' && *(ptr + 1) == '\n') {
++                 ++ptr;
+++            }
++         } else {
++             ++col;
++         }
++     }
++     *column = col;
++     *line = row;
++ }
++ 
+diff --git a/js/src/vm/JSONPrinter.cpp b/js/src/vm/JSONPrinter.cpp
+--- a/js/src/vm/JSONPrinter.cpp
++++ b/js/src/vm/JSONPrinter.cpp
+@@ -13,34 +13,37 @@
+ #include <stdarg.h>
+ 
+ #include "util/DoubleToString.h"
+ 
+ using namespace js;
+ 
+ JSONPrinter::~JSONPrinter()
+ {
+-    if (dtoaState_)
++    if (dtoaState_) {
+         DestroyDtoaState(dtoaState_);
++    }
+ }
+ 
+ void
+ JSONPrinter::indent()
+ {
+     MOZ_ASSERT(indentLevel_ >= 0);
+     out_.printf("\n");
+-    for (int i = 0; i < indentLevel_; i++)
++    for (int i = 0; i < indentLevel_; i++) {
+         out_.printf("  ");
++    }
+ }
+ 
+ void
+ JSONPrinter::propertyName(const char* name)
+ {
+-    if (!first_)
++    if (!first_) {
+         out_.printf(",");
++    }
+     indent();
+     out_.printf("\"%s\":", name);
+     first_ = false;
+ }
+ 
+ void
+ JSONPrinter::beginObject()
+ {
+@@ -105,18 +108,19 @@ JSONPrinter::formatProperty(const char* 
+ }
+ 
+ void
+ JSONPrinter::value(const char* format, ...)
+ {
+     va_list ap;
+     va_start(ap, format);
+ 
+-    if (!first_)
++    if (!first_) {
+         out_.printf(",");
++    }
+     out_.printf("\"");
+     out_.vprintf(format, ap);
+     out_.printf("\"");
+ 
+     va_end(ap);
+     first_ = false;
+ }
+ 
+@@ -125,18 +129,19 @@ JSONPrinter::property(const char* name, 
+ {
+     propertyName(name);
+     out_.printf("%" PRId32, value);
+ }
+ 
+ void
+ JSONPrinter::value(int val)
+ {
+-    if (!first_)
++    if (!first_) {
+         out_.printf(",");
++    }
+     out_.printf("%d", val);
+     first_ = false;
+ }
+ 
+ void
+ JSONPrinter::property(const char* name, uint32_t value)
+ {
+     propertyName(name);
+diff --git a/js/src/vm/JSObject-inl.h b/js/src/vm/JSObject-inl.h
+--- a/js/src/vm/JSObject-inl.h
++++ b/js/src/vm/JSObject-inl.h
+@@ -34,18 +34,19 @@
+ #include "vm/TypeInference-inl.h"
+ 
+ namespace js {
+ 
+ // This is needed here for ensureShape() below.
+ inline bool
+ MaybeConvertUnboxedObjectToNative(JSContext* cx, JSObject* obj)
+ {
+-    if (obj->is<UnboxedPlainObject>())
++    if (obj->is<UnboxedPlainObject>()) {
+         return UnboxedPlainObject::convertToNative(cx, obj);
++    }
+     return true;
+ }
+ 
+ static MOZ_ALWAYS_INLINE bool
+ ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
+ {
+     MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
+ 
+@@ -54,39 +55,42 @@ ClassMayResolveId(const JSAtomState& nam
+         // resolve hook.
+         MOZ_ASSERT(!clasp->getMayResolve(), "Class with mayResolve hook but no resolve hook");
+         return false;
+     }
+ 
+     if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
+         // Tell the analysis our mayResolve hooks won't trigger GC.
+         JS::AutoSuppressGCAnalysis nogc;
+-        if (!mayResolve(names, id, maybeObj))
++        if (!mayResolve(names, id, maybeObj)) {
+             return false;
++        }
+     }
+ 
+     return true;
+ }
+ 
+ } // namespace js
+ 
+ inline js::Shape*
+ JSObject::maybeShape() const
+ {
+-    if (!is<js::ShapedObject>())
++    if (!is<js::ShapedObject>()) {
+         return nullptr;
++    }
+ 
+     return as<js::ShapedObject>().shape();
+ }
+ 
+ inline js::Shape*
+ JSObject::ensureShape(JSContext* cx)
+ {
+-    if (!js::MaybeConvertUnboxedObjectToNative(cx, this))
++    if (!js::MaybeConvertUnboxedObjectToNative(cx, this)) {
+         return nullptr;
++    }
+     js::Shape* shape = maybeShape();
+     MOZ_ASSERT(shape);
+     return shape;
+ }
+ 
+ inline void
+ JSObject::finalize(js::FreeOp* fop)
+ {
+@@ -97,26 +101,30 @@ JSObject::finalize(js::FreeOp* fop)
+     if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
+         /* Assert we're on the main thread. */
+         MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
+     }
+ #endif
+ 
+     const js::Class* clasp = getClass();
+     js::NativeObject* nobj = nullptr;
+-    if (clasp->isNative())
++    if (clasp->isNative()) {
+         nobj = &as<js::NativeObject>();
+-    if (clasp->hasFinalize())
++    }
++    if (clasp->hasFinalize()) {
+         clasp->doFinalize(fop, this);
++    }
+ 
+-    if (!nobj)
++    if (!nobj) {
+         return;
++    }
+ 
+-    if (nobj->hasDynamicSlots())
++    if (nobj->hasDynamicSlots()) {
+         fop->free_(nobj->slots_);
++    }
+ 
+     if (nobj->hasDynamicElements()) {
+         js::ObjectElements* elements = nobj->getElementsHeader();
+         if (elements->isCopyOnWrite()) {
+             if (elements->ownerObject() == this) {
+                 // Don't free the elements until object finalization finishes,
+                 // so that other objects can access these elements while they
+                 // are themselves finalized.
+@@ -133,52 +141,56 @@ JSObject::finalize(js::FreeOp* fop)
+ 
+ MOZ_ALWAYS_INLINE void
+ js::NativeObject::sweepDictionaryListPointer()
+ {
+     // For dictionary objects (which must be native), it's possible that
+     // unreachable shapes may be marked whose listp points into this object.  In
+     // case this happens, null out the shape's pointer so that a moving GC will
+     // not try to access the dead object.
+-    if (shape()->listp == shapePtr())
++    if (shape()->listp == shapePtr()) {
+         shape()->listp = nullptr;
++    }
+ }
+ 
+ MOZ_ALWAYS_INLINE void
+ js::NativeObject::updateDictionaryListPointerAfterMinorGC(NativeObject* old)
+ {
+     MOZ_ASSERT(this == Forwarded(old));
+ 
+     // Dictionary objects can be allocated in the nursery and when they are
+     // tenured the shape's pointer into the object needs to be updated.
+-    if (shape()->listp == old->shapePtr())
++    if (shape()->listp == old->shapePtr()) {
+         shape()->listp = shapePtr();
++    }
+ }
+ 
+ /* static */ inline bool
+ JSObject::setSingleton(JSContext* cx, js::HandleObject obj)
+ {
+     MOZ_ASSERT(!IsInsideNursery(obj));
+ 
+     js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(cx, obj->group_, obj->getClass(),
+                                                                  obj->taggedProto());
+-    if (!group)
++    if (!group) {
+         return false;
++    }
+ 
+     obj->group_ = group;
+     return true;
+ }
+ 
+ /* static */ inline js::ObjectGroup*
+ JSObject::getGroup(JSContext* cx, js::HandleObject obj)
+ {
+     MOZ_ASSERT(cx->compartment() == obj->compartment());
+     if (obj->hasLazyGroup()) {
+-        if (cx->compartment() != obj->compartment())
++        if (cx->compartment() != obj->compartment()) {
+             MOZ_CRASH();
++        }
+         return makeLazyGroup(cx, obj);
+     }
+     return obj->group_;
+ }
+ 
+ inline void
+ JSObject::setGroup(js::ObjectGroup* group)
+ {
+@@ -228,89 +240,96 @@ js::HasProperty(JSContext* cx, HandleObj
+     return HasProperty(cx, obj, id, found);
+ }
+ 
+ inline bool
+ js::GetElement(JSContext* cx, HandleObject obj, HandleValue receiver, uint32_t index,
+                MutableHandleValue vp)
+ {
+     RootedId id(cx);
+-    if (!IndexToId(cx, index, &id))
++    if (!IndexToId(cx, index, &id)) {
+         return false;
++    }
+     return GetProperty(cx, obj, receiver, id, vp);
+ }
+ 
+ inline bool
+ js::GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index,
+                MutableHandleValue vp)
+ {
+     RootedValue receiverValue(cx, ObjectValue(*receiver));
+     return GetElement(cx, obj, receiverValue, index, vp);
+ }
+ 
+ inline bool
+ js::GetElementNoGC(JSContext* cx, JSObject* obj, const Value& receiver, uint32_t index, Value* vp)
+ {
+-    if (obj->getOpsGetProperty())
++    if (obj->getOpsGetProperty()) {
+         return false;
++    }
+ 
+-    if (index > JSID_INT_MAX)
++    if (index > JSID_INT_MAX) {
+         return false;
++    }
+     return GetPropertyNoGC(cx, obj, receiver, INT_TO_JSID(index), vp);
+ }
+ 
+ inline bool
+ js::DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
+ {
+     MarkTypePropertyNonData(cx, obj, id);
+-    if (DeletePropertyOp op = obj->getOpsDeleteProperty())
++    if (DeletePropertyOp op = obj->getOpsDeleteProperty()) {
+         return op(cx, obj, id, result);
++    }
+     return NativeDeleteProperty(cx, obj.as<NativeObject>(), id, result);
+ }
+ 
+ inline bool
+ js::DeleteElement(JSContext* cx, HandleObject obj, uint32_t index, ObjectOpResult& result)
+ {
+     RootedId id(cx);
+-    if (!IndexToId(cx, index, &id))
++    if (!IndexToId(cx, index, &id)) {
+         return false;
++    }
+     return DeleteProperty(cx, obj, id, result);
+ }
+ 
+ MOZ_ALWAYS_INLINE bool
+ js::MaybeHasInterestingSymbolProperty(JSContext* cx, JSObject* obj, Symbol* symbol,
+                                       JSObject** holder)
+ {
+     MOZ_ASSERT(symbol->isInterestingSymbol());
+ 
+     jsid id = SYMBOL_TO_JSID(symbol);
+     do {
+         if (obj->maybeHasInterestingSymbolProperty() ||
+             MOZ_UNLIKELY(ClassMayResolveId(cx->names(), obj->getClass(), id, obj)))
+         {
+-            if (holder)
++            if (holder) {
+                 *holder = obj;
++            }
+             return true;
+         }
+         obj = obj->staticPrototype();
+     } while (obj);
+ 
+     return false;
+ }
+ 
+ MOZ_ALWAYS_INLINE bool
+ js::GetInterestingSymbolProperty(JSContext* cx, HandleObject obj, Symbol* sym, MutableHandleValue vp)
+ {
+     JSObject* holder;
+     if (!MaybeHasInterestingSymbolProperty(cx, obj, sym, &holder)) {
+ #ifdef DEBUG
+         RootedValue receiver(cx, ObjectValue(*obj));
+         RootedId id(cx, SYMBOL_TO_JSID(sym));
+-        if (!GetProperty(cx, obj, receiver, id, vp))
++        if (!GetProperty(cx, obj, receiver, id, vp)) {
+             return false;
++        }
+         MOZ_ASSERT(vp.isUndefined());
+ #endif
+         vp.setUndefined();
+         return true;
+     }
+ 
+     RootedObject holderRoot(cx, holder);
+     RootedValue receiver(cx, ObjectValue(*obj));
+@@ -318,35 +337,37 @@ js::GetInterestingSymbolProperty(JSConte
+     return GetProperty(cx, holderRoot, receiver, id, vp);
+ }
+ 
+ /* * */
+ 
+ inline bool
+ JSObject::isQualifiedVarObj() const
+ {
+-    if (is<js::DebugEnvironmentProxy>())
++    if (is<js::DebugEnvironmentProxy>()) {
+         return as<js::DebugEnvironmentProxy>().environment().isQualifiedVarObj();
++    }
+     bool rv = hasAllFlags(js::BaseShape::QUALIFIED_VAROBJ);
+     MOZ_ASSERT_IF(rv,
+                   is<js::GlobalObject>() ||
+                   is<js::CallObject>() ||
+                   is<js::VarEnvironmentObject>() ||
+                   is<js::ModuleEnvironmentObject>() ||
+                   is<js::NonSyntacticVariablesObject>() ||
+                   (is<js::WithEnvironmentObject>() &&
+                    !as<js::WithEnvironmentObject>().isSyntactic()));
+     return rv;
+ }
+ 
+ inline bool
+ JSObject::isUnqualifiedVarObj() const
+ {
+-    if (is<js::DebugEnvironmentProxy>())
++    if (is<js::DebugEnvironmentProxy>()) {
+         return as<js::DebugEnvironmentProxy>().environment().isUnqualifiedVarObj();
++    }
+     return is<js::GlobalObject>() || is<js::NonSyntacticVariablesObject>();
+ }
+ 
+ namespace js {
+ 
+ inline bool
+ ClassCanHaveFixedData(const Class* clasp)
+ {
+@@ -419,18 +440,19 @@ JSObject::isOwnGlobal(JSTracer* trc) con
+ {
+     return globalForTracing(trc) == this;
+ }
+ 
+ inline bool
+ JSObject::hasAllFlags(js::BaseShape::Flag flags) const
+ {
+     MOZ_ASSERT(flags);
+-    if (js::Shape* shape = maybeShape())
++    if (js::Shape* shape = maybeShape()) {
+         return shape->hasAllObjectFlags(flags);
++    }
+     return false;
+ }
+ 
+ inline bool
+ JSObject::nonProxyIsExtensible() const
+ {
+     MOZ_ASSERT(!uninlinedIsProxy());
+ 
+@@ -459,18 +481,19 @@ JSObject::hasUncacheableProto() const
+ MOZ_ALWAYS_INLINE bool
+ JSObject::maybeHasInterestingSymbolProperty() const
+ {
+     const js::NativeObject* nobj;
+     if (isNative()) {
+         nobj = &as<js::NativeObject>();
+     } else if (is<js::UnboxedPlainObject>()) {
+         nobj = as<js::UnboxedPlainObject>().maybeExpando();
+-        if (!nobj)
++        if (!nobj) {
+             return false;
++        }
+     } else {
+         return true;
+     }
+ 
+     return nobj->hasInterestingSymbol();
+ }
+ 
+ inline bool
+@@ -538,18 +561,19 @@ IsNativeFunction(const JSObject* obj, JS
+ 
+ // Return whether looking up a method on 'obj' definitely resolves to the
+ // original specified native function. The method may conservatively return
+ // 'false' in the case of proxies or other non-native objects.
+ static MOZ_ALWAYS_INLINE bool
+ HasNativeMethodPure(JSObject* obj, PropertyName* name, JSNative native, JSContext* cx)
+ {
+     Value v;
+-    if (!GetPropertyPure(cx, obj, NameToId(name), &v))
++    if (!GetPropertyPure(cx, obj, NameToId(name), &v)) {
+         return false;
++    }
+ 
+     return IsNativeFunction(v, native);
+ }
+ 
+ // Return whether 'obj' definitely has no @@toPrimitive method.
+ static MOZ_ALWAYS_INLINE bool
+ HasNoToPrimitiveMethodPure(JSObject* obj, JSContext* cx)
+ {
+@@ -562,31 +586,33 @@ HasNoToPrimitiveMethodPure(JSObject* obj
+         MOZ_ASSERT(LookupPropertyPure(cx, obj, SYMBOL_TO_JSID(toPrimitive), &pobj, &prop));
+         MOZ_ASSERT(!prop);
+ #endif
+         return true;
+     }
+ 
+     JSObject* pobj;
+     PropertyResult prop;
+-    if (!LookupPropertyPure(cx, holder, SYMBOL_TO_JSID(toPrimitive), &pobj, &prop))
++    if (!LookupPropertyPure(cx, holder, SYMBOL_TO_JSID(toPrimitive), &pobj, &prop)) {
+         return false;
++    }
+ 
+     return !prop;
+ }
+ 
+ extern bool
+ ToPropertyKeySlow(JSContext* cx, HandleValue argument, MutableHandleId result);
+ 
+ /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
+ MOZ_ALWAYS_INLINE bool
+ ToPropertyKey(JSContext* cx, HandleValue argument, MutableHandleId result)
+ {
+-    if (MOZ_LIKELY(argument.isPrimitive()))
++    if (MOZ_LIKELY(argument.isPrimitive())) {
+         return ValueToId<CanGC>(cx, argument, result);
++    }
+ 
+     return ToPropertyKeySlow(cx, argument, result);
+ }
+ 
+ /*
+  * Return true if this is a compiler-created internal function accessed by
+  * its own object. Such a function object must not be accessible to script
+  * or embedding code.
+@@ -781,26 +807,28 @@ NewObjectWithGroup(JSContext* cx, Handle
+ /*
+  * As for gc::GetGCObjectKind, where numElements is a guess at the final size of
+  * the object, zero if the final size is unknown. This should only be used for
+  * objects that do not require any fixed slots.
+  */
+ static inline gc::AllocKind
+ GuessObjectGCKind(size_t numElements)
+ {
+-    if (numElements)
++    if (numElements) {
+         return gc::GetGCObjectKind(numElements);
++    }
+     return gc::AllocKind::OBJECT4;
+ }
+ 
+ static inline gc::AllocKind
+ GuessArrayGCKind(size_t numElements)
+ {
+-    if (numElements)
++    if (numElements) {
+         return gc::GetGCArrayKind(numElements);
++    }
+     return gc::AllocKind::OBJECT8;
+ }
+ 
+ // Returns ESClass::Other if the value isn't an object, or if the object
+ // isn't of one of the enumerated classes.  Otherwise returns the appropriate
+ // class.
+ inline bool
+ GetClassOfValue(JSContext* cx, HandleValue v, ESClass* cls)
+diff --git a/js/src/vm/JSObject-inl.h.1488698-7.later b/js/src/vm/JSObject-inl.h.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSObject-inl.h.1488698-7.later
+@@ -0,0 +1,58 @@
++--- JSObject-inl.h
+++++ JSObject-inl.h
++@@ -808,18 +836,19 @@ InitClass(JSContext* cx, HandleObject ob
++           const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
++           NativeObject** ctorp = nullptr);
++ 
++ MOZ_ALWAYS_INLINE const char*
++ GetObjectClassName(JSContext* cx, HandleObject obj)
++ {
++     cx->check(obj);
++ 
++-    if (obj->is<ProxyObject>())
+++    if (obj->is<ProxyObject>()) {
++         return Proxy::className(cx, obj);
+++    }
++ 
++     return obj->getClass()->name;
++ }
++ 
++ inline bool
++ IsCallable(const Value& v)
++ {
++     return v.isObject() && v.toObject().isCallable();
++@@ -845,30 +874,32 @@ CreateThis(JSContext* cx, HandleObject c
++         MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor());
++         thisv.setMagic(JS_UNINITIALIZED_LEXICAL);
++         return true;
++     }
++ 
++     MOZ_ASSERT(thisv.isMagic(JS_IS_CONSTRUCTING));
++ 
++     JSObject* obj = CreateThisForFunction(cx, callee, newTarget, newKind);
++-    if (!obj)
+++    if (!obj) {
++         return false;
+++    }
++ 
++     thisv.setObject(*obj);
++     return true;
++ }
++ 
++ } /* namespace js */
++ 
++ MOZ_ALWAYS_INLINE bool
++ JSObject::isCallable() const
++ {
++-    if (is<JSFunction>())
+++    if (is<JSFunction>()) {
++         return true;
+++    }
++     if (is<js::ProxyObject>()) {
++         const js::ProxyObject& p = as<js::ProxyObject>();
++         return p.handler()->isCallable(const_cast<JSObject*>(this));
++     }
++     return callHook() != nullptr;
++ }
++ 
++ MOZ_ALWAYS_INLINE bool
+diff --git a/js/src/vm/JSObject.cpp b/js/src/vm/JSObject.cpp
+--- a/js/src/vm/JSObject.cpp
++++ b/js/src/vm/JSObject.cpp
+@@ -111,30 +111,37 @@ js::ReportNotObjectWithName(JSContext* c
+         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT_NAME,
+                                    name, chars);
+     }
+ }
+ 
+ JS_PUBLIC_API(const char*)
+ JS::InformalValueTypeName(const Value& v)
+ {
+-    if (v.isObject())
++    if (v.isObject()) {
+         return v.toObject().getClass()->name;
+-    if (v.isString())
++    }
++    if (v.isString()) {
+         return "string";
+-    if (v.isSymbol())
++    }
++    if (v.isSymbol()) {
+         return "symbol";
+-    if (v.isNumber())
++    }
++    if (v.isNumber()) {
+         return "number";
+-    if (v.isBoolean())
++    }
++    if (v.isBoolean()) {
+         return "boolean";
+-    if (v.isNull())
++    }
++    if (v.isNull()) {
+         return "null";
+-    if (v.isUndefined())
++    }
++    if (v.isUndefined()) {
+         return "undefined";
++    }
+     return "value";
+ }
+ 
+ // ES6 draft rev37 6.2.4.4 FromPropertyDescriptor
+ JS_PUBLIC_API(bool)
+ JS::FromPropertyDescriptor(JSContext* cx, Handle<PropertyDescriptor> desc, MutableHandleValue vp)
+ {
+     AssertHeapIsIdle();
+@@ -151,67 +158,76 @@ JS::FromPropertyDescriptor(JSContext* cx
+ }
+ 
+ bool
+ js::FromPropertyDescriptorToObject(JSContext* cx, Handle<PropertyDescriptor> desc,
+                                    MutableHandleValue vp)
+ {
+     // Step 2-3.
+     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
+-    if (!obj)
++    if (!obj) {
+         return false;
++    }
+ 
+     const JSAtomState& names = cx->names();
+ 
+     // Step 4.
+     if (desc.hasValue()) {
+-        if (!DefineDataProperty(cx, obj, names.value, desc.value()))
++        if (!DefineDataProperty(cx, obj, names.value, desc.value())) {
+             return false;
++        }
+     }
+ 
+     // Step 5.
+     RootedValue v(cx);
+     if (desc.hasWritable()) {
+         v.setBoolean(desc.writable());
+-        if (!DefineDataProperty(cx, obj, names.writable, v))
++        if (!DefineDataProperty(cx, obj, names.writable, v)) {
+             return false;
++        }
+     }
+ 
+     // Step 6.
+     if (desc.hasGetterObject()) {
+-        if (JSObject* get = desc.getterObject())
++        if (JSObject* get = desc.getterObject()) {
+             v.setObject(*get);
+-        else
++        } else {
+             v.setUndefined();
+-        if (!DefineDataProperty(cx, obj, names.get, v))
++        }
++        if (!DefineDataProperty(cx, obj, names.get, v)) {
+             return false;
++        }
+     }
+ 
+     // Step 7.
+     if (desc.hasSetterObject()) {
+-        if (JSObject* set = desc.setterObject())
++        if (JSObject* set = desc.setterObject()) {
+             v.setObject(*set);
+-        else
++        } else {
+             v.setUndefined();
+-        if (!DefineDataProperty(cx, obj, names.set, v))
++        }
++        if (!DefineDataProperty(cx, obj, names.set, v)) {
+             return false;
++        }
+     }
+ 
+     // Step 8.
+     if (desc.hasEnumerable()) {
+         v.setBoolean(desc.enumerable());
+-        if (!DefineDataProperty(cx, obj, names.enumerable, v))
++        if (!DefineDataProperty(cx, obj, names.enumerable, v)) {
+             return false;
++        }
+     }
+ 
+     // Step 9.
+     if (desc.hasConfigurable()) {
+         v.setBoolean(desc.configurable());
+-        if (!DefineDataProperty(cx, obj, names.configurable, v))
++        if (!DefineDataProperty(cx, obj, names.configurable, v)) {
+             return false;
++        }
+     }
+ 
+     vp.setObject(*obj);
+     return true;
+ }
+ 
+ bool
+ js::GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
+@@ -292,97 +308,110 @@ CheckCallable(JSContext* cx, JSObject* o
+ }
+ 
+ bool
+ js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
+                          MutableHandle<PropertyDescriptor> desc)
+ {
+     // step 2
+     RootedObject obj(cx, NonNullObjectWithName(cx, "property descriptor", descval));
+-    if (!obj)
++    if (!obj) {
+         return false;
++    }
+ 
+     // step 3
+     desc.clear();
+ 
+     bool found = false;
+     RootedId id(cx);
+     RootedValue v(cx);
+     unsigned attrs = 0;
+ 
+     // step 4
+     id = NameToId(cx->names().enumerable);
+-    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
++    if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
+         return false;
++    }
+     if (found) {
+-        if (ToBoolean(v))
++        if (ToBoolean(v)) {
+             attrs |= JSPROP_ENUMERATE;
++        }
+     } else {
+         attrs |= JSPROP_IGNORE_ENUMERATE;
+     }
+ 
+     // step 5
+     id = NameToId(cx->names().configurable);
+-    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
++    if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
+         return false;
++    }
+     if (found) {
+-        if (!ToBoolean(v))
++        if (!ToBoolean(v)) {
+             attrs |= JSPROP_PERMANENT;
++        }
+     } else {
+         attrs |= JSPROP_IGNORE_PERMANENT;
+     }
+ 
+     // step 6
+     id = NameToId(cx->names().value);
+-    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
++    if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
+         return false;
+-    if (found)
++    }
++    if (found) {
+         desc.value().set(v);
+-    else
++    } else {
+         attrs |= JSPROP_IGNORE_VALUE;
++    }
+ 
+     // step 7
+     id = NameToId(cx->names().writable);
+-    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
++    if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
+         return false;
++    }
+     if (found) {
+-        if (!ToBoolean(v))
++        if (!ToBoolean(v)) {
+             attrs |= JSPROP_READONLY;
++        }
+     } else {
+         attrs |= JSPROP_IGNORE_READONLY;
+     }
+ 
+     // step 8
+     bool hasGetOrSet;
+     id = NameToId(cx->names().get);
+-    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
++    if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
+         return false;
++    }
+     hasGetOrSet = found;
+     if (found) {
+         if (v.isObject()) {
+-            if (checkAccessors)
++            if (checkAccessors) {
+                 JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), js_getter_str));
++            }
+             desc.setGetterObject(&v.toObject());
+         } else if (!v.isUndefined()) {
+             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
+                                       js_getter_str);
+             return false;
+         }
+         attrs |= JSPROP_GETTER;
+     }
+ 
+     // step 9
+     id = NameToId(cx->names().set);
+-    if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
++    if (!GetPropertyIfPresent(cx, obj, id, &v, &found)) {
+         return false;
++    }
+     hasGetOrSet |= found;
+     if (found) {
+         if (v.isObject()) {
+-            if (checkAccessors)
++            if (checkAccessors) {
+                 JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), js_setter_str));
++            }
+             desc.setSetterObject(&v.toObject());
+         } else if (!v.isUndefined()) {
+             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
+                                       js_setter_str);
+             return false;
+         }
+         attrs |= JSPROP_SETTER;
+     }
+@@ -401,54 +430,61 @@ js::ToPropertyDescriptor(JSContext* cx, 
+     desc.setAttributes(attrs);
+     MOZ_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+     return true;
+ }
+ 
+ Result<>
+ js::CheckPropertyDescriptorAccessors(JSContext* cx, Handle<PropertyDescriptor> desc)
+ {
+-    if (desc.hasGetterObject())
++    if (desc.hasGetterObject()) {
+         MOZ_TRY(CheckCallable(cx, desc.getterObject(), js_getter_str));
+-
+-    if (desc.hasSetterObject())
++    }
++
++    if (desc.hasSetterObject()) {
+         MOZ_TRY(CheckCallable(cx, desc.setterObject(), js_setter_str));
++    }
+ 
+     return Ok();
+ }
+ 
+ void
+ js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)
+ {
+     desc.assertValid();
+ 
+     if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
+-        if (!desc.hasWritable())
++        if (!desc.hasWritable()) {
+             desc.attributesRef() |= JSPROP_READONLY;
++        }
+         desc.attributesRef() &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
+     } else {
+-        if (!desc.hasGetterObject())
++        if (!desc.hasGetterObject()) {
+             desc.setGetterObject(nullptr);
+-        if (!desc.hasSetterObject())
++        }
++        if (!desc.hasSetterObject()) {
+             desc.setSetterObject(nullptr);
++        }
+         desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER;
+     }
+-    if (!desc.hasConfigurable())
++    if (!desc.hasConfigurable()) {
+         desc.attributesRef() |= JSPROP_PERMANENT;
++    }
+     desc.attributesRef() &= ~(JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_ENUMERATE);
+ 
+     desc.assertComplete();
+ }
+ 
+ bool
+ js::ReadPropertyDescriptors(JSContext* cx, HandleObject props, bool checkAccessors,
+                             AutoIdVector* ids, MutableHandle<PropertyDescriptorVector> descs)
+ {
+-    if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids))
++    if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids)) {
+         return false;
++    }
+ 
+     RootedId id(cx);
+     for (size_t i = 0, len = ids->length(); i < len; i++) {
+         id = (*ids)[i];
+         Rooted<PropertyDescriptor> desc(cx);
+         RootedValue v(cx);
+         if (!GetProperty(cx, props, props, id, &v) ||
+             !ToPropertyDescriptor(cx, v, checkAccessors, &desc) ||
+@@ -547,87 +583,97 @@ js::SetIntegrityLevel(JSContext* cx, Han
+             id = keys[i];
+ 
+             if (level == IntegrityLevel::Sealed) {
+                 // 8.a.i.
+                 desc.setAttributes(AllowConfigure | JSPROP_PERMANENT);
+             } else {
+                 // 9.a.i-ii.
+                 Rooted<PropertyDescriptor> currentDesc(cx);
+-                if (!GetOwnPropertyDescriptor(cx, obj, id, &currentDesc))
++                if (!GetOwnPropertyDescriptor(cx, obj, id, &currentDesc)) {
+                     return false;
++                }
+ 
+                 // 9.a.iii.
+-                if (!currentDesc.object())
++                if (!currentDesc.object()) {
+                     continue;
++                }
+ 
+                 // 9.a.iii.1-2
+-                if (currentDesc.isAccessorDescriptor())
++                if (currentDesc.isAccessorDescriptor()) {
+                     desc.setAttributes(AllowConfigure | JSPROP_PERMANENT);
+-                else
++                } else {
+                     desc.setAttributes(AllowConfigureAndWritable | JSPROP_PERMANENT | JSPROP_READONLY);
++                }
+             }
+ 
+             // 8.a.i-ii. / 9.a.iii.3-4
+-            if (!DefineProperty(cx, obj, id, desc))
++            if (!DefineProperty(cx, obj, id, desc)) {
+                 return false;
++            }
+         }
+     }
+ 
+     // Finally, freeze or seal the dense elements.
+-    if (obj->isNative())
++    if (obj->isNative()) {
+         ObjectElements::FreezeOrSeal(cx, &obj->as<NativeObject>(), level);
++    }
+ 
+     return true;
+ }
+ 
+ static bool
+ ResolveLazyProperties(JSContext* cx, HandleNativeObject obj)
+ {
+     const Class* clasp = obj->getClass();
+     if (JSEnumerateOp enumerate = clasp->getEnumerate()) {
+-        if (!enumerate(cx, obj))
++        if (!enumerate(cx, obj)) {
+             return false;
++        }
+     }
+     if (clasp->getNewEnumerate() && clasp->getResolve()) {
+         AutoIdVector properties(cx);
+-        if (!clasp->getNewEnumerate()(cx, obj, properties, /* enumerableOnly = */ false))
++        if (!clasp->getNewEnumerate()(cx, obj, properties, /* enumerableOnly = */ false)) {
+             return false;
++        }
+ 
+         RootedId id(cx);
+         for (size_t i = 0; i < properties.length(); i++) {
+             id = properties[i];
+             bool found;
+-            if (!HasOwnProperty(cx, obj, id, &found))
++            if (!HasOwnProperty(cx, obj, id, &found)) {
+                 return false;
++            }
+         }
+     }
+     return true;
+ }
+ 
+ // ES6 draft rev33 (12 Feb 2015) 7.3.15
+ bool
+ js::TestIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level, bool* result)
+ {
+     // Steps 3-6. (Steps 1-2 are redundant assertions.)
+     bool status;
+-    if (!IsExtensible(cx, obj, &status))
++    if (!IsExtensible(cx, obj, &status)) {
+         return false;
++    }
+     if (status) {
+         *result = false;
+         return true;
+     }
+ 
+     // Fast path for native objects.
+     if (obj->isNative()) {
+         HandleNativeObject nobj = obj.as<NativeObject>();
+ 
+         // Force lazy properties to be resolved.
+-        if (!ResolveLazyProperties(cx, nobj))
++        if (!ResolveLazyProperties(cx, nobj)) {
+             return false;
++        }
+ 
+         // Typed array elements are non-configurable, writable properties, so
+         // if any elements are present, the typed array cannot be frozen.
+         if (nobj->is<TypedArrayObject>() && nobj->as<TypedArrayObject>().length() > 0 &&
+             level == IntegrityLevel::Frozen)
+         {
+             *result = false;
+             return true;
+@@ -666,32 +712,35 @@ js::TestIntegrityLevel(JSContext* cx, Ha
+             {
+                 *result = false;
+                 return true;
+             }
+         }
+     } else {
+         // Steps 7-8.
+         AutoIdVector props(cx);
+-        if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props))
++        if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props)) {
+             return false;
++        }
+ 
+         // Step 9.
+         RootedId id(cx);
+         Rooted<PropertyDescriptor> desc(cx);
+         for (size_t i = 0, len = props.length(); i < len; i++) {
+             id = props[i];
+ 
+             // Steps 9.a-b.
+-            if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
++            if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
+                 return false;
++            }
+ 
+             // Step 9.c.
+-            if (!desc.object())
++            if (!desc.object()) {
+                 continue;
++            }
+ 
+             // Steps 9.c.i-ii.
+             if (desc.configurable() ||
+                 (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.writable()))
+             {
+                 *result = false;
+                 return true;
+             }
+@@ -708,20 +757,22 @@ js::TestIntegrityLevel(JSContext* cx, Ha
+ 
+ /*
+  * Get the GC kind to use for scripted 'new' on the given class.
+  * FIXME bug 547327: estimate the size from the allocation site.
+  */
+ static inline gc::AllocKind
+ NewObjectGCKind(const js::Class* clasp)
+ {
+-    if (clasp == &ArrayObject::class_)
++    if (clasp == &ArrayObject::class_) {
+         return gc::AllocKind::OBJECT8;
+-    if (clasp == &JSFunction::class_)
++    }
++    if (clasp == &JSFunction::class_) {
+         return gc::AllocKind::OBJECT2;
++    }
+     return gc::AllocKind::OBJECT4;
+ }
+ 
+ static inline JSObject*
+ NewObject(JSContext* cx, HandleObjectGroup group, gc::AllocKind kind,
+           NewObjectKind newKind, uint32_t initialShapeFlags = 0)
+ {
+     const Class* clasp = group->clasp();
+@@ -734,35 +785,37 @@ NewObject(JSContext* cx, HandleObjectGro
+     // enough fixed slots to cover the number of reserved slots in the object,
+     // regardless of the allocation kind specified.
+     size_t nfixed = ClassCanHaveFixedData(clasp)
+                     ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
+                     : GetGCKindSlots(kind, clasp);
+ 
+     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed,
+                                                       initialShapeFlags));
+-    if (!shape)
++    if (!shape) {
+         return nullptr;
++    }
+ 
+     gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
+ 
+     JSObject* obj;
+     if (clasp->isJSFunction()) {
+         JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSFunction::create(cx, kind, heap, shape, group));
+     } else if (MOZ_LIKELY(clasp->isNative())) {
+         JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::create(cx, kind, heap, shape, group));
+     } else {
+         MOZ_ASSERT(IsTypedObjectClass(clasp));
+         JS_TRY_VAR_OR_RETURN_NULL(cx, obj, TypedObject::create(cx, kind, heap, shape, group));
+     }
+ 
+     if (newKind == SingletonObject) {
+         RootedObject nobj(cx, obj);
+-        if (!JSObject::setSingleton(cx, nobj))
++        if (!JSObject::setSingleton(cx, nobj)) {
+             return nullptr;
++        }
+         obj = nobj;
+     }
+ 
+     probes::CreateObject(cx, obj);
+     return obj;
+ }
+ 
+ void
+@@ -786,37 +839,41 @@ js::NewObjectWithTaggedProtoIsCachable(J
+ }
+ 
+ JSObject*
+ js::NewObjectWithGivenTaggedProto(JSContext* cx, const Class* clasp,
+                                   Handle<TaggedProto> proto,
+                                   gc::AllocKind allocKind, NewObjectKind newKind,
+                                   uint32_t initialShapeFlags)
+ {
+-    if (CanBeFinalizedInBackground(allocKind, clasp))
++    if (CanBeFinalizedInBackground(allocKind, clasp)) {
+         allocKind = GetBackgroundAllocKind(allocKind);
++    }
+ 
+     bool isCachable = NewObjectWithTaggedProtoIsCachable(cx, proto, newKind, clasp);
+     if (isCachable) {
+         NewObjectCache& cache = cx->caches().newObjectCache;
+         NewObjectCache::EntryIndex entry = -1;
+         if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
+             JSObject* obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
+-            if (obj)
++            if (obj) {
+                 return obj;
++            }
+         }
+     }
+ 
+     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, proto, nullptr));
+-    if (!group)
++    if (!group) {
+         return nullptr;
++    }
+ 
+     RootedObject obj(cx, NewObject(cx, group, allocKind, newKind, initialShapeFlags));
+-    if (!obj)
++    if (!obj) {
+         return nullptr;
++    }
+ 
+     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
+         NewObjectCache& cache = cx->caches().newObjectCache;
+         NewObjectCache::EntryIndex entry = -1;
+         cache.lookupProto(clasp, proto.toObject(), allocKind, &entry);
+         cache.fillProto(entry, clasp, proto, allocKind, &obj->as<NativeObject>());
+     }
+ 
+@@ -830,53 +887,60 @@ NewObjectIsCachable(JSContext* cx, NewOb
+            newKind == GenericObject &&
+            clasp->isNative();
+ }
+ 
+ JSObject*
+ js::NewObjectWithClassProtoCommon(JSContext* cx, const Class* clasp, HandleObject protoArg,
+                                   gc::AllocKind allocKind, NewObjectKind newKind)
+ {
+-    if (protoArg)
++    if (protoArg) {
+         return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(protoArg), allocKind, newKind);
+-
+-    if (CanBeFinalizedInBackground(allocKind, clasp))
++    }
++
++    if (CanBeFinalizedInBackground(allocKind, clasp)) {
+         allocKind = GetBackgroundAllocKind(allocKind);
++    }
+ 
+     Handle<GlobalObject*> global = cx->global();
+ 
+     bool isCachable = NewObjectIsCachable(cx, newKind, clasp);
+     if (isCachable) {
+         NewObjectCache& cache = cx->caches().newObjectCache;
+         NewObjectCache::EntryIndex entry = -1;
+         if (cache.lookupGlobal(clasp, global, allocKind, &entry)) {
+             gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
+             JSObject* obj = cache.newObjectFromHit(cx, entry, heap);
+-            if (obj)
++            if (obj) {
+                 return obj;
++            }
+         }
+     }
+ 
+     // Find the appropriate proto for clasp. Built-in classes have a cached
+     // proto on cx->global(); all others get %ObjectPrototype%.
+     JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
+-    if (protoKey == JSProto_Null)
++    if (protoKey == JSProto_Null) {
+         protoKey = JSProto_Object;
++    }
+ 
+     JSObject* proto = GlobalObject::getOrCreatePrototype(cx, protoKey);
+-    if (!proto)
++    if (!proto) {
+         return nullptr;
++    }
+ 
+     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, TaggedProto(proto)));
+-    if (!group)
++    if (!group) {
+         return nullptr;
++    }
+ 
+     JSObject* obj = NewObject(cx, group, allocKind, newKind);
+-    if (!obj)
++    if (!obj) {
+         return nullptr;
++    }
+ 
+     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
+         NewObjectCache& cache = cx->caches().newObjectCache;
+         NewObjectCache::EntryIndex entry = -1;
+         cache.lookupGlobal(clasp, global, allocKind, &entry);
+         cache.fillGlobal(entry, clasp, global, allocKind, &obj->as<NativeObject>());
+     }
+ 
+@@ -903,34 +967,37 @@ NewObjectWithGroupIsCachable(JSContext* 
+  * Create a plain object with the specified group. This bypasses getNewGroup to
+  * avoid losing creation site information for objects made by scripted 'new'.
+  */
+ JSObject*
+ js::NewObjectWithGroupCommon(JSContext* cx, HandleObjectGroup group,
+                              gc::AllocKind allocKind, NewObjectKind newKind)
+ {
+     MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
+-    if (CanBeFinalizedInBackground(allocKind, group->clasp()))
++    if (CanBeFinalizedInBackground(allocKind, group->clasp())) {
+         allocKind = GetBackgroundAllocKind(allocKind);
++    }
+ 
+     bool isCachable = NewObjectWithGroupIsCachable(cx, group, newKind);
+     if (isCachable) {
+         NewObjectCache& cache = cx->caches().newObjectCache;
+         NewObjectCache::EntryIndex entry = -1;
+         if (cache.lookupGroup(group, allocKind, &entry)) {
+             JSObject* obj = cache.newObjectFromHit(cx, entry,
+                                                    GetInitialHeap(newKind, group->clasp()));
+-            if (obj)
++            if (obj) {
+                 return obj;
++            }
+         }
+     }
+ 
+     JSObject* obj = NewObject(cx, group, allocKind, newKind);
+-    if (!obj)
++    if (!obj) {
+         return nullptr;
++    }
+ 
+     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
+         NewObjectCache& cache = cx->caches().newObjectCache;
+         NewObjectCache::EntryIndex entry = -1;
+         cache.lookupGroup(group, allocKind, &entry);
+         cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
+     }
+ 
+@@ -939,96 +1006,106 @@ js::NewObjectWithGroupCommon(JSContext* 
+ 
+ bool
+ js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj)
+ {
+     jsbytecode* pc;
+     RootedScript script(cx, cx->currentScript(&pc));
+     gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
+     NewObjectKind newKind = GenericObject;
+-    if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &PlainObject::class_))
++    if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &PlainObject::class_)) {
+         newKind = SingletonObject;
++    }
+     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
+-    if (!obj)
++    if (!obj) {
+         return false;
++    }
+ 
+     if (script) {
+         /* Try to specialize the group of the object to the scripted call site. */
+-        if (!ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, newKind == SingletonObject))
++        if (!ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, newKind == SingletonObject)) {
+             return false;
++        }
+     }
+ 
+     pobj.set(obj);
+     return true;
+ }
+ 
+ JSObject*
+ js::CreateThis(JSContext* cx, const Class* newclasp, HandleObject callee)
+ {
+     RootedObject proto(cx);
+-    if (!GetPrototypeFromConstructor(cx, callee, &proto))
++    if (!GetPrototypeFromConstructor(cx, callee, &proto)) {
+         return nullptr;
++    }
+     gc::AllocKind kind = NewObjectGCKind(newclasp);
+     return NewObjectWithClassProto(cx, newclasp, proto, kind);
+ }
+ 
+ static inline JSObject*
+ CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group,
+                                NewObjectKind newKind)
+ {
+     bool isUnboxed;
+     TypeNewScript* maybeNewScript;
+     {
+         AutoSweepObjectGroup sweep(group);
+         isUnboxed = group->maybeUnboxedLayout(sweep);
+         maybeNewScript = group->newScript(sweep);
+     }
+ 
+-    if (isUnboxed && newKind != SingletonObject)
++    if (isUnboxed && newKind != SingletonObject) {
+         return UnboxedPlainObject::create(cx, group, newKind);
++    }
+ 
+     if (maybeNewScript) {
+         if (maybeNewScript->analyzed()) {
+             // The definite properties analysis has been performed for this
+             // group, so get the shape and alloc kind to use from the
+             // TypeNewScript's template.
+             RootedPlainObject templateObject(cx, maybeNewScript->templateObject());
+             MOZ_ASSERT(templateObject->group() == group);
+ 
+             RootedPlainObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
+-            if (!res)
++            if (!res) {
+                 return nullptr;
++            }
+ 
+             if (newKind == SingletonObject) {
+                 Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->staticPrototype()));
+-                if (!JSObject::splicePrototype(cx, res, &PlainObject::class_, proto))
++                if (!JSObject::splicePrototype(cx, res, &PlainObject::class_, proto)) {
+                     return nullptr;
++                }
+             } else {
+                 res->setGroup(group);
+             }
+             return res;
+         }
+ 
+         // The initial objects registered with a TypeNewScript can't be in the
+         // nursery.
+-        if (newKind == GenericObject)
++        if (newKind == GenericObject) {
+             newKind = TenuredObject;
++        }
+ 
+         // Not enough objects with this group have been created yet, so make a
+         // plain object and register it with the group. Use the maximum number
+         // of fixed slots, as is also required by the TypeNewScript.
+         gc::AllocKind allocKind = GuessObjectGCKind(NativeObject::MAX_FIXED_SLOTS);
+         PlainObject* res = NewObjectWithGroup<PlainObject>(cx, group, allocKind, newKind);
+-        if (!res)
++        if (!res) {
+             return nullptr;
++        }
+ 
+         // Make sure group->newScript is still there.
+         AutoSweepObjectGroup sweep(group);
+-        if (newKind != SingletonObject && group->newScript(sweep))
++        if (newKind != SingletonObject && group->newScript(sweep)) {
+             group->newScript(sweep)->registerNewObject(res);
++        }
+ 
+         return res;
+     }
+ 
+     gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
+ 
+     if (newKind == SingletonObject) {
+         Rooted<TaggedProto> protoRoot(cx, group->proto());
+@@ -1041,25 +1118,27 @@ JSObject*
+ js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObject newTarget,
+                                    HandleObject proto, NewObjectKind newKind /* = GenericObject */)
+ {
+     RootedObject res(cx);
+ 
+     if (proto) {
+         RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
+                                                                  newTarget));
+-        if (!group)
++        if (!group) {
+             return nullptr;
++        }
+ 
+         {
+             AutoSweepObjectGroup sweep(group);
+             if (group->newScript(sweep) && !group->newScript(sweep)->analyzed()) {
+                 bool regenerate;
+-                if (!group->newScript(sweep)->maybeAnalyze(cx, group, &regenerate))
++                if (!group->newScript(sweep)->maybeAnalyze(cx, group, &regenerate)) {
+                     return nullptr;
++                }
+                 if (regenerate) {
+                     // The script was analyzed successfully and may have changed
+                     // the new type table, so refetch the group.
+                     group = ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
+                                                          newTarget);
+                     AutoSweepObjectGroup sweepNewGroup(group);
+                     MOZ_ASSERT(group && group->newScript(sweepNewGroup));
+                 }
+@@ -1068,41 +1147,44 @@ js::CreateThisForFunctionWithProto(JSCon
+ 
+         res = CreateThisForFunctionWithGroup(cx, group, newKind);
+     } else {
+         res = NewBuiltinClassInstance<PlainObject>(cx, newKind);
+     }
+ 
+     if (res) {
+         JSScript* script = JSFunction::getOrCreateScript(cx, callee.as<JSFunction>());
+-        if (!script)
++        if (!script) {
+             return nullptr;
++        }
+         TypeScript::SetThis(cx, script, TypeSet::ObjectType(res));
+     }
+ 
+     return res;
+ }
+ 
+ bool
+ js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, MutableHandleObject proto)
+ {
+     RootedValue protov(cx);
+-    if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov))
++    if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) {
+         return false;
++    }
+     proto.set(protov.isObject() ? &protov.toObject() : nullptr);
+     return true;
+ }
+ 
+ JSObject*
+ js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTarget,
+                           NewObjectKind newKind)
+ {
+     RootedObject proto(cx);
+-    if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
++    if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) {
+         return nullptr;
++    }
+ 
+     JSObject* obj = CreateThisForFunctionWithProto(cx, callee, newTarget, proto, newKind);
+ 
+     if (obj && newKind == SingletonObject) {
+         RootedPlainObject nobj(cx, &obj->as<PlainObject>());
+ 
+         /* Reshape the singleton before passing it as the 'this' value. */
+         NativeObject::clear(cx, nobj);
+@@ -1189,28 +1271,30 @@ CopyProxyObject(JSContext* cx, Handle<Pr
+ 
+     if (from->is<WrapperObject>() &&
+         (Wrapper::wrapperHandler(from)->flags() &
+          Wrapper::CROSS_COMPARTMENT))
+     {
+         to->setCrossCompartmentPrivate(GetProxyPrivate(from));
+     } else {
+         RootedValue v(cx, GetProxyPrivate(from));
+-        if (!cx->compartment()->wrap(cx, &v))
++        if (!cx->compartment()->wrap(cx, &v)) {
+             return false;
++        }
+         to->setSameCompartmentPrivate(v);
+     }
+ 
+     MOZ_ASSERT(from->numReservedSlots() == to->numReservedSlots());
+ 
+     RootedValue v(cx);
+     for (size_t n = 0; n < from->numReservedSlots(); n++) {
+         v = GetProxyReservedSlot(from, n);
+-        if (!cx->compartment()->wrap(cx, &v))
++        if (!cx->compartment()->wrap(cx, &v)) {
+             return false;
++        }
+         SetProxyReservedSlot(to, n, v);
+     }
+ 
+     return true;
+ }
+ 
+ JSObject*
+ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
+@@ -1218,90 +1302,99 @@ js::CloneObject(JSContext* cx, HandleObj
+     if (!obj->isNative() && !obj->is<ProxyObject>()) {
+         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
+         return nullptr;
+     }
+ 
+     RootedObject clone(cx);
+     if (obj->isNative()) {
+         clone = NewObjectWithGivenTaggedProto(cx, obj->getClass(), proto);
+-        if (!clone)
++        if (!clone) {
+             return nullptr;
++        }
+ 
+         if (clone->is<JSFunction>() && (obj->compartment() != clone->compartment())) {
+             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
+             return nullptr;
+         }
+ 
+-        if (obj->as<NativeObject>().hasPrivate())
++        if (obj->as<NativeObject>().hasPrivate()) {
+             clone->as<NativeObject>().setPrivate(obj->as<NativeObject>().getPrivate());
++        }
+     } else {
+         ProxyOptions options;
+         options.setClass(obj->getClass());
+ 
+         clone = ProxyObject::New(cx, GetProxyHandler(obj), JS::NullHandleValue, proto, options);
+-        if (!clone)
++        if (!clone) {
+             return nullptr;
+-
+-        if (!CopyProxyObject(cx, obj.as<ProxyObject>(), clone.as<ProxyObject>()))
++        }
++
++        if (!CopyProxyObject(cx, obj.as<ProxyObject>(), clone.as<ProxyObject>())) {
+             return nullptr;
++        }
+     }
+ 
+     return clone;
+ }
+ 
+ static bool
+ GetScriptArrayObjectElements(HandleArrayObject arr, MutableHandle<GCVector<Value>> values)
+ {
+     MOZ_ASSERT(!arr->isSingleton());
+     MOZ_ASSERT(!arr->isIndexed());
+ 
+     size_t length = arr->length();
+-    if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length))
++    if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length)) {
+         return false;
++    }
+ 
+     size_t initlen = arr->getDenseInitializedLength();
+-    for (size_t i = 0; i < initlen; i++)
++    for (size_t i = 0; i < initlen; i++) {
+         values[i].set(arr->getDenseElement(i));
++    }
+ 
+     return true;
+ }
+ 
+ static bool
+ GetScriptPlainObjectProperties(HandleObject obj, MutableHandle<IdValueVector> properties)
+ {
+     if (obj->is<PlainObject>()) {
+         PlainObject* nobj = &obj->as<PlainObject>();
+ 
+-        if (!properties.appendN(IdValuePair(), nobj->slotSpan()))
++        if (!properties.appendN(IdValuePair(), nobj->slotSpan())) {
+             return false;
++        }
+ 
+         for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
+             Shape& shape = r.front();
+             MOZ_ASSERT(shape.isDataDescriptor());
+             uint32_t slot = shape.slot();
+             properties[slot].get().id = shape.propid();
+             properties[slot].get().value = nobj->getSlot(slot);
+         }
+ 
+         for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
+             Value v = nobj->getDenseElement(i);
+-            if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v)))
++            if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v))) {
+                 return false;
++            }
+         }
+ 
+         return true;
+     }
+ 
+     if (obj->is<UnboxedPlainObject>()) {
+         UnboxedPlainObject* nobj = &obj->as<UnboxedPlainObject>();
+ 
+         const UnboxedLayout& layout = nobj->layout();
+-        if (!properties.appendN(IdValuePair(), layout.properties().length()))
++        if (!properties.appendN(IdValuePair(), layout.properties().length())) {
+             return false;
++        }
+ 
+         for (size_t i = 0; i < layout.properties().length(); i++) {
+             const UnboxedLayout::Property& property = layout.properties()[i];
+             properties[i].get().id = NameToId(property.name);
+             properties[i].get().value = nobj->getValue(property);
+         }
+ 
+         return true;
+@@ -1311,18 +1404,19 @@ GetScriptPlainObjectProperties(HandleObj
+ }
+ 
+ static bool
+ DeepCloneValue(JSContext* cx, Value* vp, NewObjectKind newKind)
+ {
+     if (vp->isObject()) {
+         RootedObject obj(cx, &vp->toObject());
+         obj = DeepCloneObjectLiteral(cx, obj, newKind);
+-        if (!obj)
++        if (!obj) {
+             return false;
++        }
+         vp->setObject(*obj);
+     } else {
+         cx->markAtomValue(*vp);
+     }
+     return true;
+ }
+ 
+ JSObject*
+@@ -1395,39 +1489,44 @@ InitializePropertiesFromCompatibleNative
+     if (src->staticPrototype() == dst->staticPrototype()) {
+         shape = src->lastProperty();
+     } else {
+         // We need to generate a new shape for dst that has dst's proto but all
+         // the property information from src.  Note that we asserted above that
+         // dst's object flags are 0.
+         shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->taggedProto(),
+                                             dst->numFixedSlots(), 0);
+-        if (!shape)
++        if (!shape) {
+             return false;
++        }
+ 
+         // Get an in-order list of the shapes in the src object.
+         Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
+         for (Shape::Range<NoGC> r(src->lastProperty()); !r.empty(); r.popFront()) {
+-            if (!shapes.append(&r.front()))
++            if (!shapes.append(&r.front())) {
+                 return false;
++            }
+         }
+         Reverse(shapes.begin(), shapes.end());
+ 
+         for (Shape* shapeToClone : shapes) {
+             Rooted<StackShape> child(cx, StackShape(shapeToClone));
+             shape = cx->zone()->propertyTree().getChild(cx, shape, child);
+-            if (!shape)
++            if (!shape) {
+                 return false;
++            }
+         }
+     }
+     size_t span = shape->slotSpan();
+-    if (!dst->setLastProperty(cx, shape))
++    if (!dst->setLastProperty(cx, shape)) {
+         return false;
+-    for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < span; i++)
++    }
++    for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < span; i++) {
+         dst->setSlot(i, src->getSlot(i));
++    }
+ 
+     return true;
+ }
+ 
+ JS_FRIEND_API(bool)
+ JS_InitializePropertiesFromCompatibleNativeObject(JSContext* cx,
+                                                   HandleObject dst,
+                                                   HandleObject src)
+@@ -1461,90 +1560,100 @@ js::XDRObjectLiteral(XDRState<mode>* xdr
+ 
+     RootedValue tmpValue(cx), tmpIdValue(cx);
+     RootedId tmpId(cx);
+ 
+     if (isArray) {
+         Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
+         if (mode == XDR_ENCODE) {
+             RootedArrayObject arr(cx, &obj->as<ArrayObject>());
+-            if (!GetScriptArrayObjectElements(arr, &values))
++            if (!GetScriptArrayObjectElements(arr, &values)) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+         }
+ 
+         uint32_t initialized;
+-        if (mode == XDR_ENCODE)
++        if (mode == XDR_ENCODE) {
+             initialized = values.length();
++        }
+         MOZ_TRY(xdr->codeUint32(&initialized));
+-        if (mode == XDR_DECODE && !values.appendN(MagicValue(JS_ELEMENTS_HOLE), initialized))
++        if (mode == XDR_DECODE && !values.appendN(MagicValue(JS_ELEMENTS_HOLE), initialized)) {
+             return xdr->fail(JS::TranscodeResult_Throw);
++        }
+ 
+         // Recursively copy dense elements.
+-        for (unsigned i = 0; i < initialized; i++)
++        for (unsigned i = 0; i < initialized; i++) {
+             MOZ_TRY(XDRScriptConst(xdr, values[i]));
++        }
+ 
+         uint32_t copyOnWrite;
+         if (mode == XDR_ENCODE) {
+             copyOnWrite = obj->is<ArrayObject>() &&
+                           obj->as<ArrayObject>().denseElementsAreCopyOnWrite();
+         }
+         MOZ_TRY(xdr->codeUint32(&copyOnWrite));
+ 
+         if (mode == XDR_DECODE) {
+             ObjectGroup::NewArrayKind arrayKind = copyOnWrite
+                                                   ? ObjectGroup::NewArrayKind::CopyOnWrite
+                                                   : ObjectGroup::NewArrayKind::Normal;
+             obj.set(ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
+                                                 TenuredObject, arrayKind));
+-            if (!obj)
++            if (!obj) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+         }
+ 
+         return Ok();
+     }
+ 
+     // Code the properties in the object.
+     Rooted<IdValueVector> properties(cx, IdValueVector(cx));
+-    if (mode == XDR_ENCODE && !GetScriptPlainObjectProperties(obj, &properties))
++    if (mode == XDR_ENCODE && !GetScriptPlainObjectProperties(obj, &properties)) {
+         return xdr->fail(JS::TranscodeResult_Throw);
++    }
+ 
+     uint32_t nproperties = properties.length();
+     MOZ_TRY(xdr->codeUint32(&nproperties));
+ 
+-    if (mode == XDR_DECODE && !properties.appendN(IdValuePair(), nproperties))
++    if (mode == XDR_DECODE && !properties.appendN(IdValuePair(), nproperties)) {
+         return xdr->fail(JS::TranscodeResult_Throw);
++    }
+ 
+     for (size_t i = 0; i < nproperties; i++) {
+         if (mode == XDR_ENCODE) {
+             tmpIdValue = IdToValue(properties[i].get().id);
+             tmpValue = properties[i].get().value;
+         }
+ 
+         MOZ_TRY(XDRScriptConst(xdr, &tmpIdValue));
+         MOZ_TRY(XDRScriptConst(xdr, &tmpValue));
+ 
+         if (mode == XDR_DECODE) {
+-            if (!ValueToId<CanGC>(cx, tmpIdValue, &tmpId))
++            if (!ValueToId<CanGC>(cx, tmpIdValue, &tmpId)) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+             properties[i].get().id = tmpId;
+             properties[i].get().value = tmpValue;
+         }
+     }
+ 
+     // Code whether the object is a singleton.
+     uint32_t isSingleton;
+-    if (mode == XDR_ENCODE)
++    if (mode == XDR_ENCODE) {
+         isSingleton = obj->isSingleton() ? 1 : 0;
++    }
+     MOZ_TRY(xdr->codeUint32(&isSingleton));
+ 
+     if (mode == XDR_DECODE) {
+         NewObjectKind newKind = isSingleton ? SingletonObject : TenuredObject;
+         obj.set(ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind));
+-        if (!obj)
++        if (!obj) {
+             return xdr->fail(JS::TranscodeResult_Throw);
++        }
+     }
+ 
+     return Ok();
+ }
+ 
+ template XDRResult
+ js::XDRObjectLiteral(XDRState<XDR_ENCODE>* xdr, MutableHandleObject obj);
+ 
+@@ -1632,23 +1741,25 @@ ProxyObject::initExternalValueArrayAfter
+ 
+     // |values| contains the private slot and the reserved slots.
+     MOZ_ASSERT(values.length() == 1 + nreserved);
+ 
+     size_t nbytes = js::detail::ProxyValueArray::sizeOf(nreserved);
+ 
+     auto* valArray =
+         reinterpret_cast<js::detail::ProxyValueArray*>(cx->zone()->pod_malloc<uint8_t>(nbytes));
+-    if (!valArray)
++    if (!valArray) {
+         return false;
++    }
+ 
+     valArray->privateSlot = values[0];
+ 
+-    for (size_t i = 0; i < nreserved; i++)
++    for (size_t i = 0; i < nreserved; i++) {
+         valArray->reservedSlots.slots[i] = values[i + 1];
++    }
+ 
+     // Note: we allocate external slots iff the proxy had an inline
+     // ProxyValueArray, so at this point reservedSlots points into the
+     // old object and we don't have to free anything.
+     data.reservedSlots = &valArray->reservedSlots;
+     return true;
+ }
+ 
+@@ -1661,20 +1772,22 @@ JSObject::swap(JSContext* cx, HandleObje
+                IsBackgroundFinalized(b->asTenured().getAllocKind()));
+     MOZ_ASSERT(a->compartment() == b->compartment());
+ 
+     // You must have entered the objects' compartment before calling this.
+     MOZ_ASSERT(cx->compartment() == a->compartment());
+ 
+     AutoEnterOOMUnsafeRegion oomUnsafe;
+ 
+-    if (!JSObject::getGroup(cx, a))
++    if (!JSObject::getGroup(cx, a)) {
+         oomUnsafe.crash("JSObject::swap");
+-    if (!JSObject::getGroup(cx, b))
++    }
++    if (!JSObject::getGroup(cx, b)) {
+         oomUnsafe.crash("JSObject::swap");
++    }
+ 
+     /*
+      * Neither object may be in the nursery, but ensure we update any embedded
+      * nursery pointers in either object.
+      */
+     MOZ_ASSERT(!IsInsideNursery(a) && !IsInsideNursery(b));
+     cx->runtime()->gc.storeBuffer().putWholeCell(a);
+     cx->runtime()->gc.storeBuffer().putWholeCell(b);
+@@ -2012,20 +2125,22 @@ JSObject::fixupAfterMovingGC()
+     // For copy-on-write objects that don't own their elements, fix up the
+     // elements pointer if it points to inline elements in the owning object.
+     if (is<NativeObject>()) {
+         NativeObject& obj = as<NativeObject>();
+         if (obj.denseElementsAreCopyOnWrite()) {
+             NativeObject* owner = obj.getElementsHeader()->ownerObject();
+             // Get the new owner pointer but don't call MaybeForwarded as we
+             // don't need to access the object's shape.
+-            if (IsForwarded(owner))
++            if (IsForwarded(owner)) {
+                 owner = Forwarded(owner);
+-            if (owner != &obj && owner->hasFixedElements())
++            }
++            if (owner != &obj && owner->hasFixedElements()) {
+                 obj.elements_ = owner->getElementsHeader()->elements();
++            }
+             MOZ_ASSERT(!IsForwarded(obj.getElementsHeader()->ownerObject().get()));
+         }
+     }
+ }
+ 
+ static bool
+ ReshapeForProtoMutation(JSContext* cx, HandleObject obj)
+ {
+@@ -2055,85 +2170,94 @@ ReshapeForProtoMutation(JSContext* cx, H
+     RootedObject pobj(cx, obj);
+ 
+     while (pobj && pobj->isNative()) {
+         if (pobj->isSingleton()) {
+             // If object was converted to a singleton it should have cleared
+             // any UNCACHEABLE_PROTO flags.
+             MOZ_ASSERT(!pobj->hasUncacheableProto());
+ 
+-            if (!NativeObject::reshapeForProtoMutation(cx, pobj.as<NativeObject>()))
++            if (!NativeObject::reshapeForProtoMutation(cx, pobj.as<NativeObject>())) {
+                 return false;
++            }
+         } else {
+-            if (!JSObject::setUncacheableProto(cx, pobj))
++            if (!JSObject::setUncacheableProto(cx, pobj)) {
+                 return false;
++            }
+         }
+ 
+-        if (!obj->isDelegate())
++        if (!obj->isDelegate()) {
+             break;
++        }
+ 
+         pobj = pobj->staticPrototype();
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+ SetClassAndProto(JSContext* cx, HandleObject obj,
+                  const Class* clasp, Handle<js::TaggedProto> proto)
+ {
+     // Regenerate object shape (and possibly prototype shape) to invalidate JIT
+     // code that is affected by a prototype mutation.
+-    if (!ReshapeForProtoMutation(cx, obj))
++    if (!ReshapeForProtoMutation(cx, obj)) {
+         return false;
++    }
+ 
+     if (proto.isObject()) {
+         RootedObject protoObj(cx, proto.toObject());
+-        if (!JSObject::setDelegate(cx, protoObj))
++        if (!JSObject::setDelegate(cx, protoObj)) {
+             return false;
++        }
+     }
+ 
+     if (obj->isSingleton()) {
+         /*
+          * Just splice the prototype, but mark the properties as unknown for
+          * consistent behavior.
+          */
+-        if (!JSObject::splicePrototype(cx, obj, clasp, proto))
++        if (!JSObject::splicePrototype(cx, obj, clasp, proto)) {
+             return false;
++        }
+         MarkObjectGroupUnknownProperties(cx, obj->group());
+         return true;
+     }
+ 
+     RootedObjectGroup oldGroup(cx, obj->group());
+ 
+     ObjectGroup* newGroup;
+     if (oldGroup->maybeInterpretedFunction()) {
+         // We're changing the group/proto of a scripted function. Create a new
+         // group so we can keep track of the interpreted function for Ion
+         // inlining.
+         MOZ_ASSERT(obj->is<JSFunction>());
+         newGroup = ObjectGroupRealm::makeGroup(cx, oldGroup->realm(), &JSFunction::class_, proto);
+-        if (!newGroup)
++        if (!newGroup) {
+             return false;
++        }
+         newGroup->setInterpretedFunction(oldGroup->maybeInterpretedFunction());
+     } else {
+         newGroup = ObjectGroup::defaultNewGroup(cx, clasp, proto);
+-        if (!newGroup)
++        if (!newGroup) {
+             return false;
++        }
+     }
+ 
+     obj->setGroup(newGroup);
+ 
+     // Add the object's property types to the new group.
+     AutoSweepObjectGroup sweep(newGroup);
+     if (!newGroup->unknownProperties(sweep)) {
+-        if (obj->isNative())
++        if (obj->isNative()) {
+             AddPropertyTypesAfterProtoChange(cx, &obj->as<NativeObject>(), oldGroup);
+-        else
++        } else {
+             MarkObjectGroupUnknownProperties(cx, newGroup);
++        }
+     }
+ 
+     // Type sets containing this object will contain the old group but not the
+     // new group of the object, so we need to treat all such type sets as
+     // unknown.
+     MarkObjectGroupUnknownProperties(cx, oldGroup);
+ 
+     return true;
+@@ -2143,18 +2267,19 @@ SetClassAndProto(JSContext* cx, HandleOb
+ JSObject::changeToSingleton(JSContext* cx, HandleObject obj)
+ {
+     MOZ_ASSERT(!obj->isSingleton());
+ 
+     MarkObjectGroupUnknownProperties(cx, obj->group());
+ 
+     ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->group(), obj->getClass(),
+                                                          obj->taggedProto());
+-    if (!group)
++    if (!group) {
+         return false;
++    }
+ 
+     obj->group_ = group;
+     return true;
+ }
+ 
+ /**
+  * Returns the original Object.prototype from the embedding-provided incumbent
+  * global.
+@@ -2285,20 +2410,22 @@ bool
+ js::LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* envChain,
+                    JSObject** objp, JSObject** pobjp, PropertyResult* propp)
+ {
+     AutoAssertNoException nogc(cx);
+ 
+     MOZ_ASSERT(!*objp && !*pobjp && !*propp);
+ 
+     for (JSObject* env = envChain; env; env = env->enclosingEnvironment()) {
+-        if (env->getOpsLookupProperty())
++        if (env->getOpsLookupProperty()) {
+             return false;
+-        if (!LookupPropertyInline<NoGC>(cx, &env->as<NativeObject>(), NameToId(name), pobjp, propp))
++        }
++        if (!LookupPropertyInline<NoGC>(cx, &env->as<NativeObject>(), NameToId(name), pobjp, propp)) {
+             return false;
++        }
+         if (*propp) {
+             *objp = env;
+             return true;
+         }
+     }
+ 
+     return true;
+ }
+@@ -2309,20 +2436,22 @@ js::LookupNameWithGlobalDefault(JSContex
+ {
+     RootedId id(cx, NameToId(name));
+ 
+     RootedObject pobj(cx);
+     Rooted<PropertyResult> prop(cx);
+ 
+     RootedObject env(cx, envChain);
+     for (; !env->is<GlobalObject>(); env = env->enclosingEnvironment()) {
+-        if (!LookupProperty(cx, env, id, &pobj, &prop))
++        if (!LookupProperty(cx, env, id, &pobj, &prop)) {
+             return false;
+-        if (prop)
++        }
++        if (prop) {
+             break;
++        }
+     }
+ 
+     objp.set(env);
+     return true;
+ }
+ 
+ bool
+ js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject envChain,
+@@ -2330,91 +2459,100 @@ js::LookupNameUnqualified(JSContext* cx,
+ {
+     RootedId id(cx, NameToId(name));
+ 
+     RootedObject pobj(cx);
+     Rooted<PropertyResult> prop(cx);
+ 
+     RootedObject env(cx, envChain);
+     for (; !env->isUnqualifiedVarObj(); env = env->enclosingEnvironment()) {
+-        if (!LookupProperty(cx, env, id, &pobj, &prop))
++        if (!LookupProperty(cx, env, id, &pobj, &prop)) {
+             return false;
+-        if (prop)
++        }
++        if (prop) {
+             break;
++        }
+     }
+ 
+     // See note above RuntimeLexicalErrorObject.
+     if (pobj == env) {
+         bool isTDZ = false;
+         if (prop && name != cx->names().dotThis) {
+             // Treat Debugger environments specially for TDZ checks, as they
+             // look like non-native environments but in fact wrap native
+             // environments.
+             if (env->is<DebugEnvironmentProxy>()) {
+                 RootedValue v(cx);
+                 Rooted<DebugEnvironmentProxy*> envProxy(cx, &env->as<DebugEnvironmentProxy>());
+-                if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id, &v))
++                if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id, &v)) {
+                     return false;
++                }
+                 isTDZ = IsUninitializedLexical(v);
+             } else {
+                 isTDZ = IsUninitializedLexicalSlot(env, prop);
+             }
+         }
+ 
+         if (isTDZ) {
+             env = RuntimeLexicalErrorObject::create(cx, env, JSMSG_UNINITIALIZED_LEXICAL);
+-            if (!env)
++            if (!env) {
+                 return false;
++            }
+         } else if (env->is<LexicalEnvironmentObject>() && !prop.shape()->writable()) {
+             // Assigning to a named lambda callee name is a no-op in sloppy mode.
+             Rooted<LexicalEnvironmentObject*> lexicalEnv(cx, &env->as<LexicalEnvironmentObject>());
+             if (lexicalEnv->isExtensible() ||
+                 lexicalEnv->scope().kind() != ScopeKind::NamedLambda)
+             {
+                 MOZ_ASSERT(name != cx->names().dotThis);
+                 env = RuntimeLexicalErrorObject::create(cx, env, JSMSG_BAD_CONST_ASSIGN);
+-                if (!env)
++                if (!env) {
+                     return false;
++                }
+             }
+         }
+     }
+ 
+     objp.set(env);
+     return true;
+ }
+ 
+ bool
+ js::HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id, bool* result)
+ {
+-    if (obj->is<ProxyObject>())
++    if (obj->is<ProxyObject>()) {
+         return Proxy::hasOwn(cx, obj, id, result);
++    }
+ 
+     if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
+         Rooted<PropertyDescriptor> desc(cx);
+-        if (!op(cx, obj, id, &desc))
++        if (!op(cx, obj, id, &desc)) {
+             return false;
++        }
+         *result = !!desc.object();
+         return true;
+     }
+ 
+     Rooted<PropertyResult> prop(cx);
+-    if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &prop))
++    if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &prop)) {
+         return false;
++    }
+     *result = prop.isFound();
+     return true;
+ }
+ 
+ bool
+ js::LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id, JSObject** objp,
+                        PropertyResult* propp)
+ {
+     bool isTypedArrayOutOfRange = false;
+     do {
+-        if (!LookupOwnPropertyPure(cx, obj, id, propp, &isTypedArrayOutOfRange))
++        if (!LookupOwnPropertyPure(cx, obj, id, propp, &isTypedArrayOutOfRange)) {
+             return false;
++        }
+ 
+         if (*propp) {
+             *objp = obj;
+             return true;
+         }
+ 
+         if (isTypedArrayOutOfRange) {
+             *objp = nullptr;
+@@ -2429,50 +2567,53 @@ js::LookupPropertyPure(JSContext* cx, JS
+     return true;
+ }
+ 
+ bool
+ js::LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, PropertyResult* propp,
+                           bool* isTypedArrayOutOfRange /* = nullptr */)
+ {
+     JS::AutoCheckCannotGC nogc;
+-    if (isTypedArrayOutOfRange)
++    if (isTypedArrayOutOfRange) {
+         *isTypedArrayOutOfRange = false;
++    }
+ 
+     if (obj->isNative()) {
+         // Search for a native dense element, typed array element, or property.
+ 
+         if (JSID_IS_INT(id) && obj->as<NativeObject>().containsDenseElement(JSID_TO_INT(id))) {
+             propp->setDenseOrTypedArrayElement();
+             return true;
+         }
+ 
+         if (obj->is<TypedArrayObject>()) {
+             uint64_t index;
+             if (IsTypedArrayIndex(id, &index)) {
+                 if (index < obj->as<TypedArrayObject>().length()) {
+                     propp->setDenseOrTypedArrayElement();
+                 } else {
+                     propp->setNotFound();
+-                    if (isTypedArrayOutOfRange)
++                    if (isTypedArrayOutOfRange) {
+                         *isTypedArrayOutOfRange = true;
++                    }
+                 }
+                 return true;
+             }
+         }
+ 
+         if (Shape* shape = obj->as<NativeObject>().lookupPure(id)) {
+             propp->setNativeProperty(shape);
+             return true;
+         }
+ 
+         // Fail if there's a resolve hook, unless the mayResolve hook tells
+         // us the resolve hook won't define a property with this id.
+-        if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
++        if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) {
+             return false;
++        }
+     } else if (obj->is<UnboxedPlainObject>()) {
+         if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
+             propp->setNonNativeProperty();
+             return true;
+         }
+     } else if (obj->is<TypedObject>()) {
+         if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
+             propp->setNonNativeProperty();
+@@ -2556,85 +2697,93 @@ NativeGetGetterPureInline(PropertyResult
+ 
+ bool
+ js::GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp)
+ {
+     /* Just like GetPropertyPure, but get getter function, without invoking
+      * it. */
+     JSObject* pobj;
+     PropertyResult prop;
+-    if (!LookupPropertyPure(cx, obj, id, &pobj, &prop))
++    if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) {
+         return false;
++    }
+ 
+     if (!prop) {
+         *fp = nullptr;
+         return true;
+     }
+ 
+     return prop.isNativeProperty() && NativeGetGetterPureInline(prop, fp);
+ }
+ 
+ bool
+ js::GetOwnGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp)
+ {
+     JS::AutoCheckCannotGC nogc;
+     PropertyResult prop;
+-    if (!LookupOwnPropertyPure(cx, obj, id, &prop))
++    if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
+         return false;
++    }
+ 
+     if (!prop) {
+         *fp = nullptr;
+         return true;
+     }
+ 
+     return prop.isNativeProperty() && NativeGetGetterPureInline(prop, fp);
+ }
+ 
+ bool
+ js::GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id, JSNative* native)
+ {
+     JS::AutoCheckCannotGC nogc;
+     *native = nullptr;
+     PropertyResult prop;
+-    if (!LookupOwnPropertyPure(cx, obj, id, &prop))
++    if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
+         return false;
+-
+-    if (!prop || prop.isDenseOrTypedArrayElement() || !prop.shape()->hasGetterObject())
++    }
++
++    if (!prop || prop.isDenseOrTypedArrayElement() || !prop.shape()->hasGetterObject()) {
+         return true;
++    }
+ 
+     JSObject* getterObj = prop.shape()->getterObject();
+-    if (!getterObj->is<JSFunction>())
++    if (!getterObj->is<JSFunction>()) {
+         return true;
++    }
+ 
+     JSFunction* getter = &getterObj->as<JSFunction>();
+-    if (!getter->isNative())
++    if (!getter->isNative()) {
+         return true;
++    }
+ 
+     *native = getter->native();
+     return true;
+ }
+ 
+ bool
+ js::HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result)
+ {
+     PropertyResult prop;
+-    if (!LookupOwnPropertyPure(cx, obj, id, &prop))
++    if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
+         return false;
++    }
+ 
+     *result = prop && !prop.isDenseOrTypedArrayElement() &&
+               prop.shape()->isDataProperty();
+     return true;
+ }
+ 
+ bool
+ js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary,
+                            MutableHandleObject protop)
+ {
+-    if (obj->is<js::ProxyObject>())
++    if (obj->is<js::ProxyObject>()) {
+         return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop);
++    }
+ 
+     *isOrdinary = true;
+     protop.set(obj->staticPrototype());
+     return true;
+ }
+ 
+ /*** ES6 standard internal methods ***************************************************************/
+ 
+@@ -2778,18 +2927,19 @@ js::PreventExtensions(JSContext* cx, Han
+ }
+ 
+ bool
+ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
+                              MutableHandle<PropertyDescriptor> desc)
+ {
+     if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
+         bool ok = op(cx, obj, id, desc);
+-        if (ok)
++        if (ok) {
+             desc.assertCompleteIfFound();
++        }
+         return ok;
+     }
+ 
+     return NativeGetOwnPropertyDescriptor(cx, obj.as<NativeObject>(), id, desc);
+ }
+ 
+ bool
+ js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc)
+@@ -2932,59 +3082,65 @@ js::DefineAccessorElement(JSContext* cx,
+     return DefineAccessorProperty(cx, obj, id, getter, setter, attrs);
+ }
+ 
+ bool
+ js::DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
+                       unsigned attrs)
+ {
+     RootedId id(cx);
+-    if (!IndexToId(cx, index, &id))
++    if (!IndexToId(cx, index, &id)) {
+         return false;
++    }
+     return DefineDataProperty(cx, obj, id, value, attrs);
+ }
+ 
+ /*** SpiderMonkey nonstandard internal methods ***************************************************/
+ 
+ bool
+ js::SetImmutablePrototype(JSContext* cx, HandleObject obj, bool* succeeded)
+ {
+     if (obj->hasDynamicPrototype()) {
+         MOZ_ASSERT(!cx->helperThread());
+         return Proxy::setImmutablePrototype(cx, obj, succeeded);
+     }
+ 
+-    if (!JSObject::setFlags(cx, obj, BaseShape::IMMUTABLE_PROTOTYPE))
++    if (!JSObject::setFlags(cx, obj, BaseShape::IMMUTABLE_PROTOTYPE)) {
+         return false;
++    }
+     *succeeded = true;
+     return true;
+ }
+ 
+ bool
+ js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
+                           MutableHandle<PropertyDescriptor> desc)
+ {
+     RootedObject pobj(cx);
+ 
+     for (pobj = obj; pobj;) {
+         if (pobj->is<ProxyObject>()) {
+             bool ok = Proxy::getPropertyDescriptor(cx, pobj, id, desc);
+-            if (ok)
++            if (ok) {
+                 desc.assertCompleteIfFound();
++            }
+             return ok;
+         }
+ 
+-        if (!GetOwnPropertyDescriptor(cx, pobj, id, desc))
++        if (!GetOwnPropertyDescriptor(cx, pobj, id, desc)) {
+             return false;
+-
+-        if (desc.object())
++        }
++
++        if (desc.object()) {
+             return true;
+-
+-        if (!GetPrototype(cx, pobj, &pobj))
++        }
++
++        if (!GetPrototype(cx, pobj, &pobj)) {
+             return false;
++        }
+     }
+ 
+     MOZ_ASSERT(!desc.object());
+     return true;
+ }
+ 
+ /* * */
+ 
+@@ -2992,37 +3148,41 @@ extern bool
+ PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
+                      js::PinningBehavior pin = js::DoNotPinAtom);
+ 
+ static bool
+ DefineFunctionFromSpec(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs, unsigned flags,
+                        DefineAsIntrinsic intrinsic)
+ {
+     RootedId id(cx);
+-    if (!PropertySpecNameToId(cx, fs->name, &id))
++    if (!PropertySpecNameToId(cx, fs->name, &id)) {
+         return false;
++    }
+ 
+     JSFunction* fun = NewFunctionFromSpec(cx, fs, id);
+-    if (!fun)
++    if (!fun) {
+         return false;
+-
+-    if (intrinsic == AsIntrinsic)
++    }
++
++    if (intrinsic == AsIntrinsic) {
+         fun->setIsIntrinsic();
++    }
+ 
+     RootedValue funVal(cx, ObjectValue(*fun));
+     return DefineDataProperty(cx, obj, id, funVal, flags & ~JSFUN_FLAGS_MASK);
+ }
+ 
+ bool
+ js::DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
+                     DefineAsIntrinsic intrinsic)
+ {
+     for (; fs->name; fs++) {
+-        if (!DefineFunctionFromSpec(cx, obj, fs, fs->flags, intrinsic))
++        if (!DefineFunctionFromSpec(cx, obj, fs, fs->flags, intrinsic)) {
+             return false;
++        }
+     }
+     return true;
+ }
+ 
+ 
+ /*** ToPrimitive *************************************************************/
+ 
+ /*
+@@ -3031,18 +3191,19 @@ js::DefineFunctions(JSContext* cx, Handl
+  * as |this|, returning the result in *vp.
+  *
+  * This is a mini-abstraction for ES6 draft rev 36 (2015 Mar 17),
+  * 7.1.1, second algorithm (OrdinaryToPrimitive), steps 5.a-c.
+  */
+ static bool
+ MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
+ {
+-    if (!GetProperty(cx, obj, obj, id, vp))
++    if (!GetProperty(cx, obj, obj, id, vp)) {
+         return false;
++    }
+     if (!IsCallable(vp)) {
+         vp.setObject(*obj);
+         return true;
+     }
+ 
+     return js::Call(cx, vp, obj, vp);
+ }
+ 
+@@ -3050,18 +3211,19 @@ static bool
+ ReportCantConvert(JSContext* cx, unsigned errorNumber, HandleObject obj, JSType hint)
+ {
+     const Class* clasp = obj->getClass();
+ 
+     // Avoid recursive death when decompiling in ReportValueError.
+     RootedString str(cx);
+     if (hint == JSTYPE_STRING) {
+         str = JS_AtomizeAndPinString(cx, clasp->name);
+-        if (!str)
++        if (!str) {
+             return false;
++        }
+     } else {
+         str = nullptr;
+     }
+ 
+     RootedValue val(cx, ObjectValue(*obj));
+     ReportValueError(cx, errorNumber, JSDVG_SEARCH_STACK, val, str,
+                      hint == JSTYPE_UNDEFINED
+                      ? "primitive type"
+@@ -3084,26 +3246,30 @@ JS::OrdinaryToPrimitive(JSContext* cx, H
+         if (clasp == &StringObject::class_) {
+             StringObject* nobj = &obj->as<StringObject>();
+             if (HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) {
+                 vp.setString(nobj->unbox());
+                 return true;
+             }
+         }
+ 
+-        if (!MaybeCallMethod(cx, obj, id, vp))
++        if (!MaybeCallMethod(cx, obj, id, vp)) {
+             return false;
+-        if (vp.isPrimitive())
++        }
++        if (vp.isPrimitive()) {
+             return true;
++        }
+ 
+         id = NameToId(cx->names().valueOf);
+-        if (!MaybeCallMethod(cx, obj, id, vp))
++        if (!MaybeCallMethod(cx, obj, id, vp)) {
+             return false;
+-        if (vp.isPrimitive())
++        }
++        if (vp.isPrimitive()) {
+             return true;
++        }
+     } else {
+         id = NameToId(cx->names().valueOf);
+ 
+         /* Optimize new String(...).valueOf(). */
+         if (clasp == &StringObject::class_) {
+             StringObject* nobj = &obj->as<StringObject>();
+             if (HasNativeMethodPure(nobj, cx->names().valueOf, str_toString, cx)) {
+                 vp.setString(nobj->unbox());
+@@ -3115,26 +3281,30 @@ JS::OrdinaryToPrimitive(JSContext* cx, H
+         if (clasp == &NumberObject::class_) {
+             NumberObject* nobj = &obj->as<NumberObject>();
+             if (HasNativeMethodPure(nobj, cx->names().valueOf, num_valueOf, cx)) {
+                 vp.setNumber(nobj->unbox());
+                 return true;
+             }
+         }
+ 
+-        if (!MaybeCallMethod(cx, obj, id, vp))
++        if (!MaybeCallMethod(cx, obj, id, vp)) {
+             return false;
+-        if (vp.isPrimitive())
++        }
++        if (vp.isPrimitive()) {
+             return true;
++        }
+ 
+         id = NameToId(cx->names().toString);
+-        if (!MaybeCallMethod(cx, obj, id, vp))
++        if (!MaybeCallMethod(cx, obj, id, vp)) {
+             return false;
+-        if (vp.isPrimitive())
++        }
++        if (vp.isPrimitive()) {
+             return true;
++        }
+     }
+ 
+     return ReportCantConvert(cx, JSMSG_CANT_CONVERT_TO, obj, hint);
+ }
+ 
+ bool
+ js::ToPrimitiveSlow(JSContext* cx, JSType preferredType, MutableHandleValue vp)
+ {
+@@ -3223,20 +3393,22 @@ js::IsDelegateOfObject(JSContext* cx, Ha
+ 
+ JSObject*
+ js::PrimitiveToObject(JSContext* cx, const Value& v)
+ {
+     if (v.isString()) {
+         Rooted<JSString*> str(cx, v.toString());
+         return StringObject::create(cx, str);
+     }
+-    if (v.isNumber())
++    if (v.isNumber()) {
+         return NumberObject::create(cx, v.toNumber());
+-    if (v.isBoolean())
++    }
++    if (v.isBoolean()) {
+         return BooleanObject::create(cx, v.toBoolean());
++    }
+ #ifdef ENABLE_BIGINT
+     if (v.isSymbol()) {
+         RootedSymbol symbol(cx, v.toSymbol());
+         return SymbolObject::create(cx, symbol);
+     }
+     MOZ_ASSERT(v.isBigInt());
+     RootedBigInt bigInt(cx, v.toBigInt());
+     return BigIntObject::create(cx, bigInt);
+@@ -3332,43 +3504,47 @@ GetObjectSlotNameFunctor::operator()(JS:
+     }
+ 
+     if (!shape) {
+         do {
+             const char* slotname = nullptr;
+             const char* pattern = nullptr;
+             if (obj->is<GlobalObject>()) {
+                 pattern = "CLASS_OBJECT(%s)";
+-                if (false)
++                if (false) {
+                     ;
++                }
+ #define TEST_SLOT_MATCHES_PROTOTYPE(name,init,clasp) \
+                 else if ((JSProto_##name) == slot) { slotname = js_##name##_str; }
+                 JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
+ #undef TEST_SLOT_MATCHES_PROTOTYPE
+             } else {
+                 pattern = "%s";
+                 if (obj->is<EnvironmentObject>()) {
+                     if (slot == EnvironmentObject::enclosingEnvironmentSlot()) {
+                         slotname = "enclosing_environment";
+                     } else if (obj->is<CallObject>()) {
+-                        if (slot == CallObject::calleeSlot())
++                        if (slot == CallObject::calleeSlot()) {
+                             slotname = "callee_slot";
++                        }
+                     } else if (obj->is<WithEnvironmentObject>()) {
+-                        if (slot == WithEnvironmentObject::objectSlot())
++                        if (slot == WithEnvironmentObject::objectSlot()) {
+                             slotname = "with_object";
+-                        else if (slot == WithEnvironmentObject::thisSlot())
++                        } else if (slot == WithEnvironmentObject::thisSlot()) {
+                             slotname = "with_this";
++                        }
+                     }
+                 }
+             }
+ 
+-            if (slotname)
++            if (slotname) {
+                 snprintf(buf, bufsize, pattern, slotname);
+-            else
++            } else {
+                 snprintf(buf, bufsize, "**UNKNOWN SLOT %" PRIu32 "**", slot);
++            }
+         } while (false);
+     } else {
+         jsid propid = shape->propid();
+         if (JSID_IS_INT(propid)) {
+             snprintf(buf, bufsize, "%" PRId32, JSID_TO_INT(propid));
+         } else if (JSID_IS_ATOM(propid)) {
+             PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
+         } else if (JSID_IS_SYMBOL(propid)) {
+@@ -3387,28 +3563,29 @@ GetObjectSlotNameFunctor::operator()(JS:
+  * Routines to print out values during debugging.  These are FRIEND_API to help
+  * the debugger find them and to support temporarily hacking js::Dump* calls
+  * into other code.
+  */
+ 
+ static void
+ dumpValue(const Value& v, js::GenericPrinter& out)
+ {
+-    if (v.isNull())
++    if (v.isNull()) {
+         out.put("null");
+-    else if (v.isUndefined())
++    } else if (v.isUndefined()) {
+         out.put("undefined");
+-    else if (v.isInt32())
++    } else if (v.isInt32()) {
+         out.printf("%d", v.toInt32());
+-    else if (v.isDouble())
++    } else if (v.isDouble()) {
+         out.printf("%g", v.toDouble());
+-    else if (v.isString())
++    } else if (v.isString()) {
+         v.toString()->dumpNoNewline(out);
+-    else if (v.isSymbol())
++    } else if (v.isSymbol()) {
+         v.toSymbol()->dump(out);
++    }
+     else if (v.isObject() && v.toObject().is<JSFunction>()) {
+         JSFunction* fun = &v.toObject().as<JSFunction>();
+         if (fun->displayAtom()) {
+             out.put("<function ");
+             EscapedStringPrinter(out, fun->displayAtom(), 0);
+         } else {
+             out.put("<unnamed function");
+         }
+@@ -3421,20 +3598,21 @@ dumpValue(const Value& v, js::GenericPri
+     } else if (v.isObject()) {
+         JSObject* obj = &v.toObject();
+         const Class* clasp = obj->getClass();
+         out.printf("<%s%s at %p>",
+                 clasp->name,
+                 (clasp == &PlainObject::class_) ? "" : " object",
+                 (void*) obj);
+     } else if (v.isBoolean()) {
+-        if (v.toBoolean())
++        if (v.toBoolean()) {
+             out.put("true");
+-        else
++        } else {
+             out.put("false");
++        }
+     } else if (v.isMagic()) {
+         out.put("<invalid");
+         switch (v.whyMagic()) {
+           case JS_ELEMENTS_HOLE:     out.put(" elements hole");      break;
+           case JS_NO_ITER_VALUE:     out.put(" no iter value");      break;
+           case JS_GENERATOR_CLOSING: out.put(" generator closing");  break;
+           case JS_OPTIMIZED_OUT:     out.put(" optimized out");      break;
+           default:                   out.put(" ?!");                 break;
+@@ -3475,49 +3653,53 @@ js::DumpId(jsid id, js::GenericPrinter& 
+     dumpValue(IdToValue(id), out);
+     out.putChar('\n');
+ }
+ 
+ static void
+ DumpProperty(const NativeObject* obj, Shape& shape, js::GenericPrinter& out)
+ {
+     jsid id = shape.propid();
+-    if (JSID_IS_ATOM(id))
++    if (JSID_IS_ATOM(id)) {
+         JSID_TO_ATOM(id)->dumpCharsNoNewline(out);
+-    else if (JSID_IS_INT(id))
++    } else if (JSID_IS_INT(id)) {
+        out.printf("%d", JSID_TO_INT(id));
+-    else if (JSID_IS_SYMBOL(id))
++    } else if (JSID_IS_SYMBOL(id)) {
+         JSID_TO_SYMBOL(id)->dump(out);
+-    else
++    } else {
+         out.printf("id %p", reinterpret_cast<void*>(JSID_BITS(id)));
++    }
+ 
+     if (shape.isDataProperty()) {
+         out.printf(": ");
+         dumpValue(obj->getSlot(shape.maybeSlot()), out);
+     }
+ 
+     out.printf(" (shape %p", (void*) &shape);
+ 
+     uint8_t attrs = shape.attributes();
+     if (attrs & JSPROP_ENUMERATE) out.put(" enumerate");
+     if (attrs & JSPROP_READONLY) out.put(" readonly");
+     if (attrs & JSPROP_PERMANENT) out.put(" permanent");
+ 
+-    if (shape.hasGetterValue())
++    if (shape.hasGetterValue()) {
+         out.printf(" getterValue %p", shape.getterObject());
+-    else if (!shape.hasDefaultGetter())
++    } else if (!shape.hasDefaultGetter()) {
+         out.printf(" getterOp %p", JS_FUNC_TO_DATA_PTR(void*, shape.getterOp()));
+-
+-    if (shape.hasSetterValue())
++    }
++
++    if (shape.hasSetterValue()) {
+         out.printf(" setterValue %p", shape.setterObject());
+-    else if (!shape.hasDefaultSetter())
++    } else if (!shape.hasDefaultSetter()) {
+         out.printf(" setterOp %p", JS_FUNC_TO_DATA_PTR(void*, shape.setterOp()));
+-
+-    if (shape.isDataProperty())
++    }
++
++    if (shape.isDataProperty()) {
+         out.printf(" slot %d", shape.maybeSlot());
++    }
+ 
+     out.printf(")\n");
+ }
+ 
+ bool
+ JSObject::uninlinedIsProxy() const
+ {
+     return is<ProxyObject>();
+@@ -3552,45 +3734,52 @@ JSObject::dump(js::GenericPrinter& out) 
+     if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) out.put(" not_extensible");
+     if (obj->maybeHasInterestingSymbolProperty()) out.put(" maybe_has_interesting_symbol");
+     if (obj->isBoundFunction()) out.put(" bound_function");
+     if (obj->isQualifiedVarObj()) out.put(" varobj");
+     if (obj->isUnqualifiedVarObj()) out.put(" unqualified_varobj");
+     if (obj->isIteratedSingleton()) out.put(" iterated_singleton");
+     if (obj->isNewGroupUnknown()) out.put(" new_type_unknown");
+     if (obj->hasUncacheableProto()) out.put(" has_uncacheable_proto");
+-    if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable())
++    if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable()) {
+         out.put(" immutable_prototype");
++    }
+ 
+     const NativeObject* nobj = obj->isNative() ? &obj->as<NativeObject>() : nullptr;
+     if (nobj) {
+-        if (nobj->inDictionaryMode())
++        if (nobj->inDictionaryMode()) {
+             out.put(" inDictionaryMode");
+-        if (nobj->hasShapeTable())
++        }
++        if (nobj->hasShapeTable()) {
+             out.put(" hasShapeTable");
+-        if (nobj->hadElementsAccess())
++        }
++        if (nobj->hadElementsAccess()) {
+             out.put(" had_elements_access");
+-        if (nobj->isIndexed())
++        }
++        if (nobj->isIndexed()) {
+             out.put(" indexed");
++        }
+     } else {
+         out.put(" not_native\n");
+     }
+     out.putChar('\n');
+ 
+     out.put("  proto ");
+     TaggedProto proto = obj->taggedProto();
+-    if (proto.isDynamic())
++    if (proto.isDynamic()) {
+         out.put("<dynamic>");
+-    else
++    } else {
+         dumpValue(ObjectOrNullValue(proto.toObjectOrNull()), out);
++    }
+     out.putChar('\n');
+ 
+     if (nobj) {
+-        if (clasp->flags & JSCLASS_HAS_PRIVATE)
++        if (clasp->flags & JSCLASS_HAS_PRIVATE) {
+             out.printf("  private %p\n", nobj->getPrivate());
++        }
+ 
+         uint32_t reserved = JSCLASS_RESERVED_SLOTS(clasp);
+         if (reserved) {
+             out.printf("  reserved slots:\n");
+             for (uint32_t i = 0; i < reserved; i++) {
+                 out.printf("    %3d ", i);
+                 out.put(": ");
+                 dumpValue(nobj->getSlot(i), out);
+@@ -3660,31 +3849,33 @@ js::DumpInterpreterFrame(JSContext* cx, 
+     /* This should only called during live debugging. */
+     ScriptFrameIter i(cx);
+     if (!start) {
+         if (i.done()) {
+             out.printf("no stack for cx = %p\n", (void*) cx);
+             return;
+         }
+     } else {
+-        while (!i.done() && !i.isJSJit() && i.interpFrame() != start)
++        while (!i.done() && !i.isJSJit() && i.interpFrame() != start) {
+             ++i;
++        }
+ 
+         if (i.done()) {
+             out.printf("fp = %p not found in cx = %p\n",
+                     (void*)start, (void*)cx);
+             return;
+         }
+     }
+ 
+     for (; !i.done(); ++i) {
+-        if (i.isJSJit())
++        if (i.isJSJit()) {
+             out.put("JIT frame\n");
+-        else
++        } else {
+             out.printf("InterpreterFrame at %p\n", (void*) i.interpFrame());
++        }
+ 
+         if (i.isFunctionFrame()) {
+             out.put("callee fun: ");
+             RootedValue v(cx);
+             JSObject* fun = i.callee(cx);
+             v.setObject(*fun);
+             dumpValue(v, out);
+         } else {
+@@ -3695,31 +3886,35 @@ js::DumpInterpreterFrame(JSContext* cx, 
+         out.printf("file %s line %u\n",
+                 i.script()->filename(), i.script()->lineno());
+ 
+         if (jsbytecode* pc = i.pc()) {
+             out.printf("  pc = %p\n", pc);
+             out.printf("  current op: %s\n", CodeName[*pc]);
+             MaybeDumpScope(i.script()->lookupScope(pc), out);
+         }
+-        if (i.isFunctionFrame())
++        if (i.isFunctionFrame()) {
+             MaybeDumpValue("this", i.thisArgument(cx), out);
++        }
+         if (!i.isJSJit()) {
+             out.put("  rval: ");
+             dumpValue(i.interpFrame()->returnValue(), out);
+             out.putChar('\n');
+         }
+ 
+         out.put("  flags:");
+-        if (i.isConstructing())
++        if (i.isConstructing()) {
+             out.put(" constructing");
+-        if (!i.isJSJit() && i.interpFrame()->isDebuggerEvalFrame())
++        }
++        if (!i.isJSJit() && i.interpFrame()->isDebuggerEvalFrame()) {
+             out.put(" debugger eval");
+-        if (i.isEvalFrame())
++        }
++        if (i.isEvalFrame()) {
+             out.put(" eval");
++        }
+         out.putChar('\n');
+ 
+         out.printf("  envChain: (JSObject*) %p\n", (void*) i.environmentChain(cx));
+ 
+         out.putChar('\n');
+     }
+ }
+ 
+@@ -3914,49 +4109,53 @@ JSObject::sizeOfIncludingThisInNursery()
+     if (is<NativeObject>()) {
+         const NativeObject& native = as<NativeObject>();
+ 
+         size += native.numFixedSlots() * sizeof(Value);
+         size += native.numDynamicSlots() * sizeof(Value);
+ 
+         if (native.hasDynamicElements()) {
+             js::ObjectElements& elements = *native.getElementsHeader();
+-            if (!elements.isCopyOnWrite() || elements.ownerObject() == this)
++            if (!elements.isCopyOnWrite() || elements.ownerObject() == this) {
+                 size += (elements.capacity + elements.numShiftedElements()) * sizeof(HeapSlot);
++            }
+         }
+ 
+-        if (is<ArgumentsObject>())
++        if (is<ArgumentsObject>()) {
+             size += as<ArgumentsObject>().sizeOfData();
++        }
+     }
+ 
+     return size;
+ }
+ 
+ JS::ubi::Node::Size
+ JS::ubi::Concrete<JSObject>::size(mozilla::MallocSizeOf mallocSizeOf) const
+ {
+     JSObject& obj = get();
+ 
+-    if (!obj.isTenured())
++    if (!obj.isTenured()) {
+         return obj.sizeOfIncludingThisInNursery();
++    }
+ 
+     JS::ClassInfo info;
+     obj.addSizeOfExcludingThis(mallocSizeOf, &info);
+     return obj.tenuredSizeOfThis() + info.sizeOfAllThings();
+ }
+ 
+ const char16_t JS::ubi::Concrete<JSObject>::concreteTypeName[] = u"JSObject";
+ 
+ void
+ JSObject::traceChildren(JSTracer* trc)
+ {
+     TraceEdge(trc, &group_, "group");
+ 
+-    if (is<ShapedObject>())
++    if (is<ShapedObject>()) {
+         as<ShapedObject>().traceShape(trc);
++    }
+ 
+     const Class* clasp = group_->clasp();
+     if (clasp->isNative()) {
+         NativeObject* nobj = &as<NativeObject>();
+ 
+         {
+             GetObjectSlotNameFunctor func(nobj);
+             JS::AutoTracingDetails ctx(trc, func);
+@@ -3985,47 +4184,51 @@ JSObject::traceChildren(JSTracer* trc)
+                        nobj->getDenseInitializedLength(),
+                        static_cast<HeapSlot*>(nobj->getDenseElementsAllowCopyOnWrite()),
+                        "objectElements");
+         } while (false);
+     }
+ 
+     // Call the trace hook at the end so that during a moving GC the trace hook
+     // will see updated fields and slots.
+-    if (clasp->hasTrace())
++    if (clasp->hasTrace()) {
+         clasp->doTrace(trc, this);
++    }
+ }
+ 
+ static JSAtom*
+ displayAtomFromObjectGroup(ObjectGroup& group)
+ {
+     AutoSweepObjectGroup sweep(&group);
+     TypeNewScript* script = group.newScript(sweep);
+-    if (!script)
++    if (!script) {
+         return nullptr;
++    }
+ 
+     return script->function()->displayAtom();
+ }
+ 
+ /* static */ bool
+ JSObject::constructorDisplayAtom(JSContext* cx, js::HandleObject obj, js::MutableHandleAtom name)
+ {
+     ObjectGroup *g = JSObject::getGroup(cx, obj);
+-    if (!g)
++    if (!g) {
+         return false;
++    }
+ 
+     name.set(displayAtomFromObjectGroup(*g));
+     return true;
+ }
+ 
+ JSAtom*
+ JSObject::maybeConstructorDisplayAtom() const
+ {
+-    if (hasLazyGroup())
++    if (hasLazyGroup()) {
+         return nullptr;
++    }
+     return displayAtomFromObjectGroup(*group());
+ }
+ 
+ // ES 2016 7.3.20.
+ MOZ_MUST_USE JSObject*
+ js::SpeciesConstructor(JSContext* cx, HandleObject obj, HandleObject defaultCtor,
+                        bool (*isDefaultSpecies)(JSContext*, JSFunction*))
+ {
+@@ -4046,99 +4249,108 @@ js::SpeciesConstructor(JSContext* cx, Ha
+         if (GetGetterPure(cx, defaultCtor, speciesId, &getter) && getter &&
+             isDefaultSpecies(cx, getter))
+         {
+             return defaultCtor;
+         }
+     }
+ 
+     // Step 2.
+-    if (!ctorGetSucceeded && !GetProperty(cx, obj, obj, cx->names().constructor, &ctor))
++    if (!ctorGetSucceeded && !GetProperty(cx, obj, obj, cx->names().constructor, &ctor)) {
+         return nullptr;
++    }
+ 
+     // Step 3.
+-    if (ctor.isUndefined())
++    if (ctor.isUndefined()) {
+         return defaultCtor;
++    }
+ 
+     // Step 4.
+     if (!ctor.isObject()) {
+         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
+                                   "object's 'constructor' property");
+         return nullptr;
+     }
+ 
+     // Step 5.
+     RootedObject ctorObj(cx, &ctor.toObject());
+     RootedValue s(cx);
+     RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
+-    if (!GetProperty(cx, ctorObj, ctor, speciesId, &s))
++    if (!GetProperty(cx, ctorObj, ctor, speciesId, &s)) {
+         return nullptr;
++    }
+ 
+     // Step 6.
+-    if (s.isNullOrUndefined())
++    if (s.isNullOrUndefined()) {
+         return defaultCtor;
++    }
+ 
+     // Step 7.
+-    if (IsConstructor(s))
++    if (IsConstructor(s)) {
+         return &s.toObject();
++    }
+ 
+     // Step 8.
+     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR,
+                               "[Symbol.species] property of object's constructor");
+     return nullptr;
+ }
+ 
+ MOZ_MUST_USE JSObject*
+ js::SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
+                        bool (*isDefaultSpecies)(JSContext*, JSFunction*))
+ {
+     RootedObject defaultCtor(cx, GlobalObject::getOrCreateConstructor(cx, ctorKey));
+-    if (!defaultCtor)
++    if (!defaultCtor) {
+         return nullptr;
++    }
+     return SpeciesConstructor(cx, obj, defaultCtor, isDefaultSpecies);
+ }
+ 
+ bool
+ js::Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp)
+ {
+-    if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
++    if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
+         return Proxy::boxedValue_unbox(cx, obj, vp);
+-
+-    if (obj->is<BooleanObject>())
++    }
++
++    if (obj->is<BooleanObject>()) {
+         vp.setBoolean(obj->as<BooleanObject>().unbox());
+-    else if (obj->is<NumberObject>())
++    } else if (obj->is<NumberObject>()) {
+         vp.setNumber(obj->as<NumberObject>().unbox());
+-    else if (obj->is<StringObject>())
++    } else if (obj->is<StringObject>()) {
+         vp.setString(obj->as<StringObject>().unbox());
+-    else if (obj->is<DateObject>())
++    } else if (obj->is<DateObject>()) {
+         vp.set(obj->as<DateObject>().UTCTime());
+-    else if (obj->is<SymbolObject>())
++    } else if (obj->is<SymbolObject>()) {
+         vp.setSymbol(obj->as<SymbolObject>().unbox());
+ #ifdef ENABLE_BIGINT
+-    else if (obj->is<BigIntObject>())
++    } else if (obj->is<BigIntObject>()) {
+         vp.setBigInt(obj->as<BigIntObject>().unbox());
+ #endif
+-    else
++    } else {
+         vp.setUndefined();
++    }
+ 
+     return true;
+ }
+ 
+ #ifdef DEBUG
+ /* static */ void
+ JSObject::debugCheckNewObject(ObjectGroup* group, Shape* shape, js::gc::AllocKind allocKind,
+                               js::gc::InitialHeap heap)
+ {
+     const js::Class* clasp = group->clasp();
+     MOZ_ASSERT(clasp != &ArrayObject::class_);
+ 
+-    if (shape)
++    if (shape) {
+         MOZ_ASSERT(clasp == shape->getObjectClass());
+-    else
++    } else {
+         MOZ_ASSERT(clasp == &UnboxedPlainObject::class_);
++    }
+ 
+     if (!ClassCanHaveFixedData(clasp)) {
+         MOZ_ASSERT(shape);
+         MOZ_ASSERT(gc::GetGCKindSlots(allocKind, clasp) == shape->numFixedSlots());
+     }
+ 
+     // Classes with a finalizer must specify whether instances will be finalized
+     // on the main thread or in the background, except proxies whose behaviour
+diff --git a/js/src/vm/JSObject.cpp.1488698-7.later b/js/src/vm/JSObject.cpp.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSObject.cpp.1488698-7.later
+@@ -0,0 +1,1338 @@
++--- JSObject.cpp
+++++ JSObject.cpp
++@@ -220,55 +236,59 @@ js::GetFirstArgumentAsObject(JSContext* 
++         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
++                                   method, "0", "s");
++         return false;
++     }
++ 
++     HandleValue v = args[0];
++     if (!v.isObject()) {
++         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
++-        if (!bytes)
+++        if (!bytes) {
++             return false;
+++        }
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
++                                  bytes.get(), "not an object");
++         return false;
++     }
++ 
++     objp.set(&v.toObject());
++     return true;
++ }
++ 
++ static bool
++ GetPropertyIfPresent(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
++                      bool* foundp)
++ {
++-    if (!HasProperty(cx, obj, id, foundp))
+++    if (!HasProperty(cx, obj, id, foundp)) {
++         return false;
+++    }
++     if (!*foundp) {
++         vp.setUndefined();
++         return true;
++     }
++ 
++     return GetProperty(cx, obj, obj, id, vp);
++ }
++ 
++ bool
++ js::Throw(JSContext* cx, HandleId id, unsigned errorNumber, const char* details)
++ {
++     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == (details ? 2 : 1));
++     MOZ_ASSERT_IF(details, JS::StringIsASCII(details));
++ 
++     UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
++-    if (!bytes)
+++    if (!bytes) {
++         return false;
++-
++-    if (details)
+++    }
+++
+++    if (details) {
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, bytes.get(), details);
++-    else
+++    } else {
++         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, bytes.get());
+++    }
++ 
++     return false;
++ }
++ 
++ 
++ /*** PropertyDescriptor operations and DefineProperties ******************************************/
++ 
++ static const char js_getter_str[] = "getter";
++@@ -455,84 +495,91 @@ js::ReadPropertyDescriptors(JSContext* c
++ }
++ 
++ /*** Seal and freeze *****************************************************************************/
++ 
++ static unsigned
++ GetSealedOrFrozenAttributes(unsigned attrs, IntegrityLevel level)
++ {
++     /* Make all attributes permanent; if freezing, make data attributes read-only. */
++-    if (level == IntegrityLevel::Frozen && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
+++    if (level == IntegrityLevel::Frozen && !(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
++         return JSPROP_PERMANENT | JSPROP_READONLY;
+++    }
++     return JSPROP_PERMANENT;
++ }
++ 
++ /* ES6 draft rev 29 (6 Dec 2014) 7.3.13. */
++ bool
++ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level)
++ {
++     cx->check(obj);
++ 
++     // Steps 3-5. (Steps 1-2 are redundant assertions.)
++-    if (!PreventExtensions(cx, obj))
+++    if (!PreventExtensions(cx, obj)) {
++         return false;
+++    }
++ 
++     // Steps 6-9, loosely interpreted.
++     if (obj->isNative() && !obj->as<NativeObject>().inDictionaryMode() &&
++         !obj->is<TypedArrayObject>() && !obj->is<MappedArgumentsObject>())
++     {
++         HandleNativeObject nobj = obj.as<NativeObject>();
++ 
++         // Seal/freeze non-dictionary objects by constructing a new shape
++         // hierarchy mirroring the original one, which can be shared if many
++         // objects with the same structure are sealed/frozen. If we use the
++         // generic path below then any non-empty object will be converted to
++         // dictionary mode.
++         RootedShape last(cx, EmptyShape::getInitialShape(cx, nobj->getClass(),
++                                                          nobj->taggedProto(),
++                                                          nobj->numFixedSlots(),
++                                                          nobj->lastProperty()->getObjectFlags()));
++-        if (!last)
+++        if (!last) {
++             return false;
+++        }
++ 
++         // Get an in-order list of the shapes in this object.
++         using ShapeVec = GCVector<Shape*, 8>;
++         Rooted<ShapeVec> shapes(cx, ShapeVec(cx));
++         for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
++-            if (!shapes.append(&r.front()))
+++            if (!shapes.append(&r.front())) {
++                 return false;
+++            }
++         }
++         Reverse(shapes.begin(), shapes.end());
++ 
++         for (Shape* shape : shapes) {
++             Rooted<StackShape> child(cx, StackShape(shape));
++             child.setAttrs(child.attrs() | GetSealedOrFrozenAttributes(child.attrs(), level));
++ 
++-            if (!JSID_IS_EMPTY(child.get().propid) && level == IntegrityLevel::Frozen)
+++            if (!JSID_IS_EMPTY(child.get().propid) && level == IntegrityLevel::Frozen) {
++                 MarkTypePropertyNonWritable(cx, nobj, child.get().propid);
+++            }
++ 
++             last = cx->zone()->propertyTree().getChild(cx, last, child);
++-            if (!last)
+++            if (!last) {
++                 return false;
+++            }
++         }
++ 
++         MOZ_ASSERT(nobj->lastProperty()->slotSpan() == last->slotSpan());
++         MOZ_ALWAYS_TRUE(nobj->setLastProperty(cx, last));
++ 
++         // Ordinarily ArraySetLength handles this, but we're going behind its back
++         // right now, so we must do this manually.
++         if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
++             MOZ_ASSERT(!nobj->denseElementsAreCopyOnWrite());
++             obj->as<ArrayObject>().setNonWritableLength(cx);
++         }
++     } else {
++         // Steps 6-7.
++         AutoIdVector keys(cx);
++-        if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys))
+++        if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys)) {
++             return false;
+++        }
++ 
++         RootedId id(cx);
++         Rooted<PropertyDescriptor> desc(cx);
++ 
++         const unsigned AllowConfigure = JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY |
++                                         JSPROP_IGNORE_VALUE;
++         const unsigned AllowConfigureAndWritable = AllowConfigure & ~JSPROP_IGNORE_READONLY;
++ 
++@@ -1117,74 +1210,81 @@ JSObject::nonNativeSetProperty(JSContext
++     return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result);
++ }
++ 
++ /* static */ bool
++ JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v,
++                               HandleValue receiver, ObjectOpResult& result)
++ {
++     RootedId id(cx);
++-    if (!IndexToId(cx, index, &id))
+++    if (!IndexToId(cx, index, &id)) {
++         return false;
+++    }
++     return nonNativeSetProperty(cx, obj, id, v, receiver, result);
++ }
++ 
++ JS_FRIEND_API(bool)
++ JS_CopyPropertyFrom(JSContext* cx, HandleId id, HandleObject target,
++                     HandleObject obj, PropertyCopyBehavior copyBehavior)
++ {
++     // |target| must not be a CCW because we need to enter its realm below and
++     // CCWs are not associated with a single realm.
++     MOZ_ASSERT(!IsCrossCompartmentWrapper(target));
++ 
++     // |obj| and |cx| are generally not same-compartment with |target| here.
++     cx->check(obj, id);
++     Rooted<PropertyDescriptor> desc(cx);
++ 
++-    if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
+++    if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
++         return false;
+++    }
++     MOZ_ASSERT(desc.object());
++ 
++     // Silently skip JSGetterOp/JSSetterOp-implemented accessors.
++-    if (desc.getter() && !desc.hasGetterObject())
+++    if (desc.getter() && !desc.hasGetterObject()) {
++         return true;
++-    if (desc.setter() && !desc.hasSetterObject())
+++    }
+++    if (desc.setter() && !desc.hasSetterObject()) {
++         return true;
+++    }
++ 
++     if (copyBehavior == MakeNonConfigurableIntoConfigurable) {
++         // Mask off the JSPROP_PERMANENT bit.
++         desc.attributesRef() &= ~JSPROP_PERMANENT;
++     }
++ 
++     JSAutoRealm ar(cx, target);
++     cx->markId(id);
++     RootedId wrappedId(cx, id);
++-    if (!cx->compartment()->wrap(cx, &desc))
+++    if (!cx->compartment()->wrap(cx, &desc)) {
++         return false;
+++    }
++ 
++     return DefineProperty(cx, target, wrappedId, desc);
++ }
++ 
++ JS_FRIEND_API(bool)
++ JS_CopyPropertiesFrom(JSContext* cx, HandleObject target, HandleObject obj)
++ {
++     // Both |obj| and |target| must not be CCWs because we need to enter their
++     // realms below and CCWs are not associated with a single realm.
++     MOZ_ASSERT(!IsCrossCompartmentWrapper(obj));
++     MOZ_ASSERT(!IsCrossCompartmentWrapper(target));
++ 
++     JSAutoRealm ar(cx, obj);
++ 
++     AutoIdVector props(cx);
++-    if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props))
+++    if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props)) {
++         return false;
+++    }
++ 
++     for (size_t i = 0; i < props.length(); ++i) {
++-        if (!JS_CopyPropertyFrom(cx, props[i], target, obj))
+++        if (!JS_CopyPropertyFrom(cx, props[i], target, obj)) {
++             return false;
+++        }
++     }
++ 
++     return true;
++ }
++ 
++ static bool
++ CopyProxyObject(JSContext* cx, Handle<ProxyObject*> from, Handle<ProxyObject*> to)
++ {
++@@ -1335,62 +1447,69 @@ js::DeepCloneObjectLiteral(JSContext* cx
++     MOZ_ASSERT_IF(obj->isSingleton(),
++                   cx->realm()->behaviors().getSingletonsAsTemplates());
++     MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() ||
++                obj->is<ArrayObject>());
++     MOZ_ASSERT(newKind != SingletonObject);
++ 
++     if (obj->is<ArrayObject>()) {
++         Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
++-        if (!GetScriptArrayObjectElements(obj.as<ArrayObject>(), &values))
+++        if (!GetScriptArrayObjectElements(obj.as<ArrayObject>(), &values)) {
++             return nullptr;
+++        }
++ 
++         // Deep clone any elements.
++         for (uint32_t i = 0; i < values.length(); ++i) {
++-            if (!DeepCloneValue(cx, values[i].address(), newKind))
+++            if (!DeepCloneValue(cx, values[i].address(), newKind)) {
++                 return nullptr;
+++            }
++         }
++ 
++         ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal;
++-        if (obj->is<ArrayObject>() && obj->as<ArrayObject>().denseElementsAreCopyOnWrite())
+++        if (obj->is<ArrayObject>() && obj->as<ArrayObject>().denseElementsAreCopyOnWrite()) {
++             arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite;
+++        }
++ 
++         return ObjectGroup::newArrayObject(cx, values.begin(), values.length(), newKind,
++                                            arrayKind);
++     }
++ 
++     Rooted<IdValueVector> properties(cx, IdValueVector(cx));
++-    if (!GetScriptPlainObjectProperties(obj, &properties))
+++    if (!GetScriptPlainObjectProperties(obj, &properties)) {
++         return nullptr;
+++    }
++ 
++     for (size_t i = 0; i < properties.length(); i++) {
++         cx->markId(properties[i].get().id);
++-        if (!DeepCloneValue(cx, &properties[i].get().value, newKind))
+++        if (!DeepCloneValue(cx, &properties[i].get().value, newKind)) {
++             return nullptr;
++-    }
++-
++-    if (obj->isSingleton())
+++        }
+++    }
+++
+++    if (obj->isSingleton()) {
++         newKind = SingletonObject;
+++    }
++ 
++     return ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind);
++ }
++ 
++ static bool
++ InitializePropertiesFromCompatibleNativeObject(JSContext* cx,
++                                                HandleNativeObject dst,
++                                                HandleNativeObject src)
++ {
++     cx->check(src, dst);
++     MOZ_ASSERT(src->getClass() == dst->getClass());
++     MOZ_ASSERT(dst->lastProperty()->getObjectFlags() == 0);
++     MOZ_ASSERT(!src->isSingleton());
++     MOZ_ASSERT(src->numFixedSlots() == dst->numFixedSlots());
++ 
++-    if (!dst->ensureElements(cx, src->getDenseInitializedLength()))
+++    if (!dst->ensureElements(cx, src->getDenseInitializedLength())) {
++         return false;
+++    }
++ 
++     uint32_t initialized = src->getDenseInitializedLength();
++     for (uint32_t i = 0; i < initialized; ++i) {
++         dst->setDenseInitializedLength(i + 1);
++         dst->initDenseElement(i, src->getDenseElement(i));
++     }
++ 
++     MOZ_ASSERT(!src->hasPrivate());
++@@ -1561,63 +1695,68 @@ NativeObject::fillInAfterSwap(JSContext*
++     // This object has just been swapped with some other object, and its shape
++     // no longer reflects its allocated size. Correct this information and
++     // fill the slots in with the specified values.
++     MOZ_ASSERT(obj->slotSpan() == values.length());
++ 
++     // Make sure the shape's numFixedSlots() is correct.
++     size_t nfixed = gc::GetGCKindSlots(obj->asTenured().getAllocKind(), obj->getClass());
++     if (nfixed != obj->shape()->numFixedSlots()) {
++-        if (!NativeObject::generateOwnShape(cx, obj))
+++        if (!NativeObject::generateOwnShape(cx, obj)) {
++             return false;
+++        }
++         obj->shape()->setNumFixedSlots(nfixed);
++     }
++ 
++-    if (obj->hasPrivate())
+++    if (obj->hasPrivate()) {
++         obj->setPrivate(priv);
++-    else
+++    } else {
++         MOZ_ASSERT(!priv);
+++    }
++ 
++     if (obj->slots_) {
++         js_free(obj->slots_);
++         obj->slots_ = nullptr;
++     }
++ 
++     if (size_t ndynamic = dynamicSlotsCount(nfixed, values.length(), obj->getClass())) {
++         obj->slots_ = cx->pod_malloc<HeapSlot>(ndynamic);
++-        if (!obj->slots_)
+++        if (!obj->slots_) {
++             return false;
+++        }
++         Debug_SetSlotRangeToCrashOnTouch(obj->slots_, ndynamic);
++     }
++ 
++     obj->initSlotRange(0, values.begin(), values.length());
++     return true;
++ }
++ 
++ void
++ JSObject::fixDictionaryShapeAfterSwap()
++ {
++     // Dictionary shapes can point back to their containing objects, so after
++     // swapping the guts of those objects fix the pointers up.
++-    if (isNative() && as<NativeObject>().inDictionaryMode())
+++    if (isNative() && as<NativeObject>().inDictionaryMode()) {
++         as<NativeObject>().shape()->listp = as<NativeObject>().shapePtr();
+++    }
++ }
++ 
++ static MOZ_MUST_USE bool
++ CopyProxyValuesBeforeSwap(JSContext* cx, ProxyObject* proxy, AutoValueVector& values)
++ {
++     MOZ_ASSERT(values.empty());
++ 
++     // Remove the GCPtrValues we're about to swap from the store buffer, to
++     // ensure we don't trace bogus values.
++     gc::StoreBuffer& sb = cx->runtime()->gc.storeBuffer();
++ 
++     // Reserve space for the private slot and the reserved slots.
++-    if (!values.reserve(1 + proxy->numReservedSlots()))
+++    if (!values.reserve(1 + proxy->numReservedSlots())) {
++         return false;
+++    }
++ 
++     js::detail::ProxyValueArray* valArray = js::detail::GetProxyDataLayout(proxy)->values();
++     sb.unputValue(&valArray->privateSlot);
++     values.infallibleAppend(valArray->privateSlot);
++ 
++     for (size_t i = 0; i < proxy->numReservedSlots(); i++) {
++         sb.unputValue(&valArray->reservedSlots.slots[i]);
++         values.infallibleAppend(valArray->reservedSlots.slots[i]);
++@@ -1721,20 +1864,22 @@ JSObject::swap(JSContext* cx, HandleObje
++ 
++         js_memcpy(tmp, a, size);
++         js_memcpy(a, b, size);
++         js_memcpy(b, tmp, size);
++ 
++         a->fixDictionaryShapeAfterSwap();
++         b->fixDictionaryShapeAfterSwap();
++ 
++-        if (aIsProxyWithInlineValues)
+++        if (aIsProxyWithInlineValues) {
++             b->as<ProxyObject>().setInlineValueArray();
++-        if (bIsProxyWithInlineValues)
+++        }
+++        if (bIsProxyWithInlineValues) {
++             a->as<ProxyObject>().setInlineValueArray();
+++        }
++     } else {
++         // Avoid GC in here to avoid confusing the tracing code with our
++         // intermediate state.
++         gc::AutoSuppressGC suppress(cx);
++ 
++         // When the objects have different sizes, they will have different
++         // numbers of fixed slots before and after the swap, so the slots for
++         // native objects will need to be rearranged.
++@@ -1742,67 +1887,75 @@ JSObject::swap(JSContext* cx, HandleObje
++         NativeObject* nb = b->isNative() ? &b->as<NativeObject>() : nullptr;
++ 
++         // Remember the original values from the objects.
++         AutoValueVector avals(cx);
++         void* apriv = nullptr;
++         if (na) {
++             apriv = na->hasPrivate() ? na->getPrivate() : nullptr;
++             for (size_t i = 0; i < na->slotSpan(); i++) {
++-                if (!avals.append(na->getSlot(i)))
+++                if (!avals.append(na->getSlot(i))) {
++                     oomUnsafe.crash("JSObject::swap");
+++                }
++             }
++         }
++         AutoValueVector bvals(cx);
++         void* bpriv = nullptr;
++         if (nb) {
++             bpriv = nb->hasPrivate() ? nb->getPrivate() : nullptr;
++             for (size_t i = 0; i < nb->slotSpan(); i++) {
++-                if (!bvals.append(nb->getSlot(i)))
+++                if (!bvals.append(nb->getSlot(i))) {
++                     oomUnsafe.crash("JSObject::swap");
+++                }
++             }
++         }
++ 
++         // Do the same for proxies storing ProxyValueArray inline.
++         ProxyObject* proxyA = a->is<ProxyObject>() ? &a->as<ProxyObject>() : nullptr;
++         ProxyObject* proxyB = b->is<ProxyObject>() ? &b->as<ProxyObject>() : nullptr;
++ 
++         if (aIsProxyWithInlineValues) {
++-            if (!CopyProxyValuesBeforeSwap(cx, proxyA, avals))
+++            if (!CopyProxyValuesBeforeSwap(cx, proxyA, avals)) {
++                 oomUnsafe.crash("CopyProxyValuesBeforeSwap");
+++            }
++         }
++         if (bIsProxyWithInlineValues) {
++-            if (!CopyProxyValuesBeforeSwap(cx, proxyB, bvals))
+++            if (!CopyProxyValuesBeforeSwap(cx, proxyB, bvals)) {
++                 oomUnsafe.crash("CopyProxyValuesBeforeSwap");
+++            }
++         }
++ 
++         // Swap the main fields of the objects, whether they are native objects or proxies.
++         char tmp[sizeof(JSObject_Slots0)];
++         js_memcpy(&tmp, a, sizeof tmp);
++         js_memcpy(a, b, sizeof tmp);
++         js_memcpy(b, &tmp, sizeof tmp);
++ 
++         a->fixDictionaryShapeAfterSwap();
++         b->fixDictionaryShapeAfterSwap();
++ 
++         if (na) {
++-            if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), avals, apriv))
+++            if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), avals, apriv)) {
++                 oomUnsafe.crash("fillInAfterSwap");
+++            }
++         }
++         if (nb) {
++-            if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), bvals, bpriv))
+++            if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), bvals, bpriv)) {
++                 oomUnsafe.crash("fillInAfterSwap");
+++            }
++         }
++         if (aIsProxyWithInlineValues) {
++-            if (!b->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, avals))
+++            if (!b->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, avals)) {
++                 oomUnsafe.crash("initExternalValueArray");
+++            }
++         }
++         if (bIsProxyWithInlineValues) {
++-            if (!a->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, bvals))
+++            if (!a->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, bvals)) {
++                 oomUnsafe.crash("initExternalValueArray");
+++            }
++         }
++     }
++ 
++     // Swapping the contents of two objects invalidates type sets which contain
++     // either of the objects, so mark all such sets as unknown.
++     MarkObjectGroupUnknownProperties(cx, a->group());
++     MarkObjectGroupUnknownProperties(cx, b->group());
++ 
++@@ -1822,28 +1975,30 @@ JSObject::swap(JSContext* cx, HandleObje
++ 
++     NotifyGCPostSwap(a, b, r);
++     return true;
++ }
++ 
++ static void
++ SetClassObject(JSObject* obj, JSProtoKey key, JSObject* cobj, JSObject* proto)
++ {
++-    if (!obj->is<GlobalObject>())
+++    if (!obj->is<GlobalObject>()) {
++         return;
+++    }
++ 
++     obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
++     obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
++ }
++ 
++ static void
++ ClearClassObject(JSObject* obj, JSProtoKey key)
++ {
++-    if (!obj->is<GlobalObject>())
+++    if (!obj->is<GlobalObject>()) {
++         return;
+++    }
++ 
++     obj->as<GlobalObject>().setConstructor(key, UndefinedValue());
++     obj->as<GlobalObject>().setPrototype(key, UndefinedValue());
++ }
++ 
++ static NativeObject*
++ DefineConstructorAndPrototype(JSContext* cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
++                               HandleObject protoProto, const Class* clasp,
++@@ -1874,18 +2029,19 @@ DefineConstructorAndPrototype(JSContext*
++      * on (1) and (2).
++      */
++ 
++     /*
++      * Create the prototype object.  (GlobalObject::createBlankPrototype isn't
++      * used because it won't let us use protoProto as the proto.
++      */
++     RootedNativeObject proto(cx, NewNativeObjectWithClassProto(cx, clasp, protoProto, SingletonObject));
++-    if (!proto)
+++    if (!proto) {
++         return nullptr;
+++    }
++ 
++     /*
++      * Whether we need to define a constructor property on |obj| and which
++      * attributes to use.
++      */
++     bool defineConstructorProperty = false;
++     uint32_t propertyAttrs = 0;
++ 
++@@ -1906,18 +2062,19 @@ DefineConstructorAndPrototype(JSContext*
++             propertyAttrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
++                             ? JSPROP_READONLY | JSPROP_PERMANENT
++                             : 0;
++         }
++ 
++         ctor = proto;
++     } else {
++         RootedFunction fun(cx, NewNativeConstructor(cx, constructor, nargs, atom));
++-        if (!fun)
+++        if (!fun) {
++             goto bad;
+++        }
++ 
++         /*
++          * Set the class object early for standard class constructors. Type
++          * inference may need to access these, and js::GetBuiltinPrototype will
++          * fail if it tries to do a reentrant reconstruction of the class.
++          */
++         if (key != JSProto_Null) {
++             SetClassObject(obj, key, fun, proto);
++@@ -1928,79 +2085,87 @@ DefineConstructorAndPrototype(JSContext*
++         propertyAttrs = 0;
++ 
++         /*
++          * Optionally construct the prototype object, before the class has
++          * been fully initialized.  Allow the ctor to replace proto with a
++          * different object, as is done for operator new.
++          */
++         ctor = fun;
++-        if (!LinkConstructorAndPrototype(cx, ctor, proto))
+++        if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
++             goto bad;
+++        }
++ 
++         /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
++         Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
++-        if (ctor->getClass() == clasp && !JSObject::splicePrototype(cx, ctor, clasp, tagged))
+++        if (ctor->getClass() == clasp && !JSObject::splicePrototype(cx, ctor, clasp, tagged)) {
++             goto bad;
+++        }
++     }
++ 
++     if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) ||
++         (ctor != proto && !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs)))
++     {
++         goto bad;
++     }
++ 
++     if (defineConstructorProperty) {
++         RootedId id(cx, AtomToId(atom));
++         RootedValue value(cx, ObjectValue(*ctor));
++-        if (!DefineDataProperty(cx, obj, id, value, propertyAttrs))
+++        if (!DefineDataProperty(cx, obj, id, value, propertyAttrs)) {
++             goto bad;
+++        }
++     }
++ 
++     /* If this is a standard class, cache its prototype. */
++-    if (!cached && key != JSProto_Null)
+++    if (!cached && key != JSProto_Null) {
++         SetClassObject(obj, key, ctor, proto);
++-
++-    if (ctorp)
+++    }
+++
+++    if (ctorp) {
++         *ctorp = ctor;
+++    }
++     return proto;
++ 
++ bad:
++-    if (cached)
+++    if (cached) {
++         ClearClassObject(obj, key);
+++    }
++     return nullptr;
++ }
++ 
++ NativeObject*
++ js::InitClass(JSContext* cx, HandleObject obj, HandleObject protoProto_,
++               const Class* clasp, Native constructor, unsigned nargs,
++               const JSPropertySpec* ps, const JSFunctionSpec* fs,
++               const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
++               NativeObject** ctorp)
++ {
++     RootedObject protoProto(cx, protoProto_);
++ 
++     RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
++-    if (!atom)
+++    if (!atom) {
++         return nullptr;
+++    }
++ 
++     /*
++      * All instances of the class will inherit properties from the prototype
++      * object we are about to create (in DefineConstructorAndPrototype), which
++      * in turn will inherit from protoProto.
++      *
++      * When initializing a standard class (other than Object), if protoProto is
++      * null, default to Object.prototype. The engine's internal uses of
++      * js::InitClass depend on this nicety.
++      */
++     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
++     if (key != JSProto_Null && !protoProto) {
++         protoProto = GlobalObject::getOrCreatePrototype(cx, JSProto_Object);
++-        if (!protoProto)
+++        if (!protoProto) {
++             return nullptr;
+++        }
++     }
++ 
++     return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
++                                          ps, fs, static_ps, static_fs, ctorp);
++ }
++ 
++ void
++ JSObject::fixupAfterMovingGC()
++@@ -2175,23 +2352,25 @@ js::GetObjectFromIncumbentGlobal(JSConte
++     if (!globalObj) {
++         obj.set(nullptr);
++         return true;
++     }
++ 
++     {
++         AutoRealm ar(cx, globalObj);
++         obj.set(GlobalObject::getOrCreateObjectPrototype(cx, globalObj));
++-        if (!obj)
+++        if (!obj) {
++             return false;
+++        }
++     }
++ 
++     // The object might be from a different compartment, so wrap it.
++-    if (obj && !cx->compartment()->wrap(cx, obj))
+++    if (obj && !cx->compartment()->wrap(cx, obj)) {
++         return false;
+++    }
++ 
++     return true;
++ }
++ 
++ static bool
++ IsStandardPrototype(JSObject* obj, JSProtoKey key)
++ {
++     Value v = obj->nonCCWGlobal().getPrototype(key);
++@@ -2199,76 +2378,82 @@ IsStandardPrototype(JSObject* obj, JSPro
++ }
++ 
++ JSProtoKey
++ JS::IdentifyStandardInstance(JSObject* obj)
++ {
++     // Note: The prototype shares its JSClass with instances.
++     MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
++     JSProtoKey key = StandardProtoKeyOrNull(obj);
++-    if (key != JSProto_Null && !IsStandardPrototype(obj, key))
+++    if (key != JSProto_Null && !IsStandardPrototype(obj, key)) {
++         return key;
+++    }
++     return JSProto_Null;
++ }
++ 
++ JSProtoKey
++ JS::IdentifyStandardPrototype(JSObject* obj)
++ {
++     // Note: The prototype shares its JSClass with instances.
++     MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
++     JSProtoKey key = StandardProtoKeyOrNull(obj);
++-    if (key != JSProto_Null && IsStandardPrototype(obj, key))
+++    if (key != JSProto_Null && IsStandardPrototype(obj, key)) {
++         return key;
+++    }
++     return JSProto_Null;
++ }
++ 
++ JSProtoKey
++ JS::IdentifyStandardInstanceOrPrototype(JSObject* obj)
++ {
++     return StandardProtoKeyOrNull(obj);
++ }
++ 
++ JSProtoKey
++ JS::IdentifyStandardConstructor(JSObject* obj)
++ {
++     // Note that NATIVE_CTOR does not imply that we are a standard constructor,
++     // but the converse is true (at least until we start having self-hosted
++     // constructors for standard classes). This lets us avoid a costly loop for
++     // many functions (which, depending on the call site, may be the common case).
++-    if (!obj->is<JSFunction>() || !(obj->as<JSFunction>().flags() & JSFunction::NATIVE_CTOR))
+++    if (!obj->is<JSFunction>() || !(obj->as<JSFunction>().flags() & JSFunction::NATIVE_CTOR)) {
++         return JSProto_Null;
+++    }
++ 
++     GlobalObject& global = obj->as<JSFunction>().global();
++     for (size_t k = 0; k < JSProto_LIMIT; ++k) {
++         JSProtoKey key = static_cast<JSProtoKey>(k);
++-        if (global.getConstructor(key) == ObjectValue(*obj))
+++        if (global.getConstructor(key) == ObjectValue(*obj)) {
++             return key;
+++        }
++     }
++ 
++     return JSProto_Null;
++ }
++ 
++ bool
++ js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id,
++                    MutableHandleObject objp, MutableHandle<PropertyResult> propp)
++ {
++-    if (LookupPropertyOp op = obj->getOpsLookupProperty())
+++    if (LookupPropertyOp op = obj->getOpsLookupProperty()) {
++         return op(cx, obj, id, objp, propp);
+++    }
++     return LookupPropertyInline<CanGC>(cx, obj.as<NativeObject>(), id, objp, propp);
++ }
++ 
++ bool
++ js::LookupName(JSContext* cx, HandlePropertyName name, HandleObject envChain,
++                MutableHandleObject objp, MutableHandleObject pobjp, MutableHandle<PropertyResult> propp)
++ {
++     RootedId id(cx, NameToId(name));
++ 
++     for (RootedObject env(cx, envChain); env; env = env->enclosingEnvironment()) {
++-        if (!LookupProperty(cx, env, id, pobjp, propp))
+++        if (!LookupProperty(cx, env, id, pobjp, propp)) {
++             return false;
+++        }
++         if (propp) {
++             objp.set(env);
++             return true;
++         }
++     }
++ 
++     objp.set(nullptr);
++     pobjp.set(nullptr);
++@@ -2481,86 +2682,93 @@ js::LookupOwnPropertyPure(JSContext* cx,
++     return true;
++ }
++ 
++ static inline bool
++ NativeGetPureInline(NativeObject* pobj, jsid id, PropertyResult prop, Value* vp)
++ {
++     if (prop.isDenseOrTypedArrayElement()) {
++         // For simplicity we ignore the TypedArray with string index case.
++-        if (!JSID_IS_INT(id))
+++        if (!JSID_IS_INT(id)) {
++             return false;
+++        }
++ 
++         *vp = pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
++         return true;
++     }
++ 
++     // Fail if we have a custom getter.
++     Shape* shape = prop.shape();
++-    if (!shape->isDataProperty())
+++    if (!shape->isDataProperty()) {
++         return false;
+++    }
++ 
++     *vp = pobj->getSlot(shape->slot());
++     MOZ_ASSERT(!vp->isMagic());
++     return true;
++ }
++ 
++ static inline bool
++ UnboxedGetPureInline(JSObject* pobj, jsid id, PropertyResult prop, Value* vp)
++ {
++     MOZ_ASSERT(prop.isNonNativeProperty());
++ 
++     // This might be a TypedObject.
++-    if (!pobj->is<UnboxedPlainObject>())
+++    if (!pobj->is<UnboxedPlainObject>()) {
++         return false;
+++    }
++ 
++     const UnboxedLayout& layout = pobj->as<UnboxedPlainObject>().layout();
++     if (const UnboxedLayout::Property* property = layout.lookup(id)) {
++         *vp = pobj->as<UnboxedPlainObject>().getValue(*property);
++         return true;
++     }
++ 
++     // Don't bother supporting expandos for now.
++     return false;
++ }
++ 
++ bool
++ js::GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp)
++ {
++     JSObject* pobj;
++     PropertyResult prop;
++-    if (!LookupPropertyPure(cx, obj, id, &pobj, &prop))
+++    if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) {
++         return false;
+++    }
++ 
++     if (!prop) {
++         vp->setUndefined();
++         return true;
++     }
++ 
++-    if (MOZ_LIKELY(pobj->isNative()))
+++    if (MOZ_LIKELY(pobj->isNative())) {
++         return NativeGetPureInline(&pobj->as<NativeObject>(), id, prop, vp);
+++    }
++     return UnboxedGetPureInline(pobj, id, prop, vp);
++ }
++ 
++ bool
++ js::GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp, bool* found)
++ {
++     PropertyResult prop;
++-    if (!LookupOwnPropertyPure(cx, obj, id, &prop))
+++    if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
++         return false;
+++    }
++ 
++     if (!prop) {
++         *found = false;
++         vp->setUndefined();
++         return true;
++     }
++ 
++     *found = true;
++-    if (MOZ_LIKELY(obj->isNative()))
+++    if (MOZ_LIKELY(obj->isNative())) {
++         return NativeGetPureInline(&obj->as<NativeObject>(), id, prop, vp);
+++    }
++     return UnboxedGetPureInline(obj, id, prop, vp);
++ }
++ 
++ static inline bool
++ NativeGetGetterPureInline(PropertyResult prop, JSFunction** fp)
++ {
++     if (!prop.isDenseOrTypedArrayElement() && prop.shape()->hasGetterObject()) {
++         Shape* shape = prop.shape();
++@@ -2667,119 +2883,134 @@ js::SetPrototype(JSContext* cx, HandleOb
++         MOZ_ASSERT(obj->is<ProxyObject>());
++         return Proxy::setPrototype(cx, obj, proto, result);
++     }
++ 
++     /*
++      * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true.
++      * Since the values in question are objects, we can just compare pointers.
++      */
++-    if (proto == obj->staticPrototype())
+++    if (proto == obj->staticPrototype()) {
++         return result.succeed();
+++    }
++ 
++     /* Disallow mutation of immutable [[Prototype]]s. */
++-    if (obj->staticPrototypeIsImmutable())
+++    if (obj->staticPrototypeIsImmutable()) {
++         return result.fail(JSMSG_CANT_SET_PROTO);
+++    }
++ 
++     /*
++      * Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
++      */
++     if (obj->is<TypedObject>()) {
++         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF,
++                                   "incompatible TypedObject");
++         return false;
++     }
++ 
++     /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
++     bool extensible;
++-    if (!IsExtensible(cx, obj, &extensible))
+++    if (!IsExtensible(cx, obj, &extensible)) {
++         return false;
++-    if (!extensible)
+++    }
+++    if (!extensible) {
++         return result.fail(JSMSG_CANT_SET_PROTO);
+++    }
++ 
++     // If this is a global object, resolve the Object class so that its
++     // [[Prototype]] chain is always properly immutable, even in the presence
++     // of lazy standard classes.
++     if (obj->is<GlobalObject>()) {
++         Handle<GlobalObject*> global = obj.as<GlobalObject>();
++-        if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object))
+++        if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object)) {
++             return false;
+++        }
++     }
++ 
++     /*
++      * ES6 9.1.2 step 6 forbids generating cyclical prototype chains. But we
++      * have to do this comparison on the observable WindowProxy, not on the
++      * possibly-Window object we're setting the proto on.
++      */
++     RootedObject objMaybeWindowProxy(cx, ToWindowProxyIfWindow(obj));
++     RootedObject obj2(cx, proto);
++     while (obj2) {
++         MOZ_ASSERT(!IsWindow(obj2));
++-        if (obj2 == objMaybeWindowProxy)
+++        if (obj2 == objMaybeWindowProxy) {
++             return result.fail(JSMSG_CANT_SET_PROTO_CYCLE);
+++        }
++ 
++         bool isOrdinary;
++-        if (!GetPrototypeIfOrdinary(cx, obj2, &isOrdinary, &obj2))
+++        if (!GetPrototypeIfOrdinary(cx, obj2, &isOrdinary, &obj2)) {
++             return false;
++-        if (!isOrdinary)
+++        }
+++        if (!isOrdinary) {
++             break;
+++        }
++     }
++ 
++     // Convert unboxed objects to their native representations before changing
++     // their prototype/group, as they depend on the group for their layout.
++-    if (!MaybeConvertUnboxedObjectToNative(cx, obj))
+++    if (!MaybeConvertUnboxedObjectToNative(cx, obj)) {
++         return false;
+++    }
++ 
++     Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
++-    if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto))
+++    if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto)) {
++         return false;
+++    }
++ 
++     return result.succeed();
++ }
++ 
++ bool
++ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto)
++ {
++     ObjectOpResult result;
++     return SetPrototype(cx, obj, proto, result) && result.checkStrict(cx, obj);
++ }
++ 
++ bool
++ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result)
++ {
++-    if (obj->is<ProxyObject>())
+++    if (obj->is<ProxyObject>()) {
++         return js::Proxy::preventExtensions(cx, obj, result);
+++    }
++ 
++     if (!obj->nonProxyIsExtensible()) {
++         // If the following assertion fails, there's somewhere else a missing
++         // call to shrinkCapacityToInitializedLength() which needs to be found
++         // and fixed.
++         MOZ_ASSERT_IF(obj->isNative(),
++                       obj->as<NativeObject>().getDenseInitializedLength() ==
++                       obj->as<NativeObject>().getDenseCapacity());
++ 
++         return result.succeed();
++     }
++ 
++-    if (!MaybeConvertUnboxedObjectToNative(cx, obj))
+++    if (!MaybeConvertUnboxedObjectToNative(cx, obj)) {
++         return false;
+++    }
++ 
++     if (obj->isNative()) {
++         // Force lazy properties to be resolved.
++-        if (!ResolveLazyProperties(cx, obj.as<NativeObject>()))
+++        if (!ResolveLazyProperties(cx, obj.as<NativeObject>())) {
++             return false;
+++        }
++ 
++         // Prepare the elements. We have to do this before we mark the object
++         // non-extensible; that's fine because these changes are not observable.
++-        if (!ObjectElements::PreventExtensions(cx, &obj->as<NativeObject>()))
+++        if (!ObjectElements::PreventExtensions(cx, &obj->as<NativeObject>())) {
++             return false;
++-    }
++-
++-    if (!JSObject::setFlags(cx, obj, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE))
+++        }
+++    }
+++
+++    if (!JSObject::setFlags(cx, obj, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) {
++         return false;
+++    }
++ 
++     return result.succeed();
++ }
++ 
++ bool
++ js::PreventExtensions(JSContext* cx, HandleObject obj)
++ {
++     ObjectOpResult result;
++@@ -2808,18 +3040,19 @@ js::DefineProperty(JSContext* cx, Handle
++            result.checkStrict(cx, obj, id);
++ }
++ 
++ bool
++ js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
++                    ObjectOpResult& result)
++ {
++     desc.assertValid();
++-    if (DefinePropertyOp op = obj->getOpsDefineProperty())
+++    if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
++         return op(cx, obj, id, desc, result);
+++    }
++     return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
++ }
++ 
++ bool
++ js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
++                            HandleObject getter, HandleObject setter, unsigned attrs,
++                            ObjectOpResult& result)
++ {
++@@ -2859,43 +3092,46 @@ js::DefineDataProperty(JSContext* cx, Ha
++     return DefineDataProperty(cx, obj, id, value, attrs, result);
++ }
++ 
++ bool
++ js::DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
++                       unsigned attrs, ObjectOpResult& result)
++ {
++     RootedId id(cx);
++-    if (!IndexToId(cx, index, &id))
+++    if (!IndexToId(cx, index, &id)) {
++         return false;
+++    }
++     return DefineDataProperty(cx, obj, id, value, attrs, result);
++ }
++ 
++ bool
++ js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
++                            HandleObject getter, HandleObject setter, unsigned attrs)
++ {
++     ObjectOpResult result;
++-    if (!DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result))
+++    if (!DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result)) {
++         return false;
+++    }
++     if (!result) {
++         MOZ_ASSERT(!cx->helperThread());
++         result.reportError(cx, obj, id);
++         return false;
++     }
++     return true;
++ }
++ 
++ bool
++ js::DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
++                        unsigned attrs)
++ {
++     ObjectOpResult result;
++-    if (!DefineDataProperty(cx, obj, id, value, attrs, result))
+++    if (!DefineDataProperty(cx, obj, id, value, attrs, result)) {
++         return false;
+++    }
++     if (!result) {
++         MOZ_ASSERT(!cx->helperThread());
++         result.reportError(cx, obj, id);
++         return false;
++     }
++     return true;
++ }
++ 
++@@ -3117,69 +3373,75 @@ js::ToPrimitiveSlow(JSContext* cx, JSTyp
++     // (2015 Mar 17) 7.1.1 ToPrimitive.
++     MOZ_ASSERT(preferredType == JSTYPE_UNDEFINED ||
++                preferredType == JSTYPE_STRING ||
++                preferredType == JSTYPE_NUMBER);
++     RootedObject obj(cx, &vp.toObject());
++ 
++     // Steps 4-5.
++     RootedValue method(cx);
++-    if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toPrimitive, &method))
+++    if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toPrimitive, &method)) {
++         return false;
+++    }
++ 
++     // Step 6.
++     if (!method.isNullOrUndefined()) {
++         // Step 6 of GetMethod. js::Call() below would do this check and throw a
++         // TypeError anyway, but this produces a better error message.
++-        if (!IsCallable(method))
+++        if (!IsCallable(method)) {
++             return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_NOT_CALLABLE, obj, preferredType);
+++        }
++ 
++         // Steps 1-3, 6.a-b.
++         RootedValue arg0(cx, StringValue(preferredType == JSTYPE_STRING
++                                          ? cx->names().string
++                                          : preferredType == JSTYPE_NUMBER
++                                          ? cx->names().number
++                                          : cx->names().default_));
++ 
++-        if (!js::Call(cx, method, vp, arg0, vp))
+++        if (!js::Call(cx, method, vp, arg0, vp)) {
++             return false;
+++        }
++ 
++         // Steps 6.c-d.
++-        if (vp.isObject())
+++        if (vp.isObject()) {
++             return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_RETURNED_OBJECT, obj, preferredType);
+++        }
++         return true;
++     }
++ 
++     return OrdinaryToPrimitive(cx, obj, preferredType, vp);
++ }
++ 
++ /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
++ bool
++ js::ToPropertyKeySlow(JSContext* cx, HandleValue argument, MutableHandleId result)
++ {
++     MOZ_ASSERT(argument.isObject());
++ 
++     // Steps 1-2.
++     RootedValue key(cx, argument);
++-    if (!ToPrimitiveSlow(cx, JSTYPE_STRING, &key))
+++    if (!ToPrimitiveSlow(cx, JSTYPE_STRING, &key)) {
++         return false;
+++    }
++ 
++     // Steps 3-4.
++     return ValueToId<CanGC>(cx, key, result);
++ }
++ 
++ /* * */
++ 
++ bool
++ js::IsPrototypeOf(JSContext* cx, HandleObject protoObj, JSObject* obj, bool* result)
++ {
++     RootedObject obj2(cx, obj);
++     for (;;) {
++-        if (!GetPrototype(cx, obj2, &obj2))
+++        if (!GetPrototype(cx, obj2, &obj2)) {
++             return false;
+++        }
++         if (!obj2) {
++             *result = false;
++             return true;
++         }
++         if (obj2 == protoObj) {
++             *result = true;
++             return true;
++         }
++@@ -3269,35 +3533,37 @@ js::ToObjectSlowForPropertyAccess(JSCont
++                                   bool reportScanStack)
++ {
++     MOZ_ASSERT(!val.isMagic());
++     MOZ_ASSERT(!val.isObject());
++ 
++     if (val.isNullOrUndefined()) {
++         RootedId key(cx);
++         if (keyValue.isPrimitive()) {
++-            if (!ValueToId<CanGC>(cx, keyValue, &key))
+++            if (!ValueToId<CanGC>(cx, keyValue, &key)) {
++                 return nullptr;
+++            }
++             ReportIsNullOrUndefinedForPropertyAccess(cx, val, key, reportScanStack);
++         } else {
++             ReportIsNullOrUndefinedForPropertyAccess(cx, val, reportScanStack);
++         }
++         return nullptr;
++     }
++ 
++     return PrimitiveToObject(cx, val);
++ }
++ 
++ Value
++ js::GetThisValue(JSObject* obj)
++ {
++     // Use the WindowProxy if the global is a Window, as Window must never be
++     // exposed to script.
++-    if (obj->is<GlobalObject>())
+++    if (obj->is<GlobalObject>()) {
++         return ObjectValue(*ToWindowProxyIfWindow(obj));
+++    }
++ 
++     // We should not expose any environments except NSVOs to script. The NSVO is
++     // pretending to be the global object in this case.
++     MOZ_ASSERT(obj->is<NonSyntacticVariablesObject>() || !obj->is<EnvironmentObject>());
++ 
++     return ObjectValue(*obj);
++ }
++ 
++@@ -3809,71 +4098,77 @@ JSObject::allocKindForTenure(const js::N
++ 
++     MOZ_ASSERT(IsInsideNursery(this));
++ 
++     if (is<ArrayObject>()) {
++         const ArrayObject& aobj = as<ArrayObject>();
++         MOZ_ASSERT(aobj.numFixedSlots() == 0);
++ 
++         /* Use minimal size object if we are just going to copy the pointer. */
++-        if (!nursery.isInside(aobj.getElementsHeader()))
+++        if (!nursery.isInside(aobj.getElementsHeader())) {
++             return gc::AllocKind::OBJECT0_BACKGROUND;
+++        }
++ 
++         size_t nelements = aobj.getDenseCapacity();
++         return GetBackgroundAllocKind(GetGCArrayKind(nelements));
++     }
++ 
++     // Unboxed plain objects are sized according to the data they store.
++     if (is<UnboxedPlainObject>()) {
++         size_t nbytes = as<UnboxedPlainObject>().layoutDontCheckGeneration().size();
++         return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes);
++     }
++ 
++-    if (is<JSFunction>())
+++    if (is<JSFunction>()) {
++         return as<JSFunction>().getAllocKind();
+++    }
++ 
++     /*
++      * Typed arrays in the nursery may have a lazily allocated buffer, make
++      * sure there is room for the array's fixed data when moving the array.
++      */
++     if (is<TypedArrayObject>() && !as<TypedArrayObject>().hasBuffer()) {
++         size_t nbytes = as<TypedArrayObject>().byteLength();
++-        if (as<TypedArrayObject>().hasInlineElements())
+++        if (as<TypedArrayObject>().hasInlineElements()) {
++             return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
+++        }
++         return GetGCObjectKind(getClass());
++     }
++ 
++     // Proxies that are CrossCompartmentWrappers may be nursery allocated.
++-    if (IsProxy(this))
+++    if (IsProxy(this)) {
++         return as<ProxyObject>().allocKindForTenure();
+++    }
++ 
++     // Inlined typed objects are followed by their data, so make sure we copy
++     // it all over to the new object.
++     if (is<InlineTypedObject>()) {
++         // Figure out the size of this object, from the prototype's TypeDescr.
++         // The objects we are traversing here are all tenured, so we don't need
++         // to check forwarding pointers.
++         TypeDescr& descr = as<InlineTypedObject>().typeDescr();
++         MOZ_ASSERT(!IsInsideNursery(&descr));
++         return InlineTypedObject::allocKindForTypeDescriptor(&descr);
++     }
++ 
++     // Outline typed objects use the minimum allocation kind.
++-    if (is<OutlineTypedObject>())
+++    if (is<OutlineTypedObject>()) {
++         return gc::AllocKind::OBJECT0;
+++    }
++ 
++     // All nursery allocatable non-native objects are handled above.
++     return as<NativeObject>().allocKindForTenure();
++ }
++ 
++ void
++ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info)
++ {
++-    if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots())
+++    if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots()) {
++         info->objectsMallocHeapSlots += mallocSizeOf(as<NativeObject>().slots_);
+++    }
++ 
++     if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
++         js::ObjectElements* elements = as<NativeObject>().getElementsHeader();
++         if (!elements->isCopyOnWrite() || elements->ownerObject() == this) {
++             void* allocatedElements = as<NativeObject>().getUnshiftedElementsHeader();
++             info->objectsMallocHeapElementsNormal += mallocSizeOf(allocatedElements);
++         }
++     }
+diff --git a/js/src/vm/JSObject.h b/js/src/vm/JSObject.h
+--- a/js/src/vm/JSObject.h
++++ b/js/src/vm/JSObject.h
+@@ -601,49 +601,53 @@ struct JSObject_Slots2 : JSObject { void
+ struct JSObject_Slots4 : JSObject { void* data[2]; js::Value fslots[4]; };
+ struct JSObject_Slots8 : JSObject { void* data[2]; js::Value fslots[8]; };
+ struct JSObject_Slots12 : JSObject { void* data[2]; js::Value fslots[12]; };
+ struct JSObject_Slots16 : JSObject { void* data[2]; js::Value fslots[16]; };
+ 
+ /* static */ MOZ_ALWAYS_INLINE void
+ JSObject::readBarrier(JSObject* obj)
+ {
+-    if (obj && obj->isTenured())
++    if (obj && obj->isTenured()) {
+         obj->asTenured().readBarrier(&obj->asTenured());
++    }
+ }
+ 
+ /* static */ MOZ_ALWAYS_INLINE void
+ JSObject::writeBarrierPre(JSObject* obj)
+ {
+-    if (obj && obj->isTenured())
++    if (obj && obj->isTenured()) {
+         obj->asTenured().writeBarrierPre(&obj->asTenured());
++    }
+ }
+ 
+ /* static */ MOZ_ALWAYS_INLINE void
+ JSObject::writeBarrierPost(void* cellp, JSObject* prev, JSObject* next)
+ {
+     MOZ_ASSERT(cellp);
+ 
+     // If the target needs an entry, add it.
+     js::gc::StoreBuffer* buffer;
+     if (next && (buffer = next->storeBuffer())) {
+         // If we know that the prev has already inserted an entry, we can skip
+         // doing the lookup to add the new entry. Note that we cannot safely
+         // assert the presence of the entry because it may have been added
+         // via a different store buffer.
+-        if (prev && prev->storeBuffer())
++        if (prev && prev->storeBuffer()) {
+             return;
++        }
+         buffer->putCell(static_cast<js::gc::Cell**>(cellp));
+         return;
+     }
+ 
+     // Remove the prev entry if the new value does not need it. There will only
+     // be a prev entry if the prev value was in the nursery.
+-    if (prev && (buffer = prev->storeBuffer()))
++    if (prev && (buffer = prev->storeBuffer())) {
+         buffer->unputCell(static_cast<js::gc::Cell**>(cellp));
++    }
+ }
+ 
+ namespace js {
+ 
+ /*** Standard internal methods ********************************************************************
+  *
+  * The functions below are the fundamental operations on objects. See the
+  * comment about "Standard internal methods" in jsapi.h.
+@@ -978,26 +982,28 @@ DefineFunctions(JSContext* cx, HandleObj
+ 
+ /* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
+ extern bool
+ ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
+ 
+ inline bool
+ ToPrimitive(JSContext* cx, MutableHandleValue vp)
+ {
+-    if (vp.isPrimitive())
++    if (vp.isPrimitive()) {
+         return true;
++    }
+     return ToPrimitiveSlow(cx, JSTYPE_UNDEFINED, vp);
+ }
+ 
+ inline bool
+ ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
+ {
+-    if (vp.isPrimitive())
++    if (vp.isPrimitive()) {
+         return true;
++    }
+     return ToPrimitiveSlow(cx, preferredType, vp);
+ }
+ 
+ /*
+  * toString support. (This isn't called GetClassName because there's a macro in
+  * <windows.h> with that name.)
+  */
+ MOZ_ALWAYS_INLINE const char*
+@@ -1031,20 +1037,22 @@ inline gc::InitialHeap
+ GetInitialHeap(NewObjectKind newKind, const Class* clasp)
+ {
+     if (newKind == NurseryAllocatedProxy) {
+         MOZ_ASSERT(clasp->isProxy());
+         MOZ_ASSERT(clasp->hasFinalize());
+         MOZ_ASSERT(!CanNurseryAllocateFinalizedClass(clasp));
+         return gc::DefaultHeap;
+     }
+-    if (newKind != GenericObject)
++    if (newKind != GenericObject) {
+         return gc::TenuredHeap;
+-    if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp))
++    }
++    if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp)) {
+         return gc::TenuredHeap;
++    }
+     return gc::DefaultHeap;
+ }
+ 
+ bool
+ NewObjectWithTaggedProtoIsCachable(JSContext* cx, Handle<TaggedProto> proto,
+                                    NewObjectKind newKind, const Class* clasp);
+ 
+ // ES6 9.1.15 GetPrototypeFromConstructor.
+diff --git a/js/src/vm/JSObject.h.1488698-7.later b/js/src/vm/JSObject.h.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSObject.h.1488698-7.later
+@@ -0,0 +1,139 @@
++--- JSObject.h
+++++ JSObject.h
++@@ -1190,50 +1198,54 @@ PrimitiveToObject(JSContext* cx, const V
++ } /* namespace js */
++ 
++ namespace js {
++ 
++ /* For converting stack values to objects. */
++ MOZ_ALWAYS_INLINE JSObject*
++ ToObjectFromStack(JSContext* cx, HandleValue vp)
++ {
++-    if (vp.isObject())
+++    if (vp.isObject()) {
++         return &vp.toObject();
+++    }
++     return js::ToObjectSlow(cx, vp, true);
++ }
++ 
++ JSObject*
++ ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandleId key,
++                               bool reportScanStack);
++ JSObject*
++ ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandlePropertyName key,
++                               bool reportScanStack);
++ JSObject*
++ ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandleValue keyValue,
++                               bool reportScanStack);
++ 
++ MOZ_ALWAYS_INLINE JSObject*
++ ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandleId key)
++ {
++-    if (vp.isObject())
+++    if (vp.isObject()) {
++         return &vp.toObject();
+++    }
++     return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
++ }
++ MOZ_ALWAYS_INLINE JSObject*
++ ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandlePropertyName key)
++ {
++-    if (vp.isObject())
+++    if (vp.isObject()) {
++         return &vp.toObject();
+++    }
++     return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
++ }
++ MOZ_ALWAYS_INLINE JSObject*
++ ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandleValue key)
++ {
++-    if (vp.isObject())
+++    if (vp.isObject()) {
++         return &vp.toObject();
+++    }
++     return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
++ }
++ 
++ template<XDRMode mode>
++ XDRResult
++ XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
++ 
++ /*
++@@ -1241,51 +1253,54 @@ XDRObjectLiteral(XDRState<mode>* xdr, Mu
++  * Using NotNullObject is usually less code.
++  */
++ extern void
++ ReportNotObject(JSContext* cx, HandleValue v);
++ 
++ inline JSObject*
++ NonNullObject(JSContext* cx, HandleValue v)
++ {
++-    if (v.isObject())
+++    if (v.isObject()) {
++         return &v.toObject();
+++    }
++     ReportNotObject(cx, v);
++     return nullptr;
++ }
++ 
++ 
++ /*
++  * Report a TypeError: "N-th argument of FUN must be an object, got VALUE".
++  * Using NotNullObjectArg is usually less code.
++  */
++ extern void
++ ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v);
++ 
++ inline JSObject*
++ NonNullObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v)
++ {
++-    if (v.isObject())
+++    if (v.isObject()) {
++         return &v.toObject();
+++    }
++     ReportNotObjectArg(cx, nth, fun, v);
++     return nullptr;
++ }
++ 
++ /*
++  * Report a TypeError: "SOMETHING must be an object, got VALUE".
++  * Using NotNullObjectWithName is usually less code.
++  */
++ extern void
++ ReportNotObjectWithName(JSContext* cx, const char* name, HandleValue v);
++ 
++ inline JSObject*
++ NonNullObjectWithName(JSContext* cx, const char* name, HandleValue v)
++ {
++-    if (v.isObject())
+++    if (v.isObject()) {
++         return &v.toObject();
+++    }
++     ReportNotObjectWithName(cx, name, v);
++     return nullptr;
++ }
++ 
++ 
++ extern bool
++ GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
++                          MutableHandleObject objp);
++@@ -1327,17 +1342,18 @@ SpeciesConstructor(JSContext* cx, Handle
++ extern bool
++ GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj);
++ 
++ 
++ #ifdef DEBUG
++ inline bool
++ IsObjectValueInCompartment(const Value& v, JS::Compartment* comp)
++ {
++-    if (!v.isObject())
+++    if (!v.isObject()) {
++         return true;
+++    }
++     return v.toObject().compartment() == comp;
++ }
++ #endif
++ 
++ }  /* namespace js */
++ 
++ #endif /* vm_JSObject_h */
+diff --git a/js/src/vm/JSScript-inl.h b/js/src/vm/JSScript-inl.h
+--- a/js/src/vm/JSScript-inl.h
++++ b/js/src/vm/JSScript-inl.h
+@@ -75,44 +75,47 @@ ScriptAndCounts::ScriptAndCounts(ScriptA
+ void
+ SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame,
+                         HandleScript script, JSObject* argsobj);
+ 
+ /* static */ inline JSFunction*
+ LazyScript::functionDelazifying(JSContext* cx, Handle<LazyScript*> script)
+ {
+     RootedFunction fun(cx, script->function_);
+-    if (script->function_ && !JSFunction::getOrCreateScript(cx, fun))
++    if (script->function_ && !JSFunction::getOrCreateScript(cx, fun)) {
+         return nullptr;
++    }
+     return script->function_;
+ }
+ 
+ } // namespace js
+ 
+ inline JSFunction*
+ JSScript::functionDelazifying() const
+ {
+     JSFunction* fun = function();
+     if (fun && fun->isInterpretedLazy()) {
+         fun->setUnlazifiedScript(const_cast<JSScript*>(this));
+         // If this script has a LazyScript, make sure the LazyScript has a
+         // reference to the script when delazifying its canonical function.
+-        if (lazyScript && !lazyScript->maybeScript())
++        if (lazyScript && !lazyScript->maybeScript()) {
+             lazyScript->initScript(const_cast<JSScript*>(this));
++        }
+     }
+     return fun;
+ }
+ 
+ inline void
+ JSScript::ensureNonLazyCanonicalFunction()
+ {
+     // Infallibly delazify the canonical script.
+     JSFunction* fun = function();
+-    if (fun && fun->isInterpretedLazy())
++    if (fun && fun->isInterpretedLazy()) {
+         functionDelazifying();
++    }
+ }
+ 
+ inline JSFunction*
+ JSScript::getFunction(size_t index)
+ {
+     JSObject* obj = getObject(index);
+     MOZ_RELEASE_ASSERT(obj->is<JSFunction>(), "Script object is not JSFunction");
+     JSFunction* fun = &obj->as<JSFunction>();
+@@ -163,48 +166,52 @@ JSScript::maybeNamedLambdaScope() const
+     return nullptr;
+ }
+ 
+ inline js::Shape*
+ JSScript::initialEnvironmentShape() const
+ {
+     js::Scope* scope = bodyScope();
+     if (scope->is<js::FunctionScope>()) {
+-        if (js::Shape* envShape = scope->environmentShape())
++        if (js::Shape* envShape = scope->environmentShape()) {
+             return envShape;
+-        if (js::Scope* namedLambdaScope = maybeNamedLambdaScope())
++        }
++        if (js::Scope* namedLambdaScope = maybeNamedLambdaScope()) {
+             return namedLambdaScope->environmentShape();
++        }
+     } else if (scope->is<js::EvalScope>()) {
+         return scope->environmentShape();
+     }
+     return nullptr;
+ }
+ 
+ inline JSPrincipals*
+ JSScript::principals()
+ {
+     return realm()->principals();
+ }
+ 
+ inline void
+ JSScript::setBaselineScript(JSRuntime* rt, js::jit::BaselineScript* baselineScript)
+ {
+-    if (hasBaselineScript())
++    if (hasBaselineScript()) {
+         js::jit::BaselineScript::writeBarrierPre(zone(), baseline);
++    }
+     MOZ_ASSERT(!ion || ion == ION_DISABLED_SCRIPT);
+     baseline = baselineScript;
+     resetWarmUpResetCounter();
+     updateJitCodeRaw(rt);
+ }
+ 
+ inline bool
+ JSScript::ensureHasAnalyzedArgsUsage(JSContext* cx)
+ {
+-    if (analyzedArgsUsage())
++    if (analyzedArgsUsage()) {
+         return true;
++    }
+     return js::jit::AnalyzeArgumentsUsage(cx, this);
+ }
+ 
+ inline bool
+ JSScript::isDebuggee() const
+ {
+     return realm_->debuggerObservesAllExecution() || bitFields_.hasDebugScript_;
+ }
+diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
+--- a/js/src/vm/JSScript.cpp
++++ b/js/src/vm/JSScript.cpp
+@@ -116,71 +116,84 @@ js::XDRScriptConst(XDRState<mode>* xdr, 
+         }
+     }
+ 
+     MOZ_TRY(xdr->codeEnum32(&tag));
+ 
+     switch (tag) {
+       case SCRIPT_INT: {
+         uint32_t i;
+-        if (mode == XDR_ENCODE)
++        if (mode == XDR_ENCODE) {
+             i = uint32_t(vp.toInt32());
++        }
+         MOZ_TRY(xdr->codeUint32(&i));
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.set(Int32Value(int32_t(i)));
++        }
+         break;
+       }
+       case SCRIPT_DOUBLE: {
+         double d;
+-        if (mode == XDR_ENCODE)
++        if (mode == XDR_ENCODE) {
+             d = vp.toDouble();
++        }
+         MOZ_TRY(xdr->codeDouble(&d));
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.set(DoubleValue(d));
++        }
+         break;
+       }
+       case SCRIPT_ATOM: {
+         RootedAtom atom(cx);
+-        if (mode == XDR_ENCODE)
++        if (mode == XDR_ENCODE) {
+             atom = &vp.toString()->asAtom();
++        }
+         MOZ_TRY(XDRAtom(xdr, &atom));
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.set(StringValue(atom));
++        }
+         break;
+       }
+       case SCRIPT_TRUE:
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.set(BooleanValue(true));
++        }
+         break;
+       case SCRIPT_FALSE:
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.set(BooleanValue(false));
++        }
+         break;
+       case SCRIPT_NULL:
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.set(NullValue());
++        }
+         break;
+       case SCRIPT_OBJECT: {
+         RootedObject obj(cx);
+-        if (mode == XDR_ENCODE)
++        if (mode == XDR_ENCODE) {
+             obj = &vp.toObject();
++        }
+ 
+         MOZ_TRY(XDRObjectLiteral(xdr, &obj));
+ 
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.setObject(*obj);
++        }
+         break;
+       }
+       case SCRIPT_VOID:
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.set(UndefinedValue());
++        }
+         break;
+       case SCRIPT_HOLE:
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             vp.setMagic(JS_ELEMENTS_HOLE);
++        }
+         break;
+       default:
+         // Fail in debug, but only soft-fail in release
+         MOZ_ASSERT(false, "Bad XDR value kind");
+         return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
+     }
+     return Ok();
+ }
+@@ -202,23 +215,25 @@ XDRLazyClosedOverBindings(XDRState<mode>
+         uint8_t endOfScopeSentinel;
+         if (mode == XDR_ENCODE) {
+             atom = lazy->closedOverBindings()[i];
+             endOfScopeSentinel = !atom;
+         }
+ 
+         MOZ_TRY(xdr->codeUint8(&endOfScopeSentinel));
+ 
+-        if (endOfScopeSentinel)
++        if (endOfScopeSentinel) {
+             atom = nullptr;
+-        else
++        } else {
+             MOZ_TRY(XDRAtom(xdr, &atom));
+-
+-        if (mode == XDR_DECODE)
++        }
++
++        if (mode == XDR_DECODE) {
+             lazy->closedOverBindings()[i] = atom;
++        }
+     }
+ 
+     return Ok();
+ }
+ 
+ // Code the missing part needed to re-create a LazyScript from a JSScript.
+ template<XDRMode mode>
+ static XDRResult
+@@ -255,18 +270,19 @@ XDRRelazificationInfo(XDRState<mode>* xd
+ 
+         MOZ_TRY(xdr->codeUint64(&packedFields));
+ 
+         if (mode == XDR_DECODE) {
+             RootedScriptSourceObject sourceObject(cx, &script->scriptSourceUnwrap());
+             lazy.set(LazyScript::CreateForXDR(cx, fun, script, enclosingScope, sourceObject,
+                                               packedFields, sourceStart, sourceEnd, toStringStart,
+                                               lineno, column));
+-            if (!lazy)
++            if (!lazy) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+ 
+             lazy->setToStringEnd(toStringEnd);
+ 
+             // As opposed to XDRLazyScript, we need to restore the runtime bits
+             // of the script, as we are trying to match the fact this function
+             // has already been parsed and that it would need to be re-lazified.
+             lazy->initRuntimeFields(packedFields);
+         }
+@@ -283,18 +299,19 @@ XDRRelazificationInfo(XDRState<mode>* xd
+ 
+ static inline uint32_t
+ FindScopeIndex(JSScript* script, Scope& scope)
+ {
+     ScopeArray* scopes = script->scopes();
+     GCPtrScope* vector = scopes->vector;
+     unsigned length = scopes->length;
+     for (uint32_t i = 0; i < length; ++i) {
+-        if (vector[i] == &scope)
++        if (vector[i] == &scope) {
+             return i;
++        }
+     }
+ 
+     MOZ_CRASH("Scope not found");
+ }
+ 
+ enum XDRClassKind {
+     CK_RegexpObject,
+     CK_JSFunction,
+@@ -364,96 +381,126 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+             if (!realm->creationOptions().cloneSingletons() ||
+                 !realm->behaviors().getSingletonsAsTemplates())
+             {
+                 return xdr->fail(JS::TranscodeResult_Failure_RunOnceNotSupported);
+             }
+         }
+     }
+ 
+-    if (mode == XDR_ENCODE)
++    if (mode == XDR_ENCODE) {
+         length = script->length();
++    }
+     MOZ_TRY(xdr->codeUint32(&length));
+ 
+     if (mode == XDR_ENCODE) {
+         prologueLength = script->mainOffset();
+         lineno = script->lineno();
+         column = script->column();
+         nfixed = script->nfixed();
+         nslots = script->nslots();
+ 
+         bodyScopeIndex = script->bodyScopeIndex();
+         natoms = script->natoms();
+ 
+         nsrcnotes = script->numNotes();
+ 
+-        if (script->hasConsts())
++        if (script->hasConsts()) {
+             nconsts = script->consts()->length;
+-        if (script->hasObjects())
++        }
++        if (script->hasObjects()) {
+             nobjects = script->objects()->length;
++        }
+         nscopes = script->scopes()->length;
+-        if (script->hasTrynotes())
++        if (script->hasTrynotes()) {
+             ntrynotes = script->trynotes()->length;
+-        if (script->hasScopeNotes())
++        }
++        if (script->hasScopeNotes()) {
+             nscopenotes = script->scopeNotes()->length;
+-        if (script->hasYieldAndAwaitOffsets())
++        }
++        if (script->hasYieldAndAwaitOffsets()) {
+             nyieldoffsets = script->yieldAndAwaitOffsets().length();
++        }
+ 
+         nTypeSets = script->nTypeSets();
+         funLength = script->funLength();
+ 
+-        if (script->noScriptRval())
++        if (script->noScriptRval()) {
+             scriptBits |= (1 << NoScriptRval);
+-        if (script->strict())
++        }
++        if (script->strict()) {
+             scriptBits |= (1 << Strict);
+-        if (script->explicitUseStrict())
++        }
++        if (script->explicitUseStrict()) {
+             scriptBits |= (1 << ExplicitUseStrict);
+-        if (script->selfHosted())
++        }
++        if (script->selfHosted()) {
+             scriptBits |= (1 << SelfHosted);
+-        if (script->bindingsAccessedDynamically())
++        }
++        if (script->bindingsAccessedDynamically()) {
+             scriptBits |= (1 << ContainsDynamicNameAccess);
+-        if (script->funHasExtensibleScope())
++        }
++        if (script->funHasExtensibleScope()) {
+             scriptBits |= (1 << FunHasExtensibleScope);
+-        if (script->funHasAnyAliasedFormal())
++        }
++        if (script->funHasAnyAliasedFormal()) {
+             scriptBits |= (1 << FunHasAnyAliasedFormal);
+-        if (script->argumentsHasVarBinding())
++        }
++        if (script->argumentsHasVarBinding()) {
+             scriptBits |= (1 << ArgumentsHasVarBinding);
+-        if (script->analyzedArgsUsage() && script->needsArgsObj())
++        }
++        if (script->analyzedArgsUsage() && script->needsArgsObj()) {
+             scriptBits |= (1 << NeedsArgsObj);
+-        if (script->hasMappedArgsObj())
++        }
++        if (script->hasMappedArgsObj()) {
+             scriptBits |= (1 << HasMappedArgsObj);
+-        if (script->functionHasThisBinding())
++        }
++        if (script->functionHasThisBinding()) {
+             scriptBits |= (1 << FunctionHasThisBinding);
+-        if (script->functionHasExtraBodyVarScope())
++        }
++        if (script->functionHasExtraBodyVarScope()) {
+             scriptBits |= (1 << FunctionHasExtraBodyVarScope);
++        }
+         MOZ_ASSERT_IF(sourceObjectArg, sourceObjectArg->source() == script->scriptSource());
+-        if (!sourceObjectArg)
++        if (!sourceObjectArg) {
+             scriptBits |= (1 << OwnSource);
+-        if (script->isGenerator())
++        }
++        if (script->isGenerator()) {
+             scriptBits |= (1 << IsGenerator);
+-        if (script->isAsync())
++        }
++        if (script->isAsync()) {
+             scriptBits |= (1 << IsAsync);
+-        if (script->hasRest())
++        }
++        if (script->hasRest()) {
+             scriptBits |= (1 << HasRest);
+-        if (script->hasSingletons())
++        }
++        if (script->hasSingletons()) {
+             scriptBits |= (1 << HasSingleton);
+-        if (script->treatAsRunOnce())
++        }
++        if (script->treatAsRunOnce()) {
+             scriptBits |= (1 << TreatAsRunOnce);
+-        if (script->isRelazifiable())
++        }
++        if (script->isRelazifiable()) {
+             scriptBits |= (1 << HasLazyScript);
+-        if (script->hasNonSyntacticScope())
++        }
++        if (script->hasNonSyntacticScope()) {
+             scriptBits |= (1 << HasNonSyntacticScope);
+-        if (script->hasInnerFunctions())
++        }
++        if (script->hasInnerFunctions()) {
+             scriptBits |= (1 << HasInnerFunctions);
+-        if (script->needsHomeObject())
++        }
++        if (script->needsHomeObject()) {
+             scriptBits |= (1 << NeedsHomeObject);
+-        if (script->isDerivedClassConstructor())
++        }
++        if (script->isDerivedClassConstructor()) {
+             scriptBits |= (1 << IsDerivedClassConstructor);
+-        if (script->isDefaultClassConstructor())
++        }
++        if (script->isDefaultClassConstructor()) {
+             scriptBits |= (1 << IsDefaultClassConstructor);
++        }
+     }
+ 
+     MOZ_TRY(xdr->codeUint32(&prologueLength));
+ 
+     // To fuse allocations, we need lengths of all embedded arrays early.
+     MOZ_TRY(xdr->codeUint32(&natoms));
+     MOZ_TRY(xdr->codeUint32(&nsrcnotes));
+     MOZ_TRY(xdr->codeUint32(&nconsts));
+@@ -485,50 +532,55 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+         } else {
+             options.emplace(xdr->cx());
+             (*options).setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
+                       .setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
+         }
+ 
+         if (scriptBits & (1 << OwnSource)) {
+             ScriptSource* ss = cx->new_<ScriptSource>();
+-            if (!ss)
++            if (!ss) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+             ScriptSourceHolder ssHolder(ss);
+ 
+             /*
+              * We use this CompileOptions only to initialize the
+              * ScriptSourceObject. Most CompileOptions fields aren't used by
+              * ScriptSourceObject, and those that are (element; elementAttributeName)
+              * aren't preserved by XDR. So this can be simple.
+              */
+-            if (!ss->initFromOptions(cx, *options))
++            if (!ss->initFromOptions(cx, *options)) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+ 
+             sourceObject = ScriptSourceObject::create(cx, ss);
+-            if (!sourceObject)
++            if (!sourceObject) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+ 
+             if (xdr->hasScriptSourceObjectOut()) {
+                 // When the ScriptSourceObjectOut is provided by ParseTask, it
+                 // is stored in a location which is traced by the GC.
+                 *xdr->scriptSourceObjectOut() = sourceObject;
+             } else if (!ScriptSourceObject::initFromOptions(cx, sourceObject, *options)) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
+             }
+         }
+ 
+         script = JSScript::Create(cx, *options, sourceObject, 0, 0, 0, 0);
+-        if (!script)
++        if (!script) {
+             return xdr->fail(JS::TranscodeResult_Throw);
++        }
+ 
+         // Set the script in its function now so that inner scripts to be
+         // decoded may iterate the static scope chain.
+-        if (fun)
++        if (fun) {
+             fun->initScript(script);
++        }
+     } else {
+         // When encoding, we do not mutate any of the JSScript or LazyScript, so
+         // we can safely unwrap it here.
+         sourceObject = &script->scriptSourceUnwrap();
+     }
+ 
+     if (mode == XDR_DECODE) {
+         if (!JSScript::partiallyInit(cx, script, nscopes, nconsts, nobjects, ntrynotes,
+@@ -541,63 +593,84 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+         script->mainOffset_ = prologueLength;
+         script->funLength_ = funLength;
+ 
+         MOZ_ASSERT(nTypeSets <= UINT16_MAX);
+         script->nTypeSets_ = uint16_t(nTypeSets);
+ 
+         scriptp.set(script);
+ 
+-        if (scriptBits & (1 << Strict))
++        if (scriptBits & (1 << Strict)) {
+             script->bitFields_.strict_ = true;
+-        if (scriptBits & (1 << ExplicitUseStrict))
++        }
++        if (scriptBits & (1 << ExplicitUseStrict)) {
+             script->bitFields_.explicitUseStrict_ = true;
+-        if (scriptBits & (1 << ContainsDynamicNameAccess))
++        }
++        if (scriptBits & (1 << ContainsDynamicNameAccess)) {
+             script->bitFields_.bindingsAccessedDynamically_ = true;
+-        if (scriptBits & (1 << FunHasExtensibleScope))
++        }
++        if (scriptBits & (1 << FunHasExtensibleScope)) {
+             script->bitFields_.funHasExtensibleScope_ = true;
+-        if (scriptBits & (1 << FunHasAnyAliasedFormal))
++        }
++        if (scriptBits & (1 << FunHasAnyAliasedFormal)) {
+             script->bitFields_.funHasAnyAliasedFormal_ = true;
+-        if (scriptBits & (1 << ArgumentsHasVarBinding))
++        }
++        if (scriptBits & (1 << ArgumentsHasVarBinding)) {
+             script->setArgumentsHasVarBinding();
+-        if (scriptBits & (1 << NeedsArgsObj))
++        }
++        if (scriptBits & (1 << NeedsArgsObj)) {
+             script->setNeedsArgsObj(true);
+-        if (scriptBits & (1 << HasMappedArgsObj))
++        }
++        if (scriptBits & (1 << HasMappedArgsObj)) {
+             script->bitFields_.hasMappedArgsObj_ = true;
+-        if (scriptBits & (1 << FunctionHasThisBinding))
++        }
++        if (scriptBits & (1 << FunctionHasThisBinding)) {
+             script->bitFields_.functionHasThisBinding_ = true;
+-        if (scriptBits & (1 << FunctionHasExtraBodyVarScope))
++        }
++        if (scriptBits & (1 << FunctionHasExtraBodyVarScope)) {
+             script->bitFields_.functionHasExtraBodyVarScope_ = true;
+-        if (scriptBits & (1 << HasSingleton))
++        }
++        if (scriptBits & (1 << HasSingleton)) {
+             script->bitFields_.hasSingletons_ = true;
+-        if (scriptBits & (1 << TreatAsRunOnce))
++        }
++        if (scriptBits & (1 << TreatAsRunOnce)) {
+             script->bitFields_.treatAsRunOnce_ = true;
+-        if (scriptBits & (1 << HasNonSyntacticScope))
++        }
++        if (scriptBits & (1 << HasNonSyntacticScope)) {
+             script->bitFields_.hasNonSyntacticScope_ = true;
+-        if (scriptBits & (1 << HasInnerFunctions))
++        }
++        if (scriptBits & (1 << HasInnerFunctions)) {
+             script->bitFields_.hasInnerFunctions_ = true;
+-        if (scriptBits & (1 << NeedsHomeObject))
++        }
++        if (scriptBits & (1 << NeedsHomeObject)) {
+             script->bitFields_.needsHomeObject_ = true;
+-        if (scriptBits & (1 << IsDerivedClassConstructor))
++        }
++        if (scriptBits & (1 << IsDerivedClassConstructor)) {
+             script->bitFields_.isDerivedClassConstructor_ = true;
+-        if (scriptBits & (1 << IsDefaultClassConstructor))
++        }
++        if (scriptBits & (1 << IsDefaultClassConstructor)) {
+             script->bitFields_.isDefaultClassConstructor_ = true;
+-        if (scriptBits & (1 << IsGenerator))
++        }
++        if (scriptBits & (1 << IsGenerator)) {
+             script->setGeneratorKind(GeneratorKind::Generator);
+-        if (scriptBits & (1 << IsAsync))
++        }
++        if (scriptBits & (1 << IsAsync)) {
+             script->setAsyncKind(FunctionAsyncKind::AsyncFunction);
+-        if (scriptBits & (1 << HasRest))
++        }
++        if (scriptBits & (1 << HasRest)) {
+             script->setHasRest();
++        }
+     }
+ 
+     JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
+     JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
+ 
+-    if (scriptBits & (1 << OwnSource))
++    if (scriptBits & (1 << OwnSource)) {
+         MOZ_TRY(sourceObject->source()->performXDR<mode>(xdr));
++    }
+     MOZ_TRY(xdr->codeUint32(&script->sourceStart_));
+     MOZ_TRY(xdr->codeUint32(&script->sourceEnd_));
+     MOZ_TRY(xdr->codeUint32(&script->toStringStart_));
+     MOZ_TRY(xdr->codeUint32(&script->toStringEnd_));
+     MOZ_TRY(xdr->codeUint32(&lineno));
+     MOZ_TRY(xdr->codeUint32(&column));
+     MOZ_TRY(xdr->codeUint32(&nfixed));
+     MOZ_TRY(xdr->codeUint32(&nslots));
+@@ -607,23 +680,25 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+         script->lineno_ = lineno;
+         script->column_ = column;
+         script->nfixed_ = nfixed;
+         script->nslots_ = nslots;
+         script->bodyScopeIndex_ = bodyScopeIndex;
+     }
+ 
+     if (mode == XDR_DECODE) {
+-        if (!script->createScriptData(cx, length, nsrcnotes, natoms))
++        if (!script->createScriptData(cx, length, nsrcnotes, natoms)) {
+             return xdr->fail(JS::TranscodeResult_Throw);
++        }
+     }
+ 
+     auto scriptDataGuard = mozilla::MakeScopeExit([&] {
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             script->freeScriptData();
++        }
+     });
+ 
+     jsbytecode* code = script->code();
+     MOZ_TRY(xdr->codeBytes(code, length));
+     MOZ_TRY(xdr->codeBytes(code + length, nsrcnotes));
+ 
+     for (i = 0; i != natoms; ++i) {
+         if (mode == XDR_DECODE) {
+@@ -633,29 +708,32 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+         } else {
+             RootedAtom tmp(cx, script->atoms()[i]);
+             MOZ_TRY(XDRAtom(xdr, &tmp));
+         }
+     }
+ 
+     scriptDataGuard.release();
+     if (mode == XDR_DECODE) {
+-        if (!script->shareScriptData(cx))
++        if (!script->shareScriptData(cx)) {
+             return xdr->fail(JS::TranscodeResult_Throw);
++        }
+     }
+ 
+     if (nconsts) {
+         GCPtrValue* vector = script->consts()->vector;
+         RootedValue val(cx);
+         for (i = 0; i != nconsts; ++i) {
+-            if (mode == XDR_ENCODE)
++            if (mode == XDR_ENCODE) {
+                 val = vector[i];
++            }
+             MOZ_TRY(XDRScriptConst(xdr, &val));
+-            if (mode == XDR_DECODE)
++            if (mode == XDR_DECODE) {
+                 vector[i].init(val);
++            }
+         }
+     }
+ 
+     {
+         MOZ_ASSERT(nscopes != 0);
+         GCPtrScope* vector = script->scopes()->vector;
+         RootedScope scope(cx);
+         RootedScope enclosing(cx);
+@@ -706,18 +784,19 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+               case ScopeKind::Catch:
+               case ScopeKind::NamedLambda:
+               case ScopeKind::StrictNamedLambda:
+                 MOZ_TRY(LexicalScope::XDR(xdr, scopeKind, enclosing, &scope));
+                 break;
+               case ScopeKind::With:
+                 if (mode == XDR_DECODE) {
+                     scope = WithScope::create(cx, enclosing);
+-                    if (!scope)
++                    if (!scope) {
+                         return xdr->fail(JS::TranscodeResult_Throw);
++                    }
+                 }
+                 break;
+               case ScopeKind::Eval:
+               case ScopeKind::StrictEval:
+                 MOZ_TRY(EvalScope::XDR(xdr, scopeKind, enclosing, &scope));
+                 break;
+               case ScopeKind::Global:
+               case ScopeKind::NonSyntactic:
+@@ -731,18 +810,19 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+                 MOZ_CRASH("wasm functions cannot be nested in JSScripts");
+                 break;
+               default:
+                 // Fail in debug, but only soft-fail in release
+                 MOZ_ASSERT(false, "Bad XDR scope kind");
+                 return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
+             }
+ 
+-            if (mode == XDR_DECODE)
++            if (mode == XDR_DECODE) {
+                 vector[i].init(scope);
++            }
+         }
+ 
+         // Verify marker to detect data corruption after decoding scope data. A
+         // mismatch here indicates we will almost certainly crash in release.
+         MOZ_TRY(xdr->codeMarker(0x48922BAB));
+     }
+ 
+     /*
+@@ -751,36 +831,39 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+      * after the enclosing block has been XDR'd.
+      */
+     for (i = 0; i != nobjects; ++i) {
+         GCPtrObject* objp = &script->objects()->vector[i];
+         XDRClassKind classk;
+ 
+         if (mode == XDR_ENCODE) {
+             JSObject* obj = *objp;
+-            if (obj->is<RegExpObject>())
++            if (obj->is<RegExpObject>()) {
+                 classk = CK_RegexpObject;
+-            else if (obj->is<JSFunction>())
++            } else if (obj->is<JSFunction>()) {
+                 classk = CK_JSFunction;
+-            else if (obj->is<PlainObject>() || obj->is<ArrayObject>())
++            } else if (obj->is<PlainObject>() || obj->is<ArrayObject>()) {
+                 classk = CK_JSObject;
+-            else
++            } else {
+                 MOZ_CRASH("Cannot encode this class of object.");
++            }
+         }
+ 
+         MOZ_TRY(xdr->codeEnum32(&classk));
+ 
+         switch (classk) {
+           case CK_RegexpObject: {
+             Rooted<RegExpObject*> regexp(cx);
+-            if (mode == XDR_ENCODE)
++            if (mode == XDR_ENCODE) {
+                 regexp = &(*objp)->as<RegExpObject>();
++            }
+             MOZ_TRY(XDRScriptRegExpObject(xdr, &regexp));
+-            if (mode == XDR_DECODE)
++            if (mode == XDR_DECODE) {
+                 *objp = regexp;
++            }
+             break;
+           }
+ 
+           case CK_JSFunction: {
+             /* Code the nested function's enclosing scope. */
+             uint32_t funEnclosingScopeIndex = 0;
+             RootedScope funEnclosingScope(cx);
+             if (mode == XDR_ENCODE) {
+@@ -802,18 +885,19 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+ 
+             if (mode == XDR_DECODE) {
+                 MOZ_ASSERT(funEnclosingScopeIndex < script->scopes()->length);
+                 funEnclosingScope = script->scopes()->vector[funEnclosingScopeIndex];
+             }
+ 
+             // Code nested function and script.
+             RootedFunction tmp(cx);
+-            if (mode == XDR_ENCODE)
++            if (mode == XDR_ENCODE) {
+                 tmp = &(*objp)->as<JSFunction>();
++            }
+             MOZ_TRY(XDRInterpretedFunction(xdr, funEnclosingScope, sourceObject, &tmp));
+             *objp = tmp;
+             break;
+           }
+ 
+           case CK_JSObject: {
+             /* Code object literal. */
+             RootedObject tmp(cx, *objp);
+@@ -857,31 +941,34 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+ 
+     for (i = 0; i < nyieldoffsets; ++i) {
+         uint32_t* offset = &script->yieldAndAwaitOffsets()[i];
+         MOZ_TRY(xdr->codeUint32(offset));
+     }
+ 
+     if (scriptBits & (1 << HasLazyScript)) {
+         Rooted<LazyScript*> lazy(cx);
+-        if (mode == XDR_ENCODE)
++        if (mode == XDR_ENCODE) {
+             lazy = script->maybeLazyScript();
++        }
+ 
+         MOZ_TRY(XDRRelazificationInfo(xdr, fun, script, scriptEnclosingScope, &lazy));
+ 
+-        if (mode == XDR_DECODE)
++        if (mode == XDR_DECODE) {
+             script->setLazyScript(lazy);
++        }
+     }
+ 
+     if (mode == XDR_DECODE) {
+         scriptp.set(script);
+ 
+         /* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
+-        if (!fun && !cx->helperThread())
++        if (!fun && !cx->helperThread()) {
+             Debugger::onNewScript(cx, script);
++        }
+     }
+ 
+     return Ok();
+ }
+ 
+ template XDRResult
+ js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSourceObject, HandleFunction,
+               MutableHandleScript);
+@@ -932,41 +1019,44 @@ js::XDRLazyScript(XDRState<mode>* xdr, H
+         MOZ_TRY(xdr->codeUint32(&lineno));
+         MOZ_TRY(xdr->codeUint32(&column));
+         MOZ_TRY(xdr->codeUint64(&packedFields));
+ 
+         if (mode == XDR_DECODE) {
+             lazy.set(LazyScript::CreateForXDR(cx, fun, nullptr, enclosingScope, sourceObject,
+                                               packedFields, sourceStart, sourceEnd, toStringStart,
+                                               lineno, column));
+-            if (!lazy)
++            if (!lazy) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+             lazy->setToStringEnd(toStringEnd);
+             fun->initLazyScript(lazy);
+         }
+     }
+ 
+     // Code closed-over bindings.
+     MOZ_TRY(XDRLazyClosedOverBindings(xdr, lazy));
+ 
+     // Code inner functions.
+     {
+         RootedFunction func(cx);
+         GCPtrFunction* innerFunctions = lazy->innerFunctions();
+         size_t numInnerFunctions = lazy->numInnerFunctions();
+         for (size_t i = 0; i < numInnerFunctions; i++) {
+-            if (mode == XDR_ENCODE)
++            if (mode == XDR_ENCODE) {
+                 func = innerFunctions[i];
++            }
+ 
+             MOZ_TRY(XDRInterpretedFunction(xdr, nullptr, sourceObject, &func));
+ 
+             if (mode == XDR_DECODE) {
+                 innerFunctions[i] = func;
+-                if (innerFunctions[i]->isInterpretedLazy())
++                if (innerFunctions[i]->isInterpretedLazy()) {
+                     innerFunctions[i]->lazyScript()->setEnclosingLazyScript(lazy);
++                }
+             }
+         }
+     }
+ 
+     return Ok();
+ }
+ 
+ template XDRResult
+@@ -1041,24 +1131,26 @@ JSScript::initScriptCounts(JSContext* cx
+ 
+     // Initialize all PCCounts counters to 0.
+     ScriptCounts::PCCountsVector base;
+     if (!base.reserve(jumpTargets.length())) {
+         ReportOutOfMemory(cx);
+         return false;
+     }
+ 
+-    for (size_t i = 0; i < jumpTargets.length(); i++)
++    for (size_t i = 0; i < jumpTargets.length(); i++) {
+         base.infallibleEmplaceBack(pcToOffset(jumpTargets[i]));
++    }
+ 
+     // Create realm's scriptCountsMap if necessary.
+     if (!realm()->scriptCountsMap) {
+         auto map = cx->make_unique<ScriptCountsMap>();
+-        if (!map)
++        if (!map) {
+             return false;
++        }
+ 
+         if (!map->init()) {
+             ReportOutOfMemory(cx);
+             return false;
+         }
+ 
+         realm()->scriptCountsMap = std::move(map);
+     }
+@@ -1077,18 +1169,19 @@ JSScript::initScriptCounts(JSContext* cx
+     }
+ 
+     // safe to set this;  we can't fail after this point.
+     bitFields_.hasScriptCounts_ = true;
+ 
+     // Enable interrupts in any interpreter frames running on this script. This
+     // is used to let the interpreter increment the PCCounts, if present.
+     for (ActivationIterator iter(cx); !iter.done(); ++iter) {
+-        if (iter->isInterpreter())
++        if (iter->isInterpreter()) {
+             iter->asInterpreter()->enableInterruptsIfRunning(this);
++        }
+     }
+ 
+     return true;
+ }
+ 
+ static inline ScriptCountsMap::Ptr
+ GetScriptCountsMapEntry(JSScript* script)
+ {
+@@ -1119,93 +1212,104 @@ JSScript::getScriptName()
+     auto p = GetScriptNameMapEntry(this);
+     return p->value().get();
+ }
+ 
+ js::PCCounts*
+ ScriptCounts::maybeGetPCCounts(size_t offset) {
+     PCCounts searched = PCCounts(offset);
+     PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
+-    if (elem == pcCounts_.end() || elem->pcOffset() != offset)
++    if (elem == pcCounts_.end() || elem->pcOffset() != offset) {
+         return nullptr;
++    }
+     return elem;
+ }
+ 
+ const js::PCCounts*
+ ScriptCounts::maybeGetPCCounts(size_t offset) const {
+     PCCounts searched = PCCounts(offset);
+     const PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
+-    if (elem == pcCounts_.end() || elem->pcOffset() != offset)
++    if (elem == pcCounts_.end() || elem->pcOffset() != offset) {
+         return nullptr;
++    }
+     return elem;
+ }
+ 
+ js::PCCounts*
+ ScriptCounts::getImmediatePrecedingPCCounts(size_t offset)
+ {
+     PCCounts searched = PCCounts(offset);
+     PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
+-    if (elem == pcCounts_.end())
++    if (elem == pcCounts_.end()) {
+         return &pcCounts_.back();
+-    if (elem->pcOffset() == offset)
++    }
++    if (elem->pcOffset() == offset) {
+         return elem;
+-    if (elem != pcCounts_.begin())
++    }
++    if (elem != pcCounts_.begin()) {
+         return elem - 1;
++    }
+     return nullptr;
+ }
+ 
+ const js::PCCounts*
+ ScriptCounts::maybeGetThrowCounts(size_t offset) const {
+     PCCounts searched = PCCounts(offset);
+     const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
+-    if (elem == throwCounts_.end() || elem->pcOffset() != offset)
++    if (elem == throwCounts_.end() || elem->pcOffset() != offset) {
+         return nullptr;
++    }
+     return elem;
+ }
+ 
+ const js::PCCounts*
+ ScriptCounts::getImmediatePrecedingThrowCounts(size_t offset) const
+ {
+     PCCounts searched = PCCounts(offset);
+     const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
+     if (elem == throwCounts_.end()) {
+-        if (throwCounts_.begin() == throwCounts_.end())
++        if (throwCounts_.begin() == throwCounts_.end()) {
+             return nullptr;
++        }
+         return &throwCounts_.back();
+     }
+-    if (elem->pcOffset() == offset)
++    if (elem->pcOffset() == offset) {
+         return elem;
+-    if (elem != throwCounts_.begin())
++    }
++    if (elem != throwCounts_.begin()) {
+         return elem - 1;
++    }
+     return nullptr;
+ }
+ 
+ js::PCCounts*
+ ScriptCounts::getThrowCounts(size_t offset) {
+     PCCounts searched = PCCounts(offset);
+     PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
+-    if (elem == throwCounts_.end() || elem->pcOffset() != offset)
++    if (elem == throwCounts_.end() || elem->pcOffset() != offset) {
+         elem = throwCounts_.insert(elem, searched);
++    }
+     return elem;
+ }
+ 
+ size_t
+ ScriptCounts::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+     return mallocSizeOf(this) +
+         pcCounts_.sizeOfExcludingThis(mallocSizeOf) +
+         throwCounts_.sizeOfExcludingThis(mallocSizeOf) +
+         ionCounts_->sizeOfIncludingThis(mallocSizeOf);
+ }
+ 
+ void
+ JSScript::setIonScript(JSRuntime* rt, js::jit::IonScript* ionScript)
+ {
+     MOZ_ASSERT_IF(ionScript != ION_DISABLED_SCRIPT, !baselineScript()->hasPendingIonBuilder());
+-    if (hasIonScript())
++    if (hasIonScript()) {
+         js::jit::IonScript::writeBarrierPre(zone(), ion);
++    }
+     ion = ionScript;
+     MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
+     updateJitCodeRaw(rt);
+ }
+ 
+ js::PCCounts*
+ JSScript::maybeGetPCCounts(jsbytecode* pc) {
+     MOZ_ASSERT(containsPC(pc));
+@@ -1223,59 +1327,67 @@ JSScript::getThrowCounts(jsbytecode* pc)
+     MOZ_ASSERT(containsPC(pc));
+     return getScriptCounts().getThrowCounts(pcToOffset(pc));
+ }
+ 
+ uint64_t
+ JSScript::getHitCount(jsbytecode* pc)
+ {
+     MOZ_ASSERT(containsPC(pc));
+-    if (pc < main())
++    if (pc < main()) {
+         pc = main();
++    }
+ 
+     ScriptCounts& sc = getScriptCounts();
+     size_t targetOffset = pcToOffset(pc);
+     const js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(targetOffset);
+-    if (!baseCount)
++    if (!baseCount) {
+         return 0;
+-    if (baseCount->pcOffset() == targetOffset)
++    }
++    if (baseCount->pcOffset() == targetOffset) {
+         return baseCount->numExec();
++    }
+     MOZ_ASSERT(baseCount->pcOffset() < targetOffset);
+     uint64_t count = baseCount->numExec();
+     do {
+         const js::PCCounts* throwCount = sc.getImmediatePrecedingThrowCounts(targetOffset);
+-        if (!throwCount)
++        if (!throwCount) {
+             return count;
+-        if (throwCount->pcOffset() <= baseCount->pcOffset())
++        }
++        if (throwCount->pcOffset() <= baseCount->pcOffset()) {
+             return count;
++        }
+         count -= throwCount->numExec();
+         targetOffset = throwCount->pcOffset() - 1;
+     } while (true);
+ }
+ 
+ void
+ JSScript::incHitCount(jsbytecode* pc)
+ {
+     MOZ_ASSERT(containsPC(pc));
+-    if (pc < main())
++    if (pc < main()) {
+         pc = main();
++    }
+ 
+     ScriptCounts& sc = getScriptCounts();
+     js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(pcToOffset(pc));
+-    if (!baseCount)
++    if (!baseCount) {
+         return;
++    }
+     baseCount->numExec()++;
+ }
+ 
+ void
+ JSScript::addIonCounts(jit::IonScriptCounts* ionCounts)
+ {
+     ScriptCounts& sc = getScriptCounts();
+-    if (sc.ionCounts_)
++    if (sc.ionCounts_) {
+         ionCounts->setPrevious(sc.ionCounts_);
++    }
+     sc.ionCounts_ = ionCounts;
+ }
+ 
+ jit::IonScriptCounts*
+ JSScript::getIonCounts()
+ {
+     return getScriptCounts().ionCounts_;
+ }
+@@ -1309,18 +1421,19 @@ JSScript::destroyScriptName()
+ {
+     auto p = GetScriptNameMapEntry(this);
+     realm()->scriptNameMap->remove(p);
+ }
+ 
+ bool
+ JSScript::hasScriptName()
+ {
+-    if (!realm()->scriptNameMap)
++    if (!realm()->scriptNameMap) {
+         return false;
++    }
+ 
+     auto p = realm()->scriptNameMap->lookup(this);
+     return p.found();
+ }
+ 
+ void
+ ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
+ {
+@@ -1350,18 +1463,19 @@ const Class ScriptSourceObject::class_ =
+     JSCLASS_FOREGROUND_FINALIZE,
+     &ScriptSourceObjectClassOps
+ };
+ 
+ ScriptSourceObject*
+ ScriptSourceObject::create(JSContext* cx, ScriptSource* source)
+ {
+     RootedScriptSourceObject sourceObject(cx, NewObjectWithGivenProto<ScriptSourceObject>(cx, nullptr));
+-    if (!sourceObject)
++    if (!sourceObject) {
+         return nullptr;
++    }
+ 
+     source->incref();    // The matching decref is in ScriptSourceObject::finalize.
+     sourceObject->initReservedSlot(SOURCE_SLOT, PrivateValue(source));
+ 
+     // The remaining slots should eventually be populated by a call to
+     // initFromOptions. Poison them until that point.
+     sourceObject->initReservedSlot(ELEMENT_SLOT, MagicValue(JS_GENERIC_MAGIC));
+     sourceObject->initReservedSlot(ELEMENT_PROPERTY_SLOT, MagicValue(JS_GENERIC_MAGIC));
+@@ -1376,18 +1490,19 @@ ScriptSourceObject::initFromOptions(JSCo
+ {
+     releaseAssertSameCompartment(cx, source);
+     MOZ_ASSERT(source->getReservedSlot(ELEMENT_SLOT).isMagic(JS_GENERIC_MAGIC));
+     MOZ_ASSERT(source->getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic(JS_GENERIC_MAGIC));
+     MOZ_ASSERT(source->getReservedSlot(INTRODUCTION_SCRIPT_SLOT).isMagic(JS_GENERIC_MAGIC));
+ 
+     RootedObject element(cx, options.element());
+     RootedString elementAttributeName(cx, options.elementAttributeName());
+-    if (!initElementProperties(cx, source, element, elementAttributeName))
++    if (!initElementProperties(cx, source, element, elementAttributeName)) {
+         return false;
++    }
+ 
+     // There is no equivalent of cross-compartment wrappers for scripts. If the
+     // introduction script and ScriptSourceObject are in different compartments,
+     // we would be creating a cross-compartment script reference, which is
+     // forbidden. In that case, simply don't bother to retain the introduction
+     // script.
+     Value introductionScript = UndefinedValue();
+     if (options.introductionScript() &&
+@@ -1400,46 +1515,53 @@ ScriptSourceObject::initFromOptions(JSCo
+     return true;
+ }
+ 
+ /* static */ bool
+ ScriptSourceObject::initElementProperties(JSContext* cx, HandleScriptSourceObject source,
+                                           HandleObject element, HandleString elementAttrName)
+ {
+     RootedValue elementValue(cx, ObjectOrNullValue(element));
+-    if (!cx->compartment()->wrap(cx, &elementValue))
++    if (!cx->compartment()->wrap(cx, &elementValue)) {
+         return false;
++    }
+ 
+     RootedValue nameValue(cx);
+-    if (elementAttrName)
++    if (elementAttrName) {
+         nameValue = StringValue(elementAttrName);
+-    if (!cx->compartment()->wrap(cx, &nameValue))
++    }
++    if (!cx->compartment()->wrap(cx, &nameValue)) {
+         return false;
++    }
+ 
+     source->setReservedSlot(ELEMENT_SLOT, elementValue);
+     source->setReservedSlot(ELEMENT_PROPERTY_SLOT, nameValue);
+ 
+     return true;
+ }
+ 
+ /* static */ bool
+ JSScript::loadSource(JSContext* cx, ScriptSource* ss, bool* worked)
+ {
+     MOZ_ASSERT(!ss->hasSourceData());
+     *worked = false;
+-    if (!cx->runtime()->sourceHook.ref() || !ss->sourceRetrievable())
++    if (!cx->runtime()->sourceHook.ref() || !ss->sourceRetrievable()) {
+         return true;
++    }
+     char16_t* src = nullptr;
+     size_t length;
+-    if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length))
++    if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length)) {
+         return false;
+-    if (!src)
++    }
++    if (!src) {
+         return true;
+-    if (!ss->setSource(cx, UniqueTwoByteChars(src), length))
++    }
++    if (!ss->setSource(cx, UniqueTwoByteChars(src), length)) {
+         return false;
++    }
+ 
+     *worked = true;
+     return true;
+ }
+ 
+ /* static */ JSFlatString*
+ JSScript::sourceData(JSContext* cx, HandleScript script)
+ {
+@@ -1562,18 +1684,19 @@ UncompressedSourceCache::purge()
+ }
+ 
+ size_t
+ UncompressedSourceCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+ {
+     size_t n = 0;
+     if (map_ && !map_->empty()) {
+         n += map_->sizeOfIncludingThis(mallocSizeOf);
+-        for (Map::Range r = map_->all(); !r.empty(); r.popFront())
++        for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
+             n += mallocSizeOf(r.front().value().get());
++        }
+     }
+     return n;
+ }
+ 
+ const char16_t*
+ ScriptSource::chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
+                          size_t chunk)
+ {
+@@ -1628,26 +1751,28 @@ ScriptSource::PinnedChars::PinnedChars(J
+     }
+ }
+ 
+ ScriptSource::PinnedChars::~PinnedChars()
+ {
+     if (chars_) {
+         MOZ_ASSERT(*stack_ == this);
+         *stack_ = prev_;
+-        if (!prev_)
++        if (!prev_) {
+             source_->movePendingCompressedSource();
++        }
+     }
+ }
+ 
+ void
+ ScriptSource::movePendingCompressedSource()
+ {
+-    if (!pendingCompressed_)
++    if (!pendingCompressed_) {
+         return;
++    }
+ 
+     MOZ_ASSERT(data.is<Missing>() || data.is<Uncompressed>());
+     MOZ_ASSERT_IF(data.is<Uncompressed>(),
+                   data.as<Uncompressed>().string.length() ==
+                   pendingCompressed_->uncompressedLength);
+ 
+     data = SourceType(Compressed(std::move(pendingCompressed_->raw),
+                                  pendingCompressed_->uncompressedLength));
+@@ -1657,41 +1782,44 @@ ScriptSource::movePendingCompressedSourc
+ const char16_t*
+ ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
+                     size_t begin, size_t len)
+ {
+     MOZ_ASSERT(begin + len <= length());
+ 
+     if (data.is<Uncompressed>()) {
+         const char16_t* chars = data.as<Uncompressed>().string.chars();
+-        if (!chars)
++        if (!chars) {
+             return nullptr;
++        }
+         return chars + begin;
+     }
+ 
+-    if (data.is<Missing>())
++    if (data.is<Missing>()) {
+         MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing");
++    }
+ 
+     MOZ_ASSERT(data.is<Compressed>());
+ 
+     // Determine which chunk(s) we are interested in, and the offsets within
+     // these chunks.
+     size_t firstChunk, lastChunk;
+     size_t firstChunkOffset, lastChunkOffset;
+     MOZ_ASSERT(len > 0);
+     Compressor::toChunkOffset(begin * sizeof(char16_t), &firstChunk, &firstChunkOffset);
+     Compressor::toChunkOffset((begin + len - 1) * sizeof(char16_t), &lastChunk, &lastChunkOffset);
+ 
+     MOZ_ASSERT(firstChunkOffset % sizeof(char16_t) == 0);
+     size_t firstChar = firstChunkOffset / sizeof(char16_t);
+ 
+     if (firstChunk == lastChunk) {
+         const char16_t* chars = chunkChars(cx, holder, firstChunk);
+-        if (!chars)
++        if (!chars) {
+             return nullptr;
++        }
+         return chars + firstChar;
+     }
+ 
+     // We need multiple chunks. Allocate a (null-terminated) buffer to hold
+     // |len| chars and copy uncompressed chars from the chunks into it. We use
+     // chunkChars() so we benefit from chunk caching by UncompressedSourceCache.
+ 
+     MOZ_ASSERT(firstChunk < lastChunk);
+@@ -1704,18 +1832,19 @@ ScriptSource::chars(JSContext* cx, Uncom
+     }
+ 
+     size_t totalLengthInBytes = length() * sizeof(char16_t);
+     char16_t* cursor = decompressed.get();
+ 
+     for (size_t i = firstChunk; i <= lastChunk; i++) {
+         UncompressedSourceCache::AutoHoldEntry chunkHolder;
+         const char16_t* chars = chunkChars(cx, chunkHolder, i);
+-        if (!chars)
++        if (!chars) {
+             return nullptr;
++        }
+ 
+         size_t numChars = Compressor::chunkSize(totalLengthInBytes, i) / sizeof(char16_t);
+         if (i == firstChunk) {
+             MOZ_ASSERT(firstChar < numChars);
+             chars += firstChar;
+             numChars -= firstChar;
+         } else if (i == lastChunk) {
+             size_t numCharsNew = lastChunkOffset / sizeof(char16_t) + 1;
+@@ -1737,44 +1866,48 @@ ScriptSource::chars(JSContext* cx, Uncom
+ 
+ JSFlatString*
+ ScriptSource::substring(JSContext* cx, size_t start, size_t stop)
+ {
+     MOZ_ASSERT(start <= stop);
+     size_t len = stop - start;
+     UncompressedSourceCache::AutoHoldEntry holder;
+     PinnedChars chars(cx, this, holder, start, len);
+-    if (!chars.get())
++    if (!chars.get()) {
+         return nullptr;
++    }
+     return NewStringCopyN<CanGC>(cx, chars.get(), len);
+ }
+ 
+ JSFlatString*
+ ScriptSource::substringDontDeflate(JSContext* cx, size_t start, size_t stop)
+ {
+     MOZ_ASSERT(start <= stop);
+     size_t len = stop - start;
+     UncompressedSourceCache::AutoHoldEntry holder;
+     PinnedChars chars(cx, this, holder, start, len);
+-    if (!chars.get())
++    if (!chars.get()) {
+         return nullptr;
++    }
+     return NewStringCopyNDontDeflate<CanGC>(cx, chars.get(), len);
+ }
+ 
+ bool
+ ScriptSource::appendSubstring(JSContext* cx, StringBuffer& buf, size_t start, size_t stop)
+ {
+     MOZ_ASSERT(start <= stop);
+     size_t len = stop - start;
+     UncompressedSourceCache::AutoHoldEntry holder;
+     PinnedChars chars(cx, this, holder, start, len);
+-    if (!chars.get())
++    if (!chars.get()) {
+         return false;
+-    if (len > SourceDeflateLimit && !buf.ensureTwoByteChars())
++    }
++    if (len > SourceDeflateLimit && !buf.ensureTwoByteChars()) {
+         return false;
++    }
+     return buf.append(chars.get(), len);
+ }
+ 
+ JSFlatString*
+ ScriptSource::functionBodyString(JSContext* cx)
+ {
+     MOZ_ASSERT(isFunctionBody());
+ 
+@@ -1801,46 +1934,49 @@ ScriptSource::setSource(SharedImmutableT
+ {
+     MOZ_ASSERT(data.is<Missing>());
+     data = SourceType(Uncompressed(std::move(string)));
+ }
+ 
+ bool
+ ScriptSource::tryCompressOffThread(JSContext* cx)
+ {
+-    if (!data.is<Uncompressed>())
++    if (!data.is<Uncompressed>()) {
+         return true;
++    }
+ 
+     // There are several cases where source compression is not a good idea:
+     //  - If the script is tiny, then compression will save little or no space.
+     //  - If there is only one core, then compression will contend with JS
+     //    execution (which hurts benchmarketing).
+     //
+     // Otherwise, enqueue a compression task to be processed when a major
+     // GC is requested.
+ 
+     bool canCompressOffThread =
+         HelperThreadState().cpuCount > 1 &&
+         HelperThreadState().threadCount >= 2 &&
+         CanUseExtraThreads();
+     const size_t TINY_SCRIPT = 256;
+-    if (TINY_SCRIPT > length() || !canCompressOffThread)
++    if (TINY_SCRIPT > length() || !canCompressOffThread) {
+         return true;
++    }
+ 
+     // The SourceCompressionTask needs to record the major GC number for
+     // scheduling. If we're parsing off thread, this number is not safe to
+     // access.
+     //
+     // When parsing on the main thread, the attempts made to compress off
+     // thread in BytecodeCompiler will succeed.
+     //
+     // When parsing off-thread, the above attempts will fail and the attempt
+     // made in ParseTask::finish will succeed.
+-    if (!CurrentThreadCanAccessRuntime(cx->runtime()))
++    if (!CurrentThreadCanAccessRuntime(cx->runtime())) {
+         return true;
++    }
+ 
+     // Heap allocate the task. It will be freed upon compression
+     // completing in AttachFinishedCompressedSources.
+     auto task = MakeUnique<SourceCompressionTask>(cx->runtime(), this);
+     if (!task) {
+         ReportOutOfMemory(cx);
+         return false;
+     }
+@@ -1863,20 +1999,21 @@ ScriptSource::setCompressedSource(JSCont
+ }
+ 
+ void
+ ScriptSource::setCompressedSource(SharedImmutableString&& raw, size_t uncompressedLength)
+ {
+     MOZ_ASSERT(data.is<Missing>() || data.is<Uncompressed>());
+     MOZ_ASSERT_IF(data.is<Uncompressed>(),
+                   data.as<Uncompressed>().string.length() == uncompressedLength);
+-    if (pinnedCharsStack_)
++    if (pinnedCharsStack_) {
+         pendingCompressed_ = mozilla::Some(Compressed(std::move(raw), uncompressedLength));
+-    else
++    } else {
+         data = SourceType(Compressed(std::move(raw), uncompressedLength));
++    }
+ }
+ 
+ bool
+ ScriptSource::setSourceCopy(JSContext* cx, SourceBufferHolder& srcBuf)
+ {
+     MOZ_ASSERT(!hasSourceData());
+ 
+     JSRuntime* runtime = cx->zone()->runtimeFromAnyThread();
+@@ -1894,91 +2031,99 @@ ScriptSource::setSourceCopy(JSContext* c
+ 
+     return true;
+ }
+ 
+ static MOZ_MUST_USE bool
+ reallocUniquePtr(UniqueChars& unique, size_t size)
+ {
+     auto newPtr = static_cast<char*>(js_realloc(unique.get(), size));
+-    if (!newPtr)
++    if (!newPtr) {
+         return false;
++    }
+ 
+     // Since the realloc succeeded, unique is now holding a freed pointer.
+     mozilla::Unused << unique.release();
+     unique.reset(newPtr);
+     return true;
+ }
+ 
+ void
+ SourceCompressionTask::work()
+ {
+-    if (shouldCancel())
++    if (shouldCancel()) {
+         return;
++    }
+ 
+     ScriptSource* source = sourceHolder_.get();
+     MOZ_ASSERT(source->data.is<ScriptSource::Uncompressed>());
+ 
+     // Try to keep the maximum memory usage down by only allocating half the
+     // size of the string, first.
+     size_t inputBytes = source->length() * sizeof(char16_t);
+     size_t firstSize = inputBytes / 2;
+     UniqueChars compressed(js_pod_malloc<char>(firstSize));
+-    if (!compressed)
++    if (!compressed) {
+         return;
++    }
+ 
+     const char16_t* chars = source->data.as<ScriptSource::Uncompressed>().string.chars();
+     Compressor comp(reinterpret_cast<const unsigned char*>(chars),
+                     inputBytes);
+-    if (!comp.init())
++    if (!comp.init()) {
+         return;
++    }
+ 
+     comp.setOutput(reinterpret_cast<unsigned char*>(compressed.get()), firstSize);
+     bool cont = true;
+     bool reallocated = false;
+     while (cont) {
+-        if (shouldCancel())
++        if (shouldCancel()) {
+             return;
++        }
+ 
+         switch (comp.compressMore()) {
+           case Compressor::CONTINUE:
+             break;
+           case Compressor::MOREOUTPUT: {
+             if (reallocated) {
+                 // The compressed string is longer than the original string.
+                 return;
+             }
+ 
+             // The compressed output is greater than half the size of the
+             // original string. Reallocate to the full size.
+-            if (!reallocUniquePtr(compressed, inputBytes))
++            if (!reallocUniquePtr(compressed, inputBytes)) {
+                 return;
++            }
+ 
+             comp.setOutput(reinterpret_cast<unsigned char*>(compressed.get()), inputBytes);
+             reallocated = true;
+             break;
+           }
+           case Compressor::DONE:
+             cont = false;
+             break;
+           case Compressor::OOM:
+             return;
+         }
+     }
+ 
+     size_t totalBytes = comp.totalBytesNeeded();
+ 
+     // Shrink the buffer to the size of the compressed data.
+-    if (!reallocUniquePtr(compressed, totalBytes))
++    if (!reallocUniquePtr(compressed, totalBytes)) {
+         return;
++    }
+ 
+     comp.finish(compressed.get(), totalBytes);
+ 
+-    if (shouldCancel())
++    if (shouldCancel()) {
+         return;
++    }
+ 
+     auto& strings = runtime_->sharedImmutableStrings();
+     resultString_ = strings.getOrCreate(std::move(compressed), totalBytes);
+ }
+ 
+ void
+ SourceCompressionTask::complete()
+ {
+@@ -1997,18 +2142,19 @@ ScriptSource::addSizeOfIncludingThis(moz
+                   mallocSizeOf(introducerFilename_.get());
+     info->numScripts++;
+ }
+ 
+ bool
+ ScriptSource::xdrEncodeTopLevel(JSContext* cx, HandleScript script)
+ {
+     // Encoding failures are reported by the xdrFinalizeEncoder function.
+-    if (containsAsmJS())
++    if (containsAsmJS()) {
+         return true;
++    }
+ 
+     xdrEncoder_ = js::MakeUnique<XDRIncrementalEncoder>(cx);
+     if (!xdrEncoder_) {
+         ReportOutOfMemory(cx);
+         return false;
+     }
+ 
+     MOZ_ASSERT(hasEncoder());
+@@ -2021,18 +2167,19 @@ ScriptSource::xdrEncodeTopLevel(JSContex
+         return false;
+     }
+ 
+     RootedScript s(cx, script);
+     XDRResult res = xdrEncoder_->codeScript(&s);
+     if (res.isErr()) {
+         // On encoding failure, let failureCase destroy encoder and return true
+         // to avoid failing any currently executing script.
+-        if (res.unwrapErr() & JS::TranscodeResult_Failure)
++        if (res.unwrapErr() & JS::TranscodeResult_Failure) {
+             return true;
++        }
+ 
+         return false;
+     }
+ 
+     failureCase.release();
+     return true;
+ }
+ 
+@@ -2046,30 +2193,32 @@ ScriptSource::xdrEncodeFunction(JSContex
+         xdrEncoder_.reset(nullptr);
+     });
+ 
+     RootedFunction f(cx, fun);
+     XDRResult res = xdrEncoder_->codeFunction(&f, sourceObject);
+     if (res.isErr()) {
+         // On encoding failure, let failureCase destroy encoder and return true
+         // to avoid failing any currently executing script.
+-        if (res.unwrapErr() & JS::TranscodeResult_Failure)
++        if (res.unwrapErr() & JS::TranscodeResult_Failure) {
+             return true;
++        }
+         return false;
+     }
+ 
+     failureCase.release();
+     return true;
+ }
+ 
+ bool
+ ScriptSource::xdrFinalizeEncoder(JS::TranscodeBuffer& buffer)
+ {
+-    if (!hasEncoder())
++    if (!hasEncoder()) {
+         return false;
++    }
+ 
+     auto cleanup = mozilla::MakeScopeExit([&] {
+         xdrEncoder_.reset(nullptr);
+     });
+ 
+     XDRResult res = xdrEncoder_->linearize(buffer);
+     return res.isOk();
+ }
+@@ -2114,41 +2263,45 @@ ScriptSource::performXDR(XDRState<mode>*
+     MOZ_TRY(xdr->codeUint8(&hasSource));
+ 
+     uint8_t retrievable = sourceRetrievable_;
+     MOZ_TRY(xdr->codeUint8(&retrievable));
+     sourceRetrievable_ = retrievable;
+ 
+     if (hasSource && !sourceRetrievable_) {
+         uint32_t len = 0;
+-        if (mode == XDR_ENCODE)
++        if (mode == XDR_ENCODE) {
+             len = length();
++        }
+         MOZ_TRY(xdr->codeUint32(&len));
+ 
+         uint32_t compressedLength;
+         if (mode == XDR_ENCODE) {
+             CompressedLengthMatcher m;
+             compressedLength = data.match(m);
+         }
+         MOZ_TRY(xdr->codeUint32(&compressedLength));
+ 
+         size_t byteLen = compressedLength ? compressedLength : (len * sizeof(char16_t));
+         if (mode == XDR_DECODE) {
+             auto bytes = xdr->cx()->template make_pod_array<char>(Max<size_t>(byteLen, 1));
+-            if (!bytes)
++            if (!bytes) {
+                 return xdr->fail(JS::TranscodeResult_Throw);
++            }
+             MOZ_TRY(xdr->codeBytes(bytes.get(), byteLen));
+ 
+             if (compressedLength) {
+-                if (!setCompressedSource(xdr->cx(), std::move(bytes), byteLen, len))
++                if (!setCompressedSource(xdr->cx(), std::move(bytes), byteLen, len)) {
+                     return xdr->fail(JS::TranscodeResult_Throw);
++                }
+             } else {
+                 UniqueTwoByteChars source(reinterpret_cast<char16_t*>(bytes.release()));
+-                if (!setSource(xdr->cx(), std::move(source), len))
++                if (!setSource(xdr->cx(), std::move(source), len)) {
+                     return xdr->fail(JS::TranscodeResult_Throw);
++                }
+             }
+         } else {
+             RawDataMatcher rdm;
+             void* p = data.match(rdm);
+             MOZ_TRY(xdr->codeBytes(p, byteLen));
+         }
+     }
+ 
+@@ -2260,28 +2413,31 @@ ScriptSource::initFromOptions(JSContext*
+     setIntroductionOffset(options.introductionOffset);
+     parameterListEnd_ = parameterListEnd.isSome() ? parameterListEnd.value() : 0;
+ 
+     if (options.hasIntroductionInfo) {
+         MOZ_ASSERT(options.introductionType != nullptr);
+         const char* filename = options.filename() ? options.filename() : "<unknown>";
+         char* formatted = FormatIntroducedFilename(cx, filename, options.introductionLineno,
+                                                    options.introductionType);
+-        if (!formatted)
++        if (!formatted) {
+             return false;
++        }
+         filename_.reset(formatted);
+     } else if (options.filename()) {
+-        if (!setFilename(cx, options.filename()))
++        if (!setFilename(cx, options.filename())) {
+             return false;
++        }
+     }
+ 
+     if (options.introducerFilename()) {
+         introducerFilename_ = DuplicateString(cx, options.introducerFilename());
+-        if (!introducerFilename_)
++        if (!introducerFilename_) {
+             return false;
++        }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ ScriptSource::setFilename(JSContext* cx, const char* filename)
+ {
+@@ -2301,31 +2457,33 @@ ScriptSource::setDisplayURL(JSContext* c
+                                                 GetErrorMessage, nullptr,
+                                                 JSMSG_ALREADY_HAS_PRAGMA, filename_.get(),
+                                                 "//# sourceURL"))
+         {
+             return false;
+         }
+     }
+     size_t len = js_strlen(displayURL) + 1;
+-    if (len == 1)
++    if (len == 1) {
+         return true;
++    }
+ 
+     displayURL_ = DuplicateString(cx, displayURL);
+     return displayURL_ != nullptr;
+ }
+ 
+ bool
+ ScriptSource::setSourceMapURL(JSContext* cx, const char16_t* sourceMapURL)
+ {
+     MOZ_ASSERT(sourceMapURL);
+ 
+     size_t len = js_strlen(sourceMapURL) + 1;
+-    if (len == 1)
++    if (len == 1) {
+         return true;
++    }
+ 
+     sourceMapURL_ = DuplicateString(cx, sourceMapURL);
+     return sourceMapURL_ != nullptr;
+ }
+ 
+ /*
+  * [SMDOC] JSScript data layout (shared)
+  *
+@@ -2364,18 +2522,19 @@ js::SharedScriptData::new_(JSContext* cx
+ 
+     /*
+      * Call constructors to initialize the storage that will be accessed as a
+      * GCPtrAtom array via atoms().
+      */
+     static_assert(offsetof(SharedScriptData, data_) % sizeof(GCPtrAtom) == 0,
+                   "atoms must have GCPtrAtom alignment");
+     GCPtrAtom* atoms = entry->atoms();
+-    for (unsigned i = 0; i < natoms; ++i)
++    for (unsigned i = 0; i < natoms; ++i) {
+         new (&atoms[i]) GCPtrAtom();
++    }
+ 
+     // Sanity check the dataLength() computation
+     MOZ_ASSERT(entry->dataLength() == dataLength);
+ 
+     return entry;
+ }
+ 
+ inline
+@@ -2393,18 +2552,19 @@ js::ScriptBytecodeHasher::Lookup::~Looku
+ }
+ 
+ bool
+ JSScript::createScriptData(JSContext* cx, uint32_t codeLength, uint32_t srcnotesLength,
+                            uint32_t natoms)
+ {
+     MOZ_ASSERT(!scriptData());
+     SharedScriptData* ssd = SharedScriptData::new_(cx, codeLength, srcnotesLength, natoms);
+-    if (!ssd)
++    if (!ssd) {
+         return false;
++    }
+ 
+     setScriptData(ssd);
+     return true;
+ }
+ 
+ void
+ JSScript::freeScriptData()
+ {
+@@ -2596,26 +2756,31 @@ JS_STATIC_ASSERT((NoPaddingBetweenEntrie
+ static inline size_t
+ ScriptDataSize(uint32_t nscopes, uint32_t nconsts, uint32_t nobjects,
+                uint32_t ntrynotes, uint32_t nscopenotes, uint32_t nyieldoffsets)
+ {
+     size_t size = 0;
+ 
+     MOZ_ASSERT(nscopes != 0);
+     size += sizeof(ScopeArray) + nscopes * sizeof(Scope*);
+-    if (nconsts != 0)
++    if (nconsts != 0) {
+         size += sizeof(ConstArray) + nconsts * sizeof(Value);
+-    if (nobjects != 0)
++    }
++    if (nobjects != 0) {
+         size += sizeof(ObjectArray) + nobjects * sizeof(NativeObject*);
+-    if (ntrynotes != 0)
++    }
++    if (ntrynotes != 0) {
+         size += sizeof(TryNoteArray) + ntrynotes * sizeof(JSTryNote);
+-    if (nscopenotes != 0)
++    }
++    if (nscopenotes != 0) {
+         size += sizeof(ScopeNoteArray) + nscopenotes * sizeof(ScopeNote);
+-    if (nyieldoffsets != 0)
++    }
++    if (nyieldoffsets != 0) {
+         size += sizeof(YieldAndAwaitOffsetArray) + nyieldoffsets * sizeof(uint32_t);
++    }
+ 
+      return size;
+ }
+ 
+ JSScript::JSScript(JS::Realm* realm, uint8_t* stubEntry, const ReadOnlyCompileOptions& options,
+                    HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd,
+                    uint32_t toStringStart, uint32_t toStringEnd)
+   :
+@@ -2651,18 +2816,19 @@ JSScript::JSScript(JS::Realm* realm, uin
+ 
+ /* static */ JSScript*
+ JSScript::createInitialized(JSContext* cx, const ReadOnlyCompileOptions& options,
+                             HandleObject sourceObject,
+                             uint32_t bufStart, uint32_t bufEnd,
+                             uint32_t toStringStart, uint32_t toStringEnd)
+ {
+     void* script = Allocate<JSScript>(cx);
+-    if (!script)
++    if (!script) {
+         return nullptr;
++    }
+ 
+     uint8_t* stubEntry =
+ #ifndef JS_CODEGEN_NONE
+         cx->runtime()->jitRuntime()->interpreterStub().value
+ #else
+         nullptr
+ #endif
+         ;
+@@ -2849,25 +3015,27 @@ JSScript::initFunctionPrototype(JSContex
+         return false;
+     }
+ 
+     script->nTypeSets_ = 0;
+ 
+     RootedScope enclosing(cx, &cx->global()->emptyGlobalScope());
+     Scope* functionProtoScope = FunctionScope::create(cx, nullptr, false, false, functionProto,
+                                                       enclosing);
+-    if (!functionProtoScope)
++    if (!functionProtoScope) {
+         return false;
++    }
+     script->scopes()->vector[0].init(functionProtoScope);
+ 
+     uint32_t codeLength = 1;
+     uint32_t srcNotesLength = 1;
+     uint32_t numAtoms = 0;
+-    if (!script->createScriptData(cx, codeLength, srcNotesLength, numAtoms))
++    if (!script->createScriptData(cx, codeLength, srcNotesLength, numAtoms)) {
+         return false;
++    }
+ 
+     jsbytecode* code = script->code();
+     code[0] = JSOP_RETRVAL;
+     code[1] = SRC_NULL;
+     return script->shareScriptData(cx);
+ }
+ 
+ static void
+@@ -2880,47 +3048,51 @@ InitAtomMap(frontend::AtomIndexMap& indi
+         atoms[index].init(atom);
+     }
+ }
+ 
+ /* static */ void
+ JSScript::initFromFunctionBox(HandleScript script, frontend::FunctionBox* funbox)
+ {
+     JSFunction* fun = funbox->function();
+-    if (fun->isInterpretedLazy())
++    if (fun->isInterpretedLazy()) {
+         fun->setUnlazifiedScript(script);
+-    else
++    } else {
+         fun->setScript(script);
++    }
+ 
+     script->bitFields_.funHasExtensibleScope_ = funbox->hasExtensibleScope();
+     script->bitFields_.needsHomeObject_ = funbox->needsHomeObject();
+     script->bitFields_.isDerivedClassConstructor_ = funbox->isDerivedClassConstructor();
+ 
+     if (funbox->argumentsHasLocalBinding()) {
+         script->setArgumentsHasVarBinding();
+-        if (funbox->definitelyNeedsArgsObj())
++        if (funbox->definitelyNeedsArgsObj()) {
+             script->setNeedsArgsObj(true);
++        }
+     } else {
+         MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
+     }
+     script->bitFields_.hasMappedArgsObj_ = funbox->hasMappedArgsObj();
+ 
+     script->bitFields_.functionHasThisBinding_ = funbox->hasThisBinding();
+     script->bitFields_.functionHasExtraBodyVarScope_ = funbox->hasExtraBodyVarScope();
+ 
+     script->funLength_ = funbox->length;
+ 
+     script->setGeneratorKind(funbox->generatorKind());
+     script->setAsyncKind(funbox->asyncKind());
+-    if (funbox->hasRest())
++    if (funbox->hasRest()) {
+         script->setHasRest();
++    }
+ 
+     PositionalFormalParameterIter fi(script);
+-    while (fi && !fi.closedOver())
++    while (fi && !fi.closedOver()) {
+         fi++;
++    }
+     script->bitFields_.funHasAnyAliasedFormal_ = !!fi;
+ 
+     script->setHasInnerFunctions(funbox->hasInnerFunctions());
+ }
+ 
+ /* static */ void
+ JSScript::initFromModuleContext(HandleScript script)
+ {
+@@ -2964,18 +3136,19 @@ JSScript::fullyInitFromEmitter(JSContext
+         return false;
+     }
+ 
+     MOZ_ASSERT(script->mainOffset() == 0);
+     script->mainOffset_ = prologueLength;
+     script->nTypeSets_ = bce->typesetCount;
+     script->lineno_ = bce->firstLine;
+ 
+-    if (!script->createScriptData(cx, prologueLength + mainLength, nsrcnotes, natoms))
++    if (!script->createScriptData(cx, prologueLength + mainLength, nsrcnotes, natoms)) {
+         return false;
++    }
+ 
+     // Any fallible operation after JSScript::createScriptData should reset
+     // JSScript.scriptData_, in order to treat this script as uncompleted,
+     // in JSScript::isUncompleted.
+     // JSScript::shareScriptData resets it before returning false.
+ 
+     jsbytecode* code = script->code();
+     PodCopy<jsbytecode>(code, bce->prologue.code.begin(), prologueLength);
+@@ -3005,25 +3178,27 @@ JSScript::fullyInitFromEmitter(JSContext
+     script->nslots_ = nslots;
+     script->bodyScopeIndex_ = bce->bodyScopeIndex;
+     script->bitFields_.hasNonSyntacticScope_ =
+         bce->outermostScope()->hasOnChain(ScopeKind::NonSyntactic);
+ 
+     // There shouldn't be any fallible operation after initFromFunctionBox,
+     // JSFunction::hasUncompletedScript relies on the fact that the existence
+     // of the pointer to JSScript means the pointed JSScript is complete.
+-    if (bce->sc->isFunctionBox())
++    if (bce->sc->isFunctionBox()) {
+         initFromFunctionBox(script, bce->sc->asFunctionBox());
+-    else if (bce->sc->isModuleContext())
++    } else if (bce->sc->isModuleContext()) {
+         initFromModuleContext(script);
++    }
+ 
+     // Copy yield offsets last, as the generator kind is set in
+     // initFromFunctionBox.
+-    if (bce->yieldAndAwaitOffsetList.length() != 0)
++    if (bce->yieldAndAwaitOffsetList.length() != 0) {
+         bce->yieldAndAwaitOffsetList.finish(script->yieldAndAwaitOffsets(), prologueLength);
++    }
+ 
+ #ifdef DEBUG
+     script->assertValidJumpTargets();
+ #endif
+ 
+     return true;
+ }
+ 
+@@ -3074,18 +3249,19 @@ JSScript::assertValidJumpTargets() const
+ 
+     // Check catch/finally blocks as jump targets.
+     if (hasTrynotes()) {
+         JSTryNote* tn = trynotes()->vector;
+         JSTryNote* tnlimit = tn + trynotes()->length;
+         for (; tn < tnlimit; tn++) {
+             jsbytecode* tryStart = mainEntry + tn->start;
+             jsbytecode* tryPc = tryStart - 1;
+-            if (tn->kind != JSTRY_CATCH && tn->kind != JSTRY_FINALLY)
++            if (tn->kind != JSTRY_CATCH && tn->kind != JSTRY_FINALLY) {
+                 continue;
++            }
+ 
+             MOZ_ASSERT(JSOp(*tryPc) == JSOP_TRY);
+             jsbytecode* tryTarget = tryStart + tn->length;
+             MOZ_ASSERT(mainEntry <= tryTarget && tryTarget < end);
+             MOZ_ASSERT(BytecodeIsJumpTarget(JSOp(*tryTarget)));
+         }
+     }
+ }
+@@ -3128,31 +3304,33 @@ JSScript::finalize(FreeOp* fop)
+     MOZ_ASSERT_IF(hasScriptName(), fop->runtime()->lcovOutput().isEnabled());
+     if (fop->runtime()->lcovOutput().isEnabled() && hasScriptName()) {
+         realm()->lcovOutput.collectCodeCoverageInfo(realm(), this, getScriptName());
+         destroyScriptName();
+     }
+ 
+     fop->runtime()->geckoProfiler().onScriptFinalized(this);
+ 
+-    if (types_)
++    if (types_) {
+         types_->destroy();
++    }
+ 
+     jit::DestroyJitScripts(fop, this);
+ 
+     destroyScriptCounts();
+     destroyDebugScript(fop);
+ 
+     if (data) {
+         JS_POISON(data, 0xdb, computedSizeOfData(), MemCheckKind::MakeNoAccess);
+         fop->free_(data);
+     }
+ 
+-    if (scriptData_)
++    if (scriptData_) {
+         scriptData_->decRefCount();
++    }
+ 
+     // In most cases, our LazyScript's script pointer will reference this
+     // script, and thus be nulled out by normal weakref processing. However, if
+     // we unlazified the LazyScript during incremental sweeping, it will have a
+     // completely different JSScript.
+     MOZ_ASSERT_IF(lazyScript && !IsAboutToBeFinalizedUnbarriered(&lazyScript),
+                   !lazyScript->hasScript() || lazyScript->maybeScriptUnbarriered() != this);
+ }
+@@ -3166,18 +3344,19 @@ GSNCache::purge()
+     if (map.initialized())
+         map.finish();
+ }
+ 
+ jssrcnote*
+ js::GetSrcNote(GSNCache& cache, JSScript* script, jsbytecode* pc)
+ {
+     size_t target = pc - script->code();
+-    if (target >= script->length())
++    if (target >= script->length()) {
+         return nullptr;
++    }
+ 
+     if (cache.code == script->code()) {
+         MOZ_ASSERT(cache.map.initialized());
+         GSNCache::Map::Ptr p = cache.map.lookup(pc);
+         return p ? p->value() : nullptr;
+     }
+ 
+     size_t offset = 0;
+@@ -3194,32 +3373,34 @@ js::GetSrcNote(GSNCache& cache, JSScript
+         }
+     }
+ 
+     if (cache.code != script->code() && script->length() >= GSN_CACHE_THRESHOLD) {
+         unsigned nsrcnotes = 0;
+         for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn);
+              sn = SN_NEXT(sn))
+         {
+-            if (SN_IS_GETTABLE(sn))
++            if (SN_IS_GETTABLE(sn)) {
+                 ++nsrcnotes;
++            }
+         }
+         if (cache.code) {
+             MOZ_ASSERT(cache.map.initialized());
+             cache.map.finish();
+             cache.code = nullptr;
+         }
+         if (cache.map.init(nsrcnotes)) {
+             pc = script->code();
+             for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn);
+                  sn = SN_NEXT(sn))
+             {
+                 pc += SN_DELTA(sn);
+-                if (SN_IS_GETTABLE(sn))
++                if (SN_IS_GETTABLE(sn)) {
+                     cache.map.putNewInfallible(pc, sn);
++                }
+             }
+             cache.code = script->code();
+         }
+     }
+ 
+     return result;
+ }
+ 
+@@ -3240,98 +3421,105 @@ js::PCToLineNumber(unsigned startLine, j
+      * Walk through source notes accumulating their deltas, keeping track of
+      * line-number notes, until we pass the note for pc's offset within
+      * script->code.
+      */
+     ptrdiff_t offset = 0;
+     ptrdiff_t target = pc - code;
+     for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
+         offset += SN_DELTA(sn);
+-        if (offset > target)
++        if (offset > target) {
+             break;
++        }
+ 
+         SrcNoteType type = SN_TYPE(sn);
+         if (type == SRC_SETLINE) {
+             lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line));
+             column = 0;
+         } else if (type == SRC_NEWLINE) {
+             lineno++;
+             column = 0;
+         } else if (type == SRC_COLSPAN) {
+             ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, SrcNote::ColSpan::Span));
+             MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
+             column += colspan;
+         }
+     }
+ 
+-    if (columnp)
++    if (columnp) {
+         *columnp = column;
++    }
+ 
+     return lineno;
+ }
+ 
+ unsigned
+ js::PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp)
+ {
+     /* Cope with InterpreterFrame.pc value prior to entering Interpret. */
+-    if (!pc)
++    if (!pc) {
+         return 0;
++    }
+ 
+     return PCToLineNumber(script->lineno(), script->notes(), script->code(), pc, columnp);
+ }
+ 
+ jsbytecode*
+ js::LineNumberToPC(JSScript* script, unsigned target)
+ {
+     ptrdiff_t offset = 0;
+     ptrdiff_t best = -1;
+     unsigned lineno = script->lineno();
+     unsigned bestdiff = SN_MAX_OFFSET;
+     for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
+         /*
+          * Exact-match only if offset is not in the prologue; otherwise use
+          * nearest greater-or-equal line number match.
+          */
+-        if (lineno == target && offset >= ptrdiff_t(script->mainOffset()))
++        if (lineno == target && offset >= ptrdiff_t(script->mainOffset())) {
+             goto out;
++        }
+         if (lineno >= target) {
+             unsigned diff = lineno - target;
+             if (diff < bestdiff) {
+                 bestdiff = diff;
+                 best = offset;
+             }
+         }
+         offset += SN_DELTA(sn);
+         SrcNoteType type = SN_TYPE(sn);
+         if (type == SRC_SETLINE) {
+             lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line));
+         } else if (type == SRC_NEWLINE) {
+             lineno++;
+         }
+     }
+-    if (best >= 0)
++    if (best >= 0) {
+         offset = best;
++    }
+ out:
+     return script->offsetToPC(offset);
+ }
+ 
+ JS_FRIEND_API(unsigned)
+ js::GetScriptLineExtent(JSScript* script)
+ {
+     unsigned lineno = script->lineno();
+     unsigned maxLineNo = lineno;
+     for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
+         SrcNoteType type = SN_TYPE(sn);
+-        if (type == SRC_SETLINE)
++        if (type == SRC_SETLINE) {
+             lineno = unsigned(GetSrcNoteOffset(sn, SrcNote::SetLine::Line));
+-        else if (type == SRC_NEWLINE)
++        } else if (type == SRC_NEWLINE) {
+             lineno++;
+-
+-        if (maxLineNo < lineno)
++        }
++
++        if (maxLineNo < lineno) {
+             maxLineNo = lineno;
++        }
+     }
+ 
+     return 1 + maxLineNo - script->lineno();
+ }
+ 
+ void
+ js::DescribeScriptedCallerForCompilation(JSContext* cx, MutableHandleScript maybeScript,
+                                          const char** file, unsigned* linenop,
+@@ -3395,47 +3583,53 @@ Rebase(JSScript* dst, JSScript* src, T* 
+ 
+ static JSObject*
+ CloneInnerInterpretedFunction(JSContext* cx, HandleScope enclosingScope, HandleFunction srcFun)
+ {
+     /* NB: Keep this in sync with XDRInterpretedFunction. */
+     RootedObject cloneProto(cx);
+     if (srcFun->isGenerator() || srcFun->isAsync()) {
+         cloneProto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
+-        if (!cloneProto)
++        if (!cloneProto) {
+             return nullptr;
++        }
+     }
+ 
+     gc::AllocKind allocKind = srcFun->getAllocKind();
+     uint16_t flags = srcFun->flags();
+     if (srcFun->isSelfHostedBuiltin()) {
+         // Functions in the self-hosting compartment are only extended in
+         // debug mode. For top-level functions, FUNCTION_EXTENDED gets used by
+         // the cloning algorithm. Do the same for inner functions here.
+         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+         flags |= JSFunction::Flags::EXTENDED;
+     }
+     RootedAtom atom(cx, srcFun->displayAtom());
+-    if (atom)
++    if (atom) {
+         cx->markAtom(atom);
++    }
+     RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, srcFun->nargs(),
+                                                   JSFunction::Flags(flags), nullptr, atom,
+                                                   cloneProto, allocKind, TenuredObject));
+-    if (!clone)
++    if (!clone) {
+         return nullptr;
++    }
+ 
+     JSScript::AutoDelazify srcScript(cx, srcFun);
+-    if (!srcScript)
++    if (!srcScript) {
+         return nullptr;
++    }
+     JSScript* cloneScript = CloneScriptIntoFunction(cx, enclosingScope, clone, srcScript);
+-    if (!cloneScript)
++    if (!cloneScript) {
+         return nullptr;
+-
+-    if (!JSFunction::setTypeForScriptedFunction(cx, clone))
++    }
++
++    if (!JSFunction::setTypeForScriptedFunction(cx, clone)) {
+         return nullptr;
++    }
+ 
+     return clone;
+ }
+ 
+ bool
+ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
+                        MutableHandle<GCVector<Scope*>> scopes)
+ {
+@@ -3504,59 +3698,64 @@ js::detail::CopyScript(JSContext* cx, Ha
+                         MOZ_ASSERT(innerFun->isAsmJSNative());
+                         JS_ReportErrorASCII(cx, "AsmJS modules do not yet support cloning.");
+                         return false;
+                     }
+                     clone = innerFun;
+                 } else {
+                     if (innerFun->isInterpretedLazy()) {
+                         AutoRealm ar(cx, innerFun);
+-                        if (!JSFunction::getOrCreateScript(cx, innerFun))
++                        if (!JSFunction::getOrCreateScript(cx, innerFun)) {
+                             return false;
++                        }
+                     }
+ 
+                     Scope* enclosing = innerFun->nonLazyScript()->enclosingScope();
+                     RootedScope enclosingClone(cx, scopes[FindScopeIndex(src, *enclosing)]);
+                     clone = CloneInnerInterpretedFunction(cx, enclosingClone, innerFun);
+                 }
+             } else {
+                 clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
+             }
+ 
+-            if (!clone || !objects.append(clone))
++            if (!clone || !objects.append(clone)) {
+                 return false;
++            }
+         }
+     }
+ 
+     /* This assignment must occur before all the Rebase calls. */
+     dst->data = data.release();
+     dst->dataSize_ = size;
+     MOZ_ASSERT(bool(dst->data) == bool(src->data));
+-    if (dst->data)
++    if (dst->data) {
+         memcpy(dst->data, src->data, size);
++    }
+ 
+     if (cx->zone() != src->zoneFromAnyThread()) {
+-        for (size_t i = 0; i < src->scriptData()->natoms(); i++)
++        for (size_t i = 0; i < src->scriptData()->natoms(); i++) {
+             cx->markAtom(src->scriptData()->atoms()[i]);
++        }
+     }
+ 
+     /* Script filenames, bytecodes and atoms are runtime-wide. */
+     dst->setScriptData(src->scriptData());
+ 
+     dst->lineno_ = src->lineno();
+     dst->mainOffset_ = src->mainOffset();
+     dst->nfixed_ = src->nfixed();
+     dst->nslots_ = src->nslots();
+     dst->bodyScopeIndex_ = src->bodyScopeIndex_;
+     dst->funLength_ = src->funLength();
+     dst->nTypeSets_ = src->nTypeSets();
+     if (src->argumentsHasVarBinding()) {
+         dst->setArgumentsHasVarBinding();
+-        if (src->analyzedArgsUsage())
++        if (src->analyzedArgsUsage()) {
+             dst->setNeedsArgsObj(src->needsArgsObj());
++        }
+     }
+     dst->bitFields_.hasMappedArgsObj_ = src->hasMappedArgsObj();
+     dst->bitFields_.functionHasThisBinding_ = src->functionHasThisBinding();
+     dst->bitFields_.functionHasExtraBodyVarScope_ = src->functionHasExtraBodyVarScope();
+     dst->cloneHasArray(src);
+     dst->bitFields_.strict_ = src->strict();
+     dst->bitFields_.explicitUseStrict_ = src->explicitUseStrict();
+     dst->bitFields_.hasNonSyntacticScope_ = scopes[0]->hasOnChain(ScopeKind::NonSyntactic);
+@@ -3572,35 +3771,40 @@ js::detail::CopyScript(JSContext* cx, Ha
+     dst->bitFields_.isDefaultClassConstructor_ = src->isDefaultClassConstructor();
+     dst->bitFields_.isAsync_ = src->bitFields_.isAsync_;
+     dst->bitFields_.hasRest_ = src->bitFields_.hasRest_;
+     dst->bitFields_.hideScriptFromDebugger_ = src->bitFields_.hideScriptFromDebugger_;
+ 
+     if (nconsts != 0) {
+         GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
+         dst->consts()->vector = vector;
+-        for (unsigned i = 0; i < nconsts; ++i)
++        for (unsigned i = 0; i < nconsts; ++i) {
+             MOZ_ASSERT_IF(vector[i].isGCThing(), vector[i].toString()->isAtom());
++        }
+     }
+     if (nobjects != 0) {
+         GCPtrObject* vector = Rebase<GCPtrObject>(dst, src, src->objects()->vector);
+         dst->objects()->vector = vector;
+-        for (unsigned i = 0; i < nobjects; ++i)
++        for (unsigned i = 0; i < nobjects; ++i) {
+             vector[i].init(&objects[i]->as<NativeObject>());
++        }
+     }
+     {
+         GCPtrScope* vector = Rebase<GCPtrScope>(dst, src, src->scopes()->vector);
+         dst->scopes()->vector = vector;
+-        for (uint32_t i = 0; i < nscopes; ++i)
++        for (uint32_t i = 0; i < nscopes; ++i) {
+             vector[i].init(scopes[i]);
+-    }
+-    if (ntrynotes != 0)
++        }
++    }
++    if (ntrynotes != 0) {
+         dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
+-    if (nscopenotes != 0)
++    }
++    if (nscopenotes != 0) {
+         dst->scopeNotes()->vector = Rebase<ScopeNote>(dst, src, src->scopeNotes()->vector);
++    }
+     if (nyieldoffsets != 0) {
+         dst->yieldAndAwaitOffsets().vector_ =
+             Rebase<uint32_t>(dst, src, src->yieldAndAwaitOffsets().vector_);
+     }
+ 
+     return true;
+ }
+ 
+@@ -3614,25 +3818,27 @@ CreateEmptyScriptForClone(JSContext* cx,
+      */
+     RootedObject sourceObject(cx);
+     if (src->realm()->isSelfHostingRealm()) {
+         if (!cx->realm()->selfHostingScriptSource) {
+             CompileOptions options(cx);
+             FillSelfHostingCompileOptions(options);
+ 
+             ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options);
+-            if (!obj)
++            if (!obj) {
+                 return nullptr;
++            }
+             cx->realm()->selfHostingScriptSource.set(obj);
+         }
+         sourceObject = cx->realm()->selfHostingScriptSource;
+     } else {
+         sourceObject = src->sourceObject();
+-        if (!cx->compartment()->wrap(cx, &sourceObject))
++        if (!cx->compartment()->wrap(cx, &sourceObject)) {
+             return nullptr;
++        }
+     }
+ 
+     CompileOptions options(cx);
+     options.setMutedErrors(src->mutedErrors())
+            .setSelfHostingMode(src->selfHosted())
+            .setNoScriptRval(src->noScriptRval());
+ 
+     return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(),
+@@ -3640,79 +3846,86 @@ CreateEmptyScriptForClone(JSContext* cx,
+ }
+ 
+ JSScript*
+ js::CloneGlobalScript(JSContext* cx, ScopeKind scopeKind, HandleScript src)
+ {
+     MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
+ 
+     RootedScript dst(cx, CreateEmptyScriptForClone(cx, src));
+-    if (!dst)
++    if (!dst) {
+         return nullptr;
++    }
+ 
+     MOZ_ASSERT(src->bodyScopeIndex() == 0);
+     Rooted<GCVector<Scope*>> scopes(cx, GCVector<Scope*>(cx));
+     Rooted<GlobalScope*> original(cx, &src->bodyScope()->as<GlobalScope>());
+     GlobalScope* clone = GlobalScope::clone(cx, original, scopeKind);
+-    if (!clone || !scopes.append(clone))
++    if (!clone || !scopes.append(clone)) {
+         return nullptr;
+-
+-    if (!detail::CopyScript(cx, src, dst, &scopes))
++    }
++
++    if (!detail::CopyScript(cx, src, dst, &scopes)) {
+         return nullptr;
++    }
+ 
+     return dst;
+ }
+ 
+ JSScript*
+ js::CloneScriptIntoFunction(JSContext* cx, HandleScope enclosingScope, HandleFunction fun,
+                             HandleScript src)
+ {
+     MOZ_ASSERT(fun->isInterpreted());
+     MOZ_ASSERT(!fun->hasScript() || fun->hasUncompletedScript());
+ 
+     RootedScript dst(cx, CreateEmptyScriptForClone(cx, src));
+-    if (!dst)
++    if (!dst) {
+         return nullptr;
++    }
+ 
+     // Clone the non-intra-body scopes.
+     Rooted<GCVector<Scope*>> scopes(cx, GCVector<Scope*>(cx));
+     RootedScope original(cx);
+     RootedScope enclosingClone(cx);
+     for (uint32_t i = 0; i <= src->bodyScopeIndex(); i++) {
+         original = src->getScope(i);
+ 
+         if (i == 0) {
+             enclosingClone = enclosingScope;
+         } else {
+             MOZ_ASSERT(src->getScope(i - 1) == original->enclosing());
+             enclosingClone = scopes[i - 1];
+         }
+ 
+         Scope* clone;
+-        if (original->is<FunctionScope>())
++        if (original->is<FunctionScope>()) {
+             clone = FunctionScope::clone(cx, original.as<FunctionScope>(), fun, enclosingClone);
+-        else
++        } else {
+             clone = Scope::clone(cx, original, enclosingClone);
+-
+-        if (!clone || !scopes.append(clone))
++        }
++
++        if (!clone || !scopes.append(clone)) {
+             return nullptr;
++        }
+     }
+ 
+     // Save flags in case we need to undo the early mutations.
+     const int preservedFlags = fun->flags();
+     if (!detail::CopyScript(cx, src, dst, &scopes)) {
+         fun->setFlags(preservedFlags);
+         return nullptr;
+     }
+ 
+     // Finally set the script after all the fallible operations.
+-    if (fun->isInterpretedLazy())
++    if (fun->isInterpretedLazy()) {
+         fun->setUnlazifiedScript(dst);
+-    else
++    } else {
+         fun->initScript(dst);
++    }
+ 
+     return dst;
+ }
+ 
+ DebugScript*
+ JSScript::debugScript()
+ {
+     MOZ_ASSERT(bitFields_.hasDebugScript_);
+@@ -3765,18 +3978,19 @@ JSScript::ensureHasDebugScript(JSContext
+     if (!debug) {
+         ReportOutOfMemory(cx);
+         return false;
+     }
+ 
+     /* Create realm's debugScriptMap if necessary. */
+     if (!realm()->debugScriptMap) {
+         auto map = cx->make_unique<DebugScriptMap>();
+-        if (!map)
++        if (!map) {
+             return false;
++        }
+ 
+         if (!map->init()) {
+             ReportOutOfMemory(cx);
+             return false;
+         }
+ 
+         realm()->debugScriptMap = std::move(map);
+     }
+@@ -3804,32 +4018,35 @@ JSScript::ensureHasDebugScript(JSContext
+ void
+ JSScript::setNewStepMode(FreeOp* fop, uint32_t newValue)
+ {
+     DebugScript* debug = debugScript();
+     uint32_t prior = debug->stepMode;
+     debug->stepMode = newValue;
+ 
+     if (!prior != !newValue) {
+-        if (hasBaselineScript())
++        if (hasBaselineScript()) {
+             baseline->toggleDebugTraps(this, nullptr);
+-
+-        if (!stepModeEnabled() && !debug->numSites)
++        }
++
++        if (!stepModeEnabled() && !debug->numSites) {
+             fop->free_(releaseDebugScript());
++        }
+     }
+ }
+ 
+ bool
+ JSScript::incrementStepModeCount(JSContext* cx)
+ {
+     assertSameCompartment(cx, this);
+     MOZ_ASSERT(cx->realm()->isDebuggee());
+ 
+-    if (!ensureHasDebugScript(cx))
++    if (!ensureHasDebugScript(cx)) {
+         return false;
++    }
+ 
+     DebugScript* debug = debugScript();
+     uint32_t count = debug->stepMode;
+     setNewStepMode(cx->runtime()->defaultFreeOp(), count + 1);
+     return true;
+ }
+ 
+ void
+@@ -3839,18 +4056,19 @@ JSScript::decrementStepModeCount(FreeOp*
+     uint32_t count = debug->stepMode;
+     MOZ_ASSERT(count > 0);
+     setNewStepMode(fop, count - 1);
+ }
+ 
+ BreakpointSite*
+ JSScript::getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc)
+ {
+-    if (!ensureHasDebugScript(cx))
++    if (!ensureHasDebugScript(cx)) {
+         return nullptr;
++    }
+ 
+     DebugScript* debug = debugScript();
+     BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
+ 
+     if (!site) {
+         site = cx->zone()->new_<JSBreakpointSite>(this, pc);
+         if (!site) {
+             ReportOutOfMemory(cx);
+@@ -3867,74 +4085,81 @@ JSScript::destroyBreakpointSite(FreeOp* 
+ {
+     DebugScript* debug = debugScript();
+     BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
+     MOZ_ASSERT(site);
+ 
+     fop->delete_(site);
+     site = nullptr;
+ 
+-    if (--debug->numSites == 0 && !stepModeEnabled())
++    if (--debug->numSites == 0 && !stepModeEnabled()) {
+         fop->free_(releaseDebugScript());
++    }
+ }
+ 
+ void
+ JSScript::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, JSObject* handler)
+ {
+-    if (!hasAnyBreakpointsOrStepMode())
++    if (!hasAnyBreakpointsOrStepMode()) {
+         return;
++    }
+ 
+     for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
+         BreakpointSite* site = getBreakpointSite(pc);
+         if (site) {
+             Breakpoint* nextbp;
+             for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
+                 nextbp = bp->nextInSite();
+-                if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler))
++                if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler)) {
+                     bp->destroy(fop);
++                }
+             }
+         }
+     }
+ }
+ 
+ bool
+ JSScript::hasBreakpointsAt(jsbytecode* pc)
+ {
+     BreakpointSite* site = getBreakpointSite(pc);
+-    if (!site)
++    if (!site) {
+         return false;
++    }
+ 
+     return site->enabledCount > 0;
+ }
+ 
+ void
+ SharedScriptData::traceChildren(JSTracer* trc)
+ {
+     MOZ_ASSERT(refCount() != 0);
+-    for (uint32_t i = 0; i < natoms(); ++i)
++    for (uint32_t i = 0; i < natoms(); ++i) {
+         TraceNullableEdge(trc, &atoms()[i], "atom");
++    }
+ }
+ 
+ void
+ JSScript::traceChildren(JSTracer* trc)
+ {
+     // NOTE: this JSScript may be partially initialized at this point.  E.g. we
+     // may have created it and partially initialized it with
+     // JSScript::Create(), but not yet finished initializing it with
+     // fullyInitFromEmitter() or fullyInitTrivial().
+ 
+     MOZ_ASSERT_IF(trc->isMarkingTracer() &&
+                   GCMarker::fromTracer(trc)->shouldCheckCompartments(),
+                   zone()->isCollecting());
+ 
+-    if (scriptData())
++    if (scriptData()) {
+         scriptData()->traceChildren(trc);
+-
+-    if (ScopeArray* scopearray = scopes())
++    }
++
++    if (ScopeArray* scopearray = scopes()) {
+         TraceRange(trc, scopearray->length, scopearray->vector, "scopes");
++    }
+ 
+     if (hasConsts()) {
+         ConstArray* constarray = consts();
+         TraceRange(trc, constarray->length, constarray->vector, "consts");
+     }
+ 
+     if (hasObjects()) {
+         ObjectArray* objarray = objects();
+@@ -3961,47 +4186,51 @@ LazyScript::finalize(FreeOp* fop)
+ 
+ size_t
+ JSScript::calculateLiveFixed(jsbytecode* pc)
+ {
+     size_t nlivefixed = numAlwaysLiveFixedSlots();
+ 
+     if (nfixed() != nlivefixed) {
+         Scope* scope = lookupScope(pc);
+-        if (scope)
++        if (scope) {
+             scope = MaybeForwarded(scope);
++        }
+ 
+         // Find the nearest LexicalScope in the same script.
+         while (scope && scope->is<WithScope>()) {
+             scope = scope->enclosing();
+-            if (scope)
++            if (scope) {
+                 scope = MaybeForwarded(scope);
++            }
+         }
+ 
+         if (scope) {
+-            if (scope->is<LexicalScope>())
++            if (scope->is<LexicalScope>()) {
+                 nlivefixed = scope->as<LexicalScope>().nextFrameSlot();
+-            else if (scope->is<VarScope>())
++            } else if (scope->is<VarScope>()) {
+                 nlivefixed = scope->as<VarScope>().nextFrameSlot();
++            }
+         }
+     }
+ 
+     MOZ_ASSERT(nlivefixed <= nfixed());
+     MOZ_ASSERT(nlivefixed >= numAlwaysLiveFixedSlots());
+ 
+     return nlivefixed;
+ }
+ 
+ Scope*
+ JSScript::lookupScope(jsbytecode* pc)
+ {
+     MOZ_ASSERT(containsPC(pc));
+ 
+-    if (!hasScopeNotes())
++    if (!hasScopeNotes()) {
+         return nullptr;
++    }
+ 
+     size_t offset = pc - code();
+ 
+     ScopeNoteArray* notes = scopeNotes();
+     Scope* scope = nullptr;
+ 
+     // Find the innermost block chain using a binary search.
+     size_t bottom = 0;
+@@ -4018,40 +4247,43 @@ JSScript::lookupScope(jsbytecode* pc)
+             // the searched range for coverage.
+             size_t check = mid;
+             while (check >= bottom) {
+                 const ScopeNote* checkNote = &notes->vector[check];
+                 MOZ_ASSERT(checkNote->start <= offset);
+                 if (offset < checkNote->start + checkNote->length) {
+                     // We found a matching block chain but there may be inner ones
+                     // at a higher block chain index than mid. Continue the binary search.
+-                    if (checkNote->index == ScopeNote::NoScopeIndex)
++                    if (checkNote->index == ScopeNote::NoScopeIndex) {
+                         scope = nullptr;
+-                    else
++                    } else {
+                         scope = getScope(checkNote->index);
++                    }
+                     break;
+                 }
+-                if (checkNote->parent == UINT32_MAX)
++                if (checkNote->parent == UINT32_MAX) {
+                     break;
++                }
+                 check = checkNote->parent;
+             }
+             bottom = mid + 1;
+         } else {
+             top = mid;
+         }
+     }
+ 
+     return scope;
+ }
+ 
+ Scope*
+ JSScript::innermostScope(jsbytecode* pc)
+ {
+-    if (Scope* scope = lookupScope(pc))
++    if (Scope* scope = lookupScope(pc)) {
+         return scope;
++    }
+     return bodyScope();
+ }
+ 
+ void
+ JSScript::setArgumentsHasVarBinding()
+ {
+     bitFields_.argsHasVarBinding_ = true;
+     bitFields_.needsArgsAnalysis_ = true;
+@@ -4070,43 +4302,48 @@ js::SetFrameArgumentsObject(JSContext* c
+                             HandleScript script, JSObject* argsobj)
+ {
+     /*
+      * Replace any optimized arguments in the frame with an explicit arguments
+      * object. Note that 'arguments' may have already been overwritten.
+      */
+ 
+     Rooted<BindingIter> bi(cx, BindingIter(script));
+-    while (bi && bi.name() != cx->names().arguments)
++    while (bi && bi.name() != cx->names().arguments) {
+         bi++;
+-    if (!bi)
++    }
++    if (!bi) {
+         return;
++    }
+ 
+     if (bi.location().kind() == BindingLocation::Kind::Environment) {
+         /*
+          * Scan the script to find the slot in the call object that 'arguments'
+          * is assigned to.
+          */
+         jsbytecode* pc = script->code();
+-        while (*pc != JSOP_ARGUMENTS)
++        while (*pc != JSOP_ARGUMENTS) {
+             pc += GetBytecodeLength(pc);
++        }
+         pc += JSOP_ARGUMENTS_LENGTH;
+         MOZ_ASSERT(*pc == JSOP_SETALIASEDVAR);
+ 
+         // Note that here and below, it is insufficient to only check for
+         // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the
+         // arguments slot.
+         EnvironmentObject& env = frame.callObj().as<EnvironmentObject>();
+-        if (IsOptimizedPlaceholderMagicValue(env.aliasedBinding(bi)))
++        if (IsOptimizedPlaceholderMagicValue(env.aliasedBinding(bi))) {
+             env.setAliasedBinding(cx, bi, ObjectValue(*argsobj));
++        }
+     } else {
+         MOZ_ASSERT(bi.location().kind() == BindingLocation::Kind::Frame);
+         uint32_t frameSlot = bi.location().slot();
+-        if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(frameSlot)))
++        if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(frameSlot))) {
+             frame.unaliasedLocal(frameSlot) = ObjectValue(*argsobj);
++        }
+     }
+ }
+ 
+ /* static */ bool
+ JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script)
+ {
+     MOZ_ASSERT(script->functionNonDelazifying());
+     MOZ_ASSERT(script->analyzedArgsUsage());
+@@ -4114,31 +4351,33 @@ JSScript::argumentsOptimizationFailed(JS
+ 
+     /*
+      * It is possible that the arguments optimization has already failed,
+      * everything has been fixed up, but there was an outstanding magic value
+      * on the stack that has just now flowed into an apply. In this case, there
+      * is nothing to do; GuardFunApplySpeculation will patch in the real
+      * argsobj.
+      */
+-    if (script->needsArgsObj())
++    if (script->needsArgsObj()) {
+         return true;
++    }
+ 
+     MOZ_ASSERT(!script->isGenerator());
+     MOZ_ASSERT(!script->isAsync());
+ 
+     script->bitFields_.needsArgsObj_ = true;
+ 
+     /*
+      * Since we can't invalidate baseline scripts, set a flag that's checked from
+      * JIT code to indicate the arguments optimization failed and JSOP_ARGUMENTS
+      * should create an arguments object next time.
+      */
+-    if (script->hasBaselineScript())
++    if (script->hasBaselineScript()) {
+         script->baselineScript()->setNeedsArgsObj();
++    }
+ 
+     /*
+      * By design, the arguments optimization is only made when there are no
+      * outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) at any points
+      * where the optimization could fail, other than an active invocation of
+      * 'f.apply(x, arguments)'. Thus, there are no outstanding values of
+      * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are
+      * three things that need fixup:
+@@ -4151,41 +4390,45 @@ JSScript::argumentsOptimizationFailed(JS
+     for (AllScriptFramesIter i(cx); !i.done(); ++i) {
+         /*
+          * We cannot reliably create an arguments object for Ion activations of
+          * this script.  To maintain the invariant that "script->needsArgsObj
+          * implies fp->hasArgsObj", the Ion bail mechanism will create an
+          * arguments object right after restoring the BaselineFrame and before
+          * entering Baseline code (in jit::FinishBailoutToBaseline).
+          */
+-        if (i.isIon())
++        if (i.isIon()) {
+             continue;
++        }
+         AbstractFramePtr frame = i.abstractFramePtr();
+         if (frame.isFunctionFrame() && frame.script() == script) {
+             /* We crash on OOM since cleaning up here would be complicated. */
+             AutoEnterOOMUnsafeRegion oomUnsafe;
+             ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame);
+-            if (!argsobj)
++            if (!argsobj) {
+                 oomUnsafe.crash("JSScript::argumentsOptimizationFailed");
++            }
+             SetFrameArgumentsObject(cx, frame, script, argsobj);
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ JSScript::formalIsAliased(unsigned argSlot)
+ {
+-    if (functionHasParameterExprs())
++    if (functionHasParameterExprs()) {
+         return false;
++    }
+ 
+     for (PositionalFormalParameterIter fi(this); fi; fi++) {
+-        if (fi.argumentSlot() == argSlot)
++        if (fi.argumentSlot() == argSlot) {
+             return fi.closedOver();
++        }
+     }
+     MOZ_CRASH("Argument slot not found");
+ }
+ 
+ bool
+ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
+ {
+     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
+@@ -4326,28 +4569,31 @@ LazyScript::Create(JSContext* cx, Handle
+     p.isLikelyConstructorWrapper = false;
+     p.isDerivedClassConstructor = false;
+     p.needsHomeObject = false;
+     p.parseGoal = uint32_t(parseGoal);
+ 
+     LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
+                                             sourceStart, sourceEnd,
+                                             toStringStart, lineno, column);
+-    if (!res)
++    if (!res) {
+         return nullptr;
++    }
+ 
+     JSAtom** resClosedOverBindings = res->closedOverBindings();
+-    for (size_t i = 0; i < res->numClosedOverBindings(); i++)
++    for (size_t i = 0; i < res->numClosedOverBindings(); i++) {
+         resClosedOverBindings[i] = closedOverBindings[i];
++    }
+ 
+     GCPtrFunction* resInnerFunctions = res->innerFunctions();
+     for (size_t i = 0; i < res->numInnerFunctions(); i++) {
+         resInnerFunctions[i].init(innerFunctions[i]);
+-        if (resInnerFunctions[i]->isInterpretedLazy())
++        if (resInnerFunctions[i]->isInterpretedLazy()) {
+             resInnerFunctions[i]->lazyScript()->setEnclosingLazyScript(res);
++        }
+     }
+ 
+     return res;
+ }
+ 
+ /* static */ LazyScript*
+ LazyScript::CreateForXDR(JSContext* cx, HandleFunction fun,
+                          HandleScript script, HandleScope enclosingScope,
+@@ -4360,42 +4606,47 @@ LazyScript::CreateForXDR(JSContext* cx, 
+ 
+     // Dummy function which is not a valid function as this is the one which is
+     // holding this lazy script.
+     HandleFunction dummyFun = fun;
+ 
+     LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
+                                             sourceStart, sourceEnd,
+                                             toStringStart, lineno, column);
+-    if (!res)
++    if (!res) {
+         return nullptr;
++    }
+ 
+     // Fill with dummies, to be GC-safe after the initialization of the free
+     // variables and inner functions.
+     size_t i, num;
+     JSAtom** closedOverBindings = res->closedOverBindings();
+-    for (i = 0, num = res->numClosedOverBindings(); i < num; i++)
++    for (i = 0, num = res->numClosedOverBindings(); i < num; i++) {
+         closedOverBindings[i] = dummyAtom;
++    }
+ 
+     GCPtrFunction* functions = res->innerFunctions();
+-    for (i = 0, num = res->numInnerFunctions(); i < num; i++)
++    for (i = 0, num = res->numInnerFunctions(); i < num; i++) {
+         functions[i].init(dummyFun);
++    }
+ 
+     // Set the enclosing scope of the lazy function. This value should only be
+     // set if we have a non-lazy enclosing script at this point.
+     // LazyScript::enclosingScriptHasEverBeenCompiled relies on the enclosing
+     // scope being non-null if we have ever been nested inside non-lazy
+     // function.
+     MOZ_ASSERT(!res->hasEnclosingScope());
+-    if (enclosingScope)
++    if (enclosingScope) {
+         res->setEnclosingScope(enclosingScope);
++    }
+ 
+     MOZ_ASSERT(!res->hasScript());
+-    if (script)
++    if (script) {
+         res->initScript(script);
++    }
+ 
+     return res;
+ }
+ 
+ void
+ LazyScript::initRuntimeFields(uint64_t packedFields)
+ {
+     union {
+@@ -4428,18 +4679,19 @@ JSScript::updateJitCodeRaw(JSRuntime* rt
+     }
+     MOZ_ASSERT(jitCodeRaw_);
+     MOZ_ASSERT(jitCodeSkipArgCheck_);
+ }
+ 
+ bool
+ JSScript::hasLoops()
+ {
+-    if (!hasTrynotes())
++    if (!hasTrynotes()) {
+         return false;
++    }
+     JSTryNote* tn = trynotes()->vector;
+     JSTryNote* tnlimit = tn + trynotes()->length;
+     for (; tn < tnlimit; tn++) {
+         switch (tn->kind) {
+           case JSTRY_FOR_IN:
+           case JSTRY_FOR_OF:
+           case JSTRY_LOOP:
+             return true;
+@@ -4526,13 +4778,14 @@ JS::ubi::Concrete<js::LazyScript>::size(
+     size += get().sizeOfExcludingThis(mallocSizeOf);
+     return size;
+ }
+ 
+ const char*
+ JS::ubi::Concrete<js::LazyScript>::scriptFilename() const
+ {
+     auto source = get().sourceObject().source();
+-    if (!source)
++    if (!source) {
+         return nullptr;
++    }
+ 
+     return source->filename();
+ }
+diff --git a/js/src/vm/JSScript.cpp.1488698-7.later b/js/src/vm/JSScript.cpp.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSScript.cpp.1488698-7.later
+@@ -0,0 +1,850 @@
++--- JSScript.cpp
+++++ JSScript.cpp
++@@ -1045,24 +1135,26 @@ JSScript::initScriptCounts(JSContext* cx
++ 
++     // Initialize all PCCounts counters to 0.
++     ScriptCounts::PCCountsVector base;
++     if (!base.reserve(jumpTargets.length())) {
++         ReportOutOfMemory(cx);
++         return false;
++     }
++ 
++-    for (size_t i = 0; i < jumpTargets.length(); i++)
+++    for (size_t i = 0; i < jumpTargets.length(); i++) {
++         base.infallibleEmplaceBack(pcToOffset(jumpTargets[i]));
+++    }
++ 
++     // Create realm's scriptCountsMap if necessary.
++     if (!realm()->scriptCountsMap) {
++         auto map = cx->make_unique<ScriptCountsMap>();
++-        if (!map)
+++        if (!map) {
++             return false;
+++        }
++ 
++         realm()->scriptCountsMap = std::move(map);
++     }
++ 
++     // Allocate the ScriptCounts.
++     UniqueScriptCounts sc = cx->make_unique<ScriptCounts>(std::move(base));
++     if (!sc) {
++         ReportOutOfMemory(cx);
++@@ -1509,51 +1631,55 @@ UncompressedSourceCache::releaseEntry(Au
++     MOZ_ASSERT(holder_ == &holder);
++     holder_ = nullptr;
++ }
++ 
++ const char16_t*
++ UncompressedSourceCache::lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& holder)
++ {
++     MOZ_ASSERT(!holder_);
++-    if (!map_)
+++    if (!map_) {
++         return nullptr;
+++    }
++     if (Map::Ptr p = map_->lookup(ssc)) {
++         holdEntry(holder, ssc);
++         return p->value().get();
++     }
++     return nullptr;
++ }
++ 
++ bool
++ UncompressedSourceCache::put(const ScriptSourceChunk& ssc, UniqueTwoByteChars str,
++                              AutoHoldEntry& holder)
++ {
++     MOZ_ASSERT(!holder_);
++ 
++     if (!map_) {
++         UniquePtr<Map> map = MakeUnique<Map>();
++-        if (!map)
+++        if (!map) {
++             return false;
+++        }
++ 
++         map_ = std::move(map);
++     }
++ 
++-    if (!map_->put(ssc, std::move(str)))
+++    if (!map_->put(ssc, std::move(str))) {
++         return false;
+++    }
++ 
++     holdEntry(holder, ssc);
++     return true;
++ }
++ 
++ void
++ UncompressedSourceCache::purge()
++ {
++-    if (!map_)
+++    if (!map_) {
++         return;
+++    }
++ 
++     for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
++         if (holder_ && r.front().key() == holder_->sourceChunk()) {
++             holder_->deferDelete(std::move(r.front().value()));
++             holder_ = nullptr;
++         }
++     }
++ 
++@@ -1561,31 +1687,33 @@ UncompressedSourceCache::purge()
++ }
++ 
++ size_t
++ UncompressedSourceCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
++ {
++     size_t n = 0;
++     if (map_ && !map_->empty()) {
++         n += map_->shallowSizeOfIncludingThis(mallocSizeOf);
++-        for (Map::Range r = map_->all(); !r.empty(); r.popFront())
+++        for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
++             n += mallocSizeOf(r.front().value().get());
+++        }
++     }
++     return n;
++ }
++ 
++ const char16_t*
++ ScriptSource::chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
++                          size_t chunk)
++ {
++     const Compressed& c = data.as<Compressed>();
++ 
++     ScriptSourceChunk ssc(this, chunk);
++-    if (const char16_t* decompressed = cx->caches().uncompressedSourceCache.lookup(ssc, holder))
+++    if (const char16_t* decompressed = cx->caches().uncompressedSourceCache.lookup(ssc, holder)) {
++         return decompressed;
+++    }
++ 
++     size_t totalLengthInBytes = length() * sizeof(char16_t);
++     size_t chunkBytes = Compressor::chunkSize(totalLengthInBytes, chunk);
++ 
++     MOZ_ASSERT((chunkBytes % sizeof(char16_t)) == 0);
++     const size_t lengthWithNull = (chunkBytes / sizeof(char16_t)) + 1;
++     UniqueTwoByteChars decompressed(js_pod_malloc<char16_t>(lengthWithNull));
++     if (!decompressed) {
++@@ -2150,67 +2308,73 @@ ScriptSource::performXDR(XDRState<mode>*
++     MOZ_TRY(xdr->codeUint8(&haveSourceMap));
++ 
++     if (haveSourceMap) {
++         uint32_t sourceMapURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(sourceMapURL_.get());
++         MOZ_TRY(xdr->codeUint32(&sourceMapURLLen));
++ 
++         if (mode == XDR_DECODE) {
++             sourceMapURL_ = xdr->cx()->template make_pod_array<char16_t>(sourceMapURLLen + 1);
++-            if (!sourceMapURL_)
+++            if (!sourceMapURL_) {
++                 return xdr->fail(JS::TranscodeResult_Throw);
+++            }
++         }
++         auto guard = mozilla::MakeScopeExit([&] {
++-            if (mode == XDR_DECODE)
+++            if (mode == XDR_DECODE) {
++                 sourceMapURL_ = nullptr;
+++            }
++         });
++         MOZ_TRY(xdr->codeChars(sourceMapURL_.get(), sourceMapURLLen));
++         guard.release();
++         sourceMapURL_[sourceMapURLLen] = '\0';
++     }
++ 
++     uint8_t haveDisplayURL = hasDisplayURL();
++     MOZ_TRY(xdr->codeUint8(&haveDisplayURL));
++ 
++     if (haveDisplayURL) {
++         uint32_t displayURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(displayURL_.get());
++         MOZ_TRY(xdr->codeUint32(&displayURLLen));
++ 
++         if (mode == XDR_DECODE) {
++             displayURL_ = xdr->cx()->template make_pod_array<char16_t>(displayURLLen + 1);
++-            if (!displayURL_)
+++            if (!displayURL_) {
++                 return xdr->fail(JS::TranscodeResult_Throw);
+++            }
++         }
++         auto guard = mozilla::MakeScopeExit([&] {
++-            if (mode == XDR_DECODE)
+++            if (mode == XDR_DECODE) {
++                 displayURL_ = nullptr;
+++            }
++         });
++         MOZ_TRY(xdr->codeChars(displayURL_.get(), displayURLLen));
++         guard.release();
++         displayURL_[displayURLLen] = '\0';
++     }
++ 
++     uint8_t haveFilename = !!filename_;
++     MOZ_TRY(xdr->codeUint8(&haveFilename));
++ 
++     if (haveFilename) {
++         const char* fn = filename();
++         MOZ_TRY(xdr->codeCString(&fn));
++         // Note: If the decoder has an option, then the filename is defined by
++         // the CompileOption from the document.
++         MOZ_ASSERT_IF(mode == XDR_DECODE && xdr->hasOptions(), filename());
++-        if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn))
+++        if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn)) {
++             return xdr->fail(JS::TranscodeResult_Throw);
+++        }
++ 
++         // Note the content of sources decoded when recording or replaying.
++         if (mode == XDR_DECODE && hasSourceData() && mozilla::recordreplay::IsRecordingOrReplaying()) {
++             UncompressedSourceCache::AutoHoldEntry holder;
++             ScriptSource::PinnedChars chars(xdr->cx(), this, holder, 0, length());
++-            if (!chars.get())
+++            if (!chars.get()) {
++                 return xdr->fail(JS::TranscodeResult_Throw);
+++            }
++             mozilla::recordreplay::NoteContentParse(this, filename(), "application/javascript",
++                                                     chars.get(), length());
++         }
++     }
++ 
++     return Ok();
++ }
++ 
++@@ -2235,18 +2399,19 @@ js::FormatIntroducedFilename(JSContext* 
++     size_t introducerLen = strlen(introducer);
++     size_t len = filenameLen                    +
++                  6 /* == strlen(" line ") */    +
++                  linenoLen                      +
++                  3 /* == strlen(" > ") */       +
++                  introducerLen                  +
++                  1 /* \0 */;
++     char* formatted = cx->pod_malloc<char>(len);
++-    if (!formatted)
+++    if (!formatted) {
++         return nullptr;
+++    }
++ 
++     mozilla::DebugOnly<size_t> checkLen = snprintf(formatted, len, "%s line %s > %s",
++                                                    filename, linenoBuf, introducer);
++     MOZ_ASSERT(checkLen == len - 1);
++ 
++     return formatted;
++ }
++ 
++@@ -2674,40 +2852,44 @@ JSScript::createInitialized(JSContext* c
++ 
++ /* static */ JSScript*
++ JSScript::Create(JSContext* cx, const ReadOnlyCompileOptions& options,
++                  HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd,
++                  uint32_t toStringStart, uint32_t toStringEnd)
++ {
++     RootedScript script(cx, createInitialized(cx, options, sourceObject, bufStart, bufEnd,
++                                               toStringStart, toStringEnd));
++-    if (!script)
+++    if (!script) {
++         return nullptr;
+++    }
++ 
++     if (cx->runtime()->lcovOutput().isEnabled()) {
++-        if (!script->initScriptName(cx))
+++        if (!script->initScriptName(cx)) {
++             return nullptr;
+++        }
++     }
++ 
++     return script;
++ }
++ 
++ bool
++ JSScript::initScriptName(JSContext* cx)
++ {
++     MOZ_ASSERT(!hasScriptName());
++ 
++-    if (!filename())
+++    if (!filename()) {
++         return true;
+++    }
++ 
++     // Create realm's scriptNameMap if necessary.
++     if (!realm()->scriptNameMap) {
++         auto map = cx->make_unique<ScriptNameMap>();
++-        if (!map)
+++        if (!map) {
++             return false;
+++        }
++ 
++         realm()->scriptNameMap = std::move(map);
++     }
++ 
++     UniqueChars name = DuplicateString(filename());
++     if (!name) {
++         ReportOutOfMemory(cx);
++         return false;
++@@ -2720,38 +2902,41 @@ JSScript::initScriptName(JSContext* cx)
++     }
++ 
++     return true;
++ }
++ 
++ static inline uint8_t*
++ AllocScriptData(JSContext* cx, size_t size)
++ {
++-    if (!size)
+++    if (!size) {
++         return nullptr;
+++    }
++ 
++     uint8_t* data = cx->pod_calloc<uint8_t>(JS_ROUNDUP(size, sizeof(Value)));
++-    if (!data)
+++    if (!data) {
++         return nullptr;
+++    }
++     MOZ_ASSERT(size_t(data) % sizeof(Value) == 0);
++     return data;
++ }
++ 
++ /* static */ bool
++ JSScript::partiallyInit(JSContext* cx, HandleScript script, uint32_t nscopes,
++                         uint32_t nconsts, uint32_t nobjects, uint32_t ntrynotes,
++                         uint32_t nscopenotes, uint32_t nyieldoffsets)
++ {
++     cx->check(script);
++ 
++     size_t size = ScriptDataSize(nscopes, nconsts, nobjects, ntrynotes,
++                                  nscopenotes, nyieldoffsets);
++     script->data = AllocScriptData(cx, size);
++-    if (size && !script->data)
+++    if (size && !script->data) {
++         return false;
+++    }
++ 
++     script->dataSize_ = size;
++ 
++     uint8_t* cursor = script->data;
++ 
++     // There must always be at least 1 scope, the body scope.
++     MOZ_ASSERT(nscopes != 0);
++     cursor += sizeof(ScopeArray);
++@@ -2945,82 +3136,92 @@ JSScript::fullyInitFromEmitter(JSContext
++     if (nslots > UINT32_MAX) {
++         bce->reportError(nullptr, JSMSG_NEED_DIET, js_script_str);
++         return false;
++     }
++ 
++     uint32_t mainLength = bce->offset();
++     uint32_t prologueLength = bce->prologueOffset();
++     uint32_t nsrcnotes;
++-    if (!bce->finishTakingSrcNotes(&nsrcnotes))
+++    if (!bce->finishTakingSrcNotes(&nsrcnotes)) {
++         return false;
+++    }
++     uint32_t natoms = bce->atomIndices->count();
++     if (!partiallyInit(cx, script,
++                        bce->scopeList.length(), bce->numberList.length(), bce->objectList.length,
++                        bce->tryNoteList.length(), bce->scopeNoteList.length(),
++                        bce->yieldAndAwaitOffsetList.length()))
++     {
++         return false;
++     }
++ 
++     MOZ_ASSERT(script->mainOffset() == 0);
++     script->mainOffset_ = prologueLength;
++     script->nTypeSets_ = bce->typesetCount;
++     script->lineno_ = bce->firstLine;
++ 
++-    if (!script->createScriptData(cx, prologueLength + mainLength, nsrcnotes, natoms))
+++    if (!script->createScriptData(cx, prologueLength + mainLength, nsrcnotes, natoms)) {
++         return false;
+++    }
++ 
++     // Any fallible operation after JSScript::createScriptData should reset
++     // JSScript.scriptData_, in order to treat this script as uncompleted,
++     // in JSScript::isUncompleted.
++     // JSScript::shareScriptData resets it before returning false.
++ 
++     jsbytecode* code = script->code();
++     PodCopy<jsbytecode>(code, bce->prologue.code.begin(), prologueLength);
++     PodCopy<jsbytecode>(code + prologueLength, bce->main.code.begin(), mainLength);
++     bce->copySrcNotes((jssrcnote*)(code + script->length()), nsrcnotes);
++     InitAtomMap(*bce->atomIndices, script->atoms());
++ 
++-    if (!script->shareScriptData(cx))
+++    if (!script->shareScriptData(cx)) {
++         return false;
++-
++-    if (bce->numberList.length() != 0)
+++    }
+++
+++    if (bce->numberList.length() != 0) {
++         bce->numberList.finish(script->consts());
++-    if (bce->objectList.length != 0)
+++    }
+++    if (bce->objectList.length != 0) {
++         bce->objectList.finish(script->objects());
++-    if (bce->scopeList.length() != 0)
+++    }
+++    if (bce->scopeList.length() != 0) {
++         bce->scopeList.finish(script->scopes());
++-    if (bce->tryNoteList.length() != 0)
+++    }
+++    if (bce->tryNoteList.length() != 0) {
++         bce->tryNoteList.finish(script->trynotes());
++-    if (bce->scopeNoteList.length() != 0)
+++    }
+++    if (bce->scopeNoteList.length() != 0) {
++         bce->scopeNoteList.finish(script->scopeNotes(), prologueLength);
+++    }
++     script->bitFields_.strict_ = bce->sc->strict();
++     script->bitFields_.explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
++     script->bitFields_.bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
++     script->bitFields_.hasSingletons_ = bce->hasSingletons;
++ 
++     script->nfixed_ = bce->maxFixedSlots;
++     script->nslots_ = nslots;
++     script->bodyScopeIndex_ = bce->bodyScopeIndex;
++     script->bitFields_.hasNonSyntacticScope_ =
++         bce->outermostScope()->hasOnChain(ScopeKind::NonSyntactic);
++ 
++     // There shouldn't be any fallible operation after initFromFunctionBox,
++     // JSFunction::hasUncompletedScript relies on the fact that the existence
++     // of the pointer to JSScript means the pointed JSScript is complete.
++-    if (bce->sc->isFunctionBox())
+++    if (bce->sc->isFunctionBox()) {
++         initFromFunctionBox(script, bce->sc->asFunctionBox());
++-    else if (bce->sc->isModuleContext())
+++    } else if (bce->sc->isModuleContext()) {
++         initFromModuleContext(script);
+++    }
++ 
++     // Copy yield offsets last, as the generator kind is set in
++     // initFromFunctionBox.
++-    if (bce->yieldAndAwaitOffsetList.length() != 0)
+++    if (bce->yieldAndAwaitOffsetList.length() != 0) {
++         bce->yieldAndAwaitOffsetList.finish(script->yieldAndAwaitOffsets(), prologueLength);
+++    }
++ 
++ #ifdef DEBUG
++     script->assertValidJumpTargets();
++ #endif
++ 
++     return true;
++ }
++ 
++@@ -3162,18 +3366,19 @@ GSNCache::purge()
++     code = nullptr;
++     map.clearAndCompact();
++ }
++ 
++ jssrcnote*
++ js::GetSrcNote(GSNCache& cache, JSScript* script, jsbytecode* pc)
++ {
++     size_t target = pc - script->code();
++-    if (target >= script->length())
+++    if (target >= script->length()) {
++         return nullptr;
+++    }
++ 
++     if (cache.code == script->code()) {
++         GSNCache::Map::Ptr p = cache.map.lookup(pc);
++         return p ? p->value() : nullptr;
++     }
++ 
++     size_t offset = 0;
++     jssrcnote* result;
++@@ -3187,31 +3392,33 @@ js::GetSrcNote(GSNCache& cache, JSScript
++             result = sn;
++             break;
++         }
++     }
++ 
++     if (cache.code != script->code() && script->length() >= GSN_CACHE_THRESHOLD) {
++         unsigned nsrcnotes = 0;
++         for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
++-            if (SN_IS_GETTABLE(sn))
+++            if (SN_IS_GETTABLE(sn)) {
++                 ++nsrcnotes;
+++            }
++         }
++         if (cache.code) {
++             cache.map.clear();
++             cache.code = nullptr;
++         }
++         if (cache.map.reserve(nsrcnotes)) {
++             pc = script->code();
++             for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn);
++                  sn = SN_NEXT(sn))
++             {
++                 pc += SN_DELTA(sn);
++-                if (SN_IS_GETTABLE(sn))
+++                if (SN_IS_GETTABLE(sn)) {
++                     cache.map.putNewInfallible(pc, sn);
+++                }
++             }
++             cache.code = script->code();
++         }
++     }
++ 
++     return result;
++ }
++ 
++@@ -3451,36 +3671,38 @@ js::detail::CopyScript(JSContext* cx, Ha
++     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
++     uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes()->length : 0;
++     uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().length() : 0;
++ 
++     /* Script data */
++ 
++     size_t size = src->dataSize();
++     UniquePtr<uint8_t, JS::FreePolicy> data(AllocScriptData(cx, size));
++-    if (size && !data)
+++    if (size && !data) {
++         return false;
+++    }
++ 
++     /* Scopes */
++ 
++     // The passed in scopes vector contains body scopes that needed to be
++     // cloned especially, depending on whether the script is a function or
++     // global scope. Starting at scopes.length() means we only deal with
++     // intra-body scopes.
++     {
++         MOZ_ASSERT(nscopes != 0);
++         MOZ_ASSERT(src->bodyScopeIndex() + 1 == scopes.length());
++         GCPtrScope* vector = src->scopes()->vector;
++         RootedScope original(cx);
++         RootedScope clone(cx);
++         for (uint32_t i = scopes.length(); i < nscopes; i++) {
++             original = vector[i];
++             clone = Scope::clone(cx, original, scopes[FindScopeIndex(src, *original->enclosing())]);
++-            if (!clone || !scopes.append(clone))
+++            if (!clone || !scopes.append(clone)) {
++                 return false;
+++            }
++         }
++     }
++ 
++     /* Objects */
++ 
++     AutoObjectVector objects(cx);
++     if (nobjects != 0) {
++         GCPtrObject* vector = src->objects()->vector;
++@@ -3746,29 +3987,32 @@ JSScript::destroyDebugScript(FreeOp* fop
++ #endif
++         fop->free_(releaseDebugScript());
++     }
++ }
++ 
++ bool
++ JSScript::ensureHasDebugScript(JSContext* cx)
++ {
++-    if (bitFields_.hasDebugScript_)
+++    if (bitFields_.hasDebugScript_) {
++         return true;
+++    }
++ 
++     size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
++     UniqueDebugScript debug(reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
++-    if (!debug)
+++    if (!debug) {
++         return false;
+++    }
++ 
++     /* Create realm's debugScriptMap if necessary. */
++     if (!realm()->debugScriptMap) {
++         auto map = cx->make_unique<DebugScriptMap>();
++-        if (!map)
+++        if (!map) {
++             return false;
+++        }
++ 
++         realm()->debugScriptMap = std::move(map);
++     }
++ 
++     if (!realm()->debugScriptMap->putNew(this, std::move(debug))) {
++         ReportOutOfMemory(cx);
++         return false;
++     }
++@@ -3776,49 +4020,53 @@ JSScript::ensureHasDebugScript(JSContext
++     bitFields_.hasDebugScript_ = true; // safe to set this;  we can't fail after this point
++ 
++     /*
++      * Ensure that any Interpret() instances running on this script have
++      * interrupts enabled. The interrupts must stay enabled until the
++      * debug state is destroyed.
++      */
++     for (ActivationIterator iter(cx); !iter.done(); ++iter) {
++-        if (iter->isInterpreter())
+++        if (iter->isInterpreter()) {
++             iter->asInterpreter()->enableInterruptsIfRunning(this);
+++        }
++     }
++ 
++     return true;
++ }
++ 
++ void
++ JSScript::setNewStepMode(FreeOp* fop, uint32_t newValue)
++ {
++     DebugScript* debug = debugScript();
++     uint32_t prior = debug->stepMode;
++     debug->stepMode = newValue;
++ 
++     if (!prior != !newValue) {
++-        if (hasBaselineScript())
+++        if (hasBaselineScript()) {
++             baseline->toggleDebugTraps(this, nullptr);
++-
++-        if (!stepModeEnabled() && !debug->numSites)
+++        }
+++
+++        if (!stepModeEnabled() && !debug->numSites) {
++             fop->free_(releaseDebugScript());
+++        }
++     }
++ }
++ 
++ bool
++ JSScript::incrementStepModeCount(JSContext* cx)
++ {
++     cx->check(this);
++     MOZ_ASSERT(cx->realm()->isDebuggee());
++ 
++     AutoRealm ar(cx, this);
++ 
++-    if (!ensureHasDebugScript(cx))
+++    if (!ensureHasDebugScript(cx)) {
++         return false;
+++    }
++ 
++     DebugScript* debug = debugScript();
++     uint32_t count = debug->stepMode;
++     setNewStepMode(cx->runtime()->defaultFreeOp(), count + 1);
++     return true;
++ }
++ 
++ void
++@@ -3830,119 +4078,130 @@ JSScript::decrementStepModeCount(FreeOp*
++     setNewStepMode(fop, count - 1);
++ }
++ 
++ BreakpointSite*
++ JSScript::getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc)
++ {
++     AutoRealm ar(cx, this);
++ 
++-    if (!ensureHasDebugScript(cx))
+++    if (!ensureHasDebugScript(cx)) {
++         return nullptr;
+++    }
++ 
++     DebugScript* debug = debugScript();
++     BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
++ 
++     if (!site) {
++         site = cx->new_<JSBreakpointSite>(this, pc);
++-        if (!site)
+++        if (!site) {
++             return nullptr;
+++        }
++         debug->numSites++;
++     }
++ 
++     return site;
++ }
++ 
++ void
++ JSScript::destroyBreakpointSite(FreeOp* fop, jsbytecode* pc)
++ {
++     DebugScript* debug = debugScript();
++     BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
++     MOZ_ASSERT(site);
++ 
++     fop->delete_(site);
++     site = nullptr;
++ 
++-    if (--debug->numSites == 0 && !stepModeEnabled())
+++    if (--debug->numSites == 0 && !stepModeEnabled()) {
++         fop->free_(releaseDebugScript());
+++    }
++ }
++ 
++ void
++ JSScript::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, JSObject* handler)
++ {
++-    if (!hasAnyBreakpointsOrStepMode())
+++    if (!hasAnyBreakpointsOrStepMode()) {
++         return;
+++    }
++ 
++     for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
++         BreakpointSite* site = getBreakpointSite(pc);
++         if (site) {
++             Breakpoint* nextbp;
++             for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
++                 nextbp = bp->nextInSite();
++-                if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler))
+++                if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler)) {
++                     bp->destroy(fop);
+++                }
++             }
++         }
++     }
++ }
++ 
++ bool
++ JSScript::hasBreakpointsAt(jsbytecode* pc)
++ {
++     BreakpointSite* site = getBreakpointSite(pc);
++-    if (!site)
+++    if (!site) {
++         return false;
+++    }
++ 
++     return site->enabledCount > 0;
++ }
++ 
++ void
++ SharedScriptData::traceChildren(JSTracer* trc)
++ {
++     MOZ_ASSERT(refCount() != 0);
++-    for (uint32_t i = 0; i < natoms(); ++i)
+++    for (uint32_t i = 0; i < natoms(); ++i) {
++         TraceNullableEdge(trc, &atoms()[i], "atom");
+++    }
++ }
++ 
++ void
++ JSScript::traceChildren(JSTracer* trc)
++ {
++     // NOTE: this JSScript may be partially initialized at this point.  E.g. we
++     // may have created it and partially initialized it with
++     // JSScript::Create(), but not yet finished initializing it with
++     // fullyInitFromEmitter() or fullyInitTrivial().
++ 
++     MOZ_ASSERT_IF(trc->isMarkingTracer() &&
++                   GCMarker::fromTracer(trc)->shouldCheckCompartments(),
++                   zone()->isCollecting());
++ 
++-    if (scriptData())
+++    if (scriptData()) {
++         scriptData()->traceChildren(trc);
++-
++-    if (ScopeArray* scopearray = scopes())
+++    }
+++
+++    if (ScopeArray* scopearray = scopes()) {
++         TraceRange(trc, scopearray->length, scopearray->vector, "scopes");
+++    }
++ 
++     if (hasConsts()) {
++         ConstArray* constarray = consts();
++         TraceRange(trc, constarray->length, constarray->vector, "consts");
++     }
++ 
++     if (hasObjects()) {
++         ObjectArray* objarray = objects();
++         TraceRange(trc, objarray->length, objarray->vector, "objects");
++     }
++ 
++     MOZ_ASSERT_IF(sourceObject(), MaybeForwarded(sourceObject())->compartment() == compartment());
++     TraceNullableEdge(trc, &sourceObject_, "sourceObject");
++ 
++-    if (maybeLazyScript())
+++    if (maybeLazyScript()) {
++         TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
++-
++-    if (trc->isMarkingTracer())
+++    }
+++
+++    if (trc->isMarkingTracer()) {
++         realm()->mark();
+++    }
++ 
++     jit::TraceJitScripts(trc, this);
++ }
++ 
++ void
++ LazyScript::finalize(FreeOp* fop)
++ {
++     fop->free_(table_);
++@@ -4280,23 +4557,25 @@ LazyScript::CreateRaw(JSContext* cx, Han
++     p.treatAsRunOnce = false;
++ 
++     size_t bytes = (p.numClosedOverBindings * sizeof(JSAtom*))
++                  + (p.numInnerFunctions * sizeof(GCPtrFunction));
++ 
++     UniquePtr<uint8_t, JS::FreePolicy> table;
++     if (bytes) {
++         table.reset(cx->pod_malloc<uint8_t>(bytes));
++-        if (!table)
+++        if (!table) {
++             return nullptr;
+++        }
++     }
++ 
++     LazyScript* res = Allocate<LazyScript>(cx);
++-    if (!res)
+++    if (!res) {
++         return nullptr;
+++    }
++ 
++     cx->realm()->scheduleDelazificationForDebugger();
++ 
++     return new (res) LazyScript(fun, *sourceObject, table.release(), packed, sourceStart,
++                                 sourceEnd, toStringStart, lineno, column);
++ }
++ 
++ /* static */ LazyScript*
++@@ -4484,46 +4772,50 @@ JSScript::AutoDelazify::holdScript(JS::H
++     }
++ }
++ 
++ void
++ JSScript::AutoDelazify::dropScript()
++ {
++     // Don't touch script_ if it's in the self-hosting realm, see the comment
++     // in holdScript.
++-    if (script_ && !script_->realm()->isSelfHostingRealm())
+++    if (script_ && !script_->realm()->isSelfHostingRealm()) {
++         script_->setDoNotRelazify(oldDoNotRelazify_);
+++    }
++     script_ = nullptr;
++ }
++ 
++ void
++ JSScript::setTopLevelPrivate(void* value)
++ {
++     MOZ_ASSERT(module() || isGlobalCode());
++ 
++     Scope* scope = bodyScope();
++-    if (scope->is<GlobalScope>())
+++    if (scope->is<GlobalScope>()) {
++         scope->as<GlobalScope>().setTopLevelPrivate(value);
++-    else if (scope->is<ModuleScope>())
+++    } else if (scope->is<ModuleScope>()) {
++         scope->as<ModuleScope>().setTopLevelPrivate(value);
++-    else
+++    } else {
++         MOZ_CRASH("Unexpected scope kind");
+++    }
++ }
++ 
++ void*
++ JSScript::maybeTopLevelPrivate() const
++ {
++-    if (!module() && !isGlobalCode())
+++    if (!module() && !isGlobalCode()) {
++         return nullptr;
+++    }
++ 
++     Scope* scope = bodyScope();
++-    if (scope->is<GlobalScope>())
+++    if (scope->is<GlobalScope>()) {
++         return scope->as<GlobalScope>().topLevelPrivate();
++-    else if (scope->is<ModuleScope>())
+++    } else if (scope->is<ModuleScope>()) {
++         return scope->as<ModuleScope>().topLevelPrivate();
+++    }
++ 
++     MOZ_CRASH("Unexpected scope kind");
++ }
++ 
++ JS::ubi::Base::Size
++ JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
++ {
++     Size size = gc::Arena::thingSize(get().asTenured().getAllocKind());
+diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h
+--- a/js/src/vm/JSScript.h
++++ b/js/src/vm/JSScript.h
+@@ -694,25 +694,28 @@ class ScriptSourceHolder
+     {}
+     explicit ScriptSourceHolder(ScriptSource* ss)
+       : ss(ss)
+     {
+         ss->incref();
+     }
+     ~ScriptSourceHolder()
+     {
+-        if (ss)
++        if (ss) {
+             ss->decref();
++        }
+     }
+     void reset(ScriptSource* newss) {
+         // incref before decref just in case ss == newss.
+-        if (newss)
++        if (newss) {
+             newss->incref();
+-        if (ss)
++        }
++        if (ss) {
+             ss->decref();
++        }
+         ss = newss;
+     }
+     ScriptSource* get() const {
+         return ss;
+     }
+ };
+ 
+ class ScriptSourceObject : public NativeObject
+@@ -741,18 +744,19 @@ class ScriptSourceObject : public Native
+         return getReservedSlot(ELEMENT_SLOT).toObjectOrNull();
+     }
+     const Value& elementAttributeName() const {
+         MOZ_ASSERT(!getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic());
+         return getReservedSlot(ELEMENT_PROPERTY_SLOT);
+     }
+     JSScript* introductionScript() const {
+         Value value = getReservedSlot(INTRODUCTION_SCRIPT_SLOT);
+-        if (value.isUndefined())
++        if (value.isUndefined()) {
+             return nullptr;
++        }
+         return value.toGCThing()->as<JSScript>();
+     }
+ 
+   private:
+     static const uint32_t SOURCE_SLOT = 0;
+     static const uint32_t ELEMENT_SLOT = 1;
+     static const uint32_t ELEMENT_PROPERTY_SLOT = 2;
+     static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3;
+@@ -809,36 +813,38 @@ class SharedScriptData
+         return refCount_;
+     }
+     void incRefCount() {
+         refCount_++;
+     }
+     void decRefCount() {
+         MOZ_ASSERT(refCount_ != 0);
+         uint32_t remain = --refCount_;
+-        if (remain == 0)
++        if (remain == 0) {
+             js_free(this);
++        }
+     }
+ 
+     size_t dataLength() const {
+         return (natoms_ * sizeof(GCPtrAtom)) + codeLength_ + noteLength_;
+     }
+     const uint8_t* data() const {
+         return reinterpret_cast<const uint8_t*>(data_);
+     }
+     uint8_t* data() {
+         return reinterpret_cast<uint8_t*>(data_);
+     }
+ 
+     uint32_t natoms() const {
+         return natoms_;
+     }
+     GCPtrAtom* atoms() {
+-        if (!natoms_)
++        if (!natoms_) {
+             return nullptr;
++        }
+         return reinterpret_cast<GCPtrAtom*>(data());
+     }
+ 
+     uint32_t codeLength() const {
+         return codeLength_;
+     }
+     jsbytecode* code() {
+         return reinterpret_cast<jsbytecode*>(data() + natoms_ * sizeof(GCPtrAtom));
+@@ -1245,18 +1251,19 @@ class JSScript : public js::gc::TenuredC
+     JS::Realm* realm() const { return realm_; }
+ 
+     js::SharedScriptData* scriptData() {
+         return scriptData_;
+     }
+ 
+     // Script bytecode is immutable after creation.
+     jsbytecode* code() const {
+-        if (!scriptData_)
++        if (!scriptData_) {
+             return nullptr;
++        }
+         return scriptData_->code();
+     }
+     bool isUncompleted() const {
+         // code() becomes non-null only if this script is complete.
+         // See the comment in JSScript::fullyInitFromEmitter.
+         return !code();
+     }
+ 
+@@ -1305,43 +1312,47 @@ class JSScript : public js::gc::TenuredC
+     // module code) and block-scoped locals (in all kinds of code).
+     size_t nfixed() const {
+         return nfixed_;
+     }
+ 
+     // Number of fixed slots reserved for slots that are always live. Only
+     // nonzero for function or module code.
+     size_t numAlwaysLiveFixedSlots() const {
+-        if (bodyScope()->is<js::FunctionScope>())
++        if (bodyScope()->is<js::FunctionScope>()) {
+             return bodyScope()->as<js::FunctionScope>().nextFrameSlot();
+-        if (bodyScope()->is<js::ModuleScope>())
++        }
++        if (bodyScope()->is<js::ModuleScope>()) {
+             return bodyScope()->as<js::ModuleScope>().nextFrameSlot();
++        }
+         return 0;
+     }
+ 
+     // Calculate the number of fixed slots that are live at a particular bytecode.
+     size_t calculateLiveFixed(jsbytecode* pc);
+ 
+     size_t nslots() const {
+         return nslots_;
+     }
+ 
+     unsigned numArgs() const {
+-        if (bodyScope()->is<js::FunctionScope>())
++        if (bodyScope()->is<js::FunctionScope>()) {
+             return bodyScope()->as<js::FunctionScope>().numPositionalFormalParameters();
++        }
+         return 0;
+     }
+ 
+     inline js::Shape* initialEnvironmentShape() const;
+ 
+     bool functionHasParameterExprs() const {
+         // Only functions have parameters.
+         js::Scope* scope = bodyScope();
+-        if (!scope->is<js::FunctionScope>())
++        if (!scope->is<js::FunctionScope>()) {
+             return false;
++        }
+         return scope->as<js::FunctionScope>().hasParameterExprs();
+     }
+ 
+     size_t nTypeSets() const {
+         return nTypeSets_;
+     }
+ 
+     size_t funLength() const {
+@@ -1671,29 +1682,31 @@ class JSScript : public js::gc::TenuredC
+      * nullptr for global and eval scripts.
+      * The delazifying variant ensures that the function isn't lazy. The
+      * non-delazifying variant must only be used after earlier code has
+      * called ensureNonLazyCanonicalFunction and while the function can't
+      * have been relazified.
+      */
+     inline JSFunction* functionDelazifying() const;
+     JSFunction* functionNonDelazifying() const {
+-        if (bodyScope()->is<js::FunctionScope>())
++        if (bodyScope()->is<js::FunctionScope>()) {
+             return bodyScope()->as<js::FunctionScope>().canonicalFunction();
++        }
+         return nullptr;
+     }
+     /*
+      * De-lazifies the canonical function. Must be called before entering code
+      * that expects the function to be non-lazy.
+      */
+     inline void ensureNonLazyCanonicalFunction();
+ 
+     js::ModuleObject* module() const {
+-        if (bodyScope()->is<js::ModuleScope>())
++        if (bodyScope()->is<js::ModuleScope>()) {
+             return bodyScope()->as<js::ModuleScope>().module();
++        }
+         return nullptr;
+     }
+ 
+     bool isGlobalOrEvalCode() const {
+         return bodyScope()->is<js::GlobalScope>() || bodyScope()->is<js::EvalScope>();
+     }
+     bool isGlobalCode() const {
+         return bodyScope()->is<js::GlobalScope>();
+@@ -1733,18 +1746,19 @@ class JSScript : public js::gc::TenuredC
+     /* Return whether this script was compiled for 'eval' */
+     bool isForEval() const {
+         MOZ_ASSERT_IF(isCachedEval() || isActiveEval(), bodyScope()->is<js::EvalScope>());
+         return isCachedEval() || isActiveEval();
+     }
+ 
+     /* Return whether this is a 'direct eval' script in a function scope. */
+     bool isDirectEvalInFunction() const {
+-        if (!isForEval())
++        if (!isForEval()) {
+             return false;
++        }
+         return bodyScope()->hasOnChain(js::ScopeKind::Function);
+     }
+ 
+     /*
+      * Return whether this script is a top-level script.
+      *
+      * If we evaluate some code which contains a syntax error, then we might
+      * produce a JSScript which has no associated bytecode. Testing with
+@@ -1787,27 +1801,29 @@ class JSScript : public js::gc::TenuredC
+         MOZ_ASSERT_IF(bitFields_.functionHasExtraBodyVarScope_, functionHasParameterExprs());
+         return bitFields_.functionHasExtraBodyVarScope_;
+     }
+ 
+     js::VarScope* functionExtraBodyVarScope() const {
+         MOZ_ASSERT(functionHasExtraBodyVarScope());
+         for (uint32_t i = 0; i < scopes()->length; i++) {
+             js::Scope* scope = getScope(i);
+-            if (scope->kind() == js::ScopeKind::FunctionBodyVar)
++            if (scope->kind() == js::ScopeKind::FunctionBodyVar) {
+                 return &scope->as<js::VarScope>();
++            }
+         }
+         MOZ_CRASH("Function extra body var scope not found");
+     }
+ 
+     bool needsBodyEnvironment() const {
+         for (uint32_t i = 0; i < scopes()->length; i++) {
+             js::Scope* scope = getScope(i);
+-            if (ScopeKindIsInBody(scope->kind()) && scope->hasEnvironment())
++            if (ScopeKindIsInBody(scope->kind()) && scope->hasEnvironment()) {
+                 return true;
++            }
+         }
+         return false;
+     }
+ 
+     inline js::LexicalScope* maybeNamedLambdaScope() const;
+ 
+     js::Scope* enclosingScope() const {
+         return outermostScope()->enclosing();
+@@ -1988,18 +2004,19 @@ class JSScript : public js::gc::TenuredC
+         MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
+         MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPE,
+                    "Did you mean to use lookupScope(pc)?");
+         return getScope(GET_UINT32_INDEX(pc));
+     }
+ 
+     inline JSFunction* getFunction(size_t index);
+     JSFunction* function() const {
+-        if (functionNonDelazifying())
++        if (functionNonDelazifying()) {
+             return functionNonDelazifying();
++        }
+         return nullptr;
+     }
+ 
+     inline js::RegExpObject* getRegExp(size_t index);
+     inline js::RegExpObject* getRegExp(jsbytecode* pc);
+ 
+     const js::Value& getConst(size_t index) {
+         js::ConstArray* arr = consts();
+@@ -2016,22 +2033,24 @@ class JSScript : public js::gc::TenuredC
+     js::Scope* innermostScope() { return innermostScope(main()); }
+ 
+     /*
+      * The isEmpty method tells whether this script has code that computes any
+      * result (not return value, result AKA normal completion value) other than
+      * JSVAL_VOID, or any other effects.
+      */
+     bool isEmpty() const {
+-        if (length() > 3)
++        if (length() > 3) {
+             return false;
++        }
+ 
+         jsbytecode* pc = code();
+-        if (noScriptRval() && JSOp(*pc) == JSOP_FALSE)
++        if (noScriptRval() && JSOp(*pc) == JSOP_FALSE) {
+             ++pc;
++        }
+         return JSOp(*pc) == JSOP_RETRVAL;
+     }
+ 
+     bool formalIsAliased(unsigned argSlot);
+     bool formalLivesInArgumentsObject(unsigned argSlot);
+ 
+   private:
+     /* Change this->stepMode to |newValue|. */
+diff --git a/js/src/vm/JSScript.h.1488698-7.later b/js/src/vm/JSScript.h.1488698-7.later
+new file mode 100644
+--- /dev/null
++++ b/js/src/vm/JSScript.h.1488698-7.later
+@@ -0,0 +1,53 @@
++--- JSScript.h
+++++ JSScript.h
++@@ -536,18 +536,19 @@ class ScriptSource
++ 
++     ~ScriptSource() {
++         MOZ_ASSERT(refs == 0);
++     }
++ 
++     void incref() { refs++; }
++     void decref() {
++         MOZ_ASSERT(refs != 0);
++-        if (--refs == 0)
+++        if (--refs == 0) {
++             js_delete(this);
+++        }
++     }
++     MOZ_MUST_USE bool initFromOptions(JSContext* cx,
++                                       const JS::ReadOnlyCompileOptions& options,
++                                       const mozilla::Maybe<uint32_t>& parameterListEnd = mozilla::Nothing());
++     MOZ_MUST_USE bool setSourceCopy(JSContext* cx, JS::SourceBufferHolder& srcBuf);
++     void setSourceRetrievable() { sourceRetrievable_ = true; }
++     bool sourceRetrievable() const { return sourceRetrievable_; }
++     bool hasSourceData() const { return !data.is<Missing>(); }
++@@ -876,22 +883,25 @@ struct ScriptBytecodeHasher
++         ~Lookup();
++     };
++ 
++     static HashNumber hash(const Lookup& l) {
++         return l.hash;
++     }
++     static bool match(SharedScriptData* entry, const Lookup& lookup) {
++         const SharedScriptData* data = lookup.scriptData;
++-        if (entry->natoms() != data->natoms())
+++        if (entry->natoms() != data->natoms()) {
+++            return false;
+++        }
+++        if (entry->codeLength() != data->codeLength()) {
++             return false;
++-        if (entry->codeLength() != data->codeLength())
+++        }
+++        if (entry->numNotes() != data->numNotes()) {
++             return false;
++-        if (entry->numNotes() != data->numNotes())
++-            return false;
+++        }
++         return mozilla::ArrayEqual<uint8_t>(entry->data(), data->data(), data->dataLength());
++     }
++ };
++ 
++ class AutoLockScriptData;
++ 
++ using ScriptDataTable = HashSet<SharedScriptData*,
++                                 ScriptBytecodeHasher,

+ 26 - 26
frg/work-js/mozilla-release/patches/1499140-1-64a1.patch

@@ -2,7 +2,7 @@
 # User Jon Coppeard <jcoppeard@mozilla.com>
 # User Jon Coppeard <jcoppeard@mozilla.com>
 # Date 1540204096 -3600
 # Date 1540204096 -3600
 # Node ID 430db29f46858faff930e2ec3ed45fbf13a73a20
 # Node ID 430db29f46858faff930e2ec3ed45fbf13a73a20
-# Parent  36b67822ed118c69cfd6db466edacf8e221f0ad2
+# Parent  220b83ea1d722c383a90338dad8a64617eaf3da0
 Bug 1499140 - Implement support for dynamic module import in the interpreter r=jandem
 Bug 1499140 - Implement support for dynamic module import in the interpreter r=jandem
 
 
 diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
 diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
@@ -153,7 +153,7 @@ diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h
 diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
 diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
 --- a/js/src/builtin/Promise.cpp
 --- a/js/src/builtin/Promise.cpp
 +++ b/js/src/builtin/Promise.cpp
 +++ b/js/src/builtin/Promise.cpp
-@@ -2491,16 +2491,26 @@ js::OriginalPromiseThen(JSContext* cx, H
+@@ -2524,16 +2524,26 @@ js::OriginalPromiseThen(JSContext* cx, H
      // Step 5.
      // Step 5.
      if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise, resolve, reject))
      if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise, resolve, reject))
          return false;
          return false;
@@ -205,12 +205,12 @@ diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h
 diff --git a/js/src/builtin/Stream.cpp b/js/src/builtin/Stream.cpp
 diff --git a/js/src/builtin/Stream.cpp b/js/src/builtin/Stream.cpp
 --- a/js/src/builtin/Stream.cpp
 --- a/js/src/builtin/Stream.cpp
 +++ b/js/src/builtin/Stream.cpp
 +++ b/js/src/builtin/Stream.cpp
-@@ -279,25 +279,16 @@ ReportArgTypeError(JSContext* cx, const 
-         return false;
+@@ -277,25 +277,16 @@ ReportArgTypeError(JSContext* cx, const 
+     if (!bytes)
+         return;
  
  
-     return JS_ReportErrorFlagsAndNumberLatin1(cx, JSREPORT_ERROR, GetErrorMessage,
-                                               nullptr, JSMSG_NOT_EXPECTED_TYPE,
-                                               funName, expectedType, bytes.get());
+     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, funName,
+                                expectedType, bytes.get());
  }
  }
  
  
  static MOZ_MUST_USE bool
  static MOZ_MUST_USE bool
@@ -231,7 +231,7 @@ diff --git a/js/src/builtin/Stream.cpp b/js/src/builtin/Stream.cpp
  
  
      args.rval().setObject(*promise);
      args.rval().setObject(*promise);
      return true;
      return true;
-@@ -1139,18 +1130,19 @@ ReadableStreamTee_Cancel(JSContext* cx, 
+@@ -1137,18 +1128,19 @@ ReadableStreamTee_Cancel(JSContext* cx, 
          compositeReason->initDenseElement(1, teeState->reason2());
          compositeReason->initDenseElement(1, teeState->reason2());
          RootedValue compositeReasonVal(cx, ObjectValue(*compositeReason));
          RootedValue compositeReasonVal(cx, ObjectValue(*compositeReason));
  
  
@@ -255,7 +255,7 @@ diff --git a/js/src/builtin/Stream.cpp b/js/src/builtin/Stream.cpp
 diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
 diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
 --- a/js/src/frontend/BytecodeEmitter.cpp
 --- a/js/src/frontend/BytecodeEmitter.cpp
 +++ b/js/src/frontend/BytecodeEmitter.cpp
 +++ b/js/src/frontend/BytecodeEmitter.cpp
-@@ -8297,18 +8297,24 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+@@ -8296,18 +8296,24 @@ BytecodeEmitter::emitTree(ParseNode* pn,
          break;
          break;
  
  
        case ParseNodeKind::ImportMeta:
        case ParseNodeKind::ImportMeta:
@@ -285,7 +285,7 @@ diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitt
 diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
 diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
 --- a/js/src/frontend/FullParseHandler.h
 --- a/js/src/frontend/FullParseHandler.h
 +++ b/js/src/frontend/FullParseHandler.h
 +++ b/js/src/frontend/FullParseHandler.h
-@@ -556,17 +556,17 @@ class FullParseHandler
+@@ -557,17 +557,17 @@ class FullParseHandler
          return new_<NullaryNode>(ParseNodeKind::ExportBatchSpec, JSOP_NOP, pos);
          return new_<NullaryNode>(ParseNodeKind::ExportBatchSpec, JSOP_NOP, pos);
      }
      }
  
  
@@ -307,7 +307,7 @@ diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandl
 diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
 diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
 --- a/js/src/frontend/Parser.cpp
 --- a/js/src/frontend/Parser.cpp
 +++ b/js/src/frontend/Parser.cpp
 +++ b/js/src/frontend/Parser.cpp
-@@ -9957,17 +9957,17 @@ GeneralParser<ParseHandler, CharT>::impo
+@@ -10056,17 +10056,17 @@ GeneralParser<ParseHandler, CharT>::impo
          return handler.newImportMeta(importHolder, metaHolder);
          return handler.newImportMeta(importHolder, metaHolder);
      } else if (next == TokenKind::LeftParen) {
      } else if (next == TokenKind::LeftParen) {
          Node arg = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
          Node arg = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
@@ -351,7 +351,7 @@ diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
 diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
 diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
 --- a/js/src/jit/IonBuilder.cpp
 --- a/js/src/jit/IonBuilder.cpp
 +++ b/js/src/jit/IonBuilder.cpp
 +++ b/js/src/jit/IonBuilder.cpp
-@@ -2441,16 +2441,17 @@ IonBuilder::inspectOpcode(JSOp op)
+@@ -2437,16 +2437,17 @@ IonBuilder::inspectOpcode(JSOp op)
        // Misc
        // Misc
        case JSOP_DELNAME:
        case JSOP_DELNAME:
        case JSOP_FINALLY:
        case JSOP_FINALLY:
@@ -372,7 +372,7 @@ diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
 diff --git a/js/src/js.msg b/js/src/js.msg
 diff --git a/js/src/js.msg b/js/src/js.msg
 --- a/js/src/js.msg
 --- a/js/src/js.msg
 +++ b/js/src/js.msg
 +++ b/js/src/js.msg
-@@ -593,16 +593,17 @@ MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT,       
+@@ -600,16 +600,17 @@ MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT,       
  MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT,   0, JSEXN_SYNTAXERR, "indirect export not found")
  MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT,   0, JSEXN_SYNTAXERR, "indirect export not found")
  MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "ambiguous indirect export")
  MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "ambiguous indirect export")
  MSG_DEF(JSMSG_MISSING_IMPORT,            0, JSEXN_SYNTAXERR, "import not found")
  MSG_DEF(JSMSG_MISSING_IMPORT,            0, JSEXN_SYNTAXERR, "import not found")
@@ -393,7 +393,7 @@ diff --git a/js/src/js.msg b/js/src/js.msg
 diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
 diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
 --- a/js/src/jsapi.cpp
 --- a/js/src/jsapi.cpp
 +++ b/js/src/jsapi.cpp
 +++ b/js/src/jsapi.cpp
-@@ -4879,16 +4879,41 @@ JS::GetModuleMetadataHook(JSRuntime* rt)
+@@ -4831,16 +4831,41 @@ JS::GetModuleMetadataHook(JSRuntime* rt)
  
  
  JS_PUBLIC_API(void)
  JS_PUBLIC_API(void)
  JS::SetModuleMetadataHook(JSRuntime* rt, JS::ModuleMetadataHook func)
  JS::SetModuleMetadataHook(JSRuntime* rt, JS::ModuleMetadataHook func)
@@ -438,7 +438,7 @@ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
 diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 --- a/js/src/jsapi.h
 --- a/js/src/jsapi.h
 +++ b/js/src/jsapi.h
 +++ b/js/src/jsapi.h
-@@ -3890,16 +3890,35 @@ GetModuleMetadataHook(JSRuntime* rt);
+@@ -3829,16 +3829,35 @@ GetModuleMetadataHook(JSRuntime* rt);
  
  
  /**
  /**
   * Set the hook for populating the import.meta metadata object to the given
   * Set the hook for populating the import.meta metadata object to the given
@@ -477,7 +477,7 @@ diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 --- a/js/src/shell/js.cpp
 --- a/js/src/shell/js.cpp
 +++ b/js/src/shell/js.cpp
 +++ b/js/src/shell/js.cpp
-@@ -4284,17 +4284,17 @@ SetModuleResolveHook(JSContext* cx, unsi
+@@ -4300,17 +4300,17 @@ SetModuleResolveHook(JSContext* cx, unsi
      Handle<GlobalObject*> global = cx->global();
      Handle<GlobalObject*> global = cx->global();
      global->setReservedSlot(GlobalAppSlotModuleResolveHook, args[0]);
      global->setReservedSlot(GlobalAppSlotModuleResolveHook, args[0]);
  
  
@@ -496,7 +496,7 @@ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
          return nullptr;
          return nullptr;
      }
      }
      MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
      MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
-@@ -9280,17 +9280,17 @@ main(int argc, char** argv, char** envp)
+@@ -9301,17 +9301,17 @@ main(int argc, char** argv, char** envp)
          JS_SetGCParameter(cx, JSGC_DYNAMIC_HEAP_GROWTH, 1);
          JS_SetGCParameter(cx, JSGC_DYNAMIC_HEAP_GROWTH, 1);
          JS_SetGCParameter(cx, JSGC_DYNAMIC_MARK_SLICE, 1);
          JS_SetGCParameter(cx, JSGC_DYNAMIC_MARK_SLICE, 1);
          JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET, 10);
          JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET, 10);
@@ -518,7 +518,7 @@ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp
 diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp
 --- a/js/src/vm/EnvironmentObject.cpp
 --- a/js/src/vm/EnvironmentObject.cpp
 +++ b/js/src/vm/EnvironmentObject.cpp
 +++ b/js/src/vm/EnvironmentObject.cpp
-@@ -3245,16 +3245,33 @@ js::GetModuleObjectForScript(JSScript* s
+@@ -3246,16 +3246,33 @@ js::GetModuleObjectForScript(JSScript* s
  {
  {
      for (ScopeIter si(script); si; si++) {
      for (ScopeIter si(script); si; si++) {
          if (si.kind() == ScopeKind::Module)
          if (si.kind() == ScopeKind::Module)
@@ -555,7 +555,7 @@ diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp
 diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h
 diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h
 --- a/js/src/vm/EnvironmentObject.h
 --- a/js/src/vm/EnvironmentObject.h
 +++ b/js/src/vm/EnvironmentObject.h
 +++ b/js/src/vm/EnvironmentObject.h
-@@ -1161,17 +1161,21 @@ IsFrameInitialEnvironment(AbstractFrameP
+@@ -1164,17 +1164,21 @@ IsFrameInitialEnvironment(AbstractFrameP
      return false;
      return false;
  }
  }
  
  
@@ -581,7 +581,7 @@ diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h
 diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
 diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
 --- a/js/src/vm/Interpreter.cpp
 --- a/js/src/vm/Interpreter.cpp
 +++ b/js/src/vm/Interpreter.cpp
 +++ b/js/src/vm/Interpreter.cpp
-@@ -4321,17 +4321,33 @@ CASE(JSOP_IMPORTMETA)
+@@ -4318,17 +4318,33 @@ CASE(JSOP_IMPORTMETA)
      MOZ_ASSERT(module);
      MOZ_ASSERT(module);
  
  
      JSObject* metaObject = GetOrCreateModuleMetaObject(cx, module);
      JSObject* metaObject = GetOrCreateModuleMetaObject(cx, module);
@@ -619,7 +619,7 @@ diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
 diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
 diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
 --- a/js/src/vm/Opcodes.h
 --- a/js/src/vm/Opcodes.h
 +++ b/js/src/vm/Opcodes.h
 +++ b/js/src/vm/Opcodes.h
-@@ -2358,24 +2358,33 @@ 1234567890123456789012345678901234567890
+@@ -2357,24 +2357,33 @@ 1234567890123456789012345678901234567890
      /*
      /*
       * Push "import.meta"
       * Push "import.meta"
       *
       *
@@ -658,7 +658,7 @@ diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
 diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
 diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
 --- a/js/src/vm/Runtime.cpp
 --- a/js/src/vm/Runtime.cpp
 +++ b/js/src/vm/Runtime.cpp
 +++ b/js/src/vm/Runtime.cpp
-@@ -171,17 +171,18 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
+@@ -173,17 +173,18 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
      oomCallback(nullptr),
      oomCallback(nullptr),
      debuggerMallocSizeOf(ReturnZeroSize),
      debuggerMallocSizeOf(ReturnZeroSize),
      lastAnimationTime(0),
      lastAnimationTime(0),
@@ -681,7 +681,7 @@ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
 diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
 diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
 --- a/js/src/vm/Runtime.h
 --- a/js/src/vm/Runtime.h
 +++ b/js/src/vm/Runtime.h
 +++ b/js/src/vm/Runtime.h
-@@ -935,16 +935,20 @@ struct JSRuntime : public js::MallocProv
+@@ -936,16 +936,20 @@ struct JSRuntime : public js::MallocProv
  
  
      // The implementation-defined abstract operation HostResolveImportedModule.
      // The implementation-defined abstract operation HostResolveImportedModule.
      js::MainThreadData<JS::ModuleResolveHook> moduleResolveHook;
      js::MainThreadData<JS::ModuleResolveHook> moduleResolveHook;
@@ -705,7 +705,7 @@ diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
 diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp
 diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp
 --- a/js/src/vm/Scope.cpp
 --- a/js/src/vm/Scope.cpp
 +++ b/js/src/vm/Scope.cpp
 +++ b/js/src/vm/Scope.cpp
-@@ -1201,22 +1201,16 @@ ModuleScope::createWithData(JSContext* c
+@@ -1204,22 +1204,16 @@ ModuleScope::createWithData(JSContext* c
  
  
  /* static */ Shape*
  /* static */ Shape*
  ModuleScope::getEmptyEnvironmentShape(JSContext* cx)
  ModuleScope::getEmptyEnvironmentShape(JSContext* cx)
@@ -753,7 +753,7 @@ diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h
 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 --- a/js/src/vm/SelfHosting.cpp
 --- a/js/src/vm/SelfHosting.cpp
 +++ b/js/src/vm/SelfHosting.cpp
 +++ b/js/src/vm/SelfHosting.cpp
-@@ -2151,25 +2151,18 @@ intrinsic_NameForTypedArray(JSContext* c
+@@ -2153,25 +2153,18 @@ intrinsic_NameForTypedArray(JSContext* c
  static bool
  static bool
  intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, Value* vp)
  intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, Value* vp)
  {
  {
@@ -783,7 +783,7 @@ diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
 diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
 --- a/js/src/vm/Stack.cpp
 --- a/js/src/vm/Stack.cpp
 +++ b/js/src/vm/Stack.cpp
 +++ b/js/src/vm/Stack.cpp
-@@ -146,23 +146,21 @@ AssertScopeMatchesEnvironment(Scope* sco
+@@ -147,23 +147,21 @@ AssertScopeMatchesEnvironment(Scope* sco
                  env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
                  env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
                  MOZ_ASSERT(env->is<GlobalObject>());
                  MOZ_ASSERT(env->is<GlobalObject>());
                  break;
                  break;

+ 6 - 3
frg/work-js/mozilla-release/patches/1504727-65a1.patch

@@ -2,7 +2,7 @@
 # User Matthew Gaudet <mgaudet@mozilla.com>
 # User Matthew Gaudet <mgaudet@mozilla.com>
 # Date 1541435389 0
 # Date 1541435389 0
 # Node ID 2030d86b1ee8efd585ceb245eba10d3dcf124e7f
 # Node ID 2030d86b1ee8efd585ceb245eba10d3dcf124e7f
-# Parent  cd7182796f6841250b78eb74f6074b44de5866d8
+# Parent  f510e1544d9c238ca38a79d2a6c95174386ee5fd
 Bug 1504727: Remove last vestiges of C1 Spewer r=IainIreland
 Bug 1504727: Remove last vestiges of C1 Spewer r=IainIreland
 
 
 Differential Revision: https://phabricator.services.mozilla.com/D10923
 Differential Revision: https://phabricator.services.mozilla.com/D10923
@@ -10,7 +10,7 @@ Differential Revision: https://phabricator.services.mozilla.com/D10923
 diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp
 diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp
 --- a/js/src/jit/JitSpewer.cpp
 --- a/js/src/jit/JitSpewer.cpp
 +++ b/js/src/jit/JitSpewer.cpp
 +++ b/js/src/jit/JitSpewer.cpp
-@@ -43,17 +43,16 @@
+@@ -38,17 +38,16 @@
  
  
  using namespace js;
  using namespace js;
  using namespace js::jit;
  using namespace js::jit;
@@ -28,7 +28,7 @@ diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp
      void release();
      void release();
  
  
    public:
    public:
-@@ -150,56 +149,44 @@ jit::EnableIonDebugAsyncLogging()
+@@ -147,59 +146,47 @@ jit::EnableIonDebugAsyncLogging()
  {
  {
      ionspewer.init();
      ionspewer.init();
      ionspewer.setAsyncLogging(true);
      ionspewer.setAsyncLogging(true);
@@ -50,6 +50,9 @@ diff --git a/js/src/jit/JitSpewer.cpp b/js/src/jit/JitSpewer.cpp
      if (inited_)
      if (inited_)
          return true;
          return true;
  
  
+     // Filter expression for spewing
+     gSpewFilter = getenv("IONFILTER");
+ 
      const size_t bufferLength = 256;
      const size_t bufferLength = 256;
 -    char c1Buffer[bufferLength];
 -    char c1Buffer[bufferLength];
      char jsonBuffer[bufferLength];
      char jsonBuffer[bufferLength];

+ 84 - 50
frg/work-js/mozilla-release/patches/1539780-70a1.patch

@@ -2,7 +2,7 @@
 # User Andre Bargull <andre.bargull@gmail.com>
 # User Andre Bargull <andre.bargull@gmail.com>
 # Date 1565379799 0
 # Date 1565379799 0
 # Node ID 5967a3947ad0eeeecd97099e50655765259db28a
 # Node ID 5967a3947ad0eeeecd97099e50655765259db28a
-# Parent  4a482a17231ef7536a278ae6a7fd1fbd9e269e8f
+# Parent  5e3cf437ac9587823313209fcbe8a8625cb39a4a
 Bug 1539780: Remove "--with-intl-api=build" build config option. r=jwalden
 Bug 1539780: Remove "--with-intl-api=build" build config option. r=jwalden
 
 
 There are about the same number of occurrences of "ENABLE_INTL_API" and "EXPOSE_INTL_API"
 There are about the same number of occurrences of "ENABLE_INTL_API" and "EXPOSE_INTL_API"
@@ -67,6 +67,28 @@ diff --git a/build/autoconf/icu.m4 b/build/autoconf/icu.m4
  if test -n "$USE_ICU"; then
  if test -n "$USE_ICU"; then
      icudir="$_topsrcdir/intl/icu/source"
      icudir="$_topsrcdir/intl/icu/source"
      if test ! -d "$icudir"; then
      if test ! -d "$icudir"; then
+diff --git a/js/public/LocaleSensitive.h b/js/public/LocaleSensitive.h
+--- a/js/public/LocaleSensitive.h
++++ b/js/public/LocaleSensitive.h
+@@ -63,17 +63,17 @@ using JSLocaleCompare =
+ using JSLocaleToUnicode =
+     bool (*)(JSContext* cx, const char* src, JS::MutableHandle<JS::Value> rval);
+ 
+ /**
+  * A suite of locale-specific string conversion and error message callbacks
+  * used to implement locale-sensitive behaviors (such as those performed by
+  * the various toLocaleString and toLocale{Date,Time}String functions).
+  *
+- * If SpiderMonkey is compiled --with-intl-api, then #if EXPOSE_INTL_API.  In
++ * If SpiderMonkey is compiled --with-intl-api, then #if ENABLE_INTL_API.  In
+  * this case, SpiderMonkey itself will implement ECMA-402-compliant behavior by
+  * calling on ICU, and none of the fields in this struct will ever be used.
+  * (You'll still be able to call the get/set-callbacks functions; they just
+  * won't affect JavaScript semantics.)
+  */
+ struct JSLocaleCallbacks
+ {
+     JSLocaleToUpperCase localeToUpperCase;
 diff --git a/js/public/ProtoKey.h b/js/public/ProtoKey.h
 diff --git a/js/public/ProtoKey.h b/js/public/ProtoKey.h
 --- a/js/public/ProtoKey.h
 --- a/js/public/ProtoKey.h
 +++ b/js/public/ProtoKey.h
 +++ b/js/public/ProtoKey.h
@@ -133,7 +155,26 @@ diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js
 diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
 diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
 --- a/js/src/builtin/String.cpp
 --- a/js/src/builtin/String.cpp
 +++ b/js/src/builtin/String.cpp
 +++ b/js/src/builtin/String.cpp
-@@ -1068,17 +1068,17 @@ js::intl_toLocaleLowerCase(JSContext* cx
+@@ -27,17 +27,17 @@
+ 
+ #include "builtin/Array.h"
+ #include "builtin/Boolean.h"
+ #include "builtin/intl/CommonFunctions.h"
+ #include "builtin/intl/ICUStubs.h"
+ #include "builtin/RegExp.h"
+ #include "jit/InlinableNatives.h"
+ #include "js/Conversions.h"
+-#if !EXPOSE_INTL_API
++#if !ENABLE_INTL_API
+ #include "js/LocaleSensitive.h"
+ #endif
+ #include "js/StableStringChars.h"
+ #include "js/UniquePtr.h"
+ #if ENABLE_INTL_API
+ # include "unicode/uchar.h"
+ # include "unicode/unorm2.h"
+ #endif
+@@ -1071,17 +1071,17 @@ js::intl_toLocaleLowerCase(JSContext* cx
      JSString* result = NewStringCopyN<CanGC>(cx, chars.begin(), size);
      JSString* result = NewStringCopyN<CanGC>(cx, chars.begin(), size);
      if (!result)
      if (!result)
          return false;
          return false;
@@ -152,7 +193,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  
  
  bool
  bool
  js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
  js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
-@@ -1109,17 +1109,17 @@ js::str_toLocaleLowerCase(JSContext* cx,
+@@ -1112,17 +1112,17 @@ js::str_toLocaleLowerCase(JSContext* cx,
      JSString* result = StringToLowerCase(cx, linear);
      JSString* result = StringToLowerCase(cx, linear);
      if (!result)
      if (!result)
          return false;
          return false;
@@ -171,7 +212,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
      // special casing rules, so detect it inline.
      // special casing rules, so detect it inline.
      bool hasUpperCaseSpecialCasing = charCode == unicode::LATIN_SMALL_LETTER_SHARP_S;
      bool hasUpperCaseSpecialCasing = charCode == unicode::LATIN_SMALL_LETTER_SHARP_S;
      MOZ_ASSERT(hasUpperCaseSpecialCasing == unicode::ChangesWhenUpperCasedSpecialCasing(charCode));
      MOZ_ASSERT(hasUpperCaseSpecialCasing == unicode::ChangesWhenUpperCasedSpecialCasing(charCode));
-@@ -1466,17 +1466,17 @@ js::intl_toLocaleUpperCase(JSContext* cx
+@@ -1469,17 +1469,17 @@ js::intl_toLocaleUpperCase(JSContext* cx
      JSString* result = NewStringCopyN<CanGC>(cx, chars.begin(), size);
      JSString* result = NewStringCopyN<CanGC>(cx, chars.begin(), size);
      if (!result)
      if (!result)
          return false;
          return false;
@@ -190,7 +231,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  
  
  bool
  bool
  js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
  js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
-@@ -1507,19 +1507,19 @@ js::str_toLocaleUpperCase(JSContext* cx,
+@@ -1510,19 +1510,19 @@ js::str_toLocaleUpperCase(JSContext* cx,
      JSString* result = StringToUpperCase(cx, linear);
      JSString* result = StringToUpperCase(cx, linear);
      if (!result)
      if (!result)
          return false;
          return false;
@@ -213,7 +254,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  bool
  bool
  js::str_localeCompare(JSContext* cx, unsigned argc, Value* vp)
  js::str_localeCompare(JSContext* cx, unsigned argc, Value* vp)
  {
  {
-@@ -1544,19 +1544,19 @@ js::str_localeCompare(JSContext* cx, uns
+@@ -1547,19 +1547,19 @@ js::str_localeCompare(JSContext* cx, uns
      int32_t result;
      int32_t result;
      if (!CompareStrings(cx, str, thatStr, &result))
      if (!CompareStrings(cx, str, thatStr, &result))
          return false;
          return false;
@@ -236,7 +277,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  {
  {
      CallArgs args = CallArgsFromVp(argc, vp);
      CallArgs args = CallArgsFromVp(argc, vp);
  
  
-@@ -1671,17 +1671,17 @@ js::str_normalize(JSContext* cx, unsigne
+@@ -1674,17 +1674,17 @@ js::str_normalize(JSContext* cx, unsigne
      if (!ns)
      if (!ns)
          return false;
          return false;
  
  
@@ -255,7 +296,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  
  
      RootedString str(cx);
      RootedString str(cx);
      size_t i;
      size_t i;
-@@ -3548,27 +3548,27 @@ static const JSFunctionSpec string_metho
+@@ -3551,27 +3551,27 @@ static const JSFunctionSpec string_metho
      JS_FN("includes",          str_includes,          1,0),
      JS_FN("includes",          str_includes,          1,0),
      JS_FN("indexOf",           str_indexOf,           1,0),
      JS_FN("indexOf",           str_indexOf,           1,0),
      JS_FN("lastIndexOf",       str_lastIndexOf,       1,0),
      JS_FN("lastIndexOf",       str_lastIndexOf,       1,0),
@@ -285,7 +326,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
      JS_SELF_HOSTED_FN("search", "String_search",      1,0),
      JS_SELF_HOSTED_FN("search", "String_search",      1,0),
      JS_SELF_HOSTED_FN("replace", "String_replace",    2,0),
      JS_SELF_HOSTED_FN("replace", "String_replace",    2,0),
      JS_SELF_HOSTED_FN("replaceAll", "String_replaceAll", 2, 0),
      JS_SELF_HOSTED_FN("replaceAll", "String_replaceAll", 2, 0),
-@@ -3854,17 +3854,17 @@ static const JSFunctionSpec string_stati
+@@ -3857,17 +3857,17 @@ static const JSFunctionSpec string_stati
      JS_SELF_HOSTED_FN("lastIndexOf",     "String_static_lastIndexOf",   2,0),
      JS_SELF_HOSTED_FN("lastIndexOf",     "String_static_lastIndexOf",   2,0),
      JS_SELF_HOSTED_FN("startsWith",      "String_static_startsWith",    2,0),
      JS_SELF_HOSTED_FN("startsWith",      "String_static_startsWith",    2,0),
      JS_SELF_HOSTED_FN("endsWith",        "String_static_endsWith",      2,0),
      JS_SELF_HOSTED_FN("endsWith",        "String_static_endsWith",      2,0),
@@ -523,36 +564,10 @@ diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js
          // Step 9.e.
          // Step 9.e.
          R = S + R;
          R = S + R;
      }
      }
-diff --git a/js/src/jsapi.h b/js/src/jsapi.h
---- a/js/src/jsapi.h
-+++ b/js/src/jsapi.h
-@@ -4837,19 +4837,19 @@ JS_GetDefaultLocale(JSContext* cx);
-  */
- extern JS_PUBLIC_API(void)
- JS_ResetDefaultLocale(JSRuntime* rt);
- 
- /**
-  * Locale specific string conversion and error message callbacks.
-  */
- struct JSLocaleCallbacks {
--    JSLocaleToUpperCase     localeToUpperCase; // not used #if EXPOSE_INTL_API
--    JSLocaleToLowerCase     localeToLowerCase; // not used #if EXPOSE_INTL_API
--    JSLocaleCompare         localeCompare; // not used #if EXPOSE_INTL_API
-+    JSLocaleToUpperCase     localeToUpperCase; // not used #if ENABLE_INTL_API
-+    JSLocaleToLowerCase     localeToLowerCase; // not used #if ENABLE_INTL_API
-+    JSLocaleCompare         localeCompare; // not used #if ENABLE_INTL_API
-     JSLocaleToUnicode       localeToUnicode;
- };
- 
- /**
-  * Establish locale callbacks. The pointer must persist as long as the
-  * JSContext.  Passing nullptr restores the default behaviour.
-  */
- extern JS_PUBLIC_API(void)
 diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
 diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
 --- a/js/src/jsdate.cpp
 --- a/js/src/jsdate.cpp
 +++ b/js/src/jsdate.cpp
 +++ b/js/src/jsdate.cpp
-@@ -2879,17 +2879,17 @@ FormatDate(JSContext* cx, double utcTime
+@@ -2878,17 +2878,17 @@ FormatDate(JSContext* cx, double utcTime
          if (!str)
          if (!str)
              return false;
              return false;
      }
      }
@@ -571,7 +586,7 @@ diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
      char buf[100];
      char buf[100];
      if (!IsFinite(utcTime)) {
      if (!IsFinite(utcTime)) {
          strcpy(buf, js_InvalidDate_str);
          strcpy(buf, js_InvalidDate_str);
-@@ -2993,17 +2993,17 @@ date_toLocaleTimeString_impl(JSContext* 
+@@ -2992,17 +2992,17 @@ date_toLocaleTimeString_impl(JSContext* 
  }
  }
  
  
  static bool
  static bool
@@ -590,7 +605,7 @@ diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
      return FormatDate(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
      return FormatDate(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
                        FormatSpec::Time, args.rval());
                        FormatSpec::Time, args.rval());
  }
  }
-@@ -3149,17 +3149,17 @@ static const JSFunctionSpec date_methods
+@@ -3148,17 +3148,17 @@ static const JSFunctionSpec date_methods
      JS_FN("setUTCHours",         date_setUTCHours,        4,0),
      JS_FN("setUTCHours",         date_setUTCHours,        4,0),
      JS_FN("setMinutes",          date_setMinutes,         3,0),
      JS_FN("setMinutes",          date_setMinutes,         3,0),
      JS_FN("setUTCMinutes",       date_setUTCMinutes,      3,0),
      JS_FN("setUTCMinutes",       date_setUTCMinutes,      3,0),
@@ -612,7 +627,26 @@ diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
 diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
 diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
 --- a/js/src/jsnum.cpp
 --- a/js/src/jsnum.cpp
 +++ b/js/src/jsnum.cpp
 +++ b/js/src/jsnum.cpp
-@@ -770,17 +770,17 @@ num_toString_impl(JSContext* cx, const C
+@@ -23,17 +23,17 @@
+ #include <math.h>
+ #include <string.h>
+ 
+ #include "jstypes.h"
+ 
+ #include "builtin/String.h"
+ #include "double-conversion/double-conversion.h"
+ #include "js/Conversions.h"
+-#if !EXPOSE_INTL_API
++#if !ENABLE_INTL_API
+ #include "js/LocaleSensitive.h"
+ #endif
+ #include "util/DoubleToString.h"
+ #include "util/StringBuffer.h"
+ #ifdef ENABLE_BIGINT
+ #include "vm/BigIntType.h"
+ #endif
+ #include "vm/GlobalObject.h"
+@@ -773,17 +773,17 @@ num_toString_impl(JSContext* cx, const C
  
  
  bool
  bool
  js::num_toString(JSContext* cx, unsigned argc, Value* vp)
  js::num_toString(JSContext* cx, unsigned argc, Value* vp)
@@ -631,7 +665,7 @@ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
      double d = Extract(args.thisv());
      double d = Extract(args.thisv());
  
  
      RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
      RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
-@@ -903,17 +903,17 @@ num_toLocaleString_impl(JSContext* cx, c
+@@ -906,17 +906,17 @@ num_toLocaleString_impl(JSContext* cx, c
  }
  }
  
  
  static bool
  static bool
@@ -650,7 +684,7 @@ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
      args.rval().setNumber(Extract(args.thisv()));
      args.rval().setNumber(Extract(args.thisv()));
      return true;
      return true;
  }
  }
-@@ -1122,17 +1122,17 @@ num_toPrecision(JSContext* cx, unsigned 
+@@ -1125,17 +1125,17 @@ num_toPrecision(JSContext* cx, unsigned 
  {
  {
      CallArgs args = CallArgsFromVp(argc, vp);
      CallArgs args = CallArgsFromVp(argc, vp);
      return CallNonGenericMethod<IsNumber, num_toPrecision_impl>(cx, args);
      return CallNonGenericMethod<IsNumber, num_toPrecision_impl>(cx, args);
@@ -669,7 +703,7 @@ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
      JS_FN("toFixed",             num_toFixed,           1, 0),
      JS_FN("toFixed",             num_toFixed,           1, 0),
      JS_FN("toExponential",       num_toExponential,     1, 0),
      JS_FN("toExponential",       num_toExponential,     1, 0),
      JS_FN("toPrecision",         num_toPrecision,       1, 0),
      JS_FN("toPrecision",         num_toPrecision,       1, 0),
-@@ -1153,20 +1153,20 @@ static const JSFunctionSpec number_stati
+@@ -1156,20 +1156,20 @@ static const JSFunctionSpec number_stati
      JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1,0),
      JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1,0),
      JS_FS_END
      JS_FS_END
  };
  };
@@ -692,7 +726,7 @@ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
      struct lconv* locale = localeconv();
      struct lconv* locale = localeconv();
      thousandsSeparator = locale->thousands_sep;
      thousandsSeparator = locale->thousands_sep;
      decimalPoint = locale->decimal_point;
      decimalPoint = locale->decimal_point;
-@@ -1202,21 +1202,21 @@ js::InitRuntimeNumberState(JSRuntime* rt
+@@ -1205,21 +1205,21 @@ js::InitRuntimeNumberState(JSRuntime* rt
      storage += thousandsSeparatorSize;
      storage += thousandsSeparatorSize;
  
  
      js_memcpy(storage, decimalPoint, decimalPointSize);
      js_memcpy(storage, decimalPoint, decimalPointSize);
@@ -741,7 +775,7 @@ diff --git a/js/src/jsnum.h b/js/src/jsnum.h
 diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp
 diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp
 --- a/js/src/vm/Initialization.cpp
 --- a/js/src/vm/Initialization.cpp
 +++ b/js/src/vm/Initialization.cpp
 +++ b/js/src/vm/Initialization.cpp
-@@ -117,22 +117,22 @@ JS::detail::InitWithFailureDiagnostic(bo
+@@ -119,22 +119,22 @@ JS::detail::InitWithFailureDiagnostic(bo
      RETURN_IF_FAIL(js::jit::InitializeIon());
      RETURN_IF_FAIL(js::jit::InitializeIon());
  
  
      RETURN_IF_FAIL(js::InitDateTimeState());
      RETURN_IF_FAIL(js::InitDateTimeState());
@@ -766,7 +800,7 @@ diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp
  #ifdef JS_SIMULATOR
  #ifdef JS_SIMULATOR
      RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
      RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
  #endif
  #endif
-@@ -187,19 +187,19 @@ JS_ShutDown(void)
+@@ -189,19 +189,19 @@ JS_ShutDown(void)
      // PRMJ_Now subsystem.  (For reinitialization to be permitted, we'd need to
      // PRMJ_Now subsystem.  (For reinitialization to be permitted, we'd need to
      // "reset" the called-once status -- doable, but more trouble than it's
      // "reset" the called-once status -- doable, but more trouble than it's
      // worth now.)  Initializing that subsystem from JS_Init eliminates the
      // worth now.)  Initializing that subsystem from JS_Init eliminates the
@@ -788,7 +822,7 @@ diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp
      js::FinishDateTimeState();
      js::FinishDateTimeState();
  
  
      if (!JSRuntime::hasLiveRuntimes()) {
      if (!JSRuntime::hasLiveRuntimes()) {
-@@ -214,16 +214,16 @@ JS_ShutDown(void)
+@@ -216,16 +216,16 @@ JS_ShutDown(void)
  
  
  JS_PUBLIC_API(bool)
  JS_PUBLIC_API(bool)
  JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn)
  JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn)
@@ -866,7 +900,7 @@ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
      js_delete(defaultFreeOp_.ref());
      js_delete(defaultFreeOp_.ref());
  
  
      defaultLocale = nullptr;
      defaultLocale = nullptr;
-@@ -551,17 +551,17 @@ JSRuntime::resetDefaultLocale()
+@@ -548,17 +548,17 @@ JSRuntime::resetDefaultLocale()
  const char*
  const char*
  JSRuntime::getDefaultLocale()
  JSRuntime::getDefaultLocale()
  {
  {
@@ -888,7 +922,7 @@ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
 diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
 diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
 --- a/js/src/vm/Runtime.h
 --- a/js/src/vm/Runtime.h
 +++ b/js/src/vm/Runtime.h
 +++ b/js/src/vm/Runtime.h
-@@ -652,17 +652,17 @@ struct JSRuntime : public js::MallocProv
+@@ -653,17 +653,17 @@ struct JSRuntime : public js::MallocProv
      js::WriteOnceData<js::FreeOp*> defaultFreeOp_;
      js::WriteOnceData<js::FreeOp*> defaultFreeOp_;
  
  
    public:
    public:
@@ -910,7 +944,7 @@ diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 --- a/js/src/vm/SelfHosting.cpp
 --- a/js/src/vm/SelfHosting.cpp
 +++ b/js/src/vm/SelfHosting.cpp
 +++ b/js/src/vm/SelfHosting.cpp
-@@ -2403,17 +2403,17 @@ static const JSFunctionSpec intrinsic_fu
+@@ -2402,17 +2402,17 @@ static const JSFunctionSpec intrinsic_fu
      JS_INLINABLE_FN("std_String_toLowerCase",    str_toLowerCase,              0,0, StringToLowerCase),
      JS_INLINABLE_FN("std_String_toLowerCase",    str_toLowerCase,              0,0, StringToLowerCase),
      JS_INLINABLE_FN("std_String_toUpperCase",    str_toUpperCase,              0,0, StringToUpperCase),
      JS_INLINABLE_FN("std_String_toUpperCase",    str_toUpperCase,              0,0, StringToUpperCase),
  
  
@@ -932,7 +966,7 @@ diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 diff --git a/js/xpconnect/src/XPCLocale.cpp b/js/xpconnect/src/XPCLocale.cpp
 diff --git a/js/xpconnect/src/XPCLocale.cpp b/js/xpconnect/src/XPCLocale.cpp
 --- a/js/xpconnect/src/XPCLocale.cpp
 --- a/js/xpconnect/src/XPCLocale.cpp
 +++ b/js/xpconnect/src/XPCLocale.cpp
 +++ b/js/xpconnect/src/XPCLocale.cpp
-@@ -68,17 +68,17 @@ XPCLocaleObserver::Observe(nsISupports* 
+@@ -66,17 +66,17 @@ XPCLocaleObserver::Observe(nsISupports* 
  struct XPCLocaleCallbacks : public JSLocaleCallbacks
  struct XPCLocaleCallbacks : public JSLocaleCallbacks
  {
  {
    XPCLocaleCallbacks()
    XPCLocaleCallbacks()

+ 8 - 7
frg/work-js/mozilla-release/patches/1586165-71a1.patch

@@ -2,7 +2,7 @@
 # User Jan de Mooij <jdemooij@mozilla.com>
 # User Jan de Mooij <jdemooij@mozilla.com>
 # Date 1570193180 0
 # Date 1570193180 0
 # Node ID 1cf96914dd193a3a596e6b35a12fdaeb816e095a
 # Node ID 1cf96914dd193a3a596e6b35a12fdaeb816e095a
-# Parent  fc32527395e2f6c83fe7c84708f08abe8ef04fae
+# Parent  1a4bb790f578f9c8cfdafd74ce8f12e29ebb4095
 Bug 1586165 - Null check ionCounts_ before calling sizeOfIncludingThis on it. r=nbp
 Bug 1586165 - Null check ionCounts_ before calling sizeOfIncludingThis on it. r=nbp
 
 
 Differential Revision: https://phabricator.services.mozilla.com/D48163
 Differential Revision: https://phabricator.services.mozilla.com/D48163
@@ -10,7 +10,7 @@ Differential Revision: https://phabricator.services.mozilla.com/D48163
 diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h
 diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h
 --- a/js/src/jit/IonCode.h
 --- a/js/src/jit/IonCode.h
 +++ b/js/src/jit/IonCode.h
 +++ b/js/src/jit/IonCode.h
-@@ -724,21 +724,20 @@ struct IonScriptCounts
+@@ -721,21 +721,20 @@ struct IonScriptCounts
  
  
      IonScriptCounts* previous() const {
      IonScriptCounts* previous() const {
          return previous_;
          return previous_;
@@ -39,10 +39,10 @@ diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h
 diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
 diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
 --- a/js/src/vm/JSScript.cpp
 --- a/js/src/vm/JSScript.cpp
 +++ b/js/src/vm/JSScript.cpp
 +++ b/js/src/vm/JSScript.cpp
-@@ -1184,20 +1184,22 @@ ScriptCounts::getThrowCounts(size_t offs
-     PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
-     if (elem == throwCounts_.end() || elem->pcOffset() != offset)
+@@ -1287,20 +1287,23 @@ ScriptCounts::getThrowCounts(size_t offs
+     if (elem == throwCounts_.end() || elem->pcOffset() != offset) {
          elem = throwCounts_.insert(elem, searched);
          elem = throwCounts_.insert(elem, searched);
+     }
      return elem;
      return elem;
  }
  }
  
  
@@ -55,8 +55,9 @@ diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
 +    size_t size = mallocSizeOf(this);
 +    size_t size = mallocSizeOf(this);
 +    size += pcCounts_.sizeOfExcludingThis(mallocSizeOf);
 +    size += pcCounts_.sizeOfExcludingThis(mallocSizeOf);
 +    size += throwCounts_.sizeOfExcludingThis(mallocSizeOf);
 +    size += throwCounts_.sizeOfExcludingThis(mallocSizeOf);
-+    if (ionCounts_)
++    if (ionCounts_) {
 +        size += ionCounts_->sizeOfIncludingThis(mallocSizeOf);
 +        size += ionCounts_->sizeOfIncludingThis(mallocSizeOf);
++    }
 +    return size;
 +    return size;
  }
  }
  
  
@@ -64,5 +65,5 @@ diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
  JSScript::setIonScript(JSRuntime* rt, js::jit::IonScript* ionScript)
  JSScript::setIonScript(JSRuntime* rt, js::jit::IonScript* ionScript)
  {
  {
      MOZ_ASSERT_IF(ionScript != ION_DISABLED_SCRIPT, !baselineScript()->hasPendingIonBuilder());
      MOZ_ASSERT_IF(ionScript != ION_DISABLED_SCRIPT, !baselineScript()->hasPendingIonBuilder());
-     if (hasIonScript())
+     if (hasIonScript()) {
          js::jit::IonScript::writeBarrierPre(zone(), ion);
          js::jit::IonScript::writeBarrierPre(zone(), ion);

+ 73 - 52
frg/work-js/mozilla-release/patches/1590907-5-72a1.patch

@@ -2,7 +2,7 @@
 # User Philip Chimento <philip.chimento@gmail.com>
 # User Philip Chimento <philip.chimento@gmail.com>
 # Date 1574753135 0
 # Date 1574753135 0
 # Node ID bfdd5f34f2f4a11b84742400bb3130192c89d4fb
 # Node ID bfdd5f34f2f4a11b84742400bb3130192c89d4fb
-# Parent  a2e9f9cb37ab7fc445cdcc7a3161459d6c7ec494
+# Parent  84f3d8cd9e94d27187d17c1ba1ebcb269670bdbc
 Bug 1590907 - Make ENABLE_INTL_API and ENABLE_TYPED_OBJECTS into js-config macros. r=sfink,firefox-build-system-reviewers,mshal
 Bug 1590907 - Make ENABLE_INTL_API and ENABLE_TYPED_OBJECTS into js-config macros. r=sfink,firefox-build-system-reviewers,mshal
 
 
 Whether ENABLE_INTL_API and ENABLE_TYPED_OBJECTS are defined, affects
 Whether ENABLE_INTL_API and ENABLE_TYPED_OBJECTS are defined, affects
@@ -104,6 +104,28 @@ new file mode 100644
 + @depends(milestone.is_nightly)
 + @depends(milestone.is_nightly)
 + def default_wasm_bulk_memory(is_nightly):
 + def default_wasm_bulk_memory(is_nightly):
 +     return is_nightly
 +     return is_nightly
+diff --git a/js/public/LocaleSensitive.h b/js/public/LocaleSensitive.h
+--- a/js/public/LocaleSensitive.h
++++ b/js/public/LocaleSensitive.h
+@@ -63,17 +63,17 @@ using JSLocaleCompare =
+ using JSLocaleToUnicode =
+     bool (*)(JSContext* cx, const char* src, JS::MutableHandle<JS::Value> rval);
+ 
+ /**
+  * A suite of locale-specific string conversion and error message callbacks
+  * used to implement locale-sensitive behaviors (such as those performed by
+  * the various toLocaleString and toLocale{Date,Time}String functions).
+  *
+- * If SpiderMonkey is compiled --with-intl-api, then #if ENABLE_INTL_API.  In
++ * If SpiderMonkey is compiled --with-intl-api, then #if JS_HAS_INTL_API.  In
+  * this case, SpiderMonkey itself will implement ECMA-402-compliant behavior by
+  * calling on ICU, and none of the fields in this struct will ever be used.
+  * (You'll still be able to call the get/set-callbacks functions; they just
+  * won't affect JavaScript semantics.)
+  */
+ struct JSLocaleCallbacks
+ {
+     JSLocaleToUpperCase localeToUpperCase;
 diff --git a/js/public/ProtoKey.h b/js/public/ProtoKey.h
 diff --git a/js/public/ProtoKey.h b/js/public/ProtoKey.h
 --- a/js/public/ProtoKey.h
 --- a/js/public/ProtoKey.h
 +++ b/js/public/ProtoKey.h
 +++ b/js/public/ProtoKey.h
@@ -210,13 +232,19 @@ diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js
 diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
 diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
 --- a/js/src/builtin/String.cpp
 --- a/js/src/builtin/String.cpp
 +++ b/js/src/builtin/String.cpp
 +++ b/js/src/builtin/String.cpp
-@@ -29,17 +29,17 @@
+@@ -27,22 +27,22 @@
+ 
+ #include "builtin/Array.h"
  #include "builtin/Boolean.h"
  #include "builtin/Boolean.h"
  #include "builtin/intl/CommonFunctions.h"
  #include "builtin/intl/CommonFunctions.h"
  #include "builtin/intl/ICUStubs.h"
  #include "builtin/intl/ICUStubs.h"
  #include "builtin/RegExp.h"
  #include "builtin/RegExp.h"
  #include "jit/InlinableNatives.h"
  #include "jit/InlinableNatives.h"
  #include "js/Conversions.h"
  #include "js/Conversions.h"
+-#if !ENABLE_INTL_API
++#if !JS_HAS_INTL_API
+ #include "js/LocaleSensitive.h"
+ #endif
  #include "js/StableStringChars.h"
  #include "js/StableStringChars.h"
  #include "js/UniquePtr.h"
  #include "js/UniquePtr.h"
 -#if ENABLE_INTL_API
 -#if ENABLE_INTL_API
@@ -229,7 +257,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  #include "vm/BytecodeUtil.h"
  #include "vm/BytecodeUtil.h"
  #include "vm/GlobalObject.h"
  #include "vm/GlobalObject.h"
  #include "vm/Interpreter.h"
  #include "vm/Interpreter.h"
-@@ -730,17 +730,17 @@ js::SubstringKernel(JSContext* cx, Handl
+@@ -733,17 +733,17 @@ js::SubstringKernel(JSContext* cx, Handl
  static char16_t
  static char16_t
  Final_Sigma(const char16_t* chars, size_t length, size_t index)
  Final_Sigma(const char16_t* chars, size_t length, size_t index)
  {
  {
@@ -248,7 +276,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
      for (size_t i = index; i > 0; ) {
      for (size_t i = index; i > 0; ) {
          char16_t c = chars[--i];
          char16_t c = chars[--i];
          uint32_t codePoint = c;
          uint32_t codePoint = c;
-@@ -1068,17 +1068,17 @@ js::intl_toLocaleLowerCase(JSContext* cx
+@@ -1071,17 +1071,17 @@ js::intl_toLocaleLowerCase(JSContext* cx
      JSString* result = NewStringCopyN<CanGC>(cx, chars.begin(), size);
      JSString* result = NewStringCopyN<CanGC>(cx, chars.begin(), size);
      if (!result)
      if (!result)
          return false;
          return false;
@@ -267,7 +295,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  
  
  bool
  bool
  js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
  js::str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
-@@ -1109,17 +1109,17 @@ js::str_toLocaleLowerCase(JSContext* cx,
+@@ -1112,17 +1112,17 @@ js::str_toLocaleLowerCase(JSContext* cx,
      JSString* result = StringToLowerCase(cx, linear);
      JSString* result = StringToLowerCase(cx, linear);
      if (!result)
      if (!result)
          return false;
          return false;
@@ -286,7 +314,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
      // special casing rules, so detect it inline.
      // special casing rules, so detect it inline.
      bool hasUpperCaseSpecialCasing = charCode == unicode::LATIN_SMALL_LETTER_SHARP_S;
      bool hasUpperCaseSpecialCasing = charCode == unicode::LATIN_SMALL_LETTER_SHARP_S;
      MOZ_ASSERT(hasUpperCaseSpecialCasing == unicode::ChangesWhenUpperCasedSpecialCasing(charCode));
      MOZ_ASSERT(hasUpperCaseSpecialCasing == unicode::ChangesWhenUpperCasedSpecialCasing(charCode));
-@@ -1466,17 +1466,17 @@ js::intl_toLocaleUpperCase(JSContext* cx
+@@ -1469,17 +1469,17 @@ js::intl_toLocaleUpperCase(JSContext* cx
      JSString* result = NewStringCopyN<CanGC>(cx, chars.begin(), size);
      JSString* result = NewStringCopyN<CanGC>(cx, chars.begin(), size);
      if (!result)
      if (!result)
          return false;
          return false;
@@ -305,7 +333,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  
  
  bool
  bool
  js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
  js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
-@@ -1507,19 +1507,19 @@ js::str_toLocaleUpperCase(JSContext* cx,
+@@ -1510,19 +1510,19 @@ js::str_toLocaleUpperCase(JSContext* cx,
      JSString* result = StringToUpperCase(cx, linear);
      JSString* result = StringToUpperCase(cx, linear);
      if (!result)
      if (!result)
          return false;
          return false;
@@ -328,7 +356,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  bool
  bool
  js::str_localeCompare(JSContext* cx, unsigned argc, Value* vp)
  js::str_localeCompare(JSContext* cx, unsigned argc, Value* vp)
  {
  {
-@@ -1544,19 +1544,19 @@ js::str_localeCompare(JSContext* cx, uns
+@@ -1547,19 +1547,19 @@ js::str_localeCompare(JSContext* cx, uns
      int32_t result;
      int32_t result;
      if (!CompareStrings(cx, str, thatStr, &result))
      if (!CompareStrings(cx, str, thatStr, &result))
          return false;
          return false;
@@ -351,7 +379,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  {
  {
      CallArgs args = CallArgsFromVp(argc, vp);
      CallArgs args = CallArgsFromVp(argc, vp);
  
  
-@@ -1671,17 +1671,17 @@ js::str_normalize(JSContext* cx, unsigne
+@@ -1674,17 +1674,17 @@ js::str_normalize(JSContext* cx, unsigne
      if (!ns)
      if (!ns)
          return false;
          return false;
  
  
@@ -370,7 +398,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
  
  
      RootedString str(cx);
      RootedString str(cx);
      size_t i;
      size_t i;
-@@ -3548,27 +3548,27 @@ static const JSFunctionSpec string_metho
+@@ -3551,27 +3551,27 @@ static const JSFunctionSpec string_metho
      JS_FN("includes",          str_includes,          1,0),
      JS_FN("includes",          str_includes,          1,0),
      JS_FN("indexOf",           str_indexOf,           1,0),
      JS_FN("indexOf",           str_indexOf,           1,0),
      JS_FN("lastIndexOf",       str_lastIndexOf,       1,0),
      JS_FN("lastIndexOf",       str_lastIndexOf,       1,0),
@@ -400,7 +428,7 @@ diff --git a/js/src/builtin/String.cpp b/js/src/builtin/String.cpp
      JS_SELF_HOSTED_FN("search", "String_search",      1,0),
      JS_SELF_HOSTED_FN("search", "String_search",      1,0),
      JS_SELF_HOSTED_FN("replace", "String_replace",    2,0),
      JS_SELF_HOSTED_FN("replace", "String_replace",    2,0),
      JS_SELF_HOSTED_FN("replaceAll", "String_replaceAll", 2, 0),
      JS_SELF_HOSTED_FN("replaceAll", "String_replaceAll", 2, 0),
-@@ -3854,17 +3854,17 @@ static const JSFunctionSpec string_stati
+@@ -3857,17 +3857,17 @@ static const JSFunctionSpec string_stati
      JS_SELF_HOSTED_FN("lastIndexOf",     "String_static_lastIndexOf",   2,0),
      JS_SELF_HOSTED_FN("lastIndexOf",     "String_static_lastIndexOf",   2,0),
      JS_SELF_HOSTED_FN("startsWith",      "String_static_startsWith",    2,0),
      JS_SELF_HOSTED_FN("startsWith",      "String_static_startsWith",    2,0),
      JS_SELF_HOSTED_FN("endsWith",        "String_static_endsWith",      2,0),
      JS_SELF_HOSTED_FN("endsWith",        "String_static_endsWith",      2,0),
@@ -578,7 +606,7 @@ diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js
 diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
 diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
 --- a/js/src/builtin/TestingFunctions.cpp
 --- a/js/src/builtin/TestingFunctions.cpp
 +++ b/js/src/builtin/TestingFunctions.cpp
 +++ b/js/src/builtin/TestingFunctions.cpp
-@@ -270,17 +270,17 @@ GetBuildConfiguration(JSContext* cx, uns
+@@ -271,17 +271,17 @@ GetBuildConfiguration(JSContext* cx, uns
  #ifdef ENABLE_BINARYDATA
  #ifdef ENABLE_BINARYDATA
      value = BooleanValue(true);
      value = BooleanValue(true);
  #else
  #else
@@ -796,32 +824,6 @@ new file mode 100644
 + #include "gc/Policy.h"
 + #include "gc/Policy.h"
 + #include "gc/PublicIterators.h"
 + #include "gc/PublicIterators.h"
 + #include "gc/WeakMap.h"
 + #include "gc/WeakMap.h"
-diff --git a/js/src/jsapi.h b/js/src/jsapi.h
---- a/js/src/jsapi.h
-+++ b/js/src/jsapi.h
-@@ -4837,19 +4837,19 @@ JS_GetDefaultLocale(JSContext* cx);
-  */
- extern JS_PUBLIC_API(void)
- JS_ResetDefaultLocale(JSRuntime* rt);
- 
- /**
-  * Locale specific string conversion and error message callbacks.
-  */
- struct JSLocaleCallbacks {
--    JSLocaleToUpperCase     localeToUpperCase; // not used #if ENABLE_INTL_API
--    JSLocaleToLowerCase     localeToLowerCase; // not used #if ENABLE_INTL_API
--    JSLocaleCompare         localeCompare; // not used #if ENABLE_INTL_API
-+    JSLocaleToUpperCase     localeToUpperCase; // not used #if JS_HAS_INTL_API
-+    JSLocaleToLowerCase     localeToLowerCase; // not used #if JS_HAS_INTL_API
-+    JSLocaleCompare         localeCompare; // not used #if JS_HAS_INTL_API
-     JSLocaleToUnicode       localeToUnicode;
- };
- 
- /**
-  * Establish locale callbacks. The pointer must persist as long as the
-  * JSContext.  Passing nullptr restores the default behaviour.
-  */
- extern JS_PUBLIC_API(void)
 diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
 diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
 --- a/js/src/jsdate.cpp
 --- a/js/src/jsdate.cpp
 +++ b/js/src/jsdate.cpp
 +++ b/js/src/jsdate.cpp
@@ -961,7 +963,26 @@ diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
 diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
 diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
 --- a/js/src/jsnum.cpp
 --- a/js/src/jsnum.cpp
 +++ b/js/src/jsnum.cpp
 +++ b/js/src/jsnum.cpp
-@@ -770,17 +770,17 @@ num_toString_impl(JSContext* cx, const C
+@@ -23,17 +23,17 @@
+ #include <math.h>
+ #include <string.h>
+ 
+ #include "jstypes.h"
+ 
+ #include "builtin/String.h"
+ #include "double-conversion/double-conversion.h"
+ #include "js/Conversions.h"
+-#if !ENABLE_INTL_API
++#if !JS_HAS_INTL_API
+ #include "js/LocaleSensitive.h"
+ #endif
+ #include "util/DoubleToString.h"
+ #include "util/StringBuffer.h"
+ #ifdef ENABLE_BIGINT
+ #include "vm/BigIntType.h"
+ #endif
+ #include "vm/GlobalObject.h"
+@@ -773,17 +773,17 @@ num_toString_impl(JSContext* cx, const C
  
  
  bool
  bool
  js::num_toString(JSContext* cx, unsigned argc, Value* vp)
  js::num_toString(JSContext* cx, unsigned argc, Value* vp)
@@ -980,7 +1001,7 @@ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
      double d = Extract(args.thisv());
      double d = Extract(args.thisv());
  
  
      RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
      RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
-@@ -903,17 +903,17 @@ num_toLocaleString_impl(JSContext* cx, c
+@@ -906,17 +906,17 @@ num_toLocaleString_impl(JSContext* cx, c
  }
  }
  
  
  static bool
  static bool
@@ -999,7 +1020,7 @@ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
      args.rval().setNumber(Extract(args.thisv()));
      args.rval().setNumber(Extract(args.thisv()));
      return true;
      return true;
  }
  }
-@@ -1122,17 +1122,17 @@ num_toPrecision(JSContext* cx, unsigned 
+@@ -1125,17 +1125,17 @@ num_toPrecision(JSContext* cx, unsigned 
  {
  {
      CallArgs args = CallArgsFromVp(argc, vp);
      CallArgs args = CallArgsFromVp(argc, vp);
      return CallNonGenericMethod<IsNumber, num_toPrecision_impl>(cx, args);
      return CallNonGenericMethod<IsNumber, num_toPrecision_impl>(cx, args);
@@ -1018,7 +1039,7 @@ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
      JS_FN("toFixed",             num_toFixed,           1, 0),
      JS_FN("toFixed",             num_toFixed,           1, 0),
      JS_FN("toExponential",       num_toExponential,     1, 0),
      JS_FN("toExponential",       num_toExponential,     1, 0),
      JS_FN("toPrecision",         num_toPrecision,       1, 0),
      JS_FN("toPrecision",         num_toPrecision,       1, 0),
-@@ -1153,20 +1153,20 @@ static const JSFunctionSpec number_stati
+@@ -1156,20 +1156,20 @@ static const JSFunctionSpec number_stati
      JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1,0),
      JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1,0),
      JS_FS_END
      JS_FS_END
  };
  };
@@ -1041,7 +1062,7 @@ diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
      struct lconv* locale = localeconv();
      struct lconv* locale = localeconv();
      thousandsSeparator = locale->thousands_sep;
      thousandsSeparator = locale->thousands_sep;
      decimalPoint = locale->decimal_point;
      decimalPoint = locale->decimal_point;
-@@ -1202,21 +1202,21 @@ js::InitRuntimeNumberState(JSRuntime* rt
+@@ -1205,21 +1205,21 @@ js::InitRuntimeNumberState(JSRuntime* rt
      storage += thousandsSeparatorSize;
      storage += thousandsSeparatorSize;
  
  
      js_memcpy(storage, decimalPoint, decimalPointSize);
      js_memcpy(storage, decimalPoint, decimalPointSize);
@@ -1090,7 +1111,7 @@ diff --git a/js/src/jsnum.h b/js/src/jsnum.h
 diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 --- a/js/src/shell/js.cpp
 --- a/js/src/shell/js.cpp
 +++ b/js/src/shell/js.cpp
 +++ b/js/src/shell/js.cpp
-@@ -1075,17 +1075,17 @@ BindToAsyncStack(JSContext* cx, unsigned
+@@ -1076,17 +1076,17 @@ BindToAsyncStack(JSContext* cx, unsigned
          return false;
          return false;
      SetFunctionNativeReserved(bound, 0, args[0]);
      SetFunctionNativeReserved(bound, 0, args[0]);
      SetFunctionNativeReserved(bound, 1, args[1]);
      SetFunctionNativeReserved(bound, 1, args[1]);
@@ -1109,7 +1130,7 @@ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
          JS_ReportErrorASCII(cx, "addIntlExtras must be passed an object");
          JS_ReportErrorASCII(cx, "addIntlExtras must be passed an object");
          return false;
          return false;
      }
      }
-@@ -1102,17 +1102,17 @@ AddIntlExtras(JSContext* cx, unsigned ar
+@@ -1103,17 +1103,17 @@ AddIntlExtras(JSContext* cx, unsigned ar
          return false;
          return false;
  
  
      if (!js::AddMozDateTimeFormatConstructor(cx, intl))
      if (!js::AddMozDateTimeFormatConstructor(cx, intl))
@@ -1128,7 +1149,7 @@ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
      // Eval.
      // Eval.
      JS::CompileOptions options(cx);
      JS::CompileOptions options(cx);
      options.setIntroductionType("js shell interactive")
      options.setIntroductionType("js shell interactive")
-@@ -7229,25 +7229,25 @@ static const JSFunctionSpecWithHelp shel
+@@ -7230,25 +7230,25 @@ static const JSFunctionSpecWithHelp shel
  "  cause:    A string, supplied as the async cause on the top frame of\n"
  "  cause:    A string, supplied as the async cause on the top frame of\n"
  "            captured async stacks.\n"
  "            captured async stacks.\n"
  "\n"
  "\n"
@@ -1531,7 +1552,7 @@ diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp
  #include "vm/Runtime.h"
  #include "vm/Runtime.h"
  #include "vm/Time.h"
  #include "vm/Time.h"
  #include "vm/TraceLogging.h"
  #include "vm/TraceLogging.h"
-@@ -117,22 +117,22 @@ JS::detail::InitWithFailureDiagnostic(bo
+@@ -119,22 +119,22 @@ JS::detail::InitWithFailureDiagnostic(bo
      RETURN_IF_FAIL(js::jit::InitializeIon());
      RETURN_IF_FAIL(js::jit::InitializeIon());
  
  
      RETURN_IF_FAIL(js::InitDateTimeState());
      RETURN_IF_FAIL(js::InitDateTimeState());
@@ -1556,7 +1577,7 @@ diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp
  #ifdef JS_SIMULATOR
  #ifdef JS_SIMULATOR
      RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
      RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
  #endif
  #endif
-@@ -187,19 +187,19 @@ JS_ShutDown(void)
+@@ -189,19 +189,19 @@ JS_ShutDown(void)
      // PRMJ_Now subsystem.  (For reinitialization to be permitted, we'd need to
      // PRMJ_Now subsystem.  (For reinitialization to be permitted, we'd need to
      // "reset" the called-once status -- doable, but more trouble than it's
      // "reset" the called-once status -- doable, but more trouble than it's
      // worth now.)  Initializing that subsystem from JS_Init eliminates the
      // worth now.)  Initializing that subsystem from JS_Init eliminates the
@@ -1578,7 +1599,7 @@ diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp
      js::FinishDateTimeState();
      js::FinishDateTimeState();
  
  
      if (!JSRuntime::hasLiveRuntimes()) {
      if (!JSRuntime::hasLiveRuntimes()) {
-@@ -214,16 +214,16 @@ JS_ShutDown(void)
+@@ -216,16 +216,16 @@ JS_ShutDown(void)
  
  
  JS_PUBLIC_API(bool)
  JS_PUBLIC_API(bool)
  JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn)
  JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn)
@@ -1656,7 +1677,7 @@ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
      js_delete(defaultFreeOp_.ref());
      js_delete(defaultFreeOp_.ref());
  
  
      defaultLocale = nullptr;
      defaultLocale = nullptr;
-@@ -551,17 +551,17 @@ JSRuntime::resetDefaultLocale()
+@@ -548,17 +548,17 @@ JSRuntime::resetDefaultLocale()
  const char*
  const char*
  JSRuntime::getDefaultLocale()
  JSRuntime::getDefaultLocale()
  {
  {
@@ -1678,7 +1699,7 @@ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
 diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
 diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
 --- a/js/src/vm/Runtime.h
 --- a/js/src/vm/Runtime.h
 +++ b/js/src/vm/Runtime.h
 +++ b/js/src/vm/Runtime.h
-@@ -652,17 +652,17 @@ struct JSRuntime : public js::MallocProv
+@@ -653,17 +653,17 @@ struct JSRuntime : public js::MallocProv
      js::WriteOnceData<js::FreeOp*> defaultFreeOp_;
      js::WriteOnceData<js::FreeOp*> defaultFreeOp_;
  
  
    public:
    public:
@@ -1770,7 +1791,7 @@ diff --git a/js/sub.configure b/js/sub.configure
 diff --git a/js/xpconnect/src/XPCLocale.cpp b/js/xpconnect/src/XPCLocale.cpp
 diff --git a/js/xpconnect/src/XPCLocale.cpp b/js/xpconnect/src/XPCLocale.cpp
 --- a/js/xpconnect/src/XPCLocale.cpp
 --- a/js/xpconnect/src/XPCLocale.cpp
 +++ b/js/xpconnect/src/XPCLocale.cpp
 +++ b/js/xpconnect/src/XPCLocale.cpp
-@@ -68,17 +68,17 @@ XPCLocaleObserver::Observe(nsISupports* 
+@@ -66,17 +66,17 @@ XPCLocaleObserver::Observe(nsISupports* 
  struct XPCLocaleCallbacks : public JSLocaleCallbacks
  struct XPCLocaleCallbacks : public JSLocaleCallbacks
  {
  {
    XPCLocaleCallbacks()
    XPCLocaleCallbacks()

+ 9 - 9
frg/work-js/mozilla-release/patches/1770048-9110.patch

@@ -2,7 +2,7 @@
 # User Iain Ireland <iireland@mozilla.com>
 # User Iain Ireland <iireland@mozilla.com>
 # Date 1652942930 0
 # Date 1652942930 0
 # Node ID 757b55aab315b014546ac7f0bbf81a5c097c67f4
 # Node ID 757b55aab315b014546ac7f0bbf81a5c097c67f4
-# Parent  00a070832de6916758f4fc41806be6e7d8c2bb87
+# Parent  86b55b97f2985bd72a89ee6066e3340f3499a1ad
 Bug 1770048: Improve self-hosted new_List (ESR) r=jandem,tcampbell, a=dsmith
 Bug 1770048: Improve self-hosted new_List (ESR) r=jandem,tcampbell, a=dsmith
 
 
 Differential Revision: https://phabricator.services.mozilla.com/D146774
 Differential Revision: https://phabricator.services.mozilla.com/D146774
@@ -10,7 +10,7 @@ Differential Revision: https://phabricator.services.mozilla.com/D146774
 diff --git a/js/src/builtin/Array.cpp b/js/src/builtin/Array.cpp
 diff --git a/js/src/builtin/Array.cpp b/js/src/builtin/Array.cpp
 --- a/js/src/builtin/Array.cpp
 --- a/js/src/builtin/Array.cpp
 +++ b/js/src/builtin/Array.cpp
 +++ b/js/src/builtin/Array.cpp
-@@ -4066,16 +4066,53 @@ js::NewCopiedArrayForCallingAllocationSi
+@@ -4091,16 +4091,53 @@ js::NewCopiedArrayForCallingAllocationSi
                                             HandleObject proto /* = nullptr */)
                                             HandleObject proto /* = nullptr */)
  {
  {
      RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
      RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
@@ -67,7 +67,7 @@ diff --git a/js/src/builtin/Array.cpp b/js/src/builtin/Array.cpp
 diff --git a/js/src/builtin/Array.h b/js/src/builtin/Array.h
 diff --git a/js/src/builtin/Array.h b/js/src/builtin/Array.h
 --- a/js/src/builtin/Array.h
 --- a/js/src/builtin/Array.h
 +++ b/js/src/builtin/Array.h
 +++ b/js/src/builtin/Array.h
-@@ -165,16 +165,18 @@ array_slice_dense(JSContext* cx, HandleO
+@@ -160,16 +160,18 @@ array_slice_dense(JSContext* cx, HandleO
  extern bool
  extern bool
  array_reverse(JSContext* cx, unsigned argc, js::Value* vp);
  array_reverse(JSContext* cx, unsigned argc, js::Value* vp);
  
  
@@ -147,13 +147,13 @@ diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js
  
  
      // Step 1
      // Step 1
      let module = this;
      let module = this;
-@@ -194,17 +194,17 @@ function GetModuleNamespace(module)
+@@ -193,17 +193,17 @@ function GetModuleNamespace(module)
             "Bad module state in GetModuleNamespace");
             "Bad module state in GetModuleNamespace");
  
  
-     // Step 4
+     // Step 3
      let namespace = module.namespace;
      let namespace = module.namespace;
  
  
-     // Step 3
+     // Step 4
      if (typeof namespace === "undefined") {
      if (typeof namespace === "undefined") {
          let exportedNames = callFunction(module.getExportedNames, module);
          let exportedNames = callFunction(module.getExportedNames, module);
 -        let unambiguousNames = [];
 -        let unambiguousNames = [];
@@ -166,7 +166,7 @@ diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js
          }
          }
          namespace = ModuleNamespaceCreate(module, unambiguousNames);
          namespace = ModuleNamespaceCreate(module, unambiguousNames);
      }
      }
-@@ -287,17 +287,17 @@ function ModuleInstantiate()
+@@ -286,17 +286,17 @@ function ModuleInstantiate()
      // Step 2
      // Step 2
      if (module.status === MODULE_STATUS_INSTANTIATING ||
      if (module.status === MODULE_STATUS_INSTANTIATING ||
          module.status === MODULE_STATUS_EVALUATING)
          module.status === MODULE_STATUS_EVALUATING)
@@ -185,7 +185,7 @@ diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js
          for (let i = 0; i < stack.length; i++) {
          for (let i = 0; i < stack.length; i++) {
              let m = stack[i];
              let m = stack[i];
              assert(m.status === MODULE_STATUS_INSTANTIATING,
              assert(m.status === MODULE_STATUS_INSTANTIATING,
-@@ -517,17 +517,17 @@ function ModuleEvaluate()
+@@ -516,17 +516,17 @@ function ModuleEvaluate()
      if (module.status !== MODULE_STATUS_INSTANTIATED &&
      if (module.status !== MODULE_STATUS_INSTANTIATED &&
          module.status !== MODULE_STATUS_EVALUATED &&
          module.status !== MODULE_STATUS_EVALUATED &&
          module.status !== MODULE_STATUS_EVALUATED_ERROR)
          module.status !== MODULE_STATUS_EVALUATED_ERROR)
@@ -324,7 +324,7 @@ diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js
 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
 --- a/js/src/vm/SelfHosting.cpp
 --- a/js/src/vm/SelfHosting.cpp
 +++ b/js/src/vm/SelfHosting.cpp
 +++ b/js/src/vm/SelfHosting.cpp
-@@ -2169,16 +2169,17 @@ intrinsic_PromiseResolve(JSContext* cx, 
+@@ -2324,16 +2324,17 @@ intrinsic_CopyDataPropertiesOrGetOwnKeys
  // content script might have changed the builtins' prototypes' members.
  // content script might have changed the builtins' prototypes' members.
  // Installing the whole set of builtins in the self-hosting compartment, OTOH,
  // Installing the whole set of builtins in the self-hosting compartment, OTOH,
  // would be wasteful: it increases memory usage and initialization time for
  // would be wasteful: it increases memory usage and initialization time for

+ 0 - 232
frg/work-js/mozilla-release/patches/L-1459220-62a1.patch

@@ -1,232 +0,0 @@
-# HG changeset patch
-# User Tooru Fujisawa <arai_a@mac.com>
-# Date 1526015035 -32400
-# Node ID 1c1d3f24941ae0710a55bdcae8ee3d2126ab5b82
-# Parent  4ef3bf9d981a0edf151dc1373ad2385db966a93f
-Bug 1459220 - Add LazyScript::isEnclosingScriptLazy. r=jimb
-
-diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
---- a/js/src/frontend/BytecodeCompiler.cpp
-+++ b/js/src/frontend/BytecodeCompiler.cpp
-@@ -736,16 +736,18 @@ frontend::CompileModule(JSContext* cx, c
-     assertException.reset();
-     return module;
- }
- 
- bool
- frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
- {
-     MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
-+    // We can't be running this script unless we've run its parent.
-+    MOZ_ASSERT(!lazy->isEnclosingScriptLazy());
- 
-     AutoAssertReportedException assertException(cx);
- 
-     CompileOptions options(cx);
-     options.setMutedErrors(lazy->mutedErrors())
-            .setFileAndLine(lazy->filename(), lazy->lineno())
-            .setColumn(lazy->column())
-            .setScriptSourceOffset(lazy->sourceStart())
-diff --git a/js/src/vm/JSCompartment.cpp b/js/src/vm/JSCompartment.cpp
---- a/js/src/vm/JSCompartment.cpp
-+++ b/js/src/vm/JSCompartment.cpp
-@@ -1085,20 +1085,20 @@ AddInnerLazyFunctionsFromScript(JSScript
-     }
-     return true;
- }
- 
- static bool
- AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, AllocKind kind)
- {
-     // Find all live root lazy functions in the compartment: those which have a
--    // source object, indicating that they have a parent, and which do not have
--    // an uncompiled enclosing script. The last condition is so that we don't
--    // compile lazy scripts whose enclosing scripts failed to compile,
--    // indicating that the lazy script did not escape the script.
-+    // non-lazy enclosing script, and which do not have an uncompiled enclosing
-+    // script. The last condition is so that we don't compile lazy scripts
-+    // whose enclosing scripts failed to compile, indicating that the lazy
-+    // script did not escape the script.
-     //
-     // Some LazyScripts have a non-null |JSScript* script| pointer. We still
-     // want to delazify in that case: this pointer is weak so the JSScript
-     // could be destroyed at the next GC.
- 
-     for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
-         JSFunction* fun = &i->as<JSFunction>();
- 
-@@ -1108,17 +1108,17 @@ AddLazyFunctionsForCompartment(JSContext
-         if (gc::IsAboutToBeFinalizedUnbarriered(&fun) ||
-             fun->compartment() != cx->compartment())
-         {
-             continue;
-         }
- 
-         if (fun->isInterpretedLazy()) {
-             LazyScript* lazy = fun->lazyScriptOrNull();
--            if (lazy && lazy->sourceObject() && !lazy->hasUncompiledEnclosingScript()) {
-+            if (lazy && !lazy->isEnclosingScriptLazy() && !lazy->hasUncompletedEnclosingScript()) {
-                 if (!lazyFunctions.append(fun))
-                     return false;
-             }
-         }
-     }
- 
-     return true;
- }
-diff --git a/js/src/vm/JSFunction.cpp b/js/src/vm/JSFunction.cpp
---- a/js/src/vm/JSFunction.cpp
-+++ b/js/src/vm/JSFunction.cpp
-@@ -787,17 +787,17 @@ JSFunction::trace(JSTracer* trc)
-     }
- 
-     TraceNullableEdge(trc, &atom_, "atom");
- 
-     if (isInterpreted()) {
-         // Functions can be be marked as interpreted despite having no script
-         // yet at some points when parsing, and can be lazy with no lazy script
-         // for self-hosted code.
--        if (hasScript() && !hasUncompiledScript())
-+        if (hasScript() && !hasUncompletedScript())
-             TraceManuallyBarrieredEdge(trc, &u.scripted.s.script_, "script");
-         else if (isInterpretedLazy() && u.scripted.s.lazy_)
-             TraceManuallyBarrieredEdge(trc, &u.scripted.s.lazy_, "lazyScript");
- 
-         if (u.scripted.env_)
-             TraceManuallyBarrieredEdge(trc, &u.scripted.env_, "fun_environment");
-     }
- }
-diff --git a/js/src/vm/JSFunction.h b/js/src/vm/JSFunction.h
---- a/js/src/vm/JSFunction.h
-+++ b/js/src/vm/JSFunction.h
-@@ -559,23 +559,23 @@ class JSFunction : public js::NativeObje
-             initScript(script);
-         }
-         return nonLazyScript();
-     }
- 
-     // The state of a JSFunction whose script errored out during bytecode
-     // compilation. Such JSFunctions are only reachable via GC iteration and
-     // not from script.
--    bool hasUncompiledScript() const {
-+    bool hasUncompletedScript() const {
-         MOZ_ASSERT(hasScript());
-         return !u.scripted.s.script_;
-     }
- 
-     JSScript* nonLazyScript() const {
--        MOZ_ASSERT(!hasUncompiledScript());
-+        MOZ_ASSERT(!hasUncompletedScript());
-         return u.scripted.s.script_;
-     }
- 
-     static bool getLength(JSContext* cx, js::HandleFunction fun, uint16_t* length);
- 
-     js::LazyScript* lazyScript() const {
-         MOZ_ASSERT(isInterpretedLazy() && u.scripted.s.lazy_);
-         return u.scripted.s.lazy_;
-diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp
---- a/js/src/vm/JSScript.cpp
-+++ b/js/src/vm/JSScript.cpp
-@@ -3650,17 +3650,17 @@ js::CloneGlobalScript(JSContext* cx, Sco
-     return dst;
- }
- 
- JSScript*
- js::CloneScriptIntoFunction(JSContext* cx, HandleScope enclosingScope, HandleFunction fun,
-                             HandleScript src)
- {
-     MOZ_ASSERT(fun->isInterpreted());
--    MOZ_ASSERT(!fun->hasScript() || fun->hasUncompiledScript());
-+    MOZ_ASSERT(!fun->hasScript() || fun->hasUncompletedScript());
- 
-     RootedScript dst(cx, CreateEmptyScriptForClone(cx, src));
-     if (!dst)
-         return nullptr;
- 
-     // Clone the non-intra-body scopes.
-     Rooted<GCVector<Scope*>> scopes(cx, GCVector<Scope*>(cx));
-     RootedScope original(cx);
-@@ -4341,17 +4341,17 @@ LazyScript::Create(JSContext* cx, Handle
-         closedOverBindings[i] = dummyAtom;
- 
-     GCPtrFunction* functions = res->innerFunctions();
-     for (i = 0, num = res->numInnerFunctions(); i < num; i++)
-         functions[i].init(dummyFun);
- 
-     // Set the enclosing scope and source object of the lazy function. These
-     // values should only be non-null if we have a non-lazy enclosing script.
--    // AddLazyFunctionsForCompartment relies on the source object being null
-+    // LazyScript::isEnclosingScriptLazy relies on the source object being null
-     // if we're nested inside another lazy function.
-     MOZ_ASSERT(!!sourceObject == !!enclosingScope);
-     MOZ_ASSERT(!res->sourceObject());
-     MOZ_ASSERT(!res->enclosingScope());
-     if (sourceObject)
-         res->setEnclosingScopeAndSource(enclosingScope, sourceObject);
- 
-     MOZ_ASSERT(!res->hasScript());
-@@ -4370,31 +4370,31 @@ LazyScript::initRuntimeFields(uint64_t p
-     };
- 
-     packed = packedFields;
-     p_.hasBeenCloned = p.hasBeenCloned;
-     p_.treatAsRunOnce = p.treatAsRunOnce;
- }
- 
- bool
--LazyScript::hasUncompiledEnclosingScript() const
-+LazyScript::hasUncompletedEnclosingScript() const
- {
-     // It can happen that we created lazy scripts while compiling an enclosing
-     // script, but we errored out while compiling that script. When we iterate
-     // over lazy script in a compartment, we might see lazy scripts that never
-     // escaped to script and should be ignored.
-     //
-     // If the enclosing scope is a function with a null script or has a script
-     // without code, it was not successfully compiled.
- 
-     if (!enclosingScope() || !enclosingScope()->is<FunctionScope>())
-         return false;
- 
-     JSFunction* fun = enclosingScope()->as<FunctionScope>().canonicalFunction();
--    return !fun->hasScript() || fun->hasUncompiledScript() || !fun->nonLazyScript()->code();
-+    return !fun->hasScript() || fun->hasUncompletedScript() || !fun->nonLazyScript()->code();
- }
- 
- void
- JSScript::updateJitCodeRaw(JSRuntime* rt)
- {
-     MOZ_ASSERT(rt);
-     if (hasBaselineScript() && baseline->hasPendingIonBuilder()) {
-         MOZ_ASSERT(!isIonCompilingOffThread());
-diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h
---- a/js/src/vm/JSScript.h
-+++ b/js/src/vm/JSScript.h
-@@ -2377,17 +2377,24 @@ class LazyScript : public gc::TenuredCel
-     }
- 
-     void setToStringEnd(uint32_t toStringEnd) {
-         MOZ_ASSERT(toStringStart_ <= toStringEnd);
-         MOZ_ASSERT(toStringEnd_ >= sourceEnd_);
-         toStringEnd_ = toStringEnd;
-     }
- 
--    bool hasUncompiledEnclosingScript() const;
-+    // Returns true if the enclosing script failed to compile.
-+    // See the comment in the definition for more details.
-+    bool hasUncompletedEnclosingScript() const;
-+
-+    // Returns true if the enclosing script is also lazy.
-+    bool isEnclosingScriptLazy() const {
-+        return !sourceObject_;
-+    }
- 
-     friend class GCMarker;
-     void traceChildren(JSTracer* trc);
-     void finalize(js::FreeOp* fop);
- 
-     static const JS::TraceKind TraceKind = JS::TraceKind::LazyScript;
- 
-     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
-

+ 29 - 0
frg/work-js/mozilla-release/patches/NOBUG-20180712-typetraits-63a1.patch

@@ -0,0 +1,29 @@
+# HG changeset patch
+# User Jeff Walden <jwalden@mit.edu>
+# Date 1531387609 25200
+# Node ID 86902c248760cf7b9580a5433b23e979593953c8
+# Parent  3d61b1272eae89fa6b51ab45f74e2b9fb1f1f1cf
+Add support for char32_t to mozilla::IsIntegral.  Found in the process of fixing another bug, no bug and r=me as so unbelievably trivial that it can't possibly deserve anything else
+
+diff --git a/mfbt/TypeTraits.h b/mfbt/TypeTraits.h
+--- a/mfbt/TypeTraits.h
++++ b/mfbt/TypeTraits.h
+@@ -94,16 +94,17 @@ template<> struct IsIntegralHelper<int> 
+ template<> struct IsIntegralHelper<unsigned int>       : TrueType {};
+ template<> struct IsIntegralHelper<long>               : TrueType {};
+ template<> struct IsIntegralHelper<unsigned long>      : TrueType {};
+ template<> struct IsIntegralHelper<long long>          : TrueType {};
+ template<> struct IsIntegralHelper<unsigned long long> : TrueType {};
+ template<> struct IsIntegralHelper<bool>               : TrueType {};
+ template<> struct IsIntegralHelper<wchar_t>            : TrueType {};
+ template<> struct IsIntegralHelper<char16_t>           : TrueType {};
++template<> struct IsIntegralHelper<char32_t>           : TrueType {};
+ 
+ } /* namespace detail */
+ 
+ /**
+  * IsIntegral determines whether a type is an integral type.
+  *
+  * mozilla::IsIntegral<int>::value is true;
+  * mozilla::IsIntegral<unsigned short>::value is true;
+

+ 105 - 0
frg/work-js/mozilla-release/patches/NOBUG-20180809-utf8-63a1.patch

@@ -0,0 +1,105 @@
+# HG changeset patch
+# User Jeff Walden <jwalden@mit.edu>
+# Date 1533873863 25200
+# Node ID ed645e2703e989bd2cb7971162d06fef54d7daf2
+# Parent  f244ce374cd26b231d5309900b4648db90ad39cb
+Make DecodeOneUtf8CodePoint* take |const EndIter& aEnd| to enforce immutability of the end iterator.  No bug, r=me as trivial
+
+diff --git a/mfbt/Utf8.h b/mfbt/Utf8.h
+--- a/mfbt/Utf8.h
++++ b/mfbt/Utf8.h
+@@ -258,17 +258,17 @@ inline bool
+ IsTrailingUnit(Utf8Unit aUnit)
+ {
+   return (aUnit.toUint8() & 0b1100'0000) == 0b1000'0000;
+ }
+ 
+ /**
+  * Given |aLeadUnit| that is a non-ASCII code unit, a pointer to an |Iter aIter|
+  * that (initially) itself points one unit past |aLeadUnit|, and
+- * |const EndIter aEnd| that denotes the end of the UTF-8 data when compared
++ * |const EndIter& aEnd| that denotes the end of the UTF-8 data when compared
+  * against |*aIter| using |aEnd - *aIter|:
+  *
+  * If |aLeadUnit| and subsequent code units computed using |*aIter| (up to
+  * |aEnd|) encode a valid code point -- not exceeding Unicode's range, not a
+  * surrogate, in shortest form -- then return Some(that code point) and advance
+  * |*aIter| past those code units.
+  *
+  * Otherwise decrement |*aIter| (so that it points at |aLeadUnit|) and return
+@@ -294,17 +294,17 @@ template<typename Iter,
+          typename EndIter,
+          class OnBadLeadUnit,
+          class OnNotEnoughUnits,
+          class OnBadTrailingUnit,
+          class OnBadCodePoint,
+          class OnNotShortestForm>
+ MOZ_ALWAYS_INLINE Maybe<char32_t>
+ DecodeOneUtf8CodePointInline(const Utf8Unit aLeadUnit,
+-                             Iter* aIter, const EndIter aEnd,
++                             Iter* aIter, const EndIter& aEnd,
+                              OnBadLeadUnit aOnBadLeadUnit,
+                              OnNotEnoughUnits aOnNotEnoughUnits,
+                              OnBadTrailingUnit aOnBadTrailingUnit,
+                              OnBadCodePoint aOnBadCodePoint,
+                              OnNotShortestForm aOnNotShortestForm)
+ {
+   MOZ_ASSERT(Utf8Unit((*aIter)[-1]) == aLeadUnit);
+ 
+@@ -386,17 +386,17 @@ template<typename Iter,
+          typename EndIter,
+          class OnBadLeadUnit,
+          class OnNotEnoughUnits,
+          class OnBadTrailingUnit,
+          class OnBadCodePoint,
+          class OnNotShortestForm>
+ inline Maybe<char32_t>
+ DecodeOneUtf8CodePoint(const Utf8Unit aLeadUnit,
+-                       Iter* aIter, const EndIter aEnd,
++                       Iter* aIter, const EndIter& aEnd,
+                        OnBadLeadUnit aOnBadLeadUnit,
+                        OnNotEnoughUnits aOnNotEnoughUnits,
+                        OnBadTrailingUnit aOnBadTrailingUnit,
+                        OnBadCodePoint aOnBadCodePoint,
+                        OnNotShortestForm aOnNotShortestForm)
+ {
+   return DecodeOneUtf8CodePointInline(aLeadUnit, aIter, aEnd,
+                                       aOnBadLeadUnit, aOnNotEnoughUnits,
+@@ -409,17 +409,17 @@ DecodeOneUtf8CodePoint(const Utf8Unit aL
+  * trailing if-invalid notifier functors.
+  *
+  * This function is MOZ_ALWAYS_INLINE: if you don't need that, use the version
+  * of this function without the "Inline" suffix on the name.
+  */
+ template<typename Iter, typename EndIter>
+ MOZ_ALWAYS_INLINE Maybe<char32_t>
+ DecodeOneUtf8CodePointInline(const Utf8Unit aLeadUnit,
+-                             Iter* aIter, const EndIter aEnd)
++                             Iter* aIter, const EndIter& aEnd)
+ {
+   // aOnBadLeadUnit is called when |aLeadUnit| itself is an invalid lead unit in
+   // a multi-unit code point.  It is passed no arguments: the caller already has
+   // |aLeadUnit| on hand, so no need to provide it again.
+   auto onBadLeadUnit = []() {};
+ 
+   // aOnNotEnoughUnits is called when |aLeadUnit| properly indicates a code
+   // point length, but there aren't enough units from |*aIter| to |aEnd| to
+@@ -461,16 +461,16 @@ DecodeOneUtf8CodePointInline(const Utf8U
+ 
+ /**
+  * Identical to the above function, but not forced to be instantiated inline --
+  * the compiler/linker are allowed to common up separate invocations.
+  */
+ template<typename Iter, typename EndIter>
+ inline Maybe<char32_t>
+ DecodeOneUtf8CodePoint(const Utf8Unit aLeadUnit,
+-                       Iter* aIter, const EndIter aEnd)
++                       Iter* aIter, const EndIter& aEnd)
+ {
+   return DecodeOneUtf8CodePointInline(aLeadUnit, aIter, aEnd);
+ }
+ 
+ } // namespace mozilla
+ 
+ #endif /* mozilla_Utf8_h */
+

+ 10 - 10
frg/work-js/mozilla-release/patches/TOP-NOBUG-REGEXP-03-1537978-68a1-25318.patch

@@ -1,7 +1,7 @@
 # HG changeset patch
 # HG changeset patch
 # User Dmitry Butskoy <buc@buc.me>
 # User Dmitry Butskoy <buc@buc.me>
 # Date 1690629335 -7200
 # Date 1690629335 -7200
-# Parent  02fe021ade73ebec5f3f57b3b0b5d6688063890f
+# Parent  a64f45d3d95b42495184c61822c9f06b8e253ea7
 No Bug - Import new regexp V8 engine. r=frg a=frg
 No Bug - Import new regexp V8 engine. r=frg a=frg
 
 
  Bug 1537978 - Move regular expression-related functionality out of jsapi.h
  Bug 1537978 - Move regular expression-related functionality out of jsapi.h
@@ -12,8 +12,7 @@ No Bug - Import new regexp V8 engine. r=frg a=frg
 diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
 diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
 --- a/dom/base/nsContentUtils.cpp
 --- a/dom/base/nsContentUtils.cpp
 +++ b/dom/base/nsContentUtils.cpp
 +++ b/dom/base/nsContentUtils.cpp
-@@ -15,16 +15,17 @@
- #include "harfbuzz/hb.h"
+@@ -16,16 +16,17 @@
  #include "imgICache.h"
  #include "imgICache.h"
  #include "imgIContainer.h"
  #include "imgIContainer.h"
  #include "imgINotificationObserver.h"
  #include "imgINotificationObserver.h"
@@ -21,6 +20,7 @@ diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
  #include "imgRequestProxy.h"
  #include "imgRequestProxy.h"
  #include "jsapi.h"
  #include "jsapi.h"
  #include "jsfriendapi.h"
  #include "jsfriendapi.h"
+ #include "js/JSON.h"
 +#include "js/RegExp.h"
 +#include "js/RegExp.h"
  #include "js/Value.h"
  #include "js/Value.h"
  #include "Layers.h"
  #include "Layers.h"
@@ -30,7 +30,7 @@ diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
  #include "ImageOps.h"
  #include "ImageOps.h"
  #include "mozAutoDocUpdate.h"
  #include "mozAutoDocUpdate.h"
  #include "mozilla/ArrayUtils.h"
  #include "mozilla/ArrayUtils.h"
-@@ -7348,30 +7349,30 @@ nsContentUtils::IsPatternMatching(nsAStr
+@@ -7349,30 +7350,30 @@ nsContentUtils::IsPatternMatching(nsAStr
    // regexp evaluation, not actual script execution.
    // regexp evaluation, not actual script execution.
    JSAutoRealm ar(cx, xpc::UnprivilegedJunkScope());
    JSAutoRealm ar(cx, xpc::UnprivilegedJunkScope());
  
  
@@ -377,7 +377,7 @@ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
  #endif
  #endif
  #include "frontend/BytecodeCompiler.h"
  #include "frontend/BytecodeCompiler.h"
  #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
  #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
-@@ -6633,148 +6632,16 @@ JS_ObjectIsDate(JSContext* cx, HandleObj
+@@ -6641,148 +6640,16 @@ JS_ObjectIsDate(JSContext* cx, HandleObj
          return false;
          return false;
  
  
      *isDate = cls == ESClass::Date;
      *isDate = cls == ESClass::Date;
@@ -529,7 +529,7 @@ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
 diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 --- a/js/src/jsapi.h
 --- a/js/src/jsapi.h
 +++ b/js/src/jsapi.h
 +++ b/js/src/jsapi.h
-@@ -5326,67 +5326,16 @@ JS_NewDateObject(JSContext* cx, int year
+@@ -4962,67 +4962,16 @@ JS_NewDateObject(JSContext* cx, int year
   * This method returns true with |*isDate == false| when passed a proxy whose
   * This method returns true with |*isDate == false| when passed a proxy whose
   * target is a Date, or when passed a revoked proxy.
   * target is a Date, or when passed a revoked proxy.
   */
   */
@@ -600,7 +600,7 @@ diff --git a/js/src/jsapi.h b/js/src/jsapi.h
 diff --git a/js/src/moz.build b/js/src/moz.build
 diff --git a/js/src/moz.build b/js/src/moz.build
 --- a/js/src/moz.build
 --- a/js/src/moz.build
 +++ b/js/src/moz.build
 +++ b/js/src/moz.build
-@@ -134,16 +134,17 @@ EXPORTS.js += [
+@@ -137,16 +137,17 @@ EXPORTS.js += [
      '../public/Principals.h',
      '../public/Principals.h',
      '../public/Printf.h',
      '../public/Printf.h',
      '../public/ProfilingFrameIterator.h',
      '../public/ProfilingFrameIterator.h',
@@ -621,14 +621,14 @@ diff --git a/js/src/moz.build b/js/src/moz.build
 diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
 --- a/js/src/shell/js.cpp
 --- a/js/src/shell/js.cpp
 +++ b/js/src/shell/js.cpp
 +++ b/js/src/shell/js.cpp
-@@ -74,16 +74,17 @@
- #include "jit/JitcodeMap.h"
+@@ -75,16 +75,17 @@
  #include "jit/JitRealm.h"
  #include "jit/JitRealm.h"
  #include "jit/shared/CodeGenerator-shared.h"
  #include "jit/shared/CodeGenerator-shared.h"
  #include "js/AutoByteString.h"
  #include "js/AutoByteString.h"
  #include "js/Debug.h"
  #include "js/Debug.h"
  #include "js/GCVector.h"
  #include "js/GCVector.h"
  #include "js/Initialization.h"
  #include "js/Initialization.h"
+ #include "js/JSON.h"
  #include "js/Printf.h"
  #include "js/Printf.h"
 +#include "js/RegExp.h"  // JS::ObjectIsRegExp
 +#include "js/RegExp.h"  // JS::ObjectIsRegExp
  #include "js/StableStringChars.h"
  #include "js/StableStringChars.h"
@@ -639,7 +639,7 @@ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
  #include "shell/jsoptparse.h"
  #include "shell/jsoptparse.h"
  #include "shell/jsshell.h"
  #include "shell/jsshell.h"
  #include "shell/OSObject.h"
  #include "shell/OSObject.h"
-@@ -7502,17 +7503,17 @@ Help(JSContext* cx, unsigned argc, Value
+@@ -7503,17 +7504,17 @@ Help(JSContext* cx, unsigned argc, Value
          JS_ReportErrorASCII(cx, "primitive arg");
          JS_ReportErrorASCII(cx, "primitive arg");
          return false;
          return false;
      }
      }

+ 14 - 14
frg/work-js/mozilla-release/patches/TOP-NOBUG-REGEXP-04-1539690-68a1-25318.patch

@@ -1,13 +1,13 @@
 # HG changeset patch
 # HG changeset patch
 # User Dmitry Butskoy <buc@buc.me>
 # User Dmitry Butskoy <buc@buc.me>
 # Date 1690629350 -7200
 # Date 1690629350 -7200
-# Parent  de481a152244153d879e946d7e7f99204889b55a
+# Parent  01111b6bfca044c27f5b74dd392c4b7cb1f8e695
 No Bug - Import new regexp V8 engine. r=frg a=frg
 No Bug - Import new regexp V8 engine. r=frg a=frg
 
 
 diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
 diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
 --- a/dom/base/nsContentUtils.cpp
 --- a/dom/base/nsContentUtils.cpp
 +++ b/dom/base/nsContentUtils.cpp
 +++ b/dom/base/nsContentUtils.cpp
-@@ -7373,17 +7373,17 @@ nsContentUtils::IsPatternMatching(nsAStr
+@@ -7352,17 +7352,17 @@ nsContentUtils::IsPatternMatching(nsAStr
  
  
    // The pattern has to match the entire value.
    // The pattern has to match the entire value.
    aPattern.InsertLiteral(u"^(?:", 0);
    aPattern.InsertLiteral(u"^(?:", 0);
@@ -536,8 +536,7 @@ diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp
 diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
 diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
 --- a/js/src/builtin/TestingFunctions.cpp
 --- a/js/src/builtin/TestingFunctions.cpp
 +++ b/js/src/builtin/TestingFunctions.cpp
 +++ b/js/src/builtin/TestingFunctions.cpp
-@@ -39,16 +39,17 @@
- #endif
+@@ -40,16 +40,17 @@
  #include "gc/Heap.h"
  #include "gc/Heap.h"
  #include "jit/BaselineJIT.h"
  #include "jit/BaselineJIT.h"
  #include "jit/InlinableNatives.h"
  #include "jit/InlinableNatives.h"
@@ -545,6 +544,7 @@ diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctio
  #include "js/AutoByteString.h"
  #include "js/AutoByteString.h"
  #include "js/Debug.h"
  #include "js/Debug.h"
  #include "js/HashTable.h"
  #include "js/HashTable.h"
+ #include "js/LocaleSensitive.h"
 +#include "js/RegExpFlags.h"
 +#include "js/RegExpFlags.h"
  #include "js/StableStringChars.h"
  #include "js/StableStringChars.h"
  #include "js/StructuredClone.h"
  #include "js/StructuredClone.h"
@@ -574,7 +574,7 @@ diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctio
  // OOM conditions.
  // OOM conditions.
  static mozilla::Atomic<bool> disableOOMFunctions(false);
  static mozilla::Atomic<bool> disableOOMFunctions(false);
  
  
-@@ -4792,17 +4796,17 @@ ParseRegExp(JSContext* cx, unsigned argc
+@@ -4754,17 +4758,17 @@ ParseRegExp(JSContext* cx, unsigned argc
          return false;
          return false;
      }
      }
  
  
@@ -593,7 +593,7 @@ diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctio
          RootedString flagStr(cx, args[1].toString());
          RootedString flagStr(cx, args[1].toString());
          if (!ParseRegExpFlags(cx, flagStr, &flags))
          if (!ParseRegExpFlags(cx, flagStr, &flags))
              return false;
              return false;
-@@ -4821,21 +4825,17 @@ ParseRegExp(JSContext* cx, unsigned argc
+@@ -4783,21 +4787,17 @@ ParseRegExp(JSContext* cx, unsigned argc
      if (!pattern)
      if (!pattern)
          return false;
          return false;
  
  
@@ -1164,7 +1164,7 @@ diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
  #include "vm/RegExpStatics.h"
  #include "vm/RegExpStatics.h"
  #include "vm/StringType.h"
  #include "vm/StringType.h"
  #include "vm/TraceLogging.h"
  #include "vm/TraceLogging.h"
-@@ -1482,17 +1483,17 @@ PrepareAndExecuteRegExp(JSContext* cx, M
+@@ -1481,17 +1482,17 @@ PrepareAndExecuteRegExp(JSContext* cx, M
      masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), failure);
      masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), failure);
  
  
      // ES6 21.2.2.2 step 2.
      // ES6 21.2.2.2 step 2.
@@ -1186,7 +1186,7 @@ diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
 diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
 diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
 --- a/js/src/jit/MCallOptimize.cpp
 --- a/js/src/jit/MCallOptimize.cpp
 +++ b/js/src/jit/MCallOptimize.cpp
 +++ b/js/src/jit/MCallOptimize.cpp
-@@ -20,16 +20,17 @@
+@@ -19,16 +19,17 @@
  #include "builtin/TestingFunctions.h"
  #include "builtin/TestingFunctions.h"
  #include "builtin/TypedObject.h"
  #include "builtin/TypedObject.h"
  #include "jit/BaselineInspector.h"
  #include "jit/BaselineInspector.h"
@@ -1204,7 +1204,7 @@ diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
  #include "vm/SharedArrayObject.h"
  #include "vm/SharedArrayObject.h"
  #include "vm/TypedArrayObject.h"
  #include "vm/TypedArrayObject.h"
  
  
-@@ -38,16 +39,18 @@
+@@ -37,16 +38,18 @@
  #include "vm/NativeObject-inl.h"
  #include "vm/NativeObject-inl.h"
  #include "vm/StringObject-inl.h"
  #include "vm/StringObject-inl.h"
  #include "vm/UnboxedObject-inl.h"
  #include "vm/UnboxedObject-inl.h"
@@ -1223,7 +1223,7 @@ diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
  IonBuilder::InliningResult
  IonBuilder::InliningResult
  IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
  IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
  {
  {
-@@ -428,26 +431,26 @@ IonBuilder::inlineNativeGetter(CallInfo&
+@@ -405,26 +408,26 @@ IonBuilder::inlineNativeGetter(CallInfo&
              return InliningStatus_NotInlined;
              return InliningStatus_NotInlined;
  
  
          MInstruction* length = addTypedArrayLength(thisArg);
          MInstruction* length = addTypedArrayLength(thisArg);
@@ -1362,7 +1362,7 @@ diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h
 diff --git a/js/src/moz.build b/js/src/moz.build
 diff --git a/js/src/moz.build b/js/src/moz.build
 --- a/js/src/moz.build
 --- a/js/src/moz.build
 +++ b/js/src/moz.build
 +++ b/js/src/moz.build
-@@ -135,16 +135,17 @@ EXPORTS.js += [
+@@ -138,16 +138,17 @@ EXPORTS.js += [
      '../public/Printf.h',
      '../public/Printf.h',
      '../public/ProfilingFrameIterator.h',
      '../public/ProfilingFrameIterator.h',
      '../public/ProfilingStack.h',
      '../public/ProfilingStack.h',
@@ -1782,7 +1782,7 @@ diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
      size_t len = linear->length();
      size_t len = linear->length();
  
  
      bool ok;
      bool ok;
-@@ -1457,28 +1462,28 @@ js::ParseRegExpFlags(JSContext* cx, JSSt
+@@ -1456,28 +1461,28 @@ js::ParseRegExpFlags(JSContext* cx, JSSt
  
  
  template<XDRMode mode>
  template<XDRMode mode>
  XDRResult
  XDRResult
@@ -1815,7 +1815,7 @@ diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
      }
      }
      return Ok();
      return Ok();
  }
  }
-@@ -1512,37 +1517,37 @@ JS::ubi::Concrete<RegExpShared>::size(mo
+@@ -1511,37 +1516,37 @@ JS::ubi::Concrete<RegExpShared>::size(mo
      return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
      return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
          get().sizeOfExcludingThis(mallocSizeOf);
          get().sizeOfExcludingThis(mallocSizeOf);
  }
  }
@@ -1857,7 +1857,7 @@ diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
  {
  {
      AssertHeapIsIdle();
      AssertHeapIsIdle();
      CHECK_REQUEST(cx);
      CHECK_REQUEST(cx);
-@@ -1615,25 +1620,25 @@ JS::ObjectIsRegExp(JSContext* cx, Handle
+@@ -1614,25 +1619,25 @@ JS::ObjectIsRegExp(JSContext* cx, Handle
      ESClass cls;
      ESClass cls;
      if (!GetBuiltinClass(cx, obj, &cls))
      if (!GetBuiltinClass(cx, obj, &cls))
          return false;
          return false;

+ 198 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478476.patch

@@ -0,0 +1,198 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726335 -32400
+#      Wed Sep 12 13:25:35 2018 +0900
+# Node ID 3d11a950a4806cc825806eff147912e4593b7b1e
+# Parent  f5063652dacb667a451348c37c983cf962eaf324
+Bug 1479659 - Part 0: Fix return value of MUST_MATCH_TOKEN* macros in Parser methods which returns bool. r=jwalden
+
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -70,41 +70,53 @@ using DeclaredNamePtr = ParseContext::Sc
+ using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
+ using BindingIter = ParseContext::Scope::BindingIter;
+ using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
+ 
+ using BindingNameVector = Vector<BindingName, 6>;
+ 
+ // Read a token. Report an error and return null() if that token doesn't match
+ // to the condition.  Do not use MUST_MATCH_TOKEN_INTERNAL directly.
+-#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorReport)                              \
++#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorReport, failureValue)                \
+     JS_BEGIN_MACRO                                                                          \
+         TokenKind token;                                                                    \
+         if (!tokenStream.getToken(&token, modifier))                                        \
+-            return null();                                                                  \
++            return failureValue;                                                            \
+         if (!(cond)) {                                                                      \
+             errorReport;                                                                    \
+-            return null();                                                                  \
++            return failureValue;                                                            \
+         }                                                                                   \
+     JS_END_MACRO
+ 
++#define MUST_MATCH_TOKEN_MOD_OR(tt, modifier, errorNumber, failureValue) \
++    MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, error(errorNumber), failureValue)
++
+ #define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \
+-    MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, error(errorNumber))
++    MUST_MATCH_TOKEN_MOD_OR(tt, modifier, errorNumber, null())
++
++#define MUST_MATCH_TOKEN_OR(tt, errorNumber, failureValue) \
++    MUST_MATCH_TOKEN_MOD_OR(tt, TokenStream::None, errorNumber, failureValue)
+ 
+ #define MUST_MATCH_TOKEN(tt, errorNumber) \
+-    MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber)
+-
+-#define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \
+-    MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, error(errorNumber))
++    MUST_MATCH_TOKEN_OR(tt, errorNumber, null())
++
++#define MUST_MATCH_TOKEN_FUNC_MOD_OR(func, modifier, errorNumber, failureValue) \
++    MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, error(errorNumber), failureValue)
++
++#define MUST_MATCH_TOKEN_FUNC_OR(func, errorNumber, failureValue) \
++    MUST_MATCH_TOKEN_FUNC_MOD_OR(func, TokenStream::None, errorNumber, failureValue)
+ 
+ #define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \
+-    MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber)
++    MUST_MATCH_TOKEN_FUNC_OR(func, errorNumber, null())
++
++#define MUST_MATCH_TOKEN_MOD_WITH_REPORT_OR(tt, modifier, errorReport, failureValue) \
++    MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorReport, failureValue)
+ 
+ #define MUST_MATCH_TOKEN_MOD_WITH_REPORT(tt, modifier, errorReport) \
+-    MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorReport)
++    MUST_MATCH_TOKEN_MOD_WITH_REPORT_OR(tt, modifier, errorReport, null())
+ 
+ template <class T, class U>
+ static inline void
+ PropagateTransitiveParseFlags(const T* inner, U* outer)
+ {
+     if (inner->bindingsAccessedDynamically()) {
+         outer->setBindingsAccessedDynamically();
+     }
+@@ -3536,17 +3548,17 @@ GeneralParser<ParseHandler, CharT>::func
+                 return false;
+             }
+             if (!matched) {
+                 break;
+             }
+ 
+             if (!hasRest) {
+                 if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
+-                    return null();
++                    return false;
+                 }
+                 if (tt == TokenKind::RightParen) {
+                     break;
+                 }
+             }
+         }
+ 
+         if (!parenFreeArrow) {
+@@ -4221,19 +4233,20 @@ GeneralParser<ParseHandler, CharT>::func
+ 
+         uint32_t nameOffset = handler.getFunctionNameOffset(*pn, anyChars);
+         if (!checkBindingIdentifier(propertyName, nameOffset, nameYieldHandling)) {
+             return false;
+         }
+     }
+ 
+     if (bodyType == StatementListBody) {
+-        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
+-                                         reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
+-                                                              JSMSG_CURLY_OPENED, openedPos));
++        MUST_MATCH_TOKEN_MOD_WITH_REPORT_OR(TokenKind::RightCurly, TokenStream::Operand,
++                                            reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
++                                                                 JSMSG_CURLY_OPENED, openedPos),
++                                            false);
+         funbox->setEnd(anyChars);
+     } else {
+         MOZ_ASSERT(kind == FunctionSyntaxKind::Arrow);
+ 
+         if (anyChars.hadError()) {
+             return false;
+         }
+         funbox->setEnd(anyChars);
+@@ -5581,17 +5594,17 @@ Parser<FullParseHandler, CharT>::namedIm
+                 return false;
+             }
+ 
+             Rooted<PropertyName*> importName(context, anyChars.currentName());
+             TokenPos importNamePos = pos();
+ 
+             bool matched;
+             if (!tokenStream.matchToken(&matched, TokenKind::As)) {
+-                return null();
++                return false;
+             }
+ 
+             if (matched) {
+                 TokenKind afterAs;
+                 if (!tokenStream.getToken(&afterAs)) {
+                     return false;
+                 }
+ 
+@@ -5647,19 +5660,19 @@ Parser<FullParseHandler, CharT>::namedIm
+             if (next != TokenKind::Comma) {
+                 error(JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
+                 return false;
+             }
+         }
+     } else {
+         MOZ_ASSERT(tt == TokenKind::Mul);
+ 
+-        MUST_MATCH_TOKEN(TokenKind::As, JSMSG_AS_AFTER_IMPORT_STAR);
+-
+-        MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME);
++        MUST_MATCH_TOKEN_OR(TokenKind::As, JSMSG_AS_AFTER_IMPORT_STAR, false);
++
++        MUST_MATCH_TOKEN_FUNC_OR(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME, false);
+ 
+         Node importName = newName(context->names().star);
+         if (!importName) {
+             return false;
+         }
+ 
+         // Namespace imports are are not indirect bindings but lexical
+         // definitions that hold a module namespace object. They are treated
+@@ -6905,17 +6918,17 @@ GeneralParser<ParseHandler, CharT>::forH
+                                                  ParseNodeKind* forHeadKind, Node* forInitialPart,
+                                                  Maybe<ParseContext::Scope>& forLoopLexicalScope,
+                                                  Node* forInOrOfExpression)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftParen));
+ 
+     TokenKind tt;
+     if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
+-        return null();
++        return false;
+     }
+ 
+     // Super-duper easy case: |for (;| is a C-style for-loop with no init
+     // component.
+     if (tt == TokenKind::Semi) {
+         *forInitialPart = null();
+         *forHeadKind = ParseNodeKind::ForHead;
+         return true;
+@@ -6960,17 +6973,17 @@ GeneralParser<ParseHandler, CharT>::forH
+             anyChars.ungetToken();
+             letIsIdentifier = true;
+         }
+     }
+ 
+     if (parsingLexicalDeclaration) {
+         forLoopLexicalScope.emplace(this);
+         if (!forLoopLexicalScope->init(pc)) {
+-            return null();
++            return false;
+         }
+ 
+         // Push a temporary ForLoopLexicalHead Statement that allows for
+         // lexical declarations, as they are usually allowed only in braced
+         // statements.
+         ParseContext::Statement forHeadStmt(pc, StatementKind::ForLoopLexicalHead);
+ 
+         *forInitialPart = declarationList(yieldHandling,

+ 8467 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478477.patch

@@ -0,0 +1,8467 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726362 -32400
+#      Wed Sep 12 13:26:02 2018 +0900
+# Node ID 4ad37258ece079095198576a0c3b00e13d90205c
+# Parent  3d11a950a4806cc825806eff147912e4593b7b1e
+Bug 1479659 - Part 1: Add accessors to ListNode. r=jwalden
+
+diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
+--- a/js/src/builtin/ModuleObject.cpp
++++ b/js/src/builtin/ModuleObject.cpp
+@@ -1375,17 +1375,17 @@ ModuleBuilder::processImport(frontend::P
+     MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::ImportSpecList));
+     MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::String));
+ 
+     RootedAtom module(cx_, pn->pn_right->pn_atom);
+     if (!maybeAppendRequestedModule(module, pn->pn_right)) {
+         return false;
+     }
+ 
+-    for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
++    for (ParseNode* spec : pn->pn_left->as<ListNode>().contents()) {
+         MOZ_ASSERT(spec->isKind(ParseNodeKind::ImportSpec));
+         MOZ_ASSERT(spec->pn_left->isArity(PN_NAME));
+         MOZ_ASSERT(spec->pn_right->isArity(PN_NAME));
+ 
+         RootedAtom importName(cx_, spec->pn_left->pn_atom);
+         RootedAtom localName(cx_, spec->pn_right->pn_atom);
+ 
+         uint32_t line;
+@@ -1425,17 +1425,17 @@ ModuleBuilder::processExport(frontend::P
+         HandlePropertyName localName = cx_->names().default_;
+         HandlePropertyName exportName = cx_->names().default_;
+         return appendExportEntry(exportName, localName);
+     }
+ 
+     switch (kid->getKind()) {
+       case ParseNodeKind::ExportSpecList:
+         MOZ_ASSERT(!isDefault);
+-        for (ParseNode* spec = kid->pn_head; spec; spec = spec->pn_next) {
++        for (ParseNode* spec : kid->as<ListNode>().contents()) {
+             MOZ_ASSERT(spec->isKind(ParseNodeKind::ExportSpec));
+             RootedAtom localName(cx_, spec->pn_left->pn_atom);
+             RootedAtom exportName(cx_, spec->pn_right->pn_atom);
+             if (!appendExportEntry(exportName, localName, spec)) {
+                 return false;
+             }
+         }
+         break;
+@@ -1449,37 +1449,36 @@ ModuleBuilder::processExport(frontend::P
+             return false;
+         }
+         break;
+       }
+ 
+       case ParseNodeKind::Var:
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let: {
+-        MOZ_ASSERT(kid->isArity(PN_LIST));
+-        for (ParseNode* binding = kid->pn_head; binding; binding = binding->pn_next) {
++        for (ParseNode* binding : kid->as<ListNode>().contents()) {
+             if (binding->isKind(ParseNodeKind::Assign)) {
+                 binding = binding->pn_left;
+             } else {
+                 MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
+             }
+ 
+             if (binding->isKind(ParseNodeKind::Name)) {
+                 RootedAtom localName(cx_, binding->pn_atom);
+                 RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
+                 if (!appendExportEntry(exportName, localName)) {
+                     return false;
+                 }
+             } else if (binding->isKind(ParseNodeKind::Array)) {
+-                if (!processExportArrayBinding(binding)) {
++                if (!processExportArrayBinding(&binding->as<ListNode>())) {
+                     return false;
+                 }
+             } else {
+                 MOZ_ASSERT(binding->isKind(ParseNodeKind::Object));
+-                if (!processExportObjectBinding(binding)) {
++                if (!processExportObjectBinding(&binding->as<ListNode>())) {
+                     return false;
+                 }
+             }
+         }
+         break;
+       }
+ 
+       case ParseNodeKind::Function: {
+@@ -1507,32 +1506,31 @@ ModuleBuilder::processExportBinding(fron
+     using namespace js::frontend;
+ 
+     if (binding->isKind(ParseNodeKind::Name)) {
+         RootedAtom name(cx_, binding->pn_atom);
+         return appendExportEntry(name, name);
+     }
+ 
+     if (binding->isKind(ParseNodeKind::Array)) {
+-        return processExportArrayBinding(binding);
++        return processExportArrayBinding(&binding->as<ListNode>());
+     }
+ 
+     MOZ_ASSERT(binding->isKind(ParseNodeKind::Object));
+-    return processExportObjectBinding(binding);
++    return processExportObjectBinding(&binding->as<ListNode>());
+ }
+ 
+ bool
+-ModuleBuilder::processExportArrayBinding(frontend::ParseNode* pn)
++ModuleBuilder::processExportArrayBinding(frontend::ListNode* array)
+ {
+     using namespace js::frontend;
+ 
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Array));
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
++    MOZ_ASSERT(array->isKind(ParseNodeKind::Array));
+ 
+-    for (ParseNode* node = pn->pn_head; node; node = node->pn_next) {
++    for (ParseNode* node : array->contents()) {
+         if (node->isKind(ParseNodeKind::Elision)) {
+             continue;
+         }
+ 
+         if (node->isKind(ParseNodeKind::Spread)) {
+             node = node->pn_kid;
+         } else if (node->isKind(ParseNodeKind::Assign)) {
+             node = node->pn_left;
+@@ -1542,24 +1540,23 @@ ModuleBuilder::processExportArrayBinding
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-ModuleBuilder::processExportObjectBinding(frontend::ParseNode* pn)
++ModuleBuilder::processExportObjectBinding(frontend::ListNode* obj)
+ {
+     using namespace js::frontend;
+ 
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Object));
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
++    MOZ_ASSERT(obj->isKind(ParseNodeKind::Object));
+ 
+-    for (ParseNode* node = pn->pn_head; node; node = node->pn_next) {
++    for (ParseNode* node : obj->contents()) {
+         MOZ_ASSERT(node->isKind(ParseNodeKind::MutateProto) ||
+                    node->isKind(ParseNodeKind::Colon) ||
+                    node->isKind(ParseNodeKind::Shorthand) ||
+                    node->isKind(ParseNodeKind::Spread));
+ 
+         ParseNode* target;
+         if (node->isKind(ParseNodeKind::Spread)) {
+             target = node->pn_kid;
+@@ -1593,17 +1590,17 @@ ModuleBuilder::processExportFrom(fronten
+     MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::ExportSpecList));
+     MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::String));
+ 
+     RootedAtom module(cx_, pn->pn_right->pn_atom);
+     if (!maybeAppendRequestedModule(module, pn->pn_right)) {
+         return false;
+     }
+ 
+-    for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
++    for (ParseNode* spec : pn->pn_left->as<ListNode>().contents()) {
+         if (spec->isKind(ParseNodeKind::ExportSpec)) {
+             RootedAtom bindingName(cx_, spec->pn_left->pn_atom);
+             RootedAtom exportName(cx_, spec->pn_right->pn_atom);
+             if (!appendExportFromEntry(exportName, module, bindingName, spec->pn_left)) {
+                 return false;
+             }
+         } else {
+             MOZ_ASSERT(spec->isKind(ParseNodeKind::ExportBatchSpec));
+diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h
+--- a/js/src/builtin/ModuleObject.h
++++ b/js/src/builtin/ModuleObject.h
+@@ -21,16 +21,17 @@
+ #include "vm/ProxyObject.h"
+ 
+ namespace js {
+ 
+ class ModuleEnvironmentObject;
+ class ModuleObject;
+ 
+ namespace frontend {
++class ListNode;
+ class ParseNode;
+ class TokenStreamAnyChars;
+ } /* namespace frontend */
+ 
+ typedef Rooted<ModuleObject*> RootedModuleObject;
+ typedef Handle<ModuleObject*> HandleModuleObject;
+ typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
+ typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
+@@ -388,18 +389,18 @@ class MOZ_STACK_CLASS ModuleBuilder
+     RootedAtomSet exportNames_;
+     RootedExportEntryVector localExportEntries_;
+     RootedExportEntryVector indirectExportEntries_;
+     RootedExportEntryVector starExportEntries_;
+ 
+     ImportEntryObject* importEntryFor(JSAtom* localName) const;
+ 
+     bool processExportBinding(frontend::ParseNode* pn);
+-    bool processExportArrayBinding(frontend::ParseNode* pn);
+-    bool processExportObjectBinding(frontend::ParseNode* pn);
++    bool processExportArrayBinding(frontend::ListNode* array);
++    bool processExportObjectBinding(frontend::ListNode* obj);
+ 
+     bool appendImportEntryObject(HandleImportEntryObject importEntry);
+ 
+     bool appendExportEntry(HandleAtom exportName, HandleAtom localName,
+                            frontend::ParseNode* node = nullptr);
+     bool appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,
+                                HandleAtom importName, frontend::ParseNode* node);
+     bool appendExportEntryObject(HandleExportEntryObject exportEntry);
+diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
+--- a/js/src/builtin/ReflectParse.cpp
++++ b/js/src/builtin/ReflectParse.cpp
+@@ -1730,27 +1730,27 @@ class ASTSerializer
+     Value unrootedAtomContents(JSAtom* atom) {
+         return StringValue(atom ? atom : cx->names().empty);
+     }
+ 
+     BinaryOperator binop(ParseNodeKind kind);
+     UnaryOperator unop(ParseNodeKind kind);
+     AssignmentOperator aop(ParseNodeKind kind);
+ 
+-    bool statements(ParseNode* pn, NodeVector& elts);
+-    bool expressions(ParseNode* pn, NodeVector& elts);
+-    bool leftAssociate(ParseNode* pn, MutableHandleValue dst);
+-    bool rightAssociate(ParseNode* pn, MutableHandleValue dst);
+-    bool functionArgs(ParseNode* pn, ParseNode* pnargs,
++    bool statements(ListNode* stmtList, NodeVector& elts);
++    bool expressions(ListNode* exprList, NodeVector& elts);
++    bool leftAssociate(ListNode* node, MutableHandleValue dst);
++    bool rightAssociate(ListNode* node, MutableHandleValue dst);
++    bool functionArgs(ParseNode* pn, ListNode* argsList,
+                       NodeVector& args, NodeVector& defaults, MutableHandleValue rest);
+ 
+     bool sourceElement(ParseNode* pn, MutableHandleValue dst);
+ 
+     bool declaration(ParseNode* pn, MutableHandleValue dst);
+-    bool variableDeclaration(ParseNode* pn, bool lexical, MutableHandleValue dst);
++    bool variableDeclaration(ListNode* declList, bool lexical, MutableHandleValue dst);
+     bool variableDeclarator(ParseNode* pn, MutableHandleValue dst);
+     bool importDeclaration(ParseNode* pn, MutableHandleValue dst);
+     bool importSpecifier(ParseNode* pn, MutableHandleValue dst);
+     bool exportDeclaration(ParseNode* pn, MutableHandleValue dst);
+     bool exportSpecifier(ParseNode* pn, MutableHandleValue dst);
+     bool classDefinition(ParseNode* pn, bool expr, MutableHandleValue dst);
+ 
+     bool optStatement(ParseNode* pn, MutableHandleValue dst) {
+@@ -1762,17 +1762,17 @@ class ASTSerializer
+     }
+ 
+     bool forInit(ParseNode* pn, MutableHandleValue dst);
+     bool forIn(ParseNode* loop, ParseNode* head, HandleValue var, HandleValue stmt,
+                MutableHandleValue dst);
+     bool forOf(ParseNode* loop, ParseNode* head, HandleValue var, HandleValue stmt,
+                MutableHandleValue dst);
+     bool statement(ParseNode* pn, MutableHandleValue dst);
+-    bool blockStatement(ParseNode* pn, MutableHandleValue dst);
++    bool blockStatement(ListNode* node, MutableHandleValue dst);
+     bool switchStatement(ParseNode* pn, MutableHandleValue dst);
+     bool switchCase(ParseNode* pn, MutableHandleValue dst);
+     bool tryStatement(ParseNode* pn, MutableHandleValue dst);
+     bool catchClause(ParseNode* pn, MutableHandleValue dst);
+ 
+     bool optExpression(ParseNode* pn, MutableHandleValue dst) {
+         if (!pn) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+@@ -1804,18 +1804,18 @@ class ASTSerializer
+         if (!pn) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return pattern(pn, dst);
+     }
+ 
+     bool pattern(ParseNode* pn, MutableHandleValue dst);
+-    bool arrayPattern(ParseNode* pn, MutableHandleValue dst);
+-    bool objectPattern(ParseNode* pn, MutableHandleValue dst);
++    bool arrayPattern(ListNode* array, MutableHandleValue dst);
++    bool objectPattern(ListNode* obj, MutableHandleValue dst);
+ 
+     bool function(ParseNode* pn, ASTType type, MutableHandleValue dst);
+     bool functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
+                              bool isAsync, bool isExpression,
+                              MutableHandleValue body, MutableHandleValue rest);
+     bool functionBody(ParseNode* pn, TokenPos* pos, MutableHandleValue dst);
+ 
+   public:
+@@ -1832,17 +1832,17 @@ class ASTSerializer
+         return builder.init(userobj);
+     }
+ 
+     void setParser(Parser<FullParseHandler, char16_t>* p) {
+         parser = p;
+         builder.setTokenStream(&p->anyChars);
+     }
+ 
+-    bool program(ParseNode* pn, MutableHandleValue dst);
++    bool program(ListNode* node, MutableHandleValue dst);
+ };
+ 
+ } /* anonymous namespace */
+ 
+ AssignmentOperator
+ ASTSerializer::aop(ParseNodeKind kind)
+ {
+     switch (kind) {
+@@ -1957,76 +1957,75 @@ ASTSerializer::binop(ParseNodeKind kind)
+       case ParseNodeKind::Pipeline:
+         return BINOP_PIPELINE;
+       default:
+         return BINOP_ERR;
+     }
+ }
+ 
+ bool
+-ASTSerializer::statements(ParseNode* pn, NodeVector& elts)
++ASTSerializer::statements(ListNode* stmtList, NodeVector& elts)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::StatementList));
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-
+-    if (!elts.reserve(pn->pn_count)) {
++    MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
++
++    if (!elts.reserve(stmtList->count())) {
+         return false;
+     }
+ 
+-    for (ParseNode* next = pn->pn_head; next; next = next->pn_next) {
+-        MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
++    for (ParseNode* stmt : stmtList->contents()) {
++        MOZ_ASSERT(stmtList->pn_pos.encloses(stmt->pn_pos));
+ 
+         RootedValue elt(cx);
+-        if (!sourceElement(next, &elt)) {
++        if (!sourceElement(stmt, &elt)) {
+             return false;
+         }
+         elts.infallibleAppend(elt);
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-ASTSerializer::expressions(ParseNode* pn, NodeVector& elts)
++ASTSerializer::expressions(ListNode* exprList, NodeVector& elts)
+ {
+-    if (!elts.reserve(pn->pn_count)) {
++    if (!elts.reserve(exprList->count())) {
+         return false;
+     }
+ 
+-    for (ParseNode* next = pn->pn_head; next; next = next->pn_next) {
+-        MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
++    for (ParseNode* expr : exprList->contents()) {
++        MOZ_ASSERT(exprList->pn_pos.encloses(expr->pn_pos));
+ 
+         RootedValue elt(cx);
+-        if (!expression(next, &elt)) {
++        if (!expression(expr, &elt)) {
+             return false;
+         }
+         elts.infallibleAppend(elt);
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-ASTSerializer::blockStatement(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::blockStatement(ListNode* node, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::StatementList));
++    MOZ_ASSERT(node->isKind(ParseNodeKind::StatementList));
+ 
+     NodeVector stmts(cx);
+-    return statements(pn, stmts) &&
+-           builder.blockStatement(stmts, &pn->pn_pos, dst);
++    return statements(node, stmts) &&
++           builder.blockStatement(stmts, &node->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::program(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::program(ListNode* node, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(parser->anyChars.srcCoords.lineNum(pn->pn_pos.begin) == lineno);
++    MOZ_ASSERT(parser->anyChars.srcCoords.lineNum(node->pn_pos.begin) == lineno);
+ 
+     NodeVector stmts(cx);
+-    return statements(pn, stmts) &&
+-           builder.program(stmts, &pn->pn_pos, dst);
++    return statements(node, stmts) &&
++           builder.program(stmts, &node->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::sourceElement(ParseNode* pn, MutableHandleValue dst)
+ {
+     /* SpiderMonkey allows declarations even in pure statement contexts. */
+     return statement(pn, dst);
+ }
+@@ -2039,51 +2038,52 @@ ASTSerializer::declaration(ParseNode* pn
+                pn->isKind(ParseNodeKind::Let) ||
+                pn->isKind(ParseNodeKind::Const));
+ 
+     switch (pn->getKind()) {
+       case ParseNodeKind::Function:
+         return function(pn, AST_FUNC_DECL, dst);
+ 
+       case ParseNodeKind::Var:
+-        return variableDeclaration(pn, false, dst);
++        return variableDeclaration(&pn->as<ListNode>(), false, dst);
+ 
+       default:
+         MOZ_ASSERT(pn->isKind(ParseNodeKind::Let) || pn->isKind(ParseNodeKind::Const));
+-        return variableDeclaration(pn, true, dst);
++        return variableDeclaration(&pn->as<ListNode>(), true, dst);
+     }
+ }
+ 
+ bool
+-ASTSerializer::variableDeclaration(ParseNode* pn, bool lexical, MutableHandleValue dst)
++ASTSerializer::variableDeclaration(ListNode* declList, bool lexical, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT_IF(lexical, pn->isKind(ParseNodeKind::Let) || pn->isKind(ParseNodeKind::Const));
+-    MOZ_ASSERT_IF(!lexical, pn->isKind(ParseNodeKind::Var));
++    MOZ_ASSERT_IF(lexical,
++                  declList->isKind(ParseNodeKind::Let) || declList->isKind(ParseNodeKind::Const));
++    MOZ_ASSERT_IF(!lexical, declList->isKind(ParseNodeKind::Var));
+ 
+     VarDeclKind kind = VARDECL_ERR;
+     // Treat both the toplevel const binding (secretly var-like) and the lexical const
+     // the same way
+     if (lexical) {
+-        kind = pn->isKind(ParseNodeKind::Let) ? VARDECL_LET : VARDECL_CONST;
++        kind = declList->isKind(ParseNodeKind::Let) ? VARDECL_LET : VARDECL_CONST;
+     } else {
+-        kind = pn->isKind(ParseNodeKind::Var) ? VARDECL_VAR : VARDECL_CONST;
++        kind = declList->isKind(ParseNodeKind::Var) ? VARDECL_VAR : VARDECL_CONST;
+     }
+ 
+     NodeVector dtors(cx);
+-    if (!dtors.reserve(pn->pn_count)) {
++    if (!dtors.reserve(declList->count())) {
+         return false;
+     }
+-    for (ParseNode* next = pn->pn_head; next; next = next->pn_next) {
++    for (ParseNode* decl : declList->contents()) {
+         RootedValue child(cx);
+-        if (!variableDeclarator(next, &child)) {
++        if (!variableDeclarator(decl, &child)) {
+             return false;
+         }
+         dtors.infallibleAppend(child);
+     }
+-    return builder.variableDeclaration(dtors, kind, &pn->pn_pos, dst);
++    return builder.variableDeclaration(dtors, kind, &declList->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::variableDeclarator(ParseNode* pn, MutableHandleValue dst)
+ {
+     ParseNode* pnleft;
+     ParseNode* pnright;
+ 
+@@ -2111,24 +2111,26 @@ ASTSerializer::variableDeclarator(ParseN
+ bool
+ ASTSerializer::importDeclaration(ParseNode* pn, MutableHandleValue dst)
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Import));
+     MOZ_ASSERT(pn->isArity(PN_BINARY));
+     MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::ImportSpecList));
+     MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::String));
+ 
++    ListNode* specList = &pn->pn_left->as<ListNode>();
++
+     NodeVector elts(cx);
+-    if (!elts.reserve(pn->pn_left->pn_count)) {
++    if (!elts.reserve(specList->count())) {
+         return false;
+     }
+ 
+-    for (ParseNode* next = pn->pn_left->pn_head; next; next = next->pn_next) {
++    for (ParseNode* spec : specList->contents()) {
+         RootedValue elt(cx);
+-        if (!importSpecifier(next, &elt)) {
++        if (!importSpecifier(spec, &elt)) {
+             return false;
+         }
+         elts.infallibleAppend(elt);
+     }
+ 
+     RootedValue moduleSpec(cx);
+     return literal(pn->pn_right, &moduleSpec) &&
+            builder.importDeclaration(elts, moduleSpec, &pn->pn_pos, dst);
+@@ -2155,52 +2157,54 @@ ASTSerializer::exportDeclaration(ParseNo
+     MOZ_ASSERT(pn->getArity() == (pn->isKind(ParseNodeKind::Export) ? PN_UNARY : PN_BINARY));
+     MOZ_ASSERT_IF(pn->isKind(ParseNodeKind::ExportFrom), pn->pn_right->isKind(ParseNodeKind::String));
+ 
+     RootedValue decl(cx, NullValue());
+     NodeVector elts(cx);
+ 
+     ParseNode* kid = pn->isKind(ParseNodeKind::Export) ? pn->pn_kid : pn->pn_left;
+     switch (ParseNodeKind kind = kid->getKind()) {
+-      case ParseNodeKind::ExportSpecList:
+-        if (!elts.reserve(pn->pn_left->pn_count)) {
++      case ParseNodeKind::ExportSpecList: {
++        ListNode* specList = &pn->pn_left->as<ListNode>();
++        if (!elts.reserve(specList->count())) {
+             return false;
+         }
+ 
+-        for (ParseNode* next = pn->pn_left->pn_head; next; next = next->pn_next) {
++        for (ParseNode* spec : specList->contents()) {
+             RootedValue elt(cx);
+-            if (next->isKind(ParseNodeKind::ExportSpec)) {
+-                if (!exportSpecifier(next, &elt)) {
++            if (spec->isKind(ParseNodeKind::ExportSpec)) {
++                if (!exportSpecifier(spec, &elt)) {
+                     return false;
+                 }
+             } else {
+                 if (!builder.exportBatchSpecifier(&pn->pn_pos, &elt)) {
+                     return false;
+                 }
+             }
+             elts.infallibleAppend(elt);
+         }
+         break;
++      }
+ 
+       case ParseNodeKind::Function:
+         if (!function(kid, AST_FUNC_DECL, &decl)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Class:
+         if (!classDefinition(kid, false, &decl)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Var:
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+-        if (!variableDeclaration(kid, kind != ParseNodeKind::Var, &decl)) {
++        if (!variableDeclaration(&kid->as<ListNode>(), kind != ParseNodeKind::Var, &decl)) {
+             return false;
+         }
+         break;
+ 
+       default:
+           if (!expression(kid, &decl)) {
+               return false;
+           }
+@@ -2254,41 +2258,34 @@ ASTSerializer::switchStatement(ParseNode
+     MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+ 
+     RootedValue disc(cx);
+ 
+     if (!expression(pn->pn_left, &disc)) {
+         return false;
+     }
+ 
+-    ParseNode* listNode;
+-    bool lexical;
+-
+-    if (pn->pn_right->isKind(ParseNodeKind::LexicalScope)) {
+-        listNode = pn->pn_right->pn_expr;
+-        lexical = true;
+-    } else {
+-        listNode = pn->pn_right;
+-        lexical = false;
+-    }
++    MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::LexicalScope));
++    ListNode* caseList = &pn->pn_right->scopeBody()->as<ListNode>();
+ 
+     NodeVector cases(cx);
+-    if (!cases.reserve(listNode->pn_count)) {
++    if (!cases.reserve(caseList->count())) {
+         return false;
+     }
+ 
+-    for (ParseNode* next = listNode->pn_head; next; next = next->pn_next) {
++    for (ParseNode* caseNode : caseList->contents()) {
+         RootedValue child(cx);
+-        if (!switchCase(next, &child)) {
++        if (!switchCase(caseNode, &child)) {
+             return false;
+         }
+         cases.infallibleAppend(child);
+     }
+ 
+-    return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst);
++    // `lexical` field is always true.
++    return builder.switchStatement(disc, cases, true, &pn->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::catchClause(ParseNode* pn, MutableHandleValue dst)
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Catch));
+     MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
+     MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+@@ -2333,17 +2330,17 @@ ASTSerializer::forInit(ParseNode* pn, Mu
+ {
+     if (!pn) {
+         dst.setMagic(JS_SERIALIZE_NO_NODE);
+         return true;
+     }
+ 
+     bool lexical = pn->isKind(ParseNodeKind::Let) || pn->isKind(ParseNodeKind::Const);
+     return (lexical || pn->isKind(ParseNodeKind::Var))
+-           ? variableDeclaration(pn, lexical, dst)
++           ? variableDeclaration(&pn->as<ListNode>(), lexical, dst)
+            : expression(pn, dst);
+ }
+ 
+ bool
+ ASTSerializer::forOf(ParseNode* loop, ParseNode* head, HandleValue var, HandleValue stmt,
+                          MutableHandleValue dst)
+ {
+     RootedValue expr(cx);
+@@ -2417,17 +2414,17 @@ ASTSerializer::statement(ParseNode* pn, 
+       case ParseNodeKind::LexicalScope:
+         pn = pn->pn_expr;
+         if (!pn->isKind(ParseNodeKind::StatementList)) {
+             return statement(pn, dst);
+         }
+         MOZ_FALLTHROUGH;
+ 
+       case ParseNodeKind::StatementList:
+-        return blockStatement(pn, dst);
++        return blockStatement(&pn->as<ListNode>(), dst);
+ 
+       case ParseNodeKind::If:
+       {
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+         MOZ_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
+ 
+         RootedValue test(cx), cons(cx), alt(cx);
+@@ -2485,28 +2482,28 @@ ASTSerializer::statement(ParseNode* pn, 
+         RootedValue stmt(cx);
+         if (!statement(pn->pn_right, &stmt)) {
+             return false;
+         }
+ 
+         if (head->isKind(ParseNodeKind::ForIn) || head->isKind(ParseNodeKind::ForOf)) {
+             RootedValue var(cx);
+             if (head->pn_kid1->isKind(ParseNodeKind::LexicalScope)) {
+-                if (!variableDeclaration(head->pn_kid1->pn_expr, true, &var)) {
++                if (!variableDeclaration(&head->pn_kid1->scopeBody()->as<ListNode>(), true, &var)) {
+                     return false;
+                 }
+             } else if (!head->pn_kid1->isKind(ParseNodeKind::Var) &&
+                        !head->pn_kid1->isKind(ParseNodeKind::Let) &&
+                        !head->pn_kid1->isKind(ParseNodeKind::Const))
+             {
+                 if (!pattern(head->pn_kid1, &var)) {
+                     return false;
+                 }
+             } else {
+-                if (!variableDeclaration(head->pn_kid1,
++                if (!variableDeclaration(&head->pn_kid1->as<ListNode>(),
+                                          head->pn_kid1->isKind(ParseNodeKind::Let) ||
+                                          head->pn_kid1->isKind(ParseNodeKind::Const),
+                                          &var))
+                 {
+                     return false;
+                 }
+             }
+             if (head->isKind(ParseNodeKind::ForIn)) {
+@@ -2568,26 +2565,27 @@ ASTSerializer::statement(ParseNode* pn, 
+       case ParseNodeKind::Debugger:
+         return builder.debuggerStatement(&pn->pn_pos, dst);
+ 
+       case ParseNodeKind::Class:
+         return classDefinition(pn, false, dst);
+ 
+       case ParseNodeKind::ClassMethodList:
+       {
++        ListNode* methodList = &pn->as<ListNode>();
+         NodeVector methods(cx);
+-        if (!methods.reserve(pn->pn_count)) {
++        if (!methods.reserve(methodList->count())) {
+             return false;
+         }
+ 
+-        for (ParseNode* next = pn->pn_head; next; next = next->pn_next) {
+-            MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
++        for (ParseNode* method : methodList->contents()) {
++            MOZ_ASSERT(methodList->pn_pos.encloses(method->pn_pos));
+ 
+             RootedValue prop(cx);
+-            if (!classMethod(next, &prop)) {
++            if (!classMethod(method, &prop)) {
+                 return false;
+             }
+             methods.infallibleAppend(prop);
+         }
+ 
+         return builder.classMethods(methods, dst);
+       }
+ 
+@@ -2620,66 +2618,64 @@ ASTSerializer::classMethod(ParseNode* pn
+     RootedValue key(cx), val(cx);
+     bool isStatic = pn->as<ClassMethod>().isStatic();
+     return propertyName(pn->pn_left, &key) &&
+            expression(pn->pn_right, &val) &&
+            builder.classMethod(key, val, kind, isStatic, &pn->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::leftAssociate(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::leftAssociate(ListNode* node, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    MOZ_ASSERT(pn->pn_count >= 1);
+-
+-    ParseNodeKind kind = pn->getKind();
++    MOZ_ASSERT(!node->empty());
++
++    ParseNodeKind kind = node->getKind();
+     bool lor = kind == ParseNodeKind::Or;
+     bool logop = lor || (kind == ParseNodeKind::And);
+ 
+-    ParseNode* head = pn->pn_head;
++    ParseNode* head = node->head();
+     RootedValue left(cx);
+     if (!expression(head, &left)) {
+         return false;
+     }
+-    for (ParseNode* next = head->pn_next; next; next = next->pn_next) {
++    for (ParseNode* next : node->contentsFrom(head->pn_next)) {
+         RootedValue right(cx);
+         if (!expression(next, &right)) {
+             return false;
+         }
+ 
+-        TokenPos subpos(pn->pn_pos.begin, next->pn_pos.end);
++        TokenPos subpos(node->pn_pos.begin, next->pn_pos.end);
+ 
+         if (logop) {
+             if (!builder.logicalExpression(lor, left, right, &subpos, &left)) {
+                 return false;
+             }
+         } else {
+-            BinaryOperator op = binop(pn->getKind());
++            BinaryOperator op = binop(node->getKind());
+             LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
+ 
+             if (!builder.binaryExpression(op, left, right, &subpos, &left)) {
+                 return false;
+             }
+         }
+     }
+ 
+     dst.set(left);
+     return true;
+ }
+ 
+ bool
+-ASTSerializer::rightAssociate(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::rightAssociate(ListNode* node, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    MOZ_ASSERT(pn->pn_count >= 1);
++    MOZ_ASSERT(!node->empty());
+ 
+     // First, we need to reverse the list, so that we can traverse it in the right order.
+     // It's OK to destructively reverse the list, because there are no other consumers.
+ 
+-    ParseNode* head = pn->pn_head;
++    ParseNode* head = node->head();
+     ParseNode* prev = nullptr;
+     ParseNode* current = head;
+     ParseNode* next;
+     while (current != nullptr) {
+         next = current->pn_next;
+         current->pn_next = prev;
+         prev = current;
+         current = next;
+@@ -2692,19 +2688,19 @@ ASTSerializer::rightAssociate(ParseNode*
+         return false;
+     }
+     for (ParseNode* next = head->pn_next; next; next = next->pn_next) {
+         RootedValue left(cx);
+         if (!expression(next, &left)) {
+             return false;
+         }
+ 
+-        TokenPos subpos(pn->pn_pos.begin, next->pn_pos.end);
+-
+-        BinaryOperator op = binop(pn->getKind());
++        TokenPos subpos(node->pn_pos.begin, next->pn_pos.end);
++
++        BinaryOperator op = binop(node->getKind());
+         LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
+ 
+         if (!builder.binaryExpression(op, left, right, &subpos, &right)) {
+             return false;
+         }
+     }
+ 
+     dst.set(right);
+@@ -2723,17 +2719,17 @@ ASTSerializer::expression(ParseNode* pn,
+       {
+         ASTType type = pn->pn_funbox->function()->isArrow() ? AST_ARROW_EXPR : AST_FUNC_EXPR;
+         return function(pn, type, dst);
+       }
+ 
+       case ParseNodeKind::Comma:
+       {
+         NodeVector exprs(cx);
+-        return expressions(pn, exprs) &&
++        return expressions(&pn->as<ListNode>(), exprs) &&
+                builder.sequenceExpression(exprs, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Conditional:
+       {
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
+@@ -2743,17 +2739,17 @@ ASTSerializer::expression(ParseNode* pn,
+         return expression(pn->pn_kid1, &test) &&
+                expression(pn->pn_kid2, &cons) &&
+                expression(pn->pn_kid3, &alt) &&
+                builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Or:
+       case ParseNodeKind::And:
+-        return leftAssociate(pn, dst);
++        return leftAssociate(&pn->as<ListNode>(), dst);
+ 
+       case ParseNodeKind::PreIncrement:
+       case ParseNodeKind::PreDecrement:
+       {
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+ 
+         bool inc = pn->isKind(ParseNodeKind::PreIncrement);
+         RootedValue expr(cx);
+@@ -2815,20 +2811,20 @@ ASTSerializer::expression(ParseNode* pn,
+       case ParseNodeKind::Star:
+       case ParseNodeKind::Div:
+       case ParseNodeKind::Mod:
+       case ParseNodeKind::BitOr:
+       case ParseNodeKind::BitXor:
+       case ParseNodeKind::BitAnd:
+       case ParseNodeKind::In:
+       case ParseNodeKind::InstanceOf:
+-        return leftAssociate(pn, dst);
++        return leftAssociate(&pn->as<ListNode>(), dst);
+ 
+       case ParseNodeKind::Pow:
+-        return rightAssociate(pn, dst);
++        return rightAssociate(&pn->as<ListNode>(), dst);
+ 
+       case ParseNodeKind::DeleteName:
+       case ParseNodeKind::DeleteProp:
+       case ParseNodeKind::DeleteElem:
+       case ParseNodeKind::DeleteExpr:
+       case ParseNodeKind::TypeOfName:
+       case ParseNodeKind::TypeOfExpr:
+       case ParseNodeKind::Void:
+@@ -2848,41 +2844,41 @@ ASTSerializer::expression(ParseNode* pn,
+       }
+ 
+       case ParseNodeKind::New:
+       case ParseNodeKind::TaggedTemplate:
+       case ParseNodeKind::Call:
+       case ParseNodeKind::SuperCall:
+       {
+         ParseNode* pn_callee = pn->pn_left;
+-        ParseNode* pn_args = pn->pn_right;
++        ListNode* argsList = &pn->pn_right->as<ListNode>();
+         MOZ_ASSERT(pn->pn_pos.encloses(pn_callee->pn_pos));
+ 
+         RootedValue callee(cx);
+         if (pn->isKind(ParseNodeKind::SuperCall)) {
+             MOZ_ASSERT(pn_callee->isKind(ParseNodeKind::SuperBase));
+             if (!builder.super(&pn_callee->pn_pos, &callee)) {
+                 return false;
+             }
+         } else {
+             if (!expression(pn_callee, &callee)) {
+                 return false;
+             }
+         }
+ 
+         NodeVector args(cx);
+-        if (!args.reserve(pn_args->pn_count)) {
++        if (!args.reserve(argsList->count())) {
+             return false;
+         }
+ 
+-        for (ParseNode* next = pn_args->pn_head; next; next = next->pn_next) {
+-            MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
++        for (ParseNode* argNode : argsList->contents()) {
++            MOZ_ASSERT(pn->pn_pos.encloses(argNode->pn_pos));
+ 
+             RootedValue arg(cx);
+-            if (!expression(next, &arg)) {
++            if (!expression(argNode, &arg)) {
+                 return false;
+             }
+             args.infallibleAppend(arg);
+         }
+ 
+         if (pn->getKind() == ParseNodeKind::TaggedTemplate) {
+             return builder.taggedTemplate(callee, args, &pn->pn_pos, dst);
+         }
+@@ -2934,71 +2930,76 @@ ASTSerializer::expression(ParseNode* pn,
+         }
+ 
+         return expression(pn->pn_right, &right) &&
+                builder.memberExpression(true, left, right, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::CallSiteObj:
+       {
++        CallSiteNode* callSiteObj = &pn->as<CallSiteNode>();
++        ListNode* rawNodes = callSiteObj->rawNodes();
+         NodeVector raw(cx);
+-        if (!raw.reserve(pn->pn_head->pn_count)) {
++        if (!raw.reserve(rawNodes->count())) {
+             return false;
+         }
+-        for (ParseNode* next = pn->pn_head->pn_head; next; next = next->pn_next) {
+-            MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
++        for (ParseNode* rawItem : rawNodes->contents()) {
++            MOZ_ASSERT(callSiteObj->pn_pos.encloses(rawItem->pn_pos));
+ 
+             RootedValue expr(cx);
+-            expr.setString(next->pn_atom);
++            expr.setString(rawItem->pn_atom);
+             raw.infallibleAppend(expr);
+         }
+ 
+         NodeVector cooked(cx);
+-        if (!cooked.reserve(pn->pn_count - 1)) {
++        if (!cooked.reserve(callSiteObj->count() - 1)) {
+             return false;
+         }
+ 
+-        for (ParseNode* next = pn->pn_head->pn_next; next; next = next->pn_next) {
+-            MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
++        for (ParseNode* cookedItem = callSiteObj->head()->pn_next;
++             cookedItem;
++             cookedItem = cookedItem->pn_next) {
++            MOZ_ASSERT(callSiteObj->pn_pos.encloses(cookedItem->pn_pos));
+ 
+             RootedValue expr(cx);
+-            if (next->isKind(ParseNodeKind::RawUndefined)) {
++            if (cookedItem->isKind(ParseNodeKind::RawUndefined)) {
+                 expr.setUndefined();
+             } else {
+-                MOZ_ASSERT(next->isKind(ParseNodeKind::TemplateString));
+-                expr.setString(next->pn_atom);
++                MOZ_ASSERT(cookedItem->isKind(ParseNodeKind::TemplateString));
++                expr.setString(cookedItem->pn_atom);
+             }
+             cooked.infallibleAppend(expr);
+         }
+ 
+-        return builder.callSiteObj(raw, cooked, &pn->pn_pos, dst);
++        return builder.callSiteObj(raw, cooked, &callSiteObj->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Array:
+       {
++        ListNode* array = &pn->as<ListNode>();
+         NodeVector elts(cx);
+-        if (!elts.reserve(pn->pn_count)) {
++        if (!elts.reserve(array->count())) {
+             return false;
+         }
+ 
+-        for (ParseNode* next = pn->pn_head; next; next = next->pn_next) {
+-            MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
+-
+-            if (next->isKind(ParseNodeKind::Elision)) {
++        for (ParseNode* item : array->contents()) {
++            MOZ_ASSERT(array->pn_pos.encloses(item->pn_pos));
++
++            if (item->isKind(ParseNodeKind::Elision)) {
+                 elts.infallibleAppend(NullValue());
+             } else {
+                 RootedValue expr(cx);
+-                if (!expression(next, &expr)) {
++                if (!expression(item, &expr)) {
+                     return false;
+                 }
+                 elts.infallibleAppend(expr);
+             }
+         }
+ 
+-        return builder.arrayExpression(elts, &pn->pn_pos, dst);
++        return builder.arrayExpression(elts, &array->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Spread:
+       {
+           RootedValue expr(cx);
+           return expression(pn->pn_kid, &expr) &&
+                  builder.spreadExpression(expr, &pn->pn_pos, dst);
+       }
+@@ -3007,58 +3008,60 @@ ASTSerializer::expression(ParseNode* pn,
+       {
+          RootedValue name(cx);
+          return expression(pn->pn_kid, &name) &&
+                 builder.computedName(name, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Object:
+       {
++        ListNode* obj = &pn->as<ListNode>();
+         NodeVector elts(cx);
+-        if (!elts.reserve(pn->pn_count)) {
++        if (!elts.reserve(obj->count())) {
+             return false;
+         }
+ 
+-        for (ParseNode* next = pn->pn_head; next; next = next->pn_next) {
+-            MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
++        for (ParseNode* item : obj->contents()) {
++            MOZ_ASSERT(obj->pn_pos.encloses(item->pn_pos));
+ 
+             RootedValue prop(cx);
+-            if (!property(next, &prop)) {
++            if (!property(item, &prop)) {
+                 return false;
+             }
+             elts.infallibleAppend(prop);
+         }
+ 
+-        return builder.objectExpression(elts, &pn->pn_pos, dst);
++        return builder.objectExpression(elts, &obj->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Name:
+         return identifier(pn, dst);
+ 
+       case ParseNodeKind::This:
+         return builder.thisExpression(&pn->pn_pos, dst);
+ 
+       case ParseNodeKind::TemplateStringList:
+       {
++        ListNode* list = &pn->as<ListNode>();
+         NodeVector elts(cx);
+-        if (!elts.reserve(pn->pn_count)) {
++        if (!elts.reserve(list->count())) {
+             return false;
+         }
+ 
+-        for (ParseNode* next = pn->pn_head; next; next = next->pn_next) {
+-            MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
++        for (ParseNode* item : list->contents()) {
++            MOZ_ASSERT(list->pn_pos.encloses(item->pn_pos));
+ 
+             RootedValue expr(cx);
+-            if (!expression(next, &expr)) {
++            if (!expression(item, &expr)) {
+                 return false;
+             }
+             elts.infallibleAppend(expr);
+         }
+ 
+-        return builder.templateLiteral(elts, &pn->pn_pos, dst);
++        return builder.templateLiteral(elts, &list->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::TemplateString:
+       case ParseNodeKind::String:
+       case ParseNodeKind::RegExp:
+       case ParseNodeKind::Number:
+       case ParseNodeKind::True:
+       case ParseNodeKind::False:
+@@ -3242,60 +3245,60 @@ ASTSerializer::literal(ParseNode* pn, Mu
+       default:
+         LOCAL_NOT_REACHED("unexpected literal type");
+     }
+ 
+     return builder.literal(val, &pn->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::arrayPattern(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::arrayPattern(ListNode* array, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Array));
++    MOZ_ASSERT(array->isKind(ParseNodeKind::Array));
+ 
+     NodeVector elts(cx);
+-    if (!elts.reserve(pn->pn_count)) {
++    if (!elts.reserve(array->count())) {
+         return false;
+     }
+ 
+-    for (ParseNode* next = pn->pn_head; next; next = next->pn_next) {
+-        if (next->isKind(ParseNodeKind::Elision)) {
++    for (ParseNode* item : array->contents()) {
++        if (item->isKind(ParseNodeKind::Elision)) {
+             elts.infallibleAppend(NullValue());
+-        } else if (next->isKind(ParseNodeKind::Spread)) {
++        } else if (item->isKind(ParseNodeKind::Spread)) {
+             RootedValue target(cx);
+             RootedValue spread(cx);
+-            if (!pattern(next->pn_kid, &target)) {
++            if (!pattern(item->pn_kid, &target)) {
+                 return false;
+             }
+-            if(!builder.spreadExpression(target, &next->pn_pos, &spread))
++            if(!builder.spreadExpression(target, &item->pn_pos, &spread))
+                 return false;
+             elts.infallibleAppend(spread);
+         } else {
+             RootedValue patt(cx);
+-            if (!pattern(next, &patt)) {
++            if (!pattern(item, &patt)) {
+                 return false;
+             }
+             elts.infallibleAppend(patt);
+         }
+     }
+ 
+-    return builder.arrayPattern(elts, &pn->pn_pos, dst);
++    return builder.arrayPattern(elts, &array->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::objectPattern(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::objectPattern(ListNode* obj, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Object));
++    MOZ_ASSERT(obj->isKind(ParseNodeKind::Object));
+ 
+     NodeVector elts(cx);
+-    if (!elts.reserve(pn->pn_count)) {
++    if (!elts.reserve(obj->count())) {
+         return false;
+     }
+ 
+-    for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
++    for (ParseNode* propdef : obj->contents()) {
+         if (propdef->isKind(ParseNodeKind::Spread)) {
+             RootedValue target(cx);
+             RootedValue spread(cx);
+             if (!pattern(propdef->pn_kid, &target)) {
+                 return false;
+             }
+             if(!builder.spreadExpression(target, &propdef->pn_pos, &spread))
+                 return false;
+@@ -3326,32 +3329,32 @@ ASTSerializer::objectPattern(ParseNode* 
+                                      &prop))
+         {
+             return false;
+         }
+ 
+         elts.infallibleAppend(prop);
+     }
+ 
+-    return builder.objectPattern(elts, &pn->pn_pos, dst);
++    return builder.objectPattern(elts, &obj->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::pattern(ParseNode* pn, MutableHandleValue dst)
+ {
+     if (!CheckRecursionLimit(cx)) {
+         return false;
+     }
+ 
+     switch (pn->getKind()) {
+       case ParseNodeKind::Object:
+-        return objectPattern(pn, dst);
++        return objectPattern(&pn->as<ListNode>(), dst);
+ 
+       case ParseNodeKind::Array:
+-        return arrayPattern(pn, dst);
++        return arrayPattern(&pn->as<ListNode>(), dst);
+ 
+       default:
+         return expression(pn, dst);
+     }
+ }
+ 
+ bool
+ ASTSerializer::identifier(HandleAtom atom, TokenPos* pos, MutableHandleValue dst)
+@@ -3403,80 +3406,80 @@ ASTSerializer::function(ParseNode* pn, A
+                             rest, generatorStyle, isAsync, isExpression, dst);
+ }
+ 
+ bool
+ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
+                                    bool isAsync, bool isExpression,
+                                    MutableHandleValue body, MutableHandleValue rest)
+ {
+-    ParseNode* pnargs;
++    ListNode* argsList;
+     ParseNode* pnbody;
+ 
+     /* Extract the args and body separately. */
+     if (pn->isKind(ParseNodeKind::ParamsBody)) {
+-        pnargs = pn;
+-        pnbody = pn->last();
++        argsList = &pn->as<ListNode>();
++        pnbody = argsList->last();
+     } else {
+-        pnargs = nullptr;
++        argsList = nullptr;
+         pnbody = pn;
+     }
+ 
+     if (pnbody->isKind(ParseNodeKind::LexicalScope)) {
+         pnbody = pnbody->scopeBody();
+     }
+ 
+     /* Serialize the arguments and body. */
+     switch (pnbody->getKind()) {
+       case ParseNodeKind::Return: /* expression closure, no destructured args */
+-        return functionArgs(pn, pnargs, args, defaults, rest) &&
++        return functionArgs(pn, argsList, args, defaults, rest) &&
+                expression(pnbody->pn_kid, body);
+ 
+       case ParseNodeKind::StatementList:     /* statement closure */
+       {
+-        ParseNode* pnstart = pnbody->pn_head;
++        ParseNode* firstNode = pnbody->as<ListNode>().head();
+ 
+         // Skip over initial yield in generator.
+-        if (pnstart && pnstart->isKind(ParseNodeKind::InitialYield)) {
+-            pnstart = pnstart->pn_next;
++        if (firstNode && firstNode->isKind(ParseNodeKind::InitialYield)) {
++            firstNode = firstNode->pn_next;
+         }
+ 
+         // Async arrow with expression body is converted into STATEMENTLIST
+         // to insert initial yield.
+         if (isAsync && isExpression) {
+-            MOZ_ASSERT(pnstart->getKind() == ParseNodeKind::Return);
+-            return functionArgs(pn, pnargs, args, defaults, rest) &&
+-                   expression(pnstart->pn_kid, body);
++            MOZ_ASSERT(firstNode->getKind() == ParseNodeKind::Return);
++            return functionArgs(pn, argsList, args, defaults, rest) &&
++                   expression(firstNode->pn_kid, body);
+         }
+ 
+-        return functionArgs(pn, pnargs, args, defaults, rest) &&
+-               functionBody(pnstart, &pnbody->pn_pos, body);
++        return functionArgs(pn, argsList, args, defaults, rest) &&
++               functionBody(firstNode, &pnbody->pn_pos, body);
+       }
+ 
+       default:
+         LOCAL_NOT_REACHED("unexpected function contents");
+     }
+ }
+ 
+ bool
+-ASTSerializer::functionArgs(ParseNode* pn, ParseNode* pnargs,
++ASTSerializer::functionArgs(ParseNode* pn, ListNode* argsList,
+                             NodeVector& args, NodeVector& defaults,
+                             MutableHandleValue rest)
+ {
+-    if (!pnargs) {
++    if (!argsList) {
+         return true;
+     }
+ 
+     RootedValue node(cx);
+     bool defaultsNull = true;
+     MOZ_ASSERT(defaults.empty(),
+                "must be initially empty for it to be proper to clear this "
+                "when there are no defaults");
+ 
+-    for (ParseNode* arg = pnargs->pn_head; arg && arg != pnargs->last(); arg = arg->pn_next) {
++    for (ParseNode* arg : argsList->contentsTo(argsList->last())) {
+         ParseNode* pat;
+         ParseNode* defNode;
+         if (arg->isKind(ParseNodeKind::Name) ||
+             arg->isKind(ParseNodeKind::Array) ||
+             arg->isKind(ParseNodeKind::Object))
+         {
+             pat = arg;
+             defNode = nullptr;
+@@ -3488,17 +3491,17 @@ ASTSerializer::functionArgs(ParseNode* p
+ 
+         // Process the name or pattern.
+         MOZ_ASSERT(pat->isKind(ParseNodeKind::Name) ||
+                    pat->isKind(ParseNodeKind::Array) ||
+                    pat->isKind(ParseNodeKind::Object));
+         if (!pattern(pat, &node)) {
+             return false;
+         }
+-        if (rest.isUndefined() && arg->pn_next == pnargs->last()) {
++        if (rest.isUndefined() && arg->pn_next == argsList->last()) {
+             rest.setObject(node.toObject());
+         } else {
+             if (!args.append(node)) {
+                 return false;
+             }
+         }
+ 
+         // Process its default (or lack thereof).
+@@ -3728,17 +3731,17 @@ reflect_parse(JSContext* cx, uint32_t ar
+             return false;
+         }
+ 
+         MOZ_ASSERT(pn->getKind() == ParseNodeKind::Module);
+         pn = pn->pn_body;
+     }
+ 
+     RootedValue val(cx);
+-    if (!serialize.program(pn, &val)) {
++    if (!serialize.program(&pn->as<ListNode>(), &val)) {
+         args.rval().setNull();
+         return false;
+     }
+ 
+     args.rval().set(val);
+     return true;
+ }
+ 
+diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp
+--- a/js/src/frontend/BinSource-auto.cpp
++++ b/js/src/frontend/BinSource-auto.cpp
+@@ -3151,17 +3151,17 @@ BinASTParser<Tok>::parseInterfaceBinaryE
+         break;
+     }
+ 
+     ParseNode* result;
+     if (left->isKind(pnk) &&
+         pnk != ParseNodeKind::Pow /* ParseNodeKind::Pow is not left-associative */)
+     {
+         // Regroup left-associative operations into lists.
+-        left->appendWithoutOrderAssumption(right);
++        left->template as<ListNode>().appendWithoutOrderAssumption(right);
+         result = left;
+     } else {
+         BINJS_TRY_DECL(list, factory_.newList(pnk, tokenizer_->pos(start)));
+ 
+         list->appendWithoutOrderAssumption(left);
+         list->appendWithoutOrderAssumption(right);
+         result = list;
+     }
+@@ -4977,17 +4977,17 @@ BinASTParser<Tok>::parseInterfaceForStat
+ 
+ 
+ /*
+  interface FormalParameters : Node {
+     FrozenArray<Parameter> items;
+     Binding? rest;
+  }
+ */
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseFormalParameters()
+ {
+     BinKind kind;
+     BinFields fields(cx_);
+     AutoTaggedTuple guard(*tokenizer_);
+ 
+     MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+     if (kind != BinKind::FormalParameters) {
+@@ -4995,17 +4995,17 @@ BinASTParser<Tok>::parseFormalParameters
+     }
+     const auto start = tokenizer_->offset();
+     BINJS_MOZ_TRY_DECL(result, parseInterfaceFormalParameters(start, kind, fields));
+     MOZ_TRY(guard.done());
+ 
+     return result;
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseInterfaceFormalParameters(const size_t start, const BinKind kind, const BinFields& fields)
+ {
+     MOZ_ASSERT(kind == BinKind::FormalParameters);
+     BINJS_TRY(CheckRecursionLimit(cx_));
+ 
+ #if defined(DEBUG)
+     const BinField expected_fields[2] = { BinField::Items, BinField::Rest };
+     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+@@ -6461,17 +6461,17 @@ BinASTParser<Tok>::parseInterfaceSwitchS
+ 
+     BINJS_MOZ_TRY_DECL(defaultCase, parseSwitchDefault());
+ 
+     BINJS_MOZ_TRY_DECL(postDefaultCases, parseListOfSwitchCase());
+ 
+     // Concatenate `preDefaultCase`, `defaultCase`, `postDefaultCase`
+     auto cases = preDefaultCases;
+     factory_.addList(cases, defaultCase);
+-    ParseNode* iter = postDefaultCases->pn_head;
++    ParseNode* iter = postDefaultCases->head();
+     while (iter) {
+         ParseNode* next = iter->pn_next;
+         factory_.addList(cases, iter);
+         iter = next;
+     }
+     BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
+     BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, true));
+     return result;
+@@ -6915,17 +6915,17 @@ BinASTParser<Tok>::parseInterfaceVariabl
+     AutoVariableDeclarationKind kindGuard(this);
+ 
+     BINJS_MOZ_TRY_DECL(kind_, parseVariableDeclarationKind());
+     // Restored by `kindGuard`.
+     variableDeclarationKind_ = kind_;
+     BINJS_MOZ_TRY_DECL(declarators, parseListOfVariableDeclarator());
+ 
+     // By specification, the list may not be empty.
+-    if (declarators->pn_count == 0)
++    if (declarators->empty())
+         return raiseEmpty("VariableDeclaration");
+ 
+     ParseNodeKind pnk;
+     switch (kind_) {
+       case VariableDeclarationKind::Var:
+         pnk = ParseNodeKind::Var;
+         break;
+       case VariableDeclarationKind::Let:
+@@ -7417,17 +7417,17 @@ BinASTParser<Tok>::parseListOfBindingPro
+ }
+ 
+ template<typename Tok> JS::Result<ParseNode*>
+ BinASTParser<Tok>::parseListOfClassElement()
+ {
+     return raiseError("FIXME: Not implemented yet (ListOfClassElement)");
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseListOfDirective()
+ {
+     uint32_t length;
+     AutoList guard(*tokenizer_);
+ 
+     const auto start = tokenizer_->offset();
+     MOZ_TRY(tokenizer_->enterList(length, guard));
+     BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
+@@ -7472,17 +7472,17 @@ BinASTParser<Tok>::parseListOfImportDecl
+ }
+ 
+ template<typename Tok> JS::Result<ParseNode*>
+ BinASTParser<Tok>::parseListOfImportSpecifier()
+ {
+     return raiseError("FIXME: Not implemented yet (ListOfImportSpecifier)");
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseListOfObjectProperty()
+ {
+     uint32_t length;
+     AutoList guard(*tokenizer_);
+ 
+     const auto start = tokenizer_->offset();
+     MOZ_TRY(tokenizer_->enterList(length, guard));
+     BINJS_TRY_DECL(result, factory_.newObjectLiteral(start));
+@@ -7497,17 +7497,17 @@ BinASTParser<Tok>::parseListOfObjectProp
+ }
+ 
+ template<typename Tok> JS::Result<ParseNode*>
+ BinASTParser<Tok>::parseListOfOptionalBindingOrBindingWithInitializer()
+ {
+     return raiseError("FIXME: Not implemented yet (ListOfOptionalBindingOrBindingWithInitializer)");
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseListOfOptionalSpreadElementOrExpression()
+ {
+     uint32_t length;
+     AutoList guard(*tokenizer_);
+ 
+     const auto start = tokenizer_->offset();
+     MOZ_TRY(tokenizer_->enterList(length, guard));
+     BINJS_TRY_DECL(result, factory_.newArrayLiteral(start));
+@@ -7519,17 +7519,17 @@ BinASTParser<Tok>::parseListOfOptionalSp
+         else
+             BINJS_TRY(factory_.addElision(result, tokenizer_->pos(start)));
+     }
+ 
+     MOZ_TRY(guard.done());
+     return result;
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseListOfParameter()
+ {
+     uint32_t length;
+     AutoList guard(*tokenizer_);
+ 
+     const auto start = tokenizer_->offset();
+     MOZ_TRY(tokenizer_->enterList(length, guard));
+     BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+@@ -7538,17 +7538,17 @@ BinASTParser<Tok>::parseListOfParameter(
+         BINJS_MOZ_TRY_DECL(item, parseParameter());
+         factory_.addList(/* list = */ result, /* kid = */ item);
+     }
+ 
+     MOZ_TRY(guard.done());
+     return result;
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseListOfStatement()
+ {
+     uint32_t length;
+     AutoList guard(*tokenizer_);
+ 
+     const auto start = tokenizer_->offset();
+     MOZ_TRY(tokenizer_->enterList(length, guard));
+     BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
+@@ -7557,17 +7557,17 @@ BinASTParser<Tok>::parseListOfStatement(
+         BINJS_MOZ_TRY_DECL(item, parseStatement());
+         factory_.addStatementToList(result, item);
+     }
+ 
+     MOZ_TRY(guard.done());
+     return result;
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseListOfSwitchCase()
+ {
+     uint32_t length;
+     AutoList guard(*tokenizer_);
+ 
+     const auto start = tokenizer_->offset();
+     MOZ_TRY(tokenizer_->enterList(length, guard));
+     BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
+@@ -7576,17 +7576,17 @@ BinASTParser<Tok>::parseListOfSwitchCase
+         BINJS_MOZ_TRY_DECL(item, parseSwitchCase());
+         factory_.addCaseStatementToList(result, item);
+     }
+ 
+     MOZ_TRY(guard.done());
+     return result;
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<ListNode*>
+ BinASTParser<Tok>::parseListOfVariableDeclarator()
+ {
+     uint32_t length;
+     AutoList guard(*tokenizer_);
+ 
+     const auto start = tokenizer_->offset();
+     MOZ_TRY(tokenizer_->enterList(length, guard));
+     BINJS_TRY_DECL(result, factory_.newDeclarationList(ParseNodeKind::Const /*Placeholder*/,
+diff --git a/js/src/frontend/BinSource-auto.h b/js/src/frontend/BinSource-auto.h
+--- a/js/src/frontend/BinSource-auto.h
++++ b/js/src/frontend/BinSource-auto.h
+@@ -207,17 +207,17 @@ JS::Result<ParseNode*> parseExportFrom()
+ JS::Result<ParseNode*> parseExportFromSpecifier();
+ JS::Result<ParseNode*> parseExportLocalSpecifier();
+ JS::Result<ParseNode*> parseExportLocals();
+ JS::Result<ParseNode*> parseExpressionStatement();
+ JS::Result<ParseNode*> parseForInOfBinding();
+ JS::Result<ParseNode*> parseForInStatement();
+ JS::Result<ParseNode*> parseForOfStatement();
+ JS::Result<ParseNode*> parseForStatement();
+-JS::Result<ParseNode*> parseFormalParameters();
++JS::Result<ListNode*> parseFormalParameters();
+ JS::Result<ParseNode*> parseFunctionBody();
+ JS::Result<ParseNode*> parseIdentifierExpression();
+ JS::Result<ParseNode*> parseIfStatement();
+ JS::Result<ParseNode*> parseImport();
+ JS::Result<ParseNode*> parseImportNamespace();
+ JS::Result<ParseNode*> parseImportSpecifier();
+ JS::Result<ParseNode*> parseLabelledStatement();
+ JS::Result<ParseNode*> parseLiteralBooleanExpression();
+@@ -313,17 +313,17 @@ JS::Result<ParseNode*> parseInterfaceExp
+ JS::Result<ParseNode*> parseInterfaceExportFromSpecifier(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceExportLocalSpecifier(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceExportLocals(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceExpressionStatement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceForInOfBinding(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceForInStatement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceForOfStatement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceForStatement(const size_t start, const BinKind kind, const BinFields& fields);
+-JS::Result<ParseNode*> parseInterfaceFormalParameters(const size_t start, const BinKind kind, const BinFields& fields);
++JS::Result<ListNode*> parseInterfaceFormalParameters(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceIdentifierExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceIfStatement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceImport(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceImportNamespace(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceImportSpecifier(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceLabelledStatement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceLiteralBooleanExpression(const size_t start, const BinKind kind, const BinFields& fields);
+@@ -383,30 +383,30 @@ JS::Result<typename BinASTParser<Tok>::V
+ 
+ // ----- Lists (by lexicographical order)
+ // Implementations are autogenerated
+ JS::Result<ParseNode*> parseArguments();
+ JS::Result<ParseNode*> parseListOfAssignmentTargetOrAssignmentTargetWithInitializer();
+ JS::Result<ParseNode*> parseListOfAssignmentTargetProperty();
+ JS::Result<ParseNode*> parseListOfBindingProperty();
+ JS::Result<ParseNode*> parseListOfClassElement();
+-JS::Result<ParseNode*> parseListOfDirective();
++JS::Result<ListNode*> parseListOfDirective();
+ JS::Result<ParseNode*> parseListOfExportFromSpecifier();
+ JS::Result<ParseNode*> parseListOfExportLocalSpecifier();
+ JS::Result<ParseNode*> parseListOfExpressionOrTemplateElement();
+ JS::Result<ParseNode*> parseListOfIdentifierName();
+ JS::Result<ParseNode*> parseListOfImportDeclarationOrExportDeclarationOrStatement();
+ JS::Result<ParseNode*> parseListOfImportSpecifier();
+-JS::Result<ParseNode*> parseListOfObjectProperty();
++JS::Result<ListNode*> parseListOfObjectProperty();
+ JS::Result<ParseNode*> parseListOfOptionalBindingOrBindingWithInitializer();
+-JS::Result<ParseNode*> parseListOfOptionalSpreadElementOrExpression();
+-JS::Result<ParseNode*> parseListOfParameter();
+-JS::Result<ParseNode*> parseListOfStatement();
+-JS::Result<ParseNode*> parseListOfSwitchCase();
+-JS::Result<ParseNode*> parseListOfVariableDeclarator();
++JS::Result<ListNode*> parseListOfOptionalSpreadElementOrExpression();
++JS::Result<ListNode*> parseListOfParameter();
++JS::Result<ListNode*> parseListOfStatement();
++JS::Result<ListNode*> parseListOfSwitchCase();
++JS::Result<ListNode*> parseListOfVariableDeclarator();
+ 
+ 
+ // ----- Default values (by lexicographical order)
+ // Implementations are autogenerated
+ JS::Result<Ok> parseOptionalAssertedBlockScope();
+ JS::Result<Ok> parseOptionalAssertedParameterScope();
+ JS::Result<Ok> parseOptionalAssertedVarScope();
+ JS::Result<ParseNode*> parseOptionalAssignmentTarget();
+diff --git a/js/src/frontend/BinSource.cpp b/js/src/frontend/BinSource.cpp
+--- a/js/src/frontend/BinSource.cpp
++++ b/js/src/frontend/BinSource.cpp
+@@ -179,23 +179,23 @@ BinASTParser<Tok>::buildFunctionBox(Gene
+ 
+     traceListHead_ = funbox;
+     funbox->initWithEnclosingParseContext(parseContext_, syntax);
+     return funbox;
+ }
+ 
+ template<typename Tok> JS::Result<ParseNode*>
+ BinASTParser<Tok>::buildFunction(const size_t start, const BinKind kind, ParseNode* name,
+-                                 ParseNode* params, ParseNode* body, FunctionBox* funbox)
++                                 ListNode* params, ParseNode* body, FunctionBox* funbox)
+ {
+     TokenPos pos = tokenizer_->pos(start);
+ 
+     // Set the argument count for building argument packets. Function.length is handled
+     // by setting the appropriate funbox field during argument parsing.
+-    funbox->function()->setArgCount(params ? uint16_t(params->pn_count) : 0);
++    funbox->function()->setArgCount(params ? uint16_t(params->count()) : 0);
+ 
+     // ParseNode represents the body as concatenated after the params.
+     params->appendWithoutOrderAssumption(body);
+ 
+     bool isStatement = kind == BinKind::EagerFunctionDeclaration ||
+                        kind == BinKind::SkippableFunctionDeclaration;
+ 
+     BINJS_TRY_DECL(result, isStatement
+@@ -360,42 +360,42 @@ BinASTParser<Tok>::checkFunctionClosedVa
+     if (parseContext_->functionBox()->function()->isNamedLambda()) {
+         MOZ_TRY(checkClosedVars(parseContext_->namedLambdaScope()));
+     }
+ 
+     return Ok();
+ }
+ 
+ template<typename Tok> JS::Result<ParseNode*>
+-BinASTParser<Tok>::appendDirectivesToBody(ParseNode* body, ParseNode* directives)
++BinASTParser<Tok>::appendDirectivesToBody(ListNode* body, ListNode* directives)
+ {
++    if (!directives) {
++        return body;
++    }
++
+     ParseNode* result = body;
+-    if (directives && directives->pn_count >= 1) {
+-        MOZ_ASSERT(directives->isArity(PN_LIST));
+-
++    if (!directives->empty()) {
+         // Convert directive list to a list of strings.
+-        BINJS_TRY_DECL(prefix, factory_.newStatementList(directives->pn_head->pn_pos));
+-        for (ParseNode* iter = directives->pn_head; iter != nullptr; iter = iter->pn_next) {
++        auto pos = directives->head()->pn_pos;
++        BINJS_TRY_DECL(prefix, factory_.newStatementList(pos));
++        for (ParseNode* iter : directives->contents()) {
+             BINJS_TRY_DECL(statement, factory_.newExprStatement(iter, iter->pn_pos.end));
+             prefix->appendWithoutOrderAssumption(statement);
+         }
+ 
+         // Prepend to the body.
+-        ParseNode* iter = body->pn_head;
++        ParseNode* iter = body->head();
+         while (iter) {
+             ParseNode* next = iter->pn_next;
+             prefix->appendWithoutOrderAssumption(iter);
+             iter = next;
+         }
+         prefix->setKind(body->getKind());
+         prefix->setOp(body->getOp());
+         result = prefix;
+-#if defined(DEBUG)
+-        result->checkListConsistency();
+-#endif // defined(DEBUG)
+     }
+ 
+     return result;
+ }
+ 
+ template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
+ BinASTParser<Tok>::raiseInvalidClosedVar(JSAtom* name)
+ {
+diff --git a/js/src/frontend/BinSource.h b/js/src/frontend/BinSource.h
+--- a/js/src/frontend/BinSource.h
++++ b/js/src/frontend/BinSource.h
+@@ -158,17 +158,17 @@ class BinASTParser : public BinASTParser
+ 
+     // Auto-generated methods
+ #include "frontend/BinSource-auto.h"
+ 
+     // --- Auxiliary parsing functions
+ 
+     // Build a function object for a function-producing production. Called AFTER creating the scope.
+     JS::Result<ParseNode*>
+-    buildFunction(const size_t start, const BinKind kind, ParseNode* name, ParseNode* params,
++    buildFunction(const size_t start, const BinKind kind, ParseNode* name, ListNode* params,
+         ParseNode* body, FunctionBox* funbox);
+     JS::Result<FunctionBox*>
+     buildFunctionBox(GeneratorKind generatorKind, FunctionAsyncKind functionAsyncKind, FunctionSyntaxKind syntax, ParseNode* name);
+ 
+     // Parse full scope information to a specific var scope / let scope combination.
+     MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScope(ParseContext::Scope& varScope,
+         ParseContext::Scope& letScope);
+     // Parse a list of names and add it to a given scope.
+@@ -180,18 +180,18 @@ class BinASTParser : public BinASTParser
+     // When leaving a scope, check that none of its bindings are known closed over and un-marked.
+     MOZ_MUST_USE JS::Result<Ok> checkClosedVars(ParseContext::Scope& scope);
+ 
+     // As a convenience, a helper that checks the body, parameter, and recursive binding scopes.
+     MOZ_MUST_USE JS::Result<Ok> checkFunctionClosedVars();
+ 
+     // --- Utilities.
+ 
+-    MOZ_MUST_USE JS::Result<ParseNode*> appendDirectivesToBody(ParseNode* body,
+-        ParseNode* directives);
++    MOZ_MUST_USE JS::Result<ParseNode*> appendDirectivesToBody(ListNode* body,
++        ListNode* directives);
+ 
+   private: // Implement ErrorReporter
+     const JS::ReadOnlyCompileOptions& options_;
+ 
+     const JS::ReadOnlyCompileOptions& options() const override {
+         return this->options_;
+     }
+ 
+diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml
+--- a/js/src/frontend/BinSource.yaml
++++ b/js/src/frontend/BinSource.yaml
+@@ -363,17 +363,17 @@ BinaryExpression:
+             break;
+         }
+ 
+         ParseNode* result;
+         if (left->isKind(pnk) &&
+             pnk != ParseNodeKind::Pow /* ParseNodeKind::Pow is not left-associative */)
+         {
+             // Regroup left-associative operations into lists.
+-            left->appendWithoutOrderAssumption(right);
++            left->template as<ListNode>().appendWithoutOrderAssumption(right);
+             result = left;
+         } else {
+             BINJS_TRY_DECL(list, factory_.newList(pnk, tokenizer_->pos(start)));
+ 
+             list->appendWithoutOrderAssumption(left);
+             list->appendWithoutOrderAssumption(right);
+             result = list;
+         }
+@@ -664,16 +664,18 @@ ForInStatement:
+         BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));
+ 
+         if (!scope.isEmpty()) {
+             BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
+             BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
+         }
+ 
+ FormalParameters:
++    type-ok:
++        ListNode*
+     build: |
+         auto result = items;
+         if (rest) {
+             BINJS_TRY_DECL(spread, factory_.newSpread(start, rest));
+             factory_.addList(result, spread);
+         }
+ 
+ ForStatement:
+@@ -716,60 +718,76 @@ LabelledStatement:
+             after: |
+                 if (!IsIdentifier(label))
+                     return raiseError("Invalid identifier");
+                 ParseContext::LabelStatement stmt(parseContext_, label);
+     build:
+         BINJS_TRY_DECL(result, factory_.newLabeledStatement(label->asPropertyName(), body, start));
+ 
+ ListOfDirective:
++    type-ok:
++        ListNode*
+     init:
+         BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
+     append:
+         factory_.addStatementToList(result, item);
+ 
+ ListOfObjectProperty:
++    type-ok:
++        ListNode*
+     init:
+         BINJS_TRY_DECL(result, factory_.newObjectLiteral(start));
+ 
+ ListOfOptionalSpreadElementOrExpression:
++    type-ok:
++        ListNode*
+     init:
+         BINJS_TRY_DECL(result, factory_.newArrayLiteral(start));
+     append: |
+         if (item)
+             factory_.addArrayElement(result, item); // Infallible.
+         else
+             BINJS_TRY(factory_.addElision(result, tokenizer_->pos(start)));
+ 
+ ListOfParameter:
++    type-ok:
++        ListNode*
+     init: |
+         BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+     append:
+         factory_.addList(/* list = */ result, /* kid = */ item);
+ 
+ ListOfStatement:
++    type-ok:
++        ListNode*
+     init:
+         BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
+     append:
+         factory_.addStatementToList(result, item);
+ 
+ 
+ #ListOfSpreadElementOrExpression:
++#    type-ok:
++#        ListNode*
+ #    init:
+ #        BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos()));
+ #    append:
+ #        result->appendWithoutOrderAssumption(item);
+ 
+ ListOfSwitchCase:
++    type-ok:
++        ListNode*
+     init:
+         BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
+     append:
+         factory_.addCaseStatementToList(result, item);
+ 
+ ListOfVariableDeclarator:
++    type-ok:
++        ListNode*
+     init: |
+         BINJS_TRY_DECL(result, factory_.newDeclarationList(ParseNodeKind::Const /*Placeholder*/,
+             tokenizer_->pos(start)));
+ 
+ LiteralBooleanExpression:
+     build:
+         BINJS_TRY_DECL(result, factory_.newBooleanLiteral(value, tokenizer_->pos(start)));
+ 
+@@ -903,17 +921,17 @@ SwitchStatement:
+         BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
+         BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, false));
+ 
+ SwitchStatementWithDefault:
+     build: |
+         // Concatenate `preDefaultCase`, `defaultCase`, `postDefaultCase`
+         auto cases = preDefaultCases;
+         factory_.addList(cases, defaultCase);
+-        ParseNode* iter = postDefaultCases->pn_head;
++        ParseNode* iter = postDefaultCases->head();
+         while (iter) {
+             ParseNode* next = iter->pn_next;
+             factory_.addList(cases, iter);
+             iter = next;
+         }
+         BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
+         BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope, true));
+ 
+@@ -1062,17 +1080,17 @@ VariableDeclaration:
+     fields:
+         kind:
+             after: |
+                 // Restored by `kindGuard`.
+                 variableDeclarationKind_ = kind_;
+ 
+     build: |
+         // By specification, the list may not be empty.
+-        if (declarators->pn_count == 0)
++        if (declarators->empty())
+             return raiseEmpty("VariableDeclaration");
+ 
+         ParseNodeKind pnk;
+         switch (kind_) {
+           case VariableDeclarationKind::Var:
+             pnk = ParseNodeKind::Var;
+             break;
+           case VariableDeclarationKind::Let:
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -560,19 +560,20 @@ BytecodeEmitter::getOffsetForLoop(ParseN
+     }
+ 
+     // Try to give the JSOP_LOOPHEAD and JSOP_LOOPENTRY the same line number as
+     // the next instruction. nextpn is often a block, in which case the next
+     // instruction typically comes from the first statement inside.
+     if (nextpn->isKind(ParseNodeKind::LexicalScope)) {
+         nextpn = nextpn->scopeBody();
+     }
+-    MOZ_ASSERT_IF(nextpn->isKind(ParseNodeKind::StatementList), nextpn->isArity(PN_LIST));
+-    if (nextpn->isKind(ParseNodeKind::StatementList) && nextpn->pn_head) {
+-        nextpn = nextpn->pn_head;
++    if (nextpn->isKind(ParseNodeKind::StatementList)) {
++        if (ParseNode* firstStatement = nextpn->as<ListNode>().head()) {
++            nextpn = firstStatement;
++        }
+     }
+ 
+     return Some(nextpn->pn_pos.begin);
+ }
+ 
+ void
+ BytecodeEmitter::checkTypeSet(JSOp op)
+ {
+@@ -1195,23 +1196,22 @@ BytecodeEmitter::checkSideEffects(ParseN
+       // Strict equality operations and logical operators are well-behaved and
+       // perform no conversions.
+       case ParseNodeKind::Or:
+       case ParseNodeKind::And:
+       case ParseNodeKind::StrictEq:
+       case ParseNodeKind::StrictNe:
+       // Any subexpression of a comma expression could be effectful.
+       case ParseNodeKind::Comma:
+-        MOZ_ASSERT(pn->pn_count > 0);
++        MOZ_ASSERT(!pn->as<ListNode>().empty());
+         MOZ_FALLTHROUGH;
+       // Subcomponents of a literal may be effectful.
+       case ParseNodeKind::Array:
+       case ParseNodeKind::Object:
+-        MOZ_ASSERT(pn->isArity(PN_LIST));
+-        for (ParseNode* item = pn->pn_head; item; item = item->pn_next) {
++        for (ParseNode* item : pn->as<ListNode>().contents()) {
+             if (!checkSideEffects(item, answer)) {
+                 return false;
+             }
+             if (*answer) {
+                 return true;
+             }
+         }
+         return true;
+@@ -1237,18 +1237,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+       case ParseNodeKind::Rsh:
+       case ParseNodeKind::Ursh:
+       case ParseNodeKind::Add:
+       case ParseNodeKind::Sub:
+       case ParseNodeKind::Star:
+       case ParseNodeKind::Div:
+       case ParseNodeKind::Mod:
+       case ParseNodeKind::Pow:
+-        MOZ_ASSERT(pn->isArity(PN_LIST));
+-        MOZ_ASSERT(pn->pn_count >= 2);
++        MOZ_ASSERT(pn->as<ListNode>().count() >= 2);
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::Colon:
+       case ParseNodeKind::Case:
+         MOZ_ASSERT(pn->isArity(PN_BINARY));
+         if (!checkSideEffects(pn->pn_left, answer)) {
+             return false;
+@@ -1294,17 +1293,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+         MOZ_ASSERT(pn->isArity(PN_BINARY));
+         *answer = true;
+         return true;
+ 
+       // Declarations affect the name set of the relevant scope.
+       case ParseNodeKind::Var:
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+-        MOZ_ASSERT(pn->isArity(PN_LIST));
++        MOZ_ASSERT(pn->is<ListNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::If:
+       case ParseNodeKind::Conditional:
+         MOZ_ASSERT(pn->isArity(PN_TERNARY));
+         if (!checkSideEffects(pn->pn_kid1, answer)) {
+             return false;
+@@ -1331,23 +1330,22 @@ BytecodeEmitter::checkSideEffects(ParseN
+         MOZ_ASSERT(pn->isArity(PN_BINARY));
+         *answer = true;
+         return true;
+ 
+       // Function arg lists can contain arbitrary expressions. Technically
+       // this only causes side-effects if one of the arguments does, but since
+       // the call being made will always trigger side-effects, it isn't needed.
+       case ParseNodeKind::Arguments:
+-        MOZ_ASSERT(pn->isArity(PN_LIST));
++        MOZ_ASSERT(pn->is<ListNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::Pipeline:
+-        MOZ_ASSERT(pn->isArity(PN_LIST));
+-        MOZ_ASSERT(pn->pn_count >= 2);
++        MOZ_ASSERT(pn->as<ListNode>().count() >= 2);
+         *answer = true;
+         return true;
+ 
+       // Classes typically introduce names.  Even if no name is introduced,
+       // the heritage and/or class body (through computed property names)
+       // usually have effects.
+       case ParseNodeKind::Class:
+         MOZ_ASSERT(pn->isArity(PN_TERNARY));
+@@ -1445,24 +1443,25 @@ BytecodeEmitter::checkSideEffects(ParseN
+ 
+       case ParseNodeKind::LexicalScope:
+         MOZ_ASSERT(pn->isArity(PN_SCOPE));
+         return checkSideEffects(pn->scopeBody(), answer);
+ 
+       // We could methodically check every interpolated expression, but it's
+       // probably not worth the trouble.  Treat template strings as effect-free
+       // only if they don't contain any substitutions.
+-      case ParseNodeKind::TemplateStringList:
+-        MOZ_ASSERT(pn->isArity(PN_LIST));
+-        MOZ_ASSERT(pn->pn_count > 0);
+-        MOZ_ASSERT((pn->pn_count % 2) == 1,
++      case ParseNodeKind::TemplateStringList: {
++        ListNode* list = &pn->as<ListNode>();
++        MOZ_ASSERT(!list->empty());
++        MOZ_ASSERT((list->count() % 2) == 1,
+                    "template strings must alternate template and substitution "
+                    "parts");
+-        *answer = pn->pn_count > 1;
++        *answer = list->count() > 1;
+         return true;
++      }
+ 
+       // This should be unreachable but is left as-is for now.
+       case ParseNodeKind::ParamsBody:
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::ForIn:           // by ParseNodeKind::For
+       case ParseNodeKind::ForOf:           // by ParseNodeKind::For
+@@ -2518,17 +2517,17 @@ BytecodeEmitter::emitNumberOp(double dva
+  * LLVM is deciding to inline this function which uses a lot of stack space
+  * into emitTree which is recursive and uses relatively little stack space.
+  */
+ MOZ_NEVER_INLINE bool
+ BytecodeEmitter::emitSwitch(SwitchStatement* pn)
+ {
+     ParseNode& lexical = pn->lexicalForCaseList();
+     MOZ_ASSERT(lexical.isKind(ParseNodeKind::LexicalScope));
+-    ParseNode* cases = lexical.scopeBody();
++    ListNode* cases = &lexical.scopeBody()->as<ListNode>();
+     MOZ_ASSERT(cases->isKind(ParseNodeKind::StatementList));
+ 
+     SwitchEmitter se(this);
+     if (!se.emitDiscriminant(Some(pn->pn_pos.begin))) {
+         return false;
+     }
+     if (!emitTree(&pn->discriminant())) {
+         return false;
+@@ -2539,36 +2538,37 @@ BytecodeEmitter::emitSwitch(SwitchStatem
+     if (!lexical.isEmptyScope()) {
+         if (!se.emitLexical(lexical.scopeBindings())) {
+             return false;
+         }
+ 
+         // A switch statement may contain hoisted functions inside its
+         // cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST
+         // bodies of the cases to the case list.
+-        if (cases->pn_xflags & PNX_FUNCDEFS) {
+-            for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
+-                if (caseNode->pn_right->pn_xflags & PNX_FUNCDEFS) {
+-                    if (!emitHoistedFunctionsInList(caseNode->pn_right)) {
++        if (cases->hasTopLevelFunctionDeclarations()) {
++            for (ParseNode* caseNode : cases->contents()) {
++                ListNode* statements = &caseNode->pn_right->as<ListNode>();
++                if (statements->hasTopLevelFunctionDeclarations()) {
++                    if (!emitHoistedFunctionsInList(statements)) {
+                         return false;
+                     }
+                 }
+             }
+         }
+     } else {
+-        MOZ_ASSERT(!(cases->pn_xflags & PNX_FUNCDEFS));
++        MOZ_ASSERT(!cases->hasTopLevelFunctionDeclarations());
+     }
+ 
+     SwitchEmitter::TableGenerator tableGen(this);
+-    uint32_t caseCount = cases->pn_count - (pn->hasDefault() ? 1 : 0);
+-    CaseClause* firstCase = cases->pn_head ? &cases->pn_head->as<CaseClause>() : nullptr;
++    uint32_t caseCount = cases->count() - (pn->hasDefault() ? 1 : 0);
+     if (caseCount == 0) {
+         tableGen.finish(0);
+     } else {
+-        for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
++        for (ParseNode* item : cases->contents()) {
++            CaseClause* caseNode = &item->as<CaseClause>();
+             if (caseNode->isDefault()) {
+                 continue;
+             }
+ 
+             ParseNode* caseValue = caseNode->caseExpression();
+ 
+             if (caseValue->getKind() != ParseNodeKind::Number) {
+                 tableGen.setInvalid();
+@@ -2599,17 +2599,18 @@ BytecodeEmitter::emitSwitch(SwitchStatem
+             return false;
+         }
+     } else {
+         if (!se.emitCond()) {
+             return false;
+         }
+ 
+         // Emit code for evaluating cases and jumping to case statements.
+-        for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
++        for (ParseNode* item : cases->contents()) {
++            CaseClause* caseNode = &item->as<CaseClause>();
+             if (caseNode->isDefault()) {
+                 continue;
+             }
+ 
+             ParseNode* caseValue = caseNode->caseExpression();
+             // If the expression is a literal, suppress line number emission so
+             // that debugging works more naturally.
+             if (!emitTree(caseValue, ValueUsage::WantValue,
+@@ -2620,17 +2621,18 @@ BytecodeEmitter::emitSwitch(SwitchStatem
+ 
+             if (!se.emitCaseJump()) {
+                 return false;
+             }
+         }
+     }
+ 
+     // Emit code for each case's statements.
+-    for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
++    for (ParseNode* item : cases->contents()) {
++        CaseClause* caseNode = &item->as<CaseClause>();
+         if (caseNode->isDefault()) {
+             if (!se.emitDefaultBody()) {
+                 return false;
+             }
+         } else {
+             if (isTableSwitch) {
+                 ParseNode* caseValue = caseNode->caseExpression();
+                 MOZ_ASSERT(caseValue->isKind(ParseNodeKind::Number));
+@@ -3006,17 +3008,17 @@ BytecodeEmitter::emitSetOrInitializeDest
+     // the matched value. Otherwise emit an lvalue bytecode sequence followed
+     // by an assignment op.
+     if (target->isKind(ParseNodeKind::Spread)) {
+         target = target->pn_kid;
+     } else if (target->isKind(ParseNodeKind::Assign)) {
+         target = target->pn_left;
+     }
+     if (target->isKind(ParseNodeKind::Array) || target->isKind(ParseNodeKind::Object)) {
+-        if (!emitDestructuringOps(target, flav)) {
++        if (!emitDestructuringOps(&target->as<ListNode>(), flav)) {
+             return false;
+         }
+         // Per its post-condition, emitDestructuringOps has left the
+         // to-be-destructured value on top of the stack.
+         if (!emit1(JSOP_POP)) {
+             return false;
+         }
+     } else {
+@@ -3489,20 +3491,19 @@ BytecodeEmitter::emitInitializer(ParseNo
+ bool
+ BytecodeEmitter::emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern)
+ {
+     TDZCheckCache tdzCache(this);
+     return emitInitializer(initializer, pattern);
+ }
+ 
+ bool
+-BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlavor flav)
++BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, DestructuringFlavor flav)
+ {
+     MOZ_ASSERT(pattern->isKind(ParseNodeKind::Array));
+-    MOZ_ASSERT(pattern->isArity(PN_LIST));
+     MOZ_ASSERT(this->stackDepth != 0);
+ 
+     // Here's pseudo code for |let [a, b, , c=y, ...d] = x;|
+     //
+     // Lines that are annotated "covered by trynote" mean that upon throwing
+     // an exception, IteratorClose is called on iter only if done is false.
+     //
+     //   let x, y;
+@@ -3592,17 +3593,17 @@ BytecodeEmitter::emitDestructuringOpsArr
+         return false;
+     }
+     if (!emitIterator()) {                                        // ... OBJ NEXT ITER
+         return false;
+     }
+ 
+     // For an empty pattern [], call IteratorClose unconditionally. Nothing
+     // else needs to be done.
+-    if (!pattern->pn_head) {
++    if (!pattern->head()) {
+         if (!emit1(JSOP_SWAP)) {                                  // ... OBJ ITER NEXT
+             return false;
+         }
+         if (!emit1(JSOP_POP)) {                                   // ... OBJ ITER
+             return false;
+         }
+ 
+         return emitIteratorCloseInInnermostScope();               // ... OBJ
+@@ -3613,18 +3614,18 @@ BytecodeEmitter::emitDestructuringOpsArr
+         return false;
+     }
+ 
+     // JSTRY_DESTRUCTURING_ITERCLOSE expects the iterator and the done value
+     // to be the second to top and the top of the stack, respectively.
+     // IteratorClose is called upon exception only if done is false.
+     int32_t tryNoteDepth = stackDepth;
+ 
+-    for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
+-        bool isFirst = member == pattern->pn_head;
++    for (ParseNode* member : pattern->contents()) {
++        bool isFirst = member == pattern->head();
+         DebugOnly<bool> hasNext = !!member->pn_next;
+ 
+         size_t emitted = 0;
+ 
+         // Spec requires LHS reference to be evaluated first.
+         ParseNode* lhsPattern = member;
+         if (lhsPattern->isKind(ParseNodeKind::Assign)) {
+             lhsPattern = lhsPattern->pn_left;
+@@ -3871,40 +3872,39 @@ BytecodeEmitter::emitDestructuringOpsArr
+ bool
+ BytecodeEmitter::emitComputedPropertyName(ParseNode* computedPropName)
+ {
+     MOZ_ASSERT(computedPropName->isKind(ParseNodeKind::ComputedName));
+     return emitTree(computedPropName->pn_kid) && emit1(JSOP_TOID);
+ }
+ 
+ bool
+-BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFlavor flav)
++BytecodeEmitter::emitDestructuringOpsObject(ListNode* pattern, DestructuringFlavor flav)
+ {
+     MOZ_ASSERT(pattern->isKind(ParseNodeKind::Object));
+-    MOZ_ASSERT(pattern->isArity(PN_LIST));
+ 
+     MOZ_ASSERT(this->stackDepth > 0);                             // ... RHS
+ 
+     if (!emit1(JSOP_CHECKOBJCOERCIBLE)) {                         // ... RHS
+         return false;
+     }
+ 
+-    bool needsRestPropertyExcludedSet = pattern->pn_count > 1 &&
++    bool needsRestPropertyExcludedSet = pattern->count() > 1 &&
+                                         pattern->last()->isKind(ParseNodeKind::Spread);
+     if (needsRestPropertyExcludedSet) {
+         if (!emitDestructuringObjRestExclusionSet(pattern)) {     // ... RHS SET
+             return false;
+         }
+ 
+         if (!emit1(JSOP_SWAP)) {                                  // ... SET RHS
+             return false;
+         }
+     }
+ 
+-    for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
++    for (ParseNode* member : pattern->contents()) {
+         ParseNode* subpattern;
+         if (member->isKind(ParseNodeKind::MutateProto) ||
+             member->isKind(ParseNodeKind::Spread))
+         {
+             subpattern = member->pn_kid;
+         } else {
+             subpattern = member->pn_right;
+         }
+@@ -4031,43 +4031,42 @@ BytecodeEmitter::emitDestructuringOpsObj
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern)
++BytecodeEmitter::emitDestructuringObjRestExclusionSet(ListNode* pattern)
+ {
+     MOZ_ASSERT(pattern->isKind(ParseNodeKind::Object));
+-    MOZ_ASSERT(pattern->isArity(PN_LIST));
+     MOZ_ASSERT(pattern->last()->isKind(ParseNodeKind::Spread));
+ 
+     ptrdiff_t offset = this->offset();
+     if (!emitNewInit()) {
+         return false;
+     }
+ 
+     // Try to construct the shape of the object as we go, so we can emit a
+     // JSOP_NEWOBJECT with the final shape instead.
+     // In the case of computed property names and indices, we cannot fix the
+     // shape at bytecode compile time. When the shape cannot be determined,
+     // |obj| is nulled out.
+ 
+     // No need to do any guessing for the object kind, since we know the upper
+     // bound of how many properties we plan to have.
+-    gc::AllocKind kind = gc::GetGCObjectKind(pattern->pn_count - 1);
++    gc::AllocKind kind = gc::GetGCObjectKind(pattern->count() - 1);
+     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
+     if (!obj) {
+         return false;
+     }
+ 
+     RootedAtom pnatom(cx);
+-    for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
++    for (ParseNode* member : pattern->contents()) {
+         if (member->isKind(ParseNodeKind::Spread)) {
+             break;
+         }
+ 
+         bool isIndex = false;
+         if (member->isKind(ParseNodeKind::MutateProto)) {
+             pnatom.set(cx->names().proto);
+         } else {
+@@ -4129,50 +4128,48 @@ BytecodeEmitter::emitDestructuringObjRes
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav)
++BytecodeEmitter::emitDestructuringOps(ListNode* pattern, DestructuringFlavor flav)
+ {
+     if (pattern->isKind(ParseNodeKind::Array)) {
+         return emitDestructuringOpsArray(pattern, flav);
+     }
+     return emitDestructuringOpsObject(pattern, flav);
+ }
+ 
+ bool
+-BytecodeEmitter::emitTemplateString(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-
++BytecodeEmitter::emitTemplateString(ListNode* templateString)
++{
+     bool pushedString = false;
+ 
+-    for (ParseNode* pn2 = pn->pn_head; pn2 != NULL; pn2 = pn2->pn_next) {
+-        bool isString = (pn2->getKind() == ParseNodeKind::String ||
+-                         pn2->getKind() == ParseNodeKind::TemplateString);
++    for (ParseNode* item : templateString->contents()) {
++        bool isString = (item->getKind() == ParseNodeKind::String ||
++                         item->getKind() == ParseNodeKind::TemplateString);
+ 
+         // Skip empty strings. These are very common: a template string like
+         // `${a}${b}` has three empty strings and without this optimization
+         // we'd emit four JSOP_ADD operations instead of just one.
+-        if (isString && pn2->pn_atom->empty()) {
++        if (isString && item->pn_atom->empty()) {
+             continue;
+         }
+ 
+         if (!isString) {
+             // We update source notes before emitting the expression
+-            if (!updateSourceCoordNotes(pn2->pn_pos.begin)) {
+-                return false;
+-            }
+-        }
+-
+-        if (!emitTree(pn2)) {
++            if (!updateSourceCoordNotes(item->pn_pos.begin)) {
++                return false;
++            }
++        }
++
++        if (!emitTree(item)) {
+             return false;
+         }
+ 
+         if (!isString) {
+             // We need to convert the expression to a string
+             if (!emit1(JSOP_TOSTRING)) {
+                 return false;
+             }
+@@ -4195,32 +4192,29 @@ BytecodeEmitter::emitTemplateString(Pars
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitDeclarationList(ParseNode* declList)
+-{
+-    MOZ_ASSERT(declList->isArity(PN_LIST));
++BytecodeEmitter::emitDeclarationList(ListNode* declList)
++{
+     MOZ_ASSERT(declList->isOp(JSOP_NOP));
+ 
+-    ParseNode* next;
+-    for (ParseNode* decl = declList->pn_head; decl; decl = next) {
++    for (ParseNode* decl : declList->contents()) {
+         if (!updateSourceCoordNotes(decl->pn_pos.begin)) {
+             return false;
+         }
+-        next = decl->pn_next;
+ 
+         if (decl->isKind(ParseNodeKind::Assign)) {
+             MOZ_ASSERT(decl->isOp(JSOP_NOP));
+ 
+-            ParseNode* pattern = decl->pn_left;
++            ListNode* pattern = &decl->pn_left->as<ListNode>();
+             MOZ_ASSERT(pattern->isKind(ParseNodeKind::Array) ||
+                        pattern->isKind(ParseNodeKind::Object));
+ 
+             if (!emitTree(decl->pn_right)) {
+                 return false;
+             }
+ 
+             if (!emitDestructuringOps(pattern, DestructuringDeclaration)) {
+@@ -4512,17 +4506,17 @@ BytecodeEmitter::emitAssignment(ParseNod
+                        sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
+         if (!emit1(setOp)) {
+             return false;
+         }
+         break;
+       }
+       case ParseNodeKind::Array:
+       case ParseNodeKind::Object:
+-        if (!emitDestructuringOps(lhs, DestructuringAssignment)) {
++        if (!emitDestructuringOps(&lhs->as<ListNode>(), DestructuringAssignment)) {
+             return false;
+         }
+         break;
+       default:
+         MOZ_ASSERT(0);
+     }
+     return true;
+ }
+@@ -4566,22 +4560,22 @@ ParseNode::getConstantValue(JSContext* c
+ 
+         ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal;
+         if (allowObjects == ForCopyOnWriteArray) {
+             arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite;
+             allowObjects = DontAllowObjects;
+         }
+ 
+         if (getKind() == ParseNodeKind::CallSiteObj) {
+-            count = pn_count - 1;
+-            pn = pn_head->pn_next;
++            count = as<CallSiteNode>().count() - 1;
++            pn = as<CallSiteNode>().head()->pn_next;
+         } else {
+-            MOZ_ASSERT(!(pn_xflags & PNX_NONCONST));
+-            count = pn_count;
+-            pn = pn_head;
++            MOZ_ASSERT(!as<ListNode>().hasNonConstInitializer());
++            count = as<ListNode>().count();
++            pn = as<ListNode>().head();
+         }
+ 
+         AutoValueVector values(cx);
+         if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), count)) {
+             return false;
+         }
+         size_t idx;
+         for (idx = 0; pn; idx++, pn = pn->pn_next) {
+@@ -4604,44 +4598,44 @@ ParseNode::getConstantValue(JSContext* c
+         if (!CombineArrayElementTypes(cx, obj, compare, ncompare)) {
+             return false;
+         }
+ 
+         vp.setObject(*obj);
+         return true;
+       }
+       case ParseNodeKind::Object: {
+-        MOZ_ASSERT(!(pn_xflags & PNX_NONCONST));
++        MOZ_ASSERT(!as<ListNode>().hasNonConstInitializer());
+ 
+         if (allowObjects == DontAllowObjects) {
+             vp.setMagic(JS_GENERIC_MAGIC);
+             return true;
+         }
+         MOZ_ASSERT(allowObjects == AllowObjects);
+ 
+         Rooted<IdValueVector> properties(cx, IdValueVector(cx));
+ 
+         RootedValue value(cx), idvalue(cx);
+-        for (ParseNode* pn = pn_head; pn; pn = pn->pn_next) {
+-            if (!pn->pn_right->getConstantValue(cx, allowObjects, &value)) {
++        for (ParseNode* prop : as<ListNode>().contents()) {
++            if (!prop->pn_right->getConstantValue(cx, allowObjects, &value)) {
+                 return false;
+             }
+             if (value.isMagic(JS_GENERIC_MAGIC)) {
+                 vp.setMagic(JS_GENERIC_MAGIC);
+                 return true;
+             }
+ 
+-            ParseNode* pnid = pn->pn_left;
+-            if (pnid->isKind(ParseNodeKind::Number)) {
+-                idvalue = NumberValue(pnid->pn_dval);
++            ParseNode* key = prop->pn_left;
++            if (key->isKind(ParseNodeKind::Number)) {
++                idvalue = NumberValue(key->pn_dval);
+             } else {
+-                MOZ_ASSERT(pnid->isKind(ParseNodeKind::ObjectPropertyName) ||
+-                           pnid->isKind(ParseNodeKind::String));
+-                MOZ_ASSERT(pnid->pn_atom != cx->names().proto);
+-                idvalue = StringValue(pnid->pn_atom);
++                MOZ_ASSERT(key->isKind(ParseNodeKind::ObjectPropertyName) ||
++                           key->isKind(ParseNodeKind::String));
++                MOZ_ASSERT(key->pn_atom != cx->names().proto);
++                idvalue = StringValue(key->pn_atom);
+             }
+ 
+             RootedId id(cx);
+             if (!ValueToId<CanGC>(cx, idvalue, &id)) {
+                 return false;
+             }
+ 
+             if (!properties.append(IdValuePair(id, value))) {
+@@ -4685,31 +4679,31 @@ BytecodeEmitter::emitSingletonInitialise
+     if (!objbox) {
+         return false;
+     }
+ 
+     return emitObjectOp(objbox, JSOP_OBJECT);
+ }
+ 
+ bool
+-BytecodeEmitter::emitCallSiteObject(ParseNode* pn)
++BytecodeEmitter::emitCallSiteObject(CallSiteNode* callSiteObj)
+ {
+     RootedValue value(cx);
+-    if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value)) {
++    if (!callSiteObj->getConstantValue(cx, ParseNode::AllowObjects, &value)) {
+         return false;
+     }
+ 
+     MOZ_ASSERT(value.isObject());
+ 
+     ObjectBox* objbox1 = parser->newObjectBox(&value.toObject());
+     if (!objbox1) {
+         return false;
+     }
+ 
+-    if (!pn->as<CallSiteNode>().getRawArrayValue(cx, &value)) {
++    if (!callSiteObj->getRawArrayValue(cx, &value)) {
+         return false;
+     }
+ 
+     MOZ_ASSERT(value.isObject());
+ 
+     ObjectBox* objbox2 = parser->newObjectBox(&value.toObject());
+     if (!objbox2) {
+         return false;
+@@ -4746,17 +4740,17 @@ BytecodeEmitter::emitCatch(ParseNode* pn
+         // Catch parameter was omitted; just discard the exception.
+         if (!emit1(JSOP_POP)) {
+             return false;
+         }
+     } else {
+         switch (pn2->getKind()) {
+           case ParseNodeKind::Array:
+           case ParseNodeKind::Object:
+-            if (!emitDestructuringOps(pn2, DestructuringDeclaration)) {
++            if (!emitDestructuringOps(&pn2->as<ListNode>(), DestructuringDeclaration)) {
+                 return false;
+             }
+             if (!emit1(JSOP_POP)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::Name:
+@@ -4903,22 +4897,22 @@ BytecodeEmitter::emitIf(ParseNode* pn)
+     if (!ifThenElse.emitEnd()) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitHoistedFunctionsInList(ParseNode* list)
+-{
+-    MOZ_ASSERT(list->pn_xflags & PNX_FUNCDEFS);
+-
+-    for (ParseNode* pn = list->pn_head; pn; pn = pn->pn_next) {
+-        ParseNode* maybeFun = pn;
++BytecodeEmitter::emitHoistedFunctionsInList(ListNode* stmtList)
++{
++    MOZ_ASSERT(stmtList->hasTopLevelFunctionDeclarations());
++
++    for (ParseNode* stmt : stmtList->contents()) {
++        ParseNode* maybeFun = stmt;
+ 
+         if (!sc->strict()) {
+             while (maybeFun->isKind(ParseNodeKind::Label)) {
+                 maybeFun = maybeFun->as<LabeledStatement>().statement();
+             }
+         }
+ 
+         if (maybeFun->isKind(ParseNodeKind::Function) && maybeFun->functionIsHoisted()) {
+@@ -4929,21 +4923,23 @@ BytecodeEmitter::emitHoistedFunctionsInL
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLineNote)
+ {
+-    if (body->isKind(ParseNodeKind::StatementList) && body->pn_xflags & PNX_FUNCDEFS) {
++    if (body->isKind(ParseNodeKind::StatementList) &&
++        body->as<ListNode>().hasTopLevelFunctionDeclarations())
++    {
+         // This block contains function statements whose definitions are
+         // hoisted to the top of the block. Emit these as a separate pass
+         // before the rest of the block.
+-        if (!emitHoistedFunctionsInList(body)) {
++        if (!emitHoistedFunctionsInList(&body->as<ListNode>())) {
+             return false;
+         }
+     }
+ 
+     // Line notes were updated by emitLexicalScope.
+     return emitTree(body, ValueUsage::WantValue, emitLineNote);
+ }
+ 
+@@ -5333,17 +5329,17 @@ BytecodeEmitter::emitInitializeForInOrOf
+     // assignment to that name (which does *not* necessarily assign to the
+     // variable!) must be generated.
+ 
+     if (!updateSourceCoordNotes(target->pn_pos.begin)) {
+         return false;
+     }
+ 
+     MOZ_ASSERT(target->isForLoopDeclaration());
+-    target = parser->astGenerator().singleBindingFromDeclaration(target);
++    target = parser->astGenerator().singleBindingFromDeclaration(&target->as<ListNode>());
+ 
+     if (target->isKind(ParseNodeKind::Name)) {
+         auto emitSwapScopeAndRhs = [](BytecodeEmitter* bce, const NameLocation&,
+                                       bool emittedBindOp)
+         {
+             if (emittedBindOp) {
+                 // Per-iteration initialization in for-in/of loops computes the
+                 // iteration value *before* initializing.  Thus the
+@@ -5363,17 +5359,17 @@ BytecodeEmitter::emitInitializeForInOrOf
+         return emitInitializeName(target, emitSwapScopeAndRhs);
+     }
+ 
+     MOZ_ASSERT(!target->isKind(ParseNodeKind::Assign),
+                "for-in/of loop destructuring declarations can't have initializers");
+ 
+     MOZ_ASSERT(target->isKind(ParseNodeKind::Array) ||
+                target->isKind(ParseNodeKind::Object));
+-    return emitDestructuringOps(target, DestructuringDeclaration);
++    return emitDestructuringOps(&target->as<ListNode>(), DestructuringDeclaration);
+ }
+ 
+ bool
+ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, const EmitterScope* headLexicalEmitterScope)
+ {
+     MOZ_ASSERT(forOfLoop->isKind(ParseNodeKind::For));
+     MOZ_ASSERT(forOfLoop->isArity(PN_BINARY));
+ 
+@@ -5453,17 +5449,18 @@ BytecodeEmitter::emitForIn(ParseNode* fo
+     MOZ_ASSERT(forInHead->isArity(PN_TERNARY));
+ 
+     ForInEmitter forIn(this, headLexicalEmitterScope);
+ 
+     // Annex B: Evaluate the var-initializer expression if present.
+     // |for (var i = initializer in expr) { ... }|
+     ParseNode* forInTarget = forInHead->pn_kid1;
+     if (parser->astGenerator().isDeclarationList(forInTarget)) {
+-        ParseNode* decl = parser->astGenerator().singleBindingFromDeclaration(forInTarget);
++        ParseNode* decl =
++            parser->astGenerator().singleBindingFromDeclaration(&forInTarget->as<ListNode>());
+         if (decl->isKind(ParseNodeKind::Name)) {
+             if (ParseNode* initializer = decl->expr()) {
+                 MOZ_ASSERT(forInTarget->isKind(ParseNodeKind::Var),
+                            "for-in initializers are only permitted for |var| declarations");
+ 
+                 if (!updateSourceCoordNotes(decl->pn_pos.begin)) {
+                     return false;
+                 }
+@@ -6662,21 +6659,20 @@ BytecodeEmitter::emitYieldStar(ParseNode
+     }
+ 
+     MOZ_ASSERT(this->stackDepth == startDepth - 2);
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitStatementList(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    for (ParseNode* pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
+-        if (!emitTree(pn2)) {
++BytecodeEmitter::emitStatementList(ListNode* stmtList)
++{
++    for (ParseNode* stmt : stmtList->contents()) {
++        if (!emitTree(stmt)) {
+             return false;
+         }
+     }
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitExpressionStatement(ParseNode* pn)
+@@ -6912,33 +6908,33 @@ BytecodeEmitter::emitSelfHostedCallFunct
+     // callFunction(fun, thisArg, arg0, arg1) thus becomes:
+     // - emit lookup for fun
+     // - emit lookup for thisArg
+     // - emit lookups for arg0, arg1
+     //
+     // argc is set to the amount of actually emitted args and the
+     // emitting of args below is disabled by setting emitArgs to false.
+     ParseNode* pn_callee = pn->pn_left;
+-    ParseNode* pn_args = pn->pn_right;
++    ListNode* argsList = &pn->pn_right->as<ListNode>();
+ 
+     const char* errorName = SelfHostedCallFunctionName(pn_callee->name(), cx);
+ 
+-    if (pn_args->pn_count < 2) {
++    if (argsList->count() < 2) {
+         reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s");
+         return false;
+     }
+ 
+     JSOp callOp = pn->getOp();
+     if (callOp != JSOP_CALL) {
+         reportError(pn, JSMSG_NOT_CONSTRUCTOR, errorName);
+         return false;
+     }
+ 
+     bool constructing = pn_callee->name() == cx->names().constructContentFunction;
+-    ParseNode* funNode = pn_args->pn_head;
++    ParseNode* funNode = argsList->head();
+     if (constructing) {
+         callOp = JSOP_NEW;
+     } else if (funNode->getKind() == ParseNodeKind::Name &&
+                funNode->name() == cx->names().std_Function_apply) {
+         callOp = JSOP_FUNAPPLY;
+     }
+ 
+     if (!emitTree(funNode)) {
+@@ -6976,37 +6972,37 @@ BytecodeEmitter::emitSelfHostedCallFunct
+     }
+ 
+     if (constructing) {
+         if (!emitTree(thisOrNewTarget)) {
+             return false;
+         }
+     }
+ 
+-    uint32_t argc = pn_args->pn_count - 2;
++    uint32_t argc = argsList->count() - 2;
+     if (!emitCall(callOp, argc)) {
+         return false;
+     }
+ 
+     checkTypeSet(callOp);
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn)
+ {
+-    ParseNode* pn_args = pn->pn_right;
++    ListNode* argsList = &pn->pn_right->as<ListNode>();
+ 
+     // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return')
+-    if (pn_args->pn_count != 3) {
++    if (argsList->count() != 3) {
+         reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
+         return false;
+     }
+ 
+-    ParseNode* genNode = pn_args->pn_head;
++    ParseNode* genNode = argsList->head();
+     if (!emitTree(genNode)) {
+         return false;
+     }
+ 
+     ParseNode* valNode = genNode->pn_next;
+     if (!emitTree(valNode)) {
+         return false;
+     }
+@@ -7033,36 +7029,36 @@ BytecodeEmitter::emitSelfHostedForceInte
+         return false;
+     }
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitSelfHostedAllowContentIter(ParseNode* pn)
+ {
+-    ParseNode* pn_args = pn->pn_right;
+-
+-    if (pn_args->pn_count != 1) {
++    ListNode* argsList = &pn->pn_right->as<ListNode>();
++
++    if (argsList->count() != 1) {
+         reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", "");
+         return false;
+     }
+ 
+     // We're just here as a sentinel. Pass the value through directly.
+-    return emitTree(pn_args->pn_head);
++    return emitTree(argsList->head());
+ }
+ 
+ bool
+ BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
+ {
+-    ParseNode* pn_args = pn->pn_right;
++    ListNode* argsList = &pn->pn_right->as<ListNode>();
+ 
+     // Only optimize when 3 arguments are passed.
+-    MOZ_ASSERT(pn_args->pn_count == 3);
+-
+-    ParseNode* objNode = pn_args->pn_head;
++    MOZ_ASSERT(argsList->count() == 3);
++
++    ParseNode* objNode = argsList->head();
+     if (!emitTree(objNode)) {
+         return false;
+     }
+ 
+     ParseNode* idNode = objNode->pn_next;
+     if (!emitTree(idNode)) {
+         return false;
+     }
+@@ -7076,47 +7072,47 @@ BytecodeEmitter::emitSelfHostedDefineDat
+     // but that's fine because the self-hosted code doesn't use the return
+     // value.
+     return emit1(JSOP_INITELEM);
+ }
+ 
+ bool
+ BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
+ {
+-    ParseNode* pn_args = pn->pn_right;
+-
+-    if (pn_args->pn_count != 2) {
++    ListNode* argsList = &pn->pn_right->as<ListNode>();
++
++    if (argsList->count() != 2) {
+         reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
+         return false;
+     }
+ 
+-    ParseNode* idNode = pn_args->pn_head;
++    ParseNode* idNode = argsList->head();
+     if (!emitTree(idNode)) {
+         return false;
+     }
+ 
+     ParseNode* objNode = idNode->pn_next;
+     if (!emitTree(objNode)) {
+         return false;
+     }
+ 
+     return emit1(JSOP_HASOWN);
+ }
+ 
+ bool
+ BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn)
+ {
+-    ParseNode* pn_args = pn->pn_right;
+-
+-    if (pn_args->pn_count != 3) {
++    ListNode* argsList = &pn->pn_right->as<ListNode>();
++
++    if (argsList->count() != 3) {
+         reportError(pn, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", "");
+         return false;
+     }
+ 
+-    ParseNode* objNode = pn_args->pn_head;
++    ParseNode* objNode = argsList->head();
+     ParseNode* idNode = objNode->pn_next;
+     ParseNode* receiverNode = idNode->pn_next;
+ 
+     if (!emitTree(receiverNode)) {
+         return false;
+     }
+ 
+     if (!emitTree(idNode)) {
+@@ -7144,17 +7140,17 @@ BytecodeEmitter::isRestParameter(ParseNo
+     }
+ 
+     if (!pn->isKind(ParseNodeKind::Name)) {
+         if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) {
+             ParseNode* pn_callee = pn->pn_left;
+             if (pn_callee->getKind() == ParseNodeKind::Name &&
+                 pn_callee->name() == cx->names().allowContentIter)
+             {
+-                return isRestParameter(pn->pn_right->pn_head);
++                return isRestParameter(pn->pn_right->as<ListNode>().head());
+             }
+         }
+         return false;
+     }
+ 
+     JSAtom* name = pn->name();
+     Maybe<NameLocation> paramLoc = locationOfNameBoundInFunctionScope(name);
+     if (paramLoc && lookupName(name) == *paramLoc) {
+@@ -7250,72 +7246,71 @@ BytecodeEmitter::emitCallee(ParseNode* c
+         *callop = false;             /* trigger JSOP_UNDEFINED after */
+         break;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitPipeline(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    MOZ_ASSERT(pn->pn_count >= 2);
+-
+-    if (!emitTree(pn->pn_head)) {
+-        return false;
+-    }
+-
+-    ParseNode* callee = pn->pn_head->pn_next;
++BytecodeEmitter::emitPipeline(ListNode* node)
++{
++    MOZ_ASSERT(node->count() >= 2);
++
++    if (!emitTree(node->head())) {
++        return false;
++    }
++
++    ParseNode* callee = node->head()->pn_next;
+ 
+     do {
+         bool callop = true;
+-        if (!emitCallee(callee, pn, &callop)) {
++        if (!emitCallee(callee, node, &callop)) {
+             return false;
+         }
+ 
+         // Emit room for |this|
+         if (!callop) {
+             if (!emit1(JSOP_UNDEFINED)) {
+                 return false;
+             }
+         }
+ 
+         if (!emit2(JSOP_PICK, 2)) {
+             return false;
+         }
+ 
+-        if (!emitCall(JSOP_CALL, 1, pn)) {
++        if (!emitCall(JSOP_CALL, 1, node)) {
+             return false;
+         }
+ 
+         checkTypeSet(JSOP_CALL);
+     } while ((callee = callee->pn_next));
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread)
+-{
+-    uint32_t argc = pn->pn_count;
++BytecodeEmitter::emitArguments(ListNode* argsList, bool callop, bool spread)
++{
++    uint32_t argc = argsList->count();
+ 
+     if (argc >= ARGC_LIMIT) {
+-        reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
++        reportError(argsList, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
+         return false;
+     }
+ 
+     if (!spread) {
+-        for (ParseNode* pn3 = pn->pn_head; pn3; pn3 = pn3->pn_next) {
+-            if (!emitTree(pn3)) {
++        for (ParseNode* arg : argsList->contents()) {
++            if (!emitTree(arg)) {
+                 return false;
+             }
+         }
+     } else {
+-        ParseNode* args = pn->pn_head;
++        ParseNode* args = argsList->head();
+         bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid);
+         InternalIfEmitter ifNotOptimizable(this);
+ 
+         if (emitOptCode) {
+             // Emit a preparation code to optimize the spread call with a rest
+             // parameter:
+             //
+             //   function f(...args) {
+@@ -7379,17 +7374,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
+      * JSOP_{SET,INIT}PROP.
+      *
+      * Then (or in a call case that has no explicit reference-base
+      * object) we emit JSOP_UNDEFINED to produce the undefined |this|
+      * value required for calls (which non-strict mode functions
+      * will box into the global object).
+      */
+     ParseNode* pn_callee = pn->pn_left;
+-    ParseNode* pn_args = pn->pn_right;
++    ListNode* argsList = &pn->pn_right->as<ListNode>();
+ 
+     bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
+ 
+     if (pn_callee->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
+         // Calls to "forceInterpreter", "callFunction",
+         // "callContentFunction", or "resumeGenerator" in self-hosted
+         // code generate inline bytecode.
+         if (pn_callee->name() == cx->names().callFunction ||
+@@ -7402,17 +7397,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
+             return emitSelfHostedResumeGenerator(pn);
+         }
+         if (pn_callee->name() == cx->names().forceInterpreter) {
+             return emitSelfHostedForceInterpreter();
+         }
+         if (pn_callee->name() == cx->names().allowContentIter) {
+             return emitSelfHostedAllowContentIter(pn);
+         }
+-        if (pn_callee->name() == cx->names().defineDataPropertyIntrinsic && pn_args->pn_count == 3) {
++        if (pn_callee->name() == cx->names().defineDataPropertyIntrinsic && argsList->count() == 3) {
+             return emitSelfHostedDefineDataProperty(pn);
+         }
+         if (pn_callee->name() == cx->names().hasOwn) {
+             return emitSelfHostedHasOwn(pn);
+         }
+         if (pn_callee->name() == cx->names().getPropertySuper) {
+             return emitSelfHostedGetPropertySuper(pn);
+         }
+@@ -7434,21 +7429,21 @@ BytecodeEmitter::emitCallOrNew(ParseNode
+             }
+         } else {
+             if (!emit1(JSOP_UNDEFINED)) {
+                 return false;
+             }
+         }
+     }
+ 
+-    if (!emitArguments(pn_args, callop, spread)) {
+-        return false;
+-    }
+-
+-    uint32_t argc = pn_args->pn_count;
++    if (!emitArguments(argsList, callop, spread)) {
++        return false;
++    }
++
++    uint32_t argc = argsList->count();
+ 
+     /*
+      * Emit code for each argument in order, then emit the JSOP_*CALL or
+      * JSOP_NEW bytecode with a two-byte immediate telling how many args
+      * were pushed on the operand stack.
+      */
+     if (isNewOp) {
+         if (pn->isKind(ParseNodeKind::SuperCall)) {
+@@ -7495,17 +7490,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
+                 //       ^          // column coord
+                 coordNode = pn_callee->pn_right;
+             }
+             break;
+           }
+           case ParseNodeKind::Elem:
+             // obj[expr]() // expression
+             //          ^  // column coord
+-            coordNode = pn_args;
++            coordNode = argsList;
+             break;
+           default:
+             break;
+         }
+     }
+ 
+     if (!spread) {
+         if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
+@@ -7579,117 +7574,113 @@ static inline JSOp
+ BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk)
+ {
+     MOZ_ASSERT(pnk >= ParseNodeKind::BinOpFirst);
+     MOZ_ASSERT(pnk <= ParseNodeKind::BinOpLast);
+     return ParseNodeKindToJSOp[size_t(pnk) - size_t(ParseNodeKind::BinOpFirst)];
+ }
+ 
+ bool
+-BytecodeEmitter::emitRightAssociative(ParseNode* pn)
++BytecodeEmitter::emitRightAssociative(ListNode* node)
+ {
+     // ** is the only right-associative operator.
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Pow));
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
++    MOZ_ASSERT(node->isKind(ParseNodeKind::Pow));
+ 
+     // Right-associative operator chain.
+-    for (ParseNode* subexpr = pn->pn_head; subexpr; subexpr = subexpr->pn_next) {
++    for (ParseNode* subexpr : node->contents()) {
+         if (!emitTree(subexpr)) {
+             return false;
+         }
+     }
+-    for (uint32_t i = 0; i < pn->pn_count - 1; i++) {
++    for (uint32_t i = 0; i < node->count() - 1; i++) {
+         if (!emit1(JSOP_POW)) {
+             return false;
+         }
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitLeftAssociative(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-
++BytecodeEmitter::emitLeftAssociative(ListNode* node)
++{
+     // Left-associative operator chain.
+-    if (!emitTree(pn->pn_head)) {
+-        return false;
+-    }
+-    JSOp op = BinaryOpParseNodeKindToJSOp(pn->getKind());
+-    ParseNode* nextExpr = pn->pn_head->pn_next;
++    if (!emitTree(node->head())) {
++        return false;
++    }
++    JSOp op = BinaryOpParseNodeKindToJSOp(node->getKind());
++    ParseNode* nextExpr = node->head()->pn_next;
+     do {
+         if (!emitTree(nextExpr)) {
+             return false;
+         }
+         if (!emit1(op)) {
+             return false;
+         }
+     } while ((nextExpr = nextExpr->pn_next));
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitLogical(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Or) || pn->isKind(ParseNodeKind::And));
++BytecodeEmitter::emitLogical(ListNode* node)
++{
++    MOZ_ASSERT(node->isKind(ParseNodeKind::Or) || node->isKind(ParseNodeKind::And));
+ 
+     /*
+      * JSOP_OR converts the operand on the stack to boolean, leaves the original
+      * value on the stack and jumps if true; otherwise it falls into the next
+      * bytecode, which pops the left operand and then evaluates the right operand.
+      * The jump goes around the right operand evaluation.
+      *
+      * JSOP_AND converts the operand on the stack to boolean and jumps if false;
+      * otherwise it falls into the right operand's bytecode.
+      */
+ 
+     TDZCheckCache tdzCache(this);
+ 
+     /* Left-associative operator chain: avoid too much recursion. */
+-    ParseNode* pn2 = pn->pn_head;
+-    if (!emitTree(pn2)) {
+-        return false;
+-    }
+-    JSOp op = pn->isKind(ParseNodeKind::Or) ? JSOP_OR : JSOP_AND;
++    ParseNode* expr = node->head();
++    if (!emitTree(expr)) {
++        return false;
++    }
++    JSOp op = node->isKind(ParseNodeKind::Or) ? JSOP_OR : JSOP_AND;
+     JumpList jump;
+     if (!emitJump(op, &jump)) {
+         return false;
+     }
+     if (!emit1(JSOP_POP)) {
+         return false;
+     }
+ 
+     /* Emit nodes between the head and the tail. */
+-    while ((pn2 = pn2->pn_next)->pn_next) {
+-        if (!emitTree(pn2)) {
++    while ((expr = expr->pn_next)->pn_next) {
++        if (!emitTree(expr)) {
+             return false;
+         }
+         if (!emitJump(op, &jump)) {
+             return false;
+         }
+         if (!emit1(JSOP_POP)) {
+             return false;
+         }
+     }
+-    if (!emitTree(pn2)) {
++    if (!emitTree(expr)) {
+         return false;
+     }
+ 
+     if (!emitJumpTargetAndPatch(jump)) {
+         return false;
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitSequenceExpr(ParseNode* pn,
++BytecodeEmitter::emitSequenceExpr(ListNode* node,
+                                   ValueUsage valueUsage /* = ValueUsage::WantValue */)
+ {
+-    for (ParseNode* child = pn->pn_head; ; child = child->pn_next) {
++    for (ParseNode* child = node->head(); ; child = child->pn_next) {
+         if (!updateSourceCoordNotes(child->pn_pos.begin)) {
+             return false;
+         }
+         if (!emitTree(child, child->pn_next ? ValueUsage::IgnoreValue : valueUsage)) {
+             return false;
+         }
+         if (!child->pn_next) {
+             break;
+@@ -7788,19 +7779,19 @@ BytecodeEmitter::emitConditionalExpressi
+         return false;
+     }
+     MOZ_ASSERT(cond.pushed() == 1);
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, PropListType type)
+-{
+-    for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
++BytecodeEmitter::emitPropertyList(ListNode* obj, MutableHandlePlainObject objp, PropListType type)
++{
++    for (ParseNode* propdef : obj->contents()) {
+         if (!updateSourceCoordNotes(propdef->pn_pos.begin)) {
+             return false;
+         }
+ 
+         // Handle __proto__: v specially because *only* this form, and no other
+         // involving "__proto__", performs [[Prototype]] mutation.
+         if (propdef->isKind(ParseNodeKind::MutateProto)) {
+             MOZ_ASSERT(type == ObjectLiteral);
+@@ -7982,20 +7973,20 @@ BytecodeEmitter::emitPropertyList(ParseN
+         }
+     }
+     return true;
+ }
+ 
+ // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
+ // the comment on emitSwitch.
+ MOZ_NEVER_INLINE bool
+-BytecodeEmitter::emitObject(ParseNode* pn)
+-{
+-    if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && checkSingletonContext()) {
+-        return emitSingletonInitialiser(pn);
++BytecodeEmitter::emitObject(ListNode* objNode)
++{
++    if (!objNode->hasNonConstInitializer() && objNode->head() && checkSingletonContext()) {
++        return emitSingletonInitialiser(objNode);
+     }
+ 
+     /*
+      * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
+      * a new object and defining (in source order) each property on the object
+      * (or mutating the object's [[Prototype]], in the case of __proto__).
+      */
+     ptrdiff_t offset = this->offset();
+@@ -8006,23 +7997,23 @@ BytecodeEmitter::emitObject(ParseNode* p
+     // Try to construct the shape of the object as we go, so we can emit a
+     // JSOP_NEWOBJECT with the final shape instead.
+     // In the case of computed property names and indices, we cannot fix the
+     // shape at bytecode compile time. When the shape cannot be determined,
+     // |obj| is nulled out.
+ 
+     // No need to do any guessing for the object kind, since we know the upper
+     // bound of how many properties we plan to have.
+-    gc::AllocKind kind = gc::GetGCObjectKind(pn->pn_count);
++    gc::AllocKind kind = gc::GetGCObjectKind(objNode->count());
+     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
+     if (!obj) {
+         return false;
+     }
+ 
+-    if (!emitPropertyList(pn, &obj, ObjectLiteral)) {
++    if (!emitPropertyList(objNode, &obj, ObjectLiteral)) {
+         return false;
+     }
+ 
+     if (obj) {
+         // The object survived and has a predictable shape: update the original
+         // bytecode.
+         if (!replaceNewInitWithNewObject(obj, offset)) {
+             return false;
+@@ -8049,34 +8040,34 @@ BytecodeEmitter::replaceNewInitWithNewOb
+     MOZ_ASSERT(code[0] == JSOP_NEWINIT);
+     code[0] = JSOP_NEWOBJECT;
+     SET_UINT32(code, index);
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitArrayLiteral(ParseNode* pn)
+-{
+-    if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head) {
++BytecodeEmitter::emitArrayLiteral(ListNode* array)
++{
++    if (!array->hasNonConstInitializer() && array->head()) {
+         if (checkSingletonContext()) {
+             // Bake in the object entirely if it will only be created once.
+-            return emitSingletonInitialiser(pn);
++            return emitSingletonInitialiser(array);
+         }
+ 
+         // If the array consists entirely of primitive values, make a
+         // template object with copy on write elements that can be reused
+         // every time the initializer executes. Don't do this if the array is
+         // small: copying the elements lazily is not worth it in that case.
+         static const size_t MinElementsForCopyOnWrite = 5;
+         if (emitterMode != BytecodeEmitter::SelfHosting &&
+-            pn->pn_count >= MinElementsForCopyOnWrite)
++            array->count() >= MinElementsForCopyOnWrite)
+         {
+             RootedValue value(cx);
+-            if (!pn->getConstantValue(cx, ParseNode::ForCopyOnWriteArray, &value)) {
++            if (!array->getConstantValue(cx, ParseNode::ForCopyOnWriteArray, &value)) {
+                 return false;
+             }
+             if (!value.isMagic(JS_GENERIC_MAGIC)) {
+                 // Note: the group of the template object might not yet reflect
+                 // that the object has copy on write elements. When the
+                 // interpreter or JIT compiler fetches the template, it should
+                 // use ObjectGroup::getOrFixupCopyOnWriteObject to make sure the
+                 // group for the template is accurate. We don't do this here as we
+@@ -8091,35 +8082,34 @@ BytecodeEmitter::emitArrayLiteral(ParseN
+                     return false;
+                 }
+ 
+                 return emitObjectOp(objbox, JSOP_NEWARRAY_COPYONWRITE);
+             }
+         }
+     }
+ 
+-    return emitArray(pn->pn_head, pn->pn_count);
+-}
+-
+-bool
+-BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
+-{
+-
++    return emitArray(array->head(), array->count());
++}
++
++bool
++BytecodeEmitter::emitArray(ParseNode* arrayHead, uint32_t count)
++{
+     /*
+      * Emit code for [a, b, c] that is equivalent to constructing a new
+      * array and in source order evaluating each element value and adding
+      * it to the array, without invoking latent setters.  We use the
+      * JSOP_NEWINIT and JSOP_INITELEM_ARRAY bytecodes to ignore setters and
+      * to avoid dup'ing and popping the array as each element is added, as
+      * JSOP_SETELEM/JSOP_SETPROP would do.
+      */
+ 
+     uint32_t nspread = 0;
+-    for (ParseNode* elt = pn; elt; elt = elt->pn_next) {
+-        if (elt->isKind(ParseNodeKind::Spread)) {
++    for (ParseNode* elem = arrayHead; elem; elem = elem->pn_next) {
++        if (elem->isKind(ParseNodeKind::Spread)) {
+             nspread++;
+         }
+     }
+ 
+     // Array literal's length is limited to NELEMENTS_LIMIT in parser.
+     static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX,
+                   "array literals' maximum length must not exceed limits "
+                   "required by BaselineCompiler::emit_JSOP_NEWARRAY, "
+@@ -8131,54 +8121,54 @@ BytecodeEmitter::emitArray(ParseNode* pn
+                "length");
+ 
+     // For arrays with spread, this is a very pessimistic allocation, the
+     // minimum possible final size.
+     if (!emitUint32Operand(JSOP_NEWARRAY, count - nspread)) {       // ARRAY
+         return false;
+     }
+ 
+-    ParseNode* pn2 = pn;
++    ParseNode* elem = arrayHead;
+     uint32_t index;
+     bool afterSpread = false;
+-    for (index = 0; pn2; index++, pn2 = pn2->pn_next) {
+-        if (!afterSpread && pn2->isKind(ParseNodeKind::Spread)) {
++    for (index = 0; elem; index++, elem = elem->pn_next) {
++        if (!afterSpread && elem->isKind(ParseNodeKind::Spread)) {
+             afterSpread = true;
+             if (!emitNumberOp(index)) {                             // ARRAY INDEX
+                 return false;
+             }
+         }
+-        if (!updateSourceCoordNotes(pn2->pn_pos.begin)) {
++        if (!updateSourceCoordNotes(elem->pn_pos.begin)) {
+             return false;
+         }
+ 
+         bool allowSelfHostedIter = false;
+-        if (pn2->isKind(ParseNodeKind::Elision)) {
++        if (elem->isKind(ParseNodeKind::Elision)) {
+             if (!emit1(JSOP_HOLE)) {
+                 return false;
+             }
+         } else {
+             ParseNode* expr;
+-            if (pn2->isKind(ParseNodeKind::Spread)) {
+-                expr = pn2->pn_kid;
++            if (elem->isKind(ParseNodeKind::Spread)) {
++                expr = elem->pn_kid;
+ 
+                 if (emitterMode == BytecodeEmitter::SelfHosting &&
+                     expr->isKind(ParseNodeKind::Call) &&
+                     expr->pn_left->name() == cx->names().allowContentIter)
+                 {
+                     allowSelfHostedIter = true;
+                 }
+             } else {
+-                expr = pn2;
++                expr = elem;
+             }
+             if (!emitTree(expr)) {                                       // ARRAY INDEX? VALUE
+                 return false;
+             }
+         }
+-        if (pn2->isKind(ParseNodeKind::Spread)) {
++        if (elem->isKind(ParseNodeKind::Spread)) {
+             if (!emitIterator()) {                                       // ARRAY INDEX NEXT ITER
+                 return false;
+             }
+             if (!emit2(JSOP_PICK, 3)) {                                  // INDEX NEXT ITER ARRAY
+                 return false;
+             }
+             if (!emit2(JSOP_PICK, 3)) {                                  // NEXT ITER ARRAY INDEX
+                 return false;
+@@ -8243,36 +8233,36 @@ BytecodeEmitter::emitTypeof(ParseNode* n
+     if (!emitTree(node->pn_kid)) {
+         return false;
+     }
+ 
+     return emit1(op);
+ }
+ 
+ bool
+-BytecodeEmitter::emitFunctionFormalParametersAndBody(ParseNode *pn)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::ParamsBody));
+-
+-    ParseNode* funBody = pn->last();
++BytecodeEmitter::emitFunctionFormalParametersAndBody(ListNode* paramsBody)
++{
++    MOZ_ASSERT(paramsBody->isKind(ParseNodeKind::ParamsBody));
++
++    ParseNode* funBody = paramsBody->last();
+     FunctionBox* funbox = sc->asFunctionBox();
+ 
+     TDZCheckCache tdzCache(this);
+ 
+     if (funbox->hasParameterExprs) {
+         EmitterScope funEmitterScope(this);
+         if (!funEmitterScope.enterFunction(this, funbox)) {
+             return false;
+         }
+ 
+         if (!emitInitializeFunctionSpecialNames()) {
+             return false;
+         }
+ 
+-        if (!emitFunctionFormalParameters(pn)) {
++        if (!emitFunctionFormalParameters(paramsBody)) {
+             return false;
+         }
+ 
+         {
+             Maybe<EmitterScope> extraVarEmitterScope;
+ 
+             if (funbox->hasExtraBodyVarScope()) {
+                 extraVarEmitterScope.emplace(this);
+@@ -8346,39 +8336,39 @@ BytecodeEmitter::emitFunctionFormalParam
+         return false;
+     }
+ 
+     if (!emitInitializeFunctionSpecialNames()) {
+         return false;
+     }
+     switchToMain();
+ 
+-    if (!emitFunctionFormalParameters(pn)) {
++    if (!emitFunctionFormalParameters(paramsBody)) {
+         return false;
+     }
+ 
+     if (!emitFunctionBody(funBody)) {
+         return false;
+     }
+ 
+     return emitterScope.leave(this);
+ }
+ 
+ bool
+-BytecodeEmitter::emitFunctionFormalParameters(ParseNode* pn)
+-{
+-    ParseNode* funBody = pn->last();
++BytecodeEmitter::emitFunctionFormalParameters(ListNode* paramsBody)
++{
++    ParseNode* funBody = paramsBody->last();
+     FunctionBox* funbox = sc->asFunctionBox();
+     EmitterScope* funScope = innermostEmitterScope();
+ 
+     bool hasParameterExprs = funbox->hasParameterExprs;
+     bool hasRest = funbox->hasRest();
+ 
+-    uint16_t argSlot = 0;
+-    for (ParseNode* arg = pn->pn_head; arg != funBody; arg = arg->pn_next, argSlot++) {
++    int16_t argSlot = 0;
++    for (ParseNode* arg : paramsBody->contentsTo(funBody)) {
+         ParseNode* bindingElement = arg;
+         ParseNode* initializer = nullptr;
+         if (arg->isKind(ParseNodeKind::Assign)) {
+             bindingElement = arg->pn_left;
+             initializer = arg->pn_right;
+         }
+ 
+         // Left-hand sides are either simple names or destructuring patterns.
+@@ -8454,17 +8444,17 @@ BytecodeEmitter::emitFunctionFormalParam
+             // already on the stack.
+             if (!initializer && !isRest && !emitArgOp(JSOP_GETARG, argSlot)) {
+                 return false;
+             }
+ 
+             // If there's an parameter expression var scope, the destructuring
+             // declaration needs to initialize the name in the function scope,
+             // which is not the innermost scope.
+-            if (!emitDestructuringOps(bindingElement,
++            if (!emitDestructuringOps(&bindingElement->as<ListNode>(),
+                                       paramExprVarScope
+                                       ? DestructuringFormalParameterInVarScope
+                                       : DestructuringDeclaration))
+             {
+                 return false;
+             }
+ 
+             if (!emit1(JSOP_POP)) {
+@@ -8507,16 +8497,18 @@ BytecodeEmitter::emitFunctionFormalParam
+             }
+         }
+ 
+         if (paramExprVarScope) {
+             if (!paramExprVarScope->leave(this)) {
+                 return false;
+             }
+         }
++
++        argSlot++;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitInitializeFunctionSpecialNames()
+ {
+@@ -8646,19 +8638,19 @@ bool
+ BytecodeEmitter::emitClass(ParseNode* pn)
+ {
+     ClassNode& classNode = pn->as<ClassNode>();
+ 
+     ClassNames* names = classNode.names();
+ 
+     ParseNode* heritageExpression = classNode.heritage();
+ 
+-    ParseNode* classMethods = classNode.methodList();
++    ListNode* classMethods = classNode.methodList();
+     ParseNode* constructor = nullptr;
+-    for (ParseNode* mn = classMethods->pn_head; mn; mn = mn->pn_next) {
++    for (ParseNode* mn : classMethods->contents()) {
+         ClassMethod& method = mn->as<ClassMethod>();
+         ParseNode& methodName = method.name();
+         if (!method.isStatic() &&
+             (methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
+              methodName.isKind(ParseNodeKind::String)) &&
+             methodName.pn_atom == cx->names().constructor)
+         {
+             constructor = &method.method();
+@@ -8929,17 +8921,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+     switch (pn->getKind()) {
+       case ParseNodeKind::Function:
+         if (!emitFunction(pn)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::ParamsBody:
+-        if (!emitFunctionFormalParametersAndBody(pn)) {
++        if (!emitFunctionFormalParametersAndBody(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::If:
+         if (!emitIf(pn)) {
+             return false;
+         }
+@@ -9005,17 +8997,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::Catch:
+         if (!emitCatch(pn)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Var:
+-        if (!emitDeclarationList(pn)) {
++        if (!emitDeclarationList(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Return:
+         if (!emitReturn(pn)) {
+             return false;
+         }
+@@ -9047,17 +9039,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::Await:
+         if (!emitAwaitInInnermostScope(pn)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::StatementList:
+-        if (!emitStatementList(pn)) {
++        if (!emitStatementList(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::EmptyStatement:
+         break;
+ 
+       case ParseNodeKind::ExpressionStatement:
+@@ -9068,17 +9060,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::Label:
+         if (!emitLabeledStatement(&pn->as<LabeledStatement>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Comma:
+-        if (!emitSequenceExpr(pn, valueUsage)) {
++        if (!emitSequenceExpr(&pn->as<ListNode>(), valueUsage)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Assign:
+       case ParseNodeKind::AddAssign:
+       case ParseNodeKind::SubAssign:
+       case ParseNodeKind::BitOrAssign:
+@@ -9099,17 +9091,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+       case ParseNodeKind::Conditional:
+         if (!emitConditionalExpression(pn->as<ConditionalExpression>(), valueUsage)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Or:
+       case ParseNodeKind::And:
+-        if (!emitLogical(pn)) {
++        if (!emitLogical(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Add:
+       case ParseNodeKind::Sub:
+       case ParseNodeKind::BitOr:
+       case ParseNodeKind::BitXor:
+@@ -9125,29 +9117,29 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+       case ParseNodeKind::In:
+       case ParseNodeKind::InstanceOf:
+       case ParseNodeKind::Lsh:
+       case ParseNodeKind::Rsh:
+       case ParseNodeKind::Ursh:
+       case ParseNodeKind::Star:
+       case ParseNodeKind::Div:
+       case ParseNodeKind::Mod:
+-        if (!emitLeftAssociative(pn)) {
++        if (!emitLeftAssociative(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Pow:
+-        if (!emitRightAssociative(pn)) {
++        if (!emitRightAssociative(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Pipeline:
+-        if (!emitPipeline(pn)) {
++        if (!emitPipeline(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::TypeOfName:
+         if (!emitTypeof(pn, JSOP_TYPEOF)) {
+             return false;
+         }
+@@ -9239,17 +9231,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+       case ParseNodeKind::LexicalScope:
+         if (!emitLexicalScope(pn)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+-        if (!emitDeclarationList(pn)) {
++        if (!emitDeclarationList(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Import:
+         MOZ_ASSERT(sc->isModuleContext());
+         break;
+ 
+@@ -9269,41 +9261,41 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+         }
+         break;
+ 
+       case ParseNodeKind::ExportFrom:
+         MOZ_ASSERT(sc->isModuleContext());
+         break;
+ 
+       case ParseNodeKind::CallSiteObj:
+-        if (!emitCallSiteObject(pn)) {
++        if (!emitCallSiteObject(&pn->as<CallSiteNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Array:
+-        if (!emitArrayLiteral(pn)) {
++        if (!emitArrayLiteral(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Object:
+-        if (!emitObject(pn)) {
++        if (!emitObject(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Name:
+         if (!emitGetName(pn)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::TemplateStringList:
+-        if (!emitTemplateString(pn)) {
++        if (!emitTemplateString(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::TemplateString:
+       case ParseNodeKind::String:
+         if (!emitAtomOp(pn, JSOP_STRING)) {
+             return false;
+diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
+--- a/js/src/frontend/BytecodeEmitter.h
++++ b/js/src/frontend/BytecodeEmitter.h
+@@ -558,33 +558,33 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+                                SrcNoteType noteType = SRC_NULL);
+ 
+     MOZ_MUST_USE bool emitIndex32(JSOp op, uint32_t index);
+     MOZ_MUST_USE bool emitIndexOp(JSOp op, uint32_t index);
+ 
+     MOZ_MUST_USE bool emitAtomOp(JSAtom* atom, JSOp op);
+     MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op);
+ 
+-    MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn);
+-    MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count);
++    MOZ_MUST_USE bool emitArrayLiteral(ListNode* array);
++    MOZ_MUST_USE bool emitArray(ParseNode* arrayHead, uint32_t count);
+ 
+     MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
+     MOZ_MUST_USE bool emitInternedObjectOp(uint32_t index, JSOp op);
+     MOZ_MUST_USE bool emitObjectOp(ObjectBox* objbox, JSOp op);
+     MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
+     MOZ_MUST_USE bool emitRegExp(uint32_t index);
+ 
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false);
+-    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn);
++    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ListNode* objNode);
+ 
+     MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
+ 
+-    MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn);
++    MOZ_MUST_USE bool emitHoistedFunctionsInList(ListNode* stmtList);
+ 
+-    MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
++    MOZ_MUST_USE bool emitPropertyList(ListNode* obj, MutableHandlePlainObject objp,
+                                        PropListType type);
+ 
+     // To catch accidental misuse, emitUint16Operand/emit3 assert that they are
+     // not used to unconditionally emit JSOP_GETLOCAL. Variable access should
+     // instead be emitted using EmitVarOp. In special cases, when the caller
+     // definitely knows that a given local slot is unaliased, this function may be
+     // used as a non-asserting version of emitUint16Operand.
+     MOZ_MUST_USE bool emitLocalOp(JSOp op, uint32_t slot);
+@@ -628,18 +628,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_MUST_USE bool emitInitializeName(HandleAtom name, RHSEmitter emitRhs) {
+         return emitSetOrInitializeName(name, emitRhs, true);
+     }
+ 
+     MOZ_MUST_USE bool emitTDZCheckIfNeeded(JSAtom* name, const NameLocation& loc);
+ 
+     MOZ_MUST_USE bool emitNameIncDec(ParseNode* pn);
+ 
+-    MOZ_MUST_USE bool emitDeclarationList(ParseNode* decls);
+-    MOZ_MUST_USE bool emitSingleDeclaration(ParseNode* decls, ParseNode* decl,
++    MOZ_MUST_USE bool emitDeclarationList(ListNode* declList);
++    MOZ_MUST_USE bool emitSingleDeclaration(ParseNode* declList, ParseNode* decl,
+                                             ParseNode* initializer);
+ 
+     MOZ_MUST_USE bool emitNewInit();
+     MOZ_MUST_USE bool emitSingletonInitialiser(ParseNode* pn);
+ 
+     MOZ_MUST_USE bool emitPrepareIteratorResult();
+     MOZ_MUST_USE bool emitFinishIteratorResult(bool done);
+     MOZ_MUST_USE bool iteratorResultShape(unsigned* shape);
+@@ -715,24 +715,24 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     // emitSetOrInitializeDestructuring assumes the lhs expression's reference
+     // and the to-be-destructured value has been pushed on the stack.  It emits
+     // code to destructure a single lhs expression (either a name or a compound
+     // []/{} expression).
+     MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav);
+ 
+     // emitDestructuringObjRestExclusionSet emits the property exclusion set
+     // for the rest-property in an object pattern.
+-    MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ParseNode* pattern);
++    MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ListNode* pattern);
+ 
+     // emitDestructuringOps assumes the to-be-destructured value has been
+     // pushed on the stack and emits code to destructure each part of a [] or
+     // {} lhs expression.
+-    MOZ_MUST_USE bool emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav);
+-    MOZ_MUST_USE bool emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlavor flav);
+-    MOZ_MUST_USE bool emitDestructuringOpsObject(ParseNode* pattern, DestructuringFlavor flav);
++    MOZ_MUST_USE bool emitDestructuringOps(ListNode* pattern, DestructuringFlavor flav);
++    MOZ_MUST_USE bool emitDestructuringOpsArray(ListNode* pattern, DestructuringFlavor flav);
++    MOZ_MUST_USE bool emitDestructuringOpsObject(ListNode* pattern, DestructuringFlavor flav);
+ 
+     enum class CopyOption {
+         Filtered, Unfiltered
+     };
+ 
+     // Calls either the |CopyDataProperties| or the
+     // |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or
+     // two in the latter case) elements from the stack.
+@@ -771,47 +771,47 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     // is called at compile time.
+     MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern);
+ 
+     MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name);
+ 
+     MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
+     MOZ_MUST_USE bool emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern);
+ 
+-    MOZ_MUST_USE bool emitCallSiteObject(ParseNode* pn);
+-    MOZ_MUST_USE bool emitTemplateString(ParseNode* pn);
++    MOZ_MUST_USE bool emitCallSiteObject(CallSiteNode* callSiteObj);
++    MOZ_MUST_USE bool emitTemplateString(ListNode* templateString);
+     MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rhs);
+ 
+     MOZ_MUST_USE bool emitReturn(ParseNode* pn);
+     MOZ_MUST_USE bool emitExpressionStatement(ParseNode* pn);
+-    MOZ_MUST_USE bool emitStatementList(ParseNode* pn);
++    MOZ_MUST_USE bool emitStatementList(ListNode* stmtList);
+ 
+     MOZ_MUST_USE bool emitDeleteName(ParseNode* pn);
+     MOZ_MUST_USE bool emitDeleteProperty(ParseNode* pn);
+     MOZ_MUST_USE bool emitDeleteElement(ParseNode* pn);
+     MOZ_MUST_USE bool emitDeleteExpression(ParseNode* pn);
+ 
+     // |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR.
+     MOZ_MUST_USE bool emitTypeof(ParseNode* node, JSOp op);
+ 
+     MOZ_MUST_USE bool emitUnary(ParseNode* pn);
+-    MOZ_MUST_USE bool emitRightAssociative(ParseNode* pn);
+-    MOZ_MUST_USE bool emitLeftAssociative(ParseNode* pn);
+-    MOZ_MUST_USE bool emitLogical(ParseNode* pn);
+-    MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn,
++    MOZ_MUST_USE bool emitRightAssociative(ListNode* node);
++    MOZ_MUST_USE bool emitLeftAssociative(ListNode* node);
++    MOZ_MUST_USE bool emitLogical(ListNode* node);
++    MOZ_MUST_USE bool emitSequenceExpr(ListNode* node,
+                                        ValueUsage valueUsage = ValueUsage::WantValue);
+ 
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn);
+ 
+     MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional,
+                                                 ValueUsage valueUsage = ValueUsage::WantValue);
+ 
+     bool isRestParameter(ParseNode* pn);
+ 
+-    MOZ_MUST_USE bool emitArguments(ParseNode* pn, bool callop, bool spread);
++    MOZ_MUST_USE bool emitArguments(ListNode* argsList, bool callop, bool spread);
+     MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
+     MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
+     MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
+     MOZ_MUST_USE bool emitSelfHostedForceInterpreter();
+     MOZ_MUST_USE bool emitSelfHostedAllowContentIter(ParseNode* pn);
+     MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(ParseNode* pn);
+     MOZ_MUST_USE bool emitSelfHostedGetPropertySuper(ParseNode* pn);
+     MOZ_MUST_USE bool emitSelfHostedHasOwn(ParseNode* pn);
+@@ -825,18 +825,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_MUST_USE bool emitForIn(ParseNode* pn, const EmitterScope* headLexicalEmitterScope);
+     MOZ_MUST_USE bool emitForOf(ParseNode* pn, const EmitterScope* headLexicalEmitterScope);
+ 
+     MOZ_MUST_USE bool emitInitializeForInOrOfTarget(ParseNode* forHead);
+ 
+     MOZ_MUST_USE bool emitBreak(PropertyName* label);
+     MOZ_MUST_USE bool emitContinue(PropertyName* label);
+ 
+-    MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ParseNode* pn);
+-    MOZ_MUST_USE bool emitFunctionFormalParameters(ParseNode* pn);
++    MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ListNode* paramsBody);
++    MOZ_MUST_USE bool emitFunctionFormalParameters(ListNode* paramsBody);
+     MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
+     MOZ_MUST_USE bool emitFunctionBody(ParseNode* pn);
+     MOZ_MUST_USE bool emitLexicalInitialization(ParseNode* pn);
+ 
+     // Emit bytecode for the spread operator.
+     //
+     // emitSpread expects the current index (I) of the array, the array itself
+     // and the iterator to be on the stack in that order (iterator on the bottom).
+@@ -850,17 +850,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_MUST_USE bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
+     MOZ_MUST_USE bool emitSuperGetProp(ParseNode* pn, bool isCall = false);
+     MOZ_MUST_USE bool emitSuperElemOperands(ParseNode* pn,
+                                             EmitElemOption opts = EmitElemOption::Get);
+     MOZ_MUST_USE bool emitSuperGetElem(ParseNode* pn, bool isCall = false);
+ 
+     MOZ_MUST_USE bool emitCallee(ParseNode* callee, ParseNode* call, bool* callop);
+ 
+-    MOZ_MUST_USE bool emitPipeline(ParseNode* pn);
++    MOZ_MUST_USE bool emitPipeline(ListNode* node);
+ 
+     MOZ_MUST_USE bool emitExportDefault(ParseNode* pn);
+ };
+ 
+ class MOZ_RAII AutoCheckUnstableEmitterScope {
+ #ifdef DEBUG
+     bool prev_;
+     BytecodeEmitter* bce_;
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -28,17 +28,17 @@ using JS::ToInt32;
+ using JS::ToUint32;
+ 
+ static bool
+ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result);
+ 
+ static bool
+ ListContainsHoistedDeclaration(JSContext* cx, ListNode* list, bool* result)
+ {
+-    for (ParseNode* node = list->pn_head; node; node = node->pn_next) {
++    for (ParseNode* node : list->contents()) {
+         if (!ContainsHoistedDeclaration(cx, node, result)) {
+             return false;
+         }
+         if (*result) {
+             return true;
+         }
+     }
+ 
+@@ -73,17 +73,17 @@ ContainsHoistedDeclaration(JSContext* cx
+       // Base case.
+       case ParseNodeKind::Var:
+         *result = true;
+         return true;
+ 
+       // Non-global lexical declarations are block-scoped (ergo not hoistable).
+       case ParseNodeKind::Let:
+       case ParseNodeKind::Const:
+-        MOZ_ASSERT(node->isArity(PN_LIST));
++        MOZ_ASSERT(node->is<ListNode>());
+         *result = false;
+         return true;
+ 
+       // Similarly to the lexical declarations above, classes cannot add hoisted
+       // declarations
+       case ParseNodeKind::Class:
+         MOZ_ASSERT(node->isArity(PN_TERNARY));
+         *result = false;
+@@ -392,17 +392,17 @@ ContainsHoistedDeclaration(JSContext* cx
+       case ParseNodeKind::PosHolder:
+       case ParseNodeKind::SuperCall:
+       case ParseNodeKind::SuperBase:
+       case ParseNodeKind::SetThis:
+         MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
+                   "some parent node without recurring to test this node");
+ 
+       case ParseNodeKind::Pipeline:
+-        MOZ_ASSERT(node->isArity(PN_LIST));
++        MOZ_ASSERT(node->is<ListNode>());
+         *result = false;
+         return true;
+ 
+       case ParseNodeKind::Limit: // invalid sentinel value
+         MOZ_CRASH("unexpected ParseNodeKind::Limit in node");
+     }
+ 
+     MOZ_CRASH("invalid node kind");
+@@ -743,23 +743,22 @@ FoldIncrementDecrement(JSContext* cx, Pa
+     MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls));
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldAndOr(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
+ {
+-    ParseNode* node = *nodePtr;
++    ListNode* node = &(*nodePtr)->as<ListNode>();
+ 
+     MOZ_ASSERT(node->isKind(ParseNodeKind::And) || node->isKind(ParseNodeKind::Or));
+-    MOZ_ASSERT(node->isArity(PN_LIST));
+ 
+     bool isOrNode = node->isKind(ParseNodeKind::Or);
+-    ParseNode** elem = &node->pn_head;
++    ParseNode** elem = node->unsafeHeadReference();
+     do {
+         if (!Fold(cx, elem, parser)) {
+             return false;
+         }
+ 
+         Truthiness t = Boolish(*elem);
+ 
+         // If we don't know the constant-folded node's truthiness, we can't
+@@ -771,17 +770,17 @@ FoldAndOr(JSContext* cx, ParseNode** nod
+         }
+ 
+         // If the constant-folded node's truthiness will terminate the
+         // condition -- `a || true || expr` or |b && false && expr| -- then
+         // trailing nodes will never be evaluated.  Truncate the list after
+         // the known-truthiness node, as it's the overall result.
+         if ((t == Truthy) == isOrNode) {
+             for (ParseNode* next = (*elem)->pn_next; next; next = next->pn_next) {
+-                --node->pn_count;
++                node->unsafeDecrementCount();
+             }
+ 
+             // Terminate the original and/or list at the known-truthiness
+             // node.
+             (*elem)->pn_next = nullptr;
+             elem = &(*elem)->pn_next;
+             break;
+         }
+@@ -790,35 +789,31 @@ FoldAndOr(JSContext* cx, ParseNode** nod
+ 
+         // We've encountered a vacuous node that'll never short-circuit
+         // evaluation.
+         if ((*elem)->pn_next) {
+             // This node is never the overall result when there are
+             // subsequent nodes.  Remove it.
+             ParseNode* elt = *elem;
+             *elem = elt->pn_next;
+-            --node->pn_count;
++            node->unsafeDecrementCount();
+         } else {
+             // Otherwise this node is the result of the overall expression,
+             // so leave it alone.  And we're done.
+             elem = &(*elem)->pn_next;
+             break;
+         }
+     } while (*elem);
+ 
+-    // If the last node in the list was replaced, we need to update the
+-    // tail pointer in the original and/or node.
+-    node->pn_tail = elem;
+-
+-    node->checkListConsistency();
++    node->unsafeReplaceTail(elem);
+ 
+     // If we removed nodes, we may have to replace a one-element list with
+     // its element.
+-    if (node->pn_count == 1) {
+-        ParseNode* first = node->pn_head;
++    if (node->count() == 1) {
++        ParseNode* first = node->head();
+         ReplaceNode(nodePtr, first);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldConditional(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
+@@ -961,17 +956,17 @@ FoldIf(JSContext* cx, ParseNode** nodePt
+         }
+ 
+         if (!replacement) {
+             // If there's no replacement node, we have a constantly-false |if|
+             // with no |else|.  Replace the entire thing with an empty
+             // statement list.
+             node->setKind(ParseNodeKind::StatementList);
+             node->setArity(PN_LIST);
+-            node->makeEmpty();
++            node->as<ListNode>().makeEmpty();
+         } else {
+             // Replacement invalidates |nextNode|, so reset it (if the
+             // replacement requires folding) or clear it (if |alternative|
+             // is dead code) as needed.
+             if (nextNode) {
+                 nextNode = (*nextNode == replacement) ? nodePtr : nullptr;
+             }
+             ReplaceNode(nodePtr, replacement);
+@@ -1044,49 +1039,47 @@ FoldModule(JSContext* cx, ParseNode* nod
+     MOZ_ASSERT(node->isArity(PN_CODE));
+ 
+     ParseNode*& moduleBody = node->pn_body;
+     MOZ_ASSERT(moduleBody);
+     return Fold(cx, &moduleBody, parser);
+ }
+ 
+ static bool
+-FoldBinaryArithmetic(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldBinaryArithmetic(JSContext* cx, ListNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Sub) ||
+                node->isKind(ParseNodeKind::Star) ||
+                node->isKind(ParseNodeKind::Lsh) ||
+                node->isKind(ParseNodeKind::Rsh) ||
+                node->isKind(ParseNodeKind::Ursh) ||
+                node->isKind(ParseNodeKind::Div) ||
+                node->isKind(ParseNodeKind::Mod));
+-    MOZ_ASSERT(node->isArity(PN_LIST));
+-    MOZ_ASSERT(node->pn_count >= 2);
++    MOZ_ASSERT(node->count() >= 2);
+ 
+     // Fold each operand, ideally into a number.
+-    ParseNode** listp = &node->pn_head;
++    ParseNode** listp = node->unsafeHeadReference();
+     for (; *listp; listp = &(*listp)->pn_next) {
+         if (!Fold(cx, listp, parser)) {
+             return false;
+         }
+ 
+         if (!FoldType(cx, *listp, ParseNodeKind::Number)) {
+             return false;
+         }
+     }
+ 
+-    // Repoint the list's tail pointer.
+-    node->pn_tail = listp;
++    node->unsafeReplaceTail(listp);
+ 
+     // Now fold all leading numeric terms together into a single number.
+     // (Trailing terms for the non-shift operations can't be folded together
+     // due to floating point imprecision.  For example, if |x === -2**53|,
+     // |x - 1 - 1 === -2**53| but |x - 2 === -2**53 - 2|.  Shifts could be
+     // folded, but it doesn't seem worth the effort.)
+-    ParseNode* elem = node->pn_head;
++    ParseNode* elem = node->head();
+     ParseNode* next = elem->pn_next;
+     if (elem->isKind(ParseNodeKind::Number)) {
+         ParseNodeKind kind = node->getKind();
+         while (true) {
+             if (!next || !next->isKind(ParseNodeKind::Number)) {
+                 break;
+             }
+ 
+@@ -1095,96 +1088,89 @@ FoldBinaryArithmetic(JSContext* cx, Pars
+             next = next->pn_next;
+             elem->pn_next = next;
+ 
+             elem->setKind(ParseNodeKind::Number);
+             elem->setOp(JSOP_DOUBLE);
+             elem->setArity(PN_NULLARY);
+             elem->pn_dval = d;
+ 
+-            node->pn_count--;
++            node->unsafeDecrementCount();
+         }
+ 
+-        if (node->pn_count == 1) {
+-            MOZ_ASSERT(node->pn_head == elem);
++        if (node->count() == 1) {
++            MOZ_ASSERT(node->head() == elem);
+             MOZ_ASSERT(elem->isKind(ParseNodeKind::Number));
+ 
+             double d = elem->pn_dval;
+             node->setKind(ParseNodeKind::Number);
+             node->setArity(PN_NULLARY);
+             node->setOp(JSOP_DOUBLE);
+             node->pn_dval = d;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldExponentiation(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldExponentiation(JSContext* cx, ListNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Pow));
+-    MOZ_ASSERT(node->isArity(PN_LIST));
+-    MOZ_ASSERT(node->pn_count >= 2);
++    MOZ_ASSERT(node->count() >= 2);
+ 
+     // Fold each operand, ideally into a number.
+-    ParseNode** listp = &node->pn_head;
++    ParseNode** listp = node->unsafeHeadReference();
+     for (; *listp; listp = &(*listp)->pn_next) {
+         if (!Fold(cx, listp, parser)) {
+             return false;
+         }
+ 
+         if (!FoldType(cx, *listp, ParseNodeKind::Number)) {
+             return false;
+         }
+     }
+ 
+-    // Repoint the list's tail pointer.
+-    node->pn_tail = listp;
++    node->unsafeReplaceTail(listp);
+ 
+     // Unlike all other binary arithmetic operators, ** is right-associative:
+     // 2**3**5 is 2**(3**5), not (2**3)**5.  As list nodes singly-link their
+     // children, full constant-folding requires either linear space or dodgy
+     // in-place linked list reversal.  So we only fold one exponentiation: it's
+     // easy and addresses common cases like |2**32|.
+-    if (node->pn_count > 2) {
++    if (node->count() > 2) {
+         return true;
+     }
+ 
+-    ParseNode* base = node->pn_head;
++    ParseNode* base = node->head();
+     ParseNode* exponent = base->pn_next;
+     if (!base->isKind(ParseNodeKind::Number) || !exponent->isKind(ParseNodeKind::Number)) {
+         return true;
+     }
+ 
+     double d1 = base->pn_dval, d2 = exponent->pn_dval;
+ 
+     node->setKind(ParseNodeKind::Number);
+     node->setArity(PN_NULLARY);
+     node->setOp(JSOP_DOUBLE);
+     node->pn_dval = ecmaPow(d1, d2);
+     return true;
+ }
+ 
+ static bool
+-FoldList(JSContext* cx, ParseNode* list, PerHandlerParser<FullParseHandler>& parser)
++FoldList(JSContext* cx, ListNode* list, PerHandlerParser<FullParseHandler>& parser)
+ {
+-    MOZ_ASSERT(list->isArity(PN_LIST));
+-
+-    ParseNode** elem = &list->pn_head;
++    ParseNode** elem = list->unsafeHeadReference();
+     for (; *elem; elem = &(*elem)->pn_next) {
+         if (!Fold(cx, elem, parser)) {
+             return false;
+         }
+     }
+ 
+-    // Repoint the list's tail pointer if the final element was replaced.
+-    list->pn_tail = elem;
+-
+-    list->checkListConsistency();
++    list->unsafeReplaceTail(elem);
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldReturn(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Return));
+@@ -1333,47 +1319,45 @@ FoldElement(JSContext* cx, ParseNode** n
+     ReplaceNode(nodePtr, dottedAccess);
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldAdd(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
+ {
+-    ParseNode* node = *nodePtr;
++    ListNode* node = &(*nodePtr)->as<ListNode>();
+ 
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Add));
+-    MOZ_ASSERT(node->isArity(PN_LIST));
+-    MOZ_ASSERT(node->pn_count >= 2);
++    MOZ_ASSERT(node->count() >= 2);
+ 
+     // Generically fold all operands first.
+     if (!FoldList(cx, node, parser)) {
+         return false;
+     }
+ 
+     // Fold leading numeric operands together:
+     //
+     //   (1 + 2 + x)  becomes  (3 + x)
+     //
+     // Don't go past the leading operands: additions after a string are
+     // string concatenations, not additions: ("1" + 2 + 3 === "123").
+-    ParseNode* current = node->pn_head;
++    ParseNode* current = node->head();
+     ParseNode* next = current->pn_next;
+     if (current->isKind(ParseNodeKind::Number)) {
+         do {
+             if (!next->isKind(ParseNodeKind::Number)) {
+                 break;
+             }
+ 
+             current->pn_dval += next->pn_dval;
+             current->pn_next = next->pn_next;
+             next = current->pn_next;
+ 
+-            MOZ_ASSERT(node->pn_count > 1);
+-            node->pn_count--;
++            node->unsafeDecrementCount();
+         } while (next);
+     }
+ 
+     // If any operands remain, attempt string concatenation folding.
+     do {
+         // If no operands remain, we're done.
+         if (!next) {
+             break;
+@@ -1430,18 +1414,17 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
+                 combination = ConcatStrings<CanGC>(cx, combination, tmp);
+                 if (!combination) {
+                     return false;
+                 }
+ 
+                 next = next->pn_next;
+                 current->pn_next = next;
+ 
+-                MOZ_ASSERT(node->pn_count > 1);
+-                node->pn_count--;
++                node->unsafeDecrementCount();
+             } while (next);
+ 
+             // Replace |current|'s string with the entire combination.
+             MOZ_ASSERT(current->isKind(ParseNodeKind::String));
+             combination = AtomizeString(cx, combination);
+             if (!combination) {
+                 return false;
+             }
+@@ -1473,20 +1456,19 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
+                 next = current->pn_next;
+             } while (!current->isKind(ParseNodeKind::String) && next);
+         } while (next);
+     } while (false);
+ 
+     MOZ_ASSERT(!next, "must have considered all nodes here");
+     MOZ_ASSERT(!current->pn_next, "current node must be the last node");
+ 
+-    node->pn_tail = &current->pn_next;
+-    node->checkListConsistency();
++    node->unsafeReplaceTail(&current->pn_next);
+ 
+-    if (node->pn_count == 1) {
++    if (node->count() == 1) {
+         // We reduced the list to a constant.  Replace the ParseNodeKind::Add node
+         // with that constant.
+         ReplaceNode(nodePtr, current);
+     }
+ 
+     return true;
+ }
+ 
+@@ -1521,32 +1503,28 @@ FoldCall(JSContext* cx, ParseNode* node,
+     if (!Fold(cx, pn_args, parser)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldArguments(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldArguments(JSContext* cx, ListNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Arguments));
+-    MOZ_ASSERT(node->isArity(PN_LIST));
+ 
+-    ParseNode** listp = &node->pn_head;
++    ParseNode** listp = node->unsafeHeadReference();
+     for (; *listp; listp = &(*listp)->pn_next) {
+         if (!Fold(cx, listp, parser)) {
+             return false;
+         }
+     }
+ 
+-    // If the last node in the list was replaced, pn_tail points into the wrong node.
+-    node->pn_tail = listp;
+-
+-    node->checkListConsistency();
++    node->unsafeReplaceTail(listp);
+     return true;
+ }
+ 
+ static bool
+ FoldForInOrOf(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::ForIn) ||
+                node->isKind(ParseNodeKind::ForOf));
+@@ -1714,35 +1692,36 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+         }
+         return true;
+ 
+       case ParseNodeKind::Pipeline:
+         return true;
+ 
+       case ParseNodeKind::And:
+       case ParseNodeKind::Or:
++        MOZ_ASSERT((*pnp)->is<ListNode>());
+         return FoldAndOr(cx, pnp, parser);
+ 
+       case ParseNodeKind::Function:
+         return FoldFunction(cx, pn, parser);
+ 
+       case ParseNodeKind::Module:
+         return FoldModule(cx, pn, parser);
+ 
+       case ParseNodeKind::Sub:
+       case ParseNodeKind::Star:
+       case ParseNodeKind::Lsh:
+       case ParseNodeKind::Rsh:
+       case ParseNodeKind::Ursh:
+       case ParseNodeKind::Div:
+       case ParseNodeKind::Mod:
+-        return FoldBinaryArithmetic(cx, pn, parser);
++        return FoldBinaryArithmetic(cx, &pn->as<ListNode>(), parser);
+ 
+       case ParseNodeKind::Pow:
+-        return FoldExponentiation(cx, pn, parser);
++        return FoldExponentiation(cx, &pn->as<ListNode>(), parser);
+ 
+       // Various list nodes not requiring care to minimally fold.  Some of
+       // these could be further folded/optimized, but we don't make the effort.
+       case ParseNodeKind::BitOr:
+       case ParseNodeKind::BitXor:
+       case ParseNodeKind::BitAnd:
+       case ParseNodeKind::StrictEq:
+       case ParseNodeKind::Eq:
+@@ -1762,17 +1741,17 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+       case ParseNodeKind::TemplateStringList:
+       case ParseNodeKind::Var:
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+       case ParseNodeKind::ParamsBody:
+       case ParseNodeKind::CallSiteObj:
+       case ParseNodeKind::ExportSpecList:
+       case ParseNodeKind::ImportSpecList:
+-        return FoldList(cx, pn, parser);
++        return FoldList(cx, &pn->as<ListNode>(), parser);
+ 
+       case ParseNodeKind::InitialYield:
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+         MOZ_ASSERT(pn->pn_kid->isKind(ParseNodeKind::Assign) &&
+                    pn->pn_kid->pn_left->isKind(ParseNodeKind::Name) &&
+                    pn->pn_kid->pn_right->isKind(ParseNodeKind::Generator));
+         return true;
+ 
+@@ -1799,26 +1778,27 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+ 
+       case ParseNodeKind::Class:
+         return FoldClass(cx, pn, parser);
+ 
+       case ParseNodeKind::Elem:
+         return FoldElement(cx, pnp, parser);
+ 
+       case ParseNodeKind::Add:
++        MOZ_ASSERT((*pnp)->is<ListNode>());
+         return FoldAdd(cx, pnp, parser);
+ 
+       case ParseNodeKind::Call:
+       case ParseNodeKind::New:
+       case ParseNodeKind::SuperCall:
+       case ParseNodeKind::TaggedTemplate:
+         return FoldCall(cx, pn, parser);
+ 
+       case ParseNodeKind::Arguments:
+-        return FoldArguments(cx, pn, parser);
++        return FoldArguments(cx, &pn->as<ListNode>(), parser);
+ 
+       case ParseNodeKind::Switch:
+       case ParseNodeKind::Colon:
+       case ParseNodeKind::Assign:
+       case ParseNodeKind::AddAssign:
+       case ParseNodeKind::SubAssign:
+       case ParseNodeKind::BitOrAssign:
+       case ParseNodeKind::BitAndAssign:
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -5,16 +5,17 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #ifndef frontend_FullParseHandler_h
+ #define frontend_FullParseHandler_h
+ 
+ #include "mozilla/Attributes.h"
+ #include "mozilla/PodOperations.h"
+ 
++#include <cstddef> // std::nullptr_t
+ #include <string.h>
+ 
+ #include "frontend/ParseNode.h"
+ #include "frontend/SharedContext.h"
+ #include "vm/JSContext.h"
+ 
+ namespace js {
+ 
+@@ -50,17 +51,25 @@ class FullParseHandler
+     size_t lazyClosedOverBindingIndex;
+ 
+     const SourceKind sourceKind_;
+ 
+   public:
+     /* new_ methods for creating parse nodes. These report OOM on context. */
+     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
+ 
+-    typedef ParseNode* Node;
++    // FIXME: Use ListNode instead of ListNodeType as an alias (bug 1489008).
++    using Node = ParseNode*;
++
++#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
++    using longTypeName = typeName*;
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
++#undef DECLARE_TYPE
++
++    using NullNode = std::nullptr_t;
+ 
+     bool isPropertyAccess(ParseNode* node) {
+         return node->isKind(ParseNodeKind::Dot) || node->isKind(ParseNodeKind::Elem);
+     }
+ 
+     bool isFunctionCall(ParseNode* node) {
+         // Note: super() is a special form, *not* a function call.
+         return node->isKind(ParseNodeKind::Call);
+@@ -84,17 +93,24 @@ class FullParseHandler
+                      SourceKind kind = SourceKind::Text)
+       : allocator(cx, alloc),
+         lazyOuterFunction_(cx, lazyOuterFunction),
+         lazyInnerFunctionIndex(0),
+         lazyClosedOverBindingIndex(0),
+         sourceKind_(SourceKind::Text)
+     {}
+ 
+-    static ParseNode* null() { return nullptr; }
++    static NullNode null() { return NullNode(); }
++
++#define DECLARE_AS(typeName, longTypeName, asMethodName) \
++    static longTypeName asMethodName(Node node) { \
++        return &node->as<typeName>(); \
++    }
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
++#undef DECLARE_AS
+ 
+     // The FullParseHandler may be used to create nodes for text sources
+     // (from Parser.h) or for binary sources (from BinSource.h). In the latter
+     // case, some common assumptions on offsets are incorrect, e.g. in `a + b`,
+     // `a`, `b` and `+` may be stored in any order. We use `sourceKind()`
+     // to determine whether we need to check these assumptions.
+     SourceKind sourceKind() const { return sourceKind_; }
+ 
+@@ -128,43 +144,44 @@ class FullParseHandler
+     ParseNode* newStringLiteral(JSAtom* atom, const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::String, JSOP_NOP, pos, atom);
+     }
+ 
+     ParseNode* newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::TemplateString, JSOP_NOP, pos, atom);
+     }
+ 
+-    ParseNode* newCallSiteObject(uint32_t begin) {
+-        ParseNode* callSite = new_<CallSiteNode>(begin);
+-        if (!callSite) {
++    CallSiteNodeType newCallSiteObject(uint32_t begin) {
++        CallSiteNode* callSiteObj = new_<CallSiteNode>(begin);
++        if (!callSiteObj) {
+             return null();
+         }
+ 
+-        Node propExpr = newArrayLiteral(callSite->pn_pos.begin);
+-        if (!propExpr) {
++        ListNode* rawNodes = newArrayLiteral(callSiteObj->pn_pos.begin);
++        if (!rawNodes) {
+             return null();
+         }
+ 
+-        addArrayElement(callSite, propExpr);
++        addArrayElement(callSiteObj, rawNodes);
+ 
+-        return callSite;
++        return callSiteObj;
+     }
+ 
+-    void addToCallSiteObject(ParseNode* callSiteObj, ParseNode* rawNode, ParseNode* cookedNode) {
++    void addToCallSiteObject(CallSiteNodeType callSiteObj, ParseNode* rawNode,
++                             ParseNode* cookedNode) {
+         MOZ_ASSERT(callSiteObj->isKind(ParseNodeKind::CallSiteObj));
+ 
+         addArrayElement(callSiteObj, cookedNode);
+-        addArrayElement(callSiteObj->pn_head, rawNode);
++        addArrayElement(callSiteObj->rawNodes(), rawNode);
+ 
+         /*
+          * We don't know when the last noSubstTemplate will come in, and we
+          * don't want to deal with this outside this method
+          */
+-        setEndPosition(callSiteObj, callSiteObj->pn_head);
++        setEndPosition(callSiteObj, callSiteObj->rawNodes());
+     }
+ 
+     ParseNode* newThisLiteral(const TokenPos& pos, ParseNode* thisName) {
+         return new_<ThisLiteral>(pos, thisName);
+     }
+ 
+     ParseNode* newNullLiteral(const TokenPos& pos) {
+         return new_<NullLiteral>(pos);
+@@ -241,186 +258,179 @@ class FullParseHandler
+     ParseNode* appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
+                                   ParseContext* pc)
+     {
+         return ParseNode::appendOrCreateList(kind, left, right, this, pc);
+     }
+ 
+     // Expressions
+ 
+-    ParseNode* newArrayLiteral(uint32_t begin) {
++    ListNodeType newArrayLiteral(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::Array, TokenPos(begin, begin + 1));
+     }
+ 
+-    MOZ_MUST_USE bool addElision(ParseNode* literal, const TokenPos& pos) {
++    MOZ_MUST_USE bool addElision(ListNodeType literal, const TokenPos& pos) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Array));
+-        MOZ_ASSERT(literal->isArity(PN_LIST));
+ 
+         ParseNode* elision = new_<NullaryNode>(ParseNodeKind::Elision, pos);
+         if (!elision) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ elision);
+-        literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
++        literal->setHasArrayHoleOrSpread();
++        literal->setHasNonConstInitializer();
+         return true;
+     }
+ 
+-    MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) {
++    MOZ_MUST_USE bool addSpreadElement(ListNodeType literal, uint32_t begin, Node inner) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Array));
+-        MOZ_ASSERT(literal->isArity(PN_LIST));
+ 
+         ParseNode* spread = newSpread(begin, inner);
+         if (!spread) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ spread);
+-        literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
++        literal->setHasArrayHoleOrSpread();
++        literal->setHasNonConstInitializer();
+         return true;
+     }
+ 
+-    void addArrayElement(ParseNode* literal, ParseNode* element) {
+-        MOZ_ASSERT(literal->isArity(PN_LIST));
+-
++    void addArrayElement(ListNodeType literal, Node element) {
+         if (!element->isConstant()) {
+-            literal->pn_xflags |= PNX_NONCONST;
++            literal->setHasNonConstInitializer();
+         }
+         addList(/* list = */ literal, /* child = */ element);
+     }
+ 
+     ParseNode* newCall(ParseNode* callee, ParseNode* args) {
+         return new_<BinaryNode>(ParseNodeKind::Call, JSOP_CALL, callee, args);
+     }
+ 
+-    ParseNode* newArguments(const TokenPos& pos) {
++    ListNodeType newArguments(const TokenPos& pos) {
+         return new_<ListNode>(ParseNodeKind::Arguments, JSOP_NOP, pos);
+     }
+ 
+     ParseNode* newSuperCall(ParseNode* callee, ParseNode* args) {
+         return new_<BinaryNode>(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee, args);
+     }
+ 
+     ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) {
+         return new_<BinaryNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args);
+     }
+ 
+-    ParseNode* newObjectLiteral(uint32_t begin) {
++    ListNodeType newObjectLiteral(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::Object, TokenPos(begin, begin + 1));
+     }
+ 
+     ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock,
+                         const TokenPos& pos)
+     {
+         return new_<ClassNode>(name, heritage, methodBlock, pos);
+     }
+-    ParseNode* newClassMethodList(uint32_t begin) {
++    ListNodeType newClassMethodList(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::ClassMethodList, TokenPos(begin, begin + 1));
+     }
+     ParseNode* newClassNames(ParseNode* outer, ParseNode* inner, const TokenPos& pos) {
+         return new_<ClassNames>(outer, inner, pos);
+     }
+     ParseNode* newNewTarget(ParseNode* newHolder, ParseNode* targetHolder) {
+         return new_<BinaryNode>(ParseNodeKind::NewTarget, JSOP_NOP, newHolder, targetHolder);
+     }
+     ParseNode* newPosHolder(const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::PosHolder, pos);
+     }
+     ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) {
+         return new_<UnaryNode>(ParseNodeKind::SuperBase, pos, thisName);
+     }
+-    MOZ_MUST_USE bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) {
++    MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+-        MOZ_ASSERT(literal->isArity(PN_LIST));
+ 
+         // Object literals with mutated [[Prototype]] are non-constant so that
+         // singleton objects will have Object.prototype as their [[Prototype]].
+-        setListFlag(literal, PNX_NONCONST);
++        literal->setHasNonConstInitializer();
+ 
+         ParseNode* mutation = newUnary(ParseNodeKind::MutateProto, begin, expr);
+         if (!mutation) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ mutation);
+         return true;
+     }
+ 
+     ParseNode* newPropertyDefinition(ParseNode* key, ParseNode* val) {
+         MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+         checkAndSetIsDirectRHSAnonFunction(val);
+         return newBinary(ParseNodeKind::Colon, key, val, JSOP_INITPROP);
+     }
+ 
+-    void addPropertyDefinition(ParseNode* literal, ParseNode* propdef) {
++    void addPropertyDefinition(ListNodeType literal, Node propdef) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+-        MOZ_ASSERT(literal->isArity(PN_LIST));
+         MOZ_ASSERT(propdef->isKind(ParseNodeKind::Colon));
+ 
+         if (!propdef->pn_right->isConstant()) {
+-            setListFlag(literal, PNX_NONCONST);
++            literal->setHasNonConstInitializer();
+         }
+ 
+         addList(/* list = */ literal, /* child = */ propdef);
+     }
+ 
+-    MOZ_MUST_USE bool addPropertyDefinition(ParseNode* literal, ParseNode* key, ParseNode* val) {
++    MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node val) {
+         ParseNode* propdef = newPropertyDefinition(key, val);
+         if (!propdef) {
+             return false;
+         }
+         addPropertyDefinition(literal, propdef);
+         return true;
+     }
+ 
+-    MOZ_MUST_USE bool addShorthand(ParseNode* literal, ParseNode* name, ParseNode* expr) {
++    MOZ_MUST_USE bool addShorthand(ListNodeType literal, Node name, Node expr) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+-        MOZ_ASSERT(literal->isArity(PN_LIST));
+         MOZ_ASSERT(name->isKind(ParseNodeKind::ObjectPropertyName));
+         MOZ_ASSERT(expr->isKind(ParseNodeKind::Name));
+         MOZ_ASSERT(name->pn_atom == expr->pn_atom);
+ 
+-        setListFlag(literal, PNX_NONCONST);
++        literal->setHasNonConstInitializer();
+         ParseNode* propdef = newBinary(ParseNodeKind::Shorthand, name, expr, JSOP_INITPROP);
+         if (!propdef) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ propdef);
+         return true;
+     }
+ 
+-    MOZ_MUST_USE bool addSpreadProperty(ParseNode* literal, uint32_t begin, ParseNode* inner) {
++    MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+-        MOZ_ASSERT(literal->isArity(PN_LIST));
+ 
+-        setListFlag(literal, PNX_NONCONST);
++        literal->setHasNonConstInitializer();
+         ParseNode* spread = newSpread(begin, inner);
+         if (!spread) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ spread);
+         return true;
+     }
+ 
+-    MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
+-                                                AccessorType atype)
++    MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key,
++                                                Node fn, AccessorType atype)
+     {
+-        MOZ_ASSERT(literal->isArity(PN_LIST));
+-        literal->pn_xflags |= PNX_NONCONST;
++        literal->setHasNonConstInitializer();
+ 
+         checkAndSetIsDirectRHSAnonFunction(fn);
+ 
+         ParseNode* propdef = newObjectMethodOrPropertyDefinition(key, fn, atype);
+         if (!propdef) {
+             return false;
+         }
+ 
+         addList(/* list = */ literal, /* child = */ propdef);
+         return true;
+     }
+ 
+-    MOZ_MUST_USE bool addClassMethodDefinition(ParseNode* methodList, ParseNode* key, ParseNode* fn,
+-                                               AccessorType atype, bool isStatic)
++    MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key,
++                                               Node fn, AccessorType atype, bool isStatic)
+     {
+         MOZ_ASSERT(methodList->isKind(ParseNodeKind::ClassMethodList));
+         MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+ 
+         checkAndSetIsDirectRHSAnonFunction(fn);
+ 
+         ParseNode* classMethod = new_<ClassMethod>(key, fn, AccessorTypeToJSOp(atype), isStatic);
+         if (!classMethod) {
+@@ -447,60 +457,58 @@ class FullParseHandler
+ 
+     ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value) {
+         TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
+         return new_<UnaryNode>(ParseNodeKind::Await, pos, value);
+     }
+ 
+     // Statements
+ 
+-    ParseNode* newStatementList(const TokenPos& pos) {
++    ListNodeType newStatementList(const TokenPos& pos) {
+         return new_<ListNode>(ParseNodeKind::StatementList, pos);
+     }
+ 
+     MOZ_MUST_USE bool isFunctionStmt(ParseNode* stmt) {
+         while (stmt->isKind(ParseNodeKind::Label)) {
+             stmt = stmt->as<LabeledStatement>().statement();
+         }
+         return stmt->isKind(ParseNodeKind::Function);
+     }
+ 
+-    void addStatementToList(ParseNode* list, ParseNode* stmt) {
++    void addStatementToList(ListNodeType list, Node stmt) {
+         MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));
+ 
+         addList(/* list = */ list, /* child = */ stmt);
+ 
+         if (isFunctionStmt(stmt)) {
+-            // PNX_FUNCDEFS notifies the emitter that the block contains
+-            // body-level function definitions that should be processed
+-            // before the rest of nodes.
+-            list->pn_xflags |= PNX_FUNCDEFS;
++            // Notify the emitter that the block contains body-level function
++            // definitions that should be processed before the rest of nodes.
++            list->setHasTopLevelFunctionDeclarations();
+         }
+     }
+ 
+-    void setListEndPosition(ParseNode* list, const TokenPos& pos) {
++    void setListEndPosition(ListNodeType list, const TokenPos& pos) {
+         MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));
+         list->pn_pos.end = pos.end;
+     }
+ 
+-    void addCaseStatementToList(ParseNode* list, ParseNode* casepn) {
++    void addCaseStatementToList(ListNodeType list, Node caseClause) {
+         MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));
+-        MOZ_ASSERT(casepn->isKind(ParseNodeKind::Case));
+-        MOZ_ASSERT(casepn->pn_right->isKind(ParseNodeKind::StatementList));
++        MOZ_ASSERT(caseClause->isKind(ParseNodeKind::Case));
++        MOZ_ASSERT(caseClause->pn_right->isKind(ParseNodeKind::StatementList));
+ 
+-        addList(/* list = */ list, /* child = */ casepn);
++        addList(/* list = */ list, /* child = */ caseClause);
+ 
+-        if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS) {
+-            list->pn_xflags |= PNX_FUNCDEFS;
++        if (caseClause->pn_right->as<ListNode>().hasTopLevelFunctionDeclarations()) {
++            list->setHasTopLevelFunctionDeclarations();
+         }
+     }
+ 
+-    MOZ_MUST_USE bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) {
++    MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) {
+         MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
+-        MOZ_ASSERT(stmtList->isArity(PN_LIST));
+ 
+         TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1);
+         ParseNode* makeGen = new_<NullaryNode>(ParseNodeKind::Generator, yieldPos);
+         if (!makeGen) {
+             return false;
+         }
+ 
+         MOZ_ASSERT(genName->getOp() == JSOP_GETNAME);
+@@ -755,21 +763,21 @@ class FullParseHandler
+         funcNode->pn_body = kid;
+     }
+     void setFunctionBox(ParseNode* pn, FunctionBox* funbox) {
+         MOZ_ASSERT(pn->isKind(ParseNodeKind::Function));
+         pn->pn_funbox = funbox;
+         funbox->functionNode = pn;
+     }
+     void addFunctionFormalParameter(ParseNode* pn, ParseNode* argpn) {
+-        addList(/* list = */ pn->pn_body, /* child = */ argpn);
++        addList(/* list = */ &pn->pn_body->as<ListNode>(), /* child = */ argpn);
+     }
+     void setFunctionBody(ParseNode* fn, ParseNode* body) {
+         MOZ_ASSERT(fn->pn_body->isKind(ParseNodeKind::ParamsBody));
+-        addList(/* list = */ fn->pn_body, /* child = */ body);
++        addList(/* list = */ &fn->pn_body->as<ListNode>(), /* child = */ body);
+     }
+ 
+     ParseNode* newModule(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Module, JSOP_NOP, pos);
+     }
+ 
+     ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) {
+         return new_<LexicalScopeNode>(bindings, body);
+@@ -863,64 +871,63 @@ class FullParseHandler
+     }
+ 
+     bool isDeclarationKind(ParseNodeKind kind) {
+         return kind == ParseNodeKind::Var ||
+                kind == ParseNodeKind::Let ||
+                kind == ParseNodeKind::Const;
+     }
+ 
+-    ParseNode* newList(ParseNodeKind kind, const TokenPos& pos) {
++    ListNodeType newList(ParseNodeKind kind, const TokenPos& pos) {
+         MOZ_ASSERT(!isDeclarationKind(kind));
+         return new_<ListNode>(kind, JSOP_NOP, pos);
+     }
+ 
+   private:
+     template<typename T>
+     ParseNode* newList(ParseNodeKind kind, const T& begin) = delete;
+ 
+   public:
+-    ParseNode* newList(ParseNodeKind kind, ParseNode* kid) {
++    ListNodeType newList(ParseNodeKind kind, Node kid) {
+         MOZ_ASSERT(!isDeclarationKind(kind));
+         return new_<ListNode>(kind, JSOP_NOP, kid);
+     }
+ 
+-    ParseNode* newDeclarationList(ParseNodeKind kind, const TokenPos& pos) {
++    ListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) {
+         MOZ_ASSERT(isDeclarationKind(kind));
+         return new_<ListNode>(kind, JSOP_NOP, pos);
+     }
+ 
+     bool isDeclarationList(ParseNode* node) {
+         return isDeclarationKind(node->getKind());
+     }
+ 
+-    ParseNode* singleBindingFromDeclaration(ParseNode* decl) {
++    Node singleBindingFromDeclaration(ListNodeType decl) {
+         MOZ_ASSERT(isDeclarationList(decl));
+-        MOZ_ASSERT(decl->pn_count == 1);
+-        return decl->pn_head;
++        MOZ_ASSERT(decl->count() == 1);
++        return decl->head();
+     }
+ 
+-    ParseNode* newCommaExpressionList(ParseNode* kid) {
++    ListNodeType newCommaExpressionList(Node kid) {
+         return new_<ListNode>(ParseNodeKind::Comma, JSOP_NOP, kid);
+     }
+ 
+-    void addList(ParseNode* list, ParseNode* kid) {
++    void addList(ListNodeType list, Node kid) {
+         if (sourceKind_ == SourceKind::Text) {
+             list->append(kid);
+         } else {
+             list->appendWithoutOrderAssumption(kid);
+         }
+     }
+ 
+     void setOp(ParseNode* pn, JSOp op) {
+         pn->setOp(op);
+     }
+-    void setListFlag(ParseNode* pn, unsigned flag) {
+-        MOZ_ASSERT(pn->isArity(PN_LIST));
+-        pn->pn_xflags |= flag;
++    void setListHasNonConstInitializer(ListNodeType literal) {
++        literal->setHasNonConstInitializer();
+     }
+     MOZ_MUST_USE ParseNode* parenthesize(ParseNode* pn) {
+         pn->setInParens(true);
+         return pn;
+     }
+     MOZ_MUST_USE ParseNode* setLikelyIIFE(ParseNode* pn) {
+         return parenthesize(pn);
+     }
+@@ -979,37 +986,24 @@ class FullParseHandler
+ 
+ inline bool
+ FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
+                                                         ParseNode* defaultValue)
+ {
+     MOZ_ASSERT(funcpn->isKind(ParseNodeKind::Function));
+     MOZ_ASSERT(funcpn->isArity(PN_CODE));
+ 
+-    ParseNode* arg = funcpn->pn_body->last();
++    ListNode* body = &funcpn->pn_body->as<ListNode>();
++    ParseNode* arg = body->last();
+     ParseNode* pn = newAssignment(ParseNodeKind::Assign, arg, defaultValue);
+     if (!pn) {
+         return false;
+     }
+ 
+-    funcpn->pn_body->pn_pos.end = pn->pn_pos.end;
+-    ParseNode* pnchild = funcpn->pn_body->pn_head;
+-    ParseNode* pnlast = funcpn->pn_body->last();
+-    MOZ_ASSERT(pnchild);
+-    if (pnchild == pnlast) {
+-        funcpn->pn_body->pn_head = pn;
+-    } else {
+-        while (pnchild->pn_next != pnlast) {
+-            MOZ_ASSERT(pnchild->pn_next);
+-            pnchild = pnchild->pn_next;
+-        }
+-        pnchild->pn_next = pn;
+-    }
+-    funcpn->pn_body->pn_tail = &pn->pn_next;
+-
++    body->replaceLast(pn);
+     return true;
+ }
+ 
+ inline bool
+ FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Name));
+     MOZ_ASSERT(!pn->isInParens());
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -309,22 +309,22 @@ class NameResolver
+     }
+ 
+     /*
+      * Tests whether parents[pos] is a function call whose callee is cur.
+      * This is the case for functions which do things like simply create a scope
+      * for new variables and then return an anonymous function using this scope.
+      */
+     bool isDirectCall(int pos, ParseNode* cur) {
+-        return pos >= 0 && call(parents[pos]) && parents[pos]->pn_head == cur;
++        return pos >= 0 && call(parents[pos]) && parents[pos]->pn_left == cur;
+     }
+ 
+-    bool resolveTemplateLiteral(ParseNode* node, HandleAtom prefix) {
++    bool resolveTemplateLiteral(ListNode* node, HandleAtom prefix) {
+         MOZ_ASSERT(node->isKind(ParseNodeKind::TemplateStringList));
+-        ParseNode* element = node->pn_head;
++        ParseNode* element = node->head();
+         while (true) {
+             MOZ_ASSERT(element->isKind(ParseNodeKind::TemplateString));
+ 
+             element = element->pn_next;
+             if (!element) {
+                 return true;
+             }
+ 
+@@ -345,28 +345,27 @@ class NameResolver
+         // that might contain functions.
+         if (!resolve(tag, prefix)) {
+             return false;
+         }
+ 
+         // The callsite object node is first.  This node only contains
+         // internal strings or undefined and an array -- no user-controlled
+         // expressions.
+-        ParseNode* element = node->pn_right->pn_head;
++        CallSiteNode* element = &node->pn_right->as<ListNode>().head()->as<CallSiteNode>();
+ #ifdef DEBUG
+         {
+-            MOZ_ASSERT(element->isKind(ParseNodeKind::CallSiteObj));
+-            ParseNode* array = element->pn_head;
+-            MOZ_ASSERT(array->isKind(ParseNodeKind::Array));
+-            for (ParseNode* kid = array->pn_head; kid; kid = kid->pn_next) {
+-                MOZ_ASSERT(kid->isKind(ParseNodeKind::TemplateString));
++            ListNode* rawNodes = &element->head()->as<ListNode>();
++            MOZ_ASSERT(rawNodes->isKind(ParseNodeKind::Array));
++            for (ParseNode* raw : rawNodes->contents()) {
++                MOZ_ASSERT(raw->isKind(ParseNodeKind::TemplateString));
+             }
+-            for (ParseNode* next = array->pn_next; next; next = next->pn_next) {
+-                MOZ_ASSERT(next->isKind(ParseNodeKind::TemplateString) ||
+-                           next->isKind(ParseNodeKind::RawUndefined));
++            for (ParseNode* cooked : element->contentsFrom(rawNodes->pn_next)) {
++                MOZ_ASSERT(cooked->isKind(ParseNodeKind::TemplateString) ||
++                           cooked->isKind(ParseNodeKind::RawUndefined));
+             }
+         }
+ #endif
+ 
+         // Next come any interpolated expressions in the tagged template.
+         ParseNode* interpolated = element->pn_next;
+         for (; interpolated; interpolated = interpolated->pn_next) {
+             if (!resolve(interpolated, prefix)) {
+@@ -762,39 +761,36 @@ class NameResolver
+           case ParseNodeKind::Array:
+           case ParseNodeKind::StatementList:
+           case ParseNodeKind::ParamsBody:
+           // Initializers for individual variables, and computed property names
+           // within destructuring patterns, may contain unnamed functions.
+           case ParseNodeKind::Var:
+           case ParseNodeKind::Const:
+           case ParseNodeKind::Let:
+-            MOZ_ASSERT(cur->isArity(PN_LIST));
+-            for (ParseNode* element = cur->pn_head; element; element = element->pn_next) {
++            for (ParseNode* element : cur->as<ListNode>().contents()) {
+                 if (!resolve(element, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           case ParseNodeKind::Object:
+           case ParseNodeKind::ClassMethodList:
+-            MOZ_ASSERT(cur->isArity(PN_LIST));
+-            for (ParseNode* element = cur->pn_head; element; element = element->pn_next) {
++            for (ParseNode* element : cur->as<ListNode>().contents()) {
+                 if (!resolve(element, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           // A template string list's contents alternate raw template string
+           // contents with expressions interpolated into the overall literal.
+           case ParseNodeKind::TemplateStringList:
+-            MOZ_ASSERT(cur->isArity(PN_LIST));
+-            if (!resolveTemplateLiteral(cur, prefix)) {
++            if (!resolveTemplateLiteral(&cur->as<ListNode>(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::TaggedTemplate:
+             MOZ_ASSERT(cur->isArity(PN_BINARY));
+             if (!resolveTaggedTemplate(cur, prefix)) {
+                 return false;
+@@ -812,38 +808,37 @@ class NameResolver
+                 return false;
+             }
+             break;
+ 
+           // Handles the arguments for new/call/supercall, but does _not_ handle
+           // the Arguments node used by tagged template literals, since that is
+           // special-cased inside of resolveTaggedTemplate.
+           case ParseNodeKind::Arguments:
+-            MOZ_ASSERT(cur->isArity(PN_LIST));
+-            for (ParseNode* element = cur->pn_head; element; element = element->pn_next) {
++            for (ParseNode* element : cur->as<ListNode>().contents()) {
+                 if (!resolve(element, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           // Import/export spec lists contain import/export specs containing
+           // only pairs of names. Alternatively, an export spec lists may
+           // contain a single export batch specifier.
+           case ParseNodeKind::ExportSpecList:
+           case ParseNodeKind::ImportSpecList: {
+-            MOZ_ASSERT(cur->isArity(PN_LIST));
+ #ifdef DEBUG
+             bool isImport = cur->isKind(ParseNodeKind::ImportSpecList);
+-            ParseNode* item = cur->pn_head;
++            ListNode* list = &cur->as<ListNode>();
++            ParseNode* item = list->head();
+             if (!isImport && item && item->isKind(ParseNodeKind::ExportBatchSpec)) {
+                 MOZ_ASSERT(item->isArity(PN_NULLARY));
+                 break;
+             }
+-            for (; item; item = item->pn_next) {
++            for (ParseNode* item : list->contents()) {
+                 MOZ_ASSERT(item->isKind(isImport
+                                         ? ParseNodeKind::ImportSpec
+                                         : ParseNodeKind::ExportSpec));
+                 MOZ_ASSERT(item->isArity(PN_BINARY));
+                 MOZ_ASSERT(item->pn_left->isKind(ParseNodeKind::Name));
+                 MOZ_ASSERT(!item->pn_left->expr());
+                 MOZ_ASSERT(item->pn_right->isKind(ParseNodeKind::Name));
+                 MOZ_ASSERT(!item->pn_right->expr());
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -16,36 +16,34 @@
+ 
+ using namespace js;
+ using namespace js::frontend;
+ 
+ using mozilla::IsFinite;
+ 
+ #ifdef DEBUG
+ void
+-ParseNode::checkListConsistency()
++ListNode::checkConsistency() const
+ {
+-    MOZ_ASSERT(isArity(PN_LIST));
+-    ParseNode** tail;
+-    uint32_t count = 0;
+-    if (pn_head) {
+-        ParseNode* last = pn_head;
+-        ParseNode* pn = last;
++    ParseNode* const* tailNode;
++    uint32_t actualCount = 0;
++    if (const ParseNode* last = head()) {
++        const ParseNode* pn = last;
+         while (pn) {
+             last = pn;
+             pn = pn->pn_next;
+-            count++;
++            actualCount++;
+         }
+ 
+-        tail = &last->pn_next;
++        tailNode = &last->pn_next;
+     } else {
+-        tail = &pn_head;
++        tailNode = &pn_u.list.head;
+     }
+-    MOZ_ASSERT(pn_tail == tail);
+-    MOZ_ASSERT(pn_count == count);
++    MOZ_ASSERT(tail() == tailNode);
++    MOZ_ASSERT(count() == actualCount);
+ }
+ #endif
+ 
+ /*
+  * Allocate a ParseNode from parser's node freelist or, failing that, from
+  * cx's temporary arena.
+  */
+ void*
+@@ -88,17 +86,17 @@ ParseNode::appendOrCreateList(ParseNodeK
+ 
+             list->append(right);
+             list->pn_pos.end = right->pn_pos.end;
+ 
+             return list;
+         }
+     }
+ 
+-    ParseNode* list = handler->new_<ListNode>(kind, JSOP_NOP, left);
++    ListNode* list = handler->new_<ListNode>(kind, JSOP_NOP, left);
+     if (!list) {
+         return nullptr;
+     }
+ 
+     list->append(right);
+     return list;
+ }
+ 
+@@ -158,17 +156,17 @@ ParseNode::dump(GenericPrinter& out, int
+         break;
+       case PN_TERNARY:
+         ((TernaryNode*) this)->dump(out, indent);
+         break;
+       case PN_CODE:
+         ((CodeNode*) this)->dump(out, indent);
+         break;
+       case PN_LIST:
+-        ((ListNode*) this)->dump(out, indent);
++        as<ListNode>().dump(out, indent);
+         break;
+       case PN_NAME:
+         ((NameNode*) this)->dump(out, indent);
+         break;
+       case PN_SCOPE:
+         ((LexicalScopeNode*) this)->dump(out, indent);
+         break;
+       default:
+@@ -273,24 +271,22 @@ CodeNode::dump(GenericPrinter& out, int 
+     out.printf(")");
+ }
+ 
+ void
+ ListNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s [", name);
+-    if (pn_head != nullptr) {
++    if (ParseNode* listHead = head()) {
+         indent += strlen(name) + 3;
+-        DumpParseTree(pn_head, out, indent);
+-        ParseNode* pn = pn_head->pn_next;
+-        while (pn != nullptr) {
++        DumpParseTree(listHead, out, indent);
++        for (ParseNode* item : contentsFrom(listHead->pn_next)) {
+             IndentNewLine(out, indent);
+-            DumpParseTree(pn, out, indent);
+-            pn = pn->pn_next;
++            DumpParseTree(item, out, indent);
+         }
+     }
+     out.printf("])");
+ }
+ 
+ template <typename CharT>
+ static void
+ DumpName(GenericPrinter& out, const CharT* s, size_t len)
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -222,46 +222,42 @@ IsDeleteKind(ParseNodeKind kind)
+ 
+ inline bool
+ IsTypeofKind(ParseNodeKind kind)
+ {
+     return ParseNodeKind::TypeOfName <= kind && kind <= ParseNodeKind::TypeOfExpr;
+ }
+ 
+ /*
+- * Label        Variant     Members
+- * -----        -------     -------
+  * <Definitions>
+  * Function name        pn_funbox: ptr to js::FunctionBox holding function
+  *                            object containing arg and var properties.  We
+  *                            create the function object at parse (not emit)
+  *                            time to specialize arg and var bytecodes early.
+  *                          pn_body: ParamsBody, ordinarily;
+  *                            ParseNodeKind::LexicalScope for implicit function in genexpr
+- * ParamsBody list      list of formal parameters with
+- *                              Name node with non-empty name for
+- *                                SingleNameBinding without Initializer
+- *                              Assign node for SingleNameBinding with
+- *                                Initializer
+- *                              Name node with empty name for destructuring
+- *                                pn_expr: Array, Object, or Assign
+- *                                  Array or Object for BindingPattern
+- *                                    without Initializer
+- *                                  Assign for BindingPattern with
+- *                                    Initializer
+- *                          followed by:
+- *                              StatementList node for function body
+- *                                statements,
+- *                              Return for expression closure
+- *                          pn_count: 1 + number of formal parameters
+- *                          pn_tree: ParamsBody or StatementList node
++ * ParamsBody (ListNode)
++ *   head: list of formal parameters with
++ *           * Name node with non-empty name for SingleNameBinding without
++ *             Initializer
++ *           * Assign node for SingleNameBinding with Initializer
++ *           * Name node with empty name for destructuring
++ *               pn_expr: Array or Object for BindingPattern without
++ *                        Initializer, Assign for BindingPattern with
++ *                        Initializer
++ *         followed by either:
++ *           * StatementList node for function body statements
++ *           * Return for expression closure
++ *   count: number of formal parameters + 1
+  * Spread   unary       pn_kid: expression being spread
+  *
+  * <Statements>
+- * StatementList list   pn_head: list of pn_count statements
++ * StatementList (ListNode)
++ *   head: list of N statements
++ *   count: N >= 0
+  * If       ternary     pn_kid1: cond, pn_kid2: then, pn_kid3: else or null.
+  * Switch   binary      pn_left: discriminant
+  *                      pn_right: LexicalScope node that contains the list
+  *                        of Case nodes, with at most one
+  *                        default node.
+  *                      hasDefault: true if there's a default case
+  * Case     binary      pn_left: case-expression if CaseClause, or
+  *                            null if DefaultClause
+@@ -289,86 +285,80 @@ IsTypeofKind(ParseNodeKind kind)
+  * Catch    binary      pn_left: Name, Array, or Object catch
+  *                                   var node
+  *                                   (Array or Object if destructuring),
+  *                                   or null if optional catch binding
+  *                          pn_right: catch block statements
+  * Break    name        pn_atom: label or null
+  * Continue name        pn_atom: label or null
+  * With     binary      pn_left: head expr; pn_right: body;
+- * Var,     list        pn_head: list of Name or Assign nodes
+- * Let,                          each name node has either
+- * Const                           pn_used: false
+- *                                     pn_atom: variable name
+- *                                     pn_expr: initializer or null
+- *                                   or
+- *                                     pn_used: true
+- *                                     pn_atom: variable name
+- *                                     pn_lexdef: def node
+- *                                   each assignment node has
+- *                                     pn_left: Name with pn_used true and
+- *                                              pn_lexdef (NOT pn_expr) set
+- *                                     pn_right: initializer
++ * Var, Let, Const (ListNode)
++ *   head: list of N Name or Assign nodes
++ *         each name node has either
++ *           pn_used: false
++ *           pn_atom: variable name
++ *           pn_expr: initializer or null
++ *         or
++ *           pn_used: true
++ *           pn_atom: variable name
++ *           pn_lexdef: def node
++ *         each assignment node has
++ *           pn_left: Name with pn_used true and
++ *                    pn_lexdef (NOT pn_expr) set
++ *           pn_right: initializer
++ *   count: N > 0
+  * Return   unary       pn_kid: return expr or null
+  * ExpressionStatement unary    pn_kid: expr
+  *                              pn_prologue: true if Directive Prologue member
+  *                                  in original source, not introduced via
+  *                                  constant folding or other tree rewriting
+  * EmptyStatement nullary      (no fields)
+  * Label    name        pn_atom: label, pn_expr: labeled statement
+  * Import   binary      pn_left: ImportSpecList import specifiers
+  *                          pn_right: String module specifier
++ * ImportSpecList (ListNode)
++ *   head: list of N ImportSpec nodes
++ *   count: N >= 0 (N = 0 for `import {} from ...`)
+  * Export   unary       pn_kid: declaration expression
+  * ExportFrom binary   pn_left: ExportSpecList export specifiers
+  *                          pn_right: String module specifier
++ * ExportSpecList (ListNode)
++ *   head: list of N ExportSpec nodes
++ *   count: N >= 0 (N = 0 for `export {}`)
+  * ExportDefault unary pn_kid: export default declaration or expression
+  *
+  * <Expressions>
+  * All left-associated binary trees of the same type are optimized into lists
+  * to avoid recursion when processing expression chains.
+- * Comma    list        pn_head: list of pn_count comma-separated exprs
++ * Comma (ListNode)
++ *   head: list of N comma-separated exprs
++ *   count: N >= 2
+  * Assign   binary      pn_left: lvalue, pn_right: rvalue
+  * AddAssign,   binary  pn_left: lvalue, pn_right: rvalue
+  * SubAssign,           pn_op: JSOP_ADD for +=, etc.
+  * BitOrAssign,
+  * BitXorAssign,
+  * BitAndAssign,
+  * LshAssign,
+  * RshAssign,
+  * UrshAssign,
+  * MulAssign,
+  * DivAssign,
+  * ModAssign,
+  * PowAssign
+  * Conditional ternary  (cond ? trueExpr : falseExpr)
+  *                          pn_kid1: cond, pn_kid2: then, pn_kid3: else
+- * Or,      list        pn_head; list of pn_count subexpressions
+- * And,                 All of these operators are left-associative except (**).
+- * BitOr,
+- * BitXor,
+- * BitAnd,
+- * Eq,
+- * Ne,
+- * StrictEq,
+- * StrictNe,
+- * Lt,
+- * Le,
+- * Gt,
+- * Ge,
+- * Lsh,
+- * Rsh,
+- * Ursh,
+- * Add,
+- * Sub,
+- * Star,
+- * Div,
+- * Mod,
+- * Pow                  (**) is right-associative, but still forms a list.
+- *                          See comments in ParseNode::appendOrCreateList.
+- *
++ * Pipeline, Or, And, BitOr, BitXor, BitAnd, StrictEq, Eq, StrictNe, Ne,
++ * Lt, Le, Gt, Ge, InstanceOf, In, Lsh, Rsh, Ursh, Add, Sub, Star, Div, Mod,
++ * Pow (ListNode)
++ *   head: list of N subexpressions
++ *         All of these operators are left-associative except Pow which is
++ *         right-associative, but still forms a list (see comments in
++ *         ParseNode::appendOrCreateList).
++ *   count: N >= 2
+  * Pos,     unary       pn_kid: UNARY expr
+  * Neg
+  * Void,    unary       pn_kid: UNARY expr
+  * Not,
+  * BitNot
+  * TypeOfName, unary    pn_kid: UNARY expr
+  * TypeOfExpr
+  * PreIncrement, unary  pn_kid: MEMBER expr
+@@ -387,42 +377,58 @@ IsTypeofKind(ParseNodeKind kind)
+  *                          occurred
+  * PropertyName name    pn_atom: property name being accessed
+  * Dot      binary      pn_left: MEMBER expr to left of .
+  *                          pn_right: PropertyName to right of .
+  * Elem     binary      pn_left: MEMBER expr to left of [
+  *                          pn_right: expr between [ and ]
+  * Call     binary      pn_left: callee expression on the left of the (
+  *                          pn_right: Arguments
+- * Arguments list       pn_head: list of arg1, arg2, ... argN
+- *                          pn_count: N (where N is number of args)
+- * Array    list        pn_head: list of pn_count array element exprs
+- *                          [,,] holes are represented by Elision nodes
+- *                          pn_xflags: PN_ENDCOMMA if extra comma at end
+- * Object   list        pn_head: list of pn_count binary Colon nodes
++ * Arguments (ListNode)
++ *   head: list of arg1, arg2, ... argN
++ *   count: N >= 0
++ * Array (ListNode)
++ *   head: list of N array element expressions
++ *         holes ([,,]) are represented by Elision nodes,
++ *         spread elements ([...X]) are represented by Spread nodes
++ *   count: N >= 0
++ * Object (ListNode)
++ *   head: list of N nodes, each item is one of:
++ *           * MutateProto
++ *           * Colon
++ *           * Shorthand
++ *           * Spread
++ *   count: N >= 0
+  * Colon    binary      key-value pair in object initializer or
+  *                          destructuring lhs
+  *                          pn_left: property id, pn_right: value
+  * Shorthand binary     Same fields as Colon. This is used for object
+  *                          literal properties using shorthand ({x}).
+  * ComputedName unary  ES6 ComputedPropertyName.
+  *                          pn_kid: the AssignmentExpression inside the square brackets
+  * Name,    name        pn_atom: name, string, or object atom
+  * String               pn_op: JSOP_GETNAME, JSOP_STRING, or JSOP_OBJECT
+  *                          If JSOP_GETNAME, pn_op may be JSOP_*ARG or JSOP_*VAR
+  *                          telling const-ness and static analysis results
+- * TemplateStringList pn_head: list of alternating expr and template strings
+- *              list
++ * TemplateStringList (ListNode)
++ *   head: list of alternating expr and template strings
++ *           TemplateString [, expression, TemplateString]+
++ *         there's at least one expression.  If the template literal contains
++ *         no ${}-delimited expression, it's parsed as a single TemplateString
+  * TemplateString      pn_atom: template string atom
+                 nullary     pn_op: JSOP_NOP
+  * TaggedTemplate      pn_left: tag expression
+  *              binary       pn_right: Arguments, with the first being the
+  *                           call site object, then arg1, arg2, ... argN
+- * CallSiteObj list     pn_head: a Array node followed by
+- *                          list of pn_count - 1 TemplateString nodes
++ * CallSiteObj (CallSiteNode)
++ *   head:  an Array of raw TemplateString, then corresponding cooked
++ *          TemplateString nodes
++ *            Array [, cooked TemplateString]+
++ *          where the Array is
++ *            [raw TemplateString]+
+  * RegExp   nullary     pn_objbox: RegExp model object
+  * Number   dval        pn_dval: double value of numeric literal
+  * True,    nullary     pn_op: JSOp bytecode
+  * False,
+  * Null,
+  * RawUndefined
+  *
+  * This,        unary   pn_kid: '.this' Name if function `this`, else nullptr
+@@ -446,22 +452,31 @@ enum ParseNodeArity
+     PN_BINARY,                          /* two kids, plus a couple of scalars */
+     PN_TERNARY,                         /* three kids */
+     PN_CODE,                            /* module or function definition node */
+     PN_LIST,                            /* generic singly linked list */
+     PN_NAME,                            /* name, label, or regexp */
+     PN_SCOPE                            /* lexical scope */
+ };
+ 
++#define FOR_EACH_PARSENODE_SUBCLASS(macro) \
++    macro(ListNode, ListNodeType, asList) \
++    macro(CallSiteNode, CallSiteNodeType, asCallSite)
++
+ class LoopControlStatement;
+ class BreakStatement;
+ class ContinueStatement;
+ class ConditionalExpression;
+ class PropertyAccess;
+ 
++#define DECLARE_CLASS(typeName, longTypeName, asMethodName) \
++class typeName;
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
++#undef DECLARE_CLASS
++
+ class ParseNode
+ {
+     ParseNodeKind pn_type;   /* ParseNodeKind::PNK_* type */
+     // pn_op and pn_arity are not declared as the correct enum types
+     // due to difficulties with MS bitfield layout rules and a GCC
+     // bug.  See https://bugzilla.mozilla.org/show_bug.cgi?id=1383157#c4 for
+     // details.
+     uint8_t pn_op;      /* see JSOp enum and jsopcode.tbl */
+@@ -541,20 +556,22 @@ class ParseNode
+         pn_rhs_anon_fun = enabled;
+     }
+ 
+     TokenPos            pn_pos;         /* two 16-bit pairs here, for 64 bits */
+     ParseNode*          pn_next;        /* intrinsic link in parent PN_LIST */
+ 
+     union {
+         struct {                        /* list of next-linked nodes */
++          private:
++            friend class ListNode;
+             ParseNode*  head;           /* first node in list */
+-            ParseNode** tail;           /* ptr to ptr to last node in list */
++            ParseNode** tail;           /* ptr to last node's pn_next in list */
+             uint32_t    count;          /* number of nodes in list */
+-            uint32_t    xflags;         /* see PNX_* below */
++            uint32_t    xflags;         /* see ListNode class */
+         } list;
+         struct {                        /* ternary: if, for(;;), ?: */
+             ParseNode*  kid1;           /* condition, discriminant, etc. */
+             ParseNode*  kid2;           /* then-part, case list, etc. */
+             ParseNode*  kid3;           /* else-part, default case, etc. */
+         } ternary;
+         struct {                        /* two kids if binary */
+             ParseNode*  left;
+@@ -591,20 +608,16 @@ class ParseNode
+             friend class LoopControlStatement;
+             PropertyName*    label;    /* target of break/continue statement */
+         } loopControl;
+     } pn_u;
+ 
+ #define pn_objbox       pn_u.name.objbox
+ #define pn_funbox       pn_u.name.funbox
+ #define pn_body         pn_u.name.expr
+-#define pn_head         pn_u.list.head
+-#define pn_tail         pn_u.list.tail
+-#define pn_count        pn_u.list.count
+-#define pn_xflags       pn_u.list.xflags
+ #define pn_kid1         pn_u.ternary.kid1
+ #define pn_kid2         pn_u.ternary.kid2
+ #define pn_kid3         pn_u.ternary.kid3
+ #define pn_left         pn_u.binary.left
+ #define pn_right        pn_u.binary.right
+ #define pn_pval         pn_u.binary.pval
+ #define pn_iflags       pn_u.binary.iflags
+ #define pn_kid          pn_u.unary.kid
+@@ -649,23 +662,16 @@ class ParseNode
+         return pn_u.scope.body;
+     }
+ 
+     void setScopeBody(ParseNode* body) {
+         MOZ_ASSERT(pn_arity == PN_SCOPE);
+         pn_u.scope.body = body;
+     }
+ 
+-/* PN_LIST pn_xflags bits. */
+-#define PNX_FUNCDEFS    0x01            /* contains top-level function statements */
+-#define PNX_ARRAYHOLESPREAD 0x02        /* one or more of
+-                                           1. array initialiser has holes
+-                                           2. array initializer has spread node */
+-#define PNX_NONCONST    0x04            /* initialiser has non-constants */
+-
+     bool functionIsHoisted() const {
+         MOZ_ASSERT(pn_arity == PN_CODE && getKind() == ParseNodeKind::Function);
+         MOZ_ASSERT(isOp(JSOP_LAMBDA) ||        // lambda
+                    isOp(JSOP_LAMBDA_ARROW) ||  // arrow function
+                    isOp(JSOP_DEFFUN) ||        // non-body-level function statement
+                    isOp(JSOP_NOP) ||           // body-level function stmt in global code
+                    isOp(JSOP_GETLOCAL) ||      // body-level function stmt in function code
+                    isOp(JSOP_GETARG) ||        // body-level function redeclaring formal
+@@ -706,92 +712,25 @@ class ParseNode
+                isKind(ParseNodeKind::Null) ||
+                isKind(ParseNodeKind::RawUndefined);
+     }
+ 
+     /* Return true if this node appears in a Directive Prologue. */
+     bool isDirectivePrologueMember() const { return pn_prologue; }
+ 
+     // True iff this is a for-in/of loop variable declaration (var/let/const).
+-    bool isForLoopDeclaration() const {
+-        if (isKind(ParseNodeKind::Var) || isKind(ParseNodeKind::Let) || isKind(ParseNodeKind::Const)) {
+-            MOZ_ASSERT(isArity(PN_LIST));
+-            MOZ_ASSERT(pn_count > 0);
+-            return true;
+-        }
+-
+-        return false;
+-    }
+-
+-    /*
+-     * Compute a pointer to the last element in a singly-linked list. NB: list
+-     * must be non-empty for correct PN_LAST usage -- this is asserted!
+-     */
+-    ParseNode* last() const {
+-        MOZ_ASSERT(pn_arity == PN_LIST);
+-        MOZ_ASSERT(pn_count != 0);
+-        return (ParseNode*)(uintptr_t(pn_tail) - offsetof(ParseNode, pn_next));
+-    }
++    inline bool isForLoopDeclaration() const;
+ 
+     void initNumber(double value, DecimalPoint decimalPoint) {
+         MOZ_ASSERT(pn_arity == PN_NULLARY);
+         MOZ_ASSERT(getKind() == ParseNodeKind::Number);
+         pn_u.number.value = value;
+         pn_u.number.decimalPoint = decimalPoint;
+     }
+ 
+-    void makeEmpty() {
+-        MOZ_ASSERT(pn_arity == PN_LIST);
+-        pn_head = nullptr;
+-        pn_tail = &pn_head;
+-        pn_count = 0;
+-        pn_xflags = 0;
+-    }
+-
+-    void initList(ParseNode* pn) {
+-        MOZ_ASSERT(pn_arity == PN_LIST);
+-        if (pn->pn_pos.begin < pn_pos.begin) {
+-            pn_pos.begin = pn->pn_pos.begin;
+-        }
+-        pn_pos.end = pn->pn_pos.end;
+-        pn_head = pn;
+-        pn_tail = &pn->pn_next;
+-        pn_count = 1;
+-        pn_xflags = 0;
+-    }
+-
+-    void append(ParseNode* pn) {
+-        MOZ_ASSERT(pn->pn_pos.begin >= pn_pos.begin);
+-        appendWithoutOrderAssumption(pn);
+-    }
+-
+-    void appendWithoutOrderAssumption(ParseNode* pn) {
+-        MOZ_ASSERT(pn_arity == PN_LIST);
+-        pn_pos.end = pn->pn_pos.end;
+-        *pn_tail = pn;
+-        pn_tail = &pn->pn_next;
+-        pn_count++;
+-    }
+-
+-    void prepend(ParseNode* pn) {
+-        MOZ_ASSERT(pn_arity == PN_LIST);
+-        pn->pn_next = pn_head;
+-        pn_head = pn;
+-        if (pn_tail == &pn_head) {
+-            pn_tail = &pn->pn_next;
+-        }
+-        pn_count++;
+-    }
+-
+-    void checkListConsistency()
+-#ifndef DEBUG
+-    {}
+-#endif
+-    ;
+-
+     enum AllowConstantObjects {
+         DontAllowObjects = 0,
+         AllowObjects,
+         ForCopyOnWriteArray
+     };
+ 
+     MOZ_MUST_USE bool getConstantValue(JSContext* cx, AllowConstantObjects allowObjects,
+                                        MutableHandleValue vp, Value* compare = nullptr,
+@@ -900,45 +839,348 @@ struct TernaryNode : public ParseNode
+         pn_kid3 = kid3;
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
+ };
+ 
+-struct ListNode : public ParseNode
++class ListNode : public ParseNode
+ {
++  private:
++    // xflags bits.
++
++    // Statement list has top-level function statements.
++    static constexpr uint32_t hasTopLevelFunctionDeclarationsBit = 0x01;
++
++    // One or more of
++    //   * array has holes
++    //   * array has spread node
++    static constexpr uint32_t hasArrayHoleOrSpreadBit = 0x02;
++
++    // Array/Object/Class initializer has non-constants.
++    //   * array has holes
++    //   * array has spread node
++    //   * array has element which is known not to be constant
++    //   * array has no element
++    //   * object/class has __proto__
++    //   * object/class has property which is known not to be constant
++    //   * object/class shorthand property
++    //   * object/class spread property
++    //   * object/class has method
++    //   * object/class has computed property
++    static constexpr uint32_t hasNonConstInitializerBit = 0x04;
++
++    void checkConsistency() const
++#ifndef DEBUG
++    {}
++#endif
++    ;
++
++  public:
+     ListNode(ParseNodeKind kind, const TokenPos& pos)
+       : ParseNode(kind, JSOP_NOP, PN_LIST, pos)
+     {
+         makeEmpty();
+     }
+ 
+     ListNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
+       : ParseNode(kind, op, PN_LIST, pos)
+     {
+         makeEmpty();
+     }
+ 
+     ListNode(ParseNodeKind kind, JSOp op, ParseNode* kid)
+       : ParseNode(kind, op, PN_LIST, kid->pn_pos)
+     {
+-        initList(kid);
++        if (kid->pn_pos.begin < pn_pos.begin) {
++            pn_pos.begin = kid->pn_pos.begin;
++        }
++        pn_pos.end = kid->pn_pos.end;
++
++        pn_u.list.head = kid;
++        pn_u.list.tail = &kid->pn_next;
++        pn_u.list.count = 1;
++        pn_u.list.xflags = 0;
+     }
+ 
+     static bool test(const ParseNode& node) {
+         return node.isArity(PN_LIST);
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
++
++    ParseNode* head() const {
++        return pn_u.list.head;
++    }
++
++    ParseNode** tail() const {
++        return pn_u.list.tail;
++    }
++
++    uint32_t count() const {
++        return pn_u.list.count;
++    }
++
++    bool empty() const {
++        return count() == 0;
++    }
++
++    MOZ_MUST_USE bool hasTopLevelFunctionDeclarations() const {
++        MOZ_ASSERT(isKind(ParseNodeKind::StatementList));
++        return pn_u.list.xflags & hasTopLevelFunctionDeclarationsBit;
++    }
++
++    MOZ_MUST_USE bool hasArrayHoleOrSpread() const {
++        MOZ_ASSERT(isKind(ParseNodeKind::Array));
++        return pn_u.list.xflags & hasArrayHoleOrSpreadBit;
++    }
++
++    MOZ_MUST_USE bool hasNonConstInitializer() const {
++        MOZ_ASSERT(isKind(ParseNodeKind::Array) ||
++                   isKind(ParseNodeKind::Object) ||
++                   isKind(ParseNodeKind::ClassMethodList));
++        return pn_u.list.xflags & hasNonConstInitializerBit;
++    }
++
++    void setHasTopLevelFunctionDeclarations() {
++        MOZ_ASSERT(isKind(ParseNodeKind::StatementList));
++        pn_u.list.xflags |= hasTopLevelFunctionDeclarationsBit;
++    }
++
++    void setHasArrayHoleOrSpread() {
++        MOZ_ASSERT(isKind(ParseNodeKind::Array));
++        pn_u.list.xflags |= hasArrayHoleOrSpreadBit;
++    }
++
++    void setHasNonConstInitializer() {
++        MOZ_ASSERT(isKind(ParseNodeKind::Array) ||
++                   isKind(ParseNodeKind::Object) ||
++                   isKind(ParseNodeKind::ClassMethodList));
++        pn_u.list.xflags |= hasNonConstInitializerBit;
++    }
++
++    /*
++     * Compute a pointer to the last element in a singly-linked list. NB: list
++     * must be non-empty -- this is asserted!
++     */
++    ParseNode* last() const {
++        MOZ_ASSERT(!empty());
++        //
++        // ParseNode                      ParseNode
++        // +-----+---------+-----+        +-----+---------+-----+
++        // | ... | pn_next | ... | +-...->| ... | pn_next | ... |
++        // +-----+---------+-----+ |      +-----+---------+-----+
++        // ^       |               |      ^     ^
++        // |       +---------------+      |     |
++        // |                              |     tail()
++        // |                              |
++        // head()                         last()
++        //
++        return (ParseNode*)(uintptr_t(tail()) - offsetof(ParseNode, pn_next));
++    }
++
++    void replaceLast(ParseNode* node) {
++        MOZ_ASSERT(!empty());
++        pn_pos.end = node->pn_pos.end;
++
++        ParseNode* item = head();
++        ParseNode* lastNode = last();
++        MOZ_ASSERT(item);
++        if (item == lastNode) {
++            pn_u.list.head = node;
++        } else {
++            while (item->pn_next != lastNode) {
++                MOZ_ASSERT(item->pn_next);
++                item = item->pn_next;
++            }
++            item->pn_next = node;
++        }
++        pn_u.list.tail = &node->pn_next;
++    }
++
++    void makeEmpty() {
++        pn_u.list.head = nullptr;
++        pn_u.list.tail = &pn_u.list.head;
++        pn_u.list.count = 0;
++        pn_u.list.xflags = 0;
++    }
++
++    void append(ParseNode* item) {
++        MOZ_ASSERT(item->pn_pos.begin >= pn_pos.begin);
++        appendWithoutOrderAssumption(item);
++    }
++
++    void appendWithoutOrderAssumption(ParseNode* item) {
++        pn_pos.end = item->pn_pos.end;
++        *pn_u.list.tail = item;
++        pn_u.list.tail = &item->pn_next;
++        pn_u.list.count++;
++    }
++
++    void prepend(ParseNode* item) {
++        item->pn_next = pn_u.list.head;
++        pn_u.list.head = item;
++        if (pn_u.list.tail == &pn_u.list.head) {
++            pn_u.list.tail = &item->pn_next;
++        }
++        pn_u.list.count++;
++    }
++
++    // Methods used by FoldConstants.cpp.
++    // Caller is responsible for keeping the list consistent.
++    ParseNode** unsafeHeadReference() {
++        return &pn_u.list.head;
++    }
++
++    void unsafeReplaceTail(ParseNode** newTail) {
++        pn_u.list.tail = newTail;
++        checkConsistency();
++    }
++
++    void unsafeDecrementCount() {
++        MOZ_ASSERT(count() > 1);
++        pn_u.list.count--;
++    }
++
++  private:
++    // Classes to iterate over ListNode contents:
++    //
++    // Usage:
++    //   ListNode* list;
++    //   for (ParseNode* item : list->contents()) {
++    //     // item is ParseNode* typed.
++    //   }
++    class iterator
++    {
++      private:
++        ParseNode* node_;
++
++        friend class ListNode;
++        explicit iterator(ParseNode* node)
++          : node_(node)
++        {}
++
++      public:
++        bool operator==(const iterator& other) const {
++            return node_ == other.node_;
++        }
++
++        bool operator!=(const iterator& other) const {
++            return !(*this == other);
++        }
++
++        iterator& operator++() {
++            node_ = node_->pn_next;
++            return *this;
++        }
++
++        ParseNode* operator*() {
++            return node_;
++        }
++
++        const ParseNode* operator*() const {
++            return node_;
++        }
++    };
++
++    class range
++    {
++      private:
++        ParseNode* begin_;
++        ParseNode* end_;
++
++        friend class ListNode;
++        range(ParseNode* begin, ParseNode* end)
++          : begin_(begin),
++            end_(end)
++        {}
++
++      public:
++        iterator begin() {
++            return iterator(begin_);
++        }
++
++        iterator end() {
++            return iterator(end_);
++        }
++
++        const iterator begin() const {
++            return iterator(begin_);
++        }
++
++        const iterator end() const {
++            return iterator(end_);
++        }
++
++        const iterator cbegin() const {
++            return begin();
++        }
++
++        const iterator cend() const {
++            return end();
++        }
++    };
++
++#ifdef DEBUG
++  MOZ_MUST_USE bool contains(ParseNode* target) const {
++      MOZ_ASSERT(target);
++      for (ParseNode* node : contents()) {
++          if (target == node) {
++              return true;
++          }
++      }
++      return false;
++  }
++#endif
++
++  public:
++    range contents() {
++        return range(head(), nullptr);
++    }
++
++    const range contents() const {
++        return range(head(), nullptr);
++    }
++
++    range contentsFrom(ParseNode* begin) {
++        MOZ_ASSERT_IF(begin, contains(begin));
++        return range(begin, nullptr);
++    }
++
++    const range contentsFrom(ParseNode* begin) const {
++        MOZ_ASSERT_IF(begin, contains(begin));
++        return range(begin, nullptr);
++    }
++
++    range contentsTo(ParseNode* end) {
++        MOZ_ASSERT_IF(end, contains(end));
++        return range(head(), end);
++    }
++
++    const range contentsTo(ParseNode* end) const {
++        MOZ_ASSERT_IF(end, contains(end));
++        return range(head(), end);
++    }
+ };
+ 
++inline bool
++ParseNode::isForLoopDeclaration() const
++{
++    if (isKind(ParseNodeKind::Var) || isKind(ParseNodeKind::Let) || isKind(ParseNodeKind::Const)) {
++        MOZ_ASSERT(!as<ListNode>().empty());
++        return true;
++    }
++
++    return false;
++}
++
+ struct CodeNode : public ParseNode
+ {
+     CodeNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
+       : ParseNode(kind, op, PN_CODE, pos)
+     {
+         MOZ_ASSERT(kind == ParseNodeKind::Function || kind == ParseNodeKind::Module);
+         MOZ_ASSERT_IF(kind == ParseNodeKind::Module, op == JSOP_NOP);
+         MOZ_ASSERT(op == JSOP_NOP || // statement, module
+@@ -1018,20 +1260,17 @@ class LabeledStatement : public ParseNod
+ class CaseClause : public BinaryNode
+ {
+   public:
+     CaseClause(ParseNode* expr, ParseNode* stmts, uint32_t begin)
+       : BinaryNode(ParseNodeKind::Case, JSOP_NOP, TokenPos(begin, stmts->pn_pos.end), expr, stmts) {}
+ 
+     ParseNode* caseExpression() const { return pn_left; }
+     bool isDefault() const { return !caseExpression(); }
+-    ParseNode* statementList() const { return pn_right; }
+-
+-    // The next CaseClause in the same switch statement.
+-    CaseClause* next() const { return pn_next ? &pn_next->as<CaseClause>() : nullptr; }
++    ListNode* statementList() const { return &pn_right->as<ListNode>(); }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Case);
+         MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
+ };
+@@ -1238,25 +1477,34 @@ class PropertyByValue : public ParseNode
+     bool isSuper() const {
+         return pn_left->isKind(ParseNodeKind::SuperBase);
+     }
+ };
+ 
+ /*
+  * A CallSiteNode represents the implicit call site object argument in a TaggedTemplate.
+  */
+-struct CallSiteNode : public ListNode {
++class CallSiteNode : public ListNode
++{
++  public:
+     explicit CallSiteNode(uint32_t begin): ListNode(ParseNodeKind::CallSiteObj, TokenPos(begin, begin + 1)) {}
+ 
+     static bool test(const ParseNode& node) {
+-        return node.isKind(ParseNodeKind::CallSiteObj);
++        bool match = node.isKind(ParseNodeKind::CallSiteObj);
++        MOZ_ASSERT_IF(match, node.is<ListNode>());
++        return match;
+     }
+ 
+     MOZ_MUST_USE bool getRawArrayValue(JSContext* cx, MutableHandleValue vp) {
+-        return pn_head->getConstantValue(cx, AllowObjects, vp);
++        return head()->getConstantValue(cx, AllowObjects, vp);
++    }
++
++    ListNode* rawNodes() const {
++        MOZ_ASSERT(head());
++        return &head()->as<ListNode>();
+     }
+ };
+ 
+ struct ClassMethod : public BinaryNode {
+     /*
+      * Method definitions often keep a name and function body that overlap,
+      * so explicitly define the beginning and end here.
+      */
+@@ -1287,21 +1535,21 @@ struct SwitchStatement : public BinaryNo
+     SwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* lexicalForCaseList,
+                     bool hasDefault)
+       : BinaryNode(ParseNodeKind::Switch, JSOP_NOP,
+                    TokenPos(begin, lexicalForCaseList->pn_pos.end),
+                    discriminant, lexicalForCaseList)
+     {
+ #ifdef DEBUG
+         MOZ_ASSERT(lexicalForCaseList->isKind(ParseNodeKind::LexicalScope));
+-        ParseNode* cases = lexicalForCaseList->scopeBody();
++        ListNode* cases = &lexicalForCaseList->scopeBody()->as<ListNode>();
+         MOZ_ASSERT(cases->isKind(ParseNodeKind::StatementList));
+         bool found = false;
+-        CaseClause* firstCase = cases->pn_head ? &cases->pn_head->as<CaseClause>() : nullptr;
+-        for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
++        for (ParseNode* item : cases->contents()) {
++            CaseClause* caseNode = &item->as<CaseClause>();
+             if (caseNode->isDefault()) {
+                 found = true;
+                 break;
+             }
+         }
+         MOZ_ASSERT(found == hasDefault);
+ #endif
+ 
+@@ -1373,23 +1621,23 @@ struct ClassNode : public TernaryNode {
+     }
+ 
+     ClassNames* names() const {
+         return pn_kid1 ? &pn_kid1->as<ClassNames>() : nullptr;
+     }
+     ParseNode* heritage() const {
+         return pn_kid2;
+     }
+-    ParseNode* methodList() const {
++    ListNode* methodList() const {
+         if (pn_kid3->isKind(ParseNodeKind::ClassMethodList)) {
+-            return pn_kid3;
++            return &pn_kid3->as<ListNode>();
+         }
+ 
+         MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
+-        ParseNode* list = pn_kid3->scopeBody();
++        ListNode* list = &pn_kid3->scopeBody()->as<ListNode>();
+         MOZ_ASSERT(list->isKind(ParseNodeKind::ClassMethodList));
+         return list;
+     }
+     Handle<LexicalScope::Data*> scopeBindings() const {
+         MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
+         return pn_kid3->scopeBindings();
+     }
+ };
+@@ -1421,17 +1669,17 @@ ParseNode::isConstant()
+       case ParseNodeKind::TemplateString:
+       case ParseNodeKind::Null:
+       case ParseNodeKind::RawUndefined:
+       case ParseNodeKind::False:
+       case ParseNodeKind::True:
+         return true;
+       case ParseNodeKind::Array:
+       case ParseNodeKind::Object:
+-        return !(pn_xflags & PNX_NONCONST);
++        return !as<ListNode>().hasNonConstInitializer();
+       default:
+         return false;
+     }
+ }
+ 
+ class ObjectBox
+ {
+   public:
+@@ -1513,27 +1761,26 @@ IsMethodDefinitionKind(FunctionSyntaxKin
+            kind == FunctionSyntaxKind::Getter ||
+            kind == FunctionSyntaxKind::Setter;
+ }
+ 
+ static inline ParseNode*
+ FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals)
+ {
+     MOZ_ASSERT(fn->isKind(ParseNodeKind::Function));
+-    ParseNode* argsBody = fn->pn_body;
++    ListNode* argsBody = &fn->pn_body->as<ListNode>();
+     MOZ_ASSERT(argsBody->isKind(ParseNodeKind::ParamsBody));
+-    *numFormals = argsBody->pn_count;
++    *numFormals = argsBody->count();
+     if (*numFormals > 0 &&
+         argsBody->last()->isKind(ParseNodeKind::LexicalScope) &&
+         argsBody->last()->scopeBody()->isKind(ParseNodeKind::StatementList))
+     {
+         (*numFormals)--;
+     }
+-    MOZ_ASSERT(argsBody->isArity(PN_LIST));
+-    return argsBody->pn_head;
++    return argsBody->head();
+ }
+ 
+ bool
+ IsAnonymousFunctionDefinition(ParseNode* pn);
+ 
+ } /* namespace frontend */
+ } /* namespace js */
+ 
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -1159,17 +1159,17 @@ ParserBase::setSourceMapInfo()
+     return true;
+ }
+ 
+ 
+ /*
+  * Parse a top-level JS script.
+  */
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::parse()
+ {
+     MOZ_ASSERT(checkOptionsCalled);
+ 
+     Directives directives(options().strictOption);
+     GlobalSharedContext globalsc(context, ScopeKind::Global,
+                                  directives, options().extraWarningsOption);
+     SourceParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
+@@ -1177,36 +1177,38 @@ GeneralParser<ParseHandler, CharT>::pars
+         return null();
+     }
+ 
+     ParseContext::VarScope varScope(this);
+     if (!varScope.init(pc)) {
+         return null();
+     }
+ 
+-    Node pn = statementList(YieldIsName);
+-    if (!pn) {
++    ListNodeType stmtList = statementList(YieldIsName);
++    if (!stmtList) {
+         return null();
+     }
+ 
+     TokenKind tt;
+     if (!tokenStream.getToken(&tt, TokenStream::Operand)) {
+         return null();
+     }
+     if (tt != TokenKind::Eof) {
+         error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt));
+         return null();
+     }
+     if (foldConstants) {
+-        if (!FoldConstants(context, &pn, this)) {
+-            return null();
+-        }
+-    }
+-
+-    return pn;
++        Node node = stmtList;
++        if (!FoldConstants(context, &node, this)) {
++            return null();
++        }
++        stmtList = handler.asList(node);
++    }
++
++    return stmtList;
+ }
+ 
+ /*
+  * Strict mode forbids introducing new definitions for 'eval', 'arguments',
+  * 'let', 'static', 'yield', or for any strict mode reserved word.
+  */
+ bool
+ ParserBase::isValidStrictBinding(PropertyName* name)
+@@ -2452,41 +2454,43 @@ Parser<FullParseHandler, CharT>::evalBod
+         return nullptr;
+     }
+     evalsc->bindings = *bindings;
+ 
+     return body;
+ }
+ 
+ template <typename CharT>
+-ParseNode*
++ListNode*
+ Parser<FullParseHandler, CharT>::globalBody(GlobalSharedContext* globalsc)
+ {
+     SourceParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
+     if (!globalpc.init()) {
+         return nullptr;
+     }
+ 
+     ParseContext::VarScope varScope(this);
+     if (!varScope.init(pc)) {
+         return nullptr;
+     }
+ 
+-    ParseNode* body = statementList(YieldIsName);
++    ListNode* body = statementList(YieldIsName);
+     if (!body) {
+         return nullptr;
+     }
+ 
+     if (!checkStatementsEOF()) {
+         return nullptr;
+     }
+ 
+-    if (!FoldConstants(context, &body, this)) {
+-        return nullptr;
+-    }
++    ParseNode* node = body;
++    if (!FoldConstants(context, &node, this)) {
++        return null();
++    }
++    body = &node->as<ListNode>();
+ 
+     if (!this->setSourceMapInfo()) {
+         return nullptr;
+     }
+ 
+     // For global scripts, whether bindings are closed over or not doesn't
+     // matter, so no need to call propagateFreeNamesAndMarkClosedOver-
+     // Bindings. However, Annex B.3.3 functions still need to be marked.
+@@ -2520,23 +2524,23 @@ Parser<FullParseHandler, CharT>::moduleB
+     }
+ 
+     Node mn = handler.newModule(pos());
+     if (!mn) {
+         return null();
+     }
+ 
+     AutoAwaitIsKeyword<FullParseHandler, CharT> awaitIsKeyword(this, AwaitIsModuleKeyword);
+-    ParseNode* pn = statementList(YieldIsName);
+-    if (!pn) {
+-        return null();
+-    }
+-
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::StatementList));
+-    mn->pn_body = pn;
++    ListNode* stmtList = statementList(YieldIsName);
++    if (!stmtList) {
++        return null();
++    }
++
++    MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
++    mn->pn_body = stmtList;
+ 
+     TokenKind tt;
+     if (!tokenStream.getToken(&tt, TokenStream::Operand)) {
+         return null();
+     }
+     if (tt != TokenKind::Eof) {
+         error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt));
+         return null();
+@@ -2560,19 +2564,21 @@ Parser<FullParseHandler, CharT>::moduleB
+ 
+             errorAt(TokenStream::NoOffset, JSMSG_MISSING_EXPORT, str.get());
+             return null();
+         }
+ 
+         p->value()->setClosedOver();
+     }
+ 
+-    if (!FoldConstants(context, &pn, this)) {
+-        return null();
+-    }
++    ParseNode* node = stmtList;
++    if (!FoldConstants(context, &node, this)) {
++        return null();
++    }
++    stmtList = &node->as<ListNode>();
+ 
+     if (!this->setSourceMapInfo()) {
+         return null();
+     }
+ 
+     if (!propagateFreeNamesAndMarkClosedOverBindings(modulepc.varScope())) {
+         return null();
+     }
+@@ -2872,17 +2878,17 @@ Parser<FullParseHandler, CharT>::standal
+         anyChars.ungetToken();
+     }
+ 
+     Node fn = handler.newFunctionStatement(pos());
+     if (!fn) {
+         return null();
+     }
+ 
+-    ParseNode* argsbody = handler.newList(ParseNodeKind::ParamsBody, pos());
++    ListNodeType argsbody = handler.newList(ParseNodeKind::ParamsBody, pos());
+     if (!argsbody) {
+         return null();
+     }
+     fn->pn_body = argsbody;
+ 
+     FunctionBox* funbox = newFunctionBox(fn, fun, /* toStringStart = */ 0, inheritedDirectives,
+                                          generatorKind, asyncKind);
+     if (!funbox) {
+@@ -3012,21 +3018,21 @@ GeneralParser<ParseHandler, CharT>::func
+                                                  FunctionSyntaxKind kind, FunctionBodyType type)
+ {
+     MOZ_ASSERT(pc->isFunctionBox());
+ 
+ #ifdef DEBUG
+     uint32_t startYieldOffset = pc->lastYieldOffset;
+ #endif
+ 
+-    Node pn;
++    Node body;
+     if (type == StatementListBody) {
+         bool inheritedStrict = pc->sc()->strict();
+-        pn = statementList(yieldHandling);
+-        if (!pn) {
++        body = statementList(yieldHandling);
++        if (!body) {
+             return null();
+         }
+ 
+         // When we transitioned from non-strict to strict mode, we need to
+         // validate that all parameter names are valid strict mode names.
+         if (!inheritedStrict && pc->sc()->strict()) {
+             MOZ_ASSERT(pc->sc()->hasExplicitUseStrict(),
+                        "strict mode should only change when a 'use strict' directive is present");
+@@ -3037,71 +3043,71 @@ GeneralParser<ParseHandler, CharT>::func
+                 return null();
+             }
+         }
+     } else {
+         MOZ_ASSERT(type == ExpressionBody);
+ 
+         // Async functions are implemented as generators, and generators are
+         // assumed to be statement lists, to prepend initial `yield`.
+-        Node stmtList = null();
++        ListNodeType stmtList = null();
+         if (pc->isAsync()) {
+             stmtList = handler.newStatementList(pos());
+             if (!stmtList) {
+                 return null();
+             }
+         }
+ 
+         Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+         if (!kid) {
+             return null();
+         }
+ 
+-        pn = handler.newExpressionBody(kid);
+-        if (!pn) {
++        body = handler.newExpressionBody(kid);
++        if (!body) {
+             return null();
+         }
+ 
+         if (pc->isAsync()) {
+-            handler.addStatementToList(stmtList, pn);
+-            pn = stmtList;
++            handler.addStatementToList(stmtList, body);
++            body = stmtList;
+         }
+     }
+ 
+     MOZ_ASSERT_IF(!pc->isGenerator() && !pc->isAsync(), pc->lastYieldOffset == startYieldOffset);
+     MOZ_ASSERT_IF(pc->isGenerator(), kind != FunctionSyntaxKind::Arrow);
+     MOZ_ASSERT_IF(pc->isGenerator(), type == StatementListBody);
+ 
+     if (pc->needsDotGeneratorName()) {
+         MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
+         if (!declareDotGeneratorName()) {
+             return null();
+         }
+         Node generator = newDotGeneratorName();
+         if (!generator) {
+             return null();
+         }
+-        if (!handler.prependInitialYield(pn, generator)) {
++        if (!handler.prependInitialYield(handler.asList(body), generator)) {
+             return null();
+         }
+     }
+ 
+     // Declare the 'arguments' and 'this' bindings if necessary before
+     // finishing up the scope so these special bindings get marked as closed
+     // over if necessary. Arrow functions don't have these bindings.
+     if (kind != FunctionSyntaxKind::Arrow) {
+         if (!declareFunctionArgumentsObject()) {
+             return null();
+         }
+         if (!declareFunctionThis()) {
+             return null();
+         }
+     }
+ 
+-    return finishLexicalScope(pc->varScope(), pn);
++    return finishLexicalScope(pc->varScope(), body);
+ }
+ 
+ JSFunction*
+ AllocNewFunction(JSContext* cx, HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                  FunctionAsyncKind asyncKind, HandleObject proto,
+                  bool isSelfHosting /* = false */, bool inFunctionBox /* = false */)
+ {
+     MOZ_ASSERT_IF(kind == FunctionSyntaxKind::Statement, atom != nullptr);
+@@ -3352,17 +3358,17 @@ GeneralParser<ParseHandler, CharT>::func
+     } else {
+         // When delazifying, we may not have a current token and pos() is
+         // garbage. In that case, substitute the first token's position.
+         if (!tokenStream.peekTokenPos(&firstTokenPos, firstTokenModifier)) {
+             return false;
+         }
+     }
+ 
+-    Node argsbody = handler.newList(ParseNodeKind::ParamsBody, firstTokenPos);
++    ListNodeType argsbody = handler.newList(ParseNodeKind::ParamsBody, firstTokenPos);
+     if (!argsbody) {
+         return false;
+     }
+     handler.setFunctionFormalParametersAndBody(funcpn, argsbody);
+ 
+     bool hasArguments = false;
+     if (parenFreeArrow) {
+         hasArguments = true;
+@@ -3647,17 +3653,17 @@ GeneralParser<ParseHandler, CharT>::skip
+                                                           bool tryAnnexB)
+ {
+     return asFinalParser()->skipLazyInnerFunction(funcNode, toStringStart, kind, tryAnnexB);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+ GeneralParser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling,
+-                                                                   Node nodeList,
++                                                                   ListNodeType nodeList,
+                                                                    TokenKind* ttp)
+ {
+     Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
+     if (!pn) {
+         return false;
+     }
+     handler.addList(nodeList, pn);
+ 
+@@ -3670,20 +3676,21 @@ GeneralParser<ParseHandler, CharT>::addE
+         return false;
+     }
+ 
+     return tokenStream.getToken(ttp, TokenStream::TemplateTail);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+-GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node tagArgsList,
++GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling,
++                                                   ListNodeType tagArgsList,
+                                                    TokenKind tt)
+ {
+-    Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
++    CallSiteNodeType callSiteObjNode = handler.newCallSiteObject(pos().begin);
+     if (!callSiteObjNode) {
+         return false;
+     }
+     handler.addList(tagArgsList, callSiteObjNode);
+ 
+     while (true) {
+         if (!appendToCallSiteObj(callSiteObjNode)) {
+             return false;
+@@ -3696,25 +3703,25 @@ GeneralParser<ParseHandler, CharT>::tagg
+             return false;
+         }
+     }
+     handler.setEndPosition(tagArgsList, callSiteObjNode);
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling)
+ {
+     Node pn = noSubstitutionUntaggedTemplate();
+     if (!pn) {
+         return null();
+     }
+ 
+-    Node nodeList = handler.newList(ParseNodeKind::TemplateStringList, pn);
++    ListNodeType nodeList = handler.newList(ParseNodeKind::TemplateStringList, pn);
+     if (!nodeList) {
+         return null();
+     }
+ 
+     TokenKind tt;
+     do {
+         if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) {
+             return null();
+@@ -4020,17 +4027,17 @@ GeneralParser<ParseHandler, CharT>::inne
+         }
+     }
+ 
+     return innerFunc;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+-GeneralParser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
++GeneralParser<ParseHandler, CharT>::appendToCallSiteObj(CallSiteNodeType callSiteObj)
+ {
+     Node cookedNode = noSubstitutionTaggedTemplate();
+     if (!cookedNode) {
+         return false;
+     }
+ 
+     JSAtom* atom = tokenStream.getRawTemplateStringAtom();
+     if (!atom) {
+@@ -4429,17 +4436,17 @@ IsEscapeFreeStringLiteral(const TokenPos
+      * accounting for the quotes, then it must not contain any escape
+      * sequences or line continuations.
+      */
+     return pos.begin + str->length() + 2 == pos.end;
+ }
+ 
+ template <typename CharT>
+ bool
+-Parser<SyntaxParseHandler, CharT>::asmJS(Node list)
++Parser<SyntaxParseHandler, CharT>::asmJS(ListNodeType list)
+ {
+     // While asm.js could technically be validated and compiled during syntax
+     // parsing, we have no guarantee that some later JS wouldn't abort the
+     // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
+     // For simplicity, unconditionally abort the syntax parse when "use asm" is
+     // encountered so that asm.js is always validated/compiled exactly once
+     // during a full parse.
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+@@ -4450,17 +4457,17 @@ Parser<SyntaxParseHandler, CharT>::asmJS
+     if (ss) {
+         ss->setContainsAsmJS();
+     }
+     return false;
+ }
+ 
+ template <>
+ bool
+-Parser<FullParseHandler, char16_t>::asmJS(Node list)
++Parser<FullParseHandler, char16_t>::asmJS(ListNodeType list)
+ {
+     // Disable syntax parsing in anything nested inside the asm.js module.
+     disableSyntaxParser();
+ 
+     // We should be encountering the "use asm" directive for the first time; if
+     // the directive is already, we must have failed asm.js validation and we're
+     // reparsing. In that case, don't try to validate again. A non-null
+     // newDirectives means we're not in a normal function.
+@@ -4491,27 +4498,27 @@ Parser<FullParseHandler, char16_t>::asmJ
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ template <>
+ bool
+-Parser<FullParseHandler, Utf8Unit>::asmJS(Node list)
++Parser<FullParseHandler, Utf8Unit>::asmJS(ListNodeType list)
+ {
+     // Just succeed without setting the asm.js directive flag.  Given Web
+     // Assembly's rapid advance, it's probably not worth the trouble to really
+     // support UTF-8 asm.js.
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::asmJS(Node list)
++GeneralParser<ParseHandler, CharT>::asmJS(ListNodeType list)
+ {
+     return asFinalParser()->asmJS(list);
+ }
+ 
+ /*
+  * Recognize Directive Prologue members and directives. Assuming |pn| is a
+  * candidate for membership in a directive prologue, recognize directives and
+  * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
+@@ -4527,17 +4534,17 @@ GeneralParser<ParseHandler, CharT>::asmJ
+  * }
+  *
+  * That is, even though "use\x20loose" can never be a directive, now or in the
+  * future (because of the hex escape), the Directive Prologue extends through it
+  * to the "use strict" statement, which is indeed a directive.
+  */
+ template <class ParseHandler, typename CharT>
+ bool
+-GeneralParser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective,
++GeneralParser<ParseHandler, CharT>::maybeParseDirective(ListNodeType list, Node possibleDirective,
+                                                         bool* cont)
+ {
+     TokenPos directivePos;
+     JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos);
+ 
+     *cont = !!directive;
+     if (!*cont) {
+         return true;
+@@ -4592,25 +4599,25 @@ GeneralParser<ParseHandler, CharT>::mayb
+             }
+             return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL);
+         }
+     }
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling)
+ {
+     if (!CheckRecursionLimit(context)) {
+         return null();
+     }
+ 
+-    Node pn = handler.newStatementList(pos());
+-    if (!pn) {
++    ListNodeType stmtList = handler.newStatementList(pos());
++    if (!stmtList) {
+         return null();
+     }
+ 
+     bool canHaveDirectives = pc->atBodyLevel();
+     if (canHaveDirectives) {
+         anyChars.clearSawOctalEscape();
+     }
+     bool afterReturn = false;
+@@ -4624,17 +4631,17 @@ GeneralParser<ParseHandler, CharT>::stat
+             }
+             return null();
+         }
+         if (tt == TokenKind::Eof || tt == TokenKind::RightCurly) {
+             TokenPos pos;
+             if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) {
+                 return null();
+             }
+-            handler.setListEndPosition(pn, pos);
++            handler.setListEndPosition(stmtList, pos);
+             break;
+         }
+         if (afterReturn) {
+             if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand)) {
+                 return null();
+             }
+         }
+         Node next = statementListItem(yieldHandling, canHaveDirectives);
+@@ -4654,25 +4661,25 @@ GeneralParser<ParseHandler, CharT>::stat
+                     warnedAboutStatementsAfterReturn = true;
+                 }
+             } else if (handler.isReturnStatement(next)) {
+                 afterReturn = true;
+             }
+         }
+ 
+         if (canHaveDirectives) {
+-            if (!maybeParseDirective(pn, next, &canHaveDirectives)) {
++            if (!maybeParseDirective(stmtList, next, &canHaveDirectives)) {
+                 return null();
+             }
+         }
+ 
+-        handler.addStatementToList(pn, next);
+-    }
+-
+-    return pn;
++        handler.addStatementToList(stmtList, next);
++    }
++
++    return stmtList;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
+ {
+     MUST_MATCH_TOKEN(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_COND);
+ 
+@@ -4939,28 +4946,28 @@ GeneralParser<ParseHandler, CharT>::bind
+         error(JSMSG_NO_VARIABLE_NAME);
+         return null();
+     }
+ 
+     return bindingIdentifier(kind, yieldHandling);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
+                                                          YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
+ 
+     if (!CheckRecursionLimit(context)) {
+         return null();
+     }
+ 
+     uint32_t begin = pos().begin;
+-    Node literal = handler.newObjectLiteral(begin);
++    ListNodeType literal = handler.newObjectLiteral(begin);
+     if (!literal) {
+         return null();
+     }
+ 
+     Maybe<DeclarationKind> declKind = Some(kind);
+     RootedAtom propAtom(context);
+     for (;;) {
+         TokenKind tt;
+@@ -5086,28 +5093,28 @@ GeneralParser<ParseHandler, CharT>::obje
+                                      reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
+                                                           JSMSG_CURLY_OPENED, begin));
+ 
+     handler.setEndPosition(literal, pos().end);
+     return literal;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind,
+                                                         YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
+ 
+     if (!CheckRecursionLimit(context)) {
+         return null();
+     }
+ 
+     uint32_t begin = pos().begin;
+-    Node literal = handler.newArrayLiteral(begin);
++    ListNodeType literal = handler.newArrayLiteral(begin);
+     if (!literal) {
+         return null();
+     }
+ 
+      uint32_t index = 0;
+      for (; ; index++) {
+          if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
+              error(JSMSG_ARRAY_INIT_TOO_BIG);
+@@ -5236,17 +5243,17 @@ GeneralParser<ParseHandler, CharT>::bloc
+     uint32_t openedPos = pos().begin;
+ 
+     ParseContext::Statement stmt(pc, StatementKind::Block);
+     ParseContext::Scope scope(this);
+     if (!scope.init(pc)) {
+         return null();
+     }
+ 
+-    Node list = statementList(yieldHandling);
++    ListNodeType list = statementList(yieldHandling);
+     if (!list) {
+         return null();
+     }
+ 
+     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
+                                      reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
+                                                           openedPos));
+ 
+@@ -5470,17 +5477,17 @@ GeneralParser<ParseHandler, CharT>::decl
+     if (!noteDeclaredName(name, declKind, namePos)) {
+         return null();
+     }
+ 
+     return binding;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
+                                                     ParseNodeKind kind,
+                                                     ParseNodeKind* forHeadKind /* = nullptr */,
+                                                     Node* forInOrOfExpression /* = nullptr */)
+ {
+     MOZ_ASSERT(kind == ParseNodeKind::Var ||
+                kind == ParseNodeKind::Let ||
+                kind == ParseNodeKind::Const);
+@@ -5495,17 +5502,17 @@ GeneralParser<ParseHandler, CharT>::decl
+         break;
+       case ParseNodeKind::Let:
+         declKind = DeclarationKind::Let;
+         break;
+       default:
+         MOZ_CRASH("Unknown declaration kind");
+     }
+ 
+-    Node decl = handler.newDeclarationList(kind, pos());
++    ListNodeType decl = handler.newDeclarationList(kind, pos());
+     if (!decl) {
+         return null();
+     }
+ 
+     bool moreDeclarations;
+     bool initialDeclaration = true;
+     do {
+         MOZ_ASSERT_IF(!initialDeclaration && forHeadKind,
+@@ -5539,47 +5546,48 @@ GeneralParser<ParseHandler, CharT>::decl
+             return null();
+         }
+     } while (moreDeclarations);
+ 
+     return decl;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::lexicalDeclaration(YieldHandling yieldHandling,
+                                                        DeclarationKind kind)
+ {
+     MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
+ 
+     /*
+      * Parse body-level lets without a new block object. ES6 specs
+      * that an execution environment's initial lexical environment
+      * is the VariableEnvironment, i.e., body-level lets are in
+      * the same environment record as vars.
+      *
+      * However, they cannot be parsed exactly as vars, as ES6
+      * requires that uninitialized lets throw ReferenceError on use.
+      *
+      * See 8.1.1.1.6 and the note in 13.2.1.
+      */
+-    Node decl = declarationList(yieldHandling,
+-                                kind == DeclarationKind::Const
+-                                ? ParseNodeKind::Const
+-                                : ParseNodeKind::Let);
++    ListNodeType decl = declarationList(yieldHandling,
++                                        kind == DeclarationKind::Const
++                                        ? ParseNodeKind::Const
++                                        : ParseNodeKind::Let);
+     if (!decl || !matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+     return decl;
+ }
+ 
+ template <typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
++Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt,
++                                                               ListNodeType importSpecSet)
+ {
+     if (tt == TokenKind::LeftCurly) {
+         while (true) {
+             // Handle the forms |import {} from 'a'| and
+             // |import { ..., } from 'a'| (where ... is non empty), by
+             // escaping the loop early if the next token is }.
+             if (!tokenStream.getToken(&tt)) {
+                 return false;
+@@ -5717,17 +5725,17 @@ Parser<FullParseHandler, CharT>::importD
+     }
+ 
+     uint32_t begin = pos().begin;
+     TokenKind tt;
+     if (!tokenStream.getToken(&tt)) {
+         return null();
+     }
+ 
+-    Node importSpecSet = handler.newList(ParseNodeKind::ImportSpecList, pos());
++    ListNodeType importSpecSet = handler.newList(ParseNodeKind::ImportSpecList, pos());
+     if (!importSpecSet) {
+         return null();
+     }
+ 
+     if (tt == TokenKind::String) {
+         // Handle the form |import 'a'| by leaving the list empty. This is
+         // equivalent to |import {} from 'a'|.
+         importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
+@@ -5876,22 +5884,21 @@ template<class ParseHandler, typename Ch
+ inline bool
+ GeneralParser<ParseHandler, CharT>::checkExportedName(JSAtom* exportName)
+ {
+     return asFinalParser()->checkExportedName(exportName);
+ }
+ 
+ template<typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::checkExportedNamesForArrayBinding(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Array));
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-
+-    for (ParseNode* node = pn->pn_head; node; node = node->pn_next) {
++Parser<FullParseHandler, CharT>::checkExportedNamesForArrayBinding(ListNode* array)
++{
++    MOZ_ASSERT(array->isKind(ParseNodeKind::Array));
++
++    for (ParseNode* node : array->contents()) {
+         if (node->isKind(ParseNodeKind::Elision)) {
+             continue;
+         }
+ 
+         ParseNode* binding;
+         if (node->isKind(ParseNodeKind::Spread)) {
+             binding = node->pn_kid;
+         } else if (node->isKind(ParseNodeKind::Assign)) {
+@@ -5905,37 +5912,36 @@ Parser<FullParseHandler, CharT>::checkEx
+         }
+     }
+ 
+     return true;
+ }
+ 
+ template<typename CharT>
+ inline bool
+-Parser<SyntaxParseHandler, CharT>::checkExportedNamesForArrayBinding(Node node)
++Parser<SyntaxParseHandler, CharT>::checkExportedNamesForArrayBinding(ListNodeType array)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template<class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::checkExportedNamesForArrayBinding(Node node)
+-{
+-    return asFinalParser()->checkExportedNamesForArrayBinding(node);
++GeneralParser<ParseHandler, CharT>::checkExportedNamesForArrayBinding(ListNodeType array)
++{
++    return asFinalParser()->checkExportedNamesForArrayBinding(array);
+ }
+ 
+ template<typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::checkExportedNamesForObjectBinding(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Object));
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-
+-    for (ParseNode* node = pn->pn_head; node; node = node->pn_next) {
++Parser<FullParseHandler, CharT>::checkExportedNamesForObjectBinding(ListNode* obj)
++{
++    MOZ_ASSERT(obj->isKind(ParseNodeKind::Object));
++
++    for (ParseNode* node : obj->contents()) {
+         MOZ_ASSERT(node->isKind(ParseNodeKind::MutateProto) ||
+                    node->isKind(ParseNodeKind::Colon) ||
+                    node->isKind(ParseNodeKind::Shorthand) ||
+                    node->isKind(ParseNodeKind::Spread));
+ 
+         ParseNode* target;
+         if (node->isKind(ParseNodeKind::Spread)) {
+             target = node->pn_kid;
+@@ -5956,44 +5962,44 @@ Parser<FullParseHandler, CharT>::checkEx
+         }
+     }
+ 
+     return true;
+ }
+ 
+ template<typename CharT>
+ inline bool
+-Parser<SyntaxParseHandler, CharT>::checkExportedNamesForObjectBinding(Node node)
++Parser<SyntaxParseHandler, CharT>::checkExportedNamesForObjectBinding(ListNodeType obj)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template<class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::checkExportedNamesForObjectBinding(Node node)
+-{
+-    return asFinalParser()->checkExportedNamesForObjectBinding(node);
++GeneralParser<ParseHandler, CharT>::checkExportedNamesForObjectBinding(ListNodeType obj)
++{
++    return asFinalParser()->checkExportedNamesForObjectBinding(obj);
+ }
+ 
+ template<typename CharT>
+ bool
+ Parser<FullParseHandler, CharT>::checkExportedNamesForDeclaration(ParseNode* node)
+ {
+     if (node->isKind(ParseNodeKind::Name)) {
+         if (!checkExportedName(node->pn_atom)) {
+             return false;
+         }
+     } else if (node->isKind(ParseNodeKind::Array)) {
+-        if (!checkExportedNamesForArrayBinding(node)) {
++        if (!checkExportedNamesForArrayBinding(&node->as<ListNode>())) {
+             return false;
+         }
+     } else {
+         MOZ_ASSERT(node->isKind(ParseNodeKind::Object));
+-        if (!checkExportedNamesForObjectBinding(node)) {
++        if (!checkExportedNamesForObjectBinding(&node->as<ListNode>())) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ template<typename CharT>
+@@ -6008,45 +6014,44 @@ template<class ParseHandler, typename Ch
+ inline bool
+ GeneralParser<ParseHandler, CharT>::checkExportedNamesForDeclaration(Node node)
+ {
+     return asFinalParser()->checkExportedNamesForDeclaration(node);
+ }
+ 
+ template<typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::checkExportedNamesForDeclarationList(ParseNode* node)
+-{
+-    MOZ_ASSERT(node->isArity(PN_LIST));
+-    for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) {
++Parser<FullParseHandler, CharT>::checkExportedNamesForDeclarationList(ListNode* node)
++{
++    for (ParseNode* binding : node->contents()) {
+         if (binding->isKind(ParseNodeKind::Assign)) {
+             binding = binding->pn_left;
+         } else {
+             MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
+         }
+ 
+         if (!checkExportedNamesForDeclaration(binding)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ template<typename CharT>
+ inline bool
+-Parser<SyntaxParseHandler, CharT>::checkExportedNamesForDeclarationList(Node node)
++Parser<SyntaxParseHandler, CharT>::checkExportedNamesForDeclarationList(ListNodeType node)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template<class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::checkExportedNamesForDeclarationList(Node node)
++GeneralParser<ParseHandler, CharT>::checkExportedNamesForDeclarationList(ListNodeType node)
+ {
+     return asFinalParser()->checkExportedNamesForDeclarationList(node);
+ }
+ 
+ template<typename CharT>
+ inline bool
+ Parser<FullParseHandler, CharT>::checkExportedNameForClause(ParseNode* node)
+ {
+@@ -6186,17 +6191,17 @@ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::exportBatch(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Mul));
+ 
+-    Node kid = handler.newList(ParseNodeKind::ExportSpecList, pos());
++    ListNodeType kid = handler.newList(ParseNodeKind::ExportSpecList, pos());
+     if (!kid) {
+         return null();
+     }
+ 
+     // Handle the form |export *| by adding a special export batch
+     // specifier to the list.
+     Node exportSpec = handler.newExportBatchSpec(pos());
+     if (!exportSpec) {
+@@ -6207,58 +6212,58 @@ GeneralParser<ParseHandler, CharT>::expo
+ 
+     MUST_MATCH_TOKEN(TokenKind::From, JSMSG_FROM_AFTER_EXPORT_STAR);
+ 
+     return exportFrom(begin, kid);
+ }
+ 
+ template<typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::checkLocalExportNames(ParseNode* node)
++Parser<FullParseHandler, CharT>::checkLocalExportNames(ListNode* node)
+ {
+     // ES 2017 draft 15.2.3.1.
+-    for (ParseNode* next = node->pn_head; next; next = next->pn_next) {
++    for (ParseNode* next : node->contents()) {
+         ParseNode* name = next->pn_left;
+         MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
+ 
+         RootedPropertyName ident(context, name->pn_atom->asPropertyName());
+         if (!checkLocalExportName(ident, name->pn_pos.begin)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ template<typename CharT>
+ bool
+-Parser<SyntaxParseHandler, CharT>::checkLocalExportNames(Node node)
++Parser<SyntaxParseHandler, CharT>::checkLocalExportNames(ListNodeType node)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template<class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::checkLocalExportNames(Node node)
++GeneralParser<ParseHandler, CharT>::checkLocalExportNames(ListNodeType node)
+ {
+     return asFinalParser()->checkLocalExportNames(node);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::exportClause(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
+ 
+-    Node kid = handler.newList(ParseNodeKind::ExportSpecList, pos());
++    ListNodeType kid = handler.newList(ParseNodeKind::ExportSpecList, pos());
+     if (!kid) {
+         return null();
+     }
+ 
+     TokenKind tt;
+     while (true) {
+         // Handle the forms |export {}| and |export { ..., }| (where ... is non
+         // empty), by escaping the loop early if the next token is }.
+@@ -6366,17 +6371,17 @@ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Var));
+ 
+-    Node kid = declarationList(YieldIsName, ParseNodeKind::Var);
++    ListNodeType kid = declarationList(YieldIsName, ParseNodeKind::Var);
+     if (!kid) {
+         return null();
+     }
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+     if (!checkExportedNamesForDeclarationList(kid)) {
+         return null();
+@@ -6465,17 +6470,17 @@ GeneralParser<ParseHandler, CharT>::expo
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
+     MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TokenKind::Const));
+     MOZ_ASSERT_IF(kind == DeclarationKind::Let, anyChars.isCurrentTokenType(TokenKind::Let));
+ 
+-    Node kid = lexicalDeclaration(YieldIsName, kind);
++    ListNodeType kid = lexicalDeclaration(YieldIsName, kind);
+     if (!kid) {
+         return null();
+     }
+     if (!checkExportedNamesForDeclarationList(kid)) {
+         return null();
+     }
+ 
+     Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+@@ -6761,17 +6766,17 @@ GeneralParser<ParseHandler, CharT>::cons
+         }
+ 
+         TokenPos funcPos = pos();
+         Node fun = functionStmt(pos().begin, yieldHandling, NameRequired);
+         if (!fun) {
+             return null();
+         }
+ 
+-        Node block = handler.newStatementList(funcPos);
++        ListNodeType block = handler.newStatementList(funcPos);
+         if (!block) {
+             return null();
+         }
+ 
+         handler.addStatementToList(block, fun);
+         return finishLexicalScope(scope, block);
+     }
+ 
+@@ -7265,17 +7270,17 @@ GeneralParser<ParseHandler, CharT>::swit
+     MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_SWITCH);
+ 
+     ParseContext::Statement stmt(pc, StatementKind::Switch);
+     ParseContext::Scope scope(this);
+     if (!scope.init(pc)) {
+         return null();
+     }
+ 
+-    Node caseList = handler.newStatementList(pos());
++    ListNodeType caseList = handler.newStatementList(pos());
+     if (!caseList) {
+         return null();
+     }
+ 
+     bool seenDefault = false;
+     TokenKind tt;
+     while (true) {
+         if (!tokenStream.getToken(&tt, TokenStream::Operand)) {
+@@ -7306,17 +7311,17 @@ GeneralParser<ParseHandler, CharT>::swit
+ 
+           default:
+             error(JSMSG_BAD_SWITCH);
+             return null();
+         }
+ 
+         MUST_MATCH_TOKEN_MOD(TokenKind::Colon, TokenStream::Operand, JSMSG_COLON_AFTER_CASE);
+ 
+-        Node body = handler.newStatementList(pos());
++        ListNodeType body = handler.newStatementList(pos());
+         if (!body) {
+             return null();
+         }
+ 
+         bool afterReturn = false;
+         bool warnedAboutStatementsAfterReturn = false;
+         uint32_t statementBegin = 0;
+         while (true) {
+@@ -7353,24 +7358,24 @@ GeneralParser<ParseHandler, CharT>::swit
+ 
+         Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
+         if (!casepn) {
+             return null();
+         }
+         handler.addCaseStatementToList(caseList, casepn);
+     }
+ 
+-    caseList = finishLexicalScope(scope, caseList);
+-    if (!caseList) {
+-        return null();
+-    }
+-
+-    handler.setEndPosition(caseList, pos().end);
+-
+-    return handler.newSwitchStatement(begin, discriminant, caseList, seenDefault);
++    Node lexicalForCaseList = finishLexicalScope(scope, caseList);
++    if (!lexicalForCaseList) {
++        return null();
++    }
++
++    handler.setEndPosition(lexicalForCaseList, pos().end);
++
++    return handler.newSwitchStatement(begin, discriminant, lexicalForCaseList, seenDefault);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Continue));
+     uint32_t begin = pos().begin;
+@@ -7872,17 +7877,17 @@ GeneralParser<ParseHandler, CharT>::catc
+     }
+ 
+     // The catch parameter names cannot be redeclared inside the catch
+     // block, so declare the name in the inner scope.
+     if (!scope.addCatchParameters(pc, catchParamScope)) {
+         return null();
+     }
+ 
+-    Node list = statementList(yieldHandling);
++    ListNodeType list = statementList(yieldHandling);
+     if (!list) {
+         return null();
+     }
+ 
+     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
+                                      reportMissingClosing(JSMSG_CURLY_AFTER_CATCH,
+                                                           JSMSG_CURLY_OPENED, openedPos));
+ 
+@@ -8001,17 +8006,17 @@ GeneralParser<ParseHandler, CharT>::clas
+         classHeritage = memberExpr(yieldHandling, TripledotProhibited, tt);
+         if (!classHeritage) {
+             return null();
+         }
+     }
+ 
+     MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CLASS);
+ 
+-    Node classMethods = handler.newClassMethodList(pos().begin);
++    ListNodeType classMethods = handler.newClassMethodList(pos().begin);
+     if (!classMethods) {
+         return null();
+     }
+ 
+     Maybe<DeclarationKind> declKind = Nothing();
+     for (;;) {
+         TokenKind tt;
+         if (!tokenStream.getToken(&tt)) {
+@@ -8203,17 +8208,17 @@ ParserBase::nextTokenContinuesLetDeclara
+     // Otherwise a let declaration must have a name.
+     return TokenKindIsPossibleIdentifier(next);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling)
+ {
+-    Node vars = declarationList(yieldHandling, ParseNodeKind::Var);
++    ListNodeType vars = declarationList(yieldHandling, ParseNodeKind::Var);
+     if (!vars) {
+         return null();
+     }
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+     return vars;
+ }
+@@ -8668,17 +8673,17 @@ GeneralParser<ParseHandler, CharT>::expr
+     bool matched;
+     if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand)) {
+         return null();
+     }
+     if (!matched) {
+         return pn;
+     }
+ 
+-    Node seq = handler.newCommaExpressionList(pn);
++    ListNodeType seq = handler.newCommaExpressionList(pn);
+     if (!seq) {
+         return null();
+     }
+     while (true) {
+         // Trailing comma before the closing parenthesis is valid in an arrow
+         // function parameters list: `(a, b, ) => body`. Check if we are
+         // directly under CoverParenthesizedExpressionAndArrowParameterList,
+         // and the next two tokens are closing parenthesis and arrow. If all
+@@ -9403,21 +9408,21 @@ GeneralParser<ParseHandler, CharT>::assi
+             errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_PARAMETER);
+             return null();
+         }
+     }
+     return res;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, bool* isSpread,
+                                                  PossibleError* possibleError /* = nullptr */)
+ {
+-    Node argsList = handler.newArguments(pos());
++    ListNodeType argsList = handler.newArguments(pos());
+     if (!argsList) {
+         return null();
+     }
+ 
+     bool matched;
+     if (!tokenStream.matchToken(&matched, TokenKind::RightParen, TokenStream::Operand)) {
+         return null();
+     }
+@@ -9727,17 +9732,17 @@ GeneralParser<ParseHandler, CharT>::memb
+                         }
+                     }
+ 
+                     nextMember = handler.newCall(lhs, args);
+                     if (!nextMember) {
+                         return null();
+                     }
+                 } else {
+-                    Node args = handler.newArguments(pos());
++                    ListNodeType args = handler.newArguments(pos());
+                     if (!args) {
+                         return null();
+                     }
+ 
+                     if (!taggedTemplate(yieldHandling, args, tt)) {
+                         return null();
+                     }
+ 
+@@ -10128,39 +10133,39 @@ GeneralParser<ParseHandler, CharT>::chec
+ 
+         exprPossibleError->transferErrorsTo(possibleError);
+         return true;
+     }
+     return checkDestructuringAssignmentTarget(expr, exprPos, exprPossibleError, possibleError);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
+                                                      PossibleError* possibleError)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
+ 
+     uint32_t begin = pos().begin;
+-    Node literal = handler.newArrayLiteral(begin);
++    ListNodeType literal = handler.newArrayLiteral(begin);
+     if (!literal) {
+         return null();
+     }
+ 
+     TokenKind tt;
+     if (!tokenStream.getToken(&tt, TokenStream::Operand)) {
+         return null();
+     }
+ 
+     if (tt == TokenKind::RightBracket) {
+         /*
+          * Mark empty arrays as non-constant, since we cannot easily
+          * determine their type.
+          */
+-        handler.setListFlag(literal, PNX_NONCONST);
++        handler.setListHasNonConstInitializer(literal);
+     } else {
+         anyChars.ungetToken();
+ 
+         for (uint32_t index = 0; ; index++) {
+             if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
+                 error(JSMSG_ARRAY_INIT_TOO_BIG);
+                 return null();
+             }
+@@ -10249,17 +10254,17 @@ GeneralParser<ParseHandler, CharT>::arra
+     handler.setEndPosition(literal, pos().end);
+     return literal;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
+                                                  const Maybe<DeclarationKind>& maybeDecl,
+-                                                 Node propList,
++                                                 ListNodeType propList,
+                                                  PropertyType* propType,
+                                                  MutableHandleAtom propAtom)
+ {
+     TokenKind ltok;
+     if (!tokenStream.getToken(&ltok)) {
+         return null();
+     }
+ 
+@@ -10461,49 +10466,49 @@ GeneralParser<ParseHandler, CharT>::prop
+     error(JSMSG_COLON_AFTER_ID);
+     return null();
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
+                                                          const Maybe<DeclarationKind>& maybeDecl,
+-                                                         Node literal)
++                                                         ListNodeType literal)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
+ 
+     uint32_t begin = pos().begin;
+ 
+     if (maybeDecl) {
+         if (*maybeDecl == DeclarationKind::FormalParameter) {
+             pc->functionBox()->hasParameterExprs = true;
+         }
+     } else {
+-        handler.setListFlag(literal, PNX_NONCONST);
++        handler.setListHasNonConstInitializer(literal);
+     }
+ 
+     Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+     if (!assignNode) {
+         return null();
+     }
+ 
+     MUST_MATCH_TOKEN_MOD(TokenKind::RightBracket, TokenStream::Operand, JSMSG_COMP_PROP_UNTERM_EXPR);
+     return handler.newComputedName(assignNode, begin, pos().end);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
+                                                   PossibleError* possibleError)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
+ 
+     uint32_t openedPos = pos().begin;
+ 
+-    Node literal = handler.newObjectLiteral(pos().begin);
++    ListNodeType literal = handler.newObjectLiteral(pos().begin);
+     if (!literal) {
+         return null();
+     }
+ 
+     bool seenPrototypeMutation = false;
+     bool seenCoverInitializedName = false;
+     Maybe<DeclarationKind> declKind = Nothing();
+     RootedAtom propAtom(context);
+diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
+--- a/js/src/frontend/Parser.h
++++ b/js/src/frontend/Parser.h
+@@ -454,16 +454,21 @@ enum FunctionCallBehavior {
+ 
+ template <class ParseHandler>
+ class MOZ_STACK_CLASS PerHandlerParser
+   : public ParserBase
+ {
+   private:
+     using Node = typename ParseHandler::Node;
+ 
++#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
++    using longTypeName = typename ParseHandler::longTypeName;
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
++#undef DECLARE_TYPE
++
+   protected:
+     /* State specific to the kind of parse being performed. */
+     ParseHandler handler;
+ 
+     // When ParseHandler is FullParseHandler:
+     //
+     //   If non-null, this field holds the syntax parser used to attempt lazy
+     //   parsing of inner functions. If null, then lazy parsing is disabled.
+@@ -499,17 +504,17 @@ class MOZ_STACK_CLASS PerHandlerParser
+       : PerHandlerParser(cx, alloc, options, foldConstants, usedNames, lazyOuterFunction,
+                          sourceObject, parseGoal,
+                          // JSOPTION_EXTRA_WARNINGS adds extra warnings not
+                          // generated when functions are parsed lazily.
+                          // ("use strict" doesn't inhibit lazy parsing.)
+                          static_cast<void*>(options.extraWarningsOption ? nullptr : syntaxParser))
+     {}
+ 
+-    static Node null() { return ParseHandler::null(); }
++    static typename ParseHandler::NullNode null() { return ParseHandler::null(); }
+ 
+     Node stringLiteral();
+ 
+     const char* nameIsArgumentsOrEval(Node node);
+ 
+     bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
+ 
+     bool noteUsedName(HandlePropertyName name) {
+@@ -683,16 +688,22 @@ class MOZ_STACK_CLASS GeneralParser
+ {
+   public:
+     using TokenStream = TokenStreamSpecific<CharT, ParserAnyCharsAccess<GeneralParser>>;
+ 
+   private:
+     using Base = PerHandlerParser<ParseHandler>;
+     using FinalParser = Parser<ParseHandler, CharT>;
+     using Node = typename ParseHandler::Node;
++
++#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
++    using longTypeName = typename ParseHandler::longTypeName;
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
++#undef DECLARE_TYPE
++
+     using typename Base::InvokedPrediction;
+     using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
+ 
+   protected:
+     using Modifier = TokenStreamShared::Modifier;
+     using Position = typename TokenStream::Position;
+ 
+     using Base::PredictUninvoked;
+@@ -907,17 +918,17 @@ class MOZ_STACK_CLASS GeneralParser
+                   ParseGoal parseGoal);
+ 
+     inline void setAwaitHandling(AwaitHandling awaitHandling);
+     inline void setInParametersOfAsyncFunction(bool inParameters);
+ 
+     /*
+      * Parse a top-level JS script.
+      */
+-    Node parse();
++    ListNodeType parse();
+ 
+     /* Report the given error at the current offset. */
+     void error(unsigned errorNumber, ...);
+     void errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...);
+ 
+     /* Report the given error at the given offset. */
+     void errorAt(uint32_t offset, unsigned errorNumber, ...);
+     void errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+@@ -954,20 +965,20 @@ class MOZ_STACK_CLASS GeneralParser
+      * offset.
+      */
+     MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
+ 
+   private:
+     GeneralParser* thisForCtor() { return this; }
+ 
+     Node noSubstitutionUntaggedTemplate();
+-    Node templateLiteral(YieldHandling yieldHandling);
+-    bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt);
+-    bool appendToCallSiteObj(Node callSiteObj);
+-    bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
++    ListNodeType templateLiteral(YieldHandling yieldHandling);
++    bool taggedTemplate(YieldHandling yieldHandling, ListNodeType tagArgsList, TokenKind tt);
++    bool appendToCallSiteObj(CallSiteNodeType callSiteObj);
++    bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, ListNodeType nodeList,
+                                         TokenKind* ttp);
+ 
+     inline bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun,
+                                             uint32_t toStringStart, InHandling inHandling,
+                                             YieldHandling yieldHandling, FunctionSyntaxKind kind,
+                                             GeneratorKind generatorKind,
+                                             FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                             Directives inheritedDirectives,
+@@ -1005,17 +1016,17 @@ class MOZ_STACK_CLASS GeneralParser
+      */
+     Node functionStmt(uint32_t toStringStart,
+                       YieldHandling yieldHandling, DefaultHandling defaultHandling,
+                       FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+     Node functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
+                       FunctionAsyncKind asyncKind);
+ 
+     Node statement(YieldHandling yieldHandling);
+-    bool maybeParseDirective(Node list, Node pn, bool* cont);
++    bool maybeParseDirective(ListNodeType list, Node pn, bool* cont);
+ 
+     Node blockStatement(YieldHandling yieldHandling,
+                         unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
+     Node doWhileStatement(YieldHandling yieldHandling);
+     Node whileStatement(YieldHandling yieldHandling);
+ 
+     Node forStatement(YieldHandling yieldHandling);
+     bool forHeadStart(YieldHandling yieldHandling,
+@@ -1038,24 +1049,24 @@ class MOZ_STACK_CLASS GeneralParser
+     Node variableStatement(YieldHandling yieldHandling);
+ 
+     Node labeledStatement(YieldHandling yieldHandling);
+     Node labeledItem(YieldHandling yieldHandling);
+ 
+     Node ifStatement(YieldHandling yieldHandling);
+     Node consequentOrAlternative(YieldHandling yieldHandling);
+ 
+-    Node lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
++    ListNodeType lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
+ 
+     inline Node importDeclaration();
+     Node importDeclarationOrImportExpr(YieldHandling yieldHandling);
+ 
+     Node exportFrom(uint32_t begin, Node specList);
+     Node exportBatch(uint32_t begin);
+-    inline bool checkLocalExportNames(Node node);
++    inline bool checkLocalExportNames(ListNodeType node);
+     Node exportClause(uint32_t begin);
+     Node exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
+                                    FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+     Node exportVariableStatement(uint32_t begin);
+     Node exportClassDeclaration(uint32_t begin);
+     Node exportLexicalDeclaration(uint32_t begin, DeclarationKind kind);
+     Node exportDefaultFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
+                                           FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+@@ -1082,20 +1093,20 @@ class MOZ_STACK_CLASS GeneralParser
+     // indicates what was parsed.
+     //
+     // If parsing recognized a for(;;) loop, the next token is the ';' within
+     // the loop-head that separates the init/test parts.
+     //
+     // Otherwise, for for-in/of loops, the next token is the ')' ending the
+     // loop-head.  Additionally, the expression that the loop iterates over was
+     // parsed into |*forInOrOfExpression|.
+-    Node declarationList(YieldHandling yieldHandling,
+-                         ParseNodeKind kind,
+-                         ParseNodeKind* forHeadKind = nullptr,
+-                         Node* forInOrOfExpression = nullptr);
++    ListNodeType declarationList(YieldHandling yieldHandling,
++                                 ParseNodeKind kind,
++                                 ParseNodeKind* forHeadKind = nullptr,
++                                 Node* forInOrOfExpression = nullptr);
+ 
+     // The items in a declaration list are either patterns or names, with or
+     // without initializers.  These two methods parse a single pattern/name and
+     // any associated initializer -- and if parsing an |initialDeclaration|
+     // will, if parsing in a for-loop head (as specified by |forHeadKind| being
+     // non-null), consume additional tokens up to the closing ')' in a
+     // for-in/of loop head, returning the iterated expression in
+     // |*forInOrOfExpression|.  (An "initial declaration" is the first
+@@ -1166,28 +1177,28 @@ class MOZ_STACK_CLASS GeneralParser
+     enum FunctionBodyType { StatementListBody, ExpressionBody };
+     Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
+                       FunctionBodyType type);
+ 
+     Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin);
+ 
+     Node condition(InHandling inHandling, YieldHandling yieldHandling);
+ 
+-    Node argumentList(YieldHandling yieldHandling, bool* isSpread,
+-                      PossibleError* possibleError = nullptr);
++    ListNodeType argumentList(YieldHandling yieldHandling, bool* isSpread,
++                              PossibleError* possibleError = nullptr);
+     Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
+                                   TokenKind tt);
+     Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling,
+                                                      TokenKind tt);
+ 
+     inline bool checkExportedName(JSAtom* exportName);
+-    inline bool checkExportedNamesForArrayBinding(Node node);
+-    inline bool checkExportedNamesForObjectBinding(Node node);
++    inline bool checkExportedNamesForArrayBinding(ListNodeType array);
++    inline bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     inline bool checkExportedNamesForDeclaration(Node node);
+-    inline bool checkExportedNamesForDeclarationList(Node node);
++    inline bool checkExportedNamesForDeclarationList(ListNodeType node);
+     inline bool checkExportedNameForFunction(Node node);
+     inline bool checkExportedNameForClass(Node node);
+     inline bool checkExportedNameForClause(Node node);
+ 
+     enum ClassContext { ClassStatement, ClassExpression };
+     Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
+                          DefaultHandling defaultHandling);
+ 
+@@ -1222,31 +1233,33 @@ class MOZ_STACK_CLASS GeneralParser
+                              uint32_t prevPos);
+     bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
+                                        bool disallowDuplicateParams, bool* duplicatedParam);
+ 
+     bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
+                                                     DeclarationKind kind, TokenPos pos);
+ 
+     Node propertyName(YieldHandling yieldHandling,
+-                      const mozilla::Maybe<DeclarationKind>& maybeDecl, Node propList,
++                      const mozilla::Maybe<DeclarationKind>& maybeDecl,
++                      ListNodeType propList,
+                       PropertyType* propType, MutableHandleAtom propAtom);
+     Node computedPropertyName(YieldHandling yieldHandling,
+-                              const mozilla::Maybe<DeclarationKind>& maybeDecl, Node literal);
+-    Node arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
++                              const mozilla::Maybe<DeclarationKind>& maybeDecl,
++                              ListNodeType literal);
++    ListNodeType arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
+     inline Node newRegExp();
+ 
+-    Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
++    ListNodeType objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
+ 
+     Node bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling);
+     Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling);
+     Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
+                                     TokenKind tt);
+-    Node objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+-    Node arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
++    ListNodeType objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
++    ListNodeType arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+ 
+     enum class TargetBehavior {
+         PermitAssignmentPattern,
+         ForbidAssignmentPattern
+     };
+     bool checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+                                             PossibleError* exprPossibleError,
+                                             PossibleError* possibleError,
+@@ -1266,40 +1279,45 @@ class MOZ_STACK_CLASS GeneralParser
+     // the given Yield parameter.  If there is no match, report a syntax
+     // error.
+     PropertyName* bindingIdentifier(YieldHandling yieldHandling);
+ 
+     bool checkLabelOrIdentifierReference(PropertyName* ident, uint32_t offset,
+                                          YieldHandling yieldHandling,
+                                          TokenKind hint = TokenKind::Limit);
+ 
+-    Node statementList(YieldHandling yieldHandling);
++    ListNodeType statementList(YieldHandling yieldHandling);
+ 
+     MOZ_MUST_USE Node
+     innerFunction(Node funcNode, ParseContext* outerpc, HandleFunction fun,
+                   uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling,
+                   FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                   FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives,
+                   Directives* newDirectives);
+ 
+     bool matchOrInsertSemicolon();
+ 
+     bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
+ 
+   private:
+-    inline bool asmJS(Node list);
++    inline bool asmJS(ListNodeType list);
+ };
+ 
+ template <typename CharT>
+ class MOZ_STACK_CLASS Parser<SyntaxParseHandler, CharT> final
+   : public GeneralParser<SyntaxParseHandler, CharT>
+ {
+     using Base = GeneralParser<SyntaxParseHandler, CharT>;
+     using Node = SyntaxParseHandler::Node;
+ 
++#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
++    using longTypeName = SyntaxParseHandler::longTypeName;
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
++#undef DECLARE_TYPE
++
+     using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
+ 
+     // Numerous Base::* functions have bodies like
+     //
+     //   return asFinalParser()->func(...);
+     //
+     // and must be able to call functions here.  Add a friendship relationship
+     // so functions here can be hidden when appropriate.
+@@ -1370,47 +1388,52 @@ class MOZ_STACK_CLASS Parser<SyntaxParse
+     inline void setInParametersOfAsyncFunction(bool inParameters);
+ 
+     Node newRegExp();
+ 
+     // Parse a module.
+     Node moduleBody(ModuleSharedContext* modulesc);
+ 
+     inline Node importDeclaration();
+-    inline bool checkLocalExportNames(Node node);
++    inline bool checkLocalExportNames(ListNodeType node);
+     inline bool checkExportedName(JSAtom* exportName);
+-    inline bool checkExportedNamesForArrayBinding(Node node);
+-    inline bool checkExportedNamesForObjectBinding(Node node);
++    inline bool checkExportedNamesForArrayBinding(ListNodeType array);
++    inline bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     inline bool checkExportedNamesForDeclaration(Node node);
+-    inline bool checkExportedNamesForDeclarationList(Node node);
++    inline bool checkExportedNamesForDeclarationList(ListNodeType node);
+     inline bool checkExportedNameForFunction(Node node);
+     inline bool checkExportedNameForClass(Node node);
+     inline bool checkExportedNameForClause(Node node);
+ 
+     bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun, uint32_t toStringStart,
+                                      InHandling inHandling, YieldHandling yieldHandling,
+                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                      FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                      Directives inheritedDirectives, Directives* newDirectives);
+ 
+     bool skipLazyInnerFunction(Node funcNode, uint32_t toStringStart, FunctionSyntaxKind kind,
+                                bool tryAnnexB);
+ 
+-    bool asmJS(Node list);
++    bool asmJS(ListNodeType list);
+ 
+     // Functions present only in Parser<SyntaxParseHandler, CharT>.
+ };
+ 
+ template <typename CharT>
+ class MOZ_STACK_CLASS Parser<FullParseHandler, CharT> final
+   : public GeneralParser<FullParseHandler, CharT>
+ {
+     using Base = GeneralParser<FullParseHandler, CharT>;
+     using Node = FullParseHandler::Node;
+ 
++#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
++    using longTypeName = FullParseHandler::longTypeName;
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
++#undef DECLARE_TYPE
++
+     using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
+ 
+     // Numerous Base::* functions have bodies like
+     //
+     //   return asFinalParser()->func(...);
+     //
+     // and must be able to call functions here.  Add a friendship relationship
+     // so functions here can be hidden when appropriate.
+@@ -1490,22 +1513,22 @@ class MOZ_STACK_CLASS Parser<FullParseHa
+     inline void setInParametersOfAsyncFunction(bool inParameters);
+ 
+     Node newRegExp();
+ 
+     // Parse a module.
+     Node moduleBody(ModuleSharedContext* modulesc);
+ 
+     Node importDeclaration();
+-    bool checkLocalExportNames(Node node);
++    bool checkLocalExportNames(ListNodeType node);
+     bool checkExportedName(JSAtom* exportName);
+-    bool checkExportedNamesForArrayBinding(Node node);
+-    bool checkExportedNamesForObjectBinding(Node node);
++    bool checkExportedNamesForArrayBinding(ListNodeType array);
++    bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     bool checkExportedNamesForDeclaration(Node node);
+-    bool checkExportedNamesForDeclarationList(Node node);
++    bool checkExportedNamesForDeclarationList(ListNodeType node);
+     bool checkExportedNameForFunction(Node node);
+     bool checkExportedNameForClass(Node node);
+     inline bool checkExportedNameForClause(Node node);
+ 
+     bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun, uint32_t toStringStart,
+                                      InHandling inHandling, YieldHandling yieldHandling,
+                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                      FunctionAsyncKind asyncKind, bool tryAnnexB,
+@@ -1533,29 +1556,29 @@ class MOZ_STACK_CLASS Parser<FullParseHa
+     Node standaloneFunction(HandleFunction fun, HandleScope enclosingScope,
+                             const mozilla::Maybe<uint32_t>& parameterListEnd,
+                             GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+                             Directives inheritedDirectives, Directives* newDirectives);
+ 
+     bool checkStatementsEOF();
+ 
+     // Parse the body of a global script.
+-    Node globalBody(GlobalSharedContext* globalsc);
++    ListNodeType globalBody(GlobalSharedContext* globalsc);
+ 
+-    bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
++    bool namedImportsOrNamespaceImport(TokenKind tt, ListNodeType importSpecSet);
+ 
+     PropertyName* importedBinding() {
+         return bindingIdentifier(YieldIsName);
+     }
+ 
+     bool checkLocalExportName(PropertyName* ident, uint32_t offset) {
+         return checkLabelOrIdentifierReference(ident, offset, YieldIsName);
+     }
+ 
+-    bool asmJS(Node list);
++    bool asmJS(ListNodeType list);
+ };
+ 
+ template<class Parser>
+ /* static */ inline const TokenStreamAnyChars&
+ ParserAnyCharsAccess<Parser>::anyChars(const GeneralTokenStreamChars* ts)
+ {
+     // The structure we're walking through looks like this:
+     //
+diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
+--- a/js/src/frontend/SyntaxParseHandler.h
++++ b/js/src/frontend/SyntaxParseHandler.h
+@@ -131,16 +131,23 @@ class SyntaxParseHandler
+         // We want to reject |-2 ** 3|, but still need to allow |(-2) ** 3|.
+         NodeUnparenthesizedUnary,
+ 
+         // This node is necessary to determine if the LHS of a property access is
+         // super related.
+         NodeSuperBase
+     };
+ 
++#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
++    using longTypeName = Node;
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
++#undef DECLARE_TYPE
++
++    using NullNode = Node;
++
+     bool isNonArrowFunctionExpression(Node node) const {
+         return node == NodeFunctionExpression;
+     }
+ 
+     bool isPropertyAccess(Node node) {
+         return node == NodeDottedProperty || node == NodeElement;
+     }
+ 
+@@ -161,17 +168,24 @@ class SyntaxParseHandler
+         return node == NodeParenthesizedArray || node == NodeParenthesizedObject;
+     }
+ 
+   public:
+     SyntaxParseHandler(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
+       : lastAtom(nullptr)
+     {}
+ 
+-    static Node null() { return NodeFailure; }
++    static NullNode null() { return NodeFailure; }
++
++#define DECLARE_AS(typeName, longTypeName, asMethodName) \
++    static longTypeName asMethodName(Node node) { \
++        return node; \
++    }
++FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
++#undef DECLARE_AS
+ 
+     Node newName(PropertyName* name, const TokenPos& pos, JSContext* cx) {
+         lastAtom = name;
+         if (name == cx->names().arguments) {
+             return NodeArgumentsName;
+         }
+         if (pos.begin + strlen("async") == pos.end && name == cx->names().async) {
+             return NodePotentialAsyncKeyword;
+@@ -198,21 +212,21 @@ class SyntaxParseHandler
+         lastStringPos = pos;
+         return NodeUnparenthesizedString;
+     }
+ 
+     Node newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+ 
+-    Node newCallSiteObject(uint32_t begin) {
++    CallSiteNodeType newCallSiteObject(uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+-    void addToCallSiteObject(Node callSiteObj, Node rawNode, Node cookedNode) {}
++    void addToCallSiteObject(CallSiteNodeType callSiteObj, Node rawNode, Node cookedNode) {}
+ 
+     Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
+     Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
+     Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
+ 
+     template <class Boxer>
+     Node newRegExp(Node reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
+ 
+@@ -241,55 +255,55 @@ class SyntaxParseHandler
+     }
+ 
+     Node appendOrCreateList(ParseNodeKind kind, Node left, Node right, ParseContext* pc) {
+         return NodeGeneric;
+     }
+ 
+     // Expressions
+ 
+-    Node newArrayLiteral(uint32_t begin) { return NodeUnparenthesizedArray; }
+-    MOZ_MUST_USE bool addElision(Node literal, const TokenPos& pos) { return true; }
+-    MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
+-    void addArrayElement(Node literal, Node element) { }
++    ListNodeType newArrayLiteral(uint32_t begin) { return NodeUnparenthesizedArray; }
++    MOZ_MUST_USE bool addElision(ListNodeType literal, const TokenPos& pos) { return true; }
++    MOZ_MUST_USE bool addSpreadElement(ListNodeType literal, uint32_t begin, Node inner) { return true; }
++    void addArrayElement(ListNodeType literal, Node element) { }
+ 
+-    Node newArguments(const TokenPos& pos) { return NodeGeneric; }
++    ListNodeType newArguments(const TokenPos& pos) { return NodeGeneric; }
+     Node newCall(Node callee, Node args) { return NodeFunctionCall; }
+ 
+     Node newSuperCall(Node callee, Node args) { return NodeGeneric; }
+     Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; }
+ 
+-    Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
+-    Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
++    ListNodeType newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
++    ListNodeType newClassMethodList(uint32_t begin) { return NodeGeneric; }
+     Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; }
+     Node newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
+ 
+     Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
+     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
+     Node newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
+ 
+-    MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
++    MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) { return true; }
+     Node newPropertyDefinition(Node name, Node expr) { return NodeGeneric; }
+-    void addPropertyDefinition(Node literal, Node propdef) {}
+-    MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
+-    MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; }
+-    MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; }
+-    MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, AccessorType atype) { return true; }
+-    MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, AccessorType atype, bool isStatic) { return true; }
++    void addPropertyDefinition(ListNodeType literal, Node propdef) {}
++    MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node expr) { return true; }
++    MOZ_MUST_USE bool addShorthand(ListNodeType literal, Node name, Node expr) { return true; }
++    MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { return true; }
++    MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key, Node fn, AccessorType atype) { return true; }
++    MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key, Node fn, AccessorType atype, bool isStatic) { return true; }
+     Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
+     Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
+     Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
+ 
+     // Statements
+ 
+-    Node newStatementList(const TokenPos& pos) { return NodeGeneric; }
+-    void addStatementToList(Node list, Node stmt) {}
+-    void setListEndPosition(Node list, const TokenPos& pos) {}
+-    void addCaseStatementToList(Node list, Node stmt) {}
+-    MOZ_MUST_USE bool prependInitialYield(Node stmtList, Node gen) { return true; }
++    ListNodeType newStatementList(const TokenPos& pos) { return NodeGeneric; }
++    void addStatementToList(ListNodeType list, Node stmt) {}
++    void setListEndPosition(ListNodeType list, const TokenPos& pos) {}
++    void addCaseStatementToList(ListNodeType list, Node caseClause) {}
++    MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) { return true; }
+     Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
+ 
+     Node newExportDeclaration(Node kid, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+     Node newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
+         return NodeGeneric;
+     }
+@@ -397,47 +411,47 @@ class SyntaxParseHandler
+         // XXX This offset isn't relevant to the offending function name.  But
+         //     we may not *have* that function name around, because of how lazy
+         //     parsing works -- the actual name could be outside
+         //     |tokenStream.userbuf|'s observed region.  So the current offset
+         //     is the best we can do.
+         return ts.currentToken().pos.begin;
+     }
+ 
+-    Node newList(ParseNodeKind kind, const TokenPos& pos) {
++    ListNodeType newList(ParseNodeKind kind, const TokenPos& pos) {
+         MOZ_ASSERT(kind != ParseNodeKind::Var);
+         MOZ_ASSERT(kind != ParseNodeKind::Let);
+         MOZ_ASSERT(kind != ParseNodeKind::Const);
+         return NodeGeneric;
+     }
+ 
+-    Node newList(ParseNodeKind kind, Node kid) {
++    ListNodeType newList(ParseNodeKind kind, Node kid) {
+         return newList(kind, TokenPos());
+     }
+ 
+-    Node newDeclarationList(ParseNodeKind kind, const TokenPos& pos) {
++    ListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) {
+         if (kind == ParseNodeKind::Var) {
+             return NodeVarDeclaration;
+         }
+         MOZ_ASSERT(kind == ParseNodeKind::Let || kind == ParseNodeKind::Const);
+         return NodeLexicalDeclaration;
+     }
+ 
+     bool isDeclarationList(Node node) {
+         return node == NodeVarDeclaration || node == NodeLexicalDeclaration;
+     }
+ 
+     // This method should only be called from parsers using FullParseHandler.
+-    Node singleBindingFromDeclaration(Node decl) = delete;
++    Node singleBindingFromDeclaration(ListNodeType decl) = delete;
+ 
+-    Node newCommaExpressionList(Node kid) {
++    ListNodeType newCommaExpressionList(Node kid) {
+         return NodeGeneric;
+     }
+ 
+-    void addList(Node list, Node kid) {
++    void addList(ListNodeType list, Node kid) {
+         MOZ_ASSERT(list == NodeGeneric ||
+                    list == NodeUnparenthesizedArray ||
+                    list == NodeUnparenthesizedObject ||
+                    list == NodeVarDeclaration ||
+                    list == NodeLexicalDeclaration ||
+                    list == NodeFunctionCall);
+     }
+ 
+@@ -469,17 +483,17 @@ class SyntaxParseHandler
+                pn == NodeEmptyStatement;
+     }
+ 
+     bool isSuperBase(Node pn) {
+         return pn == NodeSuperBase;
+     }
+ 
+     void setOp(Node pn, JSOp op) {}
+-    void setListFlag(Node pn, unsigned flag) {}
++    void setListHasNonConstInitializer(ListNodeType literal) {}
+     MOZ_MUST_USE Node parenthesize(Node node) {
+         // A number of nodes have different behavior upon parenthesization, but
+         // only in some circumstances.  Convert these nodes to special
+         // parenthesized forms.
+         if (node == NodeUnparenthesizedArray) {
+             return NodeParenthesizedArray;
+         }
+         if (node == NodeUnparenthesizedObject) {
+diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
+--- a/js/src/wasm/AsmJS.cpp
++++ b/js/src/wasm/AsmJS.cpp
+@@ -441,25 +441,23 @@ TernaryKid3(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isArity(PN_TERNARY));
+     return pn->pn_kid3;
+ }
+ 
+ static inline ParseNode*
+ ListHead(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    return pn->pn_head;
++    return pn->as<ListNode>().head();
+ }
+ 
+ static inline unsigned
+ ListLength(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    return pn->pn_count;
++    return pn->as<ListNode>().count();
+ }
+ 
+ static inline ParseNode*
+ CallCallee(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Call));
+     return BinaryLeft(pn);
+ }
+@@ -502,27 +500,25 @@ CaseBody(ParseNode* pn)
+ {
+     return pn->as<CaseClause>().statementList();
+ }
+ 
+ static inline ParseNode*
+ BinaryOpLeft(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isBinaryOperation());
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    MOZ_ASSERT(pn->pn_count == 2);
++    MOZ_ASSERT(pn->as<ListNode>().count() == 2);
+     return ListHead(pn);
+ }
+ 
+ static inline ParseNode*
+ BinaryOpRight(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isBinaryOperation());
+-    MOZ_ASSERT(pn->isArity(PN_LIST));
+-    MOZ_ASSERT(pn->pn_count == 2);
++    MOZ_ASSERT(pn->as<ListNode>().count() == 2);
+     return NextNode(ListHead(pn));
+ }
+ 
+ static inline ParseNode*
+ BitwiseLeft(ParseNode* pn)
+ {
+     return BinaryOpLeft(pn);
+ }
+@@ -680,17 +676,17 @@ FunctionName(ParseNode* fn)
+     }
+     return nullptr;
+ }
+ 
+ static inline ParseNode*
+ FunctionStatementList(ParseNode* fn)
+ {
+     MOZ_ASSERT(fn->pn_body->isKind(ParseNodeKind::ParamsBody));
+-    ParseNode* last = fn->pn_body->last();
++    ParseNode* last = fn->pn_body->as<ListNode>().last();
+     MOZ_ASSERT(last->isKind(ParseNodeKind::LexicalScope));
+     MOZ_ASSERT(last->isEmptyScope());
+     ParseNode* body = last->scopeBody();
+     MOZ_ASSERT(body->isKind(ParseNodeKind::StatementList));
+     return body;
+ }
+ 
+ static inline bool

+ 2677 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478478.patch

@@ -0,0 +1,2677 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726367 -32400
+#      Wed Sep 12 13:26:07 2018 +0900
+# Node ID 0c08c096decb00e0895f94436c5d7aca0c80a872
+# Parent  4ad37258ece079095198576a0c3b00e13d90205c
+Bug 1479659 - Part 2: Add accessors to TernaryNode. r=jwalden
+
+diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
+--- a/js/src/builtin/ReflectParse.cpp
++++ b/js/src/builtin/ReflectParse.cpp
+@@ -1746,36 +1746,36 @@ class ASTSerializer
+ 
+     bool declaration(ParseNode* pn, MutableHandleValue dst);
+     bool variableDeclaration(ListNode* declList, bool lexical, MutableHandleValue dst);
+     bool variableDeclarator(ParseNode* pn, MutableHandleValue dst);
+     bool importDeclaration(ParseNode* pn, MutableHandleValue dst);
+     bool importSpecifier(ParseNode* pn, MutableHandleValue dst);
+     bool exportDeclaration(ParseNode* pn, MutableHandleValue dst);
+     bool exportSpecifier(ParseNode* pn, MutableHandleValue dst);
+-    bool classDefinition(ParseNode* pn, bool expr, MutableHandleValue dst);
++    bool classDefinition(ClassNode* pn, bool expr, MutableHandleValue dst);
+ 
+     bool optStatement(ParseNode* pn, MutableHandleValue dst) {
+         if (!pn) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return statement(pn, dst);
+     }
+ 
+     bool forInit(ParseNode* pn, MutableHandleValue dst);
+-    bool forIn(ParseNode* loop, ParseNode* head, HandleValue var, HandleValue stmt,
++    bool forIn(ParseNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+                MutableHandleValue dst);
+-    bool forOf(ParseNode* loop, ParseNode* head, HandleValue var, HandleValue stmt,
++    bool forOf(ParseNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+                MutableHandleValue dst);
+     bool statement(ParseNode* pn, MutableHandleValue dst);
+     bool blockStatement(ListNode* node, MutableHandleValue dst);
+     bool switchStatement(ParseNode* pn, MutableHandleValue dst);
+     bool switchCase(ParseNode* pn, MutableHandleValue dst);
+-    bool tryStatement(ParseNode* pn, MutableHandleValue dst);
++    bool tryStatement(TernaryNode* tryNode, MutableHandleValue dst);
+     bool catchClause(ParseNode* pn, MutableHandleValue dst);
+ 
+     bool optExpression(ParseNode* pn, MutableHandleValue dst) {
+         if (!pn) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return expression(pn, dst);
+@@ -2186,17 +2186,17 @@ ASTSerializer::exportDeclaration(ParseNo
+ 
+       case ParseNodeKind::Function:
+         if (!function(kid, AST_FUNC_DECL, &decl)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Class:
+-        if (!classDefinition(kid, false, &decl)) {
++        if (!classDefinition(&kid->as<ClassNode>(), false, &decl)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Var:
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+         if (!variableDeclaration(&kid->as<ListNode>(), kind != ParseNodeKind::Var, &decl)) {
+@@ -2296,38 +2296,43 @@ ASTSerializer::catchClause(ParseNode* pn
+         return false;
+     }
+ 
+     return statement(pn->pn_right, &body) &&
+            builder.catchClause(var, body, &pn->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::tryStatement(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::tryStatement(TernaryNode* tryNode, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+-    MOZ_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+-    MOZ_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
++    ParseNode* bodyNode = tryNode->kid1();
++    MOZ_ASSERT(tryNode->pn_pos.encloses(bodyNode->pn_pos));
++
++    ParseNode* catchNode = tryNode->kid2();
++    MOZ_ASSERT_IF(catchNode, tryNode->pn_pos.encloses(catchNode->pn_pos));
++
++    ParseNode* finallyNode = tryNode->kid3();
++    MOZ_ASSERT_IF(finallyNode, tryNode->pn_pos.encloses(finallyNode->pn_pos));
+ 
+     RootedValue body(cx);
+-    if (!statement(pn->pn_kid1, &body)) {
++    if (!statement(bodyNode, &body)) {
+         return false;
+     }
+ 
+     RootedValue handler(cx, NullValue());
+-    if (ParseNode* catchScope = pn->pn_kid2) {
++    if (ParseNode* catchScope = catchNode) {
+         MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+         if (!catchClause(catchScope->scopeBody(), &handler)) {
+             return false;
+         }
+     }
+ 
+     RootedValue finally(cx);
+-    return optStatement(pn->pn_kid3, &finally) &&
+-           builder.tryStatement(body, handler, finally, &pn->pn_pos, dst);
++    return optStatement(finallyNode, &finally) &&
++           builder.tryStatement(body, handler, finally, &tryNode->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::forInit(ParseNode* pn, MutableHandleValue dst)
+ {
+     if (!pn) {
+         dst.setMagic(JS_SERIALIZE_NO_NODE);
+         return true;
+@@ -2335,50 +2340,50 @@ ASTSerializer::forInit(ParseNode* pn, Mu
+ 
+     bool lexical = pn->isKind(ParseNodeKind::Let) || pn->isKind(ParseNodeKind::Const);
+     return (lexical || pn->isKind(ParseNodeKind::Var))
+            ? variableDeclaration(&pn->as<ListNode>(), lexical, dst)
+            : expression(pn, dst);
+ }
+ 
+ bool
+-ASTSerializer::forOf(ParseNode* loop, ParseNode* head, HandleValue var, HandleValue stmt,
++ASTSerializer::forOf(ParseNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+                          MutableHandleValue dst)
+ {
+     RootedValue expr(cx);
+ 
+-    return expression(head->pn_kid3, &expr) &&
++    return expression(iterExpr, &expr) &&
+         builder.forOfStatement(var, expr, stmt, &loop->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::forIn(ParseNode* loop, ParseNode* head, HandleValue var, HandleValue stmt,
++ASTSerializer::forIn(ParseNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+                          MutableHandleValue dst)
+ {
+     RootedValue expr(cx);
+ 
+-    return expression(head->pn_kid3, &expr) &&
++    return expression(iterExpr, &expr) &&
+         builder.forInStatement(var, expr, stmt, &loop->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::classDefinition(ParseNode* pn, bool expr, MutableHandleValue dst)
++ASTSerializer::classDefinition(ClassNode* pn, bool expr, MutableHandleValue dst)
+ {
+     RootedValue className(cx, MagicValue(JS_SERIALIZE_NO_NODE));
+     RootedValue heritage(cx);
+     RootedValue classBody(cx);
+ 
+-    if (pn->pn_kid1) {
+-        if (!identifier(pn->pn_kid1->as<ClassNames>().innerBinding(), &className)) {
++    if (ClassNames* names = pn->names()) {
++        if (!identifier(names->innerBinding(), &className)) {
+             return false;
+         }
+     }
+ 
+-    return optExpression(pn->pn_kid2, &heritage) &&
+-           statement(pn->pn_kid3, &classBody) &&
++    return optExpression(pn->heritage(), &heritage) &&
++           statement(pn->methodList(), &classBody) &&
+            builder.classDefinition(expr, className, heritage, classBody, &pn->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
+ {
+     if (!CheckRecursionLimit(cx)) {
+         return false;
+@@ -2418,33 +2423,40 @@ ASTSerializer::statement(ParseNode* pn, 
+         }
+         MOZ_FALLTHROUGH;
+ 
+       case ParseNodeKind::StatementList:
+         return blockStatement(&pn->as<ListNode>(), dst);
+ 
+       case ParseNodeKind::If:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+-        MOZ_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
++        TernaryNode* ifNode = &pn->as<TernaryNode>();
++
++        ParseNode* testNode = ifNode->kid1();
++        MOZ_ASSERT(ifNode->pn_pos.encloses(testNode->pn_pos));
++
++        ParseNode* consNode = ifNode->kid2();
++        MOZ_ASSERT(ifNode->pn_pos.encloses(consNode->pn_pos));
++
++        ParseNode* altNode = ifNode->kid3();
++        MOZ_ASSERT_IF(altNode, ifNode->pn_pos.encloses(altNode->pn_pos));
+ 
+         RootedValue test(cx), cons(cx), alt(cx);
+ 
+-        return expression(pn->pn_kid1, &test) &&
+-               statement(pn->pn_kid2, &cons) &&
+-               optStatement(pn->pn_kid3, &alt) &&
+-               builder.ifStatement(test, cons, alt, &pn->pn_pos, dst);
++        return expression(testNode, &test) &&
++               statement(consNode, &cons) &&
++               optStatement(altNode, &alt) &&
++               builder.ifStatement(test, cons, alt, &ifNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Switch:
+         return switchStatement(pn, dst);
+ 
+       case ParseNodeKind::Try:
+-        return tryStatement(pn, dst);
++        return tryStatement(&pn->as<TernaryNode>(), dst);
+ 
+       case ParseNodeKind::With:
+       case ParseNodeKind::While:
+       {
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+ 
+         RootedValue expr(cx), stmt(cx);
+@@ -2468,60 +2480,65 @@ ASTSerializer::statement(ParseNode* pn, 
+                builder.doWhileStatement(stmt, test, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::For:
+       {
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+ 
+-        ParseNode* head = pn->pn_left;
+-
+-        MOZ_ASSERT_IF(head->pn_kid1, head->pn_pos.encloses(head->pn_kid1->pn_pos));
+-        MOZ_ASSERT_IF(head->pn_kid2, head->pn_pos.encloses(head->pn_kid2->pn_pos));
+-        MOZ_ASSERT_IF(head->pn_kid3, head->pn_pos.encloses(head->pn_kid3->pn_pos));
++        TernaryNode* head = &pn->pn_left->as<TernaryNode>();
++
++        ParseNode* initNode = head->kid1();
++        MOZ_ASSERT_IF(initNode, head->pn_pos.encloses(initNode->pn_pos));
++
++        ParseNode* maybeTest = head->kid2();
++        MOZ_ASSERT_IF(maybeTest, head->pn_pos.encloses(maybeTest->pn_pos));
++
++        ParseNode* updateOrIter = head->kid3();
++        MOZ_ASSERT_IF(updateOrIter, head->pn_pos.encloses(updateOrIter->pn_pos));
+ 
+         RootedValue stmt(cx);
+         if (!statement(pn->pn_right, &stmt)) {
+             return false;
+         }
+ 
+         if (head->isKind(ParseNodeKind::ForIn) || head->isKind(ParseNodeKind::ForOf)) {
+             RootedValue var(cx);
+-            if (head->pn_kid1->isKind(ParseNodeKind::LexicalScope)) {
+-                if (!variableDeclaration(&head->pn_kid1->scopeBody()->as<ListNode>(), true, &var)) {
++            if (initNode->isKind(ParseNodeKind::LexicalScope)) {
++                if (!variableDeclaration(&initNode->scopeBody()->as<ListNode>(), true, &var)) {
+                     return false;
+                 }
+-            } else if (!head->pn_kid1->isKind(ParseNodeKind::Var) &&
+-                       !head->pn_kid1->isKind(ParseNodeKind::Let) &&
+-                       !head->pn_kid1->isKind(ParseNodeKind::Const))
++            } else if (!initNode->isKind(ParseNodeKind::Var) &&
++                       !initNode->isKind(ParseNodeKind::Let) &&
++                       !initNode->isKind(ParseNodeKind::Const))
+             {
+-                if (!pattern(head->pn_kid1, &var)) {
++                if (!pattern(initNode, &var)) {
+                     return false;
+                 }
+             } else {
+-                if (!variableDeclaration(&head->pn_kid1->as<ListNode>(),
+-                                         head->pn_kid1->isKind(ParseNodeKind::Let) ||
+-                                         head->pn_kid1->isKind(ParseNodeKind::Const),
++                if (!variableDeclaration(&initNode->as<ListNode>(),
++                                         initNode->isKind(ParseNodeKind::Let) ||
++                                         initNode->isKind(ParseNodeKind::Const),
+                                          &var))
+                 {
+                     return false;
+                 }
+             }
+             if (head->isKind(ParseNodeKind::ForIn)) {
+-                return forIn(pn, head, var, stmt, dst);
++                return forIn(pn, updateOrIter, var, stmt, dst);
+             }
+-            return forOf(pn, head, var, stmt, dst);
++            return forOf(pn, updateOrIter, var, stmt, dst);
+         }
+ 
+         RootedValue init(cx), test(cx), update(cx);
+ 
+-        return forInit(head->pn_kid1, &init) &&
+-               optExpression(head->pn_kid2, &test) &&
+-               optExpression(head->pn_kid3, &update) &&
++        return forInit(initNode, &init) &&
++               optExpression(maybeTest, &test) &&
++               optExpression(updateOrIter, &update) &&
+                builder.forStatement(init, test, update, stmt, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Break:
+       case ParseNodeKind::Continue:
+       {
+         RootedValue label(cx);
+         RootedAtom pnAtom(cx, pn->pn_atom);
+@@ -2561,17 +2578,17 @@ ASTSerializer::statement(ParseNode* pn, 
+         return optExpression(pn->pn_kid, &arg) &&
+                builder.returnStatement(arg, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Debugger:
+         return builder.debuggerStatement(&pn->pn_pos, dst);
+ 
+       case ParseNodeKind::Class:
+-        return classDefinition(pn, false, dst);
++        return classDefinition(&pn->as<ClassNode>(), false, dst);
+ 
+       case ParseNodeKind::ClassMethodList:
+       {
+         ListNode* methodList = &pn->as<ListNode>();
+         NodeVector methods(cx);
+         if (!methods.reserve(methodList->count())) {
+             return false;
+         }
+@@ -2725,26 +2742,30 @@ ASTSerializer::expression(ParseNode* pn,
+       {
+         NodeVector exprs(cx);
+         return expressions(&pn->as<ListNode>(), exprs) &&
+                builder.sequenceExpression(exprs, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Conditional:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
++        ConditionalExpression* condNode = &pn->as<ConditionalExpression>();
++        ParseNode* testNode = condNode->kid1();
++        ParseNode* consNode = condNode->kid2();
++        ParseNode* altNode = condNode->kid3();
++        MOZ_ASSERT(condNode->pn_pos.encloses(testNode->pn_pos));
++        MOZ_ASSERT(condNode->pn_pos.encloses(consNode->pn_pos));
++        MOZ_ASSERT(condNode->pn_pos.encloses(altNode->pn_pos));
+ 
+         RootedValue test(cx), cons(cx), alt(cx);
+ 
+-        return expression(pn->pn_kid1, &test) &&
+-               expression(pn->pn_kid2, &cons) &&
+-               expression(pn->pn_kid3, &alt) &&
+-               builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst);
++        return expression(testNode, &test) &&
++               expression(consNode, &cons) &&
++               expression(altNode, &alt) &&
++               builder.conditionalExpression(test, cons, alt, &condNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Or:
+       case ParseNodeKind::And:
+         return leftAssociate(&pn->as<ListNode>(), dst);
+ 
+       case ParseNodeKind::PreIncrement:
+       case ParseNodeKind::PreDecrement:
+@@ -3083,17 +3104,17 @@ ASTSerializer::expression(ParseNode* pn,
+         MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+ 
+         RootedValue arg(cx);
+         return optExpression(pn->pn_kid, &arg) &&
+                builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Class:
+-        return classDefinition(pn, true, dst);
++        return classDefinition(&pn->as<ClassNode>(), true, dst);
+ 
+       case ParseNodeKind::NewTarget:
+       case ParseNodeKind::ImportMeta:
+       {
+         MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+         MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::PosHolder));
+         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -1299,33 +1299,35 @@ BytecodeEmitter::checkSideEffects(ParseN
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+         MOZ_ASSERT(pn->is<ListNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::If:
+       case ParseNodeKind::Conditional:
+-        MOZ_ASSERT(pn->isArity(PN_TERNARY));
+-        if (!checkSideEffects(pn->pn_kid1, answer)) {
++      {
++        TernaryNode* node = &pn->as<TernaryNode>();
++        if (!checkSideEffects(node->kid1(), answer)) {
+             return false;
+         }
+         if (*answer) {
+             return true;
+         }
+-        if (!checkSideEffects(pn->pn_kid2, answer)) {
++        if (!checkSideEffects(node->kid2(), answer)) {
+             return false;
+         }
+         if (*answer) {
+             return true;
+         }
+-        if ((pn = pn->pn_kid3)) {
++        if ((pn = node->kid3())) {
+             goto restart;
+         }
+         return true;
++      }
+ 
+       // Function calls can invoke non-local code.
+       case ParseNodeKind::New:
+       case ParseNodeKind::Call:
+       case ParseNodeKind::TaggedTemplate:
+       case ParseNodeKind::SuperCall:
+         MOZ_ASSERT(pn->isArity(PN_BINARY));
+         *answer = true;
+@@ -1343,17 +1345,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+         MOZ_ASSERT(pn->as<ListNode>().count() >= 2);
+         *answer = true;
+         return true;
+ 
+       // Classes typically introduce names.  Even if no name is introduced,
+       // the heritage and/or class body (through computed property names)
+       // usually have effects.
+       case ParseNodeKind::Class:
+-        MOZ_ASSERT(pn->isArity(PN_TERNARY));
++        MOZ_ASSERT(pn->is<ClassNode>());
+         *answer = true;
+         return true;
+ 
+       // |with| calls |ToObject| on its expression and so throws if that value
+       // is null/undefined.
+       case ParseNodeKind::With:
+         MOZ_ASSERT(pn->isArity(PN_BINARY));
+         *answer = true;
+@@ -1390,38 +1392,40 @@ BytecodeEmitter::checkSideEffects(ParseN
+         *answer = false;
+         return true;
+ 
+       case ParseNodeKind::Module:
+         *answer = false;
+         return true;
+ 
+       case ParseNodeKind::Try:
+-        MOZ_ASSERT(pn->isArity(PN_TERNARY));
+-        if (!checkSideEffects(pn->pn_kid1, answer)) {
++      {
++        TernaryNode* tryNode = &pn->as<TernaryNode>();
++        if (!checkSideEffects(tryNode->kid1(), answer)) {
+             return false;
+         }
+         if (*answer) {
+             return true;
+         }
+-        if (ParseNode* catchScope = pn->pn_kid2) {
++        if (ParseNode* catchScope = tryNode->kid2()) {
+             MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+             if (!checkSideEffects(catchScope, answer)) {
+                 return false;
+             }
+             if (*answer) {
+                 return true;
+             }
+         }
+-        if (ParseNode* finallyBlock = pn->pn_kid3) {
++        if (ParseNode* finallyBlock = tryNode->kid3()) {
+             if (!checkSideEffects(finallyBlock, answer)) {
+                 return false;
+             }
+         }
+         return true;
++      }
+ 
+       case ParseNodeKind::Catch:
+         MOZ_ASSERT(pn->isArity(PN_BINARY));
+         if (ParseNode* name = pn->pn_left) {
+             if (!checkSideEffects(name, answer)) {
+                 return false;
+             }
+             if (*answer) {
+@@ -4769,20 +4773,20 @@ BytecodeEmitter::emitCatch(ParseNode* pn
+ 
+     /* Emit the catch body. */
+     return emitTree(pn->pn_right);
+ }
+ 
+ // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See the
+ // comment on EmitSwitch.
+ MOZ_NEVER_INLINE bool
+-BytecodeEmitter::emitTry(ParseNode* pn)
+-{
+-    ParseNode* catchScope = pn->pn_kid2;
+-    ParseNode* finallyNode = pn->pn_kid3;
++BytecodeEmitter::emitTry(TernaryNode* tryNode)
++{
++    ParseNode* catchScope = tryNode->kid2();
++    ParseNode* finallyNode = tryNode->kid3();
+ 
+     TryEmitter::Kind kind;
+     if (catchScope) {
+         if (finallyNode) {
+             kind = TryEmitter::Kind::TryCatchFinally;
+         } else {
+             kind = TryEmitter::Kind::TryCatch;
+         }
+@@ -4791,17 +4795,17 @@ BytecodeEmitter::emitTry(ParseNode* pn)
+         kind = TryEmitter::Kind::TryFinally;
+     }
+     TryEmitter tryCatch(this, kind, TryEmitter::ControlKind::Syntactic);
+ 
+     if (!tryCatch.emitTry()) {
+         return false;
+     }
+ 
+-    if (!emitTree(pn->pn_kid1)) {
++    if (!emitTree(tryNode->kid1())) {
+         return false;
+     }
+ 
+     // If this try has a catch block, emit it.
+     if (catchScope) {
+         // The emitted code for a catch block looks like:
+         //
+         // [pushlexicalenv]             only if any local aliased
+@@ -4838,51 +4842,51 @@ BytecodeEmitter::emitTry(ParseNode* pn)
+     if (!tryCatch.emitEnd()) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitIf(ParseNode* pn)
++BytecodeEmitter::emitIf(TernaryNode* ifNode)
+ {
+     IfEmitter ifThenElse(this);
+ 
+-    if (!ifThenElse.emitIf(Some(pn->pn_pos.begin))) {
++    if (!ifThenElse.emitIf(Some(ifNode->pn_pos.begin))) {
+         return false;
+     }
+ 
+   if_again:
+     /* Emit code for the condition before pushing stmtInfo. */
+-    if (!emitTree(pn->pn_kid1)) {
+-        return false;
+-    }
+-
+-    ParseNode* elseNode = pn->pn_kid3;
++    if (!emitTree(ifNode->kid1())) {
++        return false;
++    }
++
++    ParseNode* elseNode = ifNode->kid3();
+     if (elseNode) {
+         if (!ifThenElse.emitThenElse()) {
+             return false;
+         }
+     } else {
+         if (!ifThenElse.emitThen()) {
+             return false;
+         }
+     }
+ 
+     /* Emit code for the then part. */
+-    if (!emitTree(pn->pn_kid2)) {
++    if (!emitTree(ifNode->kid2())) {
+         return false;
+     }
+ 
+     if (elseNode) {
+         if (elseNode->isKind(ParseNodeKind::If)) {
+-            pn = elseNode;
+-
+-            if (!ifThenElse.emitElseIf(Some(pn->pn_pos.begin))) {
++            ifNode = &elseNode->as<TernaryNode>();
++
++            if (!ifThenElse.emitElseIf(Some(ifNode->pn_pos.begin))) {
+                 return false;
+             }
+ 
+             goto if_again;
+         }
+ 
+         if (!ifThenElse.emitElse()) {
+             return false;
+@@ -5299,27 +5303,26 @@ BytecodeEmitter::emitSpread(bool allowSe
+     if (!emit2(JSOP_PICK, 4)) {                           // ARR FINAL_INDEX RESULT NEXT ITER
+         return false;
+     }
+ 
+     return emitPopN(3);                                   // ARR FINAL_INDEX
+ }
+ 
+ bool
+-BytecodeEmitter::emitInitializeForInOrOfTarget(ParseNode* forHead)
++BytecodeEmitter::emitInitializeForInOrOfTarget(TernaryNode* forHead)
+ {
+     MOZ_ASSERT(forHead->isKind(ParseNodeKind::ForIn) ||
+                forHead->isKind(ParseNodeKind::ForOf));
+-    MOZ_ASSERT(forHead->isArity(PN_TERNARY));
+ 
+     MOZ_ASSERT(this->stackDepth >= 1,
+                "must have a per-iteration value for initializing");
+ 
+-    ParseNode* target = forHead->pn_kid1;
+-    MOZ_ASSERT(!forHead->pn_kid2);
++    ParseNode* target = forHead->kid1();
++    MOZ_ASSERT(!forHead->kid2());
+ 
+     // If the for-in/of loop didn't have a variable declaration, per-loop
+     // initialization is just assigning the iteration value to a target
+     // expression.
+     if (!parser->astGenerator().isDeclarationList(target)) {
+         return emitAssignment(target, ParseNodeKind::Assign, nullptr); // ... ITERVAL
+     }
+ 
+@@ -5368,28 +5371,27 @@ BytecodeEmitter::emitInitializeForInOrOf
+ }
+ 
+ bool
+ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, const EmitterScope* headLexicalEmitterScope)
+ {
+     MOZ_ASSERT(forOfLoop->isKind(ParseNodeKind::For));
+     MOZ_ASSERT(forOfLoop->isArity(PN_BINARY));
+ 
+-    ParseNode* forOfHead = forOfLoop->pn_left;
++    TernaryNode* forOfHead = &forOfLoop->pn_left->as<TernaryNode>();
+     MOZ_ASSERT(forOfHead->isKind(ParseNodeKind::ForOf));
+-    MOZ_ASSERT(forOfHead->isArity(PN_TERNARY));
+ 
+     unsigned iflags = forOfLoop->pn_iflags;
+     IteratorKind iterKind = (iflags & JSITER_FORAWAITOF)
+                             ? IteratorKind::Async
+                             : IteratorKind::Sync;
+     MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox());
+     MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()->isAsync());
+ 
+-    ParseNode* forHeadExpr = forOfHead->pn_kid3;
++    ParseNode* forHeadExpr = forOfHead->kid3();
+ 
+     // Certain builtins (e.g. Array.from) are implemented in self-hosting
+     // as for-of loops.
+     bool allowSelfHostedIter = false;
+     if (emitterMode == BytecodeEmitter::SelfHosting &&
+         forHeadExpr->isKind(ParseNodeKind::Call) &&
+         forHeadExpr->pn_left->name() == cx->names().allowContentIter)
+     {
+@@ -5402,17 +5404,17 @@ BytecodeEmitter::emitForOf(ParseNode* fo
+         return false;
+     }
+ 
+     if (!emitTree(forHeadExpr)) {                         // ITERABLE
+         return false;
+     }
+ 
+     if (headLexicalEmitterScope) {
+-        DebugOnly<ParseNode*> forOfTarget = forOfHead->pn_kid1;
++        DebugOnly<ParseNode*> forOfTarget = forOfHead->kid1();
+         MOZ_ASSERT(forOfTarget->isKind(ParseNodeKind::Let) ||
+                    forOfTarget->isKind(ParseNodeKind::Const));
+     }
+ 
+     if (!forOf.emitInitialize(Some(forOfHead->pn_pos.begin))) {
+         return false;                                     // NEXT ITER VALUE
+     }
+ 
+@@ -5439,25 +5441,24 @@ BytecodeEmitter::emitForOf(ParseNode* fo
+ 
+ bool
+ BytecodeEmitter::emitForIn(ParseNode* forInLoop, const EmitterScope* headLexicalEmitterScope)
+ {
+     MOZ_ASSERT(forInLoop->isKind(ParseNodeKind::For));
+     MOZ_ASSERT(forInLoop->isArity(PN_BINARY));
+     MOZ_ASSERT(forInLoop->isOp(JSOP_ITER));
+ 
+-    ParseNode* forInHead = forInLoop->pn_left;
++    TernaryNode* forInHead = &forInLoop->pn_left->as<TernaryNode>();
+     MOZ_ASSERT(forInHead->isKind(ParseNodeKind::ForIn));
+-    MOZ_ASSERT(forInHead->isArity(PN_TERNARY));
+ 
+     ForInEmitter forIn(this, headLexicalEmitterScope);
+ 
+     // Annex B: Evaluate the var-initializer expression if present.
+     // |for (var i = initializer in expr) { ... }|
+-    ParseNode* forInTarget = forInHead->pn_kid1;
++    ParseNode* forInTarget = forInHead->kid1();
+     if (parser->astGenerator().isDeclarationList(forInTarget)) {
+         ParseNode* decl =
+             parser->astGenerator().singleBindingFromDeclaration(&forInTarget->as<ListNode>());
+         if (decl->isKind(ParseNodeKind::Name)) {
+             if (ParseNode* initializer = decl->expr()) {
+                 MOZ_ASSERT(forInTarget->isKind(ParseNodeKind::Var),
+                            "for-in initializers are only permitted for |var| declarations");
+ 
+@@ -5481,17 +5482,17 @@ BytecodeEmitter::emitForIn(ParseNode* fo
+         }
+     }
+ 
+     if (!forIn.emitIterated()) {                          //
+         return false;
+     }
+ 
+     // Evaluate the expression being iterated.
+-    ParseNode* expr = forInHead->pn_kid3;
++    ParseNode* expr = forInHead->kid3();
+     if (!emitTree(expr)) {                                // EXPR
+         return false;
+     }
+ 
+     MOZ_ASSERT(forInLoop->pn_iflags == 0);
+ 
+     MOZ_ASSERT_IF(headLexicalEmitterScope,
+                   forInTarget->isKind(ParseNodeKind::Let) ||
+@@ -5521,21 +5522,21 @@ BytecodeEmitter::emitForIn(ParseNode* fo
+ 
+     return true;
+ }
+ 
+ /* C-style `for (init; cond; update) ...` loop. */
+ bool
+ BytecodeEmitter::emitCStyleFor(ParseNode* pn, const EmitterScope* headLexicalEmitterScope)
+ {
+-    ParseNode* forHead = pn->pn_left;
++    TernaryNode* forHead = &pn->pn_left->as<TernaryNode>();
+     ParseNode* forBody = pn->pn_right;
+-    ParseNode* init = forHead->pn_kid1;
+-    ParseNode* cond = forHead->pn_kid2;
+-    ParseNode* update = forHead->pn_kid3;
++    ParseNode* init = forHead->kid1();
++    ParseNode* cond = forHead->kid2();
++    ParseNode* update = forHead->kid3();
+     bool isLet = init && init->isKind(ParseNodeKind::Let);
+ 
+     CForEmitter cfor(this, isLet ? headLexicalEmitterScope : nullptr);
+ 
+     if (!cfor.emitInit(init ? Some(init->pn_pos.begin) : Nothing())) {
+         return false;                                     //
+     }
+ 
+@@ -8630,25 +8631,21 @@ BytecodeEmitter::emitLexicalInitializati
+         return true;
+     };
+     return emitInitializeName(pn, assertLexical);
+ }
+ 
+ // This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
+ // (BindingClassDeclarationEvaluation).
+ bool
+-BytecodeEmitter::emitClass(ParseNode* pn)
+-{
+-    ClassNode& classNode = pn->as<ClassNode>();
+-
+-    ClassNames* names = classNode.names();
+-
+-    ParseNode* heritageExpression = classNode.heritage();
+-
+-    ListNode* classMethods = classNode.methodList();
++BytecodeEmitter::emitClass(ClassNode* classNode)
++{
++    ClassNames* names = classNode->names();
++    ParseNode* heritageExpression = classNode->heritage();
++    ListNode* classMethods = classNode->methodList();
+     ParseNode* constructor = nullptr;
+     for (ParseNode* mn : classMethods->contents()) {
+         ClassMethod& method = mn->as<ClassMethod>();
+         ParseNode& methodName = method.name();
+         if (!method.isStatic() &&
+             (methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
+              methodName.isKind(ParseNodeKind::String)) &&
+             methodName.pn_atom == cx->names().constructor)
+@@ -8660,17 +8657,17 @@ BytecodeEmitter::emitClass(ParseNode* pn
+ 
+     bool savedStrictness = sc->setLocalStrictMode(true);
+ 
+     Maybe<TDZCheckCache> tdzCache;
+     Maybe<EmitterScope> emitterScope;
+     if (names) {
+         tdzCache.emplace(this);
+         emitterScope.emplace(this);
+-        if (!emitterScope->enterLexical(this, ScopeKind::Lexical, classNode.scopeBindings())) {
++        if (!emitterScope->enterLexical(this, ScopeKind::Lexical, classNode->scopeBindings())) {
+             return false;
+         }
+     }
+ 
+     // Pseudocode for class declarations:
+     //
+     //     class extends BaseExpression {
+     //       constructor() { ... }
+@@ -8794,18 +8791,18 @@ BytecodeEmitter::emitClass(ParseNode* pn
+                 return false;
+             }
+         }
+     } else {
+         // In the case of default class constructors, emit the start and end
+         // offsets in the source buffer as source notes so that when we
+         // actually make the constructor during execution, we can give it the
+         // correct toString output.
+-        ptrdiff_t classStart = ptrdiff_t(pn->pn_pos.begin);
+-        ptrdiff_t classEnd = ptrdiff_t(pn->pn_pos.end);
++        ptrdiff_t classStart = ptrdiff_t(classNode->pn_pos.begin);
++        ptrdiff_t classEnd = ptrdiff_t(classNode->pn_pos.end);
+         if (!newSrcNote3(SRC_CLASS_SPAN, classStart, classEnd)) {
+             return false;
+         }
+ 
+         JSAtom *name = names ? names->innerBinding()->pn_atom : cx->names().empty;
+         if (heritageExpression) {
+             if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR)) {   // ... HOMEOBJ CONSTRUCTOR
+                 return false;
+@@ -8927,17 +8924,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::ParamsBody:
+         if (!emitFunctionFormalParametersAndBody(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::If:
+-        if (!emitIf(pn)) {
++        if (!emitIf(&pn->as<TernaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Switch:
+         if (!emitSwitch(&pn->as<SwitchStatement>())) {
+             return false;
+         }
+@@ -8985,17 +8982,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::With:
+         if (!emitWith(pn)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Try:
+-        if (!emitTry(pn)) {
++        if (!emitTry(&pn->as<TernaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Catch:
+         if (!emitCatch(pn)) {
+             return false;
+         }
+@@ -9334,17 +9331,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+             return false;
+         }
+         if (!emit1(JSOP_DEBUGGER)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Class:
+-        if (!emitClass(pn)) {
++        if (!emitClass(&pn->as<ClassNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::NewTarget:
+         if (!emit1(JSOP_NEWTARGET)) {
+             return false;
+         }
+diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
+--- a/js/src/frontend/BytecodeEmitter.h
++++ b/js/src/frontend/BytecodeEmitter.h
+@@ -675,25 +675,25 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     enum class EmitElemOption { Get, Call, IncDec, CompoundAssign, Ref };
+     MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts);
+ 
+     MOZ_MUST_USE bool emitElemOpBase(JSOp op);
+     MOZ_MUST_USE bool emitElemOp(ParseNode* pn, JSOp op);
+     MOZ_MUST_USE bool emitElemIncDec(ParseNode* pn);
+ 
+     MOZ_MUST_USE bool emitCatch(ParseNode* pn);
+-    MOZ_MUST_USE bool emitIf(ParseNode* pn);
++    MOZ_MUST_USE bool emitIf(TernaryNode* ifNode);
+     MOZ_MUST_USE bool emitWith(ParseNode* pn);
+ 
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(const LabeledStatement* pn);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn);
+     MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body,
+                                            EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* pn);
+-    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(ParseNode* pn);
++    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(TernaryNode* tryNode);
+ 
+     enum DestructuringFlavor {
+         // Destructuring into a declaration.
+         DestructuringDeclaration,
+ 
+         // Destructuring into a formal parameter, when the formal parameters
+         // contain an expression that might be evaluated, and thus require
+         // this destructuring to assign not into the innermost scope that
+@@ -820,17 +820,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_MUST_USE bool emitWhile(ParseNode* pn);
+ 
+     MOZ_MUST_USE bool emitFor(ParseNode* pn,
+                               const EmitterScope* headLexicalEmitterScope = nullptr);
+     MOZ_MUST_USE bool emitCStyleFor(ParseNode* pn, const EmitterScope* headLexicalEmitterScope);
+     MOZ_MUST_USE bool emitForIn(ParseNode* pn, const EmitterScope* headLexicalEmitterScope);
+     MOZ_MUST_USE bool emitForOf(ParseNode* pn, const EmitterScope* headLexicalEmitterScope);
+ 
+-    MOZ_MUST_USE bool emitInitializeForInOrOfTarget(ParseNode* forHead);
++    MOZ_MUST_USE bool emitInitializeForInOrOfTarget(TernaryNode* forHead);
+ 
+     MOZ_MUST_USE bool emitBreak(PropertyName* label);
+     MOZ_MUST_USE bool emitContinue(PropertyName* label);
+ 
+     MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ListNode* paramsBody);
+     MOZ_MUST_USE bool emitFunctionFormalParameters(ListNode* paramsBody);
+     MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
+     MOZ_MUST_USE bool emitFunctionBody(ParseNode* pn);
+@@ -841,17 +841,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     // emitSpread expects the current index (I) of the array, the array itself
+     // and the iterator to be on the stack in that order (iterator on the bottom).
+     // It will pop the iterator and I, then iterate over the iterator by calling
+     // |.next()| and put the results into the I-th element of array with
+     // incrementing I, then push the result I (it will be original I +
+     // iteration count). The stack after iteration will look like |ARRAY INDEX|.
+     MOZ_MUST_USE bool emitSpread(bool allowSelfHosted = false);
+ 
+-    MOZ_MUST_USE bool emitClass(ParseNode* pn);
++    MOZ_MUST_USE bool emitClass(ClassNode* classNode);
+     MOZ_MUST_USE bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
+     MOZ_MUST_USE bool emitSuperGetProp(ParseNode* pn, bool isCall = false);
+     MOZ_MUST_USE bool emitSuperElemOperands(ParseNode* pn,
+                                             EmitElemOption opts = EmitElemOption::Get);
+     MOZ_MUST_USE bool emitSuperGetElem(ParseNode* pn, bool isCall = false);
+ 
+     MOZ_MUST_USE bool emitCallee(ParseNode* callee, ParseNode* call, bool* callop);
+ 
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -80,17 +80,17 @@ ContainsHoistedDeclaration(JSContext* cx
+       case ParseNodeKind::Const:
+         MOZ_ASSERT(node->is<ListNode>());
+         *result = false;
+         return true;
+ 
+       // Similarly to the lexical declarations above, classes cannot add hoisted
+       // declarations
+       case ParseNodeKind::Class:
+-        MOZ_ASSERT(node->isArity(PN_TERNARY));
++        MOZ_ASSERT(node->is<ClassNode>());
+         *result = false;
+         return true;
+ 
+       // Function declarations *can* be hoisted declarations.  But in the
+       // magical world of the rewritten frontend, the declaration necessitated
+       // by a nested function statement, not at body level, doesn't require
+       // that we preserve an unreachable function declaration node against
+       // dead-code removal.
+@@ -158,65 +158,65 @@ ContainsHoistedDeclaration(JSContext* cx
+       case ParseNodeKind::Label:
+         return ContainsHoistedDeclaration(cx, node->pn_expr, result);
+ 
+       // Statements with more complicated structures.
+ 
+       // if-statement nodes may have hoisted declarations in their consequent
+       // and alternative components.
+       case ParseNodeKind::If: {
+-        MOZ_ASSERT(node->isArity(PN_TERNARY));
+-
+-        ParseNode* consequent = node->pn_kid2;
++        TernaryNode* ifNode = &node->as<TernaryNode>();
++        ParseNode* consequent = ifNode->kid2();
+         if (!ContainsHoistedDeclaration(cx, consequent, result)) {
+             return false;
+         }
+         if (*result) {
+             return true;
+         }
+ 
+-        if ((node = node->pn_kid3)) {
++        if ((node = ifNode->kid3())) {
+             goto restart;
+         }
+ 
+         *result = false;
+         return true;
+       }
+ 
+       // try-statements have statements to execute, and one or both of a
+       // catch-list and a finally-block.
+       case ParseNodeKind::Try: {
+-        MOZ_ASSERT(node->isArity(PN_TERNARY));
+-        MOZ_ASSERT(node->pn_kid2 || node->pn_kid3,
+-                   "must have either catch(es) or finally");
++        TernaryNode* tryNode = &node->as<TernaryNode>();
+ 
+-        ParseNode* tryBlock = node->pn_kid1;
++        MOZ_ASSERT(tryNode->kid2() || tryNode->kid3(),
++                   "must have either catch or finally");
++
++        ParseNode* tryBlock = tryNode->kid1();
+         if (!ContainsHoistedDeclaration(cx, tryBlock, result)) {
+             return false;
+         }
+         if (*result) {
+             return true;
+         }
+ 
+-        if (ParseNode* catchScope = node->pn_kid2) {
++        if (ParseNode* catchScope = tryNode->kid2()) {
+             MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+ 
+             ParseNode* catchNode = catchScope->pn_expr;
+             MOZ_ASSERT(catchNode->isKind(ParseNodeKind::Catch));
+ 
+             ParseNode* catchStatements = catchNode->pn_right;
+             if (!ContainsHoistedDeclaration(cx, catchStatements, result)) {
+                 return false;
+             }
+             if (*result) {
+                 return true;
+             }
+         }
+ 
+-        if (ParseNode* finallyBlock = node->pn_kid3) {
++        if (ParseNode* finallyBlock = tryNode->kid3()) {
+             return ContainsHoistedDeclaration(cx, finallyBlock, result);
+         }
+ 
+         *result = false;
+         return true;
+       }
+ 
+       // A switch node's left half is an expression; only its right half (a
+@@ -227,30 +227,28 @@ ContainsHoistedDeclaration(JSContext* cx
+         return ContainsHoistedDeclaration(cx, node->pn_right, result);
+ 
+       case ParseNodeKind::Case:
+         return ContainsHoistedDeclaration(cx, node->as<CaseClause>().statementList(), result);
+ 
+       case ParseNodeKind::For: {
+         MOZ_ASSERT(node->isArity(PN_BINARY));
+ 
+-        ParseNode* loopHead = node->pn_left;
++        TernaryNode* loopHead = &node->pn_left->as<TernaryNode>();
+         MOZ_ASSERT(loopHead->isKind(ParseNodeKind::ForHead) ||
+                    loopHead->isKind(ParseNodeKind::ForIn) ||
+                    loopHead->isKind(ParseNodeKind::ForOf));
+ 
+         if (loopHead->isKind(ParseNodeKind::ForHead)) {
+             // for (init?; cond?; update?), with only init possibly containing
+             // a hoisted declaration.  (Note: a lexical-declaration |init| is
+             // (at present) hoisted in SpiderMonkey parlance -- but such
+             // hoisting doesn't extend outside of this statement, so it is not
+             // hoisting in the sense meant by ContainsHoistedDeclaration.)
+-            MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
+-
+-            ParseNode* init = loopHead->pn_kid1;
++            ParseNode* init = loopHead->kid1();
+             if (init && init->isKind(ParseNodeKind::Var)) {
+                 *result = true;
+                 return true;
+             }
+         } else {
+             MOZ_ASSERT(loopHead->isKind(ParseNodeKind::ForIn) ||
+                        loopHead->isKind(ParseNodeKind::ForOf));
+ 
+@@ -259,19 +257,17 @@ ContainsHoistedDeclaration(JSContext* cx
+             //
+             //   -- or --
+             //
+             // for (target of ...), where only target may introduce hoisted
+             // declarations.
+             //
+             // Either way, if |target| contains a declaration, it's |loopHead|'s
+             // first kid.
+-            MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
+-
+-            ParseNode* decl = loopHead->pn_kid1;
++            ParseNode* decl = loopHead->kid1();
+             if (decl && decl->isKind(ParseNodeKind::Var)) {
+                 *result = true;
+                 return true;
+             }
+         }
+ 
+         ParseNode* loopBody = node->pn_right;
+         return ContainsHoistedDeclaration(cx, loopBody, result);
+@@ -822,55 +818,55 @@ FoldConditional(JSContext* cx, ParseNode
+ 
+     do {
+         // |nextNode| on entry points to the C?T:F expression to be folded.
+         // Reset it to exit the loop in the common case where F isn't another
+         // ?: expression.
+         nodePtr = nextNode;
+         nextNode = nullptr;
+ 
+-        ParseNode* node = *nodePtr;
++        TernaryNode* node = &(*nodePtr)->as<TernaryNode>();
+         MOZ_ASSERT(node->isKind(ParseNodeKind::Conditional));
+-        MOZ_ASSERT(node->isArity(PN_TERNARY));
+ 
+-        ParseNode*& expr = node->pn_kid1;
+-        if (!FoldCondition(cx, &expr, parser)) {
++        ParseNode** expr = node->unsafeKid1Reference();
++        if (!FoldCondition(cx, expr, parser)) {
+             return false;
+         }
+ 
+-        ParseNode*& ifTruthy = node->pn_kid2;
+-        if (!Fold(cx, &ifTruthy, parser)) {
++        ParseNode** ifTruthy = node->unsafeKid2Reference();
++        if (!Fold(cx, ifTruthy, parser)) {
+             return false;
+         }
+ 
+-        ParseNode*& ifFalsy = node->pn_kid3;
++        ParseNode** ifFalsy = node->unsafeKid3Reference();
+ 
+         // If our C?T:F node has F as another ?: node, *iteratively* constant-
+         // fold F *after* folding C and T (and possibly eliminating C and one
+         // of T/F entirely); otherwise fold F normally.  Making |nextNode| non-
+         // null causes this loop to run again to fold F.
+         //
+         // Conceivably we could instead/also iteratively constant-fold T, if T
+         // were more complex than F.  Such an optimization is unimplemented.
+-        if (ifFalsy->isKind(ParseNodeKind::Conditional)) {
+-            nextNode = &ifFalsy;
++        if ((*ifFalsy)->isKind(ParseNodeKind::Conditional)) {
++            MOZ_ASSERT((*ifFalsy)->is<TernaryNode>());
++            nextNode = ifFalsy;
+         } else {
+-            if (!Fold(cx, &ifFalsy, parser)) {
++            if (!Fold(cx, ifFalsy, parser)) {
+                 return false;
+             }
+         }
+ 
+         // Try to constant-fold based on the condition expression.
+-        Truthiness t = Boolish(expr);
++        Truthiness t = Boolish(*expr);
+         if (t == Unknown) {
+             continue;
+         }
+ 
+         // Otherwise reduce 'C ? T : F' to T or F as directed by C.
+-        ParseNode* replacement = t == Truthy ? ifTruthy : ifFalsy;
++        ParseNode* replacement = t == Truthy ? *ifTruthy : *ifFalsy;
+ 
+         // Otherwise perform a replacement.  This invalidates |nextNode|, so
+         // reset it (if the replacement requires folding) or clear it (if
+         // |ifFalsy| is dead code) as needed.
+         if (nextNode) {
+             nextNode = (*nextNode == replacement) ? nodePtr : nullptr;
+         }
+         ReplaceNode(nodePtr, replacement);
+@@ -885,63 +881,63 @@ FoldIf(JSContext* cx, ParseNode** nodePt
+     ParseNode** nextNode = nodePtr;
+ 
+     do {
+         // |nextNode| on entry points to the initial |if| to be folded.  Reset
+         // it to exit the loop when the |else| arm isn't another |if|.
+         nodePtr = nextNode;
+         nextNode = nullptr;
+ 
+-        ParseNode* node = *nodePtr;
++        TernaryNode* node = &(*nodePtr)->as<TernaryNode>();
+         MOZ_ASSERT(node->isKind(ParseNodeKind::If));
+-        MOZ_ASSERT(node->isArity(PN_TERNARY));
+ 
+-        ParseNode*& expr = node->pn_kid1;
+-        if (!FoldCondition(cx, &expr, parser)) {
++        ParseNode** expr = node->unsafeKid1Reference();
++        if (!FoldCondition(cx, expr, parser)) {
+             return false;
+         }
+ 
+-        ParseNode*& consequent = node->pn_kid2;
+-        if (!Fold(cx, &consequent, parser)) {
++        ParseNode** consequent = node->unsafeKid2Reference();
++        if (!Fold(cx, consequent, parser)) {
+             return false;
+         }
+ 
+-        ParseNode*& alternative = node->pn_kid3;
+-        if (alternative) {
++        ParseNode** alternative = node->unsafeKid3Reference();
++        if (*alternative) {
+             // If in |if (C) T; else F;| we have |F| as another |if|,
+             // *iteratively* constant-fold |F| *after* folding |C| and |T| (and
+             // possibly completely replacing the whole thing with |T| or |F|);
+             // otherwise fold F normally.  Making |nextNode| non-null causes
+             // this loop to run again to fold F.
+-            if (alternative->isKind(ParseNodeKind::If)) {
+-                nextNode = &alternative;
++            if ((*alternative)->isKind(ParseNodeKind::If)) {
++                MOZ_ASSERT((*alternative)->is<TernaryNode>());
++                nextNode = alternative;
+             } else {
+-                if (!Fold(cx, &alternative, parser)) {
++                if (!Fold(cx, alternative, parser)) {
+                     return false;
+                 }
+             }
+         }
+ 
+         // Eliminate the consequent or alternative if the condition has
+         // constant truthiness.
+-        Truthiness t = Boolish(expr);
++        Truthiness t = Boolish(*expr);
+         if (t == Unknown) {
+             continue;
+         }
+ 
+         // Careful!  Either of these can be null: |replacement| in |if (0) T;|,
+         // and |discarded| in |if (true) T;|.
+         ParseNode* replacement;
+         ParseNode* discarded;
+         if (t == Truthy) {
+-            replacement = consequent;
+-            discarded = alternative;
++            replacement = *consequent;
++            discarded = *alternative;
+         } else {
+-            replacement = alternative;
+-            discarded = consequent;
++            replacement = *alternative;
++            discarded = *consequent;
+         }
+ 
+         bool performReplacement = true;
+         if (discarded) {
+             // A declaration that hoists outside the discarded arm prevents the
+             // |if| from being folded away.
+             bool containsHoistedDecls;
+             if (!ContainsHoistedDeclaration(cx, discarded, &containsHoistedDecls)) {
+@@ -1181,34 +1177,35 @@ FoldReturn(JSContext* cx, ParseNode* nod
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldTry(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldTry(JSContext* cx, TernaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Try));
+-    MOZ_ASSERT(node->isArity(PN_TERNARY));
+ 
+-    ParseNode*& statements = node->pn_kid1;
+-    if (!Fold(cx, &statements, parser)) {
++    ParseNode** statements = node->unsafeKid1Reference();
++    if (!Fold(cx, statements, parser)) {
+         return false;
+     }
+ 
+-    if (ParseNode*& catchScope = node->pn_kid2) {
+-        if (!Fold(cx, &catchScope, parser)) {
++    ParseNode** catchScope = node->unsafeKid2Reference();
++    if (*catchScope) {
++        if (!Fold(cx, catchScope, parser)) {
+             return false;
+         }
+     }
+ 
+-    if (ParseNode*& finally = node->pn_kid3) {
+-        if (!Fold(cx, &finally, parser)) {
++    ParseNode** finally = node->unsafeKid3Reference();
++    if (*finally) {
++        if (!Fold(cx, finally, parser)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+@@ -1228,35 +1225,36 @@ FoldCatch(JSContext* cx, ParseNode* node
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldClass(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldClass(JSContext* cx, ClassNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Class));
+-    MOZ_ASSERT(node->isArity(PN_TERNARY));
+ 
+-    if (ParseNode*& classNames = node->pn_kid1) {
+-        if (!Fold(cx, &classNames, parser)) {
++    ParseNode** classNames = node->unsafeKid1Reference();
++    if (*classNames) {
++        if (!Fold(cx, classNames, parser)) {
+             return false;
+         }
+     }
+ 
+-    if (ParseNode*& heritage = node->pn_kid2) {
+-        if (!Fold(cx, &heritage, parser)) {
++    ParseNode** heritage = node->unsafeKid2Reference();
++    if (*heritage) {
++        if (!Fold(cx, heritage, parser)) {
+             return false;
+         }
+     }
+ 
+-    ParseNode*& body = node->pn_kid3;
+-    return Fold(cx, &body, parser);
++    ParseNode** body = node->unsafeKid3Reference();
++    return Fold(cx, body, parser);
+ }
+ 
+ static bool
+ FoldElement(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
+ {
+     ParseNode* node = *nodePtr;
+ 
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Elem));
+@@ -1519,51 +1517,52 @@ FoldArguments(JSContext* cx, ListNode* n
+         }
+     }
+ 
+     node->unsafeReplaceTail(listp);
+     return true;
+ }
+ 
+ static bool
+-FoldForInOrOf(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldForInOrOf(JSContext* cx, TernaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::ForIn) ||
+                node->isKind(ParseNodeKind::ForOf));
+-    MOZ_ASSERT(node->isArity(PN_TERNARY));
+-    MOZ_ASSERT(!node->pn_kid2);
++    MOZ_ASSERT(!node->kid2());
+ 
+-    return Fold(cx, &node->pn_kid1, parser) &&
+-           Fold(cx, &node->pn_kid3, parser);
++    return Fold(cx, node->unsafeKid1Reference(), parser) &&
++           Fold(cx, node->unsafeKid3Reference(), parser);
+ }
+ 
+ static bool
+-FoldForHead(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldForHead(JSContext* cx, TernaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::ForHead));
+-    MOZ_ASSERT(node->isArity(PN_TERNARY));
+ 
+-    if (ParseNode*& init = node->pn_kid1) {
+-        if (!Fold(cx, &init, parser)) {
++    ParseNode** init = node->unsafeKid1Reference();
++    if (*init) {
++        if (!Fold(cx, init, parser)) {
+             return false;
+         }
+     }
+ 
+-    if (ParseNode*& test = node->pn_kid2) {
+-        if (!FoldCondition(cx, &test, parser)) {
++    ParseNode** test = node->unsafeKid2Reference();
++    if (*test) {
++        if (!FoldCondition(cx, test, parser)) {
+             return false;
+         }
+ 
+-        if (test->isKind(ParseNodeKind::True)) {
+-            test = nullptr;
++        if ((*test)->isKind(ParseNodeKind::True)) {
++            (*test) = nullptr;
+         }
+     }
+ 
+-    if (ParseNode*& update = node->pn_kid3) {
+-        if (!Fold(cx, &update, parser)) {
++    ParseNode** update = node->unsafeKid3Reference();
++    if (*update) {
++        if (!Fold(cx, update, parser)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+@@ -1647,19 +1646,21 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+ 
+       case ParseNodeKind::DeleteElem:
+         return FoldDeleteElement(cx, pn, parser);
+ 
+       case ParseNodeKind::DeleteProp:
+         return FoldDeleteProperty(cx, pn, parser);
+ 
+       case ParseNodeKind::Conditional:
++        MOZ_ASSERT((*pnp)->is<TernaryNode>());
+         return FoldConditional(cx, pnp, parser);
+ 
+       case ParseNodeKind::If:
++        MOZ_ASSERT((*pnp)->is<TernaryNode>());
+         return FoldIf(cx, pnp, parser);
+ 
+       case ParseNodeKind::Not:
+         return FoldNot(cx, pn, parser);
+ 
+       case ParseNodeKind::BitNot:
+       case ParseNodeKind::Pos:
+       case ParseNodeKind::Neg:
+@@ -1766,23 +1767,23 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+             return true;
+         }
+         return Fold(cx, &pn->pn_kid, parser);
+ 
+       case ParseNodeKind::Return:
+         return FoldReturn(cx, pn, parser);
+ 
+       case ParseNodeKind::Try:
+-        return FoldTry(cx, pn, parser);
++        return FoldTry(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::Catch:
+         return FoldCatch(cx, pn, parser);
+ 
+       case ParseNodeKind::Class:
+-        return FoldClass(cx, pn, parser);
++        return FoldClass(cx, &pn->as<ClassNode>(), parser);
+ 
+       case ParseNodeKind::Elem:
+         return FoldElement(cx, pnp, parser);
+ 
+       case ParseNodeKind::Add:
+         MOZ_ASSERT((*pnp)->is<ListNode>());
+         return FoldAdd(cx, pnp, parser);
+ 
+@@ -1867,20 +1868,20 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+ 
+       case ParseNodeKind::With:
+         MOZ_ASSERT(pn->isArity(PN_BINARY));
+         return Fold(cx, &pn->pn_left, parser) &&
+                Fold(cx, &pn->pn_right, parser);
+ 
+       case ParseNodeKind::ForIn:
+       case ParseNodeKind::ForOf:
+-        return FoldForInOrOf(cx, pn, parser);
++        return FoldForInOrOf(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::ForHead:
+-        return FoldForHead(cx, pn, parser);
++        return FoldForHead(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::Label:
+         MOZ_ASSERT(pn->isArity(PN_NAME));
+         return Fold(cx, &pn->pn_expr, parser);
+ 
+       case ParseNodeKind::PropertyName:
+         MOZ_CRASH("unreachable, handled by ::Dot");
+ 
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -198,17 +198,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     ParseNode* newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) {
+         ObjectBox* objbox = boxer.newObjectBox(reobj);
+         if (!objbox) {
+             return null();
+         }
+         return new_<RegExpLiteral>(objbox, pos);
+     }
+ 
+-    ParseNode* newConditional(ParseNode* cond, ParseNode* thenExpr, ParseNode* elseExpr) {
++    ConditionalExpressionType newConditional(Node cond, Node thenExpr, Node elseExpr) {
+         return new_<ConditionalExpression>(cond, thenExpr, elseExpr);
+     }
+ 
+     ParseNode* newDelete(uint32_t begin, ParseNode* expr) {
+         if (expr->isKind(ParseNodeKind::Name)) {
+             expr->setOp(JSOP_DELNAME);
+             return newUnary(ParseNodeKind::DeleteName, begin, expr);
+         }
+@@ -315,19 +315,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) {
+         return new_<BinaryNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args);
+     }
+ 
+     ListNodeType newObjectLiteral(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::Object, TokenPos(begin, begin + 1));
+     }
+ 
+-    ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock,
+-                        const TokenPos& pos)
+-    {
++    ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) {
+         return new_<ClassNode>(name, heritage, methodBlock, pos);
+     }
+     ListNodeType newClassMethodList(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::ClassMethodList, TokenPos(begin, begin + 1));
+     }
+     ParseNode* newClassNames(ParseNode* outer, ParseNode* inner, const TokenPos& pos) {
+         return new_<ClassNames>(outer, inner, pos);
+     }
+@@ -598,59 +596,53 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+ 
+     ParseNode* newExprStatement(ParseNode* expr, uint32_t end) {
+         MOZ_ASSERT(expr->pn_pos.end <= end);
+         return new_<UnaryNode>(ParseNodeKind::ExpressionStatement,
+                                TokenPos(expr->pn_pos.begin, end), expr);
+     }
+ 
+-    ParseNode* newIfStatement(uint32_t begin, ParseNode* cond, ParseNode* thenBranch,
+-                              ParseNode* elseBranch)
+-    {
+-        ParseNode* pn = new_<TernaryNode>(ParseNodeKind::If, cond, thenBranch, elseBranch);
+-        if (!pn) {
+-            return null();
++    TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch, Node elseBranch) {
++        TernaryNode* node = new_<TernaryNode>(ParseNodeKind::If, cond, thenBranch, elseBranch);
++        if (!node) {
++            return nullptr;
+         }
+-        pn->pn_pos.begin = begin;
+-        return pn;
++        node->pn_pos.begin = begin;
++        return node;
+     }
+ 
+     ParseNode* newDoWhileStatement(ParseNode* body, ParseNode* cond, const TokenPos& pos) {
+         return new_<BinaryNode>(ParseNodeKind::DoWhile, JSOP_NOP, pos, body, cond);
+     }
+ 
+     ParseNode* newWhileStatement(uint32_t begin, ParseNode* cond, ParseNode* body) {
+         TokenPos pos(begin, body->pn_pos.end);
+         return new_<BinaryNode>(ParseNodeKind::While, JSOP_NOP, pos, cond, body);
+     }
+ 
+-    ParseNode* newForStatement(uint32_t begin, ParseNode* forHead, ParseNode* body,
+-                               unsigned iflags)
+-    {
++    Node newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) {
+         /* A FOR node is binary, left is loop control and right is the body. */
+         JSOp op = forHead->isKind(ParseNodeKind::ForIn) ? JSOP_ITER : JSOP_NOP;
+         BinaryNode* pn = new_<BinaryNode>(ParseNodeKind::For, op,
+                                           TokenPos(begin, body->pn_pos.end),
+                                           forHead, body);
+         if (!pn) {
+             return null();
+         }
+         pn->pn_iflags = iflags;
+         return pn;
+     }
+ 
+-    ParseNode* newForHead(ParseNode* init, ParseNode* test, ParseNode* update,
+-                          const TokenPos& pos)
+-    {
++    TernaryNodeType newForHead(Node init, Node test, Node update, const TokenPos& pos) {
+         return new_<TernaryNode>(ParseNodeKind::ForHead, init, test, update, pos);
+     }
+ 
+-    ParseNode* newForInOrOfHead(ParseNodeKind kind, ParseNode* target, ParseNode* iteratedExpr,
+-                                const TokenPos& pos)
++    TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target, Node iteratedExpr,
++                                     const TokenPos& pos)
+     {
+         MOZ_ASSERT(kind == ParseNodeKind::ForIn || kind == ParseNodeKind::ForOf);
+         return new_<TernaryNode>(kind, target, nullptr, iteratedExpr, pos);
+     }
+ 
+     ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant,
+                                   ParseNode* lexicalForCaseList, bool hasDefault)
+     {
+@@ -687,18 +679,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         return new_<LabeledStatement>(label, stmt, begin);
+     }
+ 
+     ParseNode* newThrowStatement(ParseNode* expr, const TokenPos& pos) {
+         MOZ_ASSERT(pos.encloses(expr->pn_pos));
+         return new_<UnaryNode>(ParseNodeKind::Throw, pos, expr);
+     }
+ 
+-    ParseNode* newTryStatement(uint32_t begin, ParseNode* body, ParseNode* catchScope,
+-                               ParseNode* finallyBlock) {
++    TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope,
++                                    Node finallyBlock)
++    {
+         TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
+         return new_<TernaryNode>(ParseNodeKind::Try, body, catchScope, finallyBlock, pos);
+     }
+ 
+     ParseNode* newDebuggerStatement(const TokenPos& pos) {
+         return new_<DebuggerStatement>(pos);
+     }
+ 
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -594,131 +594,140 @@ class NameResolver
+             if (!resolve(cur->pn_left, prefix)) {
+                 return false;
+             }
+             MOZ_ASSERT_IF(!cur->isKind(ParseNodeKind::ExportDefault),
+                           cur->pn_right->isKind(ParseNodeKind::String));
+             break;
+ 
+           // Ternary nodes with three expression children.
+-          case ParseNodeKind::Conditional:
+-            MOZ_ASSERT(cur->isArity(PN_TERNARY));
+-            if (!resolve(cur->pn_kid1, prefix)) {
++          case ParseNodeKind::Conditional: {
++            TernaryNode* condNode = &cur->as<TernaryNode>();
++            if (!resolve(condNode->kid1(), prefix)) {
+                 return false;
+             }
+-            if (!resolve(cur->pn_kid2, prefix)) {
++            if (!resolve(condNode->kid2(), prefix)) {
+                 return false;
+             }
+-            if (!resolve(cur->pn_kid3, prefix)) {
++            if (!resolve(condNode->kid3(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+           // The first part of a for-in/of is the declaration in the loop (or
+           // null if no declaration).  The latter two parts are the location
+           // assigned each loop and the value being looped over; obviously,
+           // either might contain functions to name.  Declarations may (through
+           // computed property names, and possibly through [deprecated!]
+           // initializers) also contain functions to name.
+           case ParseNodeKind::ForIn:
+-          case ParseNodeKind::ForOf:
+-            MOZ_ASSERT(cur->isArity(PN_TERNARY));
+-            if (!resolve(cur->pn_kid1, prefix)) {
++          case ParseNodeKind::ForOf: {
++            TernaryNode* forHead = &cur->as<TernaryNode>();
++            if (!resolve(forHead->kid1(), prefix)) {
+                 return false;
+             }
+-            MOZ_ASSERT(!cur->pn_kid2);
+-            if (!resolve(cur->pn_kid3, prefix)) {
++            MOZ_ASSERT(!forHead->kid2());
++            if (!resolve(forHead->kid3(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+           // Every part of a for(;;) head may contain a function needing name
+           // resolution.
+-          case ParseNodeKind::ForHead:
+-            MOZ_ASSERT(cur->isArity(PN_TERNARY));
+-            if (ParseNode* init = cur->pn_kid1) {
++          case ParseNodeKind::ForHead: {
++            TernaryNode* forHead = &cur->as<TernaryNode>();
++            if (ParseNode* init = forHead->kid1()) {
+                 if (!resolve(init, prefix)) {
+                     return false;
+                 }
+             }
+-            if (ParseNode* cond = cur->pn_kid2) {
++            if (ParseNode* cond = forHead->kid2()) {
+                 if (!resolve(cond, prefix)) {
+                     return false;
+                 }
+             }
+-            if (ParseNode* step = cur->pn_kid3) {
+-                if (!resolve(step, prefix)) {
++            if (ParseNode* update = forHead->kid3()) {
++                if (!resolve(update, prefix)) {
+                     return false;
+                 }
+             }
+             break;
++          }
+ 
+           // The first child of a class is a pair of names referring to it,
+           // inside and outside the class.  The second is the class's heritage,
+           // if any.  The third is the class body.
+-          case ParseNodeKind::Class:
+-            MOZ_ASSERT(cur->isArity(PN_TERNARY));
+-            MOZ_ASSERT_IF(cur->pn_kid1, cur->pn_kid1->isKind(ParseNodeKind::ClassNames));
+-            MOZ_ASSERT_IF(cur->pn_kid1, cur->pn_kid1->isArity(PN_BINARY));
+-            MOZ_ASSERT_IF(cur->pn_kid1 && cur->pn_kid1->pn_left,
+-                          cur->pn_kid1->pn_left->isKind(ParseNodeKind::Name));
+-            MOZ_ASSERT_IF(cur->pn_kid1 && cur->pn_kid1->pn_left,
+-                          !cur->pn_kid1->pn_left->expr());
+-            MOZ_ASSERT_IF(cur->pn_kid1, cur->pn_kid1->pn_right->isKind(ParseNodeKind::Name));
+-            MOZ_ASSERT_IF(cur->pn_kid1, !cur->pn_kid1->pn_right->expr());
+-            if (cur->pn_kid2) {
+-                if (!resolve(cur->pn_kid2, prefix)) {
++          case ParseNodeKind::Class: {
++            ClassNode* classNode = &cur->as<ClassNode>();
++#ifdef DEBUG
++            if (classNode->names()) {
++                ParseNode* name = classNode->names();
++                MOZ_ASSERT(name->isKind(ParseNodeKind::ClassNames));
++                MOZ_ASSERT(name->isArity(PN_BINARY));
++                MOZ_ASSERT_IF(name->pn_left, name->pn_left->isKind(ParseNodeKind::Name));
++                MOZ_ASSERT_IF(name->pn_left, !name->pn_left->expr());
++                MOZ_ASSERT(name->pn_right->isKind(ParseNodeKind::Name));
++                MOZ_ASSERT(!name->pn_right->expr());
++            }
++#endif
++            if (ParseNode* heritage = classNode->heritage()) {
++                if (!resolve(heritage, prefix)) {
+                     return false;
+                 }
+             }
+-            if (!resolve(cur->pn_kid3, prefix)) {
++            if (!resolve(classNode->methodList(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+           // The condition and consequent are non-optional, but the alternative
+           // might be omitted.
+-          case ParseNodeKind::If:
+-            MOZ_ASSERT(cur->isArity(PN_TERNARY));
+-            if (!resolve(cur->pn_kid1, prefix)) {
++          case ParseNodeKind::If: {
++            TernaryNode* ifNode = &cur->as<TernaryNode>();
++            if (!resolve(ifNode->kid1(), prefix)) {
+                 return false;
+             }
+-            if (!resolve(cur->pn_kid2, prefix)) {
++            if (!resolve(ifNode->kid2(), prefix)) {
+                 return false;
+             }
+-            if (cur->pn_kid3) {
+-                if (!resolve(cur->pn_kid3, prefix)) {
++            if (ParseNode* alternative = ifNode->kid3()) {
++                if (!resolve(alternative, prefix)) {
+                     return false;
+                 }
+             }
+             break;
++          }
+ 
+           // The statements in the try-block are mandatory.  The catch-blocks
+           // and finally block are optional (but at least one or the other must
+           // be present).
+-          case ParseNodeKind::Try:
+-            MOZ_ASSERT(cur->isArity(PN_TERNARY));
+-            if (!resolve(cur->pn_kid1, prefix)) {
++          case ParseNodeKind::Try: {
++            TernaryNode* tryNode = &cur->as<TernaryNode>();
++            if (!resolve(tryNode->kid1(), prefix)) {
+                 return false;
+             }
+-            MOZ_ASSERT(cur->pn_kid2 || cur->pn_kid3);
+-            if (ParseNode* catchScope = cur->pn_kid2) {
++            MOZ_ASSERT(tryNode->kid2() || tryNode->kid3());
++            if (ParseNode* catchScope = tryNode->kid2()) {
+                 MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+                 MOZ_ASSERT(catchScope->scopeBody()->isKind(ParseNodeKind::Catch));
+                 MOZ_ASSERT(catchScope->scopeBody()->isArity(PN_BINARY));
+                 if (!resolve(catchScope->scopeBody(), prefix)) {
+                     return false;
+                 }
+             }
+-            if (ParseNode* finallyBlock = cur->pn_kid3) {
++            if (ParseNode* finallyBlock = tryNode->kid3()) {
+                 if (!resolve(finallyBlock, prefix)) {
+                     return false;
+                 }
+             }
+             break;
++          }
+ 
+           // The first child, the catch-pattern, may contain functions via
+           // computed property names.  The optional catch-conditions may
+           // contain any expression.  The catch statements, of course, may
+           // contain arbitrary expressions.
+           case ParseNodeKind::Catch:
+             MOZ_ASSERT(cur->isArity(PN_BINARY));
+             if (cur->pn_left) {
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -150,17 +150,17 @@ ParseNode::dump(GenericPrinter& out, int
+         break;
+       case PN_UNARY:
+         ((UnaryNode*) this)->dump(out, indent);
+         break;
+       case PN_BINARY:
+         ((BinaryNode*) this)->dump(out, indent);
+         break;
+       case PN_TERNARY:
+-        ((TernaryNode*) this)->dump(out, indent);
++        as<TernaryNode>().dump(out, indent);
+         break;
+       case PN_CODE:
+         ((CodeNode*) this)->dump(out, indent);
+         break;
+       case PN_LIST:
+         as<ListNode>().dump(out, indent);
+         break;
+       case PN_NAME:
+@@ -248,21 +248,21 @@ BinaryNode::dump(GenericPrinter& out, in
+ }
+ 
+ void
+ TernaryNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s ", name);
+     indent += strlen(name) + 2;
+-    DumpParseTree(pn_kid1, out, indent);
++    DumpParseTree(kid1(), out, indent);
+     IndentNewLine(out, indent);
+-    DumpParseTree(pn_kid2, out, indent);
++    DumpParseTree(kid2(), out, indent);
+     IndentNewLine(out, indent);
+-    DumpParseTree(pn_kid3, out, indent);
++    DumpParseTree(kid3(), out, indent);
+     out.printf(")");
+ }
+ 
+ void
+ CodeNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s ", name);
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -243,50 +243,73 @@ IsTypeofKind(ParseNodeKind kind)
+  *               pn_expr: Array or Object for BindingPattern without
+  *                        Initializer, Assign for BindingPattern with
+  *                        Initializer
+  *         followed by either:
+  *           * StatementList node for function body statements
+  *           * Return for expression closure
+  *   count: number of formal parameters + 1
+  * Spread   unary       pn_kid: expression being spread
++ * Class (ClassNode)
++ *   kid1: ClassNames for class name. can be null for anonymous class.
++ *   kid2: expression after `extends`. null if no expression
++ *   kid3: either of
++ *           * ClassMethodList, if anonymous class
++ *           * LexicalScopeNode which contains ClassMethodList as scopeBody,
++ *             if named class
++ * ClassNames (ClassNames)
++ *   pn_left: Name node for outer binding. can be null
++ *   pn_right: Name node for inner binding
++ * ClassMethodList (ListNode)
++ *   head: list of N ClassMethod nodes
++ *   count: N >= 0
++ * ClassMethod (ClassMethod)
++ *   name: propertyName
++ *   method: methodDefinition
+  *
+  * <Statements>
+  * StatementList (ListNode)
+  *   head: list of N statements
+  *   count: N >= 0
+- * If       ternary     pn_kid1: cond, pn_kid2: then, pn_kid3: else or null.
++ * If (TernaryNode)
++ *   kid1: cond
++ *   kid2: then
++ *   kid3: else or null
+  * Switch   binary      pn_left: discriminant
+  *                      pn_right: LexicalScope node that contains the list
+  *                        of Case nodes, with at most one
+  *                        default node.
+  *                      hasDefault: true if there's a default case
+  * Case     binary      pn_left: case-expression if CaseClause, or
+  *                            null if DefaultClause
+  *                          pn_right: StatementList node for this case's
+  *                            statements
+  * While    binary      pn_left: cond, pn_right: body
+  * DoWhile  binary      pn_left: body, pn_right: cond
+  * For      binary      pn_left: either ForIn (for-in statement),
+  *                            ForOf (for-of) or ForHead (for(;;))
+  *                          pn_right: body
+- * ForIn    ternary     pn_kid1: declaration or expression to left of 'in'
+- *                          pn_kid2: null
+- *                          pn_kid3: object expr to right of 'in'
+- * ForOf    ternary     pn_kid1: declaration or expression to left of 'of'
+- *                          pn_kid2: null
+- *                          pn_kid3: expr to right of 'of'
+- * ForHead  ternary     pn_kid1:  init expr before first ';' or nullptr
+- *                          pn_kid2:  cond expr before second ';' or nullptr
+- *                          pn_kid3:  update expr after second ';' or nullptr
++ * ForIn (TernaryNode)
++ *   kid1: declaration or expression to left of 'in'
++ *   kid2: null
++ *   kid3: object expr to right of 'in'
++ * ForOf (TernaryNode)
++ *   kid1: declaration or expression to left of 'of'
++ *   kid2: null
++ *   kid3: expr to right of 'of'
++ * ForHead (TernaryNode)
++ *   kid1:  init expr before first ';' or nullptr
++ *   kid2:  cond expr before second ';' or nullptr
++ *   kid3:  update expr after second ';' or nullptr
+  * Throw    unary       pn_kid: exception
+- * Try      ternary     pn_kid1: try block
+- *                          pn_kid2: null or LexicalScope for catch-block
+- *                                   with pn_expr pointing to a Catch node
+- *                          pn_kid3: null or finally block
++ * Try (TernaryNode)
++ *   kid1: try block
++ *   kid2: null or LexicalScope for catch-block with scopeBody pointing to a
++ *         Catch node
++ *   kid3: null or finally block
+  * Catch    binary      pn_left: Name, Array, or Object catch
+  *                                   var node
+  *                                   (Array or Object if destructuring),
+  *                                   or null if optional catch binding
+  *                          pn_right: catch block statements
+  * Break    name        pn_atom: label or null
+  * Continue name        pn_atom: label or null
+  * With     binary      pn_left: head expr; pn_right: body;
+@@ -339,18 +362,21 @@ IsTypeofKind(ParseNodeKind kind)
+  * BitAndAssign,
+  * LshAssign,
+  * RshAssign,
+  * UrshAssign,
+  * MulAssign,
+  * DivAssign,
+  * ModAssign,
+  * PowAssign
+- * Conditional ternary  (cond ? trueExpr : falseExpr)
+- *                          pn_kid1: cond, pn_kid2: then, pn_kid3: else
++ * Conditional (ConditionalExpression)
++ *   (cond ? thenExpr : elseExpr)
++ *   kid1: cond
++ *   kid2: thenExpr
++ *   kid3: elseExpr
+  * Pipeline, Or, And, BitOr, BitXor, BitAnd, StrictEq, Eq, StrictNe, Ne,
+  * Lt, Le, Gt, Ge, InstanceOf, In, Lsh, Rsh, Ursh, Add, Sub, Star, Div, Mod,
+  * Pow (ListNode)
+  *   head: list of N subexpressions
+  *         All of these operators are left-associative except Pow which is
+  *         right-associative, but still forms a list (see comments in
+  *         ParseNode::appendOrCreateList).
+  *   count: N >= 2
+@@ -454,22 +480,25 @@ enum ParseNodeArity
+     PN_CODE,                            /* module or function definition node */
+     PN_LIST,                            /* generic singly linked list */
+     PN_NAME,                            /* name, label, or regexp */
+     PN_SCOPE                            /* lexical scope */
+ };
+ 
+ #define FOR_EACH_PARSENODE_SUBCLASS(macro) \
+     macro(ListNode, ListNodeType, asList) \
+-    macro(CallSiteNode, CallSiteNodeType, asCallSite)
++    macro(CallSiteNode, CallSiteNodeType, asCallSite) \
++    \
++    macro(TernaryNode, TernaryNodeType, asTernary) \
++    macro(ClassNode, ClassNodeType, asClass) \
++    macro(ConditionalExpression, ConditionalExpressionType, asConditionalExpression)
+ 
+ class LoopControlStatement;
+ class BreakStatement;
+ class ContinueStatement;
+-class ConditionalExpression;
+ class PropertyAccess;
+ 
+ #define DECLARE_CLASS(typeName, longTypeName, asMethodName) \
+ class typeName;
+ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
+ #undef DECLARE_CLASS
+ 
+ class ParseNode
+@@ -564,16 +593,18 @@ class ParseNode
+           private:
+             friend class ListNode;
+             ParseNode*  head;           /* first node in list */
+             ParseNode** tail;           /* ptr to last node's pn_next in list */
+             uint32_t    count;          /* number of nodes in list */
+             uint32_t    xflags;         /* see ListNode class */
+         } list;
+         struct {                        /* ternary: if, for(;;), ?: */
++          private:
++            friend class TernaryNode;
+             ParseNode*  kid1;           /* condition, discriminant, etc. */
+             ParseNode*  kid2;           /* then-part, case list, etc. */
+             ParseNode*  kid3;           /* else-part, default case, etc. */
+         } ternary;
+         struct {                        /* two kids if binary */
+             ParseNode*  left;
+             ParseNode*  right;
+             union {
+@@ -608,19 +639,16 @@ class ParseNode
+             friend class LoopControlStatement;
+             PropertyName*    label;    /* target of break/continue statement */
+         } loopControl;
+     } pn_u;
+ 
+ #define pn_objbox       pn_u.name.objbox
+ #define pn_funbox       pn_u.name.funbox
+ #define pn_body         pn_u.name.expr
+-#define pn_kid1         pn_u.ternary.kid1
+-#define pn_kid2         pn_u.ternary.kid2
+-#define pn_kid3         pn_u.ternary.kid3
+ #define pn_left         pn_u.binary.left
+ #define pn_right        pn_u.binary.right
+ #define pn_pval         pn_u.binary.pval
+ #define pn_iflags       pn_u.binary.iflags
+ #define pn_kid          pn_u.unary.kid
+ #define pn_prologue     pn_u.unary.prologue
+ #define pn_atom         pn_u.name.atom
+ #define pn_objbox       pn_u.name.objbox
+@@ -813,40 +841,66 @@ struct BinaryNode : public ParseNode
+         pn_right = right;
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
+ };
+ 
+-struct TernaryNode : public ParseNode
++class TernaryNode : public ParseNode
+ {
++  public:
+     TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3)
+-      : ParseNode(kind, JSOP_NOP, PN_TERNARY,
+-                  TokenPos((kid1 ? kid1 : kid2 ? kid2 : kid3)->pn_pos.begin,
+-                           (kid3 ? kid3 : kid2 ? kid2 : kid1)->pn_pos.end))
+-    {
+-        pn_kid1 = kid1;
+-        pn_kid2 = kid2;
+-        pn_kid3 = kid3;
+-    }
++      : TernaryNode(kind, kid1, kid2, kid3,
++                    TokenPos((kid1 ? kid1 : kid2 ? kid2 : kid3)->pn_pos.begin,
++                             (kid3 ? kid3 : kid2 ? kid2 : kid1)->pn_pos.end))
++    {}
+ 
+     TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3,
+                 const TokenPos& pos)
+       : ParseNode(kind, JSOP_NOP, PN_TERNARY, pos)
+     {
+-        pn_kid1 = kid1;
+-        pn_kid2 = kid2;
+-        pn_kid3 = kid3;
++        pn_u.ternary.kid1 = kid1;
++        pn_u.ternary.kid2 = kid2;
++        pn_u.ternary.kid3 = kid3;
++    }
++
++    static bool test(const ParseNode& node) {
++        return node.isArity(PN_TERNARY);
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
++
++    ParseNode* kid1() const {
++        return pn_u.ternary.kid1;
++    }
++
++    ParseNode* kid2() const {
++        return pn_u.ternary.kid2;
++    }
++
++    ParseNode* kid3() const {
++        return pn_u.ternary.kid3;
++    }
++
++    // Methods used by FoldConstants.cpp.
++    ParseNode** unsafeKid1Reference() {
++        return &pn_u.ternary.kid1;
++    }
++
++    ParseNode** unsafeKid2Reference() {
++        return &pn_u.ternary.kid2;
++    }
++
++    ParseNode** unsafeKid3Reference() {
++        return &pn_u.ternary.kid3;
++    }
+ };
+ 
+ class ListNode : public ParseNode
+ {
+   private:
+     // xflags bits.
+ 
+     // Statement list has top-level function statements.
+@@ -1332,46 +1386,43 @@ class ContinueStatement : public LoopCon
+ class DebuggerStatement : public ParseNode
+ {
+   public:
+     explicit DebuggerStatement(const TokenPos& pos)
+       : ParseNode(ParseNodeKind::Debugger, JSOP_NOP, PN_NULLARY, pos)
+     { }
+ };
+ 
+-class ConditionalExpression : public ParseNode
++class ConditionalExpression : public TernaryNode
+ {
+   public:
+     ConditionalExpression(ParseNode* condition, ParseNode* thenExpr, ParseNode* elseExpr)
+-      : ParseNode(ParseNodeKind::Conditional, JSOP_NOP, PN_TERNARY,
+-                  TokenPos(condition->pn_pos.begin, elseExpr->pn_pos.end))
++      : TernaryNode(ParseNodeKind::Conditional, condition, thenExpr, elseExpr,
++                    TokenPos(condition->pn_pos.begin, elseExpr->pn_pos.end))
+     {
+         MOZ_ASSERT(condition);
+         MOZ_ASSERT(thenExpr);
+         MOZ_ASSERT(elseExpr);
+-        pn_u.ternary.kid1 = condition;
+-        pn_u.ternary.kid2 = thenExpr;
+-        pn_u.ternary.kid3 = elseExpr;
+     }
+ 
+     ParseNode& condition() const {
+-        return *pn_u.ternary.kid1;
++        return *kid1();
+     }
+ 
+     ParseNode& thenExpression() const {
+-        return *pn_u.ternary.kid2;
++        return *kid2();
+     }
+ 
+     ParseNode& elseExpression() const {
+-        return *pn_u.ternary.kid3;
++        return *kid3();
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Conditional);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_TERNARY));
++        MOZ_ASSERT_IF(match, node.is<TernaryNode>());
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
+ };
+ 
+ class ThisLiteral : public UnaryNode
+ {
+   public:
+@@ -1599,51 +1650,55 @@ struct ClassNames : public BinaryNode {
+     ParseNode* outerBinding() const {
+         return pn_u.binary.left;
+     }
+     ParseNode* innerBinding() const {
+         return pn_u.binary.right;
+     }
+ };
+ 
+-struct ClassNode : public TernaryNode {
++class ClassNode : public TernaryNode
++{
++  public:
+     ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock,
+               const TokenPos& pos)
+       : TernaryNode(ParseNodeKind::Class, names, heritage, methodsOrBlock, pos)
+     {
+         MOZ_ASSERT_IF(names, names->is<ClassNames>());
+         MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() ||
+                    methodsOrBlock->isKind(ParseNodeKind::ClassMethodList));
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Class);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_TERNARY));
++        MOZ_ASSERT_IF(match, node.is<TernaryNode>());
+         return match;
+     }
+ 
+     ClassNames* names() const {
+-        return pn_kid1 ? &pn_kid1->as<ClassNames>() : nullptr;
++        return kid1() ? &kid1()->as<ClassNames>() : nullptr;
+     }
+     ParseNode* heritage() const {
+-        return pn_kid2;
++        return kid2();
+     }
+     ListNode* methodList() const {
+-        if (pn_kid3->isKind(ParseNodeKind::ClassMethodList)) {
+-            return &pn_kid3->as<ListNode>();
++        ParseNode* methodsOrBlock = kid3();
++        if (methodsOrBlock->isKind(ParseNodeKind::ClassMethodList)) {
++            return &methodsOrBlock->as<ListNode>();
+         }
+ 
+-        MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
+-        ListNode* list = &pn_kid3->scopeBody()->as<ListNode>();
++        MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>());
++        ListNode* list = &methodsOrBlock->scopeBody()->as<ListNode>();
+         MOZ_ASSERT(list->isKind(ParseNodeKind::ClassMethodList));
+         return list;
+     }
+     Handle<LexicalScope::Data*> scopeBindings() const {
+-        MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
+-        return pn_kid3->scopeBindings();
++        ParseNode* scope = kid3();
++        MOZ_ASSERT(scope->is<LexicalScopeNode>());
++        return scope->scopeBindings();
+     }
+ };
+ 
+ #ifdef DEBUG
+ void DumpParseTree(ParseNode* pn, GenericPrinter& out, int indent = 0);
+ #endif
+ 
+ class ParseNodeAllocator
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -6092,36 +6092,35 @@ template<class ParseHandler, typename Ch
+ inline bool
+ GeneralParser<ParseHandler, CharT>::checkExportedNameForFunction(Node node)
+ {
+     return asFinalParser()->checkExportedNameForFunction(node);
+ }
+ 
+ template<typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::checkExportedNameForClass(ParseNode* node)
+-{
+-    const ClassNode& cls = node->as<ClassNode>();
+-    MOZ_ASSERT(cls.names());
+-    return checkExportedName(cls.names()->innerBinding()->pn_atom);
++Parser<FullParseHandler, CharT>::checkExportedNameForClass(ClassNode* classNode)
++{
++    MOZ_ASSERT(classNode->names());
++    return checkExportedName(classNode->names()->innerBinding()->pn_atom);
+ }
+ 
+ template<typename CharT>
+ inline bool
+-Parser<SyntaxParseHandler, CharT>::checkExportedNameForClass(Node node)
++Parser<SyntaxParseHandler, CharT>::checkExportedNameForClass(ClassNodeType classNode)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template<class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::checkExportedNameForClass(Node node)
+-{
+-    return asFinalParser()->checkExportedNameForClass(node);
++GeneralParser<ParseHandler, CharT>::checkExportedNameForClass(ClassNodeType classNode)
++{
++    return asFinalParser()->checkExportedNameForClass(classNode);
+ }
+ 
+ template<>
+ inline bool
+ PerHandlerParser<FullParseHandler>::processExport(ParseNode* node)
+ {
+     return pc->sc()->asModuleContext()->builder.processExport(node);
+ }
+@@ -6437,17 +6436,17 @@ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class));
+ 
+-    Node kid = classDefinition(YieldIsName, ClassStatement, NameRequired);
++    ClassNodeType kid = classDefinition(YieldIsName, ClassStatement, NameRequired);
+     if (!kid) {
+         return null();
+     }
+ 
+     if (!checkExportedNameForClass(kid)) {
+         return null();
+     }
+ 
+@@ -6529,17 +6528,17 @@ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class));
+ 
+-    Node kid = classDefinition(YieldIsName, ClassStatement, AllowDefaultName);
++    ClassNodeType kid = classDefinition(YieldIsName, ClassStatement, AllowDefaultName);
+     if (!kid) {
+         return null();
+     }
+ 
+     Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+@@ -6779,17 +6778,17 @@ GeneralParser<ParseHandler, CharT>::cons
+         handler.addStatementToList(block, fun);
+         return finishLexicalScope(scope, block);
+     }
+ 
+     return statement(yieldHandling);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::TernaryNodeType
+ GeneralParser<ParseHandler, CharT>::ifStatement(YieldHandling yieldHandling)
+ {
+     Vector<Node, 4> condList(context), thenList(context);
+     Vector<uint32_t, 4> posList(context);
+     Node elseBranch;
+ 
+     ParseContext::Statement stmt(pc, StatementKind::If);
+ 
+@@ -6837,24 +6836,26 @@ GeneralParser<ParseHandler, CharT>::ifSt
+                 return null();
+             }
+         } else {
+             elseBranch = null();
+         }
+         break;
+     }
+ 
++    TernaryNodeType ifNode;
+     for (int i = condList.length() - 1; i >= 0; i--) {
+-        elseBranch = handler.newIfStatement(posList[i], condList[i], thenList[i], elseBranch);
+-        if (!elseBranch) {
+-            return null();
+-        }
+-    }
+-
+-    return elseBranch;
++        ifNode = handler.newIfStatement(posList[i], condList[i], thenList[i], elseBranch);
++        if (!ifNode) {
++            return null();
++        }
++        elseBranch = ifNode;
++    }
++
++    return ifNode;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::doWhileStatement(YieldHandling yieldHandling)
+ {
+     uint32_t begin = pos().begin;
+     ParseContext::Statement stmt(pc, StatementKind::DoLoop);
+@@ -7157,17 +7158,17 @@ GeneralParser<ParseHandler, CharT>::forS
+                headKind == ParseNodeKind::ForOf ||
+                headKind == ParseNodeKind::ForHead);
+ 
+     if (iterKind == IteratorKind::Async && headKind != ParseNodeKind::ForOf) {
+         errorAt(begin, JSMSG_FOR_AWAIT_NOT_OF);
+         return null();
+     }
+ 
+-    Node forHead;
++    TernaryNodeType forHead;
+     if (headKind == ParseNodeKind::ForHead) {
+         Node init = startNode;
+ 
+         // Look for an operand: |for (;| means we might have already examined
+         // this semicolon with that modifier.
+         MUST_MATCH_TOKEN_MOD(TokenKind::Semi, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT);
+ 
+         TokenKind tt;
+@@ -7680,17 +7681,17 @@ GeneralParser<ParseHandler, CharT>::thro
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::TernaryNodeType
+ GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Try));
+     uint32_t begin = pos().begin;
+ 
+     /*
+      * try nodes are ternary.
+      * kid1 is the try statement
+@@ -7931,17 +7932,17 @@ ToAccessorType(PropertyType propType)
+       case PropertyType::DerivedConstructor:
+         return AccessorType::None;
+       default:
+         MOZ_CRASH("unexpected property type");
+     }
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ClassNodeType
+ GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
+                                                     ClassContext classContext,
+                                                     DefaultHandling defaultHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class));
+ 
+     uint32_t classStartOffset = pos().begin;
+     bool savedStrictness = setLocalStrictMode(true);
+diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
+--- a/js/src/frontend/Parser.h
++++ b/js/src/frontend/Parser.h
+@@ -1037,26 +1037,26 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
+ 
+     Node switchStatement(YieldHandling yieldHandling);
+     Node continueStatement(YieldHandling yieldHandling);
+     Node breakStatement(YieldHandling yieldHandling);
+     Node returnStatement(YieldHandling yieldHandling);
+     Node withStatement(YieldHandling yieldHandling);
+     Node throwStatement(YieldHandling yieldHandling);
+-    Node tryStatement(YieldHandling yieldHandling);
++    TernaryNodeType tryStatement(YieldHandling yieldHandling);
+     Node catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope);
+     Node debuggerStatement();
+ 
+     Node variableStatement(YieldHandling yieldHandling);
+ 
+     Node labeledStatement(YieldHandling yieldHandling);
+     Node labeledItem(YieldHandling yieldHandling);
+ 
+-    Node ifStatement(YieldHandling yieldHandling);
++    TernaryNodeType ifStatement(YieldHandling yieldHandling);
+     Node consequentOrAlternative(YieldHandling yieldHandling);
+ 
+     ListNodeType lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
+ 
+     inline Node importDeclaration();
+     Node importDeclarationOrImportExpr(YieldHandling yieldHandling);
+ 
+     Node exportFrom(uint32_t begin, Node specList);
+@@ -1190,22 +1190,22 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+                                                      TokenKind tt);
+ 
+     inline bool checkExportedName(JSAtom* exportName);
+     inline bool checkExportedNamesForArrayBinding(ListNodeType array);
+     inline bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     inline bool checkExportedNamesForDeclaration(Node node);
+     inline bool checkExportedNamesForDeclarationList(ListNodeType node);
+     inline bool checkExportedNameForFunction(Node node);
+-    inline bool checkExportedNameForClass(Node node);
++    inline bool checkExportedNameForClass(ClassNodeType classNode);
+     inline bool checkExportedNameForClause(Node node);
+ 
+     enum ClassContext { ClassStatement, ClassExpression };
+-    Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
+-                         DefaultHandling defaultHandling);
++    ClassNodeType classDefinition(YieldHandling yieldHandling, ClassContext classContext,
++                                  DefaultHandling defaultHandling);
+ 
+     bool checkBindingIdentifier(PropertyName* ident,
+                                 uint32_t offset,
+                                 YieldHandling yieldHandling,
+                                 TokenKind hint = TokenKind::Limit);
+ 
+     PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
+ 
+@@ -1395,17 +1395,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     inline Node importDeclaration();
+     inline bool checkLocalExportNames(ListNodeType node);
+     inline bool checkExportedName(JSAtom* exportName);
+     inline bool checkExportedNamesForArrayBinding(ListNodeType array);
+     inline bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     inline bool checkExportedNamesForDeclaration(Node node);
+     inline bool checkExportedNamesForDeclarationList(ListNodeType node);
+     inline bool checkExportedNameForFunction(Node node);
+-    inline bool checkExportedNameForClass(Node node);
++    inline bool checkExportedNameForClass(ClassNodeType classNode);
+     inline bool checkExportedNameForClause(Node node);
+ 
+     bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun, uint32_t toStringStart,
+                                      InHandling inHandling, YieldHandling yieldHandling,
+                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                      FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                      Directives inheritedDirectives, Directives* newDirectives);
+ 
+@@ -1520,17 +1520,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     Node importDeclaration();
+     bool checkLocalExportNames(ListNodeType node);
+     bool checkExportedName(JSAtom* exportName);
+     bool checkExportedNamesForArrayBinding(ListNodeType array);
+     bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     bool checkExportedNamesForDeclaration(Node node);
+     bool checkExportedNamesForDeclarationList(ListNodeType node);
+     bool checkExportedNameForFunction(Node node);
+-    bool checkExportedNameForClass(Node node);
++    bool checkExportedNameForClass(ClassNodeType classNode);
+     inline bool checkExportedNameForClause(Node node);
+ 
+     bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun, uint32_t toStringStart,
+                                      InHandling inHandling, YieldHandling yieldHandling,
+                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                      FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                      Directives inheritedDirectives, Directives* newDirectives);
+ 
+diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
+--- a/js/src/frontend/SyntaxParseHandler.h
++++ b/js/src/frontend/SyntaxParseHandler.h
+@@ -225,17 +225,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
+     Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
+     Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
+ 
+     template <class Boxer>
+     Node newRegExp(Node reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
+ 
+-    Node newConditional(Node cond, Node thenExpr, Node elseExpr) { return NodeGeneric; }
++    ConditionalExpressionType newConditional(Node cond, Node thenExpr, Node elseExpr) {
++        return NodeGeneric;
++    }
+ 
+     Node newElision() { return NodeGeneric; }
+ 
+     Node newDelete(uint32_t begin, Node expr) {
+         return NodeUnparenthesizedUnary;
+     }
+ 
+     Node newTypeof(uint32_t begin, Node kid) {
+@@ -269,17 +271,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     Node newCall(Node callee, Node args) { return NodeFunctionCall; }
+ 
+     Node newSuperCall(Node callee, Node args) { return NodeGeneric; }
+     Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; }
+ 
+     ListNodeType newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
+     ListNodeType newClassMethodList(uint32_t begin) { return NodeGeneric; }
+     Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; }
+-    Node newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
++    ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
+ 
+     Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
+     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
+     Node newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
+ 
+     MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) { return true; }
+     Node newPropertyDefinition(Node name, Node expr) { return NodeGeneric; }
+     void addPropertyDefinition(ListNodeType literal, Node propdef) {}
+@@ -324,17 +326,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+ 
+     Node newSetThis(Node thisName, Node value) { return value; }
+ 
+     Node newExprStatement(Node expr, uint32_t end) {
+         return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
+     }
+ 
+-    Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
++    TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch, Node elseBranch) {
++        return NodeGeneric;
++    }
+     Node newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; }
+     Node newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
+     Node newSwitchStatement(uint32_t begin, Node discriminant, Node lexicalForCaseList,
+                             bool hasDefault)
+     {
+         return NodeGeneric;
+     }
+     Node newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+@@ -344,17 +348,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     Node newExpressionBody(Node expr) { return NodeReturn; }
+     Node newWithStatement(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+ 
+     Node newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
+-    Node newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
++    TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
+         return NodeGeneric;
+     }
+     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
+ 
+     Node newPropertyName(PropertyName* name, const TokenPos& pos) {
+         lastAtom = name;
+         return NodeGeneric;
+     }
+@@ -382,25 +386,25 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     Node newArrowFunction(const TokenPos& pos) { return NodeFunctionArrow; }
+ 
+     void setFunctionFormalParametersAndBody(Node funcNode, Node kid) {}
+     void setFunctionBody(Node pn, Node kid) {}
+     void setFunctionBox(Node pn, FunctionBox* funbox) {}
+     void addFunctionFormalParameter(Node pn, Node argpn) {}
+ 
+-    Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) {
++    Node newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) {
+         return NodeGeneric;
+     }
+ 
+-    Node newForHead(Node init, Node test, Node update, const TokenPos& pos) {
++    TernaryNodeType newForHead(Node init, Node test, Node update, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+ 
+-    Node newForInOrOfHead(ParseNodeKind kind, Node target, Node iteratedExpr, const TokenPos& pos) {
++    TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target, Node iteratedExpr, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+ 
+     MOZ_MUST_USE bool finishInitializerAssignment(Node pn, Node init) { return true; }
+ 
+     void setBeginPosition(Node pn, Node oth) {}
+     void setBeginPosition(Node pn, uint32_t begin) {}
+ 
+diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
+--- a/js/src/wasm/AsmJS.cpp
++++ b/js/src/wasm/AsmJS.cpp
+@@ -420,32 +420,29 @@ ReturnExpr(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Return));
+     return UnaryKid(pn);
+ }
+ 
+ static inline ParseNode*
+ TernaryKid1(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_TERNARY));
+-    return pn->pn_kid1;
++    return pn->as<TernaryNode>().kid1();
+ }
+ 
+ static inline ParseNode*
+ TernaryKid2(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_TERNARY));
+-    return pn->pn_kid2;
++    return pn->as<TernaryNode>().kid2();
+ }
+ 
+ static inline ParseNode*
+ TernaryKid3(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_TERNARY));
+-    return pn->pn_kid3;
++    return pn->as<TernaryNode>().kid3();
+ }
+ 
+ static inline ParseNode*
+ ListHead(ParseNode* pn)
+ {
+     return pn->as<ListNode>().head();
+ }
+ 

+ 7724 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478479.patch

@@ -0,0 +1,7724 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726372 -32400
+#      Wed Sep 12 13:26:12 2018 +0900
+# Node ID f1c5898b0982188af75c672dee68a0ca95051376
+# Parent  0c08c096decb00e0895f94436c5d7aca0c80a872
+Bug 1479659 - Part 3: Add accessors to BinaryNode and subclasses. r=jwalden
+
+diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
+--- a/js/src/builtin/ModuleObject.cpp
++++ b/js/src/builtin/ModuleObject.cpp
+@@ -1361,41 +1361,51 @@ ModuleBuilder::initModule()
+                                  localExportEntries,
+                                  indirectExportEntries,
+                                  starExportEntries);
+ 
+     return true;
+ }
+ 
+ bool
+-ModuleBuilder::processImport(frontend::ParseNode* pn)
++ModuleBuilder::processImport(frontend::BinaryNode* importNode)
+ {
+     using namespace js::frontend;
+ 
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Import));
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-    MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::ImportSpecList));
+-    MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::String));
++    MOZ_ASSERT(importNode->isKind(ParseNodeKind::Import));
++
++    ListNode* specList = &importNode->left()->as<ListNode>();
++    MOZ_ASSERT(specList->isKind(ParseNodeKind::ImportSpecList));
+ 
+-    RootedAtom module(cx_, pn->pn_right->pn_atom);
+-    if (!maybeAppendRequestedModule(module, pn->pn_right)) {
++    ParseNode* moduleSpec = importNode->right();
++    MOZ_ASSERT(moduleSpec->isKind(ParseNodeKind::String));
++
++    RootedAtom module(cx_, moduleSpec->pn_atom);
++    if (!maybeAppendRequestedModule(module, moduleSpec)) {
+         return false;
+     }
+ 
+-    for (ParseNode* spec : pn->pn_left->as<ListNode>().contents()) {
++    RootedAtom importName(cx_);
++    RootedAtom localName(cx_);
++    for (ParseNode* item : specList->contents()) {
++        BinaryNode* spec = &item->as<BinaryNode>();
+         MOZ_ASSERT(spec->isKind(ParseNodeKind::ImportSpec));
+-        MOZ_ASSERT(spec->pn_left->isArity(PN_NAME));
+-        MOZ_ASSERT(spec->pn_right->isArity(PN_NAME));
++
++        ParseNode* importNameNode = spec->left();
++        MOZ_ASSERT(importNameNode->isArity(PN_NAME));
+ 
+-        RootedAtom importName(cx_, spec->pn_left->pn_atom);
+-        RootedAtom localName(cx_, spec->pn_right->pn_atom);
++        ParseNode* localNameNode = spec->right();
++        MOZ_ASSERT(localNameNode->isArity(PN_NAME));
++
++        importName = importNameNode->pn_atom;
++        localName = localNameNode->pn_atom;
+ 
+         uint32_t line;
+         uint32_t column;
+-        tokenStream_.lineAndColumnAt(spec->pn_left->pn_pos.begin, &line, &column);
++        tokenStream_.lineAndColumnAt(importNameNode->pn_pos.begin, &line, &column);
+ 
+         RootedImportEntryObject importEntry(cx_);
+         importEntry = ImportEntryObject::create(cx_, module, importName, localName, line, column);
+         if (!importEntry || !appendImportEntryObject(importEntry)) {
+             return false;
+         }
+     }
+ 
+@@ -1405,45 +1415,53 @@ ModuleBuilder::processImport(frontend::P
+ bool
+ ModuleBuilder::appendImportEntryObject(HandleImportEntryObject importEntry)
+ {
+     MOZ_ASSERT(importEntry->localName());
+     return importEntries_.put(importEntry->localName(), importEntry);
+ }
+ 
+ bool
+-ModuleBuilder::processExport(frontend::ParseNode* pn)
++ModuleBuilder::processExport(frontend::ParseNode* exportNode)
+ {
+     using namespace js::frontend;
+ 
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Export) || pn->isKind(ParseNodeKind::ExportDefault));
+-    MOZ_ASSERT(pn->getArity() == (pn->isKind(ParseNodeKind::Export) ? PN_UNARY : PN_BINARY));
++    MOZ_ASSERT(exportNode->isKind(ParseNodeKind::Export) ||
++               exportNode->isKind(ParseNodeKind::ExportDefault));
++    MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::Export), exportNode->is<UnaryNode>());
+ 
+-    bool isDefault = pn->getKind() == ParseNodeKind::ExportDefault;
+-    ParseNode* kid = isDefault ? pn->pn_left : pn->pn_kid;
++    bool isDefault = exportNode->isKind(ParseNodeKind::ExportDefault);
++    ParseNode* kid = isDefault ? exportNode->as<BinaryNode>().left() : exportNode->pn_kid;
+ 
+-    if (isDefault && pn->pn_right) {
++    if (isDefault && exportNode->as<BinaryNode>().right()) {
+         // This is an export default containing an expression.
+         HandlePropertyName localName = cx_->names().default_;
+         HandlePropertyName exportName = cx_->names().default_;
+         return appendExportEntry(exportName, localName);
+     }
+ 
+     switch (kid->getKind()) {
+-      case ParseNodeKind::ExportSpecList:
++      case ParseNodeKind::ExportSpecList: {
+         MOZ_ASSERT(!isDefault);
+-        for (ParseNode* spec : kid->as<ListNode>().contents()) {
++        RootedAtom localName(cx_);
++        RootedAtom exportName(cx_);
++        for (ParseNode* item : kid->as<ListNode>().contents()) {
++            BinaryNode* spec = &item->as<BinaryNode>();
+             MOZ_ASSERT(spec->isKind(ParseNodeKind::ExportSpec));
+-            RootedAtom localName(cx_, spec->pn_left->pn_atom);
+-            RootedAtom exportName(cx_, spec->pn_right->pn_atom);
++
++            ParseNode* localNameNode = spec->left();
++            ParseNode* exportNameNode = spec->right();
++            localName = localNameNode->pn_atom;
++            exportName =  exportNameNode->pn_atom;
+             if (!appendExportEntry(exportName, localName, spec)) {
+                 return false;
+             }
+         }
+         break;
++      }
+ 
+       case ParseNodeKind::Class: {
+         const ClassNode& cls = kid->as<ClassNode>();
+         MOZ_ASSERT(cls.names());
+         RootedAtom localName(cx_, cls.names()->innerBinding()->pn_atom);
+         RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
+         if (!appendExportEntry(exportName, localName)) {
+             return false;
+@@ -1451,17 +1469,17 @@ ModuleBuilder::processExport(frontend::P
+         break;
+       }
+ 
+       case ParseNodeKind::Var:
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let: {
+         for (ParseNode* binding : kid->as<ListNode>().contents()) {
+             if (binding->isKind(ParseNodeKind::Assign)) {
+-                binding = binding->pn_left;
++                binding = binding->as<AssignmentNode>().left();
+             } else {
+                 MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
+             }
+ 
+             if (binding->isKind(ParseNodeKind::Name)) {
+                 RootedAtom localName(cx_, binding->pn_atom);
+                 RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
+                 if (!appendExportEntry(exportName, localName)) {
+@@ -1528,17 +1546,17 @@ ModuleBuilder::processExportArrayBinding
+     for (ParseNode* node : array->contents()) {
+         if (node->isKind(ParseNodeKind::Elision)) {
+             continue;
+         }
+ 
+         if (node->isKind(ParseNodeKind::Spread)) {
+             node = node->pn_kid;
+         } else if (node->isKind(ParseNodeKind::Assign)) {
+-            node = node->pn_left;
++            node = node->as<AssignmentNode>().left();
+         }
+ 
+         if (!processExportBinding(node)) {
+             return false;
+         }
+     }
+ 
+     return true;
+@@ -1559,58 +1577,65 @@ ModuleBuilder::processExportObjectBindin
+ 
+         ParseNode* target;
+         if (node->isKind(ParseNodeKind::Spread)) {
+             target = node->pn_kid;
+         } else {
+             if (node->isKind(ParseNodeKind::MutateProto)) {
+                 target = node->pn_kid;
+             } else {
+-                target = node->pn_right;
++                target = node->as<BinaryNode>().right();
+             }
+ 
+             if (target->isKind(ParseNodeKind::Assign)) {
+-                target = target->pn_left;
++                target = target->as<AssignmentNode>().left();
+             }
+         }
+ 
+         if (!processExportBinding(target)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
++ModuleBuilder::processExportFrom(frontend::BinaryNode* exportNode)
+ {
+     using namespace js::frontend;
+ 
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::ExportFrom));
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-    MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::ExportSpecList));
+-    MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::String));
++    MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportFrom));
++
++    ListNode* specList = &exportNode->left()->as<ListNode>();
++    MOZ_ASSERT(specList->isKind(ParseNodeKind::ExportSpecList));
+ 
+-    RootedAtom module(cx_, pn->pn_right->pn_atom);
+-    if (!maybeAppendRequestedModule(module, pn->pn_right)) {
++    ParseNode* moduleSpec = exportNode->right();
++    MOZ_ASSERT(moduleSpec->isKind(ParseNodeKind::String));
++
++    RootedAtom module(cx_, moduleSpec->pn_atom);
++    if (!maybeAppendRequestedModule(module, moduleSpec)) {
+         return false;
+     }
+ 
+-    for (ParseNode* spec : pn->pn_left->as<ListNode>().contents()) {
++    RootedAtom bindingName(cx_);
++    RootedAtom exportName(cx_);
++    for (ParseNode* spec : specList->contents()) {
+         if (spec->isKind(ParseNodeKind::ExportSpec)) {
+-            RootedAtom bindingName(cx_, spec->pn_left->pn_atom);
+-            RootedAtom exportName(cx_, spec->pn_right->pn_atom);
+-            if (!appendExportFromEntry(exportName, module, bindingName, spec->pn_left)) {
++            ParseNode* localNameNode = spec->as<BinaryNode>().left();
++            ParseNode* exportNameNode = spec->as<BinaryNode>().right();
++            bindingName = localNameNode->pn_atom;
++            exportName = exportNameNode->pn_atom;
++            if (!appendExportFromEntry(exportName, module, bindingName, localNameNode)) {
+                 return false;
+             }
+         } else {
+             MOZ_ASSERT(spec->isKind(ParseNodeKind::ExportBatchSpec));
+-            RootedAtom importName(cx_, cx_->names().star);
+-            if (!appendExportFromEntry(nullptr, module, importName, spec)) {
++            exportName = cx_->names().star;
++            if (!appendExportFromEntry(nullptr, module, exportName, spec)) {
+                 return false;
+             }
+         }
+     }
+ 
+     return true;
+ }
+ 
+diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h
+--- a/js/src/builtin/ModuleObject.h
++++ b/js/src/builtin/ModuleObject.h
+@@ -21,16 +21,17 @@
+ #include "vm/ProxyObject.h"
+ 
+ namespace js {
+ 
+ class ModuleEnvironmentObject;
+ class ModuleObject;
+ 
+ namespace frontend {
++class BinaryNode;
+ class ListNode;
+ class ParseNode;
+ class TokenStreamAnyChars;
+ } /* namespace frontend */
+ 
+ typedef Rooted<ModuleObject*> RootedModuleObject;
+ typedef Handle<ModuleObject*> HandleModuleObject;
+ typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
+@@ -351,19 +352,19 @@ class ModuleObject : public NativeObject
+ // Process a module's parse tree to collate the import and export data used when
+ // creating a ModuleObject.
+ class MOZ_STACK_CLASS ModuleBuilder
+ {
+   public:
+     explicit ModuleBuilder(JSContext* cx, HandleModuleObject module,
+                            const frontend::TokenStreamAnyChars& tokenStream);
+ 
+-    bool processImport(frontend::ParseNode* pn);
+-    bool processExport(frontend::ParseNode* pn);
+-    bool processExportFrom(frontend::ParseNode* pn);
++    bool processImport(frontend::BinaryNode* importNode);
++    bool processExport(frontend::ParseNode* exportNode);
++    bool processExportFrom(frontend::BinaryNode* exportNode);
+ 
+     bool hasExportedName(JSAtom* name) const;
+ 
+     using ExportEntryVector = GCVector<ExportEntryObject*>;
+     const ExportEntryVector& localExportEntries() const {
+         return localExportEntries_;
+     }
+ 
+diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
+--- a/js/src/builtin/ReflectParse.cpp
++++ b/js/src/builtin/ReflectParse.cpp
+@@ -1742,56 +1742,56 @@ class ASTSerializer
+     bool functionArgs(ParseNode* pn, ListNode* argsList,
+                       NodeVector& args, NodeVector& defaults, MutableHandleValue rest);
+ 
+     bool sourceElement(ParseNode* pn, MutableHandleValue dst);
+ 
+     bool declaration(ParseNode* pn, MutableHandleValue dst);
+     bool variableDeclaration(ListNode* declList, bool lexical, MutableHandleValue dst);
+     bool variableDeclarator(ParseNode* pn, MutableHandleValue dst);
+-    bool importDeclaration(ParseNode* pn, MutableHandleValue dst);
+-    bool importSpecifier(ParseNode* pn, MutableHandleValue dst);
+-    bool exportDeclaration(ParseNode* pn, MutableHandleValue dst);
+-    bool exportSpecifier(ParseNode* pn, MutableHandleValue dst);
++    bool importDeclaration(BinaryNode* importNode, MutableHandleValue dst);
++    bool importSpecifier(BinaryNode* importSpec, MutableHandleValue dst);
++    bool exportDeclaration(ParseNode* exportNode, MutableHandleValue dst);
++    bool exportSpecifier(BinaryNode* exportSpec, MutableHandleValue dst);
+     bool classDefinition(ClassNode* pn, bool expr, MutableHandleValue dst);
+ 
+     bool optStatement(ParseNode* pn, MutableHandleValue dst) {
+         if (!pn) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return statement(pn, dst);
+     }
+ 
+     bool forInit(ParseNode* pn, MutableHandleValue dst);
+-    bool forIn(ParseNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
++    bool forIn(ForNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+                MutableHandleValue dst);
+-    bool forOf(ParseNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
++    bool forOf(ForNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+                MutableHandleValue dst);
+     bool statement(ParseNode* pn, MutableHandleValue dst);
+     bool blockStatement(ListNode* node, MutableHandleValue dst);
+-    bool switchStatement(ParseNode* pn, MutableHandleValue dst);
+-    bool switchCase(ParseNode* pn, MutableHandleValue dst);
++    bool switchStatement(SwitchStatement* switchStmt, MutableHandleValue dst);
++    bool switchCase(CaseClause* caseClause, MutableHandleValue dst);
+     bool tryStatement(TernaryNode* tryNode, MutableHandleValue dst);
+-    bool catchClause(ParseNode* pn, MutableHandleValue dst);
++    bool catchClause(BinaryNode* catchClause, MutableHandleValue dst);
+ 
+     bool optExpression(ParseNode* pn, MutableHandleValue dst) {
+         if (!pn) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return expression(pn, dst);
+     }
+ 
+     bool expression(ParseNode* pn, MutableHandleValue dst);
+ 
+     bool propertyName(ParseNode* pn, MutableHandleValue dst);
+     bool property(ParseNode* pn, MutableHandleValue dst);
+ 
+-    bool classMethod(ParseNode* pn, MutableHandleValue dst);
++    bool classMethod(ClassMethod* classMethod, MutableHandleValue dst);
+ 
+     bool optIdentifier(HandleAtom atom, TokenPos* pos, MutableHandleValue dst) {
+         if (!atom) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return identifier(atom, pos, dst);
+     }
+@@ -2079,108 +2079,114 @@ ASTSerializer::variableDeclaration(ListN
+         dtors.infallibleAppend(child);
+     }
+     return builder.variableDeclaration(dtors, kind, &declList->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::variableDeclarator(ParseNode* pn, MutableHandleValue dst)
+ {
+-    ParseNode* pnleft;
+-    ParseNode* pnright;
++    ParseNode* patternNode;
++    ParseNode* initNode;
+ 
+     if (pn->isKind(ParseNodeKind::Name)) {
+-        pnleft = pn;
+-        pnright = pn->pn_expr;
+-        MOZ_ASSERT_IF(pnright, pn->pn_pos.encloses(pnright->pn_pos));
++        patternNode = pn;
++        initNode = pn->pn_expr;
++        MOZ_ASSERT_IF(initNode, pn->pn_pos.encloses(initNode->pn_pos));
+     } else if (pn->isKind(ParseNodeKind::Assign)) {
+-        pnleft = pn->pn_left;
+-        pnright = pn->pn_right;
+-        MOZ_ASSERT(pn->pn_pos.encloses(pnleft->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pnright->pn_pos));
++        AssignmentNode* assignNode = &pn->as<AssignmentNode>();
++        patternNode = assignNode->left();
++        initNode = assignNode->right();
++        MOZ_ASSERT(pn->pn_pos.encloses(patternNode->pn_pos));
++        MOZ_ASSERT(pn->pn_pos.encloses(initNode->pn_pos));
+     } else {
+         /* This happens for a destructuring declarator in a for-in/of loop. */
+-        pnleft = pn;
+-        pnright = nullptr;
++        patternNode = pn;
++        initNode = nullptr;
+     }
+ 
+-    RootedValue left(cx), right(cx);
+-    return pattern(pnleft, &left) &&
+-           optExpression(pnright, &right) &&
+-           builder.variableDeclarator(left, right, &pn->pn_pos, dst);
++    RootedValue patternVal(cx), init(cx);
++    return pattern(patternNode, &patternVal) &&
++           optExpression(initNode, &init) &&
++           builder.variableDeclarator(patternVal, init, &pn->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::importDeclaration(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::importDeclaration(BinaryNode* importNode, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Import));
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-    MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::ImportSpecList));
+-    MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::String));
+-
+-    ListNode* specList = &pn->pn_left->as<ListNode>();
++    MOZ_ASSERT(importNode->isKind(ParseNodeKind::Import));
++
++    ListNode* specList = &importNode->left()->as<ListNode>();
++    MOZ_ASSERT(specList->isKind(ParseNodeKind::ImportSpecList));
++
++    ParseNode* moduleSpecNode = importNode->right();
++    MOZ_ASSERT(moduleSpecNode->isKind(ParseNodeKind::String));
+ 
+     NodeVector elts(cx);
+     if (!elts.reserve(specList->count())) {
+         return false;
+     }
+ 
+-    for (ParseNode* spec : specList->contents()) {
++    for (ParseNode* item : specList->contents()) {
++        BinaryNode* spec = &item->as<BinaryNode>();
+         RootedValue elt(cx);
+         if (!importSpecifier(spec, &elt)) {
+             return false;
+         }
+         elts.infallibleAppend(elt);
+     }
+ 
+     RootedValue moduleSpec(cx);
+-    return literal(pn->pn_right, &moduleSpec) &&
+-           builder.importDeclaration(elts, moduleSpec, &pn->pn_pos, dst);
++    return literal(moduleSpecNode, &moduleSpec) &&
++           builder.importDeclaration(elts, moduleSpec, &importNode->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::importSpecifier(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::importSpecifier(BinaryNode* importSpec, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::ImportSpec));
++    MOZ_ASSERT(importSpec->isKind(ParseNodeKind::ImportSpec));
+ 
+     RootedValue importName(cx);
+     RootedValue bindingName(cx);
+-    return identifier(pn->pn_left, &importName) &&
+-           identifier(pn->pn_right, &bindingName) &&
+-           builder.importSpecifier(importName, bindingName, &pn->pn_pos, dst);
++    return identifier(importSpec->left(), &importName) &&
++           identifier(importSpec->right(), &bindingName) &&
++           builder.importSpecifier(importName, bindingName, &importSpec->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::exportDeclaration(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::exportDeclaration(ParseNode* exportNode, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Export) ||
+-               pn->isKind(ParseNodeKind::ExportFrom) ||
+-               pn->isKind(ParseNodeKind::ExportDefault));
+-    MOZ_ASSERT(pn->getArity() == (pn->isKind(ParseNodeKind::Export) ? PN_UNARY : PN_BINARY));
+-    MOZ_ASSERT_IF(pn->isKind(ParseNodeKind::ExportFrom), pn->pn_right->isKind(ParseNodeKind::String));
++    MOZ_ASSERT(exportNode->isKind(ParseNodeKind::Export) ||
++               exportNode->isKind(ParseNodeKind::ExportFrom) ||
++               exportNode->isKind(ParseNodeKind::ExportDefault));
++    MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::Export), exportNode->is<UnaryNode>());
++    MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::ExportFrom),
++                  exportNode->as<BinaryNode>().right()->isKind(ParseNodeKind::String));
+ 
+     RootedValue decl(cx, NullValue());
+     NodeVector elts(cx);
+ 
+-    ParseNode* kid = pn->isKind(ParseNodeKind::Export) ? pn->pn_kid : pn->pn_left;
++    ParseNode* kid = exportNode->isKind(ParseNodeKind::Export)
++                     ? exportNode->pn_kid
++                     : exportNode->as<BinaryNode>().left();
+     switch (ParseNodeKind kind = kid->getKind()) {
+       case ParseNodeKind::ExportSpecList: {
+-        ListNode* specList = &pn->pn_left->as<ListNode>();
++        ListNode* specList = &kid->as<ListNode>();
+         if (!elts.reserve(specList->count())) {
+             return false;
+         }
+ 
+         for (ParseNode* spec : specList->contents()) {
+             RootedValue elt(cx);
+             if (spec->isKind(ParseNodeKind::ExportSpec)) {
+-                if (!exportSpecifier(spec, &elt)) {
++                if (!exportSpecifier(&spec->as<BinaryNode>(), &elt)) {
+                     return false;
+                 }
+             } else {
+-                if (!builder.exportBatchSpecifier(&pn->pn_pos, &elt)) {
++                if (!builder.exportBatchSpecifier(&exportNode->pn_pos, &elt)) {
+                     return false;
+                 }
+             }
+             elts.infallibleAppend(elt);
+         }
+         break;
+       }
+ 
+@@ -2207,102 +2213,105 @@ ASTSerializer::exportDeclaration(ParseNo
+       default:
+           if (!expression(kid, &decl)) {
+               return false;
+           }
+           break;
+     }
+ 
+     RootedValue moduleSpec(cx, NullValue());
+-    if (pn->isKind(ParseNodeKind::ExportFrom) && !literal(pn->pn_right, &moduleSpec)) {
+-        return false;
++    if (exportNode->isKind(ParseNodeKind::ExportFrom)) {
++        if (!literal(exportNode->as<BinaryNode>().right(), &moduleSpec)) {
++            return false;
++        }
+     }
+ 
+     RootedValue isDefault(cx, BooleanValue(false));
+-    if (pn->isKind(ParseNodeKind::ExportDefault)) {
++    if (exportNode->isKind(ParseNodeKind::ExportDefault)) {
+         isDefault.setBoolean(true);
+     }
+ 
+-    return builder.exportDeclaration(decl, elts, moduleSpec, isDefault, &pn->pn_pos, dst);
+-}
+-
+-bool
+-ASTSerializer::exportSpecifier(ParseNode* pn, MutableHandleValue dst)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::ExportSpec));
+-
+-    RootedValue bindingName(cx);
+-    RootedValue exportName(cx);
+-    return identifier(pn->pn_left, &bindingName) &&
+-           identifier(pn->pn_right, &exportName) &&
+-           builder.exportSpecifier(bindingName, exportName, &pn->pn_pos, dst);
++    return builder.exportDeclaration(decl, elts, moduleSpec, isDefault, &exportNode->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::switchCase(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::exportSpecifier(BinaryNode* exportSpec, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-    MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+-
+-    NodeVector stmts(cx);
+-
+-    RootedValue expr(cx);
+-
+-    return optExpression(pn->as<CaseClause>().caseExpression(), &expr) &&
+-           statements(pn->as<CaseClause>().statementList(), stmts) &&
+-           builder.switchCase(expr, stmts, &pn->pn_pos, dst);
++    MOZ_ASSERT(exportSpec->isKind(ParseNodeKind::ExportSpec));
++
++    RootedValue bindingName(cx);
++    RootedValue exportName(cx);
++    return identifier(exportSpec->left(), &bindingName) &&
++           identifier(exportSpec->right(), &exportName) &&
++           builder.exportSpecifier(bindingName, exportName, &exportSpec->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::switchStatement(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::switchCase(CaseClause* caseClause, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-    MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
++    MOZ_ASSERT_IF(caseClause->caseExpression(),
++                  caseClause->pn_pos.encloses(caseClause->caseExpression()->pn_pos));
++    MOZ_ASSERT(caseClause->pn_pos.encloses(caseClause->statementList()->pn_pos));
++
++    NodeVector stmts(cx);
++    RootedValue expr(cx);
++    return optExpression(caseClause->caseExpression(), &expr) &&
++           statements(caseClause->statementList(), stmts) &&
++           builder.switchCase(expr, stmts, &caseClause->pn_pos, dst);
++}
++
++bool
++ASTSerializer::switchStatement(SwitchStatement* switchStmt, MutableHandleValue dst)
++{
++    MOZ_ASSERT(switchStmt->pn_pos.encloses(switchStmt->discriminant().pn_pos));
++    MOZ_ASSERT(switchStmt->pn_pos.encloses(switchStmt->lexicalForCaseList().pn_pos));
+ 
+     RootedValue disc(cx);
+-
+-    if (!expression(pn->pn_left, &disc)) {
++    if (!expression(&switchStmt->discriminant(), &disc)) {
+         return false;
+     }
+ 
+-    MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::LexicalScope));
+-    ListNode* caseList = &pn->pn_right->scopeBody()->as<ListNode>();
++    ListNode* caseList = &switchStmt->lexicalForCaseList().scopeBody()->as<ListNode>();
+ 
+     NodeVector cases(cx);
+     if (!cases.reserve(caseList->count())) {
+         return false;
+     }
+ 
+-    for (ParseNode* caseNode : caseList->contents()) {
++    for (ParseNode* item : caseList->contents()) {
++        CaseClause* caseClause = &item->as<CaseClause>();
+         RootedValue child(cx);
+-        if (!switchCase(caseNode, &child)) {
++        if (!switchCase(caseClause, &child)) {
+             return false;
+         }
+         cases.infallibleAppend(child);
+     }
+ 
+     // `lexical` field is always true.
+-    return builder.switchStatement(disc, cases, true, &pn->pn_pos, dst);
++    return builder.switchStatement(disc, cases, true, &switchStmt->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::catchClause(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::catchClause(BinaryNode* catchClause, MutableHandleValue dst)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Catch));
+-    MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-    MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
++    MOZ_ASSERT(catchClause->isKind(ParseNodeKind::Catch));
++
++    ParseNode* varNode = catchClause->left();
++    MOZ_ASSERT_IF(varNode, catchClause->pn_pos.encloses(varNode->pn_pos));
++
++    ParseNode* bodyNode = catchClause->right();
++    MOZ_ASSERT(catchClause->pn_pos.encloses(bodyNode->pn_pos));
+ 
+     RootedValue var(cx), body(cx);
+-
+-    if (!optPattern(pn->pn_left, &var)) {
++    if (!optPattern(varNode, &var)) {
+         return false;
+     }
+ 
+-    return statement(pn->pn_right, &body) &&
+-           builder.catchClause(var, body, &pn->pn_pos, dst);
++    return statement(bodyNode, &body) &&
++           builder.catchClause(var, body, &catchClause->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::tryStatement(TernaryNode* tryNode, MutableHandleValue dst)
+ {
+     ParseNode* bodyNode = tryNode->kid1();
+     MOZ_ASSERT(tryNode->pn_pos.encloses(bodyNode->pn_pos));
+ 
+@@ -2315,17 +2324,17 @@ ASTSerializer::tryStatement(TernaryNode*
+     RootedValue body(cx);
+     if (!statement(bodyNode, &body)) {
+         return false;
+     }
+ 
+     RootedValue handler(cx, NullValue());
+     if (ParseNode* catchScope = catchNode) {
+         MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+-        if (!catchClause(catchScope->scopeBody(), &handler)) {
++        if (!catchClause(&catchScope->scopeBody()->as<BinaryNode>(), &handler)) {
+             return false;
+         }
+     }
+ 
+     RootedValue finally(cx);
+     return optStatement(finallyNode, &finally) &&
+            builder.tryStatement(body, handler, finally, &tryNode->pn_pos, dst);
+ }
+@@ -2340,28 +2349,28 @@ ASTSerializer::forInit(ParseNode* pn, Mu
+ 
+     bool lexical = pn->isKind(ParseNodeKind::Let) || pn->isKind(ParseNodeKind::Const);
+     return (lexical || pn->isKind(ParseNodeKind::Var))
+            ? variableDeclaration(&pn->as<ListNode>(), lexical, dst)
+            : expression(pn, dst);
+ }
+ 
+ bool
+-ASTSerializer::forOf(ParseNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+-                         MutableHandleValue dst)
++ASTSerializer::forOf(ForNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
++                     MutableHandleValue dst)
+ {
+     RootedValue expr(cx);
+ 
+     return expression(iterExpr, &expr) &&
+         builder.forOfStatement(var, expr, stmt, &loop->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::forIn(ParseNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+-                         MutableHandleValue dst)
++ASTSerializer::forIn(ForNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
++                     MutableHandleValue dst)
+ {
+     RootedValue expr(cx);
+ 
+     return expression(iterExpr, &expr) &&
+         builder.forInStatement(var, expr, stmt, &loop->pn_pos, dst);
+ }
+ 
+ bool
+@@ -2394,17 +2403,17 @@ ASTSerializer::statement(ParseNode* pn, 
+       case ParseNodeKind::Var:
+         return declaration(pn, dst);
+ 
+       case ParseNodeKind::Let:
+       case ParseNodeKind::Const:
+         return declaration(pn, dst);
+ 
+       case ParseNodeKind::Import:
+-        return importDeclaration(pn, dst);
++        return importDeclaration(&pn->as<BinaryNode>(), dst);
+ 
+       case ParseNodeKind::Export:
+       case ParseNodeKind::ExportDefault:
+       case ParseNodeKind::ExportFrom:
+         return exportDeclaration(pn, dst);
+ 
+       case ParseNodeKind::EmptyStatement:
+         return builder.emptyStatement(&pn->pn_pos, dst);
+@@ -2443,66 +2452,79 @@ ASTSerializer::statement(ParseNode* pn, 
+ 
+         return expression(testNode, &test) &&
+                statement(consNode, &cons) &&
+                optStatement(altNode, &alt) &&
+                builder.ifStatement(test, cons, alt, &ifNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Switch:
+-        return switchStatement(pn, dst);
++        return switchStatement(&pn->as<SwitchStatement>(), dst);
+ 
+       case ParseNodeKind::Try:
+         return tryStatement(&pn->as<TernaryNode>(), dst);
+ 
+       case ParseNodeKind::With:
+       case ParseNodeKind::While:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
++        BinaryNode* node = &pn->as<BinaryNode>();
++
++        ParseNode* exprNode = node->left();
++        MOZ_ASSERT(node->pn_pos.encloses(exprNode->pn_pos));
++
++        ParseNode* stmtNode = node->right();
++        MOZ_ASSERT(node->pn_pos.encloses(stmtNode->pn_pos));
+ 
+         RootedValue expr(cx), stmt(cx);
+ 
+-        return expression(pn->pn_left, &expr) &&
+-               statement(pn->pn_right, &stmt) &&
+-               (pn->isKind(ParseNodeKind::With)
+-                ? builder.withStatement(expr, stmt, &pn->pn_pos, dst)
+-                : builder.whileStatement(expr, stmt, &pn->pn_pos, dst));
++        return expression(exprNode, &expr) &&
++               statement(stmtNode, &stmt) &&
++               (node->isKind(ParseNodeKind::With)
++                ? builder.withStatement(expr, stmt, &node->pn_pos, dst)
++                : builder.whileStatement(expr, stmt, &node->pn_pos, dst));
+       }
+ 
+       case ParseNodeKind::DoWhile:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
++        BinaryNode* node = &pn->as<BinaryNode>();
++
++        ParseNode* stmtNode = node->left();
++        MOZ_ASSERT(node->pn_pos.encloses(stmtNode->pn_pos));
++
++        ParseNode* testNode = node->right();
++        MOZ_ASSERT(node->pn_pos.encloses(testNode->pn_pos));
+ 
+         RootedValue stmt(cx), test(cx);
+ 
+-        return statement(pn->pn_left, &stmt) &&
+-               expression(pn->pn_right, &test) &&
+-               builder.doWhileStatement(stmt, test, &pn->pn_pos, dst);
++        return statement(stmtNode, &stmt) &&
++               expression(testNode, &test) &&
++               builder.doWhileStatement(stmt, test, &node->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::For:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+-
+-        TernaryNode* head = &pn->pn_left->as<TernaryNode>();
++        ForNode* forNode = &pn->as<ForNode>();
++
++        TernaryNode* head = forNode->head();
++        MOZ_ASSERT(forNode->pn_pos.encloses(head->pn_pos));
++
++        ParseNode* stmtNode = forNode->right();
++        MOZ_ASSERT(forNode->pn_pos.encloses(stmtNode->pn_pos));
+ 
+         ParseNode* initNode = head->kid1();
+         MOZ_ASSERT_IF(initNode, head->pn_pos.encloses(initNode->pn_pos));
+ 
+         ParseNode* maybeTest = head->kid2();
+         MOZ_ASSERT_IF(maybeTest, head->pn_pos.encloses(maybeTest->pn_pos));
+ 
+         ParseNode* updateOrIter = head->kid3();
+         MOZ_ASSERT_IF(updateOrIter, head->pn_pos.encloses(updateOrIter->pn_pos));
+ 
+         RootedValue stmt(cx);
+-        if (!statement(pn->pn_right, &stmt)) {
++        if (!statement(stmtNode, &stmt)) {
+             return false;
+         }
+ 
+         if (head->isKind(ParseNodeKind::ForIn) || head->isKind(ParseNodeKind::ForOf)) {
+             RootedValue var(cx);
+             if (initNode->isKind(ParseNodeKind::LexicalScope)) {
+                 if (!variableDeclaration(&initNode->scopeBody()->as<ListNode>(), true, &var)) {
+                     return false;
+@@ -2519,27 +2541,27 @@ ASTSerializer::statement(ParseNode* pn, 
+                                          initNode->isKind(ParseNodeKind::Let) ||
+                                          initNode->isKind(ParseNodeKind::Const),
+                                          &var))
+                 {
+                     return false;
+                 }
+             }
+             if (head->isKind(ParseNodeKind::ForIn)) {
+-                return forIn(pn, updateOrIter, var, stmt, dst);
++                return forIn(forNode, updateOrIter, var, stmt, dst);
+             }
+-            return forOf(pn, updateOrIter, var, stmt, dst);
++            return forOf(forNode, updateOrIter, var, stmt, dst);
+         }
+ 
+         RootedValue init(cx), test(cx), update(cx);
+ 
+         return forInit(initNode, &init) &&
+                optExpression(maybeTest, &test) &&
+                optExpression(updateOrIter, &update) &&
+-               builder.forStatement(init, test, update, stmt, &pn->pn_pos, dst);
++               builder.forStatement(init, test, update, stmt, &forNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Break:
+       case ParseNodeKind::Continue:
+       {
+         RootedValue label(cx);
+         RootedAtom pnAtom(cx, pn->pn_atom);
+         return optIdentifier(pnAtom, nullptr, &label) &&
+@@ -2588,17 +2610,18 @@ ASTSerializer::statement(ParseNode* pn, 
+       case ParseNodeKind::ClassMethodList:
+       {
+         ListNode* methodList = &pn->as<ListNode>();
+         NodeVector methods(cx);
+         if (!methods.reserve(methodList->count())) {
+             return false;
+         }
+ 
+-        for (ParseNode* method : methodList->contents()) {
++        for (ParseNode* item : methodList->contents()) {
++            ClassMethod* method = &item->as<ClassMethod>();
+             MOZ_ASSERT(methodList->pn_pos.encloses(method->pn_pos));
+ 
+             RootedValue prop(cx);
+             if (!classMethod(method, &prop)) {
+                 return false;
+             }
+             methods.infallibleAppend(prop);
+         }
+@@ -2607,20 +2630,20 @@ ASTSerializer::statement(ParseNode* pn, 
+       }
+ 
+       default:
+         LOCAL_NOT_REACHED("unexpected statement type");
+     }
+ }
+ 
+ bool
+-ASTSerializer::classMethod(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::classMethod(ClassMethod* classMethod, MutableHandleValue dst)
+ {
+     PropKind kind;
+-    switch (pn->getOp()) {
++    switch (classMethod->getOp()) {
+       case JSOP_INITPROP:
+         kind = PROP_INIT;
+         break;
+ 
+       case JSOP_INITPROP_GETTER:
+         kind = PROP_GETTER;
+         break;
+ 
+@@ -2628,20 +2651,20 @@ ASTSerializer::classMethod(ParseNode* pn
+         kind = PROP_SETTER;
+         break;
+ 
+       default:
+         LOCAL_NOT_REACHED("unexpected object-literal property");
+     }
+ 
+     RootedValue key(cx), val(cx);
+-    bool isStatic = pn->as<ClassMethod>().isStatic();
+-    return propertyName(pn->pn_left, &key) &&
+-           expression(pn->pn_right, &val) &&
+-           builder.classMethod(key, val, kind, isStatic, &pn->pn_pos, dst);
++    bool isStatic = classMethod->isStatic();
++    return propertyName(&classMethod->name(), &key) &&
++           expression(&classMethod->method(), &val) &&
++           builder.classMethod(key, val, kind, isStatic, &classMethod->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::leftAssociate(ListNode* node, MutableHandleValue dst)
+ {
+     MOZ_ASSERT(!node->empty());
+ 
+     ParseNodeKind kind = node->getKind();
+@@ -2798,26 +2821,29 @@ ASTSerializer::expression(ParseNode* pn,
+       case ParseNodeKind::LshAssign:
+       case ParseNodeKind::RshAssign:
+       case ParseNodeKind::UrshAssign:
+       case ParseNodeKind::MulAssign:
+       case ParseNodeKind::DivAssign:
+       case ParseNodeKind::ModAssign:
+       case ParseNodeKind::PowAssign:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+-
+-        AssignmentOperator op = aop(pn->getKind());
++        AssignmentNode* assignNode = &pn->as<AssignmentNode>();
++        ParseNode* lhsNode = assignNode->left();
++        ParseNode* rhsNode = assignNode->right();
++        MOZ_ASSERT(assignNode->pn_pos.encloses(lhsNode->pn_pos));
++        MOZ_ASSERT(assignNode->pn_pos.encloses(rhsNode->pn_pos));
++
++        AssignmentOperator op = aop(assignNode->getKind());
+         LOCAL_ASSERT(op > AOP_ERR && op < AOP_LIMIT);
+ 
+         RootedValue lhs(cx), rhs(cx);
+-        return pattern(pn->pn_left, &lhs) &&
+-               expression(pn->pn_right, &rhs) &&
+-               builder.assignmentExpression(op, lhs, rhs, &pn->pn_pos, dst);
++        return pattern(lhsNode, &lhs) &&
++               expression(rhsNode, &rhs) &&
++               builder.assignmentExpression(op, lhs, rhs, &assignNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Pipeline:
+       case ParseNodeKind::Add:
+       case ParseNodeKind::Sub:
+       case ParseNodeKind::StrictEq:
+       case ParseNodeKind::Eq:
+       case ParseNodeKind::StrictNe:
+@@ -2864,99 +2890,101 @@ ASTSerializer::expression(ParseNode* pn,
+                builder.unaryExpression(op, expr, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::New:
+       case ParseNodeKind::TaggedTemplate:
+       case ParseNodeKind::Call:
+       case ParseNodeKind::SuperCall:
+       {
+-        ParseNode* pn_callee = pn->pn_left;
+-        ListNode* argsList = &pn->pn_right->as<ListNode>();
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn_callee->pn_pos));
++        BinaryNode* node = &pn->as<BinaryNode>();
++        ParseNode* calleeNode = node->left();
++        ListNode* argsList = &node->right()->as<ListNode>();
++        MOZ_ASSERT(node->pn_pos.encloses(calleeNode->pn_pos));
+ 
+         RootedValue callee(cx);
+-        if (pn->isKind(ParseNodeKind::SuperCall)) {
+-            MOZ_ASSERT(pn_callee->isKind(ParseNodeKind::SuperBase));
+-            if (!builder.super(&pn_callee->pn_pos, &callee)) {
++        if (node->isKind(ParseNodeKind::SuperCall)) {
++            MOZ_ASSERT(calleeNode->isKind(ParseNodeKind::SuperBase));
++            if (!builder.super(&calleeNode->pn_pos, &callee)) {
+                 return false;
+             }
+         } else {
+-            if (!expression(pn_callee, &callee)) {
++            if (!expression(calleeNode, &callee)) {
+                 return false;
+             }
+         }
+ 
+         NodeVector args(cx);
+         if (!args.reserve(argsList->count())) {
+             return false;
+         }
+ 
+         for (ParseNode* argNode : argsList->contents()) {
+-            MOZ_ASSERT(pn->pn_pos.encloses(argNode->pn_pos));
++            MOZ_ASSERT(node->pn_pos.encloses(argNode->pn_pos));
+ 
+             RootedValue arg(cx);
+             if (!expression(argNode, &arg)) {
+                 return false;
+             }
+             args.infallibleAppend(arg);
+         }
+ 
+-        if (pn->getKind() == ParseNodeKind::TaggedTemplate) {
+-            return builder.taggedTemplate(callee, args, &pn->pn_pos, dst);
++        if (node->getKind() == ParseNodeKind::TaggedTemplate) {
++            return builder.taggedTemplate(callee, args, &node->pn_pos, dst);
+         }
+ 
+         // SUPERCALL is Call(super, args)
+-        return pn->isKind(ParseNodeKind::New)
+-               ? builder.newExpression(callee, args, &pn->pn_pos, dst)
+-
+-            : builder.callExpression(callee, args, &pn->pn_pos, dst);
++        return node->isKind(ParseNodeKind::New)
++               ? builder.newExpression(callee, args, &node->pn_pos, dst)
++               : builder.callExpression(callee, args, &node->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Dot:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
++        PropertyAccess* prop = &pn->as<PropertyAccess>();
++        MOZ_ASSERT(prop->pn_pos.encloses(prop->expression().pn_pos));
+ 
+         RootedValue expr(cx);
+         RootedValue propname(cx);
+-        RootedAtom pnAtom(cx, pn->pn_right->pn_atom);
+-
+-        if (pn->as<PropertyAccess>().isSuper()) {
+-            if (!builder.super(&pn->pn_left->pn_pos, &expr)) {
++        RootedAtom pnAtom(cx, prop->key().pn_atom);
++
++        if (prop->isSuper()) {
++            if (!builder.super(&prop->expression().pn_pos, &expr)) {
+                 return false;
+             }
+         } else {
+-            if (!expression(pn->pn_left, &expr)) {
++            if (!expression(&prop->expression(), &expr)) {
+                 return false;
+             }
+         }
+ 
+         return identifier(pnAtom, nullptr, &propname) &&
+-               builder.memberExpression(false, expr, propname, &pn->pn_pos, dst);
++               builder.memberExpression(false, expr, propname, &prop->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Elem:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+-
+-        RootedValue left(cx), right(cx);
+-
+-        if (pn->as<PropertyByValue>().isSuper()) {
+-            if (!builder.super(&pn->pn_left->pn_pos, &left)) {
++        PropertyByValue* elem = &pn->as<PropertyByValue>();
++        MOZ_ASSERT(elem->pn_pos.encloses(elem->expression().pn_pos));
++        MOZ_ASSERT(elem->pn_pos.encloses(elem->key().pn_pos));
++
++        RootedValue expr(cx), key(cx);
++
++        if (elem->isSuper()) {
++            if (!builder.super(&elem->expression().pn_pos, &expr)) {
+                 return false;
+             }
+         } else {
+-            if (!expression(pn->pn_left, &left)) {
++            if (!expression(&elem->expression(), &expr)) {
+                 return false;
+             }
+         }
+ 
+-        return expression(pn->pn_right, &right) &&
+-               builder.memberExpression(true, left, right, &pn->pn_pos, dst);
++        return expression(&elem->key(), &key) &&
++               builder.memberExpression(true, expr, key, &elem->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::CallSiteObj:
+       {
+         CallSiteNode* callSiteObj = &pn->as<CallSiteNode>();
+         ListNode* rawNodes = callSiteObj->rawNodes();
+         NodeVector raw(cx);
+         if (!raw.reserve(rawNodes->count())) {
+@@ -3109,60 +3137,70 @@ ASTSerializer::expression(ParseNode* pn,
+       }
+ 
+       case ParseNodeKind::Class:
+         return classDefinition(&pn->as<ClassNode>(), true, dst);
+ 
+       case ParseNodeKind::NewTarget:
+       case ParseNodeKind::ImportMeta:
+       {
+-        MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-        MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::PosHolder));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
++        BinaryNode* node = &pn->as<BinaryNode>();
++        ParseNode* firstNode = node->left();
++        MOZ_ASSERT(firstNode->isKind(ParseNodeKind::PosHolder));
++        MOZ_ASSERT(node->pn_pos.encloses(firstNode->pn_pos));
++
++        ParseNode* secondNode = node->right();
++        MOZ_ASSERT(secondNode->isKind(ParseNodeKind::PosHolder));
++        MOZ_ASSERT(node->pn_pos.encloses(secondNode->pn_pos));
+ 
+         RootedValue firstIdent(cx);
+         RootedValue secondIdent(cx);
+ 
+         RootedAtom firstStr(cx);
+         RootedAtom secondStr(cx);
+ 
+-        if (pn->getKind() == ParseNodeKind::NewTarget) {
++        if (node->getKind() == ParseNodeKind::NewTarget) {
+             firstStr = cx->names().new_;
+             secondStr = cx->names().target;
+         } else {
+             firstStr = cx->names().import;
+             secondStr = cx->names().meta;
+         }
+ 
+-        return identifier(firstStr, &pn->pn_left->pn_pos, &firstIdent) &&
+-               identifier(secondStr, &pn->pn_right->pn_pos, &secondIdent) &&
+-               builder.metaProperty(firstIdent, secondIdent, &pn->pn_pos, dst);
++        return identifier(firstStr, &firstNode->pn_pos, &firstIdent) &&
++               identifier(secondStr, &secondNode->pn_pos, &secondIdent) &&
++               builder.metaProperty(firstIdent, secondIdent, &node->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::CallImport:
+       {
+-        MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
++        BinaryNode* node = &pn->as<BinaryNode>();
++        ParseNode* identNode = node->left();
++        MOZ_ASSERT(identNode->isKind(ParseNodeKind::PosHolder));
++        MOZ_ASSERT(identNode->pn_pos.encloses(identNode->pn_pos));
++
++        ParseNode* argNode = node->right();
++        MOZ_ASSERT(node->pn_pos.encloses(argNode->pn_pos));
+ 
+         RootedValue ident(cx);
+         RootedValue arg(cx);
+ 
+         HandlePropertyName name = cx->names().import;
+-        return identifier(name, &pn->pn_left->pn_pos, &ident) &&
+-               expression(pn->pn_right, &arg) &&
++        return identifier(name, &identNode->pn_pos, &ident) &&
++               expression(argNode, &arg) &&
+                builder.callImportExpression(ident, arg, &pn->pn_pos, dst);
+       }
+ 
+-      case ParseNodeKind::SetThis:
++      case ParseNodeKind::SetThis: {
+         // SETTHIS is used to assign the result of a super() call to |this|.
+         // It's not part of the original AST, so just forward to the call.
+-        MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::Name));
+-        return expression(pn->pn_right, dst);
++        BinaryNode* node = &pn->as<BinaryNode>();
++        MOZ_ASSERT(node->left()->isKind(ParseNodeKind::Name));
++        return expression(node->right(), dst);
++      }
+ 
+       default:
+         LOCAL_NOT_REACHED("unexpected expression type");
+     }
+ }
+ 
+ bool
+ ASTSerializer::propertyName(ParseNode* pn, MutableHandleValue dst)
+@@ -3204,24 +3242,28 @@ ASTSerializer::property(ParseNode* pn, M
+       case JSOP_INITPROP_SETTER:
+         kind = PROP_SETTER;
+         break;
+ 
+       default:
+         LOCAL_NOT_REACHED("unexpected object-literal property");
+     }
+ 
+-    bool isShorthand = pn->isKind(ParseNodeKind::Shorthand);
++    BinaryNode* node = &pn->as<BinaryNode>();
++    ParseNode* keyNode = node->left();
++    ParseNode* valNode = node->right();
++
++    bool isShorthand = node->isKind(ParseNodeKind::Shorthand);
+     bool isMethod =
+-        pn->pn_right->isKind(ParseNodeKind::Function) &&
+-        pn->pn_right->pn_funbox->function()->kind() == JSFunction::Method;
++        valNode->isKind(ParseNodeKind::Function) &&
++        valNode->pn_funbox->function()->kind() == JSFunction::Method;
+     RootedValue key(cx), val(cx);
+-    return propertyName(pn->pn_left, &key) &&
+-           expression(pn->pn_right, &val) &&
+-           builder.propertyInitializer(key, val, kind, isShorthand, isMethod, &pn->pn_pos, dst);
++    return propertyName(keyNode, &key) &&
++           expression(valNode, &val) &&
++           builder.propertyInitializer(key, val, kind, isShorthand, isMethod, &node->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst)
+ {
+     RootedValue val(cx);
+     switch (pn->getKind()) {
+       case ParseNodeKind::TemplateString:
+@@ -3332,20 +3374,21 @@ ASTSerializer::objectPattern(ListNode* o
+         ParseNode* target;
+         if (propdef->isKind(ParseNodeKind::MutateProto)) {
+             RootedValue pname(cx, StringValue(cx->names().proto));
+             if (!builder.literal(pname, &propdef->pn_pos, &key)) {
+                 return false;
+             }
+             target = propdef->pn_kid;
+         } else {
+-            if (!propertyName(propdef->pn_left, &key)) {
++            BinaryNode* prop = &propdef->as<BinaryNode>();
++            if (!propertyName(prop->left(), &key)) {
+                 return false;
+             }
+-            target = propdef->pn_right;
++            target = prop->right();
+         }
+ 
+         RootedValue patt(cx), prop(cx);
+         if (!pattern(target, &patt) ||
+             !builder.propertyPattern(key, patt, propdef->isKind(ParseNodeKind::Shorthand),
+                                      &propdef->pn_pos,
+                                      &prop))
+         {
+@@ -3501,18 +3544,19 @@ ASTSerializer::functionArgs(ParseNode* p
+         if (arg->isKind(ParseNodeKind::Name) ||
+             arg->isKind(ParseNodeKind::Array) ||
+             arg->isKind(ParseNodeKind::Object))
+         {
+             pat = arg;
+             defNode = nullptr;
+         } else {
+             MOZ_ASSERT(arg->isKind(ParseNodeKind::Assign));
+-            pat = arg->pn_left;
+-            defNode = arg->pn_right;
++            AssignmentNode* assignNode = &arg->as<AssignmentNode>();
++            pat = assignNode->left();
++            defNode = assignNode->right();
+         }
+ 
+         // Process the name or pattern.
+         MOZ_ASSERT(pat->isKind(ParseNodeKind::Name) ||
+                    pat->isKind(ParseNodeKind::Array) ||
+                    pat->isKind(ParseNodeKind::Object));
+         if (!pattern(pat, &node)) {
+             return false;
+diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp
+--- a/js/src/frontend/BinSource-auto.cpp
++++ b/js/src/frontend/BinSource-auto.cpp
+@@ -4866,17 +4866,18 @@ BinASTParser<Tok>::parseInterfaceForInSt
+ 
+     BINJS_MOZ_TRY_DECL(left, parseForInOfBindingOrAssignmentTarget());
+ 
+     BINJS_MOZ_TRY_DECL(right, parseExpression());
+ 
+     BINJS_MOZ_TRY_DECL(body, parseStatement());
+ 
+     BINJS_TRY_DECL(forHead, factory_.newForInOrOfHead(ParseNodeKind::ForIn, left, right, tokenizer_->pos(start)));
+-    BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));
++    ParseNode* result;
++    BINJS_TRY_VAR(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));
+ 
+     if (!scope.isEmpty()) {
+         BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
+         BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
+     }
+     return result;
+ }
+ 
+@@ -4961,17 +4962,18 @@ BinASTParser<Tok>::parseInterfaceForStat
+ 
+     BINJS_MOZ_TRY_DECL(test, parseOptionalExpression());
+ 
+     BINJS_MOZ_TRY_DECL(update, parseOptionalExpression());
+ 
+     BINJS_MOZ_TRY_DECL(body, parseStatement());
+ 
+     BINJS_TRY_DECL(forHead, factory_.newForHead(init, test, update, tokenizer_->pos(start)));
+-    BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));
++    ParseNode* result;
++    BINJS_TRY_VAR(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));
+ 
+     if (!scope.isEmpty()) {
+         BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
+         BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
+     }
+     return result;
+ }
+ 
+@@ -6289,17 +6291,17 @@ BinASTParser<Tok>::parseInterfaceSuper(c
+ 
+ 
+ /*
+  interface SwitchCase : Node {
+     Expression test;
+     FrozenArray<Statement> consequent;
+  }
+ */
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<CaseClause*>
+ BinASTParser<Tok>::parseSwitchCase()
+ {
+     BinKind kind;
+     BinFields fields(cx_);
+     AutoTaggedTuple guard(*tokenizer_);
+ 
+     MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+     if (kind != BinKind::SwitchCase) {
+@@ -6307,17 +6309,17 @@ BinASTParser<Tok>::parseSwitchCase()
+     }
+     const auto start = tokenizer_->offset();
+     BINJS_MOZ_TRY_DECL(result, parseInterfaceSwitchCase(start, kind, fields));
+     MOZ_TRY(guard.done());
+ 
+     return result;
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<CaseClause*>
+ BinASTParser<Tok>::parseInterfaceSwitchCase(const size_t start, const BinKind kind, const BinFields& fields)
+ {
+     MOZ_ASSERT(kind == BinKind::SwitchCase);
+     BINJS_TRY(CheckRecursionLimit(cx_));
+ 
+ #if defined(DEBUG)
+     const BinField expected_fields[2] = { BinField::Test, BinField::Consequent };
+     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+diff --git a/js/src/frontend/BinSource-auto.h b/js/src/frontend/BinSource-auto.h
+--- a/js/src/frontend/BinSource-auto.h
++++ b/js/src/frontend/BinSource-auto.h
+@@ -241,17 +241,17 @@ JS::Result<ParseNode*> parseSkippableFun
+ JS::Result<ParseNode*> parseSkippableFunctionExpression();
+ JS::Result<ParseNode*> parseSkippableGetter();
+ JS::Result<ParseNode*> parseSkippableMethod();
+ JS::Result<ParseNode*> parseSkippableSetter();
+ JS::Result<ParseNode*> parseSpreadElement();
+ JS::Result<ParseNode*> parseStaticMemberAssignmentTarget();
+ JS::Result<ParseNode*> parseStaticMemberExpression();
+ JS::Result<ParseNode*> parseSuper();
+-JS::Result<ParseNode*> parseSwitchCase();
++JS::Result<CaseClause*> parseSwitchCase();
+ JS::Result<ParseNode*> parseSwitchDefault();
+ JS::Result<ParseNode*> parseSwitchStatement();
+ JS::Result<ParseNode*> parseSwitchStatementWithDefault();
+ JS::Result<ParseNode*> parseTemplateElement();
+ JS::Result<ParseNode*> parseTemplateExpression();
+ JS::Result<ParseNode*> parseThisExpression();
+ JS::Result<ParseNode*> parseThrowStatement();
+ JS::Result<ParseNode*> parseTryCatchStatement();
+@@ -347,17 +347,17 @@ JS::Result<ParseNode*> parseInterfaceSki
+ JS::Result<ParseNode*> parseInterfaceSkippableFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceSkippableGetter(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceSkippableMethod(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceSkippableSetter(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceSpreadElement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceStaticMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceStaticMemberExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceSuper(const size_t start, const BinKind kind, const BinFields& fields);
+-JS::Result<ParseNode*> parseInterfaceSwitchCase(const size_t start, const BinKind kind, const BinFields& fields);
++JS::Result<CaseClause*> parseInterfaceSwitchCase(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceSwitchDefault(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceSwitchStatement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceSwitchStatementWithDefault(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceTemplateElement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceTemplateExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceThisExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceThrowStatement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceTryCatchStatement(const size_t start, const BinKind kind, const BinFields& fields);
+diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml
+--- a/js/src/frontend/BinSource.yaml
++++ b/js/src/frontend/BinSource.yaml
+@@ -656,17 +656,18 @@ ForInStatement:
+ 
+         // Implicit scope around the `for`, used to store `for (let x in  ...)`
+         // or `for (const x in ...)`-style declarations. Detail on the
+         // declaration is stored as part of `scope`.
+         ParseContext::Scope scope(cx_, parseContext_, usedNames_);
+         BINJS_TRY(scope.init(parseContext_));
+     build: |
+         BINJS_TRY_DECL(forHead, factory_.newForInOrOfHead(ParseNodeKind::ForIn, left, right, tokenizer_->pos(start)));
+-        BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));
++        ParseNode* result;
++        BINJS_TRY_VAR(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));
+ 
+         if (!scope.isEmpty()) {
+             BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
+             BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
+         }
+ 
+ FormalParameters:
+     type-ok:
+@@ -684,17 +685,18 @@ ForStatement:
+ 
+         // Implicit scope around the `for`, used to store `for (let x; ...; ...)`
+         // or `for (const x; ...; ...)`-style declarations. Detail on the
+         // declaration is stored as part of `BINJS_Scope`.
+         ParseContext::Scope scope(cx_, parseContext_, usedNames_);
+         BINJS_TRY(scope.init(parseContext_));
+     build: |
+         BINJS_TRY_DECL(forHead, factory_.newForHead(init, test, update, tokenizer_->pos(start)));
+-        BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));
++        ParseNode* result;
++        BINJS_TRY_VAR(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));
+ 
+         if (!scope.isEmpty()) {
+             BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
+             BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
+         }
+ 
+ FunctionBody:
+     build: |
+@@ -904,16 +906,18 @@ Setter:
+ ShorthandProperty:
+     build: |
+         if (!factory_.isUsableAsObjectPropertyName(name))
+             BINJS_TRY_VAR(name, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
+ 
+         BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, name, AccessorType::None));
+ 
+ SwitchCase:
++    type-ok:
++        CaseClause*
+     build: |
+         BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, test, consequent));
+ 
+ SwitchDefault:
+     build: |
+         BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, nullptr, consequent));
+ 
+ SwitchStatement:
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -1056,33 +1056,33 @@ BytecodeEmitter::checkSideEffects(ParseN
+       // functions or eval.
+       case ParseNodeKind::This:
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+         *answer = sc->needsThisTDZChecks();
+         return true;
+ 
+       // Trivial binary nodes with more token pos holders.
+       case ParseNodeKind::NewTarget:
+-      case ParseNodeKind::ImportMeta:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
+-        MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::PosHolder));
++      case ParseNodeKind::ImportMeta: {
++        MOZ_ASSERT(pn->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
++        MOZ_ASSERT(pn->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
+         *answer = false;
+         return true;
++      }
+ 
+       case ParseNodeKind::Break:
+       case ParseNodeKind::Continue:
+       case ParseNodeKind::Debugger:
+         MOZ_ASSERT(pn->isArity(PN_NULLARY));
+         *answer = true;
+         return true;
+ 
+       // Watch out for getters!
+       case ParseNodeKind::Dot:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Unary cases with side effects only if the child has them.
+       case ParseNodeKind::TypeOfExpr:
+       case ParseNodeKind::Void:
+       case ParseNodeKind::Not:
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+@@ -1182,18 +1182,22 @@ BytecodeEmitter::checkSideEffects(ParseN
+       case ParseNodeKind::BitAndAssign:
+       case ParseNodeKind::LshAssign:
+       case ParseNodeKind::RshAssign:
+       case ParseNodeKind::UrshAssign:
+       case ParseNodeKind::MulAssign:
+       case ParseNodeKind::DivAssign:
+       case ParseNodeKind::ModAssign:
+       case ParseNodeKind::PowAssign:
++        MOZ_ASSERT(pn->is<AssignmentNode>());
++        *answer = true;
++        return true;
++
+       case ParseNodeKind::SetThis:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::StatementList:
+       // Strict equality operations and logical operators are well-behaved and
+       // perform no conversions.
+       case ParseNodeKind::Or:
+       case ParseNodeKind::And:
+@@ -1242,60 +1246,61 @@ BytecodeEmitter::checkSideEffects(ParseN
+       case ParseNodeKind::Div:
+       case ParseNodeKind::Mod:
+       case ParseNodeKind::Pow:
+         MOZ_ASSERT(pn->as<ListNode>().count() >= 2);
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::Colon:
+-      case ParseNodeKind::Case:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        if (!checkSideEffects(pn->pn_left, answer)) {
++      case ParseNodeKind::Case: {
++        BinaryNode* node = &pn->as<BinaryNode>();
++        if (!checkSideEffects(node->left(), answer)) {
+             return false;
+         }
+         if (*answer) {
+             return true;
+         }
+-        return checkSideEffects(pn->pn_right, answer);
++        return checkSideEffects(node->right(), answer);
++      }
+ 
+       // More getters.
+       case ParseNodeKind::Elem:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       // These affect visible names in this code, or in other code.
+       case ParseNodeKind::Import:
+       case ParseNodeKind::ExportFrom:
+       case ParseNodeKind::ExportDefault:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Likewise.
+       case ParseNodeKind::Export:
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::CallImport:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Every part of a loop might be effect-free, but looping infinitely *is*
+       // an effect.  (Language lawyer trivia: C++ says threads can be assumed
+       // to exit or have side effects, C++14 [intro.multithread]p27, so a C++
+       // implementation's equivalent of the below could set |*answer = false;|
+       // if all loop sub-nodes set |*answer = false|!)
+       case ParseNodeKind::DoWhile:
+       case ParseNodeKind::While:
+       case ParseNodeKind::For:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Declarations affect the name set of the relevant scope.
+       case ParseNodeKind::Var:
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+         MOZ_ASSERT(pn->is<ListNode>());
+@@ -1324,17 +1329,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+         return true;
+       }
+ 
+       // Function calls can invoke non-local code.
+       case ParseNodeKind::New:
+       case ParseNodeKind::Call:
+       case ParseNodeKind::TaggedTemplate:
+       case ParseNodeKind::SuperCall:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Function arg lists can contain arbitrary expressions. Technically
+       // this only causes side-effects if one of the arguments does, but since
+       // the call being made will always trigger side-effects, it isn't needed.
+       case ParseNodeKind::Arguments:
+         MOZ_ASSERT(pn->is<ListNode>());
+@@ -1352,36 +1357,36 @@ BytecodeEmitter::checkSideEffects(ParseN
+       case ParseNodeKind::Class:
+         MOZ_ASSERT(pn->is<ClassNode>());
+         *answer = true;
+         return true;
+ 
+       // |with| calls |ToObject| on its expression and so throws if that value
+       // is null/undefined.
+       case ParseNodeKind::With:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::Return:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::Name:
+         MOZ_ASSERT(pn->isArity(PN_NAME));
+         *answer = true;
+         return true;
+ 
+       // Shorthands could trigger getters: the |x| in the object literal in
+       // |with ({ get x() { throw 42; } }) ({ x });|, for example, triggers
+       // one.  (Of course, it isn't necessary to use |with| for a shorthand to
+       // trigger a getter.)
+       case ParseNodeKind::Shorthand:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::Function:
+         MOZ_ASSERT(pn->isArity(PN_CODE));
+         /*
+          * A named function, contrary to ES3, is no longer effectful, because
+          * we bind its name lexically (using JSOP_CALLEE) instead of creating
+@@ -1417,34 +1422,36 @@ BytecodeEmitter::checkSideEffects(ParseN
+         if (ParseNode* finallyBlock = tryNode->kid3()) {
+             if (!checkSideEffects(finallyBlock, answer)) {
+                 return false;
+             }
+         }
+         return true;
+       }
+ 
+-      case ParseNodeKind::Catch:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        if (ParseNode* name = pn->pn_left) {
++      case ParseNodeKind::Catch: {
++        BinaryNode* catchClause = &pn->as<BinaryNode>();
++        if (ParseNode* name = catchClause->left()) {
+             if (!checkSideEffects(name, answer)) {
+                 return false;
+             }
+             if (*answer) {
+                 return true;
+             }
+         }
+-        return checkSideEffects(pn->pn_right, answer);
+-
+-      case ParseNodeKind::Switch:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        if (!checkSideEffects(pn->pn_left, answer)) {
+-            return false;
+-        }
+-        return *answer || checkSideEffects(pn->pn_right, answer);
++        return checkSideEffects(catchClause->right(), answer);
++      }
++
++      case ParseNodeKind::Switch: {
++        SwitchStatement* switchStmt = &pn->as<SwitchStatement>();
++        if (!checkSideEffects(&switchStmt->discriminant(), answer)) {
++            return false;
++        }
++        return *answer || checkSideEffects(&switchStmt->lexicalForCaseList(), answer);
++      }
+ 
+       case ParseNodeKind::Label:
+         MOZ_ASSERT(pn->isArity(PN_NAME));
+         return checkSideEffects(pn->expr(), answer);
+ 
+       case ParseNodeKind::LexicalScope:
+         MOZ_ASSERT(pn->isArity(PN_SCOPE));
+         return checkSideEffects(pn->scopeBody(), answer);
+@@ -2003,64 +2010,65 @@ BytecodeEmitter::emitTDZCheckIfNeeded(JS
+             return false;
+         }
+     }
+ 
+     return innermostTDZCheckCache->noteTDZCheck(this, name, DontCheckTDZ);
+ }
+ 
+ bool
+-BytecodeEmitter::emitPropLHS(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
+-    MOZ_ASSERT(!pn->as<PropertyAccess>().isSuper());
+-
+-    ParseNode* pn2 = pn->pn_left;
+-
+-    /*
+-     * If the object operand is also a dotted property reference, reverse the
+-     * list linked via pn_left temporarily so we can iterate over it from the
+-     * bottom up (reversing again as we go), to avoid excessive recursion.
+-     */
+-    if (pn2->isKind(ParseNodeKind::Dot) && !pn2->as<PropertyAccess>().isSuper()) {
+-        ParseNode* pndot = pn2;
+-        ParseNode* pnup = nullptr;
+-        ParseNode* pndown;
+-        for (;;) {
+-            /* Reverse pndot->pn_left to point up, not down. */
+-            pndown = pndot->pn_left;
+-            pndot->pn_left = pnup;
+-            if (!pndown->isKind(ParseNodeKind::Dot) || pndown->as<PropertyAccess>().isSuper()) {
+-                break;
+-            }
+-            pnup = pndot;
+-            pndot = pndown;
+-        }
+-
+-        /* pndown is a primary expression, not a dotted property reference. */
+-        if (!emitTree(pndown)) {
+-            return false;
+-        }
+-
+-        do {
+-            /* Walk back up the list, emitting annotated name ops. */
+-            if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP)) {
+-                return false;
+-            }
+-
+-            /* Reverse the pn_left link again. */
+-            pnup = pndot->pn_left;
+-            pndot->pn_left = pndown;
+-            pndown = pndot;
+-        } while ((pndot = pnup) != nullptr);
+-        return true;
+-    }
+-
+-    // The non-optimized case.
+-    return emitTree(pn2);
++BytecodeEmitter::emitPropLHS(PropertyAccess* prop)
++{
++    MOZ_ASSERT(!prop->isSuper());
++
++    ParseNode* expr = &prop->expression();
++
++    if (!expr->is<PropertyAccess>() || expr->as<PropertyAccess>().isSuper()) {
++        // The non-optimized case.
++        return emitTree(expr);
++    }
++
++    // If the object operand is also a dotted property reference, reverse the
++    // list linked via expression() temporarily so we can iterate over it from
++    // the bottom up (reversing again as we go), to avoid excessive recursion.
++    PropertyAccess* pndot = &expr->as<PropertyAccess>();
++    ParseNode* pnup = nullptr;
++    ParseNode* pndown;
++    for (;;) {
++        // Reverse pndot->expression() to point up, not down.
++        pndown = &pndot->expression();
++        pndot->setExpression(pnup);
++        if (!pndown->is<PropertyAccess>() || pndown->as<PropertyAccess>().isSuper()) {
++            break;
++        }
++        pnup = pndot;
++        pndot = &pndown->as<PropertyAccess>();
++    }
++
++    // pndown is a primary expression, not a dotted property reference.
++    if (!emitTree(pndown)) {
++        return false;
++    }
++
++    while (true) {
++        // Walk back up the list, emitting annotated name ops.
++        if (!emitAtomOp(&pndot->key(), JSOP_GETPROP)) {
++            return false;
++        }
++
++        // Reverse the pndot->expression() link again.
++        pnup = pndot->maybeExpression();
++        pndot->setExpression(pndown);
++        pndown = pndot;
++        if (!pnup) {
++            break;
++        }
++        pndot = &pnup->as<PropertyAccess>();
++    }
++    return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall)
+ {
+     if (!emitGetThisForSuperBase(superBase)) {
+         return false;
+     }
+@@ -2069,84 +2077,82 @@ BytecodeEmitter::emitSuperPropLHS(ParseN
+     }
+     if (!emit1(JSOP_SUPERBASE)) {
+         return false;
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
+-{
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-
+-    if (!emitPropLHS(pn)) {
++BytecodeEmitter::emitPropOp(PropertyAccess* prop, JSOp op)
++{
++    if (!emitPropLHS(prop)) {
+         return false;
+     }
+ 
+     if (op == JSOP_CALLPROP && !emit1(JSOP_DUP)) {
+         return false;
+     }
+ 
+-    if (!emitAtomOp(pn->pn_right, op)) {
++    if (!emitAtomOp(&prop->key(), op)) {
+         return false;
+     }
+ 
+     if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitSuperGetProp(ParseNode* pn, bool isCall)
+-{
+-    ParseNode* base = &pn->as<PropertyAccess>().expression();
++BytecodeEmitter::emitSuperGetProp(PropertyAccess* prop, bool isCall)
++{
++    ParseNode* base = &prop->expression();
+     if (!emitSuperPropLHS(base, isCall)) {
+         return false;
+     }
+ 
+-    if (!emitAtomOp(pn->pn_right, JSOP_GETPROP_SUPER)) {
++    if (!emitAtomOp(&prop->key(), JSOP_GETPROP_SUPER)) {
+         return false;
+     }
+ 
+     if (isCall && !emit1(JSOP_SWAP)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitPropIncDec(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->pn_kid->isKind(ParseNodeKind::Dot));
++    PropertyAccess* prop = &pn->pn_kid->as<PropertyAccess>();
+ 
+     bool post;
+-    bool isSuper = pn->pn_kid->as<PropertyAccess>().isSuper();
++    bool isSuper = prop->isSuper();
+     JSOp binop = GetIncDecInfo(pn->getKind(), &post);
+ 
+     if (isSuper) {
+-        ParseNode* base = &pn->pn_kid->as<PropertyAccess>().expression();
++        ParseNode* base = &prop->expression();
+         if (!emitSuperPropLHS(base)) {              // THIS OBJ
+             return false;
+         }
+         if (!emit1(JSOP_DUP2)) {                    // THIS OBJ THIS OBJ
+             return false;
+         }
+     } else {
+-        if (!emitPropLHS(pn->pn_kid)) {             // OBJ
+-            return false;
++        if (!emitPropLHS(prop)) {
++            return false;                           // OBJ
+         }
+         if (!emit1(JSOP_DUP)) {                     // OBJ OBJ
+             return false;
+         }
+     }
+-    if (!emitAtomOp(pn->pn_kid->pn_right, isSuper ? JSOP_GETPROP_SUPER : JSOP_GETPROP)) { // OBJ V
++    if (!emitAtomOp(&prop->key(), isSuper ? JSOP_GETPROP_SUPER : JSOP_GETPROP)) { // OBJ V
+         return false;
+     }
+     if (!emit1(JSOP_POS)) {                         // OBJ N
+         return false;
+     }
+     if (post && !emit1(JSOP_DUP)) {                 // OBJ N? N
+         return false;
+     }
+@@ -2171,17 +2177,17 @@ BytecodeEmitter::emitPropIncDec(ParseNod
+             if (!emit1(JSOP_SWAP)) {               // N THIS OBJ N+1
+                 return false;
+             }
+         }
+     }
+ 
+     JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
+                          : sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
+-    if (!emitAtomOp(pn->pn_kid->pn_right, setOp)) { // N? N+1
++    if (!emitAtomOp(&prop->key(), setOp)) {         // N? N+1
+         return false;
+     }
+     if (post && !emit1(JSOP_POP)) {                 // RESULT
+         return false;
+     }
+ 
+     return true;
+ }
+@@ -2259,64 +2265,62 @@ BytecodeEmitter::emitNameIncDec(ParseNod
+     if (post && !emit1(JSOP_POP)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitElemOperands(ParseNode* pn, EmitElemOption opts)
+-{
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-
+-    if (!emitTree(pn->pn_left)) {
++BytecodeEmitter::emitElemOperands(PropertyByValue* elem, EmitElemOption opts)
++{
++    if (!emitTree(&elem->expression())) {
+         return false;
+     }
+ 
+     if (opts == EmitElemOption::IncDec) {
+         if (!emit1(JSOP_CHECKOBJCOERCIBLE)) {
+             return false;
+         }
+     } else if (opts == EmitElemOption::Call) {
+         if (!emit1(JSOP_DUP)) {
+             return false;
+         }
+     }
+ 
+-    if (!emitTree(pn->pn_right)) {
++    if (!emitTree(&elem->key())) {
+         return false;
+     }
+ 
+     if (opts == EmitElemOption::IncDec || opts == EmitElemOption::CompoundAssign) {
+         if (!emit1(JSOP_TOID)) {
+             return false;
+         }
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, EmitElemOption opts)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Elem) && pn->as<PropertyByValue>().isSuper());
+-
+-    if (!emitGetThisForSuperBase(pn->pn_left)) {    // THIS
+-        return false;
++BytecodeEmitter::emitSuperElemOperands(PropertyByValue* elem, EmitElemOption opts)
++{
++    MOZ_ASSERT(elem->isSuper());
++
++    if (!emitGetThisForSuperBase(&elem->expression())) {
++        return false;                               // THIS
+     }
+ 
+     if (opts == EmitElemOption::Call) {
+         // We need a second |this| that will be consumed during computation of
+         // the property value. (The original |this| is passed to the call.)
+         if (!emit1(JSOP_DUP)) {                     // THIS THIS
+             return false;
+         }
+     }
+ 
+-    if (!emitTree(pn->pn_right)) {                  // THIS? THIS KEY
++    if (!emitTree(&elem->key())) {                  // THIS? THIS KEY
+         return false;
+     }
+ 
+     // We need to convert the key to an object id first, so that we do not do
+     // it inside both the GETELEM and the SETELEM.
+     if (opts == EmitElemOption::IncDec || opts == EmitElemOption::CompoundAssign) {
+         if (!emit1(JSOP_TOID)) {                    // THIS? THIS KEY
+             return false;
+@@ -2337,63 +2341,62 @@ BytecodeEmitter::emitElemOpBase(JSOp op)
+         return false;
+     }
+ 
+     checkTypeSet(op);
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitElemOp(ParseNode* pn, JSOp op)
++BytecodeEmitter::emitElemOp(PropertyByValue* elem, JSOp op)
+ {
+     MOZ_ASSERT(op == JSOP_GETELEM ||
+                op == JSOP_CALLELEM ||
+                op == JSOP_DELELEM ||
+                op == JSOP_STRICTDELELEM);
+ 
+     EmitElemOption opts = op == JSOP_CALLELEM ? EmitElemOption::Call : EmitElemOption::Get;
+ 
+-    return emitElemOperands(pn, opts) && emitElemOpBase(op);
+-}
+-
+-bool
+-BytecodeEmitter::emitSuperGetElem(ParseNode* pn, bool isCall)
++    return emitElemOperands(elem, opts) && emitElemOpBase(op);
++}
++
++bool
++BytecodeEmitter::emitSuperGetElem(PropertyByValue* elem, bool isCall)
+ {
+     EmitElemOption opts = isCall ? EmitElemOption::Call : EmitElemOption::Get;
+ 
+-    if (!emitSuperElemOperands(pn, opts)) {         // THIS? THIS KEY SUPERBASE
++    if (!emitSuperElemOperands(elem, opts)) {       // THIS? THIS KEY SUPERBASE
+         return false;
+     }
+     if (!emitElemOpBase(JSOP_GETELEM_SUPER)) {      // THIS? VALUE
+         return false;
+     }
+ 
+     if (isCall && !emit1(JSOP_SWAP)) {              // VALUE THIS
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitElemIncDec(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->pn_kid->isKind(ParseNodeKind::Elem));
+-
+-    bool isSuper = pn->pn_kid->as<PropertyByValue>().isSuper();
++    PropertyByValue* elem = &pn->pn_kid->as<PropertyByValue>();
++    bool isSuper = elem->isSuper();
+ 
+     // We need to convert the key to an object id first, so that we do not do
+     // it inside both the GETELEM and the SETELEM. This is done by
+     // emit(Super)ElemOperands.
+     if (isSuper) {
+-        if (!emitSuperElemOperands(pn->pn_kid, EmitElemOption::IncDec)) {
++        if (!emitSuperElemOperands(elem, EmitElemOption::IncDec)) {
+             return false;
+         }
+     } else {
+-        if (!emitElemOperands(pn->pn_kid, EmitElemOption::IncDec)) {
++        if (!emitElemOperands(elem, EmitElemOption::IncDec)) {
+             return false;
+         }
+     }
+ 
+     bool post;
+     JSOp binop = GetIncDecInfo(pn->getKind(), &post);
+ 
+     JSOp getOp;
+@@ -2517,67 +2520,69 @@ BytecodeEmitter::emitNumberOp(double dva
+ }
+ 
+ /*
+  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047.
+  * LLVM is deciding to inline this function which uses a lot of stack space
+  * into emitTree which is recursive and uses relatively little stack space.
+  */
+ MOZ_NEVER_INLINE bool
+-BytecodeEmitter::emitSwitch(SwitchStatement* pn)
+-{
+-    ParseNode& lexical = pn->lexicalForCaseList();
++BytecodeEmitter::emitSwitch(SwitchStatement* switchStmt)
++{
++    ParseNode& lexical = switchStmt->lexicalForCaseList();
+     MOZ_ASSERT(lexical.isKind(ParseNodeKind::LexicalScope));
+     ListNode* cases = &lexical.scopeBody()->as<ListNode>();
+     MOZ_ASSERT(cases->isKind(ParseNodeKind::StatementList));
+ 
+     SwitchEmitter se(this);
+-    if (!se.emitDiscriminant(Some(pn->pn_pos.begin))) {
+-        return false;
+-    }
+-    if (!emitTree(&pn->discriminant())) {
++    if (!se.emitDiscriminant(Some(switchStmt->pn_pos.begin))) {
++        return false;
++    }
++    if (!emitTree(&switchStmt->discriminant())) {
+         return false;
+     }
+ 
+     // Enter the scope before pushing the switch BreakableControl since all
+     // breaks are under this scope.
++
+     if (!lexical.isEmptyScope()) {
+         if (!se.emitLexical(lexical.scopeBindings())) {
+             return false;
+         }
+ 
+         // A switch statement may contain hoisted functions inside its
+         // cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST
+         // bodies of the cases to the case list.
+         if (cases->hasTopLevelFunctionDeclarations()) {
+-            for (ParseNode* caseNode : cases->contents()) {
+-                ListNode* statements = &caseNode->pn_right->as<ListNode>();
++            for (ParseNode* item : cases->contents()) {
++                CaseClause* caseClause = &item->as<CaseClause>();
++                ListNode* statements = caseClause->statementList();
+                 if (statements->hasTopLevelFunctionDeclarations()) {
+                     if (!emitHoistedFunctionsInList(statements)) {
+                         return false;
+                     }
+                 }
+             }
+         }
+     } else {
+         MOZ_ASSERT(!cases->hasTopLevelFunctionDeclarations());
+     }
+ 
+     SwitchEmitter::TableGenerator tableGen(this);
+-    uint32_t caseCount = cases->count() - (pn->hasDefault() ? 1 : 0);
++    uint32_t caseCount = cases->count() - (switchStmt->hasDefault() ? 1 : 0);
+     if (caseCount == 0) {
+         tableGen.finish(0);
+     } else {
+         for (ParseNode* item : cases->contents()) {
+-            CaseClause* caseNode = &item->as<CaseClause>();
+-            if (caseNode->isDefault()) {
++            CaseClause* caseClause = &item->as<CaseClause>();
++            if (caseClause->isDefault()) {
+                 continue;
+             }
+ 
+-            ParseNode* caseValue = caseNode->caseExpression();
++            ParseNode* caseValue = caseClause->caseExpression();
+ 
+             if (caseValue->getKind() != ParseNodeKind::Number) {
+                 tableGen.setInvalid();
+                 break;
+             }
+ 
+             int32_t i;
+             if (!NumberEqualsInt32(caseValue->pn_dval, &i)) {
+@@ -2604,62 +2609,62 @@ BytecodeEmitter::emitSwitch(SwitchStatem
+         }
+     } else {
+         if (!se.emitCond()) {
+             return false;
+         }
+ 
+         // Emit code for evaluating cases and jumping to case statements.
+         for (ParseNode* item : cases->contents()) {
+-            CaseClause* caseNode = &item->as<CaseClause>();
+-            if (caseNode->isDefault()) {
++            CaseClause* caseClause = &item->as<CaseClause>();
++            if (caseClause->isDefault()) {
+                 continue;
+             }
+ 
+-            ParseNode* caseValue = caseNode->caseExpression();
++            ParseNode* caseValue = caseClause->caseExpression();
+             // If the expression is a literal, suppress line number emission so
+             // that debugging works more naturally.
+             if (!emitTree(caseValue, ValueUsage::WantValue,
+                           caseValue->isLiteral() ? SUPPRESS_LINENOTE : EMIT_LINENOTE))
+             {
+                 return false;
+             }
+ 
+             if (!se.emitCaseJump()) {
+                 return false;
+             }
+         }
+     }
+ 
+     // Emit code for each case's statements.
+     for (ParseNode* item : cases->contents()) {
+-        CaseClause* caseNode = &item->as<CaseClause>();
+-        if (caseNode->isDefault()) {
++        CaseClause* caseClause = &item->as<CaseClause>();
++        if (caseClause->isDefault()) {
+             if (!se.emitDefaultBody()) {
+                 return false;
+             }
+         } else {
+             if (isTableSwitch) {
+-                ParseNode* caseValue = caseNode->caseExpression();
++                ParseNode* caseValue = caseClause->caseExpression();
+                 MOZ_ASSERT(caseValue->isKind(ParseNodeKind::Number));
+ 
+                 int32_t i = int32_t(caseValue->pn_dval);
+                 MOZ_ASSERT(double(i) == caseValue->pn_dval);
+ 
+                 if (!se.emitCaseBody(i, tableGen)) {
+                     return false;
+                 }
+             } else {
+                 if (!se.emitCaseBody()) {
+                     return false;
+                 }
+             }
+         }
+ 
+-        if (!emitTree(caseNode->statementList())) {
++        if (!emitTree(caseClause->statementList())) {
+             return false;
+         }
+     }
+ 
+     if (!se.emitEnd()) {
+         return false;
+     }
+ 
+@@ -2717,28 +2722,28 @@ BytecodeEmitter::emitYieldOp(JSOp op)
+     if (!yieldAndAwaitOffsetList.append(offset())) {
+         return false;
+     }
+ 
+     return emit1(JSOP_DEBUGAFTERYIELD);
+ }
+ 
+ bool
+-BytecodeEmitter::emitSetThis(ParseNode* pn)
++BytecodeEmitter::emitSetThis(BinaryNode* setThisNode)
+ {
+     // ParseNodeKind::SetThis is used to update |this| after a super() call
+     // in a derived class constructor.
+ 
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::SetThis));
+-    MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::Name));
+-
+-    RootedAtom name(cx, pn->pn_left->name());
+-    auto emitRhs = [&name, pn](BytecodeEmitter* bce, const NameLocation&, bool) {
++    MOZ_ASSERT(setThisNode->isKind(ParseNodeKind::SetThis));
++    MOZ_ASSERT(setThisNode->left()->isKind(ParseNodeKind::Name));
++
++    RootedAtom name(cx, setThisNode->left()->name());
++    auto emitRhs = [&name, setThisNode](BytecodeEmitter* bce, const NameLocation&, bool) {
+         // Emit the new |this| value.
+-        if (!bce->emitTree(pn->pn_right)) {
++        if (!bce->emitTree(setThisNode->right())) {
+             return false;
+         }
+         // Get the original |this| and throw if we already initialized
+         // it. Do *not* use the NameLocation argument, as that's the special
+         // lexical location below to deal with super() semantics.
+         if (!bce->emitGetName(name)) {
+             return false;
+         }
+@@ -2935,17 +2940,17 @@ BytecodeEmitter::emitFunctionScript(Pars
+ bool
+ BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted)
+ {
+     *emitted = 0;
+ 
+     if (target->isKind(ParseNodeKind::Spread)) {
+         target = target->pn_kid;
+     } else if (target->isKind(ParseNodeKind::Assign)) {
+-        target = target->pn_left;
++        target = target->as<AssignmentNode>().left();
+     }
+ 
+     // No need to recur into ParseNodeKind::Array and
+     // ParseNodeKind::Object subpatterns here, since
+     // emitSetOrInitializeDestructuring does the recursion when
+     // setting or initializing value.  Getting reference doesn't recur.
+     if (target->isKind(ParseNodeKind::Name) ||
+         target->isKind(ParseNodeKind::Array) ||
+@@ -2955,38 +2960,40 @@ BytecodeEmitter::emitDestructuringLHSRef
+     }
+ 
+ #ifdef DEBUG
+     int depth = stackDepth;
+ #endif
+ 
+     switch (target->getKind()) {
+       case ParseNodeKind::Dot: {
+-        if (target->as<PropertyAccess>().isSuper()) {
+-            if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression())) {
++        PropertyAccess* prop = &target->as<PropertyAccess>();
++        if (prop->isSuper()) {
++            if (!emitSuperPropLHS(&prop->expression())) {
+                 return false;
+             }
+             *emitted = 2;
+         } else {
+-            if (!emitTree(target->pn_left)) {
++            if (!emitTree(&prop->expression())) {
+                 return false;
+             }
+             *emitted = 1;
+         }
+         break;
+       }
+ 
+       case ParseNodeKind::Elem: {
+-        if (target->as<PropertyByValue>().isSuper()) {
+-            if (!emitSuperElemOperands(target, EmitElemOption::Ref)) {
++        PropertyByValue* elem = &target->as<PropertyByValue>();
++        if (elem->isSuper()) {
++            if (!emitSuperElemOperands(elem, EmitElemOption::Ref)) {
+                 return false;
+             }
+             *emitted = 3;
+         } else {
+-            if (!emitElemOperands(target, EmitElemOption::Ref)) {
++            if (!emitElemOperands(elem, EmitElemOption::Ref)) {
+                 return false;
+             }
+             *emitted = 2;
+         }
+         break;
+       }
+ 
+       case ParseNodeKind::Call:
+@@ -3009,17 +3016,17 @@ BytecodeEmitter::emitSetOrInitializeDest
+ {
+     // Now emit the lvalue opcode sequence. If the lvalue is a nested
+     // destructuring initialiser-form, call ourselves to handle it, then pop
+     // the matched value. Otherwise emit an lvalue bytecode sequence followed
+     // by an assignment op.
+     if (target->isKind(ParseNodeKind::Spread)) {
+         target = target->pn_kid;
+     } else if (target->isKind(ParseNodeKind::Assign)) {
+-        target = target->pn_left;
++        target = target->as<AssignmentNode>().left();
+     }
+     if (target->isKind(ParseNodeKind::Array) || target->isKind(ParseNodeKind::Object)) {
+         if (!emitDestructuringOps(&target->as<ListNode>(), flav)) {
+             return false;
+         }
+         // Per its post-condition, emitDestructuringOps has left the
+         // to-be-destructured value on top of the stack.
+         if (!emit1(JSOP_POP)) {
+@@ -3083,31 +3090,33 @@ BytecodeEmitter::emitSetOrInitializeDest
+                 break;
+             }
+ 
+             break;
+           }
+ 
+           case ParseNodeKind::Dot: {
+             // The reference is already pushed by emitDestructuringLHSRef.
++            PropertyAccess* prop = &target->as<PropertyAccess>();
+             JSOp setOp;
+-            if (target->as<PropertyAccess>().isSuper()) {
++            if (prop->isSuper()) {
+                 setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
+             } else {
+                 setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
+             }
+-            if (!emitAtomOp(target->pn_right, setOp)) {
++            if (!emitAtomOp(&prop->key(), setOp)) {
+                 return false;
+             }
+             break;
+           }
+ 
+           case ParseNodeKind::Elem: {
+             // The reference is already pushed by emitDestructuringLHSRef.
+-            if (target->as<PropertyByValue>().isSuper()) {
++            PropertyByValue* elem = &target->as<PropertyByValue>();
++            if (elem->isSuper()) {
+                 JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER;
+                 // emitDestructuringLHSRef already did emitSuperElemOperands
+                 // part of emitSuperElemOp.  Perform remaining part here.
+                 if (!emitElemOpBase(setOp)) {
+                     return false;
+                 }
+             } else {
+                 JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
+@@ -3627,17 +3636,17 @@ BytecodeEmitter::emitDestructuringOpsArr
+         bool isFirst = member == pattern->head();
+         DebugOnly<bool> hasNext = !!member->pn_next;
+ 
+         size_t emitted = 0;
+ 
+         // Spec requires LHS reference to be evaluated first.
+         ParseNode* lhsPattern = member;
+         if (lhsPattern->isKind(ParseNodeKind::Assign)) {
+-            lhsPattern = lhsPattern->pn_left;
++            lhsPattern = lhsPattern->as<AssignmentNode>().left();
+         }
+ 
+         bool isElision = lhsPattern->isKind(ParseNodeKind::Elision);
+         if (!isElision) {
+             auto emitLHSRef = [lhsPattern, &emitted](BytecodeEmitter* bce) {
+                 return bce->emitDestructuringLHSRef(lhsPattern, &emitted); // ... OBJ NEXT ITER DONE *LREF
+             };
+             if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitLHSRef)) {
+@@ -3724,17 +3733,17 @@ BytecodeEmitter::emitDestructuringOpsArr
+             }
+ 
+             MOZ_ASSERT(!hasNext);
+             break;
+         }
+ 
+         ParseNode* pndefault = nullptr;
+         if (member->isKind(ParseNodeKind::Assign)) {
+-            pndefault = member->pn_right;
++            pndefault = member->as<AssignmentNode>().right();
+         }
+ 
+         MOZ_ASSERT(!member->isKind(ParseNodeKind::Spread));
+ 
+         InternalIfEmitter ifAlreadyDone(this);
+         if (!isFirst) {
+                                                                   // ... OBJ NEXT ITER *LREF DONE
+             if (!ifAlreadyDone.emitThenElse()) {                  // ... OBJ NEXT ITER *LREF
+@@ -3905,24 +3914,26 @@ BytecodeEmitter::emitDestructuringOpsObj
+ 
+     for (ParseNode* member : pattern->contents()) {
+         ParseNode* subpattern;
+         if (member->isKind(ParseNodeKind::MutateProto) ||
+             member->isKind(ParseNodeKind::Spread))
+         {
+             subpattern = member->pn_kid;
+         } else {
+-            subpattern = member->pn_right;
++            MOZ_ASSERT(member->isKind(ParseNodeKind::Colon) ||
++                       member->isKind(ParseNodeKind::Shorthand));
++            subpattern = member->as<BinaryNode>().right();
+         }
+ 
+         ParseNode* lhs = subpattern;
+         MOZ_ASSERT_IF(member->isKind(ParseNodeKind::Spread),
+                       !lhs->isKind(ParseNodeKind::Assign));
+         if (lhs->isKind(ParseNodeKind::Assign)) {
+-            lhs = lhs->pn_left;
++            lhs = lhs->as<AssignmentNode>().left();
+         }
+ 
+         size_t emitted;
+         if (!emitDestructuringLHSRef(lhs, &emitted)) {            // ... *SET RHS *LREF
+             return false;
+         }
+ 
+         // Duplicate the value being destructured to use as a reference base.
+@@ -3976,17 +3987,17 @@ BytecodeEmitter::emitDestructuringOpsObj
+             if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) {   // ... *SET RHS *LREF PROP
+                 return false;
+             }
+             needsGetElem = false;
+         } else {
+             MOZ_ASSERT(member->isKind(ParseNodeKind::Colon) ||
+                        member->isKind(ParseNodeKind::Shorthand));
+ 
+-            ParseNode* key = member->pn_left;
++            ParseNode* key = member->as<BinaryNode>().left();
+             if (key->isKind(ParseNodeKind::Number)) {
+                 if (!emitNumberOp(key->pn_dval)) {                // ... *SET RHS *LREF RHS KEY
+                     return false;
+                 }
+             } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                        key->isKind(ParseNodeKind::String))
+             {
+                 if (!emitAtomOp(key->pn_atom, JSOP_GETPROP)) {    // ... *SET RHS *LREF PROP
+@@ -4020,18 +4031,18 @@ BytecodeEmitter::emitDestructuringOpsObj
+         }
+ 
+         // Get the property value if not done already.
+         if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) {      // ... *SET RHS *LREF PROP
+             return false;
+         }
+ 
+         if (subpattern->isKind(ParseNodeKind::Assign)) {
+-            if (!emitDefault(subpattern->pn_right, lhs)) {        // ... *SET RHS *LREF VALUE
+-                return false;
++            if (!emitDefault(subpattern->as<AssignmentNode>().right(), lhs)) {
++                return false;                                     // ... *SET RHS *LREF VALUE
+             }
+         }
+ 
+         // Destructure PROP per this member's lhs.
+         if (!emitSetOrInitializeDestructuring(subpattern, flav)) {  // ... *SET RHS
+             return false;
+         }
+     }
+@@ -4069,17 +4080,17 @@ BytecodeEmitter::emitDestructuringObjRes
+         if (member->isKind(ParseNodeKind::Spread)) {
+             break;
+         }
+ 
+         bool isIndex = false;
+         if (member->isKind(ParseNodeKind::MutateProto)) {
+             pnatom.set(cx->names().proto);
+         } else {
+-            ParseNode* key = member->pn_left;
++            ParseNode* key = member->as<BinaryNode>().left();
+             if (key->isKind(ParseNodeKind::Number)) {
+                 if (!emitNumberOp(key->pn_dval)) {
+                     return false;
+                 }
+                 isIndex = true;
+             } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                        key->isKind(ParseNodeKind::String))
+             {
+@@ -4208,21 +4219,22 @@ BytecodeEmitter::emitDeclarationList(Lis
+     for (ParseNode* decl : declList->contents()) {
+         if (!updateSourceCoordNotes(decl->pn_pos.begin)) {
+             return false;
+         }
+ 
+         if (decl->isKind(ParseNodeKind::Assign)) {
+             MOZ_ASSERT(decl->isOp(JSOP_NOP));
+ 
+-            ListNode* pattern = &decl->pn_left->as<ListNode>();
++            AssignmentNode* assignNode = &decl->as<AssignmentNode>();
++            ListNode* pattern = &assignNode->left()->as<ListNode>();
+             MOZ_ASSERT(pattern->isKind(ParseNodeKind::Array) ||
+                        pattern->isKind(ParseNodeKind::Object));
+ 
+-            if (!emitTree(decl->pn_right)) {
++            if (!emitTree(assignNode->right())) {
+                 return false;
+             }
+ 
+             if (!emitDestructuringOps(pattern, DestructuringDeclaration)) {
+                 return false;
+             }
+ 
+             if (!emit1(JSOP_POP)) {
+@@ -4358,42 +4370,44 @@ BytecodeEmitter::emitAssignment(ParseNod
+         return emitSetName(lhs, emitRhs);
+     }
+ 
+     // Deal with non-name assignments.
+     uint32_t atomIndex = (uint32_t) -1;
+     uint8_t offset = 1;
+ 
+     switch (lhs->getKind()) {
+-      case ParseNodeKind::Dot:
+-        if (lhs->as<PropertyAccess>().isSuper()) {
+-            if (!emitSuperPropLHS(&lhs->as<PropertyAccess>().expression())) {
++      case ParseNodeKind::Dot: {
++        PropertyAccess* prop = &lhs->as<PropertyAccess>();
++        if (prop->isSuper()) {
++            if (!emitSuperPropLHS(&prop->expression())) {
+                 return false;
+             }
+             offset += 2;
+         } else {
+-            if (!emitTree(lhs->pn_left)) {
++            if (!emitTree(&prop->expression())) {
+                 return false;
+             }
+             offset += 1;
+         }
+-        if (!makeAtomIndex(lhs->pn_right->pn_atom, &atomIndex)) {
++        if (!makeAtomIndex(prop->key().pn_atom, &atomIndex)) {
+             return false;
+         }
+         break;
++      }
+       case ParseNodeKind::Elem: {
+-        MOZ_ASSERT(lhs->isArity(PN_BINARY));
++        PropertyByValue* elem = &lhs->as<PropertyByValue>();
+         EmitElemOption opt = op == JSOP_NOP ? EmitElemOption::Get : EmitElemOption::CompoundAssign;
+-        if (lhs->as<PropertyByValue>().isSuper()) {
+-            if (!emitSuperElemOperands(lhs, opt)) {
++        if (elem->isSuper()) {
++            if (!emitSuperElemOperands(elem, opt)) {
+                 return false;
+             }
+             offset += 3;
+         } else {
+-            if (!emitElemOperands(lhs, opt)) {
++            if (!emitElemOperands(elem, opt)) {
+                 return false;
+             }
+             offset += 2;
+         }
+         break;
+       }
+       case ParseNodeKind::Array:
+       case ParseNodeKind::Object:
+@@ -4418,36 +4432,38 @@ BytecodeEmitter::emitAssignment(ParseNod
+         MOZ_ASSERT(0);
+     }
+ 
+     if (op != JSOP_NOP) {
+         MOZ_ASSERT(rhs);
+         switch (lhs->getKind()) {
+           case ParseNodeKind::Dot: {
+             JSOp getOp;
+-            if (lhs->as<PropertyAccess>().isSuper()) {
++            PropertyAccess* prop = &lhs->as<PropertyAccess>();
++            if (prop->isSuper()) {
+                 if (!emit1(JSOP_DUP2)) {
+                     return false;
+                 }
+                 getOp = JSOP_GETPROP_SUPER;
+             } else {
+                 if (!emit1(JSOP_DUP)) {
+                     return false;
+                 }
+-                bool isLength = (lhs->pn_right->pn_atom == cx->names().length);
++                bool isLength = (prop->key().pn_atom == cx->names().length);
+                 getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP;
+             }
+             if (!emitIndex32(getOp, atomIndex)) {
+                 return false;
+             }
+             break;
+           }
+           case ParseNodeKind::Elem: {
+             JSOp elemOp;
+-            if (lhs->as<PropertyByValue>().isSuper()) {
++            PropertyByValue* elem = &lhs->as<PropertyByValue>();
++            if (elem->isSuper()) {
+                 if (!emitDupAt(2)) {
+                     return false;
+                 }
+                 if (!emitDupAt(2)) {
+                     return false;
+                 }
+                 if (!emitDupAt(2)) {
+                     return false;
+@@ -4613,26 +4629,28 @@ ParseNode::getConstantValue(JSContext* c
+             vp.setMagic(JS_GENERIC_MAGIC);
+             return true;
+         }
+         MOZ_ASSERT(allowObjects == AllowObjects);
+ 
+         Rooted<IdValueVector> properties(cx, IdValueVector(cx));
+ 
+         RootedValue value(cx), idvalue(cx);
+-        for (ParseNode* prop : as<ListNode>().contents()) {
+-            if (!prop->pn_right->getConstantValue(cx, allowObjects, &value)) {
++        for (ParseNode* item : as<ListNode>().contents()) {
++            // MutateProto and Spread, both are unary, cannot appear here.
++            BinaryNode* prop = &item->as<BinaryNode>();
++            if (!prop->right()->getConstantValue(cx, allowObjects, &value)) {
+                 return false;
+             }
+             if (value.isMagic(JS_GENERIC_MAGIC)) {
+                 vp.setMagic(JS_GENERIC_MAGIC);
+                 return true;
+             }
+ 
+-            ParseNode* key = prop->pn_left;
++            ParseNode* key = prop->left();
+             if (key->isKind(ParseNodeKind::Number)) {
+                 idvalue = NumberValue(key->pn_dval);
+             } else {
+                 MOZ_ASSERT(key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                            key->isKind(ParseNodeKind::String));
+                 MOZ_ASSERT(key->pn_atom != cx->names().proto);
+                 idvalue = StringValue(key->pn_atom);
+             }
+@@ -4724,60 +4742,60 @@ class EmitLevelManager
+   public:
+     explicit EmitLevelManager(BytecodeEmitter* bce) : bce(bce) { bce->emitLevel++; }
+     ~EmitLevelManager() { bce->emitLevel--; }
+ };
+ 
+ } /* anonymous namespace */
+ 
+ bool
+-BytecodeEmitter::emitCatch(ParseNode* pn)
++BytecodeEmitter::emitCatch(BinaryNode* catchClause)
+ {
+     // We must be nested under a try-finally statement.
+     MOZ_ASSERT(innermostNestableControl->is<TryFinallyControl>());
+ 
+     /* Pick up the pending exception and bind it to the catch variable. */
+     if (!emit1(JSOP_EXCEPTION)) {
+         return false;
+     }
+ 
+-    ParseNode* pn2 = pn->pn_left;
+-    if (!pn2) {
++    ParseNode* param = catchClause->left();
++    if (!param) {
+         // Catch parameter was omitted; just discard the exception.
+         if (!emit1(JSOP_POP)) {
+             return false;
+         }
+     } else {
+-        switch (pn2->getKind()) {
++        switch (param->getKind()) {
+           case ParseNodeKind::Array:
+           case ParseNodeKind::Object:
+-            if (!emitDestructuringOps(&pn2->as<ListNode>(), DestructuringDeclaration)) {
++            if (!emitDestructuringOps(&param->as<ListNode>(), DestructuringDeclaration)) {
+                 return false;
+             }
+             if (!emit1(JSOP_POP)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::Name:
+-            if (!emitLexicalInitialization(pn2)) {
++            if (!emitLexicalInitialization(param)) {
+                 return false;
+             }
+             if (!emit1(JSOP_POP)) {
+                 return false;
+             }
+             break;
+ 
+           default:
+             MOZ_ASSERT(0);
+         }
+     }
+ 
+     /* Emit the catch body. */
+-    return emitTree(pn->pn_right);
++    return emitTree(catchClause->right());
+ }
+ 
+ // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See the
+ // comment on EmitSwitch.
+ MOZ_NEVER_INLINE bool
+ BytecodeEmitter::emitTry(TernaryNode* tryNode)
+ {
+     ParseNode* catchScope = tryNode->kid2();
+@@ -4969,61 +4987,62 @@ BytecodeEmitter::emitLexicalScope(ParseN
+         if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
+             return false;
+         }
+     }
+ 
+     EmitterScope emitterScope(this);
+     ScopeKind kind;
+     if (body->isKind(ParseNodeKind::Catch)) {
+-        kind = (!body->pn_left || body->pn_left->isKind(ParseNodeKind::Name))
++        BinaryNode* catchNode = &body->as<BinaryNode>();
++        kind = (!catchNode->left() || catchNode->left()->isKind(ParseNodeKind::Name))
+                ? ScopeKind::SimpleCatch
+                : ScopeKind::Catch;
+     } else {
+         kind = ScopeKind::Lexical;
+     }
+ 
+     if (!emitterScope.enterLexical(this, kind, pn->scopeBindings())) {
+         return false;
+     }
+ 
+     if (body->isKind(ParseNodeKind::For)) {
+         // for loops need to emit {FRESHEN,RECREATE}LEXICALENV if there are
+         // lexical declarations in the head. Signal this by passing a
+         // non-nullptr lexical scope.
+-        if (!emitFor(body, &emitterScope)) {
++        if (!emitFor(&body->as<ForNode>(), &emitterScope)) {
+             return false;
+         }
+     } else {
+         if (!emitLexicalScopeBody(body, SUPPRESS_LINENOTE)) {
+             return false;
+         }
+     }
+ 
+     return emitterScope.leave(this);
+ }
+ 
+ bool
+-BytecodeEmitter::emitWith(ParseNode* pn)
++BytecodeEmitter::emitWith(BinaryNode* withNode)
+ {
+     // Ensure that the column of the 'with' is set properly.
+-    if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
+-        return false;
+-    }
+-
+-    if (!emitTree(pn->pn_left)) {
++    if (!updateSourceCoordNotes(withNode->pn_pos.begin)) {
++        return false;
++    }
++
++    if (!emitTree(withNode->left())) {
+         return false;
+     }
+ 
+     EmitterScope emitterScope(this);
+     if (!emitterScope.enterWith(this)) {
+         return false;
+     }
+ 
+-    if (!emitTree(pn->pn_right)) {
++    if (!emitTree(withNode->right())) {
+         return false;
+     }
+ 
+     return emitterScope.leave(this);
+ }
+ 
+ bool
+ BytecodeEmitter::emitCopyDataProperties(CopyOption option)
+@@ -5366,39 +5385,38 @@ BytecodeEmitter::emitInitializeForInOrOf
+                "for-in/of loop destructuring declarations can't have initializers");
+ 
+     MOZ_ASSERT(target->isKind(ParseNodeKind::Array) ||
+                target->isKind(ParseNodeKind::Object));
+     return emitDestructuringOps(&target->as<ListNode>(), DestructuringDeclaration);
+ }
+ 
+ bool
+-BytecodeEmitter::emitForOf(ParseNode* forOfLoop, const EmitterScope* headLexicalEmitterScope)
++BytecodeEmitter::emitForOf(ForNode* forOfLoop, const EmitterScope* headLexicalEmitterScope)
+ {
+     MOZ_ASSERT(forOfLoop->isKind(ParseNodeKind::For));
+-    MOZ_ASSERT(forOfLoop->isArity(PN_BINARY));
+-
+-    TernaryNode* forOfHead = &forOfLoop->pn_left->as<TernaryNode>();
++
++    TernaryNode* forOfHead = forOfLoop->head();
+     MOZ_ASSERT(forOfHead->isKind(ParseNodeKind::ForOf));
+ 
+-    unsigned iflags = forOfLoop->pn_iflags;
++    unsigned iflags = forOfLoop->iflags();
+     IteratorKind iterKind = (iflags & JSITER_FORAWAITOF)
+                             ? IteratorKind::Async
+                             : IteratorKind::Sync;
+     MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox());
+     MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()->isAsync());
+ 
+     ParseNode* forHeadExpr = forOfHead->kid3();
+ 
+     // Certain builtins (e.g. Array.from) are implemented in self-hosting
+     // as for-of loops.
+     bool allowSelfHostedIter = false;
+     if (emitterMode == BytecodeEmitter::SelfHosting &&
+         forHeadExpr->isKind(ParseNodeKind::Call) &&
+-        forHeadExpr->pn_left->name() == cx->names().allowContentIter)
++        forHeadExpr->as<BinaryNode>().left()->name() == cx->names().allowContentIter)
+     {
+         allowSelfHostedIter = true;
+     }
+ 
+     ForOfEmitter forOf(this, headLexicalEmitterScope, allowSelfHostedIter, iterKind);
+ 
+     if (!forOf.emitIterated()) {                          //
+         return false;
+@@ -5422,36 +5440,34 @@ BytecodeEmitter::emitForOf(ParseNode* fo
+         return false;
+     }
+ 
+     if (!forOf.emitBody()) {                              // NEXT ITER UNDEF
+         return false;
+     }
+ 
+     // Perform the loop body.
+-    ParseNode* forBody = forOfLoop->pn_right;
++    ParseNode* forBody = forOfLoop->body();
+     if (!emitTree(forBody)) {                             // NEXT ITER UNDEF
+         return false;
+     }
+ 
+     if (!forOf.emitEnd(Some(forHeadExpr->pn_pos.begin))) {  //
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitForIn(ParseNode* forInLoop, const EmitterScope* headLexicalEmitterScope)
+-{
+-    MOZ_ASSERT(forInLoop->isKind(ParseNodeKind::For));
+-    MOZ_ASSERT(forInLoop->isArity(PN_BINARY));
++BytecodeEmitter::emitForIn(ForNode* forInLoop, const EmitterScope* headLexicalEmitterScope)
++{
+     MOZ_ASSERT(forInLoop->isOp(JSOP_ITER));
+ 
+-    TernaryNode* forInHead = &forInLoop->pn_left->as<TernaryNode>();
++    TernaryNode* forInHead = forInLoop->head();
+     MOZ_ASSERT(forInHead->isKind(ParseNodeKind::ForIn));
+ 
+     ForInEmitter forIn(this, headLexicalEmitterScope);
+ 
+     // Annex B: Evaluate the var-initializer expression if present.
+     // |for (var i = initializer in expr) { ... }|
+     ParseNode* forInTarget = forInHead->kid1();
+     if (parser->astGenerator().isDeclarationList(forInTarget)) {
+@@ -5487,17 +5503,17 @@ BytecodeEmitter::emitForIn(ParseNode* fo
+     }
+ 
+     // Evaluate the expression being iterated.
+     ParseNode* expr = forInHead->kid3();
+     if (!emitTree(expr)) {                                // EXPR
+         return false;
+     }
+ 
+-    MOZ_ASSERT(forInLoop->pn_iflags == 0);
++    MOZ_ASSERT(forInLoop->iflags() == 0);
+ 
+     MOZ_ASSERT_IF(headLexicalEmitterScope,
+                   forInTarget->isKind(ParseNodeKind::Let) ||
+                   forInTarget->isKind(ParseNodeKind::Const));
+ 
+     if (!forIn.emitInitialize()) {                        // ITER ITERVAL
+         return false;
+     }
+@@ -5506,34 +5522,34 @@ BytecodeEmitter::emitForIn(ParseNode* fo
+         return false;
+     }
+ 
+     if (!forIn.emitBody()) {                              // ITER ITERVAL
+         return false;
+     }
+ 
+     // Perform the loop body.
+-    ParseNode* forBody = forInLoop->pn_right;
++    ParseNode* forBody = forInLoop->body();
+     if (!emitTree(forBody)) {                             // ITER ITERVAL
+         return false;
+     }
+ 
+     if (!forIn.emitEnd(Some(forInHead->pn_pos.begin))) {  //
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ /* C-style `for (init; cond; update) ...` loop. */
+ bool
+-BytecodeEmitter::emitCStyleFor(ParseNode* pn, const EmitterScope* headLexicalEmitterScope)
+-{
+-    TernaryNode* forHead = &pn->pn_left->as<TernaryNode>();
+-    ParseNode* forBody = pn->pn_right;
++BytecodeEmitter::emitCStyleFor(ForNode* forNode, const EmitterScope* headLexicalEmitterScope)
++{
++    TernaryNode* forHead = forNode->head();
++    ParseNode* forBody = forNode->body();
+     ParseNode* init = forHead->kid1();
+     ParseNode* cond = forHead->kid2();
+     ParseNode* update = forHead->kid3();
+     bool isLet = init && init->isKind(ParseNodeKind::Let);
+ 
+     CForEmitter cfor(this, isLet ? headLexicalEmitterScope : nullptr);
+ 
+     if (!cfor.emitInit(init ? Some(init->pn_pos.begin) : Nothing())) {
+@@ -5582,19 +5598,19 @@ BytecodeEmitter::emitCStyleFor(ParseNode
+ 
+     // Check for update code to do before the condition (if any).
+     if (update) {
+         if (!emitTree(update, ValueUsage::IgnoreValue)) { // VAL
+             return false;
+         }
+     }
+ 
+-    if (!cfor.emitCond(Some(pn->pn_pos.begin),
++    if (!cfor.emitCond(Some(forNode->pn_pos.begin),
+                        cond ? Some(cond->pn_pos.begin) : Nothing(),
+-                       Some(pn->pn_pos.end)))             //
++                       Some(forNode->pn_pos.end)))        //
+     {
+         return false;
+     }
+ 
+     if (cond) {
+         if (!emitTree(cond)) {                            // VAL
+             return false;
+         }
+@@ -5603,34 +5619,32 @@ BytecodeEmitter::emitCStyleFor(ParseNode
+     if (!cfor.emitEnd()) {                                //
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitFor(ParseNode* pn, const EmitterScope* headLexicalEmitterScope)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::For));
+-
+-    if (pn->pn_left->isKind(ParseNodeKind::ForHead)) {
+-        return emitCStyleFor(pn, headLexicalEmitterScope);
+-    }
+-
+-    if (!updateLineNumberNotes(pn->pn_pos.begin)) {
+-        return false;
+-    }
+-
+-    if (pn->pn_left->isKind(ParseNodeKind::ForIn)) {
+-        return emitForIn(pn, headLexicalEmitterScope);
+-    }
+-
+-    MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::ForOf));
+-    return emitForOf(pn, headLexicalEmitterScope);
++BytecodeEmitter::emitFor(ForNode* forNode, const EmitterScope* headLexicalEmitterScope)
++{
++    if (forNode->head()->isKind(ParseNodeKind::ForHead)) {
++        return emitCStyleFor(forNode, headLexicalEmitterScope);
++    }
++
++    if (!updateLineNumberNotes(forNode->pn_pos.begin)) {
++        return false;
++    }
++
++    if (forNode->head()->isKind(ParseNodeKind::ForIn)) {
++        return emitForIn(forNode, headLexicalEmitterScope);
++    }
++
++    MOZ_ASSERT(forNode->head()->isKind(ParseNodeKind::ForOf));
++    return emitForOf(forNode, headLexicalEmitterScope);
+ }
+ 
+ MOZ_NEVER_INLINE bool
+ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
+ {
+     FunctionBox* funbox = pn->pn_funbox;
+     RootedFunction fun(cx, funbox->function());
+     RootedAtom name(cx, fun->explicitName());
+@@ -5926,60 +5940,67 @@ BytecodeEmitter::emitAsyncWrapper(unsign
+         if (!emit1(JSOP_TOASYNC)) {
+             return false;
+         }
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitDo(ParseNode* pn)
+-{
++BytecodeEmitter::emitDo(BinaryNode* doNode)
++{
++    ParseNode* bodyNode = doNode->left();
++
+     DoWhileEmitter doWhile(this);
+-
+-    if (!doWhile.emitBody(Some(pn->pn_pos.begin), getOffsetForLoop(pn->pn_left))) {
+-        return false;
+-    }
+-
+-    if (!emitTree(pn->pn_left)) {
++    if (!doWhile.emitBody(Some(doNode->pn_pos.begin), getOffsetForLoop(bodyNode))) {
++        return false;
++    }
++
++    if (!emitTree(bodyNode)) {
+         return false;
+     }
+ 
+     if (!doWhile.emitCond()) {
+         return false;
+     }
+ 
+-    if (!emitTree(pn->pn_right)) {
++    ParseNode* condNode = doNode->right();
++    if (!emitTree(condNode)) {
+         return false;
+     }
+ 
+     if (!doWhile.emitEnd()) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitWhile(ParseNode* pn)
+-{
++BytecodeEmitter::emitWhile(BinaryNode* whileNode)
++{
++    ParseNode* bodyNode = whileNode->right();
++
+     WhileEmitter wh(this);
+-    if (!wh.emitBody(Some(pn->pn_pos.begin), getOffsetForLoop(pn->pn_right), Some(pn->pn_pos.end))) {
+-        return false;
+-    }
+-
+-    if (!emitTree(pn->pn_right)) {
+-        return false;
+-    }
+-
+-    if (!wh.emitCond(getOffsetForLoop(pn->pn_left))) {
+-        return false;
+-    }
+-
+-    if (!emitTree(pn->pn_left)) {
++    if (!wh.emitBody(Some(whileNode->pn_pos.begin),
++                     getOffsetForLoop(bodyNode), Some(whileNode->pn_pos.end)))
++    {
++        return false;
++    }
++
++    if (!emitTree(bodyNode)) {
++        return false;
++    }
++
++    ParseNode* condNode = whileNode->left();
++    if (!wh.emitCond(getOffsetForLoop(condNode))) {
++        return false;
++    }
++
++    if (!emitTree(condNode)) {
+         return false;
+     }
+ 
+     if (!wh.emitEnd()) {
+         return false;
+     }
+ 
+     return true;
+@@ -6824,21 +6845,21 @@ BytecodeEmitter::emitDeleteElement(Parse
+     PropertyByValue* elemExpr = &node->pn_kid->as<PropertyByValue>();
+ 
+     if (elemExpr->isSuper()) {
+         // The expression |delete super[foo];| has to evaluate |super[foo]|,
+         // which could throw if |this| hasn't yet been set by a |super(...)|
+         // call, or trigger side-effects when evaluating ToPropertyKey(foo),
+         // or also throw when the super-base is not an object, before throwing
+         // a ReferenceError for attempting to delete a super-reference.
+-        if (!emitGetThisForSuperBase(elemExpr->pn_left)) {
+-            return false;
+-        }
+-
+-        if (!emitTree(elemExpr->pn_right)) {
++        if (!emitGetThisForSuperBase(&elemExpr->expression())) {
++            return false;
++        }
++
++        if (!emitTree(&elemExpr->key())) {
+             return false;
+         }
+         if (!emit1(JSOP_TOID)) {
+             return false;
+         }
+ 
+         if (!emit1(JSOP_SUPERBASE)) {
+             return false;
+@@ -6897,59 +6918,59 @@ SelfHostedCallFunctionName(JSAtom* name,
+     if (name == cx->names().constructContentFunction) {
+         return "constructContentFunction";
+     }
+ 
+     MOZ_CRASH("Unknown self-hosted call function name");
+ }
+ 
+ bool
+-BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
++BytecodeEmitter::emitSelfHostedCallFunction(BinaryNode* callNode)
+ {
+     // Special-casing of callFunction to emit bytecode that directly
+     // invokes the callee with the correct |this| object and arguments.
+     // callFunction(fun, thisArg, arg0, arg1) thus becomes:
+     // - emit lookup for fun
+     // - emit lookup for thisArg
+     // - emit lookups for arg0, arg1
+     //
+     // argc is set to the amount of actually emitted args and the
+     // emitting of args below is disabled by setting emitArgs to false.
+-    ParseNode* pn_callee = pn->pn_left;
+-    ListNode* argsList = &pn->pn_right->as<ListNode>();
+-
+-    const char* errorName = SelfHostedCallFunctionName(pn_callee->name(), cx);
++    ParseNode* calleeNode = callNode->left();
++    ListNode* argsList = &callNode->right()->as<ListNode>();
++
++    const char* errorName = SelfHostedCallFunctionName(calleeNode->name(), cx);
+ 
+     if (argsList->count() < 2) {
+-        reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s");
+-        return false;
+-    }
+-
+-    JSOp callOp = pn->getOp();
++        reportError(callNode, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s");
++        return false;
++    }
++
++    JSOp callOp = callNode->getOp();
+     if (callOp != JSOP_CALL) {
+-        reportError(pn, JSMSG_NOT_CONSTRUCTOR, errorName);
+-        return false;
+-    }
+-
+-    bool constructing = pn_callee->name() == cx->names().constructContentFunction;
++        reportError(callNode, JSMSG_NOT_CONSTRUCTOR, errorName);
++        return false;
++    }
++
++    bool constructing = calleeNode->name() == cx->names().constructContentFunction;
+     ParseNode* funNode = argsList->head();
+     if (constructing) {
+         callOp = JSOP_NEW;
+     } else if (funNode->getKind() == ParseNodeKind::Name &&
+                funNode->name() == cx->names().std_Function_apply) {
+         callOp = JSOP_FUNAPPLY;
+     }
+ 
+     if (!emitTree(funNode)) {
+         return false;
+     }
+ 
+ #ifdef DEBUG
+     if (emitterMode == BytecodeEmitter::SelfHosting &&
+-        pn_callee->name() == cx->names().callFunction)
++        calleeNode->name() == cx->names().callFunction)
+     {
+         if (!emit1(JSOP_DEBUGCHECKSELFHOSTED)) {
+             return false;
+         }
+     }
+ #endif
+ 
+     ParseNode* thisOrNewTarget = funNode->pn_next;
+@@ -6983,23 +7004,23 @@ BytecodeEmitter::emitSelfHostedCallFunct
+         return false;
+     }
+ 
+     checkTypeSet(callOp);
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn)
+-{
+-    ListNode* argsList = &pn->pn_right->as<ListNode>();
++BytecodeEmitter::emitSelfHostedResumeGenerator(BinaryNode* callNode)
++{
++    ListNode* argsList = &callNode->right()->as<ListNode>();
+ 
+     // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return')
+     if (argsList->count() != 3) {
+-        reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
++        reportError(callNode, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
+         return false;
+     }
+ 
+     ParseNode* genNode = argsList->head();
+     if (!emitTree(genNode)) {
+         return false;
+     }
+ 
+@@ -7028,33 +7049,33 @@ BytecodeEmitter::emitSelfHostedForceInte
+     }
+     if (!emit1(JSOP_UNDEFINED)) {
+         return false;
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitSelfHostedAllowContentIter(ParseNode* pn)
+-{
+-    ListNode* argsList = &pn->pn_right->as<ListNode>();
++BytecodeEmitter::emitSelfHostedAllowContentIter(BinaryNode* callNode)
++{
++    ListNode* argsList = &callNode->right()->as<ListNode>();
+ 
+     if (argsList->count() != 1) {
+-        reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", "");
++        reportError(callNode, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", "");
+         return false;
+     }
+ 
+     // We're just here as a sentinel. Pass the value through directly.
+     return emitTree(argsList->head());
+ }
+ 
+ bool
+-BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn)
+-{
+-    ListNode* argsList = &pn->pn_right->as<ListNode>();
++BytecodeEmitter::emitSelfHostedDefineDataProperty(BinaryNode* callNode)
++{
++    ListNode* argsList = &callNode->right()->as<ListNode>();
+ 
+     // Only optimize when 3 arguments are passed.
+     MOZ_ASSERT(argsList->count() == 3);
+ 
+     ParseNode* objNode = argsList->head();
+     if (!emitTree(objNode)) {
+         return false;
+     }
+@@ -7071,22 +7092,22 @@ BytecodeEmitter::emitSelfHostedDefineDat
+ 
+     // This will leave the object on the stack instead of pushing |undefined|,
+     // but that's fine because the self-hosted code doesn't use the return
+     // value.
+     return emit1(JSOP_INITELEM);
+ }
+ 
+ bool
+-BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
+-{
+-    ListNode* argsList = &pn->pn_right->as<ListNode>();
++BytecodeEmitter::emitSelfHostedHasOwn(BinaryNode* callNode)
++{
++    ListNode* argsList = &callNode->right()->as<ListNode>();
+ 
+     if (argsList->count() != 2) {
+-        reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
++        reportError(callNode, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
+         return false;
+     }
+ 
+     ParseNode* idNode = argsList->head();
+     if (!emitTree(idNode)) {
+         return false;
+     }
+ 
+@@ -7094,22 +7115,22 @@ BytecodeEmitter::emitSelfHostedHasOwn(Pa
+     if (!emitTree(objNode)) {
+         return false;
+     }
+ 
+     return emit1(JSOP_HASOWN);
+ }
+ 
+ bool
+-BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn)
+-{
+-    ListNode* argsList = &pn->pn_right->as<ListNode>();
++BytecodeEmitter::emitSelfHostedGetPropertySuper(BinaryNode* callNode)
++{
++    ListNode* argsList = &callNode->right()->as<ListNode>();
+ 
+     if (argsList->count() != 3) {
+-        reportError(pn, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", "");
++        reportError(callNode, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", "");
+         return false;
+     }
+ 
+     ParseNode* objNode = argsList->head();
+     ParseNode* idNode = objNode->pn_next;
+     ParseNode* receiverNode = idNode->pn_next;
+ 
+     if (!emitTree(receiverNode)) {
+@@ -7137,21 +7158,22 @@ BytecodeEmitter::isRestParameter(ParseNo
+     FunctionBox* funbox = sc->asFunctionBox();
+     RootedFunction fun(cx, funbox->function());
+     if (!funbox->hasRest()) {
+         return false;
+     }
+ 
+     if (!pn->isKind(ParseNodeKind::Name)) {
+         if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) {
+-            ParseNode* pn_callee = pn->pn_left;
+-            if (pn_callee->getKind() == ParseNodeKind::Name &&
+-                pn_callee->name() == cx->names().allowContentIter)
++            BinaryNode* callNode = &pn->as<BinaryNode>();
++            ParseNode* calleeNode = callNode->left();
++            if (calleeNode->getKind() == ParseNodeKind::Name &&
++                calleeNode->name() == cx->names().allowContentIter)
+             {
+-                return isRestParameter(pn->pn_right->as<ListNode>().head());
++                return isRestParameter(callNode->right()->as<ListNode>().head());
+             }
+         }
+         return false;
+     }
+ 
+     JSAtom* name = pn->name();
+     Maybe<NameLocation> paramLoc = locationOfNameBoundInFunctionScope(name);
+     if (paramLoc && lookupName(name) == *paramLoc) {
+@@ -7172,47 +7194,51 @@ bool
+ BytecodeEmitter::emitCallee(ParseNode* callee, ParseNode* call, bool* callop)
+ {
+     switch (callee->getKind()) {
+       case ParseNodeKind::Name:
+         if (!emitGetName(callee, *callop)) {
+             return false;
+         }
+         break;
+-      case ParseNodeKind::Dot:
++      case ParseNodeKind::Dot: {
++        PropertyAccess* prop = &callee->as<PropertyAccess>();
+         MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
+-        if (callee->as<PropertyAccess>().isSuper()) {
+-            if (!emitSuperGetProp(callee, /* isCall = */ *callop)) {
++        if (prop->isSuper()) {
++            if (!emitSuperGetProp(prop, /* isCall = */ *callop)) {
+                 return false;
+             }
+         } else {
+-            if (!emitPropOp(callee, *callop ? JSOP_CALLPROP : JSOP_GETPROP)) {
++            if (!emitPropOp(prop, *callop ? JSOP_CALLPROP : JSOP_GETPROP)) {
+                 return false;
+             }
+         }
+ 
+         break;
+-      case ParseNodeKind::Elem:
++      }
++      case ParseNodeKind::Elem: {
++        PropertyByValue* elem = &callee->as<PropertyByValue>();
+         MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
+-        if (callee->as<PropertyByValue>().isSuper()) {
+-            if (!emitSuperGetElem(callee, /* isCall = */ *callop)) {
++        if (elem->isSuper()) {
++            if (!emitSuperGetElem(elem, /* isCall = */ *callop)) {
+                 return false;
+             }
+         } else {
+-            if (!emitElemOp(callee, *callop ? JSOP_CALLELEM : JSOP_GETELEM)) {
++            if (!emitElemOp(elem, *callop ? JSOP_CALLELEM : JSOP_GETELEM)) {
+                 return false;
+             }
+             if (*callop) {
+                 if (!emit1(JSOP_SWAP)) {
+                     return false;
+                 }
+             }
+         }
+ 
+         break;
++      }
+       case ParseNodeKind::Function:
+         /*
+          * Top level lambdas which are immediately invoked should be
+          * treated as only running once. Every time they execute we will
+          * create new types and scripts for their contents, to increase
+          * the quality of type information within them and enable more
+          * backend optimizations. Note that this does not depend on the
+          * lambda being invoked at most once (it may be named or be
+@@ -7354,78 +7380,82 @@ BytecodeEmitter::emitArguments(ListNode*
+             }
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
++BytecodeEmitter::emitCallOrNew(BinaryNode* callNode,
++                               ValueUsage valueUsage /* = ValueUsage::WantValue */)
+ {
+     bool callop =
+-        pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate);
++        callNode->isKind(ParseNodeKind::Call) || callNode->isKind(ParseNodeKind::TaggedTemplate);
+ 
+     /*
+      * Emit callable invocation or operator new (constructor call) code.
+      * First, emit code for the left operand to evaluate the callable or
+      * constructable object expression.
+      *
+      * For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
+      * This is necessary to interpose the lambda-initialized method read
+      * barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
+      * JSOP_{SET,INIT}PROP.
+      *
+      * Then (or in a call case that has no explicit reference-base
+      * object) we emit JSOP_UNDEFINED to produce the undefined |this|
+      * value required for calls (which non-strict mode functions
+      * will box into the global object).
+      */
+-    ParseNode* pn_callee = pn->pn_left;
+-    ListNode* argsList = &pn->pn_right->as<ListNode>();
+-
+-    bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
+-
+-    if (pn_callee->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
++    ParseNode* calleeNode = callNode->left();
++    ListNode* argsList = &callNode->right()->as<ListNode>();
++
++    bool spread = JOF_OPTYPE(callNode->getOp()) == JOF_BYTE;
++
++    if (calleeNode->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
+         // Calls to "forceInterpreter", "callFunction",
+         // "callContentFunction", or "resumeGenerator" in self-hosted
+         // code generate inline bytecode.
+-        if (pn_callee->name() == cx->names().callFunction ||
+-            pn_callee->name() == cx->names().callContentFunction ||
+-            pn_callee->name() == cx->names().constructContentFunction)
++        PropertyName* calleeName = calleeNode->name();
++        if (calleeName == cx->names().callFunction ||
++            calleeName == cx->names().callContentFunction ||
++            calleeName == cx->names().constructContentFunction)
+         {
+-            return emitSelfHostedCallFunction(pn);
+-        }
+-        if (pn_callee->name() == cx->names().resumeGenerator) {
+-            return emitSelfHostedResumeGenerator(pn);
+-        }
+-        if (pn_callee->name() == cx->names().forceInterpreter) {
++            return emitSelfHostedCallFunction(callNode);
++        }
++        if (calleeName == cx->names().resumeGenerator) {
++            return emitSelfHostedResumeGenerator(callNode);
++        }
++        if (calleeName == cx->names().forceInterpreter) {
+             return emitSelfHostedForceInterpreter();
+         }
+-        if (pn_callee->name() == cx->names().allowContentIter) {
+-            return emitSelfHostedAllowContentIter(pn);
+-        }
+-        if (pn_callee->name() == cx->names().defineDataPropertyIntrinsic && argsList->count() == 3) {
+-            return emitSelfHostedDefineDataProperty(pn);
+-        }
+-        if (pn_callee->name() == cx->names().hasOwn) {
+-            return emitSelfHostedHasOwn(pn);
+-        }
+-        if (pn_callee->name() == cx->names().getPropertySuper) {
+-            return emitSelfHostedGetPropertySuper(pn);
++        if (calleeName == cx->names().allowContentIter) {
++            return emitSelfHostedAllowContentIter(callNode);
++        }
++        if (calleeName == cx->names().defineDataPropertyIntrinsic && argsList->count() == 3) {
++            return emitSelfHostedDefineDataProperty(callNode);
++        }
++        if (calleeName == cx->names().hasOwn) {
++            return emitSelfHostedHasOwn(callNode);
++        }
++        if (calleeName == cx->names().getPropertySuper) {
++            return emitSelfHostedGetPropertySuper(callNode);
+         }
+         // Fall through
+     }
+ 
+-    if (!emitCallee(pn_callee, pn, &callop)) {
+-        return false;
+-    }
+-
+-    bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
+-                   pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;
++    if (!emitCallee(calleeNode, callNode, &callop)) {
++        return false;
++    }
++
++    JSOp op = callNode->getOp();
++    bool isNewOp = op == JSOP_NEW || op == JSOP_SPREADNEW ||
++                   op == JSOP_SUPERCALL ||
++                   op == JSOP_SPREADSUPERCALL;
+ 
+     // Emit room for |this|.
+     if (!callop) {
+         if (isNewOp) {
+             if (!emit1(JSOP_IS_CONSTRUCTING)) {
+                 return false;
+             }
+         } else {
+@@ -7442,102 +7472,103 @@ BytecodeEmitter::emitCallOrNew(ParseNode
+     uint32_t argc = argsList->count();
+ 
+     /*
+      * Emit code for each argument in order, then emit the JSOP_*CALL or
+      * JSOP_NEW bytecode with a two-byte immediate telling how many args
+      * were pushed on the operand stack.
+      */
+     if (isNewOp) {
+-        if (pn->isKind(ParseNodeKind::SuperCall)) {
++        if (callNode->isKind(ParseNodeKind::SuperCall)) {
+             if (!emit1(JSOP_NEWTARGET)) {
+                 return false;
+             }
+         } else if (!spread) {
+             // Repush the callee as new.target
+             if (!emitDupAt(argc + 1)) {
+                 return false;
+             }
+         } else {
+             if (!emitDupAt(2)) {
+                 return false;
+             }
+         }
+     }
+ 
+-    ParseNode* coordNode = pn;
+-    if (pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL)) {
+-        switch (pn_callee->getKind()) {
++    ParseNode* coordNode = callNode;
++    if (op == JSOP_CALL || op == JSOP_SPREADCALL) {
++        switch (calleeNode->getKind()) {
+           case ParseNodeKind::Dot: {
+ 
+             // Check if this member is a simple chain of simple chain of
+             // property accesses, e.g. x.y.z, this.x.y, super.x.y
+             bool simpleDotChain = false;
+-            for (ParseNode* cur = pn_callee; cur->isKind(ParseNodeKind::Dot); cur = cur->pn_left) {
+-                ParseNode* left = cur->pn_left;
++            for (ParseNode* cur = calleeNode;
++                 cur->isKind(ParseNodeKind::Dot);
++                 cur = &cur->as<PropertyAccess>().expression())
++            {
++                ParseNode* left = &cur->as<PropertyAccess>().expression();
+                 if (left->isKind(ParseNodeKind::Name) || left->isKind(ParseNodeKind::This) ||
+                     left->isKind(ParseNodeKind::SuperBase))
+                 {
+                     simpleDotChain = true;
+                 }
+             }
+ 
+             if (!simpleDotChain) {
+                 // obj().aprop() // expression
+                 //       ^       // column coord
+                 //
+                 // Note: Because of the constant folding logic in FoldElement,
+                 // this case also applies for constant string properties.
+                 //
+                 // obj()['aprop']() // expression
+                 //       ^          // column coord
+-                coordNode = pn_callee->pn_right;
++                coordNode = &calleeNode->as<PropertyAccess>().key();
+             }
+             break;
+           }
+           case ParseNodeKind::Elem:
+             // obj[expr]() // expression
+             //          ^  // column coord
+             coordNode = argsList;
+             break;
+           default:
+             break;
+         }
+     }
+ 
+     if (!spread) {
+-        if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
++        if (op == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
+             if (!emitCall(JSOP_CALL_IGNORES_RV, argc, coordNode)) {
+                 return false;
+             }
+             checkTypeSet(JSOP_CALL_IGNORES_RV);
+         } else {
+-            if (!emitCall(pn->getOp(), argc, coordNode)) {
+-                return false;
+-            }
+-            checkTypeSet(pn->getOp());
++            if (!emitCall(op, argc, coordNode)) {
++                return false;
++            }
++            checkTypeSet(op);
+         }
+     } else {
+         if (coordNode) {
+             if (!updateSourceCoordNotes(coordNode->pn_pos.begin)) {
+                 return false;
+             }
+         }
+ 
+-        if (!emit1(pn->getOp())) {
+-            return false;
+-        }
+-        checkTypeSet(pn->getOp());
+-    }
+-    if (pn->isOp(JSOP_EVAL) ||
+-        pn->isOp(JSOP_STRICTEVAL) ||
+-        pn->isOp(JSOP_SPREADEVAL) ||
+-        pn->isOp(JSOP_STRICTSPREADEVAL))
++        if (!emit1(op)) {
++            return false;
++        }
++        checkTypeSet(op);
++    }
++    if (op == JSOP_EVAL || op == JSOP_STRICTEVAL ||
++        op == JSOP_SPREADEVAL || op == JSOP_STRICTSPREADEVAL)
+     {
+-        uint32_t lineNum = parser->errorReporter().lineAt(pn->pn_pos.begin);
++        uint32_t lineNum = parser->errorReporter().lineAt(callNode->pn_pos.begin);
+         if (!emitUint32Operand(JSOP_LINENO, lineNum)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+@@ -7832,17 +7863,17 @@ BytecodeEmitter::emitPropertyList(ListNo
+                 return false;
+             }
+             if (!emit1(JSOP_POP)) {
+                 return false;
+             }
+         }
+ 
+         /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
+-        ParseNode* key = propdef->pn_left;
++        ParseNode* key = propdef->as<BinaryNode>().left();
+         bool isIndex = false;
+         if (key->isKind(ParseNodeKind::Number)) {
+             if (!emitNumberOp(key->pn_dval)) {
+                 return false;
+             }
+             isIndex = true;
+         } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                    key->isKind(ParseNodeKind::String))
+@@ -7856,38 +7887,39 @@ BytecodeEmitter::emitPropertyList(ListNo
+         } else {
+             if (!emitComputedPropertyName(key)) {
+                 return false;
+             }
+             isIndex = true;
+         }
+ 
+         /* Emit code for the property initializer. */
+-        if (!emitTree(propdef->pn_right)) {
++        ParseNode* propVal = propdef->as<BinaryNode>().right();
++        if (!emitTree(propVal)) {
+             return false;
+         }
+ 
+         JSOp op = propdef->getOp();
+         MOZ_ASSERT(op == JSOP_INITPROP ||
+                    op == JSOP_INITPROP_GETTER ||
+                    op == JSOP_INITPROP_SETTER);
+ 
+         FunctionPrefixKind prefixKind = op == JSOP_INITPROP_GETTER ? FunctionPrefixKind::Get
+                                         : op == JSOP_INITPROP_SETTER ? FunctionPrefixKind::Set
+                                         : FunctionPrefixKind::None;
+ 
+         if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER) {
+             objp.set(nullptr);
+         }
+ 
+-        if (propdef->pn_right->isKind(ParseNodeKind::Function) &&
+-            propdef->pn_right->pn_funbox->needsHomeObject())
++        if (propVal->isKind(ParseNodeKind::Function) &&
++            propVal->pn_funbox->needsHomeObject())
+         {
+-            MOZ_ASSERT(propdef->pn_right->pn_funbox->function()->allowSuperProperty());
+-            bool isAsync = propdef->pn_right->pn_funbox->isAsync();
++            MOZ_ASSERT(propVal->pn_funbox->function()->allowSuperProperty());
++            bool isAsync = propVal->pn_funbox->isAsync();
+             if (isAsync) {
+                 if (!emit1(JSOP_SWAP)) {
+                     return false;
+                 }
+             }
+             if (!emit2(JSOP_INITHOMEOBJECT, isIndex + isAsync)) {
+                 return false;
+             }
+@@ -7914,17 +7946,17 @@ BytecodeEmitter::emitPropertyList(ListNo
+               case JSOP_INITPROP:               op = JSOP_INITELEM;              break;
+               case JSOP_INITHIDDENPROP:         op = JSOP_INITHIDDENELEM;        break;
+               case JSOP_INITPROP_GETTER:        op = JSOP_INITELEM_GETTER;       break;
+               case JSOP_INITHIDDENPROP_GETTER:  op = JSOP_INITHIDDENELEM_GETTER; break;
+               case JSOP_INITPROP_SETTER:        op = JSOP_INITELEM_SETTER;       break;
+               case JSOP_INITHIDDENPROP_SETTER:  op = JSOP_INITHIDDENELEM_SETTER; break;
+               default: MOZ_CRASH("Invalid op");
+             }
+-            if (propdef->pn_right->isDirectRHSAnonFunction()) {
++            if (propVal->isDirectRHSAnonFunction()) {
+                 if (!emitDupAt(1)) {
+                     return false;
+                 }
+                 if (!emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
+                     return false;
+                 }
+             }
+             if (!emit1(op)) {
+@@ -7949,21 +7981,21 @@ BytecodeEmitter::emitPropertyList(ListNo
+                 {
+                     return false;
+                 }
+                 if (objp->inDictionaryMode()) {
+                     objp.set(nullptr);
+                 }
+             }
+ 
+-            if (propdef->pn_right->isDirectRHSAnonFunction()) {
++            if (propVal->isDirectRHSAnonFunction()) {
+                 MOZ_ASSERT(prefixKind == FunctionPrefixKind::None);
+ 
+                 RootedAtom keyName(cx, key->pn_atom);
+-                if (!setOrEmitSetFunName(propdef->pn_right, keyName)) {
++                if (!setOrEmitSetFunName(propVal, keyName)) {
+                     return false;
+                 }
+             }
+             if (!emitIndex32(op, index)) {
+                 return false;
+             }
+         }
+ 
+@@ -8148,17 +8180,17 @@ BytecodeEmitter::emitArray(ParseNode* ar
+             }
+         } else {
+             ParseNode* expr;
+             if (elem->isKind(ParseNodeKind::Spread)) {
+                 expr = elem->pn_kid;
+ 
+                 if (emitterMode == BytecodeEmitter::SelfHosting &&
+                     expr->isKind(ParseNodeKind::Call) &&
+-                    expr->pn_left->name() == cx->names().allowContentIter)
++                    expr->as<BinaryNode>().left()->name() == cx->names().allowContentIter)
+                 {
+                     allowSelfHostedIter = true;
+                 }
+             } else {
+                 expr = elem;
+             }
+             if (!emitTree(expr)) {                                       // ARRAY INDEX? VALUE
+                 return false;
+@@ -8363,18 +8395,18 @@ BytecodeEmitter::emitFunctionFormalParam
+     bool hasParameterExprs = funbox->hasParameterExprs;
+     bool hasRest = funbox->hasRest();
+ 
+     int16_t argSlot = 0;
+     for (ParseNode* arg : paramsBody->contentsTo(funBody)) {
+         ParseNode* bindingElement = arg;
+         ParseNode* initializer = nullptr;
+         if (arg->isKind(ParseNodeKind::Assign)) {
+-            bindingElement = arg->pn_left;
+-            initializer = arg->pn_right;
++            bindingElement = arg->as<AssignmentNode>().left();
++            initializer = arg->as<AssignmentNode>().right();
+         }
+ 
+         // Left-hand sides are either simple names or destructuring patterns.
+         MOZ_ASSERT(bindingElement->isKind(ParseNodeKind::Name) ||
+                    bindingElement->isKind(ParseNodeKind::Array) ||
+                    bindingElement->isKind(ParseNodeKind::Object));
+ 
+         // The rest parameter doesn't have an initializer.
+@@ -8865,30 +8897,33 @@ BytecodeEmitter::emitClass(ClassNode* cl
+     // The CONSTRUCTOR is left on stack if this is an expression.
+ 
+     MOZ_ALWAYS_TRUE(sc->setLocalStrictMode(savedStrictness));
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitExportDefault(ParseNode* pn)
+-{
+-    if (!emitTree(pn->pn_left)) {
+-        return false;
+-    }
+-
+-    if (pn->pn_right) {
+-        if (!emitLexicalInitialization(pn->pn_right)) {
+-            return false;
+-        }
+-
+-        if (pn->pn_left->isDirectRHSAnonFunction()) {
++BytecodeEmitter::emitExportDefault(BinaryNode* exportNode)
++{
++    MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportDefault));
++
++    ParseNode* nameNode = exportNode->left();
++    if (!emitTree(nameNode)) {
++        return false;
++    }
++
++    if (ParseNode* binding = exportNode->right()) {
++        if (!emitLexicalInitialization(binding)) {
++            return false;
++        }
++
++        if (nameNode->isDirectRHSAnonFunction()) {
+             HandlePropertyName name = cx->names().default_;
+-            if (!setOrEmitSetFunName(pn->pn_left, name)) {
++            if (!setOrEmitSetFunName(nameNode, name)) {
+                 return false;
+             }
+         }
+ 
+         if (!emit1(JSOP_POP)) {
+             return false;
+         }
+     }
+@@ -8936,29 +8971,29 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::Switch:
+         if (!emitSwitch(&pn->as<SwitchStatement>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::While:
+-        if (!emitWhile(pn)) {
++        if (!emitWhile(&pn->as<BinaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::DoWhile:
+-        if (!emitDo(pn)) {
++        if (!emitDo(&pn->as<BinaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::For:
+-        if (!emitFor(pn)) {
++        if (!emitFor(&pn->as<ForNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Break:
+         // Ensure that the column of the 'break' is set properly.
+         if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
+             return false;
+@@ -8976,29 +9011,29 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+         }
+ 
+         if (!emitContinue(pn->as<ContinueStatement>().label())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::With:
+-        if (!emitWith(pn)) {
++        if (!emitWith(&pn->as<BinaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Try:
+         if (!emitTry(&pn->as<TernaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Catch:
+-        if (!emitCatch(pn)) {
++        if (!emitCatch(&pn->as<BinaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Var:
+         if (!emitDeclarationList(&pn->as<ListNode>())) {
+             return false;
+         }
+@@ -9074,21 +9109,23 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+       case ParseNodeKind::BitXorAssign:
+       case ParseNodeKind::BitAndAssign:
+       case ParseNodeKind::LshAssign:
+       case ParseNodeKind::RshAssign:
+       case ParseNodeKind::UrshAssign:
+       case ParseNodeKind::MulAssign:
+       case ParseNodeKind::DivAssign:
+       case ParseNodeKind::ModAssign:
+-      case ParseNodeKind::PowAssign:
+-        if (!emitAssignment(pn->pn_left, pn->getKind(), pn->pn_right)) {
++      case ParseNodeKind::PowAssign: {
++        AssignmentNode* assignNode = &pn->as<AssignmentNode>();
++        if (!emitAssignment(assignNode->left(), assignNode->getKind(), assignNode->right())) {
+             return false;
+         }
+         break;
++      }
+ 
+       case ParseNodeKind::Conditional:
+         if (!emitConditionalExpression(pn->as<ConditionalExpression>(), valueUsage)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Or:
+@@ -9187,45 +9224,49 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+         break;
+ 
+       case ParseNodeKind::DeleteExpr:
+         if (!emitDeleteExpression(pn)) {
+             return false;
+         }
+         break;
+ 
+-      case ParseNodeKind::Dot:
+-        if (pn->as<PropertyAccess>().isSuper()) {
+-            if (!emitSuperGetProp(pn)) {
++      case ParseNodeKind::Dot: {
++        PropertyAccess* prop = &pn->as<PropertyAccess>();
++        if (prop->isSuper()) {
++            if (!emitSuperGetProp(prop)) {
+                 return false;
+             }
+         } else {
+-            if (!emitPropOp(pn, JSOP_GETPROP)) {
++            if (!emitPropOp(prop, JSOP_GETPROP)) {
+                 return false;
+             }
+         }
+         break;
+-
+-      case ParseNodeKind::Elem:
+-        if (pn->as<PropertyByValue>().isSuper()) {
+-            if (!emitSuperGetElem(pn)) {
++      }
++
++      case ParseNodeKind::Elem: {
++        PropertyByValue* elem = &pn->as<PropertyByValue>();
++        if (elem->isSuper()) {
++            if (!emitSuperGetElem(elem)) {
+                 return false;
+             }
+         } else {
+-            if (!emitElemOp(pn, JSOP_GETELEM)) {
++            if (!emitElemOp(elem, JSOP_GETELEM)) {
+                 return false;
+             }
+         }
+         break;
++      }
+ 
+       case ParseNodeKind::New:
+       case ParseNodeKind::TaggedTemplate:
+       case ParseNodeKind::Call:
+       case ParseNodeKind::SuperCall:
+-        if (!emitCallOrNew(pn, valueUsage)) {
++        if (!emitCallOrNew(&pn->as<BinaryNode>(), valueUsage)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::LexicalScope:
+         if (!emitLexicalScope(pn)) {
+             return false;
+         }
+@@ -9248,17 +9289,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+             if (!emitTree(pn->pn_kid)) {
+                 return false;
+             }
+         }
+         break;
+ 
+       case ParseNodeKind::ExportDefault:
+         MOZ_ASSERT(sc->isModuleContext());
+-        if (!emitExportDefault(pn)) {
++        if (!emitExportDefault(&pn->as<BinaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::ExportFrom:
+         MOZ_ASSERT(sc->isModuleContext());
+         break;
+ 
+@@ -9353,17 +9394,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+         }
+         break;
+ 
+       case ParseNodeKind::CallImport:
+         reportError(nullptr, JSMSG_NO_DYNAMIC_IMPORT);
+         return false;
+ 
+       case ParseNodeKind::SetThis:
+-        if (!emitSetThis(pn)) {
++        if (!emitSetThis(&pn->as<BinaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::PropertyName:
+       case ParseNodeKind::PosHolder:
+         MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder or ::Property");
+ 
+diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
+--- a/js/src/frontend/BytecodeEmitter.h
++++ b/js/src/frontend/BytecodeEmitter.h
+@@ -530,17 +530,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
+     MOZ_MUST_USE bool emitN(JSOp op, size_t extra, ptrdiff_t* offset = nullptr);
+ 
+     MOZ_MUST_USE bool emitNumberOp(double dval);
+ 
+     MOZ_MUST_USE bool emitThisLiteral(ParseNode* pn);
+     MOZ_MUST_USE bool emitGetFunctionThis(ParseNode* pn);
+     MOZ_MUST_USE bool emitGetThisForSuperBase(ParseNode* pn);
+-    MOZ_MUST_USE bool emitSetThis(ParseNode* pn);
++    MOZ_MUST_USE bool emitSetThis(BinaryNode* setThisNode);
+     MOZ_MUST_USE bool emitCheckDerivedClassConstructorReturn();
+ 
+     // Handle jump opcodes and jump targets.
+     MOZ_MUST_USE bool emitJumpTarget(JumpTarget* target);
+     MOZ_MUST_USE bool emitJumpNoFallthrough(JSOp op, JumpList* jump);
+     MOZ_MUST_USE bool emitJump(JSOp op, JumpList* jump);
+     MOZ_MUST_USE bool emitBackwardJump(JSOp op, JumpTarget target, JumpList* jump,
+                                        JumpTarget* fallthrough);
+@@ -654,45 +654,45 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_MUST_USE bool emitYieldOp(JSOp op);
+     MOZ_MUST_USE bool emitYieldStar(ParseNode* iter);
+     MOZ_MUST_USE bool emitAwaitInInnermostScope() {
+         return emitAwaitInScope(*innermostEmitterScope());
+     }
+     MOZ_MUST_USE bool emitAwaitInInnermostScope(ParseNode* pn);
+     MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope);
+ 
+-    MOZ_MUST_USE bool emitPropLHS(ParseNode* pn);
+-    MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op);
++    MOZ_MUST_USE bool emitPropLHS(PropertyAccess* prop);
++    MOZ_MUST_USE bool emitPropOp(PropertyAccess* prop, JSOp op);
+     MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn);
+ 
+     MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
+     MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow,
+                                        bool isGenerator);
+ 
+     MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName);
+ 
+     // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
+     // opcode onto the stack in the right order. In the case of SETELEM, the
+     // value to be assigned must already be pushed.
+     enum class EmitElemOption { Get, Call, IncDec, CompoundAssign, Ref };
+-    MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts);
++    MOZ_MUST_USE bool emitElemOperands(PropertyByValue* elem, EmitElemOption opts);
+ 
+     MOZ_MUST_USE bool emitElemOpBase(JSOp op);
+-    MOZ_MUST_USE bool emitElemOp(ParseNode* pn, JSOp op);
++    MOZ_MUST_USE bool emitElemOp(PropertyByValue* elem, JSOp op);
+     MOZ_MUST_USE bool emitElemIncDec(ParseNode* pn);
+ 
+-    MOZ_MUST_USE bool emitCatch(ParseNode* pn);
++    MOZ_MUST_USE bool emitCatch(BinaryNode* catchClause);
+     MOZ_MUST_USE bool emitIf(TernaryNode* ifNode);
+-    MOZ_MUST_USE bool emitWith(ParseNode* pn);
++    MOZ_MUST_USE bool emitWith(BinaryNode* withNode);
+ 
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(const LabeledStatement* pn);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn);
+     MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body,
+                                            EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
+-    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* pn);
++    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* switchStmt);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(TernaryNode* tryNode);
+ 
+     enum DestructuringFlavor {
+         // Destructuring into a declaration.
+         DestructuringDeclaration,
+ 
+         // Destructuring into a formal parameter, when the formal parameters
+         // contain an expression that might be evaluated, and thus require
+@@ -802,33 +802,34 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn);
+ 
+     MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional,
+                                                 ValueUsage valueUsage = ValueUsage::WantValue);
+ 
+     bool isRestParameter(ParseNode* pn);
+ 
+     MOZ_MUST_USE bool emitArguments(ListNode* argsList, bool callop, bool spread);
+-    MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
+-    MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
+-    MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
++    MOZ_MUST_USE bool emitCallOrNew(BinaryNode* callNode,
++                                    ValueUsage valueUsage = ValueUsage::WantValue);
++    MOZ_MUST_USE bool emitSelfHostedCallFunction(BinaryNode* callNode);
++    MOZ_MUST_USE bool emitSelfHostedResumeGenerator(BinaryNode* callNode);
+     MOZ_MUST_USE bool emitSelfHostedForceInterpreter();
+-    MOZ_MUST_USE bool emitSelfHostedAllowContentIter(ParseNode* pn);
+-    MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(ParseNode* pn);
+-    MOZ_MUST_USE bool emitSelfHostedGetPropertySuper(ParseNode* pn);
+-    MOZ_MUST_USE bool emitSelfHostedHasOwn(ParseNode* pn);
++    MOZ_MUST_USE bool emitSelfHostedAllowContentIter(BinaryNode* callNode);
++    MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(BinaryNode* callNode);
++    MOZ_MUST_USE bool emitSelfHostedGetPropertySuper(BinaryNode* callNode);
++    MOZ_MUST_USE bool emitSelfHostedHasOwn(BinaryNode* callNode);
+ 
+-    MOZ_MUST_USE bool emitDo(ParseNode* pn);
+-    MOZ_MUST_USE bool emitWhile(ParseNode* pn);
++    MOZ_MUST_USE bool emitDo(BinaryNode* doNode);
++    MOZ_MUST_USE bool emitWhile(BinaryNode* whileNode);
+ 
+-    MOZ_MUST_USE bool emitFor(ParseNode* pn,
++    MOZ_MUST_USE bool emitFor(ForNode* forNode,
+                               const EmitterScope* headLexicalEmitterScope = nullptr);
+-    MOZ_MUST_USE bool emitCStyleFor(ParseNode* pn, const EmitterScope* headLexicalEmitterScope);
+-    MOZ_MUST_USE bool emitForIn(ParseNode* pn, const EmitterScope* headLexicalEmitterScope);
+-    MOZ_MUST_USE bool emitForOf(ParseNode* pn, const EmitterScope* headLexicalEmitterScope);
++    MOZ_MUST_USE bool emitCStyleFor(ForNode* forNode, const EmitterScope* headLexicalEmitterScope);
++    MOZ_MUST_USE bool emitForIn(ForNode* forNode, const EmitterScope* headLexicalEmitterScope);
++    MOZ_MUST_USE bool emitForOf(ForNode* forNode, const EmitterScope* headLexicalEmitterScope);
+ 
+     MOZ_MUST_USE bool emitInitializeForInOrOfTarget(TernaryNode* forHead);
+ 
+     MOZ_MUST_USE bool emitBreak(PropertyName* label);
+     MOZ_MUST_USE bool emitContinue(PropertyName* label);
+ 
+     MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ListNode* paramsBody);
+     MOZ_MUST_USE bool emitFunctionFormalParameters(ListNode* paramsBody);
+@@ -843,26 +844,26 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     // It will pop the iterator and I, then iterate over the iterator by calling
+     // |.next()| and put the results into the I-th element of array with
+     // incrementing I, then push the result I (it will be original I +
+     // iteration count). The stack after iteration will look like |ARRAY INDEX|.
+     MOZ_MUST_USE bool emitSpread(bool allowSelfHosted = false);
+ 
+     MOZ_MUST_USE bool emitClass(ClassNode* classNode);
+     MOZ_MUST_USE bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
+-    MOZ_MUST_USE bool emitSuperGetProp(ParseNode* pn, bool isCall = false);
+-    MOZ_MUST_USE bool emitSuperElemOperands(ParseNode* pn,
++    MOZ_MUST_USE bool emitSuperGetProp(PropertyAccess* prop, bool isCall = false);
++    MOZ_MUST_USE bool emitSuperElemOperands(PropertyByValue* elem,
+                                             EmitElemOption opts = EmitElemOption::Get);
+-    MOZ_MUST_USE bool emitSuperGetElem(ParseNode* pn, bool isCall = false);
++    MOZ_MUST_USE bool emitSuperGetElem(PropertyByValue* elem, bool isCall = false);
+ 
+     MOZ_MUST_USE bool emitCallee(ParseNode* callee, ParseNode* call, bool* callop);
+ 
+     MOZ_MUST_USE bool emitPipeline(ListNode* node);
+ 
+-    MOZ_MUST_USE bool emitExportDefault(ParseNode* pn);
++    MOZ_MUST_USE bool emitExportDefault(BinaryNode* exportNode);
+ };
+ 
+ class MOZ_RAII AutoCheckUnstableEmitterScope {
+ #ifdef DEBUG
+     bool prev_;
+     BytecodeEmitter* bce_;
+ #endif
+ 
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -141,24 +141,24 @@ ContainsHoistedDeclaration(JSContext* cx
+       case ParseNodeKind::ExportBatchSpec:
+       case ParseNodeKind::CallImport:
+         *result = false;
+         return true;
+ 
+       // Statements possibly containing hoistable declarations only in the left
+       // half, in ParseNode terms -- the loop body in AST terms.
+       case ParseNodeKind::DoWhile:
+-        return ContainsHoistedDeclaration(cx, node->pn_left, result);
++        return ContainsHoistedDeclaration(cx, node->as<BinaryNode>().left(), result);
+ 
+       // Statements possibly containing hoistable declarations only in the
+       // right half, in ParseNode terms -- the loop body or nested statement
+       // (usually a block statement), in AST terms.
+       case ParseNodeKind::While:
+       case ParseNodeKind::With:
+-        return ContainsHoistedDeclaration(cx, node->pn_right, result);
++        return ContainsHoistedDeclaration(cx, node->as<BinaryNode>().right(), result);
+ 
+       case ParseNodeKind::Label:
+         return ContainsHoistedDeclaration(cx, node->pn_expr, result);
+ 
+       // Statements with more complicated structures.
+ 
+       // if-statement nodes may have hoisted declarations in their consequent
+       // and alternative components.
+@@ -194,20 +194,20 @@ ContainsHoistedDeclaration(JSContext* cx
+         }
+         if (*result) {
+             return true;
+         }
+ 
+         if (ParseNode* catchScope = tryNode->kid2()) {
+             MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+ 
+-            ParseNode* catchNode = catchScope->pn_expr;
++            BinaryNode* catchNode = &catchScope->scopeBody()->as<BinaryNode>();
+             MOZ_ASSERT(catchNode->isKind(ParseNodeKind::Catch));
+ 
+-            ParseNode* catchStatements = catchNode->pn_right;
++            ParseNode* catchStatements = catchNode->right();
+             if (!ContainsHoistedDeclaration(cx, catchStatements, result)) {
+                 return false;
+             }
+             if (*result) {
+                 return true;
+             }
+         }
+ 
+@@ -217,27 +217,29 @@ ContainsHoistedDeclaration(JSContext* cx
+ 
+         *result = false;
+         return true;
+       }
+ 
+       // A switch node's left half is an expression; only its right half (a
+       // list of cases/defaults, or a block node) could contain hoisted
+       // declarations.
+-      case ParseNodeKind::Switch:
+-        MOZ_ASSERT(node->isArity(PN_BINARY));
+-        return ContainsHoistedDeclaration(cx, node->pn_right, result);
++      case ParseNodeKind::Switch: {
++        SwitchStatement* switchNode = &node->as<SwitchStatement>();
++        return ContainsHoistedDeclaration(cx, &switchNode->lexicalForCaseList(), result);
++      }
+ 
+-      case ParseNodeKind::Case:
+-        return ContainsHoistedDeclaration(cx, node->as<CaseClause>().statementList(), result);
++      case ParseNodeKind::Case: {
++        CaseClause* caseClause = &node->as<CaseClause>();
++        return ContainsHoistedDeclaration(cx, caseClause->statementList(), result);
++      }
+ 
+       case ParseNodeKind::For: {
+-        MOZ_ASSERT(node->isArity(PN_BINARY));
+-
+-        TernaryNode* loopHead = &node->pn_left->as<TernaryNode>();
++        ForNode* forNode = &node->as<ForNode>();
++        TernaryNode* loopHead = forNode->head();
+         MOZ_ASSERT(loopHead->isKind(ParseNodeKind::ForHead) ||
+                    loopHead->isKind(ParseNodeKind::ForIn) ||
+                    loopHead->isKind(ParseNodeKind::ForOf));
+ 
+         if (loopHead->isKind(ParseNodeKind::ForHead)) {
+             // for (init?; cond?; update?), with only init possibly containing
+             // a hoisted declaration.  (Note: a lexical-declaration |init| is
+             // (at present) hoisted in SpiderMonkey parlance -- but such
+@@ -264,17 +266,17 @@ ContainsHoistedDeclaration(JSContext* cx
+             // first kid.
+             ParseNode* decl = loopHead->kid1();
+             if (decl && decl->isKind(ParseNodeKind::Var)) {
+                 *result = true;
+                 return true;
+             }
+         }
+ 
+-        ParseNode* loopBody = node->pn_right;
++        ParseNode* loopBody = forNode->body();
+         return ContainsHoistedDeclaration(cx, loopBody, result);
+       }
+ 
+       case ParseNodeKind::LexicalScope: {
+         MOZ_ASSERT(node->isArity(PN_SCOPE));
+         ParseNode* expr = node->pn_expr;
+ 
+         if (expr->isKind(ParseNodeKind::For) || expr->isKind(ParseNodeKind::Function)) {
+@@ -1204,29 +1206,28 @@ FoldTry(JSContext* cx, TernaryNode* node
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldCatch(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldCatch(JSContext* cx, BinaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::Catch));
+-    MOZ_ASSERT(node->isArity(PN_BINARY));
+-
+-    if (ParseNode*& declPattern = node->pn_left) {
+-        if (!Fold(cx, &declPattern, parser)) {
++    ParseNode** declPattern = node->unsafeLeftReference();
++    if (*declPattern) {
++        if (!Fold(cx, declPattern, parser)) {
+             return false;
+         }
+     }
+ 
+-    if (ParseNode*& statements = node->pn_right) {
+-        if (!Fold(cx, &statements, parser)) {
++    ParseNode** statements = node->unsafeRightReference();
++    if (*statements) {
++        if (!Fold(cx, statements, parser)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+@@ -1250,31 +1251,28 @@ FoldClass(JSContext* cx, ClassNode* node
+ 
+     ParseNode** body = node->unsafeKid3Reference();
+     return Fold(cx, body, parser);
+ }
+ 
+ static bool
+ FoldElement(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
+ {
+-    ParseNode* node = *nodePtr;
++    PropertyByValue* elem = &(*nodePtr)->as<PropertyByValue>();
+ 
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::Elem));
+-    MOZ_ASSERT(node->isArity(PN_BINARY));
+-
+-    ParseNode*& expr = node->pn_left;
+-    if (!Fold(cx, &expr, parser)) {
++    if (!Fold(cx, elem->unsafeLeftReference(), parser)) {
+         return false;
+     }
+ 
+-    ParseNode*& key = node->pn_right;
+-    if (!Fold(cx, &key, parser)) {
++    if (!Fold(cx, elem->unsafeRightReference(), parser)) {
+         return false;
+     }
+ 
++    ParseNode* expr = &elem->expression();
++    ParseNode* key = &elem->key();
+     PropertyName* name = nullptr;
+     if (key->isKind(ParseNodeKind::String)) {
+         JSAtom* atom = key->pn_atom;
+         uint32_t index;
+ 
+         if (atom->isIndex(&index)) {
+             // Optimization 1: We have something like expr["100"]. This is
+             // equivalent to expr[100] which is faster.
+@@ -1308,17 +1306,17 @@ FoldElement(JSContext* cx, ParseNode** n
+     ParseNode* nameNode = parser.newPropertyName(name, key->pn_pos);
+     if (!nameNode) {
+         return false;
+     }
+     ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode);
+     if (!dottedAccess) {
+         return false;
+     }
+-    dottedAccess->setInParens(node->isInParens());
++    dottedAccess->setInParens(elem->isInParens());
+     ReplaceNode(nodePtr, dottedAccess);
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldAdd(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
+ {
+@@ -1466,44 +1464,42 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
+         // with that constant.
+         ReplaceNode(nodePtr, current);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldCall(JSContext* cx, BinaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Call) ||
+                node->isKind(ParseNodeKind::SuperCall) ||
+                node->isKind(ParseNodeKind::New) ||
+                node->isKind(ParseNodeKind::TaggedTemplate));
+-    MOZ_ASSERT(node->isArity(PN_BINARY));
+ 
+     // Don't fold a parenthesized callable component in an invocation, as this
+     // might cause a different |this| value to be used, changing semantics:
+     //
+     //   var prop = "global";
+     //   var obj = { prop: "obj", f: function() { return this.prop; } };
+     //   assertEq((true ? obj.f : null)(), "global");
+     //   assertEq(obj.f(), "obj");
+     //   assertEq((true ? obj.f : null)``, "global");
+     //   assertEq(obj.f``, "obj");
+     //
+     // See bug 537673 and bug 1182373.
+-    ParseNode** pn_callee = &node->pn_left;
+-    if (node->isKind(ParseNodeKind::New) || !(*pn_callee)->isInParens()) {
+-        if (!Fold(cx, pn_callee, parser)) {
++    ParseNode* callee = node->left();
++    if (node->isKind(ParseNodeKind::New) || !callee->isInParens()) {
++        if (!Fold(cx, node->unsafeLeftReference(), parser)) {
+             return false;
+         }
+     }
+ 
+-    ParseNode** pn_args = &node->pn_right;
+-    if (!Fold(cx, pn_args, parser)) {
++    if (!Fold(cx, node->unsafeRightReference(), parser)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldArguments(JSContext* cx, ListNode* node, PerHandlerParser<FullParseHandler>& parser)
+@@ -1561,27 +1557,23 @@ FoldForHead(JSContext* cx, TernaryNode* 
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldDottedProperty(JSContext* cx, PropertyAccess* prop, PerHandlerParser<FullParseHandler>& parser)
+ {
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::Dot));
+-    MOZ_ASSERT(node->isArity(PN_BINARY));
+-
+     // Iterate through a long chain of dotted property accesses to find the
+     // most-nested non-dotted property node, then fold that.
+-    ParseNode** nested = &node->pn_left;
++    ParseNode** nested = prop->unsafeLeftReference();
+     while ((*nested)->isKind(ParseNodeKind::Dot)) {
+-        MOZ_ASSERT((*nested)->isArity(PN_BINARY));
+-        nested = &(*nested)->pn_left;
++        nested = (*nested)->as<PropertyAccess>().unsafeLeftReference();
+     }
+ 
+     return Fold(cx, nested, parser);
+ }
+ 
+ static bool
+ FoldName(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+@@ -1678,18 +1670,17 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+       case ParseNodeKind::ComputedName:
+       case ParseNodeKind::Spread:
+       case ParseNodeKind::Export:
+       case ParseNodeKind::Void:
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+         return Fold(cx, &pn->pn_kid, parser);
+ 
+       case ParseNodeKind::ExportDefault:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        return Fold(cx, &pn->pn_left, parser);
++        return Fold(cx, pn->as<BinaryNode>().unsafeLeftReference(), parser);
+ 
+       case ParseNodeKind::This:
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+         if (ParseNode*& expr = pn->pn_kid) {
+             return Fold(cx, &expr, parser);
+         }
+         return true;
+ 
+@@ -1744,22 +1735,25 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+       case ParseNodeKind::ParamsBody:
+       case ParseNodeKind::CallSiteObj:
+       case ParseNodeKind::ExportSpecList:
+       case ParseNodeKind::ImportSpecList:
+         return FoldList(cx, &pn->as<ListNode>(), parser);
+ 
+-      case ParseNodeKind::InitialYield:
++      case ParseNodeKind::InitialYield: {
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        MOZ_ASSERT(pn->pn_kid->isKind(ParseNodeKind::Assign) &&
+-                   pn->pn_kid->pn_left->isKind(ParseNodeKind::Name) &&
+-                   pn->pn_kid->pn_right->isKind(ParseNodeKind::Generator));
++#ifdef DEBUG
++        AssignmentNode* assignNode = &pn->pn_kid->as<AssignmentNode>();
++        MOZ_ASSERT(assignNode->left()->isKind(ParseNodeKind::Name));
++        MOZ_ASSERT(assignNode->right()->isKind(ParseNodeKind::Generator));
++#endif
+         return true;
++      }
+ 
+       case ParseNodeKind::YieldStar:
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+         return Fold(cx, &pn->pn_kid, parser);
+ 
+       case ParseNodeKind::Yield:
+       case ParseNodeKind::Await:
+         MOZ_ASSERT(pn->isArity(PN_UNARY));
+@@ -1770,33 +1764,35 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+ 
+       case ParseNodeKind::Return:
+         return FoldReturn(cx, pn, parser);
+ 
+       case ParseNodeKind::Try:
+         return FoldTry(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::Catch:
+-        return FoldCatch(cx, pn, parser);
++        return FoldCatch(cx, &pn->as<BinaryNode>(), parser);
+ 
+       case ParseNodeKind::Class:
+         return FoldClass(cx, &pn->as<ClassNode>(), parser);
+ 
+-      case ParseNodeKind::Elem:
++      case ParseNodeKind::Elem: {
++        MOZ_ASSERT((*pnp)->is<PropertyByValue>());
+         return FoldElement(cx, pnp, parser);
++      }
+ 
+       case ParseNodeKind::Add:
+         MOZ_ASSERT((*pnp)->is<ListNode>());
+         return FoldAdd(cx, pnp, parser);
+ 
+       case ParseNodeKind::Call:
+       case ParseNodeKind::New:
+       case ParseNodeKind::SuperCall:
+       case ParseNodeKind::TaggedTemplate:
+-        return FoldCall(cx, pn, parser);
++        return FoldCall(cx, &pn->as<BinaryNode>(), parser);
+ 
+       case ParseNodeKind::Arguments:
+         return FoldArguments(cx, &pn->as<ListNode>(), parser);
+ 
+       case ParseNodeKind::Switch:
+       case ParseNodeKind::Colon:
+       case ParseNodeKind::Assign:
+       case ParseNodeKind::AddAssign:
+@@ -1813,85 +1809,94 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+       case ParseNodeKind::PowAssign:
+       case ParseNodeKind::Import:
+       case ParseNodeKind::ExportFrom:
+       case ParseNodeKind::Shorthand:
+       case ParseNodeKind::For:
+       case ParseNodeKind::ClassMethod:
+       case ParseNodeKind::ImportSpec:
+       case ParseNodeKind::ExportSpec:
+-      case ParseNodeKind::SetThis:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        return Fold(cx, &pn->pn_left, parser) &&
+-               Fold(cx, &pn->pn_right, parser);
++      case ParseNodeKind::SetThis: {
++        BinaryNode* node = &pn->as<BinaryNode>();
++        return Fold(cx, node->unsafeLeftReference(), parser) &&
++               Fold(cx, node->unsafeRightReference(), parser);
++      }
+ 
+       case ParseNodeKind::NewTarget:
+-      case ParseNodeKind::ImportMeta:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
+-        MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::PosHolder));
++      case ParseNodeKind::ImportMeta: {
++#ifdef DEBUG
++        BinaryNode* node = &pn->as<BinaryNode>();
++        MOZ_ASSERT(node->left()->isKind(ParseNodeKind::PosHolder));
++        MOZ_ASSERT(node->right()->isKind(ParseNodeKind::PosHolder));
++#endif
+         return true;
++      }
+ 
+-      case ParseNodeKind::CallImport:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
+-        return Fold(cx, &pn->pn_right, parser);
++      case ParseNodeKind::CallImport: {
++        BinaryNode* node = &pn->as<BinaryNode>();
++        MOZ_ASSERT(node->left()->isKind(ParseNodeKind::PosHolder));
++        return Fold(cx, node->unsafeRightReference(), parser);
++      }
+ 
+-      case ParseNodeKind::ClassNames:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        if (ParseNode*& outerBinding = pn->pn_left) {
+-            if (!Fold(cx, &outerBinding, parser)) {
++      case ParseNodeKind::ClassNames: {
++        ClassNames* names = &pn->as<ClassNames>();
++        if (names->outerBinding()) {
++            if (!Fold(cx, names->unsafeLeftReference(), parser)) {
+                 return false;
+             }
+         }
+-        return Fold(cx, &pn->pn_right, parser);
++        return Fold(cx, names->unsafeRightReference(), parser);
++      }
+ 
+-      case ParseNodeKind::DoWhile:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        return Fold(cx, &pn->pn_left, parser) &&
+-               FoldCondition(cx, &pn->pn_right, parser);
++      case ParseNodeKind::DoWhile: {
++        BinaryNode* node = &pn->as<BinaryNode>();
++        return Fold(cx, node->unsafeLeftReference(), parser) &&
++               FoldCondition(cx, node->unsafeRightReference(), parser);
++      }
+ 
+-      case ParseNodeKind::While:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        return FoldCondition(cx, &pn->pn_left, parser) &&
+-               Fold(cx, &pn->pn_right, parser);
++      case ParseNodeKind::While: {
++        BinaryNode* node = &pn->as<BinaryNode>();
++        return FoldCondition(cx, node->unsafeLeftReference(), parser) &&
++               Fold(cx, node->unsafeRightReference(), parser);
++      }
+ 
+       case ParseNodeKind::Case: {
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
++        CaseClause* caseClause = &pn->as<CaseClause>();
+ 
+-        // pn_left is null for DefaultClauses.
+-        if (pn->pn_left) {
+-            if (!Fold(cx, &pn->pn_left, parser)) {
++        // left (caseExpression) is null for DefaultClauses.
++        if (caseClause->left()) {
++            if (!Fold(cx, caseClause->unsafeLeftReference(), parser)) {
+                 return false;
+             }
+         }
+-        return Fold(cx, &pn->pn_right, parser);
++        return Fold(cx, caseClause->unsafeRightReference(), parser);
+       }
+ 
+-      case ParseNodeKind::With:
+-        MOZ_ASSERT(pn->isArity(PN_BINARY));
+-        return Fold(cx, &pn->pn_left, parser) &&
+-               Fold(cx, &pn->pn_right, parser);
++      case ParseNodeKind::With: {
++        BinaryNode* node = &pn->as<BinaryNode>();
++        return Fold(cx, node->unsafeLeftReference(), parser) &&
++               Fold(cx, node->unsafeRightReference(), parser);
++      }
+ 
+       case ParseNodeKind::ForIn:
+       case ParseNodeKind::ForOf:
+         return FoldForInOrOf(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::ForHead:
+         return FoldForHead(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::Label:
+         MOZ_ASSERT(pn->isArity(PN_NAME));
+         return Fold(cx, &pn->pn_expr, parser);
+ 
+       case ParseNodeKind::PropertyName:
+         MOZ_CRASH("unreachable, handled by ::Dot");
+ 
+       case ParseNodeKind::Dot:
+-        return FoldDottedProperty(cx, pn, parser);
++        return FoldDottedProperty(cx, &pn->as<PropertyAccess>(), parser);
+ 
+       case ParseNodeKind::LexicalScope:
+         MOZ_ASSERT(pn->isArity(PN_SCOPE));
+         if (!pn->scopeBody()) {
+             return true;
+         }
+         return Fold(cx, &pn->pn_u.scope.body, parser);
+ 
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -242,19 +242,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+ 
+     ParseNode* newSpread(uint32_t begin, ParseNode* kid) {
+         TokenPos pos(begin, kid->pn_pos.end);
+         return new_<UnaryNode>(ParseNodeKind::Spread, pos, kid);
+     }
+ 
+   private:
+-    ParseNode* newBinary(ParseNodeKind kind, ParseNode* left, ParseNode* right,
+-                         JSOp op = JSOP_NOP)
+-    {
++    BinaryNodeType newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
+         TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
+         return new_<BinaryNode>(kind, op, pos, left, right);
+     }
+ 
+   public:
+     ParseNode* appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
+                                   ParseContext* pc)
+     {
+@@ -295,46 +293,46 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     void addArrayElement(ListNodeType literal, Node element) {
+         if (!element->isConstant()) {
+             literal->setHasNonConstInitializer();
+         }
+         addList(/* list = */ literal, /* child = */ element);
+     }
+ 
+-    ParseNode* newCall(ParseNode* callee, ParseNode* args) {
++    BinaryNodeType newCall(Node callee, Node args) {
+         return new_<BinaryNode>(ParseNodeKind::Call, JSOP_CALL, callee, args);
+     }
+ 
+     ListNodeType newArguments(const TokenPos& pos) {
+         return new_<ListNode>(ParseNodeKind::Arguments, JSOP_NOP, pos);
+     }
+ 
+-    ParseNode* newSuperCall(ParseNode* callee, ParseNode* args) {
++    BinaryNodeType newSuperCall(Node callee, Node args) {
+         return new_<BinaryNode>(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee, args);
+     }
+ 
+-    ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) {
++    BinaryNodeType newTaggedTemplate(Node tag, Node args) {
+         return new_<BinaryNode>(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args);
+     }
+ 
+     ListNodeType newObjectLiteral(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::Object, TokenPos(begin, begin + 1));
+     }
+ 
+     ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) {
+         return new_<ClassNode>(name, heritage, methodBlock, pos);
+     }
+     ListNodeType newClassMethodList(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::ClassMethodList, TokenPos(begin, begin + 1));
+     }
+-    ParseNode* newClassNames(ParseNode* outer, ParseNode* inner, const TokenPos& pos) {
++    ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
+         return new_<ClassNames>(outer, inner, pos);
+     }
+-    ParseNode* newNewTarget(ParseNode* newHolder, ParseNode* targetHolder) {
++    BinaryNodeType newNewTarget(Node newHolder, Node targetHolder) {
+         return new_<BinaryNode>(ParseNodeKind::NewTarget, JSOP_NOP, newHolder, targetHolder);
+     }
+     ParseNode* newPosHolder(const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::PosHolder, pos);
+     }
+     ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) {
+         return new_<UnaryNode>(ParseNodeKind::SuperBase, pos, thisName);
+     }
+@@ -348,50 +346,50 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         ParseNode* mutation = newUnary(ParseNodeKind::MutateProto, begin, expr);
+         if (!mutation) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ mutation);
+         return true;
+     }
+ 
+-    ParseNode* newPropertyDefinition(ParseNode* key, ParseNode* val) {
++    BinaryNodeType newPropertyDefinition(Node key, Node val) {
+         MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+         checkAndSetIsDirectRHSAnonFunction(val);
+         return newBinary(ParseNodeKind::Colon, key, val, JSOP_INITPROP);
+     }
+ 
+-    void addPropertyDefinition(ListNodeType literal, Node propdef) {
++    void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+         MOZ_ASSERT(propdef->isKind(ParseNodeKind::Colon));
+ 
+-        if (!propdef->pn_right->isConstant()) {
++        if (!propdef->right()->isConstant()) {
+             literal->setHasNonConstInitializer();
+         }
+ 
+         addList(/* list = */ literal, /* child = */ propdef);
+     }
+ 
+     MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node val) {
+-        ParseNode* propdef = newPropertyDefinition(key, val);
++        BinaryNode* propdef = newPropertyDefinition(key, val);
+         if (!propdef) {
+             return false;
+         }
+         addPropertyDefinition(literal, propdef);
+         return true;
+     }
+ 
+     MOZ_MUST_USE bool addShorthand(ListNodeType literal, Node name, Node expr) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+         MOZ_ASSERT(name->isKind(ParseNodeKind::ObjectPropertyName));
+         MOZ_ASSERT(expr->isKind(ParseNodeKind::Name));
+         MOZ_ASSERT(name->pn_atom == expr->pn_atom);
+ 
+         literal->setHasNonConstInitializer();
+-        ParseNode* propdef = newBinary(ParseNodeKind::Shorthand, name, expr, JSOP_INITPROP);
++        BinaryNode* propdef = newBinary(ParseNodeKind::Shorthand, name, expr, JSOP_INITPROP);
+         if (!propdef) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ propdef);
+         return true;
+     }
+ 
+     MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) {
+@@ -425,17 +423,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key,
+                                                Node fn, AccessorType atype, bool isStatic)
+     {
+         MOZ_ASSERT(methodList->isKind(ParseNodeKind::ClassMethodList));
+         MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+ 
+         checkAndSetIsDirectRHSAnonFunction(fn);
+ 
+-        ParseNode* classMethod = new_<ClassMethod>(key, fn, AccessorTypeToJSOp(atype), isStatic);
++        ClassMethod* classMethod = new_<ClassMethod>(key, fn, AccessorTypeToJSOp(atype), isStatic);
+         if (!classMethod) {
+             return false;
+         }
+         addList(/* list = */ methodList, /* child = */ classMethod);
+         return true;
+     }
+ 
+     ParseNode* newInitialYieldExpression(uint32_t begin, ParseNode* gen) {
+@@ -483,24 +481,22 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         }
+     }
+ 
+     void setListEndPosition(ListNodeType list, const TokenPos& pos) {
+         MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));
+         list->pn_pos.end = pos.end;
+     }
+ 
+-    void addCaseStatementToList(ListNodeType list, Node caseClause) {
++    void addCaseStatementToList(ListNodeType list, CaseClauseType caseClause) {
+         MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));
+-        MOZ_ASSERT(caseClause->isKind(ParseNodeKind::Case));
+-        MOZ_ASSERT(caseClause->pn_right->isKind(ParseNodeKind::StatementList));
+ 
+         addList(/* list = */ list, /* child = */ caseClause);
+ 
+-        if (caseClause->pn_right->as<ListNode>().hasTopLevelFunctionDeclarations()) {
++        if (caseClause->statementList()->hasTopLevelFunctionDeclarations()) {
+             list->setHasTopLevelFunctionDeclarations();
+         }
+     }
+ 
+     MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) {
+         MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
+ 
+         TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1);
+@@ -521,82 +517,73 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         if (!initialYield) {
+             return false;
+         }
+ 
+         stmtList->prepend(initialYield);
+         return true;
+     }
+ 
+-    ParseNode* newSetThis(ParseNode* thisName, ParseNode* val) {
++    BinaryNodeType newSetThis(Node thisName, Node value) {
+         MOZ_ASSERT(thisName->getOp() == JSOP_GETNAME);
+         thisName->setOp(JSOP_SETNAME);
+-        return newBinary(ParseNodeKind::SetThis, thisName, val);
++        return newBinary(ParseNodeKind::SetThis, thisName, value);
+     }
+ 
+     ParseNode* newEmptyStatement(const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::EmptyStatement, pos);
+     }
+ 
+-    ParseNode* newImportDeclaration(ParseNode* importSpecSet,
+-                                    ParseNode* moduleSpec, const TokenPos& pos)
+-    {
+-        ParseNode* pn = new_<BinaryNode>(ParseNodeKind::Import, JSOP_NOP, pos,
+-                                         importSpecSet, moduleSpec);
+-        if (!pn) {
+-            return null();
+-        }
+-        return pn;
++    BinaryNodeType newImportDeclaration(Node importSpecSet, Node moduleSpec, const TokenPos& pos) {
++        return new_<BinaryNode>(ParseNodeKind::Import, JSOP_NOP, pos,
++                                importSpecSet, moduleSpec);
+     }
+ 
+-    ParseNode* newImportSpec(ParseNode* importNameNode, ParseNode* bindingName) {
++    BinaryNodeType newImportSpec(Node importNameNode, Node bindingName) {
+         return newBinary(ParseNodeKind::ImportSpec, importNameNode, bindingName);
+     }
+ 
+     ParseNode* newExportDeclaration(ParseNode* kid, const TokenPos& pos) {
+         return new_<UnaryNode>(ParseNodeKind::Export, pos, kid);
+     }
+ 
+-    ParseNode* newExportFromDeclaration(uint32_t begin, ParseNode* exportSpecSet,
+-                                        ParseNode* moduleSpec)
+-    {
+-        ParseNode* pn = new_<BinaryNode>(ParseNodeKind::ExportFrom, JSOP_NOP, exportSpecSet,
+-                                         moduleSpec);
+-        if (!pn) {
+-            return null();
++    BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
++        BinaryNode* decl = new_<BinaryNode>(ParseNodeKind::ExportFrom, JSOP_NOP, exportSpecSet,
++                                            moduleSpec);
++        if (!decl) {
++            return nullptr;
+         }
+-        pn->pn_pos.begin = begin;
+-        return pn;
++        decl->pn_pos.begin = begin;
++        return decl;
+     }
+ 
+-    ParseNode* newExportDefaultDeclaration(ParseNode* kid, ParseNode* maybeBinding,
+-                                           const TokenPos& pos) {
++    BinaryNodeType newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) {
+         if (maybeBinding) {
+             MOZ_ASSERT(maybeBinding->isKind(ParseNodeKind::Name));
+             MOZ_ASSERT(!maybeBinding->isInParens());
+ 
+             checkAndSetIsDirectRHSAnonFunction(kid);
+         }
+ 
+         return new_<BinaryNode>(ParseNodeKind::ExportDefault, JSOP_NOP, pos, kid, maybeBinding);
+     }
+ 
+-    ParseNode* newExportSpec(ParseNode* bindingName, ParseNode* exportName) {
++    BinaryNodeType newExportSpec(Node bindingName, Node exportName) {
+         return newBinary(ParseNodeKind::ExportSpec, bindingName, exportName);
+     }
+ 
+     ParseNode* newExportBatchSpec(const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::ExportBatchSpec, JSOP_NOP, pos);
+     }
+ 
+-    ParseNode* newImportMeta(ParseNode* importHolder, ParseNode* metaHolder) {
++    BinaryNodeType newImportMeta(Node importHolder, Node metaHolder) {
+         return new_<BinaryNode>(ParseNodeKind::ImportMeta, JSOP_NOP, importHolder, metaHolder);
+     }
+ 
+-    ParseNode* newCallImport(ParseNode* importHolder, ParseNode* singleArg) {
++    BinaryNodeType newCallImport(Node importHolder, Node singleArg) {
+         return new_<BinaryNode>(ParseNodeKind::CallImport, JSOP_NOP, importHolder, singleArg);
+     }
+ 
+     ParseNode* newExprStatement(ParseNode* expr, uint32_t end) {
+         MOZ_ASSERT(expr->pn_pos.end <= end);
+         return new_<UnaryNode>(ParseNodeKind::ExpressionStatement,
+                                TokenPos(expr->pn_pos.begin, end), expr);
+     }
+@@ -605,56 +592,49 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         TernaryNode* node = new_<TernaryNode>(ParseNodeKind::If, cond, thenBranch, elseBranch);
+         if (!node) {
+             return nullptr;
+         }
+         node->pn_pos.begin = begin;
+         return node;
+     }
+ 
+-    ParseNode* newDoWhileStatement(ParseNode* body, ParseNode* cond, const TokenPos& pos) {
++    BinaryNodeType newDoWhileStatement(Node body, Node cond, const TokenPos& pos) {
+         return new_<BinaryNode>(ParseNodeKind::DoWhile, JSOP_NOP, pos, body, cond);
+     }
+ 
+-    ParseNode* newWhileStatement(uint32_t begin, ParseNode* cond, ParseNode* body) {
++    BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) {
+         TokenPos pos(begin, body->pn_pos.end);
+         return new_<BinaryNode>(ParseNodeKind::While, JSOP_NOP, pos, cond, body);
+     }
+ 
+-    Node newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) {
+-        /* A FOR node is binary, left is loop control and right is the body. */
+-        JSOp op = forHead->isKind(ParseNodeKind::ForIn) ? JSOP_ITER : JSOP_NOP;
+-        BinaryNode* pn = new_<BinaryNode>(ParseNodeKind::For, op,
+-                                          TokenPos(begin, body->pn_pos.end),
+-                                          forHead, body);
+-        if (!pn) {
+-            return null();
+-        }
+-        pn->pn_iflags = iflags;
+-        return pn;
++    ForNodeType newForStatement(uint32_t begin, TernaryNodeType forHead, Node body,
++                                unsigned iflags)
++    {
++        return new_<ForNode>(TokenPos(begin, body->pn_pos.end), forHead, body, iflags);
+     }
+ 
+     TernaryNodeType newForHead(Node init, Node test, Node update, const TokenPos& pos) {
+         return new_<TernaryNode>(ParseNodeKind::ForHead, init, test, update, pos);
+     }
+ 
+     TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target, Node iteratedExpr,
+                                      const TokenPos& pos)
+     {
+         MOZ_ASSERT(kind == ParseNodeKind::ForIn || kind == ParseNodeKind::ForOf);
+         return new_<TernaryNode>(kind, target, nullptr, iteratedExpr, pos);
+     }
+ 
+-    ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant,
+-                                  ParseNode* lexicalForCaseList, bool hasDefault)
++    SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant,
++                                           Node lexicalForCaseList, bool hasDefault)
+     {
+         return new_<SwitchStatement>(begin, discriminant, lexicalForCaseList, hasDefault);
+     }
+ 
+-    ParseNode* newCaseOrDefault(uint32_t begin, ParseNode* expr, ParseNode* body) {
++    CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) {
+         return new_<CaseClause>(expr, body, begin);
+     }
+ 
+     ParseNode* newContinueStatement(PropertyName* label, const TokenPos& pos) {
+         return new_<ContinueStatement>(label, pos);
+     }
+ 
+     ParseNode* newBreakStatement(PropertyName* label, const TokenPos& pos) {
+@@ -665,17 +645,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos));
+         return new_<UnaryNode>(ParseNodeKind::Return, pos, expr);
+     }
+ 
+     ParseNode* newExpressionBody(ParseNode* expr) {
+         return new_<UnaryNode>(ParseNodeKind::Return, expr->pn_pos, expr);
+     }
+ 
+-    ParseNode* newWithStatement(uint32_t begin, ParseNode* expr, ParseNode* body) {
++    BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) {
+         return new_<BinaryNode>(ParseNodeKind::With, JSOP_NOP, TokenPos(begin, body->pn_pos.end),
+                                 expr, body);
+     }
+ 
+     ParseNode* newLabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin) {
+         return new_<LabeledStatement>(label, stmt, begin);
+     }
+ 
+@@ -694,36 +674,36 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     ParseNode* newDebuggerStatement(const TokenPos& pos) {
+         return new_<DebuggerStatement>(pos);
+     }
+ 
+     ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) {
+         return new_<NameNode>(ParseNodeKind::PropertyName, JSOP_NOP, name, pos);
+     }
+ 
+-    ParseNode* newPropertyAccess(ParseNode* expr, ParseNode* key) {
++    PropertyAccessType newPropertyAccess(Node expr, Node key) {
+         return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end);
+     }
+ 
+-    ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
++    PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) {
+         return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
+     }
+ 
+     bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) {
+-        ParseNode* catchpn;
++        BinaryNode* catchClause;
+         if (catchName) {
+-            catchpn = new_<BinaryNode>(ParseNodeKind::Catch, JSOP_NOP, catchName, catchBody);
++            catchClause = new_<BinaryNode>(ParseNodeKind::Catch, JSOP_NOP, catchName, catchBody);
+         } else {
+-            catchpn = new_<BinaryNode>(ParseNodeKind::Catch, JSOP_NOP, catchBody->pn_pos,
++            catchClause = new_<BinaryNode>(ParseNodeKind::Catch, JSOP_NOP, catchBody->pn_pos,
+                                        catchName, catchBody);
+         }
+-        if (!catchpn) {
++        if (!catchClause) {
+             return false;
+         }
+-        lexicalScope->setScopeBody(catchpn);
++        lexicalScope->setScopeBody(catchClause);
+         return true;
+     }
+ 
+     inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(ParseNode* funcpn,
+                                                                    ParseNode* pn);
+ 
+   private:
+     void checkAndSetIsDirectRHSAnonFunction(ParseNode* pn) {
+@@ -740,17 +720,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     ParseNode* newFunctionExpression(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Function, JSOP_LAMBDA, pos);
+     }
+ 
+     ParseNode* newArrowFunction(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Function, JSOP_LAMBDA_ARROW, pos);
+     }
+ 
+-    ParseNode* newObjectMethodOrPropertyDefinition(ParseNode* key, ParseNode* fn, AccessorType atype) {
++    BinaryNodeType newObjectMethodOrPropertyDefinition(Node key, Node fn, AccessorType atype) {
+         MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+ 
+         return newBinary(ParseNodeKind::Colon, key, fn, AccessorTypeToJSOp(atype));
+     }
+ 
+     void setFunctionFormalParametersAndBody(ParseNode* funcNode, ParseNode* kid) {
+         MOZ_ASSERT_IF(kid, kid->isKind(ParseNodeKind::ParamsBody));
+         funcNode->pn_body = kid;
+@@ -771,28 +751,29 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     ParseNode* newModule(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Module, JSOP_NOP, pos);
+     }
+ 
+     ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) {
+         return new_<LexicalScopeNode>(bindings, body);
+     }
+ 
+-    Node newNewExpression(uint32_t begin, ParseNode* ctor, ParseNode* args) {
+-        return new_<BinaryNode>(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end), ctor, args);
++    BinaryNodeType newNewExpression(uint32_t begin, Node ctor, Node args) {
++        return new_<BinaryNode>(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end),
++                                ctor, args);
+     }
+ 
+-    ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) {
++    AssignmentNodeType newAssignment(ParseNodeKind kind, Node lhs, Node rhs) {
+         if (kind == ParseNodeKind::Assign && lhs->isKind(ParseNodeKind::Name) &&
+             !lhs->isInParens())
+         {
+             checkAndSetIsDirectRHSAnonFunction(rhs);
+         }
+ 
+-        return newBinary(kind, lhs, rhs);
++        return new_<AssignmentNode>(kind, JSOP_NOP, lhs, rhs);
+     }
+ 
+     bool isUnparenthesizedAssignment(Node node) {
+         if (node->isKind(ParseNodeKind::Assign) && !node->isInParens()) {
+             // ParseNodeKind::Assign is also (mis)used for things like
+             // |var name = expr;|. But this method is only called on actual
+             // expressions, so we can just assert the node's op is the one used
+             // for plain assignment.
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -73,47 +73,51 @@ class NameResolver
+     /*
+      * Walk over the given ParseNode, attempting to convert it to a stringified
+      * name that respresents where the function is being assigned to.
+      *
+      * |*foundName| is set to true if a name is found for the expression.
+      */
+     bool nameExpression(ParseNode* n, bool* foundName) {
+         switch (n->getKind()) {
+-          case ParseNodeKind::Dot:
+-            if (!nameExpression(n->pn_left, foundName)) {
++          case ParseNodeKind::Dot: {
++            PropertyAccess* prop = &n->as<PropertyAccess>();
++            if (!nameExpression(&prop->expression(), foundName)) {
+                 return false;
+             }
+             if (!*foundName) {
+                 return true;
+             }
+-            return appendPropertyReference(n->pn_right->pn_atom);
++            return appendPropertyReference(prop->right()->pn_atom);
++          }
+ 
+           case ParseNodeKind::Name:
+             *foundName = true;
+             return buf->append(n->pn_atom);
+ 
+           case ParseNodeKind::This:
+             *foundName = true;
+             return buf->append("this");
+ 
+-          case ParseNodeKind::Elem:
+-            if (!nameExpression(n->pn_left, foundName)) {
++          case ParseNodeKind::Elem: {
++            PropertyByValue* elem = &n->as<PropertyByValue>();
++            if (!nameExpression(&elem->expression(), foundName)) {
+                 return false;
+             }
+             if (!*foundName) {
+                 return true;
+             }
+-            if (!buf->append('[') || !nameExpression(n->pn_right, foundName)) {
++            if (!buf->append('[') || !nameExpression(elem->right(), foundName)) {
+                 return false;
+             }
+             if (!*foundName) {
+                 return true;
+             }
+             return buf->append(']');
++          }
+ 
+           case ParseNodeKind::Number:
+             *foundName = true;
+             return appendNumber(n->pn_dval);
+ 
+           default:
+             /* We're confused as to what to call this function. */
+             *foundName = false;
+@@ -134,17 +138,17 @@ class NameResolver
+      * innermost node relevant to naming, and the last element will be the
+      * outermost node.
+      */
+     ParseNode* gatherNameable(ParseNode** nameable, size_t* size) {
+         *size = 0;
+ 
+         for (int pos = nparents - 1; pos >= 0; pos--) {
+             ParseNode* cur = parents[pos];
+-            if (cur->isAssignment()) {
++            if (cur->is<AssignmentNode>()) {
+                 return cur;
+             }
+ 
+             switch (cur->getKind()) {
+               case ParseNodeKind::Name:     return cur;  /* found the initialized declaration */
+               case ParseNodeKind::This:     return cur;  /* Setting a property of 'this'. */
+               case ParseNodeKind::Function: return nullptr; /* won't find an assignment or declaration */
+ 
+@@ -231,18 +235,18 @@ class NameResolver
+ 
+         /* Gather all nodes relevant to naming */
+         ParseNode* toName[MaxParents];
+         size_t size;
+         ParseNode* assignment = gatherNameable(toName, &size);
+ 
+         /* If the function is assigned to something, then that is very relevant */
+         if (assignment) {
+-            if (assignment->isAssignment()) {
+-                assignment = assignment->pn_left;
++            if (assignment->is<AssignmentNode>()) {
++                assignment = assignment->as<AssignmentNode>().left();
+             }
+             bool foundName = false;
+             if (!nameExpression(assignment, &foundName)) {
+                 return false;
+             }
+             if (!foundName) {
+                 return true;
+             }
+@@ -252,17 +256,17 @@ class NameResolver
+          * Other than the actual assignment, other relevant nodes to naming are
+          * those in object initializers and then particular nodes marking a
+          * contribution.
+          */
+         for (int pos = size - 1; pos >= 0; pos--) {
+             ParseNode* node = toName[pos];
+ 
+             if (node->isKind(ParseNodeKind::Colon) || node->isKind(ParseNodeKind::Shorthand)) {
+-                ParseNode* left = node->pn_left;
++                ParseNode* left = node->as<BinaryNode>().left();
+                 if (left->isKind(ParseNodeKind::ObjectPropertyName) ||
+                     left->isKind(ParseNodeKind::String))
+                 {
+                     if (!appendPropertyReference(left->pn_atom)) {
+                         return false;
+                     }
+                 } else if (left->isKind(ParseNodeKind::Number)) {
+                     if (!appendNumericPropertyReference(left->pn_dval)) {
+@@ -309,17 +313,17 @@ class NameResolver
+     }
+ 
+     /*
+      * Tests whether parents[pos] is a function call whose callee is cur.
+      * This is the case for functions which do things like simply create a scope
+      * for new variables and then return an anonymous function using this scope.
+      */
+     bool isDirectCall(int pos, ParseNode* cur) {
+-        return pos >= 0 && call(parents[pos]) && parents[pos]->pn_left == cur;
++        return pos >= 0 && call(parents[pos]) && parents[pos]->as<BinaryNode>().left() == cur;
+     }
+ 
+     bool resolveTemplateLiteral(ListNode* node, HandleAtom prefix) {
+         MOZ_ASSERT(node->isKind(ParseNodeKind::TemplateStringList));
+         ParseNode* element = node->head();
+         while (true) {
+             MOZ_ASSERT(element->isKind(ParseNodeKind::TemplateString));
+ 
+@@ -331,31 +335,32 @@ class NameResolver
+             if (!resolve(element, prefix)) {
+                 return false;
+             }
+ 
+             element = element->pn_next;
+         }
+     }
+ 
+-    bool resolveTaggedTemplate(ParseNode* node, HandleAtom prefix) {
+-        MOZ_ASSERT(node->isKind(ParseNodeKind::TaggedTemplate));
++    bool resolveTaggedTemplate(BinaryNode* taggedTemplate, HandleAtom prefix) {
++        MOZ_ASSERT(taggedTemplate->isKind(ParseNodeKind::TaggedTemplate));
+ 
+-        ParseNode* tag = node->pn_left;
++        ParseNode* tag = taggedTemplate->left();
+ 
+         // The leading expression, e.g. |tag| in |tag`foo`|,
+         // that might contain functions.
+         if (!resolve(tag, prefix)) {
+             return false;
+         }
+ 
+         // The callsite object node is first.  This node only contains
+         // internal strings or undefined and an array -- no user-controlled
+         // expressions.
+-        CallSiteNode* element = &node->pn_right->as<ListNode>().head()->as<CallSiteNode>();
++        CallSiteNode* element =
++            &taggedTemplate->right()->as<ListNode>().head()->as<CallSiteNode>();
+ #ifdef DEBUG
+         {
+             ListNode* rawNodes = &element->head()->as<ListNode>();
+             MOZ_ASSERT(rawNodes->isKind(ParseNodeKind::Array));
+             for (ParseNode* raw : rawNodes->contents()) {
+                 MOZ_ASSERT(raw->isKind(ParseNodeKind::TemplateString));
+             }
+             for (ParseNode* cooked : element->contentsFrom(rawNodes->pn_next)) {
+@@ -441,21 +446,21 @@ class NameResolver
+           case ParseNodeKind::TypeOfName:
+           case ParseNodeKind::SuperBase:
+             MOZ_ASSERT(cur->isArity(PN_UNARY));
+             MOZ_ASSERT(cur->pn_kid->isKind(ParseNodeKind::Name));
+             MOZ_ASSERT(!cur->pn_kid->expr());
+             break;
+ 
+           case ParseNodeKind::NewTarget:
+-          case ParseNodeKind::ImportMeta:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            MOZ_ASSERT(cur->pn_left->isKind(ParseNodeKind::PosHolder));
+-            MOZ_ASSERT(cur->pn_right->isKind(ParseNodeKind::PosHolder));
++          case ParseNodeKind::ImportMeta: {
++            MOZ_ASSERT(cur->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
++            MOZ_ASSERT(cur->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
+             break;
++          }
+ 
+           // Nodes with a single non-null child requiring name resolution.
+           case ParseNodeKind::ExpressionStatement:
+           case ParseNodeKind::TypeOfExpr:
+           case ParseNodeKind::Void:
+           case ParseNodeKind::Not:
+           case ParseNodeKind::BitNot:
+           case ParseNodeKind::Throw:
+@@ -505,63 +510,70 @@ class NameResolver
+           case ParseNodeKind::PowAssign:
+           case ParseNodeKind::Colon:
+           case ParseNodeKind::Shorthand:
+           case ParseNodeKind::DoWhile:
+           case ParseNodeKind::While:
+           case ParseNodeKind::Switch:
+           case ParseNodeKind::For:
+           case ParseNodeKind::ClassMethod:
+-          case ParseNodeKind::SetThis:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            if (!resolve(cur->pn_left, prefix)) {
++          case ParseNodeKind::SetThis: {
++            BinaryNode* node = &cur->as<BinaryNode>();
++            if (!resolve(node->left(), prefix)) {
+                 return false;
+             }
+-            if (!resolve(cur->pn_right, prefix)) {
++            if (!resolve(node->right(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+-          case ParseNodeKind::Elem:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            if (!cur->as<PropertyByValue>().isSuper() && !resolve(cur->pn_left, prefix)) {
++          case ParseNodeKind::Elem: {
++            PropertyByValue* elem = &cur->as<PropertyByValue>();
++            if (!elem->isSuper() && !resolve(&elem->expression(), prefix)) {
+                 return false;
+             }
+-            if (!resolve(cur->pn_right, prefix)) {
++            if (!resolve(&elem->key(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+-          case ParseNodeKind::With:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            if (!resolve(cur->pn_left, prefix)) {
++          case ParseNodeKind::With: {
++            BinaryNode* node = &cur->as<BinaryNode>();
++            if (!resolve(node->left(), prefix)) {
+                 return false;
+             }
+-            if (!resolve(cur->pn_right, prefix)) {
++            if (!resolve(node->right(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+-          case ParseNodeKind::Case:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            if (ParseNode* caseExpr = cur->pn_left) {
++          case ParseNodeKind::Case: {
++            CaseClause* caseClause = &cur->as<CaseClause>();
++            if (ParseNode* caseExpr = caseClause->caseExpression()) {
+                 if (!resolve(caseExpr, prefix)) {
+                     return false;
+                 }
+             }
+-            if (!resolve(cur->pn_right, prefix)) {
++            if (!resolve(caseClause->statementList(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+-          case ParseNodeKind::InitialYield:
+-            MOZ_ASSERT(cur->pn_kid->isKind(ParseNodeKind::Assign) &&
+-                       cur->pn_kid->pn_left->isKind(ParseNodeKind::Name) &&
+-                       cur->pn_kid->pn_right->isKind(ParseNodeKind::Generator));
++          case ParseNodeKind::InitialYield: {
++#ifdef DEBUG
++            AssignmentNode* assignNode = &cur->pn_kid->as<AssignmentNode>();
++            MOZ_ASSERT(assignNode->left()->isKind(ParseNodeKind::Name));
++            MOZ_ASSERT(assignNode->right()->isKind(ParseNodeKind::Generator));
++#endif
+             break;
++          }
+ 
+           case ParseNodeKind::YieldStar:
+             MOZ_ASSERT(cur->isArity(PN_UNARY));
+             if (!resolve(cur->pn_kid, prefix)) {
+                 return false;
+             }
+             break;
+ 
+@@ -581,27 +593,28 @@ class NameResolver
+                 if (!resolve(returnValue, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           case ParseNodeKind::Import:
+           case ParseNodeKind::ExportFrom:
+-          case ParseNodeKind::ExportDefault:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
++          case ParseNodeKind::ExportDefault: {
++            BinaryNode* node = &cur->as<BinaryNode>();
+             // The left halves of Import and ExportFrom don't contain any
+             // unconstrained expressions, but it's very hard to assert this to
+             // safely rely on it. So recur anyway.
+-            if (!resolve(cur->pn_left, prefix)) {
++            if (!resolve(node->left(), prefix)) {
+                 return false;
+             }
+-            MOZ_ASSERT_IF(!cur->isKind(ParseNodeKind::ExportDefault),
+-                          cur->pn_right->isKind(ParseNodeKind::String));
++            MOZ_ASSERT_IF(!node->isKind(ParseNodeKind::ExportDefault),
++                          node->right()->isKind(ParseNodeKind::String));
+             break;
++          }
+ 
+           // Ternary nodes with three expression children.
+           case ParseNodeKind::Conditional: {
+             TernaryNode* condNode = &cur->as<TernaryNode>();
+             if (!resolve(condNode->kid1(), prefix)) {
+                 return false;
+             }
+             if (!resolve(condNode->kid2(), prefix)) {
+@@ -656,23 +669,25 @@ class NameResolver
+ 
+           // The first child of a class is a pair of names referring to it,
+           // inside and outside the class.  The second is the class's heritage,
+           // if any.  The third is the class body.
+           case ParseNodeKind::Class: {
+             ClassNode* classNode = &cur->as<ClassNode>();
+ #ifdef DEBUG
+             if (classNode->names()) {
+-                ParseNode* name = classNode->names();
+-                MOZ_ASSERT(name->isKind(ParseNodeKind::ClassNames));
+-                MOZ_ASSERT(name->isArity(PN_BINARY));
+-                MOZ_ASSERT_IF(name->pn_left, name->pn_left->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT_IF(name->pn_left, !name->pn_left->expr());
+-                MOZ_ASSERT(name->pn_right->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!name->pn_right->expr());
++                ClassNames* names = classNode->names();
++                if (ParseNode* outerBinding = names->outerBinding()) {
++                    MOZ_ASSERT(outerBinding->isKind(ParseNodeKind::Name));
++                    MOZ_ASSERT(!outerBinding->expr());
++                }
++
++                ParseNode* innerBinding = names->innerBinding();
++                MOZ_ASSERT(innerBinding->isKind(ParseNodeKind::Name));
++                MOZ_ASSERT(!innerBinding->expr());
+             }
+ #endif
+             if (ParseNode* heritage = classNode->heritage()) {
+                 if (!resolve(heritage, prefix)) {
+                     return false;
+                 }
+             }
+             if (!resolve(classNode->methodList(), prefix)) {
+@@ -706,44 +721,45 @@ class NameResolver
+             TernaryNode* tryNode = &cur->as<TernaryNode>();
+             if (!resolve(tryNode->kid1(), prefix)) {
+                 return false;
+             }
+             MOZ_ASSERT(tryNode->kid2() || tryNode->kid3());
+             if (ParseNode* catchScope = tryNode->kid2()) {
+                 MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+                 MOZ_ASSERT(catchScope->scopeBody()->isKind(ParseNodeKind::Catch));
+-                MOZ_ASSERT(catchScope->scopeBody()->isArity(PN_BINARY));
++                MOZ_ASSERT(catchScope->scopeBody()->is<BinaryNode>());
+                 if (!resolve(catchScope->scopeBody(), prefix)) {
+                     return false;
+                 }
+             }
+             if (ParseNode* finallyBlock = tryNode->kid3()) {
+                 if (!resolve(finallyBlock, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+           }
+ 
+           // The first child, the catch-pattern, may contain functions via
+           // computed property names.  The optional catch-conditions may
+           // contain any expression.  The catch statements, of course, may
+           // contain arbitrary expressions.
+-          case ParseNodeKind::Catch:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            if (cur->pn_left) {
+-                if (!resolve(cur->pn_left, prefix)) {
++          case ParseNodeKind::Catch: {
++            BinaryNode* node = &cur->as<BinaryNode>();
++            if (ParseNode* varNode = node->left()) {
++                if (!resolve(varNode, prefix)) {
+                     return false;
+                 }
+             }
+-            if (!resolve(cur->pn_right, prefix)) {
++            if (!resolve(node->right(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+           // Nodes with arbitrary-expression children.
+           case ParseNodeKind::Or:
+           case ParseNodeKind::And:
+           case ParseNodeKind::BitOr:
+           case ParseNodeKind::BitXor:
+           case ParseNodeKind::BitAnd:
+           case ParseNodeKind::StrictEq:
+@@ -795,33 +811,33 @@ class NameResolver
+           // contents with expressions interpolated into the overall literal.
+           case ParseNodeKind::TemplateStringList:
+             if (!resolveTemplateLiteral(&cur->as<ListNode>(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::TaggedTemplate:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            if (!resolveTaggedTemplate(cur, prefix)) {
++            if (!resolveTaggedTemplate(&cur->as<BinaryNode>(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::New:
+           case ParseNodeKind::Call:
+-          case ParseNodeKind::SuperCall:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            if (!resolve(cur->pn_left, prefix)) {
++          case ParseNodeKind::SuperCall: {
++            BinaryNode* callNode = &cur->as<BinaryNode>();
++            if (!resolve(callNode->left(), prefix)) {
+                 return false;
+             }
+-            if (!resolve(cur->pn_right, prefix)) {
++            if (!resolve(callNode->right(), prefix)) {
+                 return false;
+             }
+             break;
++          }
+ 
+           // Handles the arguments for new/call/supercall, but does _not_ handle
+           // the Arguments node used by tagged template literals, since that is
+           // special-cased inside of resolveTaggedTemplate.
+           case ParseNodeKind::Arguments:
+             for (ParseNode* element : cur->as<ListNode>().contents()) {
+                 if (!resolve(element, prefix)) {
+                     return false;
+@@ -838,46 +854,48 @@ class NameResolver
+             bool isImport = cur->isKind(ParseNodeKind::ImportSpecList);
+             ListNode* list = &cur->as<ListNode>();
+             ParseNode* item = list->head();
+             if (!isImport && item && item->isKind(ParseNodeKind::ExportBatchSpec)) {
+                 MOZ_ASSERT(item->isArity(PN_NULLARY));
+                 break;
+             }
+             for (ParseNode* item : list->contents()) {
+-                MOZ_ASSERT(item->isKind(isImport
++                BinaryNode* spec = &item->as<BinaryNode>();
++                MOZ_ASSERT(spec->isKind(isImport
+                                         ? ParseNodeKind::ImportSpec
+                                         : ParseNodeKind::ExportSpec));
+-                MOZ_ASSERT(item->isArity(PN_BINARY));
+-                MOZ_ASSERT(item->pn_left->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!item->pn_left->expr());
+-                MOZ_ASSERT(item->pn_right->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!item->pn_right->expr());
++                MOZ_ASSERT(spec->left()->isKind(ParseNodeKind::Name));
++                MOZ_ASSERT(!spec->left()->expr());
++                MOZ_ASSERT(spec->right()->isKind(ParseNodeKind::Name));
++                MOZ_ASSERT(!spec->right()->expr());
+             }
+ #endif
+             break;
+           }
+ 
+-          case ParseNodeKind::CallImport:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-            if (!resolve(cur->pn_right, prefix))
+-                return false;
+-            break;
+-
+-          case ParseNodeKind::Dot:
+-            MOZ_ASSERT(cur->isArity(PN_BINARY));
+-
+-            // Super prop nodes do not have a meaningful LHS
+-            if (cur->as<PropertyAccess>().isSuper()) {
+-                break;
+-            }
+-            if (!resolve(cur->pn_left, prefix)) {
++          case ParseNodeKind::CallImport: {
++            BinaryNode* node = &cur->as<BinaryNode>();
++            if (!resolve(node->right(), prefix)) {
+                 return false;
+             }
+             break;
++          }
++
++          case ParseNodeKind::Dot: {
++            // Super prop nodes do not have a meaningful LHS
++            PropertyAccess* prop = &cur->as<PropertyAccess>();
++            if (prop->isSuper()) {
++                break;
++            }
++            if (!resolve(&prop->expression(), prefix)) {
++                return false;
++            }
++            break;
++          }
+ 
+           case ParseNodeKind::Label:
+             MOZ_ASSERT(cur->isArity(PN_NAME));
+             if (!resolve(cur->expr(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -147,17 +147,17 @@ ParseNode::dump(GenericPrinter& out, int
+     switch (pn_arity) {
+       case PN_NULLARY:
+         ((NullaryNode*) this)->dump(out);
+         break;
+       case PN_UNARY:
+         ((UnaryNode*) this)->dump(out, indent);
+         break;
+       case PN_BINARY:
+-        ((BinaryNode*) this)->dump(out, indent);
++        as<BinaryNode>().dump(out, indent);
+         break;
+       case PN_TERNARY:
+         as<TernaryNode>().dump(out, indent);
+         break;
+       case PN_CODE:
+         ((CodeNode*) this)->dump(out, indent);
+         break;
+       case PN_LIST:
+@@ -220,35 +220,35 @@ UnaryNode::dump(GenericPrinter& out, int
+ }
+ 
+ void
+ BinaryNode::dump(GenericPrinter& out, int indent)
+ {
+     if (isKind(ParseNodeKind::Dot)) {
+         out.put("(.");
+ 
+-        DumpParseTree(pn_right, out, indent + 2);
++        DumpParseTree(right(), out, indent + 2);
+ 
+         out.putChar(' ');
+         if (as<PropertyAccess>().isSuper()) {
+             out.put("super");
+         } else {
+-            DumpParseTree(pn_left, out, indent + 2);
++            DumpParseTree(left(), out, indent + 2);
+         }
+ 
+         out.printf(")");
+         return;
+     }
+ 
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s ", name);
+     indent += strlen(name) + 2;
+-    DumpParseTree(pn_left, out, indent);
++    DumpParseTree(left(), out, indent);
+     IndentNewLine(out, indent);
+-    DumpParseTree(pn_right, out, indent);
++    DumpParseTree(right(), out, indent);
+     out.printf(")");
+ }
+ 
+ void
+ TernaryNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s ", name);
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -251,47 +251,54 @@ IsTypeofKind(ParseNodeKind kind)
+  * Class (ClassNode)
+  *   kid1: ClassNames for class name. can be null for anonymous class.
+  *   kid2: expression after `extends`. null if no expression
+  *   kid3: either of
+  *           * ClassMethodList, if anonymous class
+  *           * LexicalScopeNode which contains ClassMethodList as scopeBody,
+  *             if named class
+  * ClassNames (ClassNames)
+- *   pn_left: Name node for outer binding. can be null
+- *   pn_right: Name node for inner binding
++ *   left: Name node for outer binding, or null if the class is an expression
++ *         that doesn't create an outer binding
++ *   right: Name node for inner binding
+  * ClassMethodList (ListNode)
+  *   head: list of N ClassMethod nodes
+  *   count: N >= 0
+  * ClassMethod (ClassMethod)
+  *   name: propertyName
+  *   method: methodDefinition
+  *
+  * <Statements>
+  * StatementList (ListNode)
+  *   head: list of N statements
+  *   count: N >= 0
+  * If (TernaryNode)
+  *   kid1: cond
+  *   kid2: then
+  *   kid3: else or null
+- * Switch   binary      pn_left: discriminant
+- *                      pn_right: LexicalScope node that contains the list
+- *                        of Case nodes, with at most one
+- *                        default node.
+- *                      hasDefault: true if there's a default case
+- * Case     binary      pn_left: case-expression if CaseClause, or
+- *                            null if DefaultClause
+- *                          pn_right: StatementList node for this case's
+- *                            statements
+- * While    binary      pn_left: cond, pn_right: body
+- * DoWhile  binary      pn_left: body, pn_right: cond
+- * For      binary      pn_left: either ForIn (for-in statement),
+- *                            ForOf (for-of) or ForHead (for(;;))
+- *                          pn_right: body
++ * Switch (SwitchStatement)
++ *   left: discriminant
++ *   right: LexicalScope node that contains the list of Case nodes, with at
++ *          most one default node.
++ *   hasDefault: true if there's a default case
++ * Case (CaseClause)
++ *   left: case-expression if CaseClause, or null if DefaultClause
++ *   right: StatementList node for this case's statements
++ * While (BinaryNode)
++ *   left: cond
++ *   right: body
++ * DoWhile (BinaryNode)
++ *   left: body
++ *   right: cond
++ * For (ForNode)
++ *   left: one of
++ *           * ForIn: for (x in y) ...
++ *           * ForOf: for (x of x) ...
++ *           * ForHead: for (;;) ...
++ *   right: body
+  * ForIn (TernaryNode)
+  *   kid1: declaration or expression to left of 'in'
+  *   kid2: null
+  *   kid3: object expr to right of 'in'
+  * ForOf (TernaryNode)
+  *   kid1: declaration or expression to left of 'of'
+  *   kid2: null
+  *   kid3: expr to right of 'of'
+@@ -300,78 +307,86 @@ IsTypeofKind(ParseNodeKind kind)
+  *   kid2:  cond expr before second ';' or nullptr
+  *   kid3:  update expr after second ';' or nullptr
+  * Throw    unary       pn_kid: exception
+  * Try (TernaryNode)
+  *   kid1: try block
+  *   kid2: null or LexicalScope for catch-block with scopeBody pointing to a
+  *         Catch node
+  *   kid3: null or finally block
+- * Catch    binary      pn_left: Name, Array, or Object catch
+- *                                   var node
+- *                                   (Array or Object if destructuring),
+- *                                   or null if optional catch binding
+- *                          pn_right: catch block statements
++ * Catch (BinaryNode)
++ *   left: Name, Array, or Object catch var node
++ *         (Array or Object if destructuring),
++ *         or null if optional catch binding
++ *   right: catch block statements
+  * Break    name        pn_atom: label or null
+  * Continue name        pn_atom: label or null
+- * With     binary      pn_left: head expr; pn_right: body;
++ * With (BinaryNode)
++ *   left: head expr
++ *   right: body
+  * Var, Let, Const (ListNode)
+  *   head: list of N Name or Assign nodes
+  *         each name node has either
+  *           pn_used: false
+  *           pn_atom: variable name
+  *           pn_expr: initializer or null
+  *         or
+  *           pn_used: true
+  *           pn_atom: variable name
+  *           pn_lexdef: def node
+  *         each assignment node has
+- *           pn_left: Name with pn_used true and
++ *           left: Name with pn_used true and
+  *                    pn_lexdef (NOT pn_expr) set
+- *           pn_right: initializer
++ *           right: initializer
+  *   count: N > 0
+  * Return   unary       pn_kid: return expr or null
+  * ExpressionStatement unary    pn_kid: expr
+  *                              pn_prologue: true if Directive Prologue member
+  *                                  in original source, not introduced via
+  *                                  constant folding or other tree rewriting
+  * EmptyStatement nullary      (no fields)
+  * Label    name        pn_atom: label, pn_expr: labeled statement
+- * Import   binary      pn_left: ImportSpecList import specifiers
+- *                          pn_right: String module specifier
++ * Import (BinaryNode)
++ *   left: ImportSpecList import specifiers
++ *   right: String module specifier
+  * ImportSpecList (ListNode)
+  *   head: list of N ImportSpec nodes
+  *   count: N >= 0 (N = 0 for `import {} from ...`)
++ * ImportSpec (BinaryNode)
++ *   left: import name
++ *   right: local binding name
+  * Export   unary       pn_kid: declaration expression
+- * ExportFrom binary   pn_left: ExportSpecList export specifiers
+- *                          pn_right: String module specifier
++ * ExportFrom (BinaryNode)
++ *   left: ExportSpecList export specifiers
++ *   right: String module specifier
+  * ExportSpecList (ListNode)
+  *   head: list of N ExportSpec nodes
+  *   count: N >= 0 (N = 0 for `export {}`)
+- * ExportDefault unary pn_kid: export default declaration or expression
++ * ExportSpec (BinaryNode)
++ *   left: local binding name
++ *   right: export name
++ * ExportDefault (BinaryNode)
++ *   left: export default declaration or expression
++ *   right: Name node for assignment
+  *
+  * <Expressions>
+  * All left-associated binary trees of the same type are optimized into lists
+  * to avoid recursion when processing expression chains.
+  * Comma (ListNode)
+  *   head: list of N comma-separated exprs
+  *   count: N >= 2
+- * Assign   binary      pn_left: lvalue, pn_right: rvalue
+- * AddAssign,   binary  pn_left: lvalue, pn_right: rvalue
+- * SubAssign,           pn_op: JSOP_ADD for +=, etc.
+- * BitOrAssign,
+- * BitXorAssign,
+- * BitAndAssign,
+- * LshAssign,
+- * RshAssign,
+- * UrshAssign,
+- * MulAssign,
+- * DivAssign,
+- * ModAssign,
+- * PowAssign
++ * Assign (BinaryNode)
++ *   left: target of assignment
++ *   right: value to assign
++ * AddAssign, SubAssign, BitOrAssign, BitXorAssign, BitAndAssign,
++ * LshAssign, RshAssign, UrshAssign, MulAssign, DivAssign, ModAssign,
++ * PowAssign (AssignmentNode)
++ *   left: target of assignment
++ *   right: value to assign
++ *   pn_op: JSOP_ADD for +=, etc
+  * Conditional (ConditionalExpression)
+  *   (cond ? thenExpr : elseExpr)
+  *   kid1: cond
+  *   kid2: thenExpr
+  *   kid3: elseExpr
+  * Pipeline, Or, And, BitOr, BitXor, BitAnd, StrictEq, Eq, StrictNe, Ne,
+  * Lt, Le, Gt, Ge, InstanceOf, In, Lsh, Rsh, Ursh, Add, Sub, Star, Div, Mod,
+  * Pow (ListNode)
+@@ -386,86 +401,97 @@ IsTypeofKind(ParseNodeKind kind)
+  * Not,
+  * BitNot
+  * TypeOfName, unary    pn_kid: UNARY expr
+  * TypeOfExpr
+  * PreIncrement, unary  pn_kid: MEMBER expr
+  * PostIncrement,
+  * PreDecrement,
+  * PostDecrement
+- * New      binary      pn_left: ctor expression on the left of the (
+- *                          pn_right: Arguments
++ * New (BinaryNode)
++ *   left: ctor expression on the left of the '('
++ *   right: Arguments
+  * DeleteName unary     pn_kid: Name expr
+  * DeleteProp unary     pn_kid: Dot expr
+  * DeleteElem unary     pn_kid: Elem expr
+  * DeleteExpr unary     pn_kid: MEMBER expr that's evaluated, then the
+  *                          overall delete evaluates to true; can't be a kind
+  *                          for a more-specific PNK_DELETE* unless constant
+  *                          folding (or a similar parse tree manipulation) has
+  *                          occurred
+  * PropertyName name    pn_atom: property name being accessed
+- * Dot      binary      pn_left: MEMBER expr to left of .
+- *                          pn_right: PropertyName to right of .
+- * Elem     binary      pn_left: MEMBER expr to left of [
+- *                          pn_right: expr between [ and ]
+- * Call     binary      pn_left: callee expression on the left of the (
+- *                          pn_right: Arguments
++ * Dot (PropertyAccess)
++ *   left: MEMBER expr to left of '.'
++ *   right: PropertyName to right of '.'
++ * Elem (PropertyByValue)
++ *   left: MEMBER expr to left of '['
++ *   right: expr between '[' and ']'
++ * Call (BinaryNode)
++ *   left: callee expression on the left of the '('
++ *   right: Arguments
+  * Arguments (ListNode)
+  *   head: list of arg1, arg2, ... argN
+  *   count: N >= 0
+  * Array (ListNode)
+  *   head: list of N array element expressions
+  *         holes ([,,]) are represented by Elision nodes,
+  *         spread elements ([...X]) are represented by Spread nodes
+  *   count: N >= 0
+  * Object (ListNode)
+  *   head: list of N nodes, each item is one of:
+  *           * MutateProto
+  *           * Colon
+  *           * Shorthand
+  *           * Spread
+  *   count: N >= 0
+- * Colon    binary      key-value pair in object initializer or
+- *                          destructuring lhs
+- *                          pn_left: property id, pn_right: value
+- * Shorthand binary     Same fields as Colon. This is used for object
+- *                          literal properties using shorthand ({x}).
++ * Colon (BinaryNode)
++ *   key-value pair in object initializer or destructuring lhs
++ *   left: property id
++ *   right: value
++ * Shorthand (BinaryNode)
++ *   Same fields as Colon. This is used for object literal properties using
++ *   shorthand ({x}).
+  * ComputedName unary  ES6 ComputedPropertyName.
+  *                          pn_kid: the AssignmentExpression inside the square brackets
+  * Name,    name        pn_atom: name, string, or object atom
+  * String               pn_op: JSOP_GETNAME, JSOP_STRING, or JSOP_OBJECT
+  *                          If JSOP_GETNAME, pn_op may be JSOP_*ARG or JSOP_*VAR
+  *                          telling const-ness and static analysis results
+  * TemplateStringList (ListNode)
+  *   head: list of alternating expr and template strings
+  *           TemplateString [, expression, TemplateString]+
+  *         there's at least one expression.  If the template literal contains
+  *         no ${}-delimited expression, it's parsed as a single TemplateString
+  * TemplateString      pn_atom: template string atom
+                 nullary     pn_op: JSOP_NOP
+- * TaggedTemplate      pn_left: tag expression
+- *              binary       pn_right: Arguments, with the first being the
+- *                           call site object, then arg1, arg2, ... argN
++ * TaggedTemplate (BinaryNode)
++ *   left: tag expression
++ *   right: Arguments, with the first being the call site object, then
++ *          arg1, arg2, ... argN
+  * CallSiteObj (CallSiteNode)
+  *   head:  an Array of raw TemplateString, then corresponding cooked
+  *          TemplateString nodes
+  *            Array [, cooked TemplateString]+
+  *          where the Array is
+  *            [raw TemplateString]+
+  * RegExp   nullary     pn_objbox: RegExp model object
+  * Number   dval        pn_dval: double value of numeric literal
+  * True,    nullary     pn_op: JSOp bytecode
+  * False,
+  * Null,
+  * RawUndefined
+  *
+  * This,        unary   pn_kid: '.this' Name if function `this`, else nullptr
+  * SuperBase    unary   pn_kid: '.this' Name
+- * SuperCall    binary  pn_left: SuperBase pn_right: Arguments
+- * SetThis      binary  pn_left: '.this' Name, pn_right: SuperCall
++ * SuperCall (BinaryNode)
++ *   left: SuperBase
++ *   right: Arguments
++ * SetThis (BinaryNode)
++ *   left: '.this' Name
++ *   right: SuperCall
+  *
+  * LexicalScope scope   pn_u.scope.bindings: scope bindings
+  *                          pn_u.scope.body: scope body
+  * Generator    nullary
+  * InitialYield unary   pn_kid: generator object
+  * Yield,       unary   pn_kid: expr or null
+  * YieldStar,
+  * Await
+@@ -479,27 +505,36 @@ enum ParseNodeArity
+     PN_TERNARY,                         /* three kids */
+     PN_CODE,                            /* module or function definition node */
+     PN_LIST,                            /* generic singly linked list */
+     PN_NAME,                            /* name, label, or regexp */
+     PN_SCOPE                            /* lexical scope */
+ };
+ 
+ #define FOR_EACH_PARSENODE_SUBCLASS(macro) \
++    macro(BinaryNode, BinaryNodeType, asBinary) \
++    macro(AssignmentNode, AssignmentNodeType, asAssignment) \
++    macro(CaseClause, CaseClauseType, asCaseClause) \
++    macro(ClassMethod, ClassMethodType, asClassMethod) \
++    macro(ClassNames, ClassNamesType, asClassNames) \
++    macro(ForNode, ForNodeType, asFor) \
++    macro(PropertyAccess, PropertyAccessType, asPropertyAccess) \
++    macro(PropertyByValue, PropertyByValueType, asPropertyByValue) \
++    macro(SwitchStatement, SwitchStatementType, asSwitchStatement) \
++    \
+     macro(ListNode, ListNodeType, asList) \
+     macro(CallSiteNode, CallSiteNodeType, asCallSite) \
+     \
+     macro(TernaryNode, TernaryNodeType, asTernary) \
+     macro(ClassNode, ClassNodeType, asClass) \
+     macro(ConditionalExpression, ConditionalExpressionType, asConditionalExpression)
+ 
+ class LoopControlStatement;
+ class BreakStatement;
+ class ContinueStatement;
+-class PropertyAccess;
+ 
+ #define DECLARE_CLASS(typeName, longTypeName, asMethodName) \
+ class typeName;
+ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
+ #undef DECLARE_CLASS
+ 
+ class ParseNode
+ {
+@@ -558,21 +593,16 @@ class ParseNode
+         pn_type = kind;
+     }
+     bool isKind(ParseNodeKind kind) const  { return getKind() == kind; }
+ 
+     ParseNodeArity getArity() const        { return ParseNodeArity(pn_arity); }
+     bool isArity(ParseNodeArity a) const   { return getArity() == a; }
+     void setArity(ParseNodeArity a)        { pn_arity = a; }
+ 
+-    bool isAssignment() const {
+-        ParseNodeKind kind = getKind();
+-        return ParseNodeKind::AssignmentStart <= kind && kind <= ParseNodeKind::AssignmentLast;
+-    }
+-
+     bool isBinaryOperation() const {
+         ParseNodeKind kind = getKind();
+         return ParseNodeKind::BinOpFirst <= kind && kind <= ParseNodeKind::BinOpLast;
+     }
+ 
+     /* Boolean attributes. */
+     bool isInParens() const                { return pn_parens; }
+     bool isLikelyIIFE() const              { return isInParens(); }
+@@ -600,16 +630,22 @@ class ParseNode
+         struct {                        /* ternary: if, for(;;), ?: */
+           private:
+             friend class TernaryNode;
+             ParseNode*  kid1;           /* condition, discriminant, etc. */
+             ParseNode*  kid2;           /* then-part, case list, etc. */
+             ParseNode*  kid3;           /* else-part, default case, etc. */
+         } ternary;
+         struct {                        /* two kids if binary */
++          private:
++            friend class BinaryNode;
++            friend class ForNode;
++            friend class ClassMethod;
++            friend class PropertyAccess;
++            friend class SwitchStatement;
+             ParseNode*  left;
+             ParseNode*  right;
+             union {
+                 unsigned iflags;        /* JSITER_* flags for ParseNodeKind::For node */
+                 bool isStatic;          /* only for ParseNodeKind::ClassMethod */
+                 bool hasDefault;        /* only for ParseNodeKind::Switch */
+             };
+         } binary;
+@@ -639,20 +675,16 @@ class ParseNode
+             friend class LoopControlStatement;
+             PropertyName*    label;    /* target of break/continue statement */
+         } loopControl;
+     } pn_u;
+ 
+ #define pn_objbox       pn_u.name.objbox
+ #define pn_funbox       pn_u.name.funbox
+ #define pn_body         pn_u.name.expr
+-#define pn_left         pn_u.binary.left
+-#define pn_right        pn_u.binary.right
+-#define pn_pval         pn_u.binary.pval
+-#define pn_iflags       pn_u.binary.iflags
+ #define pn_kid          pn_u.unary.kid
+ #define pn_prologue     pn_u.unary.prologue
+ #define pn_atom         pn_u.name.atom
+ #define pn_objbox       pn_u.name.objbox
+ #define pn_expr         pn_u.name.expr
+ #define pn_dval         pn_u.number.value
+ 
+ 
+@@ -815,40 +847,116 @@ struct NullaryNode : public ParseNode
+ struct UnaryNode : public ParseNode
+ {
+     UnaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* kid)
+       : ParseNode(kind, JSOP_NOP, PN_UNARY, pos)
+     {
+         pn_kid = kid;
+     }
+ 
++    static bool test(const ParseNode& node) {
++        return node.isArity(PN_UNARY);
++    }
++
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
+ };
+ 
+-struct BinaryNode : public ParseNode
++class BinaryNode : public ParseNode
+ {
++  public:
+     BinaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* left, ParseNode* right)
+       : ParseNode(kind, op, PN_BINARY, pos)
+     {
+-        pn_left = left;
+-        pn_right = right;
++        pn_u.binary.left = left;
++        pn_u.binary.right = right;
+     }
+ 
+     BinaryNode(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right)
+       : ParseNode(kind, op, PN_BINARY, TokenPos::box(left->pn_pos, right->pn_pos))
+     {
+-        pn_left = left;
+-        pn_right = right;
++        pn_u.binary.left = left;
++        pn_u.binary.right = right;
++    }
++
++    static bool test(const ParseNode& node) {
++        return node.isArity(PN_BINARY);
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
++
++    ParseNode* left() const {
++        return pn_u.binary.left;
++    }
++
++    ParseNode* right() const {
++        return pn_u.binary.right;
++    }
++
++    // Methods used by FoldConstants.cpp.
++    // caller are responsible for keeping the list consistent.
++    ParseNode** unsafeLeftReference() {
++        return &pn_u.binary.left;
++    }
++
++    ParseNode** unsafeRightReference() {
++        return &pn_u.binary.right;
++    }
++};
++
++class AssignmentNode : public BinaryNode
++{
++  public:
++    AssignmentNode(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right)
++      : BinaryNode(kind, op, TokenPos(left->pn_pos.begin, right->pn_pos.end), left, right)
++    {}
++
++    static bool test(const ParseNode& node) {
++        ParseNodeKind kind = node.getKind();
++        bool match = ParseNodeKind::AssignmentStart <= kind &&
++                     kind <= ParseNodeKind::AssignmentLast;
++        MOZ_ASSERT_IF(match, node.is<BinaryNode>());
++        return match;
++    }
++};
++
++class ForNode : public BinaryNode
++{
++  public:
++    ForNode(const TokenPos& pos, ParseNode* forHead, ParseNode* body, unsigned iflags)
++      : BinaryNode(ParseNodeKind::For,
++                   forHead->isKind(ParseNodeKind::ForIn) ? JSOP_ITER : JSOP_NOP,
++                   pos, forHead, body)
++    {
++        MOZ_ASSERT(forHead->isKind(ParseNodeKind::ForIn) ||
++                   forHead->isKind(ParseNodeKind::ForOf) ||
++                   forHead->isKind(ParseNodeKind::ForHead));
++        pn_u.binary.iflags = iflags;
++    }
++
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::For);
++        MOZ_ASSERT_IF(match, node.is<BinaryNode>());
++        return match;
++    }
++
++    TernaryNode* head() const {
++        return &left()->as<TernaryNode>();
++    }
++
++    ParseNode* body() const {
++        return right();
++    }
++
++    unsigned iflags() const {
++        return pn_u.binary.iflags;
++    }
+ };
+ 
+ class TernaryNode : public ParseNode
+ {
+   public:
+     TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3)
+       : TernaryNode(kind, kid1, kid2, kid3,
+                     TokenPos((kid1 ? kid1 : kid2 ? kid2 : kid3)->pn_pos.begin,
+@@ -1310,25 +1418,34 @@ class LabeledStatement : public ParseNod
+ 
+ // Inside a switch statement, a CaseClause is a case-label and the subsequent
+ // statements. The same node type is used for DefaultClauses. The only
+ // difference is that their caseExpression() is null.
+ class CaseClause : public BinaryNode
+ {
+   public:
+     CaseClause(ParseNode* expr, ParseNode* stmts, uint32_t begin)
+-      : BinaryNode(ParseNodeKind::Case, JSOP_NOP, TokenPos(begin, stmts->pn_pos.end), expr, stmts) {}
++      : BinaryNode(ParseNodeKind::Case, JSOP_NOP, TokenPos(begin, stmts->pn_pos.end), expr, stmts)
++    {}
++
++    ParseNode* caseExpression() const {
++        return left();
++    }
+ 
+-    ParseNode* caseExpression() const { return pn_left; }
+-    bool isDefault() const { return !caseExpression(); }
+-    ListNode* statementList() const { return &pn_right->as<ListNode>(); }
++    bool isDefault() const {
++        return !caseExpression();
++    }
++
++    ListNode* statementList() const {
++        return &right()->as<ListNode>();
++    }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Case);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
++        MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
+ };
+ 
+ class LoopControlStatement : public ParseNode
+ {
+   protected:
+@@ -1485,53 +1602,73 @@ class PropertyAccess : public BinaryNode
+       : BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name)
+     {
+         MOZ_ASSERT(lhs != nullptr);
+         MOZ_ASSERT(name != nullptr);
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Dot);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
+-        MOZ_ASSERT_IF(match, node.pn_right->isKind(ParseNodeKind::PropertyName));
++        MOZ_ASSERT_IF(match, node.is<BinaryNode>());
++        MOZ_ASSERT_IF(match, node.as<BinaryNode>().right()->isKind(ParseNodeKind::PropertyName));
+         return match;
+     }
+ 
+     ParseNode& expression() const {
+-        return *pn_u.binary.left;
++        return *left();
++    }
++
++    ParseNode& key() const {
++        return *right();
++    }
++
++    // Method used by BytecodeEmitter::emitPropLHS for optimization.
++    // Those methods allow expression to temporarily be nullptr for
++    // optimization purpose.
++    ParseNode* maybeExpression() const {
++        return left();
++    }
++
++    void setExpression(ParseNode* pn) {
++        pn_u.binary.left = pn;
+     }
+ 
+     PropertyName& name() const {
+-        return *pn_u.binary.right->pn_atom->asPropertyName();
++        return *right()->pn_atom->asPropertyName();
+     }
+ 
+     bool isSuper() const {
+         // ParseNodeKind::SuperBase cannot result from any expression syntax.
+         return expression().isKind(ParseNodeKind::SuperBase);
+     }
+ };
+ 
+-class PropertyByValue : public ParseNode
++class PropertyByValue : public BinaryNode
+ {
+   public:
+     PropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, uint32_t end)
+-      : ParseNode(ParseNodeKind::Elem, JSOP_NOP, PN_BINARY, TokenPos(begin, end))
+-    {
+-        pn_u.binary.left = lhs;
+-        pn_u.binary.right = propExpr;
+-    }
++      : BinaryNode(ParseNodeKind::Elem, JSOP_NOP, TokenPos(begin, end), lhs, propExpr)
++    {}
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Elem);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
++        MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         return match;
+     }
+ 
++    ParseNode& expression() const {
++        return *left();
++    }
++
++    ParseNode& key() const {
++        return *right();
++    }
++
+     bool isSuper() const {
+-        return pn_left->isKind(ParseNodeKind::SuperBase);
++        return left()->isKind(ParseNodeKind::SuperBase);
+     }
+ };
+ 
+ /*
+  * A CallSiteNode represents the implicit call site object argument in a TaggedTemplate.
+  */
+ class CallSiteNode : public ListNode
+ {
+@@ -1549,45 +1686,51 @@ class CallSiteNode : public ListNode
+     }
+ 
+     ListNode* rawNodes() const {
+         MOZ_ASSERT(head());
+         return &head()->as<ListNode>();
+     }
+ };
+ 
+-struct ClassMethod : public BinaryNode {
++class ClassMethod : public BinaryNode
++{
++  public:
+     /*
+      * Method definitions often keep a name and function body that overlap,
+      * so explicitly define the beginning and end here.
+      */
+     ClassMethod(ParseNode* name, ParseNode* body, JSOp op, bool isStatic)
+       : BinaryNode(ParseNodeKind::ClassMethod, op, TokenPos(name->pn_pos.begin, body->pn_pos.end), name, body)
+     {
+         pn_u.binary.isStatic = isStatic;
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::ClassMethod);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
++        MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         return match;
+     }
+ 
+     ParseNode& name() const {
+-        return *pn_u.binary.left;
++        return *left();
+     }
++
+     ParseNode& method() const {
+-        return *pn_u.binary.right;
++        return *right();
+     }
++
+     bool isStatic() const {
+         return pn_u.binary.isStatic;
+     }
+ };
+ 
+-struct SwitchStatement : public BinaryNode {
++class SwitchStatement : public BinaryNode
++{
++  public:
+     SwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* lexicalForCaseList,
+                     bool hasDefault)
+       : BinaryNode(ParseNodeKind::Switch, JSOP_NOP,
+                    TokenPos(begin, lexicalForCaseList->pn_pos.end),
+                    discriminant, lexicalForCaseList)
+     {
+ #ifdef DEBUG
+         MOZ_ASSERT(lexicalForCaseList->isKind(ParseNodeKind::LexicalScope));
+@@ -1604,59 +1747,64 @@ struct SwitchStatement : public BinaryNo
+         MOZ_ASSERT(found == hasDefault);
+ #endif
+ 
+         pn_u.binary.hasDefault = hasDefault;
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Switch);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
++        MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         return match;
+     }
+ 
+     ParseNode& discriminant() const {
+-        return *pn_u.binary.left;
++        return *left();
+     }
++
+     ParseNode& lexicalForCaseList() const {
+-        return *pn_u.binary.right;
++        return *right();
+     }
++
+     bool hasDefault() const {
+         return pn_u.binary.hasDefault;
+     }
+ };
+ 
+-struct ClassNames : public BinaryNode {
++class ClassNames : public BinaryNode
++{
++  public:
+     ClassNames(ParseNode* outerBinding, ParseNode* innerBinding, const TokenPos& pos)
+       : BinaryNode(ParseNodeKind::ClassNames, JSOP_NOP, pos, outerBinding, innerBinding)
+     {
+         MOZ_ASSERT_IF(outerBinding, outerBinding->isKind(ParseNodeKind::Name));
+         MOZ_ASSERT(innerBinding->isKind(ParseNodeKind::Name));
+         MOZ_ASSERT_IF(outerBinding, innerBinding->pn_atom == outerBinding->pn_atom);
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::ClassNames);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
++        MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         return match;
+     }
+ 
+     /*
+      * Classes require two definitions: The first "outer" binding binds the
+      * class into the scope in which it was declared. the outer binding is a
+      * mutable lexial binding. The second "inner" binding binds the class by
+      * name inside a block in which the methods are evaulated. It is immutable,
+      * giving the methods access to the static members of the class even if
+      * the outer binding has been overwritten.
+      */
+     ParseNode* outerBinding() const {
+-        return pn_u.binary.left;
++        return left();
+     }
++
+     ParseNode* innerBinding() const {
+-        return pn_u.binary.right;
++        return right();
+     }
+ };
+ 
+ class ClassNode : public TernaryNode
+ {
+   public:
+     ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock,
+               const TokenPos& pos)
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -4878,38 +4878,42 @@ GeneralParser<ParseHandler, CharT>::Poss
+     MOZ_ASSERT(&parser_ == &other->parser_,
+                "Can't transfer fields to an instance which belongs to a different parser");
+ 
+     transferErrorTo(ErrorKind::Destructuring, other);
+     transferErrorTo(ErrorKind::Expression, other);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
+                                                        YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assign));
+ 
+     if (kind == DeclarationKind::FormalParameter) {
+         pc->functionBox()->hasParameterExprs = true;
+     }
+ 
+     Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+     if (!rhs) {
+         return null();
+     }
+ 
+-    Node assign = handler.newAssignment(ParseNodeKind::Assign, lhs, rhs);
++    BinaryNodeType assign = handler.newAssignment(ParseNodeKind::Assign, lhs, rhs);
+     if (!assign) {
+         return null();
+     }
+ 
+-    if (foldConstants && !FoldConstants(context, &assign, this)) {
+-        return null();
++    if (foldConstants) {
++        Node node = assign;
++        if (!FoldConstants(context, &node, this)) {
++            return null();
++        }
++        assign = handler.asBinary(node);
+     }
+ 
+     return assign;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::bindingIdentifier(DeclarationKind kind,
+@@ -5057,17 +5061,17 @@ GeneralParser<ParseHandler, CharT>::obje
+ 
+                 Node binding = bindingIdentifier(kind, yieldHandling);
+                 if (!binding) {
+                     return null();
+                 }
+ 
+                 tokenStream.consumeKnownToken(TokenKind::Assign);
+ 
+-                Node bindingExpr = bindingInitializer(binding, kind, yieldHandling);
++                BinaryNodeType bindingExpr = bindingInitializer(binding, kind, yieldHandling);
+                 if (!bindingExpr) {
+                     return null();
+                 }
+ 
+                 if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) {
+                     return null();
+                 }
+             } else {
+@@ -5644,17 +5648,17 @@ Parser<FullParseHandler, CharT>::namedIm
+                 return false;
+             }
+ 
+             Node importNameNode = newName(importName, importNamePos);
+             if (!importNameNode) {
+                 return false;
+             }
+ 
+-            Node importSpec = handler.newImportSpec(importNameNode, bindingName);
++            BinaryNodeType importSpec = handler.newImportSpec(importNameNode, bindingName);
+             if (!importSpec) {
+                 return false;
+             }
+ 
+             handler.addList(importSpecSet, importSpec);
+ 
+             TokenKind next;
+             if (!tokenStream.getToken(&next)) {
+@@ -5697,29 +5701,29 @@ Parser<FullParseHandler, CharT>::namedIm
+         if (!noteDeclaredName(bindingName, DeclarationKind::Const, pos())) {
+             return false;
+         }
+ 
+         // The namespace import name is currently required to live on the
+         // environment.
+         pc->varScope().lookupDeclaredName(bindingName)->value()->setClosedOver();
+ 
+-        Node importSpec = handler.newImportSpec(importName, bindingNameNode);
++        BinaryNodeType importSpec = handler.newImportSpec(importName, bindingNameNode);
+         if (!importSpec) {
+             return false;
+         }
+ 
+         handler.addList(importSpecSet, importSpec);
+     }
+ 
+     return true;
+ }
+ 
+ template<typename CharT>
+-ParseNode*
++BinaryNode*
+ Parser<FullParseHandler, CharT>::importDeclaration()
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import));
+ 
+     if (!pc->atModuleLevel()) {
+         error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
+         return null();
+     }
+@@ -5763,17 +5767,17 @@ Parser<FullParseHandler, CharT>::importD
+             if (!bindingName) {
+                 return null();
+             }
+ 
+             if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) {
+                 return null();
+             }
+ 
+-            Node importSpec = handler.newImportSpec(importName, bindingName);
++            BinaryNodeType importSpec = handler.newImportSpec(importName, bindingName);
+             if (!importSpec) {
+                 return null();
+             }
+ 
+             handler.addList(importSpecSet, importSpec);
+ 
+             if (!tokenStream.peekToken(&tt)) {
+                 return null();
+@@ -5808,35 +5812,35 @@ Parser<FullParseHandler, CharT>::importD
+     if (!moduleSpec) {
+         return null();
+     }
+ 
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+-    ParseNode* node =
++    BinaryNode* node =
+         handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
+     if (!node || !pc->sc()->asModuleContext()->builder.processImport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template<typename CharT>
+-inline SyntaxParseHandler::Node
++inline SyntaxParseHandler::BinaryNodeType
+ Parser<SyntaxParseHandler, CharT>::importDeclaration()
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return SyntaxParseHandler::NodeFailure;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-inline typename ParseHandler::Node
++inline typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::importDeclaration()
+ {
+     return asFinalParser()->importDeclaration();
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ inline typename ParseHandler::Node
+ GeneralParser<ParseHandler, CharT>::importDeclarationOrImportExpr(YieldHandling yieldHandling)
+@@ -5897,17 +5901,17 @@ Parser<FullParseHandler, CharT>::checkEx
+         if (node->isKind(ParseNodeKind::Elision)) {
+             continue;
+         }
+ 
+         ParseNode* binding;
+         if (node->isKind(ParseNodeKind::Spread)) {
+             binding = node->pn_kid;
+         } else if (node->isKind(ParseNodeKind::Assign)) {
+-            binding = node->pn_left;
++            binding = node->as<AssignmentNode>().left();
+         } else {
+             binding = node;
+         }
+ 
+         if (!checkExportedNamesForDeclaration(binding)) {
+             return false;
+         }
+     }
+@@ -5944,21 +5948,21 @@ Parser<FullParseHandler, CharT>::checkEx
+ 
+         ParseNode* target;
+         if (node->isKind(ParseNodeKind::Spread)) {
+             target = node->pn_kid;
+         } else {
+             if (node->isKind(ParseNodeKind::MutateProto)) {
+                 target = node->pn_kid;
+             } else {
+-                target = node->pn_right;
++                target = node->as<BinaryNode>().right();
+             }
+ 
+             if (target->isKind(ParseNodeKind::Assign)) {
+-                target = target->pn_left;
++                target = target->as<AssignmentNode>().left();
+             }
+         }
+ 
+         if (!checkExportedNamesForDeclaration(target)) {
+             return false;
+         }
+     }
+ 
+@@ -6018,17 +6022,17 @@ GeneralParser<ParseHandler, CharT>::chec
+ }
+ 
+ template<typename CharT>
+ bool
+ Parser<FullParseHandler, CharT>::checkExportedNamesForDeclarationList(ListNode* node)
+ {
+     for (ParseNode* binding : node->contents()) {
+         if (binding->isKind(ParseNodeKind::Assign)) {
+-            binding = binding->pn_left;
++            binding = binding->as<AssignmentNode>().left();
+         } else {
+             MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
+         }
+ 
+         if (!checkExportedNamesForDeclaration(binding)) {
+             return false;
+         }
+     }
+@@ -6130,31 +6134,31 @@ inline bool
+ PerHandlerParser<SyntaxParseHandler>::processExport(Node node)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template<>
+ inline bool
+-PerHandlerParser<FullParseHandler>::processExportFrom(ParseNode* node)
++PerHandlerParser<FullParseHandler>::processExportFrom(BinaryNodeType node)
+ {
+     return pc->sc()->asModuleContext()->builder.processExportFrom(node);
+ }
+ 
+ template<>
+ inline bool
+-PerHandlerParser<SyntaxParseHandler>::processExportFrom(Node node)
++PerHandlerParser<SyntaxParseHandler>::processExportFrom(BinaryNodeType node)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::From));
+ 
+@@ -6168,30 +6172,30 @@ GeneralParser<ParseHandler, CharT>::expo
+     if (!moduleSpec) {
+         return null();
+     }
+ 
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportFromDeclaration(begin, specList, moduleSpec);
++    BinaryNodeType node = handler.newExportFromDeclaration(begin, specList, moduleSpec);
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExportFrom(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportBatch(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Mul));
+ 
+@@ -6215,17 +6219,17 @@ GeneralParser<ParseHandler, CharT>::expo
+ }
+ 
+ template<typename CharT>
+ bool
+ Parser<FullParseHandler, CharT>::checkLocalExportNames(ListNode* node)
+ {
+     // ES 2017 draft 15.2.3.1.
+     for (ParseNode* next : node->contents()) {
+-        ParseNode* name = next->pn_left;
++        ParseNode* name = next->as<BinaryNode>().left();
+         MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
+ 
+         RootedPropertyName ident(context, name->pn_atom->asPropertyName());
+         if (!checkLocalExportName(ident, name->pn_pos.begin)) {
+             return false;
+         }
+     }
+ 
+@@ -6296,17 +6300,17 @@ GeneralParser<ParseHandler, CharT>::expo
+         if (!exportName) {
+             return null();
+         }
+ 
+         if (!checkExportedNameForClause(exportName)) {
+             return null();
+         }
+ 
+-        Node exportSpec = handler.newExportSpec(bindingName, exportName);
++        BinaryNodeType exportSpec = handler.newExportSpec(bindingName, exportName);
+         if (!exportSpec) {
+             return null();
+         }
+ 
+         handler.addList(kid, exportSpec);
+ 
+         TokenKind next;
+         if (!tokenStream.getToken(&next)) {
+@@ -6490,73 +6494,75 @@ GeneralParser<ParseHandler, CharT>::expo
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
+                                                                      uint32_t toStringStart,
+                                                                      FunctionAsyncKind asyncKind /* = SyncFunction */)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function));
+ 
+     Node kid = functionStmt(toStringStart, YieldIsName, AllowDefaultName, asyncKind);
+     if (!kid) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
++    BinaryNodeType node = handler.newExportDefaultDeclaration(kid, null(),
++                                                              TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class));
+ 
+     ClassNodeType kid = classDefinition(YieldIsName, ClassStatement, AllowDefaultName);
+     if (!kid) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
++    BinaryNodeType node = handler.newExportDefaultDeclaration(kid, null(),
++                                                              TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     HandlePropertyName name = context->names().default_;
+     Node nameNode = newName(name);
+@@ -6571,30 +6577,31 @@ GeneralParser<ParseHandler, CharT>::expo
+     if (!kid) {
+         return null();
+     }
+ 
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportDefaultDeclaration(kid, nameNode, TokenPos(begin, pos().end));
++    BinaryNodeType node = handler.newExportDefaultDeclaration(kid, nameNode,
++                                                              TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportDefault(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Default));
+ 
+@@ -6849,17 +6856,17 @@ GeneralParser<ParseHandler, CharT>::ifSt
+         }
+         elseBranch = ifNode;
+     }
+ 
+     return ifNode;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::doWhileStatement(YieldHandling yieldHandling)
+ {
+     uint32_t begin = pos().begin;
+     ParseContext::Statement stmt(pc, StatementKind::DoLoop);
+     Node body = statement(yieldHandling);
+     if (!body) {
+         return null();
+     }
+@@ -6878,17 +6885,17 @@ GeneralParser<ParseHandler, CharT>::doWh
+     bool ignored;
+     if (!tokenStream.matchToken(&ignored, TokenKind::Semi, TokenStream::Operand)) {
+         return null();
+     }
+     return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::whileStatement(YieldHandling yieldHandling)
+ {
+     uint32_t begin = pos().begin;
+     ParseContext::Statement stmt(pc, StatementKind::WhileLoop);
+     Node cond = condition(InAllowed, yieldHandling);
+     if (!cond) {
+         return null();
+     }
+@@ -7236,30 +7243,30 @@ GeneralParser<ParseHandler, CharT>::forS
+         }
+     }
+ 
+     Node body = statement(yieldHandling);
+     if (!body) {
+         return null();
+     }
+ 
+-    Node forLoop = handler.newForStatement(begin, forHead, body, iflags);
++    ForNodeType forLoop = handler.newForStatement(begin, forHead, body, iflags);
+     if (!forLoop) {
+         return null();
+     }
+ 
+     if (forLoopLexicalScope) {
+         return finishLexicalScope(*forLoopLexicalScope, forLoop);
+     }
+ 
+     return forLoop;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::SwitchStatementType
+ GeneralParser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Switch));
+     uint32_t begin = pos().begin;
+ 
+     MUST_MATCH_TOKEN(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_SWITCH);
+ 
+     Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
+@@ -7352,21 +7359,21 @@ GeneralParser<ParseHandler, CharT>::swit
+                     }
+                 } else if (handler.isReturnStatement(stmt)) {
+                     afterReturn = true;
+                 }
+             }
+             handler.addStatementToList(body, stmt);
+         }
+ 
+-        Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
+-        if (!casepn) {
+-            return null();
+-        }
+-        handler.addCaseStatementToList(caseList, casepn);
++        CaseClauseType caseClause = handler.newCaseOrDefault(caseBegin, caseExpr, body);
++        if (!caseClause) {
++            return null();
++        }
++        handler.addCaseStatementToList(caseList, caseClause);
+     }
+ 
+     Node lexicalForCaseList = finishLexicalScope(scope, caseList);
+     if (!lexicalForCaseList) {
+         return null();
+     }
+ 
+     handler.setEndPosition(lexicalForCaseList, pos().end);
+@@ -7538,17 +7545,17 @@ GeneralParser<ParseHandler, CharT>::yiel
+     }
+     if (kind == ParseNodeKind::YieldStar) {
+         return handler.newYieldStarExpression(begin, exprNode);
+     }
+     return handler.newYieldExpression(begin, exprNode);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::With));
+     uint32_t begin = pos().begin;
+ 
+     // Usually we want the constructs forbidden in strict mode code to be a
+     // subset of those that ContextOptions::extraWarnings() warns about, and we
+     // use strictModeError directly.  But while 'with' is forbidden in strict
+@@ -9506,18 +9513,18 @@ GeneralParser<ParseHandler, CharT>::memb
+     if (!CheckRecursionLimit(context)) {
+         return null();
+     }
+ 
+     /* Check for new expression first. */
+     if (tt == TokenKind::New) {
+         uint32_t newBegin = pos().begin;
+         // Make sure this wasn't a |new.target| in disguise.
+-        Node newTarget;
+-        if (!tryNewTarget(newTarget)) {
++        BinaryNodeType newTarget;
++        if (!tryNewTarget(&newTarget)) {
+             return null();
+         }
+         if (newTarget) {
+             lhs = newTarget;
+         } else {
+             // Gotten by tryNewTarget
+             tt = anyChars.currentToken().type;
+             Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt,
+@@ -10599,26 +10606,28 @@ GeneralParser<ParseHandler, CharT>::obje
+                     // Only |__proto__: v| mutates [[Prototype]]. Getters,
+                     // setters, method/generator definitions, computed
+                     // property name versions of all of these, and shorthands
+                     // do not.
+                     if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr)) {
+                         return null();
+                     }
+                 } else {
++                    // Use Node instead of BinaryNodeType to pass it to
++                    // FoldConstants.
+                     Node propDef = handler.newPropertyDefinition(propName, propExpr);
+                     if (!propDef) {
+                         return null();
+                     }
+ 
+                     if (foldConstants && !FoldConstants(context, &propDef, this)) {
+                         return null();
+                     }
+ 
+-                    handler.addPropertyDefinition(literal, propDef);
++                    handler.addPropertyDefinition(literal, handler.asBinary(propDef));
+                 }
+             } else if (propType == PropertyType::Shorthand) {
+                 /*
+                  * Support, e.g., |({x, y} = o)| as destructuring shorthand
+                  * for |({x: x, y: y} = o)|, and |var o = {x, y}| as
+                  * initializer shorthand for |var o = {x: x, y: y}|.
+                  */
+                 Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+@@ -10684,17 +10693,17 @@ GeneralParser<ParseHandler, CharT>::obje
+                     }
+                 }
+ 
+                 Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+                 if (!rhs) {
+                     return null();
+                 }
+ 
+-                Node propExpr = handler.newAssignment(ParseNodeKind::Assign, lhs, rhs);
++                BinaryNodeType propExpr = handler.newAssignment(ParseNodeKind::Assign, lhs, rhs);
+                 if (!propExpr) {
+                     return null();
+                 }
+ 
+                 if (!handler.addPropertyDefinition(literal, propName, propExpr)) {
+                     return null();
+                 }
+             } else {
+@@ -10799,21 +10808,21 @@ GeneralParser<ParseHandler, CharT>::meth
+     }
+ 
+     return functionDefinition(pn, toStringStart, InAllowed, yieldHandling, funName, kind,
+                               generatorKind, asyncKind);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+-GeneralParser<ParseHandler, CharT>::tryNewTarget(Node &newTarget)
++GeneralParser<ParseHandler, CharT>::tryNewTarget(BinaryNodeType* newTarget)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::New));
+ 
+-    newTarget = null();
++    *newTarget = null();
+ 
+     Node newHolder = handler.newPosHolder(pos());
+     if (!newHolder) {
+         return false;
+     }
+ 
+     uint32_t begin = pos().begin;
+ 
+@@ -10842,22 +10851,22 @@ GeneralParser<ParseHandler, CharT>::tryN
+         return false;
+     }
+ 
+     Node targetHolder = handler.newPosHolder(pos());
+     if (!targetHolder) {
+         return false;
+     }
+ 
+-    newTarget = handler.newNewTarget(newHolder, targetHolder);
+-    return !!newTarget;
+-}
+-
+-template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++    *newTarget = handler.newNewTarget(newHolder, targetHolder);
++    return !!*newTarget;
++}
++
++template <class ParseHandler, typename CharT>
++typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::importExpr(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import));
+ 
+     Node importHolder = handler.newPosHolder(pos());
+     if (!importHolder) {
+         return null();
+     }
+diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
+--- a/js/src/frontend/Parser.h
++++ b/js/src/frontend/Parser.h
+@@ -545,17 +545,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     Node newThisName();
+     Node newDotGeneratorName();
+ 
+     Node identifierReference(Handle<PropertyName*> name);
+ 
+     Node noSubstitutionTaggedTemplate();
+ 
+     inline bool processExport(Node node);
+-    inline bool processExportFrom(Node node);
++    inline bool processExportFrom(BinaryNodeType node);
+ 
+     // If ParseHandler is SyntaxParseHandler:
+     //   Do nothing.
+     // If ParseHandler is FullParseHandler:
+     //   Disable syntax parsing of all future inner functions during this
+     //   full-parse.
+     inline void disableSyntaxParser();
+ 
+@@ -583,17 +583,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+   public:
+     bool isValidSimpleAssignmentTarget(Node node,
+                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
+ 
+     Node newPropertyName(PropertyName* key, const TokenPos& pos) {
+         return handler.newPropertyName(key, pos);
+     }
+ 
+-    Node newPropertyAccess(Node expr, Node key) {
++    PropertyAccessType newPropertyAccess(Node expr, Node key) {
+         return handler.newPropertyAccess(expr, key);
+     }
+ 
+     FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
+                                 Directives directives, GeneratorKind generatorKind,
+                                 FunctionAsyncKind asyncKind);
+ };
+ 
+@@ -1020,64 +1020,64 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     Node functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
+                       FunctionAsyncKind asyncKind);
+ 
+     Node statement(YieldHandling yieldHandling);
+     bool maybeParseDirective(ListNodeType list, Node pn, bool* cont);
+ 
+     Node blockStatement(YieldHandling yieldHandling,
+                         unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
+-    Node doWhileStatement(YieldHandling yieldHandling);
+-    Node whileStatement(YieldHandling yieldHandling);
++    BinaryNodeType doWhileStatement(YieldHandling yieldHandling);
++    BinaryNodeType whileStatement(YieldHandling yieldHandling);
+ 
+     Node forStatement(YieldHandling yieldHandling);
+     bool forHeadStart(YieldHandling yieldHandling,
+                       ParseNodeKind* forHeadKind,
+                       Node* forInitialPart,
+                       mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
+                       Node* forInOrOfExpression);
+     Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
+ 
+-    Node switchStatement(YieldHandling yieldHandling);
++    SwitchStatementType switchStatement(YieldHandling yieldHandling);
+     Node continueStatement(YieldHandling yieldHandling);
+     Node breakStatement(YieldHandling yieldHandling);
+     Node returnStatement(YieldHandling yieldHandling);
+-    Node withStatement(YieldHandling yieldHandling);
++    BinaryNodeType withStatement(YieldHandling yieldHandling);
+     Node throwStatement(YieldHandling yieldHandling);
+     TernaryNodeType tryStatement(YieldHandling yieldHandling);
+     Node catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope);
+     Node debuggerStatement();
+ 
+     Node variableStatement(YieldHandling yieldHandling);
+ 
+     Node labeledStatement(YieldHandling yieldHandling);
+     Node labeledItem(YieldHandling yieldHandling);
+ 
+     TernaryNodeType ifStatement(YieldHandling yieldHandling);
+     Node consequentOrAlternative(YieldHandling yieldHandling);
+ 
+     ListNodeType lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
+ 
+-    inline Node importDeclaration();
++    inline BinaryNodeType importDeclaration();
+     Node importDeclarationOrImportExpr(YieldHandling yieldHandling);
+ 
+-    Node exportFrom(uint32_t begin, Node specList);
+-    Node exportBatch(uint32_t begin);
++    BinaryNodeType exportFrom(uint32_t begin, Node specList);
++    BinaryNodeType exportBatch(uint32_t begin);
+     inline bool checkLocalExportNames(ListNodeType node);
+     Node exportClause(uint32_t begin);
+     Node exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
+                                    FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+     Node exportVariableStatement(uint32_t begin);
+     Node exportClassDeclaration(uint32_t begin);
+     Node exportLexicalDeclaration(uint32_t begin, DeclarationKind kind);
+-    Node exportDefaultFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
+-                                          FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+-    Node exportDefaultClassDeclaration(uint32_t begin);
+-    Node exportDefaultAssignExpr(uint32_t begin);
+-    Node exportDefault(uint32_t begin);
++    BinaryNodeType exportDefaultFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
++                                                    FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
++    BinaryNodeType exportDefaultClassDeclaration(uint32_t begin);
++    BinaryNodeType exportDefaultAssignExpr(uint32_t begin);
++    BinaryNodeType exportDefault(uint32_t begin);
+     Node exportDeclaration();
+ 
+     Node expressionStatement(YieldHandling yieldHandling,
+                              InvokedPrediction invoked = PredictUninvoked);
+ 
+     // Declaration parsing.  The main entrypoint is Parser::declarationList,
+     // with sub-functionality split out into the remaining methods.
+ 
+@@ -1150,19 +1150,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+                     bool allowCallSyntax = true, PossibleError* possibleError = nullptr,
+                     InvokedPrediction invoked = PredictUninvoked);
+     Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                      TokenKind tt, PossibleError* possibleError,
+                      InvokedPrediction invoked = PredictUninvoked);
+     Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+                       TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr);
+ 
+-    bool tryNewTarget(Node& newTarget);
++    bool tryNewTarget(BinaryNodeType* newTarget);
+ 
+-    Node importExpr(YieldHandling yieldHandling);
++    BinaryNodeType importExpr(YieldHandling yieldHandling);
+ 
+     Node methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName);
+ 
+     /*
+      * Additional JS parsers.
+      */
+     bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
+                            Node funcpn);
+@@ -1244,17 +1244,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     Node computedPropertyName(YieldHandling yieldHandling,
+                               const mozilla::Maybe<DeclarationKind>& maybeDecl,
+                               ListNodeType literal);
+     ListNodeType arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
+     inline Node newRegExp();
+ 
+     ListNodeType objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
+ 
+-    Node bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling);
++    BinaryNodeType bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling);
+     Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling);
+     Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
+                                     TokenKind tt);
+     ListNodeType objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+     ListNodeType arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+ 
+     enum class TargetBehavior {
+         PermitAssignmentPattern,
+@@ -1387,17 +1387,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     inline void setAwaitHandling(AwaitHandling awaitHandling);
+     inline void setInParametersOfAsyncFunction(bool inParameters);
+ 
+     Node newRegExp();
+ 
+     // Parse a module.
+     Node moduleBody(ModuleSharedContext* modulesc);
+ 
+-    inline Node importDeclaration();
++    inline BinaryNodeType importDeclaration();
+     inline bool checkLocalExportNames(ListNodeType node);
+     inline bool checkExportedName(JSAtom* exportName);
+     inline bool checkExportedNamesForArrayBinding(ListNodeType array);
+     inline bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     inline bool checkExportedNamesForDeclaration(Node node);
+     inline bool checkExportedNamesForDeclarationList(ListNodeType node);
+     inline bool checkExportedNameForFunction(Node node);
+     inline bool checkExportedNameForClass(ClassNodeType classNode);
+@@ -1512,17 +1512,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     friend class AutoInParametersOfAsyncFunction<SyntaxParseHandler, CharT>;
+     inline void setInParametersOfAsyncFunction(bool inParameters);
+ 
+     Node newRegExp();
+ 
+     // Parse a module.
+     Node moduleBody(ModuleSharedContext* modulesc);
+ 
+-    Node importDeclaration();
++    BinaryNodeType importDeclaration();
+     bool checkLocalExportNames(ListNodeType node);
+     bool checkExportedName(JSAtom* exportName);
+     bool checkExportedNamesForArrayBinding(ListNodeType array);
+     bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     bool checkExportedNamesForDeclaration(Node node);
+     bool checkExportedNamesForDeclarationList(ListNodeType node);
+     bool checkExportedNameForFunction(Node node);
+     bool checkExportedNameForClass(ClassNodeType classNode);
+diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
+--- a/js/src/frontend/SyntaxParseHandler.h
++++ b/js/src/frontend/SyntaxParseHandler.h
+@@ -263,116 +263,122 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     // Expressions
+ 
+     ListNodeType newArrayLiteral(uint32_t begin) { return NodeUnparenthesizedArray; }
+     MOZ_MUST_USE bool addElision(ListNodeType literal, const TokenPos& pos) { return true; }
+     MOZ_MUST_USE bool addSpreadElement(ListNodeType literal, uint32_t begin, Node inner) { return true; }
+     void addArrayElement(ListNodeType literal, Node element) { }
+ 
+     ListNodeType newArguments(const TokenPos& pos) { return NodeGeneric; }
+-    Node newCall(Node callee, Node args) { return NodeFunctionCall; }
++    BinaryNodeType newCall(Node callee, Node args) { return NodeFunctionCall; }
+ 
+-    Node newSuperCall(Node callee, Node args) { return NodeGeneric; }
+-    Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; }
++    BinaryNodeType newSuperCall(Node callee, Node args) { return NodeGeneric; }
++    BinaryNodeType newTaggedTemplate(Node tag, Node args) { return NodeGeneric; }
+ 
+     ListNodeType newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
+     ListNodeType newClassMethodList(uint32_t begin) { return NodeGeneric; }
+-    Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; }
++    ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
++        return NodeGeneric;
++    }
+     ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
+ 
+-    Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
++    BinaryNodeType newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
+     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
+     Node newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
+ 
+     MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) { return true; }
+-    Node newPropertyDefinition(Node name, Node expr) { return NodeGeneric; }
+-    void addPropertyDefinition(ListNodeType literal, Node propdef) {}
++    BinaryNodeType newPropertyDefinition(Node key, Node val) { return NodeGeneric; }
++    void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {}
+     MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node expr) { return true; }
+     MOZ_MUST_USE bool addShorthand(ListNodeType literal, Node name, Node expr) { return true; }
+     MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { return true; }
+     MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key, Node fn, AccessorType atype) { return true; }
+     MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key, Node fn, AccessorType atype, bool isStatic) { return true; }
+     Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
+     Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
+     Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
+ 
+     // Statements
+ 
+     ListNodeType newStatementList(const TokenPos& pos) { return NodeGeneric; }
+     void addStatementToList(ListNodeType list, Node stmt) {}
+     void setListEndPosition(ListNodeType list, const TokenPos& pos) {}
+-    void addCaseStatementToList(ListNodeType list, Node caseClause) {}
++    void addCaseStatementToList(ListNodeType list, CaseClauseType caseClause) {}
+     MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) { return true; }
+     Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
+ 
+     Node newExportDeclaration(Node kid, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+-    Node newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
++    BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
+         return NodeGeneric;
+     }
+-    Node newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) {
++    BinaryNodeType newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+-    Node newExportSpec(Node bindingName, Node exportName) {
++    BinaryNodeType newExportSpec(Node bindingName, Node exportName) {
+         return NodeGeneric;
+     }
+     Node newExportBatchSpec(const TokenPos& pos) {
+         return NodeGeneric;
+     }
+-    Node newImportMeta(Node importHolder, Node metaHolder) {
++    BinaryNodeType newImportMeta(Node importHolder, Node metaHolder) {
+         return NodeGeneric;
+     }
+-    Node newCallImport(Node importHolder, Node singleArg) {
++    BinaryNodeType newCallImport(Node importHolder, Node singleArg) {
+         return NodeGeneric;
+     }
+ 
+-    Node newSetThis(Node thisName, Node value) { return value; }
++    BinaryNodeType newSetThis(Node thisName, Node value) { return value; }
+ 
+     Node newExprStatement(Node expr, uint32_t end) {
+         return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
+     }
+ 
+     TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch, Node elseBranch) {
+         return NodeGeneric;
+     }
+-    Node newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; }
+-    Node newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
+-    Node newSwitchStatement(uint32_t begin, Node discriminant, Node lexicalForCaseList,
+-                            bool hasDefault)
++    BinaryNodeType newDoWhileStatement(Node body, Node cond, const TokenPos& pos) {
++        return NodeGeneric;
++    }
++    BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
++    SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant,
++                                           Node lexicalForCaseList, bool hasDefault)
+     {
+         return NodeGeneric;
+     }
+-    Node newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
++    CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+     Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
+     Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
+     Node newReturnStatement(Node expr, const TokenPos& pos) { return NodeReturn; }
+     Node newExpressionBody(Node expr) { return NodeReturn; }
+-    Node newWithStatement(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
++    BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+ 
+     Node newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
+     TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
+         return NodeGeneric;
+     }
+     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
+ 
+     Node newPropertyName(PropertyName* name, const TokenPos& pos) {
+         lastAtom = name;
+         return NodeGeneric;
+     }
+ 
+-    Node newPropertyAccess(Node expr, Node key) {
++    PropertyAccessType newPropertyAccess(Node expr, Node key) {
+         return NodeDottedProperty;
+     }
+ 
+-    Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
++    PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) {
++        return NodeElement;
++    }
+ 
+     MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) {
+         return true;
+     }
+ 
+     MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(Node funcpn, Node pn) { return true; }
+ 
+     Node newFunctionStatement(const TokenPos& pos) { return NodeFunctionStatement; }
+@@ -386,17 +392,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     Node newArrowFunction(const TokenPos& pos) { return NodeFunctionArrow; }
+ 
+     void setFunctionFormalParametersAndBody(Node funcNode, Node kid) {}
+     void setFunctionBody(Node pn, Node kid) {}
+     void setFunctionBox(Node pn, FunctionBox* funbox) {}
+     void addFunctionFormalParameter(Node pn, Node argpn) {}
+ 
+-    Node newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) {
++    ForNodeType newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) {
+         return NodeGeneric;
+     }
+ 
+     TernaryNodeType newForHead(Node init, Node test, Node update, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+ 
+     TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target, Node iteratedExpr, const TokenPos& pos) {
+@@ -454,21 +460,21 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         MOZ_ASSERT(list == NodeGeneric ||
+                    list == NodeUnparenthesizedArray ||
+                    list == NodeUnparenthesizedObject ||
+                    list == NodeVarDeclaration ||
+                    list == NodeLexicalDeclaration ||
+                    list == NodeFunctionCall);
+     }
+ 
+-    Node newNewExpression(uint32_t begin, Node ctor, Node args) {
++    BinaryNodeType newNewExpression(uint32_t begin, Node ctor, Node args) {
+         return NodeGeneric;
+     }
+ 
+-    Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs) {
++    AssignmentNodeType newAssignment(ParseNodeKind kind, Node lhs, Node rhs) {
+         return kind == ParseNodeKind::Assign ? NodeUnparenthesizedAssignment : NodeGeneric;
+     }
+ 
+     bool isUnparenthesizedAssignment(Node node) {
+         return node == NodeUnparenthesizedAssignment;
+     }
+ 
+     bool isUnparenthesizedUnaryExpression(Node node) {
+diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
+--- a/js/src/wasm/AsmJS.cpp
++++ b/js/src/wasm/AsmJS.cpp
+@@ -399,25 +399,23 @@ UnaryKid(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isArity(PN_UNARY));
+     return pn->pn_kid;
+ }
+ 
+ static inline ParseNode*
+ BinaryRight(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-    return pn->pn_right;
++    return pn->as<BinaryNode>().right();
+ }
+ 
+ static inline ParseNode*
+ BinaryLeft(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-    return pn->pn_left;
++    return pn->as<BinaryNode>().left();
+ }
+ 
+ static inline ParseNode*
+ ReturnExpr(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Return));
+     return UnaryKid(pn);
+ }
+@@ -625,41 +623,35 @@ NumberNodeHasFrac(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Number));
+     return pn->pn_u.number.decimalPoint == HasDecimal;
+ }
+ 
+ static ParseNode*
+ DotBase(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-    return pn->pn_left;
++    return &pn->as<PropertyAccess>().expression();
+ }
+ 
+ static PropertyName*
+ DotMember(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot));
+-    MOZ_ASSERT(pn->isArity(PN_BINARY));
+-    return pn->pn_right->pn_atom->asPropertyName();
++    return &pn->as<PropertyAccess>().name();
+ }
+ 
+ static ParseNode*
+ ElemBase(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Elem));
+-    return BinaryLeft(pn);
++    return &pn->as<PropertyByValue>().expression();
+ }
+ 
+ static ParseNode*
+ ElemIndex(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Elem));
+-    return BinaryRight(pn);
++    return &pn->as<PropertyByValue>().key();
+ }
+ 
+ static inline JSFunction*
+ FunctionObject(ParseNode* fn)
+ {
+     MOZ_ASSERT(fn->isKind(ParseNodeKind::Function));
+     MOZ_ASSERT(fn->isArity(PN_CODE));
+     return fn->pn_funbox->function();

+ 3868 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478480.patch

@@ -0,0 +1,3868 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726377 -32400
+#      Wed Sep 12 13:26:17 2018 +0900
+# Node ID fa508359ae289d59cdbbd5061080b692fa5a3d5b
+# Parent  f1c5898b0982188af75c672dee68a0ca95051376
+Bug 1479659 - Part 4: Add accessors to UnaryNode and subclasses. r=jwalden
+
+diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
+--- a/js/src/builtin/ModuleObject.cpp
++++ b/js/src/builtin/ModuleObject.cpp
+@@ -1421,20 +1421,21 @@ ModuleBuilder::appendImportEntryObject(H
+ 
+ bool
+ ModuleBuilder::processExport(frontend::ParseNode* exportNode)
+ {
+     using namespace js::frontend;
+ 
+     MOZ_ASSERT(exportNode->isKind(ParseNodeKind::Export) ||
+                exportNode->isKind(ParseNodeKind::ExportDefault));
+-    MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::Export), exportNode->is<UnaryNode>());
+ 
+     bool isDefault = exportNode->isKind(ParseNodeKind::ExportDefault);
+-    ParseNode* kid = isDefault ? exportNode->as<BinaryNode>().left() : exportNode->pn_kid;
++    ParseNode* kid = isDefault
++                     ? exportNode->as<BinaryNode>().left()
++                     : exportNode->as<UnaryNode>().kid();
+ 
+     if (isDefault && exportNode->as<BinaryNode>().right()) {
+         // This is an export default containing an expression.
+         HandlePropertyName localName = cx_->names().default_;
+         HandlePropertyName exportName = cx_->names().default_;
+         return appendExportEntry(exportName, localName);
+     }
+ 
+@@ -1544,17 +1545,17 @@ ModuleBuilder::processExportArrayBinding
+     MOZ_ASSERT(array->isKind(ParseNodeKind::Array));
+ 
+     for (ParseNode* node : array->contents()) {
+         if (node->isKind(ParseNodeKind::Elision)) {
+             continue;
+         }
+ 
+         if (node->isKind(ParseNodeKind::Spread)) {
+-            node = node->pn_kid;
++            node = node->as<UnaryNode>().kid();
+         } else if (node->isKind(ParseNodeKind::Assign)) {
+             node = node->as<AssignmentNode>().left();
+         }
+ 
+         if (!processExportBinding(node)) {
+             return false;
+         }
+     }
+@@ -1572,20 +1573,20 @@ ModuleBuilder::processExportObjectBindin
+     for (ParseNode* node : obj->contents()) {
+         MOZ_ASSERT(node->isKind(ParseNodeKind::MutateProto) ||
+                    node->isKind(ParseNodeKind::Colon) ||
+                    node->isKind(ParseNodeKind::Shorthand) ||
+                    node->isKind(ParseNodeKind::Spread));
+ 
+         ParseNode* target;
+         if (node->isKind(ParseNodeKind::Spread)) {
+-            target = node->pn_kid;
++            target = node->as<UnaryNode>().kid();
+         } else {
+             if (node->isKind(ParseNodeKind::MutateProto)) {
+-                target = node->pn_kid;
++                target = node->as<UnaryNode>().kid();
+             } else {
+                 target = node->as<BinaryNode>().right();
+             }
+ 
+             if (target->isKind(ParseNodeKind::Assign)) {
+                 target = target->as<AssignmentNode>().left();
+             }
+         }
+diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
+--- a/js/src/builtin/ReflectParse.cpp
++++ b/js/src/builtin/ReflectParse.cpp
+@@ -2160,17 +2160,17 @@ ASTSerializer::exportDeclaration(ParseNo
+     MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::Export), exportNode->is<UnaryNode>());
+     MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::ExportFrom),
+                   exportNode->as<BinaryNode>().right()->isKind(ParseNodeKind::String));
+ 
+     RootedValue decl(cx, NullValue());
+     NodeVector elts(cx);
+ 
+     ParseNode* kid = exportNode->isKind(ParseNodeKind::Export)
+-                     ? exportNode->pn_kid
++                     ? exportNode->as<UnaryNode>().kid()
+                      : exportNode->as<BinaryNode>().left();
+     switch (ParseNodeKind kind = kid->getKind()) {
+       case ParseNodeKind::ExportSpecList: {
+         ListNode* specList = &kid->as<ListNode>();
+         if (!elts.reserve(specList->count())) {
+             return false;
+         }
+ 
+@@ -2416,17 +2416,17 @@ ASTSerializer::statement(ParseNode* pn, 
+         return exportDeclaration(pn, dst);
+ 
+       case ParseNodeKind::EmptyStatement:
+         return builder.emptyStatement(&pn->pn_pos, dst);
+ 
+       case ParseNodeKind::ExpressionStatement:
+       {
+         RootedValue expr(cx);
+-        return expression(pn->pn_kid, &expr) &&
++        return expression(pn->as<UnaryNode>().kid(), &expr) &&
+             builder.expressionStatement(expr, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::LexicalScope:
+         pn = pn->pn_expr;
+         if (!pn->isKind(ParseNodeKind::StatementList)) {
+             return statement(pn, dst);
+         }
+@@ -2578,32 +2578,36 @@ ASTSerializer::statement(ParseNode* pn, 
+         RootedAtom pnAtom(cx, pn->as<LabeledStatement>().label());
+         return identifier(pnAtom, nullptr, &label) &&
+                statement(pn->pn_expr, &stmt) &&
+                builder.labeledStatement(label, stmt, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Throw:
+       {
+-        MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
++        UnaryNode* throwNode = &pn->as<UnaryNode>();
++        ParseNode* operand = throwNode->kid();
++        MOZ_ASSERT(throwNode->pn_pos.encloses(operand->pn_pos));
+ 
+         RootedValue arg(cx);
+ 
+-        return optExpression(pn->pn_kid, &arg) &&
+-               builder.throwStatement(arg, &pn->pn_pos, dst);
++        return expression(operand, &arg) &&
++               builder.throwStatement(arg, &throwNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Return:
+       {
+-        MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
++        UnaryNode* returnNode = &pn->as<UnaryNode>();
++        ParseNode* operand = returnNode->kid();
++        MOZ_ASSERT_IF(operand, returnNode->pn_pos.encloses(operand->pn_pos));
+ 
+         RootedValue arg(cx);
+ 
+-        return optExpression(pn->pn_kid, &arg) &&
+-               builder.returnStatement(arg, &pn->pn_pos, dst);
++        return optExpression(operand, &arg) &&
++               builder.returnStatement(arg, &returnNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Debugger:
+         return builder.debuggerStatement(&pn->pn_pos, dst);
+ 
+       case ParseNodeKind::Class:
+         return classDefinition(&pn->as<ClassNode>(), false, dst);
+ 
+@@ -2788,33 +2792,37 @@ ASTSerializer::expression(ParseNode* pn,
+ 
+       case ParseNodeKind::Or:
+       case ParseNodeKind::And:
+         return leftAssociate(&pn->as<ListNode>(), dst);
+ 
+       case ParseNodeKind::PreIncrement:
+       case ParseNodeKind::PreDecrement:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+-
+-        bool inc = pn->isKind(ParseNodeKind::PreIncrement);
++        UnaryNode* incDec = &pn->as<UnaryNode>();
++        ParseNode* operand = incDec->kid();
++        MOZ_ASSERT(incDec->pn_pos.encloses(operand->pn_pos));
++
++        bool inc = incDec->isKind(ParseNodeKind::PreIncrement);
+         RootedValue expr(cx);
+-        return expression(pn->pn_kid, &expr) &&
+-               builder.updateExpression(expr, inc, true, &pn->pn_pos, dst);
++        return expression(operand, &expr) &&
++               builder.updateExpression(expr, inc, true, &incDec->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::PostIncrement:
+       case ParseNodeKind::PostDecrement:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+-
+-        bool inc = pn->isKind(ParseNodeKind::PostIncrement);
++        UnaryNode* incDec = &pn->as<UnaryNode>();
++        ParseNode* operand = incDec->kid();
++        MOZ_ASSERT(incDec->pn_pos.encloses(operand->pn_pos));
++
++        bool inc = incDec->isKind(ParseNodeKind::PostIncrement);
+         RootedValue expr(cx);
+-        return expression(pn->pn_kid, &expr) &&
+-               builder.updateExpression(expr, inc, false, &pn->pn_pos, dst);
++        return expression(operand, &expr) &&
++               builder.updateExpression(expr, inc, false, &incDec->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Assign:
+       case ParseNodeKind::AddAssign:
+       case ParseNodeKind::SubAssign:
+       case ParseNodeKind::BitOrAssign:
+       case ParseNodeKind::BitXorAssign:
+       case ParseNodeKind::BitAndAssign:
+@@ -2875,24 +2883,26 @@ ASTSerializer::expression(ParseNode* pn,
+       case ParseNodeKind::TypeOfName:
+       case ParseNodeKind::TypeOfExpr:
+       case ParseNodeKind::Void:
+       case ParseNodeKind::Not:
+       case ParseNodeKind::BitNot:
+       case ParseNodeKind::Pos:
+       case ParseNodeKind::Await:
+       case ParseNodeKind::Neg: {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+-
+-        UnaryOperator op = unop(pn->getKind());
++        UnaryNode* unaryNode = &pn->as<UnaryNode>();
++        ParseNode* operand = unaryNode->kid();
++        MOZ_ASSERT(unaryNode->pn_pos.encloses(operand->pn_pos));
++
++        UnaryOperator op = unop(unaryNode->getKind());
+         LOCAL_ASSERT(op > UNOP_ERR && op < UNOP_LIMIT);
+ 
+         RootedValue expr(cx);
+-        return expression(pn->pn_kid, &expr) &&
+-               builder.unaryExpression(op, expr, &pn->pn_pos, dst);
++        return expression(operand, &expr) &&
++               builder.unaryExpression(op, expr, &unaryNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::New:
+       case ParseNodeKind::TaggedTemplate:
+       case ParseNodeKind::Call:
+       case ParseNodeKind::SuperCall:
+       {
+         BinaryNode* node = &pn->as<BinaryNode>();
+@@ -3044,24 +3054,24 @@ ASTSerializer::expression(ParseNode* pn,
+         }
+ 
+         return builder.arrayExpression(elts, &array->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Spread:
+       {
+           RootedValue expr(cx);
+-          return expression(pn->pn_kid, &expr) &&
++          return expression(pn->as<UnaryNode>().kid(), &expr) &&
+                  builder.spreadExpression(expr, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::ComputedName:
+       {
+          RootedValue name(cx);
+-         return expression(pn->pn_kid, &name) &&
++         return expression(pn->as<UnaryNode>().kid(), &name) &&
+                 builder.computedName(name, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Object:
+       {
+         ListNode* obj = &pn->as<ListNode>();
+         NodeVector elts(cx);
+         if (!elts.reserve(obj->count())) {
+@@ -3115,30 +3125,34 @@ ASTSerializer::expression(ParseNode* pn,
+       case ParseNodeKind::True:
+       case ParseNodeKind::False:
+       case ParseNodeKind::Null:
+       case ParseNodeKind::RawUndefined:
+         return literal(pn, dst);
+ 
+       case ParseNodeKind::YieldStar:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
++        UnaryNode* yieldNode = &pn->as<UnaryNode>();
++        ParseNode* operand = yieldNode->kid();
++        MOZ_ASSERT(yieldNode->pn_pos.encloses(operand->pn_pos));
+ 
+         RootedValue arg(cx);
+-        return expression(pn->pn_kid, &arg) &&
+-               builder.yieldExpression(arg, Delegating, &pn->pn_pos, dst);
++        return expression(operand, &arg) &&
++               builder.yieldExpression(arg, Delegating, &yieldNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Yield:
+       {
+-        MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
++        UnaryNode* yieldNode = &pn->as<UnaryNode>();
++        ParseNode* operand = yieldNode->kid();
++        MOZ_ASSERT_IF(operand, yieldNode->pn_pos.encloses(operand->pn_pos));
+ 
+         RootedValue arg(cx);
+-        return optExpression(pn->pn_kid, &arg) &&
+-               builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst);
++        return optExpression(operand, &arg) &&
++               builder.yieldExpression(arg, NotDelegating, &yieldNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Class:
+         return classDefinition(&pn->as<ClassNode>(), true, dst);
+ 
+       case ParseNodeKind::NewTarget:
+       case ParseNodeKind::ImportMeta:
+       {
+@@ -3217,17 +3231,17 @@ ASTSerializer::propertyName(ParseNode* p
+     return literal(pn, dst);
+ }
+ 
+ bool
+ ASTSerializer::property(ParseNode* pn, MutableHandleValue dst)
+ {
+     if (pn->isKind(ParseNodeKind::MutateProto)) {
+         RootedValue val(cx);
+-        return expression(pn->pn_kid, &val) &&
++        return expression(pn->as<UnaryNode>().kid(), &val) &&
+                builder.prototypeMutation(val, &pn->pn_pos, dst);
+     }
+     if (pn->isKind(ParseNodeKind::Spread)) {
+         return expression(pn, dst);
+     }
+ 
+     PropKind kind;
+     switch (pn->getOp()) {
+@@ -3323,17 +3337,17 @@ ASTSerializer::arrayPattern(ListNode* ar
+     }
+ 
+     for (ParseNode* item : array->contents()) {
+         if (item->isKind(ParseNodeKind::Elision)) {
+             elts.infallibleAppend(NullValue());
+         } else if (item->isKind(ParseNodeKind::Spread)) {
+             RootedValue target(cx);
+             RootedValue spread(cx);
+-            if (!pattern(item->pn_kid, &target)) {
++            if (!pattern(item->as<UnaryNode>().kid(), &target)) {
+                 return false;
+             }
+             if(!builder.spreadExpression(target, &item->pn_pos, &spread))
+                 return false;
+             elts.infallibleAppend(spread);
+         } else {
+             RootedValue patt(cx);
+             if (!pattern(item, &patt)) {
+@@ -3355,34 +3369,34 @@ ASTSerializer::objectPattern(ListNode* o
+     if (!elts.reserve(obj->count())) {
+         return false;
+     }
+ 
+     for (ParseNode* propdef : obj->contents()) {
+         if (propdef->isKind(ParseNodeKind::Spread)) {
+             RootedValue target(cx);
+             RootedValue spread(cx);
+-            if (!pattern(propdef->pn_kid, &target)) {
++            if (!pattern(propdef->as<UnaryNode>().kid(), &target)) {
+                 return false;
+             }
+             if(!builder.spreadExpression(target, &propdef->pn_pos, &spread))
+                 return false;
+             elts.infallibleAppend(spread);
+             continue;
+         }
+         LOCAL_ASSERT(propdef->isKind(ParseNodeKind::MutateProto) != propdef->isOp(JSOP_INITPROP));
+ 
+         RootedValue key(cx);
+         ParseNode* target;
+         if (propdef->isKind(ParseNodeKind::MutateProto)) {
+             RootedValue pname(cx, StringValue(cx->names().proto));
+             if (!builder.literal(pname, &propdef->pn_pos, &key)) {
+                 return false;
+             }
+-            target = propdef->pn_kid;
++            target = propdef->as<UnaryNode>().kid();
+         } else {
+             BinaryNode* prop = &propdef->as<BinaryNode>();
+             if (!propertyName(prop->left(), &key)) {
+                 return false;
+             }
+             target = prop->right();
+         }
+ 
+@@ -3490,33 +3504,33 @@ ASTSerializer::functionArgsAndBody(Parse
+     if (pnbody->isKind(ParseNodeKind::LexicalScope)) {
+         pnbody = pnbody->scopeBody();
+     }
+ 
+     /* Serialize the arguments and body. */
+     switch (pnbody->getKind()) {
+       case ParseNodeKind::Return: /* expression closure, no destructured args */
+         return functionArgs(pn, argsList, args, defaults, rest) &&
+-               expression(pnbody->pn_kid, body);
++               expression(pnbody->as<UnaryNode>().kid(), body);
+ 
+       case ParseNodeKind::StatementList:     /* statement closure */
+       {
+         ParseNode* firstNode = pnbody->as<ListNode>().head();
+ 
+         // Skip over initial yield in generator.
+         if (firstNode && firstNode->isKind(ParseNodeKind::InitialYield)) {
+             firstNode = firstNode->pn_next;
+         }
+ 
+         // Async arrow with expression body is converted into STATEMENTLIST
+         // to insert initial yield.
+         if (isAsync && isExpression) {
+             MOZ_ASSERT(firstNode->getKind() == ParseNodeKind::Return);
+             return functionArgs(pn, argsList, args, defaults, rest) &&
+-                   expression(firstNode->pn_kid, body);
++                   expression(firstNode->as<UnaryNode>().kid(), body);
+         }
+ 
+         return functionArgs(pn, argsList, args, defaults, rest) &&
+                functionBody(firstNode, &pnbody->pn_pos, body);
+       }
+ 
+       default:
+         LOCAL_NOT_REACHED("unexpected function contents");
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -1050,17 +1050,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+       case ParseNodeKind::ObjectPropertyName:
+         MOZ_ASSERT(pn->isArity(PN_NULLARY));
+         *answer = false;
+         return true;
+ 
+       // |this| can throw in derived class constructors, including nested arrow
+       // functions or eval.
+       case ParseNodeKind::This:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = sc->needsThisTDZChecks();
+         return true;
+ 
+       // Trivial binary nodes with more token pos holders.
+       case ParseNodeKind::NewTarget:
+       case ParseNodeKind::ImportMeta: {
+         MOZ_ASSERT(pn->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
+         MOZ_ASSERT(pn->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
+@@ -1080,103 +1080,99 @@ BytecodeEmitter::checkSideEffects(ParseN
+         MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Unary cases with side effects only if the child has them.
+       case ParseNodeKind::TypeOfExpr:
+       case ParseNodeKind::Void:
+       case ParseNodeKind::Not:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        return checkSideEffects(pn->pn_kid, answer);
++        return checkSideEffects(pn->as<UnaryNode>().kid(), answer);
+ 
+       // Even if the name expression is effect-free, performing ToPropertyKey on
+       // it might not be effect-free:
+       //
+       //   RegExp.prototype.toString = () => { throw 42; };
+       //   ({ [/regex/]: 0 }); // ToPropertyKey(/regex/) throws 42
+       //
+       //   function Q() {
+       //     ({ [new.target]: 0 });
+       //   }
+       //   Q.toString = () => { throw 17; };
+       //   new Q; // new.target will be Q, ToPropertyKey(Q) throws 17
+       case ParseNodeKind::ComputedName:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Looking up or evaluating the associated name could throw.
+       case ParseNodeKind::TypeOfName:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = true;
+         return true;
+ 
+       // This unary case has side effects on the enclosing object, sure.  But
+       // that's not the question this function answers: it's whether the
+       // operation may have a side effect on something *other* than the result
+       // of the overall operation in which it's embedded.  The answer to that
+       // is no, because an object literal having a mutated prototype only
+       // produces a value, without affecting anything else.
+       case ParseNodeKind::MutateProto:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        return checkSideEffects(pn->pn_kid, answer);
++        return checkSideEffects(pn->as<UnaryNode>().kid(), answer);
+ 
+       // Unary cases with obvious side effects.
+       case ParseNodeKind::PreIncrement:
+       case ParseNodeKind::PostIncrement:
+       case ParseNodeKind::PreDecrement:
+       case ParseNodeKind::PostDecrement:
+       case ParseNodeKind::Throw:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = true;
+         return true;
+ 
+       // These might invoke valueOf/toString, even with a subexpression without
+       // side effects!  Consider |+{ valueOf: null, toString: null }|.
+       case ParseNodeKind::BitNot:
+       case ParseNodeKind::Pos:
+       case ParseNodeKind::Neg:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = true;
+         return true;
+ 
+       // This invokes the (user-controllable) iterator protocol.
+       case ParseNodeKind::Spread:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::InitialYield:
+       case ParseNodeKind::YieldStar:
+       case ParseNodeKind::Yield:
+       case ParseNodeKind::Await:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Deletion generally has side effects, even if isolated cases have none.
+       case ParseNodeKind::DeleteName:
+       case ParseNodeKind::DeleteProp:
+       case ParseNodeKind::DeleteElem:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Deletion of a non-Reference expression has side effects only through
+       // evaluating the expression.
+       case ParseNodeKind::DeleteExpr: {
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        ParseNode* expr = pn->pn_kid;
++        ParseNode* expr = pn->as<UnaryNode>().kid();
+         return checkSideEffects(expr, answer);
+       }
+ 
+       case ParseNodeKind::ExpressionStatement:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        return checkSideEffects(pn->pn_kid, answer);
++        return checkSideEffects(pn->as<UnaryNode>().kid(), answer);
+ 
+       // Binary cases with obvious side effects.
+       case ParseNodeKind::Assign:
+       case ParseNodeKind::AddAssign:
+       case ParseNodeKind::SubAssign:
+       case ParseNodeKind::BitOrAssign:
+       case ParseNodeKind::BitXorAssign:
+       case ParseNodeKind::BitAndAssign:
+@@ -1273,17 +1269,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+       case ParseNodeKind::ExportFrom:
+       case ParseNodeKind::ExportDefault:
+         MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       // Likewise.
+       case ParseNodeKind::Export:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
++        MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::CallImport:
+         MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+@@ -2062,17 +2058,17 @@ BytecodeEmitter::emitPropLHS(PropertyAcc
+             break;
+         }
+         pndot = &pnup->as<PropertyAccess>();
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall)
++BytecodeEmitter::emitSuperPropLHS(UnaryNode* superBase, bool isCall)
+ {
+     if (!emitGetThisForSuperBase(superBase)) {
+         return false;
+     }
+     if (isCall && !emit1(JSOP_DUP)) {
+         return false;
+     }
+     if (!emit1(JSOP_SUPERBASE)) {
+@@ -2101,43 +2097,43 @@ BytecodeEmitter::emitPropOp(PropertyAcce
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitSuperGetProp(PropertyAccess* prop, bool isCall)
+ {
+-    ParseNode* base = &prop->expression();
++    UnaryNode* base = &prop->expression().as<UnaryNode>();
+     if (!emitSuperPropLHS(base, isCall)) {
+         return false;
+     }
+ 
+     if (!emitAtomOp(&prop->key(), JSOP_GETPROP_SUPER)) {
+         return false;
+     }
+ 
+     if (isCall && !emit1(JSOP_SWAP)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitPropIncDec(ParseNode* pn)
+-{
+-    PropertyAccess* prop = &pn->pn_kid->as<PropertyAccess>();
++BytecodeEmitter::emitPropIncDec(UnaryNode* incDec)
++{
++    PropertyAccess* prop = &incDec->kid()->as<PropertyAccess>();
+ 
+     bool post;
+     bool isSuper = prop->isSuper();
+-    JSOp binop = GetIncDecInfo(pn->getKind(), &post);
++    JSOp binop = GetIncDecInfo(incDec->getKind(), &post);
+ 
+     if (isSuper) {
+-        ParseNode* base = &prop->expression();
++        UnaryNode* base = &prop->expression().as<UnaryNode>();
+         if (!emitSuperPropLHS(base)) {              // THIS OBJ
+             return false;
+         }
+         if (!emit1(JSOP_DUP2)) {                    // THIS OBJ THIS OBJ
+             return false;
+         }
+     } else {
+         if (!emitPropLHS(prop)) {
+@@ -2214,27 +2210,27 @@ BytecodeEmitter::emitGetNameAtLocationFo
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitNameIncDec(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->pn_kid->isKind(ParseNodeKind::Name));
++BytecodeEmitter::emitNameIncDec(UnaryNode* incDec)
++{
++    MOZ_ASSERT(incDec->kid()->isKind(ParseNodeKind::Name));
+ 
+     bool post;
+-    JSOp binop = GetIncDecInfo(pn->getKind(), &post);
+-
+-    auto emitRhs = [pn, post, binop](BytecodeEmitter* bce, const NameLocation& loc,
+-                                     bool emittedBindOp)
++    JSOp binop = GetIncDecInfo(incDec->getKind(), &post);
++
++    auto emitRhs = [incDec, post, binop](BytecodeEmitter* bce, const NameLocation& loc,
++                                         bool emittedBindOp)
+     {
+-        JSAtom* name = pn->pn_kid->name();
++        JSAtom* name = incDec->kid()->name();
+         if (!bce->emitGetNameAtLocationForCompoundAssignment(name, loc)) { // ENV? V
+             return false;
+         }
+         if (!bce->emit1(JSOP_POS)) {                       // ENV? N
+             return false;
+         }
+         if (post && !bce->emit1(JSOP_DUP)) {               // ENV? N? N
+             return false;
+@@ -2253,17 +2249,17 @@ BytecodeEmitter::emitNameIncDec(ParseNod
+             if (!bce->emit1(JSOP_SWAP)) {                  // N? ENV? N+1
+                 return false;
+             }
+         }
+ 
+         return true;
+     };
+ 
+-    if (!emitSetName(pn->pn_kid, emitRhs)) {
++    if (!emitSetName(incDec->kid(), emitRhs)) {
+         return false;
+     }
+ 
+     if (post && !emit1(JSOP_POP)) {
+         return false;
+     }
+ 
+     return true;
+@@ -2298,17 +2294,17 @@ BytecodeEmitter::emitElemOperands(Proper
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitSuperElemOperands(PropertyByValue* elem, EmitElemOption opts)
+ {
+     MOZ_ASSERT(elem->isSuper());
+ 
+-    if (!emitGetThisForSuperBase(&elem->expression())) {
++    if (!emitGetThisForSuperBase(&elem->expression().as<UnaryNode>())) {
+         return false;                               // THIS
+     }
+ 
+     if (opts == EmitElemOption::Call) {
+         // We need a second |this| that will be consumed during computation of
+         // the property value. (The original |this| is passed to the call.)
+         if (!emit1(JSOP_DUP)) {                     // THIS THIS
+             return false;
+@@ -2373,36 +2369,36 @@ BytecodeEmitter::emitSuperGetElem(Proper
+     if (isCall && !emit1(JSOP_SWAP)) {              // VALUE THIS
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitElemIncDec(ParseNode* pn)
+-{
+-    PropertyByValue* elem = &pn->pn_kid->as<PropertyByValue>();
++BytecodeEmitter::emitElemIncDec(UnaryNode* incDec)
++{
++    PropertyByValue* elem = &incDec->kid()->as<PropertyByValue>();
+     bool isSuper = elem->isSuper();
+ 
+     // We need to convert the key to an object id first, so that we do not do
+     // it inside both the GETELEM and the SETELEM. This is done by
+     // emit(Super)ElemOperands.
+     if (isSuper) {
+         if (!emitSuperElemOperands(elem, EmitElemOption::IncDec)) {
+             return false;
+         }
+     } else {
+         if (!emitElemOperands(elem, EmitElemOption::IncDec)) {
+             return false;
+         }
+     }
+ 
+     bool post;
+-    JSOp binop = GetIncDecInfo(pn->getKind(), &post);
++    JSOp binop = GetIncDecInfo(incDec->getKind(), &post);
+ 
+     JSOp getOp;
+     if (isSuper) {
+         // There's no such thing as JSOP_DUP3, so we have to be creative.
+         // Note that pushing things again is no fewer JSOps.
+         if (!emitDupAt(2)) {                            // THIS KEY OBJ THIS
+             return false;
+         }
+@@ -2449,26 +2445,25 @@ BytecodeEmitter::emitElemIncDec(ParseNod
+     if (post && !emit1(JSOP_POP)) {                     // RESULT
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitCallIncDec(ParseNode* incDec)
++BytecodeEmitter::emitCallIncDec(UnaryNode* incDec)
+ {
+     MOZ_ASSERT(incDec->isKind(ParseNodeKind::PreIncrement) ||
+                incDec->isKind(ParseNodeKind::PostIncrement) ||
+                incDec->isKind(ParseNodeKind::PreDecrement) ||
+                incDec->isKind(ParseNodeKind::PostDecrement));
+ 
+-    MOZ_ASSERT(incDec->pn_kid->isKind(ParseNodeKind::Call));
+-
+-    ParseNode* call = incDec->pn_kid;
++    ParseNode* call = incDec->kid();
++    MOZ_ASSERT(call->isKind(ParseNodeKind::Call));
+     if (!emitTree(call)) {                              // CALLRESULT
+         return false;
+     }
+     if (!emit1(JSOP_POS)) {                             // N
+         return false;
+     }
+ 
+     // The increment/decrement has no side effects, so proceed to throw for
+@@ -2938,17 +2933,17 @@ BytecodeEmitter::emitFunctionScript(Pars
+ }
+ 
+ bool
+ BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted)
+ {
+     *emitted = 0;
+ 
+     if (target->isKind(ParseNodeKind::Spread)) {
+-        target = target->pn_kid;
++        target = target->as<UnaryNode>().kid();
+     } else if (target->isKind(ParseNodeKind::Assign)) {
+         target = target->as<AssignmentNode>().left();
+     }
+ 
+     // No need to recur into ParseNodeKind::Array and
+     // ParseNodeKind::Object subpatterns here, since
+     // emitSetOrInitializeDestructuring does the recursion when
+     // setting or initializing value.  Getting reference doesn't recur.
+@@ -2962,17 +2957,17 @@ BytecodeEmitter::emitDestructuringLHSRef
+ #ifdef DEBUG
+     int depth = stackDepth;
+ #endif
+ 
+     switch (target->getKind()) {
+       case ParseNodeKind::Dot: {
+         PropertyAccess* prop = &target->as<PropertyAccess>();
+         if (prop->isSuper()) {
+-            if (!emitSuperPropLHS(&prop->expression())) {
++            if (!emitSuperPropLHS(&prop->expression().as<UnaryNode>())) {
+                 return false;
+             }
+             *emitted = 2;
+         } else {
+             if (!emitTree(&prop->expression())) {
+                 return false;
+             }
+             *emitted = 1;
+@@ -3014,17 +3009,17 @@ BytecodeEmitter::emitDestructuringLHSRef
+ bool
+ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav)
+ {
+     // Now emit the lvalue opcode sequence. If the lvalue is a nested
+     // destructuring initialiser-form, call ourselves to handle it, then pop
+     // the matched value. Otherwise emit an lvalue bytecode sequence followed
+     // by an assignment op.
+     if (target->isKind(ParseNodeKind::Spread)) {
+-        target = target->pn_kid;
++        target = target->as<UnaryNode>().kid();
+     } else if (target->isKind(ParseNodeKind::Assign)) {
+         target = target->as<AssignmentNode>().left();
+     }
+     if (target->isKind(ParseNodeKind::Array) || target->isKind(ParseNodeKind::Object)) {
+         if (!emitDestructuringOps(&target->as<ListNode>(), flav)) {
+             return false;
+         }
+         // Per its post-condition, emitDestructuringOps has left the
+@@ -3878,20 +3873,20 @@ BytecodeEmitter::emitDestructuringOpsArr
+     if (!ifDone.emitEnd()) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitComputedPropertyName(ParseNode* computedPropName)
++BytecodeEmitter::emitComputedPropertyName(UnaryNode* computedPropName)
+ {
+     MOZ_ASSERT(computedPropName->isKind(ParseNodeKind::ComputedName));
+-    return emitTree(computedPropName->pn_kid) && emit1(JSOP_TOID);
++    return emitTree(computedPropName->kid()) && emit1(JSOP_TOID);
+ }
+ 
+ bool
+ BytecodeEmitter::emitDestructuringOpsObject(ListNode* pattern, DestructuringFlavor flav)
+ {
+     MOZ_ASSERT(pattern->isKind(ParseNodeKind::Object));
+ 
+     MOZ_ASSERT(this->stackDepth > 0);                             // ... RHS
+@@ -3912,17 +3907,17 @@ BytecodeEmitter::emitDestructuringOpsObj
+         }
+     }
+ 
+     for (ParseNode* member : pattern->contents()) {
+         ParseNode* subpattern;
+         if (member->isKind(ParseNodeKind::MutateProto) ||
+             member->isKind(ParseNodeKind::Spread))
+         {
+-            subpattern = member->pn_kid;
++            subpattern = member->as<UnaryNode>().kid();
+         } else {
+             MOZ_ASSERT(member->isKind(ParseNodeKind::Colon) ||
+                        member->isKind(ParseNodeKind::Shorthand));
+             subpattern = member->as<BinaryNode>().right();
+         }
+ 
+         ParseNode* lhs = subpattern;
+         MOZ_ASSERT_IF(member->isKind(ParseNodeKind::Spread),
+@@ -4000,18 +3995,18 @@ BytecodeEmitter::emitDestructuringOpsObj
+             } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                        key->isKind(ParseNodeKind::String))
+             {
+                 if (!emitAtomOp(key->pn_atom, JSOP_GETPROP)) {    // ... *SET RHS *LREF PROP
+                     return false;
+                 }
+                 needsGetElem = false;
+             } else {
+-                if (!emitComputedPropertyName(key)) {             // ... *SET RHS *LREF RHS KEY
+-                    return false;
++                if (!emitComputedPropertyName(&key->as<UnaryNode>())) {
++                    return false;                                 // ... *SET RHS *LREF RHS KEY
+                 }
+ 
+                 // Add the computed property key to the exclusion set.
+                 if (needsRestPropertyExcludedSet) {
+                     if (!emitDupAt(emitted + 3)) {                // ... SET RHS *LREF RHS KEY SET
+                         return false;
+                     }
+                     if (!emitDupAt(1)) {                          // ... SET RHS *LREF RHS KEY SET KEY
+@@ -4373,17 +4368,17 @@ BytecodeEmitter::emitAssignment(ParseNod
+     // Deal with non-name assignments.
+     uint32_t atomIndex = (uint32_t) -1;
+     uint8_t offset = 1;
+ 
+     switch (lhs->getKind()) {
+       case ParseNodeKind::Dot: {
+         PropertyAccess* prop = &lhs->as<PropertyAccess>();
+         if (prop->isSuper()) {
+-            if (!emitSuperPropLHS(&prop->expression())) {
++            if (!emitSuperPropLHS(&prop->expression().as<UnaryNode>())) {
+                 return false;
+             }
+             offset += 2;
+         } else {
+             if (!emitTree(&prop->expression())) {
+                 return false;
+             }
+             offset += 1;
+@@ -6061,28 +6056,26 @@ BytecodeEmitter::emitGetFunctionThis(Par
+     if (sc->needsThisTDZChecks() && !emit1(JSOP_CHECKTHIS)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitGetThisForSuperBase(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::SuperBase));
+-    return emitGetFunctionThis(pn->pn_kid);
+-}
+-
+-bool
+-BytecodeEmitter::emitThisLiteral(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::This));
+-
+-    if (ParseNode* thisName = pn->pn_kid) {
++BytecodeEmitter::emitGetThisForSuperBase(UnaryNode* superBase)
++{
++    MOZ_ASSERT(superBase->isKind(ParseNodeKind::SuperBase));
++    return emitGetFunctionThis(superBase->kid());
++}
++
++bool
++BytecodeEmitter::emitThisLiteral(ThisLiteral* pn)
++{
++    if (ParseNode* thisName = pn->kid()) {
+         return emitGetFunctionThis(thisName);
+     }
+ 
+     if (sc->thisBinding() == ThisBinding::Module) {
+         return emit1(JSOP_UNDEFINED);
+     }
+ 
+     MOZ_ASSERT(sc->thisBinding() == ThisBinding::Global);
+@@ -6098,32 +6091,32 @@ BytecodeEmitter::emitCheckDerivedClassCo
+     }
+     if (!emit1(JSOP_CHECKRETURN)) {
+         return false;
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitReturn(ParseNode* pn)
+-{
+-    if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
++BytecodeEmitter::emitReturn(UnaryNode* returnNode)
++{
++    if (!updateSourceCoordNotes(returnNode->pn_pos.begin)) {
+         return false;
+     }
+ 
+     bool needsIteratorResult = sc->isFunctionBox() && sc->asFunctionBox()->needsIteratorResult();
+     if (needsIteratorResult) {
+         if (!emitPrepareIteratorResult()) {
+             return false;
+         }
+     }
+ 
+     /* Push a return value */
+-    if (ParseNode* pn2 = pn->pn_kid) {
+-        if (!emitTree(pn2)) {
++    if (ParseNode* expr = returnNode->kid()) {
++        if (!emitTree(expr)) {
+             return false;
+         }
+ 
+         bool isAsyncGenerator = sc->asFunctionBox()->isAsync() &&
+                                 sc->asFunctionBox()->isGenerator();
+         if (isAsyncGenerator) {
+             if (!emitAwaitInInnermostScope()) {
+                 return false;
+@@ -6214,47 +6207,47 @@ BytecodeEmitter::emitReturn(ParseNode* p
+ bool
+ BytecodeEmitter::emitGetDotGeneratorInScope(EmitterScope& currentScope)
+ {
+     NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator, &currentScope);
+     return emitGetNameAtLocation(cx->names().dotGenerator, loc);
+ }
+ 
+ bool
+-BytecodeEmitter::emitInitialYield(ParseNode* pn)
+-{
+-    if (!emitTree(pn->pn_kid)) {
++BytecodeEmitter::emitInitialYield(UnaryNode* yieldNode)
++{
++    if (!emitTree(yieldNode->kid())) {
+         return false;
+     }
+ 
+     if (!emitYieldOp(JSOP_INITIALYIELD)) {
+         return false;
+     }
+ 
+     if (!emit1(JSOP_POP)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitYield(ParseNode* pn)
++BytecodeEmitter::emitYield(UnaryNode* yieldNode)
+ {
+     MOZ_ASSERT(sc->isFunctionBox());
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Yield));
++    MOZ_ASSERT(yieldNode->isKind(ParseNodeKind::Yield));
+ 
+     bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult();
+     if (needsIteratorResult) {
+         if (!emitPrepareIteratorResult()) {
+             return false;
+         }
+     }
+-    if (pn->pn_kid) {
+-        if (!emitTree(pn->pn_kid)) {
++    if (ParseNode* expr = yieldNode->kid()) {
++        if (!emitTree(expr)) {
+             return false;
+         }
+     } else {
+         if (!emit1(JSOP_UNDEFINED)) {
+             return false;
+         }
+     }
+ 
+@@ -6279,22 +6272,22 @@ BytecodeEmitter::emitYield(ParseNode* pn
+     if (!emitYieldOp(JSOP_YIELD)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitAwaitInInnermostScope(ParseNode* pn)
++BytecodeEmitter::emitAwaitInInnermostScope(UnaryNode* awaitNode)
+ {
+     MOZ_ASSERT(sc->isFunctionBox());
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Await));
+-
+-    if (!emitTree(pn->pn_kid)) {
++    MOZ_ASSERT(awaitNode->isKind(ParseNodeKind::Await));
++
++    if (!emitTree(awaitNode->kid())) {
+         return false;
+     }
+     return emitAwaitInInnermostScope();
+ }
+ 
+ bool
+ BytecodeEmitter::emitAwaitInScope(EmitterScope& currentScope)
+ {
+@@ -6692,19 +6685,19 @@ BytecodeEmitter::emitStatementList(ListN
+         if (!emitTree(stmt)) {
+             return false;
+         }
+     }
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitExpressionStatement(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::ExpressionStatement));
++BytecodeEmitter::emitExpressionStatement(UnaryNode* exprStmt)
++{
++    MOZ_ASSERT(exprStmt->isKind(ParseNodeKind::ExpressionStatement));
+ 
+     /*
+      * Top-level or called-from-a-native JS_Execute/EvaluateScript,
+      * debugger, and eval frames may need the value of the ultimate
+      * expression statement as the script's result, despite the fact
+      * that it appears useless to the compiler.
+      *
+      * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
+@@ -6714,17 +6707,17 @@ BytecodeEmitter::emitExpressionStatement
+     bool useful = false;
+     if (sc->isFunctionBox()) {
+         MOZ_ASSERT(!script->noScriptRval());
+     } else {
+         useful = wantval = !script->noScriptRval();
+     }
+ 
+     /* Don't eliminate expressions with side effects. */
+-    ParseNode* expr = pn->pn_kid;
++    ParseNode* expr = exprStmt->kid();
+     if (!useful) {
+         if (!checkSideEffects(expr, &useful)) {
+             return false;
+         }
+ 
+         /*
+          * Don't eliminate apparently useless expressions if they are labeled
+          * expression statements. The startOffset() test catches the case
+@@ -6737,30 +6730,30 @@ BytecodeEmitter::emitExpressionStatement
+             useful = true;
+         }
+     }
+ 
+     if (useful) {
+         JSOp op = wantval ? JSOP_SETRVAL : JSOP_POP;
+         ValueUsage valueUsage = wantval ? ValueUsage::WantValue : ValueUsage::IgnoreValue;
+         MOZ_ASSERT_IF(expr->isKind(ParseNodeKind::Assign), expr->isOp(JSOP_NOP));
+-        if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
++        if (!updateSourceCoordNotes(exprStmt->pn_pos.begin)) {
+             return false;
+         }
+         if (!emitTree(expr, valueUsage)) {
+             return false;
+         }
+         if (!emit1(op)) {
+             return false;
+         }
+-    } else if (pn->isDirectivePrologueMember()) {
++    } else if (exprStmt->isDirectivePrologueMember()) {
+         // Don't complain about directive prologue members; just don't emit
+         // their code.
+     } else {
+-        if (JSAtom* atom = pn->isStringExprStatement()) {
++        if (JSAtom* atom = exprStmt->isStringExprStatement()) {
+             // Warn if encountering a non-directive prologue member string
+             // expression statement, that is inconsistent with the current
+             // directive prologue.  That is, a script *not* starting with
+             // "use strict" should warn for any "use strict" statements seen
+             // later in the script, because such statements are misleading.
+             const char* directive = nullptr;
+             if (atom == cx->names().useStrict) {
+                 if (!sc->strictScript) {
+@@ -6785,41 +6778,39 @@ BytecodeEmitter::emitExpressionStatement
+             }
+         }
+     }
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitDeleteName(ParseNode* node)
+-{
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteName));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+-
+-    ParseNode* nameExpr = node->pn_kid;
++BytecodeEmitter::emitDeleteName(UnaryNode* deleteNode)
++{
++    MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteName));
++
++    ParseNode* nameExpr = deleteNode->kid();
+     MOZ_ASSERT(nameExpr->isKind(ParseNodeKind::Name));
+ 
+     return emitAtomOp(nameExpr, JSOP_DELNAME);
+ }
+ 
+ bool
+-BytecodeEmitter::emitDeleteProperty(ParseNode* node)
+-{
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteProp));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+-
+-    PropertyAccess* propExpr = &node->pn_kid->as<PropertyAccess>();
++BytecodeEmitter::emitDeleteProperty(UnaryNode* deleteNode)
++{
++    MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteProp));
++
++    PropertyAccess* propExpr = &deleteNode->kid()->as<PropertyAccess>();
+ 
+     if (propExpr->isSuper()) {
+         // The expression |delete super.foo;| has to evaluate |super.foo|,
+         // which could throw if |this| hasn't yet been set by a |super(...)|
+         // call or the super-base is not an object, before throwing a
+         // ReferenceError for attempting to delete a super-reference.
+-        if (!emitGetThisForSuperBase(&propExpr->expression())) {
++        if (!emitGetThisForSuperBase(&propExpr->expression().as<UnaryNode>())) {
+             return false;
+         }
+ 
+         if (!emit1(JSOP_SUPERBASE)) {
+             return false;
+         }
+ 
+         // Unconditionally throw when attempting to delete a super-reference.
+@@ -6832,30 +6823,29 @@ BytecodeEmitter::emitDeleteProperty(Pars
+         return emit1(JSOP_POP);
+     }
+ 
+     JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
+     return emitPropOp(propExpr, delOp);
+ }
+ 
+ bool
+-BytecodeEmitter::emitDeleteElement(ParseNode* node)
+-{
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteElem));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+-
+-    PropertyByValue* elemExpr = &node->pn_kid->as<PropertyByValue>();
++BytecodeEmitter::emitDeleteElement(UnaryNode* deleteNode)
++{
++    MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteElem));
++
++    PropertyByValue* elemExpr = &deleteNode->kid()->as<PropertyByValue>();
+ 
+     if (elemExpr->isSuper()) {
+         // The expression |delete super[foo];| has to evaluate |super[foo]|,
+         // which could throw if |this| hasn't yet been set by a |super(...)|
+         // call, or trigger side-effects when evaluating ToPropertyKey(foo),
+         // or also throw when the super-base is not an object, before throwing
+         // a ReferenceError for attempting to delete a super-reference.
+-        if (!emitGetThisForSuperBase(&elemExpr->expression())) {
++        if (!emitGetThisForSuperBase(&elemExpr->expression().as<UnaryNode>())) {
+             return false;
+         }
+ 
+         if (!emitTree(&elemExpr->key())) {
+             return false;
+         }
+         if (!emit1(JSOP_TOID)) {
+             return false;
+@@ -6875,22 +6865,21 @@ BytecodeEmitter::emitDeleteElement(Parse
+         return emitPopN(2);
+     }
+ 
+     JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
+     return emitElemOp(elemExpr, delOp);
+ }
+ 
+ bool
+-BytecodeEmitter::emitDeleteExpression(ParseNode* node)
+-{
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteExpr));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+-
+-    ParseNode* expression = node->pn_kid;
++BytecodeEmitter::emitDeleteExpression(UnaryNode* deleteNode)
++{
++    MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteExpr));
++
++    ParseNode* expression = deleteNode->kid();
+ 
+     // If useless, just emit JSOP_TRUE; otherwise convert |delete <expr>| to
+     // effectively |<expr>, true|.
+     bool useful = false;
+     if (!checkSideEffects(expression, &useful)) {
+         return false;
+     }
+ 
+@@ -7328,33 +7317,34 @@ BytecodeEmitter::emitArguments(ListNode*
+     if (!spread) {
+         for (ParseNode* arg : argsList->contents()) {
+             if (!emitTree(arg)) {
+                 return false;
+             }
+         }
+     } else {
+         ParseNode* args = argsList->head();
+-        bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid);
++        MOZ_ASSERT_IF(argc == 1, args->isKind(ParseNodeKind::Spread));
++        bool emitOptCode = (argc == 1) && isRestParameter(args->as<UnaryNode>().kid());
+         InternalIfEmitter ifNotOptimizable(this);
+ 
+         if (emitOptCode) {
+             // Emit a preparation code to optimize the spread call with a rest
+             // parameter:
+             //
+             //   function f(...args) {
+             //     g(...args);
+             //   }
+             //
+             // If the spread operand is a rest parameter and it's optimizable
+             // array, skip spread operation and pass it directly to spread call
+             // operation.  See the comment in OptimizeSpreadCall in
+             // Interpreter.cpp for the optimizable conditons.
+ 
+-            if (!emitTree(args->pn_kid)) {
++            if (!emitTree(args->as<UnaryNode>().kid())) {
+                 return false;
+             }
+ 
+             if (!emit1(JSOP_OPTIMIZE_SPREADCALL)) {
+                 return false;
+             }
+ 
+             if (!emit1(JSOP_NOT)) {
+@@ -7722,27 +7712,27 @@ BytecodeEmitter::emitSequenceExpr(ListNo
+         }
+     }
+     return true;
+ }
+ 
+ // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
+ // the comment on emitSwitch.
+ MOZ_NEVER_INLINE bool
+-BytecodeEmitter::emitIncOrDec(ParseNode* pn)
+-{
+-    switch (pn->pn_kid->getKind()) {
++BytecodeEmitter::emitIncOrDec(UnaryNode* incDec)
++{
++    switch (incDec->kid()->getKind()) {
+       case ParseNodeKind::Dot:
+-        return emitPropIncDec(pn);
++        return emitPropIncDec(incDec);
+       case ParseNodeKind::Elem:
+-        return emitElemIncDec(pn);
++        return emitElemIncDec(incDec);
+       case ParseNodeKind::Call:
+-        return emitCallIncDec(pn);
++        return emitCallIncDec(incDec);
+       default:
+-        return emitNameIncDec(pn);
++        return emitNameIncDec(incDec);
+     }
+ }
+ 
+ // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
+ // the comment on emitSwitch.
+ MOZ_NEVER_INLINE bool
+ BytecodeEmitter::emitLabeledStatement(const LabeledStatement* pn)
+ {
+@@ -7822,34 +7812,34 @@ BytecodeEmitter::emitPropertyList(ListNo
+         if (!updateSourceCoordNotes(propdef->pn_pos.begin)) {
+             return false;
+         }
+ 
+         // Handle __proto__: v specially because *only* this form, and no other
+         // involving "__proto__", performs [[Prototype]] mutation.
+         if (propdef->isKind(ParseNodeKind::MutateProto)) {
+             MOZ_ASSERT(type == ObjectLiteral);
+-            if (!emitTree(propdef->pn_kid)) {
++            if (!emitTree(propdef->as<UnaryNode>().kid())) {
+                 return false;
+             }
+             objp.set(nullptr);
+             if (!emit1(JSOP_MUTATEPROTO)) {
+                 return false;
+             }
+             continue;
+         }
+ 
+         if (propdef->isKind(ParseNodeKind::Spread)) {
+             MOZ_ASSERT(type == ObjectLiteral);
+ 
+             if (!emit1(JSOP_DUP)) {
+                 return false;
+             }
+ 
+-            if (!emitTree(propdef->pn_kid)) {
++            if (!emitTree(propdef->as<UnaryNode>().kid())) {
+                 return false;
+             }
+ 
+             if (!emitCopyDataProperties(CopyOption::Unfiltered)) {
+                 return false;
+             }
+ 
+             objp.set(nullptr);
+@@ -7880,17 +7870,18 @@ BytecodeEmitter::emitPropertyList(ListNo
+         {
+             // EmitClass took care of constructor already.
+             if (type == ClassBody && key->pn_atom == cx->names().constructor &&
+                 !propdef->as<ClassMethod>().isStatic())
+             {
+                 continue;
+             }
+         } else {
+-            if (!emitComputedPropertyName(key)) {
++            MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
++            if (!emitComputedPropertyName(&key->as<UnaryNode>())) {
+                 return false;
+             }
+             isIndex = true;
+         }
+ 
+         /* Emit code for the property initializer. */
+         ParseNode* propVal = propdef->as<BinaryNode>().right();
+         if (!emitTree(propVal)) {
+@@ -8176,17 +8167,17 @@ BytecodeEmitter::emitArray(ParseNode* ar
+         bool allowSelfHostedIter = false;
+         if (elem->isKind(ParseNodeKind::Elision)) {
+             if (!emit1(JSOP_HOLE)) {
+                 return false;
+             }
+         } else {
+             ParseNode* expr;
+             if (elem->isKind(ParseNodeKind::Spread)) {
+-                expr = elem->pn_kid;
++                expr = elem->as<UnaryNode>().kid();
+ 
+                 if (emitterMode == BytecodeEmitter::SelfHosting &&
+                     expr->isKind(ParseNodeKind::Call) &&
+                     expr->as<BinaryNode>().left()->name() == cx->names().allowContentIter)
+                 {
+                     allowSelfHostedIter = true;
+                 }
+             } else {
+@@ -8238,37 +8229,37 @@ UnaryOpParseNodeKindToJSOp(ParseNodeKind
+       case ParseNodeKind::BitNot: return JSOP_BITNOT;
+       case ParseNodeKind::Pos: return JSOP_POS;
+       case ParseNodeKind::Neg: return JSOP_NEG;
+       default: MOZ_CRASH("unexpected unary op");
+     }
+ }
+ 
+ bool
+-BytecodeEmitter::emitUnary(ParseNode* pn)
+-{
+-    if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
+-        return false;
+-    }
+-    if (!emitTree(pn->pn_kid)) {
+-        return false;
+-    }
+-    return emit1(UnaryOpParseNodeKindToJSOp(pn->getKind()));
+-}
+-
+-bool
+-BytecodeEmitter::emitTypeof(ParseNode* node, JSOp op)
++BytecodeEmitter::emitUnary(UnaryNode* unaryNode)
++{
++    if (!updateSourceCoordNotes(unaryNode->pn_pos.begin)) {
++        return false;
++    }
++    if (!emitTree(unaryNode->kid())) {
++        return false;
++    }
++    return emit1(UnaryOpParseNodeKindToJSOp(unaryNode->getKind()));
++}
++
++bool
++BytecodeEmitter::emitTypeof(UnaryNode* typeofNode, JSOp op)
+ {
+     MOZ_ASSERT(op == JSOP_TYPEOF || op == JSOP_TYPEOFEXPR);
+ 
+-    if (!updateSourceCoordNotes(node->pn_pos.begin)) {
+-        return false;
+-    }
+-
+-    if (!emitTree(node->pn_kid)) {
++    if (!updateSourceCoordNotes(typeofNode->pn_pos.begin)) {
++        return false;
++    }
++
++    if (!emitTree(typeofNode->kid())) {
+         return false;
+     }
+ 
+     return emit1(op);
+ }
+ 
+ bool
+ BytecodeEmitter::emitFunctionFormalParametersAndBody(ListNode* paramsBody)
+@@ -9035,62 +9026,62 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::Var:
+         if (!emitDeclarationList(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Return:
+-        if (!emitReturn(pn)) {
++        if (!emitReturn(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::YieldStar:
+-        if (!emitYieldStar(pn->pn_kid)) {
++        if (!emitYieldStar(pn->as<UnaryNode>().kid())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Generator:
+         if (!emit1(JSOP_GENERATOR)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::InitialYield:
+-        if (!emitInitialYield(pn)) {
++        if (!emitInitialYield(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Yield:
+-        if (!emitYield(pn)) {
++        if (!emitYield(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Await:
+-        if (!emitAwaitInInnermostScope(pn)) {
++        if (!emitAwaitInInnermostScope(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::StatementList:
+         if (!emitStatementList(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::EmptyStatement:
+         break;
+ 
+       case ParseNodeKind::ExpressionStatement:
+-        if (!emitExpressionStatement(pn)) {
++        if (!emitExpressionStatement(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Label:
+         if (!emitLabeledStatement(&pn->as<LabeledStatement>())) {
+             return false;
+         }
+@@ -9169,67 +9160,67 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::Pipeline:
+         if (!emitPipeline(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::TypeOfName:
+-        if (!emitTypeof(pn, JSOP_TYPEOF)) {
++        if (!emitTypeof(&pn->as<UnaryNode>(), JSOP_TYPEOF)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::TypeOfExpr:
+-        if (!emitTypeof(pn, JSOP_TYPEOFEXPR)) {
++        if (!emitTypeof(&pn->as<UnaryNode>(), JSOP_TYPEOFEXPR)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Throw:
+       case ParseNodeKind::Void:
+       case ParseNodeKind::Not:
+       case ParseNodeKind::BitNot:
+       case ParseNodeKind::Pos:
+       case ParseNodeKind::Neg:
+-        if (!emitUnary(pn)) {
++        if (!emitUnary(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::PreIncrement:
+       case ParseNodeKind::PreDecrement:
+       case ParseNodeKind::PostIncrement:
+       case ParseNodeKind::PostDecrement:
+-        if (!emitIncOrDec(pn)) {
++        if (!emitIncOrDec(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::DeleteName:
+-        if (!emitDeleteName(pn)) {
++        if (!emitDeleteName(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::DeleteProp:
+-        if (!emitDeleteProperty(pn)) {
++        if (!emitDeleteProperty(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::DeleteElem:
+-        if (!emitDeleteElement(pn)) {
++        if (!emitDeleteElement(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::DeleteExpr:
+-        if (!emitDeleteExpression(pn)) {
++        if (!emitDeleteExpression(&pn->as<UnaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Dot: {
+         PropertyAccess* prop = &pn->as<PropertyAccess>();
+         if (prop->isSuper()) {
+             if (!emitSuperGetProp(prop)) {
+@@ -9278,24 +9269,27 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Import:
+         MOZ_ASSERT(sc->isModuleContext());
+         break;
+ 
+-      case ParseNodeKind::Export:
++      case ParseNodeKind::Export: {
+         MOZ_ASSERT(sc->isModuleContext());
+-        if (pn->pn_kid->getKind() != ParseNodeKind::ExportSpecList) {
+-            if (!emitTree(pn->pn_kid)) {
++        UnaryNode* node = &pn->as<UnaryNode>();
++        ParseNode* decl = node->kid();
++        if (decl->getKind() != ParseNodeKind::ExportSpecList) {
++            if (!emitTree(decl)) {
+                 return false;
+             }
+         }
+         break;
++      }
+ 
+       case ParseNodeKind::ExportDefault:
+         MOZ_ASSERT(sc->isModuleContext());
+         if (!emitExportDefault(&pn->as<BinaryNode>())) {
+             return false;
+         }
+         break;
+ 
+@@ -9357,17 +9351,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+       case ParseNodeKind::Null:
+       case ParseNodeKind::RawUndefined:
+         if (!emit1(pn->getOp())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::This:
+-        if (!emitThisLiteral(pn)) {
++        if (!emitThisLiteral(&pn->as<ThisLiteral>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Debugger:
+         if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
+             return false;
+         }
+diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
+--- a/js/src/frontend/BytecodeEmitter.h
++++ b/js/src/frontend/BytecodeEmitter.h
+@@ -527,35 +527,35 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     // Emit a bytecode followed by an uint32 immediate operand.
+     MOZ_MUST_USE bool emitUint32Operand(JSOp op, uint32_t operand);
+ 
+     // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
+     MOZ_MUST_USE bool emitN(JSOp op, size_t extra, ptrdiff_t* offset = nullptr);
+ 
+     MOZ_MUST_USE bool emitNumberOp(double dval);
+ 
+-    MOZ_MUST_USE bool emitThisLiteral(ParseNode* pn);
++    MOZ_MUST_USE bool emitThisLiteral(ThisLiteral* pn);
+     MOZ_MUST_USE bool emitGetFunctionThis(ParseNode* pn);
+-    MOZ_MUST_USE bool emitGetThisForSuperBase(ParseNode* pn);
++    MOZ_MUST_USE bool emitGetThisForSuperBase(UnaryNode* superBase);
+     MOZ_MUST_USE bool emitSetThis(BinaryNode* setThisNode);
+     MOZ_MUST_USE bool emitCheckDerivedClassConstructorReturn();
+ 
+     // Handle jump opcodes and jump targets.
+     MOZ_MUST_USE bool emitJumpTarget(JumpTarget* target);
+     MOZ_MUST_USE bool emitJumpNoFallthrough(JSOp op, JumpList* jump);
+     MOZ_MUST_USE bool emitJump(JSOp op, JumpList* jump);
+     MOZ_MUST_USE bool emitBackwardJump(JSOp op, JumpTarget target, JumpList* jump,
+                                        JumpTarget* fallthrough);
+     void patchJumpsToTarget(JumpList jump, JumpTarget target);
+     MOZ_MUST_USE bool emitJumpTargetAndPatch(JumpList jump);
+ 
+     MOZ_MUST_USE bool emitCall(JSOp op, uint16_t argc,
+                                const mozilla::Maybe<uint32_t>& sourceCoordOffset);
+     MOZ_MUST_USE bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr);
+-    MOZ_MUST_USE bool emitCallIncDec(ParseNode* incDec);
++    MOZ_MUST_USE bool emitCallIncDec(UnaryNode* incDec);
+ 
+     mozilla::Maybe<uint32_t> getOffsetForLoop(ParseNode* nextpn);
+ 
+     MOZ_MUST_USE bool emitGoto(NestableControl* target, JumpList* jumplist,
+                                SrcNoteType noteType = SRC_NULL);
+ 
+     MOZ_MUST_USE bool emitIndex32(JSOp op, uint32_t index);
+     MOZ_MUST_USE bool emitIndexOp(JSOp op, uint32_t index);
+@@ -626,17 +626,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     }
+     template <typename RHSEmitter>
+     MOZ_MUST_USE bool emitInitializeName(HandleAtom name, RHSEmitter emitRhs) {
+         return emitSetOrInitializeName(name, emitRhs, true);
+     }
+ 
+     MOZ_MUST_USE bool emitTDZCheckIfNeeded(JSAtom* name, const NameLocation& loc);
+ 
+-    MOZ_MUST_USE bool emitNameIncDec(ParseNode* pn);
++    MOZ_MUST_USE bool emitNameIncDec(UnaryNode* incDec);
+ 
+     MOZ_MUST_USE bool emitDeclarationList(ListNode* declList);
+     MOZ_MUST_USE bool emitSingleDeclaration(ParseNode* declList, ParseNode* decl,
+                                             ParseNode* initializer);
+ 
+     MOZ_MUST_USE bool emitNewInit();
+     MOZ_MUST_USE bool emitSingletonInitialiser(ParseNode* pn);
+ 
+@@ -644,45 +644,45 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_MUST_USE bool emitFinishIteratorResult(bool done);
+     MOZ_MUST_USE bool iteratorResultShape(unsigned* shape);
+ 
+     MOZ_MUST_USE bool emitGetDotGeneratorInInnermostScope() {
+         return emitGetDotGeneratorInScope(*innermostEmitterScope());
+     }
+     MOZ_MUST_USE bool emitGetDotGeneratorInScope(EmitterScope& currentScope);
+ 
+-    MOZ_MUST_USE bool emitInitialYield(ParseNode* pn);
+-    MOZ_MUST_USE bool emitYield(ParseNode* pn);
++    MOZ_MUST_USE bool emitInitialYield(UnaryNode* yieldNode);
++    MOZ_MUST_USE bool emitYield(UnaryNode* yieldNode);
+     MOZ_MUST_USE bool emitYieldOp(JSOp op);
+     MOZ_MUST_USE bool emitYieldStar(ParseNode* iter);
+     MOZ_MUST_USE bool emitAwaitInInnermostScope() {
+         return emitAwaitInScope(*innermostEmitterScope());
+     }
+-    MOZ_MUST_USE bool emitAwaitInInnermostScope(ParseNode* pn);
++    MOZ_MUST_USE bool emitAwaitInInnermostScope(UnaryNode* awaitNode);
+     MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope);
+ 
+     MOZ_MUST_USE bool emitPropLHS(PropertyAccess* prop);
+     MOZ_MUST_USE bool emitPropOp(PropertyAccess* prop, JSOp op);
+-    MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn);
++    MOZ_MUST_USE bool emitPropIncDec(UnaryNode* incDec);
+ 
+     MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
+     MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow,
+                                        bool isGenerator);
+ 
+-    MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName);
++    MOZ_MUST_USE bool emitComputedPropertyName(UnaryNode* computedPropName);
+ 
+     // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
+     // opcode onto the stack in the right order. In the case of SETELEM, the
+     // value to be assigned must already be pushed.
+     enum class EmitElemOption { Get, Call, IncDec, CompoundAssign, Ref };
+     MOZ_MUST_USE bool emitElemOperands(PropertyByValue* elem, EmitElemOption opts);
+ 
+     MOZ_MUST_USE bool emitElemOpBase(JSOp op);
+     MOZ_MUST_USE bool emitElemOp(PropertyByValue* elem, JSOp op);
+-    MOZ_MUST_USE bool emitElemIncDec(ParseNode* pn);
++    MOZ_MUST_USE bool emitElemIncDec(UnaryNode* incDec);
+ 
+     MOZ_MUST_USE bool emitCatch(BinaryNode* catchClause);
+     MOZ_MUST_USE bool emitIf(TernaryNode* ifNode);
+     MOZ_MUST_USE bool emitWith(BinaryNode* withNode);
+ 
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(const LabeledStatement* pn);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn);
+     MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body,
+@@ -775,36 +775,36 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+ 
+     MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
+     MOZ_MUST_USE bool emitInitializerInBranch(ParseNode* initializer, ParseNode* pattern);
+ 
+     MOZ_MUST_USE bool emitCallSiteObject(CallSiteNode* callSiteObj);
+     MOZ_MUST_USE bool emitTemplateString(ListNode* templateString);
+     MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rhs);
+ 
+-    MOZ_MUST_USE bool emitReturn(ParseNode* pn);
+-    MOZ_MUST_USE bool emitExpressionStatement(ParseNode* pn);
++    MOZ_MUST_USE bool emitReturn(UnaryNode* returnNode);
++    MOZ_MUST_USE bool emitExpressionStatement(UnaryNode* exprStmt);
+     MOZ_MUST_USE bool emitStatementList(ListNode* stmtList);
+ 
+-    MOZ_MUST_USE bool emitDeleteName(ParseNode* pn);
+-    MOZ_MUST_USE bool emitDeleteProperty(ParseNode* pn);
+-    MOZ_MUST_USE bool emitDeleteElement(ParseNode* pn);
+-    MOZ_MUST_USE bool emitDeleteExpression(ParseNode* pn);
++    MOZ_MUST_USE bool emitDeleteName(UnaryNode* deleteNode);
++    MOZ_MUST_USE bool emitDeleteProperty(UnaryNode* deleteNode);
++    MOZ_MUST_USE bool emitDeleteElement(UnaryNode* deleteNode);
++    MOZ_MUST_USE bool emitDeleteExpression(UnaryNode* deleteNode);
+ 
+     // |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR.
+-    MOZ_MUST_USE bool emitTypeof(ParseNode* node, JSOp op);
++    MOZ_MUST_USE bool emitTypeof(UnaryNode* typeofNode, JSOp op);
+ 
+-    MOZ_MUST_USE bool emitUnary(ParseNode* pn);
++    MOZ_MUST_USE bool emitUnary(UnaryNode* unaryNode);
+     MOZ_MUST_USE bool emitRightAssociative(ListNode* node);
+     MOZ_MUST_USE bool emitLeftAssociative(ListNode* node);
+     MOZ_MUST_USE bool emitLogical(ListNode* node);
+     MOZ_MUST_USE bool emitSequenceExpr(ListNode* node,
+                                        ValueUsage valueUsage = ValueUsage::WantValue);
+ 
+-    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn);
++    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(UnaryNode* incDec);
+ 
+     MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional,
+                                                 ValueUsage valueUsage = ValueUsage::WantValue);
+ 
+     bool isRestParameter(ParseNode* pn);
+ 
+     MOZ_MUST_USE bool emitArguments(ListNode* argsList, bool callop, bool spread);
+     MOZ_MUST_USE bool emitCallOrNew(BinaryNode* callNode,
+@@ -843,17 +843,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     // and the iterator to be on the stack in that order (iterator on the bottom).
+     // It will pop the iterator and I, then iterate over the iterator by calling
+     // |.next()| and put the results into the I-th element of array with
+     // incrementing I, then push the result I (it will be original I +
+     // iteration count). The stack after iteration will look like |ARRAY INDEX|.
+     MOZ_MUST_USE bool emitSpread(bool allowSelfHosted = false);
+ 
+     MOZ_MUST_USE bool emitClass(ClassNode* classNode);
+-    MOZ_MUST_USE bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false);
++    MOZ_MUST_USE bool emitSuperPropLHS(UnaryNode* superBase, bool isCall = false);
+     MOZ_MUST_USE bool emitSuperGetProp(PropertyAccess* prop, bool isCall = false);
+     MOZ_MUST_USE bool emitSuperElemOperands(PropertyByValue* elem,
+                                             EmitElemOption opts = EmitElemOption::Get);
+     MOZ_MUST_USE bool emitSuperGetElem(PropertyByValue* elem, bool isCall = false);
+ 
+     MOZ_MUST_USE bool emitCallee(ParseNode* callee, ParseNode* call, bool* callop);
+ 
+     MOZ_MUST_USE bool emitPipeline(ListNode* node);
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -109,26 +109,26 @@ ContainsHoistedDeclaration(JSContext* cx
+         MOZ_ASSERT(node->isArity(PN_NULLARY));
+         *result = false;
+         return true;
+ 
+       // Statements containing only an expression have no declarations.
+       case ParseNodeKind::ExpressionStatement:
+       case ParseNodeKind::Throw:
+       case ParseNodeKind::Return:
+-        MOZ_ASSERT(node->isArity(PN_UNARY));
++        MOZ_ASSERT(node->is<UnaryNode>());
+         *result = false;
+         return true;
+ 
+       // These two aren't statements in the spec, but we sometimes insert them
+       // in statement lists anyway.
+       case ParseNodeKind::InitialYield:
+       case ParseNodeKind::YieldStar:
+       case ParseNodeKind::Yield:
+-        MOZ_ASSERT(node->isArity(PN_UNARY));
++        MOZ_ASSERT(node->is<UnaryNode>());
+         *result = false;
+         return true;
+ 
+       // Other statements with no sub-statement components.
+       case ParseNodeKind::Break:
+       case ParseNodeKind::Continue:
+       case ParseNodeKind::Import:
+       case ParseNodeKind::ImportSpecList:
+@@ -496,17 +496,17 @@ Boolish(ParseNode* pn)
+       case ParseNodeKind::Void: {
+         // |void <foo>| evaluates to |undefined| which isn't truthy.  But the
+         // sense of this method requires that the expression be literally
+         // replaceable with true/false: not the case if the nested expression
+         // is effectful, might throw, &c.  Walk past the |void| (and nested
+         // |void| expressions, for good measure) and check that the nested
+         // expression doesn't break this requirement before indicating falsity.
+         do {
+-            pn = pn->pn_kid;
++            pn = pn->as<UnaryNode>().kid();
+         } while (pn->isKind(ParseNodeKind::Void));
+ 
+         return IsEffectless(pn) ? Falsy : Unknown;
+       }
+ 
+       default:
+         return Unknown;
+     }
+@@ -541,26 +541,26 @@ FoldCondition(JSContext* cx, ParseNode**
+         }
+         node->setArity(PN_NULLARY);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldTypeOfExpr(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldTypeOfExpr(JSContext* cx, UnaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::TypeOfExpr));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+ 
+-    ParseNode*& expr = node->pn_kid;
+-    if (!Fold(cx, &expr, parser)) {
++    if (!Fold(cx, node->unsafeKidReference(), parser)) {
+         return false;
+     }
+ 
++    ParseNode* expr = node->kid();
++
+     // Constant-fold the entire |typeof| if given a constant with known type.
+     RootedPropertyName result(cx);
+     if (expr->isKind(ParseNodeKind::String) || expr->isKind(ParseNodeKind::TemplateString)) {
+         result = cx->names().string;
+     } else if (expr->isKind(ParseNodeKind::Number)) {
+         result = cx->names().number;
+     } else if (expr->isKind(ParseNodeKind::Null)) {
+         result = cx->names().object;
+@@ -576,96 +576,94 @@ FoldTypeOfExpr(JSContext* cx, ParseNode*
+         node->setOp(JSOP_NOP);
+         node->pn_atom = result;
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldDeleteExpr(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldDeleteExpr(JSContext* cx, UnaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteExpr));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+ 
+-    ParseNode*& expr = node->pn_kid;
+-    if (!Fold(cx, &expr, parser)) {
++    if (!Fold(cx, node->unsafeKidReference(), parser)) {
+         return false;
+     }
+ 
++    ParseNode* expr = node->kid();
++
+     // Expression deletion evaluates the expression, then evaluates to true.
+     // For effectless expressions, eliminate the expression evaluation.
+     if (IsEffectless(expr)) {
+         node->setKind(ParseNodeKind::True);
+         node->setArity(PN_NULLARY);
+         node->setOp(JSOP_TRUE);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldDeleteElement(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldDeleteElement(JSContext* cx, UnaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteElem));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+-    MOZ_ASSERT(node->pn_kid->isKind(ParseNodeKind::Elem));
++    MOZ_ASSERT(node->kid()->isKind(ParseNodeKind::Elem));
+ 
+-    ParseNode*& expr = node->pn_kid;
+-    if (!Fold(cx, &expr, parser)) {
++    if (!Fold(cx, node->unsafeKidReference(), parser)) {
+         return false;
+     }
+ 
++    ParseNode* expr = node->kid();
++
+     // If we're deleting an element, but constant-folding converted our
+     // element reference into a dotted property access, we must *also*
+     // morph the node's kind.
+     //
+     // In principle this also applies to |super["foo"] -> super.foo|,
+     // but we don't constant-fold |super["foo"]| yet.
+     MOZ_ASSERT(expr->isKind(ParseNodeKind::Elem) || expr->isKind(ParseNodeKind::Dot));
+     if (expr->isKind(ParseNodeKind::Dot)) {
+         node->setKind(ParseNodeKind::DeleteProp);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldDeleteProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldDeleteProperty(JSContext* cx, UnaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteProp));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+-    MOZ_ASSERT(node->pn_kid->isKind(ParseNodeKind::Dot));
++    MOZ_ASSERT(node->kid()->isKind(ParseNodeKind::Dot));
+ 
+-    ParseNode*& expr = node->pn_kid;
+ #ifdef DEBUG
+-    ParseNodeKind oldKind = expr->getKind();
++    ParseNodeKind oldKind = node->kid()->getKind();
+ #endif
+ 
+-    if (!Fold(cx, &expr, parser)) {
++    if (!Fold(cx, node->unsafeKidReference(), parser)) {
+         return false;
+     }
+ 
+-    MOZ_ASSERT(expr->isKind(oldKind),
++    MOZ_ASSERT(node->kid()->isKind(oldKind),
+                "kind should have remained invariant under folding");
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldNot(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldNot(JSContext* cx, UnaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Not));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+ 
+-    ParseNode*& expr = node->pn_kid;
+-    if (!FoldCondition(cx, &expr, parser)) {
++    if (!FoldCondition(cx, node->unsafeKidReference(), parser)) {
+         return false;
+     }
+ 
++    ParseNode* expr = node->kid();
++
+     if (expr->isKind(ParseNodeKind::Number)) {
+         double d = expr->pn_dval;
+ 
+         if (d == 0 || IsNaN(d)) {
+             node->setKind(ParseNodeKind::True);
+             node->setOp(JSOP_TRUE);
+         } else {
+             node->setKind(ParseNodeKind::False);
+@@ -679,29 +677,29 @@ FoldNot(JSContext* cx, ParseNode* node, 
+         node->setArity(PN_NULLARY);
+         node->setOp(newval ? JSOP_TRUE : JSOP_FALSE);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldUnaryArithmetic(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldUnaryArithmetic(JSContext* cx, UnaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::BitNot) ||
+                node->isKind(ParseNodeKind::Pos) ||
+                node->isKind(ParseNodeKind::Neg),
+                "need a different method for this node kind");
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+ 
+-    ParseNode*& expr = node->pn_kid;
+-    if (!Fold(cx, &expr, parser)) {
++    if (!Fold(cx, node->unsafeKidReference(), parser)) {
+         return false;
+     }
+ 
++    ParseNode* expr = node->kid();
++
+     if (expr->isKind(ParseNodeKind::Number) ||
+         expr->isKind(ParseNodeKind::True) ||
+         expr->isKind(ParseNodeKind::False))
+     {
+         double d = expr->isKind(ParseNodeKind::Number)
+                    ? expr->pn_dval
+                    : double(expr->isKind(ParseNodeKind::True));
+ 
+@@ -718,32 +716,33 @@ FoldUnaryArithmetic(JSContext* cx, Parse
+         node->setArity(PN_NULLARY);
+         node->pn_dval = d;
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldIncrementDecrement(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldIncrementDecrement(JSContext* cx, UnaryNode* incDec,
++                       PerHandlerParser<FullParseHandler>& parser)
+ {
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::PreIncrement) ||
+-               node->isKind(ParseNodeKind::PostIncrement) ||
+-               node->isKind(ParseNodeKind::PreDecrement) ||
+-               node->isKind(ParseNodeKind::PostDecrement));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
++    MOZ_ASSERT(incDec->isKind(ParseNodeKind::PreIncrement) ||
++               incDec->isKind(ParseNodeKind::PostIncrement) ||
++               incDec->isKind(ParseNodeKind::PreDecrement) ||
++               incDec->isKind(ParseNodeKind::PostDecrement));
+ 
+-    ParseNode*& target = node->pn_kid;
+-    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls));
++    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(incDec->kid(),
++                                                    PermitAssignmentToFunctionCalls));
+ 
+-    if (!Fold(cx, &target, parser)) {
++    if (!Fold(cx, incDec->unsafeKidReference(), parser)) {
+         return false;
+     }
+ 
+-    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls));
++    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(incDec->kid(),
++                                                    PermitAssignmentToFunctionCalls));
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldAndOr(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
+ {
+     ListNode* node = &(*nodePtr)->as<ListNode>();
+@@ -1164,23 +1163,22 @@ FoldList(JSContext* cx, ListNode* list, 
+     }
+ 
+     list->unsafeReplaceTail(elem);
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldReturn(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldReturn(JSContext* cx, UnaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Return));
+-    MOZ_ASSERT(node->isArity(PN_UNARY));
+ 
+-    if (ParseNode*& expr = node->pn_kid) {
+-        if (!Fold(cx, &expr, parser)) {
++    if (node->kid()) {
++        if (!Fold(cx, node->unsafeKidReference(), parser)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+@@ -1613,81 +1611,83 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+       case ParseNodeKind::Generator:
+       case ParseNodeKind::ExportBatchSpec:
+       case ParseNodeKind::ObjectPropertyName:
+       case ParseNodeKind::PosHolder:
+         MOZ_ASSERT(pn->isArity(PN_NULLARY));
+         return true;
+ 
+       case ParseNodeKind::SuperBase:
+-      case ParseNodeKind::TypeOfName:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        MOZ_ASSERT(pn->pn_kid->isKind(ParseNodeKind::Name));
+-        MOZ_ASSERT(!pn->pn_kid->expr());
+-        return true;
+-
+-      case ParseNodeKind::TypeOfExpr:
+-        return FoldTypeOfExpr(cx, pn, parser);
+-
+-      case ParseNodeKind::DeleteName: {
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        MOZ_ASSERT(pn->pn_kid->isKind(ParseNodeKind::Name));
++      case ParseNodeKind::TypeOfName: {
++#ifdef DEBUG
++        UnaryNode* node = &pn->as<UnaryNode>();
++        MOZ_ASSERT(node->kid()->isKind(ParseNodeKind::Name));
++        MOZ_ASSERT(!node->kid()->expr());
++#endif
+         return true;
+       }
+ 
++      case ParseNodeKind::TypeOfExpr:
++        return FoldTypeOfExpr(cx, &pn->as<UnaryNode>(), parser);
++
++      case ParseNodeKind::DeleteName:
++        MOZ_ASSERT(pn->as<UnaryNode>().kid()->isKind(ParseNodeKind::Name));
++        return true;
++
+       case ParseNodeKind::DeleteExpr:
+-        return FoldDeleteExpr(cx, pn, parser);
++        return FoldDeleteExpr(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::DeleteElem:
+-        return FoldDeleteElement(cx, pn, parser);
++        return FoldDeleteElement(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::DeleteProp:
+-        return FoldDeleteProperty(cx, pn, parser);
++        return FoldDeleteProperty(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::Conditional:
+         MOZ_ASSERT((*pnp)->is<TernaryNode>());
+         return FoldConditional(cx, pnp, parser);
+ 
+       case ParseNodeKind::If:
+         MOZ_ASSERT((*pnp)->is<TernaryNode>());
+         return FoldIf(cx, pnp, parser);
+ 
+       case ParseNodeKind::Not:
+-        return FoldNot(cx, pn, parser);
++        return FoldNot(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::BitNot:
+       case ParseNodeKind::Pos:
+       case ParseNodeKind::Neg:
+-        return FoldUnaryArithmetic(cx, pn, parser);
++        return FoldUnaryArithmetic(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::PreIncrement:
+       case ParseNodeKind::PostIncrement:
+       case ParseNodeKind::PreDecrement:
+       case ParseNodeKind::PostDecrement:
+-        return FoldIncrementDecrement(cx, pn, parser);
++        return FoldIncrementDecrement(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::ExpressionStatement:
+       case ParseNodeKind::Throw:
+       case ParseNodeKind::MutateProto:
+       case ParseNodeKind::ComputedName:
+       case ParseNodeKind::Spread:
+       case ParseNodeKind::Export:
+       case ParseNodeKind::Void:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        return Fold(cx, &pn->pn_kid, parser);
++        return Fold(cx, pn->as<UnaryNode>().unsafeKidReference(), parser);
+ 
+       case ParseNodeKind::ExportDefault:
+         return Fold(cx, pn->as<BinaryNode>().unsafeLeftReference(), parser);
+ 
+-      case ParseNodeKind::This:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        if (ParseNode*& expr = pn->pn_kid) {
+-            return Fold(cx, &expr, parser);
++      case ParseNodeKind::This: {
++        ThisLiteral* node = &pn->as<ThisLiteral>();
++        ParseNode** expr = node->unsafeKidReference();
++        if (*expr) {
++            return Fold(cx, expr, parser);
+         }
+         return true;
++      }
+ 
+       case ParseNodeKind::Pipeline:
+         return true;
+ 
+       case ParseNodeKind::And:
+       case ParseNodeKind::Or:
+         MOZ_ASSERT((*pnp)->is<ListNode>());
+         return FoldAndOr(cx, pnp, parser);
+@@ -1736,39 +1736,38 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+       case ParseNodeKind::Let:
+       case ParseNodeKind::ParamsBody:
+       case ParseNodeKind::CallSiteObj:
+       case ParseNodeKind::ExportSpecList:
+       case ParseNodeKind::ImportSpecList:
+         return FoldList(cx, &pn->as<ListNode>(), parser);
+ 
+       case ParseNodeKind::InitialYield: {
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+ #ifdef DEBUG
+-        AssignmentNode* assignNode = &pn->pn_kid->as<AssignmentNode>();
++        AssignmentNode* assignNode = &pn->as<UnaryNode>().kid()->as<AssignmentNode>();
+         MOZ_ASSERT(assignNode->left()->isKind(ParseNodeKind::Name));
+         MOZ_ASSERT(assignNode->right()->isKind(ParseNodeKind::Generator));
+ #endif
+         return true;
+       }
+ 
+       case ParseNodeKind::YieldStar:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        return Fold(cx, &pn->pn_kid, parser);
++        return Fold(cx, pn->as<UnaryNode>().unsafeKidReference(), parser);
+ 
+       case ParseNodeKind::Yield:
+-      case ParseNodeKind::Await:
+-        MOZ_ASSERT(pn->isArity(PN_UNARY));
+-        if (!pn->pn_kid) {
++      case ParseNodeKind::Await: {
++        UnaryNode* node = &pn->as<UnaryNode>();
++        if (!node->kid()) {
+             return true;
+         }
+-        return Fold(cx, &pn->pn_kid, parser);
++        return Fold(cx, node->unsafeKidReference(), parser);
++      }
+ 
+       case ParseNodeKind::Return:
+-        return FoldReturn(cx, pn, parser);
++        return FoldReturn(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::Try:
+         return FoldTry(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::Catch:
+         return FoldCatch(cx, &pn->as<BinaryNode>(), parser);
+ 
+       case ParseNodeKind::Class:
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -114,17 +114,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     // to determine whether we need to check these assumptions.
+     SourceKind sourceKind() const { return sourceKind_; }
+ 
+     ParseNode* newName(PropertyName* name, const TokenPos& pos, JSContext* cx)
+     {
+         return new_<NameNode>(ParseNodeKind::Name, JSOP_GETNAME, name, pos);
+     }
+ 
+-    ParseNode* newComputedName(ParseNode* expr, uint32_t begin, uint32_t end) {
++    UnaryNodeType newComputedName(Node expr, uint32_t begin, uint32_t end) {
+         TokenPos pos(begin, end);
+         return new_<UnaryNode>(ParseNodeKind::ComputedName, pos, expr);
+     }
+ 
+     ParseNode* newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::ObjectPropertyName, JSOP_NOP, pos, atom);
+     }
+ 
+@@ -174,17 +174,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+         /*
+          * We don't know when the last noSubstTemplate will come in, and we
+          * don't want to deal with this outside this method
+          */
+         setEndPosition(callSiteObj, callSiteObj->rawNodes());
+     }
+ 
+-    ParseNode* newThisLiteral(const TokenPos& pos, ParseNode* thisName) {
++    ThisLiteralType newThisLiteral(const TokenPos& pos, Node thisName) {
+         return new_<ThisLiteral>(pos, thisName);
+     }
+ 
+     ParseNode* newNullLiteral(const TokenPos& pos) {
+         return new_<NullLiteral>(pos);
+     }
+ 
+     ParseNode* newRawUndefinedLiteral(const TokenPos& pos) {
+@@ -202,51 +202,51 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         }
+         return new_<RegExpLiteral>(objbox, pos);
+     }
+ 
+     ConditionalExpressionType newConditional(Node cond, Node thenExpr, Node elseExpr) {
+         return new_<ConditionalExpression>(cond, thenExpr, elseExpr);
+     }
+ 
+-    ParseNode* newDelete(uint32_t begin, ParseNode* expr) {
++    UnaryNodeType newDelete(uint32_t begin, Node expr) {
+         if (expr->isKind(ParseNodeKind::Name)) {
+             expr->setOp(JSOP_DELNAME);
+             return newUnary(ParseNodeKind::DeleteName, begin, expr);
+         }
+ 
+         if (expr->isKind(ParseNodeKind::Dot)) {
+             return newUnary(ParseNodeKind::DeleteProp, begin, expr);
+         }
+ 
+         if (expr->isKind(ParseNodeKind::Elem)) {
+             return newUnary(ParseNodeKind::DeleteElem, begin, expr);
+         }
+ 
+         return newUnary(ParseNodeKind::DeleteExpr, begin, expr);
+     }
+ 
+-    ParseNode* newTypeof(uint32_t begin, ParseNode* kid) {
++    UnaryNodeType newTypeof(uint32_t begin, Node kid) {
+         ParseNodeKind pnk = kid->isKind(ParseNodeKind::Name)
+                             ? ParseNodeKind::TypeOfName
+                             : ParseNodeKind::TypeOfExpr;
+         return newUnary(pnk, begin, kid);
+     }
+ 
+-    ParseNode* newUnary(ParseNodeKind kind, uint32_t begin, ParseNode* kid) {
++    UnaryNodeType newUnary(ParseNodeKind kind, uint32_t begin, Node kid) {
+         TokenPos pos(begin, kid->pn_pos.end);
+         return new_<UnaryNode>(kind, pos, kid);
+     }
+ 
+-    ParseNode* newUpdate(ParseNodeKind kind, uint32_t begin, ParseNode* kid) {
++    UnaryNodeType newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) {
+         TokenPos pos(begin, kid->pn_pos.end);
+         return new_<UnaryNode>(kind, pos, kid);
+     }
+ 
+-    ParseNode* newSpread(uint32_t begin, ParseNode* kid) {
++    UnaryNodeType newSpread(uint32_t begin, Node kid) {
+         TokenPos pos(begin, kid->pn_pos.end);
+         return new_<UnaryNode>(ParseNodeKind::Spread, pos, kid);
+     }
+ 
+   private:
+     BinaryNodeType newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
+         TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
+         return new_<BinaryNode>(kind, op, pos, left, right);
+@@ -328,27 +328,27 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         return new_<ClassNames>(outer, inner, pos);
+     }
+     BinaryNodeType newNewTarget(Node newHolder, Node targetHolder) {
+         return new_<BinaryNode>(ParseNodeKind::NewTarget, JSOP_NOP, newHolder, targetHolder);
+     }
+     ParseNode* newPosHolder(const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::PosHolder, pos);
+     }
+-    ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) {
++    UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) {
+         return new_<UnaryNode>(ParseNodeKind::SuperBase, pos, thisName);
+     }
+     MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+ 
+         // Object literals with mutated [[Prototype]] are non-constant so that
+         // singleton objects will have Object.prototype as their [[Prototype]].
+         literal->setHasNonConstInitializer();
+ 
+-        ParseNode* mutation = newUnary(ParseNodeKind::MutateProto, begin, expr);
++        UnaryNode* mutation = newUnary(ParseNodeKind::MutateProto, begin, expr);
+         if (!mutation) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ mutation);
+         return true;
+     }
+ 
+     BinaryNodeType newPropertyDefinition(Node key, Node val) {
+@@ -431,32 +431,32 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         ClassMethod* classMethod = new_<ClassMethod>(key, fn, AccessorTypeToJSOp(atype), isStatic);
+         if (!classMethod) {
+             return false;
+         }
+         addList(/* list = */ methodList, /* child = */ classMethod);
+         return true;
+     }
+ 
+-    ParseNode* newInitialYieldExpression(uint32_t begin, ParseNode* gen) {
++    UnaryNodeType newInitialYieldExpression(uint32_t begin, Node gen) {
+         TokenPos pos(begin, begin + 1);
+         return new_<UnaryNode>(ParseNodeKind::InitialYield, pos, gen);
+     }
+ 
+-    ParseNode* newYieldExpression(uint32_t begin, ParseNode* value) {
++    UnaryNodeType newYieldExpression(uint32_t begin, Node value) {
+         TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
+         return new_<UnaryNode>(ParseNodeKind::Yield, pos, value);
+     }
+ 
+-    ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value) {
++    UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) {
+         TokenPos pos(begin, value->pn_pos.end);
+         return new_<UnaryNode>(ParseNodeKind::YieldStar, pos, value);
+     }
+ 
+-    ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value) {
++    UnaryNodeType newAwaitExpression(uint32_t begin, Node value) {
+         TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
+         return new_<UnaryNode>(ParseNodeKind::Await, pos, value);
+     }
+ 
+     // Statements
+ 
+     ListNodeType newStatementList(const TokenPos& pos) {
+         return new_<ListNode>(ParseNodeKind::StatementList, pos);
+@@ -508,17 +508,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         MOZ_ASSERT(genName->getOp() == JSOP_GETNAME);
+         genName->setOp(JSOP_SETNAME);
+         ParseNode* genInit = newAssignment(ParseNodeKind::Assign, /* lhs = */ genName,
+                                            /* rhs = */ makeGen);
+         if (!genInit) {
+             return false;
+         }
+ 
+-        ParseNode* initialYield = newInitialYieldExpression(yieldPos.begin, genInit);
++        UnaryNode* initialYield = newInitialYieldExpression(yieldPos.begin, genInit);
+         if (!initialYield) {
+             return false;
+         }
+ 
+         stmtList->prepend(initialYield);
+         return true;
+     }
+ 
+@@ -536,17 +536,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         return new_<BinaryNode>(ParseNodeKind::Import, JSOP_NOP, pos,
+                                 importSpecSet, moduleSpec);
+     }
+ 
+     BinaryNodeType newImportSpec(Node importNameNode, Node bindingName) {
+         return newBinary(ParseNodeKind::ImportSpec, importNameNode, bindingName);
+     }
+ 
+-    ParseNode* newExportDeclaration(ParseNode* kid, const TokenPos& pos) {
++    UnaryNodeType newExportDeclaration(Node kid, const TokenPos& pos) {
+         return new_<UnaryNode>(ParseNodeKind::Export, pos, kid);
+     }
+ 
+     BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
+         BinaryNode* decl = new_<BinaryNode>(ParseNodeKind::ExportFrom, JSOP_NOP, exportSpecSet,
+                                             moduleSpec);
+         if (!decl) {
+             return nullptr;
+@@ -577,17 +577,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     BinaryNodeType newImportMeta(Node importHolder, Node metaHolder) {
+         return new_<BinaryNode>(ParseNodeKind::ImportMeta, JSOP_NOP, importHolder, metaHolder);
+     }
+ 
+     BinaryNodeType newCallImport(Node importHolder, Node singleArg) {
+         return new_<BinaryNode>(ParseNodeKind::CallImport, JSOP_NOP, importHolder, singleArg);
+     }
+ 
+-    ParseNode* newExprStatement(ParseNode* expr, uint32_t end) {
++    UnaryNodeType newExprStatement(Node expr, uint32_t end) {
+         MOZ_ASSERT(expr->pn_pos.end <= end);
+         return new_<UnaryNode>(ParseNodeKind::ExpressionStatement,
+                                TokenPos(expr->pn_pos.begin, end), expr);
+     }
+ 
+     TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch, Node elseBranch) {
+         TernaryNode* node = new_<TernaryNode>(ParseNodeKind::If, cond, thenBranch, elseBranch);
+         if (!node) {
+@@ -636,35 +636,35 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     ParseNode* newContinueStatement(PropertyName* label, const TokenPos& pos) {
+         return new_<ContinueStatement>(label, pos);
+     }
+ 
+     ParseNode* newBreakStatement(PropertyName* label, const TokenPos& pos) {
+         return new_<BreakStatement>(label, pos);
+     }
+ 
+-    ParseNode* newReturnStatement(ParseNode* expr, const TokenPos& pos) {
++    UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) {
+         MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos));
+         return new_<UnaryNode>(ParseNodeKind::Return, pos, expr);
+     }
+ 
+-    ParseNode* newExpressionBody(ParseNode* expr) {
++    UnaryNodeType newExpressionBody(Node expr) {
+         return new_<UnaryNode>(ParseNodeKind::Return, expr->pn_pos, expr);
+     }
+ 
+     BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) {
+         return new_<BinaryNode>(ParseNodeKind::With, JSOP_NOP, TokenPos(begin, body->pn_pos.end),
+                                 expr, body);
+     }
+ 
+     ParseNode* newLabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin) {
+         return new_<LabeledStatement>(label, stmt, begin);
+     }
+ 
+-    ParseNode* newThrowStatement(ParseNode* expr, const TokenPos& pos) {
++    UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) {
+         MOZ_ASSERT(pos.encloses(expr->pn_pos));
+         return new_<UnaryNode>(ParseNodeKind::Throw, pos, expr);
+     }
+ 
+     TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope,
+                                     Node finallyBlock)
+     {
+         TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
+@@ -900,18 +900,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+     MOZ_MUST_USE ParseNode* parenthesize(ParseNode* pn) {
+         pn->setInParens(true);
+         return pn;
+     }
+     MOZ_MUST_USE ParseNode* setLikelyIIFE(ParseNode* pn) {
+         return parenthesize(pn);
+     }
+-    void setInDirectivePrologue(ParseNode* pn) {
+-        pn->pn_prologue = true;
++    void setInDirectivePrologue(UnaryNodeType exprStmt) {
++        exprStmt->setIsDirectivePrologueMember();
+     }
+ 
+     bool isName(ParseNode* node) {
+         return node->isKind(ParseNodeKind::Name);
+     }
+ 
+     bool isArgumentsName(ParseNode* node, JSContext* cx) {
+         return node->isKind(ParseNodeKind::Name) && node->pn_atom == cx->names().arguments;
+@@ -926,19 +926,22 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+                node->pn_pos.begin + strlen("async") == node->pn_pos.end &&
+                node->pn_atom == cx->names().async;
+     }
+ 
+     PropertyName* maybeDottedProperty(ParseNode* pn) {
+         return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name() : nullptr;
+     }
+     JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) {
+-        if (JSAtom* atom = pn->isStringExprStatement()) {
+-            *pos = pn->pn_kid->pn_pos;
+-            return atom;
++        if (pn->is<UnaryNode>()) {
++            UnaryNode* unary = &pn->as<UnaryNode>();
++            if (JSAtom* atom = unary->isStringExprStatement()) {
++                *pos = unary->kid()->pn_pos;
++                return atom;
++            }
+         }
+         return nullptr;
+     }
+ 
+     void adjustGetToSet(ParseNode* node) {
+         node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
+     }
+ 
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -440,19 +440,18 @@ class NameResolver
+           case ParseNodeKind::ExportBatchSpec:
+           case ParseNodeKind::ObjectPropertyName:
+           case ParseNodeKind::PosHolder:
+             MOZ_ASSERT(cur->isArity(PN_NULLARY));
+             break;
+ 
+           case ParseNodeKind::TypeOfName:
+           case ParseNodeKind::SuperBase:
+-            MOZ_ASSERT(cur->isArity(PN_UNARY));
+-            MOZ_ASSERT(cur->pn_kid->isKind(ParseNodeKind::Name));
+-            MOZ_ASSERT(!cur->pn_kid->expr());
++            MOZ_ASSERT(cur->as<UnaryNode>().kid()->isKind(ParseNodeKind::Name));
++            MOZ_ASSERT(!cur->as<UnaryNode>().kid()->expr());
+             break;
+ 
+           case ParseNodeKind::NewTarget:
+           case ParseNodeKind::ImportMeta: {
+             MOZ_ASSERT(cur->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
+             MOZ_ASSERT(cur->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
+             break;
+           }
+@@ -473,26 +472,24 @@ class NameResolver
+           case ParseNodeKind::PreIncrement:
+           case ParseNodeKind::PostIncrement:
+           case ParseNodeKind::PreDecrement:
+           case ParseNodeKind::PostDecrement:
+           case ParseNodeKind::ComputedName:
+           case ParseNodeKind::Spread:
+           case ParseNodeKind::MutateProto:
+           case ParseNodeKind::Export:
+-            MOZ_ASSERT(cur->isArity(PN_UNARY));
+-            if (!resolve(cur->pn_kid, prefix)) {
++            if (!resolve(cur->as<UnaryNode>().kid(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           // Nodes with a single nullable child.
+           case ParseNodeKind::This:
+-            MOZ_ASSERT(cur->isArity(PN_UNARY));
+-            if (ParseNode* expr = cur->pn_kid) {
++            if (ParseNode* expr = cur->as<ThisLiteral>().kid()) {
+                 if (!resolve(expr, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           // Binary nodes with two non-null children.
+           case ParseNodeKind::Assign:
+@@ -558,43 +555,40 @@ class NameResolver
+             if (!resolve(caseClause->statementList(), prefix)) {
+                 return false;
+             }
+             break;
+           }
+ 
+           case ParseNodeKind::InitialYield: {
+ #ifdef DEBUG
+-            AssignmentNode* assignNode = &cur->pn_kid->as<AssignmentNode>();
++            AssignmentNode* assignNode = &cur->as<UnaryNode>().kid()->as<AssignmentNode>();
+             MOZ_ASSERT(assignNode->left()->isKind(ParseNodeKind::Name));
+             MOZ_ASSERT(assignNode->right()->isKind(ParseNodeKind::Generator));
+ #endif
+             break;
+           }
+ 
+           case ParseNodeKind::YieldStar:
+-            MOZ_ASSERT(cur->isArity(PN_UNARY));
+-            if (!resolve(cur->pn_kid, prefix)) {
++            if (!resolve(cur->as<UnaryNode>().kid(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::Yield:
+           case ParseNodeKind::Await:
+-            MOZ_ASSERT(cur->isArity(PN_UNARY));
+-            if (cur->pn_kid) {
+-                if (!resolve(cur->pn_kid, prefix)) {
++            if (ParseNode* expr = cur->as<UnaryNode>().kid()) {
++                if (!resolve(expr, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           case ParseNodeKind::Return:
+-            MOZ_ASSERT(cur->isArity(PN_UNARY));
+-            if (ParseNode* returnValue = cur->pn_kid) {
++            if (ParseNode* returnValue = cur->as<UnaryNode>().kid()) {
+                 if (!resolve(returnValue, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           case ParseNodeKind::Import:
+           case ParseNodeKind::ExportFrom:
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -144,17 +144,17 @@ ParseNode::dump()
+ void
+ ParseNode::dump(GenericPrinter& out, int indent)
+ {
+     switch (pn_arity) {
+       case PN_NULLARY:
+         ((NullaryNode*) this)->dump(out);
+         break;
+       case PN_UNARY:
+-        ((UnaryNode*) this)->dump(out, indent);
++        as<UnaryNode>().dump(out, indent);
+         break;
+       case PN_BINARY:
+         as<BinaryNode>().dump(out, indent);
+         break;
+       case PN_TERNARY:
+         as<TernaryNode>().dump(out, indent);
+         break;
+       case PN_CODE:
+@@ -210,17 +210,17 @@ NullaryNode::dump(GenericPrinter& out)
+ }
+ 
+ void
+ UnaryNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s ", name);
+     indent += strlen(name) + 2;
+-    DumpParseTree(pn_kid, out, indent);
++    DumpParseTree(kid(), out, indent);
+     out.printf(")");
+ }
+ 
+ void
+ BinaryNode::dump(GenericPrinter& out, int indent)
+ {
+     if (isKind(ParseNodeKind::Dot)) {
+         out.put("(.");
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -242,17 +242,18 @@ IsTypeofKind(ParseNodeKind kind)
+  *           * Name node with empty name for destructuring
+  *               pn_expr: Array or Object for BindingPattern without
+  *                        Initializer, Assign for BindingPattern with
+  *                        Initializer
+  *         followed by either:
+  *           * StatementList node for function body statements
+  *           * Return for expression closure
+  *   count: number of formal parameters + 1
+- * Spread   unary       pn_kid: expression being spread
++ * Spread (UnaryNode)
++ *   kid: expression being spread
+  * Class (ClassNode)
+  *   kid1: ClassNames for class name. can be null for anonymous class.
+  *   kid2: expression after `extends`. null if no expression
+  *   kid3: either of
+  *           * ClassMethodList, if anonymous class
+  *           * LexicalScopeNode which contains ClassMethodList as scopeBody,
+  *             if named class
+  * ClassNames (ClassNames)
+@@ -301,17 +302,18 @@ IsTypeofKind(ParseNodeKind kind)
+  * ForOf (TernaryNode)
+  *   kid1: declaration or expression to left of 'of'
+  *   kid2: null
+  *   kid3: expr to right of 'of'
+  * ForHead (TernaryNode)
+  *   kid1:  init expr before first ';' or nullptr
+  *   kid2:  cond expr before second ';' or nullptr
+  *   kid3:  update expr after second ';' or nullptr
+- * Throw    unary       pn_kid: exception
++ * Throw (UnaryNode)
++ *   kid: thrown exception
+  * Try (TernaryNode)
+  *   kid1: try block
+  *   kid2: null or LexicalScope for catch-block with scopeBody pointing to a
+  *         Catch node
+  *   kid3: null or finally block
+  * Catch (BinaryNode)
+  *   left: Name, Array, or Object catch var node
+  *         (Array or Object if destructuring),
+@@ -332,33 +334,35 @@ IsTypeofKind(ParseNodeKind kind)
+  *           pn_used: true
+  *           pn_atom: variable name
+  *           pn_lexdef: def node
+  *         each assignment node has
+  *           left: Name with pn_used true and
+  *                    pn_lexdef (NOT pn_expr) set
+  *           right: initializer
+  *   count: N > 0
+- * Return   unary       pn_kid: return expr or null
+- * ExpressionStatement unary    pn_kid: expr
+- *                              pn_prologue: true if Directive Prologue member
+- *                                  in original source, not introduced via
+- *                                  constant folding or other tree rewriting
++ * Return (UnaryNode)
++ *   kid: returned expression, or null if none
++ * ExpressionStatement (UnaryNode)
++ *   kid: expr
++ *   prologue: true if Directive Prologue member in original source, not
++ *             introduced via constant folding or other tree rewriting
+  * EmptyStatement nullary      (no fields)
+  * Label    name        pn_atom: label, pn_expr: labeled statement
+  * Import (BinaryNode)
+  *   left: ImportSpecList import specifiers
+  *   right: String module specifier
+  * ImportSpecList (ListNode)
+  *   head: list of N ImportSpec nodes
+  *   count: N >= 0 (N = 0 for `import {} from ...`)
+  * ImportSpec (BinaryNode)
+  *   left: import name
+  *   right: local binding name
+- * Export   unary       pn_kid: declaration expression
++ * Export (UnaryNode)
++ *   kid: declaration expression
+  * ExportFrom (BinaryNode)
+  *   left: ExportSpecList export specifiers
+  *   right: String module specifier
+  * ExportSpecList (ListNode)
+  *   head: list of N ExportSpec nodes
+  *   count: N >= 0 (N = 0 for `export {}`)
+  * ExportSpec (BinaryNode)
+  *   left: local binding name
+@@ -390,38 +394,32 @@ IsTypeofKind(ParseNodeKind kind)
+  * Pipeline, Or, And, BitOr, BitXor, BitAnd, StrictEq, Eq, StrictNe, Ne,
+  * Lt, Le, Gt, Ge, InstanceOf, In, Lsh, Rsh, Ursh, Add, Sub, Star, Div, Mod,
+  * Pow (ListNode)
+  *   head: list of N subexpressions
+  *         All of these operators are left-associative except Pow which is
+  *         right-associative, but still forms a list (see comments in
+  *         ParseNode::appendOrCreateList).
+  *   count: N >= 2
+- * Pos,     unary       pn_kid: UNARY expr
+- * Neg
+- * Void,    unary       pn_kid: UNARY expr
+- * Not,
+- * BitNot
+- * TypeOfName, unary    pn_kid: UNARY expr
+- * TypeOfExpr
+- * PreIncrement, unary  pn_kid: MEMBER expr
+- * PostIncrement,
+- * PreDecrement,
+- * PostDecrement
++ * Pos, Neg, Void, Not, BitNot, TypeOfName, TypeOfExpr (UnaryNode)
++ *   kid: unary expr
++ * PreIncrement PostIncrement, PreDecrement, PostDecrement (UnaryNode)
++ *   kid: member expr
+  * New (BinaryNode)
+  *   left: ctor expression on the left of the '('
+  *   right: Arguments
+- * DeleteName unary     pn_kid: Name expr
+- * DeleteProp unary     pn_kid: Dot expr
+- * DeleteElem unary     pn_kid: Elem expr
+- * DeleteExpr unary     pn_kid: MEMBER expr that's evaluated, then the
+- *                          overall delete evaluates to true; can't be a kind
+- *                          for a more-specific PNK_DELETE* unless constant
+- *                          folding (or a similar parse tree manipulation) has
+- *                          occurred
++ * DeleteName, DeleteProp, DeleteElem, DeleteExpr (UnaryNode)
++ *   kid: expression that's evaluated, then the overall delete evaluates to
++ *        true; can't be a kind for a more-specific ParseNodeKind::Delete*
++ *        unless constant folding (or a similar parse tree manipulation) has
++ *        occurred
++ *          * DeleteName: Name expr
++ *          * DeleteProp: Dot expr
++ *          * DeleteElem: Elem expr
++ *          * DeleteExpr: Member expr
+  * PropertyName name    pn_atom: property name being accessed
+  * Dot (PropertyAccess)
+  *   left: MEMBER expr to left of '.'
+  *   right: PropertyName to right of '.'
+  * Elem (PropertyByValue)
+  *   left: MEMBER expr to left of '['
+  *   right: expr between '[' and ']'
+  * Call (BinaryNode)
+@@ -444,18 +442,19 @@ IsTypeofKind(ParseNodeKind kind)
+  *   count: N >= 0
+  * Colon (BinaryNode)
+  *   key-value pair in object initializer or destructuring lhs
+  *   left: property id
+  *   right: value
+  * Shorthand (BinaryNode)
+  *   Same fields as Colon. This is used for object literal properties using
+  *   shorthand ({x}).
+- * ComputedName unary  ES6 ComputedPropertyName.
+- *                          pn_kid: the AssignmentExpression inside the square brackets
++ * ComputedName (UnaryNode)
++ *   ES6 ComputedPropertyName.
++ *   kid: the AssignmentExpression inside the square brackets
+  * Name,    name        pn_atom: name, string, or object atom
+  * String               pn_op: JSOP_GETNAME, JSOP_STRING, or JSOP_OBJECT
+  *                          If JSOP_GETNAME, pn_op may be JSOP_*ARG or JSOP_*VAR
+  *                          telling const-ness and static analysis results
+  * TemplateStringList (ListNode)
+  *   head: list of alternating expr and template strings
+  *           TemplateString [, expression, TemplateString]+
+  *         there's at least one expression.  If the template literal contains
+@@ -474,32 +473,34 @@ IsTypeofKind(ParseNodeKind kind)
+  *            [raw TemplateString]+
+  * RegExp   nullary     pn_objbox: RegExp model object
+  * Number   dval        pn_dval: double value of numeric literal
+  * True,    nullary     pn_op: JSOp bytecode
+  * False,
+  * Null,
+  * RawUndefined
+  *
+- * This,        unary   pn_kid: '.this' Name if function `this`, else nullptr
+- * SuperBase    unary   pn_kid: '.this' Name
++ * This (UnaryNode)
++ *   kid: '.this' Name if function `this`, else nullptr
++ * SuperBase (UnaryNode)
++ *   kid: '.this' Name
+  * SuperCall (BinaryNode)
+  *   left: SuperBase
+  *   right: Arguments
+  * SetThis (BinaryNode)
+  *   left: '.this' Name
+  *   right: SuperCall
+  *
+  * LexicalScope scope   pn_u.scope.bindings: scope bindings
+  *                          pn_u.scope.body: scope body
+  * Generator    nullary
+- * InitialYield unary   pn_kid: generator object
+- * Yield,       unary   pn_kid: expr or null
+- * YieldStar,
+- * Await
++ * InitialYield (UnaryNode)
++ *   kid: generator object
++ * Yield, YieldStar, Await (UnaryNode)
++ *   kid: expr or null
+  * Nop          nullary
+  */
+ enum ParseNodeArity
+ {
+     PN_NULLARY,                         /* 0 kids, only pn_atom/pn_dval/etc. */
+     PN_UNARY,                           /* one kid, plus a couple of scalars */
+     PN_BINARY,                          /* two kids, plus a couple of scalars */
+     PN_TERNARY,                         /* three kids */
+@@ -520,17 +521,19 @@ enum ParseNodeArity
+     macro(PropertyByValue, PropertyByValueType, asPropertyByValue) \
+     macro(SwitchStatement, SwitchStatementType, asSwitchStatement) \
+     \
+     macro(ListNode, ListNodeType, asList) \
+     macro(CallSiteNode, CallSiteNodeType, asCallSite) \
+     \
+     macro(TernaryNode, TernaryNodeType, asTernary) \
+     macro(ClassNode, ClassNodeType, asClass) \
+-    macro(ConditionalExpression, ConditionalExpressionType, asConditionalExpression)
++    macro(ConditionalExpression, ConditionalExpressionType, asConditionalExpression) \
++    macro(UnaryNode, UnaryNodeType, asUnary) \
++    macro(ThisLiteral, ThisLiteralType, asThisLiteral)
+ 
+ class LoopControlStatement;
+ class BreakStatement;
+ class ContinueStatement;
+ 
+ #define DECLARE_CLASS(typeName, longTypeName, asMethodName) \
+ class typeName;
+ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
+@@ -645,19 +648,20 @@ class ParseNode
+             ParseNode*  right;
+             union {
+                 unsigned iflags;        /* JSITER_* flags for ParseNodeKind::For node */
+                 bool isStatic;          /* only for ParseNodeKind::ClassMethod */
+                 bool hasDefault;        /* only for ParseNodeKind::Switch */
+             };
+         } binary;
+         struct {                        /* one kid if unary */
++          private:
++            friend class UnaryNode;
+             ParseNode*  kid;
+-            bool        prologue;       /* directive prologue member (as
+-                                           pn_prologue) */
++            bool        prologue;       /* directive prologue member */
+         } unary;
+         struct {                        /* name, labeled statement, etc. */
+             union {
+                 JSAtom*      atom;      /* lexical name or label atom */
+                 ObjectBox*   objbox;    /* regexp object */
+                 FunctionBox* funbox;    /* function object */
+             };
+             ParseNode*  expr;           /* module or function body, var
+@@ -675,18 +679,16 @@ class ParseNode
+             friend class LoopControlStatement;
+             PropertyName*    label;    /* target of break/continue statement */
+         } loopControl;
+     } pn_u;
+ 
+ #define pn_objbox       pn_u.name.objbox
+ #define pn_funbox       pn_u.name.funbox
+ #define pn_body         pn_u.name.expr
+-#define pn_kid          pn_u.unary.kid
+-#define pn_prologue     pn_u.unary.prologue
+ #define pn_atom         pn_u.name.atom
+ #define pn_objbox       pn_u.name.objbox
+ #define pn_expr         pn_u.name.expr
+ #define pn_dval         pn_u.number.value
+ 
+ 
+   public:
+     /*
+@@ -734,53 +736,26 @@ class ParseNode
+                    isOp(JSOP_DEFFUN) ||        // non-body-level function statement
+                    isOp(JSOP_NOP) ||           // body-level function stmt in global code
+                    isOp(JSOP_GETLOCAL) ||      // body-level function stmt in function code
+                    isOp(JSOP_GETARG) ||        // body-level function redeclaring formal
+                    isOp(JSOP_INITLEXICAL));    // block-level function stmt
+         return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
+     }
+ 
+-    /*
+-     * True if this statement node could be a member of a Directive Prologue: an
+-     * expression statement consisting of a single string literal.
+-     *
+-     * This considers only the node and its children, not its context. After
+-     * parsing, check the node's pn_prologue flag to see if it is indeed part of
+-     * a directive prologue.
+-     *
+-     * Note that a Directive Prologue can contain statements that cannot
+-     * themselves be directives (string literals that include escape sequences
+-     * or escaped newlines, say). This member function returns true for such
+-     * nodes; we use it to determine the extent of the prologue.
+-     */
+-    JSAtom* isStringExprStatement() const {
+-        if (getKind() == ParseNodeKind::ExpressionStatement) {
+-            MOZ_ASSERT(pn_arity == PN_UNARY);
+-            ParseNode* kid = pn_kid;
+-            if (kid->getKind() == ParseNodeKind::String && !kid->pn_parens) {
+-                return kid->pn_atom;
+-            }
+-        }
+-        return nullptr;
+-    }
+-
+     /* True if pn is a parsenode representing a literal constant. */
+     bool isLiteral() const {
+         return isKind(ParseNodeKind::Number) ||
+                isKind(ParseNodeKind::String) ||
+                isKind(ParseNodeKind::True) ||
+                isKind(ParseNodeKind::False) ||
+                isKind(ParseNodeKind::Null) ||
+                isKind(ParseNodeKind::RawUndefined);
+     }
+ 
+-    /* Return true if this node appears in a Directive Prologue. */
+-    bool isDirectivePrologueMember() const { return pn_prologue; }
+-
+     // True iff this is a for-in/of loop variable declaration (var/let/const).
+     inline bool isForLoopDeclaration() const;
+ 
+     void initNumber(double value, DecimalPoint decimalPoint) {
+         MOZ_ASSERT(pn_arity == PN_NULLARY);
+         MOZ_ASSERT(getKind() == ParseNodeKind::Number);
+         pn_u.number.value = value;
+         pn_u.number.decimalPoint = decimalPoint;
+@@ -839,31 +814,73 @@ struct NullaryNode : public ParseNode
+         pn_atom = atom;
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out);
+ #endif
+ };
+ 
+-struct UnaryNode : public ParseNode
++class UnaryNode : public ParseNode
+ {
++  public:
+     UnaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* kid)
+       : ParseNode(kind, JSOP_NOP, PN_UNARY, pos)
+     {
+-        pn_kid = kid;
++        pn_u.unary.kid = kid;
+     }
+ 
+     static bool test(const ParseNode& node) {
+         return node.isArity(PN_UNARY);
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
++
++    ParseNode* kid() const {
++        return pn_u.unary.kid;
++    }
++
++    /* Return true if this node appears in a Directive Prologue. */
++    bool isDirectivePrologueMember() const {
++        return pn_u.unary.prologue;
++    }
++
++    void setIsDirectivePrologueMember() {
++        pn_u.unary.prologue = true;
++    }
++
++    /*
++     * Non-null if this is a statement node which could be a member of a
++     * Directive Prologue: an expression statement consisting of a single
++     * string literal.
++     *
++     * This considers only the node and its children, not its context. After
++     * parsing, check the node's prologue flag to see if it is indeed part of
++     * a directive prologue.
++     *
++     * Note that a Directive Prologue can contain statements that cannot
++     * themselves be directives (string literals that include escape sequences
++     * or escaped newlines, say). This member function returns true for such
++     * nodes; we use it to determine the extent of the prologue.
++     */
++    JSAtom* isStringExprStatement() const {
++        if (isKind(ParseNodeKind::ExpressionStatement)) {
++            if (kid()->isKind(ParseNodeKind::String) && !kid()->isInParens()) {
++                return kid()->pn_atom;
++            }
++        }
++        return nullptr;
++    }
++
++    // Methods used by FoldConstants.cpp.
++    ParseNode** unsafeKidReference() {
++        return &pn_u.unary.kid;
++    }
+ };
+ 
+ class BinaryNode : public ParseNode
+ {
+   public:
+     BinaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* left, ParseNode* right)
+       : ParseNode(kind, op, PN_BINARY, pos)
+     {
+@@ -1541,16 +1558,22 @@ class ConditionalExpression : public Ter
+ };
+ 
+ class ThisLiteral : public UnaryNode
+ {
+   public:
+     ThisLiteral(const TokenPos& pos, ParseNode* thisName)
+       : UnaryNode(ParseNodeKind::This, pos, thisName)
+     { }
++
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::This);
++        MOZ_ASSERT_IF(match, node.is<UnaryNode>());
++        return match;
++    }
+ };
+ 
+ class NullLiteral : public ParseNode
+ {
+   public:
+     explicit NullLiteral(const TokenPos& pos) : ParseNode(ParseNodeKind::Null, JSOP_NULL, PN_NULLARY, pos) { }
+ };
+ 
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -4517,17 +4517,17 @@ GeneralParser<ParseHandler, CharT>::asmJ
+ {
+     return asFinalParser()->asmJS(list);
+ }
+ 
+ /*
+  * Recognize Directive Prologue members and directives. Assuming |pn| is a
+  * candidate for membership in a directive prologue, recognize directives and
+  * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
+- * |pn_prologue| flag.
++ * |prologue| flag.
+  *
+  * Note that the following is a strict mode function:
+  *
+  * function foo() {
+  *   "blah" // inserted semi colon
+  *        "blurgh"
+  *   "use\x20loose"
+  *   "use strict"
+@@ -4556,17 +4556,17 @@ GeneralParser<ParseHandler, CharT>::mayb
+         // useless code. (We mustn't just omit the statement entirely yet, as it
+         // could be producing the value of an eval or JSScript execution.)
+         //
+         // Note that even if the string isn't one we recognize as a directive,
+         // the emitter still shouldn't flag it as useless, as it could become a
+         // directive in the future. We don't want to interfere with people
+         // taking advantage of directive-prologue-enabled features that appear
+         // in other browsers first.
+-        handler.setInDirectivePrologue(possibleDirective);
++        handler.setInDirectivePrologue(handler.asUnary(possibleDirective));
+ 
+         if (directive == context->names().useStrict) {
+             // Functions with non-simple parameter lists (destructuring,
+             // default or rest parameters) must not contain a "use strict"
+             // directive.
+             if (pc->isFunctionBox()) {
+                 FunctionBox* funbox = pc->functionBox();
+                 if (!funbox->hasSimpleParameterList()) {
+@@ -5899,17 +5899,17 @@ Parser<FullParseHandler, CharT>::checkEx
+ 
+     for (ParseNode* node : array->contents()) {
+         if (node->isKind(ParseNodeKind::Elision)) {
+             continue;
+         }
+ 
+         ParseNode* binding;
+         if (node->isKind(ParseNodeKind::Spread)) {
+-            binding = node->pn_kid;
++            binding = node->as<UnaryNode>().kid();
+         } else if (node->isKind(ParseNodeKind::Assign)) {
+             binding = node->as<AssignmentNode>().left();
+         } else {
+             binding = node;
+         }
+ 
+         if (!checkExportedNamesForDeclaration(binding)) {
+             return false;
+@@ -5943,20 +5943,20 @@ Parser<FullParseHandler, CharT>::checkEx
+     for (ParseNode* node : obj->contents()) {
+         MOZ_ASSERT(node->isKind(ParseNodeKind::MutateProto) ||
+                    node->isKind(ParseNodeKind::Colon) ||
+                    node->isKind(ParseNodeKind::Shorthand) ||
+                    node->isKind(ParseNodeKind::Spread));
+ 
+         ParseNode* target;
+         if (node->isKind(ParseNodeKind::Spread)) {
+-            target = node->pn_kid;
++            target = node->as<UnaryNode>().kid();
+         } else {
+             if (node->isKind(ParseNodeKind::MutateProto)) {
+-                target = node->pn_kid;
++                target = node->as<UnaryNode>().kid();
+             } else {
+                 target = node->as<BinaryNode>().right();
+             }
+ 
+             if (target->isKind(ParseNodeKind::Assign)) {
+                 target = target->as<AssignmentNode>().left();
+             }
+         }
+@@ -6352,30 +6352,30 @@ GeneralParser<ParseHandler, CharT>::expo
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+     if (!checkLocalExportNames(kid)) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
++    UnaryNodeType node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Var));
+ 
+@@ -6385,30 +6385,30 @@ GeneralParser<ParseHandler, CharT>::expo
+     }
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+     if (!checkExportedNamesForDeclarationList(kid)) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
++    UnaryNodeType node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin,
+                                                               uint32_t toStringStart,
+                                                               FunctionAsyncKind asyncKind /* = SyncFunction */)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+@@ -6418,30 +6418,30 @@ GeneralParser<ParseHandler, CharT>::expo
+     if (!kid) {
+         return null();
+     }
+ 
+     if (!checkExportedNameForFunction(kid)) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
++    UnaryNodeType node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class));
+ 
+@@ -6449,30 +6449,30 @@ GeneralParser<ParseHandler, CharT>::expo
+     if (!kid) {
+         return null();
+     }
+ 
+     if (!checkExportedNameForClass(kid)) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
++    UnaryNodeType node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+     return node;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
+     MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TokenKind::Const));
+@@ -6481,17 +6481,17 @@ GeneralParser<ParseHandler, CharT>::expo
+     ListNodeType kid = lexicalDeclaration(YieldIsName, kind);
+     if (!kid) {
+         return null();
+     }
+     if (!checkExportedNamesForDeclarationList(kid)) {
+         return null();
+     }
+ 
+-    Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
++    UnaryNodeType node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+     if (!processExport(node)) {
+         return null();
+     }
+ 
+@@ -6709,17 +6709,17 @@ GeneralParser<ParseHandler, CharT>::expo
+ 
+       default:
+         error(JSMSG_DECLARATION_AFTER_EXPORT);
+         return null();
+     }
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling,
+                                                         InvokedPrediction invoked)
+ {
+     anyChars.ungetToken();
+     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
+                        /* possibleError = */ nullptr, invoked);
+     if (!pnexpr) {
+         return null();
+@@ -7451,17 +7451,17 @@ GeneralParser<ParseHandler, CharT>::brea
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Return));
+     uint32_t begin = pos().begin;
+ 
+     MOZ_ASSERT(pc->isFunctionBox());
+     pc->functionBox()->usesReturn = true;
+ 
+@@ -7491,17 +7491,17 @@ GeneralParser<ParseHandler, CharT>::retu
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+     return handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Yield));
+     uint32_t begin = pos().begin;
+ 
+     MOZ_ASSERT(pc->isGenerator());
+     MOZ_ASSERT(pc->isFunctionBox());
+ 
+@@ -7655,17 +7655,17 @@ GeneralParser<ParseHandler, CharT>::labe
+     if (!pn) {
+         return null();
+     }
+ 
+     return handler.newLabeledStatement(label, pn, begin);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Throw));
+     uint32_t begin = pos().begin;
+ 
+     /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
+     TokenKind tt = TokenKind::Eof;
+     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand)) {
+@@ -9245,17 +9245,17 @@ GeneralParser<ParseHandler, CharT>::chec
+     }
+ 
+     MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls),
+                "inconsistent increment/decrement operand validation");
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind,
+                                                 uint32_t begin)
+ {
+     Node kid = unaryExpr(yieldHandling, TripledotProhibited);
+     if (!kid) {
+         return null();
+     }
+     return handler.newUnary(kind, begin, kid);
+@@ -10471,17 +10471,17 @@ GeneralParser<ParseHandler, CharT>::prop
+         return propName;
+     }
+ 
+     error(JSMSG_COLON_AFTER_ID);
+     return null();
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::UnaryNodeType
+ GeneralParser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
+                                                          const Maybe<DeclarationKind>& maybeDecl,
+                                                          ListNodeType literal)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
+ 
+     uint32_t begin = pos().begin;
+ 
+diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
+--- a/js/src/frontend/Parser.h
++++ b/js/src/frontend/Parser.h
+@@ -1034,19 +1034,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+                       Node* forInitialPart,
+                       mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
+                       Node* forInOrOfExpression);
+     Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
+ 
+     SwitchStatementType switchStatement(YieldHandling yieldHandling);
+     Node continueStatement(YieldHandling yieldHandling);
+     Node breakStatement(YieldHandling yieldHandling);
+-    Node returnStatement(YieldHandling yieldHandling);
++    UnaryNodeType returnStatement(YieldHandling yieldHandling);
+     BinaryNodeType withStatement(YieldHandling yieldHandling);
+-    Node throwStatement(YieldHandling yieldHandling);
++    UnaryNodeType throwStatement(YieldHandling yieldHandling);
+     TernaryNodeType tryStatement(YieldHandling yieldHandling);
+     Node catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope);
+     Node debuggerStatement();
+ 
+     Node variableStatement(YieldHandling yieldHandling);
+ 
+     Node labeledStatement(YieldHandling yieldHandling);
+     Node labeledItem(YieldHandling yieldHandling);
+@@ -1058,30 +1058,30 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+ 
+     inline BinaryNodeType importDeclaration();
+     Node importDeclarationOrImportExpr(YieldHandling yieldHandling);
+ 
+     BinaryNodeType exportFrom(uint32_t begin, Node specList);
+     BinaryNodeType exportBatch(uint32_t begin);
+     inline bool checkLocalExportNames(ListNodeType node);
+     Node exportClause(uint32_t begin);
+-    Node exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
+-                                   FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+-    Node exportVariableStatement(uint32_t begin);
+-    Node exportClassDeclaration(uint32_t begin);
+-    Node exportLexicalDeclaration(uint32_t begin, DeclarationKind kind);
++    UnaryNodeType exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
++                                            FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
++    UnaryNodeType exportVariableStatement(uint32_t begin);
++    UnaryNodeType exportClassDeclaration(uint32_t begin);
++    UnaryNodeType exportLexicalDeclaration(uint32_t begin, DeclarationKind kind);
+     BinaryNodeType exportDefaultFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
+                                                     FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+     BinaryNodeType exportDefaultClassDeclaration(uint32_t begin);
+     BinaryNodeType exportDefaultAssignExpr(uint32_t begin);
+     BinaryNodeType exportDefault(uint32_t begin);
+     Node exportDeclaration();
+ 
+-    Node expressionStatement(YieldHandling yieldHandling,
+-                             InvokedPrediction invoked = PredictUninvoked);
++    UnaryNodeType expressionStatement(YieldHandling yieldHandling,
++                                      InvokedPrediction invoked = PredictUninvoked);
+ 
+     // Declaration parsing.  The main entrypoint is Parser::declarationList,
+     // with sub-functionality split out into the remaining methods.
+ 
+     // |blockScope| may be non-null only when |kind| corresponds to a lexical
+     // declaration (that is, PNK_LET or PNK_CONST).
+     //
+     // The for* parameters, for normal declarations, should be null/ignored.
+@@ -1131,17 +1131,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+ 
+     Node expr(InHandling inHandling, YieldHandling yieldHandling,
+               TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
+               InvokedPrediction invoked = PredictUninvoked);
+     Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+                     TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
+                     InvokedPrediction invoked = PredictUninvoked);
+     Node assignExprWithoutYieldOrAwait(YieldHandling yieldHandling);
+-    Node yieldExpression(InHandling inHandling);
++    UnaryNodeType yieldExpression(InHandling inHandling);
+     Node condExpr(InHandling inHandling, YieldHandling yieldHandling,
+                   TripledotHandling tripledotHandling, PossibleError* possibleError,
+                   InvokedPrediction invoked = PredictUninvoked);
+     Node orExpr(InHandling inHandling, YieldHandling yieldHandling,
+                 TripledotHandling tripledotHandling, PossibleError* possibleError,
+                 InvokedPrediction invoked = PredictUninvoked);
+     Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                    PossibleError* possibleError = nullptr,
+@@ -1173,17 +1173,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+                             bool tryAnnexB = false);
+ 
+     // Parse a function body.  Pass StatementListBody if the body is a list of
+     // statements; pass ExpressionBody if the body is a single expression.
+     enum FunctionBodyType { StatementListBody, ExpressionBody };
+     Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
+                       FunctionBodyType type);
+ 
+-    Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin);
++    UnaryNodeType unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin);
+ 
+     Node condition(InHandling inHandling, YieldHandling yieldHandling);
+ 
+     ListNodeType argumentList(YieldHandling yieldHandling, bool* isSpread,
+                               PossibleError* possibleError = nullptr);
+     Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
+                                   TokenKind tt);
+     Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling,
+@@ -1236,19 +1236,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+ 
+     bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
+                                                     DeclarationKind kind, TokenPos pos);
+ 
+     Node propertyName(YieldHandling yieldHandling,
+                       const mozilla::Maybe<DeclarationKind>& maybeDecl,
+                       ListNodeType propList,
+                       PropertyType* propType, MutableHandleAtom propAtom);
+-    Node computedPropertyName(YieldHandling yieldHandling,
+-                              const mozilla::Maybe<DeclarationKind>& maybeDecl,
+-                              ListNodeType literal);
++    UnaryNodeType computedPropertyName(YieldHandling yieldHandling,
++                                       const mozilla::Maybe<DeclarationKind>& maybeDecl,
++                                       ListNodeType literal);
+     ListNodeType arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
+     inline Node newRegExp();
+ 
+     ListNodeType objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
+ 
+     BinaryNodeType bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling);
+     Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling);
+     Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
+diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
+--- a/js/src/frontend/SyntaxParseHandler.h
++++ b/js/src/frontend/SyntaxParseHandler.h
+@@ -191,17 +191,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+             return NodePotentialAsyncKeyword;
+         }
+         if (name == cx->names().eval) {
+             return NodeEvalName;
+         }
+         return NodeName;
+     }
+ 
+-    Node newComputedName(Node expr, uint32_t start, uint32_t end) {
++    UnaryNodeType newComputedName(Node expr, uint32_t start, uint32_t end) {
+         return NodeGeneric;
+     }
+ 
+     Node newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
+         return NodeName;
+     }
+ 
+     Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) { return NodeGeneric; }
+@@ -218,46 +218,46 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+ 
+     CallSiteNodeType newCallSiteObject(uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+     void addToCallSiteObject(CallSiteNodeType callSiteObj, Node rawNode, Node cookedNode) {}
+ 
+-    Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
++    ThisLiteralType newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
+     Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
+     Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
+ 
+     template <class Boxer>
+     Node newRegExp(Node reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
+ 
+     ConditionalExpressionType newConditional(Node cond, Node thenExpr, Node elseExpr) {
+         return NodeGeneric;
+     }
+ 
+     Node newElision() { return NodeGeneric; }
+ 
+-    Node newDelete(uint32_t begin, Node expr) {
++    UnaryNodeType newDelete(uint32_t begin, Node expr) {
+         return NodeUnparenthesizedUnary;
+     }
+ 
+-    Node newTypeof(uint32_t begin, Node kid) {
++    UnaryNodeType newTypeof(uint32_t begin, Node kid) {
+         return NodeUnparenthesizedUnary;
+     }
+ 
+-    Node newUnary(ParseNodeKind kind, uint32_t begin, Node kid) {
++    UnaryNodeType newUnary(ParseNodeKind kind, uint32_t begin, Node kid) {
+         return NodeUnparenthesizedUnary;
+     }
+ 
+-    Node newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) {
++    UnaryNodeType newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) {
+         return NodeGeneric;
+     }
+ 
+-    Node newSpread(uint32_t begin, Node kid) {
++    UnaryNodeType newSpread(uint32_t begin, Node kid) {
+         return NodeGeneric;
+     }
+ 
+     Node appendOrCreateList(ParseNodeKind kind, Node left, Node right, ParseContext* pc) {
+         return NodeGeneric;
+     }
+ 
+     // Expressions
+@@ -277,40 +277,40 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     ListNodeType newClassMethodList(uint32_t begin) { return NodeGeneric; }
+     ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+     ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
+ 
+     BinaryNodeType newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
+     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
+-    Node newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
++    UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
+ 
+     MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) { return true; }
+     BinaryNodeType newPropertyDefinition(Node key, Node val) { return NodeGeneric; }
+     void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {}
+     MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node expr) { return true; }
+     MOZ_MUST_USE bool addShorthand(ListNodeType literal, Node name, Node expr) { return true; }
+     MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { return true; }
+     MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key, Node fn, AccessorType atype) { return true; }
+     MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key, Node fn, AccessorType atype, bool isStatic) { return true; }
+-    Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
+-    Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
+-    Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
++    UnaryNodeType newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
++    UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
++    UnaryNodeType newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
+ 
+     // Statements
+ 
+     ListNodeType newStatementList(const TokenPos& pos) { return NodeGeneric; }
+     void addStatementToList(ListNodeType list, Node stmt) {}
+     void setListEndPosition(ListNodeType list, const TokenPos& pos) {}
+     void addCaseStatementToList(ListNodeType list, CaseClauseType caseClause) {}
+     MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) { return true; }
+     Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
+ 
+-    Node newExportDeclaration(Node kid, const TokenPos& pos) {
++    UnaryNodeType newExportDeclaration(Node kid, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+     BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
+         return NodeGeneric;
+     }
+     BinaryNodeType newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+@@ -324,17 +324,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         return NodeGeneric;
+     }
+     BinaryNodeType newCallImport(Node importHolder, Node singleArg) {
+         return NodeGeneric;
+     }
+ 
+     BinaryNodeType newSetThis(Node thisName, Node value) { return value; }
+ 
+-    Node newExprStatement(Node expr, uint32_t end) {
++    UnaryNodeType newExprStatement(Node expr, uint32_t end) {
+         return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
+     }
+ 
+     TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch, Node elseBranch) {
+         return NodeGeneric;
+     }
+     BinaryNodeType newDoWhileStatement(Node body, Node cond, const TokenPos& pos) {
+         return NodeGeneric;
+@@ -343,25 +343,25 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant,
+                                            Node lexicalForCaseList, bool hasDefault)
+     {
+         return NodeGeneric;
+     }
+     CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+     Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
+     Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
+-    Node newReturnStatement(Node expr, const TokenPos& pos) { return NodeReturn; }
+-    Node newExpressionBody(Node expr) { return NodeReturn; }
++    UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) { return NodeReturn; }
++    UnaryNodeType newExpressionBody(Node expr) { return NodeReturn; }
+     BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+ 
+     Node newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+-    Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
++    UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
+     TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
+         return NodeGeneric;
+     }
+     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
+ 
+     Node newPropertyName(PropertyName* name, const TokenPos& pos) {
+         lastAtom = name;
+         return NodeGeneric;
+@@ -526,17 +526,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+         // In all other cases, the parenthesized form of |node| is equivalent
+         // to the unparenthesized form: return |node| unchanged.
+         return node;
+     }
+     MOZ_MUST_USE Node setLikelyIIFE(Node pn) {
+         return pn; // Remain in syntax-parse mode.
+     }
+-    void setInDirectivePrologue(Node pn) {}
++    void setInDirectivePrologue(UnaryNodeType exprStmt) {}
+ 
+     bool isName(Node node) {
+         return node == NodeName ||
+                node == NodeArgumentsName ||
+                node == NodeEvalName ||
+                node == NodePotentialAsyncKeyword;
+     }
+ 
+diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
+--- a/js/src/wasm/AsmJS.cpp
++++ b/js/src/wasm/AsmJS.cpp
+@@ -392,18 +392,17 @@ static inline ParseNode*
+ NextNode(ParseNode* pn)
+ {
+     return pn->pn_next;
+ }
+ 
+ static inline ParseNode*
+ UnaryKid(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isArity(PN_UNARY));
+-    return pn->pn_kid;
++    return pn->as<UnaryNode>().kid();
+ }
+ 
+ static inline ParseNode*
+ BinaryRight(ParseNode* pn)
+ {
+     return pn->as<BinaryNode>().right();
+ }
+ 

+ 7464 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478481.patch

@@ -0,0 +1,7464 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726382 -32400
+#      Wed Sep 12 13:26:22 2018 +0900
+# Node ID f4e0f1c70bf8a2ff4eadbde0e0cd9aed7fe8c135
+# Parent  fa508359ae289d59cdbbd5061080b692fa5a3d5b
+Bug 1479659 - Part 5: Add accessors to NameNode, CodeNode, RegExpLiteral, and add NumericLiteral. r=jwalden
+
+diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
+--- a/js/src/builtin/ModuleObject.cpp
++++ b/js/src/builtin/ModuleObject.cpp
+@@ -1370,38 +1370,36 @@ ModuleBuilder::processImport(frontend::B
+ {
+     using namespace js::frontend;
+ 
+     MOZ_ASSERT(importNode->isKind(ParseNodeKind::Import));
+ 
+     ListNode* specList = &importNode->left()->as<ListNode>();
+     MOZ_ASSERT(specList->isKind(ParseNodeKind::ImportSpecList));
+ 
+-    ParseNode* moduleSpec = importNode->right();
++    NameNode* moduleSpec = &importNode->right()->as<NameNode>();
+     MOZ_ASSERT(moduleSpec->isKind(ParseNodeKind::String));
+ 
+-    RootedAtom module(cx_, moduleSpec->pn_atom);
++    RootedAtom module(cx_, moduleSpec->atom());
+     if (!maybeAppendRequestedModule(module, moduleSpec)) {
+         return false;
+     }
+ 
+     RootedAtom importName(cx_);
+     RootedAtom localName(cx_);
+     for (ParseNode* item : specList->contents()) {
+         BinaryNode* spec = &item->as<BinaryNode>();
+         MOZ_ASSERT(spec->isKind(ParseNodeKind::ImportSpec));
+ 
+-        ParseNode* importNameNode = spec->left();
+-        MOZ_ASSERT(importNameNode->isArity(PN_NAME));
++        NameNode* importNameNode = &spec->left()->as<NameNode>();
+ 
+-        ParseNode* localNameNode = spec->right();
+-        MOZ_ASSERT(localNameNode->isArity(PN_NAME));
++        NameNode* localNameNode = &spec->right()->as<NameNode>();
+ 
+-        importName = importNameNode->pn_atom;
+-        localName = localNameNode->pn_atom;
++        importName = importNameNode->atom();
++        localName = localNameNode->atom();
+ 
+         uint32_t line;
+         uint32_t column;
+         tokenStream_.lineAndColumnAt(importNameNode->pn_pos.begin, &line, &column);
+ 
+         RootedImportEntryObject importEntry(cx_);
+         importEntry = ImportEntryObject::create(cx_, module, importName, localName, line, column);
+         if (!importEntry || !appendImportEntryObject(importEntry)) {
+@@ -1443,51 +1441,53 @@ ModuleBuilder::processExport(frontend::P
+       case ParseNodeKind::ExportSpecList: {
+         MOZ_ASSERT(!isDefault);
+         RootedAtom localName(cx_);
+         RootedAtom exportName(cx_);
+         for (ParseNode* item : kid->as<ListNode>().contents()) {
+             BinaryNode* spec = &item->as<BinaryNode>();
+             MOZ_ASSERT(spec->isKind(ParseNodeKind::ExportSpec));
+ 
+-            ParseNode* localNameNode = spec->left();
+-            ParseNode* exportNameNode = spec->right();
+-            localName = localNameNode->pn_atom;
+-            exportName =  exportNameNode->pn_atom;
++            NameNode* localNameNode = &spec->left()->as<NameNode>();
++            NameNode* exportNameNode = &spec->right()->as<NameNode>();
++            localName = localNameNode->atom();
++            exportName = exportNameNode->atom();
+             if (!appendExportEntry(exportName, localName, spec)) {
+                 return false;
+             }
+         }
+         break;
+       }
+ 
+       case ParseNodeKind::Class: {
+         const ClassNode& cls = kid->as<ClassNode>();
+         MOZ_ASSERT(cls.names());
+-        RootedAtom localName(cx_, cls.names()->innerBinding()->pn_atom);
++        RootedAtom localName(cx_, cls.names()->innerBinding()->atom());
+         RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
+         if (!appendExportEntry(exportName, localName)) {
+             return false;
+         }
+         break;
+       }
+ 
+       case ParseNodeKind::Var:
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let: {
++        RootedAtom localName(cx_);
++        RootedAtom exportName(cx_);
+         for (ParseNode* binding : kid->as<ListNode>().contents()) {
+             if (binding->isKind(ParseNodeKind::Assign)) {
+                 binding = binding->as<AssignmentNode>().left();
+             } else {
+                 MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
+             }
+ 
+             if (binding->isKind(ParseNodeKind::Name)) {
+-                RootedAtom localName(cx_, binding->pn_atom);
+-                RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
++                localName = binding->as<NameNode>().atom();
++                exportName = isDefault ? cx_->names().default_ : localName.get();
+                 if (!appendExportEntry(exportName, localName)) {
+                     return false;
+                 }
+             } else if (binding->isKind(ParseNodeKind::Array)) {
+                 if (!processExportArrayBinding(&binding->as<ListNode>())) {
+                     return false;
+                 }
+             } else {
+@@ -1496,17 +1496,17 @@ ModuleBuilder::processExport(frontend::P
+                     return false;
+                 }
+             }
+         }
+         break;
+       }
+ 
+       case ParseNodeKind::Function: {
+-        RootedFunction func(cx_, kid->pn_funbox->function());
++        RootedFunction func(cx_, kid->as<CodeNode>().funbox()->function());
+         MOZ_ASSERT(!func->isArrow());
+         RootedAtom localName(cx_, func->explicitName());
+         RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get());
+         MOZ_ASSERT_IF(isDefault, localName);
+         if (!appendExportEntry(exportName, localName)) {
+             return false;
+         }
+         break;
+@@ -1520,17 +1520,17 @@ ModuleBuilder::processExport(frontend::P
+ }
+ 
+ bool
+ ModuleBuilder::processExportBinding(frontend::ParseNode* binding)
+ {
+     using namespace js::frontend;
+ 
+     if (binding->isKind(ParseNodeKind::Name)) {
+-        RootedAtom name(cx_, binding->pn_atom);
++        RootedAtom name(cx_, binding->as<NameNode>().atom());
+         return appendExportEntry(name, name);
+     }
+ 
+     if (binding->isKind(ParseNodeKind::Array)) {
+         return processExportArrayBinding(&binding->as<ListNode>());
+     }
+ 
+     MOZ_ASSERT(binding->isKind(ParseNodeKind::Object));
+@@ -1604,32 +1604,32 @@ ModuleBuilder::processExportFrom(fronten
+ {
+     using namespace js::frontend;
+ 
+     MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportFrom));
+ 
+     ListNode* specList = &exportNode->left()->as<ListNode>();
+     MOZ_ASSERT(specList->isKind(ParseNodeKind::ExportSpecList));
+ 
+-    ParseNode* moduleSpec = exportNode->right();
++    NameNode* moduleSpec = &exportNode->right()->as<NameNode>();
+     MOZ_ASSERT(moduleSpec->isKind(ParseNodeKind::String));
+ 
+-    RootedAtom module(cx_, moduleSpec->pn_atom);
++    RootedAtom module(cx_, moduleSpec->atom());
+     if (!maybeAppendRequestedModule(module, moduleSpec)) {
+         return false;
+     }
+ 
+     RootedAtom bindingName(cx_);
+     RootedAtom exportName(cx_);
+     for (ParseNode* spec : specList->contents()) {
+         if (spec->isKind(ParseNodeKind::ExportSpec)) {
+-            ParseNode* localNameNode = spec->as<BinaryNode>().left();
+-            ParseNode* exportNameNode = spec->as<BinaryNode>().right();
+-            bindingName = localNameNode->pn_atom;
+-            exportName = exportNameNode->pn_atom;
++            NameNode* localNameNode = &spec->as<BinaryNode>().left()->as<NameNode>();
++            NameNode* exportNameNode = &spec->as<BinaryNode>().right()->as<NameNode>();
++            bindingName = localNameNode->atom();
++            exportName = exportNameNode->atom();
+             if (!appendExportFromEntry(exportName, module, bindingName, localNameNode)) {
+                 return false;
+             }
+         } else {
+             MOZ_ASSERT(spec->isKind(ParseNodeKind::ExportBatchSpec));
+             exportName = cx_->names().star;
+             if (!appendExportFromEntry(nullptr, module, exportName, spec)) {
+                 return false;
+diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
+--- a/js/src/builtin/ReflectParse.cpp
++++ b/js/src/builtin/ReflectParse.cpp
+@@ -1778,46 +1778,46 @@ class ASTSerializer
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return expression(pn, dst);
+     }
+ 
+     bool expression(ParseNode* pn, MutableHandleValue dst);
+ 
+-    bool propertyName(ParseNode* pn, MutableHandleValue dst);
++    bool propertyName(ParseNode* key, MutableHandleValue dst);
+     bool property(ParseNode* pn, MutableHandleValue dst);
+ 
+     bool classMethod(ClassMethod* classMethod, MutableHandleValue dst);
+ 
+     bool optIdentifier(HandleAtom atom, TokenPos* pos, MutableHandleValue dst) {
+         if (!atom) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return identifier(atom, pos, dst);
+     }
+ 
+     bool identifier(HandleAtom atom, TokenPos* pos, MutableHandleValue dst);
+-    bool identifier(ParseNode* pn, MutableHandleValue dst);
++    bool identifier(NameNode* id, MutableHandleValue dst);
+     bool literal(ParseNode* pn, MutableHandleValue dst);
+ 
+     bool optPattern(ParseNode* pn, MutableHandleValue dst) {
+         if (!pn) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return pattern(pn, dst);
+     }
+ 
+     bool pattern(ParseNode* pn, MutableHandleValue dst);
+     bool arrayPattern(ListNode* array, MutableHandleValue dst);
+     bool objectPattern(ListNode* obj, MutableHandleValue dst);
+ 
+-    bool function(ParseNode* pn, ASTType type, MutableHandleValue dst);
++    bool function(CodeNode* funNode, ASTType type, MutableHandleValue dst);
+     bool functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
+                              bool isAsync, bool isExpression,
+                              MutableHandleValue body, MutableHandleValue rest);
+     bool functionBody(ParseNode* pn, TokenPos* pos, MutableHandleValue dst);
+ 
+   public:
+     ASTSerializer(JSContext* c, bool l, char const* src, uint32_t ln)
+         : cx(c)
+@@ -2035,17 +2035,17 @@ ASTSerializer::declaration(ParseNode* pn
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Function) ||
+                pn->isKind(ParseNodeKind::Var) ||
+                pn->isKind(ParseNodeKind::Let) ||
+                pn->isKind(ParseNodeKind::Const));
+ 
+     switch (pn->getKind()) {
+       case ParseNodeKind::Function:
+-        return function(pn, AST_FUNC_DECL, dst);
++        return function(&pn->as<CodeNode>(), AST_FUNC_DECL, dst);
+ 
+       case ParseNodeKind::Var:
+         return variableDeclaration(&pn->as<ListNode>(), false, dst);
+ 
+       default:
+         MOZ_ASSERT(pn->isKind(ParseNodeKind::Let) || pn->isKind(ParseNodeKind::Const));
+         return variableDeclaration(&pn->as<ListNode>(), true, dst);
+     }
+@@ -2084,17 +2084,17 @@ ASTSerializer::variableDeclaration(ListN
+ bool
+ ASTSerializer::variableDeclarator(ParseNode* pn, MutableHandleValue dst)
+ {
+     ParseNode* patternNode;
+     ParseNode* initNode;
+ 
+     if (pn->isKind(ParseNodeKind::Name)) {
+         patternNode = pn;
+-        initNode = pn->pn_expr;
++        initNode = pn->as<NameNode>().expression();
+         MOZ_ASSERT_IF(initNode, pn->pn_pos.encloses(initNode->pn_pos));
+     } else if (pn->isKind(ParseNodeKind::Assign)) {
+         AssignmentNode* assignNode = &pn->as<AssignmentNode>();
+         patternNode = assignNode->left();
+         initNode = assignNode->right();
+         MOZ_ASSERT(pn->pn_pos.encloses(patternNode->pn_pos));
+         MOZ_ASSERT(pn->pn_pos.encloses(initNode->pn_pos));
+     } else {
+@@ -2138,21 +2138,23 @@ ASTSerializer::importDeclaration(BinaryN
+     return literal(moduleSpecNode, &moduleSpec) &&
+            builder.importDeclaration(elts, moduleSpec, &importNode->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::importSpecifier(BinaryNode* importSpec, MutableHandleValue dst)
+ {
+     MOZ_ASSERT(importSpec->isKind(ParseNodeKind::ImportSpec));
++    NameNode* importNameNode = &importSpec->left()->as<NameNode>();
++    NameNode* bindingNameNode = &importSpec->right()->as<NameNode>();
+ 
+     RootedValue importName(cx);
+     RootedValue bindingName(cx);
+-    return identifier(importSpec->left(), &importName) &&
+-           identifier(importSpec->right(), &bindingName) &&
++    return identifier(importNameNode, &importName) &&
++           identifier(bindingNameNode, &bindingName) &&
+            builder.importSpecifier(importName, bindingName, &importSpec->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::exportDeclaration(ParseNode* exportNode, MutableHandleValue dst)
+ {
+     MOZ_ASSERT(exportNode->isKind(ParseNodeKind::Export) ||
+                exportNode->isKind(ParseNodeKind::ExportFrom) ||
+@@ -2186,17 +2188,17 @@ ASTSerializer::exportDeclaration(ParseNo
+                 }
+             }
+             elts.infallibleAppend(elt);
+         }
+         break;
+       }
+ 
+       case ParseNodeKind::Function:
+-        if (!function(kid, AST_FUNC_DECL, &decl)) {
++        if (!function(&kid->as<CodeNode>(), AST_FUNC_DECL, &decl)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Class:
+         if (!classDefinition(&kid->as<ClassNode>(), false, &decl)) {
+             return false;
+         }
+@@ -2231,21 +2233,23 @@ ASTSerializer::exportDeclaration(ParseNo
+ 
+     return builder.exportDeclaration(decl, elts, moduleSpec, isDefault, &exportNode->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::exportSpecifier(BinaryNode* exportSpec, MutableHandleValue dst)
+ {
+     MOZ_ASSERT(exportSpec->isKind(ParseNodeKind::ExportSpec));
++    NameNode* bindingNameNode = &exportSpec->left()->as<NameNode>();
++    NameNode* exportNameNode = &exportSpec->right()->as<NameNode>();
+ 
+     RootedValue bindingName(cx);
+     RootedValue exportName(cx);
+-    return identifier(exportSpec->left(), &bindingName) &&
+-           identifier(exportSpec->right(), &exportName) &&
++    return identifier(bindingNameNode, &bindingName) &&
++           identifier(exportNameNode, &exportName) &&
+            builder.exportSpecifier(bindingName, exportName, &exportSpec->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::switchCase(CaseClause* caseClause, MutableHandleValue dst)
+ {
+     MOZ_ASSERT_IF(caseClause->caseExpression(),
+                   caseClause->pn_pos.encloses(caseClause->caseExpression()->pn_pos));
+@@ -2421,17 +2425,17 @@ ASTSerializer::statement(ParseNode* pn, 
+       case ParseNodeKind::ExpressionStatement:
+       {
+         RootedValue expr(cx);
+         return expression(pn->as<UnaryNode>().kid(), &expr) &&
+             builder.expressionStatement(expr, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::LexicalScope:
+-        pn = pn->pn_expr;
++        pn = pn->scopeBody();
+         if (!pn->isKind(ParseNodeKind::StatementList)) {
+             return statement(pn, dst);
+         }
+         MOZ_FALLTHROUGH;
+ 
+       case ParseNodeKind::StatementList:
+         return blockStatement(&pn->as<ListNode>(), dst);
+ 
+@@ -2557,33 +2561,36 @@ ASTSerializer::statement(ParseNode* pn, 
+                optExpression(maybeTest, &test) &&
+                optExpression(updateOrIter, &update) &&
+                builder.forStatement(init, test, update, stmt, &forNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Break:
+       case ParseNodeKind::Continue:
+       {
++        LoopControlStatement* node = &pn->as<LoopControlStatement>();
+         RootedValue label(cx);
+-        RootedAtom pnAtom(cx, pn->pn_atom);
++        RootedAtom pnAtom(cx, node->label());
+         return optIdentifier(pnAtom, nullptr, &label) &&
+-               (pn->isKind(ParseNodeKind::Break)
+-                ? builder.breakStatement(label, &pn->pn_pos, dst)
+-                : builder.continueStatement(label, &pn->pn_pos, dst));
++               (node->isKind(ParseNodeKind::Break)
++                ? builder.breakStatement(label, &node->pn_pos, dst)
++                : builder.continueStatement(label, &node->pn_pos, dst));
+       }
+ 
+       case ParseNodeKind::Label:
+       {
+-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
++        LabeledStatement* labelNode = &pn->as<LabeledStatement>();
++        ParseNode* stmtNode = labelNode->statement();
++        MOZ_ASSERT(labelNode->pn_pos.encloses(stmtNode->pn_pos));
+ 
+         RootedValue label(cx), stmt(cx);
+-        RootedAtom pnAtom(cx, pn->as<LabeledStatement>().label());
++        RootedAtom pnAtom(cx, labelNode->label());
+         return identifier(pnAtom, nullptr, &label) &&
+-               statement(pn->pn_expr, &stmt) &&
+-               builder.labeledStatement(label, stmt, &pn->pn_pos, dst);
++               statement(stmtNode, &stmt) &&
++               builder.labeledStatement(label, stmt, &labelNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Throw:
+       {
+         UnaryNode* throwNode = &pn->as<UnaryNode>();
+         ParseNode* operand = throwNode->kid();
+         MOZ_ASSERT(throwNode->pn_pos.encloses(operand->pn_pos));
+ 
+@@ -2756,18 +2763,19 @@ ASTSerializer::expression(ParseNode* pn,
+ {
+     if (!CheckRecursionLimit(cx)) {
+         return false;
+     }
+ 
+     switch (pn->getKind()) {
+       case ParseNodeKind::Function:
+       {
+-        ASTType type = pn->pn_funbox->function()->isArrow() ? AST_ARROW_EXPR : AST_FUNC_EXPR;
+-        return function(pn, type, dst);
++        CodeNode* funNode = &pn->as<CodeNode>();
++        ASTType type = funNode->funbox()->function()->isArrow() ? AST_ARROW_EXPR : AST_FUNC_EXPR;
++        return function(funNode, type, dst);
+       }
+ 
+       case ParseNodeKind::Comma:
+       {
+         NodeVector exprs(cx);
+         return expressions(&pn->as<ListNode>(), exprs) &&
+                builder.sequenceExpression(exprs, &pn->pn_pos, dst);
+       }
+@@ -2949,17 +2957,17 @@ ASTSerializer::expression(ParseNode* pn,
+ 
+       case ParseNodeKind::Dot:
+       {
+         PropertyAccess* prop = &pn->as<PropertyAccess>();
+         MOZ_ASSERT(prop->pn_pos.encloses(prop->expression().pn_pos));
+ 
+         RootedValue expr(cx);
+         RootedValue propname(cx);
+-        RootedAtom pnAtom(cx, prop->key().pn_atom);
++        RootedAtom pnAtom(cx, prop->key().atom());
+ 
+         if (prop->isSuper()) {
+             if (!builder.super(&prop->expression().pn_pos, &expr)) {
+                 return false;
+             }
+         } else {
+             if (!expression(&prop->expression(), &expr)) {
+                 return false;
+@@ -2995,40 +3003,39 @@ ASTSerializer::expression(ParseNode* pn,
+       case ParseNodeKind::CallSiteObj:
+       {
+         CallSiteNode* callSiteObj = &pn->as<CallSiteNode>();
+         ListNode* rawNodes = callSiteObj->rawNodes();
+         NodeVector raw(cx);
+         if (!raw.reserve(rawNodes->count())) {
+             return false;
+         }
+-        for (ParseNode* rawItem : rawNodes->contents()) {
++        for (ParseNode* item : rawNodes->contents()) {
++            NameNode* rawItem = &item->as<NameNode>();
+             MOZ_ASSERT(callSiteObj->pn_pos.encloses(rawItem->pn_pos));
+ 
+             RootedValue expr(cx);
+-            expr.setString(rawItem->pn_atom);
++            expr.setString(rawItem->atom());
+             raw.infallibleAppend(expr);
+         }
+ 
+         NodeVector cooked(cx);
+         if (!cooked.reserve(callSiteObj->count() - 1)) {
+             return false;
+         }
+ 
+-        for (ParseNode* cookedItem = callSiteObj->head()->pn_next;
+-             cookedItem;
+-             cookedItem = cookedItem->pn_next) {
++        for (ParseNode* cookedItem : callSiteObj->contentsFrom(rawNodes->pn_next)) {
+             MOZ_ASSERT(callSiteObj->pn_pos.encloses(cookedItem->pn_pos));
+ 
+             RootedValue expr(cx);
+             if (cookedItem->isKind(ParseNodeKind::RawUndefined)) {
+                 expr.setUndefined();
+             } else {
+                 MOZ_ASSERT(cookedItem->isKind(ParseNodeKind::TemplateString));
+-                expr.setString(cookedItem->pn_atom);
++                expr.setString(cookedItem->as<NameNode>().atom());
+             }
+             cooked.infallibleAppend(expr);
+         }
+ 
+         return builder.callSiteObj(raw, cooked, &callSiteObj->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Array:
+@@ -3087,17 +3094,17 @@ ASTSerializer::expression(ParseNode* pn,
+             }
+             elts.infallibleAppend(prop);
+         }
+ 
+         return builder.objectExpression(elts, &obj->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Name:
+-        return identifier(pn, dst);
++        return identifier(&pn->as<NameNode>(), dst);
+ 
+       case ParseNodeKind::This:
+         return builder.thisExpression(&pn->pn_pos, dst);
+ 
+       case ParseNodeKind::TemplateStringList:
+       {
+         ListNode* list = &pn->as<ListNode>();
+         NodeVector elts(cx);
+@@ -3212,28 +3219,28 @@ ASTSerializer::expression(ParseNode* pn,
+       }
+ 
+       default:
+         LOCAL_NOT_REACHED("unexpected expression type");
+     }
+ }
+ 
+ bool
+-ASTSerializer::propertyName(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::propertyName(ParseNode* key, MutableHandleValue dst)
+ {
+-    if (pn->isKind(ParseNodeKind::ComputedName)) {
+-        return expression(pn, dst);
++    if (key->isKind(ParseNodeKind::ComputedName)) {
++        return expression(key, dst);
+     }
+-    if (pn->isKind(ParseNodeKind::ObjectPropertyName)) {
+-        return identifier(pn, dst);
++    if (key->isKind(ParseNodeKind::ObjectPropertyName)) {
++        return identifier(&key->as<NameNode>(), dst);
+     }
+ 
+-    LOCAL_ASSERT(pn->isKind(ParseNodeKind::String) || pn->isKind(ParseNodeKind::Number));
+-
+-    return literal(pn, dst);
++    LOCAL_ASSERT(key->isKind(ParseNodeKind::String) || key->isKind(ParseNodeKind::Number));
++
++    return literal(key, dst);
+ }
+ 
+ bool
+ ASTSerializer::property(ParseNode* pn, MutableHandleValue dst)
+ {
+     if (pn->isKind(ParseNodeKind::MutateProto)) {
+         RootedValue val(cx);
+         return expression(pn->as<UnaryNode>().kid(), &val) &&
+@@ -3263,31 +3270,31 @@ ASTSerializer::property(ParseNode* pn, M
+ 
+     BinaryNode* node = &pn->as<BinaryNode>();
+     ParseNode* keyNode = node->left();
+     ParseNode* valNode = node->right();
+ 
+     bool isShorthand = node->isKind(ParseNodeKind::Shorthand);
+     bool isMethod =
+         valNode->isKind(ParseNodeKind::Function) &&
+-        valNode->pn_funbox->function()->kind() == JSFunction::Method;
++        valNode->as<CodeNode>().funbox()->function()->kind() == JSFunction::Method;
+     RootedValue key(cx), val(cx);
+     return propertyName(keyNode, &key) &&
+            expression(valNode, &val) &&
+            builder.propertyInitializer(key, val, kind, isShorthand, isMethod, &node->pn_pos, dst);
+ }
+ 
+ bool
+ ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst)
+ {
+     RootedValue val(cx);
+     switch (pn->getKind()) {
+       case ParseNodeKind::TemplateString:
+       case ParseNodeKind::String:
+-        val.setString(pn->pn_atom);
++        val.setString(pn->as<NameNode>().atom());
+         break;
+ 
+       case ParseNodeKind::RegExp:
+       {
+         RootedObject re1(cx, pn->as<RegExpLiteral>().objbox()->object);
+         LOCAL_ASSERT(re1 && re1->is<RegExpObject>());
+ 
+         RootedObject re2(cx, CloneRegExpObject(cx, re1.as<RegExpObject>()));
+@@ -3295,17 +3302,17 @@ ASTSerializer::literal(ParseNode* pn, Mu
+             return false;
+         }
+ 
+         val.setObject(*re2);
+         break;
+       }
+ 
+       case ParseNodeKind::Number:
+-        val.setNumber(pn->pn_dval);
++        val.setNumber(pn->as<NumericLiteral>().value());
+         break;
+ 
+       case ParseNodeKind::Null:
+         val.setNull();
+         break;
+ 
+       case ParseNodeKind::RawUndefined:
+         val.setUndefined();
+@@ -3437,55 +3444,56 @@ ASTSerializer::pattern(ParseNode* pn, Mu
+ bool
+ ASTSerializer::identifier(HandleAtom atom, TokenPos* pos, MutableHandleValue dst)
+ {
+     RootedValue atomContentsVal(cx, unrootedAtomContents(atom));
+     return builder.identifier(atomContentsVal, pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::identifier(ParseNode* pn, MutableHandleValue dst)
++ASTSerializer::identifier(NameNode* id, MutableHandleValue dst)
+ {
+-    LOCAL_ASSERT(pn->isArity(PN_NAME) || pn->isArity(PN_NULLARY));
+-    LOCAL_ASSERT(pn->pn_atom);
+-
+-    RootedAtom pnAtom(cx, pn->pn_atom);
+-    return identifier(pnAtom, &pn->pn_pos, dst);
++    LOCAL_ASSERT(id->atom());
++
++    RootedAtom pnAtom(cx, id->atom());
++    return identifier(pnAtom, &id->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
++ASTSerializer::function(CodeNode* funNode, ASTType type, MutableHandleValue dst)
+ {
+-    RootedFunction func(cx, pn->pn_funbox->function());
++    FunctionBox* funbox = funNode->funbox();
++    RootedFunction func(cx, funbox->function());
+ 
+     GeneratorStyle generatorStyle =
+-        pn->pn_funbox->isGenerator()
++        funbox->isGenerator()
+         ? GeneratorStyle::ES6
+         : GeneratorStyle::None;
+ 
+-    bool isAsync = pn->pn_funbox->isAsync();
+-    bool isExpression = pn->pn_funbox->hasExprBody();
++    bool isAsync = funbox->isAsync();
++    bool isExpression = funbox->hasExprBody();
+ 
+     RootedValue id(cx);
+     RootedAtom funcAtom(cx, func->explicitName());
+     if (!optIdentifier(funcAtom, nullptr, &id)) {
+         return false;
+     }
+ 
+     NodeVector args(cx);
+     NodeVector defaults(cx);
+ 
+     RootedValue body(cx), rest(cx);
+-    if (pn->pn_funbox->hasRest()) {
++    if (funbox->hasRest()) {
+         rest.setUndefined();
+     } else {
+         rest.setNull();
+     }
+-    return functionArgsAndBody(pn->pn_body, args, defaults, isAsync, isExpression, &body, &rest) &&
+-           builder.function(type, &pn->pn_pos, id, args, defaults, body,
++    return functionArgsAndBody(funNode->body(), args, defaults, isAsync, isExpression, &body,
++                               &rest) &&
++           builder.function(type, &funNode->pn_pos, id, args, defaults, body,
+                             rest, generatorStyle, isAsync, isExpression, dst);
+ }
+ 
+ bool
+ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
+                                    bool isAsync, bool isExpression,
+                                    MutableHandleValue body, MutableHandleValue rest)
+ {
+@@ -3806,17 +3814,17 @@ reflect_parse(JSContext* cx, uint32_t ar
+ 
+         ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder);
+         pn = parser.moduleBody(&modulesc);
+         if (!pn) {
+             return false;
+         }
+ 
+         MOZ_ASSERT(pn->getKind() == ParseNodeKind::Module);
+-        pn = pn->pn_body;
++        pn = pn->as<CodeNode>().body();
+     }
+ 
+     RootedValue val(cx);
+     if (!serialize.program(&pn->as<ListNode>(), &val)) {
+         args.rval().setNull();
+         return false;
+     }
+ 
+diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp
+--- a/js/src/frontend/BinSource-auto.cpp
++++ b/js/src/frontend/BinSource-auto.cpp
+@@ -1907,17 +1907,17 @@ BinASTParser<Tok>::parseSumParameter(con
+ {
+     ParseNode* result;
+     switch (kind) {
+       case BinKind::ArrayBinding:
+         MOZ_TRY_VAR(result, parseInterfaceArrayBinding(start, kind, fields));
+         break;
+       case BinKind::BindingIdentifier:
+         MOZ_TRY_VAR(result, parseInterfaceBindingIdentifier(start, kind, fields));
+-        if (!parseContext_->positionalFormalParameterNames().append(result->pn_atom))
++        if (!parseContext_->positionalFormalParameterNames().append(result->template as<NameNode>().atom()))
+             return raiseOOM();
+         if (parseContext_->isFunctionBox())
+             parseContext_->functionBox()->length++;
+         break;
+       case BinKind::BindingWithInitializer:
+         MOZ_TRY_VAR(result, parseInterfaceBindingWithInitializer(start, kind, fields));
+         break;
+       case BinKind::ObjectBinding:
+@@ -4805,17 +4805,17 @@ BinASTParser<Tok>::parseInterfaceForInOf
+     AutoVariableDeclarationKind kindGuard(this);
+ 
+     BINJS_MOZ_TRY_DECL(kind_, parseVariableDeclarationKind());
+ 
+     BINJS_MOZ_TRY_DECL(binding, parseBinding());
+ 
+     // Restored by `kindGuard`.
+     variableDeclarationKind_ = kind_;
+-    MOZ_TRY(checkBinding(binding->pn_atom->asPropertyName()));
++    MOZ_TRY(checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
+     auto pnk =
+         kind_ == VariableDeclarationKind::Let
+             ? ParseNodeKind::Let
+             : ParseNodeKind::Var;
+     BINJS_TRY_DECL(result, factory_.newDeclarationList(pnk, tokenizer_->pos(start)));
+     factory_.addList(result, binding);
+     return result;
+ }
+@@ -6980,21 +6980,21 @@ BinASTParser<Tok>::parseInterfaceVariabl
+ 
+     BINJS_MOZ_TRY_DECL(binding, parseBinding());
+ 
+     BINJS_MOZ_TRY_DECL(init, parseOptionalExpression());
+ 
+     ParseNode* result;
+     if (binding->isKind(ParseNodeKind::Name)) {
+         // `var foo [= bar]``
+-        MOZ_TRY(checkBinding(binding->pn_atom->asPropertyName()));
+-
+-        BINJS_TRY_VAR(result, factory_.newName(binding->pn_atom->asPropertyName(), tokenizer_->pos(start), cx_));
++        MOZ_TRY(checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
++
++        BINJS_TRY_VAR(result, factory_.newName(binding->template as<NameNode>().atom()->asPropertyName(), tokenizer_->pos(start), cx_));
+         if (init)
+-            result->pn_expr = init;
++            result->as<NameNode>().setExpression(init);
+     } else {
+         // `var pattern = bar`
+         if (!init) {
+             // Here, `init` is required.
+             return raiseMissingField("VariableDeclarator (with non-trivial pattern)", BinField::Init);
+         }
+ 
+         MOZ_CRASH("Unimplemented: AssertedScope check for BindingPattern variable declaration");
+diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml
+--- a/js/src/frontend/BinSource.yaml
++++ b/js/src/frontend/BinSource.yaml
+@@ -635,17 +635,17 @@ ExpressionStatement:
+         BINJS_TRY_DECL(result, factory_.newExprStatement(expression, tokenizer_->offset()));
+ 
+ ForInOfBinding:
+     init:
+         AutoVariableDeclarationKind kindGuard(this);
+     build: |
+         // Restored by `kindGuard`.
+         variableDeclarationKind_ = kind_;
+-        MOZ_TRY(checkBinding(binding->pn_atom->asPropertyName()));
++        MOZ_TRY(checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
+         auto pnk =
+             kind_ == VariableDeclarationKind::Let
+                 ? ParseNodeKind::Let
+                 : ParseNodeKind::Var;
+         BINJS_TRY_DECL(result, factory_.newDeclarationList(pnk, tokenizer_->pos(start)));
+         factory_.addList(result, binding);
+ 
+ 
+@@ -867,17 +867,17 @@ OptionalAssertedVarScope:
+ OptionalAssertedParameterScope:
+     type-ok:
+         Ok
+ 
+ Parameter:
+     sum-arms:
+         BindingIdentifier:
+             after: |
+-                if (!parseContext_->positionalFormalParameterNames().append(result->pn_atom))
++                if (!parseContext_->positionalFormalParameterNames().append(result->template as<NameNode>().atom()))
+                     return raiseOOM();
+                 if (parseContext_->isFunctionBox())
+                     parseContext_->functionBox()->length++;
+ 
+ ReturnStatement:
+     init: |
+         if (!parseContext_->isFunctionBox()) {
+             // Return statements are permitted only inside functions.
+@@ -1107,21 +1107,21 @@ VariableDeclaration:
+         declarators->setKind(pnk);
+         auto result = declarators;
+ 
+ VariableDeclarator:
+     build: |
+         ParseNode* result;
+         if (binding->isKind(ParseNodeKind::Name)) {
+             // `var foo [= bar]``
+-            MOZ_TRY(checkBinding(binding->pn_atom->asPropertyName()));
++            MOZ_TRY(checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
+ 
+-            BINJS_TRY_VAR(result, factory_.newName(binding->pn_atom->asPropertyName(), tokenizer_->pos(start), cx_));
++            BINJS_TRY_VAR(result, factory_.newName(binding->template as<NameNode>().atom()->asPropertyName(), tokenizer_->pos(start), cx_));
+             if (init)
+-                result->pn_expr = init;
++                result->as<NameNode>().setExpression(init);
+         } else {
+             // `var pattern = bar`
+             if (!init) {
+                 // Here, `init` is required.
+                 return raiseMissingField("VariableDeclarator (with non-trivial pattern)", BinField::Init);
+             }
+ 
+             MOZ_CRASH("Unimplemented: AssertedScope check for BindingPattern variable declaration");
+diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
+--- a/js/src/frontend/BytecodeCompiler.cpp
++++ b/js/src/frontend/BytecodeCompiler.cpp
+@@ -436,17 +436,17 @@ BytecodeCompiler::compileModule()
+     if (!pn) {
+         return nullptr;
+     }
+ 
+     Maybe<BytecodeEmitter> emitter;
+     if (!emplaceEmitter(emitter, &modulesc)) {
+         return nullptr;
+     }
+-    if (!emitter->emitScript(pn->pn_body)) {
++    if (!emitter->emitScript(pn->as<CodeNode>().body())) {
+         return nullptr;
+     }
+ 
+     if (!builder.initModule()) {
+         return nullptr;
+     }
+ 
+     RootedModuleEnvironmentObject env(cx, ModuleEnvironmentObject::create(cx, module));
+@@ -493,32 +493,35 @@ BytecodeCompiler::compileStandaloneFunct
+         Directives newDirectives = directives;
+         fn = parser->standaloneFunction(fun, enclosingScope, parameterListEnd, generatorKind,
+                                         asyncKind, directives, &newDirectives);
+         if (!fn && !handleParseFailure(newDirectives, startPosition)) {
+             return false;
+         }
+     } while (!fn);
+ 
+-    if (fn->pn_funbox->function()->isInterpreted()) {
+-        MOZ_ASSERT(fun == fn->pn_funbox->function());
++    FunctionBox* funbox = fn->as<CodeNode>().funbox();
++    if (funbox->function()->isInterpreted()) {
++        MOZ_ASSERT(fun == funbox->function());
+ 
+-        if (!createScript(fn->pn_funbox->toStringStart, fn->pn_funbox->toStringEnd)) {
++        if (!createScript(funbox->toStringStart, funbox->toStringEnd)) {
+             return false;
+         }
+ 
+         Maybe<BytecodeEmitter> emitter;
+-        if (!emplaceEmitter(emitter, fn->pn_funbox)) {
++        if (!emplaceEmitter(emitter, funbox)) {
+             return false;
+         }
+-        if (!emitter->emitFunctionScript(fn, BytecodeEmitter::TopLevelFunction::Yes)) {
++        if (!emitter->emitFunctionScript(&fn->as<CodeNode>(),
++                                         BytecodeEmitter::TopLevelFunction::Yes))
++        {
+             return false;
+         }
+     } else {
+-        fun.set(fn->pn_funbox->function());
++        fun.set(funbox->function());
+         MOZ_ASSERT(IsAsmJSModule(fun));
+     }
+ 
+     // Enqueue an off-thread source compression task after finishing parsing.
+     if (!scriptSource->tryCompressOffThread(cx)) {
+         return false;
+     }
+ 
+@@ -892,23 +895,23 @@ frontend::CompileLazyFunction(JSContext*
+ 
+     if (lazy->isLikelyConstructorWrapper()) {
+         script->setLikelyConstructorWrapper();
+     }
+     if (lazy->hasBeenCloned()) {
+         script->setHasBeenCloned();
+     }
+ 
+-    BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
++    BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->as<CodeNode>().funbox(), script, lazy,
+                         pn->pn_pos, BytecodeEmitter::LazyFunction);
+     if (!bce.init()) {
+         return false;
+     }
+ 
+-    if (!bce.emitFunctionScript(pn, BytecodeEmitter::TopLevelFunction::Yes)) {
++    if (!bce.emitFunctionScript(&pn->as<CodeNode>(), BytecodeEmitter::TopLevelFunction::Yes)) {
+         return false;
+     }
+ 
+     delazificationCompletion.complete();
+     assertException.reset();
+     return true;
+ }
+ 
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -896,20 +896,20 @@ BytecodeEmitter::emitAtomOp(JSAtom* atom
+     if (!makeAtomIndex(atom, &index)) {
+         return false;
+     }
+ 
+     return emitIndexOp(op, index);
+ }
+ 
+ bool
+-BytecodeEmitter::emitAtomOp(ParseNode* pn, JSOp op)
+-{
+-    MOZ_ASSERT(pn->pn_atom != nullptr);
+-    return emitAtomOp(pn->pn_atom, op);
++BytecodeEmitter::emitAtomOp(NameNode* nameNode, JSOp op)
++{
++    MOZ_ASSERT(nameNode->atom() != nullptr);
++    return emitAtomOp(nameNode->atom(), op);
+ }
+ 
+ bool
+ BytecodeEmitter::emitInternedScopeOp(uint32_t index, JSOp op)
+ {
+     MOZ_ASSERT(JOF_OPTYPE(op) == JOF_SCOPE);
+     MOZ_ASSERT(index < scopeList.length());
+     return emitIndex32(op, index);
+@@ -1032,28 +1032,40 @@ BytecodeEmitter::checkSideEffects(ParseN
+         return false;
+     }
+ 
+  restart:
+ 
+     switch (pn->getKind()) {
+       // Trivial cases with no side effects.
+       case ParseNodeKind::EmptyStatement:
+-      case ParseNodeKind::String:
+-      case ParseNodeKind::TemplateString:
+-      case ParseNodeKind::RegExp:
+       case ParseNodeKind::True:
+       case ParseNodeKind::False:
+       case ParseNodeKind::Null:
+       case ParseNodeKind::RawUndefined:
+       case ParseNodeKind::Elision:
+       case ParseNodeKind::Generator:
+-      case ParseNodeKind::Number:
++        MOZ_ASSERT(pn->isArity(PN_NULLARY));
++        *answer = false;
++        return true;
++
+       case ParseNodeKind::ObjectPropertyName:
+-        MOZ_ASSERT(pn->isArity(PN_NULLARY));
++      case ParseNodeKind::String:
++      case ParseNodeKind::TemplateString:
++        MOZ_ASSERT(pn->is<NameNode>());
++        *answer = false;
++        return true;
++
++      case ParseNodeKind::RegExp:
++        MOZ_ASSERT(pn->is<RegExpLiteral>());
++        *answer = false;
++        return true;
++
++      case ParseNodeKind::Number:
++        MOZ_ASSERT(pn->is<NumericLiteral>());
+         *answer = false;
+         return true;
+ 
+       // |this| can throw in derived class constructors, including nested arrow
+       // functions or eval.
+       case ParseNodeKind::This:
+         MOZ_ASSERT(pn->is<UnaryNode>());
+         *answer = sc->needsThisTDZChecks();
+@@ -1363,31 +1375,31 @@ BytecodeEmitter::checkSideEffects(ParseN
+         return true;
+ 
+       case ParseNodeKind::Return:
+         MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::Name:
+-        MOZ_ASSERT(pn->isArity(PN_NAME));
++        MOZ_ASSERT(pn->is<NameNode>());
+         *answer = true;
+         return true;
+ 
+       // Shorthands could trigger getters: the |x| in the object literal in
+       // |with ({ get x() { throw 42; } }) ({ x });|, for example, triggers
+       // one.  (Of course, it isn't necessary to use |with| for a shorthand to
+       // trigger a getter.)
+       case ParseNodeKind::Shorthand:
+         MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+ 
+       case ParseNodeKind::Function:
+-        MOZ_ASSERT(pn->isArity(PN_CODE));
++        MOZ_ASSERT(pn->is<CodeNode>());
+         /*
+          * A named function, contrary to ES3, is no longer effectful, because
+          * we bind its name lexically (using JSOP_CALLEE) instead of creating
+          * an Object instance and binding a readonly, permanent property in it
+          * (the object and binding can be detected and hijacked or captured).
+          * This is a bug fix to ES3; it is fixed in ES3.1 drafts.
+          */
+         *answer = false;
+@@ -1440,18 +1452,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+         SwitchStatement* switchStmt = &pn->as<SwitchStatement>();
+         if (!checkSideEffects(&switchStmt->discriminant(), answer)) {
+             return false;
+         }
+         return *answer || checkSideEffects(&switchStmt->lexicalForCaseList(), answer);
+       }
+ 
+       case ParseNodeKind::Label:
+-        MOZ_ASSERT(pn->isArity(PN_NAME));
+-        return checkSideEffects(pn->expr(), answer);
++        return checkSideEffects(pn->as<NameNode>().expression(), answer);
+ 
+       case ParseNodeKind::LexicalScope:
+         MOZ_ASSERT(pn->isArity(PN_SCOPE));
+         return checkSideEffects(pn->scopeBody(), answer);
+ 
+       // We could methodically check every interpolated expression, but it's
+       // probably not worth the trouble.  Treat template strings as effect-free
+       // only if they don't contain any substitutions.
+@@ -2575,17 +2586,17 @@ BytecodeEmitter::emitSwitch(SwitchStatem
+             ParseNode* caseValue = caseClause->caseExpression();
+ 
+             if (caseValue->getKind() != ParseNodeKind::Number) {
+                 tableGen.setInvalid();
+                 break;
+             }
+ 
+             int32_t i;
+-            if (!NumberEqualsInt32(caseValue->pn_dval, &i)) {
++            if (!NumberEqualsInt32(caseValue->as<NumericLiteral>().value(), &i)) {
+                 tableGen.setInvalid();
+                 break;
+             }
+ 
+             if (!tableGen.addNumber(i)) {
+                 return false;
+             }
+         }
+@@ -2636,18 +2647,22 @@ BytecodeEmitter::emitSwitch(SwitchStatem
+             if (!se.emitDefaultBody()) {
+                 return false;
+             }
+         } else {
+             if (isTableSwitch) {
+                 ParseNode* caseValue = caseClause->caseExpression();
+                 MOZ_ASSERT(caseValue->isKind(ParseNodeKind::Number));
+ 
+-                int32_t i = int32_t(caseValue->pn_dval);
+-                MOZ_ASSERT(double(i) == caseValue->pn_dval);
++                NumericLiteral* literal = &caseValue->as<NumericLiteral>();
++#ifdef DEBUG
++                int32_t v;
++                MOZ_ASSERT(mozilla::NumberIsInt32(literal->value(), &v));
++#endif
++                int32_t i = int32_t(literal->value());
+ 
+                 if (!se.emitCaseBody(i, tableGen)) {
+                     return false;
+                 }
+             } else {
+                 if (!se.emitCaseBody()) {
+                     return false;
+                 }
+@@ -2848,20 +2863,20 @@ BytecodeEmitter::emitScript(ParseNode* b
+     }
+ 
+     tellDebuggerAboutCompiledScript(cx);
+ 
+     return true;
+ }
+ 
+ bool
+-BytecodeEmitter::emitFunctionScript(ParseNode* fn, TopLevelFunction isTopLevel)
+-{
+-    MOZ_ASSERT(fn->isKind(ParseNodeKind::Function));
+-    ParseNode* body = fn->pn_body;
++BytecodeEmitter::emitFunctionScript(CodeNode* funNode, TopLevelFunction isTopLevel)
++{
++    MOZ_ASSERT(funNode->isKind(ParseNodeKind::Function));
++    ParseNode* body = funNode->body();
+     MOZ_ASSERT(body->isKind(ParseNodeKind::ParamsBody));
+     FunctionBox* funbox = sc->asFunctionBox();
+     AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser->errorReporter(), funbox);
+ 
+     setScriptStartOffsetIfUnset(body->pn_pos);
+ 
+     // The ordering of these EmitterScopes is important. The named lambda
+     // scope needs to enclose the function scope needs to enclose the extra
+@@ -2913,17 +2928,17 @@ BytecodeEmitter::emitFunctionScript(Pars
+     if (namedLambdaEmitterScope) {
+         if (!namedLambdaEmitterScope->leave(this)) {
+             return false;
+         }
+         namedLambdaEmitterScope.reset();
+     }
+ 
+     if (isTopLevel == TopLevelFunction::Yes) {
+-        if (!NameFunctions(cx, fn)) {
++        if (!NameFunctions(cx, funNode)) {
+             return false;
+         }
+     }
+ 
+     if (!JSScript::fullyInitFromEmitter(cx, script, this)) {
+         return false;
+     }
+ 
+@@ -3441,17 +3456,17 @@ BytecodeEmitter::emitDefault(ParseNode* 
+ bool
+ BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name)
+ {
+     MOZ_ASSERT(maybeFun->isDirectRHSAnonFunction());
+ 
+     if (maybeFun->isKind(ParseNodeKind::Function)) {
+         // Function doesn't have 'name' property at this point.
+         // Set function's name at compile time.
+-        JSFunction* fun = maybeFun->pn_funbox->function();
++        JSFunction* fun = maybeFun->as<CodeNode>().funbox()->function();
+ 
+         // The inferred name may already be set if this function is an
+         // interpreted lazy function and we OOM'ed after we set the inferred
+         // name the first time.
+         if (fun->hasInferredName()) {
+             MOZ_ASSERT(fun->isInterpretedLazy());
+             MOZ_ASSERT(fun->inferredName() == name);
+ 
+@@ -3984,24 +3999,24 @@ BytecodeEmitter::emitDestructuringOpsObj
+             }
+             needsGetElem = false;
+         } else {
+             MOZ_ASSERT(member->isKind(ParseNodeKind::Colon) ||
+                        member->isKind(ParseNodeKind::Shorthand));
+ 
+             ParseNode* key = member->as<BinaryNode>().left();
+             if (key->isKind(ParseNodeKind::Number)) {
+-                if (!emitNumberOp(key->pn_dval)) {                // ... *SET RHS *LREF RHS KEY
+-                    return false;
++                if (!emitNumberOp(key->as<NumericLiteral>().value())) {
++                    return false;                                 // ... *SET RHS *LREF RHS KEY
+                 }
+             } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                        key->isKind(ParseNodeKind::String))
+             {
+-                if (!emitAtomOp(key->pn_atom, JSOP_GETPROP)) {    // ... *SET RHS *LREF PROP
+-                    return false;
++                if (!emitAtomOp(key->as<NameNode>().atom(), JSOP_GETPROP)) {
++                    return false;                                 // ... *SET RHS *LREF PROP
+                 }
+                 needsGetElem = false;
+             } else {
+                 if (!emitComputedPropertyName(&key->as<UnaryNode>())) {
+                     return false;                                 // ... *SET RHS *LREF RHS KEY
+                 }
+ 
+                 // Add the computed property key to the exclusion set.
+@@ -4077,24 +4092,24 @@ BytecodeEmitter::emitDestructuringObjRes
+         }
+ 
+         bool isIndex = false;
+         if (member->isKind(ParseNodeKind::MutateProto)) {
+             pnatom.set(cx->names().proto);
+         } else {
+             ParseNode* key = member->as<BinaryNode>().left();
+             if (key->isKind(ParseNodeKind::Number)) {
+-                if (!emitNumberOp(key->pn_dval)) {
++                if (!emitNumberOp(key->as<NumericLiteral>().value())) {
+                     return false;
+                 }
+                 isIndex = true;
+             } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                        key->isKind(ParseNodeKind::String))
+             {
+-                pnatom.set(key->pn_atom);
++                pnatom.set(key->as<NameNode>().atom());
+             } else {
+                 // Otherwise this is a computed property name which needs to
+                 // be added dynamically.
+                 obj.set(nullptr);
+                 continue;
+             }
+         }
+ 
+@@ -4158,17 +4173,17 @@ BytecodeEmitter::emitTemplateString(List
+ 
+     for (ParseNode* item : templateString->contents()) {
+         bool isString = (item->getKind() == ParseNodeKind::String ||
+                          item->getKind() == ParseNodeKind::TemplateString);
+ 
+         // Skip empty strings. These are very common: a template string like
+         // `${a}${b}` has three empty strings and without this optimization
+         // we'd emit four JSOP_ADD operations instead of just one.
+-        if (isString && item->pn_atom->empty()) {
++        if (isString && item->as<NameNode>().atom()->empty()) {
+             continue;
+         }
+ 
+         if (!isString) {
+             // We update source notes before emitting the expression
+             if (!updateSourceCoordNotes(item->pn_pos.begin)) {
+                 return false;
+             }
+@@ -4231,17 +4246,17 @@ BytecodeEmitter::emitDeclarationList(Lis
+             if (!emitDestructuringOps(pattern, DestructuringDeclaration)) {
+                 return false;
+             }
+ 
+             if (!emit1(JSOP_POP)) {
+                 return false;
+             }
+         } else {
+-            if (!emitSingleDeclaration(declList, decl, decl->expr())) {
++            if (!emitSingleDeclaration(declList, decl, decl->as<NameNode>().expression())) {
+                 return false;
+             }
+         }
+     }
+     return true;
+ }
+ 
+ bool
+@@ -4378,17 +4393,17 @@ BytecodeEmitter::emitAssignment(ParseNod
+             }
+             offset += 2;
+         } else {
+             if (!emitTree(&prop->expression())) {
+                 return false;
+             }
+             offset += 1;
+         }
+-        if (!makeAtomIndex(prop->key().pn_atom, &atomIndex)) {
++        if (!makeAtomIndex(prop->key().atom(), &atomIndex)) {
+             return false;
+         }
+         break;
+       }
+       case ParseNodeKind::Elem: {
+         PropertyByValue* elem = &lhs->as<PropertyByValue>();
+         EmitElemOption opt = op == JSOP_NOP ? EmitElemOption::Get : EmitElemOption::CompoundAssign;
+         if (elem->isSuper()) {
+@@ -4437,17 +4452,17 @@ BytecodeEmitter::emitAssignment(ParseNod
+                 if (!emit1(JSOP_DUP2)) {
+                     return false;
+                 }
+                 getOp = JSOP_GETPROP_SUPER;
+             } else {
+                 if (!emit1(JSOP_DUP)) {
+                     return false;
+                 }
+-                bool isLength = (prop->key().pn_atom == cx->names().length);
++                bool isLength = prop->key().atom() == cx->names().length;
+                 getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP;
+             }
+             if (!emitIndex32(getOp, atomIndex)) {
+                 return false;
+             }
+             break;
+           }
+           case ParseNodeKind::Elem: {
+@@ -4540,21 +4555,21 @@ bool
+ ParseNode::getConstantValue(JSContext* cx, AllowConstantObjects allowObjects,
+                             MutableHandleValue vp, Value* compare, size_t ncompare,
+                             NewObjectKind newKind)
+ {
+     MOZ_ASSERT(newKind == TenuredObject || newKind == SingletonObject);
+ 
+     switch (getKind()) {
+       case ParseNodeKind::Number:
+-        vp.setNumber(pn_dval);
++        vp.setNumber(as<NumericLiteral>().value());
+         return true;
+       case ParseNodeKind::TemplateString:
+       case ParseNodeKind::String:
+-        vp.setString(pn_atom);
++        vp.setString(as<NameNode>().atom());
+         return true;
+       case ParseNodeKind::True:
+         vp.setBoolean(true);
+         return true;
+       case ParseNodeKind::False:
+         vp.setBoolean(false);
+         return true;
+       case ParseNodeKind::Null:
+@@ -4637,22 +4652,22 @@ ParseNode::getConstantValue(JSContext* c
+             }
+             if (value.isMagic(JS_GENERIC_MAGIC)) {
+                 vp.setMagic(JS_GENERIC_MAGIC);
+                 return true;
+             }
+ 
+             ParseNode* key = prop->left();
+             if (key->isKind(ParseNodeKind::Number)) {
+-                idvalue = NumberValue(key->pn_dval);
++                idvalue = NumberValue(key->as<NumericLiteral>().value());
+             } else {
+                 MOZ_ASSERT(key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                            key->isKind(ParseNodeKind::String));
+-                MOZ_ASSERT(key->pn_atom != cx->names().proto);
+-                idvalue = StringValue(key->pn_atom);
++                MOZ_ASSERT(key->as<NameNode>().atom() != cx->names().proto);
++                idvalue = StringValue(key->as<NameNode>().atom());
+             }
+ 
+             RootedId id(cx);
+             if (!ValueToId<CanGC>(cx, idvalue, &id)) {
+                 return false;
+             }
+ 
+             if (!properties.append(IdValuePair(id, value))) {
+@@ -4927,17 +4942,19 @@ BytecodeEmitter::emitHoistedFunctionsInL
+         ParseNode* maybeFun = stmt;
+ 
+         if (!sc->strict()) {
+             while (maybeFun->isKind(ParseNodeKind::Label)) {
+                 maybeFun = maybeFun->as<LabeledStatement>().statement();
+             }
+         }
+ 
+-        if (maybeFun->isKind(ParseNodeKind::Function) && maybeFun->functionIsHoisted()) {
++        if (maybeFun->isKind(ParseNodeKind::Function) &&
++            maybeFun->as<CodeNode>().functionIsHoisted())
++        {
+             if (!emitTree(maybeFun)) {
+                 return false;
+             }
+         }
+     }
+ 
+     return true;
+ }
+@@ -5464,17 +5481,17 @@ BytecodeEmitter::emitForIn(ForNode* forI
+ 
+     // Annex B: Evaluate the var-initializer expression if present.
+     // |for (var i = initializer in expr) { ... }|
+     ParseNode* forInTarget = forInHead->kid1();
+     if (parser->astGenerator().isDeclarationList(forInTarget)) {
+         ParseNode* decl =
+             parser->astGenerator().singleBindingFromDeclaration(&forInTarget->as<ListNode>());
+         if (decl->isKind(ParseNodeKind::Name)) {
+-            if (ParseNode* initializer = decl->expr()) {
++            if (ParseNode* initializer = decl->as<NameNode>().expression()) {
+                 MOZ_ASSERT(forInTarget->isKind(ParseNodeKind::Var),
+                            "for-in initializers are only permitted for |var| declarations");
+ 
+                 if (!updateSourceCoordNotes(decl->pn_pos.begin)) {
+                     return false;
+                 }
+ 
+                 auto emitRhs = [decl, initializer](BytecodeEmitter* bce, const NameLocation&, bool) {
+@@ -5633,19 +5650,19 @@ BytecodeEmitter::emitFor(ForNode* forNod
+         return emitForIn(forNode, headLexicalEmitterScope);
+     }
+ 
+     MOZ_ASSERT(forNode->head()->isKind(ParseNodeKind::ForOf));
+     return emitForOf(forNode, headLexicalEmitterScope);
+ }
+ 
+ MOZ_NEVER_INLINE bool
+-BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
+-{
+-    FunctionBox* funbox = pn->pn_funbox;
++BytecodeEmitter::emitFunction(CodeNode* funNode, bool needsProto)
++{
++    FunctionBox* funbox = funNode->funbox();
+     RootedFunction fun(cx, funbox->function());
+     RootedAtom name(cx, fun->explicitName());
+     MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
+ 
+     // Set the |wasEmitted| flag in the funbox once the function has been
+     // emitted. Function definitions that need hoisting to the top of the
+     // function will be seen by emitFunction in two places.
+     if (funbox->wasEmitted) {
+@@ -5686,17 +5703,17 @@ BytecodeEmitter::emitFunction(ParseNode*
+                 return false;
+             }
+             if (!emit1(JSOP_POP)) {
+                 return false;
+             }
+         }
+ 
+         MOZ_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
+-        MOZ_ASSERT(pn->functionIsHoisted());
++        MOZ_ASSERT(funNode->functionIsHoisted());
+         return true;
+     }
+ 
+     funbox->wasEmitted = true;
+ 
+     // Mark as singletons any function which will only be executed once, or
+     // which is inner to a lambda we only expect to run once. In the latter
+     // case, if the lambda runs multiple times then CloneFunctionObject will
+@@ -5728,45 +5745,45 @@ BytecodeEmitter::emitFunction(ParseNode*
+                                                           funbox->bufStart, funbox->bufEnd,
+                                                           funbox->toStringStart,
+                                                           funbox->toStringEnd));
+             if (!script) {
+                 return false;
+             }
+ 
+             BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ nullptr,
+-                                 pn->pn_pos, emitterMode);
++                                 funNode->pn_pos, emitterMode);
+             if (!bce2.init()) {
+                 return false;
+             }
+ 
+             /* We measured the max scope depth when we parsed the function. */
+-            if (!bce2.emitFunctionScript(pn, TopLevelFunction::No)) {
++            if (!bce2.emitFunctionScript(funNode, TopLevelFunction::No)) {
+                 return false;
+             }
+ 
+             if (funbox->isLikelyConstructorWrapper()) {
+                 script->setLikelyConstructorWrapper();
+             }
+         }
+ 
+         if (outersc->isFunctionBox()) {
+             outersc->asFunctionBox()->setHasInnerFunctions();
+         }
+     } else {
+         MOZ_ASSERT(IsAsmJSModule(fun));
+     }
+ 
+     // Make the function object a literal in the outer script's pool.
+-    unsigned index = objectList.add(pn->pn_funbox);
++    unsigned index = objectList.add(funNode->funbox());
+ 
+     // Non-hoisted functions simply emit their respective op.
+-    if (!pn->functionIsHoisted()) {
++    if (!funNode->functionIsHoisted()) {
+         // JSOP_LAMBDA_ARROW is always preceded by a new.target
+-        MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
++        MOZ_ASSERT(fun->isArrow() == (funNode->getOp() == JSOP_LAMBDA_ARROW));
+         if (funbox->isAsync()) {
+             MOZ_ASSERT(!needsProto);
+             return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(),
+                                     fun->isGenerator());
+         }
+ 
+         if (fun->isArrow()) {
+             if (sc->allowNewTarget()) {
+@@ -5776,30 +5793,30 @@ BytecodeEmitter::emitFunction(ParseNode*
+             } else {
+                 if (!emit1(JSOP_NULL)) {
+                     return false;
+                 }
+             }
+         }
+ 
+         if (needsProto) {
+-            MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
+-            pn->setOp(JSOP_FUNWITHPROTO);
+-        }
+-
+-        if (pn->getOp() == JSOP_DEFFUN) {
++            MOZ_ASSERT(funNode->getOp() == JSOP_LAMBDA);
++            funNode->setOp(JSOP_FUNWITHPROTO);
++        }
++
++        if (funNode->getOp() == JSOP_DEFFUN) {
+             if (!emitIndex32(JSOP_LAMBDA, index)) {
+                 return false;
+             }
+             return emit1(JSOP_DEFFUN);
+         }
+ 
+         // This is a FunctionExpression, ArrowFunctionExpression, or class
+         // constructor. Emit the single instruction (without location info).
+-        return emitIndex32(pn->getOp(), index);
++        return emitIndex32(funNode->getOp(), index);
+     }
+ 
+     MOZ_ASSERT(!needsProto);
+ 
+     bool topLevelFunction;
+     if (sc->isFunctionBox() || (sc->isEvalContext() && sc->strict())) {
+         // No nested functions inside other functions are top-level.
+         topLevelFunction = false;
+@@ -5818,17 +5835,17 @@ BytecodeEmitter::emitFunction(ParseNode*
+             // during ModuleInstantiate(), before the script is run.
+ 
+             RootedModuleObject module(cx, sc->asModuleContext()->module());
+             if (!module->noteFunctionDeclaration(cx, name, fun)) {
+                 return false;
+             }
+         } else {
+             MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
+-            MOZ_ASSERT(pn->getOp() == JSOP_NOP);
++            MOZ_ASSERT(funNode->getOp() == JSOP_NOP);
+             switchToPrologue();
+             if (funbox->isAsync()) {
+                 if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(),
+                                       fun->isGenerator()))
+                 {
+                     return false;
+                 }
+             } else {
+@@ -6782,17 +6799,17 @@ BytecodeEmitter::emitExpressionStatement
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitDeleteName(UnaryNode* deleteNode)
+ {
+     MOZ_ASSERT(deleteNode->isKind(ParseNodeKind::DeleteName));
+ 
+-    ParseNode* nameExpr = deleteNode->kid();
++    NameNode* nameExpr = &deleteNode->kid()->as<NameNode>();
+     MOZ_ASSERT(nameExpr->isKind(ParseNodeKind::Name));
+ 
+     return emitAtomOp(nameExpr, JSOP_DELNAME);
+ }
+ 
+ bool
+ BytecodeEmitter::emitDeleteProperty(UnaryNode* deleteNode)
+ {
+@@ -7015,17 +7032,17 @@ BytecodeEmitter::emitSelfHostedResumeGen
+ 
+     ParseNode* valNode = genNode->pn_next;
+     if (!emitTree(valNode)) {
+         return false;
+     }
+ 
+     ParseNode* kindNode = valNode->pn_next;
+     MOZ_ASSERT(kindNode->isKind(ParseNodeKind::String));
+-    uint16_t operand = GeneratorObject::getResumeKind(cx, kindNode->pn_atom);
++    uint16_t operand = GeneratorObject::getResumeKind(cx, kindNode->as<NameNode>().atom());
+     MOZ_ASSERT(!kindNode->pn_next);
+ 
+     if (!emitCall(JSOP_RESUME, operand)) {
+         return false;
+     }
+ 
+     return true;
+ }
+@@ -7856,25 +7873,25 @@ BytecodeEmitter::emitPropertyList(ListNo
+                 return false;
+             }
+         }
+ 
+         /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
+         ParseNode* key = propdef->as<BinaryNode>().left();
+         bool isIndex = false;
+         if (key->isKind(ParseNodeKind::Number)) {
+-            if (!emitNumberOp(key->pn_dval)) {
++            if (!emitNumberOp(key->as<NumericLiteral>().value())) {
+                 return false;
+             }
+             isIndex = true;
+         } else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                    key->isKind(ParseNodeKind::String))
+         {
+             // EmitClass took care of constructor already.
+-            if (type == ClassBody && key->pn_atom == cx->names().constructor &&
++            if (type == ClassBody && key->as<NameNode>().atom() == cx->names().constructor &&
+                 !propdef->as<ClassMethod>().isStatic())
+             {
+                 continue;
+             }
+         } else {
+             MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
+             if (!emitComputedPropertyName(&key->as<UnaryNode>())) {
+                 return false;
+@@ -7897,20 +7914,21 @@ BytecodeEmitter::emitPropertyList(ListNo
+                                         : op == JSOP_INITPROP_SETTER ? FunctionPrefixKind::Set
+                                         : FunctionPrefixKind::None;
+ 
+         if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER) {
+             objp.set(nullptr);
+         }
+ 
+         if (propVal->isKind(ParseNodeKind::Function) &&
+-            propVal->pn_funbox->needsHomeObject())
++            propVal->as<CodeNode>().funbox()->needsHomeObject())
+         {
+-            MOZ_ASSERT(propVal->pn_funbox->function()->allowSuperProperty());
+-            bool isAsync = propVal->pn_funbox->isAsync();
++            FunctionBox* funbox = propVal->as<CodeNode>().funbox();
++            MOZ_ASSERT(funbox->function()->allowSuperProperty());
++            bool isAsync = funbox->isAsync();
+             if (isAsync) {
+                 if (!emit1(JSOP_SWAP)) {
+                     return false;
+                 }
+             }
+             if (!emit2(JSOP_INITHOMEOBJECT, isIndex + isAsync)) {
+                 return false;
+             }
+@@ -7953,39 +7971,39 @@ BytecodeEmitter::emitPropertyList(ListNo
+             if (!emit1(op)) {
+                 return false;
+             }
+         } else {
+             MOZ_ASSERT(key->isKind(ParseNodeKind::ObjectPropertyName) ||
+                        key->isKind(ParseNodeKind::String));
+ 
+             uint32_t index;
+-            if (!makeAtomIndex(key->pn_atom, &index)) {
++            if (!makeAtomIndex(key->as<NameNode>().atom(), &index)) {
+                 return false;
+             }
+ 
+             if (objp) {
+                 MOZ_ASSERT(type == ObjectLiteral);
+                 MOZ_ASSERT(!IsHiddenInitOp(op));
+                 MOZ_ASSERT(!objp->inDictionaryMode());
+-                Rooted<jsid> id(cx, AtomToId(key->pn_atom));
++                Rooted<jsid> id(cx, AtomToId(key->as<NameNode>().atom()));
+                 if (!NativeDefineDataProperty(cx, objp, id, UndefinedHandleValue,
+                                               JSPROP_ENUMERATE))
+                 {
+                     return false;
+                 }
+                 if (objp->inDictionaryMode()) {
+                     objp.set(nullptr);
+                 }
+             }
+ 
+             if (propVal->isDirectRHSAnonFunction()) {
+                 MOZ_ASSERT(prefixKind == FunctionPrefixKind::None);
+ 
+-                RootedAtom keyName(cx, key->pn_atom);
++                RootedAtom keyName(cx, key->as<NameNode>().atom());
+                 if (!setOrEmitSetFunName(propVal, keyName)) {
+                     return false;
+                 }
+             }
+             if (!emitIndex32(op, index)) {
+                 return false;
+             }
+         }
+@@ -8659,24 +8677,24 @@ BytecodeEmitter::emitLexicalInitializati
+ // This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
+ // (BindingClassDeclarationEvaluation).
+ bool
+ BytecodeEmitter::emitClass(ClassNode* classNode)
+ {
+     ClassNames* names = classNode->names();
+     ParseNode* heritageExpression = classNode->heritage();
+     ListNode* classMethods = classNode->methodList();
+-    ParseNode* constructor = nullptr;
++    CodeNode* constructor = nullptr;
+     for (ParseNode* mn : classMethods->contents()) {
+         ClassMethod& method = mn->as<ClassMethod>();
+         ParseNode& methodName = method.name();
+         if (!method.isStatic() &&
+             (methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
+              methodName.isKind(ParseNodeKind::String)) &&
+-            methodName.pn_atom == cx->names().constructor)
++            methodName.as<NameNode>().atom() == cx->names().constructor)
+         {
+             constructor = &method.method();
+             break;
+         }
+     }
+ 
+     bool savedStrictness = sc->setLocalStrictMode(true);
+ 
+@@ -8804,33 +8822,33 @@ BytecodeEmitter::emitClass(ClassNode* cl
+ 
+     // Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
+     // is not used, an implicit value of %FunctionPrototype% is implied.
+ 
+     if (constructor) {
+         if (!emitFunction(constructor, !!heritageExpression)) { // ... HOMEOBJ CONSTRUCTOR
+             return false;
+         }
+-        if (constructor->pn_funbox->needsHomeObject()) {
++        if (constructor->funbox()->needsHomeObject()) {
+             if (!emit2(JSOP_INITHOMEOBJECT, 0)) {               // ... HOMEOBJ CONSTRUCTOR
+                 return false;
+             }
+         }
+     } else {
+         // In the case of default class constructors, emit the start and end
+         // offsets in the source buffer as source notes so that when we
+         // actually make the constructor during execution, we can give it the
+         // correct toString output.
+         ptrdiff_t classStart = ptrdiff_t(classNode->pn_pos.begin);
+         ptrdiff_t classEnd = ptrdiff_t(classNode->pn_pos.end);
+         if (!newSrcNote3(SRC_CLASS_SPAN, classStart, classEnd)) {
+             return false;
+         }
+ 
+-        JSAtom *name = names ? names->innerBinding()->pn_atom : cx->names().empty;
++        JSAtom *name = names ? names->innerBinding()->as<NameNode>().atom() : cx->names().empty;
+         if (heritageExpression) {
+             if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR)) {   // ... HOMEOBJ CONSTRUCTOR
+                 return false;
+             }
+         } else {
+             if (!emitAtomOp(name, JSOP_CLASSCONSTRUCTOR)) {     // ... HOMEOBJ CONSTRUCTOR
+                 return false;
+             }
+@@ -8938,17 +8956,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+     if (emitLineNote == EMIT_LINENOTE && !ParseNodeRequiresSpecialLineNumberNotes(pn)) {
+         if (!updateLineNumberNotes(pn->pn_pos.begin)) {
+             return false;
+         }
+     }
+ 
+     switch (pn->getKind()) {
+       case ParseNodeKind::Function:
+-        if (!emitFunction(pn)) {
++        if (!emitFunction(&pn->as<CodeNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::ParamsBody:
+         if (!emitFunctionFormalParametersAndBody(&pn->as<ListNode>())) {
+             return false;
+         }
+@@ -9324,23 +9342,23 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+       case ParseNodeKind::TemplateStringList:
+         if (!emitTemplateString(&pn->as<ListNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::TemplateString:
+       case ParseNodeKind::String:
+-        if (!emitAtomOp(pn, JSOP_STRING)) {
++        if (!emitAtomOp(&pn->as<NameNode>(), JSOP_STRING)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Number:
+-        if (!emitNumberOp(pn->pn_dval)) {
++        if (!emitNumberOp(pn->as<NumericLiteral>().value())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::RegExp:
+         if (!emitRegExp(objectList.add(pn->as<RegExpLiteral>().objbox()))) {
+             return false;
+         }
+diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
+--- a/js/src/frontend/BytecodeEmitter.h
++++ b/js/src/frontend/BytecodeEmitter.h
+@@ -475,17 +475,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     // encompasses the entire source.
+     MOZ_MUST_USE bool emitScript(ParseNode* body);
+ 
+     // Emit function code for the tree rooted at body.
+     enum class TopLevelFunction {
+         No,
+         Yes
+     };
+-    MOZ_MUST_USE bool emitFunctionScript(ParseNode* fn, TopLevelFunction isTopLevel);
++    MOZ_MUST_USE bool emitFunctionScript(CodeNode* funNode, TopLevelFunction isTopLevel);
+ 
+     // If op is JOF_TYPESET (see the type barriers comment in TypeInference.h),
+     // reserve a type set to store its result.
+     void checkTypeSet(JSOp op);
+ 
+     void updateDepth(ptrdiff_t target);
+     MOZ_MUST_USE bool updateLineNumberNotes(uint32_t offset);
+     MOZ_MUST_USE bool updateSourceCoordNotes(uint32_t offset);
+@@ -556,28 +556,28 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+ 
+     MOZ_MUST_USE bool emitGoto(NestableControl* target, JumpList* jumplist,
+                                SrcNoteType noteType = SRC_NULL);
+ 
+     MOZ_MUST_USE bool emitIndex32(JSOp op, uint32_t index);
+     MOZ_MUST_USE bool emitIndexOp(JSOp op, uint32_t index);
+ 
+     MOZ_MUST_USE bool emitAtomOp(JSAtom* atom, JSOp op);
+-    MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op);
++    MOZ_MUST_USE bool emitAtomOp(NameNode* nameNode, JSOp op);
+ 
+     MOZ_MUST_USE bool emitArrayLiteral(ListNode* array);
+     MOZ_MUST_USE bool emitArray(ParseNode* arrayHead, uint32_t count);
+ 
+     MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
+     MOZ_MUST_USE bool emitInternedObjectOp(uint32_t index, JSOp op);
+     MOZ_MUST_USE bool emitObjectOp(ObjectBox* objbox, JSOp op);
+     MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
+     MOZ_MUST_USE bool emitRegExp(uint32_t index);
+ 
+-    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false);
++    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(CodeNode* funNode, bool needsProto = false);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ListNode* objNode);
+ 
+     MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
+ 
+     MOZ_MUST_USE bool emitHoistedFunctionsInList(ListNode* stmtList);
+ 
+     MOZ_MUST_USE bool emitPropertyList(ListNode* obj, MutableHandlePlainObject objp,
+                                        PropListType type);
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -90,17 +90,17 @@ ContainsHoistedDeclaration(JSContext* cx
+         return true;
+ 
+       // Function declarations *can* be hoisted declarations.  But in the
+       // magical world of the rewritten frontend, the declaration necessitated
+       // by a nested function statement, not at body level, doesn't require
+       // that we preserve an unreachable function declaration node against
+       // dead-code removal.
+       case ParseNodeKind::Function:
+-        MOZ_ASSERT(node->isArity(PN_CODE));
++        MOZ_ASSERT(node->is<CodeNode>());
+         *result = false;
+         return true;
+ 
+       case ParseNodeKind::Module:
+         *result = false;
+         return true;
+ 
+       // Statements with no sub-components at all.
+@@ -151,17 +151,17 @@ ContainsHoistedDeclaration(JSContext* cx
+       // Statements possibly containing hoistable declarations only in the
+       // right half, in ParseNode terms -- the loop body or nested statement
+       // (usually a block statement), in AST terms.
+       case ParseNodeKind::While:
+       case ParseNodeKind::With:
+         return ContainsHoistedDeclaration(cx, node->as<BinaryNode>().right(), result);
+ 
+       case ParseNodeKind::Label:
+-        return ContainsHoistedDeclaration(cx, node->pn_expr, result);
++        return ContainsHoistedDeclaration(cx, node->as<NameNode>().expression(), result);
+ 
+       // Statements with more complicated structures.
+ 
+       // if-statement nodes may have hoisted declarations in their consequent
+       // and alternative components.
+       case ParseNodeKind::If: {
+         TernaryNode* ifNode = &node->as<TernaryNode>();
+         ParseNode* consequent = ifNode->kid2();
+@@ -272,24 +272,24 @@ ContainsHoistedDeclaration(JSContext* cx
+         }
+ 
+         ParseNode* loopBody = forNode->body();
+         return ContainsHoistedDeclaration(cx, loopBody, result);
+       }
+ 
+       case ParseNodeKind::LexicalScope: {
+         MOZ_ASSERT(node->isArity(PN_SCOPE));
+-        ParseNode* expr = node->pn_expr;
++        ParseNode* expr = node->scopeBody();
+ 
+         if (expr->isKind(ParseNodeKind::For) || expr->isKind(ParseNodeKind::Function)) {
+             return ContainsHoistedDeclaration(cx, expr, result);
+         }
+ 
+         MOZ_ASSERT(expr->isKind(ParseNodeKind::StatementList));
+-        return ListContainsHoistedDeclaration(cx, &node->pn_expr->as<ListNode>(), result);
++        return ListContainsHoistedDeclaration(cx, &node->scopeBody()->as<ListNode>(), result);
+       }
+ 
+       // List nodes with all non-null children.
+       case ParseNodeKind::StatementList:
+         return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result);
+ 
+       // Grammar sub-components that should never be reached directly by this
+       // method, because some parent component should have asserted itself.
+@@ -413,33 +413,36 @@ ContainsHoistedDeclaration(JSContext* cx
+ static bool
+ FoldType(JSContext* cx, ParseNode* pn, ParseNodeKind kind)
+ {
+     if (!pn->isKind(kind)) {
+         switch (kind) {
+           case ParseNodeKind::Number:
+             if (pn->isKind(ParseNodeKind::String)) {
+                 double d;
+-                if (!StringToNumber(cx, pn->pn_atom, &d)) {
++                if (!StringToNumber(cx, pn->as<NameNode>().atom(), &d)) {
+                     return false;
+                 }
+-                pn->pn_dval = d;
+                 pn->setKind(ParseNodeKind::Number);
++                pn->setArity(PN_NUMBER);
+                 pn->setOp(JSOP_DOUBLE);
++                pn->as<NumericLiteral>().setValue(d);
+             }
+             break;
+ 
+           case ParseNodeKind::String:
+             if (pn->isKind(ParseNodeKind::Number)) {
+-                pn->pn_atom = NumberToAtom(cx, pn->pn_dval);
+-                if (!pn->pn_atom) {
++                JSAtom* atom = NumberToAtom(cx, pn->as<NumericLiteral>().value());
++                if (!atom) {
+                     return false;
+                 }
+                 pn->setKind(ParseNodeKind::String);
++                pn->setArity(PN_NAME);
+                 pn->setOp(JSOP_STRING);
++                pn->as<NameNode>().setAtom(atom);
+             }
+             break;
+ 
+           default:;
+         }
+     }
+     return true;
+ }
+@@ -473,21 +476,21 @@ IsEffectless(ParseNode* node)
+ 
+ enum Truthiness { Truthy, Falsy, Unknown };
+ 
+ static Truthiness
+ Boolish(ParseNode* pn)
+ {
+     switch (pn->getKind()) {
+       case ParseNodeKind::Number:
+-        return (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)) ? Truthy : Falsy;
++        return (pn->as<NumericLiteral>().value() != 0 && !IsNaN(pn->as<NumericLiteral>().value())) ? Truthy : Falsy;
+ 
+       case ParseNodeKind::String:
+       case ParseNodeKind::TemplateString:
+-        return (pn->pn_atom->length() > 0) ? Truthy : Falsy;
++        return (pn->as<NameNode>().atom()->length() > 0) ? Truthy : Falsy;
+ 
+       case ParseNodeKind::True:
+       case ParseNodeKind::Function:
+         return Truthy;
+ 
+       case ParseNodeKind::False:
+       case ParseNodeKind::Null:
+       case ParseNodeKind::RawUndefined:
+@@ -567,19 +570,19 @@ FoldTypeOfExpr(JSContext* cx, UnaryNode*
+     } else if (expr->isKind(ParseNodeKind::True) || expr->isKind(ParseNodeKind::False)) {
+         result = cx->names().boolean;
+     } else if (expr->isKind(ParseNodeKind::Function)) {
+         result = cx->names().function;
+     }
+ 
+     if (result) {
+         node->setKind(ParseNodeKind::String);
+-        node->setArity(PN_NULLARY);
++        node->setArity(PN_NAME);
+         node->setOp(JSOP_NOP);
+-        node->pn_atom = result;
++        node->as<NameNode>().setAtom(result);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldDeleteExpr(JSContext* cx, UnaryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+@@ -655,17 +658,17 @@ FoldNot(JSContext* cx, UnaryNode* node, 
+ 
+     if (!FoldCondition(cx, node->unsafeKidReference(), parser)) {
+         return false;
+     }
+ 
+     ParseNode* expr = node->kid();
+ 
+     if (expr->isKind(ParseNodeKind::Number)) {
+-        double d = expr->pn_dval;
++        double d = expr->as<NumericLiteral>().value();
+ 
+         if (d == 0 || IsNaN(d)) {
+             node->setKind(ParseNodeKind::True);
+             node->setOp(JSOP_TRUE);
+         } else {
+             node->setKind(ParseNodeKind::False);
+             node->setOp(JSOP_FALSE);
+         }
+@@ -695,31 +698,31 @@ FoldUnaryArithmetic(JSContext* cx, Unary
+ 
+     ParseNode* expr = node->kid();
+ 
+     if (expr->isKind(ParseNodeKind::Number) ||
+         expr->isKind(ParseNodeKind::True) ||
+         expr->isKind(ParseNodeKind::False))
+     {
+         double d = expr->isKind(ParseNodeKind::Number)
+-                   ? expr->pn_dval
++                   ? expr->as<NumericLiteral>().value()
+                    : double(expr->isKind(ParseNodeKind::True));
+ 
+         if (node->isKind(ParseNodeKind::BitNot)) {
+             d = ~ToInt32(d);
+         } else if (node->isKind(ParseNodeKind::Neg)) {
+             d = -d;
+         } else {
+             MOZ_ASSERT(node->isKind(ParseNodeKind::Pos)); // nothing to do
+         }
+ 
+         node->setKind(ParseNodeKind::Number);
++        node->setArity(PN_NUMBER);
+         node->setOp(JSOP_DOUBLE);
+-        node->setArity(PN_NULLARY);
+-        node->pn_dval = d;
++        node->as<NumericLiteral>().setValue(d);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldIncrementDecrement(JSContext* cx, UnaryNode* incDec,
+                        PerHandlerParser<FullParseHandler>& parser)
+@@ -969,30 +972,29 @@ FoldIf(JSContext* cx, ParseNode** nodePt
+             ReplaceNode(nodePtr, replacement);
+         }
+     } while (nextNode);
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldFunction(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldFunction(JSContext* cx, CodeNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Function));
+-    MOZ_ASSERT(node->isArity(PN_CODE));
+ 
+     // Don't constant-fold inside "use asm" code, as this could create a parse
+     // tree that doesn't type-check as asm.js.
+-    if (node->pn_funbox->useAsmOrInsideUseAsm()) {
++    if (node->funbox()->useAsmOrInsideUseAsm()) {
+         return true;
+     }
+ 
+-    // Note: pn_body is null for lazily-parsed functions.
+-    if (ParseNode*& functionBody = node->pn_body) {
+-        if (!Fold(cx, &functionBody, parser)) {
++    // Note: body is null for lazily-parsed functions.
++    if (node->body()) {
++        if (!Fold(cx, node->unsafeBodyReference(), parser)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static double
+@@ -1025,24 +1027,22 @@ ComputeBinary(ParseNodeKind kind, double
+     MOZ_ASSERT(kind == ParseNodeKind::Lsh || kind == ParseNodeKind::Rsh);
+ 
+     int32_t i = ToInt32(left);
+     uint32_t j = ToUint32(right) & 31;
+     return int32_t((kind == ParseNodeKind::Lsh) ? uint32_t(i) << j : i >> j);
+ }
+ 
+ static bool
+-FoldModule(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldModule(JSContext* cx, CodeNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Module));
+-    MOZ_ASSERT(node->isArity(PN_CODE));
+ 
+-    ParseNode*& moduleBody = node->pn_body;
+-    MOZ_ASSERT(moduleBody);
+-    return Fold(cx, &moduleBody, parser);
++    MOZ_ASSERT(node->body());
++    return Fold(cx, node->unsafeBodyReference(), parser);
+ }
+ 
+ static bool
+ FoldBinaryArithmetic(JSContext* cx, ListNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(node->isKind(ParseNodeKind::Sub) ||
+                node->isKind(ParseNodeKind::Star) ||
+                node->isKind(ParseNodeKind::Lsh) ||
+@@ -1075,38 +1075,38 @@ FoldBinaryArithmetic(JSContext* cx, List
+     ParseNode* next = elem->pn_next;
+     if (elem->isKind(ParseNodeKind::Number)) {
+         ParseNodeKind kind = node->getKind();
+         while (true) {
+             if (!next || !next->isKind(ParseNodeKind::Number)) {
+                 break;
+             }
+ 
+-            double d = ComputeBinary(kind, elem->pn_dval, next->pn_dval);
++            double d = ComputeBinary(kind, elem->as<NumericLiteral>().value(), next->as<NumericLiteral>().value());
+ 
+             next = next->pn_next;
+             elem->pn_next = next;
+ 
+             elem->setKind(ParseNodeKind::Number);
++            elem->setArity(PN_NUMBER);
+             elem->setOp(JSOP_DOUBLE);
+-            elem->setArity(PN_NULLARY);
+-            elem->pn_dval = d;
++            elem->as<NumericLiteral>().setValue(d);
+ 
+             node->unsafeDecrementCount();
+         }
+ 
+         if (node->count() == 1) {
+             MOZ_ASSERT(node->head() == elem);
+             MOZ_ASSERT(elem->isKind(ParseNodeKind::Number));
+ 
+-            double d = elem->pn_dval;
++            double d = elem->as<NumericLiteral>().value();
+             node->setKind(ParseNodeKind::Number);
+-            node->setArity(PN_NULLARY);
++            node->setArity(PN_NUMBER);
+             node->setOp(JSOP_DOUBLE);
+-            node->pn_dval = d;
++            node->as<NumericLiteral>().setValue(d);
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+ FoldExponentiation(JSContext* cx, ListNode* node, PerHandlerParser<FullParseHandler>& parser)
+@@ -1138,22 +1138,23 @@ FoldExponentiation(JSContext* cx, ListNo
+     }
+ 
+     ParseNode* base = node->head();
+     ParseNode* exponent = base->pn_next;
+     if (!base->isKind(ParseNodeKind::Number) || !exponent->isKind(ParseNodeKind::Number)) {
+         return true;
+     }
+ 
+-    double d1 = base->pn_dval, d2 = exponent->pn_dval;
++    double d1 = base->as<NumericLiteral>().value();
++    double d2 = exponent->as<NumericLiteral>().value();
+ 
+     node->setKind(ParseNodeKind::Number);
+-    node->setArity(PN_NULLARY);
++    node->setArity(PN_NUMBER);
+     node->setOp(JSOP_DOUBLE);
+-    node->pn_dval = ecmaPow(d1, d2);
++    node->as<NumericLiteral>().setValue(ecmaPow(d1, d2));
+     return true;
+ }
+ 
+ static bool
+ FoldList(JSContext* cx, ListNode* list, PerHandlerParser<FullParseHandler>& parser)
+ {
+     ParseNode** elem = list->unsafeHeadReference();
+     for (; *elem; elem = &(*elem)->pn_next) {
+@@ -1263,30 +1264,31 @@ FoldElement(JSContext* cx, ParseNode** n
+     if (!Fold(cx, elem->unsafeRightReference(), parser)) {
+         return false;
+     }
+ 
+     ParseNode* expr = &elem->expression();
+     ParseNode* key = &elem->key();
+     PropertyName* name = nullptr;
+     if (key->isKind(ParseNodeKind::String)) {
+-        JSAtom* atom = key->pn_atom;
++        JSAtom* atom = key->as<NameNode>().atom();
+         uint32_t index;
+ 
+         if (atom->isIndex(&index)) {
+             // Optimization 1: We have something like expr["100"]. This is
+             // equivalent to expr[100] which is faster.
+             key->setKind(ParseNodeKind::Number);
++            key->setArity(PN_NUMBER);
+             key->setOp(JSOP_DOUBLE);
+-            key->pn_dval = index;
++            key->as<NumericLiteral>().setValue(index);
+         } else {
+             name = atom->asPropertyName();
+         }
+     } else if (key->isKind(ParseNodeKind::Number)) {
+-        double number = key->pn_dval;
++        double number = key->as<NumericLiteral>().value();
+         if (number != ToUint32(number)) {
+             // Optimization 2: We have something like expr[3.14]. The number
+             // isn't an array index, so it converts to a string ("3.14"),
+             // enabling optimization 3 below.
+             JSAtom* atom = NumberToAtom(cx, number);
+             if (!atom) {
+                 return false;
+             }
+@@ -1296,17 +1298,17 @@ FoldElement(JSContext* cx, ParseNode** n
+ 
+     // If we don't have a name, we can't optimize to getprop.
+     if (!name) {
+         return true;
+     }
+ 
+     // Optimization 3: We have expr["foo"] where foo is not an index.  Convert
+     // to a property access (like expr.foo) that optimizes better downstream.
+-    ParseNode* nameNode = parser.newPropertyName(name, key->pn_pos);
++    NameNode* nameNode = parser.newPropertyName(name, key->pn_pos);
+     if (!nameNode) {
+         return false;
+     }
+     ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode);
+     if (!dottedAccess) {
+         return false;
+     }
+     dottedAccess->setInParens(elem->isInParens());
+@@ -1337,17 +1339,19 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
+     ParseNode* current = node->head();
+     ParseNode* next = current->pn_next;
+     if (current->isKind(ParseNodeKind::Number)) {
+         do {
+             if (!next->isKind(ParseNodeKind::Number)) {
+                 break;
+             }
+ 
+-            current->pn_dval += next->pn_dval;
++            NumericLiteral* num = &current->as<NumericLiteral>();
++
++            num->setValue(num->value() + next->as<NumericLiteral>().value());
+             current->pn_next = next->pn_next;
+             next = current->pn_next;
+ 
+             node->unsafeDecrementCount();
+         } while (next);
+     }
+ 
+     // If any operands remain, attempt string concatenation folding.
+@@ -1385,31 +1389,31 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
+         RootedString combination(cx);
+         RootedString tmp(cx);
+         do {
+             // Create a rope of the current string and all succeeding
+             // constants that we can convert to strings, then atomize it
+             // and replace them all with that fresh string.
+             MOZ_ASSERT(current->isKind(ParseNodeKind::String));
+ 
+-            combination = current->pn_atom;
++            combination = current->as<NameNode>().atom();
+ 
+             do {
+                 // Try folding the next operand to a string.
+                 if (!FoldType(cx, next, ParseNodeKind::String)) {
+                     return false;
+                 }
+ 
+                 // Stop glomming once folding doesn't produce a string.
+                 if (!next->isKind(ParseNodeKind::String)) {
+                     break;
+                 }
+ 
+                 // Add this string to the combination and remove the node.
+-                tmp = next->pn_atom;
++                tmp = next->as<NameNode>().atom();
+                 combination = ConcatStrings<CanGC>(cx, combination, tmp);
+                 if (!combination) {
+                     return false;
+                 }
+ 
+                 next = next->pn_next;
+                 current->pn_next = next;
+ 
+@@ -1417,17 +1421,17 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
+             } while (next);
+ 
+             // Replace |current|'s string with the entire combination.
+             MOZ_ASSERT(current->isKind(ParseNodeKind::String));
+             combination = AtomizeString(cx, combination);
+             if (!combination) {
+                 return false;
+             }
+-            current->pn_atom = &combination->asAtom();
++            current->as<NameNode>().setAtom(&combination->asAtom());
+ 
+ 
+             // If we're out of nodes, we're done.
+             if (!next) {
+                 break;
+             }
+ 
+             current = next;
+@@ -1568,64 +1572,73 @@ FoldDottedProperty(JSContext* cx, Proper
+     while ((*nested)->isKind(ParseNodeKind::Dot)) {
+         nested = (*nested)->as<PropertyAccess>().unsafeLeftReference();
+     }
+ 
+     return Fold(cx, nested, parser);
+ }
+ 
+ static bool
+-FoldName(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldName(JSContext* cx, NameNode* nameNode, PerHandlerParser<FullParseHandler>& parser)
+ {
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::Name));
+-    MOZ_ASSERT(node->isArity(PN_NAME));
++    MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
+ 
+-    if (!node->pn_expr) {
++    if (!nameNode->expression()) {
+         return true;
+     }
+ 
+-    return Fold(cx, &node->pn_expr, parser);
++    return Fold(cx, nameNode->unsafeExpressionReference(), parser);
+ }
+ 
+ bool
+ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
+ {
+     if (!CheckRecursionLimit(cx)) {
+         return false;
+     }
+ 
+     ParseNode* pn = *pnp;
+ 
+     switch (pn->getKind()) {
+       case ParseNodeKind::EmptyStatement:
+-      case ParseNodeKind::RegExp:
+-      case ParseNodeKind::String:
+       case ParseNodeKind::True:
+       case ParseNodeKind::False:
+       case ParseNodeKind::Null:
+       case ParseNodeKind::RawUndefined:
+       case ParseNodeKind::Elision:
+-      case ParseNodeKind::Number:
+       case ParseNodeKind::Debugger:
+       case ParseNodeKind::Break:
+       case ParseNodeKind::Continue:
+-      case ParseNodeKind::TemplateString:
+       case ParseNodeKind::Generator:
+       case ParseNodeKind::ExportBatchSpec:
+-      case ParseNodeKind::ObjectPropertyName:
+       case ParseNodeKind::PosHolder:
+         MOZ_ASSERT(pn->isArity(PN_NULLARY));
+         return true;
+ 
++      case ParseNodeKind::ObjectPropertyName:
++      case ParseNodeKind::String:
++      case ParseNodeKind::TemplateString:
++        MOZ_ASSERT(pn->is<NameNode>());
++        return true;
++
++      case ParseNodeKind::RegExp:
++        MOZ_ASSERT(pn->is<RegExpLiteral>());
++        return true;
++
++      case ParseNodeKind::Number:
++        MOZ_ASSERT(pn->is<NumericLiteral>());
++        return true;
++
+       case ParseNodeKind::SuperBase:
+       case ParseNodeKind::TypeOfName: {
+ #ifdef DEBUG
+         UnaryNode* node = &pn->as<UnaryNode>();
+-        MOZ_ASSERT(node->kid()->isKind(ParseNodeKind::Name));
+-        MOZ_ASSERT(!node->kid()->expr());
++        NameNode* nameNode = &node->kid()->as<NameNode>();
++        MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
++        MOZ_ASSERT(!nameNode->expression());
+ #endif
+         return true;
+       }
+ 
+       case ParseNodeKind::TypeOfExpr:
+         return FoldTypeOfExpr(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::DeleteName:
+@@ -1688,20 +1701,20 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+         return true;
+ 
+       case ParseNodeKind::And:
+       case ParseNodeKind::Or:
+         MOZ_ASSERT((*pnp)->is<ListNode>());
+         return FoldAndOr(cx, pnp, parser);
+ 
+       case ParseNodeKind::Function:
+-        return FoldFunction(cx, pn, parser);
++        return FoldFunction(cx, &pn->as<CodeNode>(), parser);
+ 
+       case ParseNodeKind::Module:
+-        return FoldModule(cx, pn, parser);
++        return FoldModule(cx, &pn->as<CodeNode>(), parser);
+ 
+       case ParseNodeKind::Sub:
+       case ParseNodeKind::Star:
+       case ParseNodeKind::Lsh:
+       case ParseNodeKind::Rsh:
+       case ParseNodeKind::Ursh:
+       case ParseNodeKind::Div:
+       case ParseNodeKind::Mod:
+@@ -1878,34 +1891,33 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+       case ParseNodeKind::ForIn:
+       case ParseNodeKind::ForOf:
+         return FoldForInOrOf(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::ForHead:
+         return FoldForHead(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::Label:
+-        MOZ_ASSERT(pn->isArity(PN_NAME));
+-        return Fold(cx, &pn->pn_expr, parser);
++        return Fold(cx, pn->as<NameNode>().unsafeExpressionReference(), parser);
+ 
+       case ParseNodeKind::PropertyName:
+         MOZ_CRASH("unreachable, handled by ::Dot");
+ 
+       case ParseNodeKind::Dot:
+         return FoldDottedProperty(cx, &pn->as<PropertyAccess>(), parser);
+ 
+       case ParseNodeKind::LexicalScope:
+         MOZ_ASSERT(pn->isArity(PN_SCOPE));
+         if (!pn->scopeBody()) {
+             return true;
+         }
+         return Fold(cx, &pn->pn_u.scope.body, parser);
+ 
+       case ParseNodeKind::Name:
+-        return FoldName(cx, pn, parser);
++        return FoldName(cx, &pn->as<NameNode>(), parser);
+ 
+       case ParseNodeKind::Limit: // invalid sentinel value
+         MOZ_CRASH("invalid node kind");
+     }
+ 
+     MOZ_CRASH("shouldn't reach here");
+     return false;
+ }
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -109,49 +109,43 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     // The FullParseHandler may be used to create nodes for text sources
+     // (from Parser.h) or for binary sources (from BinSource.h). In the latter
+     // case, some common assumptions on offsets are incorrect, e.g. in `a + b`,
+     // `a`, `b` and `+` may be stored in any order. We use `sourceKind()`
+     // to determine whether we need to check these assumptions.
+     SourceKind sourceKind() const { return sourceKind_; }
+ 
+-    ParseNode* newName(PropertyName* name, const TokenPos& pos, JSContext* cx)
+-    {
++    NameNodeType newName(PropertyName* name, const TokenPos& pos, JSContext* cx) {
+         return new_<NameNode>(ParseNodeKind::Name, JSOP_GETNAME, name, pos);
+     }
+ 
+     UnaryNodeType newComputedName(Node expr, uint32_t begin, uint32_t end) {
+         TokenPos pos(begin, end);
+         return new_<UnaryNode>(ParseNodeKind::ComputedName, pos, expr);
+     }
+ 
+-    ParseNode* newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
+-        return new_<NullaryNode>(ParseNodeKind::ObjectPropertyName, JSOP_NOP, pos, atom);
++    NameNodeType newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
++        return new_<NameNode>(ParseNodeKind::ObjectPropertyName, JSOP_NOP, atom, pos);
+     }
+ 
+-    ParseNode* newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) {
+-        ParseNode* pn = new_<NullaryNode>(ParseNodeKind::Number, pos);
+-        if (!pn) {
+-            return nullptr;
+-        }
+-        pn->initNumber(value, decimalPoint);
+-        return pn;
++    NumericLiteralType newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) {
++        return new_<NumericLiteral>(value, decimalPoint, pos);
+     }
+ 
+     ParseNode* newBooleanLiteral(bool cond, const TokenPos& pos) {
+         return new_<BooleanLiteral>(cond, pos);
+     }
+ 
+-    ParseNode* newStringLiteral(JSAtom* atom, const TokenPos& pos) {
+-        return new_<NullaryNode>(ParseNodeKind::String, JSOP_NOP, pos, atom);
++    NameNodeType newStringLiteral(JSAtom* atom, const TokenPos& pos) {
++        return new_<NameNode>(ParseNodeKind::String, JSOP_NOP, atom, pos);
+     }
+ 
+-    ParseNode* newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
+-        return new_<NullaryNode>(ParseNodeKind::TemplateString, JSOP_NOP, pos, atom);
++    NameNodeType newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
++        return new_<NameNode>(ParseNodeKind::TemplateString, JSOP_NOP, atom, pos);
+     }
+ 
+     CallSiteNodeType newCallSiteObject(uint32_t begin) {
+         CallSiteNode* callSiteObj = new_<CallSiteNode>(begin);
+         if (!callSiteObj) {
+             return null();
+         }
+ 
+@@ -190,17 +184,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     ParseNode* newRawUndefinedLiteral(const TokenPos& pos) {
+         return new_<RawUndefinedLiteral>(pos);
+     }
+ 
+     // The Boxer object here is any object that can allocate ObjectBoxes.
+     // Specifically, a Boxer has a .newObjectBox(T) method that accepts a
+     // Rooted<RegExpObject*> argument and returns an ObjectBox*.
+     template <class Boxer>
+-    ParseNode* newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) {
++    RegExpLiteralType newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) {
+         ObjectBox* objbox = boxer.newObjectBox(reobj);
+         if (!objbox) {
+             return null();
+         }
+         return new_<RegExpLiteral>(objbox, pos);
+     }
+ 
+     ConditionalExpressionType newConditional(Node cond, Node thenExpr, Node elseExpr) {
+@@ -372,21 +366,21 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         BinaryNode* propdef = newPropertyDefinition(key, val);
+         if (!propdef) {
+             return false;
+         }
+         addPropertyDefinition(literal, propdef);
+         return true;
+     }
+ 
+-    MOZ_MUST_USE bool addShorthand(ListNodeType literal, Node name, Node expr) {
++    MOZ_MUST_USE bool addShorthand(ListNodeType literal, NameNodeType name, NameNodeType expr) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+         MOZ_ASSERT(name->isKind(ParseNodeKind::ObjectPropertyName));
+         MOZ_ASSERT(expr->isKind(ParseNodeKind::Name));
+-        MOZ_ASSERT(name->pn_atom == expr->pn_atom);
++        MOZ_ASSERT(name->atom() == expr->atom());
+ 
+         literal->setHasNonConstInitializer();
+         BinaryNode* propdef = newBinary(ParseNodeKind::Shorthand, name, expr, JSOP_INITPROP);
+         if (!propdef) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ propdef);
+         return true;
+@@ -400,40 +394,42 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         if (!spread) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ spread);
+         return true;
+     }
+ 
+     MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key,
+-                                                Node fn, AccessorType atype)
++                                                CodeNodeType funNode, AccessorType atype)
+     {
+         literal->setHasNonConstInitializer();
+ 
+-        checkAndSetIsDirectRHSAnonFunction(fn);
++        checkAndSetIsDirectRHSAnonFunction(funNode);
+ 
+-        ParseNode* propdef = newObjectMethodOrPropertyDefinition(key, fn, atype);
++        ParseNode* propdef = newObjectMethodOrPropertyDefinition(key, funNode, atype);
+         if (!propdef) {
+             return false;
+         }
+ 
+         addList(/* list = */ literal, /* child = */ propdef);
+         return true;
+     }
+ 
+     MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key,
+-                                               Node fn, AccessorType atype, bool isStatic)
++                                               CodeNodeType funNode, AccessorType atype,
++                                               bool isStatic)
+     {
+         MOZ_ASSERT(methodList->isKind(ParseNodeKind::ClassMethodList));
+         MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+ 
+-        checkAndSetIsDirectRHSAnonFunction(fn);
++        checkAndSetIsDirectRHSAnonFunction(funNode);
+ 
+-        ClassMethod* classMethod = new_<ClassMethod>(key, fn, AccessorTypeToJSOp(atype), isStatic);
++        ClassMethod* classMethod = new_<ClassMethod>(key, funNode, AccessorTypeToJSOp(atype),
++                                                     isStatic);
+         if (!classMethod) {
+             return false;
+         }
+         addList(/* list = */ methodList, /* child = */ classMethod);
+         return true;
+     }
+ 
+     UnaryNodeType newInitialYieldExpression(uint32_t begin, Node gen) {
+@@ -650,17 +646,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         return new_<UnaryNode>(ParseNodeKind::Return, expr->pn_pos, expr);
+     }
+ 
+     BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) {
+         return new_<BinaryNode>(ParseNodeKind::With, JSOP_NOP, TokenPos(begin, body->pn_pos.end),
+                                 expr, body);
+     }
+ 
+-    ParseNode* newLabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin) {
++    LabeledStatementType newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
+         return new_<LabeledStatement>(label, stmt, begin);
+     }
+ 
+     UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) {
+         MOZ_ASSERT(pos.encloses(expr->pn_pos));
+         return new_<UnaryNode>(ParseNodeKind::Throw, pos, expr);
+     }
+ 
+@@ -670,21 +666,21 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
+         return new_<TernaryNode>(ParseNodeKind::Try, body, catchScope, finallyBlock, pos);
+     }
+ 
+     ParseNode* newDebuggerStatement(const TokenPos& pos) {
+         return new_<DebuggerStatement>(pos);
+     }
+ 
+-    ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) {
++    NameNodeType newPropertyName(PropertyName* name, const TokenPos& pos) {
+         return new_<NameNode>(ParseNodeKind::PropertyName, JSOP_NOP, name, pos);
+     }
+ 
+-    PropertyAccessType newPropertyAccess(Node expr, Node key) {
++    PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
+         return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end);
+     }
+ 
+     PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) {
+         return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
+     }
+ 
+     bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) {
+@@ -697,63 +693,63 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         }
+         if (!catchClause) {
+             return false;
+         }
+         lexicalScope->setScopeBody(catchClause);
+         return true;
+     }
+ 
+-    inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(ParseNode* funcpn,
+-                                                                   ParseNode* pn);
++    inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(CodeNodeType funNode,
++                                                                   Node defaultValue);
+ 
+   private:
+     void checkAndSetIsDirectRHSAnonFunction(ParseNode* pn) {
+         if (IsAnonymousFunctionDefinition(pn)) {
+             pn->setDirectRHSAnonFunction(true);
+         }
+     }
+ 
+   public:
+-    ParseNode* newFunctionStatement(const TokenPos& pos) {
++    CodeNodeType newFunctionStatement(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Function, JSOP_NOP, pos);
+     }
+ 
+-    ParseNode* newFunctionExpression(const TokenPos& pos) {
++    CodeNodeType newFunctionExpression(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Function, JSOP_LAMBDA, pos);
+     }
+ 
+-    ParseNode* newArrowFunction(const TokenPos& pos) {
++    CodeNodeType newArrowFunction(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Function, JSOP_LAMBDA_ARROW, pos);
+     }
+ 
+-    BinaryNodeType newObjectMethodOrPropertyDefinition(Node key, Node fn, AccessorType atype) {
++    BinaryNodeType newObjectMethodOrPropertyDefinition(Node key, Node value, AccessorType atype) {
+         MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+ 
+-        return newBinary(ParseNodeKind::Colon, key, fn, AccessorTypeToJSOp(atype));
++        return newBinary(ParseNodeKind::Colon, key, value, AccessorTypeToJSOp(atype));
+     }
+ 
+-    void setFunctionFormalParametersAndBody(ParseNode* funcNode, ParseNode* kid) {
+-        MOZ_ASSERT_IF(kid, kid->isKind(ParseNodeKind::ParamsBody));
+-        funcNode->pn_body = kid;
++    void setFunctionFormalParametersAndBody(CodeNodeType funNode, ListNodeType paramsBody) {
++        MOZ_ASSERT_IF(paramsBody, paramsBody->isKind(ParseNodeKind::ParamsBody));
++        funNode->setBody(paramsBody);
+     }
+-    void setFunctionBox(ParseNode* pn, FunctionBox* funbox) {
+-        MOZ_ASSERT(pn->isKind(ParseNodeKind::Function));
+-        pn->pn_funbox = funbox;
+-        funbox->functionNode = pn;
++    void setFunctionBox(CodeNodeType funNode, FunctionBox* funbox) {
++        MOZ_ASSERT(funNode->isKind(ParseNodeKind::Function));
++        funNode->setFunbox(funbox);
++        funbox->functionNode = funNode;
+     }
+-    void addFunctionFormalParameter(ParseNode* pn, ParseNode* argpn) {
+-        addList(/* list = */ &pn->pn_body->as<ListNode>(), /* child = */ argpn);
++    void addFunctionFormalParameter(CodeNodeType funNode, Node argpn) {
++        addList(/* list = */ funNode->body(), /* child = */ argpn);
+     }
+-    void setFunctionBody(ParseNode* fn, ParseNode* body) {
+-        MOZ_ASSERT(fn->pn_body->isKind(ParseNodeKind::ParamsBody));
+-        addList(/* list = */ &fn->pn_body->as<ListNode>(), /* child = */ body);
++    void setFunctionBody(CodeNodeType funNode, Node body) {
++        MOZ_ASSERT(funNode->body()->isKind(ParseNodeKind::ParamsBody));
++        addList(/* list = */ funNode->body(), /* child = */ body);
+     }
+ 
+-    ParseNode* newModule(const TokenPos& pos) {
++    CodeNodeType newModule(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Module, JSOP_NOP, pos);
+     }
+ 
+     ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) {
+         return new_<LexicalScopeNode>(bindings, body);
+     }
+ 
+     BinaryNodeType newNewExpression(uint32_t begin, Node ctor, Node args) {
+@@ -817,17 +813,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     bool isUsableAsObjectPropertyName(ParseNode* node) {
+         return node->isKind(ParseNodeKind::Number)
+             || node->isKind(ParseNodeKind::ObjectPropertyName)
+             || node->isKind(ParseNodeKind::String)
+             || node->isKind(ParseNodeKind::ComputedName);
+     }
+ 
+-    inline MOZ_MUST_USE bool finishInitializerAssignment(ParseNode* pn, ParseNode* init);
++    inline MOZ_MUST_USE bool finishInitializerAssignment(NameNodeType nameNode, Node init);
+ 
+     void setBeginPosition(ParseNode* pn, ParseNode* oth) {
+         setBeginPosition(pn, oth->pn_pos.begin);
+     }
+     void setBeginPosition(ParseNode* pn, uint32_t begin) {
+         pn->pn_pos.begin = begin;
+         MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
+     }
+@@ -850,20 +846,16 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+                kind == ParseNodeKind::Const;
+     }
+ 
+     ListNodeType newList(ParseNodeKind kind, const TokenPos& pos) {
+         MOZ_ASSERT(!isDeclarationKind(kind));
+         return new_<ListNode>(kind, JSOP_NOP, pos);
+     }
+ 
+-  private:
+-    template<typename T>
+-    ParseNode* newList(ParseNodeKind kind, const T& begin) = delete;
+-
+   public:
+     ListNodeType newList(ParseNodeKind kind, Node kid) {
+         MOZ_ASSERT(!isDeclarationKind(kind));
+         return new_<ListNode>(kind, JSOP_NOP, kid);
+     }
+ 
+     ListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) {
+         MOZ_ASSERT(isDeclarationKind(kind));
+@@ -893,43 +885,47 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+ 
+     void setOp(ParseNode* pn, JSOp op) {
+         pn->setOp(op);
+     }
+     void setListHasNonConstInitializer(ListNodeType literal) {
+         literal->setHasNonConstInitializer();
+     }
+-    MOZ_MUST_USE ParseNode* parenthesize(ParseNode* pn) {
+-        pn->setInParens(true);
+-        return pn;
++    template <typename NodeType>
++    MOZ_MUST_USE NodeType parenthesize(NodeType node) {
++        node->setInParens(true);
++        return node;
+     }
+-    MOZ_MUST_USE ParseNode* setLikelyIIFE(ParseNode* pn) {
+-        return parenthesize(pn);
++    template <typename NodeType>
++    MOZ_MUST_USE NodeType setLikelyIIFE(NodeType node) {
++        return parenthesize(node);
+     }
+     void setInDirectivePrologue(UnaryNodeType exprStmt) {
+         exprStmt->setIsDirectivePrologueMember();
+     }
+ 
+     bool isName(ParseNode* node) {
+         return node->isKind(ParseNodeKind::Name);
+     }
+ 
+     bool isArgumentsName(ParseNode* node, JSContext* cx) {
+-        return node->isKind(ParseNodeKind::Name) && node->pn_atom == cx->names().arguments;
++        return node->isKind(ParseNodeKind::Name) &&
++               node->as<NameNode>().atom() == cx->names().arguments;
+     }
+ 
+     bool isEvalName(ParseNode* node, JSContext* cx) {
+-        return node->isKind(ParseNodeKind::Name) && node->pn_atom == cx->names().eval;
++        return node->isKind(ParseNodeKind::Name) &&
++               node->as<NameNode>().atom() == cx->names().eval;
+     }
+ 
+     bool isAsyncKeyword(ParseNode* node, JSContext* cx) {
+         return node->isKind(ParseNodeKind::Name) &&
+                node->pn_pos.begin + strlen("async") == node->pn_pos.end &&
+-               node->pn_atom == cx->names().async;
++               node->as<NameNode>().atom() == cx->names().async;
+     }
+ 
+     PropertyName* maybeDottedProperty(ParseNode* pn) {
+         return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name() : nullptr;
+     }
+     JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) {
+         if (pn->is<UnaryNode>()) {
+             UnaryNode* unary = &pn->as<UnaryNode>();
+@@ -957,45 +953,42 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+     JSAtom* nextLazyClosedOverBinding() {
+         MOZ_ASSERT(lazyClosedOverBindingIndex < lazyOuterFunction_->numClosedOverBindings());
+         return lazyOuterFunction_->closedOverBindings()[lazyClosedOverBindingIndex++];
+     }
+ };
+ 
+ inline bool
+-FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
+-                                                        ParseNode* defaultValue)
++FullParseHandler::setLastFunctionFormalParameterDefault(CodeNodeType funNode, Node defaultValue)
+ {
+-    MOZ_ASSERT(funcpn->isKind(ParseNodeKind::Function));
+-    MOZ_ASSERT(funcpn->isArity(PN_CODE));
+-
+-    ListNode* body = &funcpn->pn_body->as<ListNode>();
++    MOZ_ASSERT(funNode->isKind(ParseNodeKind::Function));
++    ListNode* body = funNode->body();
+     ParseNode* arg = body->last();
+     ParseNode* pn = newAssignment(ParseNodeKind::Assign, arg, defaultValue);
+     if (!pn) {
+         return false;
+     }
+ 
+     body->replaceLast(pn);
+     return true;
+ }
+ 
+ inline bool
+-FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
++FullParseHandler::finishInitializerAssignment(NameNodeType nameNode, Node init)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Name));
+-    MOZ_ASSERT(!pn->isInParens());
++    MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
++    MOZ_ASSERT(!nameNode->isInParens());
+ 
+     checkAndSetIsDirectRHSAnonFunction(init);
+ 
+-    pn->pn_expr = init;
+-    pn->setOp(JSOP_SETNAME);
++    nameNode->setExpression(init);
++    nameNode->setOp(JSOP_SETNAME);
+ 
+     /* The declarator's position must include the initializer. */
+-    pn->pn_pos.end = init->pn_pos.end;
++    nameNode->pn_pos.end = init->pn_pos.end;
+     return true;
+ }
+ 
+ } // namespace frontend
+ } // namespace js
+ 
+ #endif /* frontend_FullParseHandler_h */
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -81,22 +81,22 @@ class NameResolver
+           case ParseNodeKind::Dot: {
+             PropertyAccess* prop = &n->as<PropertyAccess>();
+             if (!nameExpression(&prop->expression(), foundName)) {
+                 return false;
+             }
+             if (!*foundName) {
+                 return true;
+             }
+-            return appendPropertyReference(prop->right()->pn_atom);
++            return appendPropertyReference(prop->right()->as<NameNode>().atom());
+           }
+ 
+           case ParseNodeKind::Name:
+             *foundName = true;
+-            return buf->append(n->pn_atom);
++            return buf->append(n->as<NameNode>().atom());
+ 
+           case ParseNodeKind::This:
+             *foundName = true;
+             return buf->append("this");
+ 
+           case ParseNodeKind::Elem: {
+             PropertyByValue* elem = &n->as<PropertyByValue>();
+             if (!nameExpression(&elem->expression(), foundName)) {
+@@ -111,17 +111,17 @@ class NameResolver
+             if (!*foundName) {
+                 return true;
+             }
+             return buf->append(']');
+           }
+ 
+           case ParseNodeKind::Number:
+             *foundName = true;
+-            return appendNumber(n->pn_dval);
++            return appendNumber(n->as<NumericLiteral>().value());
+ 
+           default:
+             /* We're confused as to what to call this function. */
+             *foundName = false;
+             return true;
+         }
+     }
+ 
+@@ -198,21 +198,20 @@ class NameResolver
+         return nullptr;
+     }
+ 
+     /*
+      * Resolve the name of a function. If the function already has a name
+      * listed, then it is skipped. Otherwise an intelligent name is guessed to
+      * assign to the function's displayAtom field.
+      */
+-    bool resolveFun(ParseNode* pn, HandleAtom prefix, MutableHandleAtom retAtom) {
+-        MOZ_ASSERT(pn != nullptr);
+-        MOZ_ASSERT(pn->isKind(ParseNodeKind::Function));
+-        MOZ_ASSERT(pn->isArity(PN_CODE));
+-        RootedFunction fun(cx, pn->pn_funbox->function());
++    bool resolveFun(CodeNode* funNode, HandleAtom prefix, MutableHandleAtom retAtom) {
++        MOZ_ASSERT(funNode != nullptr);
++        MOZ_ASSERT(funNode->isKind(ParseNodeKind::Function));
++        RootedFunction fun(cx, funNode->funbox()->function());
+ 
+         StringBuffer buf(cx);
+         this->buf = &buf;
+ 
+         retAtom.set(nullptr);
+ 
+         /* If the function already has a name, use that */
+         if (fun->displayAtom() != nullptr) {
+@@ -260,21 +259,21 @@ class NameResolver
+         for (int pos = size - 1; pos >= 0; pos--) {
+             ParseNode* node = toName[pos];
+ 
+             if (node->isKind(ParseNodeKind::Colon) || node->isKind(ParseNodeKind::Shorthand)) {
+                 ParseNode* left = node->as<BinaryNode>().left();
+                 if (left->isKind(ParseNodeKind::ObjectPropertyName) ||
+                     left->isKind(ParseNodeKind::String))
+                 {
+-                    if (!appendPropertyReference(left->pn_atom)) {
++                    if (!appendPropertyReference(left->as<NameNode>().atom())) {
+                         return false;
+                     }
+                 } else if (left->isKind(ParseNodeKind::Number)) {
+-                    if (!appendNumericPropertyReference(left->pn_dval)) {
++                    if (!appendNumericPropertyReference(left->as<NumericLiteral>().value())) {
+                         return false;
+                     }
+                 } else {
+                     MOZ_ASSERT(left->isKind(ParseNodeKind::ComputedName));
+                 }
+             } else {
+                 /*
+                  * Don't have consecutive '<' characters, and also don't start
+@@ -301,17 +300,17 @@ class NameResolver
+ 
+         retAtom.set(buf.finishAtom());
+         if (!retAtom) {
+             return false;
+         }
+ 
+         // Skip assigning the guessed name if the function has a (dynamically)
+         // computed inferred name.
+-        if (!pn->isDirectRHSAnonFunction()) {
++        if (!funNode->isDirectRHSAnonFunction()) {
+             fun->setGuessedAtom(retAtom);
+         }
+         return true;
+     }
+ 
+     /*
+      * Tests whether parents[pos] is a function call whose callee is cur.
+      * This is the case for functions which do things like simply create a scope
+@@ -388,21 +387,21 @@ class NameResolver
+      * Resolve all names for anonymous functions recursively within the
+      * ParseNode instance given. The prefix is for each subsequent name, and
+      * should initially be nullptr.
+      */
+     bool resolve(ParseNode* const cur, HandleAtom prefixArg = nullptr) {
+         RootedAtom prefix(cx, prefixArg);
+ 
+         MOZ_ASSERT(cur != nullptr);
+-        MOZ_ASSERT(cur->isArity(PN_CODE) == (cur->isKind(ParseNodeKind::Function) ||
+-                                             cur->isKind(ParseNodeKind::Module)));
++        MOZ_ASSERT(cur->is<CodeNode>() == (cur->isKind(ParseNodeKind::Function) ||
++                                           cur->isKind(ParseNodeKind::Module)));
+         if (cur->isKind(ParseNodeKind::Function)) {
+             RootedAtom prefix2(cx);
+-            if (!resolveFun(cur, prefix, &prefix2)) {
++            if (!resolveFun(&cur->as<CodeNode>(), prefix, &prefix2)) {
+                 return false;
+             }
+ 
+             /*
+              * If a function looks like (function(){})() where the parent node
+              * of the definition of the function is a call, then it shouldn't
+              * contribute anything to the namespace, so don't bother updating
+              * the prefix to whatever was returned.
+@@ -419,39 +418,48 @@ class NameResolver
+         auto initialParents = nparents;
+         parents[initialParents] = cur;
+         nparents++;
+ 
+         switch (cur->getKind()) {
+           // Nodes with no children that might require name resolution need no
+           // further work.
+           case ParseNodeKind::EmptyStatement:
+-          case ParseNodeKind::String:
+-          case ParseNodeKind::TemplateString:
+-          case ParseNodeKind::RegExp:
+           case ParseNodeKind::True:
+           case ParseNodeKind::False:
+           case ParseNodeKind::Null:
+           case ParseNodeKind::RawUndefined:
+           case ParseNodeKind::Elision:
+           case ParseNodeKind::Generator:
+-          case ParseNodeKind::Number:
+           case ParseNodeKind::Break:
+           case ParseNodeKind::Continue:
+           case ParseNodeKind::Debugger:
+           case ParseNodeKind::ExportBatchSpec:
+-          case ParseNodeKind::ObjectPropertyName:
+           case ParseNodeKind::PosHolder:
+             MOZ_ASSERT(cur->isArity(PN_NULLARY));
+             break;
+ 
++          case ParseNodeKind::ObjectPropertyName:
++          case ParseNodeKind::String:
++          case ParseNodeKind::TemplateString:
++            MOZ_ASSERT(cur->is<NameNode>());
++            break;
++
++          case ParseNodeKind::RegExp:
++            MOZ_ASSERT(cur->is<RegExpLiteral>());
++            break;
++
++          case ParseNodeKind::Number:
++            MOZ_ASSERT(cur->is<NumericLiteral>());
++            break;
++
+           case ParseNodeKind::TypeOfName:
+           case ParseNodeKind::SuperBase:
+             MOZ_ASSERT(cur->as<UnaryNode>().kid()->isKind(ParseNodeKind::Name));
+-            MOZ_ASSERT(!cur->as<UnaryNode>().kid()->expr());
++            MOZ_ASSERT(!cur->as<UnaryNode>().kid()->as<NameNode>().expression());
+             break;
+ 
+           case ParseNodeKind::NewTarget:
+           case ParseNodeKind::ImportMeta: {
+             MOZ_ASSERT(cur->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
+             MOZ_ASSERT(cur->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
+             break;
+           }
+@@ -664,24 +672,24 @@ class NameResolver
+           // The first child of a class is a pair of names referring to it,
+           // inside and outside the class.  The second is the class's heritage,
+           // if any.  The third is the class body.
+           case ParseNodeKind::Class: {
+             ClassNode* classNode = &cur->as<ClassNode>();
+ #ifdef DEBUG
+             if (classNode->names()) {
+                 ClassNames* names = classNode->names();
+-                if (ParseNode* outerBinding = names->outerBinding()) {
++                if (NameNode* outerBinding = names->outerBinding()) {
+                     MOZ_ASSERT(outerBinding->isKind(ParseNodeKind::Name));
+-                    MOZ_ASSERT(!outerBinding->expr());
++                    MOZ_ASSERT(!outerBinding->expression());
+                 }
+ 
+-                ParseNode* innerBinding = names->innerBinding();
++                NameNode* innerBinding = names->innerBinding();
+                 MOZ_ASSERT(innerBinding->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!innerBinding->expr());
++                MOZ_ASSERT(!innerBinding->expression());
+             }
+ #endif
+             if (ParseNode* heritage = classNode->heritage()) {
+                 if (!resolve(heritage, prefix)) {
+                     return false;
+                 }
+             }
+             if (!resolve(classNode->methodList(), prefix)) {
+@@ -853,19 +861,19 @@ class NameResolver
+                 break;
+             }
+             for (ParseNode* item : list->contents()) {
+                 BinaryNode* spec = &item->as<BinaryNode>();
+                 MOZ_ASSERT(spec->isKind(isImport
+                                         ? ParseNodeKind::ImportSpec
+                                         : ParseNodeKind::ExportSpec));
+                 MOZ_ASSERT(spec->left()->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!spec->left()->expr());
++                MOZ_ASSERT(!spec->left()->as<NameNode>().expression());
+                 MOZ_ASSERT(spec->right()->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!spec->right()->expr());
++                MOZ_ASSERT(!spec->right()->as<NameNode>().expression());
+             }
+ #endif
+             break;
+           }
+ 
+           case ParseNodeKind::CallImport: {
+             BinaryNode* node = &cur->as<BinaryNode>();
+             if (!resolve(node->right(), prefix)) {
+@@ -882,42 +890,39 @@ class NameResolver
+             }
+             if (!resolve(&prop->expression(), prefix)) {
+                 return false;
+             }
+             break;
+           }
+ 
+           case ParseNodeKind::Label:
+-            MOZ_ASSERT(cur->isArity(PN_NAME));
+-            if (!resolve(cur->expr(), prefix)) {
++            if (!resolve(cur->as<NameNode>().expression(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::Name:
+-            MOZ_ASSERT(cur->isArity(PN_NAME));
+-            if (ParseNode* init = cur->expr()) {
++            if (ParseNode* init = cur->as<NameNode>().expression()) {
+                 if (!resolve(init, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           case ParseNodeKind::LexicalScope:
+             MOZ_ASSERT(cur->isArity(PN_SCOPE));
+             if (!resolve(cur->scopeBody(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::Function:
+           case ParseNodeKind::Module:
+-            MOZ_ASSERT(cur->isArity(PN_CODE));
+-            if (ParseNode* body = cur->pn_body) {
++            if (ParseNode* body = cur->as<CodeNode>().body()) {
+                 if (!resolve(body, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           // Kinds that should be handled by parent node resolution.
+ 
+diff --git a/js/src/frontend/ParseNode-inl.h b/js/src/frontend/ParseNode-inl.h
+--- a/js/src/frontend/ParseNode-inl.h
++++ b/js/src/frontend/ParseNode-inl.h
+@@ -13,16 +13,18 @@
+ 
+ namespace js {
+ namespace frontend {
+ 
+ inline PropertyName*
+ ParseNode::name() const
+ {
+     MOZ_ASSERT(isKind(ParseNodeKind::Function) || isKind(ParseNodeKind::Name));
+-    JSAtom* atom = isKind(ParseNodeKind::Function) ? pn_funbox->function()->explicitName() : pn_atom;
++    JSAtom* atom = isKind(ParseNodeKind::Function)
++                   ? as<CodeNode>().funbox()->function()->explicitName()
++                   : as<NameNode>().atom();
+     return atom->asPropertyName();
+ }
+ 
+ } /* namespace frontend */
+ } /* namespace js */
+ 
+ #endif /* frontend_ParseNode_inl_h */
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -139,82 +139,88 @@ ParseNode::dump()
+ {
+     js::Fprinter out(stderr);
+     dump(out);
+ }
+ 
+ void
+ ParseNode::dump(GenericPrinter& out, int indent)
+ {
+-    switch (pn_arity) {
++    switch (ParseNodeArity(pn_arity)) {
+       case PN_NULLARY:
+         ((NullaryNode*) this)->dump(out);
+-        break;
++        return;
+       case PN_UNARY:
+         as<UnaryNode>().dump(out, indent);
+-        break;
++        return;
+       case PN_BINARY:
+         as<BinaryNode>().dump(out, indent);
+-        break;
++        return;
+       case PN_TERNARY:
+         as<TernaryNode>().dump(out, indent);
+-        break;
++        return;
+       case PN_CODE:
+-        ((CodeNode*) this)->dump(out, indent);
+-        break;
++        as<CodeNode>().dump(out, indent);
++        return;
+       case PN_LIST:
+         as<ListNode>().dump(out, indent);
+-        break;
++        return;
+       case PN_NAME:
+-        ((NameNode*) this)->dump(out, indent);
+-        break;
++        as<NameNode>().dump(out, indent);
++        return;
++      case PN_NUMBER:
++        as<NumericLiteral>().dump(out, indent);
++        return;
++      case PN_REGEXP:
++        as<RegExpLiteral>().dump(out, indent);
++        return;
+       case PN_SCOPE:
+         ((LexicalScopeNode*) this)->dump(out, indent);
+-        break;
+-      default:
+-        out.printf("#<BAD NODE %p, kind=%u, arity=%u>",
+-                (void*) this, unsigned(getKind()), unsigned(pn_arity));
+-        break;
++        return;
+     }
++    out.printf("#<BAD NODE %p, kind=%u, arity=%u>",
++               (void*) this, unsigned(getKind()), unsigned(pn_arity));
+ }
+ 
+ void
+ NullaryNode::dump(GenericPrinter& out)
+ {
+     switch (getKind()) {
+       case ParseNodeKind::True:  out.put("#true");  break;
+       case ParseNodeKind::False: out.put("#false"); break;
+       case ParseNodeKind::Null:  out.put("#null");  break;
+       case ParseNodeKind::RawUndefined: out.put("#undefined"); break;
+ 
+-      case ParseNodeKind::Number: {
+-        ToCStringBuf cbuf;
+-        const char* cstr = NumberToCString(nullptr, &cbuf, pn_dval);
+-        if (!IsFinite(pn_dval)) {
+-            out.put("#");
+-        }
+-        if (cstr) {
+-            out.printf("%s", cstr);
+-        } else {
+-            out.printf("%g", pn_dval);
+-        }
+-        break;
+-      }
+-
+-      case ParseNodeKind::String:
+-      case ParseNodeKind::ObjectPropertyName:
+-        pn_atom->dumpCharsNoNewline(out);
+-        break;
+-
+       default:
+         out.printf("(%s)", parseNodeNames[size_t(getKind())]);
+     }
+ }
+ 
+ void
++NumericLiteral::dump(GenericPrinter& out, int indent)
++{
++    ToCStringBuf cbuf;
++    const char* cstr = NumberToCString(nullptr, &cbuf, value());
++    if (!IsFinite(value())) {
++        out.put("#");
++    }
++    if (cstr) {
++        out.printf("%s", cstr);
++    } else {
++        out.printf("%g", value());
++    }
++}
++
++void
++RegExpLiteral::dump(GenericPrinter& out, int indent)
++{
++    out.printf("(%s)", parseNodeNames[size_t(getKind())]);
++}
++
++void
+ UnaryNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s ", name);
+     indent += strlen(name) + 2;
+     DumpParseTree(kid(), out, indent);
+     out.printf(")");
+ }
+@@ -262,17 +268,17 @@ TernaryNode::dump(GenericPrinter& out, i
+ }
+ 
+ void
+ CodeNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s ", name);
+     indent += strlen(name) + 2;
+-    DumpParseTree(pn_body, out, indent);
++    DumpParseTree(body(), out, indent);
+     out.printf(")");
+ }
+ 
+ void
+ ListNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s [", name);
+@@ -305,40 +311,50 @@ DumpName(GenericPrinter& out, const Char
+             out.printf("\\u%04x", unsigned(c));
+         }
+     }
+ }
+ 
+ void
+ NameNode::dump(GenericPrinter& out, int indent)
+ {
+-    if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::PropertyName)) {
+-        if (!pn_atom) {
++    switch (getKind()) {
++      case ParseNodeKind::String:
++      case ParseNodeKind::TemplateString:
++      case ParseNodeKind::ObjectPropertyName:
++        atom()->dumpCharsNoNewline(out);
++        return;
++
++      case ParseNodeKind::Name:
++      case ParseNodeKind::PropertyName:
++        if (!atom()) {
+             out.put("#<null name>");
+-        } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) {
++        } else if (getOp() == JSOP_GETARG && atom()->length() == 0) {
+             // Dump destructuring parameter.
+             out.put("(#<zero-length name> ");
+-            DumpParseTree(expr(), out, indent + 21);
++            DumpParseTree(expression(), out, indent + 21);
+             out.printf(")");
+         } else {
+             JS::AutoCheckCannotGC nogc;
+-            if (pn_atom->hasLatin1Chars()) {
+-                DumpName(out, pn_atom->latin1Chars(nogc), pn_atom->length());
++            if (atom()->hasLatin1Chars()) {
++                DumpName(out, atom()->latin1Chars(nogc), atom()->length());
+             } else {
+-                DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length());
++                DumpName(out, atom()->twoByteChars(nogc), atom()->length());
+             }
+         }
+         return;
++
++      default: {
++        const char* name = parseNodeNames[size_t(getKind())];
++        out.printf("(%s ", name);
++        indent += strlen(name) + 2;
++        DumpParseTree(expression(), out, indent);
++        out.printf(")");
++      }
+     }
+-
+-    const char* name = parseNodeNames[size_t(getKind())];
+-    out.printf("(%s ", name);
+-    indent += strlen(name) + 2;
+-    DumpParseTree(expr(), out, indent);
+-    out.printf(")");
+ }
+ 
+ void
+ LexicalScopeNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s [", name);
+     int nameIndent = indent + strlen(name) + 3;
+@@ -417,17 +433,19 @@ FunctionBox::trace(JSTracer* trc)
+ bool
+ js::frontend::IsAnonymousFunctionDefinition(ParseNode* pn)
+ {
+     // ES 2017 draft
+     // 12.15.2 (ArrowFunction, AsyncArrowFunction).
+     // 14.1.12 (FunctionExpression).
+     // 14.4.8 (GeneratorExpression).
+     // 14.6.8 (AsyncFunctionExpression)
+-    if (pn->isKind(ParseNodeKind::Function) && !pn->pn_funbox->function()->explicitName()) {
++    if (pn->isKind(ParseNodeKind::Function) &&
++        !pn->as<CodeNode>().funbox()->function()->explicitName())
++    {
+         return true;
+     }
+ 
+     // 14.5.8 (ClassExpression)
+     if (pn->is<ClassNode>() && !pn->as<ClassNode>().names()) {
+         return true;
+     }
+ 
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -223,31 +223,31 @@ IsDeleteKind(ParseNodeKind kind)
+ inline bool
+ IsTypeofKind(ParseNodeKind kind)
+ {
+     return ParseNodeKind::TypeOfName <= kind && kind <= ParseNodeKind::TypeOfExpr;
+ }
+ 
+ /*
+  * <Definitions>
+- * Function name        pn_funbox: ptr to js::FunctionBox holding function
+- *                            object containing arg and var properties.  We
+- *                            create the function object at parse (not emit)
+- *                            time to specialize arg and var bytecodes early.
+- *                          pn_body: ParamsBody, ordinarily;
+- *                            ParseNodeKind::LexicalScope for implicit function in genexpr
++ * Function (CodeNode)
++ *   funbox: ptr to js::FunctionBox holding function object containing arg and
++ *           var properties.  We create the function object at parse (not emit)
++ *           time to specialize arg and var bytecodes early.
++ *   body: ParamsBody or null for lazily-parsed function, ordinarily;
++ *         ParseNodeKind::LexicalScope for implicit function in genexpr
+  * ParamsBody (ListNode)
+  *   head: list of formal parameters with
+  *           * Name node with non-empty name for SingleNameBinding without
+  *             Initializer
+  *           * Assign node for SingleNameBinding with Initializer
+  *           * Name node with empty name for destructuring
+- *               pn_expr: Array or Object for BindingPattern without
+- *                        Initializer, Assign for BindingPattern with
+- *                        Initializer
++ *               expr: Array or Object for BindingPattern without
++ *                     Initializer, Assign for BindingPattern with
++ *                     Initializer
+  *         followed by either:
+  *           * StatementList node for function body statements
+  *           * Return for expression closure
+  *   count: number of formal parameters + 1
+  * Spread (UnaryNode)
+  *   kid: expression being spread
+  * Class (ClassNode)
+  *   kid1: ClassNames for class name. can be null for anonymous class.
+@@ -261,16 +261,19 @@ IsTypeofKind(ParseNodeKind kind)
+  *         that doesn't create an outer binding
+  *   right: Name node for inner binding
+  * ClassMethodList (ListNode)
+  *   head: list of N ClassMethod nodes
+  *   count: N >= 0
+  * ClassMethod (ClassMethod)
+  *   name: propertyName
+  *   method: methodDefinition
++ * Module (CodeNode)
++ *   funbox: ?
++ *   body: ?
+  *
+  * <Statements>
+  * StatementList (ListNode)
+  *   head: list of N statements
+  *   count: N >= 0
+  * If (TernaryNode)
+  *   kid1: cond
+  *   kid2: then
+@@ -314,44 +317,44 @@ IsTypeofKind(ParseNodeKind kind)
+  *   kid2: null or LexicalScope for catch-block with scopeBody pointing to a
+  *         Catch node
+  *   kid3: null or finally block
+  * Catch (BinaryNode)
+  *   left: Name, Array, or Object catch var node
+  *         (Array or Object if destructuring),
+  *         or null if optional catch binding
+  *   right: catch block statements
+- * Break    name        pn_atom: label or null
+- * Continue name        pn_atom: label or null
++ * Break (BreakStatement)
++ *   atom: label or null
++ * Continue (ContinueStatement)
++ *   atom: label or null
+  * With (BinaryNode)
+  *   left: head expr
+  *   right: body
+  * Var, Let, Const (ListNode)
+  *   head: list of N Name or Assign nodes
+  *         each name node has either
+- *           pn_used: false
+- *           pn_atom: variable name
+- *           pn_expr: initializer or null
++ *           atom: variable name
++ *           expr: initializer or null
+  *         or
+- *           pn_used: true
+- *           pn_atom: variable name
+- *           pn_lexdef: def node
++ *           atom: variable name
+  *         each assignment node has
+- *           left: Name with pn_used true and
+- *                    pn_lexdef (NOT pn_expr) set
++ *           left: pattern
+  *           right: initializer
+  *   count: N > 0
+  * Return (UnaryNode)
+  *   kid: returned expression, or null if none
+  * ExpressionStatement (UnaryNode)
+  *   kid: expr
+  *   prologue: true if Directive Prologue member in original source, not
+  *             introduced via constant folding or other tree rewriting
+  * EmptyStatement nullary      (no fields)
+- * Label    name        pn_atom: label, pn_expr: labeled statement
++ * Label (NameNode)
++ *   atom: label
++ *   expr: labeled statement
+  * Import (BinaryNode)
+  *   left: ImportSpecList import specifiers
+  *   right: String module specifier
+  * ImportSpecList (ListNode)
+  *   head: list of N ImportSpec nodes
+  *   count: N >= 0 (N = 0 for `import {} from ...`)
+  * ImportSpec (BinaryNode)
+  *   left: import name
+@@ -410,17 +413,18 @@ IsTypeofKind(ParseNodeKind kind)
+  *   kid: expression that's evaluated, then the overall delete evaluates to
+  *        true; can't be a kind for a more-specific ParseNodeKind::Delete*
+  *        unless constant folding (or a similar parse tree manipulation) has
+  *        occurred
+  *          * DeleteName: Name expr
+  *          * DeleteProp: Dot expr
+  *          * DeleteElem: Elem expr
+  *          * DeleteExpr: Member expr
+- * PropertyName name    pn_atom: property name being accessed
++ * PropertyName (NameNode)
++ *   atom: property name being accessed
+  * Dot (PropertyAccess)
+  *   left: MEMBER expr to left of '.'
+  *   right: PropertyName to right of '.'
+  * Elem (PropertyByValue)
+  *   left: MEMBER expr to left of '['
+  *   right: expr between '[' and ']'
+  * Call (BinaryNode)
+  *   left: callee expression on the left of the '('
+@@ -445,39 +449,44 @@ IsTypeofKind(ParseNodeKind kind)
+  *   left: property id
+  *   right: value
+  * Shorthand (BinaryNode)
+  *   Same fields as Colon. This is used for object literal properties using
+  *   shorthand ({x}).
+  * ComputedName (UnaryNode)
+  *   ES6 ComputedPropertyName.
+  *   kid: the AssignmentExpression inside the square brackets
+- * Name,    name        pn_atom: name, string, or object atom
+- * String               pn_op: JSOP_GETNAME, JSOP_STRING, or JSOP_OBJECT
+- *                          If JSOP_GETNAME, pn_op may be JSOP_*ARG or JSOP_*VAR
+- *                          telling const-ness and static analysis results
++ * Name (NameNode)
++ *   atom: name, or object atom
++ *   pn_op: JSOP_GETNAME, JSOP_STRING, or JSOP_OBJECT
++ *          If JSOP_GETNAME, pn_op may be JSOP_*ARG or JSOP_*VAR telling
++ *          const-ness and static analysis results
++ * String (NameNode)
++ *   atom: string
+  * TemplateStringList (ListNode)
+  *   head: list of alternating expr and template strings
+  *           TemplateString [, expression, TemplateString]+
+  *         there's at least one expression.  If the template literal contains
+  *         no ${}-delimited expression, it's parsed as a single TemplateString
+- * TemplateString      pn_atom: template string atom
+-                nullary     pn_op: JSOP_NOP
++ * TemplateString (NameNode)
++ *   atom: template string atom
+  * TaggedTemplate (BinaryNode)
+  *   left: tag expression
+  *   right: Arguments, with the first being the call site object, then
+  *          arg1, arg2, ... argN
+  * CallSiteObj (CallSiteNode)
+  *   head:  an Array of raw TemplateString, then corresponding cooked
+  *          TemplateString nodes
+  *            Array [, cooked TemplateString]+
+  *          where the Array is
+  *            [raw TemplateString]+
+- * RegExp   nullary     pn_objbox: RegExp model object
+- * Number   dval        pn_dval: double value of numeric literal
++ * RegExp (RegExpLiteral)
++ *   regexp: RegExp model object
++ * Number (NumericLiteral)
++ *   value: double value of numeric literal
+  * True,    nullary     pn_op: JSOp bytecode
+  * False,
+  * Null,
+  * RawUndefined
+  *
+  * This (UnaryNode)
+  *   kid: '.this' Name if function `this`, else nullptr
+  * SuperBase (UnaryNode)
+@@ -495,40 +504,51 @@ IsTypeofKind(ParseNodeKind kind)
+  * InitialYield (UnaryNode)
+  *   kid: generator object
+  * Yield, YieldStar, Await (UnaryNode)
+  *   kid: expr or null
+  * Nop          nullary
+  */
+ enum ParseNodeArity
+ {
+-    PN_NULLARY,                         /* 0 kids, only pn_atom/pn_dval/etc. */
++    PN_NULLARY,                         /* 0 kids */
+     PN_UNARY,                           /* one kid, plus a couple of scalars */
+     PN_BINARY,                          /* two kids, plus a couple of scalars */
+     PN_TERNARY,                         /* three kids */
+     PN_CODE,                            /* module or function definition node */
+     PN_LIST,                            /* generic singly linked list */
+-    PN_NAME,                            /* name, label, or regexp */
++    PN_NAME,                            /* name, label, string */
++    PN_NUMBER,                          /* numeric literal */
++    PN_REGEXP,                          /* regexp literal */
+     PN_SCOPE                            /* lexical scope */
+ };
+ 
+ #define FOR_EACH_PARSENODE_SUBCLASS(macro) \
+     macro(BinaryNode, BinaryNodeType, asBinary) \
+     macro(AssignmentNode, AssignmentNodeType, asAssignment) \
+     macro(CaseClause, CaseClauseType, asCaseClause) \
+     macro(ClassMethod, ClassMethodType, asClassMethod) \
+     macro(ClassNames, ClassNamesType, asClassNames) \
+     macro(ForNode, ForNodeType, asFor) \
+     macro(PropertyAccess, PropertyAccessType, asPropertyAccess) \
+     macro(PropertyByValue, PropertyByValueType, asPropertyByValue) \
+     macro(SwitchStatement, SwitchStatementType, asSwitchStatement) \
+     \
++    macro(CodeNode, CodeNodeType, asCode) \
++    \
+     macro(ListNode, ListNodeType, asList) \
+     macro(CallSiteNode, CallSiteNodeType, asCallSite) \
+     \
++    macro(NameNode, NameNodeType, asName) \
++    macro(LabeledStatement, LabeledStatementType, asLabeledStatement) \
++    \
++    macro(NumericLiteral, NumericLiteralType, asNumericLiteral) \
++    \
++    macro(RegExpLiteral, RegExpLiteralType, asRegExpLiteral) \
++    \
+     macro(TernaryNode, TernaryNodeType, asTernary) \
+     macro(ClassNode, ClassNodeType, asClass) \
+     macro(ConditionalExpression, ConditionalExpressionType, asConditionalExpression) \
+     macro(UnaryNode, UnaryNodeType, asUnary) \
+     macro(ThisLiteral, ThisLiteralType, asThisLiteral)
+ 
+ class LoopControlStatement;
+ class BreakStatement;
+@@ -654,64 +674,61 @@ class ParseNode
+         } binary;
+         struct {                        /* one kid if unary */
+           private:
+             friend class UnaryNode;
+             ParseNode*  kid;
+             bool        prologue;       /* directive prologue member */
+         } unary;
+         struct {                        /* name, labeled statement, etc. */
+-            union {
+-                JSAtom*      atom;      /* lexical name or label atom */
+-                ObjectBox*   objbox;    /* regexp object */
+-                FunctionBox* funbox;    /* function object */
+-            };
+-            ParseNode*  expr;           /* module or function body, var
+-                                           initializer, or argument default */
++          private:
++            friend class NameNode;
++            JSAtom*      atom;          /* lexical name or label atom */
++            ParseNode*  expr;           /* var initializer, or argument default
++                                         */
+         } name;
+         struct {
++          private:
++            friend class RegExpLiteral;
++            ObjectBox* objbox;
++        } regexp;
++        struct {
++          private:
++            friend class CodeNode;
++            FunctionBox* funbox;        /* function object */
++            ParseNode*  body;           /* module or function body */
++        } code;
++        struct {
+             LexicalScope::Data* bindings;
+             ParseNode*          body;
+         } scope;
+         struct {
++          private:
++            friend class NumericLiteral;
+             double       value;         /* aligned numeric literal value */
+             DecimalPoint decimalPoint;  /* Whether the number has a decimal point */
+         } number;
+         class {
+             friend class LoopControlStatement;
+             PropertyName*    label;    /* target of break/continue statement */
+         } loopControl;
+     } pn_u;
+ 
+-#define pn_objbox       pn_u.name.objbox
+-#define pn_funbox       pn_u.name.funbox
+-#define pn_body         pn_u.name.expr
+-#define pn_atom         pn_u.name.atom
+-#define pn_objbox       pn_u.name.objbox
+-#define pn_expr         pn_u.name.expr
+-#define pn_dval         pn_u.number.value
+-
+-
+   public:
+     /*
+      * If |left| is a list of the given kind/left-associative op, append
+      * |right| to it and return |left|.  Otherwise return a [left, right] list.
+      */
+     static ParseNode*
+     appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
+                        FullParseHandler* handler, ParseContext* pc);
+ 
+     // include "ParseNode-inl.h" for these methods.
+     inline PropertyName* name() const;
+ 
+-    ParseNode* expr() const {
+-        MOZ_ASSERT(pn_arity == PN_NAME || pn_arity == PN_CODE);
+-        return pn_expr;
+-    }
+-
+     bool isEmptyScope() const {
+         MOZ_ASSERT(pn_arity == PN_SCOPE);
+         return !pn_u.scope.bindings;
+     }
+ 
+     Handle<LexicalScope::Data*> scopeBindings() const {
+         MOZ_ASSERT(!isEmptyScope());
+         // Bindings' GC safety depend on the presence of an AutoKeepAtoms that
+@@ -724,48 +741,29 @@ class ParseNode
+         return pn_u.scope.body;
+     }
+ 
+     void setScopeBody(ParseNode* body) {
+         MOZ_ASSERT(pn_arity == PN_SCOPE);
+         pn_u.scope.body = body;
+     }
+ 
+-    bool functionIsHoisted() const {
+-        MOZ_ASSERT(pn_arity == PN_CODE && getKind() == ParseNodeKind::Function);
+-        MOZ_ASSERT(isOp(JSOP_LAMBDA) ||        // lambda
+-                   isOp(JSOP_LAMBDA_ARROW) ||  // arrow function
+-                   isOp(JSOP_DEFFUN) ||        // non-body-level function statement
+-                   isOp(JSOP_NOP) ||           // body-level function stmt in global code
+-                   isOp(JSOP_GETLOCAL) ||      // body-level function stmt in function code
+-                   isOp(JSOP_GETARG) ||        // body-level function redeclaring formal
+-                   isOp(JSOP_INITLEXICAL));    // block-level function stmt
+-        return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
+-    }
+-
+     /* True if pn is a parsenode representing a literal constant. */
+     bool isLiteral() const {
+         return isKind(ParseNodeKind::Number) ||
+                isKind(ParseNodeKind::String) ||
+                isKind(ParseNodeKind::True) ||
+                isKind(ParseNodeKind::False) ||
+                isKind(ParseNodeKind::Null) ||
+                isKind(ParseNodeKind::RawUndefined);
+     }
+ 
+     // True iff this is a for-in/of loop variable declaration (var/let/const).
+     inline bool isForLoopDeclaration() const;
+ 
+-    void initNumber(double value, DecimalPoint decimalPoint) {
+-        MOZ_ASSERT(pn_arity == PN_NULLARY);
+-        MOZ_ASSERT(getKind() == ParseNodeKind::Number);
+-        pn_u.number.value = value;
+-        pn_u.number.decimalPoint = decimalPoint;
+-    }
+-
+     enum AllowConstantObjects {
+         DontAllowObjects = 0,
+         AllowObjects,
+         ForCopyOnWriteArray
+     };
+ 
+     MOZ_MUST_USE bool getConstantValue(JSContext* cx, AllowConstantObjects allowObjects,
+                                        MutableHandleValue vp, Value* compare = nullptr,
+@@ -800,28 +798,67 @@ class ParseNode
+ 
+ struct NullaryNode : public ParseNode
+ {
+     NullaryNode(ParseNodeKind kind, const TokenPos& pos)
+       : ParseNode(kind, JSOP_NOP, PN_NULLARY, pos) {}
+     NullaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
+       : ParseNode(kind, op, PN_NULLARY, pos) {}
+ 
+-    // This constructor is for a few mad uses in the emitter. It populates
+-    // the pn_atom field even though that field belongs to a branch in pn_u
+-    // that nullary nodes shouldn't use -- bogus.
+-    NullaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, JSAtom* atom)
+-      : ParseNode(kind, op, PN_NULLARY, pos)
++#ifdef DEBUG
++    void dump(GenericPrinter& out);
++#endif
++};
++
++class NameNode : public ParseNode
++{
++  protected:
++    NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, ParseNode* expr, const TokenPos& pos)
++      : ParseNode(kind, op, PN_NAME, pos)
+     {
+-        pn_atom = atom;
++        pn_u.name.atom = atom;
++        pn_u.name.expr = expr;
++    }
++
++  public:
++    NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, const TokenPos& pos)
++      : ParseNode(kind, op, PN_NAME, pos)
++    {
++        pn_u.name.atom = atom;
++        pn_u.name.expr = nullptr;
++    }
++
++    static bool test(const ParseNode& node) {
++        return node.isArity(PN_NAME);
+     }
+ 
+ #ifdef DEBUG
+-    void dump(GenericPrinter& out);
++    void dump(GenericPrinter& out, int indent);
+ #endif
++
++    JSAtom* atom() const {
++        return pn_u.name.atom;
++    }
++
++    ParseNode* expression() const {
++        return pn_u.name.expr;
++    }
++
++    void setAtom(JSAtom* atom) {
++        pn_u.name.atom = atom;
++    }
++
++    void setExpression(ParseNode* expr) {
++        pn_u.name.expr = expr;
++    }
++
++    // Methods used by FoldConstants.cpp.
++    ParseNode** unsafeExpressionReference() {
++        return &pn_u.name.expr;
++    }
+ };
+ 
+ class UnaryNode : public ParseNode
+ {
+   public:
+     UnaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* kid)
+       : ParseNode(kind, JSOP_NOP, PN_UNARY, pos)
+     {
+@@ -861,17 +898,17 @@ class UnaryNode : public ParseNode
+      * Note that a Directive Prologue can contain statements that cannot
+      * themselves be directives (string literals that include escape sequences
+      * or escaped newlines, say). This member function returns true for such
+      * nodes; we use it to determine the extent of the prologue.
+      */
+     JSAtom* isStringExprStatement() const {
+         if (isKind(ParseNodeKind::ExpressionStatement)) {
+             if (kid()->isKind(ParseNodeKind::String) && !kid()->isInParens()) {
+-                return kid()->pn_atom;
++                return kid()->as<NameNode>().atom();
+             }
+         }
+         return nullptr;
+     }
+ 
+     // Methods used by FoldConstants.cpp.
+     ParseNode** unsafeKidReference() {
+         return &pn_u.unary.kid;
+@@ -1350,48 +1387,106 @@ ParseNode::isForLoopDeclaration() const
+     if (isKind(ParseNodeKind::Var) || isKind(ParseNodeKind::Let) || isKind(ParseNodeKind::Const)) {
+         MOZ_ASSERT(!as<ListNode>().empty());
+         return true;
+     }
+ 
+     return false;
+ }
+ 
+-struct CodeNode : public ParseNode
++class CodeNode : public ParseNode
+ {
++  public:
+     CodeNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
+       : ParseNode(kind, op, PN_CODE, pos)
+     {
+         MOZ_ASSERT(kind == ParseNodeKind::Function || kind == ParseNodeKind::Module);
+         MOZ_ASSERT_IF(kind == ParseNodeKind::Module, op == JSOP_NOP);
+         MOZ_ASSERT(op == JSOP_NOP || // statement, module
+                    op == JSOP_LAMBDA_ARROW || // arrow function
+                    op == JSOP_LAMBDA); // expression, method, accessor, &c.
+-        MOZ_ASSERT(!pn_body);
+-        MOZ_ASSERT(!pn_objbox);
++        MOZ_ASSERT(!pn_u.code.body);
++        MOZ_ASSERT(!pn_u.code.funbox);
+     }
+ 
+-  public:
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::Function) || node.isKind(ParseNodeKind::Module);
++        MOZ_ASSERT_IF(match, node.isArity(PN_CODE));
++        return match;
++    }
++
+ #ifdef DEBUG
+   void dump(GenericPrinter& out, int indent);
+ #endif
++
++    FunctionBox* funbox() const {
++        return pn_u.code.funbox;
++    }
++
++    ListNode* body() const {
++        return pn_u.code.body ? &pn_u.code.body->as<ListNode>() : nullptr;
++    }
++
++    void setFunbox(FunctionBox* funbox) {
++        pn_u.code.funbox = funbox;
++    }
++
++    void setBody(ListNode* body) {
++        pn_u.code.body = body;
++    }
++
++    // Methods used by FoldConstants.cpp.
++    ParseNode** unsafeBodyReference() {
++        return &pn_u.code.body;
++    }
++
++    bool functionIsHoisted() const {
++        MOZ_ASSERT(isKind(ParseNodeKind::Function));
++        MOZ_ASSERT(isOp(JSOP_LAMBDA) ||        // lambda
++                   isOp(JSOP_LAMBDA_ARROW) ||  // arrow function
++                   isOp(JSOP_DEFFUN) ||        // non-body-level function statement
++                   isOp(JSOP_NOP) ||           // body-level function stmt in global code
++                   isOp(JSOP_GETLOCAL) ||      // body-level function stmt in function code
++                   isOp(JSOP_GETARG) ||        // body-level function redeclaring formal
++                   isOp(JSOP_INITLEXICAL));    // block-level function stmt
++        return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
++    }
+ };
+ 
+-struct NameNode : public ParseNode
++class NumericLiteral : public ParseNode
+ {
+-    NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, const TokenPos& pos)
+-      : ParseNode(kind, op, PN_NAME, pos)
++  public:
++    NumericLiteral(double value, DecimalPoint decimalPoint, const TokenPos& pos)
++      : ParseNode(ParseNodeKind::Number, JSOP_NOP, PN_NUMBER, pos)
+     {
+-        pn_atom = atom;
+-        pn_expr = nullptr;
++        pn_u.number.value = value;
++        pn_u.number.decimalPoint = decimalPoint;
++    }
++
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::Number);
++        MOZ_ASSERT_IF(match, node.isArity(PN_NUMBER));
++        return match;
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
++
++    double value() const {
++        return pn_u.number.value;
++    }
++
++    DecimalPoint decimalPoint() const {
++        return pn_u.number.decimalPoint;
++    }
++
++    void setValue(double v) {
++        pn_u.number.value = v;
++    }
+ };
+ 
+ struct LexicalScopeNode : public ParseNode
+ {
+     LexicalScopeNode(LexicalScope::Data* bindings, ParseNode* body)
+       : ParseNode(ParseNodeKind::LexicalScope, JSOP_NOP, PN_SCOPE, body->pn_pos)
+     {
+         pn_u.scope.bindings = bindings;
+@@ -1402,32 +1497,29 @@ struct LexicalScopeNode : public ParseNo
+         return node.isKind(ParseNodeKind::LexicalScope);
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
+ };
+ 
+-class LabeledStatement : public ParseNode
++class LabeledStatement : public NameNode
+ {
+   public:
+     LabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin)
+-      : ParseNode(ParseNodeKind::Label, JSOP_NOP, PN_NAME, TokenPos(begin, stmt->pn_pos.end))
+-    {
+-        pn_atom = label;
+-        pn_expr = stmt;
+-    }
++      : NameNode(ParseNodeKind::Label, JSOP_NOP, label, stmt, TokenPos(begin, stmt->pn_pos.end))
++    {}
+ 
+     PropertyName* label() const {
+-        return pn_atom->asPropertyName();
++        return atom()->asPropertyName();
+     }
+ 
+     ParseNode* statement() const {
+-        return pn_expr;
++        return expression();
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Label);
+         MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
+@@ -1566,101 +1658,110 @@ class ThisLiteral : public UnaryNode
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::This);
+         MOZ_ASSERT_IF(match, node.is<UnaryNode>());
+         return match;
+     }
+ };
+ 
+-class NullLiteral : public ParseNode
++class NullLiteral : public NullaryNode
+ {
+   public:
+-    explicit NullLiteral(const TokenPos& pos) : ParseNode(ParseNodeKind::Null, JSOP_NULL, PN_NULLARY, pos) { }
++    explicit NullLiteral(const TokenPos& pos)
++      : NullaryNode(ParseNodeKind::Null, JSOP_NULL, pos)
++    { }
+ };
+ 
+ // This is only used internally, currently just for tagged templates.
+ // It represents the value 'undefined' (aka `void 0`), like NullLiteral
+ // represents the value 'null'.
+-class RawUndefinedLiteral : public ParseNode
++class RawUndefinedLiteral : public NullaryNode
+ {
+   public:
+     explicit RawUndefinedLiteral(const TokenPos& pos)
+-      : ParseNode(ParseNodeKind::RawUndefined, JSOP_UNDEFINED, PN_NULLARY, pos) { }
++      : NullaryNode(ParseNodeKind::RawUndefined, JSOP_UNDEFINED, pos) { }
+ };
+ 
+-class BooleanLiteral : public ParseNode
++class BooleanLiteral : public NullaryNode
+ {
+   public:
+     BooleanLiteral(bool b, const TokenPos& pos)
+-      : ParseNode(b ? ParseNodeKind::True : ParseNodeKind::False, b ? JSOP_TRUE : JSOP_FALSE, PN_NULLARY, pos)
++      : NullaryNode(b ? ParseNodeKind::True : ParseNodeKind::False,
++                    b ? JSOP_TRUE : JSOP_FALSE, pos)
+     { }
+ };
+ 
+-class RegExpLiteral : public NullaryNode
++class RegExpLiteral : public ParseNode
+ {
+   public:
+     RegExpLiteral(ObjectBox* reobj, const TokenPos& pos)
+-      : NullaryNode(ParseNodeKind::RegExp, JSOP_REGEXP, pos)
++      : ParseNode(ParseNodeKind::RegExp, JSOP_REGEXP, PN_REGEXP, pos)
+     {
+-        pn_objbox = reobj;
++        pn_u.regexp.objbox = reobj;
+     }
+ 
+-    ObjectBox* objbox() const { return pn_objbox; }
++    ObjectBox* objbox() const {
++        return pn_u.regexp.objbox;
++    }
++
++#ifdef DEBUG
++    void dump(GenericPrinter& out, int indent);
++#endif
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::RegExp);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
++        MOZ_ASSERT_IF(match, node.isArity(PN_REGEXP));
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_REGEXP));
+         return match;
+     }
+ };
+ 
+ class PropertyAccess : public BinaryNode
+ {
+   public:
+     /*
+      * PropertyAccess nodes can have any expression/'super' as left-hand
+      * side, but the name must be a ParseNodeKind::PropertyName node.
+      */
+-    PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
++    PropertyAccess(ParseNode* lhs, NameNode* name, uint32_t begin, uint32_t end)
+       : BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name)
+     {
+-        MOZ_ASSERT(lhs != nullptr);
+-        MOZ_ASSERT(name != nullptr);
++        MOZ_ASSERT(lhs);
++        MOZ_ASSERT(name);
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Dot);
+         MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         MOZ_ASSERT_IF(match, node.as<BinaryNode>().right()->isKind(ParseNodeKind::PropertyName));
+         return match;
+     }
+ 
+     ParseNode& expression() const {
+         return *left();
+     }
+ 
+-    ParseNode& key() const {
+-        return *right();
++    NameNode& key() const {
++        return right()->as<NameNode>();
+     }
+ 
+     // Method used by BytecodeEmitter::emitPropLHS for optimization.
+     // Those methods allow expression to temporarily be nullptr for
+     // optimization purpose.
+     ParseNode* maybeExpression() const {
+         return left();
+     }
+ 
+     void setExpression(ParseNode* pn) {
+         pn_u.binary.left = pn;
+     }
+ 
+     PropertyName& name() const {
+-        return *right()->pn_atom->asPropertyName();
++        return *right()->as<NameNode>().atom()->asPropertyName();
+     }
+ 
+     bool isSuper() const {
+         // ParseNodeKind::SuperBase cannot result from any expression syntax.
+         return expression().isKind(ParseNodeKind::SuperBase);
+     }
+ };
+ 
+@@ -1732,18 +1833,18 @@ class ClassMethod : public BinaryNode
+         MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         return match;
+     }
+ 
+     ParseNode& name() const {
+         return *left();
+     }
+ 
+-    ParseNode& method() const {
+-        return *right();
++    CodeNode& method() const {
++        return right()->as<CodeNode>();
+     }
+ 
+     bool isStatic() const {
+         return pn_u.binary.isStatic;
+     }
+ };
+ 
+ class SwitchStatement : public BinaryNode
+@@ -1795,39 +1896,43 @@ class SwitchStatement : public BinaryNod
+ class ClassNames : public BinaryNode
+ {
+   public:
+     ClassNames(ParseNode* outerBinding, ParseNode* innerBinding, const TokenPos& pos)
+       : BinaryNode(ParseNodeKind::ClassNames, JSOP_NOP, pos, outerBinding, innerBinding)
+     {
+         MOZ_ASSERT_IF(outerBinding, outerBinding->isKind(ParseNodeKind::Name));
+         MOZ_ASSERT(innerBinding->isKind(ParseNodeKind::Name));
+-        MOZ_ASSERT_IF(outerBinding, innerBinding->pn_atom == outerBinding->pn_atom);
++        MOZ_ASSERT_IF(outerBinding,
++                      innerBinding->as<NameNode>().atom() == outerBinding->as<NameNode>().atom());
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::ClassNames);
+         MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         return match;
+     }
+ 
+     /*
+      * Classes require two definitions: The first "outer" binding binds the
+      * class into the scope in which it was declared. the outer binding is a
+      * mutable lexial binding. The second "inner" binding binds the class by
+      * name inside a block in which the methods are evaulated. It is immutable,
+      * giving the methods access to the static members of the class even if
+      * the outer binding has been overwritten.
+      */
+-    ParseNode* outerBinding() const {
+-        return left();
++    NameNode* outerBinding() const {
++        if (ParseNode* binding = left()) {
++            return &binding->as<NameNode>();
++        }
++        return nullptr;
+     }
+ 
+-    ParseNode* innerBinding() const {
+-        return right();
++    NameNode* innerBinding() const {
++        return &right()->as<NameNode>();
+     }
+ };
+ 
+ class ClassNode : public TernaryNode
+ {
+   public:
+     ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock,
+               const TokenPos& pos)
+@@ -1987,17 +2092,17 @@ IsMethodDefinitionKind(FunctionSyntaxKin
+            kind == FunctionSyntaxKind::Getter ||
+            kind == FunctionSyntaxKind::Setter;
+ }
+ 
+ static inline ParseNode*
+ FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals)
+ {
+     MOZ_ASSERT(fn->isKind(ParseNodeKind::Function));
+-    ListNode* argsBody = &fn->pn_body->as<ListNode>();
++    ListNode* argsBody = fn->as<CodeNode>().body();
+     MOZ_ASSERT(argsBody->isKind(ParseNodeKind::ParamsBody));
+     *numFormals = argsBody->count();
+     if (*numFormals > 0 &&
+         argsBody->last()->isKind(ParseNodeKind::LexicalScope) &&
+         argsBody->last()->scopeBody()->isKind(ParseNodeKind::StatementList))
+     {
+         (*numFormals)--;
+     }
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -1058,17 +1058,18 @@ ParserBase::newObjectBox(JSObject* obj)
+ 
+     traceListHead = objbox;
+ 
+     return objbox;
+ }
+ 
+ template <class ParseHandler>
+ FunctionBox*
+-PerHandlerParser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
++PerHandlerParser<ParseHandler>::newFunctionBox(CodeNodeType funNode, JSFunction* fun,
++                                               uint32_t toStringStart,
+                                                Directives inheritedDirectives,
+                                                GeneratorKind generatorKind,
+                                                FunctionAsyncKind asyncKind)
+ {
+     MOZ_ASSERT(fun);
+ 
+     /*
+      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
+@@ -1081,18 +1082,18 @@ PerHandlerParser<ParseHandler>::newFunct
+         alloc.new_<FunctionBox>(context, traceListHead, fun, toStringStart, inheritedDirectives,
+                                 options().extraWarningsOption, generatorKind, asyncKind);
+     if (!funbox) {
+         ReportOutOfMemory(context);
+         return nullptr;
+     }
+ 
+     traceListHead = funbox;
+-    if (fn) {
+-        handler.setFunctionBox(fn, funbox);
++    if (funNode) {
++        handler.setFunctionBox(funNode, funbox);
+     }
+ 
+     return funbox;
+ }
+ 
+ ModuleSharedContext::ModuleSharedContext(JSContext* cx, ModuleObject* module,
+                                          Scope* enclosingScope, ModuleBuilder& builder)
+   : SharedContext(cx, Kind::Module, Directives(true), false),
+@@ -1327,17 +1328,18 @@ GeneralParser<ParseHandler, CharT>::repo
+ //
+ // The 'disallowDuplicateParams' bool indicates whether the use of another
+ // feature (destructuring or default arguments) disables duplicate arguments.
+ // (ECMA-262 requires us to support duplicate parameter names, but, for newer
+ // features, we consider the code to have "opted in" to higher standards and
+ // forbid duplicates.)
+ template <class ParseHandler, typename CharT>
+ bool
+-GeneralParser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
++GeneralParser<ParseHandler, CharT>::notePositionalFormalParameter(CodeNodeType funNode,
++                                                                  HandlePropertyName name,
+                                                                   uint32_t beginPos,
+                                                                   bool disallowDuplicateParams,
+                                                                   bool* duplicatedParam)
+ {
+     if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) {
+         if (disallowDuplicateParams) {
+             error(JSMSG_BAD_DUP_ARGS);
+             return false;
+@@ -1365,37 +1367,38 @@ GeneralParser<ParseHandler, CharT>::note
+         }
+     }
+ 
+     if (!pc->positionalFormalParameterNames().append(name)) {
+         ReportOutOfMemory(context);
+         return false;
+     }
+ 
+-    Node paramNode = newName(name);
++    NameNodeType paramNode = newName(name);
+     if (!paramNode) {
+         return false;
+     }
+ 
+-    handler.addFunctionFormalParameter(fn, paramNode);
++    handler.addFunctionFormalParameter(funNode, paramNode);
+     return true;
+ }
+ 
+ template <class ParseHandler>
+ bool
+-PerHandlerParser<ParseHandler>::noteDestructuredPositionalFormalParameter(Node fn, Node destruct)
++PerHandlerParser<ParseHandler>::noteDestructuredPositionalFormalParameter(CodeNodeType funNode,
++                                                                          Node destruct)
+ {
+     // Append an empty name to the positional formals vector to keep track of
+     // argument slots when making FunctionScope::Data.
+     if (!pc->positionalFormalParameterNames().append(nullptr)) {
+         ReportOutOfMemory(context);
+         return false;
+     }
+ 
+-    handler.addFunctionFormalParameter(fn, destruct);
++    handler.addFunctionFormalParameter(funNode, destruct);
+     return true;
+ }
+ 
+ static bool
+ DeclarationKindIsVar(DeclarationKind kind)
+ {
+     return kind == DeclarationKind::Var ||
+            kind == DeclarationKind::BodyLevelFunction ||
+@@ -2503,44 +2506,44 @@ Parser<FullParseHandler, CharT>::globalB
+         return nullptr;
+     }
+     globalsc->bindings = *bindings;
+ 
+     return body;
+ }
+ 
+ template <typename CharT>
+-ParseNode*
++CodeNode*
+ Parser<FullParseHandler, CharT>::moduleBody(ModuleSharedContext* modulesc)
+ {
+     MOZ_ASSERT(checkOptionsCalled);
+ 
+     SourceParseContext modulepc(this, modulesc, nullptr);
+     if (!modulepc.init()) {
+         return null();
+     }
+ 
+     ParseContext::VarScope varScope(this);
+     if (!varScope.init(pc)) {
+         return nullptr;
+     }
+ 
+-    Node mn = handler.newModule(pos());
+-    if (!mn) {
++    CodeNodeType moduleNode = handler.newModule(pos());
++    if (!moduleNode) {
+         return null();
+     }
+ 
+     AutoAwaitIsKeyword<FullParseHandler, CharT> awaitIsKeyword(this, AwaitIsModuleKeyword);
+     ListNode* stmtList = statementList(YieldIsName);
+     if (!stmtList) {
+         return null();
+     }
+ 
+     MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
+-    mn->pn_body = stmtList;
++    moduleNode->setBody(&stmtList->as<ListNode>());
+ 
+     TokenKind tt;
+     if (!tokenStream.getToken(&tt, TokenStream::Operand)) {
+         return null();
+     }
+     if (tt != TokenKind::Eof) {
+         error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt));
+         return null();
+@@ -2584,21 +2587,21 @@ Parser<FullParseHandler, CharT>::moduleB
+     }
+ 
+     Maybe<ModuleScope::Data*> bindings = newModuleScopeData(modulepc.varScope());
+     if (!bindings) {
+         return nullptr;
+     }
+ 
+     modulesc->bindings = *bindings;
+-    return mn;
++    return moduleNode;
+ }
+ 
+ template <typename CharT>
+-SyntaxParseHandler::Node
++SyntaxParseHandler::CodeNodeType
+ Parser<SyntaxParseHandler, CharT>::moduleBody(ModuleSharedContext* modulesc)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return SyntaxParseHandler::NodeFailure;
+ }
+ 
+ bool
+ ParserBase::hasUsedFunctionSpecialName(HandlePropertyName name)
+@@ -2640,38 +2643,38 @@ PerHandlerParser<ParseHandler>::declareF
+         }
+         funbox->setHasThisBinding();
+     }
+ 
+     return true;
+ }
+ 
+ template <class ParseHandler>
+-typename ParseHandler::Node
++typename ParseHandler::NameNodeType
+ PerHandlerParser<ParseHandler>::newInternalDotName(HandlePropertyName name)
+ {
+-    Node nameNode = newName(name);
++    NameNodeType nameNode = newName(name);
+     if (!nameNode) {
+         return null();
+     }
+     if (!noteUsedName(name)) {
+         return null();
+     }
+     return nameNode;
+ }
+ 
+ template <class ParseHandler>
+-typename ParseHandler::Node
++typename ParseHandler::NameNodeType
+ PerHandlerParser<ParseHandler>::newThisName()
+ {
+     return newInternalDotName(context->names().dotThis);
+ }
+ 
+ template <class ParseHandler>
+-typename ParseHandler::Node
++typename ParseHandler::NameNodeType
+ PerHandlerParser<ParseHandler>::newDotGeneratorName()
+ {
+     return newInternalDotName(context->names().dotGenerator);
+ }
+ 
+ bool
+ ParserBase::declareDotGeneratorName()
+ {
+@@ -2831,17 +2834,17 @@ GetAwaitHandling(FunctionAsyncKind async
+ {
+     if (asyncKind == FunctionAsyncKind::SyncFunction) {
+         return AwaitIsName;
+     }
+     return AwaitIsKeyword;
+ }
+ 
+ template <typename CharT>
+-ParseNode*
++CodeNode*
+ Parser<FullParseHandler, CharT>::standaloneFunction(HandleFunction fun,
+                                                     HandleScope enclosingScope,
+                                                     const Maybe<uint32_t>& parameterListEnd,
+                                                     GeneratorKind generatorKind,
+                                                     FunctionAsyncKind asyncKind,
+                                                     Directives inheritedDirectives,
+                                                     Directives* newDirectives)
+ {
+@@ -2873,66 +2876,69 @@ Parser<FullParseHandler, CharT>::standal
+     // Skip function name, if present.
+     if (TokenKindIsPossibleIdentifierName(tt)) {
+         MOZ_ASSERT(anyChars.currentName() == fun->explicitName());
+     } else {
+         MOZ_ASSERT(fun->explicitName() == nullptr);
+         anyChars.ungetToken();
+     }
+ 
+-    Node fn = handler.newFunctionStatement(pos());
+-    if (!fn) {
++    CodeNodeType funNode = handler.newFunctionStatement(pos());
++    if (!funNode) {
+         return null();
+     }
+ 
+     ListNodeType argsbody = handler.newList(ParseNodeKind::ParamsBody, pos());
+     if (!argsbody) {
+         return null();
+     }
+-    fn->pn_body = argsbody;
+-
+-    FunctionBox* funbox = newFunctionBox(fn, fun, /* toStringStart = */ 0, inheritedDirectives,
+-                                         generatorKind, asyncKind);
++    funNode->setBody(argsbody);
++
++    FunctionBox* funbox = newFunctionBox(funNode, fun, /* toStringStart = */ 0,
++                                         inheritedDirectives, generatorKind, asyncKind);
+     if (!funbox) {
+         return null();
+     }
+     funbox->initStandaloneFunction(enclosingScope);
+ 
+     SourceParseContext funpc(this, funbox, newDirectives);
+     if (!funpc.init()) {
+         return null();
+     }
+     funpc.setIsStandaloneFunctionBody();
+ 
+     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
+     AwaitHandling awaitHandling = GetAwaitHandling(asyncKind);
+     AutoAwaitIsKeyword<FullParseHandler, CharT> awaitIsKeyword(this, awaitHandling);
+-    if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &fn, FunctionSyntaxKind::Statement,
++    if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode,
++                                         FunctionSyntaxKind::Statement,
+                                          parameterListEnd, /* isStandaloneFunction = */ true))
+     {
+         return null();
+     }
+ 
+     if (!tokenStream.getToken(&tt, TokenStream::Operand)) {
+         return null();
+     }
+     if (tt != TokenKind::Eof) {
+         error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt));
+         return null();
+     }
+ 
+-    if (!FoldConstants(context, &fn, this)) {
+-        return null();
+-    }
++    ParseNode* node = funNode;
++    if (!FoldConstants(context, &node, this)) {
++        return null();
++    }
++    funNode = &node->as<CodeNode>();
+ 
+     if (!this->setSourceMapInfo()) {
+         return null();
+     }
+ 
+-    return fn;
++    return funNode;
+ }
+ 
+ template <class ParseHandler>
+ bool
+ PerHandlerParser<ParseHandler>::declareFunctionArgumentsObject()
+ {
+     FunctionBox* funbox = pc->functionBox();
+     ParseContext::Scope& funScope = pc->functionScope();
+@@ -3076,17 +3082,17 @@ GeneralParser<ParseHandler, CharT>::func
+     MOZ_ASSERT_IF(pc->isGenerator(), kind != FunctionSyntaxKind::Arrow);
+     MOZ_ASSERT_IF(pc->isGenerator(), type == StatementListBody);
+ 
+     if (pc->needsDotGeneratorName()) {
+         MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
+         if (!declareDotGeneratorName()) {
+             return null();
+         }
+-        Node generator = newDotGeneratorName();
++        NameNodeType generator = newDotGeneratorName();
+         if (!generator) {
+             return null();
+         }
+         if (!handler.prependInitialYield(handler.asList(body), generator)) {
+             return null();
+         }
+     }
+ 
+@@ -3287,17 +3293,17 @@ ParserBase::prefixAccessorName(PropertyT
+ 
+     return AtomizeString(context, str);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+ GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling,
+                                                       FunctionSyntaxKind kind,
+-                                                      Node funcpn)
++                                                      CodeNodeType funNode)
+ {
+     FunctionBox* funbox = pc->functionBox();
+ 
+     bool parenFreeArrow = false;
+     // Modifier for the following tokens.
+     // TokenStream::None for the following cases:
+     //   async a => 1
+     //         ^
+@@ -3362,17 +3368,17 @@ GeneralParser<ParseHandler, CharT>::func
+             return false;
+         }
+     }
+ 
+     ListNodeType argsbody = handler.newList(ParseNodeKind::ParamsBody, firstTokenPos);
+     if (!argsbody) {
+         return false;
+     }
+-    handler.setFunctionFormalParametersAndBody(funcpn, argsbody);
++    handler.setFunctionFormalParametersAndBody(funNode, argsbody);
+ 
+     bool hasArguments = false;
+     if (parenFreeArrow) {
+         hasArguments = true;
+     } else {
+         bool matched;
+         if (!tokenStream.matchToken(&matched, TokenKind::RightParen, TokenStream::Operand)) {
+             return false;
+@@ -3451,17 +3457,17 @@ GeneralParser<ParseHandler, CharT>::func
+ 
+                 Node destruct = destructuringDeclarationWithoutYieldOrAwait(
+                     DeclarationKind::FormalParameter,
+                     yieldHandling, tt);
+                 if (!destruct) {
+                     return false;
+                 }
+ 
+-                if (!noteDestructuredPositionalFormalParameter(funcpn, destruct)) {
++                if (!noteDestructuredPositionalFormalParameter(funNode, destruct)) {
+                     return false;
+                 }
+ 
+                 break;
+               }
+ 
+               default: {
+                 if (!TokenKindIsPossibleIdentifier(tt)) {
+@@ -3473,17 +3479,17 @@ GeneralParser<ParseHandler, CharT>::func
+                     funbox->setStart(anyChars);
+                 }
+ 
+                 RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+                 if (!name) {
+                     return false;
+                 }
+ 
+-                if (!notePositionalFormalParameter(funcpn, name, pos().begin,
++                if (!notePositionalFormalParameter(funNode, name, pos().begin,
+                                                    disallowDuplicateParams, &duplicatedParam))
+                 {
+                     return false;
+                 }
+                 if (duplicatedParam) {
+                     funbox->hasDuplicateParameters = true;
+                 }
+ 
+@@ -3535,17 +3541,17 @@ GeneralParser<ParseHandler, CharT>::func
+                     funbox->length = positionalFormals.length() - 1;
+                 }
+                 funbox->hasParameterExprs = true;
+ 
+                 Node def_expr = assignExprWithoutYieldOrAwait(yieldHandling);
+                 if (!def_expr) {
+                     return false;
+                 }
+-                if (!handler.setLastFunctionFormalParameterDefault(funcpn, def_expr)) {
++                if (!handler.setLastFunctionFormalParameterDefault(funNode, def_expr)) {
+                     return false;
+                 }
+             }
+ 
+             // Setter syntax uniquely requires exactly one argument.
+             if (kind == FunctionSyntaxKind::Setter) {
+                 break;
+             }
+@@ -3597,27 +3603,27 @@ GeneralParser<ParseHandler, CharT>::func
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ template <typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::skipLazyInnerFunction(ParseNode* funcNode, uint32_t toStringStart,
++Parser<FullParseHandler, CharT>::skipLazyInnerFunction(CodeNode* funNode, uint32_t toStringStart,
+                                                        FunctionSyntaxKind kind, bool tryAnnexB)
+ {
+     // When a lazily-parsed function is called, we only fully parse (and emit)
+     // that function, not any of its nested children. The initial syntax-only
+     // parse recorded the free variables of nested functions and their extents,
+     // so we can skip over them after accounting for their free variables.
+ 
+     RootedFunction fun(context, handler.nextLazyInnerFunction());
+     FunctionBox* funbox =
+-        newFunctionBox(funcNode, fun, toStringStart, Directives(/* strict = */ false),
++        newFunctionBox(funNode, fun, toStringStart, Directives(/* strict = */ false),
+                        fun->generatorKind(), fun->asyncKind());
+     if (!funbox) {
+         return false;
+     }
+ 
+     LazyScript* lazy = fun->lazyScript();
+     if (lazy->needsHomeObject()) {
+         funbox->setNeedsHomeObject();
+@@ -3634,30 +3640,32 @@ Parser<FullParseHandler, CharT>::skipLaz
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ template <typename CharT>
+ bool
+-Parser<SyntaxParseHandler, CharT>::skipLazyInnerFunction(Node funcNode, uint32_t toStringStart,
++Parser<SyntaxParseHandler, CharT>::skipLazyInnerFunction(CodeNodeType funNode,
++                                                         uint32_t toStringStart,
+                                                          FunctionSyntaxKind kind,
+                                                          bool tryAnnexB)
+ {
+     MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+-GeneralParser<ParseHandler, CharT>::skipLazyInnerFunction(Node funcNode, uint32_t toStringStart,
++GeneralParser<ParseHandler, CharT>::skipLazyInnerFunction(CodeNodeType funNode,
++                                                          uint32_t toStringStart,
+                                                           FunctionSyntaxKind kind,
+                                                           bool tryAnnexB)
+ {
+-    return asFinalParser()->skipLazyInnerFunction(funcNode, toStringStart, kind, tryAnnexB);
++    return asFinalParser()->skipLazyInnerFunction(funNode, toStringStart, kind, tryAnnexB);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+ GeneralParser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling,
+                                                                    ListNodeType nodeList,
+                                                                    TokenKind* ttp)
+ {
+@@ -3706,62 +3714,63 @@ GeneralParser<ParseHandler, CharT>::tagg
+     handler.setEndPosition(tagArgsList, callSiteObjNode);
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::ListNodeType
+ GeneralParser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling)
+ {
+-    Node pn = noSubstitutionUntaggedTemplate();
+-    if (!pn) {
+-        return null();
+-    }
+-
+-    ListNodeType nodeList = handler.newList(ParseNodeKind::TemplateStringList, pn);
++    NameNodeType literal = noSubstitutionUntaggedTemplate();
++    if (!literal) {
++        return null();
++    }
++
++    ListNodeType nodeList = handler.newList(ParseNodeKind::TemplateStringList, literal);
+     if (!nodeList) {
+         return null();
+     }
+ 
+     TokenKind tt;
+     do {
+         if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) {
+             return null();
+         }
+ 
+-        pn = noSubstitutionUntaggedTemplate();
+-        if (!pn) {
+-            return null();
+-        }
+-
+-        handler.addList(nodeList, pn);
++        literal = noSubstitutionUntaggedTemplate();
++        if (!literal) {
++            return null();
++        }
++
++        handler.addList(nodeList, literal);
+     } while (tt == TokenKind::TemplateHead);
+     return nodeList;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
+-GeneralParser<ParseHandler, CharT>::functionDefinition(Node funcNode, uint32_t toStringStart,
+-                                                       InHandling inHandling, YieldHandling yieldHandling,
++typename ParseHandler::CodeNodeType
++GeneralParser<ParseHandler, CharT>::functionDefinition(CodeNodeType funNode, uint32_t toStringStart,
++                                                       InHandling inHandling,
++                                                       YieldHandling yieldHandling,
+                                                        HandleAtom funName, FunctionSyntaxKind kind,
+                                                        GeneratorKind generatorKind,
+                                                        FunctionAsyncKind asyncKind,
+                                                        bool tryAnnexB /* = false */)
+ {
+     MOZ_ASSERT_IF(kind == FunctionSyntaxKind::Statement, funName);
+ 
+     // When fully parsing a LazyScript, we do not fully reparse its inner
+     // functions, which are also lazy. Instead, their free variables and
+     // source extents are recorded and may be skipped.
+     if (handler.canSkipLazyInnerFunctions()) {
+-        if (!skipLazyInnerFunction(funcNode, toStringStart, kind, tryAnnexB)) {
+-            return null();
+-        }
+-
+-        return funcNode;
++        if (!skipLazyInnerFunction(funNode, toStringStart, kind, tryAnnexB)) {
++            return null();
++        }
++
++        return funNode;
+     }
+ 
+     RootedObject proto(context);
+     if (generatorKind == GeneratorKind::Generator ||
+         asyncKind == FunctionAsyncKind::AsyncFunction)
+     {
+         proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(context, context->global());
+         if (!proto) {
+@@ -3781,17 +3790,17 @@ GeneralParser<ParseHandler, CharT>::func
+     Directives newDirectives = directives;
+ 
+     Position start(keepAtoms, tokenStream);
+ 
+     // Parse the inner function. The following is a loop as we may attempt to
+     // reparse a function due to failed syntax parsing and encountering new
+     // "use foo" directives.
+     while (true) {
+-        if (trySyntaxParseInnerFunction(&funcNode, fun, toStringStart, inHandling, yieldHandling,
++        if (trySyntaxParseInnerFunction(&funNode, fun, toStringStart, inHandling, yieldHandling,
+                                         kind, generatorKind, asyncKind, tryAnnexB, directives,
+                                         &newDirectives))
+         {
+             break;
+         }
+ 
+         // Return on error.
+         if (anyChars.hadError() || directives == newDirectives) {
+@@ -3801,26 +3810,27 @@ GeneralParser<ParseHandler, CharT>::func
+         // Assignment must be monotonic to prevent infinitely attempting to
+         // reparse.
+         MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
+         MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
+         directives = newDirectives;
+ 
+         tokenStream.seek(start);
+ 
+-        // functionFormalParametersAndBody may have already set pn->pn_body before failing.
+-        handler.setFunctionFormalParametersAndBody(funcNode, null());
+-    }
+-
+-    return funcNode;
++        // functionFormalParametersAndBody may have already set body before
++        // failing.
++        handler.setFunctionFormalParametersAndBody(funNode, null());
++    }
++
++    return funNode;
+ }
+ 
+ template <typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::trySyntaxParseInnerFunction(ParseNode** funcNode,
++Parser<FullParseHandler, CharT>::trySyntaxParseInnerFunction(CodeNode** funNode,
+                                                              HandleFunction fun,
+                                                              uint32_t toStringStart,
+                                                              InHandling inHandling,
+                                                              YieldHandling yieldHandling,
+                                                              FunctionSyntaxKind kind,
+                                                              GeneratorKind generatorKind,
+                                                              FunctionAsyncKind asyncKind,
+                                                              bool tryAnnexB,
+@@ -3828,17 +3838,17 @@ Parser<FullParseHandler, CharT>::trySynt
+                                                              Directives* newDirectives)
+ {
+     // Try a syntax parse for this inner function.
+     do {
+         // If we're assuming this function is an IIFE, always perform a full
+         // parse to avoid the overhead of a lazy syntax-only parse. Although
+         // the prediction may be incorrect, IIFEs are common enough that it
+         // pays off for lots of code.
+-        if ((*funcNode)->isLikelyIIFE() &&
++        if ((*funNode)->isLikelyIIFE() &&
+             generatorKind == GeneratorKind::NotGenerator &&
+             asyncKind == FunctionAsyncKind::SyncFunction)
+         {
+             break;
+         }
+ 
+         SyntaxParser* syntaxParser = getSyntaxParser();
+         if (!syntaxParser) {
+@@ -3851,17 +3861,17 @@ Parser<FullParseHandler, CharT>::trySynt
+         Position currentPosition(keepAtoms, tokenStream);
+         if (!syntaxParser->tokenStream.seek(currentPosition, anyChars)) {
+             return false;
+         }
+ 
+         // Make a FunctionBox before we enter the syntax parser, because |pn|
+         // still expects a FunctionBox to be attached to it during BCE, and
+         // the syntax parser cannot attach one to it.
+-        FunctionBox* funbox = newFunctionBox(*funcNode, fun, toStringStart, inheritedDirectives,
++        FunctionBox* funbox = newFunctionBox(*funNode, fun, toStringStart, inheritedDirectives,
+                                              generatorKind, asyncKind);
+         if (!funbox) {
+             return false;
+         }
+         funbox->initWithEnclosingParseContext(pc, kind);
+ 
+         SyntaxParseHandler::Node syntaxNode =
+             syntaxParser->innerFunctionForFunctionBox(SyntaxParseHandler::NodeGeneric, pc, funbox,
+@@ -3883,88 +3893,88 @@ Parser<FullParseHandler, CharT>::trySynt
+ 
+         // Advance this parser over tokens processed by the syntax parser.
+         Position currentSyntaxPosition(keepAtoms, syntaxParser->tokenStream);
+         if (!tokenStream.seek(currentSyntaxPosition, syntaxParser->anyChars)) {
+             return false;
+         }
+ 
+         // Update the end position of the parse node.
+-        (*funcNode)->pn_pos.end = anyChars.currentToken().pos.end;
++        (*funNode)->pn_pos.end = anyChars.currentToken().pos.end;
+ 
+         // Append possible Annex B function box only upon successfully parsing.
+         if (tryAnnexB) {
+             if (!pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox)) {
+                 return false;
+             }
+         }
+ 
+         return true;
+     } while (false);
+ 
+     // We failed to do a syntax parse above, so do the full parse.
+-    Node innerFunc =
+-        innerFunction(*funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
++    CodeNodeType innerFunc =
++        innerFunction(*funNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
+                       generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
+     if (!innerFunc) {
+         return false;
+     }
+ 
+-    *funcNode = innerFunc;
++    *funNode = innerFunc;
+     return true;
+ }
+ 
+ template <typename CharT>
+ bool
+-Parser<SyntaxParseHandler, CharT>::trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun,
++Parser<SyntaxParseHandler, CharT>::trySyntaxParseInnerFunction(CodeNodeType* funNode, HandleFunction fun,
+                                                                uint32_t toStringStart,
+                                                                InHandling inHandling,
+                                                                YieldHandling yieldHandling,
+                                                                FunctionSyntaxKind kind,
+                                                                GeneratorKind generatorKind,
+                                                                FunctionAsyncKind asyncKind,
+                                                                bool tryAnnexB,
+                                                                Directives inheritedDirectives,
+                                                                Directives* newDirectives)
+ {
+     // This is already a syntax parser, so just parse the inner function.
+-    Node innerFunc =
+-        innerFunction(*funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
++    CodeNodeType innerFunc =
++        innerFunction(*funNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
+                       generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
+ 
+     if (!innerFunc) {
+         return false;
+     }
+ 
+-    *funcNode = innerFunc;
++    *funNode = innerFunc;
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun,
++GeneralParser<ParseHandler, CharT>::trySyntaxParseInnerFunction(CodeNodeType* funNode, HandleFunction fun,
+                                                                 uint32_t toStringStart,
+                                                                 InHandling inHandling,
+                                                                 YieldHandling yieldHandling,
+                                                                 FunctionSyntaxKind kind,
+                                                                 GeneratorKind generatorKind,
+                                                                 FunctionAsyncKind asyncKind,
+                                                                 bool tryAnnexB,
+                                                                 Directives inheritedDirectives,
+                                                                 Directives* newDirectives)
+ {
+-    return asFinalParser()->trySyntaxParseInnerFunction(funcNode, fun, toStringStart, inHandling,
++    return asFinalParser()->trySyntaxParseInnerFunction(funNode, fun, toStringStart, inHandling,
+                                                         yieldHandling, kind, generatorKind,
+                                                         asyncKind, tryAnnexB, inheritedDirectives,
+                                                         newDirectives);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
+-GeneralParser<ParseHandler, CharT>::innerFunctionForFunctionBox(Node funcNode,
++typename ParseHandler::CodeNodeType
++GeneralParser<ParseHandler, CharT>::innerFunctionForFunctionBox(CodeNodeType funNode,
+                                                                 ParseContext* outerpc,
+                                                                 FunctionBox* funbox,
+                                                                 InHandling inHandling,
+                                                                 YieldHandling yieldHandling,
+                                                                 FunctionSyntaxKind kind,
+                                                                 Directives* newDirectives)
+ {
+     // Note that it is possible for outerpc != this->pc, as we may be
+@@ -3973,53 +3983,53 @@ GeneralParser<ParseHandler, CharT>::inne
+     // instead of the current top of the stack of the syntax parser.
+ 
+     // Push a new ParseContext.
+     SourceParseContext funpc(this, funbox, newDirectives);
+     if (!funpc.init()) {
+         return null();
+     }
+ 
+-    if (!functionFormalParametersAndBody(inHandling, yieldHandling, &funcNode, kind)) {
++    if (!functionFormalParametersAndBody(inHandling, yieldHandling, &funNode, kind)) {
+         return null();
+     }
+ 
+     if (!leaveInnerFunction(outerpc)) {
+         return null();
+     }
+ 
+-    return funcNode;
+-}
+-
+-template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
+-GeneralParser<ParseHandler, CharT>::innerFunction(Node funcNode, ParseContext* outerpc,
++    return funNode;
++}
++
++template <class ParseHandler, typename CharT>
++typename ParseHandler::CodeNodeType
++GeneralParser<ParseHandler, CharT>::innerFunction(CodeNodeType funNode, ParseContext* outerpc,
+                                                   HandleFunction fun, uint32_t toStringStart,
+                                                   InHandling inHandling,
+                                                   YieldHandling yieldHandling,
+                                                   FunctionSyntaxKind kind,
+                                                   GeneratorKind generatorKind,
+                                                   FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                                   Directives inheritedDirectives,
+                                                   Directives* newDirectives)
+ {
+     // Note that it is possible for outerpc != this->pc, as we may be
+     // attempting to syntax parse an inner function from an outer full
+     // parser. In that case, outerpc is a SourceParseContext from the full parser
+     // instead of the current top of the stack of the syntax parser.
+ 
+-    FunctionBox* funbox = newFunctionBox(funcNode, fun, toStringStart, inheritedDirectives,
++    FunctionBox* funbox = newFunctionBox(funNode, fun, toStringStart, inheritedDirectives,
+                                          generatorKind, asyncKind);
+     if (!funbox) {
+         return null();
+     }
+     funbox->initWithEnclosingParseContext(outerpc, kind);
+ 
+-    Node innerFunc =
+-        innerFunctionForFunctionBox(funcNode, outerpc, funbox, inHandling, yieldHandling, kind,
++    CodeNodeType innerFunc =
++        innerFunctionForFunctionBox(funNode, outerpc, funbox, inHandling, yieldHandling, kind,
+                                      newDirectives);
+     if (!innerFunc) {
+         return null();
+     }
+ 
+     // Append possible Annex B function box only upon successfully parsing.
+     if (tryAnnexB) {
+         if (!pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox)) {
+@@ -4038,61 +4048,61 @@ GeneralParser<ParseHandler, CharT>::appe
+     if (!cookedNode) {
+         return false;
+     }
+ 
+     JSAtom* atom = tokenStream.getRawTemplateStringAtom();
+     if (!atom) {
+         return false;
+     }
+-    Node rawNode = handler.newTemplateStringLiteral(atom, pos());
++    NameNodeType rawNode = handler.newTemplateStringLiteral(atom, pos());
+     if (!rawNode) {
+         return false;
+     }
+ 
+     handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
+     return true;
+ }
+ 
+ template <typename CharT>
+-ParseNode*
++CodeNode*
+ Parser<FullParseHandler, CharT>::standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart,
+                                                         bool strict, GeneratorKind generatorKind,
+                                                         FunctionAsyncKind asyncKind)
+ {
+     MOZ_ASSERT(checkOptionsCalled);
+ 
+-    Node pn = handler.newFunctionStatement(pos());
+-    if (!pn) {
++    CodeNodeType funNode = handler.newFunctionStatement(pos());
++    if (!funNode) {
+         return null();
+     }
+ 
+     Directives directives(strict);
+-    FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, directives, generatorKind,
++    FunctionBox* funbox = newFunctionBox(funNode, fun, toStringStart, directives, generatorKind,
+                                          asyncKind);
+     if (!funbox) {
+         return null();
+     }
+     funbox->initFromLazyFunction();
+ 
+     Directives newDirectives = directives;
+     SourceParseContext funpc(this, funbox, &newDirectives);
+     if (!funpc.init()) {
+         return null();
+     }
+ 
+-    // Our tokenStream has no current token, so pn's position is garbage.
++    // Our tokenStream has no current token, so funNode's position is garbage.
+     // Substitute the position of the first token in our source.  If the
+     // function is a not-async arrow, use TokenStream::Operand to keep
+     // verifyConsistentModifier from complaining (we will use
+     // TokenStream::Operand in functionArguments).
+     Modifier modifier = (fun->isArrow() && asyncKind == FunctionAsyncKind::SyncFunction)
+                         ? TokenStream::Operand
+                         : TokenStream::None;
+-    if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier)) {
++    if (!tokenStream.peekTokenPos(&funNode->pn_pos, modifier)) {
+         return null();
+     }
+ 
+     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
+     FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
+     if (fun->isClassConstructor()) {
+         syntaxKind = FunctionSyntaxKind::ClassConstructor;
+     } else if (fun->isMethod()) {
+@@ -4100,33 +4110,35 @@ Parser<FullParseHandler, CharT>::standal
+     } else if (fun->isGetter()) {
+         syntaxKind = FunctionSyntaxKind::Getter;
+     } else if (fun->isSetter()) {
+         syntaxKind = FunctionSyntaxKind::Setter;
+     } else if (fun->isArrow()) {
+         syntaxKind = FunctionSyntaxKind::Arrow;
+     }
+ 
+-    if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &pn, syntaxKind)) {
++    if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode, syntaxKind)) {
+         MOZ_ASSERT(directives == newDirectives);
+         return null();
+     }
+ 
+-    if (!FoldConstants(context, &pn, this)) {
+-        return null();
+-    }
+-
+-    return pn;
++    ParseNode* node = funNode;
++    if (!FoldConstants(context, &node, this)) {
++        return null();
++    }
++    funNode = &node->as<CodeNode>();
++
++    return funNode;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+ GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandling,
+                                                                     YieldHandling yieldHandling,
+-                                                                    Node* pn,
++                                                                    CodeNodeType* funNode,
+                                                                     FunctionSyntaxKind kind,
+                                                                     const Maybe<uint32_t>& parameterListEnd /* = Nothing() */,
+                                                                     bool isStandaloneFunction /* = false */)
+ {
+     // Given a properly initialized parse context, try to parse an actual
+     // function without concern for conversion to strict mode, use of lazy
+     // parsing and such.
+ 
+@@ -4137,17 +4149,17 @@ GeneralParser<ParseHandler, CharT>::func
+     // function bodies are parsed with different yield/await settings.
+     {
+         AwaitHandling awaitHandling =
+             (funbox->isAsync() || (kind == FunctionSyntaxKind::Arrow && awaitIsKeyword()))
+             ? AwaitIsKeyword
+             : AwaitIsName;
+         AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, awaitHandling);
+         AutoInParametersOfAsyncFunction<ParseHandler, CharT> inParameters(this, funbox->isAsync());
+-        if (!functionArguments(yieldHandling, kind, *pn)) {
++        if (!functionArguments(yieldHandling, kind, *funNode)) {
+             return false;
+         }
+     }
+ 
+     Maybe<ParseContext::VarScope> varScope;
+     if (funbox->hasParameterExprs) {
+         varScope.emplace(this);
+         if (!varScope->init(pc)) {
+@@ -4233,17 +4245,17 @@ GeneralParser<ParseHandler, CharT>::func
+             // because of different context.
+             // It should already be checked before this point.
+             nameYieldHandling = YieldIsName;
+         }
+ 
+         // We already use the correct await-handling at this point, therefore
+         // we don't need call AutoAwaitIsKeyword here.
+ 
+-        uint32_t nameOffset = handler.getFunctionNameOffset(*pn, anyChars);
++        uint32_t nameOffset = handler.getFunctionNameOffset(*funNode, anyChars);
+         if (!checkBindingIdentifier(propertyName, nameOffset, nameYieldHandling)) {
+             return false;
+         }
+     }
+ 
+     if (bodyType == StatementListBody) {
+         MUST_MATCH_TOKEN_MOD_WITH_REPORT_OR(TokenKind::RightCurly, TokenStream::Operand,
+                                             reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
+@@ -4266,24 +4278,24 @@ GeneralParser<ParseHandler, CharT>::func
+         funbox->setNeedsHomeObject();
+     }
+ 
+     if (!finishFunction(isStandaloneFunction)) {
+         return false;
+     }
+ 
+     handler.setEndPosition(body, pos().begin);
+-    handler.setEndPosition(*pn, pos().end);
+-    handler.setFunctionBody(*pn, body);
++    handler.setEndPosition(*funNode, pos().end);
++    handler.setFunctionBody(*funNode, body);
+ 
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::CodeNodeType
+ GeneralParser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart,
+                                                  YieldHandling yieldHandling,
+                                                  DefaultHandling defaultHandling,
+                                                  FunctionAsyncKind asyncKind)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function));
+ 
+     // In sloppy mode, Annex B.3.2 allows labelled function declarations.
+@@ -4350,37 +4362,37 @@ GeneralParser<ParseHandler, CharT>::func
+                ? DeclarationKind::ModuleBodyLevelFunction
+                : DeclarationKind::BodyLevelFunction;
+     }
+ 
+     if (!noteDeclaredName(name, kind, pos())) {
+         return null();
+     }
+ 
+-    Node pn = handler.newFunctionStatement(pos());
+-    if (!pn) {
++    CodeNodeType funNode = handler.newFunctionStatement(pos());
++    if (!funNode) {
+         return null();
+     }
+ 
+     // Under sloppy mode, try Annex B.3.3 semantics. If making an additional
+     // 'var' binding of the same name does not throw an early error, do so.
+     // This 'var' binding would be assigned the function object when its
+     // declaration is reached, not at the start of the block.
+     //
+     // This semantics is implemented upon Scope exit in
+     // Scope::propagateAndMarkAnnexBFunctionBoxes.
+     bool tryAnnexB = kind == DeclarationKind::SloppyLexicalFunction;
+ 
+     YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
+-    return functionDefinition(pn, toStringStart, InAllowed, newYieldHandling, name,
++    return functionDefinition(funNode, toStringStart, InAllowed, newYieldHandling, name,
+                               FunctionSyntaxKind::Statement, generatorKind, asyncKind, tryAnnexB);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::CodeNodeType
+ GeneralParser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart,
+                                                  InvokedPrediction invoked,
+                                                  FunctionAsyncKind asyncKind)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function));
+ 
+     AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, GetAwaitHandling(asyncKind));
+     GeneratorKind generatorKind = GeneratorKind::NotGenerator;
+@@ -4403,26 +4415,26 @@ GeneralParser<ParseHandler, CharT>::func
+         name = bindingIdentifier(yieldHandling);
+         if (!name) {
+             return null();
+         }
+     } else {
+         anyChars.ungetToken();
+     }
+ 
+-    Node pn = handler.newFunctionExpression(pos());
+-    if (!pn) {
++    CodeNodeType funNode = handler.newFunctionExpression(pos());
++    if (!funNode) {
+         return null();
+     }
+ 
+     if (invoked) {
+-        pn = handler.setLikelyIIFE(pn);
+-    }
+-
+-    return functionDefinition(pn, toStringStart, InAllowed, yieldHandling, name,
++        funNode = handler.setLikelyIIFE(funNode);
++    }
++
++    return functionDefinition(funNode, toStringStart, InAllowed, yieldHandling, name,
+                               FunctionSyntaxKind::Expression, generatorKind, asyncKind);
+ }
+ 
+ /*
+  * Return true if this node, known to be an unparenthesized string literal,
+  * could be the string of a directive in a Directive Prologue. Directive
+  * strings never contain escape sequences or line continuations.
+  * isEscapeFreeStringLiteral, below, checks whether the node itself could be
+@@ -4910,26 +4922,26 @@ GeneralParser<ParseHandler, CharT>::bind
+         }
+         assign = handler.asBinary(node);
+     }
+ 
+     return assign;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::NameNodeType
+ GeneralParser<ParseHandler, CharT>::bindingIdentifier(DeclarationKind kind,
+                                                       YieldHandling yieldHandling)
+ {
+     RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+     if (!name) {
+         return null();
+     }
+ 
+-    Node binding = newName(name);
++    NameNodeType binding = newName(name);
+     if (!binding || !noteDeclaredName(name, kind, pos())) {
+         return null();
+     }
+ 
+     return binding;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+@@ -4992,17 +5004,17 @@ GeneralParser<ParseHandler, CharT>::obje
+                 return null();
+             }
+ 
+             if (!TokenKindIsPossibleIdentifierName(tt)) {
+                 error(JSMSG_NO_VARIABLE_NAME);
+                 return null();
+             }
+ 
+-            Node inner = bindingIdentifier(kind, yieldHandling);
++            NameNodeType inner = bindingIdentifier(kind, yieldHandling);
+             if (!inner) {
+                 return null();
+             }
+ 
+             if (!handler.addSpreadProperty(literal, begin, inner)) {
+                 return null();
+             }
+         } else {
+@@ -5041,30 +5053,30 @@ GeneralParser<ParseHandler, CharT>::obje
+                 if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) {
+                     return null();
+                 }
+             } else if (propType == PropertyType::Shorthand) {
+                 // Handle e.g., |var {x, y} = o| as destructuring shorthand
+                 // for |var {x: x, y: y} = o|.
+                 MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
+ 
+-                Node binding = bindingIdentifier(kind, yieldHandling);
++                NameNodeType binding = bindingIdentifier(kind, yieldHandling);
+                 if (!binding) {
+                     return null();
+                 }
+ 
+-                if (!handler.addShorthand(literal, propName, binding)) {
++                if (!handler.addShorthand(literal, handler.asName(propName), binding)) {
+                     return null();
+                 }
+             } else if (propType == PropertyType::CoverInitializedName) {
+                 // Handle e.g., |var {x=1, y=2} = o| as destructuring
+                 // shorthand with default values.
+                 MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
+ 
+-                Node binding = bindingIdentifier(kind, yieldHandling);
++                NameNodeType binding = bindingIdentifier(kind, yieldHandling);
+                 if (!binding) {
+                     return null();
+                 }
+ 
+                 tokenStream.consumeKnownToken(TokenKind::Assign);
+ 
+                 BinaryNodeType bindingExpr = bindingInitializer(binding, kind, yieldHandling);
+                 if (!bindingExpr) {
+@@ -5329,17 +5341,17 @@ GeneralParser<ParseHandler, CharT>::decl
+         return null();
+     }
+ 
+     return handler.newAssignment(ParseNodeKind::Assign, pattern, init);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+-GeneralParser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding,
++GeneralParser<ParseHandler, CharT>::initializerInNameDeclaration(NameNodeType binding,
+                                                                  DeclarationKind declKind,
+                                                                  bool initialDeclaration,
+                                                                  YieldHandling yieldHandling,
+                                                                  ParseNodeKind* forHeadKind,
+                                                                  Node* forInOrOfExpression)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assign));
+ 
+@@ -5392,17 +5404,17 @@ GeneralParser<ParseHandler, CharT>::init
+             *forHeadKind = ParseNodeKind::ForHead;
+         }
+     }
+ 
+     return handler.finishInitializerAssignment(binding, initializer);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::NameNodeType
+ GeneralParser<ParseHandler, CharT>::declarationName(DeclarationKind declKind, TokenKind tt,
+                                                     bool initialDeclaration,
+                                                     YieldHandling yieldHandling,
+                                                     ParseNodeKind* forHeadKind,
+                                                     Node* forInOrOfExpression)
+ {
+     // Anything other than possible identifier is an error.
+     if (!TokenKindIsPossibleIdentifier(tt)) {
+@@ -5410,17 +5422,17 @@ GeneralParser<ParseHandler, CharT>::decl
+         return null();
+     }
+ 
+     RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+     if (!name) {
+         return null();
+     }
+ 
+-    Node binding = newName(name);
++    NameNodeType binding = newName(name);
+     if (!binding) {
+         return null();
+     }
+ 
+     TokenPos namePos = pos();
+ 
+     // The '=' context after a variable name in a declaration is an opportunity
+     // for ASI, and thus for the next token to start an ExpressionStatement:
+@@ -5635,25 +5647,25 @@ Parser<FullParseHandler, CharT>::namedIm
+                 }
+             }
+ 
+             RootedPropertyName bindingAtom(context, importedBinding());
+             if (!bindingAtom) {
+                 return false;
+             }
+ 
+-            Node bindingName = newName(bindingAtom);
++            NameNodeType bindingName = newName(bindingAtom);
+             if (!bindingName) {
+                 return false;
+             }
+             if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) {
+                 return false;
+             }
+ 
+-            Node importNameNode = newName(importName, importNamePos);
++            NameNodeType importNameNode = newName(importName, importNamePos);
+             if (!importNameNode) {
+                 return false;
+             }
+ 
+             BinaryNodeType importSpec = handler.newImportSpec(importNameNode, bindingName);
+             if (!importSpec) {
+                 return false;
+             }
+@@ -5676,30 +5688,30 @@ Parser<FullParseHandler, CharT>::namedIm
+         }
+     } else {
+         MOZ_ASSERT(tt == TokenKind::Mul);
+ 
+         MUST_MATCH_TOKEN_OR(TokenKind::As, JSMSG_AS_AFTER_IMPORT_STAR, false);
+ 
+         MUST_MATCH_TOKEN_FUNC_OR(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME, false);
+ 
+-        Node importName = newName(context->names().star);
++        NameNodeType importName = newName(context->names().star);
+         if (!importName) {
+             return false;
+         }
+ 
+         // Namespace imports are are not indirect bindings but lexical
+         // definitions that hold a module namespace object. They are treated
+         // as const variables which are initialized during the
+         // ModuleInstantiate step.
+         RootedPropertyName bindingName(context, importedBinding());
+         if (!bindingName) {
+             return false;
+         }
+-        Node bindingNameNode = newName(bindingName);
++        NameNodeType bindingNameNode = newName(bindingName);
+         if (!bindingNameNode) {
+             return false;
+         }
+         if (!noteDeclaredName(bindingName, DeclarationKind::Const, pos())) {
+             return false;
+         }
+ 
+         // The namespace import name is currently required to live on the
+@@ -5803,17 +5815,17 @@ Parser<FullParseHandler, CharT>::importD
+             return null();
+         }
+ 
+         MUST_MATCH_TOKEN(TokenKind::From, JSMSG_FROM_AFTER_IMPORT_CLAUSE);
+ 
+         MUST_MATCH_TOKEN(TokenKind::String, JSMSG_MODULE_SPEC_AFTER_FROM);
+     }
+ 
+-    Node moduleSpec = stringLiteral();
++    NameNodeType moduleSpec = stringLiteral();
+     if (!moduleSpec) {
+         return null();
+     }
+ 
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+@@ -5984,17 +5996,17 @@ GeneralParser<ParseHandler, CharT>::chec
+     return asFinalParser()->checkExportedNamesForObjectBinding(obj);
+ }
+ 
+ template<typename CharT>
+ bool
+ Parser<FullParseHandler, CharT>::checkExportedNamesForDeclaration(ParseNode* node)
+ {
+     if (node->isKind(ParseNodeKind::Name)) {
+-        if (!checkExportedName(node->pn_atom)) {
++        if (!checkExportedName(node->as<NameNode>().atom())) {
+             return false;
+         }
+     } else if (node->isKind(ParseNodeKind::Array)) {
+         if (!checkExportedNamesForArrayBinding(&node->as<ListNode>())) {
+             return false;
+         }
+     } else {
+         MOZ_ASSERT(node->isKind(ParseNodeKind::Object));
+@@ -6052,64 +6064,64 @@ template<class ParseHandler, typename Ch
+ inline bool
+ GeneralParser<ParseHandler, CharT>::checkExportedNamesForDeclarationList(ListNodeType node)
+ {
+     return asFinalParser()->checkExportedNamesForDeclarationList(node);
+ }
+ 
+ template<typename CharT>
+ inline bool
+-Parser<FullParseHandler, CharT>::checkExportedNameForClause(ParseNode* node)
+-{
+-    return checkExportedName(node->pn_atom);
++Parser<FullParseHandler, CharT>::checkExportedNameForClause(NameNode* nameNode)
++{
++    return checkExportedName(nameNode->atom());
+ }
+ 
+ template<typename CharT>
+ inline bool
+-Parser<SyntaxParseHandler, CharT>::checkExportedNameForClause(Node node)
++Parser<SyntaxParseHandler, CharT>::checkExportedNameForClause(NameNodeType nameNode)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template<class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::checkExportedNameForClause(Node node)
+-{
+-    return asFinalParser()->checkExportedNameForClause(node);
++GeneralParser<ParseHandler, CharT>::checkExportedNameForClause(NameNodeType nameNode)
++{
++    return asFinalParser()->checkExportedNameForClause(nameNode);
+ }
+ 
+ template<typename CharT>
+ bool
+-Parser<FullParseHandler, CharT>::checkExportedNameForFunction(ParseNode* node)
+-{
+-    return checkExportedName(node->pn_funbox->function()->explicitName());
++Parser<FullParseHandler, CharT>::checkExportedNameForFunction(CodeNode* funNode)
++{
++    return checkExportedName(funNode->funbox()->function()->explicitName());
+ }
+ 
+ template<typename CharT>
+ inline bool
+-Parser<SyntaxParseHandler, CharT>::checkExportedNameForFunction(Node node)
++Parser<SyntaxParseHandler, CharT>::checkExportedNameForFunction(CodeNodeType funNode)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+ }
+ 
+ template<class ParseHandler, typename CharT>
+ inline bool
+-GeneralParser<ParseHandler, CharT>::checkExportedNameForFunction(Node node)
+-{
+-    return asFinalParser()->checkExportedNameForFunction(node);
++GeneralParser<ParseHandler, CharT>::checkExportedNameForFunction(CodeNodeType funNode)
++{
++    return asFinalParser()->checkExportedNameForFunction(funNode);
+ }
+ 
+ template<typename CharT>
+ bool
+ Parser<FullParseHandler, CharT>::checkExportedNameForClass(ClassNode* classNode)
+ {
+     MOZ_ASSERT(classNode->names());
+-    return checkExportedName(classNode->names()->innerBinding()->pn_atom);
++    return checkExportedName(classNode->names()->innerBinding()->atom());
+ }
+ 
+ template<typename CharT>
+ inline bool
+ Parser<SyntaxParseHandler, CharT>::checkExportedNameForClass(ClassNodeType classNode)
+ {
+     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+     return false;
+@@ -6163,17 +6175,17 @@ GeneralParser<ParseHandler, CharT>::expo
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::From));
+ 
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     MUST_MATCH_TOKEN(TokenKind::String, JSMSG_MODULE_SPEC_AFTER_FROM);
+ 
+-    Node moduleSpec = stringLiteral();
++    NameNodeType moduleSpec = stringLiteral();
+     if (!moduleSpec) {
+         return null();
+     }
+ 
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+@@ -6222,17 +6234,17 @@ template<typename CharT>
+ bool
+ Parser<FullParseHandler, CharT>::checkLocalExportNames(ListNode* node)
+ {
+     // ES 2017 draft 15.2.3.1.
+     for (ParseNode* next : node->contents()) {
+         ParseNode* name = next->as<BinaryNode>().left();
+         MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
+ 
+-        RootedPropertyName ident(context, name->pn_atom->asPropertyName());
++        RootedPropertyName ident(context, name->as<NameNode>().atom()->asPropertyName());
+         if (!checkLocalExportName(ident, name->pn_pos.begin)) {
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+@@ -6278,30 +6290,30 @@ GeneralParser<ParseHandler, CharT>::expo
+             break;
+         }
+ 
+         if (!TokenKindIsPossibleIdentifierName(tt)) {
+             error(JSMSG_NO_BINDING_NAME);
+             return null();
+         }
+ 
+-        Node bindingName = newName(anyChars.currentName());
++        NameNodeType bindingName = newName(anyChars.currentName());
+         if (!bindingName) {
+             return null();
+         }
+ 
+         bool foundAs;
+         if (!tokenStream.matchToken(&foundAs, TokenKind::As)) {
+             return null();
+         }
+         if (foundAs) {
+             MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
+         }
+ 
+-        Node exportName = newName(anyChars.currentName());
++        NameNodeType exportName = newName(anyChars.currentName());
+         if (!exportName) {
+             return null();
+         }
+ 
+         if (!checkExportedNameForClause(exportName)) {
+             return null();
+         }
+ 
+@@ -6414,17 +6426,17 @@ GeneralParser<ParseHandler, CharT>::expo
+ 
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function));
+ 
+     Node kid = functionStmt(toStringStart, YieldIsName, NameRequired, asyncKind);
+     if (!kid) {
+         return null();
+     }
+ 
+-    if (!checkExportedNameForFunction(kid)) {
++    if (!checkExportedNameForFunction(handler.asCode(kid))) {
+         return null();
+     }
+ 
+     UnaryNodeType node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+     if (!node) {
+         return null();
+     }
+ 
+@@ -6560,17 +6572,17 @@ template <class ParseHandler, typename C
+ typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
+ {
+     if (!abortIfSyntaxParser()) {
+         return null();
+     }
+ 
+     HandlePropertyName name = context->names().default_;
+-    Node nameNode = newName(name);
++    NameNodeType nameNode = newName(name);
+     if (!nameNode) {
+         return null();
+     }
+     if (!noteDeclaredName(name, DeclarationKind::Const, pos())) {
+         return null();
+     }
+ 
+     Node kid = assignExpr(InAllowed, YieldIsName, TripledotProhibited);
+@@ -7623,17 +7635,17 @@ GeneralParser<ParseHandler, CharT>::labe
+         return functionStmt(pos().begin, yieldHandling, NameRequired);
+     }
+ 
+     anyChars.ungetToken();
+     return statement(yieldHandling);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::LabeledStatementType
+ GeneralParser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling)
+ {
+     RootedPropertyName label(context, labelIdentifier(yieldHandling));
+     if (!label) {
+         return null();
+     }
+ 
+     auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
+@@ -8113,24 +8125,24 @@ GeneralParser<ParseHandler, CharT>::clas
+             if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) {
+                 funName = propAtom;
+             }
+         }
+ 
+         // Calling toString on constructors need to return the source text for
+         // the entire class. The end offset is unknown at this point in
+         // parsing and will be amended when class parsing finishes below.
+-        Node fn = methodDefinition(isConstructor ? classStartOffset : nameOffset,
+-                                   propType, funName);
+-        if (!fn) {
++        CodeNodeType funNode = methodDefinition(isConstructor ? classStartOffset : nameOffset,
++                                                propType, funName);
++        if (!funNode) {
+             return null();
+         }
+ 
+         AccessorType atype = ToAccessorType(propType);
+-        if (!handler.addClassMethodDefinition(classMethods, propName, fn, atype, isStatic)) {
++        if (!handler.addClassMethodDefinition(classMethods, propName, funNode, atype, isStatic)) {
+             return null();
+         }
+     }
+ 
+     // Amend the toStringEnd offset for the constructor now that we've
+     // finished parsing the class.
+     uint32_t classEndOffset = pos().end;
+     if (FunctionBox* ctorbox = classStmt.constructorBox) {
+@@ -8143,33 +8155,33 @@ GeneralParser<ParseHandler, CharT>::clas
+     Node nameNode = null();
+     Node methodsOrBlock = classMethods;
+     if (name) {
+         // The inner name is immutable.
+         if (!noteDeclaredName(name, DeclarationKind::Const, namePos)) {
+             return null();
+         }
+ 
+-        Node innerName = newName(name, namePos);
++        NameNodeType innerName = newName(name, namePos);
+         if (!innerName) {
+             return null();
+         }
+ 
+         Node classBlock = finishLexicalScope(*innerScope, classMethods);
+         if (!classBlock) {
+             return null();
+         }
+ 
+         methodsOrBlock = classBlock;
+ 
+         // Pop the inner scope.
+         innerScope.reset();
+         innerScopeStmt.reset();
+ 
+-        Node outerName = null();
++        NameNodeType outerName = null();
+         if (classContext == ClassStatement) {
+             // The outer name is mutable.
+             if (!noteDeclaredName(name, DeclarationKind::Class, namePos)) {
+                 return null();
+             }
+ 
+             outerName = newName(name, namePos);
+             if (!outerName) {
+@@ -9081,22 +9093,22 @@ GeneralParser<ParseHandler, CharT>::assi
+             //   async [no LineTerminator here] ArrowFormalParameters ...
+             if (TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TokenKind::LeftParen) {
+                 asyncKind = FunctionAsyncKind::AsyncFunction;
+             } else {
+                 anyChars.ungetToken();
+             }
+         }
+ 
+-        Node pn = handler.newArrowFunction(pos());
+-        if (!pn) {
+-            return null();
+-        }
+-
+-        return functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr,
++        CodeNodeType funNode = handler.newArrowFunction(pos());
++        if (!funNode) {
++            return null();
++        }
++
++        return functionDefinition(funNode, toStringStart, inHandling, yieldHandling, nullptr,
+                                   FunctionSyntaxKind::Arrow, GeneratorKind::NotGenerator,
+                                   asyncKind);
+     }
+ 
+     MOZ_ALWAYS_TRUE(tokenStream.getToken(&tokenAfterLHS, TokenStream::Operand));
+ 
+     ParseNodeKind kind;
+     switch (tokenAfterLHS) {
+@@ -9556,17 +9568,17 @@ GeneralParser<ParseHandler, CharT>::memb
+                 return null();
+             }
+ 
+             if (isSpread) {
+                 handler.setOp(lhs, JSOP_SPREADNEW);
+             }
+         }
+     } else if (tt == TokenKind::Super) {
+-        Node thisName = newThisName();
++        NameNodeType thisName = newThisName();
+         if (!thisName) {
+             return null();
+         }
+         lhs = handler.newSuperBase(thisName, pos());
+         if (!lhs) {
+             return null();
+         }
+     } else if (tt == TokenKind::Import) {
+@@ -9598,17 +9610,17 @@ GeneralParser<ParseHandler, CharT>::memb
+             }
+             if (TokenKindIsPossibleIdentifierName(tt)) {
+                 PropertyName* field = anyChars.currentName();
+                 if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
+                     error(JSMSG_BAD_SUPERPROP, "property");
+                     return null();
+                 }
+ 
+-                Node name = handler.newPropertyName(field, pos());
++                NameNodeType name = handler.newPropertyName(field, pos());
+                 if (!name) {
+                     return null();
+                 }
+ 
+                 nextMember = handler.newPropertyAccess(lhs, name);
+                 if (!nextMember) {
+                     return null();
+                 }
+@@ -9660,17 +9672,17 @@ GeneralParser<ParseHandler, CharT>::memb
+                 if (!nextMember) {
+                     return null();
+                 }
+ 
+                 if (isSpread) {
+                     handler.setOp(nextMember, JSOP_SPREADSUPERCALL);
+                 }
+ 
+-                Node thisName = newThisName();
++                NameNodeType thisName = newThisName();
+                 if (!thisName) {
+                     return null();
+                 }
+ 
+                 nextMember = handler.newSetThis(thisName, nextMember);
+                 if (!nextMember) {
+                     return null();
+                 }
+@@ -9776,24 +9788,24 @@ GeneralParser<ParseHandler, CharT>::memb
+         error(JSMSG_BAD_SUPER);
+         return null();
+     }
+ 
+     return lhs;
+ }
+ 
+ template <class ParseHandler>
+-inline typename ParseHandler::Node
++inline typename ParseHandler::NameNodeType
+ PerHandlerParser<ParseHandler>::newName(PropertyName* name)
+ {
+     return newName(name, pos());
+ }
+ 
+ template <class ParseHandler>
+-inline typename ParseHandler::Node
++inline typename ParseHandler::NameNodeType
+ PerHandlerParser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
+ {
+     return handler.newName(name, pos, context);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+ GeneralParser<ParseHandler, CharT>::checkLabelOrIdentifierReference(PropertyName* ident,
+@@ -9926,33 +9938,33 @@ GeneralParser<ParseHandler, CharT>::bind
+     RootedPropertyName ident(context, anyChars.currentName());
+     if (!checkBindingIdentifier(ident, pos().begin, yieldHandling, hint)) {
+         return nullptr;
+     }
+     return ident;
+ }
+ 
+ template <class ParseHandler>
+-typename ParseHandler::Node
++typename ParseHandler::NameNodeType
+ PerHandlerParser<ParseHandler>::identifierReference(Handle<PropertyName*> name)
+ {
+-    Node pn = newName(name);
+-    if (!pn) {
++    NameNodeType id = newName(name);
++    if (!id) {
+         return null();
+     }
+ 
+     if (!noteUsedName(name)) {
+         return null();
+     }
+ 
+-    return pn;
++    return id;
+ }
+ 
+ template <class ParseHandler>
+-typename ParseHandler::Node
++typename ParseHandler::NameNodeType
+ PerHandlerParser<ParseHandler>::stringLiteral()
+ {
+     return handler.newStringLiteral(anyChars.currentToken().atom(), pos());
+ }
+ 
+ template <class ParseHandler>
+ typename ParseHandler::Node
+ PerHandlerParser<ParseHandler>::noSubstitutionTaggedTemplate()
+@@ -9961,28 +9973,28 @@ PerHandlerParser<ParseHandler>::noSubsti
+         anyChars.clearInvalidTemplateEscape();
+         return handler.newRawUndefinedLiteral(pos());
+     }
+ 
+     return handler.newTemplateStringLiteral(anyChars.currentToken().atom(), pos());
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::NameNodeType
+ GeneralParser<ParseHandler, CharT>::noSubstitutionUntaggedTemplate()
+ {
+     if (!tokenStream.checkForInvalidTemplateEscapeError()) {
+         return null();
+     }
+ 
+     return handler.newTemplateStringLiteral(anyChars.currentToken().atom(), pos());
+ }
+ 
+ template <typename CharT>
+-ParseNode*
++RegExpLiteral*
+ Parser<FullParseHandler, CharT>::newRegExp()
+ {
+     MOZ_ASSERT(!options().selfHostingMode);
+ 
+     // Create the regexp and check its syntax.
+     const auto& chars = tokenStream.getCharBuffer();
+     RegExpFlag flags = anyChars.currentToken().regExpFlags();
+ 
+@@ -9992,17 +10004,17 @@ Parser<FullParseHandler, CharT>::newRegE
+     if (!reobj) {
+         return null();
+     }
+ 
+     return handler.newRegExp(reobj, pos(), *this);
+ }
+ 
+ template <typename CharT>
+-SyntaxParseHandler::Node
++SyntaxParseHandler::RegExpLiteralType
+ Parser<SyntaxParseHandler, CharT>::newRegExp()
+ {
+     MOZ_ASSERT(!options().selfHostingMode);
+ 
+     // Only check the regexp's syntax, but don't create a regexp object.
+     const auto& chars = tokenStream.getCharBuffer();
+     RegExpFlag flags = anyChars.currentToken().regExpFlags();
+ 
+@@ -10010,17 +10022,17 @@ Parser<SyntaxParseHandler, CharT>::newRe
+     if (!js::irregexp::ParsePatternSyntax(anyChars, alloc, source, flags & UnicodeFlag)) {
+         return null();
+     }
+ 
+     return handler.newRegExp(SyntaxParseHandler::NodeGeneric, pos(), *this);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::RegExpLiteralType
+ GeneralParser<ParseHandler, CharT>::newRegExp()
+ {
+     return asFinalParser()->newRegExp();
+ }
+ 
+ // |exprPossibleError| is the PossibleError state within |expr|,
+ // |possibleError| is the surrounding PossibleError state.
+ template <class ParseHandler, typename CharT>
+@@ -10045,17 +10057,17 @@ GeneralParser<ParseHandler, CharT>::chec
+     exprPossibleError->transferErrorsTo(possibleError);
+ 
+     // Return early if a pending destructuring error is already present.
+     if (possibleError->hasPendingDestructuringError()) {
+         return true;
+     }
+ 
+     if (handler.isName(expr)) {
+-        checkDestructuringAssignmentName(expr, exprPos, possibleError);
++        checkDestructuringAssignmentName(handler.asName(expr), exprPos, possibleError);
+         return true;
+     }
+ 
+     if (handler.isUnparenthesizedDestructuringPattern(expr)) {
+         if (behavior == TargetBehavior::ForbidAssignmentPattern) {
+             possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
+         }
+         return true;
+@@ -10072,17 +10084,18 @@ GeneralParser<ParseHandler, CharT>::chec
+         possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
+     }
+ 
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ void
+-GeneralParser<ParseHandler, CharT>::checkDestructuringAssignmentName(Node name, TokenPos namePos,
++GeneralParser<ParseHandler, CharT>::checkDestructuringAssignmentName(NameNodeType name,
++                                                                     TokenPos namePos,
+                                                                      PossibleError* possibleError)
+ {
+ #ifdef DEBUG
+     // GCC 8.0.1 crashes if this is a one-liner.
+     bool isName = handler.isName(name);
+     MOZ_ASSERT(isName);
+ #endif
+ 
+@@ -10630,26 +10643,26 @@ GeneralParser<ParseHandler, CharT>::obje
+                  * for |({x: x, y: y} = o)|, and |var o = {x, y}| as
+                  * initializer shorthand for |var o = {x: x, y: y}|.
+                  */
+                 Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+                 if (!name) {
+                     return null();
+                 }
+ 
+-                Node nameExpr = identifierReference(name);
++                NameNodeType nameExpr = identifierReference(name);
+                 if (!nameExpr) {
+                     return null();
+                 }
+ 
+                 if (possibleError) {
+                     checkDestructuringAssignmentName(nameExpr, namePos, possibleError);
+                 }
+ 
+-                if (!handler.addShorthand(literal, propName, nameExpr)) {
++                if (!handler.addShorthand(literal, handler.asName(propName), nameExpr)) {
+                     return null();
+                 }
+             } else if (propType == PropertyType::CoverInitializedName) {
+                 /*
+                  * Support, e.g., |({x=1, y=2} = o)| as destructuring
+                  * shorthand with default values, as per ES6 12.14.5
+                  */
+                 Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+@@ -10714,23 +10727,23 @@ GeneralParser<ParseHandler, CharT>::obje
+                     if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
+                         funName = prefixAccessorName(propType, propAtom);
+                         if (!funName) {
+                             return null();
+                         }
+                     }
+                 }
+ 
+-                Node fn = methodDefinition(namePos.begin, propType, funName);
+-                if (!fn) {
++                CodeNodeType funNode = methodDefinition(namePos.begin, propType, funName);
++                if (!funNode) {
+                     return null();
+                 }
+ 
+                 AccessorType atype = ToAccessorType(propType);
+-                if (!handler.addObjectMethodDefinition(literal, propName, fn, atype)) {
++                if (!handler.addObjectMethodDefinition(literal, propName, funNode, atype)) {
+                     return null();
+                 }
+ 
+                 if (possibleError) {
+                     possibleError->setPendingDestructuringErrorAt(namePos,
+                                                                   JSMSG_BAD_DESTRUCT_TARGET);
+                 }
+             }
+@@ -10752,17 +10765,17 @@ GeneralParser<ParseHandler, CharT>::obje
+                                      reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
+                                                           JSMSG_CURLY_OPENED, openedPos));
+ 
+     handler.setEndPosition(literal, pos().end);
+     return literal;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::CodeNodeType
+ GeneralParser<ParseHandler, CharT>::methodDefinition(uint32_t toStringStart, PropertyType propType,
+                                                      HandleAtom funName)
+ {
+     FunctionSyntaxKind kind;
+     switch (propType) {
+       case PropertyType::Getter:
+         kind = FunctionSyntaxKind::Getter;
+         break;
+@@ -10797,22 +10810,22 @@ GeneralParser<ParseHandler, CharT>::meth
+ 
+     FunctionAsyncKind asyncKind = (propType == PropertyType::AsyncMethod ||
+                                    propType == PropertyType::AsyncGeneratorMethod)
+                                   ? FunctionAsyncKind::AsyncFunction
+                                   : FunctionAsyncKind::SyncFunction;
+ 
+     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
+ 
+-    Node pn = handler.newFunctionExpression(pos());
+-    if (!pn) {
+-        return null();
+-    }
+-
+-    return functionDefinition(pn, toStringStart, InAllowed, yieldHandling, funName, kind,
++    CodeNodeType funNode = handler.newFunctionExpression(pos());
++    if (!funNode) {
++        return null();
++    }
++
++    return functionDefinition(funNode, toStringStart, InAllowed, yieldHandling, funName, kind,
+                               generatorKind, asyncKind);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ bool
+ GeneralParser<ParseHandler, CharT>::tryNewTarget(BinaryNodeType* newTarget)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::New));
+@@ -11015,17 +11028,17 @@ GeneralParser<ParseHandler, CharT>::prim
+       case TokenKind::True:
+         return handler.newBooleanLiteral(true, pos());
+       case TokenKind::False:
+         return handler.newBooleanLiteral(false, pos());
+       case TokenKind::This: {
+         if (pc->isFunctionBox()) {
+             pc->functionBox()->usesThis = true;
+         }
+-        Node thisName = null();
++        NameNodeType thisName = null();
+         if (pc->sc()->thisBinding() == ThisBinding::Function) {
+             thisName = newThisName();
+             if (!thisName) {
+                 return null();
+             }
+         }
+         return handler.newThisLiteral(pos(), thisName);
+       }
+diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
+--- a/js/src/frontend/Parser.h
++++ b/js/src/frontend/Parser.h
+@@ -506,21 +506,21 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+                          // JSOPTION_EXTRA_WARNINGS adds extra warnings not
+                          // generated when functions are parsed lazily.
+                          // ("use strict" doesn't inhibit lazy parsing.)
+                          static_cast<void*>(options.extraWarningsOption ? nullptr : syntaxParser))
+     {}
+ 
+     static typename ParseHandler::NullNode null() { return ParseHandler::null(); }
+ 
+-    Node stringLiteral();
++    NameNodeType stringLiteral();
+ 
+     const char* nameIsArgumentsOrEval(Node node);
+ 
+-    bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
++    bool noteDestructuredPositionalFormalParameter(CodeNodeType funNode, Node destruct);
+ 
+     bool noteUsedName(HandlePropertyName name) {
+         // If the we are delazifying, the LazyScript already has all the
+         // closed-over info for bindings and there's no need to track used
+         // names.
+         if (handler.canSkipLazyClosedOverBindings()) {
+             return true;
+         }
+@@ -533,24 +533,24 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+ 
+     bool finishFunctionScopes(bool isStandaloneFunction);
+     Node finishLexicalScope(ParseContext::Scope& scope, Node body);
+     bool finishFunction(bool isStandaloneFunction = false);
+ 
+     bool declareFunctionThis();
+     bool declareFunctionArgumentsObject();
+ 
+-    inline Node newName(PropertyName* name);
+-    inline Node newName(PropertyName* name, TokenPos pos);
++    inline NameNodeType newName(PropertyName* name);
++    inline NameNodeType newName(PropertyName* name, TokenPos pos);
+ 
+-    Node newInternalDotName(HandlePropertyName name);
+-    Node newThisName();
+-    Node newDotGeneratorName();
++    NameNodeType newInternalDotName(HandlePropertyName name);
++    NameNodeType newThisName();
++    NameNodeType newDotGeneratorName();
+ 
+-    Node identifierReference(Handle<PropertyName*> name);
++    NameNodeType identifierReference(Handle<PropertyName*> name);
+ 
+     Node noSubstitutionTaggedTemplate();
+ 
+     inline bool processExport(Node node);
+     inline bool processExportFrom(BinaryNodeType node);
+ 
+     // If ParseHandler is SyntaxParseHandler:
+     //   Do nothing.
+@@ -579,25 +579,25 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     // If ParseHandler is FullParseHandler:
+     //   Do nothing.
+     inline void clearAbortedSyntaxParse();
+ 
+   public:
+     bool isValidSimpleAssignmentTarget(Node node,
+                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
+ 
+-    Node newPropertyName(PropertyName* key, const TokenPos& pos) {
++    NameNodeType newPropertyName(PropertyName* key, const TokenPos& pos) {
+         return handler.newPropertyName(key, pos);
+     }
+ 
+-    PropertyAccessType newPropertyAccess(Node expr, Node key) {
++    PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
+         return handler.newPropertyAccess(expr, key);
+     }
+ 
+-    FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
++    FunctionBox* newFunctionBox(CodeNodeType funNode, JSFunction* fun, uint32_t toStringStart,
+                                 Directives directives, GeneratorKind generatorKind,
+                                 FunctionAsyncKind asyncKind);
+ };
+ 
+ #define ABORTED_SYNTAX_PARSE_SENTINEL reinterpret_cast<void*>(0x1)
+ 
+ template<>
+ inline void
+@@ -964,66 +964,66 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+      * If extra warnings are enabled, report the given warning at the given
+      * offset.
+      */
+     MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
+ 
+   private:
+     GeneralParser* thisForCtor() { return this; }
+ 
+-    Node noSubstitutionUntaggedTemplate();
++    NameNodeType noSubstitutionUntaggedTemplate();
+     ListNodeType templateLiteral(YieldHandling yieldHandling);
+     bool taggedTemplate(YieldHandling yieldHandling, ListNodeType tagArgsList, TokenKind tt);
+     bool appendToCallSiteObj(CallSiteNodeType callSiteObj);
+     bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, ListNodeType nodeList,
+                                         TokenKind* ttp);
+ 
+-    inline bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun,
++    inline bool trySyntaxParseInnerFunction(CodeNodeType* funNode, HandleFunction fun,
+                                             uint32_t toStringStart, InHandling inHandling,
+                                             YieldHandling yieldHandling, FunctionSyntaxKind kind,
+                                             GeneratorKind generatorKind,
+                                             FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                             Directives inheritedDirectives,
+                                             Directives* newDirectives);
+ 
+-    inline bool skipLazyInnerFunction(Node funcNode, uint32_t toStringStart,
++    inline bool skipLazyInnerFunction(CodeNodeType funNode, uint32_t toStringStart,
+                                       FunctionSyntaxKind kind, bool tryAnnexB);
+ 
+   public:
+     /* Public entry points for parsing. */
+     Node statementListItem(YieldHandling yieldHandling, bool canHaveDirectives = false);
+ 
+     // Parse an inner function given an enclosing ParseContext and a
+     // FunctionBox for the inner function.
+-    MOZ_MUST_USE Node
+-    innerFunctionForFunctionBox(Node funcNode, ParseContext* outerpc, FunctionBox* funbox,
++    MOZ_MUST_USE CodeNodeType
++    innerFunctionForFunctionBox(CodeNodeType funNode, ParseContext* outerpc, FunctionBox* funbox,
+                                 InHandling inHandling, YieldHandling yieldHandling,
+                                 FunctionSyntaxKind kind, Directives* newDirectives);
+ 
+     // Parse a function's formal parameters and its body assuming its function
+     // ParseContext is already on the stack.
+     bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
+-                                         Node* pn, FunctionSyntaxKind kind,
++                                         CodeNodeType* funNode, FunctionSyntaxKind kind,
+                                          const mozilla::Maybe<uint32_t>& parameterListEnd = mozilla::Nothing(),
+                                          bool isStandaloneFunction = false);
+ 
+   private:
+     /*
+      * JS parsers, from lowest to highest precedence.
+      *
+      * Each parser must be called during the dynamic scope of a ParseContext
+      * object, pointed to by this->pc.
+      *
+      * Each returns a parse node tree or null on error.
+      */
+-    Node functionStmt(uint32_t toStringStart,
+-                      YieldHandling yieldHandling, DefaultHandling defaultHandling,
+-                      FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+-    Node functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
+-                      FunctionAsyncKind asyncKind);
++    CodeNodeType functionStmt(uint32_t toStringStart,
++                              YieldHandling yieldHandling, DefaultHandling defaultHandling,
++                              FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
++    CodeNodeType functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
++                              FunctionAsyncKind asyncKind);
+ 
+     Node statement(YieldHandling yieldHandling);
+     bool maybeParseDirective(ListNodeType list, Node pn, bool* cont);
+ 
+     Node blockStatement(YieldHandling yieldHandling,
+                         unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
+     BinaryNodeType doWhileStatement(YieldHandling yieldHandling);
+     BinaryNodeType whileStatement(YieldHandling yieldHandling);
+@@ -1043,17 +1043,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     BinaryNodeType withStatement(YieldHandling yieldHandling);
+     UnaryNodeType throwStatement(YieldHandling yieldHandling);
+     TernaryNodeType tryStatement(YieldHandling yieldHandling);
+     Node catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope);
+     Node debuggerStatement();
+ 
+     Node variableStatement(YieldHandling yieldHandling);
+ 
+-    Node labeledStatement(YieldHandling yieldHandling);
++    LabeledStatementType labeledStatement(YieldHandling yieldHandling);
+     Node labeledItem(YieldHandling yieldHandling);
+ 
+     TernaryNodeType ifStatement(YieldHandling yieldHandling);
+     Node consequentOrAlternative(YieldHandling yieldHandling);
+ 
+     ListNodeType lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
+ 
+     inline BinaryNodeType importDeclaration();
+@@ -1110,26 +1110,26 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     // non-null), consume additional tokens up to the closing ')' in a
+     // for-in/of loop head, returning the iterated expression in
+     // |*forInOrOfExpression|.  (An "initial declaration" is the first
+     // declaration in a declaration list: |a| but not |b| in |var a, b|, |{c}|
+     // but not |d| in |let {c} = 3, d|.)
+     Node declarationPattern(DeclarationKind declKind, TokenKind tt,
+                             bool initialDeclaration, YieldHandling yieldHandling,
+                             ParseNodeKind* forHeadKind, Node* forInOrOfExpression);
+-    Node declarationName(DeclarationKind declKind, TokenKind tt,
+-                         bool initialDeclaration, YieldHandling yieldHandling,
+-                         ParseNodeKind* forHeadKind, Node* forInOrOfExpression);
++    NameNodeType declarationName(DeclarationKind declKind, TokenKind tt,
++                                 bool initialDeclaration, YieldHandling yieldHandling,
++                                 ParseNodeKind* forHeadKind, Node* forInOrOfExpression);
+ 
+     // Having parsed a name (not found in a destructuring pattern) declared by
+     // a declaration, with the current token being the '=' separating the name
+     // from its initializer, parse and bind that initializer -- and possibly
+     // consume trailing in/of and subsequent expression, if so directed by
+     // |forHeadKind|.
+-    bool initializerInNameDeclaration(Node binding,
++    bool initializerInNameDeclaration(NameNodeType binding,
+                                       DeclarationKind declKind, bool initialDeclaration,
+                                       YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
+                                       Node* forInOrOfExpression);
+ 
+     Node expr(InHandling inHandling, YieldHandling yieldHandling,
+               TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
+               InvokedPrediction invoked = PredictUninvoked);
+     Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+@@ -1154,28 +1154,30 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+                      InvokedPrediction invoked = PredictUninvoked);
+     Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+                       TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr);
+ 
+     bool tryNewTarget(BinaryNodeType* newTarget);
+ 
+     BinaryNodeType importExpr(YieldHandling yieldHandling);
+ 
+-    Node methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName);
++    CodeNodeType methodDefinition(uint32_t toStringStart, PropertyType propType,
++                                  HandleAtom funName);
+ 
+     /*
+      * Additional JS parsers.
+      */
+     bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
+-                           Node funcpn);
++                           CodeNodeType funNode);
+ 
+-    Node functionDefinition(Node funcNode, uint32_t toStringStart, InHandling inHandling,
+-                            YieldHandling yieldHandling, HandleAtom name, FunctionSyntaxKind kind,
+-                            GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+-                            bool tryAnnexB = false);
++    CodeNodeType functionDefinition(CodeNodeType funNode, uint32_t toStringStart,
++                                    InHandling inHandling, YieldHandling yieldHandling,
++                                    HandleAtom name, FunctionSyntaxKind kind,
++                                    GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
++                                    bool tryAnnexB = false);
+ 
+     // Parse a function body.  Pass StatementListBody if the body is a list of
+     // statements; pass ExpressionBody if the body is a single expression.
+     enum FunctionBodyType { StatementListBody, ExpressionBody };
+     Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
+                       FunctionBodyType type);
+ 
+     UnaryNodeType unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin);
+@@ -1189,19 +1191,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling,
+                                                      TokenKind tt);
+ 
+     inline bool checkExportedName(JSAtom* exportName);
+     inline bool checkExportedNamesForArrayBinding(ListNodeType array);
+     inline bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     inline bool checkExportedNamesForDeclaration(Node node);
+     inline bool checkExportedNamesForDeclarationList(ListNodeType node);
+-    inline bool checkExportedNameForFunction(Node node);
++    inline bool checkExportedNameForFunction(CodeNodeType funNode);
+     inline bool checkExportedNameForClass(ClassNodeType classNode);
+-    inline bool checkExportedNameForClause(Node node);
++    inline bool checkExportedNameForClause(NameNodeType nameNode);
+ 
+     enum ClassContext { ClassStatement, ClassExpression };
+     ClassNodeType classDefinition(YieldHandling yieldHandling, ClassContext classContext,
+                                   DefaultHandling defaultHandling);
+ 
+     bool checkBindingIdentifier(PropertyName* ident,
+                                 uint32_t offset,
+                                 YieldHandling yieldHandling,
+@@ -1226,73 +1228,74 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+   private:
+     bool checkIncDecOperand(Node operand, uint32_t operandOffset);
+     bool checkStrictAssignment(Node lhs);
+ 
+     void reportMissingClosing(unsigned errorNumber, unsigned noteNumber, uint32_t openedPos);
+ 
+     void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos,
+                              uint32_t prevPos);
+-    bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
++    bool notePositionalFormalParameter(CodeNodeType funNode, HandlePropertyName name,
++                                       uint32_t beginPos,
+                                        bool disallowDuplicateParams, bool* duplicatedParam);
+ 
+     bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
+                                                     DeclarationKind kind, TokenPos pos);
+ 
+     Node propertyName(YieldHandling yieldHandling,
+                       const mozilla::Maybe<DeclarationKind>& maybeDecl,
+                       ListNodeType propList,
+                       PropertyType* propType, MutableHandleAtom propAtom);
+     UnaryNodeType computedPropertyName(YieldHandling yieldHandling,
+                                        const mozilla::Maybe<DeclarationKind>& maybeDecl,
+                                        ListNodeType literal);
+     ListNodeType arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
+-    inline Node newRegExp();
++    inline RegExpLiteralType newRegExp();
+ 
+     ListNodeType objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
+ 
+     BinaryNodeType bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling);
+-    Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling);
++    NameNodeType bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling);
+     Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
+                                     TokenKind tt);
+     ListNodeType objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+     ListNodeType arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+ 
+     enum class TargetBehavior {
+         PermitAssignmentPattern,
+         ForbidAssignmentPattern
+     };
+     bool checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+                                             PossibleError* exprPossibleError,
+                                             PossibleError* possibleError,
+                                             TargetBehavior behavior = TargetBehavior::PermitAssignmentPattern);
+-    void checkDestructuringAssignmentName(Node name, TokenPos namePos,
++    void checkDestructuringAssignmentName(NameNodeType name, TokenPos namePos,
+                                           PossibleError* possibleError);
+     bool checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+                                              PossibleError* exprPossibleError,
+                                              PossibleError* possibleError);
+ 
+-    Node newNumber(const Token& tok) {
++    NumericLiteralType newNumber(const Token& tok) {
+         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
+     }
+ 
+   protected:
+     // Match the current token against the BindingIdentifier production with
+     // the given Yield parameter.  If there is no match, report a syntax
+     // error.
+     PropertyName* bindingIdentifier(YieldHandling yieldHandling);
+ 
+     bool checkLabelOrIdentifierReference(PropertyName* ident, uint32_t offset,
+                                          YieldHandling yieldHandling,
+                                          TokenKind hint = TokenKind::Limit);
+ 
+     ListNodeType statementList(YieldHandling yieldHandling);
+ 
+-    MOZ_MUST_USE Node
+-    innerFunction(Node funcNode, ParseContext* outerpc, HandleFunction fun,
++    MOZ_MUST_USE CodeNodeType
++    innerFunction(CodeNodeType funNode, ParseContext* outerpc, HandleFunction fun,
+                   uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling,
+                   FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                   FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives,
+                   Directives* newDirectives);
+ 
+     bool matchOrInsertSemicolon();
+ 
+     bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
+@@ -1382,40 +1385,41 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+         return Base::bindingIdentifier(yieldHandling);
+     }
+ 
+     // Functions present in both Parser<ParseHandler, CharT> specializations.
+ 
+     inline void setAwaitHandling(AwaitHandling awaitHandling);
+     inline void setInParametersOfAsyncFunction(bool inParameters);
+ 
+-    Node newRegExp();
++    RegExpLiteralType newRegExp();
+ 
+     // Parse a module.
+-    Node moduleBody(ModuleSharedContext* modulesc);
++    CodeNodeType moduleBody(ModuleSharedContext* modulesc);
+ 
+     inline BinaryNodeType importDeclaration();
+     inline bool checkLocalExportNames(ListNodeType node);
+     inline bool checkExportedName(JSAtom* exportName);
+     inline bool checkExportedNamesForArrayBinding(ListNodeType array);
+     inline bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     inline bool checkExportedNamesForDeclaration(Node node);
+     inline bool checkExportedNamesForDeclarationList(ListNodeType node);
+-    inline bool checkExportedNameForFunction(Node node);
++    inline bool checkExportedNameForFunction(CodeNodeType funNode);
+     inline bool checkExportedNameForClass(ClassNodeType classNode);
+-    inline bool checkExportedNameForClause(Node node);
++    inline bool checkExportedNameForClause(NameNodeType nameNode);
+ 
+-    bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun, uint32_t toStringStart,
++    bool trySyntaxParseInnerFunction(CodeNodeType* funNode, HandleFunction fun,
++                                     uint32_t toStringStart,
+                                      InHandling inHandling, YieldHandling yieldHandling,
+                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                      FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                      Directives inheritedDirectives, Directives* newDirectives);
+ 
+-    bool skipLazyInnerFunction(Node funcNode, uint32_t toStringStart, FunctionSyntaxKind kind,
+-                               bool tryAnnexB);
++    bool skipLazyInnerFunction(CodeNodeType funNode, uint32_t toStringStart,
++                               FunctionSyntaxKind kind, bool tryAnnexB);
+ 
+     bool asmJS(ListNodeType list);
+ 
+     // Functions present only in Parser<SyntaxParseHandler, CharT>.
+ };
+ 
+ template <typename CharT>
+ class MOZ_STACK_CLASS Parser<FullParseHandler, CharT> final
+@@ -1507,61 +1511,62 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     // Functions present in both Parser<ParseHandler, CharT> specializations.
+ 
+     friend class AutoAwaitIsKeyword<SyntaxParseHandler, CharT>;
+     inline void setAwaitHandling(AwaitHandling awaitHandling);
+ 
+     friend class AutoInParametersOfAsyncFunction<SyntaxParseHandler, CharT>;
+     inline void setInParametersOfAsyncFunction(bool inParameters);
+ 
+-    Node newRegExp();
++    RegExpLiteralType newRegExp();
+ 
+     // Parse a module.
+-    Node moduleBody(ModuleSharedContext* modulesc);
++    CodeNodeType moduleBody(ModuleSharedContext* modulesc);
+ 
+     BinaryNodeType importDeclaration();
+     bool checkLocalExportNames(ListNodeType node);
+     bool checkExportedName(JSAtom* exportName);
+     bool checkExportedNamesForArrayBinding(ListNodeType array);
+     bool checkExportedNamesForObjectBinding(ListNodeType obj);
+     bool checkExportedNamesForDeclaration(Node node);
+     bool checkExportedNamesForDeclarationList(ListNodeType node);
+-    bool checkExportedNameForFunction(Node node);
++    bool checkExportedNameForFunction(CodeNodeType funNode);
+     bool checkExportedNameForClass(ClassNodeType classNode);
+-    inline bool checkExportedNameForClause(Node node);
++    inline bool checkExportedNameForClause(NameNodeType nameNode);
+ 
+-    bool trySyntaxParseInnerFunction(Node* funcNode, HandleFunction fun, uint32_t toStringStart,
++    bool trySyntaxParseInnerFunction(CodeNodeType* funNode, HandleFunction fun,
++                                     uint32_t toStringStart,
+                                      InHandling inHandling, YieldHandling yieldHandling,
+                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                      FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                      Directives inheritedDirectives, Directives* newDirectives);
+ 
+-    bool skipLazyInnerFunction(Node funcNode, uint32_t toStringStart, FunctionSyntaxKind kind,
+-                               bool tryAnnexB);
++    bool skipLazyInnerFunction(CodeNodeType funNode, uint32_t toStringStart,
++                               FunctionSyntaxKind kind, bool tryAnnexB);
+ 
+     // Functions present only in Parser<FullParseHandler, CharT>.
+ 
+     // Parse the body of an eval.
+     //
+     // Eval scripts are distinguished from global scripts in that in ES6, per
+     // 18.2.1.1 steps 9 and 10, all eval scripts are executed under a fresh
+     // lexical scope.
+     Node evalBody(EvalSharedContext* evalsc);
+ 
+     // Parse a function, given only its arguments and body. Used for lazily
+     // parsed functions.
+-    Node standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart, bool strict,
+-                                GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
++    CodeNodeType standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart, bool strict,
++                                        GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
+ 
+     // Parse a function, used for the Function, GeneratorFunction, and
+     // AsyncFunction constructors.
+-    Node standaloneFunction(HandleFunction fun, HandleScope enclosingScope,
+-                            const mozilla::Maybe<uint32_t>& parameterListEnd,
+-                            GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+-                            Directives inheritedDirectives, Directives* newDirectives);
++    CodeNodeType standaloneFunction(HandleFunction fun, HandleScope enclosingScope,
++                                    const mozilla::Maybe<uint32_t>& parameterListEnd,
++                                    GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
++                                    Directives inheritedDirectives, Directives* newDirectives);
+ 
+     bool checkStatementsEOF();
+ 
+     // Parse the body of a global script.
+     ListNodeType globalBody(GlobalSharedContext* globalsc);
+ 
+     bool namedImportsOrNamespaceImport(TokenKind tt, ListNodeType importSpecSet);
+ 
+diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h
+--- a/js/src/frontend/SharedContext.h
++++ b/js/src/frontend/SharedContext.h
+@@ -316,17 +316,17 @@ class FunctionBox : public ObjectBox, pu
+ 
+     // Names from the extra 'var' scope of the function, if the parameter list
+     // has expressions.
+     VarScope::Data* extraVarScopeBindings_;
+ 
+     void initWithEnclosingScope(Scope* enclosingScope);
+ 
+   public:
+-    ParseNode*      functionNode;           /* back pointer used by asm.js for error messages */
++    CodeNode*      functionNode;            /* back pointer used by asm.js for error messages */
+     uint32_t        bufStart;
+     uint32_t        bufEnd;
+     uint32_t        startLine;
+     uint32_t        startColumn;
+     uint32_t        toStringStart;
+     uint32_t        toStringEnd;
+     uint16_t        length;
+ 
+diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
+--- a/js/src/frontend/SyntaxParseHandler.h
++++ b/js/src/frontend/SyntaxParseHandler.h
+@@ -177,17 +177,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+ 
+ #define DECLARE_AS(typeName, longTypeName, asMethodName) \
+     static longTypeName asMethodName(Node node) { \
+         return node; \
+     }
+ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ #undef DECLARE_AS
+ 
+-    Node newName(PropertyName* name, const TokenPos& pos, JSContext* cx) {
++    NameNodeType newName(PropertyName* name, const TokenPos& pos, JSContext* cx) {
+         lastAtom = name;
+         if (name == cx->names().arguments) {
+             return NodeArgumentsName;
+         }
+         if (pos.begin + strlen("async") == pos.end && name == cx->names().async) {
+             return NodePotentialAsyncKeyword;
+         }
+         if (name == cx->names().eval) {
+@@ -195,45 +195,50 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         }
+         return NodeName;
+     }
+ 
+     UnaryNodeType newComputedName(Node expr, uint32_t start, uint32_t end) {
+         return NodeGeneric;
+     }
+ 
+-    Node newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
++    NameNodeType newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
+         return NodeName;
+     }
+ 
+-    Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) { return NodeGeneric; }
++    NumericLiteralType newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) {
++        return NodeGeneric;
++    }
++
+     Node newBooleanLiteral(bool cond, const TokenPos& pos) { return NodeGeneric; }
+ 
+-    Node newStringLiteral(JSAtom* atom, const TokenPos& pos) {
++    NameNodeType newStringLiteral(JSAtom* atom, const TokenPos& pos) {
+         lastAtom = atom;
+         lastStringPos = pos;
+         return NodeUnparenthesizedString;
+     }
+ 
+-    Node newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
++    NameNodeType newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+ 
+     CallSiteNodeType newCallSiteObject(uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+     void addToCallSiteObject(CallSiteNodeType callSiteObj, Node rawNode, Node cookedNode) {}
+ 
+     ThisLiteralType newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
+     Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
+     Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
+ 
+     template <class Boxer>
+-    Node newRegExp(Node reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
++    RegExpLiteralType newRegExp(Node reobj, const TokenPos& pos, Boxer& boxer) {
++        return NodeGeneric;
++    }
+ 
+     ConditionalExpressionType newConditional(Node cond, Node thenExpr, Node elseExpr) {
+         return NodeGeneric;
+     }
+ 
+     Node newElision() { return NodeGeneric; }
+ 
+     UnaryNodeType newDelete(uint32_t begin, Node expr) {
+@@ -283,20 +288,27 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     BinaryNodeType newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
+     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
+     UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
+ 
+     MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) { return true; }
+     BinaryNodeType newPropertyDefinition(Node key, Node val) { return NodeGeneric; }
+     void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {}
+     MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node expr) { return true; }
+-    MOZ_MUST_USE bool addShorthand(ListNodeType literal, Node name, Node expr) { return true; }
++    MOZ_MUST_USE bool addShorthand(ListNodeType literal, NameNodeType name, NameNodeType expr) { return true; }
+     MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { return true; }
+-    MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key, Node fn, AccessorType atype) { return true; }
+-    MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key, Node fn, AccessorType atype, bool isStatic) { return true; }
++    MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key,
++                                                CodeNodeType funNode, AccessorType atype) {
++        return true;
++    }
++    MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType methodList, Node key,
++                                               CodeNodeType funNode, AccessorType atype,
++                                               bool isStatic) {
++        return true;
++    }
+     UnaryNodeType newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
+     UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
+     UnaryNodeType newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
+ 
+     // Statements
+ 
+     ListNodeType newStatementList(const TokenPos& pos) { return NodeGeneric; }
+     void addStatementToList(ListNodeType list, Node stmt) {}
+@@ -347,74 +359,79 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+     CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+     Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
+     Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
+     UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) { return NodeReturn; }
+     UnaryNodeType newExpressionBody(Node expr) { return NodeReturn; }
+     BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+ 
+-    Node newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
++    LabeledStatementType newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+     UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
+     TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
+         return NodeGeneric;
+     }
+     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
+ 
+-    Node newPropertyName(PropertyName* name, const TokenPos& pos) {
++    NameNodeType newPropertyName(PropertyName* name, const TokenPos& pos) {
+         lastAtom = name;
+         return NodeGeneric;
+     }
+ 
+-    PropertyAccessType newPropertyAccess(Node expr, Node key) {
++    PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
+         return NodeDottedProperty;
+     }
+ 
+     PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) {
+         return NodeElement;
+     }
+ 
+     MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) {
+         return true;
+     }
+ 
+-    MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(Node funcpn, Node pn) { return true; }
++    MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(CodeNodeType funNode,
++                                                            Node defaultValue) {
++        return true;
++    }
+ 
+-    Node newFunctionStatement(const TokenPos& pos) { return NodeFunctionStatement; }
++    CodeNodeType newFunctionStatement(const TokenPos& pos) { return NodeFunctionStatement; }
+ 
+-    Node newFunctionExpression(const TokenPos& pos) {
++    CodeNodeType newFunctionExpression(const TokenPos& pos) {
+         // All non-arrow function expressions are initially presumed to have
+         // block body.  This will be overridden later *if* the function
+         // expression permissibly has an AssignmentExpression body.
+         return NodeFunctionExpression;
+     }
+ 
+-    Node newArrowFunction(const TokenPos& pos) { return NodeFunctionArrow; }
++    CodeNodeType newArrowFunction(const TokenPos& pos) { return NodeFunctionArrow; }
+ 
+-    void setFunctionFormalParametersAndBody(Node funcNode, Node kid) {}
+-    void setFunctionBody(Node pn, Node kid) {}
+-    void setFunctionBox(Node pn, FunctionBox* funbox) {}
+-    void addFunctionFormalParameter(Node pn, Node argpn) {}
++    void setFunctionFormalParametersAndBody(CodeNodeType funNode, ListNodeType paramsBody) {}
++    void setFunctionBody(CodeNodeType funNode, Node body) {}
++    void setFunctionBox(CodeNodeType funNode, FunctionBox* funbox) {}
++    void addFunctionFormalParameter(CodeNodeType funNode, Node argpn) {}
+ 
+     ForNodeType newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) {
+         return NodeGeneric;
+     }
+ 
+     TernaryNodeType newForHead(Node init, Node test, Node update, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+ 
+     TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target, Node iteratedExpr, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+ 
+-    MOZ_MUST_USE bool finishInitializerAssignment(Node pn, Node init) { return true; }
++    MOZ_MUST_USE bool finishInitializerAssignment(NameNodeType nameNode, Node init) {
++        return true;
++    }
+ 
+     void setBeginPosition(Node pn, Node oth) {}
+     void setBeginPosition(Node pn, uint32_t begin) {}
+ 
+     void setEndPosition(Node pn, Node oth) {}
+     void setEndPosition(Node pn, uint32_t end) {}
+ 
+     uint32_t getFunctionNameOffset(Node func, TokenStreamAnyChars& ts) {
+@@ -523,18 +540,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         if (node == NodePotentialAsyncKeyword) {
+             return NodeName;
+         }
+ 
+         // In all other cases, the parenthesized form of |node| is equivalent
+         // to the unparenthesized form: return |node| unchanged.
+         return node;
+     }
+-    MOZ_MUST_USE Node setLikelyIIFE(Node pn) {
+-        return pn; // Remain in syntax-parse mode.
++    template <typename NodeType>
++    MOZ_MUST_USE NodeType setLikelyIIFE(NodeType node) {
++        return node; // Remain in syntax-parse mode.
+     }
+     void setInDirectivePrologue(UnaryNodeType exprStmt) {}
+ 
+     bool isName(Node node) {
+         return node == NodeName ||
+                node == NodeArgumentsName ||
+                node == NodeEvalName ||
+                node == NodePotentialAsyncKeyword;
+diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
+--- a/js/src/wasm/AsmJS.cpp
++++ b/js/src/wasm/AsmJS.cpp
+@@ -608,25 +608,23 @@ static inline ParseNode*
+ LabeledStatementStatement(ParseNode* pn)
+ {
+     return pn->as<LabeledStatement>().statement();
+ }
+ 
+ static double
+ NumberNodeValue(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Number));
+-    return pn->pn_dval;
++    return pn->as<NumericLiteral>().value();
+ }
+ 
+ static bool
+ NumberNodeHasFrac(ParseNode* pn)
+ {
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::Number));
+-    return pn->pn_u.number.decimalPoint == HasDecimal;
++    return pn->as<NumericLiteral>().decimalPoint() == HasDecimal;
+ }
+ 
+ static ParseNode*
+ DotBase(ParseNode* pn)
+ {
+     return &pn->as<PropertyAccess>().expression();
+ }
+ 
+@@ -644,37 +642,36 @@ ElemBase(ParseNode* pn)
+ 
+ static ParseNode*
+ ElemIndex(ParseNode* pn)
+ {
+     return &pn->as<PropertyByValue>().key();
+ }
+ 
+ static inline JSFunction*
+-FunctionObject(ParseNode* fn)
+-{
+-    MOZ_ASSERT(fn->isKind(ParseNodeKind::Function));
+-    MOZ_ASSERT(fn->isArity(PN_CODE));
+-    return fn->pn_funbox->function();
++FunctionObject(CodeNode* funNode)
++{
++    MOZ_ASSERT(funNode->isKind(ParseNodeKind::Function));
++    return funNode->funbox()->function();
+ }
+ 
+ static inline PropertyName*
+-FunctionName(ParseNode* fn)
+-{
+-    if (JSAtom* name = FunctionObject(fn)->explicitName()) {
++FunctionName(CodeNode* funNode)
++{
++    if (JSAtom* name = FunctionObject(funNode)->explicitName()) {
+         return name->asPropertyName();
+     }
+     return nullptr;
+ }
+ 
+ static inline ParseNode*
+-FunctionStatementList(ParseNode* fn)
+-{
+-    MOZ_ASSERT(fn->pn_body->isKind(ParseNodeKind::ParamsBody));
+-    ParseNode* last = fn->pn_body->as<ListNode>().last();
++FunctionStatementList(CodeNode* funNode)
++{
++    MOZ_ASSERT(funNode->body()->isKind(ParseNodeKind::ParamsBody));
++    ParseNode* last = funNode->body()->as<ListNode>().last();
+     MOZ_ASSERT(last->isKind(ParseNodeKind::LexicalScope));
+     MOZ_ASSERT(last->isEmptyScope());
+     ParseNode* body = last->scopeBody();
+     MOZ_ASSERT(body->isKind(ParseNodeKind::StatementList));
+     return body;
+ }
+ 
+ static inline bool
+@@ -685,30 +682,30 @@ IsNormalObjectField(ParseNode* pn)
+            BinaryLeft(pn)->isKind(ParseNodeKind::ObjectPropertyName);
+ }
+ 
+ static inline PropertyName*
+ ObjectNormalFieldName(ParseNode* pn)
+ {
+     MOZ_ASSERT(IsNormalObjectField(pn));
+     MOZ_ASSERT(BinaryLeft(pn)->isKind(ParseNodeKind::ObjectPropertyName));
+-    return BinaryLeft(pn)->pn_atom->asPropertyName();
++    return BinaryLeft(pn)->as<NameNode>().atom()->asPropertyName();
+ }
+ 
+ static inline ParseNode*
+ ObjectNormalFieldInitializer(ParseNode* pn)
+ {
+     MOZ_ASSERT(IsNormalObjectField(pn));
+     return BinaryRight(pn);
+ }
+ 
+ static inline ParseNode*
+ MaybeInitializer(ParseNode* pn)
+ {
+-    return pn->expr();
++    return pn->as<NameNode>().expression();
+ }
+ 
+ static inline bool
+ IsUseOfName(ParseNode* pn, PropertyName* name)
+ {
+     return pn->isKind(ParseNodeKind::Name) && pn->name() == name;
+ }
+ 
+@@ -718,17 +715,17 @@ IsIgnoredDirectiveName(JSContext* cx, JS
+     return atom != cx->names().useStrict;
+ }
+ 
+ static inline bool
+ IsIgnoredDirective(JSContext* cx, ParseNode* pn)
+ {
+     return pn->isKind(ParseNodeKind::ExpressionStatement) &&
+            UnaryKid(pn)->isKind(ParseNodeKind::String) &&
+-           IsIgnoredDirectiveName(cx, UnaryKid(pn)->pn_atom);
++           IsIgnoredDirectiveName(cx, UnaryKid(pn)->as<NameNode>().atom());
+ }
+ 
+ static inline bool
+ IsEmptyStatement(ParseNode* pn)
+ {
+     return pn->isKind(ParseNodeKind::EmptyStatement);
+ }
+ 
+@@ -1441,17 +1438,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
+     typedef HashSet<HashableSig, HashableSig> SigSet;
+     typedef HashMap<NamedSig, uint32_t, NamedSig> FuncImportMap;
+     typedef HashMap<PropertyName*, Global*> GlobalMap;
+     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
+     typedef Vector<ArrayView> ArrayViewVector;
+ 
+     JSContext*            cx_;
+     AsmJSParser&          parser_;
+-    ParseNode*            moduleFunctionNode_;
++    CodeNode*             moduleFunctionNode_;
+     PropertyName*         moduleFunctionName_;
+     PropertyName*         globalArgumentName_;
+     PropertyName*         importArgumentName_;
+     PropertyName*         bufferArgumentName_;
+     MathNameMap           standardLibraryMathNames_;
+     RootedFunction        dummyFunction_;
+ 
+     // Validation-internal state:
+@@ -1506,17 +1503,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
+             return true;
+         }
+ 
+         return newSig(std::move(sig), sigIndex) &&
+                sigSet_.add(p, HashableSig(*sigIndex, env_.types));
+     }
+ 
+   public:
+-    ModuleValidator(JSContext* cx, AsmJSParser& parser, ParseNode* moduleFunctionNode)
++    ModuleValidator(JSContext* cx, AsmJSParser& parser, CodeNode* moduleFunctionNode)
+       : cx_(cx),
+         parser_(parser),
+         moduleFunctionNode_(moduleFunctionNode),
+         moduleFunctionName_(FunctionName(moduleFunctionNode)),
+         globalArgumentName_(nullptr),
+         importArgumentName_(nullptr),
+         bufferArgumentName_(nullptr),
+         standardLibraryMathNames_(cx),
+@@ -1579,18 +1576,18 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
+ 
+   public:
+     bool init() {
+         asmJSMetadata_ = cx_->new_<AsmJSMetadata>();
+         if (!asmJSMetadata_) {
+             return false;
+         }
+ 
+-        asmJSMetadata_->toStringStart = moduleFunctionNode_->pn_funbox->toStringStart;
+-        asmJSMetadata_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin;
++        asmJSMetadata_->toStringStart = moduleFunctionNode_->funbox()->toStringStart;
++        asmJSMetadata_->srcStart = moduleFunctionNode_->body()->pn_pos.begin;
+         asmJSMetadata_->strict = parser_.pc->sc()->strict() &&
+                                  !parser_.pc->sc()->hasExplicitUseStrict();
+         asmJSMetadata_->scriptSource.reset(parser_.ss);
+ 
+         if (!addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
+             !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
+             !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
+             !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
+@@ -2743,25 +2740,26 @@ CheckModuleLevelName(ModuleValidator& m,
+     {
+         return m.failName(usepn, "duplicate name '%s' not allowed", name);
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-CheckFunctionHead(ModuleValidator& m, ParseNode* fn)
+-{
+-    MOZ_ASSERT(!fn->pn_funbox->hasExprBody());
+-
+-    if (fn->pn_funbox->hasRest()) {
+-        return m.fail(fn, "rest args not allowed");
+-    }
+-    if (fn->pn_funbox->hasDestructuringArgs) {
+-        return m.fail(fn, "destructuring args not allowed");
++CheckFunctionHead(ModuleValidator& m, CodeNode* funNode)
++{
++    FunctionBox* funbox = funNode->funbox();
++    MOZ_ASSERT(!funbox->hasExprBody());
++
++    if (funbox->hasRest()) {
++        return m.fail(funNode, "rest args not allowed");
++    }
++    if (funbox->hasDestructuringArgs) {
++        return m.fail(funNode, "destructuring args not allowed");
+     }
+     return true;
+ }
+ 
+ static bool
+ CheckArgument(ModuleValidator& m, ParseNode* arg, PropertyName** name)
+ {
+     *name = nullptr;
+@@ -2788,25 +2786,25 @@ CheckModuleArgument(ModuleValidator& m, 
+     if (!CheckModuleLevelName(m, arg, *name)) {
+         return false;
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-CheckModuleArguments(ModuleValidator& m, ParseNode* fn)
++CheckModuleArguments(ModuleValidator& m, CodeNode* funNode)
+ {
+     unsigned numFormals;
+-    ParseNode* arg1 = FunctionFormalParametersList(fn, &numFormals);
++    ParseNode* arg1 = FunctionFormalParametersList(funNode, &numFormals);
+     ParseNode* arg2 = arg1 ? NextNode(arg1) : nullptr;
+     ParseNode* arg3 = arg2 ? NextNode(arg2) : nullptr;
+ 
+     if (numFormals > 3) {
+-        return m.fail(fn, "asm.js modules takes at most 3 argument");
++        return m.fail(funNode, "asm.js modules takes at most 3 argument");
+     }
+ 
+     PropertyName* arg1Name = nullptr;
+     if (arg1 && !CheckModuleArgument(m, arg1, &arg1Name)) {
+         return false;
+     }
+     if (!m.initGlobalArgumentName(arg1Name)) {
+         return false;
+@@ -5813,17 +5811,17 @@ CheckStatement(FunctionValidator& f, Par
+       case ParseNodeKind::LexicalScope:         return CheckLexicalScope(f, stmt);
+       default:;
+     }
+ 
+     return f.fail(stmt, "unexpected statement kind");
+ }
+ 
+ static bool
+-ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
++ParseFunction(ModuleValidator& m, CodeNode** funNodeOut, unsigned* line)
+ {
+     auto& tokenStream = m.tokenStream();
+ 
+     tokenStream.consumeKnownToken(TokenKind::Function, TokenStreamShared::Operand);
+ 
+     auto& anyChars = tokenStream.anyCharsAccess();
+     uint32_t toStringStart = anyChars.currentToken().pos.begin;
+     *line = anyChars.srcCoords.lineNum(anyChars.currentToken().pos.end);
+@@ -5839,78 +5837,78 @@ ParseFunction(ModuleValidator& m, ParseN
+         return false;  // The regular parser will throw a SyntaxError, no need to m.fail.
+     }
+ 
+     RootedPropertyName name(m.cx(), m.parser().bindingIdentifier(YieldIsName));
+     if (!name) {
+         return false;
+     }
+ 
+-    ParseNode* fn = m.parser().handler.newFunctionStatement(m.parser().pos());
+-    if (!fn) {
++    CodeNode* funNode = m.parser().handler.newFunctionStatement(m.parser().pos());
++    if (!funNode) {
+         return false;
+     }
+ 
+     RootedFunction& fun = m.dummyFunction();
+     fun->setAtom(name);
+     fun->setArgCount(0);
+ 
+     ParseContext* outerpc = m.parser().pc;
+     Directives directives(outerpc);
+-    FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, toStringStart, directives,
++    FunctionBox* funbox = m.parser().newFunctionBox(funNode, fun, toStringStart, directives,
+                                                     GeneratorKind::NotGenerator,
+                                                     FunctionAsyncKind::SyncFunction);
+     if (!funbox) {
+         return false;
+     }
+     funbox->initWithEnclosingParseContext(outerpc, FunctionSyntaxKind::Statement);
+ 
+     Directives newDirectives = directives;
+     SourceParseContext funpc(&m.parser(), funbox, &newDirectives);
+     if (!funpc.init()) {
+         return false;
+     }
+ 
+-    if (!m.parser().functionFormalParametersAndBody(InAllowed, YieldIsName, &fn,
++    if (!m.parser().functionFormalParametersAndBody(InAllowed, YieldIsName, &funNode,
+                                                     FunctionSyntaxKind::Statement))
+     {
+         if (anyChars.hadError() || directives == newDirectives) {
+             return false;
+         }
+ 
+-        return m.fail(fn, "encountered new directive in function");
++        return m.fail(funNode, "encountered new directive in function");
+     }
+ 
+     MOZ_ASSERT(!anyChars.hadError());
+     MOZ_ASSERT(directives == newDirectives);
+ 
+-    *fnOut = fn;
++    *funNodeOut = funNode;
+     return true;
+ }
+ 
+ static bool
+ CheckFunction(ModuleValidator& m)
+ {
+     // asm.js modules can be quite large when represented as parse trees so pop
+     // the backing LifoAlloc after parsing/compiling each function.
+     AsmJSParser::Mark mark = m.parser().mark();
+ 
+-    ParseNode* fn = nullptr;
++    CodeNode* funNode = nullptr;
+     unsigned line = 0;
+-    if (!ParseFunction(m, &fn, &line)) {
+-        return false;
+-    }
+-
+-    if (!CheckFunctionHead(m, fn)) {
+-        return false;
+-    }
+-
+-    FunctionValidator f(m, fn);
+-
+-    ParseNode* stmtIter = ListHead(FunctionStatementList(fn));
++    if (!ParseFunction(m, &funNode, &line)) {
++        return false;
++    }
++
++    if (!CheckFunctionHead(m, funNode)) {
++        return false;
++    }
++
++    FunctionValidator f(m, funNode);
++
++    ParseNode* stmtIter = ListHead(FunctionStatementList(funNode));
+ 
+     if (!CheckProcessingDirectives(m, &stmtIter)) {
+         return false;
+     }
+ 
+     ValTypeVector args;
+     if (!CheckArguments(f, &stmtIter, &args)) {
+         return false;
+@@ -5928,24 +5926,24 @@ CheckFunction(ModuleValidator& m)
+         }
+     }
+ 
+     if (!CheckFinalReturn(f, lastNonEmptyStmt)) {
+         return false;
+     }
+ 
+     ModuleValidator::Func* func = nullptr;
+-    if (!CheckFunctionSignature(m, fn, FuncType(std::move(args), f.returnedType()),
+-                                FunctionName(fn), &func))
++    if (!CheckFunctionSignature(m, funNode, FuncType(std::move(args), f.returnedType()),
++                                FunctionName(funNode), &func))
+     {
+         return false;
+     }
+ 
+     if (func->defined()) {
+-        return m.failName(fn, "function '%s' already defined", FunctionName(fn));
++        return m.failName(funNode, "function '%s' already defined", FunctionName(funNode));
+     }
+ 
+     f.define(func, line);
+ 
+     // Release the parser's lifo memory only after the last use of a parse node.
+     m.parser().release(mark);
+     return true;
+ }
+@@ -6174,18 +6172,17 @@ CheckModuleEnd(ModuleValidator &m)
+ }
+ 
+ static SharedModule
+ CheckModule(JSContext* cx, AsmJSParser& parser, ParseNode* stmtList, UniqueLinkData* linkData,
+             unsigned* time)
+ {
+     int64_t before = PRMJ_Now();
+ 
+-    ParseNode* moduleFunctionNode = parser.pc->functionBox()->functionNode;
+-    MOZ_ASSERT(moduleFunctionNode);
++    CodeNode* moduleFunctionNode = parser.pc->functionBox()->functionNode;
+ 
+     ModuleValidator m(cx, parser, moduleFunctionNode);
+     if (!m.init()) {
+         return nullptr;
+     }
+ 
+     if (!CheckFunctionHead(m, moduleFunctionNode)) {
+         return nullptr;
+@@ -7001,17 +6998,17 @@ class ModuleCharsForStore : ModuleChars
+         // generation, modulo Assumptions.
+         //
+         // For functions created with 'new Function', function arguments are
+         // not present in the source so we must manually explicitly serialize
+         // and match the formals as a Vector of PropertyName.
+         isFunCtor_ = parser.pc->isStandaloneFunctionBody();
+         if (isFunCtor_) {
+             unsigned numArgs;
+-            ParseNode* functionNode = parser.pc->functionBox()->functionNode;
++            CodeNode* functionNode = parser.pc->functionBox()->functionNode;
+             ParseNode* arg = FunctionFormalParametersList(functionNode, &numArgs);
+             for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
+                 UniqueChars name = StringToNewUTF8CharsZ(nullptr, *arg->name());
+                 if (!name || !funCtorArgs_.append(std::move(name))) {
+                     return false;
+                 }
+             }
+         }
+@@ -7091,17 +7088,17 @@ class ModuleCharsForLookup : ModuleChars
+             // prevents
+             //   new Function('"use asm"; function f() {} return f')
+             // from incorrectly matching
+             //   new Function('"use asm"; function f() {} return ff')
+             if (parseBegin + chars_.length() != parseLimit) {
+                 return false;
+             }
+             unsigned numArgs;
+-            ParseNode* functionNode = parser.pc->functionBox()->functionNode;
++            CodeNode* functionNode = parser.pc->functionBox()->functionNode;
+             ParseNode* arg = FunctionFormalParametersList(functionNode, &numArgs);
+             if (funCtorArgs_.length() != numArgs) {
+                 return false;
+             }
+             for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
+                 UniqueChars name = StringToNewUTF8CharsZ(nullptr, *arg->name());
+                 if (!name || strcmp(funCtorArgs_[i].get(), name.get())) {
+                     return false;
+@@ -7270,17 +7267,17 @@ LookupAsmJSModuleInCache(JSContext* cx, 
+     MOZ_RELEASE_ASSERT(cursor == entry.memory + entry.serializedSize);
+ #endif
+     if (cursor != entry.memory + entry.serializedSize) {
+         return true;
+     }
+ 
+     // See AsmJSMetadata comment as well as ModuleValidator::init().
+     asmJSMetadata->toStringStart = parser.pc->functionBox()->toStringStart;
+-    asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin;
++    asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->body()->pn_pos.begin;
+     asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
+     asmJSMetadata->scriptSource.reset(parser.ss);
+ 
+     if (!parser.tokenStream.advance(asmJSMetadata->srcEndBeforeCurly())) {
+         return false;
+     }
+ 
+     int64_t after = PRMJ_Now();

+ 1100 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478482.patch

@@ -0,0 +1,1100 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726387 -32400
+#      Wed Sep 12 13:26:27 2018 +0900
+# Node ID 6a0f98626fd4a72680ebda4d7062aec25fef40b2
+# Parent  f4e0f1c70bf8a2ff4eadbde0e0cd9aed7fe8c135
+Bug 1479659 - Part 6: Add accessors to NullaryNode and change LoopControlStatement arity to PN_LOOP. r=jwalden
+
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -1038,17 +1038,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+       // Trivial cases with no side effects.
+       case ParseNodeKind::EmptyStatement:
+       case ParseNodeKind::True:
+       case ParseNodeKind::False:
+       case ParseNodeKind::Null:
+       case ParseNodeKind::RawUndefined:
+       case ParseNodeKind::Elision:
+       case ParseNodeKind::Generator:
+-        MOZ_ASSERT(pn->isArity(PN_NULLARY));
++        MOZ_ASSERT(pn->is<NullaryNode>());
+         *answer = false;
+         return true;
+ 
+       case ParseNodeKind::ObjectPropertyName:
+       case ParseNodeKind::String:
+       case ParseNodeKind::TemplateString:
+         MOZ_ASSERT(pn->is<NameNode>());
+         *answer = false;
+@@ -1076,19 +1076,27 @@ BytecodeEmitter::checkSideEffects(ParseN
+       case ParseNodeKind::ImportMeta: {
+         MOZ_ASSERT(pn->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
+         MOZ_ASSERT(pn->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
+         *answer = false;
+         return true;
+       }
+ 
+       case ParseNodeKind::Break:
++        MOZ_ASSERT(pn->is<BreakStatement>());
++        *answer = true;
++        return true;
++
+       case ParseNodeKind::Continue:
++        MOZ_ASSERT(pn->is<ContinueStatement>());
++        *answer = true;
++        return true;
++
+       case ParseNodeKind::Debugger:
+-        MOZ_ASSERT(pn->isArity(PN_NULLARY));
++        MOZ_ASSERT(pn->is<DebuggerStatement>());
+         *answer = true;
+         return true;
+ 
+       // Watch out for getters!
+       case ParseNodeKind::Dot:
+         MOZ_ASSERT(pn->is<BinaryNode>());
+         *answer = true;
+         return true;
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -100,18 +100,22 @@ ContainsHoistedDeclaration(JSContext* cx
+         return true;
+ 
+       case ParseNodeKind::Module:
+         *result = false;
+         return true;
+ 
+       // Statements with no sub-components at all.
+       case ParseNodeKind::EmptyStatement:
++        MOZ_ASSERT(node->is<NullaryNode>());
++        *result = false;
++        return true;
++
+       case ParseNodeKind::Debugger:
+-        MOZ_ASSERT(node->isArity(PN_NULLARY));
++        MOZ_ASSERT(node->is<DebuggerStatement>());
+         *result = false;
+         return true;
+ 
+       // Statements containing only an expression have no declarations.
+       case ParseNodeKind::ExpressionStatement:
+       case ParseNodeKind::Throw:
+       case ParseNodeKind::Return:
+         MOZ_ASSERT(node->is<UnaryNode>());
+@@ -1599,23 +1603,32 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+ 
+     switch (pn->getKind()) {
+       case ParseNodeKind::EmptyStatement:
+       case ParseNodeKind::True:
+       case ParseNodeKind::False:
+       case ParseNodeKind::Null:
+       case ParseNodeKind::RawUndefined:
+       case ParseNodeKind::Elision:
+-      case ParseNodeKind::Debugger:
+-      case ParseNodeKind::Break:
+-      case ParseNodeKind::Continue:
+       case ParseNodeKind::Generator:
+       case ParseNodeKind::ExportBatchSpec:
+       case ParseNodeKind::PosHolder:
+-        MOZ_ASSERT(pn->isArity(PN_NULLARY));
++        MOZ_ASSERT(pn->is<NullaryNode>());
++        return true;
++
++      case ParseNodeKind::Debugger:
++        MOZ_ASSERT(pn->is<DebuggerStatement>());
++        return true;
++
++      case ParseNodeKind::Break:
++        MOZ_ASSERT(pn->is<BreakStatement>());
++        return true;
++
++      case ParseNodeKind::Continue:
++        MOZ_ASSERT(pn->is<ContinueStatement>());
+         return true;
+ 
+       case ParseNodeKind::ObjectPropertyName:
+       case ParseNodeKind::String:
+       case ParseNodeKind::TemplateString:
+         MOZ_ASSERT(pn->is<NameNode>());
+         return true;
+ 
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -126,17 +126,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     NameNodeType newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
+         return new_<NameNode>(ParseNodeKind::ObjectPropertyName, JSOP_NOP, atom, pos);
+     }
+ 
+     NumericLiteralType newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) {
+         return new_<NumericLiteral>(value, decimalPoint, pos);
+     }
+ 
+-    ParseNode* newBooleanLiteral(bool cond, const TokenPos& pos) {
++    BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) {
+         return new_<BooleanLiteral>(cond, pos);
+     }
+ 
+     NameNodeType newStringLiteral(JSAtom* atom, const TokenPos& pos) {
+         return new_<NameNode>(ParseNodeKind::String, JSOP_NOP, atom, pos);
+     }
+ 
+     NameNodeType newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
+@@ -172,21 +172,21 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+          */
+         setEndPosition(callSiteObj, callSiteObj->rawNodes());
+     }
+ 
+     ThisLiteralType newThisLiteral(const TokenPos& pos, Node thisName) {
+         return new_<ThisLiteral>(pos, thisName);
+     }
+ 
+-    ParseNode* newNullLiteral(const TokenPos& pos) {
++    NullLiteralType newNullLiteral(const TokenPos& pos) {
+         return new_<NullLiteral>(pos);
+     }
+ 
+-    ParseNode* newRawUndefinedLiteral(const TokenPos& pos) {
++    RawUndefinedLiteralType newRawUndefinedLiteral(const TokenPos& pos) {
+         return new_<RawUndefinedLiteral>(pos);
+     }
+ 
+     // The Boxer object here is any object that can allocate ObjectBoxes.
+     // Specifically, a Boxer has a .newObjectBox(T) method that accepts a
+     // Rooted<RegExpObject*> argument and returns an ObjectBox*.
+     template <class Boxer>
+     RegExpLiteralType newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) {
+@@ -257,17 +257,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     ListNodeType newArrayLiteral(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::Array, TokenPos(begin, begin + 1));
+     }
+ 
+     MOZ_MUST_USE bool addElision(ListNodeType literal, const TokenPos& pos) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Array));
+ 
+-        ParseNode* elision = new_<NullaryNode>(ParseNodeKind::Elision, pos);
++        NullaryNode* elision = new_<NullaryNode>(ParseNodeKind::Elision, pos);
+         if (!elision) {
+             return false;
+         }
+         addList(/* list = */ literal, /* child = */ elision);
+         literal->setHasArrayHoleOrSpread();
+         literal->setHasNonConstInitializer();
+         return true;
+     }
+@@ -316,20 +316,20 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         return new_<ClassNode>(name, heritage, methodBlock, pos);
+     }
+     ListNodeType newClassMethodList(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::ClassMethodList, TokenPos(begin, begin + 1));
+     }
+     ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
+         return new_<ClassNames>(outer, inner, pos);
+     }
+-    BinaryNodeType newNewTarget(Node newHolder, Node targetHolder) {
++    BinaryNodeType newNewTarget(NullaryNodeType newHolder, NullaryNodeType targetHolder) {
+         return new_<BinaryNode>(ParseNodeKind::NewTarget, JSOP_NOP, newHolder, targetHolder);
+     }
+-    ParseNode* newPosHolder(const TokenPos& pos) {
++    NullaryNodeType newPosHolder(const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::PosHolder, pos);
+     }
+     UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) {
+         return new_<UnaryNode>(ParseNodeKind::SuperBase, pos, thisName);
+     }
+     MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) {
+         MOZ_ASSERT(literal->isKind(ParseNodeKind::Object));
+ 
+@@ -491,17 +491,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+             list->setHasTopLevelFunctionDeclarations();
+         }
+     }
+ 
+     MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) {
+         MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
+ 
+         TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1);
+-        ParseNode* makeGen = new_<NullaryNode>(ParseNodeKind::Generator, yieldPos);
++        NullaryNode* makeGen = new_<NullaryNode>(ParseNodeKind::Generator, yieldPos);
+         if (!makeGen) {
+             return false;
+         }
+ 
+         MOZ_ASSERT(genName->getOp() == JSOP_GETNAME);
+         genName->setOp(JSOP_SETNAME);
+         ParseNode* genInit = newAssignment(ParseNodeKind::Assign, /* lhs = */ genName,
+                                            /* rhs = */ makeGen);
+@@ -519,17 +519,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+ 
+     BinaryNodeType newSetThis(Node thisName, Node value) {
+         MOZ_ASSERT(thisName->getOp() == JSOP_GETNAME);
+         thisName->setOp(JSOP_SETNAME);
+         return newBinary(ParseNodeKind::SetThis, thisName, value);
+     }
+ 
+-    ParseNode* newEmptyStatement(const TokenPos& pos) {
++    NullaryNodeType newEmptyStatement(const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::EmptyStatement, pos);
+     }
+ 
+     BinaryNodeType newImportDeclaration(Node importSpecSet, Node moduleSpec, const TokenPos& pos) {
+         return new_<BinaryNode>(ParseNodeKind::Import, JSOP_NOP, pos,
+                                 importSpecSet, moduleSpec);
+     }
+ 
+@@ -561,25 +561,25 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+         return new_<BinaryNode>(ParseNodeKind::ExportDefault, JSOP_NOP, pos, kid, maybeBinding);
+     }
+ 
+     BinaryNodeType newExportSpec(Node bindingName, Node exportName) {
+         return newBinary(ParseNodeKind::ExportSpec, bindingName, exportName);
+     }
+ 
+-    ParseNode* newExportBatchSpec(const TokenPos& pos) {
++    NullaryNodeType newExportBatchSpec(const TokenPos& pos) {
+         return new_<NullaryNode>(ParseNodeKind::ExportBatchSpec, JSOP_NOP, pos);
+     }
+ 
+-    BinaryNodeType newImportMeta(Node importHolder, Node metaHolder) {
++    BinaryNodeType newImportMeta(NullaryNodeType importHolder, NullaryNodeType metaHolder) {
+         return new_<BinaryNode>(ParseNodeKind::ImportMeta, JSOP_NOP, importHolder, metaHolder);
+     }
+ 
+-    BinaryNodeType newCallImport(Node importHolder, Node singleArg) {
++    BinaryNodeType newCallImport(NullaryNodeType importHolder, Node singleArg) {
+         return new_<BinaryNode>(ParseNodeKind::CallImport, JSOP_NOP, importHolder, singleArg);
+     }
+ 
+     UnaryNodeType newExprStatement(Node expr, uint32_t end) {
+         MOZ_ASSERT(expr->pn_pos.end <= end);
+         return new_<UnaryNode>(ParseNodeKind::ExpressionStatement,
+                                TokenPos(expr->pn_pos.begin, end), expr);
+     }
+@@ -624,21 +624,21 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     {
+         return new_<SwitchStatement>(begin, discriminant, lexicalForCaseList, hasDefault);
+     }
+ 
+     CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) {
+         return new_<CaseClause>(expr, body, begin);
+     }
+ 
+-    ParseNode* newContinueStatement(PropertyName* label, const TokenPos& pos) {
++    ContinueStatementType newContinueStatement(PropertyName* label, const TokenPos& pos) {
+         return new_<ContinueStatement>(label, pos);
+     }
+ 
+-    ParseNode* newBreakStatement(PropertyName* label, const TokenPos& pos) {
++    BreakStatementType newBreakStatement(PropertyName* label, const TokenPos& pos) {
+         return new_<BreakStatement>(label, pos);
+     }
+ 
+     UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) {
+         MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos));
+         return new_<UnaryNode>(ParseNodeKind::Return, pos, expr);
+     }
+ 
+@@ -662,17 +662,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope,
+                                     Node finallyBlock)
+     {
+         TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
+         return new_<TernaryNode>(ParseNodeKind::Try, body, catchScope, finallyBlock, pos);
+     }
+ 
+-    ParseNode* newDebuggerStatement(const TokenPos& pos) {
++    DebuggerStatementType newDebuggerStatement(const TokenPos& pos) {
+         return new_<DebuggerStatement>(pos);
+     }
+ 
+     NameNodeType newPropertyName(PropertyName* name, const TokenPos& pos) {
+         return new_<NameNode>(ParseNodeKind::PropertyName, JSOP_NOP, name, pos);
+     }
+ 
+     PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -424,22 +424,31 @@ class NameResolver
+           // further work.
+           case ParseNodeKind::EmptyStatement:
+           case ParseNodeKind::True:
+           case ParseNodeKind::False:
+           case ParseNodeKind::Null:
+           case ParseNodeKind::RawUndefined:
+           case ParseNodeKind::Elision:
+           case ParseNodeKind::Generator:
+-          case ParseNodeKind::Break:
+-          case ParseNodeKind::Continue:
+-          case ParseNodeKind::Debugger:
+           case ParseNodeKind::ExportBatchSpec:
+           case ParseNodeKind::PosHolder:
+-            MOZ_ASSERT(cur->isArity(PN_NULLARY));
++            MOZ_ASSERT(cur->is<NullaryNode>());
++            break;
++
++          case ParseNodeKind::Debugger:
++            MOZ_ASSERT(cur->is<DebuggerStatement>());
++            break;
++
++          case ParseNodeKind::Break:
++            MOZ_ASSERT(cur->is<BreakStatement>());
++            break;
++
++          case ParseNodeKind::Continue:
++            MOZ_ASSERT(cur->is<ContinueStatement>());
+             break;
+ 
+           case ParseNodeKind::ObjectPropertyName:
+           case ParseNodeKind::String:
+           case ParseNodeKind::TemplateString:
+             MOZ_ASSERT(cur->is<NameNode>());
+             break;
+ 
+@@ -852,17 +861,17 @@ class NameResolver
+           // contain a single export batch specifier.
+           case ParseNodeKind::ExportSpecList:
+           case ParseNodeKind::ImportSpecList: {
+ #ifdef DEBUG
+             bool isImport = cur->isKind(ParseNodeKind::ImportSpecList);
+             ListNode* list = &cur->as<ListNode>();
+             ParseNode* item = list->head();
+             if (!isImport && item && item->isKind(ParseNodeKind::ExportBatchSpec)) {
+-                MOZ_ASSERT(item->isArity(PN_NULLARY));
++                MOZ_ASSERT(item->is<NullaryNode>());
+                 break;
+             }
+             for (ParseNode* item : list->contents()) {
+                 BinaryNode* spec = &item->as<BinaryNode>();
+                 MOZ_ASSERT(spec->isKind(isImport
+                                         ? ParseNodeKind::ImportSpec
+                                         : ParseNodeKind::ExportSpec));
+                 MOZ_ASSERT(spec->left()->isKind(ParseNodeKind::Name));
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -141,17 +141,17 @@ ParseNode::dump()
+     dump(out);
+ }
+ 
+ void
+ ParseNode::dump(GenericPrinter& out, int indent)
+ {
+     switch (ParseNodeArity(pn_arity)) {
+       case PN_NULLARY:
+-        ((NullaryNode*) this)->dump(out);
++        as<NullaryNode>().dump(out);
+         return;
+       case PN_UNARY:
+         as<UnaryNode>().dump(out, indent);
+         return;
+       case PN_BINARY:
+         as<BinaryNode>().dump(out, indent);
+         return;
+       case PN_TERNARY:
+@@ -167,16 +167,19 @@ ParseNode::dump(GenericPrinter& out, int
+         as<NameNode>().dump(out, indent);
+         return;
+       case PN_NUMBER:
+         as<NumericLiteral>().dump(out, indent);
+         return;
+       case PN_REGEXP:
+         as<RegExpLiteral>().dump(out, indent);
+         return;
++      case PN_LOOP:
++        as<LoopControlStatement>().dump(out, indent);
++        return;
+       case PN_SCOPE:
+         ((LexicalScopeNode*) this)->dump(out, indent);
+         return;
+     }
+     out.printf("#<BAD NODE %p, kind=%u, arity=%u>",
+                (void*) this, unsigned(getKind()), unsigned(pn_arity));
+ }
+ 
+@@ -211,16 +214,28 @@ NumericLiteral::dump(GenericPrinter& out
+ 
+ void
+ RegExpLiteral::dump(GenericPrinter& out, int indent)
+ {
+     out.printf("(%s)", parseNodeNames[size_t(getKind())]);
+ }
+ 
+ void
++LoopControlStatement::dump(GenericPrinter& out, int indent)
++{
++    const char* name = parseNodeNames[size_t(getKind())];
++    out.printf("(%s", name);
++    if (label()) {
++        out.printf(" ");
++        label()->dumpCharsNoNewline(out);
++    }
++    out.printf(")");
++}
++
++void
+ UnaryNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+     out.printf("(%s ", name);
+     indent += strlen(name) + 2;
+     DumpParseTree(kid(), out, indent);
+     out.printf(")");
+ }
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -341,17 +341,18 @@ IsTypeofKind(ParseNodeKind kind)
+  *           right: initializer
+  *   count: N > 0
+  * Return (UnaryNode)
+  *   kid: returned expression, or null if none
+  * ExpressionStatement (UnaryNode)
+  *   kid: expr
+  *   prologue: true if Directive Prologue member in original source, not
+  *             introduced via constant folding or other tree rewriting
+- * EmptyStatement nullary      (no fields)
++ * EmptyStatement (NullaryNode)
++ *   (no fields)
+  * Label (NameNode)
+  *   atom: label
+  *   expr: labeled statement
+  * Import (BinaryNode)
+  *   left: ImportSpecList import specifiers
+  *   right: String module specifier
+  * ImportSpecList (ListNode)
+  *   head: list of N ImportSpec nodes
+@@ -477,52 +478,55 @@ IsTypeofKind(ParseNodeKind kind)
+  *          TemplateString nodes
+  *            Array [, cooked TemplateString]+
+  *          where the Array is
+  *            [raw TemplateString]+
+  * RegExp (RegExpLiteral)
+  *   regexp: RegExp model object
+  * Number (NumericLiteral)
+  *   value: double value of numeric literal
+- * True,    nullary     pn_op: JSOp bytecode
+- * False,
+- * Null,
+- * RawUndefined
++ * True, False (BooleanLiteral)
++ *   pn_op: JSOp bytecode
++ * Null (NullLiteral)
++ *   pn_op: JSOp bytecode
++ * RawUndefined (RawUndefinedLiteral)
++ *   pn_op: JSOp bytecode
+  *
+  * This (UnaryNode)
+  *   kid: '.this' Name if function `this`, else nullptr
+  * SuperBase (UnaryNode)
+  *   kid: '.this' Name
+  * SuperCall (BinaryNode)
+  *   left: SuperBase
+  *   right: Arguments
+  * SetThis (BinaryNode)
+  *   left: '.this' Name
+  *   right: SuperCall
+  *
+  * LexicalScope scope   pn_u.scope.bindings: scope bindings
+  *                          pn_u.scope.body: scope body
+- * Generator    nullary
++ * Generator (NullaryNode)
+  * InitialYield (UnaryNode)
+  *   kid: generator object
+  * Yield, YieldStar, Await (UnaryNode)
+  *   kid: expr or null
+- * Nop          nullary
++ * Nop (NullaryNode)
+  */
+ enum ParseNodeArity
+ {
+     PN_NULLARY,                         /* 0 kids */
+     PN_UNARY,                           /* one kid, plus a couple of scalars */
+     PN_BINARY,                          /* two kids, plus a couple of scalars */
+     PN_TERNARY,                         /* three kids */
+     PN_CODE,                            /* module or function definition node */
+     PN_LIST,                            /* generic singly linked list */
+     PN_NAME,                            /* name, label, string */
+     PN_NUMBER,                          /* numeric literal */
+     PN_REGEXP,                          /* regexp literal */
++    PN_LOOP,                            /* loop control (break/continue) */
+     PN_SCOPE                            /* lexical scope */
+ };
+ 
+ #define FOR_EACH_PARSENODE_SUBCLASS(macro) \
+     macro(BinaryNode, BinaryNodeType, asBinary) \
+     macro(AssignmentNode, AssignmentNodeType, asAssignment) \
+     macro(CaseClause, CaseClauseType, asCaseClause) \
+     macro(ClassMethod, ClassMethodType, asClassMethod) \
+@@ -532,33 +536,39 @@ enum ParseNodeArity
+     macro(PropertyByValue, PropertyByValueType, asPropertyByValue) \
+     macro(SwitchStatement, SwitchStatementType, asSwitchStatement) \
+     \
+     macro(CodeNode, CodeNodeType, asCode) \
+     \
+     macro(ListNode, ListNodeType, asList) \
+     macro(CallSiteNode, CallSiteNodeType, asCallSite) \
+     \
++    macro(LoopControlStatement, LoopControlStatementType, asLoopControlStatement) \
++    macro(BreakStatement, BreakStatementType, asBreakStatement) \
++    macro(ContinueStatement, ContinueStatementType, asContinueStatement) \
++    \
+     macro(NameNode, NameNodeType, asName) \
+     macro(LabeledStatement, LabeledStatementType, asLabeledStatement) \
+     \
++    macro(NullaryNode, NullaryNodeType, asNullary) \
++    macro(BooleanLiteral, BooleanLiteralType, asBooleanLiteral) \
++    macro(DebuggerStatement, DebuggerStatementType, asDebuggerStatement) \
++    macro(NullLiteral, NullLiteralType, asNullLiteral) \
++    macro(RawUndefinedLiteral, RawUndefinedLiteralType, asRawUndefinedLiteral) \
++    \
+     macro(NumericLiteral, NumericLiteralType, asNumericLiteral) \
+     \
+     macro(RegExpLiteral, RegExpLiteralType, asRegExpLiteral) \
+     \
+     macro(TernaryNode, TernaryNodeType, asTernary) \
+     macro(ClassNode, ClassNodeType, asClass) \
+     macro(ConditionalExpression, ConditionalExpressionType, asConditionalExpression) \
+     macro(UnaryNode, UnaryNodeType, asUnary) \
+     macro(ThisLiteral, ThisLiteralType, asThisLiteral)
+ 
+-class LoopControlStatement;
+-class BreakStatement;
+-class ContinueStatement;
+-
+ #define DECLARE_CLASS(typeName, longTypeName, asMethodName) \
+ class typeName;
+ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
+ #undef DECLARE_CLASS
+ 
+ class ParseNode
+ {
+     ParseNodeKind pn_type;   /* ParseNodeKind::PNK_* type */
+@@ -791,23 +801,29 @@ class ParseNode
+ #ifdef DEBUG
+     // Debugger-friendly stderr printer.
+     void dump();
+     void dump(GenericPrinter& out);
+     void dump(GenericPrinter& out, int indent);
+ #endif
+ };
+ 
+-struct NullaryNode : public ParseNode
++class NullaryNode : public ParseNode
+ {
++  public:
+     NullaryNode(ParseNodeKind kind, const TokenPos& pos)
+       : ParseNode(kind, JSOP_NOP, PN_NULLARY, pos) {}
++
+     NullaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
+       : ParseNode(kind, op, PN_NULLARY, pos) {}
+ 
++    static bool test(const ParseNode& node) {
++        return node.isArity(PN_NULLARY);
++    }
++
+ #ifdef DEBUG
+     void dump(GenericPrinter& out);
+ #endif
+ };
+ 
+ class NameNode : public ParseNode
+ {
+   protected:
+@@ -1554,72 +1570,82 @@ class CaseClause : public BinaryNode
+         return match;
+     }
+ };
+ 
+ class LoopControlStatement : public ParseNode
+ {
+   protected:
+     LoopControlStatement(ParseNodeKind kind, PropertyName* label, const TokenPos& pos)
+-      : ParseNode(kind, JSOP_NOP, PN_NULLARY, pos)
++      : ParseNode(kind, JSOP_NOP, PN_LOOP, pos)
+     {
+         MOZ_ASSERT(kind == ParseNodeKind::Break || kind == ParseNodeKind::Continue);
+         pn_u.loopControl.label = label;
+     }
+ 
+   public:
+     /* Label associated with this break/continue statement, if any. */
+     PropertyName* label() const {
+         return pn_u.loopControl.label;
+     }
+ 
++#ifdef DEBUG
++    void dump(GenericPrinter& out, int indent);
++#endif
++
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Break) || node.isKind(ParseNodeKind::Continue);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
++        MOZ_ASSERT_IF(match, node.isArity(PN_LOOP));
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
+ };
+ 
+ class BreakStatement : public LoopControlStatement
+ {
+   public:
+     BreakStatement(PropertyName* label, const TokenPos& pos)
+       : LoopControlStatement(ParseNodeKind::Break, label, pos)
+     { }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Break);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
++        MOZ_ASSERT_IF(match, node.is<LoopControlStatement>());
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
+ };
+ 
+ class ContinueStatement : public LoopControlStatement
+ {
+   public:
+     ContinueStatement(PropertyName* label, const TokenPos& pos)
+       : LoopControlStatement(ParseNodeKind::Continue, label, pos)
+     { }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Continue);
+-        MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
++        MOZ_ASSERT_IF(match, node.is<LoopControlStatement>());
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
+ };
+ 
+-class DebuggerStatement : public ParseNode
++class DebuggerStatement : public NullaryNode
+ {
+   public:
+     explicit DebuggerStatement(const TokenPos& pos)
+-      : ParseNode(ParseNodeKind::Debugger, JSOP_NOP, PN_NULLARY, pos)
++      : NullaryNode(ParseNodeKind::Debugger, JSOP_NOP, pos)
+     { }
++
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::Debugger);
++        MOZ_ASSERT_IF(match, node.is<NullaryNode>());
++        return match;
++    }
+ };
+ 
+ class ConditionalExpression : public TernaryNode
+ {
+   public:
+     ConditionalExpression(ParseNode* condition, ParseNode* thenExpr, ParseNode* elseExpr)
+       : TernaryNode(ParseNodeKind::Conditional, condition, thenExpr, elseExpr,
+                     TokenPos(condition->pn_pos.begin, elseExpr->pn_pos.end))
+@@ -1664,35 +1690,53 @@ class ThisLiteral : public UnaryNode
+ };
+ 
+ class NullLiteral : public NullaryNode
+ {
+   public:
+     explicit NullLiteral(const TokenPos& pos)
+       : NullaryNode(ParseNodeKind::Null, JSOP_NULL, pos)
+     { }
++
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::Null);
++        MOZ_ASSERT_IF(match, node.is<NullaryNode>());
++        return match;
++    }
+ };
+ 
+ // This is only used internally, currently just for tagged templates.
+ // It represents the value 'undefined' (aka `void 0`), like NullLiteral
+ // represents the value 'null'.
+ class RawUndefinedLiteral : public NullaryNode
+ {
+   public:
+     explicit RawUndefinedLiteral(const TokenPos& pos)
+       : NullaryNode(ParseNodeKind::RawUndefined, JSOP_UNDEFINED, pos) { }
++
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::RawUndefined);
++        MOZ_ASSERT_IF(match, node.is<NullaryNode>());
++        return match;
++    }
+ };
+ 
+ class BooleanLiteral : public NullaryNode
+ {
+   public:
+     BooleanLiteral(bool b, const TokenPos& pos)
+       : NullaryNode(b ? ParseNodeKind::True : ParseNodeKind::False,
+                     b ? JSOP_TRUE : JSOP_FALSE, pos)
+     { }
++
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::True) || node.isKind(ParseNodeKind::False);
++        MOZ_ASSERT_IF(match, node.is<NullaryNode>());
++        return match;
++    }
+ };
+ 
+ class RegExpLiteral : public ParseNode
+ {
+   public:
+     RegExpLiteral(ObjectBox* reobj, const TokenPos& pos)
+       : ParseNode(ParseNodeKind::RegExp, JSOP_REGEXP, PN_REGEXP, pos)
+     {
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -6213,17 +6213,17 @@ GeneralParser<ParseHandler, CharT>::expo
+ 
+     ListNodeType kid = handler.newList(ParseNodeKind::ExportSpecList, pos());
+     if (!kid) {
+         return null();
+     }
+ 
+     // Handle the form |export *| by adding a special export batch
+     // specifier to the list.
+-    Node exportSpec = handler.newExportBatchSpec(pos());
++    NullaryNodeType exportSpec = handler.newExportBatchSpec(pos());
+     if (!exportSpec) {
+         return null();
+     }
+ 
+     handler.addList(kid, exportSpec);
+ 
+     MUST_MATCH_TOKEN(TokenKind::From, JSMSG_FROM_AFTER_EXPORT_STAR);
+ 
+@@ -7389,17 +7389,17 @@ GeneralParser<ParseHandler, CharT>::swit
+     }
+ 
+     handler.setEndPosition(lexicalForCaseList, pos().end);
+ 
+     return handler.newSwitchStatement(begin, discriminant, lexicalForCaseList, seenDefault);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::ContinueStatementType
+ GeneralParser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Continue));
+     uint32_t begin = pos().begin;
+ 
+     RootedPropertyName label(context);
+     if (!matchLabel(yieldHandling, &label)) {
+         return null();
+@@ -7421,17 +7421,17 @@ GeneralParser<ParseHandler, CharT>::cont
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+ 
+     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::BreakStatementType
+ GeneralParser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Break));
+     uint32_t begin = pos().begin;
+ 
+     RootedPropertyName label(context);
+     if (!matchLabel(yieldHandling, &label)) {
+         return null();
+@@ -7913,17 +7913,17 @@ GeneralParser<ParseHandler, CharT>::catc
+ 
+     // The catch parameter names are not bound in the body scope, so remove
+     // them before generating bindings.
+     scope.removeCatchParameters(pc, catchParamScope);
+     return finishLexicalScope(scope, list);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::DebuggerStatementType
+ GeneralParser<ParseHandler, CharT>::debuggerStatement()
+ {
+     TokenPos p;
+     p.begin = pos().begin;
+     if (!matchOrInsertSemicolon()) {
+         return null();
+     }
+     p.end = pos().end;
+@@ -10827,17 +10827,17 @@ GeneralParser<ParseHandler, CharT>::meth
+ template <class ParseHandler, typename CharT>
+ bool
+ GeneralParser<ParseHandler, CharT>::tryNewTarget(BinaryNodeType* newTarget)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::New));
+ 
+     *newTarget = null();
+ 
+-    Node newHolder = handler.newPosHolder(pos());
++    NullaryNodeType newHolder = handler.newPosHolder(pos());
+     if (!newHolder) {
+         return false;
+     }
+ 
+     uint32_t begin = pos().begin;
+ 
+     // |new| expects to look for an operand, so we will honor that.
+     TokenKind next;
+@@ -10859,32 +10859,32 @@ GeneralParser<ParseHandler, CharT>::tryN
+         return false;
+     }
+ 
+     if (!pc->sc()->allowNewTarget()) {
+         errorAt(begin, JSMSG_BAD_NEWTARGET);
+         return false;
+     }
+ 
+-    Node targetHolder = handler.newPosHolder(pos());
++    NullaryNodeType targetHolder = handler.newPosHolder(pos());
+     if (!targetHolder) {
+         return false;
+     }
+ 
+     *newTarget = handler.newNewTarget(newHolder, targetHolder);
+     return !!*newTarget;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+ typename ParseHandler::BinaryNodeType
+ GeneralParser<ParseHandler, CharT>::importExpr(YieldHandling yieldHandling)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import));
+ 
+-    Node importHolder = handler.newPosHolder(pos());
++    NullaryNodeType importHolder = handler.newPosHolder(pos());
+     if (!importHolder) {
+         return null();
+     }
+ 
+     TokenKind next;
+     if (!tokenStream.getToken(&next)) {
+         return null();
+     }
+@@ -10898,17 +10898,17 @@ GeneralParser<ParseHandler, CharT>::impo
+             return null();
+         }
+ 
+         if (parseGoal() != ParseGoal::Module) {
+             errorAt(pos().begin, JSMSG_IMPORT_META_OUTSIDE_MODULE);
+             return null();
+         }
+ 
+-        Node metaHolder = handler.newPosHolder(pos());
++        NullaryNodeType metaHolder = handler.newPosHolder(pos());
+         if (!metaHolder) {
+             return null();
+         }
+ 
+         return handler.newImportMeta(importHolder, metaHolder);
+     } else if (next == TokenKind::LeftParen) {
+         Node arg = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+         if (!arg) {
+diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
+--- a/js/src/frontend/Parser.h
++++ b/js/src/frontend/Parser.h
+@@ -1032,24 +1032,24 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+     bool forHeadStart(YieldHandling yieldHandling,
+                       ParseNodeKind* forHeadKind,
+                       Node* forInitialPart,
+                       mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
+                       Node* forInOrOfExpression);
+     Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
+ 
+     SwitchStatementType switchStatement(YieldHandling yieldHandling);
+-    Node continueStatement(YieldHandling yieldHandling);
+-    Node breakStatement(YieldHandling yieldHandling);
++    ContinueStatementType continueStatement(YieldHandling yieldHandling);
++    BreakStatementType breakStatement(YieldHandling yieldHandling);
+     UnaryNodeType returnStatement(YieldHandling yieldHandling);
+     BinaryNodeType withStatement(YieldHandling yieldHandling);
+     UnaryNodeType throwStatement(YieldHandling yieldHandling);
+     TernaryNodeType tryStatement(YieldHandling yieldHandling);
+     Node catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope);
+-    Node debuggerStatement();
++    DebuggerStatementType debuggerStatement();
+ 
+     Node variableStatement(YieldHandling yieldHandling);
+ 
+     LabeledStatementType labeledStatement(YieldHandling yieldHandling);
+     Node labeledItem(YieldHandling yieldHandling);
+ 
+     TernaryNodeType ifStatement(YieldHandling yieldHandling);
+     Node consequentOrAlternative(YieldHandling yieldHandling);
+diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
+--- a/js/src/frontend/SyntaxParseHandler.h
++++ b/js/src/frontend/SyntaxParseHandler.h
+@@ -203,17 +203,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     NameNodeType newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
+         return NodeName;
+     }
+ 
+     NumericLiteralType newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+ 
+-    Node newBooleanLiteral(bool cond, const TokenPos& pos) { return NodeGeneric; }
++    BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) { return NodeGeneric; }
+ 
+     NameNodeType newStringLiteral(JSAtom* atom, const TokenPos& pos) {
+         lastAtom = atom;
+         lastStringPos = pos;
+         return NodeUnparenthesizedString;
+     }
+ 
+     NameNodeType newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
+@@ -222,18 +222,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     CallSiteNodeType newCallSiteObject(uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+     void addToCallSiteObject(CallSiteNodeType callSiteObj, Node rawNode, Node cookedNode) {}
+ 
+     ThisLiteralType newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
+-    Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
+-    Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
++    NullLiteralType newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
++    RawUndefinedLiteralType newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
+ 
+     template <class Boxer>
+     RegExpLiteralType newRegExp(Node reobj, const TokenPos& pos, Boxer& boxer) {
+         return NodeGeneric;
+     }
+ 
+     ConditionalExpressionType newConditional(Node cond, Node thenExpr, Node elseExpr) {
+         return NodeGeneric;
+@@ -280,18 +280,20 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     ListNodeType newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
+     ListNodeType newClassMethodList(uint32_t begin) { return NodeGeneric; }
+     ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+     ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
+ 
+-    BinaryNodeType newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
+-    Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
++    BinaryNodeType newNewTarget(NullaryNodeType newHolder, NullaryNodeType targetHolder) {
++        return NodeGeneric;
++    }
++    NullaryNodeType newPosHolder(const TokenPos& pos) { return NodeGeneric; }
+     UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
+ 
+     MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) { return true; }
+     BinaryNodeType newPropertyDefinition(Node key, Node val) { return NodeGeneric; }
+     void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {}
+     MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key, Node expr) { return true; }
+     MOZ_MUST_USE bool addShorthand(ListNodeType literal, NameNodeType name, NameNodeType expr) { return true; }
+     MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { return true; }
+@@ -310,37 +312,37 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     // Statements
+ 
+     ListNodeType newStatementList(const TokenPos& pos) { return NodeGeneric; }
+     void addStatementToList(ListNodeType list, Node stmt) {}
+     void setListEndPosition(ListNodeType list, const TokenPos& pos) {}
+     void addCaseStatementToList(ListNodeType list, CaseClauseType caseClause) {}
+     MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) { return true; }
+-    Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
++    NullaryNodeType newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
+ 
+     UnaryNodeType newExportDeclaration(Node kid, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+     BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
+         return NodeGeneric;
+     }
+     BinaryNodeType newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+     BinaryNodeType newExportSpec(Node bindingName, Node exportName) {
+         return NodeGeneric;
+     }
+-    Node newExportBatchSpec(const TokenPos& pos) {
++    NullaryNodeType newExportBatchSpec(const TokenPos& pos) {
+         return NodeGeneric;
+     }
+-    BinaryNodeType newImportMeta(Node importHolder, Node metaHolder) {
++    BinaryNodeType newImportMeta(NullaryNodeType importHolder, NullaryNodeType metaHolder) {
+         return NodeGeneric;
+     }
+-    BinaryNodeType newCallImport(Node importHolder, Node singleArg) {
++    BinaryNodeType newCallImport(NullaryNodeType importHolder, Node singleArg) {
+         return NodeGeneric;
+     }
+ 
+     BinaryNodeType newSetThis(Node thisName, Node value) { return value; }
+ 
+     UnaryNodeType newExprStatement(Node expr, uint32_t end) {
+         return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
+     }
+@@ -353,31 +355,31 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+     BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
+     SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant,
+                                            Node lexicalForCaseList, bool hasDefault)
+     {
+         return NodeGeneric;
+     }
+     CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+-    Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
+-    Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
++    ContinueStatementType newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
++    BreakStatementType newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
+     UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) { return NodeReturn; }
+     UnaryNodeType newExpressionBody(Node expr) { return NodeReturn; }
+     BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+ 
+     LabeledStatementType newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+     UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
+     TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
+         return NodeGeneric;
+     }
+-    Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
++    DebuggerStatementType newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
+ 
+     NameNodeType newPropertyName(PropertyName* name, const TokenPos& pos) {
+         lastAtom = name;
+         return NodeGeneric;
+     }
+ 
+     PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
+         return NodeDottedProperty;
+diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
+--- a/js/src/wasm/AsmJS.cpp
++++ b/js/src/wasm/AsmJS.cpp
+@@ -589,17 +589,16 @@ ExpressionStatementExpr(ParseNode* pn)
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::ExpressionStatement));
+     return UnaryKid(pn);
+ }
+ 
+ static inline PropertyName*
+ LoopControlMaybeLabel(ParseNode* pn)
+ {
+     MOZ_ASSERT(pn->isKind(ParseNodeKind::Break) || pn->isKind(ParseNodeKind::Continue));
+-    MOZ_ASSERT(pn->isArity(PN_NULLARY));
+     return pn->as<LoopControlStatement>().label();
+ }
+ 
+ static inline PropertyName*
+ LabeledStatementLabel(ParseNode* pn)
+ {
+     return pn->as<LabeledStatement>().label();
+ }

+ 1388 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478483.patch

@@ -0,0 +1,1388 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726392 -32400
+#      Wed Sep 12 13:26:32 2018 +0900
+# Node ID 8789cfe8c9adf1f0d553939f4a4fed8d1899213d
+# Parent  6a0f98626fd4a72680ebda4d7062aec25fef40b2
+Bug 1479659 - Part 7: Add accessors to LexicalScopeNode. r=jwalden
+
+diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
+--- a/js/src/builtin/ReflectParse.cpp
++++ b/js/src/builtin/ReflectParse.cpp
+@@ -2326,18 +2326,18 @@ ASTSerializer::tryStatement(TernaryNode*
+     MOZ_ASSERT_IF(finallyNode, tryNode->pn_pos.encloses(finallyNode->pn_pos));
+ 
+     RootedValue body(cx);
+     if (!statement(bodyNode, &body)) {
+         return false;
+     }
+ 
+     RootedValue handler(cx, NullValue());
+-    if (ParseNode* catchScope = catchNode) {
+-        MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
++    if (catchNode) {
++        LexicalScopeNode* catchScope = &catchNode->as<LexicalScopeNode>();
+         if (!catchClause(&catchScope->scopeBody()->as<BinaryNode>(), &handler)) {
+             return false;
+         }
+     }
+ 
+     RootedValue finally(cx);
+     return optStatement(finallyNode, &finally) &&
+            builder.tryStatement(body, handler, finally, &tryNode->pn_pos, dst);
+@@ -2425,17 +2425,17 @@ ASTSerializer::statement(ParseNode* pn, 
+       case ParseNodeKind::ExpressionStatement:
+       {
+         RootedValue expr(cx);
+         return expression(pn->as<UnaryNode>().kid(), &expr) &&
+             builder.expressionStatement(expr, &pn->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::LexicalScope:
+-        pn = pn->scopeBody();
++        pn = pn->as<LexicalScopeNode>().scopeBody();
+         if (!pn->isKind(ParseNodeKind::StatementList)) {
+             return statement(pn, dst);
+         }
+         MOZ_FALLTHROUGH;
+ 
+       case ParseNodeKind::StatementList:
+         return blockStatement(&pn->as<ListNode>(), dst);
+ 
+@@ -2524,18 +2524,19 @@ ASTSerializer::statement(ParseNode* pn, 
+ 
+         RootedValue stmt(cx);
+         if (!statement(stmtNode, &stmt)) {
+             return false;
+         }
+ 
+         if (head->isKind(ParseNodeKind::ForIn) || head->isKind(ParseNodeKind::ForOf)) {
+             RootedValue var(cx);
+-            if (initNode->isKind(ParseNodeKind::LexicalScope)) {
+-                if (!variableDeclaration(&initNode->scopeBody()->as<ListNode>(), true, &var)) {
++            if (initNode->is<LexicalScopeNode>()) {
++                LexicalScopeNode* scopeNode = &initNode->as<LexicalScopeNode>();
++                if (!variableDeclaration(&scopeNode->scopeBody()->as<ListNode>(), true, &var)) {
+                     return false;
+                 }
+             } else if (!initNode->isKind(ParseNodeKind::Var) &&
+                        !initNode->isKind(ParseNodeKind::Let) &&
+                        !initNode->isKind(ParseNodeKind::Const))
+             {
+                 if (!pattern(initNode, &var)) {
+                     return false;
+@@ -3493,56 +3494,56 @@ ASTSerializer::function(CodeNode* funNod
+ }
+ 
+ bool
+ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
+                                    bool isAsync, bool isExpression,
+                                    MutableHandleValue body, MutableHandleValue rest)
+ {
+     ListNode* argsList;
+-    ParseNode* pnbody;
++    ParseNode* bodyNode;
+ 
+     /* Extract the args and body separately. */
+     if (pn->isKind(ParseNodeKind::ParamsBody)) {
+         argsList = &pn->as<ListNode>();
+-        pnbody = argsList->last();
++        bodyNode = argsList->last();
+     } else {
+         argsList = nullptr;
+-        pnbody = pn;
++        bodyNode = pn;
+     }
+ 
+-    if (pnbody->isKind(ParseNodeKind::LexicalScope)) {
+-        pnbody = pnbody->scopeBody();
++    if (bodyNode->is<LexicalScopeNode>()) {
++        bodyNode = bodyNode->as<LexicalScopeNode>().scopeBody();
+     }
+ 
+     /* Serialize the arguments and body. */
+-    switch (pnbody->getKind()) {
++    switch (bodyNode->getKind()) {
+       case ParseNodeKind::Return: /* expression closure, no destructured args */
+         return functionArgs(pn, argsList, args, defaults, rest) &&
+-               expression(pnbody->as<UnaryNode>().kid(), body);
++               expression(bodyNode->as<UnaryNode>().kid(), body);
+ 
+       case ParseNodeKind::StatementList:     /* statement closure */
+       {
+-        ParseNode* firstNode = pnbody->as<ListNode>().head();
++        ParseNode* firstNode = bodyNode->as<ListNode>().head();
+ 
+         // Skip over initial yield in generator.
+         if (firstNode && firstNode->isKind(ParseNodeKind::InitialYield)) {
+             firstNode = firstNode->pn_next;
+         }
+ 
+         // Async arrow with expression body is converted into STATEMENTLIST
+         // to insert initial yield.
+         if (isAsync && isExpression) {
+             MOZ_ASSERT(firstNode->getKind() == ParseNodeKind::Return);
+             return functionArgs(pn, argsList, args, defaults, rest) &&
+                    expression(firstNode->as<UnaryNode>().kid(), body);
+         }
+ 
+         return functionArgs(pn, argsList, args, defaults, rest) &&
+-               functionBody(firstNode, &pnbody->pn_pos, body);
++               functionBody(firstNode, &bodyNode->pn_pos, body);
+       }
+ 
+       default:
+         LOCAL_NOT_REACHED("unexpected function contents");
+     }
+ }
+ 
+ bool
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -557,18 +557,18 @@ BytecodeEmitter::getOffsetForLoop(ParseN
+ {
+     if (!nextpn) {
+         return Nothing();
+     }
+ 
+     // Try to give the JSOP_LOOPHEAD and JSOP_LOOPENTRY the same line number as
+     // the next instruction. nextpn is often a block, in which case the next
+     // instruction typically comes from the first statement inside.
+-    if (nextpn->isKind(ParseNodeKind::LexicalScope)) {
+-        nextpn = nextpn->scopeBody();
++    if (nextpn->is<LexicalScopeNode>()) {
++        nextpn = nextpn->as<LexicalScopeNode>().scopeBody();
+     }
+     if (nextpn->isKind(ParseNodeKind::StatementList)) {
+         if (ParseNode* firstStatement = nextpn->as<ListNode>().head()) {
+             nextpn = firstStatement;
+         }
+     }
+ 
+     return Some(nextpn->pn_pos.begin);
+@@ -1422,18 +1422,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+         TernaryNode* tryNode = &pn->as<TernaryNode>();
+         if (!checkSideEffects(tryNode->kid1(), answer)) {
+             return false;
+         }
+         if (*answer) {
+             return true;
+         }
+         if (ParseNode* catchScope = tryNode->kid2()) {
+-            MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+-            if (!checkSideEffects(catchScope, answer)) {
++            if (!checkSideEffects(&catchScope->as<LexicalScopeNode>(), answer)) {
+                 return false;
+             }
+             if (*answer) {
+                 return true;
+             }
+         }
+         if (ParseNode* finallyBlock = tryNode->kid3()) {
+             if (!checkSideEffects(finallyBlock, answer)) {
+@@ -1463,18 +1462,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+         }
+         return *answer || checkSideEffects(&switchStmt->lexicalForCaseList(), answer);
+       }
+ 
+       case ParseNodeKind::Label:
+         return checkSideEffects(pn->as<NameNode>().expression(), answer);
+ 
+       case ParseNodeKind::LexicalScope:
+-        MOZ_ASSERT(pn->isArity(PN_SCOPE));
+-        return checkSideEffects(pn->scopeBody(), answer);
++        return checkSideEffects(pn->as<LexicalScopeNode>().scopeBody(), answer);
+ 
+       // We could methodically check every interpolated expression, but it's
+       // probably not worth the trouble.  Treat template strings as effect-free
+       // only if they don't contain any substitutions.
+       case ParseNodeKind::TemplateStringList: {
+         ListNode* list = &pn->as<ListNode>();
+         MOZ_ASSERT(!list->empty());
+         MOZ_ASSERT((list->count() % 2) == 1,
+@@ -2536,17 +2534,17 @@ BytecodeEmitter::emitNumberOp(double dva
+ /*
+  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047.
+  * LLVM is deciding to inline this function which uses a lot of stack space
+  * into emitTree which is recursive and uses relatively little stack space.
+  */
+ MOZ_NEVER_INLINE bool
+ BytecodeEmitter::emitSwitch(SwitchStatement* switchStmt)
+ {
+-    ParseNode& lexical = switchStmt->lexicalForCaseList();
++    LexicalScopeNode& lexical = switchStmt->lexicalForCaseList();
+     MOZ_ASSERT(lexical.isKind(ParseNodeKind::LexicalScope));
+     ListNode* cases = &lexical.scopeBody()->as<ListNode>();
+     MOZ_ASSERT(cases->isKind(ParseNodeKind::StatementList));
+ 
+     SwitchEmitter se(this);
+     if (!se.emitDiscriminant(Some(switchStmt->pn_pos.begin))) {
+         return false;
+     }
+@@ -2818,31 +2816,32 @@ BytecodeEmitter::emitScript(ParseNode* b
+         if (!emitterScope.enterModule(this, sc->asModuleContext())) {
+             return false;
+         }
+     }
+ 
+     setFunctionBodyEndPos(body->pn_pos);
+ 
+     if (sc->isEvalContext() && !sc->strict() &&
+-        body->isKind(ParseNodeKind::LexicalScope) && !body->isEmptyScope())
++        body->is<LexicalScopeNode>() && !body->as<LexicalScopeNode>().isEmptyScope())
+     {
+         // Sloppy eval scripts may need to emit DEFFUNs in the prologue. If there is
+         // an immediately enclosed lexical scope, we need to enter the lexical
+         // scope in the prologue for the DEFFUNs to pick up the right
+         // environment chain.
+         EmitterScope lexicalEmitterScope(this);
++        LexicalScopeNode* scope = &body->as<LexicalScopeNode>();
+ 
+         switchToPrologue();
+-        if (!lexicalEmitterScope.enterLexical(this, ScopeKind::Lexical, body->scopeBindings())) {
++        if (!lexicalEmitterScope.enterLexical(this, ScopeKind::Lexical, scope->scopeBindings())) {
+             return false;
+         }
+         switchToMain();
+ 
+-        if (!emitLexicalScopeBody(body->scopeBody())) {
++        if (!emitLexicalScopeBody(scope->scopeBody())) {
+             return false;
+         }
+ 
+         if (!lexicalEmitterScope.leave(this)) {
+             return false;
+         }
+     } else {
+         if (!emitTree(body)) {
+@@ -4853,17 +4852,17 @@ BytecodeEmitter::emitTry(TernaryNode* tr
+         // if there is a finally block:
+         //   gosub <finally>
+         //   goto <after finally>
+         if (!tryCatch.emitCatch()) {
+             return false;
+         }
+ 
+         // Emit the lexical scope and catch body.
+-        MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
++        MOZ_ASSERT(catchScope->is<LexicalScopeNode>());
+         if (!emitTree(catchScope)) {
+             return false;
+         }
+     }
+ 
+     // Emit the finally handler, if there is one.
+     if (finallyNode) {
+         if (!tryCatch.emitFinally(Some(finallyNode->pn_pos.begin))) {
+@@ -4983,49 +4982,47 @@ BytecodeEmitter::emitLexicalScopeBody(Pa
+ 
+     // Line notes were updated by emitLexicalScope.
+     return emitTree(body, ValueUsage::WantValue, emitLineNote);
+ }
+ 
+ // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
+ // the comment on emitSwitch.
+ MOZ_NEVER_INLINE bool
+-BytecodeEmitter::emitLexicalScope(ParseNode* pn)
+-{
+-    MOZ_ASSERT(pn->isKind(ParseNodeKind::LexicalScope));
+-
++BytecodeEmitter::emitLexicalScope(LexicalScopeNode* lexicalScope)
++{
+     TDZCheckCache tdzCache(this);
+ 
+-    ParseNode* body = pn->scopeBody();
+-    if (pn->isEmptyScope()) {
++    ParseNode* body = lexicalScope->scopeBody();
++    if (lexicalScope->isEmptyScope()) {
+         return emitLexicalScopeBody(body);
+     }
+ 
+     // We are about to emit some bytecode for what the spec calls "declaration
+     // instantiation". Assign these instructions to the opening `{` of the
+     // block. (Using the location of each declaration we're instantiating is
+     // too weird when stepping in the debugger.)
+     if (!ParseNodeRequiresSpecialLineNumberNotes(body)) {
+-        if (!updateSourceCoordNotes(pn->pn_pos.begin)) {
++        if (!updateSourceCoordNotes(lexicalScope->pn_pos.begin)) {
+             return false;
+         }
+     }
+ 
+     EmitterScope emitterScope(this);
+     ScopeKind kind;
+     if (body->isKind(ParseNodeKind::Catch)) {
+         BinaryNode* catchNode = &body->as<BinaryNode>();
+         kind = (!catchNode->left() || catchNode->left()->isKind(ParseNodeKind::Name))
+                ? ScopeKind::SimpleCatch
+                : ScopeKind::Catch;
+     } else {
+         kind = ScopeKind::Lexical;
+     }
+ 
+-    if (!emitterScope.enterLexical(this, kind, pn->scopeBindings())) {
++    if (!emitterScope.enterLexical(this, kind, lexicalScope->scopeBindings())) {
+         return false;
+     }
+ 
+     if (body->isKind(ParseNodeKind::For)) {
+         // for loops need to emit {FRESHEN,RECREATE}LEXICALENV if there are
+         // lexical declarations in the head. Signal this by passing a
+         // non-nullptr lexical scope.
+         if (!emitFor(&body->as<ForNode>(), &emitterScope)) {
+@@ -9279,17 +9276,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+       case ParseNodeKind::Call:
+       case ParseNodeKind::SuperCall:
+         if (!emitCallOrNew(&pn->as<BinaryNode>(), valueUsage)) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::LexicalScope:
+-        if (!emitLexicalScope(pn)) {
++        if (!emitLexicalScope(&pn->as<LexicalScopeNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Const:
+       case ParseNodeKind::Let:
+         if (!emitDeclarationList(&pn->as<ListNode>())) {
+             return false;
+diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
+--- a/js/src/frontend/BytecodeEmitter.h
++++ b/js/src/frontend/BytecodeEmitter.h
+@@ -679,17 +679,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_MUST_USE bool emitElemOp(PropertyByValue* elem, JSOp op);
+     MOZ_MUST_USE bool emitElemIncDec(UnaryNode* incDec);
+ 
+     MOZ_MUST_USE bool emitCatch(BinaryNode* catchClause);
+     MOZ_MUST_USE bool emitIf(TernaryNode* ifNode);
+     MOZ_MUST_USE bool emitWith(BinaryNode* withNode);
+ 
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(const LabeledStatement* pn);
+-    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn);
++    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(LexicalScopeNode* lexicalScope);
+     MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body,
+                                            EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* switchStmt);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(TernaryNode* tryNode);
+ 
+     enum DestructuringFlavor {
+         // Destructuring into a declaration.
+         DestructuringDeclaration,
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -196,19 +196,18 @@ ContainsHoistedDeclaration(JSContext* cx
+         if (!ContainsHoistedDeclaration(cx, tryBlock, result)) {
+             return false;
+         }
+         if (*result) {
+             return true;
+         }
+ 
+         if (ParseNode* catchScope = tryNode->kid2()) {
+-            MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
+-
+-            BinaryNode* catchNode = &catchScope->scopeBody()->as<BinaryNode>();
++            BinaryNode* catchNode =
++                &catchScope->as<LexicalScopeNode>().scopeBody()->as<BinaryNode>();
+             MOZ_ASSERT(catchNode->isKind(ParseNodeKind::Catch));
+ 
+             ParseNode* catchStatements = catchNode->right();
+             if (!ContainsHoistedDeclaration(cx, catchStatements, result)) {
+                 return false;
+             }
+             if (*result) {
+                 return true;
+@@ -275,25 +274,25 @@ ContainsHoistedDeclaration(JSContext* cx
+             }
+         }
+ 
+         ParseNode* loopBody = forNode->body();
+         return ContainsHoistedDeclaration(cx, loopBody, result);
+       }
+ 
+       case ParseNodeKind::LexicalScope: {
+-        MOZ_ASSERT(node->isArity(PN_SCOPE));
+-        ParseNode* expr = node->scopeBody();
++        LexicalScopeNode* scope = &node->as<LexicalScopeNode>();
++        ParseNode* expr = scope->scopeBody();
+ 
+         if (expr->isKind(ParseNodeKind::For) || expr->isKind(ParseNodeKind::Function)) {
+             return ContainsHoistedDeclaration(cx, expr, result);
+         }
+ 
+         MOZ_ASSERT(expr->isKind(ParseNodeKind::StatementList));
+-        return ListContainsHoistedDeclaration(cx, &node->scopeBody()->as<ListNode>(), result);
++        return ListContainsHoistedDeclaration(cx, &scope->scopeBody()->as<ListNode>(), result);
+       }
+ 
+       // List nodes with all non-null children.
+       case ParseNodeKind::StatementList:
+         return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result);
+ 
+       // Grammar sub-components that should never be reached directly by this
+       // method, because some parent component should have asserted itself.
+@@ -1912,22 +1911,23 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+         return Fold(cx, pn->as<NameNode>().unsafeExpressionReference(), parser);
+ 
+       case ParseNodeKind::PropertyName:
+         MOZ_CRASH("unreachable, handled by ::Dot");
+ 
+       case ParseNodeKind::Dot:
+         return FoldDottedProperty(cx, &pn->as<PropertyAccess>(), parser);
+ 
+-      case ParseNodeKind::LexicalScope:
+-        MOZ_ASSERT(pn->isArity(PN_SCOPE));
+-        if (!pn->scopeBody()) {
++      case ParseNodeKind::LexicalScope: {
++        LexicalScopeNode* node = &pn->as<LexicalScopeNode>();
++        if (!node->scopeBody()) {
+             return true;
+         }
+-        return Fold(cx, &pn->pn_u.scope.body, parser);
++        return Fold(cx, node->unsafeScopeBodyReference(), parser);
++      }
+ 
+       case ParseNodeKind::Name:
+         return FoldName(cx, &pn->as<NameNode>(), parser);
+ 
+       case ParseNodeKind::Limit: // invalid sentinel value
+         MOZ_CRASH("invalid node kind");
+     }
+ 
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -615,17 +615,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target, Node iteratedExpr,
+                                      const TokenPos& pos)
+     {
+         MOZ_ASSERT(kind == ParseNodeKind::ForIn || kind == ParseNodeKind::ForOf);
+         return new_<TernaryNode>(kind, target, nullptr, iteratedExpr, pos);
+     }
+ 
+     SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant,
+-                                           Node lexicalForCaseList, bool hasDefault)
++                                           LexicalScopeNodeType lexicalForCaseList,
++                                           bool hasDefault)
+     {
+         return new_<SwitchStatement>(begin, discriminant, lexicalForCaseList, hasDefault);
+     }
+ 
+     CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) {
+         return new_<CaseClause>(expr, body, begin);
+     }
+ 
+@@ -678,17 +679,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
+         return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end);
+     }
+ 
+     PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) {
+         return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
+     }
+ 
+-    bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) {
++    bool setupCatchScope(LexicalScopeNodeType lexicalScope, Node catchName, Node catchBody) {
+         BinaryNode* catchClause;
+         if (catchName) {
+             catchClause = new_<BinaryNode>(ParseNodeKind::Catch, JSOP_NOP, catchName, catchBody);
+         } else {
+             catchClause = new_<BinaryNode>(ParseNodeKind::Catch, JSOP_NOP, catchBody->pn_pos,
+                                        catchName, catchBody);
+         }
+         if (!catchClause) {
+@@ -734,26 +735,26 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     void setFunctionBox(CodeNodeType funNode, FunctionBox* funbox) {
+         MOZ_ASSERT(funNode->isKind(ParseNodeKind::Function));
+         funNode->setFunbox(funbox);
+         funbox->functionNode = funNode;
+     }
+     void addFunctionFormalParameter(CodeNodeType funNode, Node argpn) {
+         addList(/* list = */ funNode->body(), /* child = */ argpn);
+     }
+-    void setFunctionBody(CodeNodeType funNode, Node body) {
++    void setFunctionBody(CodeNodeType funNode, LexicalScopeNodeType body) {
+         MOZ_ASSERT(funNode->body()->isKind(ParseNodeKind::ParamsBody));
+         addList(/* list = */ funNode->body(), /* child = */ body);
+     }
+ 
+     CodeNodeType newModule(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Module, JSOP_NOP, pos);
+     }
+ 
+-    ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) {
++    LexicalScopeNodeType newLexicalScope(LexicalScope::Data* bindings, Node body) {
+         return new_<LexicalScopeNode>(bindings, body);
+     }
+ 
+     BinaryNodeType newNewExpression(uint32_t begin, Node ctor, Node args) {
+         return new_<BinaryNode>(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end),
+                                 ctor, args);
+     }
+ 
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -729,18 +729,18 @@ class NameResolver
+           // and finally block are optional (but at least one or the other must
+           // be present).
+           case ParseNodeKind::Try: {
+             TernaryNode* tryNode = &cur->as<TernaryNode>();
+             if (!resolve(tryNode->kid1(), prefix)) {
+                 return false;
+             }
+             MOZ_ASSERT(tryNode->kid2() || tryNode->kid3());
+-            if (ParseNode* catchScope = tryNode->kid2()) {
+-                MOZ_ASSERT(catchScope->isKind(ParseNodeKind::LexicalScope));
++            if (tryNode->kid2()) {
++                LexicalScopeNode* catchScope = &tryNode->kid2()->as<LexicalScopeNode>();
+                 MOZ_ASSERT(catchScope->scopeBody()->isKind(ParseNodeKind::Catch));
+                 MOZ_ASSERT(catchScope->scopeBody()->is<BinaryNode>());
+                 if (!resolve(catchScope->scopeBody(), prefix)) {
+                     return false;
+                 }
+             }
+             if (ParseNode* finallyBlock = tryNode->kid3()) {
+                 if (!resolve(finallyBlock, prefix)) {
+@@ -913,18 +913,17 @@ class NameResolver
+             if (ParseNode* init = cur->as<NameNode>().expression()) {
+                 if (!resolve(init, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           case ParseNodeKind::LexicalScope:
+-            MOZ_ASSERT(cur->isArity(PN_SCOPE));
+-            if (!resolve(cur->scopeBody(), prefix)) {
++            if (!resolve(cur->as<LexicalScopeNode>().scopeBody(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::Function:
+           case ParseNodeKind::Module:
+             if (ParseNode* body = cur->as<CodeNode>().body()) {
+                 if (!resolve(body, prefix)) {
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -171,17 +171,17 @@ ParseNode::dump(GenericPrinter& out, int
+         return;
+       case PN_REGEXP:
+         as<RegExpLiteral>().dump(out, indent);
+         return;
+       case PN_LOOP:
+         as<LoopControlStatement>().dump(out, indent);
+         return;
+       case PN_SCOPE:
+-        ((LexicalScopeNode*) this)->dump(out, indent);
++        as<LexicalScopeNode>().dump(out, indent);
+         return;
+     }
+     out.printf("#<BAD NODE %p, kind=%u, arity=%u>",
+                (void*) this, unsigned(getKind()), unsigned(pn_arity));
+ }
+ 
+ void
+ NullaryNode::dump(GenericPrinter& out)
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -318,19 +318,19 @@ IsTypeofKind(ParseNodeKind kind)
+  *         Catch node
+  *   kid3: null or finally block
+  * Catch (BinaryNode)
+  *   left: Name, Array, or Object catch var node
+  *         (Array or Object if destructuring),
+  *         or null if optional catch binding
+  *   right: catch block statements
+  * Break (BreakStatement)
+- *   atom: label or null
++ *   label: label or null
+  * Continue (ContinueStatement)
+- *   atom: label or null
++ *   label: label or null
+  * With (BinaryNode)
+  *   left: head expr
+  *   right: body
+  * Var, Let, Const (ListNode)
+  *   head: list of N Name or Assign nodes
+  *         each name node has either
+  *           atom: variable name
+  *           expr: initializer or null
+@@ -496,18 +496,19 @@ IsTypeofKind(ParseNodeKind kind)
+  *   kid: '.this' Name
+  * SuperCall (BinaryNode)
+  *   left: SuperBase
+  *   right: Arguments
+  * SetThis (BinaryNode)
+  *   left: '.this' Name
+  *   right: SuperCall
+  *
+- * LexicalScope scope   pn_u.scope.bindings: scope bindings
+- *                          pn_u.scope.body: scope body
++ * LexicalScope (LexicalScopeNode)
++ *   scopeBindings: scope bindings
++ *   scopeBody: scope body
+  * Generator (NullaryNode)
+  * InitialYield (UnaryNode)
+  *   kid: generator object
+  * Yield, YieldStar, Await (UnaryNode)
+  *   kid: expr or null
+  * Nop (NullaryNode)
+  */
+ enum ParseNodeArity
+@@ -533,16 +534,18 @@ enum ParseNodeArity
+     macro(ClassNames, ClassNamesType, asClassNames) \
+     macro(ForNode, ForNodeType, asFor) \
+     macro(PropertyAccess, PropertyAccessType, asPropertyAccess) \
+     macro(PropertyByValue, PropertyByValueType, asPropertyByValue) \
+     macro(SwitchStatement, SwitchStatementType, asSwitchStatement) \
+     \
+     macro(CodeNode, CodeNodeType, asCode) \
+     \
++    macro(LexicalScopeNode, LexicalScopeNodeType, asLexicalScope) \
++    \
+     macro(ListNode, ListNodeType, asList) \
+     macro(CallSiteNode, CallSiteNodeType, asCallSite) \
+     \
+     macro(LoopControlStatement, LoopControlStatementType, asLoopControlStatement) \
+     macro(BreakStatement, BreakStatementType, asBreakStatement) \
+     macro(ContinueStatement, ContinueStatementType, asContinueStatement) \
+     \
+     macro(NameNode, NameNodeType, asName) \
+@@ -702,26 +705,29 @@ class ParseNode
+         } regexp;
+         struct {
+           private:
+             friend class CodeNode;
+             FunctionBox* funbox;        /* function object */
+             ParseNode*  body;           /* module or function body */
+         } code;
+         struct {
++          private:
++            friend class LexicalScopeNode;
+             LexicalScope::Data* bindings;
+             ParseNode*          body;
+         } scope;
+         struct {
+           private:
+             friend class NumericLiteral;
+             double       value;         /* aligned numeric literal value */
+             DecimalPoint decimalPoint;  /* Whether the number has a decimal point */
+         } number;
+         class {
++          private:
+             friend class LoopControlStatement;
+             PropertyName*    label;    /* target of break/continue statement */
+         } loopControl;
+     } pn_u;
+ 
+   public:
+     /*
+      * If |left| is a list of the given kind/left-associative op, append
+@@ -729,38 +735,16 @@ class ParseNode
+      */
+     static ParseNode*
+     appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
+                        FullParseHandler* handler, ParseContext* pc);
+ 
+     // include "ParseNode-inl.h" for these methods.
+     inline PropertyName* name() const;
+ 
+-    bool isEmptyScope() const {
+-        MOZ_ASSERT(pn_arity == PN_SCOPE);
+-        return !pn_u.scope.bindings;
+-    }
+-
+-    Handle<LexicalScope::Data*> scopeBindings() const {
+-        MOZ_ASSERT(!isEmptyScope());
+-        // Bindings' GC safety depend on the presence of an AutoKeepAtoms that
+-        // the rest of the frontend also depends on.
+-        return Handle<LexicalScope::Data*>::fromMarkedLocation(&pn_u.scope.bindings);
+-    }
+-
+-    ParseNode* scopeBody() const {
+-        MOZ_ASSERT(pn_arity == PN_SCOPE);
+-        return pn_u.scope.body;
+-    }
+-
+-    void setScopeBody(ParseNode* body) {
+-        MOZ_ASSERT(pn_arity == PN_SCOPE);
+-        pn_u.scope.body = body;
+-    }
+-
+     /* True if pn is a parsenode representing a literal constant. */
+     bool isLiteral() const {
+         return isKind(ParseNodeKind::Number) ||
+                isKind(ParseNodeKind::String) ||
+                isKind(ParseNodeKind::True) ||
+                isKind(ParseNodeKind::False) ||
+                isKind(ParseNodeKind::Null) ||
+                isKind(ParseNodeKind::RawUndefined);
+@@ -1495,32 +1479,58 @@ class NumericLiteral : public ParseNode
+         return pn_u.number.decimalPoint;
+     }
+ 
+     void setValue(double v) {
+         pn_u.number.value = v;
+     }
+ };
+ 
+-struct LexicalScopeNode : public ParseNode
++class LexicalScopeNode : public ParseNode
+ {
++  public:
+     LexicalScopeNode(LexicalScope::Data* bindings, ParseNode* body)
+       : ParseNode(ParseNodeKind::LexicalScope, JSOP_NOP, PN_SCOPE, body->pn_pos)
+     {
+         pn_u.scope.bindings = bindings;
+         pn_u.scope.body = body;
+     }
+ 
+     static bool test(const ParseNode& node) {
+-        return node.isKind(ParseNodeKind::LexicalScope);
++        bool match = node.isKind(ParseNodeKind::LexicalScope);
++        MOZ_ASSERT_IF(match, node.isArity(PN_SCOPE));
++        return match;
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
++
++    Handle<LexicalScope::Data*> scopeBindings() const {
++        MOZ_ASSERT(!isEmptyScope());
++        // Bindings' GC safety depend on the presence of an AutoKeepAtoms that
++        // the rest of the frontend also depends on.
++        return Handle<LexicalScope::Data*>::fromMarkedLocation(&pn_u.scope.bindings);
++    }
++
++    ParseNode* scopeBody() const {
++        return pn_u.scope.body;
++    }
++
++    void setScopeBody(ParseNode* body) {
++        pn_u.scope.body = body;
++    }
++
++    bool isEmptyScope() const {
++        return !pn_u.scope.bindings;
++    }
++
++    ParseNode** unsafeScopeBodyReference() {
++        return &pn_u.scope.body;
++    }
+ };
+ 
+ class LabeledStatement : public NameNode
+ {
+   public:
+     LabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin)
+       : NameNode(ParseNodeKind::Label, JSOP_NOP, label, stmt, TokenPos(begin, stmt->pn_pos.end))
+     {}
+@@ -1889,24 +1899,23 @@ class ClassMethod : public BinaryNode
+     bool isStatic() const {
+         return pn_u.binary.isStatic;
+     }
+ };
+ 
+ class SwitchStatement : public BinaryNode
+ {
+   public:
+-    SwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* lexicalForCaseList,
++    SwitchStatement(uint32_t begin, ParseNode* discriminant, LexicalScopeNode* lexicalForCaseList,
+                     bool hasDefault)
+       : BinaryNode(ParseNodeKind::Switch, JSOP_NOP,
+                    TokenPos(begin, lexicalForCaseList->pn_pos.end),
+                    discriminant, lexicalForCaseList)
+     {
+ #ifdef DEBUG
+-        MOZ_ASSERT(lexicalForCaseList->isKind(ParseNodeKind::LexicalScope));
+         ListNode* cases = &lexicalForCaseList->scopeBody()->as<ListNode>();
+         MOZ_ASSERT(cases->isKind(ParseNodeKind::StatementList));
+         bool found = false;
+         for (ParseNode* item : cases->contents()) {
+             CaseClause* caseNode = &item->as<CaseClause>();
+             if (caseNode->isDefault()) {
+                 found = true;
+                 break;
+@@ -1923,18 +1932,18 @@ class SwitchStatement : public BinaryNod
+         MOZ_ASSERT_IF(match, node.is<BinaryNode>());
+         return match;
+     }
+ 
+     ParseNode& discriminant() const {
+         return *left();
+     }
+ 
+-    ParseNode& lexicalForCaseList() const {
+-        return *right();
++    LexicalScopeNode& lexicalForCaseList() const {
++        return right()->as<LexicalScopeNode>();
+     }
+ 
+     bool hasDefault() const {
+         return pn_u.binary.hasDefault;
+     }
+ };
+ 
+ class ClassNames : public BinaryNode
+@@ -2000,25 +2009,23 @@ class ClassNode : public TernaryNode
+         return kid2();
+     }
+     ListNode* methodList() const {
+         ParseNode* methodsOrBlock = kid3();
+         if (methodsOrBlock->isKind(ParseNodeKind::ClassMethodList)) {
+             return &methodsOrBlock->as<ListNode>();
+         }
+ 
+-        MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>());
+-        ListNode* list = &methodsOrBlock->scopeBody()->as<ListNode>();
++        ListNode* list = &methodsOrBlock->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
+         MOZ_ASSERT(list->isKind(ParseNodeKind::ClassMethodList));
+         return list;
+     }
+     Handle<LexicalScope::Data*> scopeBindings() const {
+         ParseNode* scope = kid3();
+-        MOZ_ASSERT(scope->is<LexicalScopeNode>());
+-        return scope->scopeBindings();
++        return scope->as<LexicalScopeNode>().scopeBindings();
+     }
+ };
+ 
+ #ifdef DEBUG
+ void DumpParseTree(ParseNode* pn, GenericPrinter& out, int indent = 0);
+ #endif
+ 
+ class ParseNodeAllocator
+@@ -2140,18 +2147,18 @@ IsMethodDefinitionKind(FunctionSyntaxKin
+ static inline ParseNode*
+ FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals)
+ {
+     MOZ_ASSERT(fn->isKind(ParseNodeKind::Function));
+     ListNode* argsBody = fn->as<CodeNode>().body();
+     MOZ_ASSERT(argsBody->isKind(ParseNodeKind::ParamsBody));
+     *numFormals = argsBody->count();
+     if (*numFormals > 0 &&
+-        argsBody->last()->isKind(ParseNodeKind::LexicalScope) &&
+-        argsBody->last()->scopeBody()->isKind(ParseNodeKind::StatementList))
++        argsBody->last()->is<LexicalScopeNode>() &&
++        argsBody->last()->as<LexicalScopeNode>().scopeBody()->isKind(ParseNodeKind::StatementList))
+     {
+         (*numFormals)--;
+     }
+     return argsBody->head();
+ }
+ 
+ bool
+ IsAnonymousFunctionDefinition(ParseNode* pn);
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -2346,74 +2346,74 @@ NewLexicalScopeData(JSContext* context, 
+ 
+ Maybe<LexicalScope::Data*>
+ ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
+ {
+     return NewLexicalScopeData(context, scope, alloc, pc);
+ }
+ 
+ template <>
+-SyntaxParseHandler::Node
++SyntaxParseHandler::LexicalScopeNodeType
+ PerHandlerParser<SyntaxParseHandler>::finishLexicalScope(ParseContext::Scope& scope, Node body)
+ {
+     if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) {
+         return null();
+     }
+ 
+-    return body;
++    return handler.newLexicalScope(body);
+ }
+ 
+ template <>
+-ParseNode*
++LexicalScopeNode*
+ PerHandlerParser<FullParseHandler>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body)
+ {
+     if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) {
+         return nullptr;
+     }
+ 
+     Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(scope);
+     if (!bindings) {
+         return nullptr;
+     }
+ 
+     return handler.newLexicalScope(*bindings, body);
+ }
+ 
+ template <typename CharT>
+-ParseNode*
++LexicalScopeNode*
+ Parser<FullParseHandler, CharT>::evalBody(EvalSharedContext* evalsc)
+ {
+     SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
+     if (!evalpc.init()) {
+         return nullptr;
+     }
+ 
+     ParseContext::VarScope varScope(this);
+     if (!varScope.init(pc)) {
+         return nullptr;
+     }
+ 
+-    ParseNode* body;
++    LexicalScopeNode* body;
+     {
+         // All evals have an implicit non-extensible lexical scope.
+         ParseContext::Scope lexicalScope(this);
+         if (!lexicalScope.init(pc)) {
+             return nullptr;
+         }
+ 
+-        body = statementList(YieldIsName);
+-        if (!body) {
++        ParseNode* list = statementList(YieldIsName);
++        if (!list) {
+             return nullptr;
+         }
+ 
+         if (!checkStatementsEOF()) {
+             return nullptr;
+         }
+ 
+-        body = finishLexicalScope(lexicalScope, body);
++        body = finishLexicalScope(lexicalScope, list);
+         if (!body) {
+             return nullptr;
+         }
+     }
+ 
+ #ifdef DEBUG
+     if (evalpc.superScopeNeedsHomeObject() && evalsc->compilationEnclosingScope()) {
+         // If superScopeNeedsHomeObject_ is set and we are an entry-point
+@@ -2432,19 +2432,21 @@ Parser<FullParseHandler, CharT>::evalBod
+                 break;
+             }
+         }
+         MOZ_ASSERT(!si.done(),
+                    "Eval must have found an enclosing function box scope that allows super.property");
+     }
+ #endif
+ 
+-    if (!FoldConstants(context, &body, this)) {
++    ParseNode* node = body;
++    if (!FoldConstants(context, &node, this)) {
+         return nullptr;
+     }
++    body = handler.asLexicalScope(node);
+ 
+     if (!this->setSourceMapInfo()) {
+         return nullptr;
+     }
+ 
+     // For eval scripts, since all bindings are automatically considered
+     // closed over, we don't need to call propagateFreeNamesAndMarkClosed-
+     // OverBindings. However, Annex B.3.3 functions still need to be marked.
+@@ -3013,17 +3015,17 @@ PerHandlerParser<ParseHandler>::declareF
+             funbox->setDefinitelyNeedsArgsObj();
+         }
+     }
+ 
+     return true;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::LexicalScopeNodeType
+ GeneralParser<ParseHandler, CharT>::functionBody(InHandling inHandling,
+                                                  YieldHandling yieldHandling,
+                                                  FunctionSyntaxKind kind, FunctionBodyType type)
+ {
+     MOZ_ASSERT(pc->isFunctionBox());
+ 
+ #ifdef DEBUG
+     uint32_t startYieldOffset = pc->lastYieldOffset;
+@@ -4211,17 +4213,17 @@ GeneralParser<ParseHandler, CharT>::func
+     // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
+     // |yield| in the parameters is either a name or keyword, depending on
+     // whether the arrow function is enclosed in a generator function or not.
+     // Whereas the |yield| in the function body is always parsed as a name.
+     // The same goes when parsing |await| in arrow functions.
+     YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
+     AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc->asyncKind());
+     bool inheritedStrict = pc->sc()->strict();
+-    Node body;
++    LexicalScopeNodeType body;
+     {
+         AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, bodyAwaitHandling);
+         AutoInParametersOfAsyncFunction<ParseHandler, CharT> inParameters(this, false);
+         body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
+         if (!body) {
+             return false;
+         }
+     }
+@@ -5246,17 +5248,17 @@ GeneralParser<ParseHandler, CharT>::dest
+             errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_PARAMETER);
+             return null();
+         }
+     }
+     return res;
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::LexicalScopeNodeType
+ GeneralParser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling,
+                                                    unsigned errorNumber)
+ {
+     MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
+     uint32_t openedPos = pos().begin;
+ 
+     ParseContext::Statement stmt(pc, StatementKind::Block);
+     ParseContext::Scope scope(this);
+@@ -7378,17 +7380,17 @@ GeneralParser<ParseHandler, CharT>::swit
+ 
+         CaseClauseType caseClause = handler.newCaseOrDefault(caseBegin, caseExpr, body);
+         if (!caseClause) {
+             return null();
+         }
+         handler.addCaseStatementToList(caseList, caseClause);
+     }
+ 
+-    Node lexicalForCaseList = finishLexicalScope(scope, caseList);
++    LexicalScopeNodeType lexicalForCaseList = finishLexicalScope(scope, caseList);
+     if (!lexicalForCaseList) {
+         return null();
+     }
+ 
+     handler.setEndPosition(lexicalForCaseList, pos().end);
+ 
+     return handler.newSwitchStatement(begin, discriminant, lexicalForCaseList, seenDefault);
+ }
+@@ -7750,17 +7752,17 @@ GeneralParser<ParseHandler, CharT>::tryS
+             return null();
+         }
+ 
+         MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::RightCurly, TokenStream::Operand,
+                                          reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
+                                                               JSMSG_CURLY_OPENED, openedPos));
+     }
+ 
+-    Node catchScope = null();
++    LexicalScopeNodeType catchScope = null();
+     TokenKind tt;
+     if (!tokenStream.getToken(&tt)) {
+         return null();
+     }
+     if (tt == TokenKind::Catch) {
+         /*
+          * Create a lexical scope node around the whole catch clause,
+          * including the head.
+@@ -7816,17 +7818,17 @@ GeneralParser<ParseHandler, CharT>::tryS
+               }
+             }
+ 
+             MUST_MATCH_TOKEN_MOD(TokenKind::RightParen, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
+ 
+             MUST_MATCH_TOKEN(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CATCH);
+         }
+ 
+-        Node catchBody = catchBlockStatement(yieldHandling, scope);
++        LexicalScopeNodeType catchBody = catchBlockStatement(yieldHandling, scope);
+         if (!catchBody) {
+             return null();
+         }
+ 
+         catchScope = finishLexicalScope(scope, catchBody);
+         if (!catchScope) {
+             return null();
+         }
+@@ -7874,17 +7876,17 @@ GeneralParser<ParseHandler, CharT>::tryS
+         error(JSMSG_CATCH_OR_FINALLY);
+         return null();
+     }
+ 
+     return handler.newTryStatement(begin, innerBlock, catchScope, finallyBlock);
+ }
+ 
+ template <class ParseHandler, typename CharT>
+-typename ParseHandler::Node
++typename ParseHandler::LexicalScopeNodeType
+ GeneralParser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling,
+                                                         ParseContext::Scope& catchParamScope)
+ {
+     uint32_t openedPos = pos().begin;
+ 
+     ParseContext::Statement stmt(pc, StatementKind::Block);
+ 
+     // ES 13.15.7 CatchClauseEvaluation
+diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
+--- a/js/src/frontend/Parser.h
++++ b/js/src/frontend/Parser.h
+@@ -527,17 +527,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+ 
+         return ParserBase::noteUsedNameInternal(name);
+     }
+ 
+     // Required on Scope exit.
+     bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope);
+ 
+     bool finishFunctionScopes(bool isStandaloneFunction);
+-    Node finishLexicalScope(ParseContext::Scope& scope, Node body);
++    LexicalScopeNodeType finishLexicalScope(ParseContext::Scope& scope, Node body);
+     bool finishFunction(bool isStandaloneFunction = false);
+ 
+     bool declareFunctionThis();
+     bool declareFunctionArgumentsObject();
+ 
+     inline NameNodeType newName(PropertyName* name);
+     inline NameNodeType newName(PropertyName* name, TokenPos pos);
+ 
+@@ -1018,18 +1018,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+                               YieldHandling yieldHandling, DefaultHandling defaultHandling,
+                               FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
+     CodeNodeType functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
+                               FunctionAsyncKind asyncKind);
+ 
+     Node statement(YieldHandling yieldHandling);
+     bool maybeParseDirective(ListNodeType list, Node pn, bool* cont);
+ 
+-    Node blockStatement(YieldHandling yieldHandling,
+-                        unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
++    LexicalScopeNodeType blockStatement(YieldHandling yieldHandling,
++                                        unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
+     BinaryNodeType doWhileStatement(YieldHandling yieldHandling);
+     BinaryNodeType whileStatement(YieldHandling yieldHandling);
+ 
+     Node forStatement(YieldHandling yieldHandling);
+     bool forHeadStart(YieldHandling yieldHandling,
+                       ParseNodeKind* forHeadKind,
+                       Node* forInitialPart,
+                       mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
+@@ -1038,17 +1038,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+ 
+     SwitchStatementType switchStatement(YieldHandling yieldHandling);
+     ContinueStatementType continueStatement(YieldHandling yieldHandling);
+     BreakStatementType breakStatement(YieldHandling yieldHandling);
+     UnaryNodeType returnStatement(YieldHandling yieldHandling);
+     BinaryNodeType withStatement(YieldHandling yieldHandling);
+     UnaryNodeType throwStatement(YieldHandling yieldHandling);
+     TernaryNodeType tryStatement(YieldHandling yieldHandling);
+-    Node catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope);
++    LexicalScopeNodeType catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope);
+     DebuggerStatementType debuggerStatement();
+ 
+     Node variableStatement(YieldHandling yieldHandling);
+ 
+     LabeledStatementType labeledStatement(YieldHandling yieldHandling);
+     Node labeledItem(YieldHandling yieldHandling);
+ 
+     TernaryNodeType ifStatement(YieldHandling yieldHandling);
+@@ -1172,18 +1172,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+                                     InHandling inHandling, YieldHandling yieldHandling,
+                                     HandleAtom name, FunctionSyntaxKind kind,
+                                     GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+                                     bool tryAnnexB = false);
+ 
+     // Parse a function body.  Pass StatementListBody if the body is a list of
+     // statements; pass ExpressionBody if the body is a single expression.
+     enum FunctionBodyType { StatementListBody, ExpressionBody };
+-    Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
+-                      FunctionBodyType type);
++    LexicalScopeNodeType functionBody(InHandling inHandling, YieldHandling yieldHandling,
++                                      FunctionSyntaxKind kind, FunctionBodyType type);
+ 
+     UnaryNodeType unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin);
+ 
+     Node condition(InHandling inHandling, YieldHandling yieldHandling);
+ 
+     ListNodeType argumentList(YieldHandling yieldHandling, bool* isSpread,
+                               PossibleError* possibleError = nullptr);
+     Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
+@@ -1544,17 +1544,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE
+ 
+     // Functions present only in Parser<FullParseHandler, CharT>.
+ 
+     // Parse the body of an eval.
+     //
+     // Eval scripts are distinguished from global scripts in that in ES6, per
+     // 18.2.1.1 steps 9 and 10, all eval scripts are executed under a fresh
+     // lexical scope.
+-    Node evalBody(EvalSharedContext* evalsc);
++    LexicalScopeNodeType evalBody(EvalSharedContext* evalsc);
+ 
+     // Parse a function, given only its arguments and body. Used for lazily
+     // parsed functions.
+     CodeNodeType standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart, bool strict,
+                                         GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
+ 
+     // Parse a function, used for the Function, GeneratorFunction, and
+     // AsyncFunction constructors.
+diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
+--- a/js/src/frontend/SyntaxParseHandler.h
++++ b/js/src/frontend/SyntaxParseHandler.h
+@@ -280,16 +280,20 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+     ListNodeType newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
+     ListNodeType newClassMethodList(uint32_t begin) { return NodeGeneric; }
+     ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+     ClassNodeType newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
+ 
++    LexicalScopeNodeType newLexicalScope(Node body) {
++        return NodeLexicalDeclaration;
++    }
++
+     BinaryNodeType newNewTarget(NullaryNodeType newHolder, NullaryNodeType targetHolder) {
+         return NodeGeneric;
+     }
+     NullaryNodeType newPosHolder(const TokenPos& pos) { return NodeGeneric; }
+     UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
+ 
+     MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin, Node expr) { return true; }
+     BinaryNodeType newPropertyDefinition(Node key, Node val) { return NodeGeneric; }
+@@ -350,17 +354,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch, Node elseBranch) {
+         return NodeGeneric;
+     }
+     BinaryNodeType newDoWhileStatement(Node body, Node cond, const TokenPos& pos) {
+         return NodeGeneric;
+     }
+     BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
+     SwitchStatementType newSwitchStatement(uint32_t begin, Node discriminant,
+-                                           Node lexicalForCaseList, bool hasDefault)
++                                           LexicalScopeNodeType lexicalForCaseList,
++                                           bool hasDefault)
+     {
+         return NodeGeneric;
+     }
+     CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+     ContinueStatementType newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
+     BreakStatementType newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
+     UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) { return NodeReturn; }
+     UnaryNodeType newExpressionBody(Node expr) { return NodeReturn; }
+@@ -384,17 +389,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
+         return NodeDottedProperty;
+     }
+ 
+     PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) {
+         return NodeElement;
+     }
+ 
+-    MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) {
++    MOZ_MUST_USE bool setupCatchScope(LexicalScopeNodeType lexicalScope, Node catchName,
++                                      Node catchBody)
++    {
+         return true;
+     }
+ 
+     MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(CodeNodeType funNode,
+                                                             Node defaultValue) {
+         return true;
+     }
+ 
+@@ -405,17 +412,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         // block body.  This will be overridden later *if* the function
+         // expression permissibly has an AssignmentExpression body.
+         return NodeFunctionExpression;
+     }
+ 
+     CodeNodeType newArrowFunction(const TokenPos& pos) { return NodeFunctionArrow; }
+ 
+     void setFunctionFormalParametersAndBody(CodeNodeType funNode, ListNodeType paramsBody) {}
+-    void setFunctionBody(CodeNodeType funNode, Node body) {}
++    void setFunctionBody(CodeNodeType funNode, LexicalScopeNodeType body) {}
+     void setFunctionBox(CodeNodeType funNode, FunctionBox* funbox) {}
+     void addFunctionFormalParameter(CodeNodeType funNode, Node argpn) {}
+ 
+     ForNodeType newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) {
+         return NodeGeneric;
+     }
+ 
+     TernaryNodeType newForHead(Node init, Node test, Node update, const TokenPos& pos) {
+diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
+--- a/js/src/wasm/AsmJS.cpp
++++ b/js/src/wasm/AsmJS.cpp
+@@ -660,18 +660,17 @@ FunctionName(CodeNode* funNode)
+     }
+     return nullptr;
+ }
+ 
+ static inline ParseNode*
+ FunctionStatementList(CodeNode* funNode)
+ {
+     MOZ_ASSERT(funNode->body()->isKind(ParseNodeKind::ParamsBody));
+-    ParseNode* last = funNode->body()->as<ListNode>().last();
+-    MOZ_ASSERT(last->isKind(ParseNodeKind::LexicalScope));
++    LexicalScopeNode* last = &funNode->body()->as<ListNode>().last()->as<LexicalScopeNode>();
+     MOZ_ASSERT(last->isEmptyScope());
+     ParseNode* body = last->scopeBody();
+     MOZ_ASSERT(body->isKind(ParseNodeKind::StatementList));
+     return body;
+ }
+ 
+ static inline bool
+ IsNormalObjectField(ParseNode* pn)
+@@ -5554,21 +5553,22 @@ CheckSwitchExpr(FunctionValidator& f, Pa
+ static bool
+ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt)
+ {
+     MOZ_ASSERT(switchStmt->isKind(ParseNodeKind::Switch));
+ 
+     ParseNode* switchExpr = BinaryLeft(switchStmt);
+     ParseNode* switchBody = BinaryRight(switchStmt);
+ 
+-    if (switchBody->isKind(ParseNodeKind::LexicalScope)) {
+-        if (!switchBody->isEmptyScope()) {
+-            return f.fail(switchBody, "switch body may not contain lexical declarations");
+-        }
+-        switchBody = switchBody->scopeBody();
++    if (switchBody->is<LexicalScopeNode>()) {
++        LexicalScopeNode* scope = &switchBody->as<LexicalScopeNode>();
++        if (!scope->isEmptyScope()) {
++            return f.fail(scope, "switch body may not contain lexical declarations");
++        }
++        switchBody = scope->scopeBody();
+     }
+ 
+     ParseNode* stmt = ListHead(switchBody);
+     if (!stmt) {
+         if (!CheckSwitchExpr(f, switchExpr)) {
+             return false;
+         }
+         if (!f.encoder().writeOp(Op::Drop)) {
+@@ -5762,20 +5762,19 @@ CheckStatementList(FunctionValidator& f,
+ 
+     if (!f.popUnbreakableBlock(labels)) {
+         return false;
+     }
+     return true;
+ }
+ 
+ static bool
+-CheckLexicalScope(FunctionValidator& f, ParseNode* lexicalScope)
+-{
+-    MOZ_ASSERT(lexicalScope->isKind(ParseNodeKind::LexicalScope));
+-
++CheckLexicalScope(FunctionValidator& f, ParseNode* node)
++{
++    LexicalScopeNode* lexicalScope = &node->as<LexicalScopeNode>();
+     if (!lexicalScope->isEmptyScope()) {
+         return f.fail(lexicalScope, "cannot have 'let' or 'const' declarations");
+     }
+ 
+     return CheckStatement(f, lexicalScope->scopeBody());
+ }
+ 
+ static bool

+ 616 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478484.patch

@@ -0,0 +1,616 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726397 -32400
+#      Wed Sep 12 13:26:37 2018 +0900
+# Node ID 9d82b80e07af280d0d5cc3d24a5f35f0163bebaf
+# Parent  8789cfe8c9adf1f0d553939f4a4fed8d1899213d
+Bug 1479659 - Part 8: Add TryNode. r=jwalden
+
+diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
+--- a/js/src/builtin/ReflectParse.cpp
++++ b/js/src/builtin/ReflectParse.cpp
+@@ -1765,17 +1765,17 @@ class ASTSerializer
+     bool forIn(ForNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+                MutableHandleValue dst);
+     bool forOf(ForNode* loop, ParseNode* iterExpr, HandleValue var, HandleValue stmt,
+                MutableHandleValue dst);
+     bool statement(ParseNode* pn, MutableHandleValue dst);
+     bool blockStatement(ListNode* node, MutableHandleValue dst);
+     bool switchStatement(SwitchStatement* switchStmt, MutableHandleValue dst);
+     bool switchCase(CaseClause* caseClause, MutableHandleValue dst);
+-    bool tryStatement(TernaryNode* tryNode, MutableHandleValue dst);
++    bool tryStatement(TryNode* tryNode, MutableHandleValue dst);
+     bool catchClause(BinaryNode* catchClause, MutableHandleValue dst);
+ 
+     bool optExpression(ParseNode* pn, MutableHandleValue dst) {
+         if (!pn) {
+             dst.setMagic(JS_SERIALIZE_NO_NODE);
+             return true;
+         }
+         return expression(pn, dst);
+@@ -2309,25 +2309,25 @@ ASTSerializer::catchClause(BinaryNode* c
+         return false;
+     }
+ 
+     return statement(bodyNode, &body) &&
+            builder.catchClause(var, body, &catchClause->pn_pos, dst);
+ }
+ 
+ bool
+-ASTSerializer::tryStatement(TernaryNode* tryNode, MutableHandleValue dst)
++ASTSerializer::tryStatement(TryNode* tryNode, MutableHandleValue dst)
+ {
+-    ParseNode* bodyNode = tryNode->kid1();
++    ParseNode* bodyNode = tryNode->body();
+     MOZ_ASSERT(tryNode->pn_pos.encloses(bodyNode->pn_pos));
+ 
+-    ParseNode* catchNode = tryNode->kid2();
++    LexicalScopeNode* catchNode = tryNode->catchScope();
+     MOZ_ASSERT_IF(catchNode, tryNode->pn_pos.encloses(catchNode->pn_pos));
+ 
+-    ParseNode* finallyNode = tryNode->kid3();
++    ParseNode* finallyNode = tryNode->finallyBlock();
+     MOZ_ASSERT_IF(finallyNode, tryNode->pn_pos.encloses(finallyNode->pn_pos));
+ 
+     RootedValue body(cx);
+     if (!statement(bodyNode, &body)) {
+         return false;
+     }
+ 
+     RootedValue handler(cx, NullValue());
+@@ -2459,17 +2459,17 @@ ASTSerializer::statement(ParseNode* pn, 
+                optStatement(altNode, &alt) &&
+                builder.ifStatement(test, cons, alt, &ifNode->pn_pos, dst);
+       }
+ 
+       case ParseNodeKind::Switch:
+         return switchStatement(&pn->as<SwitchStatement>(), dst);
+ 
+       case ParseNodeKind::Try:
+-        return tryStatement(&pn->as<TernaryNode>(), dst);
++        return tryStatement(&pn->as<TryNode>(), dst);
+ 
+       case ParseNodeKind::With:
+       case ParseNodeKind::While:
+       {
+         BinaryNode* node = &pn->as<BinaryNode>();
+ 
+         ParseNode* exprNode = node->left();
+         MOZ_ASSERT(node->pn_pos.encloses(exprNode->pn_pos));
+diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp
+--- a/js/src/frontend/BinSource-auto.cpp
++++ b/js/src/frontend/BinSource-auto.cpp
+@@ -3471,17 +3471,17 @@ BinASTParser<Tok>::parseInterfaceCallExp
+ 
+ /*
+  interface CatchClause : Node {
+     AssertedParameterScope? bindingScope;
+     Binding binding;
+     Block body;
+  }
+ */
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<LexicalScopeNode*>
+ BinASTParser<Tok>::parseCatchClause()
+ {
+     BinKind kind;
+     BinFields fields(cx_);
+     AutoTaggedTuple guard(*tokenizer_);
+ 
+     MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+     if (kind != BinKind::CatchClause) {
+@@ -3489,17 +3489,17 @@ BinASTParser<Tok>::parseCatchClause()
+     }
+     const auto start = tokenizer_->offset();
+     BINJS_MOZ_TRY_DECL(result, parseInterfaceCatchClause(start, kind, fields));
+     MOZ_TRY(guard.done());
+ 
+     return result;
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<LexicalScopeNode*>
+ BinASTParser<Tok>::parseInterfaceCatchClause(const size_t start, const BinKind kind, const BinFields& fields)
+ {
+     MOZ_ASSERT(kind == BinKind::CatchClause);
+     BINJS_TRY(CheckRecursionLimit(cx_));
+ 
+ #if defined(DEBUG)
+     const BinField expected_fields[3] = { BinField::BindingScope, BinField::Binding, BinField::Body };
+     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+@@ -7748,25 +7748,25 @@ BinASTParser<Tok>::parseOptionalBindingO
+         const auto start = tokenizer_->offset();
+         MOZ_TRY_VAR(result, parseSumBindingOrBindingWithInitializer(start, kind, fields));
+     }
+     MOZ_TRY(guard.done());
+ 
+     return result;
+ }
+ 
+-template<typename Tok> JS::Result<ParseNode*>
++template<typename Tok> JS::Result<LexicalScopeNode*>
+ BinASTParser<Tok>::parseOptionalCatchClause()
+ {
+     BinKind kind;
+     BinFields fields(cx_);
+     AutoTaggedTuple guard(*tokenizer_);
+ 
+     MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+-    ParseNode* result;
++    LexicalScopeNode* result;
+     if (kind == BinKind::_Null) {
+         result = nullptr;
+     } else if (kind == BinKind::CatchClause) {
+         const auto start = tokenizer_->offset();
+         MOZ_TRY_VAR(result, parseInterfaceCatchClause(start, kind, fields));
+     } else {
+         return raiseInvalidKind("CatchClause", kind);
+     }
+diff --git a/js/src/frontend/BinSource-auto.h b/js/src/frontend/BinSource-auto.h
+--- a/js/src/frontend/BinSource-auto.h
++++ b/js/src/frontend/BinSource-auto.h
+@@ -174,17 +174,17 @@ JS::Result<ParseNode*> parseAwaitExpress
+ JS::Result<ParseNode*> parseBinaryExpression();
+ JS::Result<ParseNode*> parseBindingIdentifier();
+ JS::Result<ParseNode*> parseBindingPropertyIdentifier();
+ JS::Result<ParseNode*> parseBindingPropertyProperty();
+ JS::Result<ParseNode*> parseBindingWithInitializer();
+ JS::Result<ParseNode*> parseBlock();
+ JS::Result<ParseNode*> parseBreakStatement();
+ JS::Result<ParseNode*> parseCallExpression();
+-JS::Result<ParseNode*> parseCatchClause();
++JS::Result<LexicalScopeNode*> parseCatchClause();
+ JS::Result<ParseNode*> parseClassDeclaration();
+ JS::Result<ParseNode*> parseClassElement();
+ JS::Result<ParseNode*> parseClassExpression();
+ JS::Result<ParseNode*> parseCompoundAssignmentExpression();
+ JS::Result<ParseNode*> parseComputedMemberAssignmentTarget();
+ JS::Result<ParseNode*> parseComputedMemberExpression();
+ JS::Result<ParseNode*> parseComputedPropertyName();
+ JS::Result<ParseNode*> parseConditionalExpression();
+@@ -280,17 +280,17 @@ JS::Result<ParseNode*> parseInterfaceAwa
+ JS::Result<ParseNode*> parseInterfaceBinaryExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceBindingIdentifier(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceBindingPropertyIdentifier(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceBindingPropertyProperty(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceBindingWithInitializer(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceBlock(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceBreakStatement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceCallExpression(const size_t start, const BinKind kind, const BinFields& fields);
+-JS::Result<ParseNode*> parseInterfaceCatchClause(const size_t start, const BinKind kind, const BinFields& fields);
++JS::Result<LexicalScopeNode*> parseInterfaceCatchClause(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceClassDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceClassElement(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceClassExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceCompoundAssignmentExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceComputedMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceComputedMemberExpression(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceComputedPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
+ JS::Result<ParseNode*> parseInterfaceConditionalExpression(const size_t start, const BinKind kind, const BinFields& fields);
+@@ -408,16 +408,16 @@ JS::Result<ListNode*> parseListOfVariabl
+ // Implementations are autogenerated
+ JS::Result<Ok> parseOptionalAssertedBlockScope();
+ JS::Result<Ok> parseOptionalAssertedParameterScope();
+ JS::Result<Ok> parseOptionalAssertedVarScope();
+ JS::Result<ParseNode*> parseOptionalAssignmentTarget();
+ JS::Result<ParseNode*> parseOptionalBinding();
+ JS::Result<ParseNode*> parseOptionalBindingIdentifier();
+ JS::Result<ParseNode*> parseOptionalBindingOrBindingWithInitializer();
+-JS::Result<ParseNode*> parseOptionalCatchClause();
++JS::Result<LexicalScopeNode*> parseOptionalCatchClause();
+ JS::Result<ParseNode*> parseOptionalExpression();
+ JS::Result<ParseNode*> parseOptionalIdentifierName();
+ JS::Result<ParseNode*> parseOptionalLabel();
+ JS::Result<ParseNode*> parseOptionalSpreadElementOrExpression();
+ JS::Result<ParseNode*> parseOptionalStatement();
+ JS::Result<ParseNode*> parseOptionalVariableDeclarationOrExpression();
+ 
+diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml
+--- a/js/src/frontend/BinSource.yaml
++++ b/js/src/frontend/BinSource.yaml
+@@ -429,16 +429,18 @@ CallExpression:
+             }
+         }
+ 
+         BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
+         result->setOp(op);
+ 
+ 
+ CatchClause:
++    type-ok:
++        LexicalScopeNode*
+     init: |
+         ParseContext::Statement stmt(parseContext_, StatementKind::Catch);
+         ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
+         BINJS_TRY(currentScope.init(parseContext_));
+     build: |
+         BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
+         BINJS_TRY_DECL(result, factory_.newLexicalScope(*bindings, body));
+         BINJS_TRY(factory_.setupCatchScope(result, binding, body));
+@@ -863,16 +865,20 @@ OptionalAssertedBlockScope:
+ OptionalAssertedVarScope:
+     type-ok:
+         Ok
+ 
+ OptionalAssertedParameterScope:
+     type-ok:
+         Ok
+ 
++OptionalCatchClause:
++    type-ok:
++        LexicalScopeNode*
++
+ Parameter:
+     sum-arms:
+         BindingIdentifier:
+             after: |
+                 if (!parseContext_->positionalFormalParameterNames().append(result->template as<NameNode>().atom()))
+                     return raiseOOM();
+                 if (parseContext_->isFunctionBox())
+                     parseContext_->functionBox()->length++;
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -1414,32 +1414,32 @@ BytecodeEmitter::checkSideEffects(ParseN
+         return true;
+ 
+       case ParseNodeKind::Module:
+         *answer = false;
+         return true;
+ 
+       case ParseNodeKind::Try:
+       {
+-        TernaryNode* tryNode = &pn->as<TernaryNode>();
+-        if (!checkSideEffects(tryNode->kid1(), answer)) {
++        TryNode* tryNode = &pn->as<TryNode>();
++        if (!checkSideEffects(tryNode->body(), answer)) {
+             return false;
+         }
+         if (*answer) {
+             return true;
+         }
+-        if (ParseNode* catchScope = tryNode->kid2()) {
+-            if (!checkSideEffects(&catchScope->as<LexicalScopeNode>(), answer)) {
++        if (LexicalScopeNode* catchScope = tryNode->catchScope()) {
++            if (!checkSideEffects(catchScope, answer)) {
+                 return false;
+             }
+             if (*answer) {
+                 return true;
+             }
+         }
+-        if (ParseNode* finallyBlock = tryNode->kid3()) {
++        if (ParseNode* finallyBlock = tryNode->finallyBlock()) {
+             if (!checkSideEffects(finallyBlock, answer)) {
+                 return false;
+             }
+         }
+         return true;
+       }
+ 
+       case ParseNodeKind::Catch: {
+@@ -4808,20 +4808,20 @@ BytecodeEmitter::emitCatch(BinaryNode* c
+ 
+     /* Emit the catch body. */
+     return emitTree(catchClause->right());
+ }
+ 
+ // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See the
+ // comment on EmitSwitch.
+ MOZ_NEVER_INLINE bool
+-BytecodeEmitter::emitTry(TernaryNode* tryNode)
+-{
+-    ParseNode* catchScope = tryNode->kid2();
+-    ParseNode* finallyNode = tryNode->kid3();
++BytecodeEmitter::emitTry(TryNode* tryNode)
++{
++    LexicalScopeNode* catchScope = tryNode->catchScope();
++    ParseNode* finallyNode = tryNode->finallyBlock();
+ 
+     TryEmitter::Kind kind;
+     if (catchScope) {
+         if (finallyNode) {
+             kind = TryEmitter::Kind::TryCatchFinally;
+         } else {
+             kind = TryEmitter::Kind::TryCatch;
+         }
+@@ -4830,17 +4830,17 @@ BytecodeEmitter::emitTry(TernaryNode* tr
+         kind = TryEmitter::Kind::TryFinally;
+     }
+     TryEmitter tryCatch(this, kind, TryEmitter::ControlKind::Syntactic);
+ 
+     if (!tryCatch.emitTry()) {
+         return false;
+     }
+ 
+-    if (!emitTree(tryNode->kid1())) {
++    if (!emitTree(tryNode->body())) {
+         return false;
+     }
+ 
+     // If this try has a catch block, emit it.
+     if (catchScope) {
+         // The emitted code for a catch block looks like:
+         //
+         // [pushlexicalenv]             only if any local aliased
+@@ -4852,17 +4852,16 @@ BytecodeEmitter::emitTry(TernaryNode* tr
+         // if there is a finally block:
+         //   gosub <finally>
+         //   goto <after finally>
+         if (!tryCatch.emitCatch()) {
+             return false;
+         }
+ 
+         // Emit the lexical scope and catch body.
+-        MOZ_ASSERT(catchScope->is<LexicalScopeNode>());
+         if (!emitTree(catchScope)) {
+             return false;
+         }
+     }
+ 
+     // Emit the finally handler, if there is one.
+     if (finallyNode) {
+         if (!tryCatch.emitFinally(Some(finallyNode->pn_pos.begin))) {
+@@ -9031,17 +9030,17 @@ BytecodeEmitter::emitTree(ParseNode* pn,
+ 
+       case ParseNodeKind::With:
+         if (!emitWith(&pn->as<BinaryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Try:
+-        if (!emitTry(&pn->as<TernaryNode>())) {
++        if (!emitTry(&pn->as<TryNode>())) {
+             return false;
+         }
+         break;
+ 
+       case ParseNodeKind::Catch:
+         if (!emitCatch(&pn->as<BinaryNode>())) {
+             return false;
+         }
+diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
+--- a/js/src/frontend/BytecodeEmitter.h
++++ b/js/src/frontend/BytecodeEmitter.h
+@@ -683,17 +683,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
+     MOZ_MUST_USE bool emitIf(TernaryNode* ifNode);
+     MOZ_MUST_USE bool emitWith(BinaryNode* withNode);
+ 
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(const LabeledStatement* pn);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(LexicalScopeNode* lexicalScope);
+     MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body,
+                                            EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
+     MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* switchStmt);
+-    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(TernaryNode* tryNode);
++    MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(TryNode* tryNode);
+ 
+     enum DestructuringFlavor {
+         // Destructuring into a declaration.
+         DestructuringDeclaration,
+ 
+         // Destructuring into a formal parameter, when the formal parameters
+         // contain an expression that might be evaluated, and thus require
+         // this destructuring to assign not into the innermost scope that
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -1181,20 +1181,18 @@ FoldReturn(JSContext* cx, UnaryNode* nod
+             return false;
+         }
+     }
+ 
+     return true;
+ }
+ 
+ static bool
+-FoldTry(JSContext* cx, TernaryNode* node, PerHandlerParser<FullParseHandler>& parser)
++FoldTry(JSContext* cx, TryNode* node, PerHandlerParser<FullParseHandler>& parser)
+ {
+-    MOZ_ASSERT(node->isKind(ParseNodeKind::Try));
+-
+     ParseNode** statements = node->unsafeKid1Reference();
+     if (!Fold(cx, statements, parser)) {
+         return false;
+     }
+ 
+     ParseNode** catchScope = node->unsafeKid2Reference();
+     if (*catchScope) {
+         if (!Fold(cx, catchScope, parser)) {
+@@ -1785,17 +1783,17 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+         }
+         return Fold(cx, node->unsafeKidReference(), parser);
+       }
+ 
+       case ParseNodeKind::Return:
+         return FoldReturn(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::Try:
+-        return FoldTry(cx, &pn->as<TernaryNode>(), parser);
++        return FoldTry(cx, &pn->as<TryNode>(), parser);
+ 
+       case ParseNodeKind::Catch:
+         return FoldCatch(cx, &pn->as<BinaryNode>(), parser);
+ 
+       case ParseNodeKind::Class:
+         return FoldClass(cx, &pn->as<ClassNode>(), parser);
+ 
+       case ParseNodeKind::Elem: {
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -656,21 +656,20 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         return new_<LabeledStatement>(label, stmt, begin);
+     }
+ 
+     UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) {
+         MOZ_ASSERT(pos.encloses(expr->pn_pos));
+         return new_<UnaryNode>(ParseNodeKind::Throw, pos, expr);
+     }
+ 
+-    TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope,
++    TernaryNodeType newTryStatement(uint32_t begin, Node body, LexicalScopeNodeType catchScope,
+                                     Node finallyBlock)
+     {
+-        TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
+-        return new_<TernaryNode>(ParseNodeKind::Try, body, catchScope, finallyBlock, pos);
++        return new_<TryNode>(begin, body, catchScope, finallyBlock);
+     }
+ 
+     DebuggerStatementType newDebuggerStatement(const TokenPos& pos) {
+         return new_<DebuggerStatement>(pos);
+     }
+ 
+     NameNodeType newPropertyName(PropertyName* name, const TokenPos& pos) {
+         return new_<NameNode>(ParseNodeKind::PropertyName, JSOP_NOP, name, pos);
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -724,30 +724,29 @@ class NameResolver
+             }
+             break;
+           }
+ 
+           // The statements in the try-block are mandatory.  The catch-blocks
+           // and finally block are optional (but at least one or the other must
+           // be present).
+           case ParseNodeKind::Try: {
+-            TernaryNode* tryNode = &cur->as<TernaryNode>();
+-            if (!resolve(tryNode->kid1(), prefix)) {
++            TryNode* tryNode = &cur->as<TryNode>();
++            if (!resolve(tryNode->body(), prefix)) {
+                 return false;
+             }
+-            MOZ_ASSERT(tryNode->kid2() || tryNode->kid3());
+-            if (tryNode->kid2()) {
+-                LexicalScopeNode* catchScope = &tryNode->kid2()->as<LexicalScopeNode>();
++            MOZ_ASSERT(tryNode->catchScope() || tryNode->finallyBlock());
++            if (LexicalScopeNode* catchScope = tryNode->catchScope()) {
+                 MOZ_ASSERT(catchScope->scopeBody()->isKind(ParseNodeKind::Catch));
+                 MOZ_ASSERT(catchScope->scopeBody()->is<BinaryNode>());
+                 if (!resolve(catchScope->scopeBody(), prefix)) {
+                     return false;
+                 }
+             }
+-            if (ParseNode* finallyBlock = tryNode->kid3()) {
++            if (ParseNode* finallyBlock = tryNode->finallyBlock()) {
+                 if (!resolve(finallyBlock, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+           }
+ 
+           // The first child, the catch-pattern, may contain functions via
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -521,16 +521,17 @@ enum ParseNodeArity
+     PN_LIST,                            /* generic singly linked list */
+     PN_NAME,                            /* name, label, string */
+     PN_NUMBER,                          /* numeric literal */
+     PN_REGEXP,                          /* regexp literal */
+     PN_LOOP,                            /* loop control (break/continue) */
+     PN_SCOPE                            /* lexical scope */
+ };
+ 
++// FIXME: Remove `*Type` (bug 1489008)
+ #define FOR_EACH_PARSENODE_SUBCLASS(macro) \
+     macro(BinaryNode, BinaryNodeType, asBinary) \
+     macro(AssignmentNode, AssignmentNodeType, asAssignment) \
+     macro(CaseClause, CaseClauseType, asCaseClause) \
+     macro(ClassMethod, ClassMethodType, asClassMethod) \
+     macro(ClassNames, ClassNamesType, asClassNames) \
+     macro(ForNode, ForNodeType, asFor) \
+     macro(PropertyAccess, PropertyAccessType, asPropertyAccess) \
+@@ -559,16 +560,18 @@ enum ParseNodeArity
+     \
+     macro(NumericLiteral, NumericLiteralType, asNumericLiteral) \
+     \
+     macro(RegExpLiteral, RegExpLiteralType, asRegExpLiteral) \
+     \
+     macro(TernaryNode, TernaryNodeType, asTernary) \
+     macro(ClassNode, ClassNodeType, asClass) \
+     macro(ConditionalExpression, ConditionalExpressionType, asConditionalExpression) \
++    macro(TryNode, TryNodeType, asTry) \
++    \
+     macro(UnaryNode, UnaryNodeType, asUnary) \
+     macro(ThisLiteral, ThisLiteralType, asThisLiteral)
+ 
+ #define DECLARE_CLASS(typeName, longTypeName, asMethodName) \
+ class typeName;
+ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
+ #undef DECLARE_CLASS
+ 
+@@ -1680,16 +1683,47 @@ class ConditionalExpression : public Ter
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Conditional);
+         MOZ_ASSERT_IF(match, node.is<TernaryNode>());
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
+ };
+ 
++class TryNode : public TernaryNode
++{
++  public:
++    TryNode(uint32_t begin, ParseNode* body, LexicalScopeNode* catchScope,
++            ParseNode* finallyBlock)
++      : TernaryNode(ParseNodeKind::Try, body, catchScope, finallyBlock,
++                    TokenPos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end))
++    {
++        MOZ_ASSERT(body);
++        MOZ_ASSERT(catchScope || finallyBlock);
++    }
++
++    static bool test(const ParseNode& node) {
++        bool match = node.isKind(ParseNodeKind::Try);
++        MOZ_ASSERT_IF(match, node.is<TernaryNode>());
++        return match;
++    }
++
++    ParseNode* body() const {
++        return kid1();
++    }
++
++    LexicalScopeNode* catchScope() const {
++        return kid2() ? &kid2()->as<LexicalScopeNode>() : nullptr;
++    }
++
++    ParseNode* finallyBlock() const {
++        return kid3();
++    }
++};
++
+ class ThisLiteral : public UnaryNode
+ {
+   public:
+     ThisLiteral(const TokenPos& pos, ParseNode* thisName)
+       : UnaryNode(ParseNodeKind::This, pos, thisName)
+     { }
+ 
+     static bool test(const ParseNode& node) {
+diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
+--- a/js/src/frontend/SyntaxParseHandler.h
++++ b/js/src/frontend/SyntaxParseHandler.h
+@@ -371,17 +371,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     UnaryNodeType newExpressionBody(Node expr) { return NodeReturn; }
+     BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
+ 
+     LabeledStatementType newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
+         return NodeGeneric;
+     }
+ 
+     UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
+-    TernaryNodeType newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
++    TernaryNodeType newTryStatement(uint32_t begin, Node body, LexicalScopeNodeType catchScope,
++                                    Node finallyBlock)
++    {
+         return NodeGeneric;
+     }
+     DebuggerStatementType newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
+ 
+     NameNodeType newPropertyName(PropertyName* name, const TokenPos& pos) {
+         lastAtom = name;
+         return NodeGeneric;
+     }

+ 551 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478485.patch

@@ -0,0 +1,551 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726407 -32400
+#      Wed Sep 12 13:26:47 2018 +0900
+# Node ID 2ca066322bd252f0fbf4eac58ad64fb355bb512c
+# Parent  9d82b80e07af280d0d5cc3d24a5f35f0163bebaf
+Bug 1479659 - Part 9: Change NameNode::expression to NameNode::initializer and handle LabeledStatement properly. r=jwalden
+
+diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
+--- a/js/src/builtin/ReflectParse.cpp
++++ b/js/src/builtin/ReflectParse.cpp
+@@ -2084,17 +2084,17 @@ ASTSerializer::variableDeclaration(ListN
+ bool
+ ASTSerializer::variableDeclarator(ParseNode* pn, MutableHandleValue dst)
+ {
+     ParseNode* patternNode;
+     ParseNode* initNode;
+ 
+     if (pn->isKind(ParseNodeKind::Name)) {
+         patternNode = pn;
+-        initNode = pn->as<NameNode>().expression();
++        initNode = pn->as<NameNode>().initializer();
+         MOZ_ASSERT_IF(initNode, pn->pn_pos.encloses(initNode->pn_pos));
+     } else if (pn->isKind(ParseNodeKind::Assign)) {
+         AssignmentNode* assignNode = &pn->as<AssignmentNode>();
+         patternNode = assignNode->left();
+         initNode = assignNode->right();
+         MOZ_ASSERT(pn->pn_pos.encloses(patternNode->pn_pos));
+         MOZ_ASSERT(pn->pn_pos.encloses(initNode->pn_pos));
+     } else {
+diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp
+--- a/js/src/frontend/BinSource-auto.cpp
++++ b/js/src/frontend/BinSource-auto.cpp
+@@ -6984,17 +6984,17 @@ BinASTParser<Tok>::parseInterfaceVariabl
+ 
+     ParseNode* result;
+     if (binding->isKind(ParseNodeKind::Name)) {
+         // `var foo [= bar]``
+         MOZ_TRY(checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
+ 
+         BINJS_TRY_VAR(result, factory_.newName(binding->template as<NameNode>().atom()->asPropertyName(), tokenizer_->pos(start), cx_));
+         if (init)
+-            result->as<NameNode>().setExpression(init);
++            result->as<NameNode>().setInitializer(init);
+     } else {
+         // `var pattern = bar`
+         if (!init) {
+             // Here, `init` is required.
+             return raiseMissingField("VariableDeclarator (with non-trivial pattern)", BinField::Init);
+         }
+ 
+         MOZ_CRASH("Unimplemented: AssertedScope check for BindingPattern variable declaration");
+diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml
+--- a/js/src/frontend/BinSource.yaml
++++ b/js/src/frontend/BinSource.yaml
+@@ -1117,17 +1117,17 @@ VariableDeclarator:
+     build: |
+         ParseNode* result;
+         if (binding->isKind(ParseNodeKind::Name)) {
+             // `var foo [= bar]``
+             MOZ_TRY(checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
+ 
+             BINJS_TRY_VAR(result, factory_.newName(binding->template as<NameNode>().atom()->asPropertyName(), tokenizer_->pos(start), cx_));
+             if (init)
+-                result->as<NameNode>().setExpression(init);
++                result->as<NameNode>().setInitializer(init);
+         } else {
+             // `var pattern = bar`
+             if (!init) {
+                 // Here, `init` is required.
+                 return raiseMissingField("VariableDeclarator (with non-trivial pattern)", BinField::Init);
+             }
+ 
+             MOZ_CRASH("Unimplemented: AssertedScope check for BindingPattern variable declaration");
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -1459,17 +1459,17 @@ BytecodeEmitter::checkSideEffects(ParseN
+         SwitchStatement* switchStmt = &pn->as<SwitchStatement>();
+         if (!checkSideEffects(&switchStmt->discriminant(), answer)) {
+             return false;
+         }
+         return *answer || checkSideEffects(&switchStmt->lexicalForCaseList(), answer);
+       }
+ 
+       case ParseNodeKind::Label:
+-        return checkSideEffects(pn->as<NameNode>().expression(), answer);
++        return checkSideEffects(pn->as<LabeledStatement>().statement(), answer);
+ 
+       case ParseNodeKind::LexicalScope:
+         return checkSideEffects(pn->as<LexicalScopeNode>().scopeBody(), answer);
+ 
+       // We could methodically check every interpolated expression, but it's
+       // probably not worth the trouble.  Treat template strings as effect-free
+       // only if they don't contain any substitutions.
+       case ParseNodeKind::TemplateStringList: {
+@@ -4253,17 +4253,17 @@ BytecodeEmitter::emitDeclarationList(Lis
+             if (!emitDestructuringOps(pattern, DestructuringDeclaration)) {
+                 return false;
+             }
+ 
+             if (!emit1(JSOP_POP)) {
+                 return false;
+             }
+         } else {
+-            if (!emitSingleDeclaration(declList, decl, decl->as<NameNode>().expression())) {
++            if (!emitSingleDeclaration(declList, decl, decl->as<NameNode>().initializer())) {
+                 return false;
+             }
+         }
+     }
+     return true;
+ }
+ 
+ bool
+@@ -5485,17 +5485,17 @@ BytecodeEmitter::emitForIn(ForNode* forI
+ 
+     // Annex B: Evaluate the var-initializer expression if present.
+     // |for (var i = initializer in expr) { ... }|
+     ParseNode* forInTarget = forInHead->kid1();
+     if (parser->astGenerator().isDeclarationList(forInTarget)) {
+         ParseNode* decl =
+             parser->astGenerator().singleBindingFromDeclaration(&forInTarget->as<ListNode>());
+         if (decl->isKind(ParseNodeKind::Name)) {
+-            if (ParseNode* initializer = decl->as<NameNode>().expression()) {
++            if (ParseNode* initializer = decl->as<NameNode>().initializer()) {
+                 MOZ_ASSERT(forInTarget->isKind(ParseNodeKind::Var),
+                            "for-in initializers are only permitted for |var| declarations");
+ 
+                 if (!updateSourceCoordNotes(decl->pn_pos.begin)) {
+                     return false;
+                 }
+ 
+                 auto emitRhs = [decl, initializer](BytecodeEmitter* bce, const NameLocation&, bool) {
+diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
+--- a/js/src/frontend/FoldConstants.cpp
++++ b/js/src/frontend/FoldConstants.cpp
+@@ -155,17 +155,17 @@ ContainsHoistedDeclaration(JSContext* cx
+       // Statements possibly containing hoistable declarations only in the
+       // right half, in ParseNode terms -- the loop body or nested statement
+       // (usually a block statement), in AST terms.
+       case ParseNodeKind::While:
+       case ParseNodeKind::With:
+         return ContainsHoistedDeclaration(cx, node->as<BinaryNode>().right(), result);
+ 
+       case ParseNodeKind::Label:
+-        return ContainsHoistedDeclaration(cx, node->as<NameNode>().expression(), result);
++        return ContainsHoistedDeclaration(cx, node->as<LabeledStatement>().statement(), result);
+ 
+       // Statements with more complicated structures.
+ 
+       // if-statement nodes may have hoisted declarations in their consequent
+       // and alternative components.
+       case ParseNodeKind::If: {
+         TernaryNode* ifNode = &node->as<TernaryNode>();
+         ParseNode* consequent = ifNode->kid2();
+@@ -1577,21 +1577,21 @@ FoldDottedProperty(JSContext* cx, Proper
+     return Fold(cx, nested, parser);
+ }
+ 
+ static bool
+ FoldName(JSContext* cx, NameNode* nameNode, PerHandlerParser<FullParseHandler>& parser)
+ {
+     MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
+ 
+-    if (!nameNode->expression()) {
++    if (!nameNode->initializer()) {
+         return true;
+     }
+ 
+-    return Fold(cx, nameNode->unsafeExpressionReference(), parser);
++    return Fold(cx, nameNode->unsafeInitializerReference(), parser);
+ }
+ 
+ bool
+ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
+ {
+     if (!CheckRecursionLimit(cx)) {
+         return false;
+     }
+@@ -1638,17 +1638,17 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+         return true;
+ 
+       case ParseNodeKind::SuperBase:
+       case ParseNodeKind::TypeOfName: {
+ #ifdef DEBUG
+         UnaryNode* node = &pn->as<UnaryNode>();
+         NameNode* nameNode = &node->kid()->as<NameNode>();
+         MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
+-        MOZ_ASSERT(!nameNode->expression());
++        MOZ_ASSERT(!nameNode->initializer());
+ #endif
+         return true;
+       }
+ 
+       case ParseNodeKind::TypeOfExpr:
+         return FoldTypeOfExpr(cx, &pn->as<UnaryNode>(), parser);
+ 
+       case ParseNodeKind::DeleteName:
+@@ -1901,17 +1901,17 @@ Fold(JSContext* cx, ParseNode** pnp, Per
+       case ParseNodeKind::ForIn:
+       case ParseNodeKind::ForOf:
+         return FoldForInOrOf(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::ForHead:
+         return FoldForHead(cx, &pn->as<TernaryNode>(), parser);
+ 
+       case ParseNodeKind::Label:
+-        return Fold(cx, pn->as<NameNode>().unsafeExpressionReference(), parser);
++        return Fold(cx, pn->as<LabeledStatement>().unsafeStatementReference(), parser);
+ 
+       case ParseNodeKind::PropertyName:
+         MOZ_CRASH("unreachable, handled by ::Dot");
+ 
+       case ParseNodeKind::Dot:
+         return FoldDottedProperty(cx, &pn->as<PropertyAccess>(), parser);
+ 
+       case ParseNodeKind::LexicalScope: {
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -975,17 +975,17 @@ FullParseHandler::setLastFunctionFormalP
+ inline bool
+ FullParseHandler::finishInitializerAssignment(NameNodeType nameNode, Node init)
+ {
+     MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
+     MOZ_ASSERT(!nameNode->isInParens());
+ 
+     checkAndSetIsDirectRHSAnonFunction(init);
+ 
+-    nameNode->setExpression(init);
++    nameNode->setInitializer(init);
+     nameNode->setOp(JSOP_SETNAME);
+ 
+     /* The declarator's position must include the initializer. */
+     nameNode->pn_pos.end = init->pn_pos.end;
+     return true;
+ }
+ 
+ } // namespace frontend
+diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
+--- a/js/src/frontend/NameFunctions.cpp
++++ b/js/src/frontend/NameFunctions.cpp
+@@ -458,17 +458,17 @@ class NameResolver
+ 
+           case ParseNodeKind::Number:
+             MOZ_ASSERT(cur->is<NumericLiteral>());
+             break;
+ 
+           case ParseNodeKind::TypeOfName:
+           case ParseNodeKind::SuperBase:
+             MOZ_ASSERT(cur->as<UnaryNode>().kid()->isKind(ParseNodeKind::Name));
+-            MOZ_ASSERT(!cur->as<UnaryNode>().kid()->as<NameNode>().expression());
++            MOZ_ASSERT(!cur->as<UnaryNode>().kid()->as<NameNode>().initializer());
+             break;
+ 
+           case ParseNodeKind::NewTarget:
+           case ParseNodeKind::ImportMeta: {
+             MOZ_ASSERT(cur->as<BinaryNode>().left()->isKind(ParseNodeKind::PosHolder));
+             MOZ_ASSERT(cur->as<BinaryNode>().right()->isKind(ParseNodeKind::PosHolder));
+             break;
+           }
+@@ -683,22 +683,22 @@ class NameResolver
+           // if any.  The third is the class body.
+           case ParseNodeKind::Class: {
+             ClassNode* classNode = &cur->as<ClassNode>();
+ #ifdef DEBUG
+             if (classNode->names()) {
+                 ClassNames* names = classNode->names();
+                 if (NameNode* outerBinding = names->outerBinding()) {
+                     MOZ_ASSERT(outerBinding->isKind(ParseNodeKind::Name));
+-                    MOZ_ASSERT(!outerBinding->expression());
++                    MOZ_ASSERT(!outerBinding->initializer());
+                 }
+ 
+                 NameNode* innerBinding = names->innerBinding();
+                 MOZ_ASSERT(innerBinding->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!innerBinding->expression());
++                MOZ_ASSERT(!innerBinding->initializer());
+             }
+ #endif
+             if (ParseNode* heritage = classNode->heritage()) {
+                 if (!resolve(heritage, prefix)) {
+                     return false;
+                 }
+             }
+             if (!resolve(classNode->methodList(), prefix)) {
+@@ -869,19 +869,19 @@ class NameResolver
+                 break;
+             }
+             for (ParseNode* item : list->contents()) {
+                 BinaryNode* spec = &item->as<BinaryNode>();
+                 MOZ_ASSERT(spec->isKind(isImport
+                                         ? ParseNodeKind::ImportSpec
+                                         : ParseNodeKind::ExportSpec));
+                 MOZ_ASSERT(spec->left()->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!spec->left()->as<NameNode>().expression());
++                MOZ_ASSERT(!spec->left()->as<NameNode>().initializer());
+                 MOZ_ASSERT(spec->right()->isKind(ParseNodeKind::Name));
+-                MOZ_ASSERT(!spec->right()->as<NameNode>().expression());
++                MOZ_ASSERT(!spec->right()->as<NameNode>().initializer());
+             }
+ #endif
+             break;
+           }
+ 
+           case ParseNodeKind::CallImport: {
+             BinaryNode* node = &cur->as<BinaryNode>();
+             if (!resolve(node->right(), prefix)) {
+@@ -898,23 +898,23 @@ class NameResolver
+             }
+             if (!resolve(&prop->expression(), prefix)) {
+                 return false;
+             }
+             break;
+           }
+ 
+           case ParseNodeKind::Label:
+-            if (!resolve(cur->as<NameNode>().expression(), prefix)) {
++            if (!resolve(cur->as<LabeledStatement>().statement(), prefix)) {
+                 return false;
+             }
+             break;
+ 
+           case ParseNodeKind::Name:
+-            if (ParseNode* init = cur->as<NameNode>().expression()) {
++            if (ParseNode* init = cur->as<NameNode>().initializer()) {
+                 if (!resolve(init, prefix)) {
+                     return false;
+                 }
+             }
+             break;
+ 
+           case ParseNodeKind::LexicalScope:
+             if (!resolve(cur->as<LexicalScopeNode>().scopeBody(), prefix)) {
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -340,34 +340,45 @@ NameNode::dump(GenericPrinter& out, int 
+ 
+       case ParseNodeKind::Name:
+       case ParseNodeKind::PropertyName:
+         if (!atom()) {
+             out.put("#<null name>");
+         } else if (getOp() == JSOP_GETARG && atom()->length() == 0) {
+             // Dump destructuring parameter.
+             out.put("(#<zero-length name> ");
+-            DumpParseTree(expression(), out, indent + 21);
++            DumpParseTree(initializer(), out, indent + 21);
+             out.printf(")");
+         } else {
+             JS::AutoCheckCannotGC nogc;
+             if (atom()->hasLatin1Chars()) {
+                 DumpName(out, atom()->latin1Chars(nogc), atom()->length());
+             } else {
+                 DumpName(out, atom()->twoByteChars(nogc), atom()->length());
+             }
+         }
+         return;
+ 
++      case ParseNodeKind::Label: {
++        const char* name = parseNodeNames[size_t(getKind())];
++        out.printf("(%s ", name);
++        atom()->dumpCharsNoNewline(out);
++        indent += strlen(name) + atom()->length() + 2;
++        DumpParseTree(initializer(), out, indent);
++        out.printf(")");
++        return;
++      }
++
+       default: {
+         const char* name = parseNodeNames[size_t(getKind())];
+         out.printf("(%s ", name);
+         indent += strlen(name) + 2;
+-        DumpParseTree(expression(), out, indent);
++        DumpParseTree(initializer(), out, indent);
+         out.printf(")");
++        return;
+       }
+     }
+ }
+ 
+ void
+ LexicalScopeNode::dump(GenericPrinter& out, int indent)
+ {
+     const char* name = parseNodeNames[size_t(getKind())];
+diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
+--- a/js/src/frontend/ParseNode.h
++++ b/js/src/frontend/ParseNode.h
+@@ -343,17 +343,17 @@ IsTypeofKind(ParseNodeKind kind)
+  * Return (UnaryNode)
+  *   kid: returned expression, or null if none
+  * ExpressionStatement (UnaryNode)
+  *   kid: expr
+  *   prologue: true if Directive Prologue member in original source, not
+  *             introduced via constant folding or other tree rewriting
+  * EmptyStatement (NullaryNode)
+  *   (no fields)
+- * Label (NameNode)
++ * Label (LabeledStatement)
+  *   atom: label
+  *   expr: labeled statement
+  * Import (BinaryNode)
+  *   left: ImportSpecList import specifiers
+  *   right: String module specifier
+  * ImportSpecList (ListNode)
+  *   head: list of N ImportSpec nodes
+  *   count: N >= 0 (N = 0 for `import {} from ...`)
+@@ -693,18 +693,18 @@ class ParseNode
+             friend class UnaryNode;
+             ParseNode*  kid;
+             bool        prologue;       /* directive prologue member */
+         } unary;
+         struct {                        /* name, labeled statement, etc. */
+           private:
+             friend class NameNode;
+             JSAtom*      atom;          /* lexical name or label atom */
+-            ParseNode*  expr;           /* var initializer, or argument default
+-                                         */
++            ParseNode*  initOrStmt;     /* var initializer, argument default,
++                                         * or label statement target */
+         } name;
+         struct {
+           private:
+             friend class RegExpLiteral;
+             ObjectBox* objbox;
+         } regexp;
+         struct {
+           private:
+@@ -809,58 +809,58 @@ class NullaryNode : public ParseNode
+ #ifdef DEBUG
+     void dump(GenericPrinter& out);
+ #endif
+ };
+ 
+ class NameNode : public ParseNode
+ {
+   protected:
+-    NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, ParseNode* expr, const TokenPos& pos)
++    NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, ParseNode* initOrStmt, const TokenPos& pos)
+       : ParseNode(kind, op, PN_NAME, pos)
+     {
+         pn_u.name.atom = atom;
+-        pn_u.name.expr = expr;
++        pn_u.name.initOrStmt = initOrStmt;
+     }
+ 
+   public:
+     NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, const TokenPos& pos)
+       : ParseNode(kind, op, PN_NAME, pos)
+     {
+         pn_u.name.atom = atom;
+-        pn_u.name.expr = nullptr;
++        pn_u.name.initOrStmt = nullptr;
+     }
+ 
+     static bool test(const ParseNode& node) {
+         return node.isArity(PN_NAME);
+     }
+ 
+ #ifdef DEBUG
+     void dump(GenericPrinter& out, int indent);
+ #endif
+ 
+     JSAtom* atom() const {
+         return pn_u.name.atom;
+     }
+ 
+-    ParseNode* expression() const {
+-        return pn_u.name.expr;
++    ParseNode* initializer() const {
++        return pn_u.name.initOrStmt;
+     }
+ 
+     void setAtom(JSAtom* atom) {
+         pn_u.name.atom = atom;
+     }
+ 
+-    void setExpression(ParseNode* expr) {
+-        pn_u.name.expr = expr;
++    void setInitializer(ParseNode* init) {
++        pn_u.name.initOrStmt = init;
+     }
+ 
+     // Methods used by FoldConstants.cpp.
+-    ParseNode** unsafeExpressionReference() {
+-        return &pn_u.name.expr;
++    ParseNode** unsafeInitializerReference() {
++        return &pn_u.name.initOrStmt;
+     }
+ };
+ 
+ class UnaryNode : public ParseNode
+ {
+   public:
+     UnaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* kid)
+       : ParseNode(kind, JSOP_NOP, PN_UNARY, pos)
+@@ -1538,25 +1538,30 @@ class LabeledStatement : public NameNode
+       : NameNode(ParseNodeKind::Label, JSOP_NOP, label, stmt, TokenPos(begin, stmt->pn_pos.end))
+     {}
+ 
+     PropertyName* label() const {
+         return atom()->asPropertyName();
+     }
+ 
+     ParseNode* statement() const {
+-        return expression();
++        return initializer();
+     }
+ 
+     static bool test(const ParseNode& node) {
+         bool match = node.isKind(ParseNodeKind::Label);
+         MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
+         MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+         return match;
+     }
++
++    // Methods used by FoldConstants.cpp.
++    ParseNode** unsafeStatementReference() {
++        return unsafeInitializerReference();
++    }
+ };
+ 
+ // Inside a switch statement, a CaseClause is a case-label and the subsequent
+ // statements. The same node type is used for DefaultClauses. The only
+ // difference is that their caseExpression() is null.
+ class CaseClause : public BinaryNode
+ {
+   public:
+diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
+--- a/js/src/wasm/AsmJS.cpp
++++ b/js/src/wasm/AsmJS.cpp
+@@ -693,17 +693,17 @@ ObjectNormalFieldInitializer(ParseNode* 
+ {
+     MOZ_ASSERT(IsNormalObjectField(pn));
+     return BinaryRight(pn);
+ }
+ 
+ static inline ParseNode*
+ MaybeInitializer(ParseNode* pn)
+ {
+-    return pn->as<NameNode>().expression();
++    return pn->as<NameNode>().initializer();
+ }
+ 
+ static inline bool
+ IsUseOfName(ParseNode* pn, PropertyName* name)
+ {
+     return pn->isKind(ParseNodeKind::Name) && pn->name() == name;
+ }
+ 

+ 64 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478486.patch

@@ -0,0 +1,64 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536804041 -32400
+#      Thu Sep 13 11:00:41 2018 +0900
+# Node ID 7edfb6cadccf11658bd15e69b8eff22a0e50968f
+# Parent  2ca066322bd252f0fbf4eac58ad64fb355bb512c
+Bug 1479659 - Part 10: Remove magic number for the string length in NameNode::dump. r=jwalden
+
+diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
+--- a/js/src/frontend/ParseNode.cpp
++++ b/js/src/frontend/ParseNode.cpp
+@@ -1,27 +1,29 @@
+ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+  * vim: set ts=8 sts=4 et sw=4 tw=99:
+  * 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/. */
+ 
+ #include "frontend/ParseNode-inl.h"
+ 
++#include "mozilla/ArrayUtils.h"
+ #include "mozilla/FloatingPoint.h"
+ 
+ #include "jsnum.h"
+ 
+ #include "frontend/Parser.h"
+ 
+ #include "vm/JSContext-inl.h"
+ 
+ using namespace js;
+ using namespace js::frontend;
+ 
++using mozilla::ArrayLength;
+ using mozilla::IsFinite;
+ 
+ #ifdef DEBUG
+ void
+ ListNode::checkConsistency() const
+ {
+     ParseNode* const* tailNode;
+     uint32_t actualCount = 0;
+@@ -339,18 +341,20 @@ NameNode::dump(GenericPrinter& out, int 
+         return;
+ 
+       case ParseNodeKind::Name:
+       case ParseNodeKind::PropertyName:
+         if (!atom()) {
+             out.put("#<null name>");
+         } else if (getOp() == JSOP_GETARG && atom()->length() == 0) {
+             // Dump destructuring parameter.
+-            out.put("(#<zero-length name> ");
+-            DumpParseTree(initializer(), out, indent + 21);
++            static const char ZeroLengthPrefix[] = "(#<zero-length name> ";
++            constexpr size_t ZeroLengthPrefixLength = ArrayLength(ZeroLengthPrefix) - 1;
++            out.put(ZeroLengthPrefix);
++            DumpParseTree(initializer(), out, indent + ZeroLengthPrefixLength);
+             out.printf(")");
+         } else {
+             JS::AutoCheckCannotGC nogc;
+             if (atom()->hasLatin1Chars()) {
+                 DumpName(out, atom()->latin1Chars(nogc), atom()->length());
+             } else {
+                 DumpName(out, atom()->twoByteChars(nogc), atom()->length());
+             }

+ 308 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478487.patch

@@ -0,0 +1,308 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536726421 -32400
+#      Wed Sep 12 13:27:01 2018 +0900
+# Node ID 00c630c175d509d0c1caee01b633b94dde968921
+# Parent  7edfb6cadccf11658bd15e69b8eff22a0e50968f
+Bug 1479659 - Part 11: Fix remaining ParseNode* in FullParseHandler methods signature. r=jwalden
+
+diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
+--- a/js/src/frontend/FullParseHandler.h
++++ b/js/src/frontend/FullParseHandler.h
+@@ -61,31 +61,31 @@ class FullParseHandler
+ 
+ #define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
+     using longTypeName = typeName*;
+ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
+ #undef DECLARE_TYPE
+ 
+     using NullNode = std::nullptr_t;
+ 
+-    bool isPropertyAccess(ParseNode* node) {
++    bool isPropertyAccess(Node node) {
+         return node->isKind(ParseNodeKind::Dot) || node->isKind(ParseNodeKind::Elem);
+     }
+ 
+-    bool isFunctionCall(ParseNode* node) {
++    bool isFunctionCall(Node node) {
+         // Note: super() is a special form, *not* a function call.
+         return node->isKind(ParseNodeKind::Call);
+     }
+ 
+-    static bool isUnparenthesizedDestructuringPattern(ParseNode* node) {
++    static bool isUnparenthesizedDestructuringPattern(Node node) {
+         return !node->isInParens() && (node->isKind(ParseNodeKind::Object) ||
+                                        node->isKind(ParseNodeKind::Array));
+     }
+ 
+-    static bool isParenthesizedDestructuringPattern(ParseNode* node) {
++    static bool isParenthesizedDestructuringPattern(Node node) {
+         // Technically this isn't a destructuring pattern at all -- the grammar
+         // doesn't treat it as such.  But we need to know when this happens to
+         // consider it a SyntaxError rather than an invalid-left-hand-side
+         // ReferenceError.
+         return node->isInParens() && (node->isKind(ParseNodeKind::Object) ||
+                                       node->isKind(ParseNodeKind::Array));
+     }
+ 
+@@ -154,18 +154,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+             return null();
+         }
+ 
+         addArrayElement(callSiteObj, rawNodes);
+ 
+         return callSiteObj;
+     }
+ 
+-    void addToCallSiteObject(CallSiteNodeType callSiteObj, ParseNode* rawNode,
+-                             ParseNode* cookedNode) {
++    void addToCallSiteObject(CallSiteNodeType callSiteObj, Node rawNode,
++                             Node cookedNode) {
+         MOZ_ASSERT(callSiteObj->isKind(ParseNodeKind::CallSiteObj));
+ 
+         addArrayElement(callSiteObj, cookedNode);
+         addArrayElement(callSiteObj->rawNodes(), rawNode);
+ 
+         /*
+          * We don't know when the last noSubstTemplate will come in, and we
+          * don't want to deal with this outside this method
+@@ -242,19 +242,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+ 
+   private:
+     BinaryNodeType newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
+         TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
+         return new_<BinaryNode>(kind, op, pos, left, right);
+     }
+ 
+   public:
+-    ParseNode* appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
+-                                  ParseContext* pc)
+-    {
++    Node appendOrCreateList(ParseNodeKind kind, Node left, Node right, ParseContext* pc) {
+         return ParseNode::appendOrCreateList(kind, left, right, this, pc);
+     }
+ 
+     // Expressions
+ 
+     ListNodeType newArrayLiteral(uint32_t begin) {
+         return new_<ListNode>(ParseNodeKind::Array, TokenPos(begin, begin + 1));
+     }
+@@ -453,17 +451,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     }
+ 
+     // Statements
+ 
+     ListNodeType newStatementList(const TokenPos& pos) {
+         return new_<ListNode>(ParseNodeKind::StatementList, pos);
+     }
+ 
+-    MOZ_MUST_USE bool isFunctionStmt(ParseNode* stmt) {
++    MOZ_MUST_USE bool isFunctionStmt(Node stmt) {
+         while (stmt->isKind(ParseNodeKind::Label)) {
+             stmt = stmt->as<LabeledStatement>().statement();
+         }
+         return stmt->isKind(ParseNodeKind::Function);
+     }
+ 
+     void addStatementToList(ListNodeType list, Node stmt) {
+         MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));
+@@ -697,17 +695,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         lexicalScope->setScopeBody(catchClause);
+         return true;
+     }
+ 
+     inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(CodeNodeType funNode,
+                                                                    Node defaultValue);
+ 
+   private:
+-    void checkAndSetIsDirectRHSAnonFunction(ParseNode* pn) {
++    void checkAndSetIsDirectRHSAnonFunction(Node pn) {
+         if (IsAnonymousFunctionDefinition(pn)) {
+             pn->setDirectRHSAnonFunction(true);
+         }
+     }
+ 
+   public:
+     CodeNodeType newFunctionStatement(const TokenPos& pos) {
+         return new_<CodeNode>(ParseNodeKind::Function, JSOP_NOP, pos);
+@@ -775,73 +773,73 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+             // for plain assignment.
+             MOZ_ASSERT(node->isOp(JSOP_NOP));
+             return true;
+         }
+ 
+         return false;
+     }
+ 
+-    bool isUnparenthesizedUnaryExpression(ParseNode* node) {
++    bool isUnparenthesizedUnaryExpression(Node node) {
+         if (!node->isInParens()) {
+             ParseNodeKind kind = node->getKind();
+             return kind == ParseNodeKind::Void ||
+                    kind == ParseNodeKind::Not ||
+                    kind == ParseNodeKind::BitNot ||
+                    kind == ParseNodeKind::Pos ||
+                    kind == ParseNodeKind::Neg ||
+                    IsTypeofKind(kind) ||
+                    IsDeleteKind(kind);
+         }
+         return false;
+     }
+ 
+-    bool isReturnStatement(ParseNode* node) {
++    bool isReturnStatement(Node node) {
+         return node->isKind(ParseNodeKind::Return);
+     }
+ 
+-    bool isStatementPermittedAfterReturnStatement(ParseNode *node) {
++    bool isStatementPermittedAfterReturnStatement(Node node) {
+         ParseNodeKind kind = node->getKind();
+         return kind == ParseNodeKind::Function ||
+                kind == ParseNodeKind::Var ||
+                kind == ParseNodeKind::Break ||
+                kind == ParseNodeKind::Throw ||
+                kind == ParseNodeKind::EmptyStatement;
+     }
+ 
+-    bool isSuperBase(ParseNode* node) {
++    bool isSuperBase(Node node) {
+         return node->isKind(ParseNodeKind::SuperBase);
+     }
+ 
+-    bool isUsableAsObjectPropertyName(ParseNode* node) {
++    bool isUsableAsObjectPropertyName(Node node) {
+         return node->isKind(ParseNodeKind::Number)
+             || node->isKind(ParseNodeKind::ObjectPropertyName)
+             || node->isKind(ParseNodeKind::String)
+             || node->isKind(ParseNodeKind::ComputedName);
+     }
+ 
+     inline MOZ_MUST_USE bool finishInitializerAssignment(NameNodeType nameNode, Node init);
+ 
+-    void setBeginPosition(ParseNode* pn, ParseNode* oth) {
++    void setBeginPosition(Node pn, Node oth) {
+         setBeginPosition(pn, oth->pn_pos.begin);
+     }
+-    void setBeginPosition(ParseNode* pn, uint32_t begin) {
++    void setBeginPosition(Node pn, uint32_t begin) {
+         pn->pn_pos.begin = begin;
+         MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
+     }
+ 
+-    void setEndPosition(ParseNode* pn, ParseNode* oth) {
++    void setEndPosition(Node pn, Node oth) {
+         setEndPosition(pn, oth->pn_pos.end);
+     }
+-    void setEndPosition(ParseNode* pn, uint32_t end) {
++    void setEndPosition(Node pn, uint32_t end) {
+         pn->pn_pos.end = end;
+         MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
+     }
+ 
+-    uint32_t getFunctionNameOffset(ParseNode* func, TokenStreamAnyChars& ts) {
++    uint32_t getFunctionNameOffset(Node func, TokenStreamAnyChars& ts) {
+         return func->pn_pos.begin;
+     }
+ 
+     bool isDeclarationKind(ParseNodeKind kind) {
+         return kind == ParseNodeKind::Var ||
+                kind == ParseNodeKind::Let ||
+                kind == ParseNodeKind::Const;
+     }
+@@ -857,17 +855,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+         return new_<ListNode>(kind, JSOP_NOP, kid);
+     }
+ 
+     ListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) {
+         MOZ_ASSERT(isDeclarationKind(kind));
+         return new_<ListNode>(kind, JSOP_NOP, pos);
+     }
+ 
+-    bool isDeclarationList(ParseNode* node) {
++    bool isDeclarationList(Node node) {
+         return isDeclarationKind(node->getKind());
+     }
+ 
+     Node singleBindingFromDeclaration(ListNodeType decl) {
+         MOZ_ASSERT(isDeclarationList(decl));
+         MOZ_ASSERT(decl->count() == 1);
+         return decl->head();
+     }
+@@ -879,17 +877,17 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     void addList(ListNodeType list, Node kid) {
+         if (sourceKind_ == SourceKind::Text) {
+             list->append(kid);
+         } else {
+             list->appendWithoutOrderAssumption(kid);
+         }
+     }
+ 
+-    void setOp(ParseNode* pn, JSOp op) {
++    void setOp(Node pn, JSOp op) {
+         pn->setOp(op);
+     }
+     void setListHasNonConstInitializer(ListNodeType literal) {
+         literal->setHasNonConstInitializer();
+     }
+     template <typename NodeType>
+     MOZ_MUST_USE NodeType parenthesize(NodeType node) {
+         node->setInParens(true);
+@@ -898,51 +896,51 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
+     template <typename NodeType>
+     MOZ_MUST_USE NodeType setLikelyIIFE(NodeType node) {
+         return parenthesize(node);
+     }
+     void setInDirectivePrologue(UnaryNodeType exprStmt) {
+         exprStmt->setIsDirectivePrologueMember();
+     }
+ 
+-    bool isName(ParseNode* node) {
++    bool isName(Node node) {
+         return node->isKind(ParseNodeKind::Name);
+     }
+ 
+-    bool isArgumentsName(ParseNode* node, JSContext* cx) {
++    bool isArgumentsName(Node node, JSContext* cx) {
+         return node->isKind(ParseNodeKind::Name) &&
+                node->as<NameNode>().atom() == cx->names().arguments;
+     }
+ 
+-    bool isEvalName(ParseNode* node, JSContext* cx) {
++    bool isEvalName(Node node, JSContext* cx) {
+         return node->isKind(ParseNodeKind::Name) &&
+                node->as<NameNode>().atom() == cx->names().eval;
+     }
+ 
+-    bool isAsyncKeyword(ParseNode* node, JSContext* cx) {
++    bool isAsyncKeyword(Node node, JSContext* cx) {
+         return node->isKind(ParseNodeKind::Name) &&
+                node->pn_pos.begin + strlen("async") == node->pn_pos.end &&
+                node->as<NameNode>().atom() == cx->names().async;
+     }
+ 
+-    PropertyName* maybeDottedProperty(ParseNode* pn) {
++    PropertyName* maybeDottedProperty(Node pn) {
+         return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name() : nullptr;
+     }
+-    JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) {
++    JSAtom* isStringExprStatement(Node pn, TokenPos* pos) {
+         if (pn->is<UnaryNode>()) {
+             UnaryNode* unary = &pn->as<UnaryNode>();
+             if (JSAtom* atom = unary->isStringExprStatement()) {
+                 *pos = unary->kid()->pn_pos;
+                 return atom;
+             }
+         }
+         return nullptr;
+     }
+ 
+-    void adjustGetToSet(ParseNode* node) {
++    void adjustGetToSet(Node node) {
+         node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
+     }
+ 
+     bool canSkipLazyInnerFunctions() {
+         return !!lazyOuterFunction_;
+     }
+     bool canSkipLazyClosedOverBindings() {
+         return !!lazyOuterFunction_;

+ 51 - 0
frg/work-js/mozilla-release/patches/mozilla-esr68-push_478488.patch

@@ -0,0 +1,51 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1536804042 -32400
+#      Thu Sep 13 11:00:42 2018 +0900
+# Node ID dfa48fdb5b446822b5025ba30e51798937816ee2
+# Parent  00c630c175d509d0c1caee01b633b94dde968921
+Bug 1479659 - Part 12: Revert range based for in BytecodeEmitter::emitFunctionFormalParameters. r=jwalden
+
+diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
+--- a/js/src/frontend/BytecodeEmitter.cpp
++++ b/js/src/frontend/BytecodeEmitter.cpp
+@@ -8403,18 +8403,18 @@ BytecodeEmitter::emitFunctionFormalParam
+ {
+     ParseNode* funBody = paramsBody->last();
+     FunctionBox* funbox = sc->asFunctionBox();
+     EmitterScope* funScope = innermostEmitterScope();
+ 
+     bool hasParameterExprs = funbox->hasParameterExprs;
+     bool hasRest = funbox->hasRest();
+ 
+-    int16_t argSlot = 0;
+-    for (ParseNode* arg : paramsBody->contentsTo(funBody)) {
++    uint16_t argSlot = 0;
++    for (ParseNode* arg = paramsBody->head(); arg != funBody; arg = arg->pn_next, argSlot++) {
+         ParseNode* bindingElement = arg;
+         ParseNode* initializer = nullptr;
+         if (arg->isKind(ParseNodeKind::Assign)) {
+             bindingElement = arg->as<AssignmentNode>().left();
+             initializer = arg->as<AssignmentNode>().right();
+         }
+ 
+         // Left-hand sides are either simple names or destructuring patterns.
+@@ -8543,18 +8543,16 @@ BytecodeEmitter::emitFunctionFormalParam
+             }
+         }
+ 
+         if (paramExprVarScope) {
+             if (!paramExprVarScope->leave(this)) {
+                 return false;
+             }
+         }
+-
+-        argSlot++;
+     }
+ 
+     return true;
+ }
+ 
+ bool
+ BytecodeEmitter::emitInitializeFunctionSpecialNames()
+ {

+ 58 - 9
frg/work-js/mozilla-release/patches/series

@@ -4380,6 +4380,7 @@ NOBUG-20180508-hashtable-62a1.patch
 1459568-62a1.patch
 1459568-62a1.patch
 1457703-2-62a1.patch
 1457703-2-62a1.patch
 1401927-62a1.patch
 1401927-62a1.patch
+1460057-62a1.patch
 1456774-62a1.patch
 1456774-62a1.patch
 1459607-62a1.patch
 1459607-62a1.patch
 1434783-62a1.patch
 1434783-62a1.patch
@@ -4488,6 +4489,8 @@ NOBUG-20180508-hashtable-62a1.patch
 1457499-1-62a1.patch
 1457499-1-62a1.patch
 1457499-2-62a1.patch
 1457499-2-62a1.patch
 1457499-3-62a1.patch
 1457499-3-62a1.patch
+1462261-1-62a1.patch
+1462261-2-62a1.patch
 1450795-2-62a1.patch
 1450795-2-62a1.patch
 1460098-1-62a1.patch
 1460098-1-62a1.patch
 1460098-2-62a1.patch
 1460098-2-62a1.patch
@@ -5116,6 +5119,7 @@ NOBUG-20180629-testingfunctions-63a1.patch
 1473121-63a1.patch
 1473121-63a1.patch
 1472199-1-63a1.patch
 1472199-1-63a1.patch
 1472199-2-63a1.patch
 1472199-2-63a1.patch
+1473024-63a1.patch
 1473308-63a1.patch
 1473308-63a1.patch
 1473954-63a1.patch
 1473954-63a1.patch
 1468207-63a1.patch
 1468207-63a1.patch
@@ -5154,6 +5158,7 @@ NOBUG-20180629-testingfunctions-63a1.patch
 1413922-2-PARTIAL-63a1.patch
 1413922-2-PARTIAL-63a1.patch
 1443471-2-63a1.patch
 1443471-2-63a1.patch
 1474786-63a1.patch
 1474786-63a1.patch
+NOBUG-20180712-typetraits-63a1.patch
 1474871-63a1.patch
 1474871-63a1.patch
 1475067-63a1.patch
 1475067-63a1.patch
 1475058-63a1.patch
 1475058-63a1.patch
@@ -5164,8 +5169,13 @@ NOBUG-20180629-testingfunctions-63a1.patch
 1460489-3-63a1.patch
 1460489-3-63a1.patch
 1460489-4-63a1.patch
 1460489-4-63a1.patch
 1460489-5-63a1.patch
 1460489-5-63a1.patch
+1469044-1-63a1.patch
+1469044-2-63a1.patch
+1469044-3-63a1.patch
 1472291-1-63a1.patch
 1472291-1-63a1.patch
 1472291-2-63a1.patch
 1472291-2-63a1.patch
+1052582-1-63a1.patch
+1052582-2-63a1.patch
 1475504-PARTIAL-63a1.patch
 1475504-PARTIAL-63a1.patch
 1472716-1-63a1.patch
 1472716-1-63a1.patch
 1472490-63a1.patch
 1472490-63a1.patch
@@ -5263,6 +5273,7 @@ NOBUG-20180720-tokenstream-63a1.patch
 1456006-1-63a1.patch
 1456006-1-63a1.patch
 1456006-2-63a1.patch
 1456006-2-63a1.patch
 1475943-63a1.patch
 1475943-63a1.patch
+1309552-63a1.patch
 1323381-63a1.patch
 1323381-63a1.patch
 1477579-1-63a1.patch
 1477579-1-63a1.patch
 1477579-2-63a1.patch
 1477579-2-63a1.patch
@@ -5344,13 +5355,19 @@ NOBUG-20180720-tokenstream-63a1.patch
 1478587-10-63a1.patch
 1478587-10-63a1.patch
 1478892-1-63a1.patch
 1478892-1-63a1.patch
 1477157-63a1.patch
 1477157-63a1.patch
-1477090-63a1.patch
-1469004-63a1.patch
-1480720-63a1.patch
+1468524-63a1.patch
+1478982-63a1.patch
 1426176-63a1.patch
 1426176-63a1.patch
 1476034-63a1.patch
 1476034-63a1.patch
 1478499-1-63a1.patch
 1478499-1-63a1.patch
 1478499-3no2-63a1.patch
 1478499-3no2-63a1.patch
+1475228-1-63a1.patch
+1475228-2-63a1.patch
+1475228-3-63a1.patch
+1475228-4-63a1.patch
+1475228-5-63a1.patch
+1475228-6-63a1.patch
+1475228-7-63a1.patch
 1478499-4-63a1.patch
 1478499-4-63a1.patch
 1478499-5no6-63a1.patch
 1478499-5no6-63a1.patch
 1479851-63a1.patch
 1479851-63a1.patch
@@ -5383,6 +5400,12 @@ NOBUG-20180720-tokenstream-63a1.patch
 1477621-2-63a1.patch
 1477621-2-63a1.patch
 1477621-3-63a1.patch
 1477621-3-63a1.patch
 1477621-4-63a1.patch
 1477621-4-63a1.patch
+1481005-63a1.patch
+NOBUG-20180809-utf8-63a1.patch
+1477090-63a1.patch
+1469004-63a1.patch
+1480720-63a1.patch
+1480966-63a1.patch
 1480493-63a1.patch
 1480493-63a1.patch
 1481248-63a1.patch
 1481248-63a1.patch
 1456404-1-63a1.patch
 1456404-1-63a1.patch
@@ -5428,6 +5451,7 @@ NOBUG-20180720-tokenstream-63a1.patch
 1481097-4-63a1.patch
 1481097-4-63a1.patch
 1482694-63a1.patch
 1482694-63a1.patch
 1479501-63a1.patch
 1479501-63a1.patch
+1479793-63a1.patch
 1478813-1-63a1.patch
 1478813-1-63a1.patch
 1482676-63a1.patch
 1482676-63a1.patch
 1478798-63a1.patch
 1478798-63a1.patch
@@ -5435,10 +5459,14 @@ NOBUG-20180720-tokenstream-63a1.patch
 1482841-2-63a1.patch
 1482841-2-63a1.patch
 1482605-63a1.patch
 1482605-63a1.patch
 1476203-63a1.patch
 1476203-63a1.patch
+1479673-1-63a1.patch
+1479673-2-63a1.patch
 1469287-63a1.patch
 1469287-63a1.patch
 1481441-63a1.patch
 1481441-63a1.patch
 1482695-63a1.patch
 1482695-63a1.patch
 1478910-63a1.patch
 1478910-63a1.patch
+1482931-1-63a1.patch
+1482931-2-63a1.patch
 1478892-2-63a1.patch
 1478892-2-63a1.patch
 1478892-3-63a1.patch
 1478892-3-63a1.patch
 1479878-63a1.patch
 1479878-63a1.patch
@@ -5468,6 +5496,7 @@ NOBUG-20180720-tokenstream-63a1.patch
 1465046-63a1.patch
 1465046-63a1.patch
 1483937-63a1.patch
 1483937-63a1.patch
 1471371-63a1.patch
 1471371-63a1.patch
+1440648-63a1.patch
 1473228-63a1.patch
 1473228-63a1.patch
 1480558-1-63a1.patch
 1480558-1-63a1.patch
 1480558-2-63a1.patch
 1480558-2-63a1.patch
@@ -5490,10 +5519,15 @@ NOBUG-20180720-tokenstream-63a1.patch
 1484535-63a1.patch
 1484535-63a1.patch
 1484485-63a1.patch
 1484485-63a1.patch
 1470921-63a1.patch
 1470921-63a1.patch
+1483374-63a1.patch
 1040316-63a1.patch
 1040316-63a1.patch
+1484385-63a1.patch
 1484389-63a1.patch
 1484389-63a1.patch
 1033916-1-63a1.patch
 1033916-1-63a1.patch
 1033916-2-63a1.patch
 1033916-2-63a1.patch
+#1484386-63a1.patch
+1484421-63a1.patch
+1484420-63a1.patch
 1484243-63a1.patch
 1484243-63a1.patch
 1485245-63a1.patch
 1485245-63a1.patch
 1473230-63a1.patch
 1473230-63a1.patch
@@ -5501,6 +5535,7 @@ NOBUG-20180720-tokenstream-63a1.patch
 1483761-2-63a1.patch
 1483761-2-63a1.patch
 1483761-3-63a1.patch
 1483761-3-63a1.patch
 1483761-4-63a1.patch
 1483761-4-63a1.patch
+1484943-63a1.patch
 1485545-63a1.patch
 1485545-63a1.patch
 1485610-63a1.patch
 1485610-63a1.patch
 1454285-3-63a1.patch
 1454285-3-63a1.patch
@@ -5533,6 +5568,7 @@ NOBUG-20180824-buildsetting-63a1.patch
 1485759-63a1.patch
 1485759-63a1.patch
 1485347-1-63a1.patch
 1485347-1-63a1.patch
 1485347-2-63a1.patch
 1485347-2-63a1.patch
+1486444-63a1.patch
 1486729-63a1.patch
 1486729-63a1.patch
 1486957-63a1.patch
 1486957-63a1.patch
 1457092-5-63a1.patch
 1457092-5-63a1.patch
@@ -5547,6 +5583,7 @@ NOBUG-20180824-buildsetting-63a1.patch
 1486905-63a1.patch
 1486905-63a1.patch
 1485698-63a1.patch
 1485698-63a1.patch
 1486730-63a1.patch
 1486730-63a1.patch
+1476921-63a1.patch
 1487517-63a1.patch
 1487517-63a1.patch
 1480457-1-63a1.patch
 1480457-1-63a1.patch
 1480457-4-63a1.patch
 1480457-4-63a1.patch
@@ -5567,7 +5604,8 @@ NOBUG-20180824-buildsetting-63a1.patch
 1417646-1-64a1.patch
 1417646-1-64a1.patch
 1487419-64a1.patch
 1487419-64a1.patch
 1488698-2-PARTIAL-biginttype-64a1.patch
 1488698-2-PARTIAL-biginttype-64a1.patch
-#435732.patch
+1488698-7-WIP-64a1.patch
+#1488698-7-64a1.patch
 #1484948-64a1.patch
 #1484948-64a1.patch
 1489944-64a1.patch
 1489944-64a1.patch
 #1492074-64a1.patch
 #1492074-64a1.patch
@@ -5599,6 +5637,19 @@ NOBUG-20180824-buildsetting-63a1.patch
 1469027-3only-64a1.patch
 1469027-3only-64a1.patch
 1440879-1-64a1.patch
 1440879-1-64a1.patch
 1440879-2-64a1.patch
 1440879-2-64a1.patch
+#mozilla-esr68-push_478476.patch
+#mozilla-esr68-push_478477.patch
+#mozilla-esr68-push_478478.patch
+#mozilla-esr68-push_478479.patch
+#mozilla-esr68-push_478480.patch
+#mozilla-esr68-push_478481.patch
+#mozilla-esr68-push_478482.patch
+#mozilla-esr68-push_478483.patch
+#mozilla-esr68-push_478484.patch
+#mozilla-esr68-push_478485.patch
+#mozilla-esr68-push_478486.patch
+#mozilla-esr68-push_478487.patch
+#mozilla-esr68-push_478488.patch
 1489091-64a1.patch
 1489091-64a1.patch
 1399870-64a1.patch
 1399870-64a1.patch
 1492268-64a1.patch
 1492268-64a1.patch
@@ -5826,6 +5877,8 @@ NOBUG-20180824-buildsetting-63a1.patch
 1488181-64a1.patch
 1488181-64a1.patch
 1492341-64a1.patch
 1492341-64a1.patch
 1490240-64a1.patch
 1490240-64a1.patch
+#1485347-3-64a1.patch
+#stop-here
 1390547-64a1.patch
 1390547-64a1.patch
 1497692-64a1.patch
 1497692-64a1.patch
 1498274-64a1.patch
 1498274-64a1.patch
@@ -7999,6 +8052,7 @@ TOP-NOBUG-REGEXP-47-fixes-25319.patch
 1881093-11509.patch
 1881093-11509.patch
 TOP-1880562-NSS3902-11509.patch
 TOP-1880562-NSS3902-11509.patch
 NOBUG-JSFIXUPS-25319.patch
 NOBUG-JSFIXUPS-25319.patch
+1462261-3-25319.patch
 
 
 
 
 
 
@@ -8017,8 +8071,6 @@ L-1460489-6-63a1.patch
 bumm-compile-error
 bumm-compile-error
 
 
 
 
-L-1462261-1-62a1.patch
-L-1462261-2-62a1.patch
 1473003-1-63a1.patch
 1473003-1-63a1.patch
 
 
 
 
@@ -8312,7 +8364,6 @@ L-1819535-patchdiff-112a1.patch
 L-marionette_1428849-59a1.patch
 L-marionette_1428849-59a1.patch
 L-1819244-needs1511670-10210.patch
 L-1819244-needs1511670-10210.patch
 L-1405491-4-58a1.patch
 L-1405491-4-58a1.patch
-L-1459220-62a1.patch
 
 
 
 
 
 
@@ -8653,7 +8704,6 @@ python-3-end
 0892-Some-small-issues-to-address-buildability.patch
 0892-Some-small-issues-to-address-buildability.patch
 0893-Make-everything-build-on-supported-compilers.patch
 0893-Make-everything-build-on-supported-compilers.patch
 0903-Comment-out-undefined-code-for-now.patch
 0903-Comment-out-undefined-code-for-now.patch
-0926-Bug-1456494-Initialize-Zone-helperThreadUse_-first-t.patch
 0928-Fix-unified-debug-build-bustage.-2197.patch
 0928-Fix-unified-debug-build-bustage.-2197.patch
 0974-Remove-unnecessary-flag.patch
 0974-Remove-unnecessary-flag.patch
 0975-Fix-C-17-build-with-Clang.patch
 0975-Fix-C-17-build-with-Clang.patch
@@ -8751,7 +8801,6 @@ python-3-end
 1198-Bug-1515356-Do-not-fail-the-build-in-case-of-warning.patch
 1198-Bug-1515356-Do-not-fail-the-build-in-case-of-warning.patch
 1199-Bug-1573501-Disable-Wtautological-type-limit-compare.patch
 1199-Bug-1573501-Disable-Wtautological-type-limit-compare.patch
 1200-Raise-the-minimum-clang-version-to-6.patch
 1200-Raise-the-minimum-clang-version-to-6.patch
-1244-Bug-1471371-OOM-handling-in-RegExp-construction.-r-j.patch
 1246-Bug-1641352-Re-import-Irregexp-r-mgaudet.patch
 1246-Bug-1641352-Re-import-Irregexp-r-mgaudet.patch
 1247-Bug-1641352-Use-VerifyRegExpSyntax-in-CheckPatternSy.patch
 1247-Bug-1641352-Use-VerifyRegExpSyntax-in-CheckPatternSy.patch
 1248-Bug-1644412-Deactivate-peephole-optimization-on-big-.patch
 1248-Bug-1644412-Deactivate-peephole-optimization-on-big-.patch

+ 41 - 3
frg/work-js/mozilla-release/patches/series-test

@@ -4380,6 +4380,7 @@ NOBUG-20180508-hashtable-62a1.patch
 1459568-62a1.patch
 1459568-62a1.patch
 1457703-2-62a1.patch
 1457703-2-62a1.patch
 1401927-62a1.patch
 1401927-62a1.patch
+1460057-62a1.patch
 1456774-62a1.patch
 1456774-62a1.patch
 1459607-62a1.patch
 1459607-62a1.patch
 1434783-62a1.patch
 1434783-62a1.patch
@@ -4488,6 +4489,8 @@ NOBUG-20180508-hashtable-62a1.patch
 1457499-1-62a1.patch
 1457499-1-62a1.patch
 1457499-2-62a1.patch
 1457499-2-62a1.patch
 1457499-3-62a1.patch
 1457499-3-62a1.patch
+1462261-1-62a1.patch
+1462261-2-62a1.patch
 1450795-2-62a1.patch
 1450795-2-62a1.patch
 1460098-1-62a1.patch
 1460098-1-62a1.patch
 1460098-2-62a1.patch
 1460098-2-62a1.patch
@@ -5115,6 +5118,7 @@ NOBUG-20180629-testingfunctions-63a1.patch
 1473121-63a1.patch
 1473121-63a1.patch
 1472199-1-63a1.patch
 1472199-1-63a1.patch
 1472199-2-63a1.patch
 1472199-2-63a1.patch
+1473024-63a1.patch
 1473308-63a1.patch
 1473308-63a1.patch
 1473954-63a1.patch
 1473954-63a1.patch
 1468207-63a1.patch
 1468207-63a1.patch
@@ -5153,6 +5157,7 @@ NOBUG-20180629-testingfunctions-63a1.patch
 1413922-2-PARTIAL-63a1.patch
 1413922-2-PARTIAL-63a1.patch
 1443471-2-63a1.patch
 1443471-2-63a1.patch
 1474786-63a1.patch
 1474786-63a1.patch
+NOBUG-20180712-typetraits-63a1.patch
 1474871-63a1.patch
 1474871-63a1.patch
 1475067-63a1.patch
 1475067-63a1.patch
 1475058-63a1.patch
 1475058-63a1.patch
@@ -5163,8 +5168,13 @@ NOBUG-20180629-testingfunctions-63a1.patch
 1460489-3-63a1.patch
 1460489-3-63a1.patch
 1460489-4-63a1.patch
 1460489-4-63a1.patch
 1460489-5-63a1.patch
 1460489-5-63a1.patch
+1469044-1-63a1.patch
+1469044-2-63a1.patch
+1469044-3-63a1.patch
 1472291-1-63a1.patch
 1472291-1-63a1.patch
 1472291-2-63a1.patch
 1472291-2-63a1.patch
+1052582-1-63a1.patch
+1052582-2-63a1.patch
 1475504-PARTIAL-63a1.patch
 1475504-PARTIAL-63a1.patch
 1472716-1-63a1.patch
 1472716-1-63a1.patch
 1472490-63a1.patch
 1472490-63a1.patch
@@ -5257,6 +5267,7 @@ NOBUG-20180720-tokenstream-63a1.patch
 1456006-1-63a1.patch
 1456006-1-63a1.patch
 1456006-2-63a1.patch
 1456006-2-63a1.patch
 1475943-63a1.patch
 1475943-63a1.patch
+1309552-63a1.patch
 1323381-63a1.patch
 1323381-63a1.patch
 1477579-1-63a1.patch
 1477579-1-63a1.patch
 1477579-2-63a1.patch
 1477579-2-63a1.patch
@@ -5338,13 +5349,19 @@ NOBUG-20180720-tokenstream-63a1.patch
 1478587-10-63a1.patch
 1478587-10-63a1.patch
 1478892-1-63a1.patch
 1478892-1-63a1.patch
 1477157-63a1.patch
 1477157-63a1.patch
-1477090-63a1.patch
-1469004-63a1.patch
-1480720-63a1.patch
+1468524-63a1.patch
+1478982-63a1.patch
 1426176-63a1.patch
 1426176-63a1.patch
 1476034-63a1.patch
 1476034-63a1.patch
 1478499-1-63a1.patch
 1478499-1-63a1.patch
 1478499-3no2-63a1.patch
 1478499-3no2-63a1.patch
+1475228-1-63a1.patch
+1475228-2-63a1.patch
+1475228-3-63a1.patch
+1475228-4-63a1.patch
+1475228-5-63a1.patch
+1475228-6-63a1.patch
+1475228-7-63a1.patch
 1478499-4-63a1.patch
 1478499-4-63a1.patch
 1478499-5no6-63a1.patch
 1478499-5no6-63a1.patch
 1479851-63a1.patch
 1479851-63a1.patch
@@ -5377,6 +5394,12 @@ NOBUG-20180720-tokenstream-63a1.patch
 1477621-2-63a1.patch
 1477621-2-63a1.patch
 1477621-3-63a1.patch
 1477621-3-63a1.patch
 1477621-4-63a1.patch
 1477621-4-63a1.patch
+1481005-63a1.patch
+NOBUG-20180809-utf8-63a1.patch
+1477090-63a1.patch
+1469004-63a1.patch
+1480720-63a1.patch
+1480966-63a1.patch
 1480493-63a1.patch
 1480493-63a1.patch
 1481248-63a1.patch
 1481248-63a1.patch
 1456404-1-63a1.patch
 1456404-1-63a1.patch
@@ -5422,6 +5445,7 @@ NOBUG-20180720-tokenstream-63a1.patch
 1481097-4-63a1.patch
 1481097-4-63a1.patch
 1482694-63a1.patch
 1482694-63a1.patch
 1479501-63a1.patch
 1479501-63a1.patch
+1479793-63a1.patch
 1478813-1-63a1.patch
 1478813-1-63a1.patch
 1482676-63a1.patch
 1482676-63a1.patch
 1478798-63a1.patch
 1478798-63a1.patch
@@ -5429,10 +5453,14 @@ NOBUG-20180720-tokenstream-63a1.patch
 1482841-2-63a1.patch
 1482841-2-63a1.patch
 1482605-63a1.patch
 1482605-63a1.patch
 1476203-63a1.patch
 1476203-63a1.patch
+1479673-1-63a1.patch
+1479673-2-63a1.patch
 1469287-63a1.patch
 1469287-63a1.patch
 1481441-63a1.patch
 1481441-63a1.patch
 1482695-63a1.patch
 1482695-63a1.patch
 1478910-63a1.patch
 1478910-63a1.patch
+1482931-1-63a1.patch
+1482931-2-63a1.patch
 1478892-2-63a1.patch
 1478892-2-63a1.patch
 1478892-3-63a1.patch
 1478892-3-63a1.patch
 1479878-63a1.patch
 1479878-63a1.patch
@@ -5462,6 +5490,7 @@ NOBUG-20180720-tokenstream-63a1.patch
 1465046-63a1.patch
 1465046-63a1.patch
 1483937-63a1.patch
 1483937-63a1.patch
 1471371-63a1.patch
 1471371-63a1.patch
+1440648-63a1.patch
 1473228-63a1.patch
 1473228-63a1.patch
 1480558-1-63a1.patch
 1480558-1-63a1.patch
 1480558-2-63a1.patch
 1480558-2-63a1.patch
@@ -5484,10 +5513,14 @@ NOBUG-20180720-tokenstream-63a1.patch
 1484535-63a1.patch
 1484535-63a1.patch
 1484485-63a1.patch
 1484485-63a1.patch
 1470921-63a1.patch
 1470921-63a1.patch
+1483374-63a1.patch
 1040316-63a1.patch
 1040316-63a1.patch
+1484385-63a1.patch
 1484389-63a1.patch
 1484389-63a1.patch
 1033916-1-63a1.patch
 1033916-1-63a1.patch
 1033916-2-63a1.patch
 1033916-2-63a1.patch
+1484421-63a1.patch
+1484420-63a1.patch
 1484243-63a1.patch
 1484243-63a1.patch
 1485245-63a1.patch
 1485245-63a1.patch
 1473230-63a1.patch
 1473230-63a1.patch
@@ -5495,6 +5528,7 @@ NOBUG-20180720-tokenstream-63a1.patch
 1483761-2-63a1.patch
 1483761-2-63a1.patch
 1483761-3-63a1.patch
 1483761-3-63a1.patch
 1483761-4-63a1.patch
 1483761-4-63a1.patch
+1484943-63a1.patch
 1485545-63a1.patch
 1485545-63a1.patch
 1485610-63a1.patch
 1485610-63a1.patch
 1454285-3-63a1.patch
 1454285-3-63a1.patch
@@ -5527,6 +5561,7 @@ NOBUG-20180824-buildsetting-63a1.patch
 1485759-63a1.patch
 1485759-63a1.patch
 1485347-1-63a1.patch
 1485347-1-63a1.patch
 1485347-2-63a1.patch
 1485347-2-63a1.patch
+1486444-63a1.patch
 1486729-63a1.patch
 1486729-63a1.patch
 1486957-63a1.patch
 1486957-63a1.patch
 1457092-5-63a1.patch
 1457092-5-63a1.patch
@@ -5541,6 +5576,7 @@ NOBUG-20180824-buildsetting-63a1.patch
 1486905-63a1.patch
 1486905-63a1.patch
 1485698-63a1.patch
 1485698-63a1.patch
 1486730-63a1.patch
 1486730-63a1.patch
+1476921-63a1.patch
 1487517-63a1.patch
 1487517-63a1.patch
 1480457-1-63a1.patch
 1480457-1-63a1.patch
 1480457-4-63a1.patch
 1480457-4-63a1.patch
@@ -5561,6 +5597,7 @@ NOBUG-20180824-buildsetting-63a1.patch
 1417646-1-64a1.patch
 1417646-1-64a1.patch
 1487419-64a1.patch
 1487419-64a1.patch
 1488698-2-PARTIAL-biginttype-64a1.patch
 1488698-2-PARTIAL-biginttype-64a1.patch
+1488698-7-WIP-64a1.patch
 1489944-64a1.patch
 1489944-64a1.patch
 1493227-64a1.patch
 1493227-64a1.patch
 1467523-2-64a1.patch
 1467523-2-64a1.patch
@@ -7943,3 +7980,4 @@ TOP-NOBUG-REGEXP-47-fixes-25319.patch
 1881093-11509.patch
 1881093-11509.patch
 TOP-1880562-NSS3902-11509.patch
 TOP-1880562-NSS3902-11509.patch
 NOBUG-JSFIXUPS-25319.patch
 NOBUG-JSFIXUPS-25319.patch
+1462261-3-25319.patch