Bug 1539690 - Make a JS::RegExpFlag public API for flag bits, and make a JS::RegExpFlags flag-set abstraction. r=sfink
authorJeff Walden <jwalden@mit.edu>
Tue, 02 Apr 2019 03:16:43 +0000
changeset 467507 4b73382247510248b4c08283af86545ba777b627
parent 467506 68ac00e863e05091280bf23765607af045cba3cc
child 467508 b30fbb6c1b6a2db98c403c703af316d2f92bc245
push id35799
push usercbrindusan@mozilla.com
push dateTue, 02 Apr 2019 08:35:12 +0000
treeherdermozilla-central@ea0977445697 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1539690
milestone68.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 1539690 - Make a JS::RegExpFlag public API for flag bits, and make a JS::RegExpFlags flag-set abstraction. r=sfink Differential Revision: https://phabricator.services.mozilla.com/D25177
dom/base/nsContentUtils.cpp
js/ipc/WrapperAnswer.cpp
js/ipc/WrapperOwner.cpp
js/public/RegExp.h
js/public/RegExpFlags.h
js/src/builtin/RegExp.cpp
js/src/builtin/TestingFunctions.cpp
js/src/frontend/BinAST.yaml
js/src/frontend/BinASTParser.cpp
js/src/frontend/Parser.cpp
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/irregexp/RegExpParser.cpp
js/src/irregexp/RegExpParser.h
js/src/jit/CodeGenerator.cpp
js/src/jit/MCallOptimize.cpp
js/src/jsapi-tests/testRegExp.cpp
js/src/jsapi-tests/tests.h
js/src/moz.build
js/src/vm/RegExpConstants.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpShared.h
js/src/vm/RegExpStatics.h
js/src/vm/StructuredClone.cpp
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6511,17 +6511,17 @@ bool nsContentUtils::IsPatternMatching(n
 
   // The pattern has to match the entire value.
   aPattern.InsertLiteral(u"^(?:", 0);
   aPattern.AppendLiteral(")$");
 
   JS::Rooted<JSObject*> re(
       cx,
       JS::NewUCRegExpObject(cx, static_cast<char16_t*>(aPattern.BeginWriting()),
-                            aPattern.Length(), JS::RegExpFlags::Unicode));
+                            aPattern.Length(), JS::RegExpFlag::Unicode));
   if (!re) {
     // Remove extra patterns added above to report with the original pattern.
     aPattern.Cut(0, 4);
     aPattern.Cut(aPattern.Length() - 2, 2);
     ReportPatternCompileFailure(aPattern, aDocument, cx);
     return true;
   }
 
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -767,17 +767,17 @@ bool WrapperAnswer::RecvRegExpToShared(c
     return fail(jsapi, rs);
   }
   nsAutoJSString sourceStr;
   if (!sourceStr.init(cx, sourceJSStr)) {
     return fail(jsapi, rs);
   }
   source->Assign(sourceStr);
 
-  *flags = JS::GetRegExpFlags(cx, obj);
+  *flags = JS::GetRegExpFlags(cx, obj).value();
 
   return ok(rs);
 }
 
 bool WrapperAnswer::RecvGetPropertyKeys(const ObjectId& objId,
                                         const uint32_t& flags, ReturnStatus* rs,
                                         nsTArray<JSIDVariant>* ids) {
   if (!IsInAutomation()) {
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -7,16 +7,17 @@
 
 #include "WrapperOwner.h"
 #include "JavaScriptLogging.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "jsfriendapi.h"
 #include "js/CharacterEncoding.h"
 #include "js/RegExp.h"
+#include "js/RegExpFlags.h"
 #include "xpcprivate.h"
 #include "WrapperFactory.h"
 
 #include "nsIDocShellTreeItem.h"
 #include "mozilla/dom/Document.h"
 
 using namespace js;
 using namespace JS;
@@ -828,17 +829,18 @@ RegExpShared* WrapperOwner::regexp_toSha
   }
   LOG_STACK();
 
   if (!ok(cx, status)) {
     return nullptr;
   }
 
   RootedObject regexp(cx);
-  regexp = JS::NewUCRegExpObject(cx, source.get(), source.Length(), flags);
+  regexp = JS::NewUCRegExpObject(cx, source.get(), source.Length(),
+                                 RegExpFlags(flags));
   if (!regexp) {
     return nullptr;
   }
 
   return js::RegExpToSharedNonInline(cx, regexp);
 }
 
 void CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const {
--- a/js/public/RegExp.h
+++ b/js/public/RegExp.h
@@ -8,64 +8,39 @@
 
 #ifndef js_RegExp_h
 #define js_RegExp_h
 
 #include <stddef.h>  // size_t
 
 #include "jstypes.h"  // JS_PUBLIC_API
 
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "js/RootingAPI.h"  // JS::{,Mutable}Handle
 #include "js/Value.h"       // JS::Value
 
 struct JSContext;
 class JSString;
 
 namespace JS {
 
 /**
- * A namespace for all regular expression flags as they appear in the APIs below
- * as flags values.
- */
-struct RegExpFlags {
- public:
-  /**
-   * Interpret regular expression source text case-insensitively by folding
-   * uppercase letters to lowercase, i.e. /i.
-   */
-  static constexpr unsigned IgnoreCase = 0b0'0001;
-
-  /**
-   * Act globally and find *all* matches (rather than stopping after just the
-   * first one), i.e. /g.
-   */
-  static constexpr unsigned Global = 0b0'0010;
-
-  /** Treat ^ and $ as begin and end of line, i.e. /m. */
-  static constexpr unsigned Multiline = 0b0'0100;
-
-  /** Only match starting from <regular expression>.lastIndex, i.e. /y. */
-  static constexpr unsigned Sticky = 0b0'1000;
-
-  /** Use Unicode semantics, i.e. /u. */
-  static constexpr unsigned Unicode = 0b1'0000;
-};
-
-/**
  * Create a new RegExp for the given Latin-1-encoded bytes and flags.
  */
 extern JS_PUBLIC_API JSObject* NewRegExpObject(JSContext* cx, const char* bytes,
-                                               size_t length, unsigned flags);
+                                               size_t length,
+                                               RegExpFlags flags);
 
 /**
  * Create a new RegExp for the given source and flags.
  */
 extern JS_PUBLIC_API JSObject* NewUCRegExpObject(JSContext* cx,
                                                  const char16_t* chars,
-                                                 size_t length, unsigned flags);
+                                                 size_t length,
+                                                 RegExpFlags flags);
 
 extern JS_PUBLIC_API bool SetRegExpInput(JSContext* cx, Handle<JSObject*> obj,
                                          Handle<JSString*> input);
 
 extern JS_PUBLIC_API bool ClearRegExpStatics(JSContext* cx,
                                              Handle<JSObject*> obj);
 
 extern JS_PUBLIC_API bool ExecuteRegExp(JSContext* cx, Handle<JSObject*> obj,
@@ -90,20 +65,20 @@ extern JS_PUBLIC_API bool ExecuteRegExpN
  * This method returns true with |*isRegExp == false| when passed an ES6 proxy
  * whose target is a RegExp, or when passed a revoked proxy.
  */
 extern JS_PUBLIC_API bool ObjectIsRegExp(JSContext* cx, Handle<JSObject*> obj,
                                          bool* isRegExp);
 
 /**
  * Given a RegExp object (or a wrapper around one), return the set of all
- * JS::RegExpFlags::* for it.
+ * JS::RegExpFlag::* for it.
  */
-extern JS_PUBLIC_API unsigned GetRegExpFlags(JSContext* cx,
-                                             Handle<JSObject*> obj);
+extern JS_PUBLIC_API RegExpFlags GetRegExpFlags(JSContext* cx,
+                                                Handle<JSObject*> obj);
 
 /**
  * Return the source text for a RegExp object (or a wrapper around one), or null
  * on failure.
  */
 extern JS_PUBLIC_API JSString* GetRegExpSource(JSContext* cx,
                                                Handle<JSObject*> obj);
 
copy from js/public/RegExp.h
copy to js/public/RegExpFlags.h
--- a/js/public/RegExp.h
+++ b/js/public/RegExpFlags.h
@@ -1,112 +1,124 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* Regular expression-related operations. */
+/* Regular expression flags. */
 
-#ifndef js_RegExp_h
-#define js_RegExp_h
-
-#include <stddef.h>  // size_t
+#ifndef js_RegExpFlags_h
+#define js_RegExpFlags_h
 
-#include "jstypes.h"  // JS_PUBLIC_API
+#include "mozilla/Assertions.h"  // MOZ_ASSERT
+#include "mozilla/Attributes.h"  // MOZ_IMPLICIT
 
-#include "js/RootingAPI.h"  // JS::{,Mutable}Handle
-#include "js/Value.h"       // JS::Value
-
-struct JSContext;
-class JSString;
+#include <stdint.h>  // uint8_t
 
 namespace JS {
 
 /**
- * A namespace for all regular expression flags as they appear in the APIs below
- * as flags values.
+ * Regular expression flag values, suitable for initializing a collection of
+ * regular expression flags as defined below in |RegExpFlags|.  Flags are listed
+ * in alphabetical order by syntax -- /g, /i, /m, /u, /y.
  */
-struct RegExpFlags {
+class RegExpFlag {
+  // WARNING TO SPIDERMONKEY HACKERS (embedders must assume these values can
+  // change):
+  //
+  // Flag-bit values appear in XDR and structured clone data formats, so none of
+  // these values can be changed (including to assign values in numerically
+  // ascending order) unless you also add a translation layer.
+
  public:
   /**
-   * Interpret regular expression source text case-insensitively by folding
-   * uppercase letters to lowercase, i.e. /i.
-   */
-  static constexpr unsigned IgnoreCase = 0b0'0001;
-
-  /**
    * Act globally and find *all* matches (rather than stopping after just the
    * first one), i.e. /g.
    */
-  static constexpr unsigned Global = 0b0'0010;
+  static constexpr uint8_t Global = 0b0'0010;
+
+  /**
+   * Interpret regular expression source text case-insensitively by folding
+   * uppercase letters to lowercase, i.e. /i.
+   */
+  static constexpr uint8_t IgnoreCase = 0b0'0001;
 
   /** Treat ^ and $ as begin and end of line, i.e. /m. */
-  static constexpr unsigned Multiline = 0b0'0100;
+  static constexpr uint8_t Multiline = 0b0'0100;
+
+  /** Use Unicode semantics, i.e. /u. */
+  static constexpr uint8_t Unicode = 0b1'0000;
 
   /** Only match starting from <regular expression>.lastIndex, i.e. /y. */
-  static constexpr unsigned Sticky = 0b0'1000;
+  static constexpr uint8_t Sticky = 0b0'1000;
 
-  /** Use Unicode semantics, i.e. /u. */
-  static constexpr unsigned Unicode = 0b1'0000;
+  /** No regular expression flags. */
+  static constexpr uint8_t NoFlags = 0b0'0000;
+
+  /** All regular expression flags. */
+  static constexpr uint8_t AllFlags = 0b1'1111;
 };
 
 /**
- * Create a new RegExp for the given Latin-1-encoded bytes and flags.
+ * A collection of regular expression flags.  Individual flag values may be
+ * combined into a collection using bitwise operators.
  */
-extern JS_PUBLIC_API JSObject* NewRegExpObject(JSContext* cx, const char* bytes,
-                                               size_t length, unsigned flags);
+class RegExpFlags {
+ public:
+  using Flag = uint8_t;
 
-/**
- * Create a new RegExp for the given source and flags.
- */
-extern JS_PUBLIC_API JSObject* NewUCRegExpObject(JSContext* cx,
-                                                 const char16_t* chars,
-                                                 size_t length, unsigned flags);
+ private:
+  Flag flags_;
+
+ public:
+  RegExpFlags() = default;
 
-extern JS_PUBLIC_API bool SetRegExpInput(JSContext* cx, Handle<JSObject*> obj,
-                                         Handle<JSString*> input);
+  MOZ_IMPLICIT RegExpFlags(Flag flags) : flags_(flags) {
+    MOZ_ASSERT((flags & RegExpFlag::AllFlags) == flags,
+               "flags must not contain unrecognized flags");
+  }
 
-extern JS_PUBLIC_API bool ClearRegExpStatics(JSContext* cx,
-                                             Handle<JSObject*> obj);
+  RegExpFlags(const RegExpFlags&) = default;
 
-extern JS_PUBLIC_API bool ExecuteRegExp(JSContext* cx, Handle<JSObject*> obj,
-                                        Handle<JSObject*> reobj,
-                                        char16_t* chars, size_t length,
-                                        size_t* indexp, bool test,
-                                        MutableHandle<Value> rval);
+  bool operator==(const RegExpFlags& other) const {
+    return flags_ == other.flags_;
+  }
 
-/* RegExp interface for clients without a global object. */
+  bool operator!=(const RegExpFlags& other) const { return !(*this == other); }
+
+  RegExpFlags operator&(Flag flag) const { return RegExpFlags(flags_ & flag); }
+
+  RegExpFlags operator|(Flag flag) const { return RegExpFlags(flags_ | flag); }
 
-extern JS_PUBLIC_API bool ExecuteRegExpNoStatics(JSContext* cx,
-                                                 Handle<JSObject*> reobj,
-                                                 char16_t* chars, size_t length,
-                                                 size_t* indexp, bool test,
-                                                 MutableHandle<Value> rval);
+  RegExpFlags operator^(Flag flag) const { return RegExpFlags(flags_ ^ flag); }
+
+  RegExpFlags operator~() const { return RegExpFlags(~flags_); }
+
+  bool global() const { return flags_ & RegExpFlag::Global; }
+  bool ignoreCase() const { return flags_ & RegExpFlag::IgnoreCase; }
+  bool multiline() const { return flags_ & RegExpFlag::Multiline; }
+  bool unicode() const { return flags_ & RegExpFlag::Unicode; }
+  bool sticky() const { return flags_ & RegExpFlag::Sticky; }
+
+  explicit operator bool() const { return flags_ != 0; }
+
+  Flag value() const { return flags_; }
+};
 
-/**
- * On success, returns true, setting |*isRegExp| to true if |obj| is a RegExp
- * object or a wrapper around one, or to false if not.  Returns false on
- * failure.
- *
- * This method returns true with |*isRegExp == false| when passed an ES6 proxy
- * whose target is a RegExp, or when passed a revoked proxy.
- */
-extern JS_PUBLIC_API bool ObjectIsRegExp(JSContext* cx, Handle<JSObject*> obj,
-                                         bool* isRegExp);
+inline RegExpFlags& operator&=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+  flags = flags & flag;
+  return flags;
+}
 
-/**
- * Given a RegExp object (or a wrapper around one), return the set of all
- * JS::RegExpFlags::* for it.
- */
-extern JS_PUBLIC_API unsigned GetRegExpFlags(JSContext* cx,
-                                             Handle<JSObject*> obj);
+inline RegExpFlags& operator|=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+  flags = flags | flag;
+  return flags;
+}
 
-/**
- * Return the source text for a RegExp object (or a wrapper around one), or null
- * on failure.
- */
-extern JS_PUBLIC_API JSString* GetRegExpSource(JSContext* cx,
-                                               Handle<JSObject*> obj);
+inline RegExpFlags& operator^=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+  flags = flags ^ flag;
+  return flags;
+}
 
 }  // namespace JS
 
