Bug 1366287 - Part 1.0: Define a new BigInt primitive type, with a GDB prettyprinter, Rust binding support, and a new out-of-line TraceKind. (Disabled by default, implemented only incompletely, currently passing --enable-bigint will disable JITs, will be flipped on Eventually once every sub-aspect is in place, Don't Have A Cow, Man.) r=jwalden, r=Ms2ger, r=sfink
authorRobin Templeton <robin@igalia.com>
Thu, 24 May 2018 11:26:09 -0700
changeset 474010 bbe538384ae8b61c0af775305facc392f172fd94
parent 474009 e04fb853606318beafed6177faaf0aaa7fcd3503
child 474011 06507a170eac601ca2c0c2e6d9d1294bac053dd2
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden, Ms2ger, sfink
bugs1366287
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1366287 - Part 1.0: Define a new BigInt primitive type, with a GDB prettyprinter, Rust binding support, and a new out-of-line TraceKind. (Disabled by default, implemented only incompletely, currently passing --enable-bigint will disable JITs, will be flipped on Eventually once every sub-aspect is in place, Don't Have A Cow, Man.) r=jwalden, r=Ms2ger, r=sfink
js/moz.configure
js/public/Conversions.h
js/public/GCPolicyAPI.h
js/public/MemoryMetrics.h
js/public/TraceKind.h
js/public/TracingAPI.h
js/public/TypeDecls.h
js/public/UbiNode.h
js/public/Value.h
js/rust/Cargo.toml
js/rust/build.rs
js/rust/src/jsval.rs
js/rust/src/rust.rs
js/src/Cargo.toml
js/src/NamespaceImports.h
js/src/build.rs
js/src/builtin/Array.cpp
js/src/builtin/Boolean.cpp
js/src/builtin/JSON.cpp
js/src/builtin/MapObject.cpp
js/src/devtools/automation/variants/bigint
js/src/devtools/automation/variants/bigintdebug
js/src/gc/AllocKind.h
js/src/gc/AtomMarking.cpp
js/src/gc/DeletePolicy.h
js/src/gc/GC.cpp
js/src/gc/Marking-inl.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/Tracer.cpp
js/src/gdb/lib-for-tests/prologue.py
js/src/gdb/mozilla/jsval.py
js/src/gdb/tests/test-jsval.cpp
js/src/gdb/tests/test-jsval.py
js/src/js.msg
js/src/jsnum.cpp
js/src/jspubtd.h
js/src/moz.build
js/src/util/StringBuffer.cpp
js/src/vm/BigIntType.cpp
js/src/vm/BigIntType.h
js/src/vm/CommonPropertyNames.h
js/src/vm/Interpreter.cpp
js/src/vm/JSAtom.cpp
js/src/vm/JSCompartment-inl.h
js/src/vm/JSCompartment.cpp
js/src/vm/JSCompartment.h
js/src/vm/JSObject.cpp
js/src/vm/MemoryMetrics.cpp
js/src/vm/StringType.cpp
js/src/vm/TypeInference-inl.h
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
js/src/vm/UbiNode.cpp
--- a/js/moz.configure
+++ b/js/moz.configure
@@ -97,23 +97,39 @@ def disable_export_js(value):
     if value and not len(value):
         suggestion = '--disable-export-js'
     else:
         suggestion = '--enable-export-js'
 
     die('Setting %s is deprecated, use %s instead.',
         value.format('DISABLE_EXPORT_JS'), suggestion)
 
+# Experimental BigInt support
+# =======================================================
+js_option('--enable-bigint',
+          default=False,
+          help='Enable BigInt')
+
+@depends('--enable-bigint')
+def enable_bigint(value):
+    if value:
+        return True
+
+set_config('ENABLE_BIGINT', enable_bigint)
+set_define('ENABLE_BIGINT', enable_bigint)
 
 # JIT support
 # =======================================================
-@depends(target)
-def ion_default(target):
+@depends(target, '--enable-bigint')
+def ion_default(target, enable_bigint):
+    if enable_bigint:
+        return False
     if target.cpu in ('x86', 'x86_64', 'arm', 'aarch64', 'mips32', 'mips64'):
         return True
+    return False
 
 js_option('--enable-ion',
           default=ion_default,
           help='Enable use of the IonMonkey JIT')
 
 set_config('ENABLE_ION', depends_if('--enable-ion')(lambda x: True))
 
 # JIT code simulator for cross compiles
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -117,17 +117,17 @@ ToBoolean(HandleValue v)
         return false;
     if (v.isDouble()) {
         double d = v.toDouble();
         return !mozilla::IsNaN(d) && d != 0;
     }
     if (v.isSymbol())
         return true;
 
-    /* The slow path handles strings and objects. */
+    /* The slow path handles strings, BigInts and objects. */
     return js::ToBooleanSlow(v);
 }
 
 /* ES6 draft 20141224, 7.1.3. */
 MOZ_ALWAYS_INLINE bool
 ToNumber(JSContext* cx, HandleValue v, double* out)
 {
     detail::AssertArgumentsAreSane(cx, v);
--- a/js/public/GCPolicyAPI.h
+++ b/js/public/GCPolicyAPI.h
@@ -45,16 +45,17 @@
 
 #include "js/TraceKind.h"
 #include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
 
 // Expand the given macro D for each public GC pointer.
 #define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
     D(JS::Symbol*) \
+    IF_BIGINT(D(JS::BigInt*),) \
     D(JSAtom*) \
     D(JSFunction*) \
     D(JSObject*) \
     D(JSScript*) \
     D(JSString*)
 
 // Expand the given macro D for each public tagged GC pointer type.
 #define FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
@@ -122,16 +123,19 @@ struct GCPointerPolicy
             return js::gc::IsAboutToBeFinalizedUnbarriered(vp);
         return false;
     }
     static bool isValid(T v) {
         return js::gc::IsCellPointerValidOrNull(v);
     }
 };
 template <> struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
+#ifdef ENABLE_BIGINT
+template <> struct GCPolicy<JS::BigInt*> : public GCPointerPolicy<JS::BigInt*> {};
+#endif
 template <> struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
 template <> struct GCPolicy<JSFunction*> : public GCPointerPolicy<JSFunction*> {};
 template <> struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
 template <> struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
 template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
 
 template <typename T>
 struct NonGCPointerPolicy
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -624,16 +624,17 @@ struct UnusedGCThingSizes
     macro(Other, GCHeapUnused, object) \
     macro(Other, GCHeapUnused, script) \
     macro(Other, GCHeapUnused, lazyScript) \
     macro(Other, GCHeapUnused, shape) \
     macro(Other, GCHeapUnused, baseShape) \
     macro(Other, GCHeapUnused, objectGroup) \
     macro(Other, GCHeapUnused, string) \
     macro(Other, GCHeapUnused, symbol) \
+    IF_BIGINT(macro(Other, GCHeapUnused, bigInt),) \
     macro(Other, GCHeapUnused, jitcode) \
     macro(Other, GCHeapUnused, scope) \
     macro(Other, GCHeapUnused, regExpShared)
 
     UnusedGCThingSizes()
       : FOR_EACH_SIZE(ZERO_SIZE)
         dummy()
     {}
@@ -643,16 +644,19 @@ struct UnusedGCThingSizes
         dummy()
     {}
 
     void addToKind(JS::TraceKind kind, intptr_t n) {
         switch (kind) {
           case JS::TraceKind::Object:       object += n;       break;
           case JS::TraceKind::String:       string += n;       break;
           case JS::TraceKind::Symbol:       symbol += n;       break;
+#ifdef ENABLE_BIGINT
+          case JS::TraceKind::BigInt:       bigInt += n;       break;
+#endif
           case JS::TraceKind::Script:       script += n;       break;
           case JS::TraceKind::Shape:        shape += n;        break;
           case JS::TraceKind::BaseShape:    baseShape += n;    break;
           case JS::TraceKind::JitCode:      jitcode += n;      break;
           case JS::TraceKind::LazyScript:   lazyScript += n;   break;
           case JS::TraceKind::ObjectGroup:  objectGroup += n;  break;
           case JS::TraceKind::Scope:        scope += n;        break;
           case JS::TraceKind::RegExpShared: regExpShared += n; break;
@@ -684,16 +688,18 @@ struct UnusedGCThingSizes
 
 #undef FOR_EACH_SIZE
 };
 
 struct ZoneStats
 {
 #define FOR_EACH_SIZE(macro) \
     macro(Other,   GCHeapUsed,  symbolsGCHeap) \
+    IF_BIGINT(macro(Other,   GCHeapUsed,  bigIntsGCHeap),) \
+    IF_BIGINT(macro(Other,   MallocHeap,  bigIntsMallocHeap),) \
     macro(Other,   GCHeapAdmin, gcHeapArenaAdmin) \
     macro(Other,   GCHeapUsed,  lazyScriptsGCHeap) \
     macro(Other,   MallocHeap,  lazyScriptsMallocHeap) \
     macro(Other,   GCHeapUsed,  jitCodesGCHeap) \
     macro(Other,   GCHeapUsed,  objectGroupsGCHeap) \
     macro(Other,   MallocHeap,  objectGroupsMallocHeap) \
     macro(Other,   GCHeapUsed,  scopesGCHeap) \
     macro(Other,   MallocHeap,  scopesMallocHeap) \
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -56,17 +56,20 @@ enum class TraceKind
     // The kind associated with a nullptr.
     Null = 0x06,
 
     // The following kinds do not have an exposed C++ idiom.
     BaseShape = 0x0F,
     JitCode = 0x1F,
     LazyScript = 0x2F,
     Scope = 0x3F,
-    RegExpShared = 0x4F
+    RegExpShared = 0x4F,
+#ifdef ENABLE_BIGINT
+    BigInt = 0x5F
+#endif
 };
 const static uintptr_t OutOfLineTraceKindMask = 0x07;
 
 #define ASSERT_TRACE_KIND(tk) \
     static_assert((uintptr_t(tk) & OutOfLineTraceKindMask) == OutOfLineTraceKindMask, \
         "mask bits are set")
 ASSERT_TRACE_KIND(JS::TraceKind::BaseShape);
 ASSERT_TRACE_KIND(JS::TraceKind::JitCode);
@@ -92,16 +95,17 @@ struct MapTypeToTraceKind {
     D(LazyScript,    js::LazyScript,    true) \
     D(Scope,         js::Scope,         true) \
     D(Object,        JSObject,          true) \
     D(ObjectGroup,   js::ObjectGroup,   true) \
     D(Script,        JSScript,          true) \
     D(Shape,         js::Shape,         true) \
     D(String,        JSString,          false) \
     D(Symbol,        JS::Symbol,        false) \
+    IF_BIGINT(D(BigInt, JS::BigInt, false),) \
     D(RegExpShared,  js::RegExpShared,  true)
 
 // Map from all public types to their trace kind.
 #define JS_EXPAND_DEF(name, type, _) \
     template <> struct MapTypeToTraceKind<type> { \
         static const JS::TraceKind kind = JS::TraceKind::name; \
     };
 JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -146,16 +146,19 @@ class JS_PUBLIC_API(CallbackTracer) : pu
     // Override these methods to receive notification when an edge is visited
     // with the type contained in the callback. The default implementation
     // dispatches to the fully-generic onChild implementation, so for cases that
     // do not care about boxing overhead and do not need the actual edges,
     // just override the generic onChild.
     virtual void onObjectEdge(JSObject** objp) { onChild(JS::GCCellPtr(*objp)); }
     virtual void onStringEdge(JSString** strp) { onChild(JS::GCCellPtr(*strp)); }
     virtual void onSymbolEdge(JS::Symbol** symp) { onChild(JS::GCCellPtr(*symp)); }
+#ifdef ENABLE_BIGINT
+    virtual void onBigIntEdge(JS::BigInt** bip) { onChild(JS::GCCellPtr(*bip)); }
+#endif
     virtual void onScriptEdge(JSScript** scriptp) { onChild(JS::GCCellPtr(*scriptp)); }
     virtual void onShapeEdge(js::Shape** shapep) {
         onChild(JS::GCCellPtr(*shapep, JS::TraceKind::Shape));
     }
     virtual void onObjectGroupEdge(js::ObjectGroup** groupp) {
         onChild(JS::GCCellPtr(*groupp, JS::TraceKind::ObjectGroup));
     }
     virtual void onBaseShapeEdge(js::BaseShape** basep) {
@@ -238,16 +241,19 @@ class JS_PUBLIC_API(CallbackTracer) : pu
     // In C++, overriding a method hides all methods in the base class with
     // that name, not just methods with that signature. Thus, the typed edge
     // methods have to have distinct names to allow us to override them
     // individually, which is freqently useful if, for example, we only want to
     // process only one type of edge.
     void dispatchToOnEdge(JSObject** objp) { onObjectEdge(objp); }
     void dispatchToOnEdge(JSString** strp) { onStringEdge(strp); }
     void dispatchToOnEdge(JS::Symbol** symp) { onSymbolEdge(symp); }
+#ifdef ENABLE_BIGINT
+    void dispatchToOnEdge(JS::BigInt** bip) { onBigIntEdge(bip); }
+#endif
     void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); }
     void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); }
     void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); }
     void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); }
     void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); }
     void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); }
     void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); }
     void dispatchToOnEdge(js::RegExpShared** sharedp) { onRegExpSharedEdge(sharedp); }
