Bug 1516742 - Move {js::,JS_}{{Strictly,Loosely}Equal,SameValue} into js/public/Equality.h and js/src/vm/EqualityOperations.{cpp,h}. r=arai
authorJeff Walden <jwalden@mit.edu>
Sat, 29 Dec 2018 00:06:21 -0600
changeset 509935 bf02f2ce30a2be60d43a76aee69e9c9a4c15f41d
parent 509934 13029582fcc6b6fe1f6ee714d5db52577bd7ed41
child 509936 dc3c004e74d841bba1d53f22c07aeed809c448c1
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1516742
milestone66.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 1516742 - Move {js::,JS_}{{Strictly,Loosely}Equal,SameValue} into js/public/Equality.h and js/src/vm/EqualityOperations.{cpp,h}. r=arai
js/public/Equality.h
js/rust/build.rs
js/src/builtin/MapObject.cpp
js/src/builtin/ModuleObject.cpp
js/src/builtin/Object.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/VMFunctions.cpp
js/src/jsapi-tests/testLooselyEqual.cpp
js/src/jsapi-tests/testSameValue.cpp
js/src/jsapi-tests/tests.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/moz.build
js/src/proxy/ScriptedProxyHandler.cpp
js/src/shell/js.cpp
js/src/vm/BigIntType.h
js/src/vm/EqualityOperations.cpp
js/src/vm/EqualityOperations.h
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/NativeObject.cpp
new file mode 100644
--- /dev/null
+++ b/js/public/Equality.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* Equality operations. */
+
+#ifndef js_Equality_h
+#define js_Equality_h
+
+#include "jstypes.h"  // JS_PUBLIC_API
+
+#include "js/RootingAPI.h"  // JS::Handle
+#include "js/Value.h"       // JS::Value
+
+struct JSContext;
+
+namespace JS {
+
+/**
+ * Store |v1 === v2| to |*equal| -- strict equality, which performs no
+ * conversions on |v1| or |v2| before comparing.
+ *
+ * This operation can fail only if an internal error occurs (e.g. OOM while
+ * linearizing a string value).
+ */
+extern JS_PUBLIC_API bool StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> v1,
+                                        JS::Handle<JS::Value> v2, bool* equal);
+
+/**
+ * Store |v1 == v2| to |*equal| -- loose equality, which may perform
+ * user-modifiable conversions on |v1| or |v2|.
+ *
+ * This operation can fail if a user-modifiable conversion fails *or* if an
+ * internal error occurs. (e.g. OOM while linearizing a string value).
+ */
+extern JS_PUBLIC_API bool LooselyEqual(JSContext* cx, JS::Handle<JS::Value> v1,
+                                       JS::Handle<JS::Value> v2, bool* equal);
+
+/**
+ * Stores |SameValue(v1, v2)| to |*equal| -- using the SameValue operation
+ * defined in ECMAScript, initially exposed to script as |Object.is|.  SameValue
+ * behaves identically to strict equality, except that it equates two NaN values
+ * and does not equate differently-signed zeroes.  It performs no conversions on
+ * |v1| or |v2| before comparing.
+ *
+ * This operation can fail only if an internal error occurs (e.g. OOM while
+ * linearizing a string value).
+ */
+extern JS_PUBLIC_API bool SameValue(JSContext* cx, JS::Handle<JS::Value> v1,
+                                    JS::Handle<JS::Value> v2, bool* same);
+
+}  // namespace JS
+
+#endif /* js_Equality_h */
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -394,17 +394,17 @@ const WHITELIST_FUNCTIONS: &'static [&'s
     "JS_ReadUint32Pair",
     "JS_RemoveExtraGCRootsTracer",
     "js::RemoveRawValueRoot",
     "JS_ReportErrorASCII",
     "JS_ReportErrorNumberUTF8",
     "JS_RequestInterruptCallback",
     "JS_ResolveStandardClass",
     "js::RunJobs",
-    "JS_SameValue",
+    "JS::SameValue",
     "js::SetDOMCallbacks",
     "js::SetDOMProxyInformation",
     "JS::SetEnqueuePromiseJobCallback",
     "js::SetFunctionNativeReserved",
     "JS_SetGCCallback",
     "JS::SetGCSliceCallback",
     "JS_SetGCParameter",
     "JS_SetGCZeal",
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/MapObject.h"
 
 #include "ds/OrderedHashTable.h"
 #include "gc/FreeOp.h"
 #include "js/Utility.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/SymbolType.h"
 
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -12,16 +12,17 @@
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "gc/Tracer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/SelfHosting.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 
 using namespace js;
 
 static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -14,16 +14,17 @@
 #include "builtin/Eval.h"
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "jit/InlinableNatives.h"
 #include "js/UniquePtr.h"
 #include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/JSContext.h"
 #include "vm/RegExpObject.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -42,16 +42,17 @@
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "jit/StackSlotAllocator.h"
 #include "jit/VMFunctions.h"
 #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"
 #include "vm/TraceLogging.h"
 #include "vm/TypedArrayObject.h"
 #include "vtune/VTuneWrapper.h"
 #include "wasm/WasmStubs.h"
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -12,16 +12,17 @@
 #include "jit/arm/Simulator-arm.h"
 #include "jit/BaselineIC.h"
 #include "jit/JitFrames.h"
 #include "jit/JitRealm.h"
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
+#include "vm/EqualityOperations.h"  // js::StrictlyEqual
 #include "vm/Interpreter.h"
 #include "vm/SelfHosting.h"
 #include "vm/TraceLogging.h"
 
 #include "jit/BaselineFrame-inl.h"
 #include "jit/JitFrames-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/Interpreter-inl.h"