-#endif  // js_RegExp_h
+#endif  // js_RegExpFlags_h
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1,40 +1,45 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/RegExp.h"
 
+#include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/TypeTraits.h"
 
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
 #include "jit/InlinableNatives.h"
 #include "js/PropertySpec.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpStatics.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/ObjectOperations-inl.h"
 
 using namespace js;
 
+using mozilla::AssertedCast;
 using mozilla::CheckedInt;
 using mozilla::IsAsciiDigit;
 
 using JS::CompileOptions;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 16-25.
  */
 bool js::CreateRegExpMatchResult(JSContext* cx, HandleString input,
                                  const MatchPairs& matches,
                                  MutableHandleValue rval) {
@@ -182,26 +187,26 @@ bool js::ExecuteRegExpLegacy(JSContext* 
     rval.setBoolean(true);
     return true;
   }
 
   return CreateRegExpMatchResult(cx, input, matches, rval);
 }
 
 static bool CheckPatternSyntaxSlow(JSContext* cx, HandleAtom pattern,
-                                   RegExpFlag flags) {
+                                   RegExpFlags flags) {
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   CompileOptions options(cx);
   frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
   return irregexp::ParsePatternSyntax(dummyTokenStream, allocScope.alloc(),
-                                      pattern, flags & UnicodeFlag);
+                                      pattern, flags.unicode());
 }
 
 static RegExpShared* CheckPatternSyntax(JSContext* cx, HandleAtom pattern,
-                                        RegExpFlag flags) {
+                                        RegExpFlags flags) {
   // If we already have a RegExpShared for this pattern/flags, we can
   // avoid the much slower CheckPatternSyntaxSlow call.
 
   if (RegExpShared* shared = cx->zone()->regExps().maybeGet(pattern, flags)) {
 #ifdef DEBUG
     // Assert the pattern is valid.
     if (!CheckPatternSyntaxSlow(cx, pattern, flags)) {
       MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
@@ -247,17 +252,17 @@ static bool RegExpInitializeIgnoringLast
     /* Step 2. */
     pattern = ToAtom<CanGC>(cx, patternValue);
     if (!pattern) {
       return false;
     }
   }
 
   /* Step 3. */
-  RegExpFlag flags = RegExpFlag(0);
+  RegExpFlags flags = RegExpFlag::NoFlags;
   if (!flagsValue.isUndefined()) {
     /* Step 4. */
     RootedString flagStr(cx, ToString<CanGC>(cx, flagsValue));
     if (!flagStr) {
       return false;
     }
 
     /* Step 5. */
@@ -358,17 +363,17 @@ MOZ_ALWAYS_INLINE bool regexp_compile_im
     }
 
     // Beware!  |patternObj| might be a proxy into another compartment, so
     // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
     // don't reuse the RegExpShared below.
     RootedObject patternObj(cx, &patternValue.toObject());
 
     RootedAtom sourceAtom(cx);
-    RegExpFlag flags;
+    RegExpFlags flags = RegExpFlag::NoFlags;
     {
       // Step 3b.
       RegExpShared* shared = RegExpToShared(cx, patternObj);
       if (!shared) {
         return false;
       }
 
       sourceAtom = shared->getSource();
@@ -456,17 +461,17 @@ bool js::regexp_construct(JSContext* cx,
     return false;
   }
   if (cls == ESClass::RegExp) {
     // Beware!  |patternObj| might be a proxy into another compartment, so
     // don't assume |patternObj.is<RegExpObject>()|.
     RootedObject patternObj(cx, &patternValue.toObject());
 
     RootedAtom sourceAtom(cx);
-    RegExpFlag flags;
+    RegExpFlags flags;
     RootedRegExpShared shared(cx);
     {
       // Step 4.a.
       shared = RegExpToShared(cx, patternObj);
       if (!shared) {
         return false;
       }
       sourceAtom = shared->getSource();
@@ -490,31 +495,31 @@ bool js::regexp_construct(JSContext* cx,
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, GenericObject, proto));
     if (!regexp) {
       return false;
     }
 
     // Step 8.
     if (args.hasDefined(1)) {
       // Step 4.c / 21.2.3.2.2 RegExpInitialize step 4.
-      RegExpFlag flagsArg = RegExpFlag(0);
+      RegExpFlags flagsArg = RegExpFlag::NoFlags;
       RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
       if (!flagStr) {
         return false;
       }
       if (!ParseRegExpFlags(cx, flagStr, &flagsArg)) {
         return false;
       }
 
       // Don't reuse the RegExpShared if we have different flags.
       if (flags != flagsArg) {
         shared = nullptr;
       }
 
-      if (!(flags & UnicodeFlag) && flagsArg & UnicodeFlag) {
+      if (!flags.unicode() && flagsArg.unicode()) {
         // Have to check syntax again when adding 'u' flag.
 
         // ES 2017 draft rev 9b49a888e9dfe2667008a01b2754c3662059ae56
         // 21.2.3.2.2 step 7.
         shared = CheckPatternSyntax(cx, sourceAtom, flagsArg);
         if (!shared) {
           return false;
         }
@@ -589,26 +594,26 @@ bool js::regexp_construct_raw_flags(JSCo
 
   // Step 4.a.
   RootedAtom sourceAtom(cx, AtomizeString(cx, args[0].toString()));
   if (!sourceAtom) {
     return false;
   }
 
   // Step 4.c.
-  int32_t flags = int32_t(args[1].toNumber());
+  RegExpFlags flags = AssertedCast<uint8_t>(int32_t(args[1].toNumber()));
 
   // Step 7.
   RegExpObject* regexp = RegExpAlloc(cx, GenericObject);
   if (!regexp) {
     return false;
   }
 
   // Step 8.
-  regexp->initAndZeroLastIndex(sourceAtom, RegExpFlag(flags), cx);
+  regexp->initAndZeroLastIndex(sourceAtom, flags, cx);
   args.rval().setObject(*regexp);
   return true;
 }
 
 MOZ_ALWAYS_INLINE bool IsRegExpPrototype(HandleValue v, JSContext* cx) {
   return (v.isObject() &&
           cx->global()->maybeGetRegExpPrototype() == &v.toObject());
 }
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -44,16 +44,17 @@
 #include "js/ArrayBuffer.h"  // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/LocaleSensitive.h"
 #include "js/PropertySpec.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
@@ -92,16 +93,18 @@
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 
 using JS::AutoStableStringChars;
 using JS::CompileOptions;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 using JS::SourceOwnership;
 using JS::SourceText;
 
 // If fuzzingSafe is set, remove functionality that could cause problems with
 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
 mozilla::Atomic<bool> fuzzingSafe(false);
 
 // If disableOOMFunctions is set, disable functionality that causes artificial
@@ -4847,17 +4850,17 @@ static bool ParseRegExp(JSContext* cx, u
     return false;
   }
 
   if (!args[0].isString()) {
     ReportUsageErrorASCII(cx, callee, "First argument must be a String");
     return false;
   }
 
-  RegExpFlag flags = RegExpFlag(0);
+  RegExpFlags flags = RegExpFlag::NoFlags;
   if (!args.get(1).isUndefined()) {
     if (!args.get(1).isString()) {
       ReportUsageErrorASCII(cx, callee,
                             "Second argument, if present, must be a String");
       return false;
     }
     RootedString flagStr(cx, args[1].toString());
     if (!ParseRegExpFlags(cx, flagStr, &flags)) {
@@ -4882,19 +4885,17 @@ static bool ParseRegExp(JSContext* cx, u
 
   CompileOptions options(cx);
   frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
   // Data lifetime is controlled by LifoAllocScope.
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   irregexp::RegExpCompileData data;
   if (!irregexp::ParsePattern(dummyTokenStream, allocScope.alloc(), pattern,
-                              flags & MultilineFlag, match_only,
-                              flags & UnicodeFlag, flags & IgnoreCaseFlag,
-                              flags & GlobalFlag, flags & StickyFlag, &data)) {
+                              match_only, flags, &data)) {
     return false;
   }
 
   RootedObject obj(
       cx, ConvertRegExpTreeToObject(cx, allocScope.alloc(), data.tree));
   if (!obj) {
     return false;
   }
--- a/js/src/frontend/BinAST.yaml
+++ b/js/src/frontend/BinAST.yaml
@@ -39,21 +39,24 @@ cpp:
     #include "mozilla/Vector.h"
 
     #include "frontend/BinAST-macros.h"
     #include "frontend/BinASTTokenReaderMultipart.h"
     #include "frontend/FullParseHandler.h"
     #include "frontend/ParseNode.h"
     #include "frontend/Parser.h"
     #include "frontend/SharedContext.h"
-
+    #include "js/RegExpFlags.h"  //  JS::RegExpFlag, JS::RegExpFlags
     #include "vm/RegExpObject.h"
 
     #include "frontend/ParseContext-inl.h"
 
+    using JS::RegExpFlag;
+    using JS::RegExpFlags;
+
     namespace js {
     namespace frontend {
 
     // Compare a bunch of `uint8_t` values (as returned by the tokenizer_) with
     // a string literal (and ONLY a string literal).
     template<typename Tok, size_t N>
     bool operator==(const typename Tok::Chars& left, const char (&right)[N]) {
       return Tok::equals(left, right);
@@ -1276,28 +1279,28 @@ LiteralPropertyName:
 LiteralRegExpExpression:
   fields:
     flags:
       block:
         replace: |
           Chars flags(cx_);
           MOZ_TRY(tokenizer_->readChars(flags));
   build: |
-    RegExpFlag reflags = NoFlags;
+    RegExpFlags reflags = RegExpFlag::NoFlags;
     for (auto c : flags) {
-      if (c == 'g' && !(reflags & GlobalFlag)) {
-        reflags = RegExpFlag(reflags | GlobalFlag);
-      } else if (c == 'i' && !(reflags & IgnoreCaseFlag)) {
-        reflags = RegExpFlag(reflags | IgnoreCaseFlag);
-      } else if (c == 'm' && !(reflags & MultilineFlag)) {
-        reflags = RegExpFlag(reflags | MultilineFlag);
-      } else if (c == 'y' && !(reflags & StickyFlag)) {
-        reflags = RegExpFlag(reflags | StickyFlag);
-      } else if (c == 'u' && !(reflags & UnicodeFlag)) {
-        reflags = RegExpFlag(reflags | UnicodeFlag);
+      if (c == 'g' && !reflags.global()) {
+        reflags |= RegExpFlag::Global;
+      } else if (c == 'i' && !reflags.ignoreCase()) {
+        reflags |= RegExpFlag::IgnoreCase;
+      } else if (c == 'm' && !reflags.multiline()) {
+        reflags |= RegExpFlag::Multiline;
+      } else if (c == 'u' && !reflags.unicode()) {
+        reflags |= RegExpFlag::Unicode;
+      } else if (c == 'y' && !reflags.sticky()) {
+        reflags |= RegExpFlag::Sticky;
       } else {
         return raiseError("Invalid regexp flags");
       }
     }
 
 
     Rooted<RegExpObject*> reobj(cx_);
     BINJS_TRY_VAR(reobj,
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -19,21 +19,24 @@
 #include "mozilla/Vector.h"
 
 #include "frontend/BinAST-macros.h"
 #include "frontend/BinASTTokenReaderMultipart.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
-
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "vm/RegExpObject.h"
 
 #include "frontend/ParseContext-inl.h"
 
+using JS::RegExpFlag;
+using JS::RegExpFlags;
+
 namespace js {
 namespace frontend {
 
 // Compare a bunch of `uint8_t` values (as returned by the tokenizer_) with
 // a string literal (and ONLY a string literal).
 template <typename Tok, size_t N>
 bool operator==(const typename Tok::Chars& left, const char (&right)[N]) {
   return Tok::equals(left, right);
@@ -3517,28 +3520,28 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   RootedAtom pattern(cx_);
   MOZ_TRY_VAR(pattern, tokenizer_->readAtom());
   Chars flags(cx_);
   MOZ_TRY(tokenizer_->readChars(flags));
 
-  RegExpFlag reflags = NoFlags;
+  RegExpFlags reflags = RegExpFlag::NoFlags;
   for (auto c : flags) {
-    if (c == 'g' && !(reflags & GlobalFlag)) {
-      reflags = RegExpFlag(reflags | GlobalFlag);
-    } else if (c == 'i' && !(reflags & IgnoreCaseFlag)) {
-      reflags = RegExpFlag(reflags | IgnoreCaseFlag);
-    } else if (c == 'm' && !(reflags & MultilineFlag)) {
-      reflags = RegExpFlag(reflags | MultilineFlag);
-    } else if (c == 'y' && !(reflags & StickyFlag)) {
-      reflags = RegExpFlag(reflags | StickyFlag);
-    } else if (c == 'u' && !(reflags & UnicodeFlag)) {
-      reflags = RegExpFlag(reflags | UnicodeFlag);
+    if (c == 'g' && !reflags.global()) {
+      reflags |= RegExpFlag::Global;
+    } else if (c == 'i' && !reflags.ignoreCase()) {
+      reflags |= RegExpFlag::IgnoreCase;
+    } else if (c == 'm' && !reflags.multiline()) {
+      reflags |= RegExpFlag::Multiline;
+    } else if (c == 'u' && !reflags.unicode()) {
+      reflags |= RegExpFlag::Unicode;
+    } else if (c == 'y' && !reflags.sticky()) {
+      reflags |= RegExpFlag::Sticky;
     } else {
       return raiseError("Invalid regexp flags");
     }
   }
 
   Rooted<RegExpObject*> reobj(cx_);
   BINJS_TRY_VAR(reobj,
                 RegExpObject::create(cx_, pattern, reflags, TenuredObject));
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -37,16 +37,17 @@
 #include "builtin/ModuleObject.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/ModuleSharedContext.h"
 #include "frontend/ParseNode.h"
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "vm/BytecodeUtil.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/RegExpObject.h"
 #include "vm/StringType.h"
 #include "wasm/AsmJS.h"
@@ -65,16 +66,17 @@ using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::PointerRangeSize;
 using mozilla::Some;
 using mozilla::Unused;
 using mozilla::Utf8Unit;
 
 using JS::AutoGCRooter;
 using JS::ReadOnlyCompileOptions;
+using JS::RegExpFlags;
 
 namespace js {
 namespace frontend {
 
 using DeclaredNamePtr = ParseContext::Scope::DeclaredNamePtr;
 using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
 using BindingIter = ParseContext::Scope::BindingIter;
 using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
@@ -9252,17 +9254,17 @@ GeneralParser<ParseHandler, Unit>::noSub
 }
 
 template <typename Unit>
 RegExpLiteral* Parser<FullParseHandler, Unit>::newRegExp() {
   MOZ_ASSERT(!options().selfHostingMode);
 
   // Create the regexp and check its syntax.
   const auto& chars = tokenStream.getCharBuffer();
-  RegExpFlag flags = anyChars.currentToken().regExpFlags();
+  RegExpFlags flags = anyChars.currentToken().regExpFlags();
 
   Rooted<RegExpObject*> reobj(cx_);
   reobj = RegExpObject::create(cx_, chars.begin(), chars.length(), flags,
                                anyChars, TenuredObject);
   if (!reobj) {
     return null();
   }
 
@@ -9271,23 +9273,23 @@ RegExpLiteral* Parser<FullParseHandler, 
 
 template <typename Unit>
 SyntaxParseHandler::RegExpLiteralType
 Parser<SyntaxParseHandler, Unit>::newRegExp() {
   MOZ_ASSERT(!options().selfHostingMode);
 
   // Only check the regexp's syntax, but don't create a regexp object.
   const auto& chars = tokenStream.getCharBuffer();
-  RegExpFlag flags = anyChars.currentToken().regExpFlags();
+  RegExpFlags flags = anyChars.currentToken().regExpFlags();
 
   mozilla::Range<const char16_t> source(chars.begin(), chars.length());
   {
     LifoAllocScope scopeAlloc(&alloc_);
     if (!js::irregexp::ParsePatternSyntax(anyChars, scopeAlloc.alloc(), source,
-                                          flags & UnicodeFlag)) {
+                                          flags.unicode())) {
       return null();
     }
   }
 
   return handler_.newRegExp(SyntaxParseHandler::NodeGeneric, pos(), *this);
 }
 
 template <class ParseHandler, typename Unit>
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -29,16 +29,17 @@
 
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/Parser.h"
 #include "frontend/ReservedWords.h"
 #include "js/CharacterEncoding.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "js/UniquePtr.h"
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
 #include "vm/HelperThreads.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/Realm.h"
 
@@ -51,16 +52,18 @@ using mozilla::IsAsciiDigit;
 using mozilla::IsTrailingUnit;
 using mozilla::MakeScopeExit;
 using mozilla::MakeSpan;
 using mozilla::Maybe;
 using mozilla::PointerRangeSize;
 using mozilla::Utf8Unit;
 
 using JS::ReadOnlyCompileOptions;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 
 struct ReservedWordInfo {
   const char* chars;  // C string with reserved word text
   js::frontend::TokenKind tokentype;
 };
 
 static const ReservedWordInfo reservedWords[] = {
 #define RESERVED_WORD_INFO(word, name, type) \
@@ -2314,44 +2317,44 @@ MOZ_MUST_USE bool TokenStreamSpecific<Un
 
     MOZ_ASSERT(!IsLineTerminator(AssertedCast<char32_t>(unit)));
     if (!this->charBuffer.append(unit)) {
       return badToken();
     }
   } while (true);
 
   int32_t unit;
-  RegExpFlag reflags = NoFlags;
+  RegExpFlags reflags = RegExpFlag::NoFlags;
   while (true) {
-    RegExpFlag flag;
+    uint8_t flag;
     unit = getCodeUnit();
     if (unit == 'g') {
-      flag = GlobalFlag;
+      flag = RegExpFlag::Global;
     } else if (unit == 'i') {
-      flag = IgnoreCaseFlag;
+      flag = RegExpFlag::IgnoreCase;
     } else if (unit == 'm') {
-      flag = MultilineFlag;
+      flag = RegExpFlag::Multiline;
+    } else if (unit == 'u') {
+      flag = RegExpFlag::Unicode;
     } else if (unit == 'y') {
-      flag = StickyFlag;
-    } else if (unit == 'u') {
-      flag = UnicodeFlag;
+      flag = RegExpFlag::Sticky;
     } else if (IsAsciiAlpha(unit)) {
-      flag = NoFlags;
+      flag = RegExpFlag::NoFlags;
     } else {
       break;
     }
 
-    if ((reflags & flag) || flag == NoFlags) {
+    if ((reflags & flag) || flag == RegExpFlag::NoFlags) {
       ungetCodeUnit(unit);
       char buf[2] = {char(unit), '\0'};
       error(JSMSG_BAD_REGEXP_FLAG, buf);
       return badToken();
     }
 
-    reflags = RegExpFlag(reflags | flag);
+    reflags |= flag;
   }
   ungetCodeUnit(unit);
 
   newRegExpToken(reflags, start, out);
   return true;
 }
 
 template <typename Unit, class AnyCharsAccess>
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -201,23 +201,23 @@
 #include <stdint.h>
 #include <stdio.h>
 
 #include "jspubtd.h"
 
 #include "frontend/ErrorReporter.h"
 #include "frontend/TokenKind.h"
 #include "js/CompileOptions.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "util/Text.h"
 #include "util/Unicode.h"
 #include "vm/ErrorReporting.h"
 #include "vm/JSAtom.h"
-#include "vm/RegExpConstants.h"
 #include "vm/StringType.h"
 
 struct JSContext;
 struct KeywordInfo;
 
 namespace js {
 
 class AutoKeepAtoms;
@@ -375,17 +375,17 @@ struct Token {
       /** Numeric literal's value. */
       double value;
 
       /** Does the numeric literal contain a '.'? */
       DecimalPoint decimalPoint;
     } number;
 
     /** Regular expression flags; use charBuffer to access source chars. */
-    RegExpFlag reflags;
+    JS::RegExpFlags reflags;
   } u;
 
 #ifdef DEBUG
   /** The modifier used to get this token. */
   Modifier modifier;
 
   /**
    * Exception for this modifier to permit modifier mismatches in certain
@@ -402,19 +402,18 @@ struct Token {
   }
 
   void setAtom(JSAtom* atom) {
     MOZ_ASSERT(type == TokenKind::String || type == TokenKind::TemplateHead ||
                type == TokenKind::NoSubsTemplate);
     u.atom = atom;
   }
 
-  void setRegExpFlags(RegExpFlag flags) {
+  void setRegExpFlags(JS::RegExpFlags flags) {
     MOZ_ASSERT(type == TokenKind::RegExp);
-    MOZ_ASSERT((flags & AllFlags) == flags);
     u.reflags = flags;
   }
 
   void setNumber(double n, DecimalPoint decimalPoint) {
     MOZ_ASSERT(type == TokenKind::Number);
     u.number.value = n;
     u.number.decimalPoint = decimalPoint;
   }
@@ -427,19 +426,18 @@ struct Token {
   }
 
   JSAtom* atom() const {
     MOZ_ASSERT(type == TokenKind::String || type == TokenKind::TemplateHead ||
                type == TokenKind::NoSubsTemplate);
     return u.atom;
   }
 
-  RegExpFlag regExpFlags() const {
+  JS::RegExpFlags regExpFlags() const {
     MOZ_ASSERT(type == TokenKind::RegExp);
-    MOZ_ASSERT((u.reflags & AllFlags) == u.reflags);
     return u.reflags;
   }
 
   double number() const {
     MOZ_ASSERT(type == TokenKind::Number);
     return u.number.value;
   }
 
@@ -1994,17 +1992,18 @@ class GeneralTokenStreamChars : public S
 
   void newPrivateNameToken(PropertyName* name, TokenStart start,
                            TokenStreamShared::Modifier modifier,
                            TokenKind* out) {
     Token* token = newToken(TokenKind::PrivateName, start, modifier, out);
     token->setName(name);
   }
 
-  void newRegExpToken(RegExpFlag reflags, TokenStart start, TokenKind* out) {
+  void newRegExpToken(JS::RegExpFlags reflags, TokenStart start,
+                      TokenKind* out) {
     Token* token =
         newToken(TokenKind::RegExp, start, TokenStreamShared::Operand, out);
     token->setRegExpFlags(reflags);
   }
 
   MOZ_COLD bool badToken();
 
   /**
@@ -2797,17 +2796,18 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
 // (Unit, AnyCharsAccess) pairs -- and that gets super-messy as AnyCharsAccess
 // *itself* is templated.  This symbol really isn't that huge compared to some
 // defined inline in TokenStreamSpecific, so just rely on the linker commoning
 // stuff up.
 template <typename Unit>
 template <class AnyCharsAccess>
 inline TokenStreamPosition<Unit>::TokenStreamPosition(
     AutoKeepAtoms& keepAtoms,
-    TokenStreamSpecific<Unit, AnyCharsAccess>& tokenStream) {
+    TokenStreamSpecific<Unit, AnyCharsAccess>& tokenStream)
+    : currentToken(tokenStream.anyCharsAccess().currentToken()) {
   TokenStreamAnyChars& anyChars = tokenStream.anyCharsAccess();
 
   buf =
       tokenStream.sourceUnits.addressOfNextCodeUnit(/* allowPoisoned = */ true);
   flags = anyChars.flags;
   lineno = anyChars.lineno;
   linebase = anyChars.linebase;
   prevLinebase = anyChars.prevLinebase;
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -33,27 +33,31 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Move.h"
 #include "mozilla/Range.h"
 
 #include "frontend/TokenStream.h"
 #include "gc/GC.h"
 #include "irregexp/RegExpCharacters.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "util/Unicode.h"
 #include "vm/ErrorReporting.h"
 
 using namespace js;
 using namespace js::irregexp;
 
 using mozilla::AssertedCast;
 using mozilla::PointerRangeSize;
 
+using JS::RegExpFlag;
+using JS::RegExpFlags;
+
 // ----------------------------------------------------------------------------
 // RegExpBuilder
 
 RegExpBuilder::RegExpBuilder(LifoAlloc* alloc)
   : alloc(alloc),
     pending_empty_(false),
     characters_(nullptr)
 #ifdef DEBUG
@@ -235,31 +239,31 @@ RegExpBuilder::AddQuantifierToAtom(int m
     last_added_ = ADD_TERM;
 #endif
 }
 
 // ----------------------------------------------------------------------------
 // RegExpParser
 
 template <typename CharT>
-RegExpParser<CharT>::RegExpParser(frontend::TokenStreamAnyChars& ts, LifoAlloc* alloc,
-                                  const CharT* chars, const CharT* end, bool multiline_mode,
-                                  bool unicode, bool ignore_case)
+RegExpParser<CharT>::RegExpParser(frontend::TokenStreamAnyChars& ts,
+                                  LifoAlloc* alloc, RegExpFlags flags,
+                                  const CharT* chars, const CharT* end)
   : ts(ts),
     alloc(alloc),
     captures_(nullptr),
     start_(chars),
     next_pos_(start_),
     end_(end),
     current_(kEndMarker),
     capture_count_(0),
     has_more_(true),
-    multiline_(multiline_mode),
-    unicode_(unicode),
-    ignore_case_(ignore_case),
+    multiline_(flags.multiline()),
+    unicode_(flags.unicode()),
+    ignore_case_(flags.ignoreCase()),
     simple_(false),
     contains_anchor_(false),
     is_scanned_for_captures_(false)
 {
     Advance();
 }
 
 static size_t ComputeColumn(const Latin1Char* begin, const Latin1Char* end) {
@@ -1926,23 +1930,22 @@ RegExpParser<CharT>::ParseDisjunction()
 }
 
 template class irregexp::RegExpParser<Latin1Char>;
 template class irregexp::RegExpParser<char16_t>;
 
 template <typename CharT>
 static bool
 ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
-             const CharT* chars, size_t length,
-             bool multiline, bool match_only, bool unicode, bool ignore_case,
-             bool global, bool sticky, RegExpCompileData* data)
+             const CharT* chars, size_t length, bool match_only,
+             RegExpFlags flags, RegExpCompileData* data)
 {
     // We shouldn't strip pattern for exec, or test with global/sticky,
     // to reflect correct match position and lastIndex.
-    if (match_only && !global && !sticky) {
+    if (match_only && !flags.global() && !flags.sticky()) {
         // Try to strip a leading '.*' from the RegExp, but only if it is not
         // followed by a '?' (which will affect how the .* is parsed). This
         // pattern will affect the captures produced by the RegExp, but not
         // whether there is a match or not.
         if (length >= 3 && chars[0] == '.' && chars[1] == '*' && chars[2] != '?') {
             chars += 2;
             length -= 2;
         }
@@ -1953,46 +1956,47 @@ ParsePattern(frontend::TokenStreamAnyCha
         // are sure this will not affect how the RegExp is parsed.
         if (length >= 3 && !HasRegExpMetaChars(chars, length - 2) &&
             chars[length - 2] == '.' && chars[length - 1] == '*')
         {
             length -= 2;
         }
     }
 
-    RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, multiline, unicode, ignore_case);
+    RegExpParser<CharT> parser(ts, &alloc, flags, chars, chars + length);
     data->tree = parser.ParsePattern();
     if (!data->tree)
         return false;
 
     data->simple = parser.simple();
     data->contains_anchor = parser.contains_anchor();
     data->capture_count = parser.captures_started();
     return true;
 }
 
 bool
-irregexp::ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
-                       bool multiline, bool match_only, bool unicode, bool ignore_case,
-                       bool global, bool sticky, RegExpCompileData* data)
+irregexp::ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
+                       JSAtom* str, bool match_only, RegExpFlags flags,
+                       RegExpCompileData* data)
 {
     JS::AutoCheckCannotGC nogc;
     return str->hasLatin1Chars()
            ? ::ParsePattern(ts, alloc, str->latin1Chars(nogc), str->length(),
-                            multiline, match_only, unicode, ignore_case, global, sticky, data)
+                            match_only, flags, data)
            : ::ParsePattern(ts, alloc, str->twoByteChars(nogc), str->length(),
-                            multiline, match_only, unicode, ignore_case, global, sticky, data);
+                            match_only, flags, data);
 }
 
 template <typename CharT>
 static bool
 ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
                    const CharT* chars, size_t length, bool unicode)
 {
-    RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, false, unicode, false);
+    RegExpParser<CharT> parser(ts, &alloc, unicode ? RegExpFlag::Unicode : 0,
+                               chars, chars + length);
     return parser.ParsePattern() != nullptr;
 }
 
 bool
 irregexp::ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
                              bool unicode)
 {
     JS::AutoCheckCannotGC nogc;
--- a/js/src/irregexp/RegExpParser.h
+++ b/js/src/irregexp/RegExpParser.h
@@ -31,29 +31,29 @@
 #ifndef V8_PARSER_H_
 #define V8_PARSER_H_
 
 #include "mozilla/Range.h"
 
 #include <stdarg.h>
 
 #include "irregexp/RegExpAST.h"
+#include "js/RegExpFlags.h"
 
 namespace js {
 
 namespace frontend {
     class TokenStreamAnyChars;
 }
 
 namespace irregexp {
 
 extern bool
 ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
-             bool multiline, bool match_only, bool unicode, bool ignore_case,
-             bool global, bool sticky, RegExpCompileData* data);
+             bool match_only, JS::RegExpFlags flags, RegExpCompileData* data);
 
 extern bool
 ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
                    bool unicode);
 
 extern bool
 ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
                    const mozilla::Range<const char16_t> chars, bool unicode);
@@ -180,18 +180,17 @@ class RegExpBuilder
 // Characters parsed by RegExpParser can be either char16_t or kEndMarker.
 typedef uint32_t widechar;
 
 template <typename CharT>
 class RegExpParser
 {
   public:
     RegExpParser(frontend::TokenStreamAnyChars& ts, LifoAlloc* alloc,
-                 const CharT* chars, const CharT* end, bool multiline_mode, bool unicode,
-                 bool ignore_case);
+                 JS::RegExpFlags flags, const CharT* chars, const CharT* end);
 
     RegExpTree* ParsePattern();
     RegExpTree* ParseDisjunction();
     RegExpTree* ParseCharacterClass();
 
     // Parses a {...,...} quantifier and stores the range in the given
     // out parameters.
     bool ParseIntervalQuantifier(int* min_out, int* max_out);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -39,16 +39,17 @@
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "jit/StackSlotAllocator.h"
 #include "jit/VMFunctions.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag
 #include "util/Unicode.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/MatchPairs.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringType.h"
@@ -1938,17 +1939,17 @@ static bool PrepareAndExecuteRegExp(
 
   // ES6 21.2.2.2 step 2.
   // See RegExp.cpp ExecuteRegExp for more detail.
   {
     Label done;
 
     masm.branchTest32(Assembler::Zero,
                       Address(temp1, RegExpShared::offsetOfFlags()),
-                      Imm32(UnicodeFlag), &done);
+                      Imm32(int32_t(JS::RegExpFlag::Unicode)), &done);
 
     // If input is latin1, there should not be surrogate pair.
     masm.branchLatin1String(input, &done);
 
     // Check if |lastIndex > 0 && lastIndex < input->length()|.
     // lastIndex should already have no sign here.
     masm.branchTest32(Assembler::Zero, lastIndex, lastIndex, &done);
     masm.loadStringLength(input, temp2);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -19,16 +19,17 @@
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
 #include "jit/IonBuilder.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 #include "wasm/WasmInstance.h"
@@ -38,16 +39,18 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/StringObject-inl.h"
 
 using mozilla::ArrayLength;
 using mozilla::AssertedCast;
 using mozilla::Maybe;
 
 using JS::DoubleNaNValue;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 using JS::TrackedOutcome;
 
 namespace js {
 namespace jit {
 
 // Returns true if |native| can be inlined cross-realm. Especially inlined
 // natives that can allocate objects or throw exceptions shouldn't be inlined
 // cross-realm without a careful analysis because we might use the wrong realm!
@@ -619,28 +622,28 @@ IonBuilder::InliningResult IonBuilder::i
     }
 
     MInstruction* byteOffset = addTypedArrayByteOffset(thisArg);
     current->push(byteOffset);
     return InliningStatus_Inlined;
   }
 
   // Try to optimize RegExp getters.
-  RegExpFlag mask = NoFlags;
+  RegExpFlags mask = RegExpFlag::NoFlags;
   if (RegExpObject::isOriginalFlagGetter(native, &mask)) {
     const Class* clasp = thisTypes->getKnownClass(constraints());
     if (clasp != &RegExpObject::class_) {
       return InliningStatus_NotInlined;
     }
 
     MLoadFixedSlot* flags =
         MLoadFixedSlot::New(alloc(), thisArg, RegExpObject::flagsSlot());
     current->add(flags);
     flags->setResultType(MIRType::Int32);
-    MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask));
+    MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask.value()));
     current->add(maskConst);
     MBitAnd* maskedFlag = MBitAnd::New(alloc(), flags, maskConst);
     maskedFlag->setInt32Specialization();
     current->add(maskedFlag);
 
     MDefinition* result = convertToBoolean(maskedFlag);
     current->push(result);
     return InliningStatus_Inlined;
--- a/js/src/jsapi-tests/testRegExp.cpp
+++ b/js/src/jsapi-tests/testRegExp.cpp
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/RegExp.h"
+#include "js/RegExpFlags.h"
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testObjectIsRegExp) {
   JS::RootedValue val(cx);
 
   bool isRegExp;
 
   EVAL("new Object", &val);
@@ -25,26 +26,29 @@ BEGIN_TEST(testObjectIsRegExp) {
 END_TEST(testObjectIsRegExp)
 
 BEGIN_TEST(testGetRegExpFlags) {
   JS::RootedValue val(cx);
   JS::RootedObject obj(cx);
 
   EVAL("/foopy/", &val);
   obj = val.toObjectOrNull();
-  CHECK_EQUAL(JS::GetRegExpFlags(cx, obj), 0u);
+  CHECK_EQUAL(JS::GetRegExpFlags(cx, obj),
+              JS::RegExpFlags(JS::RegExpFlag::NoFlags));
 
   EVAL("/foopy/g", &val);
   obj = val.toObjectOrNull();
-  CHECK(JS::GetRegExpFlags(cx, obj) == JS::RegExpFlags::Global);
+  CHECK_EQUAL(JS::GetRegExpFlags(cx, obj),
+              JS::RegExpFlags(JS::RegExpFlag::Global));
 
   EVAL("/foopy/gi", &val);
   obj = val.toObjectOrNull();
-  CHECK(JS::GetRegExpFlags(cx, obj) ==
-        (JS::RegExpFlags::Global | JS::RegExpFlags::IgnoreCase));
+  CHECK_EQUAL(
+      JS::GetRegExpFlags(cx, obj),
+      JS::RegExpFlags(JS::RegExpFlag::Global | JS::RegExpFlag::IgnoreCase));
 
   return true;
 }
 END_TEST(testGetRegExpFlags)
 
 BEGIN_TEST(testGetRegExpSource) {
   JS::RootedValue val(cx);
   JS::RootedObject obj(cx);
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -15,16 +15,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "gc/GC.h"
 #include "js/AllocPolicy.h"
 #include "js/CharacterEncoding.h"
 #include "js/Equality.h"  // JS::SameValue
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "js/Vector.h"
 #include "vm/JSContext.h"
 
 /* Note: Aborts on OOM. */
 class JSAPITestString {
   js::Vector<char, 0, js::SystemAllocPolicy> chars;
 
  public:
@@ -154,16 +155,36 @@ class JSAPITest {
   }
 
   JSAPITestString toSource(int v) { return toSource((long)v); }
 
   JSAPITestString toSource(bool v) {
     return JSAPITestString(v ? "true" : "false");
   }
 
+  JSAPITestString toSource(JS::RegExpFlags flags) {
+    JSAPITestString str;
+    if (flags.global()) {
+      str += "g";
+    }
+    if (flags.ignoreCase()) {
+      str += "i";
+    }
+    if (flags.multiline()) {
+      str += "m";
+    }
+    if (flags.unicode()) {
+      str += "u";
+    }
+    if (flags.sticky()) {
+      str += "y";
+    }
+    return str;
+  }
+
   JSAPITestString toSource(JSAtom* v) {
     JS::RootedValue val(cx, JS::StringValue((JSString*)v));
     return jsvalToSource(val);
   }
 
   // Note that in some still-supported GCC versions (we think anything before
   // GCC 4.6), this template does not work when the second argument is
   // nullptr. It infers type U = long int. Use CHECK_NULL instead.
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -156,16 +156,17 @@ EXPORTS.js += [
     '../public/PropertyDescriptor.h',
     '../public/PropertySpec.h',
     '../public/ProtoKey.h',
     '../public/Proxy.h',
     '../public/Realm.h',
     '../public/RealmOptions.h',
     '../public/RefCounted.h',
     '../public/RegExp.h',
+    '../public/RegExpFlags.h',
     '../public/RequiredDefines.h',
     '../public/Result.h',
     '../public/RootingAPI.h',
     '../public/SavedFrameAPI.h',
     '../public/SharedArrayBuffer.h',
     '../public/SliceBudget.h',
     '../public/SourceText.h',
     '../public/StableStringChars.h',
--- a/js/src/vm/RegExpConstants.h
+++ b/js/src/vm/RegExpConstants.h
@@ -6,34 +6,16 @@
 
 #ifndef vm_RegExpConstants_h
 #define vm_RegExpConstants_h
 
 #include "builtin/SelfHostingDefines.h"
 
 namespace js {
 
-enum RegExpFlag : uint8_t {
-  IgnoreCaseFlag = 0x01,
-  GlobalFlag = 0x02,
-  MultilineFlag = 0x04,
-  StickyFlag = 0x08,
-  UnicodeFlag = 0x10,
-
-  NoFlags = 0x00,
-  AllFlags = 0x1f
-};
-
-static_assert(IgnoreCaseFlag == REGEXP_IGNORECASE_FLAG &&
-                  GlobalFlag == REGEXP_GLOBAL_FLAG &&
-                  MultilineFlag == REGEXP_MULTILINE_FLAG &&
-                  StickyFlag == REGEXP_STICKY_FLAG &&
-                  UnicodeFlag == REGEXP_UNICODE_FLAG,
-              "Flag values should be in sync with self-hosted JS");
-
 enum RegExpRunStatus {
   RegExpRunStatus_Error,
   RegExpRunStatus_Success,
   RegExpRunStatus_Success_NotFound
 };
 
 } /* namespace js */
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -10,24 +10,26 @@
 #include "mozilla/PodOperations.h"
 
 #include "builtin/String.h"
 #ifdef DEBUG
 #  include "jsutil.h"
 #endif
 
 #include "builtin/RegExp.h"
+#include "builtin/SelfHostingDefines.h"  // REGEXP_*_FLAG
 #include "frontend/TokenStream.h"
 #include "gc/HashUtil.h"
 #ifdef DEBUG
 #  include "irregexp/RegExpBytecode.h"
 #endif
 #include "irregexp/RegExpParser.h"
 #include "jit/VMFunctions.h"
 #include "js/RegExp.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlags
 #include "js/StableStringChars.h"
 #include "util/StringBuffer.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #ifdef DEBUG
 #  include "util/Unicode.h"
@@ -37,33 +39,35 @@
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::AutoStableStringChars;
 using JS::CompileOptions;
+using JS::RegExpFlag;
+using JS::RegExpFlags;
 using js::frontend::TokenStream;
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 
 using JS::AutoCheckCannotGC;
 
-static_assert(IgnoreCaseFlag == JS::RegExpFlags::IgnoreCase,
-              "public/internal /i flag bits must agree");
-static_assert(GlobalFlag == JS::RegExpFlags::Global,
-              "public/internal /g flag bits must agree");
-static_assert(MultilineFlag == JS::RegExpFlags::Multiline,
-              "public/internal /m flag bits must agree");
-static_assert(StickyFlag == JS::RegExpFlags::Sticky,
-              "public/internal /y flag bits must agree");
-static_assert(UnicodeFlag == JS::RegExpFlags::Unicode,
-              "public/internal /u flag bits must agree");
+static_assert(RegExpFlag::Global == REGEXP_GLOBAL_FLAG,
+              "self-hosted JS and /g flag bits must agree");
+static_assert(RegExpFlag::IgnoreCase == REGEXP_IGNORECASE_FLAG,
+              "self-hosted JS and /i flag bits must agree");
+static_assert(RegExpFlag::Multiline == REGEXP_MULTILINE_FLAG,
+              "self-hosted JS and /m flag bits must agree");
+static_assert(RegExpFlag::Unicode == REGEXP_UNICODE_FLAG,
+              "self-hosted JS and /u flag bits must agree");
+static_assert(RegExpFlag::Sticky == REGEXP_STICKY_FLAG,
+              "self-hosted JS and /y flag bits must agree");
 
 RegExpObject* js::RegExpAlloc(JSContext* cx, NewObjectKind newKind,
                               HandleObject proto /* = nullptr */) {
   Rooted<RegExpObject*> regexp(
       cx, NewObjectWithClassProto<RegExpObject>(cx, proto, newKind));
   if (!regexp) {
     return nullptr;
   }
@@ -112,35 +116,35 @@ RegExpShared* RegExpObject::getShared(JS
   if (regexp->hasShared()) {
     return regexp->sharedRef();
   }
 
   return createShared(cx, regexp);
 }
 
 /* static */
-bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask) {
+bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlags* mask) {
   if (native == regexp_global) {
-    *mask = GlobalFlag;
+    *mask = RegExpFlag::Global;
     return true;
   }
   if (native == regexp_ignoreCase) {
-    *mask = IgnoreCaseFlag;
+    *mask = RegExpFlag::IgnoreCase;
     return true;
   }
   if (native == regexp_multiline) {
-    *mask = MultilineFlag;
+    *mask = RegExpFlag::Multiline;
     return true;
   }
   if (native == regexp_sticky) {
-    *mask = StickyFlag;
+    *mask = RegExpFlag::Sticky;
     return true;
   }
   if (native == regexp_unicode) {
-    *mask = UnicodeFlag;
+    *mask = RegExpFlag::Unicode;
     return true;
   }
 
   return false;
 }
 
 /* static */
 void RegExpObject::trace(JSTracer* trc, JSObject* obj) {
@@ -193,83 +197,83 @@ const Class RegExpObject::class_ = {
     &RegExpObjectClassOps, &RegExpObjectClassSpec};
 
 const Class RegExpObject::protoClass_ = {
     js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp), JS_NULL_CLASS_OPS,
     &RegExpObjectClassSpec};
 
 template <typename CharT>
 RegExpObject* RegExpObject::create(JSContext* cx, const CharT* chars,
-                                   size_t length, RegExpFlag flags,
+                                   size_t length, RegExpFlags flags,
                                    frontend::TokenStreamAnyChars& tokenStream,
                                    NewObjectKind newKind) {
   static_assert(mozilla::IsSame<CharT, char16_t>::value,
                 "this code may need updating if/when CharT encodes UTF-8");
 
   RootedAtom source(cx, AtomizeChars(cx, chars, length));
   if (!source) {
     return nullptr;
   }
 
   return create(cx, source, flags, tokenStream, newKind);
 }
 
 template RegExpObject* RegExpObject::create(
-    JSContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
+    JSContext* cx, const char16_t* chars, size_t length, RegExpFlags flags,
     frontend::TokenStreamAnyChars& tokenStream, NewObjectKind newKind);
 
 template <typename CharT>
 RegExpObject* RegExpObject::create(JSContext* cx, const CharT* chars,
-                                   size_t length, RegExpFlag flags,
+                                   size_t length, RegExpFlags flags,
                                    NewObjectKind newKind) {
   static_assert(mozilla::IsSame<CharT, char16_t>::value,
                 "this code may need updating if/when CharT encodes UTF-8");
 
   RootedAtom source(cx, AtomizeChars(cx, chars, length));
   if (!source) {
     return nullptr;
   }
 
   return create(cx, source, flags, newKind);
 }
 
 template RegExpObject* RegExpObject::create(JSContext* cx,
                                             const char16_t* chars,
-                                            size_t length, RegExpFlag flags,
+                                            size_t length, RegExpFlags flags,
                                             NewObjectKind newKind);
 
 RegExpObject* RegExpObject::create(JSContext* cx, HandleAtom source,
-                                   RegExpFlag flags,
+                                   RegExpFlags flags,
                                    frontend::TokenStreamAnyChars& tokenStream,
                                    NewObjectKind newKind) {
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   if (!irregexp::ParsePatternSyntax(tokenStream, allocScope.alloc(), source,
-                                    flags & UnicodeFlag)) {
+                                    flags.unicode())) {
     return nullptr;
   }
 
   Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, newKind));
   if (!regexp) {
     return nullptr;
   }
 
   regexp->initAndZeroLastIndex(source, flags, cx);
 
   return regexp;
 }
 
 RegExpObject* RegExpObject::create(JSContext* cx, HandleAtom source,
-                                   RegExpFlag flags, NewObjectKind newKind) {
+                                   RegExpFlags flags, NewObjectKind newKind) {
   CompileOptions dummyOptions(cx);
   TokenStream dummyTokenStream(cx, dummyOptions, (const char16_t*)nullptr, 0,
                                nullptr);
 
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   if (!irregexp::ParsePatternSyntax(dummyTokenStream, allocScope.alloc(),
-                                    source, flags & UnicodeFlag)) {
+                                    source, flags.unicode())) {
     return nullptr;
   }
 
   Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, newKind));
   if (!regexp) {
     return nullptr;
   }
 
@@ -299,26 +303,26 @@ Shape* RegExpObject::assignInitialShape(
 
   JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
 
   /* The lastIndex property alone is writable but non-configurable. */
   return NativeObject::addDataProperty(cx, self, cx->names().lastIndex,
                                        LAST_INDEX_SLOT, JSPROP_PERMANENT);
 }
 
-void RegExpObject::initIgnoringLastIndex(JSAtom* source, RegExpFlag flags) {
+void RegExpObject::initIgnoringLastIndex(JSAtom* source, RegExpFlags flags) {
   // If this is a re-initialization with an existing RegExpShared, 'flags'
   // may not match getShared()->flags, so forget the RegExpShared.
   sharedRef() = nullptr;
 
   setSource(source);
   setFlags(flags);
 }
 
-void RegExpObject::initAndZeroLastIndex(JSAtom* source, RegExpFlag flags,
+void RegExpObject::initAndZeroLastIndex(JSAtom* source, RegExpFlags flags,
                                         JSContext* cx) {
   initIgnoringLastIndex(source, flags);
   zeroLastIndex(cx);
 }
 
 static MOZ_ALWAYS_INLINE bool IsRegExpLineTerminator(const JS::Latin1Char c) {
   return c == '\n' || c == '\r';
 }
@@ -893,17 +897,17 @@ bool js::StringHasRegExpMetaChars(JSLine
     return HasRegExpMetaChars(str->latin1Chars(nogc), str->length());
   }
 
   return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
 }
 
 /* RegExpShared */
 
-RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags)
+RegExpShared::RegExpShared(JSAtom* source, RegExpFlags flags)
     : source(source), flags(flags), canStringMatch(false), parenCount(0) {}
 
 void RegExpShared::traceChildren(JSTracer* trc) {
   // Discard code to avoid holding onto ExecutablePools.
   if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC()) {
     discardJitCode();
   }
 
@@ -951,19 +955,17 @@ bool RegExpShared::compile(JSContext* cx
   CompileOptions options(cx);
   frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
   /* Parse the pattern. The RegExpCompileData is allocated in LifoAlloc and
    * will only be live while LifoAllocScope is on stack. */
   LifoAllocScope allocScope(&cx->tempLifoAlloc());
   irregexp::RegExpCompileData data;
   if (!irregexp::ParsePattern(dummyTokenStream, allocScope.alloc(), pattern,
-                              re->multiline(), mode == MatchOnly, re->unicode(),
-                              re->ignoreCase(), re->global(), re->sticky(),
-                              &data)) {
+                              mode == MatchOnly, re->getFlags(), &data)) {
     return false;
   }
 
   re->parenCount = data.capture_count;
 
   JitCodeTables tables;
   irregexp::RegExpCode code = irregexp::CompilePattern(
       cx, allocScope.alloc(), re, &data, input, false /* global() */,
@@ -1245,17 +1247,17 @@ void RegExpRealm::sweep() {
 
   if (optimizableRegExpInstanceShape_ &&
       IsAboutToBeFinalized(&optimizableRegExpInstanceShape_)) {
     optimizableRegExpInstanceShape_.set(nullptr);
   }
 }
 
 RegExpShared* RegExpZone::get(JSContext* cx, HandleAtom source,
-                              RegExpFlag flags) {
+                              RegExpFlags flags) {
   DependentAddPtr<Set> p(cx, set_, Key(source, flags));
   if (p) {
     return *p;
   }
 
   auto shared = Allocate<RegExpShared>(cx);
   if (!shared) {
     return nullptr;
@@ -1267,17 +1269,17 @@ RegExpShared* RegExpZone::get(JSContext*
     ReportOutOfMemory(cx);
     return nullptr;
   }
 
   return shared;
 }
 
 RegExpShared* RegExpZone::get(JSContext* cx, HandleAtom atom, JSString* opt) {
-  RegExpFlag flags = RegExpFlag(0);
+  RegExpFlags flags = RegExpFlag::NoFlags;
   if (opt && !ParseRegExpFlags(cx, opt, &flags)) {
     return nullptr;
   }
 
   return get(cx, atom, flags);
 }
 
 size_t RegExpZone::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
@@ -1310,53 +1312,53 @@ JSObject* js::CloneRegExpObject(JSContex
   clone->initAndZeroLastIndex(shared->getSource(), shared->getFlags(), cx);
   clone->setShared(*shared);
 
   return clone;
 }
 
 template <typename CharT>
 static bool ParseRegExpFlags(const CharT* chars, size_t length,
-                             RegExpFlag* flagsOut, char16_t* invalidFlag) {
-  *flagsOut = RegExpFlag(0);
+                             RegExpFlags* flagsOut, char16_t* invalidFlag) {
+  *flagsOut = RegExpFlag::NoFlags;
 
   for (size_t i = 0; i < length; i++) {
-    RegExpFlag flag;
+    uint8_t flag;
     switch (chars[i]) {
+      case 'g':
+        flag = RegExpFlag::Global;
+        break;
       case 'i':
-        flag = IgnoreCaseFlag;
-        break;
-      case 'g':
-        flag = GlobalFlag;
+        flag = RegExpFlag::IgnoreCase;
         break;
       case 'm':
-        flag = MultilineFlag;
+        flag = RegExpFlag::Multiline;
+        break;
+      case 'u':
+        flag = RegExpFlag::Unicode;
         break;
       case 'y':
-        flag = StickyFlag;
-        break;
-      case 'u':
-        flag = UnicodeFlag;
+        flag = RegExpFlag::Sticky;
         break;
       default:
         *invalidFlag = chars[i];
         return false;
     }
     if (*flagsOut & flag) {
       *invalidFlag = chars[i];
       return false;
     }
-    *flagsOut = RegExpFlag(*flagsOut | flag);
+    *flagsOut |= flag;
   }
 
   return true;
 }
 
 bool js::ParseRegExpFlags(JSContext* cx, JSString* flagStr,
-                          RegExpFlag* flagsOut) {
+                          RegExpFlags* flagsOut) {
   JSLinearString* linear = flagStr->ensureLinear(cx);
   if (!linear) {
     return false;
   }
 
   size_t len = linear->length();
 
   bool ok;
@@ -1386,29 +1388,29 @@ bool js::ParseRegExpFlags(JSContext* cx,
 }
 
 template <XDRMode mode>
 XDRResult js::XDRScriptRegExpObject(XDRState<mode>* xdr,
                                     MutableHandle<RegExpObject*> objp) {
   /* NB: Keep this in sync with CloneScriptRegExpObject. */
 
   RootedAtom source(xdr->cx());
-  uint32_t flagsword = 0;
+  uint8_t flags = 0;
 
   if (mode == XDR_ENCODE) {
     MOZ_ASSERT(objp);
     RegExpObject& reobj = *objp;
     source = reobj.getSource();
-    flagsword = reobj.getFlags();
+    flags = reobj.getFlags().value();
   }
   MOZ_TRY(XDRAtom(xdr, &source));
-  MOZ_TRY(xdr->codeUint32(&flagsword));
+  MOZ_TRY(xdr->codeUint8(&flags));
   if (mode == XDR_DECODE) {
     RegExpObject* reobj = RegExpObject::create(
-        xdr->cx(), source, RegExpFlag(flagsword), TenuredObject);
+        xdr->cx(), source, RegExpFlags(flags), TenuredObject);
     if (!reobj) {
       return xdr->fail(JS::TranscodeResult_Throw);
     }
 
     objp.set(reobj);
   }
   return Ok();
 }
@@ -1438,37 +1440,36 @@ JS::ubi::Node::Size JS::ubi::Concrete<Re
   return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
          get().sizeOfExcludingThis(mallocSizeOf);
 }
 
 /*
  * Regular Expressions.
  */
 JS_PUBLIC_API JSObject* JS::NewRegExpObject(JSContext* cx, const char* bytes,
-                                            size_t length, unsigned flags) {
+                                            size_t length, RegExpFlags flags) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   UniqueTwoByteChars chars(InflateString(cx, bytes, length));
   if (!chars) {
     return nullptr;
   }
 
-  return RegExpObject::create(cx, chars.get(), length, RegExpFlag(flags),
-                              GenericObject);
+  return RegExpObject::create(cx, chars.get(), length, flags, GenericObject);
 }
 
 JS_PUBLIC_API JSObject* JS::NewUCRegExpObject(JSContext* cx,
                                               const char16_t* chars,
-                                              size_t length, unsigned flags) {
+                                              size_t length,
+                                              RegExpFlags flags) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
-  return RegExpObject::create(cx, chars, length, RegExpFlag(flags),
-                              GenericObject);
+  return RegExpObject::create(cx, chars, length, flags, GenericObject);
 }
 
 JS_PUBLIC_API bool JS::SetRegExpInput(JSContext* cx, HandleObject obj,
                                       HandleString input) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(input);
 
@@ -1543,23 +1544,23 @@ JS_PUBLIC_API bool JS::ObjectIsRegExp(JS
   if (!GetBuiltinClass(cx, obj, &cls)) {
     return false;
   }
 
   *isRegExp = cls == ESClass::RegExp;
   return true;
 }
 
-JS_PUBLIC_API unsigned JS::GetRegExpFlags(JSContext* cx, HandleObject obj) {
+JS_PUBLIC_API RegExpFlags JS::GetRegExpFlags(JSContext* cx, HandleObject obj) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
   RegExpShared* shared = RegExpToShared(cx, obj);
   if (!shared) {
-    return false;
+    return RegExpFlag::NoFlags;
   }
   return shared->getFlags();
 }
 
 JS_PUBLIC_API JSString* JS::GetRegExpSource(JSContext* cx, HandleObject obj) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -10,16 +10,17 @@
 #define vm_RegExpObject_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "builtin/SelfHostingDefines.h"
 #include "gc/Marking.h"
 #include "js/GCHashTable.h"
+#include "js/RegExpFlags.h"
 #include "proxy/Proxy.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpShared.h"
 #include "vm/Shape.h"
 
 /*
  * JavaScript Regular Expressions
@@ -65,29 +66,29 @@ class RegExpObject : public NativeObject
   static const Class protoClass_;
 
   // The maximum number of pairs a MatchResult can have, without having to
   // allocate a bigger MatchResult.
   static const size_t MaxPairCount = 14;
 
   template <typename CharT>
   static RegExpObject* create(JSContext* cx, const CharT* chars, size_t length,
-                              RegExpFlag flags, NewObjectKind newKind);
+                              JS::RegExpFlags flags, NewObjectKind newKind);
 
   template <typename CharT>
   static RegExpObject* create(JSContext* cx, const CharT* chars, size_t length,
-                              RegExpFlag flags,
+                              JS::RegExpFlags flags,
                               frontend::TokenStreamAnyChars& ts,
                               NewObjectKind kind);
 
   static RegExpObject* create(JSContext* cx, HandleAtom source,
-                              RegExpFlag flags, NewObjectKind newKind);
+                              JS::RegExpFlags flags, NewObjectKind newKind);
 
   static RegExpObject* create(JSContext* cx, HandleAtom source,
-                              RegExpFlag flags,
+                              JS::RegExpFlags flags,
                               frontend::TokenStreamAnyChars& ts,
                               NewObjectKind newKind);
 
   /*
    * Compute the initial shape to associate with fresh RegExp objects,
    * encoding their initial properties. Return the shape after
    * changing |obj|'s last property to it.
    */
@@ -126,28 +127,30 @@ class RegExpObject : public NativeObject
   }
 
   void setSource(JSAtom* source) { setSlot(SOURCE_SLOT, StringValue(source)); }
 
   /* Flags. */
 
   static unsigned flagsSlot() { return FLAGS_SLOT; }
 
-  RegExpFlag getFlags() const {
-    return RegExpFlag(getFixedSlot(FLAGS_SLOT).toInt32());
+  JS::RegExpFlags getFlags() const {
+    return JS::RegExpFlags(getFixedSlot(FLAGS_SLOT).toInt32());
   }
-  void setFlags(RegExpFlag flags) { setSlot(FLAGS_SLOT, Int32Value(flags)); }
+  void setFlags(JS::RegExpFlags flags) {
+    setFixedSlot(FLAGS_SLOT, Int32Value(flags.value()));
+  }
 
-  bool ignoreCase() const { return getFlags() & IgnoreCaseFlag; }
-  bool global() const { return getFlags() & GlobalFlag; }
-  bool multiline() const { return getFlags() & MultilineFlag; }
-  bool sticky() const { return getFlags() & StickyFlag; }
-  bool unicode() const { return getFlags() & UnicodeFlag; }
+  bool global() const { return getFlags().global(); }
+  bool ignoreCase() const { return getFlags().ignoreCase(); }
+  bool multiline() const { return getFlags().multiline(); }
+  bool unicode() const { return getFlags().unicode(); }
+  bool sticky() const { return getFlags().sticky(); }
 
-  static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
+  static bool isOriginalFlagGetter(JSNative native, JS::RegExpFlags* mask);
 
   static RegExpShared* getShared(JSContext* cx, Handle<RegExpObject*> regexp);
 
   bool hasShared() { return !!sharedRef(); }
 
   void setShared(RegExpShared& shared) {
     MOZ_ASSERT(!hasShared());
     sharedRef().init(&shared);
@@ -156,22 +159,23 @@ class RegExpObject : public NativeObject
   HeapPtrRegExpShared& sharedRef() {
     auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
     return reinterpret_cast<HeapPtrRegExpShared&>(ref);
   }
 
   static void trace(JSTracer* trc, JSObject* obj);
   void trace(JSTracer* trc);
 
-  void initIgnoringLastIndex(JSAtom* source, RegExpFlag flags);
+  void initIgnoringLastIndex(JSAtom* source, JS::RegExpFlags flags);
 
   // NOTE: This method is *only* safe to call on RegExps that haven't been
   //       exposed to script, because it requires that the "lastIndex"
   //       property be writable.
-  void initAndZeroLastIndex(JSAtom* source, RegExpFlag flags, JSContext* cx);
+  void initAndZeroLastIndex(JSAtom* source, JS::RegExpFlags flags,
+                            JSContext* cx);
 
 #ifdef DEBUG
   static MOZ_MUST_USE bool dumpBytecode(JSContext* cx,
                                         Handle<RegExpObject*> regexp,
                                         bool match_only,
                                         HandleLinearString input);
 #endif
 
@@ -188,17 +192,18 @@ class RegExpObject : public NativeObject
 };
 
 /*
  * Parse regexp flags. Report an error and return false if an invalid
  * sequence of flags is encountered (repeat/invalid flag).
  *
  * N.B. flagStr must be rooted.
  */
-bool ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
+bool ParseRegExpFlags(JSContext* cx, JSString* flagStr,
+                      JS::RegExpFlags* flagsOut);
 
 // Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for
 // obj.
 inline RegExpShared* RegExpToShared(JSContext* cx, HandleObject obj) {
   if (obj->is<RegExpObject>()) {
     return RegExpObject::getShared(cx, obj.as<RegExpObject>());
   }
 
--- a/js/src/vm/RegExpShared.h
+++ b/js/src/vm/RegExpShared.h
@@ -16,16 +16,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "gc/Marking.h"
 #include "gc/Zone.h"
 #include "js/AllocPolicy.h"
+#include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
 #include "js/UbiNode.h"
 #include "js/Vector.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSAtom.h"
 #include "vm/RegExpConstants.h"
 
 namespace js {
 
@@ -78,17 +79,17 @@ class RegExpShared : public gc::TenuredC
     bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
       return byteCode || (force == DontForceByteCode && jitCode);
     }
   };
 
   /* Source to the RegExp, for lazy compilation. */
   GCPtr<JSAtom*> source;
 
-  RegExpFlag flags;
+  JS::RegExpFlags flags;
   bool canStringMatch;
   size_t parenCount;
 
   RegExpCompilation compilationArray[4];
 
   static int CompilationIndex(CompilationMode mode, bool latin1) {
     switch (mode) {
       case Normal:
@@ -98,17 +99,17 @@ class RegExpShared : public gc::TenuredC
     }
     MOZ_CRASH();
   }
 
   // Tables referenced by JIT code.
   JitCodeTables tables;
 
   /* Internal functions. */
-  RegExpShared(JSAtom* source, RegExpFlag flags);
+  RegExpShared(JSAtom* source, JS::RegExpFlags flags);
 
   static bool compile(JSContext* cx, MutableHandleRegExpShared res,
                       HandleLinearString input, CompilationMode mode,
                       ForceByteCodeEnum force);
   static bool compile(JSContext* cx, MutableHandleRegExpShared res,
                       HandleAtom pattern, HandleLinearString input,
                       CompilationMode mode, ForceByteCodeEnum force);
 
@@ -143,22 +144,23 @@ class RegExpShared : public gc::TenuredC
     MOZ_ASSERT(isCompiled());
     return parenCount;
   }
 
   /* Accounts for the "0" (whole match) pair. */
   size_t pairCount() const { return getParenCount() + 1; }
 
   JSAtom* getSource() const { return source; }
-  RegExpFlag getFlags() const { return flags; }
-  bool ignoreCase() const { return flags & IgnoreCaseFlag; }
-  bool global() const { return flags & GlobalFlag; }
-  bool multiline() const { return flags & MultilineFlag; }
-  bool sticky() const { return flags & StickyFlag; }
-  bool unicode() const { return flags & UnicodeFlag; }
+  JS::RegExpFlags getFlags() const { return flags; }
+
+  bool global() const { return flags.global(); }
+  bool ignoreCase() const { return flags.ignoreCase(); }
+  bool multiline() const { return flags.multiline(); }
+  bool unicode() const { return flags.unicode(); }
+  bool sticky() const { return flags.sticky(); }
 
   bool isCompiled(CompilationMode mode, bool latin1,
                   ForceByteCodeEnum force = DontForceByteCode) const {
     return compilation(mode, latin1).compiled(force);
   }
   bool isCompiled() const {
     return isCompiled(Normal, true) || isCompiled(Normal, false) ||
            isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
@@ -192,32 +194,32 @@ class RegExpShared : public gc::TenuredC
 #ifdef DEBUG
   static bool dumpBytecode(JSContext* cx, MutableHandleRegExpShared res,
                            bool match_only, HandleLinearString input);
 #endif
 };
 
 class RegExpZone {
   struct Key {
-    JSAtom* atom;
-    uint16_t flag;
+    JSAtom* atom = nullptr;
+    JS::RegExpFlags flags = JS::RegExpFlag::NoFlags;
 
-    Key() : atom(nullptr), flag(0) {}
-    Key(JSAtom* atom, RegExpFlag flag) : atom(atom), flag(flag) {}
+    Key() = default;
+    Key(JSAtom* atom, JS::RegExpFlags flags) : atom(atom), flags(flags) {}
     MOZ_IMPLICIT Key(const ReadBarriered<RegExpShared*>& shared)
         : atom(shared.unbarrieredGet()->getSource()),
-          flag(shared.unbarrieredGet()->getFlags()) {}
+          flags(shared.unbarrieredGet()->getFlags()) {}
 
     typedef Key Lookup;
     static HashNumber hash(const Lookup& l) {
       HashNumber hash = DefaultHasher<JSAtom*>::hash(l.atom);
-      return mozilla::AddToHash(hash, l.flag);
+      return mozilla::AddToHash(hash, l.flags.value());
     }
     static bool match(Key l, Key r) {
-      return l.atom == r.atom && l.flag == r.flag;
+      return l.atom == r.atom && l.flags == r.flags;
     }
   };
 
   /*
    * The set of all RegExpShareds in the zone. On every GC, every RegExpShared
    * that was not marked is deleted and removed from the set.
    */
   using Set = JS::WeakCache<
@@ -226,22 +228,22 @@ class RegExpZone {
 
  public:
   explicit RegExpZone(Zone* zone);
 
   ~RegExpZone() { MOZ_ASSERT(set_.empty()); }
 
   bool empty() const { return set_.empty(); }
 
-  RegExpShared* maybeGet(JSAtom* source, RegExpFlag flags) const {
+  RegExpShared* maybeGet(JSAtom* source, JS::RegExpFlags flags) const {
     Set::Ptr p = set_.lookup(Key(source, flags));
     return p ? *p : nullptr;
   }
 
-  RegExpShared* get(JSContext* cx, HandleAtom source, RegExpFlag flags);
+  RegExpShared* get(JSContext* cx, HandleAtom source, JS::RegExpFlags flags);
 
   /* Like 'get', but compile 'maybeOpt' (if non-null). */
   RegExpShared* get(JSContext* cx, HandleAtom source, JSString* maybeOpt);
 
 #ifdef DEBUG
   void clear() { set_.clear(); }
 #endif
 
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -24,17 +24,17 @@ class RegExpStatics {
   HeapPtr<JSLinearString*> matchesInput;
 
   /*
    * The previous RegExp input, used to resolve lazy state.
    * A raw RegExpShared cannot be stored because it may be in
    * a different compartment via evalcx().
    */
   HeapPtr<JSAtom*> lazySource;
-  RegExpFlag lazyFlags;
+  JS::RegExpFlags lazyFlags;
   size_t lazyIndex;
 
   /* The latest RegExp input, set before execution. */
   HeapPtr<JSString*> pendingInput;
 
   /*
    * If non-zero, |matchesInput| and the |lazy*| fields may be used
    * to replay the last executed RegExp, and |matches| is invalid.
@@ -269,17 +269,17 @@ inline bool RegExpStatics::updateFromMat
 
   return true;
 }
 
 inline void RegExpStatics::clear() {
   matches.forgetArray();
   matchesInput = nullptr;
   lazySource = nullptr;
-  lazyFlags = RegExpFlag(0);
+  lazyFlags = JS::RegExpFlag::NoFlags;
   lazyIndex = size_t(-1);
   pendingInput = nullptr;
   pendingLazyEvaluation = false;
 }
 
 inline void RegExpStatics::setPendingInput(JSString* newInput) {
   pendingInput = newInput;
 }
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -23,16 +23,17 @@
  *     We serialize objects that appear in multiple places in the input as
  *     backreferences, using sequential integer indexes.
  *     See `JSStructuredCloneReader::allObjs`, our take on the "memory" map
  *     in the spec's StructuredDeserialize.
  */
 
 #include "js/StructuredClone.h"
 
+#include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/Unused.h"
 
 #include <algorithm>
 #include <memory>
@@ -41,16 +42,17 @@
 #include "jsapi.h"
 #include "jsdate.h"
 
 #include "builtin/DataViewObject.h"
 #include "builtin/MapObject.h"
 #include "js/ArrayBuffer.h"  // JS::{ArrayBufferHasData,DetachArrayBuffer,IsArrayBufferObject,New{,Mapped}ArrayBufferWithContents,ReleaseMappedArrayBufferContents}
 #include "js/Date.h"
 #include "js/GCHashTable.h"
+#include "js/RegExpFlags.h"        // JS::RegExpFlags
 #include "js/SharedArrayBuffer.h"  // JS::IsSharedArrayBufferObject
 #include "js/Wrapper.h"
 #include "vm/BigIntType.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpObject.h"
 #include "vm/SavedFrame.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
@@ -59,17 +61,19 @@
 
 #include "vm/InlineCharBuffer-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using JS::CanonicalizeNaN;
+using JS::RegExpFlags;
 using JS::RootedValueVector;
+using mozilla::AssertedCast;
 using mozilla::BitwiseCast;
 using mozilla::NativeEndian;
 using mozilla::NumbersAreIdentical;
 using mozilla::RangedPtr;
 
 // When you make updates here, make sure you consider whether you need to bump
 // the value of JS_STRUCTURED_CLONE_VERSION in js/public/StructuredClone.h.  You
 // will likely need to increment the version if anything at all changes in the
@@ -1664,17 +1668,17 @@ bool JSStructuredCloneWriter::startWrite
         }
         return out.writePair(SCTAG_BOOLEAN_OBJECT, unboxed.toBoolean());
       }
       case ESClass::RegExp: {
         RegExpShared* re = RegExpToShared(context(), obj);
         if (!re) {
           return false;
         }
-        return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
+        return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags().value()) &&
                writeString(SCTAG_STRING, re->getSource());
       }
       case ESClass::ArrayBuffer: {
         if (JS::IsArrayBufferObject(obj) && JS::ArrayBufferHasData(obj)) {
           return writeArrayBuffer(obj);
         }
         break;
       }
@@ -2443,17 +2447,17 @@ bool JSStructuredCloneReader::startRead(
       if (!obj) {
         return false;
       }
       vp.setObject(*obj);
       break;
     }
 
     case SCTAG_REGEXP_OBJECT: {
-      RegExpFlag flags = RegExpFlag(data);
+      RegExpFlags flags = AssertedCast<uint8_t>(data);
       uint32_t tag2, stringData;
       if (!in.readPair(&tag2, &stringData)) {
         return false;
       }
       if (tag2 != SCTAG_STRING) {
         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
                                   JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
         return false;