--- a/js/public/TypeDecls.h
+++ b/js/public/TypeDecls.h
@@ -36,53 +36,74 @@ struct JSFreeOp;
 
 struct jsid;
 
 namespace JS {
 
 typedef unsigned char Latin1Char;
 
 class Symbol;
+#ifdef ENABLE_BIGINT
+class BigInt;
+#endif
 union Value;
 class Realm;
 struct Runtime;
 struct Zone;
 
 template <typename T> class Handle;
 template <typename T> class MutableHandle;
 template <typename T> class Rooted;
 template <typename T> class PersistentRooted;
 
 typedef Handle<JSFunction*> HandleFunction;
 typedef Handle<jsid>        HandleId;
 typedef Handle<JSObject*>   HandleObject;
 typedef Handle<JSScript*>   HandleScript;
 typedef Handle<JSString*>   HandleString;
 typedef Handle<JS::Symbol*> HandleSymbol;
+#ifdef ENABLE_BIGINT
+typedef Handle<JS::BigInt*> HandleBigInt;
+#endif
 typedef Handle<Value>       HandleValue;
 
 typedef MutableHandle<JSFunction*> MutableHandleFunction;
 typedef MutableHandle<jsid>        MutableHandleId;
 typedef MutableHandle<JSObject*>   MutableHandleObject;
 typedef MutableHandle<JSScript*>   MutableHandleScript;
 typedef MutableHandle<JSString*>   MutableHandleString;
 typedef MutableHandle<JS::Symbol*> MutableHandleSymbol;
+#ifdef ENABLE_BIGINT
+typedef MutableHandle<JS::BigInt*> MutableHandleBigInt;
+#endif
 typedef MutableHandle<Value>       MutableHandleValue;
 
 typedef Rooted<JSObject*>       RootedObject;
 typedef Rooted<JSFunction*>     RootedFunction;
 typedef Rooted<JSScript*>       RootedScript;
 typedef Rooted<JSString*>       RootedString;
 typedef Rooted<JS::Symbol*>     RootedSymbol;
+#ifdef ENABLE_BIGINT
+typedef Rooted<JS::BigInt*>     RootedBigInt;
+#endif
 typedef Rooted<jsid>            RootedId;
 typedef Rooted<JS::Value>       RootedValue;
 
 typedef PersistentRooted<JSFunction*> PersistentRootedFunction;
 typedef PersistentRooted<jsid>        PersistentRootedId;
 typedef PersistentRooted<JSObject*>   PersistentRootedObject;
 typedef PersistentRooted<JSScript*>   PersistentRootedScript;
 typedef PersistentRooted<JSString*>   PersistentRootedString;
 typedef PersistentRooted<JS::Symbol*> PersistentRootedSymbol;
+#ifdef ENABLE_BIGINT
+typedef PersistentRooted<JS::BigInt*> PersistentRootedBigInt;
+#endif
 typedef PersistentRooted<Value>       PersistentRootedValue;
 
 } // namespace JS
 
+#ifdef ENABLE_BIGINT
+#define IF_BIGINT(x, y) x
+#else
+#define IF_BIGINT(x, y) y
+#endif
+
 #endif /* js_TypeDecls_h */
--- a/js/public/UbiNode.h
+++ b/js/public/UbiNode.h
@@ -1051,16 +1051,34 @@ class JS_PUBLIC_API(Concrete<JS::Symbol>
     }
 
     Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
 
     const char16_t* typeName() const override { return concreteTypeName; }
     static const char16_t concreteTypeName[];
 };
 
+#ifdef ENABLE_BIGINT
+template<>
+class JS_PUBLIC_API(Concrete<JS::BigInt>) : TracerConcrete<JS::BigInt> {
+  protected:
+    explicit Concrete(JS::BigInt* ptr) : TracerConcrete(ptr) {}
+
+  public:
+    static void construct(void* storage, JS::BigInt* ptr) {
+        new (storage) Concrete(ptr);
+    }
+
+    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+    const char16_t* typeName() const override { return concreteTypeName; }
+    static const char16_t concreteTypeName[];
+};
+#endif
+
 template<>
 class JS_PUBLIC_API(Concrete<JSScript>) : TracerConcreteWithCompartment<JSScript> {
   protected:
     explicit Concrete(JSScript *ptr) : TracerConcreteWithCompartment<JSScript>(ptr) { }
 
   public:
     static void construct(void *storage, JSScript *ptr) { new (storage) Concrete(ptr); }
 
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -54,16 +54,19 @@ enum JSValueType : uint8_t
     JSVAL_TYPE_INT32               = 0x01,
     JSVAL_TYPE_BOOLEAN             = 0x02,
     JSVAL_TYPE_UNDEFINED           = 0x03,
     JSVAL_TYPE_NULL                = 0x04,
     JSVAL_TYPE_MAGIC               = 0x05,
     JSVAL_TYPE_STRING              = 0x06,
     JSVAL_TYPE_SYMBOL              = 0x07,
     JSVAL_TYPE_PRIVATE_GCTHING     = 0x08,
+#ifdef ENABLE_BIGINT
+    JSVAL_TYPE_BIGINT              = 0x09,
+#endif
     JSVAL_TYPE_OBJECT              = 0x0c,
 
     /* These never appear in a jsval; they are only provided as an out-of-band value. */
     JSVAL_TYPE_UNKNOWN             = 0x20,
     JSVAL_TYPE_MISSING             = 0x21
 };
 
 static_assert(sizeof(JSValueType) == 1,
@@ -77,16 +80,19 @@ JS_ENUM_HEADER(JSValueTag, uint32_t)
     JSVAL_TAG_INT32                = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
     JSVAL_TAG_UNDEFINED            = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED,
     JSVAL_TAG_NULL                 = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
     JSVAL_TAG_BOOLEAN              = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
     JSVAL_TAG_MAGIC                = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
     JSVAL_TAG_STRING               = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
     JSVAL_TAG_SYMBOL               = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
     JSVAL_TAG_PRIVATE_GCTHING      = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING,
+#ifdef ENABLE_BIGINT
+    JSVAL_TAG_BIGINT               = JSVAL_TAG_CLEAR | JSVAL_TYPE_BIGINT,
+#endif
     JSVAL_TAG_OBJECT               = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT
 } JS_ENUM_FOOTER(JSValueTag);
 
 static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
               "compiler typed enum support is apparently buggy");
 
 #elif defined(JS_PUNBOX64)
 
@@ -96,16 +102,19 @@ JS_ENUM_HEADER(JSValueTag, uint32_t)
     JSVAL_TAG_INT32                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
     JSVAL_TAG_UNDEFINED            = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED,
     JSVAL_TAG_NULL                 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
     JSVAL_TAG_BOOLEAN              = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
     JSVAL_TAG_MAGIC                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
     JSVAL_TAG_STRING               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
     JSVAL_TAG_SYMBOL               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
     JSVAL_TAG_PRIVATE_GCTHING      = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING,
+#ifdef ENABLE_BIGINT
+    JSVAL_TAG_BIGINT               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BIGINT,
+#endif
     JSVAL_TAG_OBJECT               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT
 } JS_ENUM_FOOTER(JSValueTag);
 
 static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
               "compiler typed enum support is apparently buggy");
 
 enum JSValueShiftedTag : uint64_t
 {
@@ -113,16 +122,19 @@ enum JSValueShiftedTag : uint64_t
     JSVAL_SHIFTED_TAG_INT32           = (((uint64_t)JSVAL_TAG_INT32)           << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_UNDEFINED       = (((uint64_t)JSVAL_TAG_UNDEFINED)       << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_NULL            = (((uint64_t)JSVAL_TAG_NULL)            << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_BOOLEAN         = (((uint64_t)JSVAL_TAG_BOOLEAN)         << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_MAGIC           = (((uint64_t)JSVAL_TAG_MAGIC)           << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_STRING          = (((uint64_t)JSVAL_TAG_STRING)          << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_SYMBOL          = (((uint64_t)JSVAL_TAG_SYMBOL)          << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT),
+#ifdef ENABLE_BIGINT
+    JSVAL_SHIFTED_TAG_BIGINT          = (((uint64_t)JSVAL_TAG_BIGINT)          << JSVAL_TAG_SHIFT),
+#endif
     JSVAL_SHIFTED_TAG_OBJECT          = (((uint64_t)JSVAL_TAG_OBJECT)          << JSVAL_TAG_SHIFT)
 };
 
 static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
               "compiler typed enum support is apparently buggy");
 
 #endif
 
@@ -256,17 +268,17 @@ CanonicalizeNaN(double d)
 }
 
 /**
  * JS::Value is the interface for a single JavaScript Engine value.  A few
  * general notes on JS::Value:
  *
  * - JS::Value has setX() and isX() members for X in
  *
- *     { Int32, Double, String, Symbol, Boolean, Undefined, Null, Object, Magic }
+ *     { Int32, Double, String, Symbol, BigInt, Boolean, Undefined, Null, Object, Magic }
  *
  *   JS::Value also contains toX() for each of the non-singleton types.
  *
  * - Magic is a singleton type whose payload contains either a JSWhyMagic "reason" for
  *   the magic value or a uint32_t value. By providing JSWhyMagic values when
  *   creating and checking for magic values, it is possible to assert, at
  *   runtime, that only magic values with the expected reason flow through a
  *   particular value. For example, if cx->exception has a magic value, the
@@ -338,16 +350,19 @@ union MOZ_NON_PARAM alignas(8) Value
         JSValueTag tag_;
 #  endif // MOZ_BIG_ENDIAN
         union {
             int32_t i32_;
             uint32_t u32_;
             uint32_t  boo_;     // Don't use |bool| -- it must be four bytes.
             JSString* str_;
             JS::Symbol* sym_;
+#ifdef ENABLE_BIGINT
+            JS::BigInt* bi_;
+#endif
             JSObject* obj_;
             js::gc::Cell* cell_;
             void* ptr_;
             JSWhyMagic why_;
         } payload_;
 #  if MOZ_LITTLE_ENDIAN
         JSValueTag tag_;
 #  endif // MOZ_LITTLE_ENDIAN
@@ -449,16 +464,23 @@ union MOZ_NON_PARAM alignas(8) Value
         asBits_ = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str));
     }
 
     void setSymbol(JS::Symbol* sym) {
         MOZ_ASSERT(js::gc::IsCellPointerValid(sym));
         asBits_ = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym));
     }
 
+#ifdef ENABLE_BIGINT
+    void setBigInt(JS::BigInt* bi) {
+        MOZ_ASSERT(js::gc::IsCellPointerValid(bi));
+        asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BIGINT, PayloadType(bi));
+    }
+#endif
+
     void setObject(JSObject& obj) {
         MOZ_ASSERT(js::gc::IsCellPointerValid(&obj));
 
         // This should not be possible and is undefined behavior, but some
         // ObjectValue(nullptr) are sneaking in. Try to catch them here, if
         // indeed they are going through this code. I tested gcc, and it at
         // least will *not* elide the null check even though it would be
         // permitted according to the spec. The temporary is necessary to
@@ -615,16 +637,22 @@ union MOZ_NON_PARAM alignas(8) Value
     bool isString() const {
         return toTag() == JSVAL_TAG_STRING;
     }
 
     bool isSymbol() const {
         return toTag() == JSVAL_TAG_SYMBOL;
     }
 
+#ifdef ENABLE_BIGINT
+    bool isBigInt() const {
+        return toTag() == JSVAL_TAG_BIGINT;
+    }
+#endif
+
     bool isObject() const {
 #if defined(JS_NUNBOX32)
         return toTag() == JSVAL_TAG_OBJECT;
 #elif defined(JS_PUNBOX64)
         MOZ_ASSERT((asBits_ >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
         return asBits_ >= JSVAL_SHIFTED_TAG_OBJECT;
 #endif
     }
@@ -676,16 +704,20 @@ union MOZ_NON_PARAM alignas(8) Value
         static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
                       "Value type tags must correspond with JS::TraceKinds.");
         static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
                       "Value type tags must correspond with JS::TraceKinds.");
         static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
                       "Value type tags must correspond with JS::TraceKinds.");
         if (MOZ_UNLIKELY(isPrivateGCThing()))
             return JS::GCThingTraceKind(toGCThing());
+#ifdef ENABLE_BIGINT
+        if (MOZ_UNLIKELY(isBigInt()))
+            return JS::TraceKind::BigInt;
+#endif
         return JS::TraceKind(toTag() & 0x03);
     }
 
     JSWhyMagic whyMagic() const {
         MOZ_ASSERT(isMagic());
         return s_.payload_.why_;
     }
 
@@ -740,16 +772,27 @@ union MOZ_NON_PARAM alignas(8) Value
         MOZ_ASSERT(isSymbol());
 #if defined(JS_NUNBOX32)
         return s_.payload_.sym_;
 #elif defined(JS_PUNBOX64)
         return reinterpret_cast<JS::Symbol*>(asBits_ ^ JSVAL_SHIFTED_TAG_SYMBOL);
 #endif
     }
 
+#ifdef ENABLE_BIGINT
+    JS::BigInt* toBigInt() const {
+        MOZ_ASSERT(isBigInt());
+#if defined(JS_NUNBOX32)
+        return s_.payload_.bi_;
+#elif defined(JS_PUNBOX64)
+        return reinterpret_cast<JS::BigInt*>(asBits_ ^ JSVAL_SHIFTED_TAG_BIGINT);
+#endif
+    }
+#endif
+
     JSObject& toObject() const {
         MOZ_ASSERT(isObject());
 #if defined(JS_NUNBOX32)
         return *s_.payload_.obj_;
 #elif defined(JS_PUNBOX64)
         uint64_t ptrBits = asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT;
         MOZ_ASSERT(ptrBits);
         MOZ_ASSERT((ptrBits & 0x7) == 0);
@@ -857,16 +900,20 @@ union MOZ_NON_PARAM alignas(8) Value
      * cells.
      */
 
     void setPrivateGCThing(js::gc::Cell* cell) {
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
                    "Private GC thing Values must not be strings. Make a StringValue instead.");
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
                    "Private GC thing Values must not be symbols. Make a SymbolValue instead.");
+#ifdef ENABLE_BIGINT
+        MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::BigInt,
+                   "Private GC thing Values must not be BigInts. Make a BigIntValue instead.");
+#endif
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
                    "Private GC thing Values must not be objects. Make an ObjectValue instead.");
 
         MOZ_ASSERT(js::gc::IsCellPointerValid(cell));
 #if defined(JS_PUNBOX64)
         // VisualStudio cannot contain parenthesized C++ style cast and shift
         // inside decltype in template parameter:
         //   AssertionConditionType<decltype((uintptr_t(x) >> 1))>
@@ -982,16 +1029,26 @@ StringValue(JSString* str)
 static inline Value
 SymbolValue(JS::Symbol* sym)
 {
     Value v;
     v.setSymbol(sym);
     return v;
 }
 
+#ifdef ENABLE_BIGINT
+static inline Value
+BigIntValue(JS::BigInt* bi)
+{
+    Value v;
+    v.setBigInt(bi);
+    return v;
+}
+#endif
+
 static inline Value
 BooleanValue(bool boo)
 {
     Value v;
     v.setBoolean(boo);
     return v;
 }
 
@@ -1237,31 +1294,37 @@ class WrappedPtrOperations<JS::Value, Wr
     bool isTrue() const { return value().isTrue(); }
     bool isFalse() const { return value().isFalse(); }
     bool isNumber() const { return value().isNumber(); }
     bool isInt32() const { return value().isInt32(); }
     bool isInt32(int32_t i32) const { return value().isInt32(i32); }
     bool isDouble() const { return value().isDouble(); }
     bool isString() const { return value().isString(); }
     bool isSymbol() const { return value().isSymbol(); }
+#ifdef ENABLE_BIGINT
+    bool isBigInt() const { return value().isBigInt(); }
+#endif
     bool isObject() const { return value().isObject(); }
     bool isMagic() const { return value().isMagic(); }
     bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
     bool isGCThing() const { return value().isGCThing(); }
     bool isPrimitive() const { return value().isPrimitive(); }
 
     bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
     bool isObjectOrNull() const { return value().isObjectOrNull(); }
 
     bool toBoolean() const { return value().toBoolean(); }
     double toNumber() const { return value().toNumber(); }
     int32_t toInt32() const { return value().toInt32(); }
     double toDouble() const { return value().toDouble(); }
     JSString* toString() const { return value().toString(); }
     JS::Symbol* toSymbol() const { return value().toSymbol(); }
+#ifdef ENABLE_BIGINT
+    JS::BigInt* toBigInt() const { return value().toBigInt(); }
+#endif
     JSObject& toObject() const { return value().toObject(); }
     JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
     gc::Cell* toGCThing() const { return value().toGCThing(); }
     JS::TraceKind traceKind() const { return value().traceKind(); }
     void* toPrivate() const { return value().toPrivate(); }
     uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
 
     uint64_t asRawBits() const { return value().asRawBits(); }
@@ -1289,16 +1352,19 @@ class MutableWrappedPtrOperations<JS::Va
     void setDouble(double d) { value().setDouble(d); }
     void setNaN() { setDouble(JS::GenericNaN()); }
     void setBoolean(bool b) { value().setBoolean(b); }
     void setMagic(JSWhyMagic why) { value().setMagic(why); }
     bool setNumber(uint32_t ui) { return value().setNumber(ui); }
     bool setNumber(double d) { return value().setNumber(d); }
     void setString(JSString* str) { this->value().setString(str); }
     void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); }