--- a/js/src/jsapi-tests/testLooselyEqual.cpp
+++ b/js/src/jsapi-tests/testLooselyEqual.cpp
@@ -1,33 +1,34 @@
 /* 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 <limits>
 #include <math.h>
 
+#include "js/Equality.h"  // JS::LooselyEqual
 #include "jsapi-tests/tests.h"
 
 using namespace std;
 
 struct LooseEqualityFixture : public JSAPITest {
   virtual ~LooseEqualityFixture() {}
 
   bool leq(JS::HandleValue x, JS::HandleValue y) {
     bool equal;
-    CHECK(JS_LooselyEqual(cx, x, y, &equal) && equal);
-    CHECK(JS_LooselyEqual(cx, y, x, &equal) && equal);
+    CHECK(JS::LooselyEqual(cx, x, y, &equal) && equal);
+    CHECK(JS::LooselyEqual(cx, y, x, &equal) && equal);
     return true;
   }
 
   bool nleq(JS::HandleValue x, JS::HandleValue y) {
     bool equal;
-    CHECK(JS_LooselyEqual(cx, x, y, &equal) && !equal);
-    CHECK(JS_LooselyEqual(cx, y, x, &equal) && !equal);
+    CHECK(JS::LooselyEqual(cx, x, y, &equal) && !equal);
+    CHECK(JS::LooselyEqual(cx, y, x, &equal) && !equal);
     return true;
   }
 };
 
 struct LooseEqualityData {
   JS::RootedValue qNaN;
   JS::RootedValue sNaN;
   JS::RootedValue d42;
--- a/js/src/jsapi-tests/testSameValue.cpp
+++ b/js/src/jsapi-tests/testSameValue.cpp
@@ -1,25 +1,26 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "js/Equality.h"  // JS::SameValue
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testSameValue) {
   /*
    * NB: passing a double that fits in an integer jsval is API misuse.  As a
-   * matter of defense in depth, however, JS_SameValue should return the
+   * matter of defense in depth, however, JS::SameValue should return the
    * correct result comparing a positive-zero double to a negative-zero
    * double, and this is believed to be the only way to make such a
    * comparison possible.
    */
   JS::RootedValue v1(cx, JS::DoubleValue(0.0));
   JS::RootedValue v2(cx, JS::DoubleValue(-0.0));
   bool same;
-  CHECK(JS_SameValue(cx, v1, v2, &same));
+  CHECK(JS::SameValue(cx, v1, v2, &same));
   CHECK(!same);
   return true;
 }
 END_TEST(testSameValue)
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -14,16 +14,17 @@
 #include <errno.h>
 #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/Vector.h"
 #include "vm/JSContext.h"
 
 /* Note: Aborts on OOM. */
 class JSAPITestString {
   js::Vector<char, 0, js::SystemAllocPolicy> chars;
 
  public:
@@ -203,22 +204,22 @@ class JSAPITest {
     if (!checkNull(actual, #actual, __FILE__, __LINE__)) return false; \
   } while (false)
 
   bool checkSame(const JS::Value& actualArg, const JS::Value& expectedArg,
                  const char* actualExpr, const char* expectedExpr,
                  const char* filename, int lineno) {
     bool same;
     JS::RootedValue actual(cx, actualArg), expected(cx, expectedArg);
-    return (JS_SameValue(cx, actual, expected, &same) && same) ||
+    return (JS::SameValue(cx, actual, expected, &same) && same) ||
            fail(JSAPITestString(
-                    "CHECK_SAME failed: expected JS_SameValue(cx, ") +
+                    "CHECK_SAME failed: expected JS::SameValue(cx, ") +
                     actualExpr + ", " + expectedExpr +
-                    "), got !JS_SameValue(cx, " + jsvalToSource(actual) + ", " +
-                    jsvalToSource(expected) + ")",
+                    "), got !JS::SameValue(cx, " + jsvalToSource(actual) +
+                    ", " + jsvalToSource(expected) + ")",
                 filename, lineno);
   }
 
 #define CHECK_SAME(actual, expected)                                          \
   do {                                                                        \
     if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
       return false;                                                           \
   } while (false)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -354,43 +354,16 @@ JS_PUBLIC_API bool JS_DoubleIsInt32(doub
 
 JS_PUBLIC_API JSType JS_TypeOfValue(JSContext* cx, HandleValue value) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(value);
   return TypeOfValue(value);
 }
 
-JS_PUBLIC_API bool JS_StrictlyEqual(JSContext* cx, HandleValue value1,
-                                    HandleValue value2, bool* equal) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  cx->check(value1, value2);
-  MOZ_ASSERT(equal);
-  return StrictlyEqual(cx, value1, value2, equal);
-}
-
-JS_PUBLIC_API bool JS_LooselyEqual(JSContext* cx, HandleValue value1,
-                                   HandleValue value2, bool* equal) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  cx->check(value1, value2);
-  MOZ_ASSERT(equal);
-  return LooselyEqual(cx, value1, value2, equal);
-}
-
-JS_PUBLIC_API bool JS_SameValue(JSContext* cx, HandleValue value1,
-                                HandleValue value2, bool* same) {
-  AssertHeapIsIdle();
-  CHECK_THREAD(cx);
-  cx->check(value1, value2);
-  MOZ_ASSERT(same);
-  return SameValue(cx, value1, value2, same);
-}
-
 JS_PUBLIC_API bool JS_IsBuiltinEvalFunction(JSFunction* fun) {
   return IsAnyBuiltinEval(fun);
 }
 
 JS_PUBLIC_API bool JS_IsBuiltinFunctionConstructor(JSFunction* fun) {
   return fun->isBuiltinFunctionConstructor();
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -339,29 +339,16 @@ extern JS_PUBLIC_API JSType JS_TypeOfVal
                                            JS::Handle<JS::Value> v);
 
 namespace JS {
 
 extern JS_PUBLIC_API const char* InformalValueTypeName(const JS::Value& v);
 
 } /* namespace JS */
 