+#ifdef ENABLE_BIGINT
+    void setBigInt(JS::BigInt* bi) { this->value().setBigInt(bi); }
+#endif
     void setObject(JSObject& obj) { this->value().setObject(obj); }
     void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); }
     void setPrivate(void* ptr) { this->value().setPrivate(ptr); }
     void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); }
     void setPrivateGCThing(js::gc::Cell* cell) { this->value().setPrivateGCThing(cell); }
 };
 
 /*
@@ -1317,16 +1383,19 @@ class HeapBase<JS::Value, Wrapper> : pub
     void setUndefined() { setBarriered(JS::UndefinedValue()); }
     void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); }
     void setDouble(double d) { setBarriered(JS::DoubleValue(d)); }
     void setNaN() { setDouble(JS::GenericNaN()); }
     void setBoolean(bool b) { setBarriered(JS::BooleanValue(b)); }
     void setMagic(JSWhyMagic why) { setBarriered(JS::MagicValue(why)); }
     void setString(JSString* str) { setBarriered(JS::StringValue(str)); }
     void setSymbol(JS::Symbol* sym) { setBarriered(JS::SymbolValue(sym)); }
+#ifdef ENABLE_BIGINT
+    void setBigInt(JS::BigInt* bi) { setBarriered(JS::BigIntValue(bi)); }
+#endif
     void setObject(JSObject& obj) { setBarriered(JS::ObjectValue(obj)); }
     void setPrivateGCThing(js::gc::Cell* cell) { setBarriered(JS::PrivateGCThingValue(cell)); }
 
     bool setNumber(uint32_t ui) {
         if (ui > JSVAL_INT_MAX) {
             setDouble((double)ui);
             return false;
         } else {
@@ -1373,16 +1442,23 @@ DispatchTyped(F f, const JS::Value& val,
         MOZ_ASSERT(gc::IsCellPointerValid(obj));
         return f(obj, mozilla::Forward<Args>(args)...);
     }
     if (val.isSymbol()) {
         JS::Symbol* sym = val.toSymbol();
         MOZ_ASSERT(gc::IsCellPointerValid(sym));
         return f(sym, mozilla::Forward<Args>(args)...);
     }
+#ifdef ENABLE_BIGINT
+    if (val.isBigInt()) {
+        JS::BigInt* bi = val.toBigInt();
+        MOZ_ASSERT(gc::IsCellPointerValid(bi));
+        return f(bi, mozilla::Forward<Args>(args)...);
+    }
+#endif
     if (MOZ_UNLIKELY(val.isPrivateGCThing())) {
         MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing()));
         return DispatchTyped(f, val.toGCCellPtr(), mozilla::Forward<Args>(args)...);
     }
     MOZ_ASSERT(!val.isGCThing());
     return F::defaultValue(val);
 }
 
--- a/js/rust/Cargo.toml
+++ b/js/rust/Cargo.toml
@@ -34,16 +34,17 @@ name = "vec_conversion"
 
 [lib]
 doctest = false
 
 [features]
 debugmozjs = ['mozjs_sys/debugmozjs']
 promises = ['mozjs_sys/promises']
 nonzero = []
+bigint = ['mozjs_sys/bigint']
 
 [dependencies.mozjs_sys]
 path = "../src"
 
 [dependencies]
 lazy_static = "1.0"
 libc = "0.2"
 log = "0.4"
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -68,16 +68,20 @@ fn build_jsapi_bindings() {
 
     if cfg!(feature = "debugmozjs") {
         builder = builder
             .clang_arg("-DJS_GC_ZEAL")
             .clang_arg("-DDEBUG")
             .clang_arg("-DJS_DEBUG");
     }
 
+    if cfg!(feature = "bigint") {
+        builder = builder.clang_arg("-DENABLE_BIGINT");
+    }
+
     let include_dir = get_mozjs_include_dir();
     let include_dir = include_dir.to_str()
         .expect("Path to mozjs include dir should be utf-8");
     builder = builder.clang_arg("-I");
     builder = builder.clang_arg(include_dir);
 
     for ty in UNSAFE_IMPL_SYNC_TYPES {
         builder = builder.raw_line(format!("unsafe impl Sync for {} {{}}", ty));
@@ -94,16 +98,20 @@ fn build_jsapi_bindings() {
     for var in WHITELIST_VARS {
         builder = builder.whitelist_var(var);
     }
 
     for func in WHITELIST_FUNCTIONS {
         builder = builder.whitelist_function(func);
     }
 
+    if cfg!(feature = "bigint") {
+        builder = builder.whitelist_type("JS::BigInt");
+    }
+
     for ty in OPAQUE_TYPES {
         builder = builder.opaque_type(ty);
     }
 
     for ty in BLACKLIST_TYPES {
         builder = builder.blacklist_type(ty);
     }
 
--- a/js/rust/src/jsval.rs
+++ b/js/rust/src/jsval.rs
@@ -20,48 +20,54 @@ const JSVAL_TAG_CLEAR: u32 = 0xFFFFFF80;
 #[repr(u32)]
 #[allow(dead_code)]
 #[derive(Clone, Copy, Debug)]
 enum ValueTag {
     INT32     = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_INT32 as u32),
     UNDEFINED = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
     STRING    = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_STRING as u32),
     SYMBOL    = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
+    #[cfg(feature = "bigint")]
+    BIGINT    = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_BIGINT as u32),
     BOOLEAN   = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
     MAGIC     = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_MAGIC as u32),
     NULL      = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_NULL as u32),
     OBJECT    = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_OBJECT as u32),
 }
 
 #[cfg(target_pointer_width = "32")]
 #[repr(u32)]
 #[allow(dead_code)]
 #[derive(Clone, Copy, Debug)]
 enum ValueTag {
     PRIVATE   = 0,
     INT32     = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_INT32 as u32),
     UNDEFINED = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
     STRING    = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_STRING as u32),
     SYMBOL    = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
+    #[cfg(feature = "bigint")]
+    BIGINT    = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_BIGINT as u32),
     BOOLEAN   = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
     MAGIC     = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_MAGIC as u32),
     NULL      = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_NULL as u32),
     OBJECT    = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_OBJECT as u32),
 }
 
 #[cfg(target_pointer_width = "64")]
 #[repr(u64)]
 #[allow(dead_code)]
 #[derive(Clone, Copy, Debug)]
 enum ValueShiftedTag {
     MAX_DOUBLE = (((JSVAL_TAG_MAX_DOUBLE as u64) << JSVAL_TAG_SHIFT) | 0xFFFFFFFFu64),
     INT32      = ((ValueTag::INT32 as u64)      << JSVAL_TAG_SHIFT),
     UNDEFINED  = ((ValueTag::UNDEFINED as u64)  << JSVAL_TAG_SHIFT),
     STRING     = ((ValueTag::STRING as u64)     << JSVAL_TAG_SHIFT),
     SYMBOL     = ((ValueTag::SYMBOL as u64)     << JSVAL_TAG_SHIFT),
+    #[cfg(feature = "bigint")]
+    BIGINT     = ((ValueTag::BIGINT as u64)     << JSVAL_TAG_SHIFT),
     BOOLEAN    = ((ValueTag::BOOLEAN as u64)    << JSVAL_TAG_SHIFT),
     MAGIC      = ((ValueTag::MAGIC as u64)      << JSVAL_TAG_SHIFT),
     NULL       = ((ValueTag::NULL as u64)       << JSVAL_TAG_SHIFT),
     OBJECT     = ((ValueTag::OBJECT as u64)     << JSVAL_TAG_SHIFT),
 }
 
 
 #[cfg(target_pointer_width = "64")]
@@ -182,16 +188,33 @@ pub fn PrivateValue(o: *const c_void) ->
 #[cfg(target_pointer_width = "32")]
 #[inline(always)]
 pub fn PrivateValue(o: *const c_void) -> JS::Value {
     let ptrBits = o as usize as u64;
     assert!((ptrBits & 1) == 0);
     BuildJSVal(ValueTag::PRIVATE, ptrBits)
 }
 
+#[inline(always)]
+#[cfg(feature = "bigint")]
+#[cfg(target_pointer_width = "64")]
+pub fn BigIntValue(b: &JS::BigInt) -> JS::Value {
+    let bits = b as *const JS::BigInt as usize as u64;
+    assert!((bits >> JSVAL_TAG_SHIFT) == 0);
+    BuildJSVal(ValueTag::BIGINT, bits)
+}
+
+#[inline(always)]
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn BigIntValue(s: &JS::BigInt) -> JS::Value {
+    let bits = s as *const JS::BigInt as usize as u64;
+    BuildJSVal(ValueTag::BIGINT, bits)
+}
+
 impl JS::Value {
     #[inline(always)]
     unsafe fn asBits(&self) -> u64 {
         self.asBits_
     }
 
     #[inline(always)]
     #[cfg(target_pointer_width = "64")]
@@ -359,16 +382,34 @@ impl JS::Value {
     #[cfg(target_pointer_width = "32")]
     pub fn is_symbol(&self) -> bool {
         unsafe {
             (self.asBits() >> 32) == ValueTag::SYMBOL as u64
         }
     }
 
     #[inline(always)]
+    #[cfg(feature = "bigint")]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_bigint(&self) -> bool {
+        unsafe {
+            (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::BIGINT as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(feature = "bigint")]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_bigint(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) == ValueTag::BIGINT as u64
+        }
+    }
+
+    #[inline(always)]
     #[cfg(target_pointer_width = "64")]
     pub fn to_boolean(&self) -> bool {
         assert!(self.is_boolean());
         unsafe {
             (self.asBits() & JSVAL_PAYLOAD_MASK) != 0
         }
     }
 
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -301,16 +301,22 @@ impl RootKind for *mut JSString {
     fn rootKind() -> JS::RootKind { JS::RootKind::String }
 }
 
 impl RootKind for *mut JS::Symbol {
     #[inline(always)]
     fn rootKind() -> JS::RootKind { JS::RootKind::Symbol }
 }
 
+#[cfg(feature = "bigint")]
+impl RootKind for *mut JS::BigInt {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::BigInt }
+}
+
 impl RootKind for *mut JSScript {
     #[inline(always)]
     fn rootKind() -> JS::RootKind { JS::RootKind::Script }
 }
 
 impl RootKind for jsid {
     #[inline(always)]
     fn rootKind() -> JS::RootKind { JS::RootKind::Id }
--- a/js/src/Cargo.toml
+++ b/js/src/Cargo.toml
@@ -3,16 +3,17 @@ name = "mozjs_sys"
 version = "0.0.0"
 authors = ["Mozilla"]
 links = "mozjs"
 build = "build.rs"
 
 [features]
 debugmozjs = []
 promises = []
+bigint = []
 
 [lib]
 name = "mozjs_sys"
 path = "lib.rs"
 
 [build-dependencies]
 num_cpus = "1.1.0"
 
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -42,16 +42,19 @@ class MOZ_STACK_CLASS SourceBufferHolder
 
 class HandleValueArray;
 
 class ObjectOpResult;
 class PropertyResult;
 
 enum class SymbolCode: uint32_t;
 
+#ifdef ENABLE_BIGINT
+class BigInt;
+#endif
 } // namespace JS
 
 // Do the importing.
 namespace js {
 
 using JS::Value;
 using JS::BooleanValue;
 using JS::DoubleValue;
@@ -107,43 +110,55 @@ using JS::TransitiveCompileOptions;
 
 using JS::Rooted;
 using JS::RootedFunction;
 using JS::RootedId;
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
 using JS::RootedSymbol;
+#ifdef ENABLE_BIGINT
+using JS::RootedBigInt;
+#endif
 using JS::RootedValue;
 
 using JS::PersistentRooted;
 using JS::PersistentRootedFunction;
 using JS::PersistentRootedId;
 using JS::PersistentRootedObject;
 using JS::PersistentRootedScript;
 using JS::PersistentRootedString;
 using JS::PersistentRootedSymbol;
+#ifdef ENABLE_BIGINT
+using JS::PersistentRootedBigInt;
+#endif
 using JS::PersistentRootedValue;
 
 using JS::Handle;
 using JS::HandleFunction;
 using JS::HandleId;
 using JS::HandleObject;
 using JS::HandleScript;
 using JS::HandleString;
 using JS::HandleSymbol;
+#ifdef ENABLE_BIGINT
+using JS::HandleBigInt;
+#endif
 using JS::HandleValue;
 
 using JS::MutableHandle;
 using JS::MutableHandleFunction;
 using JS::MutableHandleId;
 using JS::MutableHandleObject;
 using JS::MutableHandleScript;
 using JS::MutableHandleString;
 using JS::MutableHandleSymbol;
+#ifdef ENABLE_BIGINT
+using JS::MutableHandleBigInt;
+#endif
 using JS::MutableHandleValue;
 
 using JS::NullHandleValue;
 using JS::UndefinedHandleValue;
 using JS::TrueHandleValue;
 using JS::FalseHandleValue;
 
 using JS::HandleValueArray;
@@ -152,11 +167,14 @@ using JS::ObjectOpResult;
 using JS::PropertyResult;
 
 using JS::Realm;
 using JS::Zone;
 
 using JS::Symbol;
 using JS::SymbolCode;
 
+#ifdef ENABLE_BIGINT
+using JS::BigInt;
+#endif
 } /* namespace js */
 
 #endif /* NamespaceImports_h */
--- a/js/src/build.rs
+++ b/js/src/build.rs
@@ -11,37 +11,35 @@ fn main() {
     let out_dir = env::var("OUT_DIR").expect("Should have env var OUT_DIR");
     let target = env::var("TARGET").expect("Should have env var TARGET");
 
     let js_src = env::var("CARGO_MANIFEST_DIR").expect("Should have env var CARGO_MANIFEST_DIR");
 
     env::set_var("MAKEFLAGS", format!("-j{}", num_cpus::get()));
     env::set_current_dir(&js_src).unwrap();
 
-    let variant = if cfg!(feature = "debugmozjs") {
-        "plaindebug"
-    } else {
-        "plain"
-    };
+    let variant = format!("{}{}",
+                          if cfg!(feature = "bigint") { "bigint" } else { "plain" },
+                          if cfg!(feature = "debugmozjs") { "debug" } else { "" });
 
     let python = env::var("PYTHON").unwrap_or("python2.7".into());
     let mut cmd = Command::new(&python);
     cmd.args(&["./devtools/automation/autospider.py",
                // Only build SpiderMonkey, don't run all the tests.
                "--build-only",
                // Disable Mozilla's jemalloc; Rust has its own jemalloc that we
                // can swap in instead and everything using a single malloc is
                // good.
                "--no-jemalloc",
                // Don't try to clobber the output directory. Without
                // this option, the build will fail because the directory
                // already exists but wasn't created by autospider.
                "--dep",
                "--objdir", &out_dir,
-               variant])
+               &variant])
         .env("SOURCE", &js_src)
         .env("PWD", &js_src)
         .stdout(Stdio::inherit())
         .stderr(Stdio::inherit());
     println!("Running command: {:?}", cmd);
     let result = cmd
         .status()
         .expect("Should spawn autospider OK");
@@ -49,16 +47,20 @@ fn main() {
 
     println!("cargo:rustc-link-search=native={}/js/src/build", out_dir);
     println!("cargo:rustc-link-search=native={}/js/src", out_dir);
     println!("cargo:rustc-link-lib=static=js_static");
 
     println!("cargo:rustc-link-search=native={}/dist/bin", out_dir);
     println!("cargo:rustc-link-lib=nspr4");
 
+    if cfg!(feature = "bigint") {
+        println!("cargo:rustc-link-lib=gmp");
+    }
+
     if target.contains("windows") {
         println!("cargo:rustc-link-lib=winmm");
         if target.contains("gnu") {
             println!("cargo:rustc-link-lib=stdc++");
         }
     } else {
         println!("cargo:rustc-link-lib=stdc++");
     }
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -1267,16 +1267,22 @@ ArrayJoinDenseKernel(JSContext* cx, Sepa
              * Object stringifying could modify the initialized length or make
              * the array sparse. Delegate it to a separate loop to keep this
              * one tight.
              *
              * Symbol stringifying is a TypeError, so into the slow path
              * with those as well.
              */
             break;
+        } else if (IF_BIGINT(elem.isBigInt(), false)) {
+            // ToString(bigint) doesn't access bigint.toString or
+            // anything like that, so it can't mutate the array we're
+            // walking through, so it *could* be handled here. We don't
+            // do so yet for reasons of initial-implementation economy.
+            break;
         } else {
             MOZ_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE) || elem.isNullOrUndefined());
         }
 
         // Steps 7.a, 7.e.
         if (++(*numProcessed) != length && !sepOp(cx, sb))
             return false;
     }
--- a/js/src/builtin/Boolean.cpp
+++ b/js/src/builtin/Boolean.cpp
@@ -10,16 +10,19 @@
 
 #include "builtin/Boolean-inl.h"
 
 #include "jsapi.h"
 #include "jstypes.h"
 
 #include "jit/InlinableNatives.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"
 #include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
 
 #include "vm/BooleanObject-inl.h"
 
@@ -159,12 +162,16 @@ js::BooleanToString(JSContext* cx, bool 
     return b ? cx->names().true_ : cx->names().false_;
 }
 
 JS_PUBLIC_API(bool)
 js::ToBooleanSlow(HandleValue v)
 {
     if (v.isString())
         return v.toString()->length() != 0;
+#ifdef ENABLE_BIGINT
+    if (v.isBigInt())
+        return v.toBigInt()->toBoolean();
+#endif
 
     MOZ_ASSERT(v.isObject());
     return !EmulatesUndefined(&v.toObject());
 }
--- a/js/src/builtin/JSON.cpp
+++ b/js/src/builtin/JSON.cpp
@@ -582,16 +582,23 @@ Str(JSContext* cx, const Value& v, Strin
                            "reachable non-finite numbers");
                 return scx->sb.append("null");
             }
         }
 
         return NumberValueToStringBuffer(cx, v, scx->sb);
     }
 