-extern JS_PUBLIC_API bool JS_StrictlyEqual(JSContext* cx,
-                                           JS::Handle<JS::Value> v1,
-                                           JS::Handle<JS::Value> v2,
-                                           bool* equal);
-
-extern JS_PUBLIC_API bool JS_LooselyEqual(JSContext* cx,
-                                          JS::Handle<JS::Value> v1,
-                                          JS::Handle<JS::Value> v2,
-                                          bool* equal);
-
-extern JS_PUBLIC_API bool JS_SameValue(JSContext* cx, JS::Handle<JS::Value> v1,
-                                       JS::Handle<JS::Value> v2, bool* same);
-
 /** True iff fun is the global eval function. */
 extern JS_PUBLIC_API bool JS_IsBuiltinEvalFunction(JSFunction* fun);
 
 /** True iff fun is the Function constructor. */
 extern JS_PUBLIC_API bool JS_IsBuiltinFunctionConstructor(JSFunction* fun);
 
 /************************************************************************/
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -118,16 +118,17 @@ EXPORTS.js += [
     '../public/CallNonGenericMethod.h',
     '../public/CharacterEncoding.h',
     '../public/Class.h',
     '../public/CompilationAndEvaluation.h',
     '../public/CompileOptions.h',
     '../public/Conversions.h',
     '../public/Date.h',
     '../public/Debug.h',
+    '../public/Equality.h',
     '../public/ErrorReport.h',
     '../public/GCAnnotations.h',
     '../public/GCAPI.h',
     '../public/GCHashTable.h',
     '../public/GCPolicyAPI.h',
     '../public/GCVariant.h',
     '../public/GCVector.h',
     '../public/HashTable.h',
@@ -258,16 +259,17 @@ UNIFIED_SOURCES += [
     'vm/CodeCoverage.cpp',
     'vm/Compartment.cpp',
     'vm/CompilationAndEvaluation.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/DebuggerMemory.cpp',
     'vm/EnvironmentObject.cpp',
+    'vm/EqualityOperations.cpp',
     'vm/ErrorObject.cpp',
     'vm/ErrorReporting.cpp',
     'vm/ForOfIterator.cpp',
     'vm/GeckoProfiler.cpp',
     'vm/GeneratorObject.cpp',
     'vm/GlobalObject.cpp',
     'vm/HelperThreads.cpp',
     'vm/Id.cpp',
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "proxy/ScriptedProxyHandler.h"
 
 #include "jsapi.h"
 
 #include "js/CharacterEncoding.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using JS::IsArrayAnswer;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -82,16 +82,17 @@
 #include "jit/JitcodeMap.h"
 #include "jit/JitRealm.h"
 #include "jit/shared/CodeGenerator-shared.h"
 #include "js/BuildId.h"  // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
+#include "js/Equality.h"  // JS::SameValue
 #include "js/GCVector.h"
 #include "js/Initialization.h"
 #include "js/JSON.h"
 #include "js/MemoryFunctions.h"
 #include "js/Printf.h"
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
@@ -2714,17 +2715,17 @@ static bool AssertEq(JSContext* cx, unsi
         (args.length() < 2)
             ? JSSMSG_NOT_ENOUGH_ARGS
             : (args.length() == 3) ? JSSMSG_INVALID_ARGS : JSSMSG_TOO_MANY_ARGS,
         "assertEq");
     return false;
   }
 
   bool same;
-  if (!JS_SameValue(cx, args[0], args[1], &same)) {
+  if (!JS::SameValue(cx, args[0], args[1], &same)) {
     return false;
   }
   if (!same) {
     UniqueChars bytes0, bytes1;
     const char* actual = ToSource(cx, args[0], &bytes0);
     const char* expected = ToSource(cx, args[1], &bytes1);
     if (args.length() == 2) {
       JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
--- a/js/src/vm/BigIntType.h
+++ b/js/src/vm/BigIntType.h
@@ -14,41 +14,48 @@
 
 #include "gc/Barrier.h"
 #include "gc/GC.h"
 #include "gc/Heap.h"
 #include "js/AllocPolicy.h"
 #include "js/GCHashTable.h"
 #include "js/Result.h"
 #include "js/RootingAPI.h"
+#include "js/TraceKind.h"
 #include "js/TypeDecls.h"
 #include "vm/StringType.h"
 #include "vm/Xdr.h"
 
+namespace JS {
+
+class BigInt;
+
+}  // namespace JS
+
 namespace js {
 
 template <typename CharT>
 static bool StringToBigIntImpl(const mozilla::Range<const CharT>& chars,
                                uint8_t radix, Handle<JS::BigInt*> res);
 
 template <XDRMode mode>
-XDRResult XDRBigInt(XDRState<mode>* xdr, MutableHandleBigInt bi);
+XDRResult XDRBigInt(XDRState<mode>* xdr, MutableHandle<JS::BigInt*> bi);
 
 }  // namespace js
 
 namespace JS {
 
 class BigInt final : public js::gc::TenuredCell {
   // StringToBigIntImpl modifies the num_ field of the res argument.
   template <typename CharT>
   friend bool js::StringToBigIntImpl(const mozilla::Range<const CharT>& chars,
                                      uint8_t radix, Handle<BigInt*> res);
   template <js::XDRMode mode>
   friend js::XDRResult js::XDRBigInt(js::XDRState<mode>* xdr,
-                                     MutableHandleBigInt bi);
+                                     MutableHandle<BigInt*> bi);
 
  protected:
   // Reserved word for Cell GC invariants. This also ensures minimum
   // structure size.
   uintptr_t reserved_;
 
  private:
   mpz_t num_;
@@ -99,18 +106,18 @@ class BigInt final : public js::gc::Tenu
   static BigInt* bitAnd(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
   static BigInt* bitXor(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
   static BigInt* bitOr(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
   static BigInt* bitNot(JSContext* cx, Handle<BigInt*> x);
 
   static int64_t toInt64(BigInt* x);
   static uint64_t toUint64(BigInt* x);
 
-  static BigInt* asIntN(JSContext* cx, HandleBigInt x, uint64_t bits);
-  static BigInt* asUintN(JSContext* cx, HandleBigInt x, uint64_t bits);
+  static BigInt* asIntN(JSContext* cx, Handle<BigInt*> x, uint64_t bits);
+  static BigInt* asUintN(JSContext* cx, Handle<BigInt*> x, uint64_t bits);
 
   // Type-checking versions of arithmetic operations. These methods
   // must be called with at least one BigInt operand. Binary
   // operations with throw a TypeError if one of the operands is not a
   // BigInt value.
   static bool add(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                   MutableHandle<Value> res);
   static bool sub(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
@@ -138,28 +145,28 @@ class BigInt final : public js::gc::Tenu
   static bool bitNot(JSContext* cx, Handle<Value> operand,
                      MutableHandle<Value> res);
 
   static double numberValue(BigInt* x);
   static JSLinearString* toString(JSContext* cx, BigInt* x, uint8_t radix);
 
   static bool equal(BigInt* lhs, BigInt* rhs);
   static bool equal(BigInt* lhs, double rhs);
-  static JS::Result<bool> looselyEqual(JSContext* cx, HandleBigInt lhs,
+  static JS::Result<bool> looselyEqual(JSContext* cx, Handle<BigInt*> lhs,
                                        HandleValue rhs);
 
   static bool lessThan(BigInt* x, BigInt* y);
 
   // These methods return Nothing when the non-BigInt operand is NaN
   // or a string that can't be interpreted as a BigInt.
   static mozilla::Maybe<bool> lessThan(BigInt* lhs, double rhs);
   static mozilla::Maybe<bool> lessThan(double lhs, BigInt* rhs);
-  static bool lessThan(JSContext* cx, HandleBigInt lhs, HandleString rhs,
+  static bool lessThan(JSContext* cx, Handle<BigInt*> lhs, HandleString rhs,
                        mozilla::Maybe<bool>& res);
-  static bool lessThan(JSContext* cx, HandleString lhs, HandleBigInt rhs,
+  static bool lessThan(JSContext* cx, HandleString lhs, Handle<BigInt*> rhs,
                        mozilla::Maybe<bool>& res);
   static bool lessThan(JSContext* cx, HandleValue lhs, HandleValue rhs,
                        mozilla::Maybe<bool>& res);
 
   // Return the length in bytes of the representation used by
   // writeBytes.
   static size_t byteLength(BigInt* x);
 
new file mode 100644
--- /dev/null
+++ b/js/src/vm/EqualityOperations.cpp
@@ -0,0 +1,258 @@
+/* -*- 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 "vm/EqualityOperations.h"  // js::LooselyEqual, js::StrictlyEqual, js::SameValue
+
+#include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_ASSERT_IF
+
+#include "jsapi.h"    // js::AssertHeapIsIdle
+#include "jsnum.h"    // js::StringToNumber
+#include "jstypes.h"  // JS_PUBLIC_API
+
+#include "js/Equality.h"  // JS::LooselyEqual, JS::StrictlyEqual, JS::SameValue
+#include "js/Result.h"    // JS_TRY_VAR_OR_RETURN_FALSE
+#include "js/RootingAPI.h"  // JS::Rooted
+#include "js/Value.h"       // JS::Int32Value, JS::SameType, JS::Value
+#ifdef ENABLE_BIGINT
+#include "vm/BigIntType.h"  // JS::BigInt
+#endif                      // ENABLE_BIGINT
+#include "vm/JSContext.h"   // CHECK_THREAD
+#include "vm/JSObject.h"    // js::ToPrimitive
+#include "vm/StringType.h"  // js::EqualStrings
+
+#include "builtin/Boolean-inl.h"  // js::EmulatesUndefined
+#include "vm/JSContext-inl.h"     // JSContext::check
+
+static bool EqualGivenSameType(JSContext* cx, JS::Handle<JS::Value> lval,
+                               JS::Handle<JS::Value> rval, bool* equal) {
+  MOZ_ASSERT(JS::SameType(lval, rval));
+
+  if (lval.isString()) {
+    return js::EqualStrings(cx, lval.toString(), rval.toString(), equal);
+  }
+
+  if (lval.isDouble()) {
+    *equal = (lval.toDouble() == rval.toDouble());
+    return true;
+  }
+
+#ifdef ENABLE_BIGINT
+  if (lval.isBigInt()) {
+    *equal = JS::BigInt::equal(lval.toBigInt(), rval.toBigInt());
+    return true;
+  }
+#endif
+
+  if (lval.isGCThing()) {  // objects or symbols
+    *equal = (lval.toGCThing() == rval.toGCThing());
+    return true;
+  }
+
+  *equal = lval.get().payloadAsRawUint32() == rval.get().payloadAsRawUint32();
+  MOZ_ASSERT_IF(lval.isUndefined() || lval.isNull(), *equal);
+  return true;
+}
+
+static bool LooselyEqualBooleanAndOther(JSContext* cx,
+                                        JS::Handle<JS::Value> lval,
+                                        JS::Handle<JS::Value> rval,
+                                        bool* result) {
+  MOZ_ASSERT(!rval.isBoolean());
+
+  JS::Rooted<JS::Value> lvalue(cx, JS::Int32Value(lval.toBoolean() ? 1 : 0));
+
+  // The tail-call would end up in Step 3.
+  if (rval.isNumber()) {
+    *result = (lvalue.toNumber() == rval.toNumber());
+    return true;
+  }
+  // The tail-call would end up in Step 6.
+  if (rval.isString()) {
+    double num;
+    if (!StringToNumber(cx, rval.toString(), &num)) {
+      return false;
+    }
+    *result = (lvalue.toNumber() == num);
+    return true;
+  }
+
+  return js::LooselyEqual(cx, lvalue, rval, result);
+}
+
+// ES6 draft rev32 7.2.12 Abstract Equality Comparison
+bool js::LooselyEqual(JSContext* cx, JS::Handle<JS::Value> lval,
+                      JS::Handle<JS::Value> rval, bool* result) {
+  // Step 3.
+  if (JS::SameType(lval, rval)) {
+    return EqualGivenSameType(cx, lval, rval, result);
+  }
+
+  // Handle int32 x double.
+  if (lval.isNumber() && rval.isNumber()) {
+    *result = (lval.toNumber() == rval.toNumber());
+    return true;
+  }
+
+  // Step 4. This a bit more complex, because of the undefined emulating object.
+  if (lval.isNullOrUndefined()) {
+    // We can return early here, because null | undefined is only equal to the
+    // same set.
+    *result = rval.isNullOrUndefined() ||
+              (rval.isObject() && EmulatesUndefined(&rval.toObject()));
+    return true;
+  }
+
+  // Step 5.
+  if (rval.isNullOrUndefined()) {
+    MOZ_ASSERT(!lval.isNullOrUndefined());
+    *result = lval.isObject() && EmulatesUndefined(&lval.toObject());
+    return true;
+  }
+
+  // Step 6.
+  if (lval.isNumber() && rval.isString()) {
+    double num;
+    if (!StringToNumber(cx, rval.toString(), &num)) {
+      return false;
+    }
+    *result = (lval.toNumber() == num);
+    return true;
+  }
+
+  // Step 7.
+  if (lval.isString() && rval.isNumber()) {
+    double num;
+    if (!StringToNumber(cx, lval.toString(), &num)) {
+      return false;
+    }
+    *result = (num == rval.toNumber());
+    return true;
+  }
+
+  // Step 8.
+  if (lval.isBoolean()) {
+    return LooselyEqualBooleanAndOther(cx, lval, rval, result);
+  }
+
+  // Step 9.
+  if (rval.isBoolean()) {
+    return LooselyEqualBooleanAndOther(cx, rval, lval, result);
+  }
+
+  // Step 10.
+  if ((lval.isString() || lval.isNumber() || lval.isSymbol()) &&
+      rval.isObject()) {
+    JS::Rooted<JS::Value> rvalue(cx, rval);
+    if (!ToPrimitive(cx, &rvalue)) {
+      return false;
+    }
+    return js::LooselyEqual(cx, lval, rvalue, result);
+  }
+
+  // Step 11.
+  if (lval.isObject() &&
+      (rval.isString() || rval.isNumber() || rval.isSymbol())) {
+    JS::Rooted<JS::Value> lvalue(cx, lval);
+    if (!ToPrimitive(cx, &lvalue)) {
+      return false;
+    }
+    return js::LooselyEqual(cx, lvalue, rval, result);
+  }
+
+#ifdef ENABLE_BIGINT
+  if (lval.isBigInt()) {
+    JS::Rooted<JS::BigInt*> lbi(cx, lval.toBigInt());
+    bool tmpResult;
+    JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult,
+                               JS::BigInt::looselyEqual(cx, lbi, rval));
+    *result = tmpResult;
+    return true;
+  }
+
+  if (rval.isBigInt()) {
+    JS::Rooted<JS::BigInt*> rbi(cx, rval.toBigInt());
+    bool tmpResult;
+    JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult,
+                               JS::BigInt::looselyEqual(cx, rbi, lval));
+    *result = tmpResult;
+    return true;
+  }
+#endif
+
+  // Step 12.
+  *result = false;
+  return true;
+}
+
+JS_PUBLIC_API bool JS::LooselyEqual(JSContext* cx, Handle<Value> value1,
+                                    Handle<Value> value2, bool* equal) {
+  js::AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  cx->check(value1, value2);
+  MOZ_ASSERT(equal);
+  return js::LooselyEqual(cx, value1, value2, equal);
+}
+
+bool js::StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> lval,
+                       JS::Handle<JS::Value> rval, bool* equal) {
+  if (SameType(lval, rval)) {
+    return EqualGivenSameType(cx, lval, rval, equal);
+  }
+
+  if (lval.isNumber() && rval.isNumber()) {
+    *equal = (lval.toNumber() == rval.toNumber());
+    return true;
+  }
+
+  *equal = false;
+  return true;
+}
+
+JS_PUBLIC_API bool JS::StrictlyEqual(JSContext* cx, Handle<Value> value1,
+                                     Handle<Value> value2, bool* equal) {
+  js::AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  cx->check(value1, value2);
+  MOZ_ASSERT(equal);
+  return js::StrictlyEqual(cx, value1, value2, equal);
+}
+
+static inline bool IsNegativeZero(const JS::Value& v) {
+  return v.isDouble() && mozilla::IsNegativeZero(v.toDouble());
+}
+
+static inline bool IsNaN(const JS::Value& v) {
+  return v.isDouble() && mozilla::IsNaN(v.toDouble());
+}
+
+bool js::SameValue(JSContext* cx, JS::Handle<JS::Value> v1,
+                   JS::Handle<JS::Value> v2, bool* same) {
+  if (IsNegativeZero(v1)) {
+    *same = IsNegativeZero(v2);
+    return true;
+  }
+
+  if (IsNegativeZero(v2)) {
+    *same = false;
+    return true;
+  }
+
+  if (IsNaN(v1) && IsNaN(v2)) {
+    *same = true;
+    return true;
+  }
+
+  return js::StrictlyEqual(cx, v1, v2, same);
+}
+
+JS_PUBLIC_API bool JS::SameValue(JSContext* cx, Handle<Value> value1,
+                                 Handle<Value> value2, bool* same) {
+  js::AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  cx->check(value1, value2);
+  MOZ_ASSERT(same);
+  return js::SameValue(cx, value1, value2, same);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/EqualityOperations.h
@@ -0,0 +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/. */
+
+/*
+ * The equality comparisons of js/Equality.h, but with extra efficiency for
+ * SpiderMonkey-internal callers.
+ *
+ * These functions, assuming they're passed C++-valid arguments, are identical
+ * to the same-named JS::-namespaced functions -- just with hidden linkage (so
+ * they're more efficient to call), and without various external-caller-focused
+ * JSAPI-usage assertions performed that SpiderMonkey users never come close to
+ * failing.
+ */
+
+#ifndef vm_EqualityOperations_h
+#define vm_EqualityOperations_h
+
+#include "js/RootingAPI.h"  // JS::Handle
+#include "js/Value.h"       // JS::Value
+
+struct JSContext;
+
+namespace js {
+
+/** Computes |lval === rval|. */
+extern bool StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> lval,
+                          JS::Handle<JS::Value> rval, bool* equal);
+
+/** Computes |lval == rval|. */
+extern bool LooselyEqual(JSContext* cx, JS::Handle<JS::Value> lval,
+                         JS::Handle<JS::Value> rval, bool* equal);
+
+/**
+ * Computes |SameValue(v1, v2)| -- strict equality except that NaNs are
+ * considered equal and opposite-signed zeroes are considered unequal.
+ */
+extern bool SameValue(JSContext* cx, JS::Handle<JS::Value> v1,
+                      JS::Handle<JS::Value> v2, bool* same);
+
+}  // namespace js
+
+#endif  // vm_EqualityOperations_h
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -35,16 +35,17 @@
 #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/EqualityOperations.h"  // js::StrictlyEqual
 #include "vm/GeneratorObject.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Opcodes.h"
@@ -858,209 +859,16 @@ bool js::HasInstance(JSContext* cx, Hand
   const Class* clasp = obj->getClass();
   RootedValue local(cx, v);
   if (JSHasInstanceOp hasInstance = clasp->getHasInstance()) {
     return hasInstance(cx, obj, &local, bp);
   }
   return JS::InstanceofOperator(cx, obj, local, bp);
 }
 
-static inline bool EqualGivenSameType(JSContext* cx, HandleValue lval,
-                                      HandleValue rval, bool* equal) {
-  MOZ_ASSERT(SameType(lval, rval));
-
-  if (lval.isString()) {
-    return EqualStrings(cx, lval.toString(), rval.toString(), equal);
-  }
-  if (lval.isDouble()) {
-    *equal = (lval.toDouble() == rval.toDouble());
-    return true;
-  }
-#ifdef ENABLE_BIGINT
-  if (lval.isBigInt()) {
-    *equal = BigInt::equal(lval.toBigInt(), rval.toBigInt());
-    return true;
-  }
-#endif
-  if (lval.isGCThing()) {  // objects or symbols
-    *equal = (lval.toGCThing() == rval.toGCThing());
-    return true;
-  }
-  *equal = lval.get().payloadAsRawUint32() == rval.get().payloadAsRawUint32();
-  MOZ_ASSERT_IF(lval.isUndefined() || lval.isNull(), *equal);
-  return true;
-}
-
-static inline bool LooselyEqualBooleanAndOther(JSContext* cx, HandleValue lval,
-                                               HandleValue rval, bool* result) {
-  MOZ_ASSERT(!rval.isBoolean());
-  RootedValue lvalue(cx, Int32Value(lval.toBoolean() ? 1 : 0));
-
-  // The tail-call would end up in Step 3.
-  if (rval.isNumber()) {
-    *result = (lvalue.toNumber() == rval.toNumber());
-    return true;
-  }
-  // The tail-call would end up in Step 6.
-  if (rval.isString()) {
-    double num;
-    if (!StringToNumber(cx, rval.toString(), &num)) {
-      return false;
-    }
-    *result = (lvalue.toNumber() == num);
-    return true;
-  }
-
-  return LooselyEqual(cx, lvalue, rval, result);
-}
-
-// ES6 draft rev32 7.2.12 Abstract Equality Comparison
-bool js::LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval,
-                      bool* result) {
-  // Step 3.
-  if (SameType(lval, rval)) {
-    return EqualGivenSameType(cx, lval, rval, result);
-  }
-
-  // Handle int32 x double.
-  if (lval.isNumber() && rval.isNumber()) {
-    *result = (lval.toNumber() == rval.toNumber());
-    return true;
-  }
-
-  // Step 4. This a bit more complex, because of the undefined emulating object.
-  if (lval.isNullOrUndefined()) {
-    // We can return early here, because null | undefined is only equal to the
-    // same set.
-    *result = rval.isNullOrUndefined() ||
-              (rval.isObject() && EmulatesUndefined(&rval.toObject()));
-    return true;
-  }
-
-  // Step 5.
-  if (rval.isNullOrUndefined()) {
-    MOZ_ASSERT(!lval.isNullOrUndefined());
-    *result = lval.isObject() && EmulatesUndefined(&lval.toObject());
-    return true;
-  }
-
-  // Step 6.
-  if (lval.isNumber() && rval.isString()) {
-    double num;
-    if (!StringToNumber(cx, rval.toString(), &num)) {
-      return false;
-    }
-    *result = (lval.toNumber() == num);
-    return true;
-  }
-
-  // Step 7.
-  if (lval.isString() && rval.isNumber()) {
-    double num;
-    if (!StringToNumber(cx, lval.toString(), &num)) {
-      return false;
-    }
-    *result = (num == rval.toNumber());
-    return true;
-  }
-
-  // Step 8.
-  if (lval.isBoolean()) {
-    return LooselyEqualBooleanAndOther(cx, lval, rval, result);
-  }
-
-  // Step 9.
-  if (rval.isBoolean()) {
-    return LooselyEqualBooleanAndOther(cx, rval, lval, result);
-  }
-
-  // Step 10.
-  if ((lval.isString() || lval.isNumber() || lval.isSymbol()) &&
-      rval.isObject()) {
-    RootedValue rvalue(cx, rval);
-    if (!ToPrimitive(cx, &rvalue)) {
-      return false;
-    }
-    return LooselyEqual(cx, lval, rvalue, result);
-  }
-
-  // Step 11.
-  if (lval.isObject() &&
-      (rval.isString() || rval.isNumber() || rval.isSymbol())) {
-    RootedValue lvalue(cx, lval);
-    if (!ToPrimitive(cx, &lvalue)) {
-      return false;
-    }
-    return LooselyEqual(cx, lvalue, rval, result);
-  }
-
-#ifdef ENABLE_BIGINT
-  if (lval.isBigInt()) {
-    RootedBigInt lbi(cx, lval.toBigInt());
-    bool tmpResult;
-    JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult,
-                               BigInt::looselyEqual(cx, lbi, rval));
-    *result = tmpResult;
-    return true;
-  }
-
-  if (rval.isBigInt()) {
-    RootedBigInt rbi(cx, rval.toBigInt());
-    bool tmpResult;
-    JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult,
-                               BigInt::looselyEqual(cx, rbi, lval));
-    *result = tmpResult;
-    return true;
-  }
-#endif
-
-  // Step 12.
-  *result = false;
-  return true;
-}
-
-bool js::StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval,
-                       bool* equal) {
-  if (SameType(lval, rval)) {
-    return EqualGivenSameType(cx, lval, rval, equal);
-  }
-
-  if (lval.isNumber() && rval.isNumber()) {
-    *equal = (lval.toNumber() == rval.toNumber());
-    return true;
-  }
-
-  *equal = false;
-  return true;
-}
-
-static inline bool IsNegativeZero(const Value& v) {
-  return v.isDouble() && mozilla::IsNegativeZero(v.toDouble());
-}
-
-static inline bool IsNaN(const Value& v) {
-  return v.isDouble() && mozilla::IsNaN(v.toDouble());
-}
-
-bool js::SameValue(JSContext* cx, HandleValue v1, HandleValue v2, bool* same) {
-  if (IsNegativeZero(v1)) {
-    *same = IsNegativeZero(v2);
-    return true;
-  }
-  if (IsNegativeZero(v2)) {
-    *same = false;
-    return true;
-  }
-  if (IsNaN(v1) && IsNaN(v2)) {
-    *same = true;
-    return true;
-  }
-  return StrictlyEqual(cx, v1, v2, same);
-}
-
 JSType js::TypeOfObject(JSObject* obj) {
   if (EmulatesUndefined(obj)) {
     return JSTYPE_UNDEFINED;
   }
   if (obj->isCallable()) {
     return JSTYPE_FUNCTION;
   }
   return JSTYPE_OBJECT;
@@ -2643,24 +2451,26 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
 
     CASE(JSOP_NE) {
       if (!LooseEqualityOp<false>(cx, REGS)) {
         goto error;
       }
     }
     END_CASE(JSOP_NE)
 
-#define STRICT_EQUALITY_OP(OP, COND)                        \
-  JS_BEGIN_MACRO                                            \
-    HandleValue lval = REGS.stackHandleAt(-2);              \
-    HandleValue rval = REGS.stackHandleAt(-1);              \
-    bool equal;                                             \
-    if (!StrictlyEqual(cx, lval, rval, &equal)) goto error; \
-    (COND) = equal OP true;                                 \
-    REGS.sp--;                                              \
+#define STRICT_EQUALITY_OP(OP, COND)                  \
+  JS_BEGIN_MACRO                                      \
+    HandleValue lval = REGS.stackHandleAt(-2);        \
+    HandleValue rval = REGS.stackHandleAt(-1);        \
+    bool equal;                                       \
+    if (!js::StrictlyEqual(cx, lval, rval, &equal)) { \
+      goto error;                                     \
+    }                                                 \
+    (COND) = equal OP true;                           \
+    REGS.sp--;                                        \
   JS_END_MACRO
 
     CASE(JSOP_STRICTEQ) {
       bool cond;
       STRICT_EQUALITY_OP(==, cond);
       REGS.sp[-1].setBoolean(cond);
     }
     END_CASE(JSOP_STRICTEQ)
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -284,26 +284,16 @@ inline void RunState::setReturnValue(con
     asInvoke()->setReturnValue(v);
   } else {
     asExecute()->setReturnValue(v);
   }
 }
 
 extern bool RunScript(JSContext* cx, RunState& state);
 
-extern bool StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval,
-                          bool* equal);
-
-extern bool LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval,
-                         bool* equal);
-
-/* === except that NaN is the same as NaN and -0 is not the same as +0. */
-extern bool SameValue(JSContext* cx, HandleValue v1, HandleValue v2,
-                      bool* same);
-
 extern JSType TypeOfObject(JSObject* obj);
 
 extern JSType TypeOfValue(const Value& v);
 
 extern bool HasInstance(JSContext* cx, HandleObject obj, HandleValue v,
                         bool* bp);
 
 // Unwind environment chain and iterator to match the scope corresponding to
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 
 #include "gc/Marking.h"
 #include "jit/BaselineIC.h"
 #include "js/CharacterEncoding.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
+#include "vm/EqualityOperations.h"  // js::SameValue
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"