+#ifdef ENABLE_BIGINT
+    if (v.isBigInt()) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BIGINT_NOT_SERIALIZABLE);
+        return false;
+    }
+#endif
+
     /* Step 10. */
     MOZ_ASSERT(v.isObject());
     RootedObject obj(cx, &v.toObject());
 
     MOZ_ASSERT(!scx->maybeSafely || obj->is<PlainObject>() || obj->is<ArrayObject>(),
                "input to JS::ToJSONMaybeSafely must not include reachable "
                "objects that are neither arrays nor plain objects");
 
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -52,17 +52,18 @@ HashableValue::setValue(JSContext* cx, H
         } else {
             value = v;
         }
     } else {
         value = v;
     }
 
     MOZ_ASSERT(value.isUndefined() || value.isNull() || value.isBoolean() || value.isNumber() ||
-               value.isString() || value.isSymbol() || value.isObject());
+               value.isString() || value.isSymbol() || value.isObject() ||
+               IF_BIGINT(value.isBigInt(), false));
     return true;
 }
 
 static HashNumber
 HashValue(const Value& v, const mozilla::HashCodeScrambler& hcs)
 {
     // HashableValue::setValue normalizes values so that the SameValue relation
     // on HashableValues is the same as the == relationship on
@@ -72,16 +73,20 @@ HashValue(const Value& v, const mozilla:
     // from the string contents rather than any pointer; to avoid revealing
     // addresses, pointer-based hash codes are computed using the
     // HashCodeScrambler.
 
     if (v.isString())
         return v.toString()->asAtom().hash();
     if (v.isSymbol())
         return v.toSymbol()->hash();
+#ifdef ENABLE_BIGINT
+    if (v.isBigInt())
+        return v.toBigInt()->hash();
+#endif
     if (v.isObject())
         return hcs.scramble(v.asRawBits());
 
     MOZ_ASSERT(!v.isGCThing(), "do not reveal pointers via hash codes");
     return mozilla::HashGeneric(v.asRawBits());
 }
 
 HashNumber
@@ -91,22 +96,34 @@ HashableValue::hash(const mozilla::HashC
 }
 
 bool
 HashableValue::operator==(const HashableValue& other) const
 {
     // Two HashableValues are equal if they have equal bits.
     bool b = (value.asRawBits() == other.value.asRawBits());
 
+#ifdef ENABLE_BIGINT
+    // BigInt values are considered equal if they represent the same
+    // integer. This test should use a comparison function that doesn't
+    // require a JSContext once one is defined in the BigInt class.
+    if (!b && (value.isBigInt() && other.value.isBigInt())) {
+        JSContext* cx = TlsContext.get();
+        RootedValue valueRoot(cx, value);
+        RootedValue otherRoot(cx, other.value);
+        SameValue(cx, valueRoot, otherRoot, &b);
+    }
+#endif
+
 #ifdef DEBUG
     bool same;
     JSContext* cx = TlsContext.get();
     RootedValue valueRoot(cx, value);
     RootedValue otherRoot(cx, other.value);
-    MOZ_ASSERT(SameValue(nullptr, valueRoot, otherRoot, &same));
+    MOZ_ASSERT(SameValue(cx, valueRoot, otherRoot, &same));
     MOZ_ASSERT(same == b);
 #endif
     return b;
 }
 
 HashableValue
 HashableValue::trace(JSTracer* trc) const
 {
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/automation/variants/bigint
@@ -0,0 +1,7 @@
+{
+    "configure-args": "--enable-bigint",
+    "optimize": true,
+    "env": {
+        "JSTESTS_EXTRA_ARGS": "--jitflags=all"
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/automation/variants/bigintdebug
@@ -0,0 +1,7 @@
+{
+    "configure-args": "--enable-bigint",
+    "debug": true,
+    "env": {
+        "JSTESTS_EXTRA_ARGS": "--jitflags=debug"
+    }
+}
--- a/js/src/gc/AllocKind.h
+++ b/js/src/gc/AllocKind.h
@@ -59,16 +59,17 @@ namespace gc {
     D(SHAPE,               Shape,        js::Shape,         js::Shape,         true,   false) \
     D(ACCESSOR_SHAPE,      Shape,        js::AccessorShape, js::AccessorShape, true,   false) \
     D(BASE_SHAPE,          BaseShape,    js::BaseShape,     js::BaseShape,     true,   false) \
     D(OBJECT_GROUP,        ObjectGroup,  js::ObjectGroup,   js::ObjectGroup,   true,   false) \
     D(EXTERNAL_STRING,     String,       JSExternalString,  JSExternalString,  true,   false) \
     D(FAT_INLINE_ATOM,     String,       js::FatInlineAtom, js::FatInlineAtom, true,   false) \
     D(ATOM,                String,       js::NormalAtom,    js::NormalAtom,    true,   false) \
     D(SYMBOL,              Symbol,       JS::Symbol,        JS::Symbol,        true,   false) \
+    IF_BIGINT(D(BIGINT,    BigInt,       JS::BigInt,        JS::BigInt,        true,   false),) \
     D(JITCODE,             JitCode,      js::jit::JitCode,  js::jit::JitCode,  false,  false) \
     D(SCOPE,               Scope,        js::Scope,         js::Scope,         true,   false) \
     D(REGEXP_SHARED,       RegExpShared, js::RegExpShared,  js::RegExpShared,  true,   false)
 
 #define FOR_EACH_NURSERY_STRING_ALLOCKIND(D) \
     D(FAT_INLINE_STRING,   String,        JSFatInlineString, JSFatInlineString, true,   true) \
     D(STRING,              String,        JSString,          JSString,          true,   true)
 
--- a/js/src/gc/AtomMarking.cpp
+++ b/js/src/gc/AtomMarking.cpp
@@ -185,17 +185,20 @@ AtomMarkingRuntime::markAtomValue(JSCont
         if (value.toString()->isAtom())
             markAtom(cx, &value.toString()->asAtom());
         return;
     }
     if (value.isSymbol()) {
         markAtom(cx, value.toSymbol());
         return;
     }
-    MOZ_ASSERT_IF(value.isGCThing(), value.isObject() || value.isPrivateGCThing());
+    MOZ_ASSERT_IF(value.isGCThing(),
+                  value.isObject() ||
+                  value.isPrivateGCThing() ||
+                  IF_BIGINT(value.isBigInt(), false));
 }
 
 void
 AtomMarkingRuntime::adoptMarkedAtoms(Zone* target, Zone* source)
 {
     MOZ_ASSERT(CurrentThreadCanAccessZone(source));
     MOZ_ASSERT(CurrentThreadCanAccessZone(target));
     target->markedAtoms().bitwiseOrWith(source->markedAtoms());
@@ -267,17 +270,20 @@ AtomMarkingRuntime::valueIsMarked(Zone* 
         if (value.toString()->isAtom())
             return atomIsMarked(zone, &value.toString()->asAtom());
         return true;
     }
 
     if (value.isSymbol())
         return atomIsMarked(zone, value.toSymbol());
 
-    MOZ_ASSERT_IF(value.isGCThing(), value.isObject() || value.isPrivateGCThing());
+    MOZ_ASSERT_IF(value.isGCThing(),
+                  value.isObject() ||
+                  value.isPrivateGCThing() ||
+                  IF_BIGINT(value.isBigInt(), false));
     return true;
 }
 
 #endif // DEBUG
 
 } // namespace gc
 
 #ifdef DEBUG
--- a/js/src/gc/DeletePolicy.h
+++ b/js/src/gc/DeletePolicy.h
@@ -3,16 +3,19 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_DeletePolicy_h
 #define gc_DeletePolicy_h
 
 #include "js/TracingAPI.h"
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
 
 namespace js {
 namespace gc {
 
 struct ClearEdgesTracer : public JS::CallbackTracer
 {
     ClearEdgesTracer();
 
@@ -21,16 +24,19 @@ struct ClearEdgesTracer : public JS::Cal
 #endif
 
     template <typename T>
     inline void clearEdge(T** thingp);
 
     void onObjectEdge(JSObject** objp) override;
     void onStringEdge(JSString** strp) override;
     void onSymbolEdge(JS::Symbol** symp) override;
+#ifdef ENABLE_BIGINT
+    void onBigIntEdge(JS::BigInt** bip) override;
+#endif
     void onScriptEdge(JSScript** scriptp) override;
     void onShapeEdge(js::Shape** shapep) override;
     void onObjectGroupEdge(js::ObjectGroup** groupp) override;
     void onBaseShapeEdge(js::BaseShape** basep) override;
     void onJitCodeEdge(js::jit::JitCode** codep) override;
     void onLazyScriptEdge(js::LazyScript** lazyp) override;
     void onScopeEdge(js::Scope** scopep) override;
     void onRegExpSharedEdge(js::RegExpShared** sharedp) override;
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -219,16 +219,19 @@
 #include "gc/Policy.h"
 #include "gc/WeakMap.h"
 #include "jit/BaselineJIT.h"
 #include "jit/IonCode.h"
 #include "jit/JitcodeMap.h"
 #include "js/SliceBudget.h"
 #include "proxy/DeadObjectProxy.h"
 #include "util/Windows.h"
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
 #include "vm/Debugger.h"
 #include "vm/GeckoProfiler.h"
 #include "vm/JSAtom.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Printer.h"
@@ -490,17 +493,20 @@ static const FinalizePhase BackgroundFin
     },
     {
         gcstats::PhaseKind::SWEEP_STRING, {
             AllocKind::FAT_INLINE_STRING,
             AllocKind::STRING,
             AllocKind::EXTERNAL_STRING,
             AllocKind::FAT_INLINE_ATOM,
             AllocKind::ATOM,
-            AllocKind::SYMBOL
+            AllocKind::SYMBOL,
+#ifdef ENABLE_BIGINT
+            AllocKind::BIGINT
+#endif
         }
     },
     {
         gcstats::PhaseKind::SWEEP_SHAPE, {
             AllocKind::SHAPE,
             AllocKind::ACCESSOR_SHAPE,
             AllocKind::BASE_SHAPE,
             AllocKind::OBJECT_GROUP
@@ -8431,16 +8437,20 @@ JS::GCCellPtr::GCCellPtr(const Value& v)
   : ptr(0)
 {
     if (v.isString())
         ptr = checkedCast(v.toString(), JS::TraceKind::String);
     else if (v.isObject())
         ptr = checkedCast(&v.toObject(), JS::TraceKind::Object);
     else if (v.isSymbol())
         ptr = checkedCast(v.toSymbol(), JS::TraceKind::Symbol);
+#ifdef ENABLE_BIGINT
+    else if (v.isBigInt())
+        ptr = checkedCast(v.toBigInt(), JS::TraceKind::BigInt);
+#endif
     else if (v.isPrivateGCThing())
         ptr = checkedCast(v.toGCThing(), v.toGCThing()->getTraceKind());
     else
         ptr = checkedCast(nullptr, JS::TraceKind::Null);
 }
 
 JS::TraceKind
 JS::GCCellPtr::outOfLineKind() const
@@ -9133,16 +9143,19 @@ js::gc::ClearEdgesTracer::clearEdge(S** 
     InternalBarrierMethods<S*>::preBarrier(*thingp);
     InternalBarrierMethods<S*>::postBarrier(thingp, *thingp, nullptr);
     *thingp = nullptr;
 }
 
 void js::gc::ClearEdgesTracer::onObjectEdge(JSObject** objp) { clearEdge(objp); }
 void js::gc::ClearEdgesTracer::onStringEdge(JSString** strp) { clearEdge(strp); }
 void js::gc::ClearEdgesTracer::onSymbolEdge(JS::Symbol** symp) { clearEdge(symp); }
+#ifdef ENABLE_BIGINT
+void js::gc::ClearEdgesTracer::onBigIntEdge(JS::BigInt** bip) { clearEdge(bip); }
+#endif
 void js::gc::ClearEdgesTracer::onScriptEdge(JSScript** scriptp) { clearEdge(scriptp); }
 void js::gc::ClearEdgesTracer::onShapeEdge(js::Shape** shapep) { clearEdge(shapep); }
 void js::gc::ClearEdgesTracer::onObjectGroupEdge(js::ObjectGroup** groupp) { clearEdge(groupp); }
 void js::gc::ClearEdgesTracer::onBaseShapeEdge(js::BaseShape** basep) { clearEdge(basep); }
 void js::gc::ClearEdgesTracer::onJitCodeEdge(js::jit::JitCode** codep) { clearEdge(codep); }
 void js::gc::ClearEdgesTracer::onLazyScriptEdge(js::LazyScript** lazyp) { clearEdge(lazyp); }
 void js::gc::ClearEdgesTracer::onScopeEdge(js::Scope** scopep) { clearEdge(scopep); }
 void js::gc::ClearEdgesTracer::onRegExpSharedEdge(js::RegExpShared** sharedp) { clearEdge(sharedp); }
--- a/js/src/gc/Marking-inl.h
+++ b/js/src/gc/Marking-inl.h
@@ -6,16 +6,20 @@
 
 #ifndef gc_Marking_inl_h
 #define gc_Marking_inl_h
 
 #include "gc/Marking.h"
 
 #include "gc/RelocationOverlay.h"
 
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
+
 namespace js {
 namespace gc {
 
 template <typename T>
 struct MightBeForwarded
 {
     static_assert(mozilla::IsBaseOf<Cell, T>::value,
                   "T must derive from Cell");
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -16,16 +16,19 @@
 
 #include "builtin/ModuleObject.h"
 #include "gc/GCInternals.h"
 #include "gc/Policy.h"
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpShared.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
 #include "vm/SymbolType.h"
 #include "vm/TypedArrayObject.h"
@@ -889,16 +892,19 @@ js::GCMarker::markAndTraceChildren(T* th
     if (ThingIsPermanentAtomOrWellKnownSymbol(thing))
         return;
     if (mark(thing))
         thing->traceChildren(this);
 }
 namespace js {
 template <> void GCMarker::traverse(BaseShape* thing) { markAndTraceChildren(thing); }
 template <> void GCMarker::traverse(JS::Symbol* thing) { markAndTraceChildren(thing); }
+#ifdef ENABLE_BIGINT
+template <> void GCMarker::traverse(JS::BigInt* thing) { markAndTraceChildren(thing); }
+#endif
 template <> void GCMarker::traverse(RegExpShared* thing) { markAndTraceChildren(thing); }
 } // namespace js
 
 // Strings, LazyScripts, Shapes, and Scopes are extremely common, but have
 // simple patterns of recursion. We traverse trees of these edges immediately,
 // with aggressive, manual inlining, implemented by eagerlyTraceChildren.
 template <typename T>
 void
@@ -1545,16 +1551,24 @@ js::GCMarker::lazilyMarkChildren(ObjectG
 
     if (TypeDescr* descr = group->maybeTypeDescr())
         traverseEdge(group, static_cast<JSObject*>(descr));
 
     if (JSFunction* fun = group->maybeInterpretedFunction())
         traverseEdge(group, static_cast<JSObject*>(fun));
 }
 
+#ifdef ENABLE_BIGINT
+void
+JS::BigInt::traceChildren(JSTracer* trc)
+{
+    return;
+}
+#endif
+
 struct TraverseObjectFunctor
 {
     template <typename T>
     void operator()(T* thing, GCMarker* gcmarker, JSObject* src) {
         gcmarker->traverseEdge(src, *thing);
     }
 };
 
@@ -1689,17 +1703,18 @@ ObjectDenseElementsMayBeMarkable(NativeO
         return true;
 
     // This typeset doesn't escape this function so avoid sweeping here.
     HeapTypeSet* typeSet = group->maybeGetPropertyDontCheckGeneration(JSID_VOID);
     if (!typeSet)
         return true;
 
     static const uint32_t flagMask =
-        TYPE_FLAG_STRING | TYPE_FLAG_SYMBOL | TYPE_FLAG_LAZYARGS | TYPE_FLAG_ANYOBJECT;
+        TYPE_FLAG_STRING | TYPE_FLAG_SYMBOL | TYPE_FLAG_LAZYARGS | TYPE_FLAG_ANYOBJECT |
+        IF_BIGINT(TYPE_FLAG_BIGINT, 0);
     bool mayBeMarkable = typeSet->hasAnyFlag(flagMask) || typeSet->getObjectCount() != 0;
 
 #ifdef DEBUG
     if (!mayBeMarkable) {
         const Value* elements = nobj->getDenseElementsAllowCopyOnWrite();
         for (unsigned i = 0; i < nobj->getDenseInitializedLength(); i++)
             MOZ_ASSERT(!elements[i].isGCThing());
     }
@@ -1803,17 +1818,23 @@ GCMarker::processMarkStackTop(SliceBudge
             if (mark(obj2)) {
                 // Save the rest of this value array for later and start scanning obj2's children.
                 pushValueArray(obj, vp, end);
                 obj = obj2;
                 goto scan_obj;
             }
         } else if (v.isSymbol()) {
             traverseEdge(obj, v.toSymbol());
-        } else if (v.isPrivateGCThing()) {
+        }
+#ifdef ENABLE_BIGINT
+        else if (v.isBigInt()) {
+            traverseEdge(obj, v.toBigInt());
+        }
+#endif
+        else if (v.isPrivateGCThing()) {
             // v.toGCCellPtr cannot be inlined, so construct one manually.
             Cell* cell = v.toGCThing();
             traverseEdge(obj, JS::GCCellPtr(cell, cell->getTraceKind()));
         }
     }
     return;
 
   scan_obj:
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -104,27 +104,34 @@ template <typename S, typename T>
 struct RewrapTaggedPointer{};
 #define DECLARE_REWRAP(S, T, method, prefix) \
     template <> struct RewrapTaggedPointer<S, T> { \
         static S wrap(T* thing) { return method ( prefix thing ); } \
     }
 DECLARE_REWRAP(JS::Value, JSObject, JS::ObjectOrNullValue, );
 DECLARE_REWRAP(JS::Value, JSString, JS::StringValue, );
 DECLARE_REWRAP(JS::Value, JS::Symbol, JS::SymbolValue, );
+#ifdef ENABLE_BIGINT
+DECLARE_REWRAP(JS::Value, JS::BigInt, JS::BigIntValue, );
+#endif
 DECLARE_REWRAP(jsid, JSString, NON_INTEGER_ATOM_TO_JSID, (JSAtom*));
 DECLARE_REWRAP(jsid, JS::Symbol, SYMBOL_TO_JSID, );
 DECLARE_REWRAP(js::TaggedProto, JSObject, js::TaggedProto, );
 #undef DECLARE_REWRAP
 
 template <typename T>
 struct IsPrivateGCThingInValue
   : public mozilla::EnableIf<mozilla::IsBaseOf<Cell, T>::value &&
                              !mozilla::IsBaseOf<JSObject, T>::value &&
                              !mozilla::IsBaseOf<JSString, T>::value &&
-                             !mozilla::IsBaseOf<JS::Symbol, T>::value, T>
+                             !mozilla::IsBaseOf<JS::Symbol, T>::value
+#ifdef ENABLE_BIGINT
+                             && !mozilla::IsBaseOf<JS::BigInt, T>::value
+#endif
+                             , T>
 {
     static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
                   "T must not be Cell or TenuredCell");
 };
 
 template <typename T>
 struct RewrapTaggedPointer<Value, T>
 {
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -11,16 +11,19 @@
 #include "jsutil.h"
 #include "NamespaceImports.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/PublicIterators.h"
 #include "gc/Zone.h"
 #include "util/Text.h"
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/Shape.h"
 #include "vm/SymbolType.h"
 
 #include "gc/GC-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/ObjectGroup-inl.h"
@@ -391,16 +394,22 @@ JS_GetTraceThingInfo(char* buf, size_t b
                ? "substring"
                : "string";
         break;
 
       case JS::TraceKind::Symbol:
         name = "symbol";
         break;
 
+#ifdef ENABLE_BIGINT
+      case JS::TraceKind::BigInt:
+        name = "BigInt";
+        break;
+#endif
+
       default:
         name = "INVALID";
         break;
     }
 
     n = strlen(name);
     if (n > bufsize - 1)
         n = bufsize - 1;
--- a/js/src/gdb/lib-for-tests/prologue.py
+++ b/js/src/gdb/lib-for-tests/prologue.py
@@ -64,16 +64,23 @@ def assert_subprinter_registered(printer
     pat = r'^( +)%(printer)s *\n(\1 +.*\n)*\1 +%(subprinter)s *\n' % names
     output = gdb.execute('info pretty-printer', to_string=True)
     if not re.search(pat, output, re.MULTILINE):
         raise AssertionError("assert_subprinter_registered failed to find pretty-printer:\n"
                              "  %s:%s\n"
                              "'info pretty-printer' says:\n"
                              "%s" % (printer, subprinter, output))
 
+enable_bigint = False
+try:
+    if gdb.lookup_type('JS::BigInt'):
+        enable_bigint = True
+except:
+    pass
+
 # Request full stack traces for Python errors.
 gdb.execute('set python print-stack full')
 
 # Tell GDB not to ask the user about the things we tell it to do.
 gdb.execute('set confirm off', False)
 
 # Some print settings that make testing easier.
 gdb.execute('set print static-members off')
--- a/js/src/gdb/mozilla/jsval.py
+++ b/js/src/gdb/mozilla/jsval.py
@@ -141,16 +141,25 @@ class JSValueTypeCache(object):
         self.UNDEFINED = get('JSVAL_TYPE_UNDEFINED')
         self.BOOLEAN = get('JSVAL_TYPE_BOOLEAN')
         self.MAGIC = get('JSVAL_TYPE_MAGIC')
         self.STRING = get('JSVAL_TYPE_STRING')
         self.SYMBOL = get('JSVAL_TYPE_SYMBOL')
         self.NULL = get('JSVAL_TYPE_NULL')
         self.OBJECT = get('JSVAL_TYPE_OBJECT')
 
+        self.enable_bigint = False
+        try:
+            # Looking up the tag will throw an exception if BigInt is not
+            # enabled.
+            self.BIGINT = get('JSVAL_TYPE_BIGINT')
+            self.enable_bigint = True
+        except:
+            pass
+
         # Let self.magic_names be an array whose i'th element is the name of
         # the i'th magic value.
         d = gdb.types.make_enum_dict(gdb.lookup_type('JSWhyMagic'))
         self.magic_names = list(range(max(d.values()) + 1))
         for (k,v) in d.items(): self.magic_names[v] = k
 
         # Choose an unboxing scheme for this architecture.
         self.boxer = Punbox if cache.void_ptr_t.sizeof == 8 else Nunbox
@@ -193,11 +202,13 @@ class JSValue(object):
             return '$JS::DoubleValue(%s)' % self.value['asDouble_']
 
         if tag == self.jtc.STRING:
             value = self.box.as_address().cast(self.cache.JSString_ptr_t)
         elif tag == self.jtc.OBJECT:
             value = self.box.as_address().cast(self.cache.JSObject_ptr_t)
         elif tag == self.jtc.SYMBOL:
             value = self.box.as_address().cast(self.cache.JSSymbol_ptr_t)
+        elif self.jtc.enable_bigint and tag == self.jtc.BIGINT:
+            return '$JS::BigIntValue()'
         else:
             value = 'unrecognized!'
         return '$JS::Value(%s)' % (value,)
--- a/js/src/gdb/tests/test-jsval.cpp
+++ b/js/src/gdb/tests/test-jsval.cpp
@@ -1,11 +1,15 @@
 #include "gdb-tests.h"
 #include "jsapi.h"
 
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
+
 FRAGMENT(jsval, simple) {
   using namespace JS;
 
   RootedValue fortytwo(cx, Int32Value(42));
   RootedValue fortytwoD(cx, DoubleValue(42));
   RootedValue negone(cx, Int32Value(-1));
   RootedValue undefined(cx, UndefinedValue());
   RootedValue null(cx, NullValue());
@@ -13,16 +17,19 @@ FRAGMENT(jsval, simple) {
   RootedValue js_false(cx, BooleanValue(false));
   RootedValue elements_hole(cx, js::MagicValue(JS_ELEMENTS_HOLE));
 
   RootedValue empty_string(cx);
   empty_string.setString(JS_NewStringCopyZ(cx, ""));
   RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!"));
   RootedValue friendly_string(cx, StringValue(hello));
   RootedValue symbol(cx, SymbolValue(GetSymbolFor(cx, hello)));
+#ifdef ENABLE_BIGINT
+  RootedValue bi(cx, BigIntValue(BigInt::create(cx)));
+#endif
 
   RootedValue global(cx);
   global.setObject(*CurrentGlobalOrNull(cx));
 
   // Some interesting value that floating-point won't munge.
   RootedValue onehundredthirtysevenonehundredtwentyeighths(cx, DoubleValue(137.0 / 128.0));
 
   breakpoint();
@@ -33,10 +40,13 @@ FRAGMENT(jsval, simple) {
   use(undefined);
   use(js_true);
   use(js_false);
   use(null);
   use(elements_hole);
   use(empty_string);
   use(friendly_string);
   use(symbol);
+#ifdef ENABLE_BIGINT
+  use(bi);
+#endif
   use(global);
 }
--- a/js/src/gdb/tests/test-jsval.py
+++ b/js/src/gdb/tests/test-jsval.py
@@ -10,10 +10,12 @@ assert_pretty('negone', '$JS::Int32Value
 assert_pretty('undefined', '$JS::UndefinedValue()')
 assert_pretty('null', '$JS::NullValue()')
 assert_pretty('js_true', '$JS::BooleanValue(true)')
 assert_pretty('js_false', '$JS::BooleanValue(false)')
 assert_pretty('elements_hole', '$JS::MagicValue(JS_ELEMENTS_HOLE)')
 assert_pretty('empty_string', '$JS::Value("")')
 assert_pretty('friendly_string', '$JS::Value("Hello!")')
 assert_pretty('symbol', '$JS::Value(Symbol.for("Hello!"))')
+if enable_bigint:
+    assert_pretty('bi', '$JS::BigIntValue()')
 assert_pretty('global', '$JS::Value((JSObject *)  [object global] delegate)')
 assert_pretty('onehundredthirtysevenonehundredtwentyeighths', '$JS::DoubleValue(1.0703125)')
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -642,8 +642,17 @@ MSG_DEF(JSMSG_STREAM_INVALID_HIGHWATERMA
 
 // Response-related
 MSG_DEF(JSMSG_ERROR_CONSUMING_RESPONSE,                  0, JSEXN_TYPEERR,  "there was an error consuming the Response")
 MSG_DEF(JSMSG_BAD_RESPONSE_VALUE,                        0, JSEXN_TYPEERR,  "expected Response or Promise resolving to Response")
 MSG_DEF(JSMSG_BAD_RESPONSE_MIME_TYPE,                    0, JSEXN_TYPEERR,  "Response has unsupported MIME type")
 MSG_DEF(JSMSG_BAD_RESPONSE_CORS_SAME_ORIGIN,             0, JSEXN_TYPEERR,  "Response.type must be 'basic', 'cors' or 'default'")
 MSG_DEF(JSMSG_BAD_RESPONSE_STATUS,                       0, JSEXN_TYPEERR,  "Response does not have ok status")
 MSG_DEF(JSMSG_RESPONSE_ALREADY_CONSUMED,                 0, JSEXN_TYPEERR,  "Response already consumed")
+
+// BigInt
+MSG_DEF(JSMSG_BIGINT_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert BigInt to number")
+MSG_DEF(JSMSG_NUMBER_TO_BIGINT, 0, JSEXN_RANGEERR, "can't convert non-finite number to BigInt")
+MSG_DEF(JSMSG_BIGINT_DIVISION_BY_ZERO, 0, JSEXN_RANGEERR, "BigInt division by zero")
+MSG_DEF(JSMSG_BIGINT_NEGATIVE_EXPONENT, 0, JSEXN_RANGEERR, "BigInt negative exponent")
+MSG_DEF(JSMSG_BIGINT_INVALID_SYNTAX, 0, JSEXN_SYNTAXERR, "invalid BigInt syntax")
+MSG_DEF(JSMSG_NOT_BIGINT, 0, JSEXN_TYPEERR, "not a BigInt")
+MSG_DEF(JSMSG_BIGINT_NOT_SERIALIZABLE, 0, JSEXN_TYPEERR, "BigInt value can't be serialized in JSON")
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1586,19 +1586,31 @@ js::ToNumberSlow(JSContext* cx, HandleVa
     if (v.isSymbol()) {
         if (!cx->helperThread()) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                       JSMSG_SYMBOL_TO_NUMBER);
         }
         return false;
     }
 
-    MOZ_ASSERT(v.isUndefined());
-    *out = GenericNaN();
-    return true;
+    if (v.isUndefined()) {
+        *out = GenericNaN();
+        return true;
+    }
+
+    MOZ_ASSERT(v.isSymbol() || IF_BIGINT(v.isBigInt(), false));
+    if (!cx->helperThread()) {
+        unsigned errnum = JSMSG_SYMBOL_TO_NUMBER;
+#ifdef ENABLE_BIGINT
+        if (v.isBigInt())
+            errnum = JSMSG_BIGINT_TO_NUMBER;
+#endif
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errnum);
+    }
+    return false;
 }
 
 /*
  * Convert a value to an int8_t, according to the WebIDL rules for byte
  * conversion. Return converted value in *out on success, false on failure.
  */
 JS_PUBLIC_API(bool)
 js::ToInt8Slow(JSContext *cx, const HandleValue v, int8_t *out)
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -44,16 +44,19 @@ enum JSType {
     JSTYPE_UNDEFINED,           /* undefined */
     JSTYPE_OBJECT,              /* object */
     JSTYPE_FUNCTION,            /* function */
     JSTYPE_STRING,              /* string */
     JSTYPE_NUMBER,              /* number */
     JSTYPE_BOOLEAN,             /* boolean */
     JSTYPE_NULL,                /* null */
     JSTYPE_SYMBOL,              /* symbol */
+#ifdef ENABLE_BIGINT
+    JSTYPE_BIGINT,              /* BigInt */
+#endif
     JSTYPE_LIMIT
 };
 
 /* Dense index into cached prototypes and class atoms for standard objects. */
 enum JSProtoKey {
 #define PROTOKEY_AND_INITIALIZER(name,init,clasp) JSProto_##name,
     JS_FOR_EACH_PROTOTYPE(PROTOKEY_AND_INITIALIZER)
 #undef PROTOKEY_AND_INITIALIZER
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -453,16 +453,21 @@ SOURCES += [
     'jsmath.cpp',
     'jsutil.cpp',
     'util/DoubleToString.cpp',
     'vm/Interpreter.cpp',
     'vm/JSAtom.cpp',
     'vm/ProfilingStack.cpp',
 ]
 
+if CONFIG['ENABLE_BIGINT']:
+    SOURCES += [
+        'vm/BigIntType.cpp',
+    ]
+
 if CONFIG['JS_POSIX_NSPR']:
     UNIFIED_SOURCES += [
         'vm/PosixNSPR.cpp',
     ]
 
 if CONFIG['MOZ_INSTRUMENTS']:
     SOURCES += [
         'devtools/Instruments.cpp',
--- a/js/src/util/StringBuffer.cpp
+++ b/js/src/util/StringBuffer.cpp
@@ -159,11 +159,19 @@ js::ValueToStringBufferSlow(JSContext* c
     if (v.isBoolean())
         return BooleanToStringBuffer(v.toBoolean(), sb);
     if (v.isNull())
         return sb.append(cx->names().null);
     if (v.isSymbol()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SYMBOL_TO_STRING);
         return false;
     }
+#ifdef ENABLE_BIGINT
+    if (v.isBigInt()) {
+        JSString* str = BigInt::toString(cx, v.toBigInt());
+        if (!str)
+            return false;
+        return sb.append(str);
+    }
+#endif
     MOZ_ASSERT(v.isUndefined());
     return sb.append(cx->names().undefined);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/vm/BigIntType.cpp
@@ -0,0 +1,83 @@
+/* -*- 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 "vm/BigIntType.h"
+
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/HashFunctions.h"
+
+#include "jsapi.h"
+
+#include "gc/Allocator.h"
+#include "gc/Tracer.h"
+#include "vm/JSContext.h"
+#include "vm/SelfHosting.h"
+
+using namespace js;
+
+BigInt*
+BigInt::create(JSContext* cx)
+{
+    BigInt* x = Allocate<BigInt>(cx);
+    if (!x)
+        return nullptr;
+    return x;
+}
+
+BigInt*
+BigInt::copy(JSContext* cx, HandleBigInt x)
+{
+    BigInt* bi = create(cx);
+    if (!bi)
+        return nullptr;
+    return bi;
+}
+
+JSLinearString*
+BigInt::toString(JSContext* cx, BigInt* x)
+{
+    return nullptr;
+}
+
+void
+BigInt::finalize(js::FreeOp* fop)
+{
+    return;
+}
+
+JSAtom*
+js::BigIntToAtom(JSContext* cx, BigInt* bi)
+{
+    JSString* str = BigInt::toString(cx, bi);
+    if (!str)
+        return nullptr;
+    return AtomizeString(cx, str);
+}
+
+bool
+BigInt::toBoolean()
+{
+    return false;
+}
+
+js::HashNumber
+BigInt::hash()
+{
+    return 0;
+}
+
+size_t
+BigInt::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    return 0;
+}
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<BigInt>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    MOZ_ASSERT(get().isTenured());
+    return js::gc::Arena::thingSize(get().asTenured().getAllocKind());
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/BigIntType.h
@@ -0,0 +1,60 @@
+/* -*- 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/. */
+
+#ifndef vm_BigIntType_h
+#define vm_BigIntType_h
+
+#include "gc/Barrier.h"
+#include "gc/GC.h"
+#include "gc/Heap.h"
+#include "js/AllocPolicy.h"
+#include "js/GCHashTable.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "vm/StringType.h"
+
+namespace JS {
+
+class BigInt final : public js::gc::TenuredCell
+{
+  private:
+    // The minimum allocation size is currently 16 bytes (see
+    // SortedArenaList in gc/ArenaList.h).
+    uint8_t unused_[js::gc::MinCellSize];
+
+  public:
+    // Allocate and initialize a BigInt value
+    static BigInt* create(JSContext* cx);
+
+    static const JS::TraceKind TraceKind = JS::TraceKind::BigInt;
+
+    void traceChildren(JSTracer* trc);
+
+    void finalize(js::FreeOp* fop);
+
+    js::HashNumber hash();
+
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+    static JSLinearString* toString(JSContext* cx, BigInt* x);
+    bool toBoolean();
+
+    static BigInt* copy(JSContext* cx, Handle<BigInt*> x);
+};
+
+static_assert(sizeof(BigInt) >= js::gc::MinCellSize,
+              "sizeof(BigInt) must be greater than the minimum allocation size");
+
+} // namespace JS
+
+namespace js {
+
+extern JSAtom*
+BigIntToAtom(JSContext* cx, JS::BigInt* bi);
+
+} // namespace js
+
+#endif
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -459,11 +459,12 @@
     macro(undefined, undefined, "undefined") \
     macro(object, object, "object") \
     macro(function, function, "function") \
     macro(string, string, "string") \
     macro(number, number, "number") \
     macro(boolean, boolean, "boolean") \
     macro(null, null, "null") \
     macro(symbol, symbol, "symbol") \
-    macro(defineDataPropertyIntrinsic, defineDataPropertyIntrinsic, "_DefineDataProperty") \
+    macro(bigint, bigint, "bigint") \
+    macro(defineDataPropertyIntrinsic, defineDataPropertyIntrinsic, "_DefineDataProperty")
 
 #endif /* vm_CommonPropertyNames_h */
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -27,16 +27,19 @@
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/Jit.h"
 #include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
 #include "vm/BytecodeUtil.h"
 #include "vm/Debugger.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
@@ -964,16 +967,20 @@ js::TypeOfValue(const Value& v)
     if (v.isNull())
         return JSTYPE_OBJECT;
     if (v.isUndefined())
         return JSTYPE_UNDEFINED;
     if (v.isObject())
         return TypeOfObject(&v.toObject());
     if (v.isBoolean())
         return JSTYPE_BOOLEAN;
+#ifdef ENABLE_BIGINT
+    if (v.isBigInt())
+        return JSTYPE_BIGINT;
+#endif
     MOZ_ASSERT(v.isSymbol());
     return JSTYPE_SYMBOL;
 }
 
 bool
 js::CheckClassHeritageOperation(JSContext* cx, HandleValue heritage)
 {
     if (IsConstructor(heritage))
@@ -4378,17 +4385,20 @@ js::GetProperty(JSContext* cx, HandleVal
     if (name == cx->names().length) {
         // Fast path for strings, arrays and arguments.
         if (GetLengthProperty(v, vp))
             return true;
     }
 
     // Optimize common cases like (2).toString() or "foo".valueOf() to not
     // create a wrapper object.
-    if (v.isPrimitive() && !v.isNullOrUndefined()) {
+    if (v.isPrimitive() &&
+        !v.isNullOrUndefined() &&
+        IF_BIGINT(!v.isBigInt(), true))
+    {
         NativeObject* proto;
         if (v.isNumber()) {
             proto = GlobalObject::getOrCreateNumberPrototype(cx, cx->global());
         } else if (v.isString()) {
             proto = GlobalObject::getOrCreateStringPrototype(cx, cx->global());
         } else if (v.isBoolean()) {
             proto = GlobalObject::getOrCreateBooleanPrototype(cx, cx->global());
         } else {
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -631,16 +631,24 @@ ToAtomSlow(JSContext* cx, typename Maybe
     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)
+            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)
 {
--- a/js/src/vm/JSCompartment-inl.h
+++ b/js/src/vm/JSCompartment-inl.h
@@ -116,16 +116,26 @@ JSCompartment::wrap(JSContext* cx, JS::M
     if (vp.isString()) {
         JS::RootedString str(cx, vp.toString());
         if (!wrap(cx, &str))
             return false;
         vp.setString(str);
         return true;
     }
 
+#ifdef ENABLE_BIGINT
+    if (vp.isBigInt()) {
+        JS::RootedBigInt bi(cx, vp.toBigInt());
+        if (!wrap(cx, &bi))
+            return false;
+        vp.setBigInt(bi);
+        return true;
+    }
+#endif
+
     MOZ_ASSERT(vp.isObject());
 
     /*
      * All that's left are objects.
      *
      * Object wrapping isn't the fastest thing in the world, in part because
      * we have to unwrap and invoke the prewrap hook to find the identity
      * object before we even start checking the cache. Neither of these
--- a/js/src/vm/JSCompartment.cpp
+++ b/js/src/vm/JSCompartment.cpp
@@ -340,16 +340,33 @@ JSCompartment::wrap(JSContext* cx, Mutab
         return false;
     if (!putWrapper(cx, CrossCompartmentKey(key), StringValue(copy)))
         return false;
 
     strp.set(copy);
     return true;
 }
 
+#ifdef ENABLE_BIGINT
+bool
+JSCompartment::wrap(JSContext* cx, MutableHandleBigInt bi)
+{
+    MOZ_ASSERT(cx->compartment() == this);
+
+    if (bi->zone() == cx->zone())
+        return true;
+
+    BigInt* copy = BigInt::copy(cx, bi);
+    if (!copy)
+        return false;
+    bi.set(copy);
+    return true;
+}
+#endif
+
 bool
 JSCompartment::getNonWrapperObjectForCurrentCompartment(JSContext* cx, MutableHandleObject obj)
 {
     // Ensure that we have entered a compartment.
     MOZ_ASSERT(cx->global());
 
     // If we have a cross-compartment wrapper, make sure that the cx isn't
     // associated with the self-hosting global. We don't want to create
--- a/js/src/vm/JSCompartment.h
+++ b/js/src/vm/JSCompartment.h
@@ -631,16 +631,19 @@ struct JSCompartment
     ~JSCompartment();
 
     MOZ_MUST_USE bool init(JSContext* maybecx);
 
   public:
     MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp);
 
     MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandleString strp);
+#ifdef ENABLE_BIGINT
+    MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandle<JS::BigInt*> bi);
+#endif
     MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandleObject obj);
     MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandle<js::PropertyDescriptor> desc);
     MOZ_MUST_USE bool wrap(JSContext* cx, JS::MutableHandle<JS::GCVector<JS::Value>> vec);
     MOZ_MUST_USE bool rewrap(JSContext* cx, JS::MutableHandleObject obj, JS::HandleObject existing);
 
     MOZ_MUST_USE bool putWrapper(JSContext* cx, const js::CrossCompartmentKey& wrapped,
                                  const js::Value& wrapper);
 
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -3246,19 +3246,30 @@ js::PrimitiveToObject(JSContext* cx, con
     if (v.isString()) {
         Rooted<JSString*> str(cx, v.toString());
         return StringObject::create(cx, str);
     }
     if (v.isNumber())
         return NumberObject::create(cx, v.toNumber());
     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 nullptr because BigIntObject has not been defined yet.
+    return nullptr;
+#else
     MOZ_ASSERT(v.isSymbol());
     RootedSymbol symbol(cx, v.toSymbol());
     return SymbolObject::create(cx, symbol);
+#endif
 }
 
 /*
  * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
  * already be an object, use ToObject. reportCantConvert controls how null and
  * undefined errors are reported.
  *
  * Callers must handle the already-object case.
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -8,16 +8,19 @@
 
 #include "gc/GC.h"
 #include "gc/Heap.h"
 #include "gc/Nursery.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "vm/ArrayObject.h"
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
 #include "vm/HelperThreads.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
 #include "vm/StringType.h"
 #include "vm/SymbolType.h"
@@ -551,16 +554,25 @@ StatsCellCallback(JSRuntime* rt, void* d
         }
         break;
       }
 
       case JS::TraceKind::Symbol:
         zStats->symbolsGCHeap += thingSize;
         break;
 
+#ifdef ENABLE_BIGINT
+      case JS::TraceKind::BigInt: {
+        JS::BigInt* bi = static_cast<BigInt*>(thing);
+        zStats->bigIntsGCHeap += thingSize;
+        zStats->bigIntsMallocHeap += bi->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        break;
+      }
+#endif
+
       case JS::TraceKind::BaseShape: {
         JS::ShapeInfo info;        // This zeroes all the sizes.
         info.shapesGCHeapBase += thingSize;
         // No malloc-heap measurements.
 
         zStats->shapeInfo.add(info);
         break;
       }
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -2066,17 +2066,25 @@ js::ToStringSlow(JSContext* cx, typename
         str = cx->names().null;
     } else if (v.isSymbol()) {
         MOZ_ASSERT(!cx->helperThread());
         if (allowGC) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                       JSMSG_SYMBOL_TO_STRING);
         }
         return nullptr;
-    } else {
+    }
+#ifdef ENABLE_BIGINT
+    else if (v.isBigInt()) {
+        if (!allowGC)
+            return nullptr;
+        str = BigInt::toString(cx, v.toBigInt());
+    }
+#endif
+    else {
         MOZ_ASSERT(v.isUndefined());
         str = cx->names().undefined;
     }
     return str;
 }
 
 template JSString*
 js::ToStringSlow<CanGC>(JSContext* cx, HandleValue arg);
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -185,16 +185,20 @@ PrimitiveTypeFlag(JSValueType type)
       case JSVAL_TYPE_INT32:
         return TYPE_FLAG_INT32;
       case JSVAL_TYPE_DOUBLE:
         return TYPE_FLAG_DOUBLE;
       case JSVAL_TYPE_STRING:
         return TYPE_FLAG_STRING;
       case JSVAL_TYPE_SYMBOL:
         return TYPE_FLAG_SYMBOL;
+#ifdef ENABLE_BIGINT
+      case JSVAL_TYPE_BIGINT:
+        return TYPE_FLAG_BIGINT;
+#endif
       case JSVAL_TYPE_MAGIC:
         return TYPE_FLAG_LAZYARGS;
       default:
         MOZ_CRASH("Bad JSValueType");
     }
 }
 
 inline JSValueType
@@ -210,16 +214,20 @@ TypeFlagPrimitive(TypeFlags flags)
       case TYPE_FLAG_INT32:
         return JSVAL_TYPE_INT32;
       case TYPE_FLAG_DOUBLE:
         return JSVAL_TYPE_DOUBLE;
       case TYPE_FLAG_STRING:
         return JSVAL_TYPE_STRING;
       case TYPE_FLAG_SYMBOL:
         return JSVAL_TYPE_SYMBOL;
+#ifdef ENABLE_BIGINT
+      case TYPE_FLAG_BIGINT:
+        return JSVAL_TYPE_BIGINT;
+#endif
       case TYPE_FLAG_LAZYARGS:
         return JSVAL_TYPE_MAGIC;
       default:
         MOZ_CRASH("Bad TypeFlags");
     }
 }
 
 /*
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -108,16 +108,20 @@ TypeSet::NonObjectTypeString(TypeSet::Ty
           case JSVAL_TYPE_INT32:
             return "int";
           case JSVAL_TYPE_DOUBLE:
             return "float";
           case JSVAL_TYPE_STRING:
             return "string";
           case JSVAL_TYPE_SYMBOL:
             return "symbol";
+#ifdef ENABLE_BIGINT
+          case JSVAL_TYPE_BIGINT:
+            return "BigInt";
+#endif
           case JSVAL_TYPE_MAGIC:
             return "lazyargs";
           default:
             MOZ_CRASH("Bad type");
         }
     }
     if (type.isUnknown())
         return "unknown";
@@ -780,16 +784,20 @@ TypeSet::print(FILE* fp)
     if (flags & TYPE_FLAG_INT32)
         fprintf(fp, " int");
     if (flags & TYPE_FLAG_DOUBLE)
         fprintf(fp, " float");
     if (flags & TYPE_FLAG_STRING)
         fprintf(fp, " string");
     if (flags & TYPE_FLAG_SYMBOL)
         fprintf(fp, " symbol");
+#ifdef ENABLE_BIGINT
+    if (flags & TYPE_FLAG_BIGINT)
+        fprintf(fp, " BigInt");
+#endif
     if (flags & TYPE_FLAG_LAZYARGS)
         fprintf(fp, " lazyargs");
 
     uint32_t objectCount = baseObjectCount();
     if (objectCount) {
         fprintf(fp, " object[%u]", objectCount);
 
         unsigned count = getObjectCount();
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -55,36 +55,48 @@ class HeapTypeSetKey;
 enum : uint32_t {
     TYPE_FLAG_UNDEFINED =   0x1,
     TYPE_FLAG_NULL      =   0x2,
     TYPE_FLAG_BOOLEAN   =   0x4,
     TYPE_FLAG_INT32     =   0x8,
     TYPE_FLAG_DOUBLE    =  0x10,
     TYPE_FLAG_STRING    =  0x20,
     TYPE_FLAG_SYMBOL    =  0x40,
-    TYPE_FLAG_LAZYARGS  =  0x80,
+#ifdef ENABLE_BIGINT
+    TYPE_FLAG_BIGINT    =  0x80,
+    TYPE_FLAG_LAZYARGS  = 0x100,
+    TYPE_FLAG_ANYOBJECT = 0x200,
+#else
+    TYPE_FLAG_LAZYARGS  = 0x80,
     TYPE_FLAG_ANYOBJECT = 0x100,
+#endif
 
     /* Mask containing all primitives */
     TYPE_FLAG_PRIMITIVE = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_BOOLEAN |
                           TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_STRING |
-                          TYPE_FLAG_SYMBOL,
+                          TYPE_FLAG_SYMBOL |
+                          IF_BIGINT(TYPE_FLAG_BIGINT, 0),
 
     /* Mask/shift for the number of objects in objectSet */
+#ifdef ENABLE_BIGINT
+    TYPE_FLAG_OBJECT_COUNT_MASK     = 0x3c00,
+    TYPE_FLAG_OBJECT_COUNT_SHIFT    = 10,
+#else
     TYPE_FLAG_OBJECT_COUNT_MASK     = 0x3e00,
     TYPE_FLAG_OBJECT_COUNT_SHIFT    = 9,
+#endif
     TYPE_FLAG_OBJECT_COUNT_LIMIT    = 7,
     TYPE_FLAG_DOMOBJECT_COUNT_LIMIT =
         TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT,
 
     /* Whether the contents of this type set are totally unknown. */
     TYPE_FLAG_UNKNOWN             = 0x00004000,
 
     /* Mask of normal type flags on a type set. */
-    TYPE_FLAG_BASE_MASK           = 0x000041ff,
+    TYPE_FLAG_BASE_MASK           = TYPE_FLAG_PRIMITIVE | TYPE_FLAG_LAZYARGS | TYPE_FLAG_ANYOBJECT | TYPE_FLAG_UNKNOWN,
 
     /* Additional flags for HeapTypeSet sets. */
 
     /*
      * Whether the property has ever been deleted or reconfigured to behave
      * differently from a plain data property, other than making the property
      * non-writable.
      */
@@ -105,16 +117,20 @@ enum : uint32_t {
      * If the property is definite, mask and shift storing the slot + 1.
      * Otherwise these bits are clear.
      */
     TYPE_FLAG_DEFINITE_MASK       = 0xfffc0000,
     TYPE_FLAG_DEFINITE_SHIFT      = 18
 };
 typedef uint32_t TypeFlags;
 
+static_assert(TYPE_FLAG_PRIMITIVE < TYPE_FLAG_ANYOBJECT &&
+              TYPE_FLAG_LAZYARGS < TYPE_FLAG_ANYOBJECT,
+              "TYPE_FLAG_ANYOBJECT should be greater than primitive type flags");
+
 /* Flags and other state stored in ObjectGroup::Flags */
 enum : uint32_t {
     /* Whether this group is associated with some allocation site. */
     OBJECT_FLAG_FROM_ALLOCATION_SITE  = 0x1,
 
     /* Whether this group is associated with a single object. */
     OBJECT_FLAG_SINGLETON             = 0x2,
 
@@ -361,16 +377,19 @@ class TypeSet
 
     static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
     static inline Type NullType()      { return Type(JSVAL_TYPE_NULL); }
     static inline Type BooleanType()   { return Type(JSVAL_TYPE_BOOLEAN); }
     static inline Type Int32Type()     { return Type(JSVAL_TYPE_INT32); }
     static inline Type DoubleType()    { return Type(JSVAL_TYPE_DOUBLE); }
     static inline Type StringType()    { return Type(JSVAL_TYPE_STRING); }
     static inline Type SymbolType()    { return Type(JSVAL_TYPE_SYMBOL); }
+#ifdef ENABLE_BIGINT
+    static inline Type BigIntType()    { return Type(JSVAL_TYPE_BIGINT); }
+#endif
     static inline Type MagicArgType()  { return Type(JSVAL_TYPE_MAGIC); }
     static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); }
     static inline Type UnknownType()   { return Type(JSVAL_TYPE_UNKNOWN); }
 
     static inline Type PrimitiveType(JSValueType type) {
         MOZ_ASSERT(type < JSVAL_TYPE_UNKNOWN);
         return Type(type);
     }
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -17,16 +17,19 @@
 
 #include "jit/IonCode.h"
 #include "js/Debug.h"
 #include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
 #include "util/Text.h"
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"
+#endif
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
@@ -198,17 +201,23 @@ Node::exposeToJS() const
             v.setUndefined();
         } else {
             v.setObject(obj);
         }
     } else if (is<JSString>()) {
         v.setString(as<JSString>());
     } else if (is<JS::Symbol>()) {
         v.setSymbol(as<JS::Symbol>());
-    } else {
+    }
+#ifdef ENABLE_BIGINT
+    else if (is<BigInt>()) {
+        v.setBigInt(as<BigInt>());
+    }
+#endif
+    else {
         v.setUndefined();
     }
 
     ExposeValueToActiveJS(v);
 
     return v;
 }
 
@@ -310,16 +319,19 @@ TracerConcrete<Referent>::zone() const
 template JS::Zone* TracerConcrete<JSScript>::zone() const;
 template JS::Zone* TracerConcrete<js::LazyScript>::zone() const;
 template JS::Zone* TracerConcrete<js::Shape>::zone() const;
 template JS::Zone* TracerConcrete<js::BaseShape>::zone() const;
 template JS::Zone* TracerConcrete<js::ObjectGroup>::zone() const;
 template JS::Zone* TracerConcrete<js::RegExpShared>::zone() const;
 template JS::Zone* TracerConcrete<js::Scope>::zone() const;
 template JS::Zone* TracerConcrete<JS::Symbol>::zone() const;
+#ifdef ENABLE_BIGINT
+template JS::Zone* TracerConcrete<BigInt>::zone() const;
+#endif
 template JS::Zone* TracerConcrete<JSString>::zone() const;
 
 template<typename Referent>
 UniquePtr<EdgeRange>
 TracerConcrete<Referent>::edges(JSContext* cx, bool wantNames) const {
     UniquePtr<SimpleEdgeRange, JS::DeletePolicy<SimpleEdgeRange>> range(js_new<SimpleEdgeRange>());
     if (!range)
         return nullptr;
@@ -333,16 +345,19 @@ TracerConcrete<Referent>::edges(JSContex
 template UniquePtr<EdgeRange> TracerConcrete<JSScript>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::LazyScript>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::Shape>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::BaseShape>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::ObjectGroup>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::RegExpShared>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::Scope>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<JS::Symbol>::edges(JSContext* cx, bool wantNames) const;
+#ifdef ENABLE_BIGINT
+template UniquePtr<EdgeRange> TracerConcrete<BigInt>::edges(JSContext* cx, bool wantNames) const;
+#endif
 template UniquePtr<EdgeRange> TracerConcrete<JSString>::edges(JSContext* cx, bool wantNames) const;
 
 template<typename Referent>
 JSCompartment*
 TracerConcreteWithCompartment<Referent>::compartment() const
 {
     return TracerBase::get().compartment();
 }
@@ -388,16 +403,19 @@ Concrete<JSObject>::jsObjectConstructorN
     if (!JS_CopyStringChars(cx, chars, name))
         return false;
 
     outName[len] = '\0';
     return true;
 }
 
 const char16_t Concrete<JS::Symbol>::concreteTypeName[] = u"JS::Symbol";
+#ifdef ENABLE_BIGINT
+const char16_t Concrete<BigInt>::concreteTypeName[] = u"JS::BigInt";
+#endif
 const char16_t Concrete<JSScript>::concreteTypeName[] = u"JSScript";
 const char16_t Concrete<js::LazyScript>::concreteTypeName[] = u"js::LazyScript";
 const char16_t Concrete<js::jit::JitCode>::concreteTypeName[] = u"js::jit::JitCode";
 const char16_t Concrete<js::Shape>::concreteTypeName[] = u"js::Shape";
 const char16_t Concrete<js::BaseShape>::concreteTypeName[] = u"js::BaseShape";
 const char16_t Concrete<js::ObjectGroup>::concreteTypeName[] = u"js::ObjectGroup";
 const char16_t Concrete<js::Scope>::concreteTypeName[] = u"js::Scope";
 const char16_t Concrete<js::RegExpShared>::concreteTypeName[] = u"js::RegExpShared";