summaryrefslogtreecommitdiffstats
path: root/js/public
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/public
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--js/public/AllocPolicy.h207
-rw-r--r--js/public/AllocationLogging.h78
-rw-r--r--js/public/AllocationRecording.h72
-rw-r--r--js/public/Array.h121
-rw-r--r--js/public/ArrayBuffer.h264
-rw-r--r--js/public/ArrayBufferMaybeShared.h94
-rw-r--r--js/public/BigInt.h139
-rw-r--r--js/public/BuildId.h84
-rw-r--r--js/public/CallArgs.h361
-rw-r--r--js/public/CallNonGenericMethod.h123
-rw-r--r--js/public/CharacterEncoding.h427
-rw-r--r--js/public/Class.h938
-rw-r--r--js/public/ComparisonOperators.h237
-rw-r--r--js/public/CompilationAndEvaluation.h248
-rw-r--r--js/public/CompileOptions.h473
-rw-r--r--js/public/ContextOptions.h297
-rw-r--r--js/public/Conversions.h590
-rw-r--r--js/public/Date.h209
-rw-r--r--js/public/Debug.h349
-rw-r--r--js/public/Equality.h66
-rw-r--r--js/public/ErrorReport.h361
-rw-r--r--js/public/Exception.h69
-rw-r--r--js/public/ForOfIterator.h118
-rw-r--r--js/public/GCAPI.h1182
-rw-r--r--js/public/GCAnnotations.h82
-rw-r--r--js/public/GCHashTable.h796
-rw-r--r--js/public/GCPolicyAPI.h242
-rw-r--r--js/public/GCTypeMacros.h45
-rw-r--r--js/public/GCVariant.h182
-rw-r--r--js/public/GCVector.h375
-rw-r--r--js/public/HashTable.h38
-rw-r--r--js/public/HeapAPI.h765
-rw-r--r--js/public/Id.h328
-rw-r--r--js/public/Initialization.h115
-rw-r--r--js/public/JSON.h95
-rw-r--r--js/public/LocaleSensitive.h95
-rw-r--r--js/public/MemoryFunctions.h100
-rw-r--r--js/public/MemoryMetrics.h902
-rw-r--r--js/public/Modules.h214
-rw-r--r--js/public/Object.h118
-rw-r--r--js/public/OffThreadScriptCompilation.h183
-rw-r--r--js/public/Principals.h160
-rw-r--r--js/public/Printf.h34
-rw-r--r--js/public/ProfilingCategory.h65
-rw-r--r--js/public/ProfilingFrameIterator.h240
-rw-r--r--js/public/ProfilingStack.h570
-rw-r--r--js/public/Promise.h587
-rw-r--r--js/public/PropertyDescriptor.h383
-rw-r--r--js/public/PropertySpec.h428
-rw-r--r--js/public/ProtoKey.h135
-rw-r--r--js/public/Proxy.h748
-rw-r--r--js/public/Realm.h118
-rw-r--r--js/public/RealmOptions.h399
-rw-r--r--js/public/RefCounted.h91
-rw-r--r--js/public/RegExp.h95
-rw-r--r--js/public/RegExpFlags.h152
-rw-r--r--js/public/RequiredDefines.h31
-rw-r--r--js/public/Result.h279
-rw-r--r--js/public/RootingAPI.h1545
-rw-r--r--js/public/SavedFrameAPI.h160
-rw-r--r--js/public/ScalarType.h166
-rw-r--r--js/public/SharedArrayBuffer.h81
-rw-r--r--js/public/SliceBudget.h97
-rw-r--r--js/public/SourceText.h289
-rw-r--r--js/public/StableStringChars.h116
-rw-r--r--js/public/Stream.h542
-rw-r--r--js/public/String.h233
-rw-r--r--js/public/StructuredClone.h752
-rw-r--r--js/public/SweepingAPI.h110
-rw-r--r--js/public/Symbol.h110
-rw-r--r--js/public/TraceKind.h277
-rw-r--r--js/public/TraceLoggerAPI.h217
-rw-r--r--js/public/TracingAPI.h546
-rw-r--r--js/public/Transcoding.h190
-rw-r--r--js/public/TypeDecls.h129
-rw-r--r--js/public/UbiNode.h1209
-rw-r--r--js/public/UbiNodeBreadthFirst.h271
-rw-r--r--js/public/UbiNodeCensus.h238
-rw-r--r--js/public/UbiNodeDominatorTree.h691
-rw-r--r--js/public/UbiNodePostOrder.h188
-rw-r--r--js/public/UbiNodeShortestPaths.h344
-rw-r--r--js/public/UbiNodeUtils.h51
-rw-r--r--js/public/UniquePtr.h56
-rw-r--r--js/public/Utility.h690
-rw-r--r--js/public/Value.h1323
-rw-r--r--js/public/ValueArray.h130
-rw-r--r--js/public/Vector.h38
-rw-r--r--js/public/Warnings.h98
-rw-r--r--js/public/WasmModule.h46
-rw-r--r--js/public/WeakMapPtr.h46
-rw-r--r--js/public/Wrapper.h511
-rw-r--r--js/public/experimental/CTypes.h105
-rw-r--r--js/public/experimental/CodeCoverage.h40
-rw-r--r--js/public/experimental/Intl.h50
-rw-r--r--js/public/experimental/JitInfo.h336
-rw-r--r--js/public/experimental/SourceHook.h99
-rw-r--r--js/public/experimental/TypedData.h422
-rw-r--r--js/public/friend/DOMProxy.h91
-rw-r--r--js/public/friend/DumpFunctions.h113
-rw-r--r--js/public/friend/ErrorMessages.h47
-rw-r--r--js/public/friend/ErrorNumbers.msg782
-rw-r--r--js/public/friend/JSMEnvironment.h93
-rw-r--r--js/public/friend/StackLimits.h165
-rw-r--r--js/public/friend/UsageStatistics.h93
-rw-r--r--js/public/friend/WindowProxy.h106
-rw-r--r--js/public/friend/XrayJitInfo.h57
-rw-r--r--js/public/shadow/Function.h48
-rw-r--r--js/public/shadow/Object.h67
-rw-r--r--js/public/shadow/ObjectGroup.h33
-rw-r--r--js/public/shadow/Realm.h38
-rw-r--r--js/public/shadow/Shape.h46
-rw-r--r--js/public/shadow/String.h120
-rw-r--r--js/public/shadow/Symbol.h38
-rw-r--r--js/public/shadow/Zone.h97
114 files changed, 30873 insertions, 0 deletions
diff --git a/js/public/AllocPolicy.h b/js/public/AllocPolicy.h
new file mode 100644
index 0000000000..e27bc4b184
--- /dev/null
+++ b/js/public/AllocPolicy.h
@@ -0,0 +1,207 @@
+/* -*- 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/. */
+
+/*
+ * JS allocation policies.
+ *
+ * The allocators here are for system memory with lifetimes which are not
+ * managed by the GC. See the comment at the top of vm/MallocProvider.h.
+ */
+
+#ifndef js_AllocPolicy_h
+#define js_AllocPolicy_h
+
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+extern MOZ_COLD JS_PUBLIC_API void JS_ReportOutOfMemory(JSContext* cx);
+
+namespace js {
+
+enum class AllocFunction { Malloc, Calloc, Realloc };
+
+/* Base class allocation policies providing allocation methods. */
+class AllocPolicyBase {
+ public:
+ template <typename T>
+ T* maybe_pod_arena_malloc(arena_id_t arenaId, size_t numElems) {
+ return js_pod_arena_malloc<T>(arenaId, numElems);
+ }
+ template <typename T>
+ T* maybe_pod_arena_calloc(arena_id_t arenaId, size_t numElems) {
+ return js_pod_arena_calloc<T>(arenaId, numElems);
+ }
+ template <typename T>
+ T* maybe_pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize,
+ size_t newSize) {
+ return js_pod_arena_realloc<T>(arenaId, p, oldSize, newSize);
+ }
+ template <typename T>
+ T* pod_arena_malloc(arena_id_t arenaId, size_t numElems) {
+ return maybe_pod_arena_malloc<T>(arenaId, numElems);
+ }
+ template <typename T>
+ T* pod_arena_calloc(arena_id_t arenaId, size_t numElems) {
+ return maybe_pod_arena_calloc<T>(arenaId, numElems);
+ }
+ template <typename T>
+ T* pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize,
+ size_t newSize) {
+ return maybe_pod_arena_realloc<T>(arenaId, p, oldSize, newSize);
+ }
+
+ template <typename T>
+ T* maybe_pod_malloc(size_t numElems) {
+ return maybe_pod_arena_malloc<T>(js::MallocArena, numElems);
+ }
+ template <typename T>
+ T* maybe_pod_calloc(size_t numElems) {
+ return maybe_pod_arena_calloc<T>(js::MallocArena, numElems);
+ }
+ template <typename T>
+ T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
+ return maybe_pod_arena_realloc<T>(js::MallocArena, p, oldSize, newSize);
+ }
+ template <typename T>
+ T* pod_malloc(size_t numElems) {
+ return pod_arena_malloc<T>(js::MallocArena, numElems);
+ }
+ template <typename T>
+ T* pod_calloc(size_t numElems) {
+ return pod_arena_calloc<T>(js::MallocArena, numElems);
+ }
+ template <typename T>
+ T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
+ return pod_arena_realloc<T>(js::MallocArena, p, oldSize, newSize);
+ }
+
+ template <typename T>
+ void free_(T* p, size_t numElems = 0) {
+ js_free(p);
+ }
+};
+
+/* Policy for using system memory functions and doing no error reporting. */
+class SystemAllocPolicy : public AllocPolicyBase {
+ public:
+ void reportAllocOverflow() const {}
+ bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); }
+};
+
+MOZ_COLD JS_FRIEND_API void ReportOutOfMemory(JSContext* cx);
+
+/*
+ * Allocation policy that calls the system memory functions and reports errors
+ * to the context. Since the JSContext given on construction is stored for
+ * the lifetime of the container, this policy may only be used for containers
+ * whose lifetime is a shorter than the given JSContext.
+ *
+ * FIXME bug 647103 - rewrite this in terms of temporary allocation functions,
+ * not the system ones.
+ */
+class TempAllocPolicy : public AllocPolicyBase {
+ JSContext* const cx_;
+
+ /*
+ * Non-inline helper to call JSRuntime::onOutOfMemory with minimal
+ * code bloat.
+ */
+ JS_FRIEND_API void* onOutOfMemory(arena_id_t arenaId, AllocFunction allocFunc,
+ size_t nbytes, void* reallocPtr = nullptr);
+
+ template <typename T>
+ T* onOutOfMemoryTyped(arena_id_t arenaId, AllocFunction allocFunc,
+ size_t numElems, void* reallocPtr = nullptr) {
+ size_t bytes;
+ if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
+ return nullptr;
+ }
+ return static_cast<T*>(
+ onOutOfMemory(arenaId, allocFunc, bytes, reallocPtr));
+ }
+
+ public:
+ MOZ_IMPLICIT TempAllocPolicy(JSContext* cx) : cx_(cx) {}
+
+ template <typename T>
+ T* pod_arena_malloc(arena_id_t arenaId, size_t numElems) {
+ T* p = this->maybe_pod_arena_malloc<T>(arenaId, numElems);
+ if (MOZ_UNLIKELY(!p)) {
+ p = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Malloc, numElems);
+ }
+ return p;
+ }
+
+ template <typename T>
+ T* pod_arena_calloc(arena_id_t arenaId, size_t numElems) {
+ T* p = this->maybe_pod_arena_calloc<T>(arenaId, numElems);
+ if (MOZ_UNLIKELY(!p)) {
+ p = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Calloc, numElems);
+ }
+ return p;
+ }
+
+ template <typename T>
+ T* pod_arena_realloc(arena_id_t arenaId, T* prior, size_t oldSize,
+ size_t newSize) {
+ T* p2 = this->maybe_pod_arena_realloc<T>(arenaId, prior, oldSize, newSize);
+ if (MOZ_UNLIKELY(!p2)) {
+ p2 = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Realloc, newSize,
+ prior);
+ }
+ return p2;
+ }
+
+ template <typename T>
+ T* pod_malloc(size_t numElems) {
+ return pod_arena_malloc<T>(js::MallocArena, numElems);
+ }
+
+ template <typename T>
+ T* pod_calloc(size_t numElems) {
+ return pod_arena_calloc<T>(js::MallocArena, numElems);
+ }
+
+ template <typename T>
+ T* pod_realloc(T* prior, size_t oldSize, size_t newSize) {
+ return pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize);
+ }
+
+ template <typename T>
+ void free_(T* p, size_t numElems = 0) {
+ js_free(p);
+ }
+
+ JS_FRIEND_API void reportAllocOverflow() const;
+
+ bool checkSimulatedOOM() const {
+ if (js::oom::ShouldFailWithOOM()) {
+ ReportOutOfMemory(cx_);
+ return false;
+ }
+
+ return true;
+ }
+};
+
+/*
+ * A replacement for MallocAllocPolicy that allocates in the JS heap and adds no
+ * extra behaviours.
+ *
+ * This is currently used for allocating source buffers for parsing. Since these
+ * are temporary and will not be freed by GC, the memory is not tracked by the
+ * usual accounting.
+ */
+class MallocAllocPolicy : public AllocPolicyBase {
+ public:
+ void reportAllocOverflow() const {}
+
+ MOZ_MUST_USE bool checkSimulatedOOM() const { return true; }
+};
+
+} /* namespace js */
+
+#endif /* js_AllocPolicy_h */
diff --git a/js/public/AllocationLogging.h b/js/public/AllocationLogging.h
new file mode 100644
index 0000000000..d1735eb1d8
--- /dev/null
+++ b/js/public/AllocationLogging.h
@@ -0,0 +1,78 @@
+/* -*- 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/. */
+
+/*
+ * Embedder-supplied tracking of the allocation and deallocation of various
+ * non-garbage-collected objects in SpiderMonkey.
+ *
+ * This functionality is intended to track allocation of non-user-visible
+ * structures, for debugging the C++ of an embedding. It is not intended to
+ * give the end user visibility into allocations in JS. Instead see
+ * AllocationRecording.h for such functionality.
+ */
+
+#ifndef js_AllocationLogging_h
+#define js_AllocationLogging_h
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/PodOperations.h"
+
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+namespace JS {
+
+using LogCtorDtor = void (*)(void*, const char*, uint32_t);
+
+/**
+ * Set global functions used to monitor classes to highlight leaks.
+ *
+ * For each C++ class that uses these mechanisms, the allocation of an instance
+ * will log the constructor call, and its subsequent deallocation will log the
+ * destructor call. If only the former occurs, the instance/allocation is
+ * leaked. With carefully-written logging functions, this can be used to debug
+ * the origin of the leaks.
+ */
+extern JS_PUBLIC_API void SetLogCtorDtorFunctions(LogCtorDtor ctor,
+ LogCtorDtor dtor);
+
+/**
+ * Log the allocation of |self|, having a type uniquely identified by the string
+ * |type|, with allocation size |sz|.
+ *
+ * You generally should use |JS_COUNT_CTOR| and |JS_COUNT_DTOR| instead of
+ * using this function directly.
+ */
+extern JS_PUBLIC_API void LogCtor(void* self, const char* type, uint32_t sz);
+
+/**
+ * Log the deallocation of |self|, having a type uniquely identified by the
+ * string |type|, with allocation size |sz|.
+ *
+ * You generally should use |JS_COUNT_CTOR| and |JS_COUNT_DTOR| instead of
+ * using this function directly.
+ */
+extern JS_PUBLIC_API void LogDtor(void* self, const char* type, uint32_t sz);
+
+/**
+ * Within each non-delegating constructor of a |Class|, use
+ * |JS_COUNT_CTOR(Class);| to log the allocation of |this|. (If you do this in
+ * delegating constructors, you might count a single allocation multiple times.)
+ */
+#define JS_COUNT_CTOR(Class) \
+ (::JS::LogCtor(static_cast<void*>(this), #Class, sizeof(Class)))
+
+/**
+ * Within the destructor of a |Class|, use |JS_COUNT_DTOR(Class);| to log the
+ * deallocation of |this|.
+ */
+#define JS_COUNT_DTOR(Class) \
+ (::JS::LogDtor(static_cast<void*>(this), #Class, sizeof(Class)))
+
+} // namespace JS
+
+#endif // js_AllocationLogging_h
diff --git a/js/public/AllocationRecording.h b/js/public/AllocationRecording.h
new file mode 100644
index 0000000000..f2487e9a93
--- /dev/null
+++ b/js/public/AllocationRecording.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_AllocationRecording_h
+#define js_AllocationRecording_h
+
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+namespace JS {
+
+/**
+ * This struct holds the information needed to create a profiler marker payload
+ * that can represent a JS allocation. It translates JS engine specific classes,
+ * into something that can be used in the profiler.
+ */
+struct RecordAllocationInfo {
+ RecordAllocationInfo(const char16_t* typeName, const char* className,
+ const char16_t* descriptiveTypeName,
+ const char* coarseType, uint64_t size, bool inNursery)
+ : typeName(typeName),
+ className(className),
+ descriptiveTypeName(descriptiveTypeName),
+ coarseType(coarseType),
+ size(size),
+ inNursery(inNursery) {}
+
+ // These pointers are borrowed from the UbiNode, and can point to live data.
+ // It is important for the consumers of this struct to correctly
+ // duplicate the strings to take ownership of them.
+ const char16_t* typeName;
+ const char* className;
+ const char16_t* descriptiveTypeName;
+
+ // The coarseType points to a string literal, so does not need to be
+ // duplicated.
+ const char* coarseType;
+
+ // The size in bytes of the allocation.
+ uint64_t size;
+
+ // Whether or not the allocation is in the nursery or not.
+ bool inNursery;
+};
+
+typedef void (*RecordAllocationsCallback)(RecordAllocationInfo&& info);
+
+/**
+ * Enable recording JS allocations. This feature hooks into the object creation
+ * in the JavaScript engine, and reports back the allocation info through the
+ * callback. This allocation tracking is turned on for all encountered realms.
+ * The JS Debugger API can also turn on allocation tracking with its own
+ * probability. If both allocation tracking mechanisms are turned on at the same
+ * time, the Debugger's probability defers to the EnableRecordingAllocations's
+ * probability setting.
+ */
+JS_FRIEND_API void EnableRecordingAllocations(
+ JSContext* cx, RecordAllocationsCallback callback, double probability);
+
+/**
+ * Turn off JS allocation recording. If any JS Debuggers are also recording
+ * allocations, then the probability will be reset to the Debugger's desired
+ * setting.
+ */
+JS_FRIEND_API void DisableRecordingAllocations(JSContext* cx);
+
+} // namespace JS
+
+#endif /* js_AllocationRecording_h */
diff --git a/js/public/Array.h b/js/public/Array.h
new file mode 100644
index 0000000000..b6f7d09c83
--- /dev/null
+++ b/js/public/Array.h
@@ -0,0 +1,121 @@
+/* -*- 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/. */
+
+/* Array-related operations. */
+
+#ifndef js_Array_h
+#define js_Array_h
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class HandleValueArray;
+
+/**
+ * Create an Array from the current realm with the given contents.
+ */
+extern JS_PUBLIC_API JSObject* NewArrayObject(JSContext* cx,
+ const HandleValueArray& contents);
+
+/**
+ * Create an Array from the current realm with the given length and allocate
+ * memory for all its elements. (The elements nonetheless will not exist as
+ * properties on the returned array until values have been assigned to them.)
+ */
+extern JS_PUBLIC_API JSObject* NewArrayObject(JSContext* cx, size_t length);
+
+/**
+ * Determine whether |value| is an Array object or a wrapper around one. (An
+ * ES6 proxy whose target is an Array object, e.g.
+ * |var target = [], handler = {}; Proxy.revocable(target, handler).proxy|, is
+ * not considered to be an Array.)
+ *
+ * On success set |*isArray| accordingly and return true; on failure return
+ * false.
+ */
+extern JS_PUBLIC_API bool IsArrayObject(JSContext* cx, Handle<Value> value,
+ bool* isArray);
+
+/**
+ * Determine whether |obj| is an Array object or a wrapper around one. (An
+ * ES6 proxy whose target is an Array object, e.g.
+ * |var target = [], handler = {}; Proxy.revocable(target, handler).proxy|, is
+ * not considered to be an Array.)
+ *
+ * On success set |*isArray| accordingly and return true; on failure return
+ * false.
+ */
+extern JS_PUBLIC_API bool IsArrayObject(JSContext* cx, Handle<JSObject*> obj,
+ bool* isArray);
+
+/**
+ * Store |*lengthp = ToLength(obj.length)| and return true on success, else
+ * return false.
+ *
+ * |ToLength| converts its input to an integer usable to index an
+ * array-like object.
+ *
+ * If |obj| is an Array, this overall operation is the same as getting
+ * |obj.length|.
+ */
+extern JS_PUBLIC_API bool GetArrayLength(JSContext* cx, Handle<JSObject*> obj,
+ uint32_t* lengthp);
+
+/**
+ * Perform |obj.length = length| as if in strict mode code, with a fast path for
+ * the case where |obj| is an Array.
+ *
+ * This operation is exactly and only assigning to a "length" property. In
+ * general, it can invoke an existing "length" setter, throw if the property is
+ * non-writable, or do anything else a property-set operation might do.
+ */
+extern JS_PUBLIC_API bool SetArrayLength(JSContext* cx, Handle<JSObject*> obj,
+ uint32_t length);
+
+/**
+ * The answer to a successful query as to whether an object is an Array per
+ * ES6's internal |IsArray| operation (as exposed by |Array.isArray|).
+ */
+enum class IsArrayAnswer { Array, NotArray, RevokedProxy };
+
+/**
+ * ES6 7.2.2.
+ *
+ * Returns false on failure, otherwise returns true and sets |*isArray|
+ * indicating whether the object passes ECMAScript's IsArray test. This is the
+ * same test performed by |Array.isArray|.
+ *
+ * This is NOT the same as asking whether |obj| is an Array or a wrapper around
+ * one. If |obj| is a proxy created by |Proxy.revocable()| and has been
+ * revoked, or if |obj| is a proxy whose target (at any number of hops) is a
+ * revoked proxy, this method throws a TypeError and returns false.
+ */
+extern JS_PUBLIC_API bool IsArray(JSContext* cx, Handle<JSObject*> obj,
+ bool* isArray);
+
+/**
+ * Identical to IsArray above, but the nature of the object (if successfully
+ * determined) is communicated via |*answer|. In particular this method
+ * returns true and sets |*answer = IsArrayAnswer::RevokedProxy| when called on
+ * a revoked proxy.
+ *
+ * Most users will want the overload above, not this one.
+ */
+extern JS_PUBLIC_API bool IsArray(JSContext* cx, Handle<JSObject*> obj,
+ IsArrayAnswer* answer);
+
+} // namespace JS
+
+#endif // js_Array_h
diff --git a/js/public/ArrayBuffer.h b/js/public/ArrayBuffer.h
new file mode 100644
index 0000000000..95704d32f2
--- /dev/null
+++ b/js/public/ArrayBuffer.h
@@ -0,0 +1,264 @@
+/* -*- 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/. */
+
+/* ArrayBuffer functionality. */
+
+#ifndef js_ArrayBuffer_h
+#define js_ArrayBuffer_h
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/GCAPI.h" // JS::AutoRequireNoGC
+#include "js/RootingAPI.h" // JS::Handle
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+// CREATION
+
+/**
+ * Create a new ArrayBuffer with the given byte length.
+ */
+extern JS_PUBLIC_API JSObject* NewArrayBuffer(JSContext* cx, uint32_t nbytes);
+
+/**
+ * Create a new ArrayBuffer with the given |contents|, which may be null only
+ * if |nbytes == 0|. |contents| must be allocated compatible with deallocation
+ * by |JS_free|.
+ *
+ * If and only if an ArrayBuffer is successfully created and returned,
+ * ownership of |contents| is transferred to the new ArrayBuffer.
+ *
+ * Care must be taken that |nbytes| bytes of |content| remain valid for the
+ * duration of this call. In particular, passing the length/pointer of existing
+ * typed array or ArrayBuffer data is generally unsafe: if a GC occurs during a
+ * call to this function, it could move those contents to a different location
+ * and invalidate the provided pointer.
+ */
+extern JS_PUBLIC_API JSObject* NewArrayBufferWithContents(JSContext* cx,
+ size_t nbytes,
+ void* contents);
+
+/**
+ * Create a new ArrayBuffer, whose bytes are set to the values of the bytes in
+ * the provided ArrayBuffer.
+ *
+ * |maybeArrayBuffer| is asserted to be non-null. An error is thrown if
+ * |maybeArrayBuffer| would fail the |IsArrayBufferObject| test given further
+ * below or if |maybeArrayBuffer| is detached.
+ *
+ * |maybeArrayBuffer| may store its contents in any fashion (i.e. it doesn't
+ * matter whether |maybeArrayBuffer| was allocated using |JS::NewArrayBuffer|,
+ * |JS::NewExternalArrayBuffer|, or any other ArrayBuffer-allocating function).
+ *
+ * The newly-created ArrayBuffer is effectively creatd as if by
+ * |JS::NewArrayBufferWithContents| passing in |maybeArrayBuffer|'s internal
+ * data pointer and length, in a manner safe against |maybeArrayBuffer|'s data
+ * being moved around by the GC. In particular, the new ArrayBuffer will not
+ * behave like one created for WASM or asm.js, so it *can* be detached.
+ */
+extern JS_PUBLIC_API JSObject* CopyArrayBuffer(
+ JSContext* cx, JS::Handle<JSObject*> maybeArrayBuffer);
+
+using BufferContentsFreeFunc = void (*)(void* contents, void* userData);
+
+/**
+ * Create a new ArrayBuffer with the given contents. The contents must not be
+ * modified by any other code, internal or external.
+ *
+ * When the ArrayBuffer is ready to be disposed of, `freeFunc(contents,
+ * freeUserData)` will be called to release the ArrayBuffer's reference on the
+ * contents.
+ *
+ * `freeFunc()` must not call any JSAPI functions that could cause a garbage
+ * collection.
+ *
+ * The caller must keep the buffer alive until `freeFunc()` is called, or, if
+ * `freeFunc` is null, until the JSRuntime is destroyed.
+ *
+ * The caller must not access the buffer on other threads. The JS engine will
+ * not allow the buffer to be transferred to other threads. If you try to
+ * transfer an external ArrayBuffer to another thread, the data is copied to a
+ * new malloc buffer. `freeFunc()` must be threadsafe, and may be called from
+ * any thread.
+ *
+ * This allows ArrayBuffers to be used with embedder objects that use reference
+ * counting, for example. In that case the caller is responsible
+ * for incrementing the reference count before passing the contents to this
+ * function. This also allows using non-reference-counted contents that must be
+ * freed with some function other than free().
+ */
+extern JS_PUBLIC_API JSObject* NewExternalArrayBuffer(
+ JSContext* cx, size_t nbytes, void* contents,
+ BufferContentsFreeFunc freeFunc, void* freeUserData = nullptr);
+
+/**
+ * Create a new ArrayBuffer with the given non-null |contents|.
+ *
+ * Ownership of |contents| remains with the caller: it isn't transferred to the
+ * returned ArrayBuffer. Callers of this function *must* ensure that they
+ * perform these two steps, in this order, to properly relinquish ownership of
+ * |contents|:
+ *
+ * 1. Call |JS::DetachArrayBuffer| on the buffer returned by this function.
+ * (|JS::DetachArrayBuffer| is generally fallible, but a call under these
+ * circumstances is guaranteed to succeed.)
+ * 2. |contents| may be deallocated or discarded consistent with the manner
+ * in which it was allocated.
+ *
+ * Do not simply allow the returned buffer to be garbage-collected before
+ * deallocating |contents|, because in general there is no way to know *when*
+ * an object is fully garbage-collected to the point where this would be safe.
+ */
+extern JS_PUBLIC_API JSObject* NewArrayBufferWithUserOwnedContents(
+ JSContext* cx, size_t nbytes, void* contents);
+
+/**
+ * Create a new mapped ArrayBuffer with the given memory mapped contents. It
+ * must be legal to free the contents pointer by unmapping it. On success,
+ * ownership is transferred to the new mapped ArrayBuffer.
+ */
+extern JS_PUBLIC_API JSObject* NewMappedArrayBufferWithContents(JSContext* cx,
+ size_t nbytes,
+ void* contents);
+
+/**
+ * Create memory mapped ArrayBuffer contents.
+ * Caller must take care of closing fd after calling this function.
+ */
+extern JS_PUBLIC_API void* CreateMappedArrayBufferContents(int fd,
+ size_t offset,
+ size_t length);
+
+/**
+ * Release the allocated resource of mapped ArrayBuffer contents before the
+ * object is created.
+ * If a new object has been created by JS::NewMappedArrayBufferWithContents()
+ * with this content, then JS::DetachArrayBuffer() should be used instead to
+ * release the resource used by the object.
+ */
+extern JS_PUBLIC_API void ReleaseMappedArrayBufferContents(void* contents,
+ size_t length);
+
+// TYPE TESTING
+
+/*
+ * Check whether obj supports the JS::GetArrayBuffer* APIs. Note that this may
+ * return false if a security wrapper is encountered that denies the unwrapping.
+ * If this test succeeds, then it is safe to call the various predicate and
+ * accessor JSAPI calls defined below.
+ */
+extern JS_PUBLIC_API bool IsArrayBufferObject(JSObject* obj);
+
+// PREDICATES
+
+/**
+ * Check whether the obj is a detached ArrayBufferObject. Note that this may
+ * return false if a security wrapper is encountered that denies the
+ * unwrapping.
+ */
+extern JS_PUBLIC_API bool IsDetachedArrayBufferObject(JSObject* obj);
+
+/**
+ * Check whether the obj is ArrayBufferObject and memory mapped. Note that this
+ * may return false if a security wrapper is encountered that denies the
+ * unwrapping.
+ */
+extern JS_PUBLIC_API bool IsMappedArrayBufferObject(JSObject* obj);
+
+/**
+ * Return true if the ArrayBuffer |obj| contains any data, i.e. it is not a
+ * detached ArrayBuffer. (ArrayBuffer.prototype is not an ArrayBuffer.)
+ *
+ * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known
+ * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
+ * ArrayBuffer, and the unwrapping will succeed.
+ */
+extern JS_PUBLIC_API bool ArrayBufferHasData(JSObject* obj);
+
+// ACCESSORS
+
+extern JS_PUBLIC_API JSObject* UnwrapArrayBuffer(JSObject* obj);
+
+/**
+ * Attempt to unwrap |obj| as an ArrayBuffer.
+ *
+ * If |obj| *is* an ArrayBuffer, return it unwrapped and set |*length| and
+ * |*data| to weakly refer to the ArrayBuffer's contents.
+ *
+ * If |obj| isn't an ArrayBuffer, return nullptr and do not modify |*length| or
+ * |*data|.
+ */
+extern JS_PUBLIC_API JSObject* GetObjectAsArrayBuffer(JSObject* obj,
+ uint32_t* length,
+ uint8_t** data);
+
+/**
+ * Return the available byte length of an ArrayBuffer.
+ *
+ * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known
+ * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
+ * ArrayBuffer, and the unwrapping will succeed.
+ */
+extern JS_PUBLIC_API uint32_t GetArrayBufferByteLength(JSObject* obj);
+
+// This one isn't inlined because there are a bunch of different ArrayBuffer
+// classes that would have to be individually handled here.
+//
+// There is an isShared out argument for API consistency (eases use from DOM).
+// It will always be set to false.
+extern JS_PUBLIC_API void GetArrayBufferLengthAndData(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ uint8_t** data);
+
+/**
+ * Return a pointer to the start of the data referenced by a typed array. The
+ * data is still owned by the typed array, and should not be modified on
+ * another thread. Furthermore, the pointer can become invalid on GC (if the
+ * data is small and fits inside the array's GC header), so callers must take
+ * care not to hold on across anything that could GC.
+ *
+ * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known
+ * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
+ * ArrayBuffer, and the unwrapping will succeed.
+ *
+ * |*isSharedMemory| is always set to false. The argument is present to
+ * simplify its use from code that also interacts with SharedArrayBuffer.
+ */
+extern JS_PUBLIC_API uint8_t* GetArrayBufferData(JSObject* obj,
+ bool* isSharedMemory,
+ const AutoRequireNoGC&);
+
+// MUTATORS
+
+/**
+ * Detach an ArrayBuffer, causing all associated views to no longer refer to
+ * the ArrayBuffer's original attached memory.
+ *
+ * This function throws only if it is provided a non-ArrayBuffer object or if
+ * the provided ArrayBuffer is a WASM-backed ArrayBuffer or an ArrayBuffer used
+ * in asm.js code.
+ */
+extern JS_PUBLIC_API bool DetachArrayBuffer(JSContext* cx,
+ Handle<JSObject*> obj);
+
+/**
+ * Steal the contents of the given ArrayBuffer. The ArrayBuffer has its length
+ * set to 0 and its contents array cleared. The caller takes ownership of the
+ * return value and must free it or transfer ownership via
+ * JS::NewArrayBufferWithContents when done using it.
+ */
+extern JS_PUBLIC_API void* StealArrayBufferContents(JSContext* cx,
+ Handle<JSObject*> obj);
+
+} // namespace JS
+
+#endif /* js_ArrayBuffer_h */
diff --git a/js/public/ArrayBufferMaybeShared.h b/js/public/ArrayBufferMaybeShared.h
new file mode 100644
index 0000000000..3281b6903d
--- /dev/null
+++ b/js/public/ArrayBufferMaybeShared.h
@@ -0,0 +1,94 @@
+/* -*- 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/. */
+
+/**
+ * Functions for working with either ArrayBuffer or SharedArrayBuffer objects
+ * in agnostic fashion.
+ */
+
+#ifndef js_ArrayBufferMaybeShared_h
+#define js_ArrayBufferMaybeShared_h
+
+#include <stdint.h> // uint8_t, uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API AutoRequireNoGC;
+
+// TYPE TESTING
+
+/**
+ * Check whether obj supports the JS::GetArrayBufferMaybeShared* APIs. Note
+ * that this may return false if a security wrapper is encountered that denies
+ * the unwrapping. If this test succeeds, then it is safe to call the various
+ * predicate and accessor JSAPI calls defined below.
+ */
+extern JS_PUBLIC_API bool IsArrayBufferObjectMaybeShared(JSObject* obj);
+
+// ACCESSORS
+
+/*
+ * Test for ArrayBufferMaybeShared subtypes and return the unwrapped object if
+ * so, else nullptr. Never throws.
+ */
+extern JS_PUBLIC_API JSObject* UnwrapArrayBufferMaybeShared(JSObject* obj);
+
+/**
+ * Get the length, sharedness, and data from an ArrayBufferMaybeShared subtypes.
+ *
+ * The computed length and data pointer may be invalidated by a GC or by an
+ * unshared array buffer becoming detached. Callers must take care not to
+ * perform any actions that could trigger a GC or result in an unshared array
+ * buffer becoming detached. If such actions nonetheless must be performed,
+ * callers should perform this call a second time (and sensibly handle results
+ * that may be different from those returned the first time). (Sharedness is an
+ * immutable characteristic of an array buffer or shared array buffer, so that
+ * boolean remains valid across GC or detaching.)
+ *
+ * |obj| must be an ArrayBufferMaybeShared subtype: an ArrayBuffer or a
+ * SharedArrayBuffer.
+ *
+ * |*length| will be set to bytes in the buffer.
+ *
+ * |*isSharedMemory| will be set to true if it is a SharedArrayBuffer, otherwise
+ * to false.
+ *
+ * |*data| will be set to a pointer to the bytes in the buffer.
+ */
+extern JS_PUBLIC_API void GetArrayBufferMaybeSharedLengthAndData(
+ JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data);
+
+/**
+ * Return a pointer to the start of the array buffer's data, and indicate
+ * whether the data is from a shared array buffer through an outparam.
+ *
+ * The returned data pointer may be invalidated by a GC or by an unshared array
+ * buffer becoming detached. Callers must take care not to perform any actions
+ * that could trigger a GC or result in an unshared array buffer becoming
+ * detached. If such actions nonetheless must be performed, callers should
+ * perform this call a second time (and sensibly handle results that may be
+ * different from those returned the first time). (Sharedness is an immutable
+ * characteristic of an array buffer or shared array buffer, so that boolean
+ * remains valid across GC or detaching.)
+ *
+ * |obj| must have passed a JS::IsArrayBufferObjectMaybeShared test, or somehow
+ * be known that it would pass such a test: it is an ArrayBuffer or
+ * SharedArrayBuffer or a wrapper of an ArrayBuffer/SharedArrayBuffer, and the
+ * unwrapping will succeed.
+ *
+ * |*isSharedMemory| will be set to true if the typed array maps a
+ * SharedArrayBuffer, otherwise to false.
+ */
+extern JS_PUBLIC_API uint8_t* GetArrayBufferMaybeSharedData(
+ JSObject* obj, bool* isSharedMemory, const AutoRequireNoGC&);
+
+} // namespace JS
+
+#endif /* js_ArrayBufferMaybeShared_h */
diff --git a/js/public/BigInt.h b/js/public/BigInt.h
new file mode 100644
index 0000000000..d098d44f14
--- /dev/null
+++ b/js/public/BigInt.h
@@ -0,0 +1,139 @@
+/* -*- 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/. */
+
+/* BigInt. */
+
+#ifndef js_BigInt_h
+#define js_BigInt_h
+
+#include "mozilla/Range.h" // mozilla::Range
+
+#include <limits> // std::numeric_limits
+#include <stdint.h> // int64_t, uint64_t
+#include <type_traits> // std::enable_if_t, std::{true,false}_type, std::is_{integral,signed,unsigned}_v
+#include <utility> // std::declval
+
+#include "jstypes.h" // JS_PUBLIC_API
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+
+namespace JS {
+
+class JS_PUBLIC_API BigInt;
+
+namespace detail {
+
+extern JS_PUBLIC_API BigInt* BigIntFromInt64(JSContext* cx, int64_t num);
+extern JS_PUBLIC_API BigInt* BigIntFromUint64(JSContext* cx, uint64_t num);
+extern JS_PUBLIC_API BigInt* BigIntFromBool(JSContext* cx, bool b);
+
+template <typename T, typename = void>
+struct NumberToBigIntConverter;
+
+template <typename SignedIntT>
+struct NumberToBigIntConverter<
+ SignedIntT,
+ std::enable_if_t<std::is_integral_v<SignedIntT> &&
+ std::is_signed_v<SignedIntT> &&
+ std::numeric_limits<SignedIntT>::digits <= 64>> {
+ static BigInt* convert(JSContext* cx, SignedIntT num) {
+ return BigIntFromInt64(cx, num);
+ }
+};
+
+template <typename UnsignedIntT>
+struct NumberToBigIntConverter<
+ UnsignedIntT,
+ std::enable_if_t<std::is_integral_v<UnsignedIntT> &&
+ std::is_unsigned_v<UnsignedIntT> &&
+ std::numeric_limits<UnsignedIntT>::digits <= 64>> {
+ static BigInt* convert(JSContext* cx, UnsignedIntT num) {
+ return BigIntFromUint64(cx, num);
+ }
+};
+
+template <>
+struct NumberToBigIntConverter<bool> {
+ static BigInt* convert(JSContext* cx, bool b) {
+ return BigIntFromBool(cx, b);
+ }
+};
+
+} // namespace detail
+
+/**
+ * Create a BigInt from an integer value. All integral types not larger than 64
+ * bits in size are supported.
+ */
+template <typename NumericT>
+extern JS_PUBLIC_API BigInt* NumberToBigInt(JSContext* cx, NumericT val) {
+ return detail::NumberToBigIntConverter<NumericT>::convert(cx, val);
+}
+
+/**
+ * Create a BigInt from a floating-point value. If the number isn't integral
+ * (that is, if it's NaN, an infinity, or contains a fractional component),
+ * this function returns null and throws an exception.
+ *
+ * Passing -0.0 will produce the bigint 0n.
+ */
+extern JS_PUBLIC_API BigInt* NumberToBigInt(JSContext* cx, double num);
+
+/**
+ * Create a BigInt by parsing a string using the ECMAScript StringToBigInt
+ * algorithm (https://tc39.es/ecma262/#sec-stringtobigint). Latin1 and two-byte
+ * character ranges are supported. It may be convenient to use
+ * JS::ConstLatin1Chars or JS::ConstTwoByteChars.
+ *
+ * (StringToBigInt performs parsing similar to that performed by the |Number|
+ * global function when passed a string, but it doesn't allow infinities,
+ * decimal points, or exponential notation, and neither algorithm allows numeric
+ * separators or an 'n' suffix character. This fast-and-loose description is
+ * offered purely as a convenience to the reader: see the specification
+ * algorithm for exact behavior.)
+ *
+ * If parsing fails, this function returns null and throws an exception.
+ */
+extern JS_PUBLIC_API BigInt* StringToBigInt(
+ JSContext* cx, mozilla::Range<const Latin1Char> chars);
+
+extern JS_PUBLIC_API BigInt* StringToBigInt(
+ JSContext* cx, mozilla::Range<const char16_t> chars);
+
+/**
+ * Create a BigInt by parsing a string consisting of an optional sign character
+ * followed by one or more alphanumeric ASCII digits in the provided radix.
+ *
+ * If the radix is not in the range [2, 36], or the string fails to parse, this
+ * function returns null and throws an exception.
+ */
+extern JS_PUBLIC_API BigInt* SimpleStringToBigInt(
+ JSContext* cx, mozilla::Span<const char> chars, unsigned radix);
+
+/**
+ * Convert a JS::Value to a BigInt using the ECMAScript ToBigInt algorithm
+ * (https://tc39.es/ecma262/#sec-tobigint).
+ *
+ * (Note in particular that this will throw if passed a value whose type is
+ * 'number'. To convert a number to a BigInt, use one of the overloads of
+ * JS::NumberToBigInt().)
+ */
+extern JS_PUBLIC_API BigInt* ToBigInt(JSContext* cx, Handle<Value> val);
+
+/**
+ * Convert the given BigInt, modulo 2**64, to a signed 64-bit integer.
+ */
+extern JS_PUBLIC_API int64_t ToBigInt64(BigInt* bi);
+
+/**
+ * Convert the given BigInt, modulo 2**64, to an unsigned 64-bit integer.
+ */
+extern JS_PUBLIC_API uint64_t ToBigUint64(BigInt* bi);
+
+} // namespace JS
+
+#endif /* js_BigInt_h */
diff --git a/js/public/BuildId.h b/js/public/BuildId.h
new file mode 100644
index 0000000000..cad364776b
--- /dev/null
+++ b/js/public/BuildId.h
@@ -0,0 +1,84 @@
+/* -*- 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/. */
+
+/**
+ * Embedding-provided build ID information, used by SpiderMonkey to tag cached
+ * compilation data so that cached data can be reused when possible, or
+ * discarded and regenerated if necessary.
+ */
+
+#ifndef js_BuildId_h
+#define js_BuildId_h
+
+#include "mozilla/Attributes.h" // MOZ_MUST_USE
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Vector.h" // js::Vector
+
+namespace js {
+
+class SystemAllocPolicy;
+
+} // namespace js
+
+namespace JS {
+
+/** Vector of characters used for holding build ids. */
+using BuildIdCharVector = js::Vector<char, 0, js::SystemAllocPolicy>;
+
+/**
+ * Return the buildId (represented as a sequence of characters) associated with
+ * the currently-executing build. If the JS engine is embedded such that a
+ * single cache entry can be observed by different compiled versions of the JS
+ * engine, it is critical that the buildId shall change for each new build of
+ * the JS engine.
+ */
+using BuildIdOp = bool (*)(BuildIdCharVector* buildId);
+
+/**
+ * Embedder hook to set the buildId-generating function.
+ */
+extern JS_PUBLIC_API void SetProcessBuildIdOp(BuildIdOp buildIdOp);
+
+/**
+ * Some cached data is, in addition to being build-specific, CPU-specific: the
+ * cached data depends on CPU features like a particular level of SSE support.
+ *
+ * This function produces a buildId that includes:
+ *
+ * * the buildId defined by the embedder-provided BuildIdOp set by
+ * JS::SetProcessBuildIdOp, and
+ * * CPU feature information for the current CPU.
+ *
+ * Embedders may use this function to tag cached data whose validity depends
+ * on having consistent buildId *and* on the CPU supporting features identical
+ * to those in play when the cached data was computed.
+ */
+extern MOZ_MUST_USE JS_PUBLIC_API bool GetOptimizedEncodingBuildId(
+ BuildIdCharVector* buildId);
+
+/**
+ * Script bytecode is dependent on the buildId and a few other things.
+ *
+ * This function produces a buildId that includes:
+ *
+ * * The buildId defined by the embedder-provided BuildIdOp set by
+ * JS::SetProcessBuildIdOp.
+ * * Additional bytes describing things like endianness, pointer size and
+ * other state XDR buffers depend on.
+ *
+ * Note: this value may depend on runtime preferences so isn't guaranteed to be
+ * stable across restarts.
+ *
+ * Embedders should use this function to tag transcoded bytecode.
+ * See Transcoding.h.
+ */
+extern MOZ_MUST_USE JS_PUBLIC_API bool GetScriptTranscodingBuildId(
+ BuildIdCharVector* buildId);
+
+} // namespace JS
+
+#endif /* js_BuildId_h */
diff --git a/js/public/CallArgs.h b/js/public/CallArgs.h
new file mode 100644
index 0000000000..8acdc62b60
--- /dev/null
+++ b/js/public/CallArgs.h
@@ -0,0 +1,361 @@
+/* -*- 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/. */
+
+/*
+ * [SMDOC] JS::CallArgs API
+ *
+ * Helper classes encapsulating access to the callee, |this| value, arguments,
+ * and argument count for a call/construct operation.
+ *
+ * JS::CallArgs encapsulates access to a JSNative's un-abstracted
+ * |unsigned argc, Value* vp| arguments. The principal way to create a
+ * JS::CallArgs is using JS::CallArgsFromVp:
+ *
+ * // If provided no arguments or a non-numeric first argument, return zero.
+ * // Otherwise return |this| exactly as given, without boxing.
+ * static bool
+ * Func(JSContext* cx, unsigned argc, JS::Value* vp)
+ * {
+ * JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ *
+ * // Guard against no arguments or a non-numeric arg0.
+ * if (args.length() == 0 || !args[0].isNumber()) {
+ * args.rval().setInt32(0);
+ * return true;
+ * }
+ *
+ * // Access to the callee must occur before accessing/setting
+ * // the return value.
+ * JSObject& callee = args.callee();
+ * args.rval().setObject(callee);
+ *
+ * // callee() and calleev() will now assert.
+ *
+ * // It's always fine to access thisv().
+ * HandleValue thisv = args.thisv();
+ * args.rval().set(thisv);
+ *
+ * // As the return value was last set to |this|, returns |this|.
+ * return true;
+ * }
+ *
+ * CallArgs is exposed publicly and used internally. Not all parts of its
+ * public interface are meant to be used by embedders! See inline comments to
+ * for details.
+ *
+ * It's possible (albeit deprecated) to manually index into |vp| to access the
+ * callee, |this|, and arguments of a function, and to set its return value.
+ * This does not have the error-handling or moving-GC correctness of CallArgs.
+ * New code should use CallArgs instead whenever possible.
+ *
+ * The eventual plan is to change JSNative to take |const CallArgs&| directly,
+ * for automatic assertion of correct use and to make calling functions more
+ * efficient. Embedders should start internally switching away from using
+ * |argc| and |vp| directly, except to create a |CallArgs|. Then, when an
+ * eventual release making that change occurs, porting efforts will require
+ * changing methods' signatures but won't require invasive changes to the
+ * methods' implementations, potentially under time pressure.
+ */
+
+#ifndef js_CallArgs_h
+#define js_CallArgs_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+
+#include <type_traits>
+
+#include "jstypes.h"
+
+#include "js/RootingAPI.h"
+#include "js/Value.h"
+
+/* Typedef for native functions called by the JS VM. */
+using JSNative = bool (*)(JSContext* cx, unsigned argc, JS::Value* vp);
+
+namespace JS {
+
+extern JS_PUBLIC_DATA const HandleValue UndefinedHandleValue;
+
+namespace detail {
+
+/*
+ * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
+ * replacing with the global object as necessary.
+ */
+extern JS_PUBLIC_API bool ComputeThis(JSContext* cx, JS::Value* vp,
+ MutableHandleObject thisObject);
+
+#ifdef JS_DEBUG
+extern JS_PUBLIC_API void CheckIsValidConstructible(const Value& v);
+#endif
+
+class MOZ_STACK_CLASS IncludeUsedRval {
+ mutable bool usedRval_;
+
+ public:
+ bool usedRval() const { return usedRval_; }
+ void setUsedRval() const { usedRval_ = true; }
+ void clearUsedRval() const { usedRval_ = false; }
+ void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); }
+};
+
+class MOZ_STACK_CLASS NoUsedRval {
+ public:
+ bool usedRval() const { return false; }
+ void setUsedRval() const {}
+ void clearUsedRval() const {}
+ void assertUnusedRval() const {}
+};
+
+template <class WantUsedRval>
+class MOZ_STACK_CLASS CallArgsBase {
+ static_assert(std::is_same_v<WantUsedRval, IncludeUsedRval> ||
+ std::is_same_v<WantUsedRval, NoUsedRval>,
+ "WantUsedRval can only be IncludeUsedRval or NoUsedRval");
+
+ protected:
+ Value* argv_;
+ unsigned argc_;
+ bool constructing_ : 1;
+
+ // True if the caller does not use the return value.
+ bool ignoresReturnValue_ : 1;
+
+#ifdef JS_DEBUG
+ WantUsedRval wantUsedRval_;
+ bool usedRval() const { return wantUsedRval_.usedRval(); }
+ void setUsedRval() const { wantUsedRval_.setUsedRval(); }
+ void clearUsedRval() const { wantUsedRval_.clearUsedRval(); }
+ void assertUnusedRval() const { wantUsedRval_.assertUnusedRval(); }
+#else
+ bool usedRval() const { return false; }
+ void setUsedRval() const {}
+ void clearUsedRval() const {}
+ void assertUnusedRval() const {}
+#endif
+
+ public:
+ // CALLEE ACCESS
+
+ /*
+ * Returns the function being called, as a value. Must not be called after
+ * rval() has been used!
+ */
+ HandleValue calleev() const {
+ this->assertUnusedRval();
+ return HandleValue::fromMarkedLocation(&argv_[-2]);
+ }
+
+ /*
+ * Returns the function being called, as an object. Must not be called
+ * after rval() has been used!
+ */
+ JSObject& callee() const { return calleev().toObject(); }
+
+ // CALLING/CONSTRUCTING-DIFFERENTIATIONS
+
+ bool isConstructing() const {
+ if (!argv_[-1].isMagic()) {
+ return false;
+ }
+
+#ifdef JS_DEBUG
+ if (!this->usedRval()) {
+ CheckIsValidConstructible(calleev());
+ }
+#endif
+
+ return true;
+ }
+
+ bool ignoresReturnValue() const { return ignoresReturnValue_; }
+
+ MutableHandleValue newTarget() const {
+ MOZ_ASSERT(constructing_);
+ return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
+ }
+
+ /*
+ * Returns the |this| value passed to the function. This method must not
+ * be called when the function is being called as a constructor via |new|.
+ * The value may or may not be an object: it is the individual function's
+ * responsibility to box the value if needed.
+ */
+ HandleValue thisv() const {
+ // Some internal code uses thisv() in constructing cases, so don't do
+ // this yet.
+ // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
+ return HandleValue::fromMarkedLocation(&argv_[-1]);
+ }
+
+ bool computeThis(JSContext* cx, MutableHandleObject thisObject) const {
+ if (thisv().isObject()) {
+ thisObject.set(&thisv().toObject());
+ return true;
+ }
+
+ return ComputeThis(cx, base(), thisObject);
+ }
+
+ // ARGUMENTS
+
+ /* Returns the number of arguments. */
+ unsigned length() const { return argc_; }
+
+ /* Returns the i-th zero-indexed argument. */
+ MutableHandleValue operator[](unsigned i) const {
+ MOZ_ASSERT(i < argc_);
+ return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
+ }
+
+ /*
+ * Returns the i-th zero-indexed argument, or |undefined| if there's no
+ * such argument.
+ */
+ HandleValue get(unsigned i) const {
+ return i < length() ? HandleValue::fromMarkedLocation(&this->argv_[i])
+ : UndefinedHandleValue;
+ }
+
+ /*
+ * Returns true if the i-th zero-indexed argument is present and is not
+ * |undefined|.
+ */
+ bool hasDefined(unsigned i) const {
+ return i < argc_ && !this->argv_[i].isUndefined();
+ }
+
+ // RETURN VALUE
+
+ /*
+ * Returns the currently-set return value. The initial contents of this
+ * value are unspecified. Once this method has been called, callee() and
+ * calleev() can no longer be used. (If you're compiling against a debug
+ * build of SpiderMonkey, these methods will assert to aid debugging.)
+ *
+ * If the method you're implementing succeeds by returning true, you *must*
+ * set this. (SpiderMonkey doesn't currently assert this, but it will do
+ * so eventually.) You don't need to use or change this if your method
+ * fails.
+ */
+ MutableHandleValue rval() const {
+ this->setUsedRval();
+ return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
+ }
+
+ /*
+ * Returns true if there are at least |required| arguments passed in. If
+ * false, it reports an error message on the context.
+ */
+ JS_PUBLIC_API inline bool requireAtLeast(JSContext* cx, const char* fnname,
+ unsigned required) const;
+
+ public:
+ // These methods are publicly exposed, but they are *not* to be used when
+ // implementing a JSNative method and encapsulating access to |vp| within
+ // it. You probably don't want to use these!
+
+ void setCallee(const Value& aCalleev) const {
+ this->clearUsedRval();
+ argv_[-2] = aCalleev;
+ }
+
+ void setThis(const Value& aThisv) const { argv_[-1] = aThisv; }
+
+ MutableHandleValue mutableThisv() const {
+ return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
+ }
+
+ public:
+ // These methods are publicly exposed, but we're unsure of the interfaces
+ // (because they're hackish and drop assertions). Avoid using these if you
+ // can.
+
+ Value* array() const { return argv_; }
+ Value* end() const { return argv_ + argc_ + constructing_; }
+
+ public:
+ // These methods are only intended for internal use. Embedders shouldn't
+ // use them!
+
+ Value* base() const { return argv_ - 2; }
+
+ Value* spAfterCall() const {
+ this->setUsedRval();
+ return argv_ - 1;
+ }
+};
+
+} // namespace detail
+
+class MOZ_STACK_CLASS CallArgs
+ : public detail::CallArgsBase<detail::IncludeUsedRval> {
+ private:
+ friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
+ friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp,
+ bool constructing, bool ignoresReturnValue);
+
+ static CallArgs create(unsigned argc, Value* argv, bool constructing,
+ bool ignoresReturnValue = false) {
+ CallArgs args;
+ args.clearUsedRval();
+ args.argv_ = argv;
+ args.argc_ = argc;
+ args.constructing_ = constructing;
+ args.ignoresReturnValue_ = ignoresReturnValue;
+#ifdef DEBUG
+ AssertValueIsNotGray(args.thisv());
+ AssertValueIsNotGray(args.calleev());
+ for (unsigned i = 0; i < argc; ++i) {
+ AssertValueIsNotGray(argv[i]);
+ }
+#endif
+ return args;
+ }
+
+ public:
+ /*
+ * Helper for requireAtLeast to report the actual exception. Public
+ * so we can call it from CallArgsBase and not need multiple
+ * per-template instantiations of it.
+ */
+ static JS_PUBLIC_API void reportMoreArgsNeeded(JSContext* cx,
+ const char* fnname,
+ unsigned required,
+ unsigned actual);
+};
+
+namespace detail {
+template <class WantUsedRval>
+JS_PUBLIC_API inline bool CallArgsBase<WantUsedRval>::requireAtLeast(
+ JSContext* cx, const char* fnname, unsigned required) const {
+ if (MOZ_LIKELY(required <= length())) {
+ return true;
+ }
+
+ CallArgs::reportMoreArgsNeeded(cx, fnname, required, length());
+ return false;
+}
+} // namespace detail
+
+MOZ_ALWAYS_INLINE CallArgs CallArgsFromVp(unsigned argc, Value* vp) {
+ return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING));
+}
+
+// This method is only intended for internal use in SpiderMonkey. We may
+// eventually move it to an internal header. Embedders should use
+// JS::CallArgsFromVp!
+MOZ_ALWAYS_INLINE CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp,
+ bool constructing = false,
+ bool ignoresReturnValue = false) {
+ return CallArgs::create(stackSlots - constructing, sp - stackSlots,
+ constructing, ignoresReturnValue);
+}
+
+} // namespace JS
+
+#endif /* js_CallArgs_h */
diff --git a/js/public/CallNonGenericMethod.h b/js/public/CallNonGenericMethod.h
new file mode 100644
index 0000000000..80359f7a6f
--- /dev/null
+++ b/js/public/CallNonGenericMethod.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_CallNonGenericMethod_h
+#define js_CallNonGenericMethod_h
+
+#include "jstypes.h"
+
+#include "js/CallArgs.h"
+
+namespace JS {
+
+// Returns true if |v| is considered an acceptable this-value.
+typedef bool (*IsAcceptableThis)(HandleValue v);
+
+// Implements the guts of a method; guaranteed to be provided an acceptable
+// this-value, as determined by a corresponding IsAcceptableThis method.
+typedef bool (*NativeImpl)(JSContext* cx, const CallArgs& args);
+
+namespace detail {
+
+// DON'T CALL THIS DIRECTLY. It's for use only by CallNonGenericMethod!
+extern JS_PUBLIC_API bool CallMethodIfWrapped(JSContext* cx,
+ IsAcceptableThis test,
+ NativeImpl impl,
+ const CallArgs& args);
+
+} // namespace detail
+
+// Methods usually act upon |this| objects only from a single global object and
+// compartment. Sometimes, however, a method must act upon |this| values from
+// multiple global objects or compartments. In such cases the |this| value a
+// method might see will be wrapped, such that various access to the object --
+// to its class, its private data, its reserved slots, and so on -- will not
+// work properly without entering that object's compartment. This method
+// implements a solution to this problem.
+//
+// To implement a method that accepts |this| values from multiple compartments,
+// define two functions. The first function matches the IsAcceptableThis type
+// and indicates whether the provided value is an acceptable |this| for the
+// method; it must be a pure function only of its argument.
+//
+// static const JSClass AnswerClass = { ... };
+//
+// static bool
+// IsAnswerObject(const Value& v)
+// {
+// if (!v.isObject()) {
+// return false;
+// }
+// return JS_GetClass(&v.toObject()) == &AnswerClass;
+// }
+//
+// The second function implements the NativeImpl signature and defines the
+// behavior of the method when it is provided an acceptable |this| value.
+// Aside from some typing niceties -- see the CallArgs interface for details --
+// its interface is the same as that of JSNative.
+//
+// static bool
+// answer_getAnswer_impl(JSContext* cx, JS::CallArgs args)
+// {
+// args.rval().setInt32(42);
+// return true;
+// }
+//
+// The implementation function is guaranteed to be called *only* with a |this|
+// value which is considered acceptable.
+//
+// Now to implement the actual method, write a JSNative that calls the method
+// declared below, passing the appropriate template and runtime arguments.
+//
+// static bool
+// answer_getAnswer(JSContext* cx, unsigned argc, JS::Value* vp)
+// {
+// JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+// return JS::CallNonGenericMethod<IsAnswerObject,
+// answer_getAnswer_impl>(cx, args);
+// }
+//
+// Note that, because they are used as template arguments, the predicate
+// and implementation functions must have external linkage. (This is
+// unfortunate, but GCC wasn't inlining things as one would hope when we
+// passed them as function arguments.)
+//
+// JS::CallNonGenericMethod will test whether |args.thisv()| is acceptable. If
+// it is, it will call the provided implementation function, which will return
+// a value and indicate success. If it is not, it will attempt to unwrap
+// |this| and call the implementation function on the unwrapped |this|. If
+// that succeeds, all well and good. If it doesn't succeed, a TypeError will
+// be thrown.
+//
+// Note: JS::CallNonGenericMethod will only work correctly if it's called in
+// tail position in a JSNative. Do not call it from any other place.
+//
+template <IsAcceptableThis Test, NativeImpl Impl>
+MOZ_ALWAYS_INLINE bool CallNonGenericMethod(JSContext* cx,
+ const CallArgs& args) {
+ HandleValue thisv = args.thisv();
+ if (Test(thisv)) {
+ return Impl(cx, args);
+ }
+
+ return detail::CallMethodIfWrapped(cx, Test, Impl, args);
+}
+
+MOZ_ALWAYS_INLINE bool CallNonGenericMethod(JSContext* cx,
+ IsAcceptableThis Test,
+ NativeImpl Impl,
+ const CallArgs& args) {
+ HandleValue thisv = args.thisv();
+ if (Test(thisv)) {
+ return Impl(cx, args);
+ }
+
+ return detail::CallMethodIfWrapped(cx, Test, Impl, args);
+}
+
+} // namespace JS
+
+#endif /* js_CallNonGenericMethod_h */
diff --git a/js/public/CharacterEncoding.h b/js/public/CharacterEncoding.h
new file mode 100644
index 0000000000..bc1c010460
--- /dev/null
+++ b/js/public/CharacterEncoding.h
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_CharacterEncoding_h
+#define js_CharacterEncoding_h
+
+#include "mozilla/Range.h"
+#include "mozilla/Span.h"
+#include "mozilla/Utf8.h"
+
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+class JSLinearString;
+
+namespace JS {
+
+/*
+ * By default, all C/C++ 1-byte-per-character strings passed into the JSAPI
+ * are treated as ISO/IEC 8859-1, also known as Latin-1. That is, each
+ * byte is treated as a 2-byte character, and there is no way to pass in a
+ * string containing characters beyond U+00FF.
+ */
+class Latin1Chars : public mozilla::Range<Latin1Char> {
+ typedef mozilla::Range<Latin1Char> Base;
+
+ public:
+ using CharT = Latin1Char;
+
+ Latin1Chars() = default;
+ Latin1Chars(char* aBytes, size_t aLength)
+ : Base(reinterpret_cast<Latin1Char*>(aBytes), aLength) {}
+ Latin1Chars(const Latin1Char* aBytes, size_t aLength)
+ : Base(const_cast<Latin1Char*>(aBytes), aLength) {}
+ Latin1Chars(const char* aBytes, size_t aLength)
+ : Base(reinterpret_cast<Latin1Char*>(const_cast<char*>(aBytes)),
+ aLength) {}
+};
+
+/*
+ * Like Latin1Chars, but the chars are const.
+ */
+class ConstLatin1Chars : public mozilla::Range<const Latin1Char> {
+ typedef mozilla::Range<const Latin1Char> Base;
+
+ public:
+ using CharT = Latin1Char;
+
+ ConstLatin1Chars() = default;
+ ConstLatin1Chars(const Latin1Char* aChars, size_t aLength)
+ : Base(aChars, aLength) {}
+};
+
+/*
+ * A Latin1Chars, but with \0 termination for C compatibility.
+ */
+class Latin1CharsZ : public mozilla::RangedPtr<Latin1Char> {
+ typedef mozilla::RangedPtr<Latin1Char> Base;
+
+ public:
+ using CharT = Latin1Char;
+
+ Latin1CharsZ() : Base(nullptr, 0) {} // NOLINT
+
+ Latin1CharsZ(char* aBytes, size_t aLength)
+ : Base(reinterpret_cast<Latin1Char*>(aBytes), aLength) {
+ MOZ_ASSERT(aBytes[aLength] == '\0');
+ }
+
+ Latin1CharsZ(Latin1Char* aBytes, size_t aLength) : Base(aBytes, aLength) {
+ MOZ_ASSERT(aBytes[aLength] == '\0');
+ }
+
+ using Base::operator=;
+
+ char* c_str() { return reinterpret_cast<char*>(get()); }
+};
+
+class UTF8Chars : public mozilla::Range<unsigned char> {
+ typedef mozilla::Range<unsigned char> Base;
+
+ public:
+ using CharT = unsigned char;
+
+ UTF8Chars() = default;
+ UTF8Chars(char* aBytes, size_t aLength)
+ : Base(reinterpret_cast<unsigned char*>(aBytes), aLength) {}
+ UTF8Chars(const char* aBytes, size_t aLength)
+ : Base(reinterpret_cast<unsigned char*>(const_cast<char*>(aBytes)),
+ aLength) {}
+ UTF8Chars(mozilla::Utf8Unit* aUnits, size_t aLength)
+ : UTF8Chars(reinterpret_cast<char*>(aUnits), aLength) {}
+ UTF8Chars(const mozilla::Utf8Unit* aUnits, size_t aLength)
+ : UTF8Chars(reinterpret_cast<const char*>(aUnits), aLength) {}
+};
+
+/*
+ * Similar to UTF8Chars, but contains WTF-8.
+ * https://simonsapin.github.io/wtf-8/
+ */
+class WTF8Chars : public mozilla::Range<unsigned char> {
+ typedef mozilla::Range<unsigned char> Base;
+
+ public:
+ using CharT = unsigned char;
+
+ WTF8Chars() = default;
+ WTF8Chars(char* aBytes, size_t aLength)
+ : Base(reinterpret_cast<unsigned char*>(aBytes), aLength) {}
+ WTF8Chars(const char* aBytes, size_t aLength)
+ : Base(reinterpret_cast<unsigned char*>(const_cast<char*>(aBytes)),
+ aLength) {}
+};
+
+/*
+ * SpiderMonkey also deals directly with UTF-8 encoded text in some places.
+ */
+class UTF8CharsZ : public mozilla::RangedPtr<unsigned char> {
+ typedef mozilla::RangedPtr<unsigned char> Base;
+
+ public:
+ using CharT = unsigned char;
+
+ UTF8CharsZ() : Base(nullptr, 0) {} // NOLINT
+
+ UTF8CharsZ(char* aBytes, size_t aLength)
+ : Base(reinterpret_cast<unsigned char*>(aBytes), aLength) {
+ MOZ_ASSERT(aBytes[aLength] == '\0');
+ }
+
+ UTF8CharsZ(unsigned char* aBytes, size_t aLength) : Base(aBytes, aLength) {
+ MOZ_ASSERT(aBytes[aLength] == '\0');
+ }
+
+ UTF8CharsZ(mozilla::Utf8Unit* aUnits, size_t aLength)
+ : UTF8CharsZ(reinterpret_cast<char*>(aUnits), aLength) {}
+
+ using Base::operator=;
+
+ char* c_str() { return reinterpret_cast<char*>(get()); }
+};
+
+/*
+ * A wrapper for a "const char*" that is encoded using UTF-8.
+ * This class does not manage ownership of the data; that is left
+ * to others. This differs from UTF8CharsZ in that the chars are
+ * const and it disallows assignment.
+ */
+class JS_PUBLIC_API ConstUTF8CharsZ {
+ const char* data_;
+
+ public:
+ using CharT = unsigned char;
+
+ ConstUTF8CharsZ() : data_(nullptr) {}
+
+ ConstUTF8CharsZ(const char* aBytes, size_t aLength) : data_(aBytes) {
+ MOZ_ASSERT(aBytes[aLength] == '\0');
+#ifdef DEBUG
+ validate(aLength);
+#endif
+ }
+
+ const void* get() const { return data_; }
+
+ const char* c_str() const { return data_; }
+
+ explicit operator bool() const { return data_ != nullptr; }
+
+ private:
+#ifdef DEBUG
+ void validate(size_t aLength);
+#endif
+};
+
+/*
+ * SpiderMonkey uses a 2-byte character representation: it is a
+ * 2-byte-at-a-time view of a UTF-16 byte stream. This is similar to UCS-2,
+ * but unlike UCS-2, we do not strip UTF-16 extension bytes. This allows a
+ * sufficiently dedicated JavaScript program to be fully unicode-aware by
+ * manually interpreting UTF-16 extension characters embedded in the JS
+ * string.
+ */
+class TwoByteChars : public mozilla::Range<char16_t> {
+ typedef mozilla::Range<char16_t> Base;
+
+ public:
+ using CharT = char16_t;
+
+ TwoByteChars() = default;
+ TwoByteChars(char16_t* aChars, size_t aLength) : Base(aChars, aLength) {}
+ TwoByteChars(const char16_t* aChars, size_t aLength)
+ : Base(const_cast<char16_t*>(aChars), aLength) {}
+};
+
+/*
+ * A TwoByteChars, but \0 terminated for compatibility with JSFlatString.
+ */
+class TwoByteCharsZ : public mozilla::RangedPtr<char16_t> {
+ typedef mozilla::RangedPtr<char16_t> Base;
+
+ public:
+ using CharT = char16_t;
+
+ TwoByteCharsZ() : Base(nullptr, 0) {} // NOLINT
+
+ TwoByteCharsZ(char16_t* chars, size_t length) : Base(chars, length) {
+ MOZ_ASSERT(chars[length] == '\0');
+ }
+
+ using Base::operator=;
+};
+
+typedef mozilla::RangedPtr<const char16_t> ConstCharPtr;
+
+/*
+ * Like TwoByteChars, but the chars are const.
+ */
+class ConstTwoByteChars : public mozilla::Range<const char16_t> {
+ typedef mozilla::Range<const char16_t> Base;
+
+ public:
+ using CharT = char16_t;
+
+ ConstTwoByteChars() = default;
+ ConstTwoByteChars(const char16_t* aChars, size_t aLength)
+ : Base(aChars, aLength) {}
+};
+
+/*
+ * Convert a 2-byte character sequence to "ISO-Latin-1". This works by
+ * truncating each 2-byte pair in the sequence to a 1-byte pair. If the source
+ * contains any UTF-16 extension characters, then this may give invalid Latin1
+ * output. The returned string is zero terminated. The returned string or the
+ * returned string's |start()| must be freed with JS_free or js_free,
+ * respectively. If allocation fails, an OOM error will be set and the method
+ * will return a nullptr chars (which can be tested for with the ! operator).
+ * This method cannot trigger GC.
+ */
+extern Latin1CharsZ LossyTwoByteCharsToNewLatin1CharsZ(
+ JSContext* cx, const mozilla::Range<const char16_t> tbchars);
+
+inline Latin1CharsZ LossyTwoByteCharsToNewLatin1CharsZ(JSContext* cx,
+ const char16_t* begin,
+ size_t length) {
+ const mozilla::Range<const char16_t> tbchars(begin, length);
+ return JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars);
+}
+
+template <typename CharT>
+extern UTF8CharsZ CharsToNewUTF8CharsZ(JSContext* maybeCx,
+ const mozilla::Range<CharT> chars);
+
+JS_PUBLIC_API uint32_t Utf8ToOneUcs4Char(const uint8_t* utf8Buffer,
+ int utf8Length);
+
+/*
+ * Inflate bytes in UTF-8 encoding to char16_t.
+ * - On error, returns an empty TwoByteCharsZ.
+ * - On success, returns a malloc'd TwoByteCharsZ, and updates |outlen| to hold
+ * its length; the length value excludes the trailing null.
+ */
+extern JS_PUBLIC_API TwoByteCharsZ
+UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen,
+ arena_id_t destArenaId);
+
+/*
+ * Like UTF8CharsToNewTwoByteCharsZ, but for WTF8Chars.
+ */
+extern JS_PUBLIC_API TwoByteCharsZ
+WTF8CharsToNewTwoByteCharsZ(JSContext* cx, const WTF8Chars wtf8, size_t* outlen,
+ arena_id_t destArenaId);
+
+/*
+ * Like UTF8CharsToNewTwoByteCharsZ, but for ConstUTF8CharsZ.
+ */
+extern JS_PUBLIC_API TwoByteCharsZ
+UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8,
+ size_t* outlen, arena_id_t destArenaId);
+
+/*
+ * The same as UTF8CharsToNewTwoByteCharsZ(), except that any malformed UTF-8
+ * characters will be replaced by \uFFFD. No exception will be thrown for
+ * malformed UTF-8 input.
+ */
+extern JS_PUBLIC_API TwoByteCharsZ
+LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8,
+ size_t* outlen, arena_id_t destArenaId);
+
+extern JS_PUBLIC_API TwoByteCharsZ
+LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8,
+ size_t* outlen, arena_id_t destArenaId);
+
+/*
+ * Returns the length of the char buffer required to encode |s| as UTF8.
+ * Does not include the null-terminator.
+ */
+JS_PUBLIC_API size_t GetDeflatedUTF8StringLength(JSLinearString* s);
+
+/*
+ * Encode whole scalar values of |src| into |dst| as UTF-8 until |src| is
+ * exhausted or too little space is available in |dst| to fit the scalar
+ * value. Lone surrogates are converted to REPLACEMENT CHARACTER. Return
+ * the number of bytes of |dst| that were filled.
+ *
+ * Use |JS_EncodeStringToUTF8BufferPartial| if your string isn't already
+ * linear.
+ *
+ * Given |JSString* str = JS_FORGET_STRING_LINEARNESS(src)|,
+ * if |JS::StringHasLatin1Chars(str)|, then |src| is always fully converted
+ * if |dst.Length() >= JS_GetStringLength(str) * 2|. Otherwise |src| is
+ * always fully converted if |dst.Length() >= JS_GetStringLength(str) * 3|.
+ *
+ * The exact space required is always |GetDeflatedUTF8StringLength(str)|.
+ */
+JS_PUBLIC_API size_t DeflateStringToUTF8Buffer(JSLinearString* src,
+ mozilla::Span<char> dst);
+
+/*
+ * The smallest character encoding capable of fully representing a particular
+ * string.
+ */
+enum class SmallestEncoding { ASCII, Latin1, UTF16 };
+
+/*
+ * Returns the smallest encoding possible for the given string: if all
+ * codepoints are <128 then ASCII, otherwise if all codepoints are <256
+ * Latin-1, else UTF16.
+ */
+JS_PUBLIC_API SmallestEncoding FindSmallestEncoding(UTF8Chars utf8);
+
+/*
+ * Return a null-terminated Latin-1 string copied from the input string,
+ * storing its length (excluding null terminator) in |*outlen|. Fail and
+ * report an error if the string contains non-Latin-1 codepoints. Returns
+ * Latin1CharsZ() on failure.
+ */
+extern JS_PUBLIC_API Latin1CharsZ
+UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen,
+ arena_id_t destArenaId);
+
+/*
+ * Return a null-terminated Latin-1 string copied from the input string,
+ * storing its length (excluding null terminator) in |*outlen|. Non-Latin-1
+ * codepoints are replaced by '?'. Returns Latin1CharsZ() on failure.
+ */
+extern JS_PUBLIC_API Latin1CharsZ
+LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8,
+ size_t* outlen, arena_id_t destArenaId);
+
+/*
+ * Returns true if all characters in the given null-terminated string are
+ * ASCII, i.e. < 0x80, false otherwise.
+ */
+extern JS_PUBLIC_API bool StringIsASCII(const char* s);
+
+/*
+ * Returns true if all characters in the given span are ASCII,
+ * i.e. < 0x80, false otherwise.
+ */
+extern JS_PUBLIC_API bool StringIsASCII(mozilla::Span<const char> s);
+
+} // namespace JS
+
+inline void JS_free(JS::Latin1CharsZ& ptr) { js_free((void*)ptr.get()); }
+inline void JS_free(JS::UTF8CharsZ& ptr) { js_free((void*)ptr.get()); }
+
+/**
+ * DEPRECATED
+ *
+ * Allocate memory sufficient to contain the characters of |str| truncated to
+ * Latin-1 and a trailing null terminator, fill the memory with the characters
+ * interpreted in that manner plus the null terminator, and return a pointer to
+ * the memory.
+ *
+ * This function *loses information* when it copies the characters of |str| if
+ * |str| contains code units greater than 0xFF. Additionally, users that
+ * depend on null-termination will misinterpret the copied characters if |str|
+ * contains any nulls. Avoid using this function if possible, because it will
+ * eventually be removed.
+ */
+extern JS_PUBLIC_API JS::UniqueChars JS_EncodeStringToLatin1(JSContext* cx,
+ JSString* str);
+
+/**
+ * DEPRECATED
+ *
+ * Same behavior as JS_EncodeStringToLatin1(), but encode into a UTF-8 string.
+ *
+ * This function *loses information* when it copies the characters of |str| if
+ * |str| contains invalid UTF-16: U+FFFD REPLACEMENT CHARACTER will be copied
+ * instead.
+ *
+ * The returned string is also subject to misinterpretation if |str| contains
+ * any nulls (which are faithfully transcribed into the returned string, but
+ * which will implicitly truncate the string if it's passed to functions that
+ * expect null-terminated strings).
+ *
+ * Avoid using this function if possible, because we'll remove it once we can
+ * devise a better API for the task.
+ */
+extern JS_PUBLIC_API JS::UniqueChars JS_EncodeStringToUTF8(
+ JSContext* cx, JS::Handle<JSString*> str);
+
+/**
+ * DEPRECATED
+ *
+ * Same behavior as JS_EncodeStringToLatin1(), but encode into an ASCII string.
+ *
+ * This function asserts in debug mode that the input string contains only
+ * ASCII characters.
+ *
+ * The returned string is also subject to misinterpretation if |str| contains
+ * any nulls (which are faithfully transcribed into the returned string, but
+ * which will implicitly truncate the string if it's passed to functions that
+ * expect null-terminated strings).
+ *
+ * Avoid using this function if possible, because we'll remove it once we can
+ * devise a better API for the task.
+ */
+extern JS_PUBLIC_API JS::UniqueChars JS_EncodeStringToASCII(JSContext* cx,
+ JSString* str);
+
+#endif /* js_CharacterEncoding_h */
diff --git a/js/public/Class.h b/js/public/Class.h
new file mode 100644
index 0000000000..e0a9f60eec
--- /dev/null
+++ b/js/public/Class.h
@@ -0,0 +1,938 @@
+/* -*- 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/. */
+
+/* JSClass definition and its component types, plus related interfaces. */
+
+#ifndef js_Class_h
+#define js_Class_h
+
+#include "mozilla/Attributes.h"
+
+#include "jstypes.h"
+
+#include "js/CallArgs.h"
+#include "js/HeapAPI.h"
+#include "js/Id.h"
+#include "js/TypeDecls.h"
+
+/*
+ * A JSClass acts as a vtable for JS objects that allows JSAPI clients to
+ * control various aspects of the behavior of an object like property lookup.
+ * It contains some engine-private extensions that allows more control over
+ * object behavior and, e.g., allows custom slow layout.
+ */
+
+struct JSAtomState;
+struct JSFunctionSpec;
+
+namespace js {
+
+class Shape;
+
+// This is equal to JSFunction::class_. Use it in places where you don't want
+// to #include jsfun.h.
+extern JS_FRIEND_DATA const JSClass* const FunctionClassPtr;
+
+} // namespace js
+
+namespace JS {
+
+/**
+ * Per ES6, the [[DefineOwnProperty]] internal method has three different
+ * possible outcomes:
+ *
+ * - It can throw an exception (which we indicate by returning false).
+ *
+ * - It can return true, indicating unvarnished success.
+ *
+ * - It can return false, indicating "strict failure". The property could
+ * not be defined. It's an error, but no exception was thrown.
+ *
+ * It's not just [[DefineOwnProperty]]: all the mutating internal methods have
+ * the same three outcomes. (The other affected internal methods are [[Set]],
+ * [[Delete]], [[SetPrototypeOf]], and [[PreventExtensions]].)
+ *
+ * If you think this design is awful, you're not alone. But as it's the
+ * standard, we must represent these boolean "success" values somehow.
+ * ObjectOpSuccess is the class for this. It's like a bool, but when it's false
+ * it also stores an error code.
+ *
+ * Typical usage:
+ *
+ * ObjectOpResult result;
+ * if (!DefineProperty(cx, obj, id, ..., result)) {
+ * return false;
+ * }
+ * if (!result) {
+ * return result.reportError(cx, obj, id);
+ * }
+ *
+ * Users don't have to call `result.report()`; another possible ending is:
+ *
+ * argv.rval().setBoolean(result.ok());
+ * return true;
+ */
+class ObjectOpResult {
+ private:
+ /**
+ * code_ is either one of the special codes OkCode or Uninitialized, or an
+ * error code. For now the error codes are JS friend API and are defined in
+ * js/public/friend/ErrorNumbers.msg.
+ *
+ * code_ is uintptr_t (rather than uint32_t) for the convenience of the
+ * JITs, which would otherwise have to deal with either padding or stack
+ * alignment on 64-bit platforms.
+ */
+ uintptr_t code_;
+
+ public:
+ enum SpecialCodes : uintptr_t { OkCode = 0, Uninitialized = uintptr_t(-1) };
+
+ ObjectOpResult() : code_(Uninitialized) {}
+
+ /* Return true if succeed() was called. */
+ bool ok() const {
+ MOZ_ASSERT(code_ != Uninitialized);
+ return code_ == OkCode;
+ }
+
+ explicit operator bool() const { return ok(); }
+
+ /* Set this ObjectOpResult to true and return true. */
+ bool succeed() {
+ code_ = OkCode;
+ return true;
+ }
+
+ /*
+ * Set this ObjectOpResult to false with an error code.
+ *
+ * Always returns true, as a convenience. Typical usage will be:
+ *
+ * if (funny condition) {
+ * return result.fail(JSMSG_CANT_DO_THE_THINGS);
+ * }
+ *
+ * The true return value indicates that no exception is pending, and it
+ * would be OK to ignore the failure and continue.
+ */
+ bool fail(uint32_t msg) {
+ MOZ_ASSERT(msg != OkCode);
+ code_ = msg;
+ return true;
+ }
+
+ JS_PUBLIC_API bool failCantRedefineProp();
+ JS_PUBLIC_API bool failReadOnly();
+ JS_PUBLIC_API bool failGetterOnly();
+ JS_PUBLIC_API bool failCantDelete();
+
+ JS_PUBLIC_API bool failCantSetInterposed();
+ JS_PUBLIC_API bool failCantDefineWindowElement();
+ JS_PUBLIC_API bool failCantDeleteWindowElement();
+ JS_PUBLIC_API bool failCantDeleteWindowNamedProperty();
+ JS_PUBLIC_API bool failCantPreventExtensions();
+ JS_PUBLIC_API bool failCantSetProto();
+ JS_PUBLIC_API bool failNoNamedSetter();
+ JS_PUBLIC_API bool failNoIndexedSetter();
+ JS_PUBLIC_API bool failNotDataDescriptor();
+
+ // Careful: This case has special handling in Object.defineProperty.
+ JS_PUBLIC_API bool failCantDefineWindowNonConfigurable();
+
+ uint32_t failureCode() const {
+ MOZ_ASSERT(!ok());
+ return uint32_t(code_);
+ }
+
+ /*
+ * Report an error if necessary; return true to proceed and
+ * false if an error was reported.
+ *
+ * The precise rules are like this:
+ *
+ * - If ok(), then we succeeded. Do nothing and return true.
+ * - Otherwise, if |strict| is true, throw a TypeError and return false.
+ * - Otherwise, do nothing and return true.
+ */
+ bool checkStrictModeError(JSContext* cx, HandleObject obj, HandleId id,
+ bool strict) {
+ if (ok() || !strict) {
+ return true;
+ }
+ return reportError(cx, obj, id);
+ }
+
+ /*
+ * The same as checkStrictModeError(cx, id, strict), except the
+ * operation is not associated with a particular property id. This is
+ * used for [[PreventExtensions]] and [[SetPrototypeOf]]. failureCode()
+ * must not be an error that has "{0}" in the error message.
+ */
+ bool checkStrictModeError(JSContext* cx, HandleObject obj, bool strict) {
+ if (ok() || !strict) {
+ return true;
+ }
+ return reportError(cx, obj);
+ }
+
+ /* Throw a TypeError. Call this only if !ok(). */
+ bool reportError(JSContext* cx, HandleObject obj, HandleId id);
+
+ /*
+ * The same as reportError(cx, obj, id), except the operation is not
+ * associated with a particular property id.
+ */
+ bool reportError(JSContext* cx, HandleObject obj);
+
+ // Convenience method. Return true if ok(); otherwise throw a TypeError
+ // and return false.
+ bool checkStrict(JSContext* cx, HandleObject obj, HandleId id) {
+ return checkStrictModeError(cx, obj, id, true);
+ }
+
+ // Convenience method. The same as checkStrict(cx, obj, id), except the
+ // operation is not associated with a particular property id.
+ bool checkStrict(JSContext* cx, HandleObject obj) {
+ return checkStrictModeError(cx, obj, true);
+ }
+};
+
+class PropertyResult {
+ enum class Kind : uint8_t {
+ NotFound,
+ NativeProperty,
+ NonNativeProperty,
+ DenseElement,
+ TypedArrayElement,
+ };
+ union {
+ // Set if kind is NativeProperty.
+ js::Shape* shape_;
+ // Set if kind is DenseElement.
+ uint32_t denseIndex_;
+ // Set if kind is TypedArrayElement.
+ size_t typedArrayIndex_;
+ };
+ Kind kind_ = Kind::NotFound;
+
+ public:
+ PropertyResult() = default;
+
+ explicit operator bool() const { return isFound(); }
+
+ bool isFound() const { return kind_ != Kind::NotFound; }
+ bool isNonNativeProperty() const { return kind_ == Kind::NonNativeProperty; }
+ bool isDenseElement() const { return kind_ == Kind::DenseElement; }
+ bool isTypedArrayElement() const { return kind_ == Kind::TypedArrayElement; }
+ bool isNativeProperty() const { return kind_ == Kind::NativeProperty; }
+
+ js::Shape* shape() const {
+ MOZ_ASSERT(isNativeProperty());
+ return shape_;
+ }
+
+ uint32_t denseElementIndex() const {
+ MOZ_ASSERT(isDenseElement());
+ return denseIndex_;
+ }
+
+ size_t typedArrayElementIndex() const {
+ MOZ_ASSERT(isTypedArrayElement());
+ return typedArrayIndex_;
+ }
+
+ void setNotFound() { kind_ = Kind::NotFound; }
+
+ void setNativeProperty(js::Shape* propertyShape) {
+ kind_ = Kind::NativeProperty;
+ shape_ = propertyShape;
+ }
+
+ void setNonNativeProperty() { kind_ = Kind::NonNativeProperty; }
+
+ void setDenseElement(uint32_t index) {
+ kind_ = Kind::DenseElement;
+ denseIndex_ = index;
+ }
+
+ void setTypedArrayElement(size_t index) {
+ kind_ = Kind::TypedArrayElement;
+ typedArrayIndex_ = index;
+ }
+
+ void trace(JSTracer* trc);
+};
+
+} // namespace JS
+
+namespace js {
+
+template <class Wrapper>
+class WrappedPtrOperations<JS::PropertyResult, Wrapper> {
+ const JS::PropertyResult& value() const {
+ return static_cast<const Wrapper*>(this)->get();
+ }
+
+ public:
+ bool isFound() const { return value().isFound(); }
+ explicit operator bool() const { return bool(value()); }
+ js::Shape* shape() const { return value().shape(); }
+ uint32_t denseElementIndex() const { return value().denseElementIndex(); }
+ size_t typedArrayElementIndex() const {
+ return value().typedArrayElementIndex();
+ }
+ bool isNativeProperty() const { return value().isNativeProperty(); }
+ bool isNonNativeProperty() const { return value().isNonNativeProperty(); }
+ bool isDenseElement() const { return value().isDenseElement(); }
+ bool isTypedArrayElement() const { return value().isTypedArrayElement(); }
+};
+
+template <class Wrapper>
+class MutableWrappedPtrOperations<JS::PropertyResult, Wrapper>
+ : public WrappedPtrOperations<JS::PropertyResult, Wrapper> {
+ JS::PropertyResult& value() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+ void setNotFound() { value().setNotFound(); }
+ void setNativeProperty(js::Shape* shape) { value().setNativeProperty(shape); }
+ void setNonNativeProperty() { value().setNonNativeProperty(); }
+ void setDenseElement(uint32_t index) { value().setDenseElement(index); }
+ void setTypedArrayElement(size_t index) {
+ value().setTypedArrayElement(index);
+ }
+};
+
+} // namespace js
+
+// JSClass operation signatures.
+
+/**
+ * Get a property named by id in obj. Note the jsid id type -- id may
+ * be a string (Unicode property identifier) or an int (element index). The
+ * *vp out parameter, on success, is the new property value after the action.
+ */
+typedef bool (*JSGetterOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+ JS::MutableHandleValue vp);
+
+/** Add a property named by id to obj. */
+typedef bool (*JSAddPropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, JS::HandleValue v);
+
+/**
+ * Set a property named by id in obj, treating the assignment as strict
+ * mode code if strict is true. Note the jsid id type -- id may be a string
+ * (Unicode property identifier) or an int (element index).
+ */
+typedef bool (*JSSetterOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+ JS::HandleValue v, JS::ObjectOpResult& result);
+
+/**
+ * Delete a property named by id in obj.
+ *
+ * If an error occurred, return false as per normal JSAPI error practice.
+ *
+ * If no error occurred, but the deletion attempt wasn't allowed (perhaps
+ * because the property was non-configurable), call result.fail() and
+ * return true. This will cause |delete obj[id]| to evaluate to false in
+ * non-strict mode code, and to throw a TypeError in strict mode code.
+ *
+ * If no error occurred and the deletion wasn't disallowed (this is *not* the
+ * same as saying that a deletion actually occurred -- deleting a non-existent
+ * property, or an inherited property, is allowed -- it's just pointless),
+ * call result.succeed() and return true.
+ */
+typedef bool (*JSDeletePropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, JS::ObjectOpResult& result);
+
+/**
+ * The type of ObjectOps::enumerate. This callback overrides a portion of
+ * SpiderMonkey's default [[Enumerate]] internal method. When an ordinary object
+ * is enumerated, that object and each object on its prototype chain is tested
+ * for an enumerate op, and those ops are called in order. The properties each
+ * op adds to the 'properties' vector are added to the set of values the for-in
+ * loop will iterate over. All of this is nonstandard.
+ *
+ * An object is "enumerated" when it's the target of a for-in loop or
+ * JS_Enumerate(). The callback's job is to populate 'properties' with the
+ * object's property keys. If `enumerableOnly` is true, the callback should only
+ * add enumerable properties.
+ */
+typedef bool (*JSNewEnumerateOp)(JSContext* cx, JS::HandleObject obj,
+ JS::MutableHandleIdVector properties,
+ bool enumerableOnly);
+
+/**
+ * The old-style JSClass.enumerate op should define all lazy properties not
+ * yet reflected in obj.
+ */
+typedef bool (*JSEnumerateOp)(JSContext* cx, JS::HandleObject obj);
+
+/**
+ * The type of ObjectOps::funToString. This callback allows an object to
+ * provide a custom string to use when Function.prototype.toString is invoked on
+ * that object. A null return value means OOM.
+ */
+typedef JSString* (*JSFunToStringOp)(JSContext* cx, JS::HandleObject obj,
+ bool isToSource);
+
+/**
+ * Resolve a lazy property named by id in obj by defining it directly in obj.
+ * Lazy properties are those reflected from some peer native property space
+ * (e.g., the DOM attributes for a given node reflected as obj) on demand.
+ *
+ * JS looks for a property in an object, and if not found, tries to resolve
+ * the given id. *resolvedp should be set to true iff the property was defined
+ * on |obj|.
+ */
+typedef bool (*JSResolveOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, bool* resolvedp);
+
+/**
+ * A class with a resolve hook can optionally have a mayResolve hook. This hook
+ * must have no side effects and must return true for a given id if the resolve
+ * hook may resolve this id. This is useful when we're doing a "pure" lookup: if
+ * mayResolve returns false, we know we don't have to call the effectful resolve
+ * hook.
+ *
+ * maybeObj, if non-null, is the object on which we're doing the lookup. This
+ * can be nullptr: during JIT compilation we sometimes know the Class but not
+ * the object.
+ */
+typedef bool (*JSMayResolveOp)(const JSAtomState& names, jsid id,
+ JSObject* maybeObj);
+
+/**
+ * Finalize obj, which the garbage collector has determined to be unreachable
+ * from other live objects or from GC roots. Obviously, finalizers must never
+ * store a reference to obj.
+ */
+typedef void (*JSFinalizeOp)(JSFreeOp* fop, JSObject* obj);
+
+/**
+ * Check whether v is an instance of obj. Return false on error or exception,
+ * true on success with true in *bp if v is an instance of obj, false in
+ * *bp otherwise.
+ */
+typedef bool (*JSHasInstanceOp)(JSContext* cx, JS::HandleObject obj,
+ JS::MutableHandleValue vp, bool* bp);
+
+/**
+ * Function type for trace operation of the class called to enumerate all
+ * traceable things reachable from obj's private data structure. For each such
+ * thing, a trace implementation must call JS::TraceEdge on the thing's
+ * location.
+ *
+ * JSTraceOp implementation can assume that no other threads mutates object
+ * state. It must not change state of the object or corresponding native
+ * structures. The only exception for this rule is the case when the embedding
+ * needs a tight integration with GC. In that case the embedding can check if
+ * the traversal is a part of the marking phase through calling
+ * JS_IsGCMarkingTracer and apply a special code like emptying caches or
+ * marking its native structures.
+ */
+typedef void (*JSTraceOp)(JSTracer* trc, JSObject* obj);
+
+typedef size_t (*JSObjectMovedOp)(JSObject* obj, JSObject* old);
+
+namespace js {
+
+/* Internal / friend API operation signatures. */
+
+typedef bool (*LookupPropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, JS::MutableHandleObject objp,
+ JS::MutableHandle<JS::PropertyResult> propp);
+typedef bool (*DefinePropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result);
+typedef bool (*HasPropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, bool* foundp);
+typedef bool (*GetPropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleValue receiver, JS::HandleId id,
+ JS::MutableHandleValue vp);
+typedef bool (*SetPropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, JS::HandleValue v,
+ JS::HandleValue receiver,
+ JS::ObjectOpResult& result);
+typedef bool (*GetOwnPropertyOp)(
+ JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc);
+typedef bool (*DeletePropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, JS::ObjectOpResult& result);
+
+class JS_FRIEND_API ElementAdder {
+ public:
+ enum GetBehavior {
+ // Check if the element exists before performing the Get and preserve
+ // holes.
+ CheckHasElemPreserveHoles,
+
+ // Perform a Get operation, like obj[index] in JS.
+ GetElement
+ };
+
+ private:
+ // Only one of these is used.
+ JS::RootedObject resObj_;
+ JS::Value* vp_;
+
+ uint32_t index_;
+#ifdef DEBUG
+ uint32_t length_;
+#endif
+ GetBehavior getBehavior_;
+
+ public:
+ ElementAdder(JSContext* cx, JSObject* obj, uint32_t length,
+ GetBehavior behavior)
+ : resObj_(cx, obj),
+ vp_(nullptr),
+ index_(0),
+#ifdef DEBUG
+ length_(length),
+#endif
+ getBehavior_(behavior) {
+ }
+ ElementAdder(JSContext* cx, JS::Value* vp, uint32_t length,
+ GetBehavior behavior)
+ : resObj_(cx),
+ vp_(vp),
+ index_(0),
+#ifdef DEBUG
+ length_(length),
+#endif
+ getBehavior_(behavior) {
+ }
+
+ GetBehavior getBehavior() const { return getBehavior_; }
+
+ bool append(JSContext* cx, JS::HandleValue v);
+ void appendHole();
+};
+
+typedef bool (*GetElementsOp)(JSContext* cx, JS::HandleObject obj,
+ uint32_t begin, uint32_t end,
+ ElementAdder* adder);
+
+/** Callback for the creation of constructor and prototype objects. */
+typedef JSObject* (*ClassObjectCreationOp)(JSContext* cx, JSProtoKey key);
+
+/**
+ * Callback for custom post-processing after class initialization via
+ * ClassSpec.
+ */
+typedef bool (*FinishClassInitOp)(JSContext* cx, JS::HandleObject ctor,
+ JS::HandleObject proto);
+
+const size_t JSCLASS_CACHED_PROTO_WIDTH = 7;
+
+struct MOZ_STATIC_CLASS ClassSpec {
+ ClassObjectCreationOp createConstructor;
+ ClassObjectCreationOp createPrototype;
+ const JSFunctionSpec* constructorFunctions;
+ const JSPropertySpec* constructorProperties;
+ const JSFunctionSpec* prototypeFunctions;
+ const JSPropertySpec* prototypeProperties;
+ FinishClassInitOp finishInit;
+ uintptr_t flags;
+
+ static const size_t ProtoKeyWidth = JSCLASS_CACHED_PROTO_WIDTH;
+
+ static const uintptr_t ProtoKeyMask = (1 << ProtoKeyWidth) - 1;
+ static const uintptr_t DontDefineConstructor = 1 << ProtoKeyWidth;
+
+ bool defined() const { return !!createConstructor; }
+
+ // The ProtoKey this class inherits from.
+ JSProtoKey inheritanceProtoKey() const {
+ MOZ_ASSERT(defined());
+ static_assert(JSProto_Null == 0, "zeroed key must be null");
+
+ // Default: Inherit from Object.
+ if (!(flags & ProtoKeyMask)) {
+ return JSProto_Object;
+ }
+
+ return JSProtoKey(flags & ProtoKeyMask);
+ }
+
+ bool shouldDefineConstructor() const {
+ MOZ_ASSERT(defined());
+ return !(flags & DontDefineConstructor);
+ }
+};
+
+struct MOZ_STATIC_CLASS ClassExtension {
+ /**
+ * Optional hook called when an object is moved by generational or
+ * compacting GC.
+ *
+ * There may exist weak pointers to an object that are not traced through
+ * when the normal trace APIs are used, for example objects in the wrapper
+ * cache. This hook allows these pointers to be updated.
+ *
+ * Note that this hook can be called before JS_NewObject() returns if a GC
+ * is triggered during construction of the object. This can happen for
+ * global objects for example.
+ *
+ * The function should return the difference between nursery bytes used and
+ * tenured bytes used, which may be nonzero e.g. if some nursery-allocated
+ * data beyond the actual GC thing is moved into malloced memory.
+ *
+ * This is used to compute the nursery promotion rate.
+ */
+ JSObjectMovedOp objectMovedOp;
+};
+
+struct MOZ_STATIC_CLASS ObjectOps {
+ LookupPropertyOp lookupProperty;
+ DefinePropertyOp defineProperty;
+ HasPropertyOp hasProperty;
+ GetPropertyOp getProperty;
+ SetPropertyOp setProperty;
+ GetOwnPropertyOp getOwnPropertyDescriptor;
+ DeletePropertyOp deleteProperty;
+ GetElementsOp getElements;
+ JSFunToStringOp funToString;
+};
+
+} // namespace js
+
+static constexpr const js::ClassSpec* JS_NULL_CLASS_SPEC = nullptr;
+static constexpr const js::ClassExtension* JS_NULL_CLASS_EXT = nullptr;
+
+static constexpr const js::ObjectOps* JS_NULL_OBJECT_OPS = nullptr;
+
+// Classes, objects, and properties.
+
+// Objects have private slot.
+static const uint32_t JSCLASS_HAS_PRIVATE = 1 << 0;
+
+// Class's initialization code will call `SetNewObjectMetadata` itself.
+static const uint32_t JSCLASS_DELAY_METADATA_BUILDER = 1 << 1;
+
+// Class is an XPCWrappedNative. WeakMaps use this to override the wrapper
+// disposal mechanism.
+static const uint32_t JSCLASS_IS_WRAPPED_NATIVE = 1 << 2;
+
+// Private is `nsISupports*`.
+static const uint32_t JSCLASS_PRIVATE_IS_NSISUPPORTS = 1 << 3;
+
+// Objects are DOM.
+static const uint32_t JSCLASS_IS_DOMJSCLASS = 1 << 4;
+
+// If wrapped by an xray wrapper, the builtin class's constructor won't be
+// unwrapped and invoked. Instead, the constructor is resolved in the caller's
+// compartment and invoked with a wrapped newTarget. The constructor has to
+// detect and handle this situation. See PromiseConstructor for details.
+static const uint32_t JSCLASS_HAS_XRAYED_CONSTRUCTOR = 1 << 5;
+
+// Objects of this class act like the value undefined, in some contexts.
+static const uint32_t JSCLASS_EMULATES_UNDEFINED = 1 << 6;
+
+// Reserved for embeddings.
+static const uint32_t JSCLASS_USERBIT1 = 1 << 7;
+
+// To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or
+// JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where n
+// is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1.
+
+// Room for 8 flags below ...
+static const uintptr_t JSCLASS_RESERVED_SLOTS_SHIFT = 8;
+// ... and 16 above this field.
+static const uint32_t JSCLASS_RESERVED_SLOTS_WIDTH = 8;
+
+static const uint32_t JSCLASS_RESERVED_SLOTS_MASK =
+ js::BitMask(JSCLASS_RESERVED_SLOTS_WIDTH);
+
+static constexpr uint32_t JSCLASS_HAS_RESERVED_SLOTS(uint32_t n) {
+ return (n & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT;
+}
+
+static constexpr uint32_t JSCLASS_HIGH_FLAGS_SHIFT =
+ JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH;
+
+static const uint32_t JSCLASS_INTERNAL_FLAG1 =
+ 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 0);
+static const uint32_t JSCLASS_IS_GLOBAL = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 1);
+static const uint32_t JSCLASS_INTERNAL_FLAG2 =
+ 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 2);
+static const uint32_t JSCLASS_IS_PROXY = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 3);
+static const uint32_t JSCLASS_SKIP_NURSERY_FINALIZE =
+ 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 4);
+
+// Reserved for embeddings.
+static const uint32_t JSCLASS_USERBIT2 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 5);
+static const uint32_t JSCLASS_USERBIT3 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 6);
+
+static const uint32_t JSCLASS_BACKGROUND_FINALIZE =
+ 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 7);
+static const uint32_t JSCLASS_FOREGROUND_FINALIZE =
+ 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 8);
+
+// Bits 25 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see
+// below.
+
+// ECMA-262 requires that most constructors used internally create objects
+// with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
+// member initial value. The "original ... value" verbiage is there because
+// in ECMA-262, global properties naming class objects are read/write and
+// deleteable, for the most part.
+//
+// Implementing this efficiently requires that global objects have classes
+// with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
+// previously allowed, but is now an ES5 violation and thus unsupported.
+//
+// JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
+// the beginning of every global object's slots for use by the
+// application.
+static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
+static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
+ JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 28;
+
+static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) {
+ return JSCLASS_IS_GLOBAL |
+ JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + n);
+}
+
+static constexpr uint32_t JSCLASS_GLOBAL_FLAGS =
+ JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0);
+
+// Fast access to the original value of each standard class's prototype.
+static const uint32_t JSCLASS_CACHED_PROTO_SHIFT = JSCLASS_HIGH_FLAGS_SHIFT + 9;
+static const uint32_t JSCLASS_CACHED_PROTO_MASK =
+ js::BitMask(js::JSCLASS_CACHED_PROTO_WIDTH);
+
+static_assert(JSProto_LIMIT <= (JSCLASS_CACHED_PROTO_MASK + 1),
+ "JSProtoKey must not exceed the maximum cacheable proto-mask");
+
+static constexpr uint32_t JSCLASS_HAS_CACHED_PROTO(JSProtoKey key) {
+ return uint32_t(key) << JSCLASS_CACHED_PROTO_SHIFT;
+}
+
+struct MOZ_STATIC_CLASS JSClassOps {
+ /* Function pointer members (may be null). */
+ JSAddPropertyOp addProperty;
+ JSDeletePropertyOp delProperty;
+ JSEnumerateOp enumerate;
+ JSNewEnumerateOp newEnumerate;
+ JSResolveOp resolve;
+ JSMayResolveOp mayResolve;
+ JSFinalizeOp finalize;
+ JSNative call;
+ JSHasInstanceOp hasInstance;
+ JSNative construct;
+ JSTraceOp trace;
+};
+
+static constexpr const JSClassOps* JS_NULL_CLASS_OPS = nullptr;
+
+struct alignas(js::gc::JSClassAlignBytes) JSClass {
+ const char* name;
+ uint32_t flags;
+ const JSClassOps* cOps;
+
+ const js::ClassSpec* spec;
+ const js::ClassExtension* ext;
+ const js::ObjectOps* oOps;
+
+ // Public accessors:
+
+ JSAddPropertyOp getAddProperty() const {
+ return cOps ? cOps->addProperty : nullptr;
+ }
+ JSDeletePropertyOp getDelProperty() const {
+ return cOps ? cOps->delProperty : nullptr;
+ }
+ JSEnumerateOp getEnumerate() const {
+ return cOps ? cOps->enumerate : nullptr;
+ }
+ JSNewEnumerateOp getNewEnumerate() const {
+ return cOps ? cOps->newEnumerate : nullptr;
+ }
+ JSResolveOp getResolve() const { return cOps ? cOps->resolve : nullptr; }
+ JSMayResolveOp getMayResolve() const {
+ return cOps ? cOps->mayResolve : nullptr;
+ }
+ JSNative getCall() const { return cOps ? cOps->call : nullptr; }
+ JSHasInstanceOp getHasInstance() const {
+ return cOps ? cOps->hasInstance : nullptr;
+ }
+ JSNative getConstruct() const { return cOps ? cOps->construct : nullptr; }
+
+ bool hasFinalize() const { return cOps && cOps->finalize; }
+ bool hasTrace() const { return cOps && cOps->trace; }
+
+ bool isTrace(JSTraceOp trace) const { return cOps && cOps->trace == trace; }
+
+ // The special treatment of |finalize| and |trace| is necessary because if we
+ // assign either of those hooks to a local variable and then call it -- as is
+ // done with the other hooks -- the GC hazard analysis gets confused.
+ void doFinalize(JSFreeOp* fop, JSObject* obj) const {
+ MOZ_ASSERT(cOps && cOps->finalize);
+ cOps->finalize(fop, obj);
+ }
+ void doTrace(JSTracer* trc, JSObject* obj) const {
+ MOZ_ASSERT(cOps && cOps->trace);
+ cOps->trace(trc, obj);
+ }
+
+ /*
+ * Objects of this class aren't native objects. They don't have Shapes that
+ * describe their properties and layout. Classes using this flag must
+ * provide their own property behavior, either by being proxy classes (do
+ * this) or by overriding all the ObjectOps except getElements
+ * (don't do this).
+ */
+ static const uint32_t NON_NATIVE = JSCLASS_INTERNAL_FLAG2;
+
+ bool isNative() const { return !(flags & NON_NATIVE); }
+
+ bool hasPrivate() const { return !!(flags & JSCLASS_HAS_PRIVATE); }
+
+ bool emulatesUndefined() const { return flags & JSCLASS_EMULATES_UNDEFINED; }
+
+ bool isJSFunction() const { return this == js::FunctionClassPtr; }
+
+ bool nonProxyCallable() const {
+ MOZ_ASSERT(!isProxy());
+ return isJSFunction() || getCall();
+ }
+
+ bool isGlobal() const { return flags & JSCLASS_IS_GLOBAL; }
+
+ bool isProxy() const { return flags & JSCLASS_IS_PROXY; }
+
+ bool isDOMClass() const { return flags & JSCLASS_IS_DOMJSCLASS; }
+
+ bool shouldDelayMetadataBuilder() const {
+ return flags & JSCLASS_DELAY_METADATA_BUILDER;
+ }
+
+ bool isWrappedNative() const { return flags & JSCLASS_IS_WRAPPED_NATIVE; }
+
+ static size_t offsetOfFlags() { return offsetof(JSClass, flags); }
+
+ // Internal / friend API accessors:
+
+ bool specDefined() const { return spec ? spec->defined() : false; }
+ JSProtoKey specInheritanceProtoKey() const {
+ return spec ? spec->inheritanceProtoKey() : JSProto_Null;
+ }
+ bool specShouldDefineConstructor() const {
+ return spec ? spec->shouldDefineConstructor() : true;
+ }
+ js::ClassObjectCreationOp specCreateConstructorHook() const {
+ return spec ? spec->createConstructor : nullptr;
+ }
+ js::ClassObjectCreationOp specCreatePrototypeHook() const {
+ return spec ? spec->createPrototype : nullptr;
+ }
+ const JSFunctionSpec* specConstructorFunctions() const {
+ return spec ? spec->constructorFunctions : nullptr;
+ }
+ const JSPropertySpec* specConstructorProperties() const {
+ return spec ? spec->constructorProperties : nullptr;
+ }
+ const JSFunctionSpec* specPrototypeFunctions() const {
+ return spec ? spec->prototypeFunctions : nullptr;
+ }
+ const JSPropertySpec* specPrototypeProperties() const {
+ return spec ? spec->prototypeProperties : nullptr;
+ }
+ js::FinishClassInitOp specFinishInitHook() const {
+ return spec ? spec->finishInit : nullptr;
+ }
+
+ JSObjectMovedOp extObjectMovedOp() const {
+ return ext ? ext->objectMovedOp : nullptr;
+ }
+
+ js::LookupPropertyOp getOpsLookupProperty() const {
+ return oOps ? oOps->lookupProperty : nullptr;
+ }
+ js::DefinePropertyOp getOpsDefineProperty() const {
+ return oOps ? oOps->defineProperty : nullptr;
+ }
+ js::HasPropertyOp getOpsHasProperty() const {
+ return oOps ? oOps->hasProperty : nullptr;
+ }
+ js::GetPropertyOp getOpsGetProperty() const {
+ return oOps ? oOps->getProperty : nullptr;
+ }
+ js::SetPropertyOp getOpsSetProperty() const {
+ return oOps ? oOps->setProperty : nullptr;
+ }
+ js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor() const {
+ return oOps ? oOps->getOwnPropertyDescriptor : nullptr;
+ }
+ js::DeletePropertyOp getOpsDeleteProperty() const {
+ return oOps ? oOps->deleteProperty : nullptr;
+ }
+ js::GetElementsOp getOpsGetElements() const {
+ return oOps ? oOps->getElements : nullptr;
+ }
+ JSFunToStringOp getOpsFunToString() const {
+ return oOps ? oOps->funToString : nullptr;
+ }
+};
+
+static constexpr uint32_t JSCLASS_RESERVED_SLOTS(const JSClass* clasp) {
+ return (clasp->flags >> JSCLASS_RESERVED_SLOTS_SHIFT) &
+ JSCLASS_RESERVED_SLOTS_MASK;
+}
+
+static constexpr bool JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(const JSClass* clasp) {
+ return (clasp->flags & JSCLASS_IS_GLOBAL) &&
+ JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT;
+}
+
+static constexpr JSProtoKey JSCLASS_CACHED_PROTO_KEY(const JSClass* clasp) {
+ return JSProtoKey((clasp->flags >> JSCLASS_CACHED_PROTO_SHIFT) &
+ JSCLASS_CACHED_PROTO_MASK);
+}
+
+namespace js {
+
+/**
+ * Enumeration describing possible values of the [[Class]] internal property
+ * value of objects.
+ */
+enum class ESClass {
+ Object,
+ Array,
+ Number,
+ String,
+ Boolean,
+ RegExp,
+ ArrayBuffer,
+ SharedArrayBuffer,
+ Date,
+ Set,
+ Map,
+ Promise,
+ MapIterator,
+ SetIterator,
+ Arguments,
+ Error,
+ BigInt,
+ Function, // Note: Only JSFunction objects.
+
+ /** None of the above. */
+ Other
+};
+
+/* Fills |vp| with the unboxed value for boxed types, or undefined otherwise. */
+bool Unbox(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp);
+
+#ifdef DEBUG
+JS_FRIEND_API bool HasObjectMovedOp(JSObject* obj);
+#endif
+
+} /* namespace js */
+
+#endif /* js_Class_h */
diff --git a/js/public/ComparisonOperators.h b/js/public/ComparisonOperators.h
new file mode 100644
index 0000000000..c7f03fa4ca
--- /dev/null
+++ b/js/public/ComparisonOperators.h
@@ -0,0 +1,237 @@
+/* -*- 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/. */
+
+/*
+ * Support comparison operations on wrapper types -- e.g. |JS::Rooted<T>|,
+ * |JS::Handle<T>|, and so on -- against raw |T| values, against pointers or
+ * |nullptr| if the wrapper is a pointer wrapper, and against other wrappers
+ * around compatible types.
+ */
+
+#ifndef js_ComparisonOperators_h
+#define js_ComparisonOperators_h
+
+#include <type_traits> // std::false_type, std::true_type, std::enable_if_t, std::is_pointer_v, std::remove_pointer_t
+
+// To define |operator==| and |operator!=| for a wrapper class |W| (which may
+// or may not be a template class) that contains a |T|:
+//
+// * Specialize |JS::detail::DefineComparisonOps| for |W|:
+// - Make it inherit from |std::true_type|.
+// - Include within your specialization a |static get(const W& v)| function
+// that returns the value (which may be an lvalue reference) of the |T| in
+// |W|.
+// * If needed, add |using JS::detail::wrapper_comparison::operator==;| and
+// |using JS::detail::wrapper_comparison::operator!=;| to the namespace
+// directly containing |W| at the end of this header. (If you are not in
+// SpiderMonkey code and have questionably decided to define your own
+// wrapper class, add these to its namespace somewhere in your code.)
+//
+// The first step opts the wrapper class into comparison support and defines a
+// generic means of extracting a comparable |T| out of an instance.
+//
+// The second step ensures that symmetric |operator==| and |operator!=| are
+// exposed for the wrapper, accepting two wrappers or a wrapper and a suitable
+// raw value.
+//
+// Failure to perform *both* steps will likely result in errors like
+// 'invalid operands to binary expression' or 'no match for operator=='
+// when comparing an instance of your wrapper.
+
+namespace JS {
+
+namespace detail {
+
+// By default, comparison ops are not supported for types.
+template <typename T>
+struct DefineComparisonOps : std::false_type {};
+
+// Define functions for the core equality operations, that the actual operators
+// can all invoke.
+
+// Compare two wrapper types. Assumes both wrapper types support comparison
+// operators.
+template <typename W, typename OW>
+inline bool WrapperEqualsWrapper(const W& wrapper, const OW& other) {
+ return JS::detail::DefineComparisonOps<W>::get(wrapper) ==
+ JS::detail::DefineComparisonOps<OW>::get(other);
+}
+
+// Compare a wrapper against a value of its unwrapped element type (or against a
+// value that implicitly converts to that unwrapped element type). Assumes its
+// wrapper argument supports comparison operators.
+template <typename W>
+inline bool WrapperEqualsUnwrapped(const W& wrapper,
+ const typename W::ElementType& value) {
+ return JS::detail::DefineComparisonOps<W>::get(wrapper) == value;
+}
+
+// Compare a wrapper containing a pointer against a pointer to const element
+// type. Assumes its wrapper argument supports comparison operators.
+template <typename W>
+inline bool WrapperEqualsPointer(
+ const W& wrapper,
+ const typename std::remove_pointer_t<typename W::ElementType>* ptr) {
+ return JS::detail::DefineComparisonOps<W>::get(wrapper) == ptr;
+}
+
+// It is idiomatic C++ to define operators on user-defined types in the
+// namespace of their operands' types (not at global scope, which isn't examined
+// if at point of operator use another operator definition shadows the global
+// definition). But our wrappers live in *multiple* namespaces (|namespace js|
+// and |namespace JS| in SpiderMonkey), so we can't literally do that without
+// defining ambiguous overloads.
+//
+// Instead, we define the operators *once* in a namespace containing nothing
+// else at all. Then we |using| the operators into each namespace containing
+// a wrapper type. |using| creates *aliases*, so two |using|s of the same
+// operator contribute only one overload to overload resolution.
+namespace wrapper_comparison {
+
+// Comparisons between potentially-differing wrappers.
+template <typename W, typename OW>
+inline typename std::enable_if_t<JS::detail::DefineComparisonOps<W>::value &&
+ JS::detail::DefineComparisonOps<OW>::value,
+ bool>
+operator==(const W& wrapper, const OW& other) {
+ return JS::detail::WrapperEqualsWrapper(wrapper, other);
+}
+
+template <typename W, typename OW>
+inline typename std::enable_if_t<JS::detail::DefineComparisonOps<W>::value &&
+ JS::detail::DefineComparisonOps<OW>::value,
+ bool>
+operator!=(const W& wrapper, const OW& other) {
+ return !JS::detail::WrapperEqualsWrapper(wrapper, other);
+}
+
+// Comparisons between a wrapper and its unwrapped element type.
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value, bool>
+operator==(const W& wrapper, const typename W::ElementType& value) {
+ return WrapperEqualsUnwrapped(wrapper, value);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value, bool>
+operator!=(const W& wrapper, const typename W::ElementType& value) {
+ return !WrapperEqualsUnwrapped(wrapper, value);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value, bool>
+operator==(const typename W::ElementType& value, const W& wrapper) {
+ return WrapperEqualsUnwrapped(wrapper, value);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value, bool>
+operator!=(const typename W::ElementType& value, const W& wrapper) {
+ return !WrapperEqualsUnwrapped(wrapper, value);
+}
+
+// For wrappers around a pointer type, comparisons between a wrapper object
+// and a const element pointer.
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value &&
+ std::is_pointer_v<typename W::ElementType>,
+ bool>
+operator==(const W& wrapper,
+ const typename std::remove_pointer_t<typename W::ElementType>* ptr) {
+ return WrapperEqualsPointer(wrapper, ptr);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value &&
+ std::is_pointer_v<typename W::ElementType>,
+ bool>
+operator!=(const W& wrapper,
+ const typename std::remove_pointer_t<typename W::ElementType>* ptr) {
+ return !WrapperEqualsPointer(wrapper, ptr);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value &&
+ std::is_pointer_v<typename W::ElementType>,
+ bool>
+operator==(const typename std::remove_pointer_t<typename W::ElementType>* ptr,
+ const W& wrapper) {
+ return WrapperEqualsPointer(wrapper, ptr);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value &&
+ std::is_pointer_v<typename W::ElementType>,
+ bool>
+operator!=(const typename std::remove_pointer_t<typename W::ElementType>* ptr,
+ const W& wrapper) {
+ return !WrapperEqualsPointer(wrapper, ptr);
+}
+
+// For wrappers around a pointer type, comparisons between a wrapper object
+// and |nullptr|.
+//
+// These overloads are a workaround for gcc hazard build bugs. Per spec,
+// |nullptr -> const T*| for the wrapper-pointer operators immediately above
+// this is a standard conversion sequence (consisting of a single pointer
+// conversion). Meanwhile, |nullptr -> T* const&| for the wrapper-element
+// operators just above that, is a pointer conversion to |T*|, then an identity
+// conversion of the |T* const| to a reference. The former conversion sequence
+// is a proper subsequence of the latter, so it *should* be a better conversion
+// sequence and thus should be the better overload. But gcc doesn't implement
+// things this way, so we add overloads directly targeting |nullptr| as an exact
+// match, preferred to either of those overloads.
+//
+// We should be able to remove these overloads when gcc hazard builds use modern
+// clang.
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value, bool>
+operator==(const W& wrapper, std::nullptr_t) {
+ return WrapperEqualsUnwrapped(wrapper, nullptr);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value, bool>
+operator!=(const W& wrapper, std::nullptr_t) {
+ return !WrapperEqualsUnwrapped(wrapper, nullptr);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value, bool>
+operator==(std::nullptr_t, const W& wrapper) {
+ return WrapperEqualsUnwrapped(wrapper, nullptr);
+}
+
+template <typename W>
+inline typename std::enable_if_t<DefineComparisonOps<W>::value, bool>
+operator!=(std::nullptr_t, const W& wrapper) {
+ return !WrapperEqualsUnwrapped(wrapper, nullptr);
+}
+
+} // namespace wrapper_comparison
+
+} // namespace detail
+
+} // namespace JS
+
+// Expose wrapper-supporting |operator==| and |operator!=| in the namespaces of
+// all SpiderMonkey's wrapper classes that support comparisons.
+
+namespace JS {
+
+using JS::detail::wrapper_comparison::operator==;
+using JS::detail::wrapper_comparison::operator!=;
+
+} // namespace JS
+
+namespace js {
+
+using JS::detail::wrapper_comparison::operator==;
+using JS::detail::wrapper_comparison::operator!=;
+
+} // namespace js
+
+#endif // js_ComparisonOperators_h
diff --git a/js/public/CompilationAndEvaluation.h b/js/public/CompilationAndEvaluation.h
new file mode 100644
index 0000000000..e9b7758d68
--- /dev/null
+++ b/js/public/CompilationAndEvaluation.h
@@ -0,0 +1,248 @@
+/* -*- 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/. */
+
+/* Functions for compiling and evaluating scripts. */
+
+#ifndef js_CompilationAndEvaluation_h
+#define js_CompilationAndEvaluation_h
+
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+
+#include <stddef.h> // size_t
+#include <stdio.h> // FILE
+
+#include "jsapi.h" // JSGetElementCallback
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/CompileOptions.h" // JS::CompileOptions, JS::ReadOnlyCompileOptions
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+#include "js/Value.h" // JS::Value and specializations of JS::*Handle-related types
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSFunction;
+class JS_PUBLIC_API JSObject;
+class JS_PUBLIC_API JSScript;
+
+namespace JS {
+
+template <typename UnitT>
+class SourceText;
+
+} // namespace JS
+
+/**
+ * Given a buffer, return false if the buffer might become a valid JavaScript
+ * script with the addition of more lines, or true if the validity of such a
+ * script is conclusively known (because it's the prefix of a valid script --
+ * and possibly the entirety of such a script).
+ *
+ * The intent of this function is to enable interactive compilation: accumulate
+ * lines in a buffer until JS_Utf8BufferIsCompilableUnit is true, then pass it
+ * to the compiler.
+ *
+ * The provided buffer is interpreted as UTF-8 data. An error is reported if
+ * a UTF-8 encoding error is encountered.
+ */
+extern JS_PUBLIC_API bool JS_Utf8BufferIsCompilableUnit(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char* utf8, size_t length);
+
+/*
+ * NB: JS_ExecuteScript and the JS::Evaluate APIs come in two flavors: either
+ * they use the global as the scope, or they take a HandleValueVector of
+ * objects to use as the scope chain. In the former case, the global is also
+ * used as the "this" keyword value and the variables object (ECMA parlance for
+ * where 'var' and 'function' bind names) of the execution context for script.
+ * In the latter case, the first object in the provided list is used, unless the
+ * list is empty, in which case the global is used.
+ *
+ * Why a runtime option? The alternative is to add APIs duplicating those
+ * for the other value of flags, and that doesn't seem worth the code bloat
+ * cost. Such new entry points would probably have less obvious names, too, so
+ * would not tend to be used. The ContextOptionsRef adjustment, OTOH, can be
+ * more easily hacked into existing code that does not depend on the bug; such
+ * code can continue to use the familiar JS::Evaluate, etc., entry points.
+ */
+
+/**
+ * Evaluate a script in the scope of the current global of cx.
+ */
+extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
+ JS::Handle<JSScript*> script,
+ JS::MutableHandle<JS::Value> rval);
+
+extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
+ JS::Handle<JSScript*> script);
+
+/**
+ * As above, but providing an explicit scope chain. envChain must not include
+ * the global object on it; that's implicit. It needs to contain the other
+ * objects that should end up on the script's scope chain.
+ */
+extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
+ JS::HandleObjectVector envChain,
+ JS::Handle<JSScript*> script,
+ JS::MutableHandle<JS::Value> rval);
+
+extern JS_PUBLIC_API bool JS_ExecuteScript(JSContext* cx,
+ JS::HandleObjectVector envChain,
+ JS::Handle<JSScript*> script);
+
+namespace JS {
+
+/**
+ * Like the above, but handles a cross-compartment script. If the script is
+ * cross-compartment, it is cloned into the current compartment before
+ * executing.
+ */
+extern JS_PUBLIC_API bool CloneAndExecuteScript(JSContext* cx,
+ Handle<JSScript*> script,
+ MutableHandle<Value> rval);
+
+/**
+ * Like CloneAndExecuteScript above, but allows executing under a non-syntactic
+ * environment chain.
+ */
+extern JS_PUBLIC_API bool CloneAndExecuteScript(JSContext* cx,
+ HandleObjectVector envChain,
+ Handle<JSScript*> script,
+ MutableHandle<Value> rval);
+
+/**
+ * Evaluate the given source buffer in the scope of the current global of cx,
+ * and return the completion value in |rval|.
+ */
+extern JS_PUBLIC_API bool Evaluate(JSContext* cx,
+ const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf,
+ MutableHandle<Value> rval);
+
+/**
+ * As above, but providing an explicit scope chain. envChain must not include
+ * the global object on it; that's implicit. It needs to contain the other
+ * objects that should end up on the script's scope chain.
+ */
+extern JS_PUBLIC_API bool Evaluate(JSContext* cx, HandleObjectVector envChain,
+ const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf,
+ MutableHandle<Value> rval);
+
+/**
+ * Evaluate the provided UTF-8 data in the scope of the current global of |cx|,
+ * and return the completion value in |rval|. If the data contains invalid
+ * UTF-8, an error is reported.
+ */
+extern JS_PUBLIC_API bool Evaluate(JSContext* cx,
+ const ReadOnlyCompileOptions& options,
+ SourceText<mozilla::Utf8Unit>& srcBuf,
+ MutableHandle<Value> rval);
+
+/**
+ * Evaluate the UTF-8 contents of the file at the given path, and return the
+ * completion value in |rval|. (The path itself is in the system encoding, not
+ * [necessarily] UTF-8.) If the contents contain any malformed UTF-8, an error
+ * is reported.
+ */
+extern JS_PUBLIC_API bool EvaluateUtf8Path(
+ JSContext* cx, const ReadOnlyCompileOptions& options, const char* filename,
+ MutableHandle<Value> rval);
+
+/**
+ * Compile the provided script using the given options. Return the script on
+ * success, or return null on failure (usually with an error reported).
+ */
+extern JS_PUBLIC_API JSScript* Compile(JSContext* cx,
+ const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf);
+
+/**
+ * Compile the provided script using the given options. Return the script on
+ * success, or return null on failure (usually with an error reported).
+ */
+extern JS_PUBLIC_API JSScript* Compile(JSContext* cx,
+ const ReadOnlyCompileOptions& options,
+ SourceText<mozilla::Utf8Unit>& srcBuf);
+
+/**
+ * Compile the provided script using the given options, and register an encoder
+ * on is script source, such that all functions can be encoded as they are
+ * parsed. This strategy is used to avoid blocking the main thread in a
+ * non-interruptible way.
+ *
+ * See also JS::FinishIncrementalEncoding.
+ *
+ * Return the script on success, or return null on failure (usually with an
+ * error reported)
+ */
+extern JS_PUBLIC_API JSScript* CompileAndStartIncrementalEncoding(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf);
+
+extern JS_PUBLIC_API JSScript* CompileAndStartIncrementalEncoding(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<mozilla::Utf8Unit>& srcBuf);
+
+/**
+ * Compile the UTF-8 contents of the given file into a script. It is an error
+ * if the file contains invalid UTF-8. Return the script on success, or return
+ * null on failure (usually with an error reported).
+ */
+extern JS_PUBLIC_API JSScript* CompileUtf8File(
+ JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file);
+
+/**
+ * Compile the UTF-8 contents of the file at the given path into a script.
+ * (The path itself is in the system encoding, not [necessarily] UTF-8.) It
+ * is an error if the file's contents are invalid UTF-8. Return the script on
+ * success, or return null on failure (usually with an error reported).
+ */
+extern JS_PUBLIC_API JSScript* CompileUtf8Path(
+ JSContext* cx, const ReadOnlyCompileOptions& options, const char* filename);
+
+/**
+ * Compile a function with envChain plus the global as its scope chain.
+ * envChain must contain objects in the current compartment of cx. The actual
+ * scope chain used for the function will consist of With wrappers for those
+ * objects, followed by the current global of the compartment cx is in. This
+ * global must not be explicitly included in the scope chain.
+ */
+extern JS_PUBLIC_API JSFunction* CompileFunction(
+ JSContext* cx, HandleObjectVector envChain,
+ const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
+ const char* const* argnames, SourceText<char16_t>& srcBuf);
+
+/**
+ * Compile a function with envChain plus the global as its scope chain.
+ * envChain must contain objects in the current compartment of cx. The actual
+ * scope chain used for the function will consist of With wrappers for those
+ * objects, followed by the current global of the compartment cx is in. This
+ * global must not be explicitly included in the scope chain.
+ */
+extern JS_PUBLIC_API JSFunction* CompileFunction(
+ JSContext* cx, HandleObjectVector envChain,
+ const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
+ const char* const* argnames, SourceText<mozilla::Utf8Unit>& srcBuf);
+
+/**
+ * Identical to the CompileFunction overload above for UTF-8, but with
+ * Rust-friendly ergonomics.
+ */
+extern JS_PUBLIC_API JSFunction* CompileFunctionUtf8(
+ JSContext* cx, HandleObjectVector envChain,
+ const ReadOnlyCompileOptions& options, const char* name, unsigned nargs,
+ const char* const* argnames, const char* utf8, size_t length);
+
+/*
+ * For a script compiled with the hideScriptFromDebugger option, expose the
+ * script to the debugger by calling the debugger's onNewScript hook.
+ */
+extern JS_PUBLIC_API void ExposeScriptToDebugger(JSContext* cx,
+ Handle<JSScript*> script);
+
+extern JS_PUBLIC_API void SetGetElementCallback(JSContext* cx,
+ JSGetElementCallback callback);
+
+} /* namespace JS */
+
+#endif /* js_CompilationAndEvaluation_h */
diff --git a/js/public/CompileOptions.h b/js/public/CompileOptions.h
new file mode 100644
index 0000000000..2eda408b26
--- /dev/null
+++ b/js/public/CompileOptions.h
@@ -0,0 +1,473 @@
+/* -*- 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/. */
+
+/*
+ * Options for JavaScript compilation.
+ *
+ * In the most common use case, a CompileOptions instance is allocated on the
+ * stack, and holds non-owning references to non-POD option values: strings,
+ * principals, objects, and so on. The code declaring the instance guarantees
+ * that such option values will outlive the CompileOptions itself: objects are
+ * otherwise rooted, principals have had their reference counts bumped, and
+ * strings won't be freed until the CompileOptions goes out of scope. In this
+ * situation, CompileOptions only refers to things others own, so it can be
+ * lightweight.
+ *
+ * In some cases, however, we need to hold compilation options with a
+ * non-stack-like lifetime. For example, JS::CompileOffThread needs to save
+ * compilation options where a worker thread can find them, then return
+ * immediately. The worker thread will come along at some later point, and use
+ * the options.
+ *
+ * The compiler itself just needs to be able to access a collection of options;
+ * it doesn't care who owns them, or what's keeping them alive. It does its
+ * own addrefs/copies/tracing/etc.
+ *
+ * Furthermore, in some cases compile options are propagated from one entity to
+ * another (e.g. from a script to a function defined in that script). This
+ * involves copying over some, but not all, of the options.
+ *
+ * So we have a class hierarchy that reflects these four use cases:
+ *
+ * - TransitiveCompileOptions is the common base class, representing options
+ * that should get propagated from a script to functions defined in that
+ * script. This class is abstract and is only ever used as a subclass.
+ *
+ * - ReadOnlyCompileOptions is the only subclass of TransitiveCompileOptions,
+ * representing a full set of compile options. It can be used by code that
+ * simply needs to access options set elsewhere, like the compiler. This
+ * class too is abstract and is only ever used as a subclass.
+ *
+ * - The usual CompileOptions class must be stack-allocated, and holds
+ * non-owning references to the filename, element, and so on. It's derived
+ * from ReadOnlyCompileOptions, so the compiler can use it.
+ *
+ * - OwningCompileOptions roots / copies / reference counts of all its values,
+ * and unroots / frees / releases them when it is destructed. It too is
+ * derived from ReadOnlyCompileOptions, so the compiler accepts it.
+ */
+
+#ifndef js_CompileOptions_h
+#define js_CompileOptions_h
+
+#include "mozilla/Attributes.h" // MOZ_MUST_USE
+#include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint8_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::PersistentRooted, JS::Rooted
+#include "js/Value.h"
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+class JS_PUBLIC_API JSScript;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+
+enum class AsmJSOption : uint8_t {
+ Enabled,
+ Disabled,
+ DisabledByDebugger,
+};
+
+/**
+ * The common base class for the CompileOptions hierarchy.
+ *
+ * Use this in code that needs to propagate compile options from one
+ * compilation unit to another.
+ */
+class JS_PUBLIC_API TransitiveCompileOptions {
+ protected:
+ /**
+ * The Web Platform allows scripts to be loaded from arbitrary cross-origin
+ * sources. This allows an attack by which a malicious website loads a
+ * sensitive file (say, a bank statement) cross-origin (using the user's
+ * cookies), and sniffs the generated syntax errors (via a window.onerror
+ * handler) for juicy morsels of its contents.
+ *
+ * To counter this attack, HTML5 specifies that script errors should be
+ * sanitized ("muted") when the script is not same-origin with the global
+ * for which it is loaded. Callers should set this flag for cross-origin
+ * scripts, and it will be propagated appropriately to child scripts and
+ * passed back in JSErrorReports.
+ */
+ bool mutedErrors_ = false;
+
+ // Either the Realm configuration or specialized VM operating modes may
+ // disallow syntax-parse altogether. These conditions are checked in the
+ // CompileOptions constructor.
+ bool forceFullParse_ = false;
+
+ // Either the Realm configuration or the compile request may force
+ // strict-mode.
+ bool forceStrictMode_ = false;
+
+ // The context has specified that source pragmas should be parsed.
+ bool sourcePragmas_ = true;
+
+ const char* filename_ = nullptr;
+ const char* introducerFilename_ = nullptr;
+ const char16_t* sourceMapURL_ = nullptr;
+
+ // Flag used to bypass the filename validation callback.
+ // See also SetFilenameValidationCallback.
+ bool skipFilenameValidation_ = false;
+
+ public:
+ // POD options.
+ bool selfHostingMode = false;
+ AsmJSOption asmJSOption = AsmJSOption::Disabled;
+ bool throwOnAsmJSValidationFailureOption = false;
+ bool forceAsync = false;
+ bool discardSource = false;
+ bool sourceIsLazy = false;
+ bool allowHTMLComments = true;
+ bool hideScriptFromDebugger = false;
+ bool nonSyntacticScope = false;
+ bool privateClassFields = false;
+ bool privateClassMethods = false;
+ bool topLevelAwait = false;
+
+ // True if transcoding to XDR should use Stencil instead of JSScripts.
+ bool useStencilXDR = false;
+
+ // True if off-thread parsing should use a parse GlobalObject in order to
+ // directly allocate to the GC from a helper thread. If false, transfer the
+ // CompilationStencil back to main thread before allocating GC objects.
+ bool useOffThreadParseGlobal = true;
+
+ /**
+ * |introductionType| is a statically allocated C string: one of "eval",
+ * "Function", or "GeneratorFunction".
+ */
+ const char* introductionType = nullptr;
+
+ unsigned introductionLineno = 0;
+ uint32_t introductionOffset = 0;
+ bool hasIntroductionInfo = false;
+
+ // Mask of operation kinds which should be instrumented.
+ uint32_t instrumentationKinds = 0;
+
+ protected:
+ TransitiveCompileOptions() = default;
+
+ // Set all POD options (those not requiring reference counts, copies,
+ // rooting, or other hand-holding) to their values in |rhs|.
+ void copyPODTransitiveOptions(const TransitiveCompileOptions& rhs);
+
+ public:
+ // Read-only accessors for non-POD options. The proper way to set these
+ // depends on the derived type.
+ bool mutedErrors() const { return mutedErrors_; }
+ bool forceFullParse() const { return forceFullParse_; }
+ bool forceStrictMode() const { return forceStrictMode_; }
+ bool skipFilenameValidation() const { return skipFilenameValidation_; }
+ bool sourcePragmas() const { return sourcePragmas_; }
+ const char* filename() const { return filename_; }
+ const char* introducerFilename() const { return introducerFilename_; }
+ const char16_t* sourceMapURL() const { return sourceMapURL_; }
+ virtual Value privateValue() const = 0;
+ virtual JSString* elementAttributeName() const = 0;
+ virtual JSScript* introductionScript() const = 0;
+
+ // For some compilations the spec requires the ScriptOrModule field of the
+ // resulting script to be set to the currently executing script. This can be
+ // achieved by setting this option with setScriptOrModule() below.
+ //
+ // Note that this field doesn't explicitly exist in our implementation;
+ // instead the ScriptSourceObject's private value is set to that associated
+ // with the specified script.
+ virtual JSScript* scriptOrModule() const = 0;
+
+ TransitiveCompileOptions(const TransitiveCompileOptions&) = delete;
+ TransitiveCompileOptions& operator=(const TransitiveCompileOptions&) = delete;
+};
+
+/**
+ * The class representing a full set of compile options.
+ *
+ * Use this in code that only needs to access compilation options created
+ * elsewhere, like the compiler. Don't instantiate this class (the constructor
+ * is protected anyway); instead, create instances only of the derived classes:
+ * CompileOptions and OwningCompileOptions.
+ */
+class JS_PUBLIC_API ReadOnlyCompileOptions : public TransitiveCompileOptions {
+ public:
+ // POD options.
+ unsigned lineno = 1;
+ unsigned column = 0;
+
+ // The offset within the ScriptSource's full uncompressed text of the first
+ // character we're presenting for compilation with this CompileOptions.
+ //
+ // When we compile a lazy script, we pass the compiler only the substring of
+ // the source the lazy function occupies. With chunked decompression, we may
+ // not even have the complete uncompressed source present in memory. But parse
+ // node positions are offsets within the ScriptSource's full text, and
+ // BaseScript indicate their substring of the full source by its starting and
+ // ending offsets within the full text. This scriptSourceOffset field lets the
+ // frontend convert between these offsets and offsets within the substring
+ // presented for compilation.
+ unsigned scriptSourceOffset = 0;
+
+ // These only apply to non-function scripts.
+ bool isRunOnce = false;
+ bool noScriptRval = false;
+
+ protected:
+ ReadOnlyCompileOptions() = default;
+
+ void copyPODNonTransitiveOptions(const ReadOnlyCompileOptions& rhs);
+
+ ReadOnlyCompileOptions(const ReadOnlyCompileOptions&) = delete;
+ ReadOnlyCompileOptions& operator=(const ReadOnlyCompileOptions&) = delete;
+};
+
+/**
+ * Compilation options, with dynamic lifetime. An instance of this type
+ * makes a copy of / holds / roots all dynamically allocated resources
+ * (principals; elements; strings) that it refers to. Its destructor frees
+ * / drops / unroots them. This is heavier than CompileOptions, below, but
+ * unlike CompileOptions, it can outlive any given stack frame.
+ *
+ * Note that this *roots* any JS values it refers to - they're live
+ * unconditionally. Thus, instances of this type can't be owned, directly
+ * or indirectly, by a JavaScript object: if any value that this roots ever
+ * comes to refer to the object that owns this, then the whole cycle, and
+ * anything else it entrains, will never be freed.
+ */
+class JS_PUBLIC_API OwningCompileOptions final : public ReadOnlyCompileOptions {
+ PersistentRooted<JSString*> elementAttributeNameRoot;
+ PersistentRooted<JSScript*> introductionScriptRoot;
+ PersistentRooted<JSScript*> scriptOrModuleRoot;
+ PersistentRooted<Value> privateValueRoot;
+
+ public:
+ // A minimal constructor, for use with OwningCompileOptions::copy.
+ explicit OwningCompileOptions(JSContext* cx);
+ ~OwningCompileOptions();
+
+ Value privateValue() const override { return privateValueRoot; }
+ JSString* elementAttributeName() const override {
+ return elementAttributeNameRoot;
+ }
+ JSScript* introductionScript() const override {
+ return introductionScriptRoot;
+ }
+ JSScript* scriptOrModule() const override { return scriptOrModuleRoot; }
+
+ /** Set this to a copy of |rhs|. Return false on OOM. */
+ bool copy(JSContext* cx, const ReadOnlyCompileOptions& rhs);
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ OwningCompileOptions& setIsRunOnce(bool once) {
+ isRunOnce = once;
+ return *this;
+ }
+
+ OwningCompileOptions& setForceStrictMode() {
+ forceStrictMode_ = true;
+ return *this;
+ }
+
+ OwningCompileOptions& setModule() {
+ // ES6 10.2.1 Module code is always strict mode code.
+ setForceStrictMode();
+ setIsRunOnce(true);
+ allowHTMLComments = false;
+ return *this;
+ }
+
+ private:
+ void release();
+
+ OwningCompileOptions(const OwningCompileOptions&) = delete;
+ OwningCompileOptions& operator=(const OwningCompileOptions&) = delete;
+};
+
+/**
+ * Compilation options stored on the stack. An instance of this type
+ * simply holds references to dynamically allocated resources (element;
+ * filename; source map URL) that are owned by something else. If you
+ * create an instance of this type, it's up to you to guarantee that
+ * everything you store in it will outlive it.
+ */
+class MOZ_STACK_CLASS JS_PUBLIC_API CompileOptions final
+ : public ReadOnlyCompileOptions {
+ private:
+ Rooted<JSString*> elementAttributeNameRoot;
+ Rooted<JSScript*> introductionScriptRoot;
+ Rooted<JSScript*> scriptOrModuleRoot;
+ Rooted<Value> privateValueRoot;
+
+ public:
+ // Default options determined using the JSContext.
+ explicit CompileOptions(JSContext* cx);
+
+ // Copy both the transitive and the non-transitive options from another
+ // options object.
+ CompileOptions(JSContext* cx, const ReadOnlyCompileOptions& rhs)
+ : ReadOnlyCompileOptions(),
+ elementAttributeNameRoot(cx),
+ introductionScriptRoot(cx),
+ scriptOrModuleRoot(cx),
+ privateValueRoot(cx) {
+ copyPODNonTransitiveOptions(rhs);
+ copyPODTransitiveOptions(rhs);
+
+ filename_ = rhs.filename();
+ introducerFilename_ = rhs.introducerFilename();
+ sourceMapURL_ = rhs.sourceMapURL();
+ privateValueRoot = rhs.privateValue();
+ elementAttributeNameRoot = rhs.elementAttributeName();
+ introductionScriptRoot = rhs.introductionScript();
+ scriptOrModuleRoot = rhs.scriptOrModule();
+ }
+
+ Value privateValue() const override { return privateValueRoot; }
+
+ JSString* elementAttributeName() const override {
+ return elementAttributeNameRoot;
+ }
+
+ JSScript* introductionScript() const override {
+ return introductionScriptRoot;
+ }
+
+ JSScript* scriptOrModule() const override { return scriptOrModuleRoot; }
+
+ CompileOptions& setFile(const char* f) {
+ filename_ = f;
+ return *this;
+ }
+
+ CompileOptions& setLine(unsigned l) {
+ lineno = l;
+ return *this;
+ }
+
+ CompileOptions& setFileAndLine(const char* f, unsigned l) {
+ filename_ = f;
+ lineno = l;
+ return *this;
+ }
+
+ CompileOptions& setSourceMapURL(const char16_t* s) {
+ sourceMapURL_ = s;
+ return *this;
+ }
+
+ CompileOptions& setPrivateValue(const Value& v) {
+ privateValueRoot = v;
+ return *this;
+ }
+
+ CompileOptions& setElementAttributeName(JSString* p) {
+ elementAttributeNameRoot = p;
+ return *this;
+ }
+
+ CompileOptions& setScriptOrModule(JSScript* s) {
+ scriptOrModuleRoot = s;
+ return *this;
+ }
+
+ CompileOptions& setMutedErrors(bool mute) {
+ mutedErrors_ = mute;
+ return *this;
+ }
+
+ CompileOptions& setColumn(unsigned c) {
+ column = c;
+ return *this;
+ }
+
+ CompileOptions& setScriptSourceOffset(unsigned o) {
+ scriptSourceOffset = o;
+ return *this;
+ }
+
+ CompileOptions& setIsRunOnce(bool once) {
+ isRunOnce = once;
+ return *this;
+ }
+
+ CompileOptions& setNoScriptRval(bool nsr) {
+ noScriptRval = nsr;
+ return *this;
+ }
+
+ CompileOptions& setSkipFilenameValidation(bool b) {
+ skipFilenameValidation_ = b;
+ return *this;
+ }
+
+ CompileOptions& setSelfHostingMode(bool shm) {
+ selfHostingMode = shm;
+ return *this;
+ }
+
+ CompileOptions& setSourceIsLazy(bool l) {
+ sourceIsLazy = l;
+ return *this;
+ }
+
+ CompileOptions& setNonSyntacticScope(bool n) {
+ nonSyntacticScope = n;
+ return *this;
+ }
+
+ CompileOptions& setIntroductionType(const char* t) {
+ introductionType = t;
+ return *this;
+ }
+
+ CompileOptions& setIntroductionInfo(const char* introducerFn,
+ const char* intro, unsigned line,
+ JSScript* script, uint32_t offset) {
+ introducerFilename_ = introducerFn;
+ introductionType = intro;
+ introductionLineno = line;
+ introductionScriptRoot = script;
+ introductionOffset = offset;
+ hasIntroductionInfo = true;
+ return *this;
+ }
+
+ // Set introduction information according to any currently executing script.
+ CompileOptions& setIntroductionInfoToCaller(JSContext* cx,
+ const char* introductionType);
+
+ CompileOptions& setForceFullParse() {
+ forceFullParse_ = true;
+ return *this;
+ }
+
+ CompileOptions& setForceStrictMode() {
+ forceStrictMode_ = true;
+ return *this;
+ }
+
+ CompileOptions& setModule() {
+ // ES6 10.2.1 Module code is always strict mode code.
+ setForceStrictMode();
+ setIsRunOnce(true);
+ allowHTMLComments = false;
+ return *this;
+ }
+
+ CompileOptions(const CompileOptions& rhs) = delete;
+ CompileOptions& operator=(const CompileOptions& rhs) = delete;
+};
+
+} // namespace JS
+
+#endif /* js_CompileOptions_h */
diff --git a/js/public/ContextOptions.h b/js/public/ContextOptions.h
new file mode 100644
index 0000000000..4da2ad9d93
--- /dev/null
+++ b/js/public/ContextOptions.h
@@ -0,0 +1,297 @@
+/* -*- 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/. */
+
+/* JavaScript API. */
+
+#ifndef js_ContextOptions_h
+#define js_ContextOptions_h
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JS_PUBLIC_API JSContext;
+
+namespace JS {
+
+class JS_PUBLIC_API ContextOptions {
+ public:
+ ContextOptions()
+ : asmJS_(true),
+ wasm_(true),
+ wasmForTrustedPrinciples_(true),
+ wasmVerbose_(false),
+ wasmBaseline_(true),
+ wasmIon_(true),
+ wasmCranelift_(false),
+ wasmReftypes_(true),
+ wasmFunctionReferences_(false),
+ wasmGc_(false),
+ wasmMultiValue_(false),
+ wasmSimd_(false),
+ wasmSimdWormhole_(false),
+ wasmExceptions_(false),
+ testWasmAwaitTier2_(false),
+ throwOnAsmJSValidationFailure_(false),
+ disableIon_(false),
+ disableEvalSecurityChecks_(false),
+ asyncStack_(true),
+ asyncStackCaptureDebuggeeOnly_(false),
+ sourcePragmas_(true),
+ throwOnDebuggeeWouldRun_(true),
+ dumpStackOnDebuggeeWouldRun_(false),
+ strictMode_(false),
+#ifdef JS_ENABLE_SMOOSH
+ trackNotImplemented_(false),
+ trySmoosh_(false),
+#endif
+ fuzzing_(false),
+ privateClassFields_(false),
+ privateClassMethods_(false),
+ topLevelAwait_(false) {
+ }
+
+ bool asmJS() const { return asmJS_; }
+ ContextOptions& setAsmJS(bool flag) {
+ asmJS_ = flag;
+ return *this;
+ }
+ ContextOptions& toggleAsmJS() {
+ asmJS_ = !asmJS_;
+ return *this;
+ }
+
+ bool wasm() const { return wasm_; }
+ ContextOptions& setWasm(bool flag) {
+ wasm_ = flag;
+ return *this;
+ }
+ ContextOptions& toggleWasm() {
+ wasm_ = !wasm_;
+ return *this;
+ }
+
+ bool wasmForTrustedPrinciples() const { return wasmForTrustedPrinciples_; }
+ ContextOptions& setWasmForTrustedPrinciples(bool flag) {
+ wasmForTrustedPrinciples_ = flag;
+ return *this;
+ }
+
+ bool wasmVerbose() const { return wasmVerbose_; }
+ ContextOptions& setWasmVerbose(bool flag) {
+ wasmVerbose_ = flag;
+ return *this;
+ }
+
+ bool wasmBaseline() const { return wasmBaseline_; }
+ ContextOptions& setWasmBaseline(bool flag) {
+ wasmBaseline_ = flag;
+ return *this;
+ }
+
+ bool wasmIon() const { return wasmIon_; }
+ ContextOptions& setWasmIon(bool flag) {
+ wasmIon_ = flag;
+ return *this;
+ }
+
+ bool wasmCranelift() const { return wasmCranelift_; }
+ // Defined out-of-line because it depends on a compile-time option
+ ContextOptions& setWasmCranelift(bool flag);
+
+ bool testWasmAwaitTier2() const { return testWasmAwaitTier2_; }
+ ContextOptions& setTestWasmAwaitTier2(bool flag) {
+ testWasmAwaitTier2_ = flag;
+ return *this;
+ }
+
+ bool wasmReftypes() const { return wasmReftypes_; }
+ ContextOptions& setWasmReftypes(bool flag) {
+ wasmReftypes_ = flag;
+ return *this;
+ }
+
+ bool wasmFunctionReferences() const { return wasmFunctionReferences_; }
+ // Defined out-of-line because it depends on a compile-time option
+ ContextOptions& setWasmFunctionReferences(bool flag);
+
+ bool wasmGc() const { return wasmGc_; }
+ // Defined out-of-line because it depends on a compile-time option
+ ContextOptions& setWasmGc(bool flag);
+
+ bool wasmMultiValue() const { return wasmMultiValue_; }
+ // Defined out-of-line because it depends on a compile-time option
+ ContextOptions& setWasmMultiValue(bool flag);
+
+ bool wasmSimd() const { return wasmSimd_; }
+ // Defined out-of-line because it depends on a compile-time option
+ ContextOptions& setWasmSimd(bool flag);
+
+ bool wasmSimdWormhole() const { return wasmSimdWormhole_; }
+ // Defined out-of-line because it depends on a compile-time option
+ ContextOptions& setWasmSimdWormhole(bool flag);
+
+ bool wasmExceptions() const { return wasmExceptions_; }
+ // Defined out-of-line because it depends on a compile-time option
+ ContextOptions& setWasmExceptions(bool flag);
+
+ bool throwOnAsmJSValidationFailure() const {
+ return throwOnAsmJSValidationFailure_;
+ }
+ ContextOptions& setThrowOnAsmJSValidationFailure(bool flag) {
+ throwOnAsmJSValidationFailure_ = flag;
+ return *this;
+ }
+ ContextOptions& toggleThrowOnAsmJSValidationFailure() {
+ throwOnAsmJSValidationFailure_ = !throwOnAsmJSValidationFailure_;
+ return *this;
+ }
+
+ // Override to allow disabling Ion for this context irrespective of the
+ // process-wide Ion-enabled setting. This must be set right after creating
+ // the context.
+ bool disableIon() const { return disableIon_; }
+ ContextOptions& setDisableIon() {
+ disableIon_ = true;
+ return *this;
+ }
+
+ bool privateClassFields() const { return privateClassFields_; }
+ ContextOptions& setPrivateClassFields(bool enabled) {
+ privateClassFields_ = enabled;
+ return *this;
+ }
+
+ bool privateClassMethods() const { return privateClassMethods_; }
+ ContextOptions& setPrivateClassMethods(bool enabled) {
+ privateClassMethods_ = enabled;
+ return *this;
+ }
+
+ bool topLevelAwait() const { return topLevelAwait_; }
+ ContextOptions& setTopLevelAwait(bool enabled) {
+ topLevelAwait_ = enabled;
+ return *this;
+ }
+
+ // Override to allow disabling the eval restriction security checks for
+ // this context.
+ bool disableEvalSecurityChecks() const { return disableEvalSecurityChecks_; }
+ ContextOptions& setDisableEvalSecurityChecks() {
+ disableEvalSecurityChecks_ = true;
+ return *this;
+ }
+
+ bool asyncStack() const { return asyncStack_; }
+ ContextOptions& setAsyncStack(bool flag) {
+ asyncStack_ = flag;
+ return *this;
+ }
+
+ bool asyncStackCaptureDebuggeeOnly() const {
+ return asyncStackCaptureDebuggeeOnly_;
+ }
+ ContextOptions& setAsyncStackCaptureDebuggeeOnly(bool flag) {
+ asyncStackCaptureDebuggeeOnly_ = flag;
+ return *this;
+ }
+
+ // Enable/disable support for parsing '//(#@) source(Mapping)?URL=' pragmas.
+ bool sourcePragmas() const { return sourcePragmas_; }
+ ContextOptions& setSourcePragmas(bool flag) {
+ sourcePragmas_ = flag;
+ return *this;
+ }
+
+ bool throwOnDebuggeeWouldRun() const { return throwOnDebuggeeWouldRun_; }
+ ContextOptions& setThrowOnDebuggeeWouldRun(bool flag) {
+ throwOnDebuggeeWouldRun_ = flag;
+ return *this;
+ }
+
+ bool dumpStackOnDebuggeeWouldRun() const {
+ return dumpStackOnDebuggeeWouldRun_;
+ }
+ ContextOptions& setDumpStackOnDebuggeeWouldRun(bool flag) {
+ dumpStackOnDebuggeeWouldRun_ = flag;
+ return *this;
+ }
+
+ bool strictMode() const { return strictMode_; }
+ ContextOptions& setStrictMode(bool flag) {
+ strictMode_ = flag;
+ return *this;
+ }
+ ContextOptions& toggleStrictMode() {
+ strictMode_ = !strictMode_;
+ return *this;
+ }
+
+#ifdef JS_ENABLE_SMOOSH
+ // Track Number of Not Implemented Calls by writing to a file
+ bool trackNotImplemented() const { return trackNotImplemented_; }
+ ContextOptions& setTrackNotImplemented(bool flag) {
+ trackNotImplemented_ = flag;
+ return *this;
+ }
+
+ // Try compiling SmooshMonkey frontend first, and fallback to C++
+ // implementation when it fails.
+ bool trySmoosh() const { return trySmoosh_; }
+ ContextOptions& setTrySmoosh(bool flag) {
+ trySmoosh_ = flag;
+ return *this;
+ }
+
+#endif // JS_ENABLE_SMOOSH
+
+ bool fuzzing() const { return fuzzing_; }
+ // Defined out-of-line because it depends on a compile-time option
+ ContextOptions& setFuzzing(bool flag);
+
+ void disableOptionsForSafeMode() {
+ setAsmJS(false);
+ setWasmBaseline(false);
+ }
+
+ private:
+ bool asmJS_ : 1;
+ bool wasm_ : 1;
+ bool wasmForTrustedPrinciples_ : 1;
+ bool wasmVerbose_ : 1;
+ bool wasmBaseline_ : 1;
+ bool wasmIon_ : 1;
+ bool wasmCranelift_ : 1;
+ bool wasmReftypes_ : 1;
+ bool wasmFunctionReferences_ : 1;
+ bool wasmGc_ : 1;
+ bool wasmMultiValue_ : 1;
+ bool wasmSimd_ : 1;
+ bool wasmSimdWormhole_ : 1;
+ bool wasmExceptions_ : 1;
+ bool testWasmAwaitTier2_ : 1;
+ bool throwOnAsmJSValidationFailure_ : 1;
+ bool disableIon_ : 1;
+ bool disableEvalSecurityChecks_ : 1;
+ bool asyncStack_ : 1;
+ bool asyncStackCaptureDebuggeeOnly_ : 1;
+ bool sourcePragmas_ : 1;
+ bool throwOnDebuggeeWouldRun_ : 1;
+ bool dumpStackOnDebuggeeWouldRun_ : 1;
+ bool strictMode_ : 1;
+#ifdef JS_ENABLE_SMOOSH
+ bool trackNotImplemented_ : 1;
+ bool trySmoosh_ : 1;
+#endif
+ bool fuzzing_ : 1;
+ bool privateClassFields_ : 1;
+ bool privateClassMethods_ : 1;
+ bool topLevelAwait_ : 1;
+};
+
+JS_PUBLIC_API ContextOptions& ContextOptionsRef(JSContext* cx);
+
+} // namespace JS
+
+#endif // js_ContextOptions_h
diff --git a/js/public/Conversions.h b/js/public/Conversions.h
new file mode 100644
index 0000000000..6c0ca7601e
--- /dev/null
+++ b/js/public/Conversions.h
@@ -0,0 +1,590 @@
+/* -*- 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/. */
+
+/* ECMAScript conversion operations. */
+
+#ifndef js_Conversions_h
+#define js_Conversions_h
+
+#include "mozilla/Casting.h"
+#include "mozilla/Compiler.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/WrappingOperations.h"
+
+#include <cmath>
+#include <stddef.h> // size_t
+#include <stdint.h> // {u,}int{8,16,32,64}_t
+#include <type_traits>
+
+#include "jspubtd.h"
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h"
+#include "js/Value.h"
+
+namespace js {
+
+/* DO NOT CALL THIS. Use JS::ToBoolean. */
+extern JS_PUBLIC_API bool ToBooleanSlow(JS::HandleValue v);
+
+/* DO NOT CALL THIS. Use JS::ToNumber. */
+extern JS_PUBLIC_API bool ToNumberSlow(JSContext* cx, JS::HandleValue v,
+ double* dp);
+
+/* DO NOT CALL THIS. Use JS::ToInt8. */
+extern JS_PUBLIC_API bool ToInt8Slow(JSContext* cx, JS::HandleValue v,
+ int8_t* out);
+
+/* DO NOT CALL THIS. Use JS::ToUint8. */
+extern JS_PUBLIC_API bool ToUint8Slow(JSContext* cx, JS::HandleValue v,
+ uint8_t* out);
+
+/* DO NOT CALL THIS. Use JS::ToInt16. */
+extern JS_PUBLIC_API bool ToInt16Slow(JSContext* cx, JS::HandleValue v,
+ int16_t* out);
+
+/* DO NOT CALL THIS. Use JS::ToInt32. */
+extern JS_PUBLIC_API bool ToInt32Slow(JSContext* cx, JS::HandleValue v,
+ int32_t* out);
+
+/* DO NOT CALL THIS. Use JS::ToUint32. */
+extern JS_PUBLIC_API bool ToUint32Slow(JSContext* cx, JS::HandleValue v,
+ uint32_t* out);
+
+/* DO NOT CALL THIS. Use JS::ToUint16. */
+extern JS_PUBLIC_API bool ToUint16Slow(JSContext* cx, JS::HandleValue v,
+ uint16_t* out);
+
+/* DO NOT CALL THIS. Use JS::ToInt64. */
+extern JS_PUBLIC_API bool ToInt64Slow(JSContext* cx, JS::HandleValue v,
+ int64_t* out);
+
+/* DO NOT CALL THIS. Use JS::ToUint64. */
+extern JS_PUBLIC_API bool ToUint64Slow(JSContext* cx, JS::HandleValue v,
+ uint64_t* out);
+
+/* DO NOT CALL THIS. Use JS::ToString. */
+extern JS_PUBLIC_API JSString* ToStringSlow(JSContext* cx, JS::HandleValue v);
+
+/* DO NOT CALL THIS. Use JS::ToObject. */
+extern JS_PUBLIC_API JSObject* ToObjectSlow(JSContext* cx, JS::HandleValue v,
+ bool reportScanStack);
+
+} // namespace js
+
+namespace JS {
+
+namespace detail {
+
+#ifdef JS_DEBUG
+/**
+ * Assert that we're not doing GC on cx, that we're in a request as
+ * needed, and that the compartments for cx and v are correct.
+ * Also check that GC would be safe at this point.
+ */
+extern JS_PUBLIC_API void AssertArgumentsAreSane(JSContext* cx, HandleValue v);
+#else
+inline void AssertArgumentsAreSane(JSContext* cx, HandleValue v) {}
+#endif /* JS_DEBUG */
+
+} // namespace detail
+
+/**
+ * ES6 draft 20141224, 7.1.1, second algorithm.
+ *
+ * Most users shouldn't call this -- use JS::ToBoolean, ToNumber, or ToString
+ * instead. This will typically only be called from custom convert hooks that
+ * wish to fall back to the ES6 default conversion behavior shared by most
+ * objects in JS, codified as OrdinaryToPrimitive.
+ */
+extern JS_PUBLIC_API bool OrdinaryToPrimitive(JSContext* cx, HandleObject obj,
+ JSType type,
+ MutableHandleValue vp);
+
+/* ES6 draft 20141224, 7.1.2. */
+MOZ_ALWAYS_INLINE bool ToBoolean(HandleValue v) {
+ if (v.isBoolean()) {
+ return v.toBoolean();
+ }
+ if (v.isInt32()) {
+ return v.toInt32() != 0;
+ }
+ if (v.isNullOrUndefined()) {
+ return false;
+ }
+ if (v.isDouble()) {
+ double d = v.toDouble();
+ return !mozilla::IsNaN(d) && d != 0;
+ }
+ if (v.isSymbol()) {
+ return true;
+ }
+
+ /* The slow path handles strings, BigInts and objects. */
+ return js::ToBooleanSlow(v);
+}
+
+/* ES6 draft 20141224, 7.1.3. */
+MOZ_ALWAYS_INLINE bool ToNumber(JSContext* cx, HandleValue v, double* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isNumber()) {
+ *out = v.toNumber();
+ return true;
+ }
+ return js::ToNumberSlow(cx, v, out);
+}
+
+// ES2020 draft rev 6b05bc56ba4e3c7a2b9922c4282d9eb844426d9b
+// 7.1.5 ToInteger ( argument )
+//
+// Specialized for double values.
+inline double ToInteger(double d) {
+ if (d == 0) {
+ return 0;
+ }
+
+ if (!mozilla::IsFinite(d)) {
+ if (mozilla::IsNaN(d)) {
+ return 0;
+ }
+ return d;
+ }
+
+ return std::trunc(d) + (+0.0); // Add zero to convert -0 to +0.
+}
+
+/* ES6 draft 20141224, 7.1.5. */
+MOZ_ALWAYS_INLINE bool ToInt32(JSContext* cx, JS::HandleValue v, int32_t* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isInt32()) {
+ *out = v.toInt32();
+ return true;
+ }
+ return js::ToInt32Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.6. */
+MOZ_ALWAYS_INLINE bool ToUint32(JSContext* cx, HandleValue v, uint32_t* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isInt32()) {
+ *out = uint32_t(v.toInt32());
+ return true;
+ }
+ return js::ToUint32Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.7. */
+MOZ_ALWAYS_INLINE bool ToInt16(JSContext* cx, JS::HandleValue v, int16_t* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isInt32()) {
+ *out = int16_t(v.toInt32());
+ return true;
+ }
+ return js::ToInt16Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.8. */
+MOZ_ALWAYS_INLINE bool ToUint16(JSContext* cx, HandleValue v, uint16_t* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isInt32()) {
+ *out = uint16_t(v.toInt32());
+ return true;
+ }
+ return js::ToUint16Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.9 */
+MOZ_ALWAYS_INLINE bool ToInt8(JSContext* cx, JS::HandleValue v, int8_t* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isInt32()) {
+ *out = int8_t(v.toInt32());
+ return true;
+ }
+ return js::ToInt8Slow(cx, v, out);
+}
+
+/* ES6 ECMA-262, 7.1.10 */
+MOZ_ALWAYS_INLINE bool ToUint8(JSContext* cx, JS::HandleValue v, uint8_t* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isInt32()) {
+ *out = uint8_t(v.toInt32());
+ return true;
+ }
+ return js::ToUint8Slow(cx, v, out);
+}
+
+/*
+ * Non-standard, with behavior similar to that of ToInt32, except in its
+ * producing an int64_t.
+ */
+MOZ_ALWAYS_INLINE bool ToInt64(JSContext* cx, HandleValue v, int64_t* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isInt32()) {
+ *out = int64_t(v.toInt32());
+ return true;
+ }
+ return js::ToInt64Slow(cx, v, out);
+}
+
+/*
+ * Non-standard, with behavior similar to that of ToUint32, except in its
+ * producing a uint64_t.
+ */
+MOZ_ALWAYS_INLINE bool ToUint64(JSContext* cx, HandleValue v, uint64_t* out) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isInt32()) {
+ *out = uint64_t(v.toInt32());
+ return true;
+ }
+ return js::ToUint64Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.12. */
+MOZ_ALWAYS_INLINE JSString* ToString(JSContext* cx, HandleValue v) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isString()) {
+ return v.toString();
+ }
+ return js::ToStringSlow(cx, v);
+}
+
+/* ES6 draft 20141224, 7.1.13. */
+inline JSObject* ToObject(JSContext* cx, HandleValue v) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.isObject()) {
+ return &v.toObject();
+ }
+ return js::ToObjectSlow(cx, v, false);
+}
+
+/**
+ * Convert a double value to UnsignedInteger (an unsigned integral type) using
+ * ECMAScript-style semantics (that is, in like manner to how ECMAScript's
+ * ToInt32 converts to int32_t).
+ *
+ * If d is infinite or NaN, return 0.
+ * Otherwise compute d2 = sign(d) * floor(abs(d)), and return the
+ * UnsignedInteger value congruent to d2 % 2**(bit width of UnsignedInteger).
+ *
+ * The algorithm below is inspired by that found in
+ * <https://trac.webkit.org/changeset/67825/webkit/trunk/JavaScriptCore/runtime/JSValue.cpp>
+ * but has been generalized to all integer widths.
+ */
+template <typename UnsignedInteger>
+inline UnsignedInteger ToUnsignedInteger(double d) {
+ static_assert(std::is_unsigned_v<UnsignedInteger>,
+ "UnsignedInteger must be an unsigned type");
+
+ uint64_t bits = mozilla::BitwiseCast<uint64_t>(d);
+ unsigned DoubleExponentShift = mozilla::FloatingPoint<double>::kExponentShift;
+
+ // Extract the exponent component. (Be careful here! It's not technically
+ // the exponent in NaN, infinities, and subnormals.)
+ int_fast16_t exp =
+ int_fast16_t((bits & mozilla::FloatingPoint<double>::kExponentBits) >>
+ DoubleExponentShift) -
+ int_fast16_t(mozilla::FloatingPoint<double>::kExponentBias);
+
+ // If the exponent's less than zero, abs(d) < 1, so the result is 0. (This
+ // also handles subnormals.)
+ if (exp < 0) {
+ return 0;
+ }
+
+ uint_fast16_t exponent = mozilla::AssertedCast<uint_fast16_t>(exp);
+
+ // If the exponent is greater than or equal to the bits of precision of a
+ // double plus UnsignedInteger's width, the number is either infinite, NaN,
+ // or too large to have lower-order bits in the congruent value. (Example:
+ // 2**84 is exactly representable as a double. The next exact double is
+ // 2**84 + 2**32. Thus if UnsignedInteger is uint32_t, an exponent >= 84
+ // implies floor(abs(d)) == 0 mod 2**32.) Return 0 in all these cases.
+ constexpr size_t ResultWidth = CHAR_BIT * sizeof(UnsignedInteger);
+ if (exponent >= DoubleExponentShift + ResultWidth) {
+ return 0;
+ }
+
+ // The significand contains the bits that will determine the final result.
+ // Shift those bits left or right, according to the exponent, to their
+ // locations in the unsigned binary representation of floor(abs(d)).
+ static_assert(sizeof(UnsignedInteger) <= sizeof(uint64_t),
+ "left-shifting below would lose upper bits");
+ UnsignedInteger result =
+ (exponent > DoubleExponentShift)
+ ? UnsignedInteger(bits << (exponent - DoubleExponentShift))
+ : UnsignedInteger(bits >> (DoubleExponentShift - exponent));
+
+ // Two further complications remain. First, |result| may contain bogus
+ // sign/exponent bits. Second, IEEE-754 numbers' significands (excluding
+ // subnormals, but we already handled those) have an implicit leading 1
+ // which may affect the final result.
+ //
+ // It may appear that there's complexity here depending on how ResultWidth
+ // and DoubleExponentShift relate, but it turns out there's not.
+ //
+ // Assume ResultWidth < DoubleExponentShift:
+ // Only right-shifts leave bogus bits in |result|. For this to happen,
+ // we must right-shift by > |DoubleExponentShift - ResultWidth|, implying
+ // |exponent < ResultWidth|.
+ // The implicit leading bit only matters if it appears in the final
+ // result -- if |2**exponent mod 2**ResultWidth != 0|. This implies
+ // |exponent < ResultWidth|.
+ // Otherwise assume ResultWidth >= DoubleExponentShift:
+ // Any left-shift less than |ResultWidth - DoubleExponentShift| leaves
+ // bogus bits in |result|. This implies |exponent < ResultWidth|. Any
+ // right-shift less than |ResultWidth| does too, which implies
+ // |DoubleExponentShift - ResultWidth < exponent|. By assumption, then,
+ // |exponent| is negative, but we excluded that above. So bogus bits
+ // need only |exponent < ResultWidth|.
+ // The implicit leading bit matters identically to the other case, so
+ // again, |exponent < ResultWidth|.
+ if (exponent < ResultWidth) {
+ const auto implicitOne =
+ static_cast<UnsignedInteger>(UnsignedInteger{1} << exponent);
+ result &= implicitOne - 1; // remove bogus bits
+ result += implicitOne; // add the implicit bit
+ }
+
+ // Compute the congruent value in the signed range.
+ return (bits & mozilla::FloatingPoint<double>::kSignBit) ? ~result + 1
+ : result;
+}
+
+template <typename SignedInteger>
+inline SignedInteger ToSignedInteger(double d) {
+ static_assert(std::is_signed_v<SignedInteger>,
+ "SignedInteger must be a signed type");
+
+ using UnsignedInteger = std::make_unsigned_t<SignedInteger>;
+ UnsignedInteger u = ToUnsignedInteger<UnsignedInteger>(d);
+
+ return mozilla::WrapToSigned(u);
+}
+
+// clang crashes compiling this when targeting arm:
+// https://llvm.org/bugs/show_bug.cgi?id=22974
+#if defined(__arm__) && MOZ_IS_GCC
+
+template <>
+inline int32_t ToSignedInteger<int32_t>(double d) {
+ int32_t i;
+ uint32_t tmp0;
+ uint32_t tmp1;
+ uint32_t tmp2;
+ asm(
+ // We use a pure integer solution here. In the 'softfp' ABI, the argument
+ // will start in r0 and r1, and VFP can't do all of the necessary ECMA
+ // conversions by itself so some integer code will be required anyway. A
+ // hybrid solution is faster on A9, but this pure integer solution is
+ // notably faster for A8.
+
+ // %0 is the result register, and may alias either of the %[QR]1
+ // registers.
+ // %Q4 holds the lower part of the mantissa.
+ // %R4 holds the sign, exponent, and the upper part of the mantissa.
+ // %1, %2 and %3 are used as temporary values.
+
+ // Extract the exponent.
+ " mov %1, %R4, LSR #20\n"
+ " bic %1, %1, #(1 << 11)\n" // Clear the sign.
+
+ // Set the implicit top bit of the mantissa. This clobbers a bit of the
+ // exponent, but we have already extracted that.
+ " orr %R4, %R4, #(1 << 20)\n"
+
+ // Special Cases
+ // We should return zero in the following special cases:
+ // - Exponent is 0x000 - 1023: +/-0 or subnormal.
+ // - Exponent is 0x7ff - 1023: +/-INFINITY or NaN
+ // - This case is implicitly handled by the standard code path
+ // anyway, as shifting the mantissa up by the exponent will
+ // result in '0'.
+ //
+ // The result is composed of the mantissa, prepended with '1' and
+ // bit-shifted left by the (decoded) exponent. Note that because the
+ // r1[20] is the bit with value '1', r1 is effectively already shifted
+ // (left) by 20 bits, and r0 is already shifted by 52 bits.
+
+ // Adjust the exponent to remove the encoding offset. If the decoded
+ // exponent is negative, quickly bail out with '0' as such values round to
+ // zero anyway. This also catches +/-0 and subnormals.
+ " sub %1, %1, #0xff\n"
+ " subs %1, %1, #0x300\n"
+ " bmi 8f\n"
+
+ // %1 = (decoded) exponent >= 0
+ // %R4 = upper mantissa and sign
+
+ // ---- Lower Mantissa ----
+ " subs %3, %1, #52\n" // Calculate exp-52
+ " bmi 1f\n"
+
+ // Shift r0 left by exp-52.
+ // Ensure that we don't overflow ARM's 8-bit shift operand range.
+ // We need to handle anything up to an 11-bit value here as we know that
+ // 52 <= exp <= 1024 (0x400). Any shift beyond 31 bits results in zero
+ // anyway, so as long as we don't touch the bottom 5 bits, we can use
+ // a logical OR to push long shifts into the 32 <= (exp&0xff) <= 255
+ // range.
+ " bic %2, %3, #0xff\n"
+ " orr %3, %3, %2, LSR #3\n"
+ // We can now perform a straight shift, avoiding the need for any
+ // conditional instructions or extra branches.
+ " mov %Q4, %Q4, LSL %3\n"
+ " b 2f\n"
+ "1:\n" // Shift r0 right by 52-exp.
+ // We know that 0 <= exp < 52, and we can shift up to 255 bits so
+ // 52-exp will always be a valid shift and we can sk%3 the range
+ // check for this case.
+ " rsb %3, %1, #52\n"
+ " mov %Q4, %Q4, LSR %3\n"
+
+ // %1 = (decoded) exponent
+ // %R4 = upper mantissa and sign
+ // %Q4 = partially-converted integer
+
+ "2:\n"
+ // ---- Upper Mantissa ----
+ // This is much the same as the lower mantissa, with a few different
+ // boundary checks and some masking to hide the exponent & sign bit in the
+ // upper word.
+ // Note that the upper mantissa is pre-shifted by 20 in %R4, but we shift
+ // it left more to remove the sign and exponent so it is effectively
+ // pre-shifted by 31 bits.
+ " subs %3, %1, #31\n" // Calculate exp-31
+ " mov %1, %R4, LSL #11\n" // Re-use %1 as a temporary register.
+ " bmi 3f\n"
+
+ // Shift %R4 left by exp-31.
+ // Avoid overflowing the 8-bit shift range, as before.
+ " bic %2, %3, #0xff\n"
+ " orr %3, %3, %2, LSR #3\n"
+ // Perform the shift.
+ " mov %2, %1, LSL %3\n"
+ " b 4f\n"
+ "3:\n" // Shift r1 right by 31-exp.
+ // We know that 0 <= exp < 31, and we can shift up to 255 bits so
+ // 31-exp will always be a valid shift and we can skip the range
+ // check for this case.
+ " rsb %3, %3, #0\n" // Calculate 31-exp from -(exp-31)
+ " mov %2, %1, LSR %3\n" // Thumb-2 can't do "LSR %3" in "orr".
+
+ // %Q4 = partially-converted integer (lower)
+ // %R4 = upper mantissa and sign
+ // %2 = partially-converted integer (upper)
+
+ "4:\n"
+ // Combine the converted parts.
+ " orr %Q4, %Q4, %2\n"
+ // Negate the result if we have to, and move it to %0 in the process. To
+ // avoid conditionals, we can do this by inverting on %R4[31], then adding
+ // %R4[31]>>31.
+ " eor %Q4, %Q4, %R4, ASR #31\n"
+ " add %0, %Q4, %R4, LSR #31\n"
+ " b 9f\n"
+ "8:\n"
+ // +/-INFINITY, +/-0, subnormals, NaNs, and anything else out-of-range
+ // that will result in a conversion of '0'.
+ " mov %0, #0\n"
+ "9:\n"
+ : "=r"(i), "=&r"(tmp0), "=&r"(tmp1), "=&r"(tmp2), "=&r"(d)
+ : "4"(d)
+ : "cc");
+ return i;
+}
+
+#endif // defined (__arm__) && MOZ_IS_GCC
+
+namespace detail {
+
+template <typename IntegerType,
+ bool IsUnsigned = std::is_unsigned_v<IntegerType>>
+struct ToSignedOrUnsignedInteger;
+
+template <typename IntegerType>
+struct ToSignedOrUnsignedInteger<IntegerType, true> {
+ static IntegerType compute(double d) {
+ return ToUnsignedInteger<IntegerType>(d);
+ }
+};
+
+template <typename IntegerType>
+struct ToSignedOrUnsignedInteger<IntegerType, false> {
+ static IntegerType compute(double d) {
+ return ToSignedInteger<IntegerType>(d);
+ }
+};
+
+} // namespace detail
+
+template <typename IntegerType>
+inline IntegerType ToSignedOrUnsignedInteger(double d) {
+ return detail::ToSignedOrUnsignedInteger<IntegerType>::compute(d);
+}
+
+/* WEBIDL 4.2.4 */
+inline int8_t ToInt8(double d) { return ToSignedInteger<int8_t>(d); }
+
+/* ECMA-262 7.1.10 ToUInt8() specialized for doubles. */
+inline int8_t ToUint8(double d) { return ToUnsignedInteger<uint8_t>(d); }
+
+/* WEBIDL 4.2.6 */
+inline int16_t ToInt16(double d) { return ToSignedInteger<int16_t>(d); }
+
+inline uint16_t ToUint16(double d) { return ToUnsignedInteger<uint16_t>(d); }
+
+/* ES5 9.5 ToInt32 (specialized for doubles). */
+inline int32_t ToInt32(double d) { return ToSignedInteger<int32_t>(d); }
+
+/* ES5 9.6 (specialized for doubles). */
+inline uint32_t ToUint32(double d) { return ToUnsignedInteger<uint32_t>(d); }
+
+/* WEBIDL 4.2.10 */
+inline int64_t ToInt64(double d) { return ToSignedInteger<int64_t>(d); }
+
+/* WEBIDL 4.2.11 */
+inline uint64_t ToUint64(double d) { return ToUnsignedInteger<uint64_t>(d); }
+
+/**
+ * An amount of space large enough to store the null-terminated result of
+ * |ToString| on any Number.
+ *
+ * The <https://tc39.es/ecma262/#sec-tostring-applied-to-the-number-type>
+ * |NumberToString| algorithm is specified in terms of results, not an
+ * algorithm. It is extremely unclear from the algorithm's definition what its
+ * longest output can be. |-(2**-19 - 2**-72)| requires 25 + 1 characters and
+ * is believed to be at least *very close* to the upper bound, so we round that
+ * *very generously* upward to a 64-bit pointer-size boundary (to be extra
+ * cautious) and assume that's adequate.
+ *
+ * If you can supply better reasoning for a tighter bound, file a bug to improve
+ * this!
+ */
+static constexpr size_t MaximumNumberToStringLength = 31 + 1;
+
+/**
+ * Store in |out| the null-terminated, base-10 result of |ToString| applied to
+ * |d| per <https://tc39.es/ecma262/#sec-tostring-applied-to-the-number-type>.
+ * (This will produce "NaN", "-Infinity", or "Infinity" for non-finite |d|.)
+ */
+extern JS_PUBLIC_API void NumberToString(
+ double d, char (&out)[MaximumNumberToStringLength]);
+
+} // namespace JS
+
+#endif /* js_Conversions_h */
diff --git a/js/public/Date.h b/js/public/Date.h
new file mode 100644
index 0000000000..e7a54d86c4
--- /dev/null
+++ b/js/public/Date.h
@@ -0,0 +1,209 @@
+/* -*- 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/. */
+
+/* JavaScript date/time computation and creation functions. */
+
+#ifndef js_Date_h
+#define js_Date_h
+
+/*
+ * Dates in JavaScript are defined by IEEE-754 double precision numbers from
+ * the set:
+ *
+ * { t ∈ ℕ : -8.64e15 ≤ t ≤ +8.64e15 } ∪ { NaN }
+ *
+ * The single NaN value represents any invalid-date value. All other values
+ * represent idealized durations in milliseconds since the UTC epoch. (Leap
+ * seconds are ignored; leap days are not.) +0 is the only zero in this set.
+ * The limit represented by 8.64e15 milliseconds is 100 million days either
+ * side of 00:00 January 1, 1970 UTC.
+ *
+ * Dates in the above set are represented by the |ClippedTime| class. The
+ * double type is a superset of the above set, so it *may* (but need not)
+ * represent a date. Use ECMAScript's |TimeClip| method to produce a date from
+ * a double.
+ *
+ * Date *objects* are simply wrappers around |TimeClip|'d numbers, with a bunch
+ * of accessor methods to the various aspects of the represented date.
+ */
+
+#include "mozilla/FloatingPoint.h" // mozilla::{IsFinite,IsNaN}, mozilla::UnspecifiedNaN
+#include "mozilla/MathAlgorithms.h" // mozilla::Abs
+
+#include "js/Conversions.h" // JS::ToInteger
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/Value.h" // JS::CanonicalizeNaN, JS::DoubleValue, JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+/**
+ * Re-query the system to determine the current time zone adjustment from UTC,
+ * including any component due to DST. If the time zone has changed, this will
+ * cause all Date object non-UTC methods and formatting functions to produce
+ * appropriately adjusted results.
+ *
+ * Left to its own devices, SpiderMonkey itself may occasionally try to detect
+ * system time changes. However, no particular frequency of checking is
+ * guaranteed. Embedders unable to accept occasional inaccuracies should call
+ * this method in response to system time changes, or immediately before
+ * operations requiring instantaneous correctness, to guarantee correct
+ * behavior.
+ */
+extern JS_PUBLIC_API void ResetTimeZone();
+
+class ClippedTime;
+inline ClippedTime TimeClip(double time);
+
+/*
+ * |ClippedTime| represents the limited subset of dates/times described above.
+ *
+ * An invalid date/time may be created through the |ClippedTime::invalid|
+ * method. Otherwise, a |ClippedTime| may be created using the |TimeClip|
+ * method.
+ *
+ * In typical use, the user might wish to manipulate a timestamp. The user
+ * performs a series of operations on it, but the final value might not be a
+ * date as defined above -- it could have overflowed, acquired a fractional
+ * component, &c. So as a *final* step, the user passes that value through
+ * |TimeClip| to produce a number restricted to JavaScript's date range.
+ *
+ * APIs that accept a JavaScript date value thus accept a |ClippedTime|, not a
+ * double. This ensures that date/time APIs will only ever receive acceptable
+ * JavaScript dates. This also forces users to perform any desired clipping,
+ * as only the user knows what behavior is desired when clipping occurs.
+ */
+class ClippedTime {
+ double t = mozilla::UnspecifiedNaN<double>();
+
+ explicit ClippedTime(double time) : t(time) {}
+ friend ClippedTime TimeClip(double time);
+
+ public:
+ // Create an invalid date.
+ ClippedTime() = default;
+
+ // Create an invalid date/time, more explicitly; prefer this to the default
+ // constructor.
+ static ClippedTime invalid() { return ClippedTime(); }
+
+ double toDouble() const { return t; }
+
+ bool isValid() const { return !mozilla::IsNaN(t); }
+};
+
+// ES6 20.3.1.15.
+//
+// Clip a double to JavaScript's date range (or to an invalid date) using the
+// ECMAScript TimeClip algorithm.
+inline ClippedTime TimeClip(double time) {
+ // Steps 1-2.
+ const double MaxTimeMagnitude = 8.64e15;
+ if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude) {
+ return ClippedTime(mozilla::UnspecifiedNaN<double>());
+ }
+
+ // Step 3.
+ return ClippedTime(ToInteger(time));
+}
+
+// Produce a double Value from the given time. Because times may be NaN,
+// prefer using this to manual canonicalization.
+inline Value TimeValue(ClippedTime time) {
+ return CanonicalizedDoubleValue(time.toDouble());
+}
+
+// Create a new Date object whose [[DateValue]] internal slot contains the
+// clipped |time|. (Users who must represent times outside that range must use
+// another representation.)
+extern JS_PUBLIC_API JSObject* NewDateObject(JSContext* cx, ClippedTime time);
+
+/**
+ * Create a new Date object for a year/month/day-of-month/hour/minute/second.
+ *
+ * The created date is initialized with the time value
+ *
+ * TimeClip(UTC(MakeDate(MakeDay(year, mon, mday),
+ * MakeTime(hour, min, sec, 0.0))))
+ *
+ * where each function/operation is as specified in ECMAScript.
+ */
+extern JS_PUBLIC_API JSObject* NewDateObject(JSContext* cx, int year, int mon,
+ int mday, int hour, int min,
+ int sec);
+
+/**
+ * On success, returns true, setting |*isDate| to true if |obj| is a Date
+ * object or a wrapper around one, or to false if not. Returns false on
+ * failure.
+ *
+ * This method returns true with |*isDate == false| when passed an ES6 proxy
+ * whose target is a Date, or when passed a revoked proxy.
+ */
+extern JS_PUBLIC_API bool ObjectIsDate(JSContext* cx, Handle<JSObject*> obj,
+ bool* isDate);
+
+// Year is a year, month is 0-11, day is 1-based. The return value is a number
+// of milliseconds since the epoch.
+//
+// Consistent with the MakeDate algorithm defined in ECMAScript, this value is
+// *not* clipped! Use JS::TimeClip if you need a clipped date.
+JS_PUBLIC_API double MakeDate(double year, unsigned month, unsigned day);
+
+// Year is a year, month is 0-11, day is 1-based, and time is in milliseconds.
+// The return value is a number of milliseconds since the epoch.
+//
+// Consistent with the MakeDate algorithm defined in ECMAScript, this value is
+// *not* clipped! Use JS::TimeClip if you need a clipped date.
+JS_PUBLIC_API double MakeDate(double year, unsigned month, unsigned day,
+ double time);
+
+// Takes an integer number of milliseconds since the epoch and returns the
+// year. Can return NaN, and will do so if NaN is passed in.
+JS_PUBLIC_API double YearFromTime(double time);
+
+// Takes an integer number of milliseconds since the epoch and returns the
+// month (0-11). Can return NaN, and will do so if NaN is passed in.
+JS_PUBLIC_API double MonthFromTime(double time);
+
+// Takes an integer number of milliseconds since the epoch and returns the
+// day (1-based). Can return NaN, and will do so if NaN is passed in.
+JS_PUBLIC_API double DayFromTime(double time);
+
+// Takes an integer year and returns the number of days from epoch to the given
+// year.
+// NOTE: The calculation performed by this function is literally that given in
+// the ECMAScript specification. Nonfinite years, years containing fractional
+// components, and years outside ECMAScript's date range are not handled with
+// any particular intelligence. Garbage in, garbage out.
+JS_PUBLIC_API double DayFromYear(double year);
+
+// Takes an integer number of milliseconds since the epoch and an integer year,
+// returns the number of days in that year. If |time| is nonfinite, returns NaN.
+// Otherwise |time| *must* correspond to a time within the valid year |year|.
+// This should usually be ensured by computing |year| as
+// |JS::DayFromYear(time)|.
+JS_PUBLIC_API double DayWithinYear(double time, double year);
+
+// The callback will be a wrapper function that accepts a single double (the
+// time to clamp and jitter.) Inside the JS Engine, other parameters that may be
+// needed are all constant, so they are handled inside the wrapper function
+using ReduceMicrosecondTimePrecisionCallback = double (*)(double, JSContext*);
+
+// Set a callback into the toolkit/components/resistfingerprinting function that
+// will centralize time resolution and jitter into one place.
+JS_PUBLIC_API void SetReduceMicrosecondTimePrecisionCallback(
+ ReduceMicrosecondTimePrecisionCallback callback);
+
+// Sets the time resolution for fingerprinting protection, and whether jitter
+// should occur. If resolution is set to zero, then no rounding or jitter will
+// occur. This is used if the callback above is not specified.
+JS_PUBLIC_API void SetTimeResolutionUsec(uint32_t resolution, bool jitter);
+
+} // namespace JS
+
+#endif /* js_Date_h */
diff --git a/js/public/Debug.h b/js/public/Debug.h
new file mode 100644
index 0000000000..182b6f0558
--- /dev/null
+++ b/js/public/Debug.h
@@ -0,0 +1,349 @@
+/* -*- 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/. */
+
+// Interfaces by which the embedding can interact with the Debugger API.
+
+#ifndef js_Debug_h
+#define js_Debug_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
+
+#include "jsapi.h"
+#include "jspubtd.h"
+
+#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+
+namespace js {
+class Debugger;
+} // namespace js
+
+namespace JS {
+namespace dbg {
+
+// [SMDOC] Debugger builder API
+//
+// Helping embedding code build objects for Debugger
+// -------------------------------------------------
+//
+// Some Debugger API features lean on the embedding application to construct
+// their result values. For example, Debugger.Frame.prototype.scriptEntryReason
+// calls hooks provided by the embedding to construct values explaining why it
+// invoked JavaScript; if F is a frame called from a mouse click event handler,
+// F.scriptEntryReason would return an object of the form:
+//
+// { eventType: "mousedown", event: <object> }
+//
+// where <object> is a Debugger.Object whose referent is the event being
+// dispatched.
+//
+// However, Debugger implements a trust boundary. Debuggee code may be
+// considered untrusted; debugger code needs to be protected from debuggee
+// getters, setters, proxies, Object.watch watchpoints, and any other feature
+// that might accidentally cause debugger code to set the debuggee running. The
+// Debugger API tries to make it easy to write safe debugger code by only
+// offering access to debuggee objects via Debugger.Object instances, which
+// ensure that only those operations whose explicit purpose is to invoke
+// debuggee code do so. But this protective membrane is only helpful if we
+// interpose Debugger.Object instances in all the necessary spots.
+//
+// SpiderMonkey's compartment system also implements a trust boundary. The
+// debuggee and debugger are always in different compartments. Inter-compartment
+// work requires carefully tracking which compartment each JSObject or JS::Value
+// belongs to, and ensuring that is is correctly wrapped for each operation.
+//
+// It seems precarious to expect the embedding's hooks to implement these trust
+// boundaries. Instead, the JS::dbg::Builder API segregates the code which
+// constructs trusted objects from that which deals with untrusted objects.
+// Trusted objects have an entirely different C++ type, so code that improperly
+// mixes trusted and untrusted objects is caught at compile time.
+//
+// In the structure shown above, there are two trusted objects, and one
+// untrusted object:
+//
+// - The overall object, with the 'eventType' and 'event' properties, is a
+// trusted object. We're going to return it to D.F.p.scriptEntryReason's
+// caller, which will handle it directly.
+//
+// - The Debugger.Object instance appearing as the value of the 'event' property
+// is a trusted object. It belongs to the same Debugger instance as the
+// Debugger.Frame instance whose scriptEntryReason accessor was called, and
+// presents a safe reflection-oriented API for inspecting its referent, which
+// is:
+//
+// - The actual event object, an untrusted object, and the referent of the
+// Debugger.Object above. (Content can do things like replacing accessors on
+// Event.prototype.)
+//
+// Using JS::dbg::Builder, all objects and values the embedding deals with
+// directly are considered untrusted, and are assumed to be debuggee values. The
+// only way to construct trusted objects is to use Builder's own methods, which
+// return a separate Object type. The only way to set a property on a trusted
+// object is through that Object type. The actual trusted object is never
+// exposed to the embedding.
+//
+// So, for example, the embedding might use code like the following to construct
+// the object shown above, given a Builder passed to it by Debugger:
+//
+// bool
+// MyScriptEntryReason::explain(JSContext* cx,
+// Builder& builder,
+// Builder::Object& result)
+// {
+// JSObject* eventObject = ... obtain debuggee event object somehow ...;
+// if (!eventObject) {
+// return false;
+// }
+// result = builder.newObject(cx);
+// return result &&
+// result.defineProperty(cx, "eventType",
+// SafelyFetchType(eventObject)) &&
+// result.defineProperty(cx, "event", eventObject);
+// }
+//
+//
+// Object::defineProperty also accepts an Object as the value to store on the
+// property. By its type, we know that the value is trusted, so we set it
+// directly as the property's value, without interposing a Debugger.Object
+// wrapper. This allows the embedding to builted nested structures of trusted
+// objects.
+//
+// The Builder and Builder::Object methods take care of doing whatever
+// compartment switching and wrapping are necessary to construct the trusted
+// values in the Debugger's compartment.
+//
+// The Object type is self-rooting. Construction, assignment, and destruction
+// all properly root the referent object.
+
+class BuilderOrigin;
+
+class Builder {
+ // The Debugger instance whose client we are building a value for. We build
+ // objects in this object's compartment.
+ PersistentRootedObject debuggerObject;
+
+ // debuggerObject's Debugger structure, for convenience.
+ js::Debugger* debugger;
+
+ // Check that |thing| is in the same compartment as our debuggerObject. Used
+ // for assertions when constructing BuiltThings. We can overload this as we
+ // add more instantiations of BuiltThing.
+#if DEBUG
+ void assertBuilt(JSObject* obj);
+#else
+ void assertBuilt(JSObject* obj) {}
+#endif
+
+ protected:
+ // A reference to a trusted object or value. At the moment, we only use it
+ // with JSObject*.
+ template <typename T>
+ class BuiltThing {
+ friend class BuilderOrigin;
+
+ protected:
+ // The Builder to which this trusted thing belongs.
+ Builder& owner;
+
+ // A rooted reference to our value.
+ PersistentRooted<T> value;
+
+ BuiltThing(JSContext* cx, Builder& owner_,
+ T value_ = SafelyInitialized<T>())
+ : owner(owner_), value(cx, value_) {
+ owner.assertBuilt(value_);
+ }
+
+ // Forward some things from our owner, for convenience.
+ js::Debugger* debugger() const { return owner.debugger; }
+ JSObject* debuggerObject() const { return owner.debuggerObject; }
+
+ public:
+ BuiltThing(const BuiltThing& rhs) : owner(rhs.owner), value(rhs.value) {}
+ BuiltThing& operator=(const BuiltThing& rhs) {
+ MOZ_ASSERT(&owner == &rhs.owner);
+ owner.assertBuilt(rhs.value);
+ value = rhs.value;
+ return *this;
+ }
+
+ explicit operator bool() const {
+ // If we ever instantiate BuiltThing<Value>, this might not suffice.
+ return value;
+ }
+
+ private:
+ BuiltThing() = delete;
+ };
+
+ public:
+ // A reference to a trusted object, possibly null. Instances of Object are
+ // always properly rooted. They can be copied and assigned, as if they were
+ // pointers.
+ class Object : private BuiltThing<JSObject*> {
+ friend class Builder; // for construction
+ friend class BuilderOrigin; // for unwrapping
+
+ typedef BuiltThing<JSObject*> Base;
+
+ // This is private, because only Builders can create Objects that
+ // actually point to something (hence the 'friend' declaration).
+ Object(JSContext* cx, Builder& owner_, HandleObject obj)
+ : Base(cx, owner_, obj.get()) {}
+
+ bool definePropertyToTrusted(JSContext* cx, const char* name,
+ JS::MutableHandleValue value);
+
+ public:
+ Object(JSContext* cx, Builder& owner_) : Base(cx, owner_, nullptr) {}
+ Object(const Object& rhs) = default;
+
+ // Our automatically-generated assignment operator can see our base
+ // class's assignment operator, so we don't need to write one out here.
+
+ // Set the property named |name| on this object to |value|.
+ //
+ // If |value| is a string or primitive, re-wrap it for the debugger's
+ // compartment.
+ //
+ // If |value| is an object, assume it is a debuggee object and make a
+ // Debugger.Object instance referring to it. Set that as the propery's
+ // value.
+ //
+ // If |value| is another trusted object, store it directly as the
+ // property's value.
+ //
+ // On error, report the problem on cx and return false.
+ bool defineProperty(JSContext* cx, const char* name, JS::HandleValue value);
+ bool defineProperty(JSContext* cx, const char* name,
+ JS::HandleObject value);
+ bool defineProperty(JSContext* cx, const char* name, Object& value);
+
+ using Base::operator bool;
+ };
+
+ // Build an empty object for direct use by debugger code, owned by this
+ // Builder. If an error occurs, report it on cx and return a false Object.
+ Object newObject(JSContext* cx);
+
+ protected:
+ Builder(JSContext* cx, js::Debugger* debugger);
+};
+
+// Debugger itself instantiates this subclass of Builder, which can unwrap
+// BuiltThings that belong to it.
+class BuilderOrigin : public Builder {
+ template <typename T>
+ T unwrapAny(const BuiltThing<T>& thing) {
+ MOZ_ASSERT(&thing.owner == this);
+ return thing.value.get();
+ }
+
+ public:
+ BuilderOrigin(JSContext* cx, js::Debugger* debugger_)
+ : Builder(cx, debugger_) {}
+
+ JSObject* unwrap(Object& object) { return unwrapAny(object); }
+};
+
+// Finding the size of blocks allocated with malloc
+// ------------------------------------------------
+//
+// Debugger.Memory wants to be able to report how many bytes items in memory are
+// consuming. To do this, it needs a function that accepts a pointer to a block,
+// and returns the number of bytes allocated to that block. SpiderMonkey itself
+// doesn't know which function is appropriate to use, but the embedding does.
+
+// Tell Debuggers in |cx| to use |mallocSizeOf| to find the size of
+// malloc'd blocks.
+JS_PUBLIC_API void SetDebuggerMallocSizeOf(JSContext* cx,
+ mozilla::MallocSizeOf mallocSizeOf);
+
+// Get the MallocSizeOf function that the given context is using to find the
+// size of malloc'd blocks.
+JS_PUBLIC_API mozilla::MallocSizeOf GetDebuggerMallocSizeOf(JSContext* cx);
+
+// Debugger and Garbage Collection Events
+// --------------------------------------
+//
+// The Debugger wants to report about its debuggees' GC cycles, however entering
+// JS after a GC is troublesome since SpiderMonkey will often do something like
+// force a GC and then rely on the nursery being empty. If we call into some
+// Debugger's hook after the GC, then JS runs and the nursery won't be
+// empty. Instead, we rely on embedders to call back into SpiderMonkey after a
+// GC and notify Debuggers to call their onGarbageCollection hook.
+
+// Determine whether it's necessary to call FireOnGarbageCollectionHook() after
+// a GC. This is only required if there are debuggers with an
+// onGarbageCollection hook observing a global in the set of collected zones.
+JS_PUBLIC_API bool FireOnGarbageCollectionHookRequired(JSContext* cx);
+
+// For each Debugger that observed a debuggee involved in the given GC event,
+// call its `onGarbageCollection` hook.
+JS_PUBLIC_API bool FireOnGarbageCollectionHook(
+ JSContext* cx, GarbageCollectionEvent::Ptr&& data);
+
+// Return true if the given value is a Debugger object, false otherwise.
+JS_PUBLIC_API bool IsDebugger(JSObject& obj);
+
+// Append each of the debuggee global objects observed by the Debugger object
+// |dbgObj| to |vector|. Returns true on success, false on failure.
+JS_PUBLIC_API bool GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj,
+ MutableHandleObjectVector vector);
+
+// Hooks for reporting where JavaScript execution began.
+//
+// Our performance tools would like to be able to label blocks of JavaScript
+// execution with the function name and source location where execution began:
+// the event handler, the callback, etc.
+//
+// Construct an instance of this class on the stack, providing a JSContext
+// belonging to the runtime in which execution will occur. Each time we enter
+// JavaScript --- specifically, each time we push a JavaScript stack frame that
+// has no older JS frames younger than this AutoEntryMonitor --- we will
+// call the appropriate |Entry| member function to indicate where we've begun
+// execution.
+
+class MOZ_STACK_CLASS JS_PUBLIC_API AutoEntryMonitor {
+ JSContext* cx_;
+ AutoEntryMonitor* savedMonitor_;
+
+ public:
+ explicit AutoEntryMonitor(JSContext* cx);
+ ~AutoEntryMonitor();
+
+ // SpiderMonkey reports the JavaScript entry points occuring within this
+ // AutoEntryMonitor's scope to the following member functions, which the
+ // embedding is expected to override.
+ //
+ // It is important to note that |asyncCause| is owned by the caller and its
+ // lifetime must outlive the lifetime of the AutoEntryMonitor object. It is
+ // strongly encouraged that |asyncCause| be a string constant or similar
+ // statically allocated string.
+
+ // We have begun executing |function|. Note that |function| may not be the
+ // actual closure we are running, but only the canonical function object to
+ // which the script refers.
+ virtual void Entry(JSContext* cx, JSFunction* function,
+ HandleValue asyncStack, const char* asyncCause) = 0;
+
+ // Execution has begun at the entry point of |script|, which is not a
+ // function body. (This is probably being executed by 'eval' or some
+ // JSAPI equivalent.)
+ virtual void Entry(JSContext* cx, JSScript* script, HandleValue asyncStack,
+ const char* asyncCause) = 0;
+
+ // Execution of the function or script has ended.
+ virtual void Exit(JSContext* cx) {}
+};
+
+} // namespace dbg
+} // namespace JS
+
+#endif /* js_Debug_h */
diff --git a/js/public/Equality.h b/js/public/Equality.h
new file mode 100644
index 0000000000..b3123682e0
--- /dev/null
+++ b/js/public/Equality.h
@@ -0,0 +1,66 @@
+/* -*- 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 "mozilla/FloatingPoint.h"
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API 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);
+
+/**
+ * Implements |SameValueZero(v1, v2)| for Number values |v1| and |v2|.
+ * SameValueZero equates NaNs, equal nonzero values, and zeroes without respect
+ * to their signs.
+ */
+static inline bool SameValueZero(double v1, double v2) {
+ return mozilla::EqualOrBothNaN(v1, v2);
+}
+
+} // namespace JS
+
+#endif /* js_Equality_h */
diff --git a/js/public/ErrorReport.h b/js/public/ErrorReport.h
new file mode 100644
index 0000000000..a53e348c04
--- /dev/null
+++ b/js/public/ErrorReport.h
@@ -0,0 +1,361 @@
+/* -*- 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/. */
+
+/*
+ * Error-reporting types and structures.
+ *
+ * Despite these types and structures existing in js/public, significant parts
+ * of their heritage date back to distant SpiderMonkey past, and they are not
+ * all universally well-thought-out as ideal, intended-to-be-permanent API.
+ * We may eventually replace this with something more consistent with
+ * ECMAScript the language and less consistent with '90s-era JSAPI inventions,
+ * but it's doubtful this will happen any time soon.
+ */
+
+#ifndef js_ErrorReport_h
+#define js_ErrorReport_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include <iterator> // std::input_iterator_tag, std::iterator
+#include <stddef.h> // size_t
+#include <stdint.h> // int16_t, uint16_t
+#include <string.h> // strlen
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/AllocPolicy.h" // js::SystemAllocPolicy
+#include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
+#include "js/Exception.h" // JS::ExceptionStack
+#include "js/RootingAPI.h" // JS::HandleObject, JS::RootedObject
+#include "js/UniquePtr.h" // js::UniquePtr
+#include "js/Vector.h" // js::Vector
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSString;
+
+/**
+ * Possible exception types. These types are part of a JSErrorFormatString
+ * structure. They define which error to throw in case of a runtime error.
+ *
+ * JSEXN_WARN is used for warnings, that are not strictly errors but are handled
+ * using the generalized error reporting mechanism. (One side effect of this
+ * type is to not prepend 'Error:' to warning messages.) This value can go away
+ * if we ever decide to use an entirely separate mechanism for warnings.
+ */
+enum JSExnType {
+ JSEXN_ERR,
+ JSEXN_FIRST = JSEXN_ERR,
+ JSEXN_INTERNALERR,
+ JSEXN_AGGREGATEERR,
+ JSEXN_EVALERR,
+ JSEXN_RANGEERR,
+ JSEXN_REFERENCEERR,
+ JSEXN_SYNTAXERR,
+ JSEXN_TYPEERR,
+ JSEXN_URIERR,
+ JSEXN_DEBUGGEEWOULDRUN,
+ JSEXN_WASMCOMPILEERROR,
+ JSEXN_WASMLINKERROR,
+ JSEXN_WASMRUNTIMEERROR,
+ JSEXN_ERROR_LIMIT,
+ JSEXN_WARN = JSEXN_ERROR_LIMIT,
+ JSEXN_NOTE,
+ JSEXN_LIMIT
+};
+
+struct JSErrorFormatString {
+ /** The error message name in ASCII. */
+ const char* name;
+
+ /** The error format string in ASCII. */
+ const char* format;
+
+ /** The number of arguments to expand in the formatted error message. */
+ uint16_t argCount;
+
+ /** One of the JSExnType constants above. */
+ int16_t exnType;
+};
+
+using JSErrorCallback =
+ const JSErrorFormatString* (*)(void* userRef, const unsigned errorNumber);
+
+/**
+ * Base class that implements parts shared by JSErrorReport and
+ * JSErrorNotes::Note.
+ */
+class JSErrorBase {
+ private:
+ // The (default) error message.
+ // If ownsMessage_ is true, the it is freed in destructor.
+ JS::ConstUTF8CharsZ message_;
+
+ public:
+ // Source file name, URL, etc., or null.
+ const char* filename;
+
+ // Unique identifier for the script source.
+ unsigned sourceId;
+
+ // Source line number.
+ unsigned lineno;
+
+ // Zero-based column index in line.
+ unsigned column;
+
+ // the error number, e.g. see js/public/friend/ErrorNumbers.msg.
+ unsigned errorNumber;
+
+ // Points to JSErrorFormatString::name.
+ // This string must never be freed.
+ const char* errorMessageName;
+
+ private:
+ bool ownsMessage_ : 1;
+
+ public:
+ JSErrorBase()
+ : filename(nullptr),
+ sourceId(0),
+ lineno(0),
+ column(0),
+ errorNumber(0),
+ errorMessageName(nullptr),
+ ownsMessage_(false) {}
+
+ ~JSErrorBase() { freeMessage(); }
+
+ public:
+ const JS::ConstUTF8CharsZ message() const { return message_; }
+
+ void initOwnedMessage(const char* messageArg) {
+ initBorrowedMessage(messageArg);
+ ownsMessage_ = true;
+ }
+ void initBorrowedMessage(const char* messageArg) {
+ MOZ_ASSERT(!message_);
+ message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
+ }
+
+ JSString* newMessageString(JSContext* cx);
+
+ private:
+ void freeMessage();
+};
+
+/**
+ * Notes associated with JSErrorReport.
+ */
+class JSErrorNotes {
+ public:
+ class Note final : public JSErrorBase {};
+
+ private:
+ // Stores pointers to each note.
+ js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_;
+
+ public:
+ JSErrorNotes();
+ ~JSErrorNotes();
+
+ // Add an note to the given position.
+ bool addNoteASCII(JSContext* cx, const char* filename, unsigned sourceId,
+ unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteLatin1(JSContext* cx, const char* filename, unsigned sourceId,
+ unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteUTF8(JSContext* cx, const char* filename, unsigned sourceId,
+ unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+
+ JS_PUBLIC_API size_t length();
+
+ // Create a deep copy of notes.
+ js::UniquePtr<JSErrorNotes> copy(JSContext* cx);
+
+ class iterator final {
+ private:
+ js::UniquePtr<Note>* note_;
+
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = js::UniquePtr<Note>;
+ using difference_type = ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note) {}
+
+ bool operator==(iterator other) const { return note_ == other.note_; }
+ bool operator!=(iterator other) const { return !(*this == other); }
+ iterator& operator++() {
+ note_++;
+ return *this;
+ }
+ reference operator*() { return *note_; }
+ };
+
+ JS_PUBLIC_API iterator begin();
+ JS_PUBLIC_API iterator end();
+};
+
+/**
+ * Describes a single error or warning that occurs in the execution of script.
+ */
+class JSErrorReport : public JSErrorBase {
+ private:
+ // Offending source line without final '\n'.
+ // If ownsLinebuf_ is true, the buffer is freed in destructor.
+ const char16_t* linebuf_;
+
+ // Number of chars in linebuf_. Does not include trailing '\0'.
+ size_t linebufLength_;
+
+ // The 0-based offset of error token in linebuf_.
+ size_t tokenOffset_;
+
+ public:
+ // Associated notes, or nullptr if there's no note.
+ js::UniquePtr<JSErrorNotes> notes;
+
+ // One of the JSExnType constants.
+ int16_t exnType;
+
+ // See the comment in TransitiveCompileOptions.
+ bool isMuted : 1;
+
+ // This error report is actually a warning.
+ bool isWarning_ : 1;
+
+ private:
+ bool ownsLinebuf_ : 1;
+
+ public:
+ JSErrorReport()
+ : linebuf_(nullptr),
+ linebufLength_(0),
+ tokenOffset_(0),
+ notes(nullptr),
+ exnType(0),
+ isMuted(false),
+ isWarning_(false),
+ ownsLinebuf_(false) {}
+
+ ~JSErrorReport() { freeLinebuf(); }
+
+ public:
+ const char16_t* linebuf() const { return linebuf_; }
+ size_t linebufLength() const { return linebufLength_; }
+ size_t tokenOffset() const { return tokenOffset_; }
+ void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+ size_t tokenOffsetArg) {
+ initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg);
+ ownsLinebuf_ = true;
+ }
+ void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+ size_t tokenOffsetArg);
+
+ bool isWarning() const { return isWarning_; }
+
+ private:
+ void freeLinebuf();
+};
+
+namespace JS {
+
+struct MOZ_STACK_CLASS JS_PUBLIC_API ErrorReportBuilder {
+ explicit ErrorReportBuilder(JSContext* cx);
+ ~ErrorReportBuilder();
+
+ enum SniffingBehavior { WithSideEffects, NoSideEffects };
+
+ /**
+ * Generate a JSErrorReport from the provided thrown value.
+ *
+ * If the value is a (possibly wrapped) Error object, the JSErrorReport will
+ * be exactly initialized from the Error object's information, without
+ * observable side effects. (The Error object's JSErrorReport is reused, if
+ * it has one.)
+ *
+ * Otherwise various attempts are made to derive JSErrorReport information
+ * from |exnStack| and from the current execution state. This process is
+ * *definitely* inconsistent with any standard, and particulars of the
+ * behavior implemented here generally shouldn't be relied upon.
+ *
+ * If the value of |sniffingBehavior| is |WithSideEffects|, some of these
+ * attempts *may* invoke user-configurable behavior when the exception is an
+ * object: converting it to a string, detecting and getting its properties,
+ * accessing its prototype chain, and others are possible. Users *must*
+ * tolerate |ErrorReportBuilder::init| potentially having arbitrary effects.
+ * Any exceptions thrown by these operations will be caught and silently
+ * ignored, and "default" values will be substituted into the JSErrorReport.
+ *
+ * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts
+ * *will not* invoke any observable side effects. The JSErrorReport will
+ * simply contain fewer, less precise details.
+ *
+ * Unlike some functions involved in error handling, this function adheres
+ * to the usual JSAPI return value error behavior.
+ */
+ bool init(JSContext* cx, const JS::ExceptionStack& exnStack,
+ SniffingBehavior sniffingBehavior);
+
+ JSErrorReport* report() const { return reportp; }
+
+ const JS::ConstUTF8CharsZ toStringResult() const { return toStringResult_; }
+
+ private:
+ // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA
+ // but fills in an ErrorReport instead of reporting it. Uses varargs to
+ // make it simpler to call js::ExpandErrorArgumentsVA.
+ //
+ // Returns false if we fail to actually populate the ErrorReport
+ // for some reason (probably out of memory).
+ bool populateUncaughtExceptionReportUTF8(JSContext* cx,
+ JS::HandleObject stack, ...);
+ bool populateUncaughtExceptionReportUTF8VA(JSContext* cx,
+ JS::HandleObject stack,
+ va_list ap);
+
+ // Reports exceptions from add-on scopes to telemetry.
+ void ReportAddonExceptionToTelemetry(JSContext* cx);
+
+ // We may have a provided JSErrorReport, so need a way to represent that.
+ JSErrorReport* reportp;
+
+ // Or we may need to synthesize a JSErrorReport one of our own.
+ JSErrorReport ownedReport;
+
+ // Root our exception value to keep a possibly borrowed |reportp| alive.
+ JS::RootedObject exnObject;
+
+ // And for our filename.
+ JS::UniqueChars filename;
+
+ // We may have a result of error.toString().
+ // FIXME: We should not call error.toString(), since it could have side
+ // effect (see bug 633623).
+ JS::ConstUTF8CharsZ toStringResult_;
+ JS::UniqueChars toStringResultBytesStorage;
+};
+
+// Writes a full report to a file descriptor. Does nothing for JSErrorReports
+// which are warnings, unless reportWarnings is set.
+extern JS_PUBLIC_API void PrintError(JSContext* cx, FILE* file,
+ JSErrorReport* report,
+ bool reportWarnings);
+
+extern JS_PUBLIC_API void PrintError(JSContext* cx, FILE* file,
+ const JS::ErrorReportBuilder& builder,
+ bool reportWarnings);
+
+} // namespace JS
+
+#endif /* js_ErrorReport_h */
diff --git a/js/public/Exception.h b/js/public/Exception.h
new file mode 100644
index 0000000000..f81f4780eb
--- /dev/null
+++ b/js/public/Exception.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Exception_h
+#define js_Exception_h
+
+#include "mozilla/Attributes.h"
+
+#include "jspubtd.h"
+#include "js/RootingAPI.h" // JS::{Handle,Rooted}
+#include "js/Value.h" // JS::Value, JS::Handle<JS::Value>
+
+namespace JS {
+
+// This class encapsulates a (pending) exception and the corresponding optional
+// SavedFrame stack object captured when the pending exception was set
+// on the JSContext. This fuzzily correlates with a `throw` statement in JS,
+// although arbitrary JSAPI consumers or VM code may also set pending exceptions
+// via `JS_SetPendingException`.
+//
+// This is not the same stack as `e.stack` when `e` is an `Error` object.
+// (That would be JS::ExceptionStackOrNull).
+class MOZ_STACK_CLASS ExceptionStack {
+ Rooted<Value> exception_;
+ Rooted<JSObject*> stack_;
+
+ friend JS_PUBLIC_API bool GetPendingExceptionStack(
+ JSContext* cx, JS::ExceptionStack* exceptionStack);
+
+ void init(HandleValue exception, HandleObject stack) {
+ exception_ = exception;
+ stack_ = stack;
+ }
+
+ public:
+ explicit ExceptionStack(JSContext* cx) : exception_(cx), stack_(cx) {}
+
+ ExceptionStack(JSContext* cx, HandleValue exception, HandleObject stack)
+ : exception_(cx, exception), stack_(cx, stack) {}
+
+ HandleValue exception() const { return exception_; }
+
+ // |stack| can be null.
+ HandleObject stack() const { return stack_; }
+};
+
+// Get the current pending exception value and stack.
+// This function asserts that there is a pending exception.
+// If this function returns false, then retrieving the current pending exception
+// failed and might have been overwritten by a new exception.
+extern JS_PUBLIC_API bool GetPendingExceptionStack(
+ JSContext* cx, JS::ExceptionStack* exceptionStack);
+
+// Similar to GetPendingExceptionStack, but also clears the current
+// pending exception.
+extern JS_PUBLIC_API bool StealPendingExceptionStack(
+ JSContext* cx, JS::ExceptionStack* exceptionStack);
+
+// Set both the exception value and its associated stack on the context as
+// the current pending exception.
+extern JS_PUBLIC_API void SetPendingExceptionStack(
+ JSContext* cx, const JS::ExceptionStack& exceptionStack);
+
+} // namespace JS
+
+#endif // js_Exception_h
diff --git a/js/public/ForOfIterator.h b/js/public/ForOfIterator.h
new file mode 100644
index 0000000000..ab65738c69
--- /dev/null
+++ b/js/public/ForOfIterator.h
@@ -0,0 +1,118 @@
+/* -*- 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/. */
+
+/*
+ * A convenience class that makes it easy to perform the operations of a for-of
+ * loop.
+ */
+
+#ifndef js_ForOfIterator_h
+#define js_ForOfIterator_h
+
+#include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS
+
+#include <stdint.h> // UINT32_MAX, uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::{Handle,Rooted}
+#include "js/Value.h" // JS::Value, JS::{,Mutable}Handle<JS::Value>
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+/**
+ * A convenience class for imitating a JS for-of loop. Typical usage:
+ *
+ * JS::ForOfIterator it(cx);
+ * if (!it.init(iterable)) {
+ * return false;
+ * }
+ * JS::Rooted<JS::Value> val(cx);
+ * while (true) {
+ * bool done;
+ * if (!it.next(&val, &done)) {
+ * return false;
+ * }
+ * if (done) {
+ * break;
+ * }
+ * if (!DoStuff(cx, val)) {
+ * return false;
+ * }
+ * }
+ */
+class MOZ_STACK_CLASS JS_PUBLIC_API ForOfIterator {
+ protected:
+ JSContext* cx_;
+
+ // Use the ForOfPIC on the global object (see vm/GlobalObject.h) to try to
+ // optimize iteration across arrays.
+ //
+ // Case 1: Regular Iteration
+ // iterator - pointer to the iterator object.
+ // nextMethod - value of |iterator|.next.
+ // index - fixed to NOT_ARRAY (== UINT32_MAX)
+ //
+ // Case 2: Optimized Array Iteration
+ // iterator - pointer to the array object.
+ // nextMethod - the undefined value.
+ // index - current position in array.
+ //
+ // The cases are distinguished by whether |index == NOT_ARRAY|.
+ Rooted<JSObject*> iterator;
+ Rooted<Value> nextMethod;
+
+ static constexpr uint32_t NOT_ARRAY = UINT32_MAX;
+
+ uint32_t index = NOT_ARRAY;
+
+ ForOfIterator(const ForOfIterator&) = delete;
+ ForOfIterator& operator=(const ForOfIterator&) = delete;
+
+ public:
+ explicit ForOfIterator(JSContext* cx)
+ : cx_(cx), iterator(cx), nextMethod(cx) {}
+
+ enum NonIterableBehavior { ThrowOnNonIterable, AllowNonIterable };
+
+ /**
+ * Initialize the iterator. If AllowNonIterable is passed then if getting
+ * the @@iterator property from iterable returns undefined init() will just
+ * return true instead of throwing. Callers must then check
+ * valueIsIterable() before continuing with the iteration.
+ */
+ MOZ_MUST_USE bool init(
+ Handle<Value> iterable,
+ NonIterableBehavior nonIterableBehavior = ThrowOnNonIterable);
+
+ /**
+ * Get the next value from the iterator. If false *done is true
+ * after this call, do not examine val.
+ */
+ MOZ_MUST_USE bool next(MutableHandle<Value> val, bool* done);
+
+ /**
+ * Close the iterator.
+ * For the case that completion type is throw.
+ */
+ void closeThrow();
+
+ /**
+ * If initialized with throwOnNonCallable = false, check whether
+ * the value is iterable.
+ */
+ bool valueIsIterable() const { return iterator; }
+
+ private:
+ inline bool nextFromOptimizedArray(MutableHandle<Value> val, bool* done);
+};
+
+} // namespace JS
+
+#endif // js_ForOfIterator_h
diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h
new file mode 100644
index 0000000000..e2211f13c9
--- /dev/null
+++ b/js/public/GCAPI.h
@@ -0,0 +1,1182 @@
+/* -*- 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/. */
+
+/*
+ * High-level interface to the JS garbage collector.
+ */
+
+#ifndef js_GCAPI_h
+#define js_GCAPI_h
+
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Vector.h"
+
+#include "js/GCAnnotations.h"
+#include "js/TypeDecls.h"
+#include "js/UniquePtr.h"
+#include "js/Utility.h"
+
+class JS_PUBLIC_API JSTracer;
+
+namespace js {
+namespace gc {
+class GCRuntime;
+} // namespace gc
+namespace gcstats {
+struct Statistics;
+} // namespace gcstats
+} // namespace js
+
+/**
+ * Kinds of js_GC invocation.
+ */
+typedef enum JSGCInvocationKind {
+ /* Normal invocation. */
+ GC_NORMAL = 0,
+
+ /* Minimize GC triggers and release empty GC chunks right away. */
+ GC_SHRINK = 1
+} JSGCInvocationKind;
+
+typedef enum JSGCParamKey {
+ /**
+ * Maximum nominal heap before last ditch GC.
+ *
+ * Soft limit on the number of bytes we are allowed to allocate in the GC
+ * heap. Attempts to allocate gcthings over this limit will return null and
+ * subsequently invoke the standard OOM machinery, independent of available
+ * physical memory.
+ *
+ * Pref: javascript.options.mem.max
+ * Default: 0xffffffff
+ */
+ JSGC_MAX_BYTES = 0,
+
+ /**
+ * Maximum size of the generational GC nurseries.
+ *
+ * This will be rounded to the nearest gc::ChunkSize.
+ *
+ * Pref: javascript.options.mem.nursery.max_kb
+ * Default: JS::DefaultNurseryMaxBytes
+ */
+ JSGC_MAX_NURSERY_BYTES = 2,
+
+ /** Amount of bytes allocated by the GC. */
+ JSGC_BYTES = 3,
+
+ /** Number of times GC has been invoked. Includes both major and minor GC. */
+ JSGC_NUMBER = 4,
+
+ /**
+ * Whether incremental GC is enabled. If not, GC will always run to
+ * completion.
+ *
+ * prefs: javascript.options.mem.gc_incremental.
+ * Default: false
+ */
+ JSGC_INCREMENTAL_GC_ENABLED = 5,
+
+ /**
+ * Whether per-zone GC is enabled. If not, all zones are collected every time.
+ *
+ * prefs: javascript.options.mem.gc_per_zone
+ * Default: false
+ */
+ JSGC_PER_ZONE_GC_ENABLED = 6,
+
+ /** Number of cached empty GC chunks. */
+ JSGC_UNUSED_CHUNKS = 7,
+
+ /** Total number of allocated GC chunks. */
+ JSGC_TOTAL_CHUNKS = 8,
+
+ /**
+ * Max milliseconds to spend in an incremental GC slice.
+ *
+ * Pref: javascript.options.mem.gc_incremental_slice_ms
+ * Default: DefaultTimeBudgetMS.
+ */
+ JSGC_SLICE_TIME_BUDGET_MS = 9,
+
+ /**
+ * Maximum size the GC mark stack can grow to.
+ *
+ * Pref: none
+ * Default: MarkStack::DefaultCapacity
+ */
+ JSGC_MARK_STACK_LIMIT = 10,
+
+ /**
+ * The "do we collect?" decision depends on various parameters and can be
+ * summarised as:
+ *
+ * ZoneSize > Max(ThresholdBase, LastSize) * GrowthFactor * ThresholdFactor
+ *
+ * Where
+ * ZoneSize: Current size of this zone.
+ * LastSize: Heap size immediately after the most recent collection.
+ * ThresholdBase: The JSGC_ALLOCATION_THRESHOLD parameter
+ * GrowthFactor: A number above 1, calculated based on some of the
+ * following parameters.
+ * See computeZoneHeapGrowthFactorForHeapSize() in GC.cpp
+ * ThresholdFactor: 1.0 to trigger an incremental collections or between
+ * JSGC_SMALL_HEAP_INCREMENTAL_LIMIT and
+ * JSGC_LARGE_HEAP_INCREMENTAL_LIMIT to trigger a
+ * non-incremental collection.
+ *
+ * The RHS of the equation above is calculated and sets
+ * zone->gcHeapThreshold.bytes(). When gcHeapSize.bytes() exeeds
+ * gcHeapThreshold.bytes() for a zone, the zone may be scheduled for a GC.
+ */
+
+ /**
+ * GCs less than this far apart in milliseconds will be considered
+ * 'high-frequency GCs'.
+ *
+ * Pref: javascript.options.mem.gc_high_frequency_time_limit_ms
+ * Default: HighFrequencyThreshold
+ */
+ JSGC_HIGH_FREQUENCY_TIME_LIMIT = 11,
+
+ /**
+ * Upper limit for classifying a heap as small (MB).
+ *
+ * Dynamic heap growth thresholds are based on whether the heap is small,
+ * medium or large. Heaps smaller than this size are classified as small;
+ * larger heaps are classified as medium or large.
+ *
+ * Pref: javascript.options.mem.gc_small_heap_size_max_mb
+ * Default: SmallHeapSizeMaxBytes
+ */
+ JSGC_SMALL_HEAP_SIZE_MAX = 12,
+
+ /**
+ * Lower limit for classifying a heap as large (MB).
+ *
+ * Dynamic heap growth thresholds are based on whether the heap is small,
+ * medium or large. Heaps larger than this size are classified as large;
+ * smaller heaps are classified as small or medium.
+ *
+ * Pref: javascript.options.mem.gc_large_heap_size_min_mb
+ * Default: LargeHeapSizeMinBytes
+ */
+ JSGC_LARGE_HEAP_SIZE_MIN = 13,
+
+ /**
+ * Heap growth factor for small heaps in the high-frequency GC state.
+ *
+ * Pref: javascript.options.mem.gc_high_frequency_small_heap_growth
+ * Default: HighFrequencySmallHeapGrowth
+ */
+ JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH = 14,
+
+ /**
+ * Heap growth factor for large heaps in the high-frequency GC state.
+ *
+ * Pref: javascript.options.mem.gc_high_frequency_large_heap_growth
+ * Default: HighFrequencyLargeHeapGrowth
+ */
+ JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH = 15,
+
+ /**
+ * Heap growth factor for low frequency GCs.
+ *
+ * This factor is applied regardless of the size of the heap when not in the
+ * high-frequency GC state.
+ *
+ * Pref: javascript.options.mem.gc_low_frequency_heap_growth
+ * Default: LowFrequencyHeapGrowth
+ */
+ JSGC_LOW_FREQUENCY_HEAP_GROWTH = 16,
+
+ /**
+ * Lower limit for collecting a zone.
+ *
+ * Zones smaller than this size will not normally be collected.
+ *
+ * Pref: javascript.options.mem.gc_allocation_threshold_mb
+ * Default GCZoneAllocThresholdBase
+ */
+ JSGC_ALLOCATION_THRESHOLD = 19,
+
+ /**
+ * We try to keep at least this many unused chunks in the free chunk pool at
+ * all times, even after a shrinking GC.
+ *
+ * Pref: javascript.options.mem.gc_min_empty_chunk_count
+ * Default: MinEmptyChunkCount
+ */
+ JSGC_MIN_EMPTY_CHUNK_COUNT = 21,
+
+ /**
+ * We never keep more than this many unused chunks in the free chunk
+ * pool.
+ *
+ * Pref: javascript.options.mem.gc_min_empty_chunk_count
+ * Default: MinEmptyChunkCount
+ */
+ JSGC_MAX_EMPTY_CHUNK_COUNT = 22,
+
+ /**
+ * Whether compacting GC is enabled.
+ *
+ * Pref: javascript.options.mem.gc_compacting
+ * Default: CompactingEnabled
+ */
+ JSGC_COMPACTING_ENABLED = 23,
+
+ /**
+ * Limit of how far over the incremental trigger threshold we allow the heap
+ * to grow before finishing a collection non-incrementally, for small heaps.
+ *
+ * We trigger an incremental GC when a trigger threshold is reached but the
+ * collection may not be fast enough to keep up with the mutator. At some
+ * point we finish the collection non-incrementally.
+ *
+ * Default: SmallHeapIncrementalLimit
+ * Pref: javascript.options.mem.gc_small_heap_incremental_limit
+ */
+ JSGC_SMALL_HEAP_INCREMENTAL_LIMIT = 25,
+
+ /**
+ * Limit of how far over the incremental trigger threshold we allow the heap
+ * to grow before finishing a collection non-incrementally, for large heaps.
+ *
+ * Default: LargeHeapIncrementalLimit
+ * Pref: javascript.options.mem.gc_large_heap_incremental_limit
+ */
+ JSGC_LARGE_HEAP_INCREMENTAL_LIMIT = 26,
+
+ /**
+ * Attempt to run a minor GC in the idle time if the free space falls
+ * below this number of bytes.
+ *
+ * Default: NurseryChunkUsableSize / 4
+ * Pref: None
+ */
+ JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION = 27,
+
+ /**
+ * If this percentage of the nursery is tenured and the nursery is at least
+ * 4MB, then proceed to examine which groups we should pretenure.
+ *
+ * Default: PretenureThreshold
+ * Pref: None
+ */
+ JSGC_PRETENURE_THRESHOLD = 28,
+
+ /**
+ * If the above condition is met, then any object group that tenures more than
+ * this number of objects will be pretenured (if it can be).
+ *
+ * Default: PretenureGroupThreshold
+ * Pref: None
+ */
+ JSGC_PRETENURE_GROUP_THRESHOLD = 29,
+
+ /**
+ * Attempt to run a minor GC in the idle time if the free space falls
+ * below this percentage (from 0 to 99).
+ *
+ * Default: 25
+ * Pref: None
+ */
+ JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT = 30,
+
+ /**
+ * Minimum size of the generational GC nurseries.
+ *
+ * This value will be rounded to the nearest Nursery::SubChunkStep if below
+ * gc::ChunkSize, otherwise it'll be rounded to the nearest gc::ChunkSize.
+ *
+ * Default: Nursery::SubChunkLimit
+ * Pref: javascript.options.mem.nursery.min_kb
+ */
+ JSGC_MIN_NURSERY_BYTES = 31,
+
+ /**
+ * The minimum time to allow between triggering last ditch GCs in seconds.
+ *
+ * Default: 60 seconds
+ * Pref: None
+ */
+ JSGC_MIN_LAST_DITCH_GC_PERIOD = 32,
+
+ /**
+ * The delay (in heapsize kilobytes) between slices of an incremental GC.
+ *
+ * Default: ZoneAllocDelayBytes
+ */
+ JSGC_ZONE_ALLOC_DELAY_KB = 33,
+
+ /*
+ * The current size of the nursery.
+ *
+ * This parameter is read-only.
+ */
+ JSGC_NURSERY_BYTES = 34,
+
+ /**
+ * Retained size base value for calculating malloc heap threshold.
+ *
+ * Default: MallocThresholdBase
+ */
+ JSGC_MALLOC_THRESHOLD_BASE = 35,
+
+ /**
+ * Growth factor for calculating malloc heap threshold.
+ *
+ * Default: MallocGrowthFactor
+ */
+ JSGC_MALLOC_GROWTH_FACTOR = 36,
+
+ /**
+ * Whether incremental weakmap marking is enabled.
+ *
+ * Pref: javascript.options.mem.incremental_weakmap
+ * Default: IncrementalWeakMarkEnabled
+ */
+ JSGC_INCREMENTAL_WEAKMAP_ENABLED = 37,
+
+ /**
+ * The chunk size in bytes for this system.
+ *
+ * This parameter is read-only.
+ */
+ JSGC_CHUNK_BYTES = 38,
+
+ /**
+ * The number of background threads to use for parallel GC work for each CPU
+ * core, expressed as an integer percentage.
+ *
+ * Pref: javascript.options.mem.gc_helper_thread_ratio
+ */
+ JSGC_HELPER_THREAD_RATIO = 39,
+
+ /**
+ * The maximum number of background threads to use for parallel GC work.
+ *
+ * Pref: javascript.options.mem.gc_max_helper_threads
+ */
+ JSGC_MAX_HELPER_THREADS = 40,
+
+ /**
+ * The number of background threads to use for parallel GC work.
+ *
+ * This parameter is read-only and is set based on the
+ * JSGC_HELPER_THREAD_RATIO and JSGC_MAX_HELPER_THREADS parameters.
+ */
+ JSGC_HELPER_THREAD_COUNT = 41,
+
+ /**
+ * If the percentage of the tenured strings exceeds this threshold, string
+ * will be allocated in tenured heap instead. (Default is allocated in
+ * nursery.)
+ */
+ JSGC_PRETENURE_STRING_THRESHOLD = 42,
+
+ /**
+ * If the finalization rate of the tenured strings exceeds this threshold,
+ * string will be allocated in nursery.
+ */
+ JSGC_STOP_PRETENURE_STRING_THRESHOLD = 43,
+
+ /**
+ * A number that is incremented on every major GC slice.
+ */
+ JSGC_MAJOR_GC_NUMBER = 44,
+
+ /**
+ * A number that is incremented on every minor GC.
+ */
+ JSGC_MINOR_GC_NUMBER = 45,
+} JSGCParamKey;
+
+/*
+ * Generic trace operation that calls JS::TraceEdge on each traceable thing's
+ * location reachable from data.
+ */
+typedef void (*JSTraceDataOp)(JSTracer* trc, void* data);
+
+typedef enum JSGCStatus { JSGC_BEGIN, JSGC_END } JSGCStatus;
+
+typedef void (*JSObjectsTenuredCallback)(JSContext* cx, void* data);
+
+typedef enum JSFinalizeStatus {
+ /**
+ * Called when preparing to sweep a group of zones, before anything has been
+ * swept. The collector will not yield to the mutator before calling the
+ * callback with JSFINALIZE_GROUP_START status.
+ */
+ JSFINALIZE_GROUP_PREPARE,
+
+ /**
+ * Called after preparing to sweep a group of zones. Weak references to
+ * unmarked things have been removed at this point, but no GC things have
+ * been swept. The collector may yield to the mutator after this point.
+ */
+ JSFINALIZE_GROUP_START,
+
+ /**
+ * Called after sweeping a group of zones. All dead GC things have been
+ * swept at this point.
+ */
+ JSFINALIZE_GROUP_END,
+
+ /**
+ * Called at the end of collection when everything has been swept.
+ */
+ JSFINALIZE_COLLECTION_END
+} JSFinalizeStatus;
+
+typedef void (*JSFinalizeCallback)(JSFreeOp* fop, JSFinalizeStatus status,
+ void* data);
+
+typedef void (*JSWeakPointerZonesCallback)(JSContext* cx, void* data);
+
+typedef void (*JSWeakPointerCompartmentCallback)(JSContext* cx,
+ JS::Compartment* comp,
+ void* data);
+
+/*
+ * This is called to tell the embedding that a FinalizationRegistry object has
+ * cleanup work, and that the engine should be called back at an appropriate
+ * later time to perform this cleanup, by calling the function |doCleanup|.
+ *
+ * This callback must not do anything that could cause GC.
+ */
+using JSHostCleanupFinalizationRegistryCallback =
+ void (*)(JSFunction* doCleanup, JSObject* incumbentGlobal, void* data);
+
+/**
+ * Each external string has a pointer to JSExternalStringCallbacks. Embedders
+ * can use this to implement custom finalization or memory reporting behavior.
+ */
+struct JSExternalStringCallbacks {
+ /**
+ * Finalizes external strings created by JS_NewExternalString. The finalizer
+ * can be called off the main thread.
+ */
+ virtual void finalize(char16_t* chars) const = 0;
+
+ /**
+ * Callback used by memory reporting to ask the embedder how much memory an
+ * external string is keeping alive. The embedder is expected to return a
+ * value that corresponds to the size of the allocation that will be released
+ * by the finalizer callback above.
+ *
+ * Implementations of this callback MUST NOT do anything that can cause GC.
+ */
+ virtual size_t sizeOfBuffer(const char16_t* chars,
+ mozilla::MallocSizeOf mallocSizeOf) const = 0;
+};
+
+namespace JS {
+
+#define GCREASONS(D) \
+ /* Reasons internal to the JS engine */ \
+ D(API, 0) \
+ D(EAGER_ALLOC_TRIGGER, 1) \
+ D(DESTROY_RUNTIME, 2) \
+ D(ROOTS_REMOVED, 3) \
+ D(LAST_DITCH, 4) \
+ D(TOO_MUCH_MALLOC, 5) \
+ D(ALLOC_TRIGGER, 6) \
+ D(DEBUG_GC, 7) \
+ D(COMPARTMENT_REVIVED, 8) \
+ D(RESET, 9) \
+ D(OUT_OF_NURSERY, 10) \
+ D(EVICT_NURSERY, 11) \
+ D(DELAYED_ATOMS_GC, 12) \
+ D(SHARED_MEMORY_LIMIT, 13) \
+ D(IDLE_TIME_COLLECTION, 14) \
+ D(BG_TASK_FINISHED, 15) \
+ D(ABORT_GC, 16) \
+ D(FULL_WHOLE_CELL_BUFFER, 17) \
+ D(FULL_GENERIC_BUFFER, 18) \
+ D(FULL_VALUE_BUFFER, 19) \
+ D(FULL_CELL_PTR_OBJ_BUFFER, 20) \
+ D(FULL_SLOT_BUFFER, 21) \
+ D(FULL_SHAPE_BUFFER, 22) \
+ D(TOO_MUCH_WASM_MEMORY, 23) \
+ D(DISABLE_GENERATIONAL_GC, 24) \
+ D(FINISH_GC, 25) \
+ D(PREPARE_FOR_TRACING, 26) \
+ D(UNUSED4, 27) \
+ D(FULL_CELL_PTR_STR_BUFFER, 28) \
+ D(TOO_MUCH_JIT_CODE, 29) \
+ D(FULL_CELL_PTR_BIGINT_BUFFER, 30) \
+ D(INIT_SELF_HOSTING, 31) \
+ D(NURSERY_MALLOC_BUFFERS, 32) \
+ \
+ /* Reasons from Firefox */ \
+ D(DOM_WINDOW_UTILS, FIRST_FIREFOX_REASON) \
+ D(COMPONENT_UTILS, 34) \
+ D(MEM_PRESSURE, 35) \
+ D(CC_FINISHED, 36) \
+ D(CC_FORCED, 37) \
+ D(LOAD_END, 38) \
+ D(UNUSED3, 39) \
+ D(PAGE_HIDE, 40) \
+ D(NSJSCONTEXT_DESTROY, 41) \
+ D(WORKER_SHUTDOWN, 42) \
+ D(SET_DOC_SHELL, 43) \
+ D(DOM_UTILS, 44) \
+ D(DOM_IPC, 45) \
+ D(DOM_WORKER, 46) \
+ D(INTER_SLICE_GC, 47) \
+ D(UNUSED1, 48) \
+ D(FULL_GC_TIMER, 49) \
+ D(SHUTDOWN_CC, 50) \
+ D(UNUSED2, 51) \
+ D(USER_INACTIVE, 52) \
+ D(XPCONNECT_SHUTDOWN, 53) \
+ D(DOCSHELL, 54) \
+ D(HTML_PARSER, 55)
+
+enum class GCReason {
+ FIRST_FIREFOX_REASON = 33,
+
+#define MAKE_REASON(name, val) name = val,
+ GCREASONS(MAKE_REASON)
+#undef MAKE_REASON
+ NO_REASON,
+ NUM_REASONS,
+
+ /*
+ * For telemetry, we want to keep a fixed max bucket size over time so we
+ * don't have to switch histograms. 100 is conservative; but the cost of extra
+ * buckets seems to be low while the cost of switching histograms is high.
+ */
+ NUM_TELEMETRY_REASONS = 100
+};
+
+/**
+ * Get a statically allocated C string explaining the given GC reason.
+ */
+extern JS_PUBLIC_API const char* ExplainGCReason(JS::GCReason reason);
+
+/**
+ * Return true if the GC reason is internal to the JS engine.
+ */
+extern JS_PUBLIC_API bool InternalGCReason(JS::GCReason reason);
+
+/*
+ * Zone GC:
+ *
+ * SpiderMonkey's GC is capable of performing a collection on an arbitrary
+ * subset of the zones in the system. This allows an embedding to minimize
+ * collection time by only collecting zones that have run code recently,
+ * ignoring the parts of the heap that are unlikely to have changed.
+ *
+ * When triggering a GC using one of the functions below, it is first necessary
+ * to select the zones to be collected. To do this, you can call
+ * PrepareZoneForGC on each zone, or you can call PrepareForFullGC to select
+ * all zones. Failing to select any zone is an error.
+ */
+
+/**
+ * Schedule the given zone to be collected as part of the next GC.
+ */
+extern JS_PUBLIC_API void PrepareZoneForGC(JSContext* cx, Zone* zone);
+
+/**
+ * Schedule all zones to be collected in the next GC.
+ */
+extern JS_PUBLIC_API void PrepareForFullGC(JSContext* cx);
+
+/**
+ * When performing an incremental GC, the zones that were selected for the
+ * previous incremental slice must be selected in subsequent slices as well.
+ * This function selects those slices automatically.
+ */
+extern JS_PUBLIC_API void PrepareForIncrementalGC(JSContext* cx);
+
+/**
+ * Returns true if any zone in the system has been scheduled for GC with one of
+ * the functions above or by the JS engine.
+ */
+extern JS_PUBLIC_API bool IsGCScheduled(JSContext* cx);
+
+/**
+ * Undoes the effect of the Prepare methods above. The given zone will not be
+ * collected in the next GC.
+ */
+extern JS_PUBLIC_API void SkipZoneForGC(JSContext* cx, Zone* zone);
+
+/*
+ * Non-Incremental GC:
+ *
+ * The following functions perform a non-incremental GC.
+ */
+
+/**
+ * Performs a non-incremental collection of all selected zones.
+ *
+ * If the gckind argument is GC_NORMAL, then some objects that are unreachable
+ * from the program may still be alive afterwards because of internal
+ * references; if GC_SHRINK is passed then caches and other temporary references
+ * to objects will be cleared and all unreferenced objects will be removed from
+ * the system.
+ */
+extern JS_PUBLIC_API void NonIncrementalGC(JSContext* cx,
+ JSGCInvocationKind gckind,
+ GCReason reason);
+
+/*
+ * Incremental GC:
+ *
+ * Incremental GC divides the full mark-and-sweep collection into multiple
+ * slices, allowing client JavaScript code to run between each slice. This
+ * allows interactive apps to avoid long collection pauses. Incremental GC does
+ * not make collection take less time, it merely spreads that time out so that
+ * the pauses are less noticable.
+ *
+ * For a collection to be carried out incrementally the following conditions
+ * must be met:
+ * - The collection must be run by calling JS::IncrementalGC() rather than
+ * JS_GC().
+ * - The GC parameter JSGC_INCREMENTAL_GC_ENABLED must be true.
+ *
+ * Note: Even if incremental GC is enabled and working correctly,
+ * non-incremental collections can still happen when low on memory.
+ */
+
+/**
+ * Begin an incremental collection and perform one slice worth of work. When
+ * this function returns, the collection may not be complete.
+ * IncrementalGCSlice() must be called repeatedly until
+ * !IsIncrementalGCInProgress(cx).
+ *
+ * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or
+ * shorter than the requested interval.
+ */
+extern JS_PUBLIC_API void StartIncrementalGC(JSContext* cx,
+ JSGCInvocationKind gckind,
+ GCReason reason,
+ int64_t millis = 0);
+
+/**
+ * Perform a slice of an ongoing incremental collection. When this function
+ * returns, the collection may not be complete. It must be called repeatedly
+ * until !IsIncrementalGCInProgress(cx).
+ *
+ * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or
+ * shorter than the requested interval.
+ */
+extern JS_PUBLIC_API void IncrementalGCSlice(JSContext* cx, GCReason reason,
+ int64_t millis = 0);
+
+/**
+ * Return whether an incremental GC has work to do on the foreground thread and
+ * would make progress if a slice was run now. If this returns false then the GC
+ * is waiting for background threads to finish their work and a slice started
+ * now would return immediately.
+ */
+extern JS_PUBLIC_API bool IncrementalGCHasForegroundWork(JSContext* cx);
+
+/**
+ * If IsIncrementalGCInProgress(cx), this call finishes the ongoing collection
+ * by performing an arbitrarily long slice. If !IsIncrementalGCInProgress(cx),
+ * this is equivalent to NonIncrementalGC. When this function returns,
+ * IsIncrementalGCInProgress(cx) will always be false.
+ */
+extern JS_PUBLIC_API void FinishIncrementalGC(JSContext* cx, GCReason reason);
+
+/**
+ * If IsIncrementalGCInProgress(cx), this call aborts the ongoing collection and
+ * performs whatever work needs to be done to return the collector to its idle
+ * state. This may take an arbitrarily long time. When this function returns,
+ * IsIncrementalGCInProgress(cx) will always be false.
+ */
+extern JS_PUBLIC_API void AbortIncrementalGC(JSContext* cx);
+
+namespace dbg {
+
+// The `JS::dbg::GarbageCollectionEvent` class is essentially a view of the
+// `js::gcstats::Statistics` data without the uber implementation-specific bits.
+// It should generally be palatable for web developers.
+class GarbageCollectionEvent {
+ // The major GC number of the GC cycle this data pertains to.
+ uint64_t majorGCNumber_;
+
+ // Reference to a non-owned, statically allocated C string. This is a very
+ // short reason explaining why a GC was triggered.
+ const char* reason;
+
+ // Reference to a nullable, non-owned, statically allocated C string. If the
+ // collection was forced to be non-incremental, this is a short reason of
+ // why the GC could not perform an incremental collection.
+ const char* nonincrementalReason;
+
+ // Represents a single slice of a possibly multi-slice incremental garbage
+ // collection.
+ struct Collection {
+ mozilla::TimeStamp startTimestamp;
+ mozilla::TimeStamp endTimestamp;
+ };
+
+ // The set of garbage collection slices that made up this GC cycle.
+ mozilla::Vector<Collection> collections;
+
+ GarbageCollectionEvent(const GarbageCollectionEvent& rhs) = delete;
+ GarbageCollectionEvent& operator=(const GarbageCollectionEvent& rhs) = delete;
+
+ public:
+ explicit GarbageCollectionEvent(uint64_t majorGCNum)
+ : majorGCNumber_(majorGCNum),
+ reason(nullptr),
+ nonincrementalReason(nullptr),
+ collections() {}
+
+ using Ptr = js::UniquePtr<GarbageCollectionEvent>;
+ static Ptr Create(JSRuntime* rt, ::js::gcstats::Statistics& stats,
+ uint64_t majorGCNumber);
+
+ JSObject* toJSObject(JSContext* cx) const;
+
+ uint64_t majorGCNumber() const { return majorGCNumber_; }
+};
+
+} // namespace dbg
+
+enum GCProgress {
+ /*
+ * During GC, the GC is bracketed by GC_CYCLE_BEGIN/END callbacks. Each
+ * slice between those (whether an incremental or the sole non-incremental
+ * slice) is bracketed by GC_SLICE_BEGIN/GC_SLICE_END.
+ */
+
+ GC_CYCLE_BEGIN,
+ GC_SLICE_BEGIN,
+ GC_SLICE_END,
+ GC_CYCLE_END
+};
+
+struct JS_PUBLIC_API GCDescription {
+ bool isZone_;
+ bool isComplete_;
+ JSGCInvocationKind invocationKind_;
+ GCReason reason_;
+
+ GCDescription(bool isZone, bool isComplete, JSGCInvocationKind kind,
+ GCReason reason)
+ : isZone_(isZone),
+ isComplete_(isComplete),
+ invocationKind_(kind),
+ reason_(reason) {}
+
+ char16_t* formatSliceMessage(JSContext* cx) const;
+ char16_t* formatSummaryMessage(JSContext* cx) const;
+
+ mozilla::TimeStamp startTime(JSContext* cx) const;
+ mozilla::TimeStamp endTime(JSContext* cx) const;
+ mozilla::TimeStamp lastSliceStart(JSContext* cx) const;
+ mozilla::TimeStamp lastSliceEnd(JSContext* cx) const;
+
+ JS::UniqueChars sliceToJSONProfiler(JSContext* cx) const;
+ JS::UniqueChars formatJSONProfiler(JSContext* cx) const;
+
+ JS::dbg::GarbageCollectionEvent::Ptr toGCEvent(JSContext* cx) const;
+};
+
+extern JS_PUBLIC_API UniqueChars MinorGcToJSON(JSContext* cx);
+
+typedef void (*GCSliceCallback)(JSContext* cx, GCProgress progress,
+ const GCDescription& desc);
+
+/**
+ * The GC slice callback is called at the beginning and end of each slice. This
+ * callback may be used for GC notifications as well as to perform additional
+ * marking.
+ */
+extern JS_PUBLIC_API GCSliceCallback
+SetGCSliceCallback(JSContext* cx, GCSliceCallback callback);
+
+/**
+ * Describes the progress of an observed nursery collection.
+ */
+enum class GCNurseryProgress {
+ /**
+ * The nursery collection is starting.
+ */
+ GC_NURSERY_COLLECTION_START,
+ /**
+ * The nursery collection is ending.
+ */
+ GC_NURSERY_COLLECTION_END
+};
+
+/**
+ * A nursery collection callback receives the progress of the nursery collection
+ * and the reason for the collection.
+ */
+using GCNurseryCollectionCallback = void (*)(JSContext* cx,
+ GCNurseryProgress progress,
+ GCReason reason);
+
+/**
+ * Set the nursery collection callback for the given runtime. When set, it will
+ * be called at the start and end of every nursery collection.
+ */
+extern JS_PUBLIC_API GCNurseryCollectionCallback SetGCNurseryCollectionCallback(
+ JSContext* cx, GCNurseryCollectionCallback callback);
+
+typedef void (*DoCycleCollectionCallback)(JSContext* cx);
+
+/**
+ * The purge gray callback is called after any COMPARTMENT_REVIVED GC in which
+ * the majority of compartments have been marked gray.
+ */
+extern JS_PUBLIC_API DoCycleCollectionCallback
+SetDoCycleCollectionCallback(JSContext* cx, DoCycleCollectionCallback callback);
+
+/**
+ * Incremental GC defaults to enabled, but may be disabled for testing or in
+ * embeddings that have not yet implemented barriers on their native classes.
+ * There is not currently a way to re-enable incremental GC once it has been
+ * disabled on the runtime.
+ */
+extern JS_PUBLIC_API void DisableIncrementalGC(JSContext* cx);
+
+/**
+ * Returns true if incremental GC is enabled. Simply having incremental GC
+ * enabled is not sufficient to ensure incremental collections are happening.
+ * See the comment "Incremental GC" above for reasons why incremental GC may be
+ * suppressed. Inspection of the "nonincremental reason" field of the
+ * GCDescription returned by GCSliceCallback may help narrow down the cause if
+ * collections are not happening incrementally when expected.
+ */
+extern JS_PUBLIC_API bool IsIncrementalGCEnabled(JSContext* cx);
+
+/**
+ * Returns true while an incremental GC is ongoing, both when actively
+ * collecting and between slices.
+ */
+extern JS_PUBLIC_API bool IsIncrementalGCInProgress(JSContext* cx);
+
+/**
+ * Returns true while an incremental GC is ongoing, both when actively
+ * collecting and between slices.
+ */
+extern JS_PUBLIC_API bool IsIncrementalGCInProgress(JSRuntime* rt);
+
+/**
+ * Returns true if the most recent GC ran incrementally.
+ */
+extern JS_PUBLIC_API bool WasIncrementalGC(JSRuntime* rt);
+
+/*
+ * Generational GC:
+ *
+ * Note: Generational GC is not yet enabled by default. The following class
+ * is non-functional unless SpiderMonkey was configured with
+ * --enable-gcgenerational.
+ */
+
+/** Ensure that generational GC is disabled within some scope. */
+class JS_PUBLIC_API AutoDisableGenerationalGC {
+ JSContext* cx;
+
+ public:
+ explicit AutoDisableGenerationalGC(JSContext* cx);
+ ~AutoDisableGenerationalGC();
+};
+
+/**
+ * Returns true if generational allocation and collection is currently enabled
+ * on the given runtime.
+ */
+extern JS_PUBLIC_API bool IsGenerationalGCEnabled(JSRuntime* rt);
+
+/**
+ * Pass a subclass of this "abstract" class to callees to require that they
+ * never GC. Subclasses can use assertions or the hazard analysis to ensure no
+ * GC happens.
+ */
+class JS_PUBLIC_API AutoRequireNoGC {
+ protected:
+ AutoRequireNoGC() = default;
+ ~AutoRequireNoGC() = default;
+};
+
+/**
+ * Diagnostic assert (see MOZ_DIAGNOSTIC_ASSERT) that GC cannot occur while this
+ * class is live. This class does not disable the static rooting hazard
+ * analysis.
+ *
+ * This works by entering a GC unsafe region, which is checked on allocation and
+ * on GC.
+ */
+class JS_PUBLIC_API AutoAssertNoGC : public AutoRequireNoGC {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ JSContext* cx_;
+
+ public:
+ // This gets the context from TLS if it is not passed in.
+ explicit AutoAssertNoGC(JSContext* cx = nullptr);
+ ~AutoAssertNoGC();
+#else
+ public:
+ explicit AutoAssertNoGC(JSContext* cx = nullptr) {}
+ ~AutoAssertNoGC() {}
+#endif
+};
+
+/**
+ * Disable the static rooting hazard analysis in the live region and assert in
+ * debug builds if any allocation that could potentially trigger a GC occurs
+ * while this guard object is live. This is most useful to help the exact
+ * rooting hazard analysis in complex regions, since it cannot understand
+ * dataflow.
+ *
+ * Note: GC behavior is unpredictable even when deterministic and is generally
+ * non-deterministic in practice. The fact that this guard has not
+ * asserted is not a guarantee that a GC cannot happen in the guarded
+ * region. As a rule, anyone performing a GC unsafe action should
+ * understand the GC properties of all code in that region and ensure
+ * that the hazard analysis is correct for that code, rather than relying
+ * on this class.
+ */
+#ifdef DEBUG
+class JS_PUBLIC_API AutoSuppressGCAnalysis : public AutoAssertNoGC {
+ public:
+ explicit AutoSuppressGCAnalysis(JSContext* cx = nullptr)
+ : AutoAssertNoGC(cx) {}
+} JS_HAZ_GC_SUPPRESSED;
+#else
+class JS_PUBLIC_API AutoSuppressGCAnalysis : public AutoRequireNoGC {
+ public:
+ explicit AutoSuppressGCAnalysis(JSContext* cx = nullptr) {}
+} JS_HAZ_GC_SUPPRESSED;
+#endif
+
+/**
+ * Assert that code is only ever called from a GC callback, disable the static
+ * rooting hazard analysis and assert if any allocation that could potentially
+ * trigger a GC occurs while this guard object is live.
+ *
+ * This is useful to make the static analysis ignore code that runs in GC
+ * callbacks.
+ */
+class JS_PUBLIC_API AutoAssertGCCallback : public AutoSuppressGCAnalysis {
+ public:
+#ifdef DEBUG
+ AutoAssertGCCallback();
+#else
+ AutoAssertGCCallback() {}
+#endif
+};
+
+/**
+ * Place AutoCheckCannotGC in scopes that you believe can never GC. These
+ * annotations will be verified both dynamically via AutoAssertNoGC, and
+ * statically with the rooting hazard analysis (implemented by making the
+ * analysis consider AutoCheckCannotGC to be a GC pointer, and therefore
+ * complain if it is live across a GC call.) It is useful when dealing with
+ * internal pointers to GC things where the GC thing itself may not be present
+ * for the static analysis: e.g. acquiring inline chars from a JSString* on the
+ * heap.
+ *
+ * We only do the assertion checking in DEBUG builds.
+ */
+#ifdef DEBUG
+class JS_PUBLIC_API AutoCheckCannotGC : public AutoAssertNoGC {
+ public:
+ explicit AutoCheckCannotGC(JSContext* cx = nullptr) : AutoAssertNoGC(cx) {}
+} JS_HAZ_GC_INVALIDATED;
+#else
+class JS_PUBLIC_API AutoCheckCannotGC : public AutoRequireNoGC {
+ public:
+ explicit AutoCheckCannotGC(JSContext* cx = nullptr) {}
+} JS_HAZ_GC_INVALIDATED;
+#endif
+
+extern JS_PUBLIC_API void SetLowMemoryState(JSContext* cx, bool newState);
+
+/*
+ * Internal to Firefox.
+ */
+extern JS_FRIEND_API void NotifyGCRootsRemoved(JSContext* cx);
+
+} /* namespace JS */
+
+typedef void (*JSGCCallback)(JSContext* cx, JSGCStatus status,
+ JS::GCReason reason, void* data);
+
+/**
+ * Register externally maintained GC roots.
+ *
+ * traceOp: the trace operation. For each root the implementation should call
+ * JS::TraceEdge whenever the root contains a traceable thing.
+ * data: the data argument to pass to each invocation of traceOp.
+ */
+extern JS_PUBLIC_API bool JS_AddExtraGCRootsTracer(JSContext* cx,
+ JSTraceDataOp traceOp,
+ void* data);
+
+/** Undo a call to JS_AddExtraGCRootsTracer. */
+extern JS_PUBLIC_API void JS_RemoveExtraGCRootsTracer(JSContext* cx,
+ JSTraceDataOp traceOp,
+ void* data);
+
+extern JS_PUBLIC_API void JS_GC(JSContext* cx,
+ JS::GCReason reason = JS::GCReason::API);
+
+extern JS_PUBLIC_API void JS_MaybeGC(JSContext* cx);
+
+extern JS_PUBLIC_API void JS_SetGCCallback(JSContext* cx, JSGCCallback cb,
+ void* data);
+
+extern JS_PUBLIC_API void JS_SetObjectsTenuredCallback(
+ JSContext* cx, JSObjectsTenuredCallback cb, void* data);
+
+extern JS_PUBLIC_API bool JS_AddFinalizeCallback(JSContext* cx,
+ JSFinalizeCallback cb,
+ void* data);
+
+extern JS_PUBLIC_API void JS_RemoveFinalizeCallback(JSContext* cx,
+ JSFinalizeCallback cb);
+
+/*
+ * Weak pointers and garbage collection
+ *
+ * Weak pointers are by their nature not marked as part of garbage collection,
+ * but they may need to be updated in two cases after a GC:
+ *
+ * 1) Their referent was found not to be live and is about to be finalized
+ * 2) Their referent has been moved by a compacting GC
+ *
+ * To handle this, any part of the system that maintain weak pointers to
+ * JavaScript GC things must register a callback with
+ * JS_(Add,Remove)WeakPointer{ZoneGroup,Compartment}Callback(). This callback
+ * must then call JS_UpdateWeakPointerAfterGC() on all weak pointers it knows
+ * about.
+ *
+ * Since sweeping is incremental, we have several callbacks to avoid repeatedly
+ * having to visit all embedder structures. The WeakPointerZonesCallback is
+ * called once for each strongly connected group of zones, whereas the
+ * WeakPointerCompartmentCallback is called once for each compartment that is
+ * visited while sweeping. Structures that cannot contain references in more
+ * than one compartment should sweep the relevant per-compartment structures
+ * using the latter callback to minimizer per-slice overhead.
+ *
+ * The argument to JS_UpdateWeakPointerAfterGC() is an in-out param. If the
+ * referent is about to be finalized the pointer will be set to null. If the
+ * referent has been moved then the pointer will be updated to point to the new
+ * location.
+ *
+ * Callers of this method are responsible for updating any state that is
+ * dependent on the object's address. For example, if the object's address is
+ * used as a key in a hashtable, then the object must be removed and
+ * re-inserted with the correct hash.
+ */
+
+extern JS_PUBLIC_API bool JS_AddWeakPointerZonesCallback(
+ JSContext* cx, JSWeakPointerZonesCallback cb, void* data);
+
+extern JS_PUBLIC_API void JS_RemoveWeakPointerZonesCallback(
+ JSContext* cx, JSWeakPointerZonesCallback cb);
+
+extern JS_PUBLIC_API bool JS_AddWeakPointerCompartmentCallback(
+ JSContext* cx, JSWeakPointerCompartmentCallback cb, void* data);
+
+extern JS_PUBLIC_API void JS_RemoveWeakPointerCompartmentCallback(
+ JSContext* cx, JSWeakPointerCompartmentCallback cb);
+
+namespace JS {
+template <typename T>
+class Heap;
+}
+
+extern JS_PUBLIC_API void JS_UpdateWeakPointerAfterGC(
+ JS::Heap<JSObject*>* objp);
+
+extern JS_PUBLIC_API void JS_UpdateWeakPointerAfterGCUnbarriered(
+ JSObject** objp);
+
+extern JS_PUBLIC_API void JS_SetGCParameter(JSContext* cx, JSGCParamKey key,
+ uint32_t value);
+
+extern JS_PUBLIC_API void JS_ResetGCParameter(JSContext* cx, JSGCParamKey key);
+
+extern JS_PUBLIC_API uint32_t JS_GetGCParameter(JSContext* cx,
+ JSGCParamKey key);
+
+extern JS_PUBLIC_API void JS_SetGCParametersBasedOnAvailableMemory(
+ JSContext* cx, uint32_t availMem);
+
+/**
+ * Create a new JSString whose chars member refers to external memory, i.e.,
+ * memory requiring application-specific finalization.
+ */
+extern JS_PUBLIC_API JSString* JS_NewExternalString(
+ JSContext* cx, const char16_t* chars, size_t length,
+ const JSExternalStringCallbacks* callbacks);
+
+/**
+ * Create a new JSString whose chars member may refer to external memory.
+ * If a new external string is allocated, |*allocatedExternal| is set to true.
+ * Otherwise the returned string is either not an external string or an
+ * external string allocated by a previous call and |*allocatedExternal| is set
+ * to false. If |*allocatedExternal| is false, |fin| won't be called.
+ */
+extern JS_PUBLIC_API JSString* JS_NewMaybeExternalString(
+ JSContext* cx, const char16_t* chars, size_t length,
+ const JSExternalStringCallbacks* callbacks, bool* allocatedExternal);
+
+/**
+ * Return the 'callbacks' arg passed to JS_NewExternalString or
+ * JS_NewMaybeExternalString.
+ */
+extern JS_PUBLIC_API const JSExternalStringCallbacks*
+JS_GetExternalStringCallbacks(JSString* str);
+
+namespace JS {
+
+extern JS_PUBLIC_API bool IsIdleGCTaskNeeded(JSRuntime* rt);
+
+extern JS_PUBLIC_API void RunIdleTimeGCTask(JSRuntime* rt);
+
+extern JS_PUBLIC_API void SetHostCleanupFinalizationRegistryCallback(
+ JSContext* cx, JSHostCleanupFinalizationRegistryCallback cb, void* data);
+
+/**
+ * Clear kept alive objects in JS WeakRef.
+ * https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects
+ */
+extern JS_PUBLIC_API void ClearKeptObjects(JSContext* cx);
+
+extern JS_PUBLIC_API bool ZoneIsCollecting(Zone* zone);
+
+} // namespace JS
+
+namespace js {
+namespace gc {
+
+/**
+ * Create an object providing access to the garbage collector's internal notion
+ * of the current state of memory (both GC heap memory and GCthing-controlled
+ * malloc memory.
+ */
+extern JS_PUBLIC_API JSObject* NewMemoryInfoObject(JSContext* cx);
+
+/*
+ * Run the finalizer of a nursery-allocated JSObject that is known to be dead.
+ *
+ * This is a dangerous operation - only use this if you know what you're doing!
+ *
+ * This is used by the browser to implement nursery-allocated wrapper cached
+ * wrappers.
+ */
+extern JS_PUBLIC_API void FinalizeDeadNurseryObject(JSContext* cx,
+ JSObject* obj);
+
+} /* namespace gc */
+} /* namespace js */
+
+#endif /* js_GCAPI_h */
diff --git a/js/public/GCAnnotations.h b/js/public/GCAnnotations.h
new file mode 100644
index 0000000000..2e9a25a9b1
--- /dev/null
+++ b/js/public/GCAnnotations.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_GCAnnotations_h
+#define js_GCAnnotations_h
+
+// Set of annotations for the rooting hazard analysis, used to categorize types
+// and functions.
+#ifdef XGILL_PLUGIN
+
+# define JS_EXPECT_HAZARDS __attribute__((annotate("Expect Hazards")))
+
+// Mark a type as being a GC thing (eg js::gc::Cell has this annotation).
+# define JS_HAZ_GC_THING __attribute__((annotate("GC Thing")))
+
+// Mark a type as holding a pointer to a GC thing (eg JS::Value has this
+// annotation.) "Inherited" by templatized types with
+// MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
+# define JS_HAZ_GC_POINTER __attribute__((annotate("GC Pointer")))
+
+// Mark a type as a rooted pointer, suitable for use on the stack (eg all
+// Rooted<T> instantiations should have this.) "Inherited" by templatized types
+// with MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
+# define JS_HAZ_ROOTED __attribute__((annotate("Rooted Pointer")))
+
+// Mark a type as something that should not be held live across a GC, but which
+// is not itself a GC pointer. Note that this property is *not* inherited by
+// templatized types with MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS.
+# define JS_HAZ_GC_INVALIDATED __attribute__((annotate("Invalidated by GC")))
+
+// Mark a class as a base class of rooted types, eg CustomAutoRooter. All
+// descendants of this class will be considered rooted, though classes that
+// merely contain these as a field member will not be. "Inherited" by
+// templatized types with MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS
+# define JS_HAZ_ROOTED_BASE __attribute__((annotate("Rooted Base")))
+
+// Mark a type that would otherwise be considered a GC Pointer (eg because it
+// contains a JS::Value field) as a non-GC pointer. It is handled almost the
+// same in the analysis as a rooted pointer, except it will not be reported as
+// an unnecessary root if used across a GC call. This should rarely be used,
+// but makes sense for something like ErrorResult, which only contains a GC
+// pointer when it holds an exception (and it does its own rooting,
+// conditionally.)
+# define JS_HAZ_NON_GC_POINTER \
+ __attribute__((annotate("Suppressed GC Pointer")))
+
+// Mark a function as something that runs a garbage collection, potentially
+// invalidating GC pointers.
+# define JS_HAZ_GC_CALL __attribute__((annotate("GC Call")))
+
+// Mark an RAII class as suppressing GC within its scope.
+# define JS_HAZ_GC_SUPPRESSED __attribute__((annotate("Suppress GC")))
+
+// Mark a function as one that can run script if called. This obviously
+// subsumes JS_HAZ_GC_CALL, since anything that can run script can GC.`
+# define JS_HAZ_CAN_RUN_SCRIPT __attribute__((annotate("Can run script")))
+
+// Mark a function as able to call JSNatives. Otherwise, JSNatives don't show
+// up in the callgraph. This doesn't matter for the can-GC analysis, but it is
+// very nice for other uses of the callgraph.
+# define JS_HAZ_JSNATIVE_CALLER __attribute__((annotate("Calls JSNatives")))
+
+#else
+
+# define JS_EXPECT_HAZARDS
+# define JS_HAZ_GC_THING
+# define JS_HAZ_GC_POINTER
+# define JS_HAZ_ROOTED
+# define JS_HAZ_GC_INVALIDATED
+# define JS_HAZ_ROOTED_BASE
+# define JS_HAZ_NON_GC_POINTER
+# define JS_HAZ_GC_CALL
+# define JS_HAZ_GC_SUPPRESSED
+# define JS_HAZ_CAN_RUN_SCRIPT
+# define JS_HAZ_JSNATIVE_CALLER
+
+#endif
+
+#endif /* js_GCAnnotations_h */
diff --git a/js/public/GCHashTable.h b/js/public/GCHashTable.h
new file mode 100644
index 0000000000..310c9fe502
--- /dev/null
+++ b/js/public/GCHashTable.h
@@ -0,0 +1,796 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GCHashTable_h
+#define GCHashTable_h
+
+#include "mozilla/Maybe.h"
+
+#include "js/GCPolicyAPI.h"
+#include "js/HashTable.h"
+#include "js/RootingAPI.h"
+#include "js/SweepingAPI.h"
+#include "js/TracingAPI.h"
+
+namespace JS {
+
+// Define a reasonable default GC policy for GC-aware Maps.
+template <typename Key, typename Value>
+struct DefaultMapSweepPolicy {
+ static bool needsSweep(Key* key, Value* value) {
+ return GCPolicy<Key>::needsSweep(key) || GCPolicy<Value>::needsSweep(value);
+ }
+
+ static bool traceWeak(JSTracer* trc, Key* key, Value* value) {
+ return GCPolicy<Key>::traceWeak(trc, key) &&
+ GCPolicy<Value>::traceWeak(trc, value);
+ }
+};
+
+// A GCHashMap is a GC-aware HashMap, meaning that it has additional trace and
+// sweep methods that know how to visit all keys and values in the table.
+// HashMaps that contain GC pointers will generally want to use this GCHashMap
+// specialization instead of HashMap, because this conveniently supports tracing
+// keys and values, and cleaning up weak entries.
+//
+// GCHashMap::trace applies GCPolicy<T>::trace to each entry's key and value.
+// Most types of GC pointers already have appropriate specializations of
+// GCPolicy, so they should just work as keys and values. Any struct type with a
+// default constructor and trace and sweep functions should work as well. If you
+// need to define your own GCPolicy specialization, generic helpers can be found
+// in js/public/TracingAPI.h.
+//
+// The MapSweepPolicy template parameter controls how the table drops entries
+// when swept. GCHashMap::sweep applies MapSweepPolicy::needsSweep to each table
+// entry; if it returns true, the entry is dropped. The default MapSweepPolicy
+// drops the entry if either the key or value is about to be finalized,
+// according to its GCPolicy<T>::needsSweep method. (This default is almost
+// always fine: it's hard to imagine keeping such an entry around anyway.)
+//
+// Note that this HashMap only knows *how* to trace and sweep, but it does not
+// itself cause tracing or sweeping to be invoked. For tracing, it must be used
+// with Rooted or PersistentRooted, or barriered and traced manually. For
+// sweeping, currently it requires an explicit call to <map>.sweep().
+template <typename Key, typename Value,
+ typename HashPolicy = js::DefaultHasher<Key>,
+ typename AllocPolicy = js::TempAllocPolicy,
+ typename MapSweepPolicy = DefaultMapSweepPolicy<Key, Value>>
+class GCHashMap : public js::HashMap<Key, Value, HashPolicy, AllocPolicy> {
+ using Base = js::HashMap<Key, Value, HashPolicy, AllocPolicy>;
+
+ public:
+ explicit GCHashMap(AllocPolicy a = AllocPolicy()) : Base(std::move(a)) {}
+ explicit GCHashMap(size_t length) : Base(length) {}
+ GCHashMap(AllocPolicy a, size_t length) : Base(std::move(a), length) {}
+
+ void trace(JSTracer* trc) {
+ for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+ GCPolicy<Value>::trace(trc, &e.front().value(), "hashmap value");
+ GCPolicy<Key>::trace(trc, &e.front().mutableKey(), "hashmap key");
+ }
+ }
+
+ bool needsSweep() const { return !this->empty(); }
+
+ void sweep() {
+ typename Base::Enum e(*this);
+ sweepEntries(e);
+ }
+
+ void sweepEntries(typename Base::Enum& e) {
+ for (; !e.empty(); e.popFront()) {
+ if (MapSweepPolicy::needsSweep(&e.front().mutableKey(),
+ &e.front().value())) {
+ e.removeFront();
+ }
+ }
+ }
+
+ void traceWeak(JSTracer* trc) {
+ for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+ if (!MapSweepPolicy::traceWeak(trc, &e.front().mutableKey(),
+ &e.front().value())) {
+ e.removeFront();
+ }
+ }
+ }
+
+ // GCHashMap is movable
+ GCHashMap(GCHashMap&& rhs) : Base(std::move(rhs)) {}
+ void operator=(GCHashMap&& rhs) {
+ MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
+ Base::operator=(std::move(rhs));
+ }
+
+ private:
+ // GCHashMap is not copyable or assignable
+ GCHashMap(const GCHashMap& hm) = delete;
+ GCHashMap& operator=(const GCHashMap& hm) = delete;
+} MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS;
+
+} // namespace JS
+
+namespace js {
+
+// HashMap that supports rekeying.
+//
+// If your keys are pointers to something like JSObject that can be tenured or
+// compacted, prefer to use GCHashMap with MovableCellHasher, which takes
+// advantage of the Zone's stable id table to make rekeying unnecessary.
+template <typename Key, typename Value,
+ typename HashPolicy = DefaultHasher<Key>,
+ typename AllocPolicy = TempAllocPolicy,
+ typename MapSweepPolicy = JS::DefaultMapSweepPolicy<Key, Value>>
+class GCRekeyableHashMap : public JS::GCHashMap<Key, Value, HashPolicy,
+ AllocPolicy, MapSweepPolicy> {
+ using Base = JS::GCHashMap<Key, Value, HashPolicy, AllocPolicy>;
+
+ public:
+ explicit GCRekeyableHashMap(AllocPolicy a = AllocPolicy())
+ : Base(std::move(a)) {}
+ explicit GCRekeyableHashMap(size_t length) : Base(length) {}
+ GCRekeyableHashMap(AllocPolicy a, size_t length)
+ : Base(std::move(a), length) {}
+
+ void sweep() {
+ for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+ Key key(e.front().key());
+ if (MapSweepPolicy::needsSweep(&key, &e.front().value())) {
+ e.removeFront();
+ } else if (!HashPolicy::match(key, e.front().key())) {
+ e.rekeyFront(key);
+ }
+ }
+ }
+
+ void traceWeak(JSTracer* trc) {
+ for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+ Key key(e.front().key());
+ if (!MapSweepPolicy::traceWeak(trc, &key, &e.front().value())) {
+ e.removeFront();
+ } else if (!HashPolicy::match(key, e.front().key())) {
+ e.rekeyFront(key);
+ }
+ }
+ }
+
+ // GCRekeyableHashMap is movable
+ GCRekeyableHashMap(GCRekeyableHashMap&& rhs) : Base(std::move(rhs)) {}
+ void operator=(GCRekeyableHashMap&& rhs) {
+ MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
+ Base::operator=(std::move(rhs));
+ }
+} MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS;
+
+template <typename Wrapper, typename... Args>
+class WrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper> {
+ using Map = JS::GCHashMap<Args...>;
+ using Lookup = typename Map::Lookup;
+
+ const Map& map() const { return static_cast<const Wrapper*>(this)->get(); }
+
+ public:
+ using AddPtr = typename Map::AddPtr;
+ using Ptr = typename Map::Ptr;
+ using Range = typename Map::Range;
+
+ Ptr lookup(const Lookup& l) const { return map().lookup(l); }
+ Range all() const { return map().all(); }
+ bool empty() const { return map().empty(); }
+ uint32_t count() const { return map().count(); }
+ size_t capacity() const { return map().capacity(); }
+ bool has(const Lookup& l) const { return map().lookup(l).found(); }
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return map().sizeOfExcludingThis(mallocSizeOf);
+ }
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return mallocSizeOf(this) + map().sizeOfExcludingThis(mallocSizeOf);
+ }
+};
+
+template <typename Wrapper, typename... Args>
+class MutableWrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper>
+ : public WrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper> {
+ using Map = JS::GCHashMap<Args...>;
+ using Lookup = typename Map::Lookup;
+
+ Map& map() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+ using AddPtr = typename Map::AddPtr;
+ struct Enum : public Map::Enum {
+ explicit Enum(Wrapper& o) : Map::Enum(o.map()) {}
+ };
+ using Ptr = typename Map::Ptr;
+ using Range = typename Map::Range;
+
+ void clear() { map().clear(); }
+ void clearAndCompact() { map().clearAndCompact(); }
+ void remove(Ptr p) { map().remove(p); }
+ AddPtr lookupForAdd(const Lookup& l) { return map().lookupForAdd(l); }
+
+ template <typename KeyInput, typename ValueInput>
+ bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
+ return map().add(p, std::forward<KeyInput>(k), std::forward<ValueInput>(v));
+ }
+
+ template <typename KeyInput>
+ bool add(AddPtr& p, KeyInput&& k) {
+ return map().add(p, std::forward<KeyInput>(k), Map::Value());
+ }
+
+ template <typename KeyInput, typename ValueInput>
+ bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
+ return map().relookupOrAdd(p, k, std::forward<KeyInput>(k),
+ std::forward<ValueInput>(v));
+ }
+
+ template <typename KeyInput, typename ValueInput>
+ bool put(KeyInput&& k, ValueInput&& v) {
+ return map().put(std::forward<KeyInput>(k), std::forward<ValueInput>(v));
+ }
+
+ template <typename KeyInput, typename ValueInput>
+ bool putNew(KeyInput&& k, ValueInput&& v) {
+ return map().putNew(std::forward<KeyInput>(k), std::forward<ValueInput>(v));
+ }
+};
+
+} // namespace js
+
+namespace JS {
+
+// A GCHashSet is a HashSet with an additional trace method that knows
+// be traced to be kept alive will generally want to use this GCHashSet
+// specialization in lieu of HashSet.
+//
+// Most types of GC pointers can be traced with no extra infrastructure. For
+// structs and non-gc-pointer members, ensure that there is a specialization of
+// GCPolicy<T> with an appropriate trace method available to handle the custom
+// type. Generic helpers can be found in js/public/TracingAPI.h.
+//
+// Note that although this HashSet's trace will deal correctly with moved
+// elements, it does not itself know when to barrier or trace elements. To
+// function properly it must either be used with Rooted or barriered and traced
+// manually.
+template <typename T, typename HashPolicy = js::DefaultHasher<T>,
+ typename AllocPolicy = js::TempAllocPolicy>
+class GCHashSet : public js::HashSet<T, HashPolicy, AllocPolicy> {
+ using Base = js::HashSet<T, HashPolicy, AllocPolicy>;
+
+ public:
+ explicit GCHashSet(AllocPolicy a = AllocPolicy()) : Base(std::move(a)) {}
+ explicit GCHashSet(size_t length) : Base(length) {}
+ GCHashSet(AllocPolicy a, size_t length) : Base(std::move(a), length) {}
+
+ void trace(JSTracer* trc) {
+ for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+ GCPolicy<T>::trace(trc, &e.mutableFront(), "hashset element");
+ }
+ }
+
+ bool needsSweep() const { return !this->empty(); }
+
+ void sweep() {
+ typename Base::Enum e(*this);
+ sweepEntries(e);
+ }
+
+ void sweepEntries(typename Base::Enum& e) {
+ for (; !e.empty(); e.popFront()) {
+ if (GCPolicy<T>::needsSweep(&e.mutableFront())) {
+ e.removeFront();
+ }
+ }
+ }
+
+ void traceWeak(JSTracer* trc) {
+ for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+ if (!GCPolicy<T>::traceWeak(trc, &e.mutableFront())) {
+ e.removeFront();
+ }
+ }
+ }
+
+ // GCHashSet is movable
+ GCHashSet(GCHashSet&& rhs) : Base(std::move(rhs)) {}
+ void operator=(GCHashSet&& rhs) {
+ MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
+ Base::operator=(std::move(rhs));
+ }
+
+ private:
+ // GCHashSet is not copyable or assignable
+ GCHashSet(const GCHashSet& hs) = delete;
+ GCHashSet& operator=(const GCHashSet& hs) = delete;
+} MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS;
+
+} // namespace JS
+
+namespace js {
+
+template <typename Wrapper, typename... Args>
+class WrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper> {
+ using Set = JS::GCHashSet<Args...>;
+
+ const Set& set() const { return static_cast<const Wrapper*>(this)->get(); }
+
+ public:
+ using Lookup = typename Set::Lookup;
+ using AddPtr = typename Set::AddPtr;
+ using Entry = typename Set::Entry;
+ using Ptr = typename Set::Ptr;
+ using Range = typename Set::Range;
+
+ Ptr lookup(const Lookup& l) const { return set().lookup(l); }
+ Range all() const { return set().all(); }
+ bool empty() const { return set().empty(); }
+ uint32_t count() const { return set().count(); }
+ size_t capacity() const { return set().capacity(); }
+ bool has(const Lookup& l) const { return set().lookup(l).found(); }
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return set().sizeOfExcludingThis(mallocSizeOf);
+ }
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return mallocSizeOf(this) + set().sizeOfExcludingThis(mallocSizeOf);
+ }
+};
+
+template <typename Wrapper, typename... Args>
+class MutableWrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper>
+ : public WrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper> {
+ using Set = JS::GCHashSet<Args...>;
+ using Lookup = typename Set::Lookup;
+
+ Set& set() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+ using AddPtr = typename Set::AddPtr;
+ using Entry = typename Set::Entry;
+ struct Enum : public Set::Enum {
+ explicit Enum(Wrapper& o) : Set::Enum(o.set()) {}
+ };
+ using Ptr = typename Set::Ptr;
+ using Range = typename Set::Range;
+
+ void clear() { set().clear(); }
+ void clearAndCompact() { set().clearAndCompact(); }
+ MOZ_MUST_USE bool reserve(uint32_t len) { return set().reserve(len); }
+ void remove(Ptr p) { set().remove(p); }
+ void remove(const Lookup& l) { set().remove(l); }
+ AddPtr lookupForAdd(const Lookup& l) { return set().lookupForAdd(l); }
+
+ template <typename TInput>
+ bool add(AddPtr& p, TInput&& t) {
+ return set().add(p, std::forward<TInput>(t));
+ }
+
+ template <typename TInput>
+ bool relookupOrAdd(AddPtr& p, const Lookup& l, TInput&& t) {
+ return set().relookupOrAdd(p, l, std::forward<TInput>(t));
+ }
+
+ template <typename TInput>
+ bool put(TInput&& t) {
+ return set().put(std::forward<TInput>(t));
+ }
+
+ template <typename TInput>
+ bool putNew(TInput&& t) {
+ return set().putNew(std::forward<TInput>(t));
+ }
+
+ template <typename TInput>
+ bool putNew(const Lookup& l, TInput&& t) {
+ return set().putNew(l, std::forward<TInput>(t));
+ }
+};
+
+} /* namespace js */
+
+namespace JS {
+
+// Specialize WeakCache for GCHashMap to provide a barriered map that does not
+// need to be swept immediately.
+template <typename Key, typename Value, typename HashPolicy,
+ typename AllocPolicy, typename MapSweepPolicy>
+class WeakCache<GCHashMap<Key, Value, HashPolicy, AllocPolicy, MapSweepPolicy>>
+ : protected detail::WeakCacheBase {
+ using Map = GCHashMap<Key, Value, HashPolicy, AllocPolicy, MapSweepPolicy>;
+ using Self = WeakCache<Map>;
+
+ Map map;
+ bool needsBarrier;
+
+ public:
+ template <typename... Args>
+ explicit WeakCache(Zone* zone, Args&&... args)
+ : WeakCacheBase(zone),
+ map(std::forward<Args>(args)...),
+ needsBarrier(false) {}
+ template <typename... Args>
+ explicit WeakCache(JSRuntime* rt, Args&&... args)
+ : WeakCacheBase(rt),
+ map(std::forward<Args>(args)...),
+ needsBarrier(false) {}
+ ~WeakCache() { MOZ_ASSERT(!needsBarrier); }
+
+ bool needsSweep() override { return map.needsSweep(); }
+
+ size_t sweep(js::gc::StoreBuffer* sbToLock) override {
+ size_t steps = map.count();
+
+ // Create an Enum and sweep the table entries.
+ mozilla::Maybe<typename Map::Enum> e;
+ e.emplace(map);
+ map.sweepEntries(e.ref());
+
+ // Potentially take a lock while the Enum's destructor is called as this can
+ // rehash/resize the table and access the store buffer.
+ mozilla::Maybe<js::gc::AutoLockStoreBuffer> lock;
+ if (sbToLock) {
+ lock.emplace(sbToLock);
+ }
+ e.reset();
+
+ return steps;
+ }
+
+ bool setNeedsIncrementalBarrier(bool needs) override {
+ MOZ_ASSERT(needsBarrier != needs);
+ needsBarrier = needs;
+ return true;
+ }
+
+ bool needsIncrementalBarrier() const override { return needsBarrier; }
+
+ private:
+ using Entry = typename Map::Entry;
+
+ static bool entryNeedsSweep(const Entry& prior) {
+ Key key(prior.key());
+ Value value(prior.value());
+ bool result = MapSweepPolicy::needsSweep(&key, &value);
+ MOZ_ASSERT(prior.key() == key); // We shouldn't update here.
+ MOZ_ASSERT(prior.value() == value); // We shouldn't update here.
+ return result;
+ }
+
+ public:
+ using Lookup = typename Map::Lookup;
+ using Ptr = typename Map::Ptr;
+ using AddPtr = typename Map::AddPtr;
+
+ struct Range {
+ explicit Range(const typename Map::Range& r) : range(r) { settle(); }
+ Range() = default;
+
+ bool empty() const { return range.empty(); }
+ const Entry& front() const { return range.front(); }
+
+ void popFront() {
+ range.popFront();
+ settle();
+ }
+
+ private:
+ typename Map::Range range;
+
+ void settle() {
+ while (!empty() && entryNeedsSweep(front())) {
+ popFront();
+ }
+ }
+ };
+
+ struct Enum : public Map::Enum {
+ explicit Enum(Self& cache) : Map::Enum(cache.map) {
+ // This operation is not allowed while barriers are in place as we
+ // may also need to enumerate the set for sweeping.
+ MOZ_ASSERT(!cache.needsBarrier);
+ }
+ };
+
+ Ptr lookup(const Lookup& l) const {
+ Ptr ptr = map.lookup(l);
+ if (needsBarrier && ptr && entryNeedsSweep(*ptr)) {
+ const_cast<Map&>(map).remove(ptr);
+ return Ptr();
+ }
+ return ptr;
+ }
+
+ AddPtr lookupForAdd(const Lookup& l) {
+ AddPtr ptr = map.lookupForAdd(l);
+ if (needsBarrier && ptr && entryNeedsSweep(*ptr)) {
+ const_cast<Map&>(map).remove(ptr);
+ return map.lookupForAdd(l);
+ }
+ return ptr;
+ }
+
+ Range all() const { return Range(map.all()); }
+
+ bool empty() const {
+ // This operation is not currently allowed while barriers are in place
+ // as it would require iterating the map and the caller expects a
+ // constant time operation.
+ MOZ_ASSERT(!needsBarrier);
+ return map.empty();
+ }
+
+ uint32_t count() const {
+ // This operation is not currently allowed while barriers are in place
+ // as it would require iterating the set and the caller expects a
+ // constant time operation.
+ MOZ_ASSERT(!needsBarrier);
+ return map.count();
+ }
+
+ size_t capacity() const { return map.capacity(); }
+
+ bool has(const Lookup& l) const { return lookup(l).found(); }
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return map.sizeOfExcludingThis(mallocSizeOf);
+ }
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return mallocSizeOf(this) + map.shallowSizeOfExcludingThis(mallocSizeOf);
+ }
+
+ void clear() {
+ // This operation is not currently allowed while barriers are in place
+ // since it doesn't make sense to clear a cache while it is being swept.
+ MOZ_ASSERT(!needsBarrier);
+ map.clear();
+ }
+
+ void clearAndCompact() {
+ // This operation is not currently allowed while barriers are in place
+ // since it doesn't make sense to clear a cache while it is being swept.
+ MOZ_ASSERT(!needsBarrier);
+ map.clearAndCompact();
+ }
+
+ void remove(Ptr p) {
+ // This currently supports removing entries during incremental
+ // sweeping. If we allow these tables to be swept incrementally this may
+ // no longer be possible.
+ map.remove(p);
+ }
+
+ void remove(const Lookup& l) {
+ Ptr p = lookup(l);
+ if (p) {
+ remove(p);
+ }
+ }
+
+ template <typename KeyInput, typename ValueInput>
+ bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
+ return map.add(p, std::forward<KeyInput>(k), std::forward<ValueInput>(v));
+ }
+
+ template <typename KeyInput, typename ValueInput>
+ bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
+ return map.relookupOrAdd(p, std::forward<KeyInput>(k),
+ std::forward<ValueInput>(v));
+ }
+
+ template <typename KeyInput, typename ValueInput>
+ bool put(KeyInput&& k, ValueInput&& v) {
+ return map.put(std::forward<KeyInput>(k), std::forward<ValueInput>(v));
+ }
+
+ template <typename KeyInput, typename ValueInput>
+ bool putNew(KeyInput&& k, ValueInput&& v) {
+ return map.putNew(std::forward<KeyInput>(k), std::forward<ValueInput>(v));
+ }
+} JS_HAZ_NON_GC_POINTER;
+
+// Specialize WeakCache for GCHashSet to provide a barriered set that does not
+// need to be swept immediately.
+template <typename T, typename HashPolicy, typename AllocPolicy>
+class WeakCache<GCHashSet<T, HashPolicy, AllocPolicy>>
+ : protected detail::WeakCacheBase {
+ using Set = GCHashSet<T, HashPolicy, AllocPolicy>;
+ using Self = WeakCache<Set>;
+
+ Set set;
+ bool needsBarrier;
+
+ public:
+ using Entry = typename Set::Entry;
+
+ template <typename... Args>
+ explicit WeakCache(Zone* zone, Args&&... args)
+ : WeakCacheBase(zone),
+ set(std::forward<Args>(args)...),
+ needsBarrier(false) {}
+ template <typename... Args>
+ explicit WeakCache(JSRuntime* rt, Args&&... args)
+ : WeakCacheBase(rt),
+ set(std::forward<Args>(args)...),
+ needsBarrier(false) {}
+
+ size_t sweep(js::gc::StoreBuffer* sbToLock) override {
+ size_t steps = set.count();
+
+ // Create an Enum and sweep the table entries. It's not necessary to take
+ // the store buffer lock yet.
+ mozilla::Maybe<typename Set::Enum> e;
+ e.emplace(set);
+ set.sweepEntries(e.ref());
+
+ // Destroy the Enum, potentially rehashing or resizing the table. Since this
+ // can access the store buffer, we need to take a lock for this if we're
+ // called off main thread.
+ mozilla::Maybe<js::gc::AutoLockStoreBuffer> lock;
+ if (sbToLock) {
+ lock.emplace(sbToLock);
+ }
+ e.reset();
+
+ return steps;
+ }
+
+ bool needsSweep() override { return set.needsSweep(); }
+
+ bool setNeedsIncrementalBarrier(bool needs) override {
+ MOZ_ASSERT(needsBarrier != needs);
+ needsBarrier = needs;
+ return true;
+ }
+
+ bool needsIncrementalBarrier() const override { return needsBarrier; }
+
+ private:
+ static bool entryNeedsSweep(const Entry& prior) {
+ Entry entry(prior);
+ bool result = GCPolicy<T>::needsSweep(&entry);
+ MOZ_ASSERT(prior == entry); // We shouldn't update here.
+ return result;
+ }
+
+ public:
+ using Lookup = typename Set::Lookup;
+ using Ptr = typename Set::Ptr;
+ using AddPtr = typename Set::AddPtr;
+
+ struct Range {
+ explicit Range(const typename Set::Range& r) : range(r) { settle(); }
+ Range() = default;
+
+ bool empty() const { return range.empty(); }
+ const Entry& front() const { return range.front(); }
+
+ void popFront() {
+ range.popFront();
+ settle();
+ }
+
+ private:
+ typename Set::Range range;
+
+ void settle() {
+ while (!empty() && entryNeedsSweep(front())) {
+ popFront();
+ }
+ }
+ };
+
+ struct Enum : public Set::Enum {
+ explicit Enum(Self& cache) : Set::Enum(cache.set) {
+ // This operation is not allowed while barriers are in place as we
+ // may also need to enumerate the set for sweeping.
+ MOZ_ASSERT(!cache.needsBarrier);
+ }
+ };
+
+ Ptr lookup(const Lookup& l) const {
+ Ptr ptr = set.lookup(l);
+ if (needsBarrier && ptr && entryNeedsSweep(*ptr)) {
+ const_cast<Set&>(set).remove(ptr);
+ return Ptr();
+ }
+ return ptr;
+ }
+
+ AddPtr lookupForAdd(const Lookup& l) {
+ AddPtr ptr = set.lookupForAdd(l);
+ if (needsBarrier && ptr && entryNeedsSweep(*ptr)) {
+ const_cast<Set&>(set).remove(ptr);
+ return set.lookupForAdd(l);
+ }
+ return ptr;
+ }
+
+ Range all() const { return Range(set.all()); }
+
+ bool empty() const {
+ // This operation is not currently allowed while barriers are in place
+ // as it would require iterating the set and the caller expects a
+ // constant time operation.
+ MOZ_ASSERT(!needsBarrier);
+ return set.empty();
+ }
+
+ uint32_t count() const {
+ // This operation is not currently allowed while barriers are in place
+ // as it would require iterating the set and the caller expects a
+ // constant time operation.
+ MOZ_ASSERT(!needsBarrier);
+ return set.count();
+ }
+
+ size_t capacity() const { return set.capacity(); }
+
+ bool has(const Lookup& l) const { return lookup(l).found(); }
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return set.shallowSizeOfExcludingThis(mallocSizeOf);
+ }
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return mallocSizeOf(this) + set.shallowSizeOfExcludingThis(mallocSizeOf);
+ }
+
+ void clear() {
+ // This operation is not currently allowed while barriers are in place
+ // since it doesn't make sense to clear a cache while it is being swept.
+ MOZ_ASSERT(!needsBarrier);
+ set.clear();
+ }
+
+ void clearAndCompact() {
+ // This operation is not currently allowed while barriers are in place
+ // since it doesn't make sense to clear a cache while it is being swept.
+ MOZ_ASSERT(!needsBarrier);
+ set.clearAndCompact();
+ }
+
+ void remove(Ptr p) {
+ // This currently supports removing entries during incremental
+ // sweeping. If we allow these tables to be swept incrementally this may
+ // no longer be possible.
+ set.remove(p);
+ }
+
+ void remove(const Lookup& l) {
+ Ptr p = lookup(l);
+ if (p) {
+ remove(p);
+ }
+ }
+
+ template <typename TInput>
+ bool add(AddPtr& p, TInput&& t) {
+ return set.add(p, std::forward<TInput>(t));
+ }
+
+ template <typename TInput>
+ bool relookupOrAdd(AddPtr& p, const Lookup& l, TInput&& t) {
+ return set.relookupOrAdd(p, l, std::forward<TInput>(t));
+ }
+
+ template <typename TInput>
+ bool put(TInput&& t) {
+ return set.put(std::forward<TInput>(t));
+ }
+
+ template <typename TInput>
+ bool putNew(TInput&& t) {
+ return set.putNew(std::forward<TInput>(t));
+ }
+
+ template <typename TInput>
+ bool putNew(const Lookup& l, TInput&& t) {
+ return set.putNew(l, std::forward<TInput>(t));
+ }
+} JS_HAZ_NON_GC_POINTER;
+
+} // namespace JS
+
+#endif /* GCHashTable_h */
diff --git a/js/public/GCPolicyAPI.h b/js/public/GCPolicyAPI.h
new file mode 100644
index 0000000000..14e35dffb3
--- /dev/null
+++ b/js/public/GCPolicyAPI.h
@@ -0,0 +1,242 @@
+/* -*- 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/. */
+
+// GC Policy Mechanism
+
+// A GCPolicy controls how the GC interacts with both direct pointers to GC
+// things (e.g. JSObject* or JSString*), tagged and/or optional pointers to GC
+// things (e.g. Value or jsid), and C++ container types (e.g.
+// JSPropertyDescriptor or GCHashMap).
+//
+// The GCPolicy provides at a minimum:
+//
+// static void trace(JSTracer, T* tp, const char* name)
+// - Trace the edge |*tp|, calling the edge |name|. Containers like
+// GCHashMap and GCHashSet use this method to trace their children.
+//
+// static bool needsSweep(T* tp)
+// - [DEPRECATED], use traceWeak instead.
+// Return true if |*tp| is about to be finalized. Otherwise, update the
+// edge for moving GC, and return false. Containers like GCHashMap and
+// GCHashSet use this method to decide when to remove an entry: if this
+// function returns true on a key/value/member/etc, its entry is dropped
+// from the container. Specializing this method is the standard way to
+// get custom weak behavior from a container type.
+//
+// static bool traceWeak(T* tp)
+// - Return false if |*tp| has been set to nullptr. Otherwise, update the
+// edge for moving GC, and return true. Containers like GCHashMap and
+// GCHashSet use this method to decide when to remove an entry: if this
+// function returns false on a key/value/member/etc, its entry is
+// dropped from the container. Specializing this method is the standard
+// way to get custom weak behavior from a container type.
+//
+// static bool isValid(const T& t)
+// - Return false only if |t| is corrupt in some way. The built-in GC
+// types do some memory layout checks. For debugging only; it is ok
+// to always return true or even to omit this member entirely.
+//
+// The default GCPolicy<T> assumes that T has a default constructor and |trace|
+// and |traceWeak| methods, and forwards to them. GCPolicy has appropriate
+// specializations for pointers to GC things and pointer-like types like
+// JS::Heap<T> and mozilla::UniquePtr<T>.
+//
+// There are some stock structs your specializations can inherit from.
+// IgnoreGCPolicy<T> does nothing. StructGCPolicy<T> forwards the methods to the
+// referent type T.
+
+#ifndef GCPolicyAPI_h
+#define GCPolicyAPI_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+
+#include <type_traits>
+
+#include "js/GCTypeMacros.h" // JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE
+#include "js/TraceKind.h"
+#include "js/TracingAPI.h"
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+// Defines a policy for container types with non-GC, i.e. C storage. This
+// policy dispatches to the underlying struct for GC interactions.
+template <typename T>
+struct StructGCPolicy {
+ static_assert(!std::is_pointer_v<T>,
+ "Pointer type not allowed for StructGCPolicy");
+
+ static void trace(JSTracer* trc, T* tp, const char* name) { tp->trace(trc); }
+
+ static void sweep(T* tp) { return tp->sweep(); }
+
+ static bool needsSweep(T* tp) { return tp->needsSweep(); }
+
+ static bool traceWeak(JSTracer* trc, T* tp) { return tp->traceWeak(trc); }
+
+ static bool isValid(const T& tp) { return true; }
+};
+
+// The default GC policy attempts to defer to methods on the underlying type.
+// Most C++ structures that contain a default constructor, a trace function and
+// a sweep function will work out of the box with Rooted, Handle, GCVector,
+// and GCHash{Set,Map}.
+template <typename T>
+struct GCPolicy : public StructGCPolicy<T> {};
+
+// This policy ignores any GC interaction, e.g. for non-GC types.
+template <typename T>
+struct IgnoreGCPolicy {
+ static void trace(JSTracer* trc, T* t, const char* name) {}
+ static bool needsSweep(T* v) { return false; }
+ static bool traceWeak(JSTracer*, T* v) { return true; }
+ static bool isValid(const T& v) { return true; }
+};
+template <>
+struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
+template <>
+struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
+
+template <typename T>
+struct GCPointerPolicy {
+ static_assert(std::is_pointer_v<T>,
+ "Non-pointer type not allowed for GCPointerPolicy");
+
+ static void trace(JSTracer* trc, T* vp, const char* name) {
+ // It's not safe to trace unbarriered pointers except as part of root
+ // marking.
+ UnsafeTraceRoot(trc, vp, name);
+ }
+ static bool needsSweep(T* vp) {
+ if (*vp) {
+ return js::gc::IsAboutToBeFinalizedUnbarriered(vp);
+ }
+ return false;
+ }
+ static bool traceWeak(JSTracer* trc, T* vp) {
+ if (*vp) {
+ return js::TraceManuallyBarrieredWeakEdge(trc, vp, "traceWeak");
+ }
+ return true;
+ }
+ static bool isTenured(T v) { return !js::gc::IsInsideNursery(v); }
+ static bool isValid(T v) { return js::gc::IsCellPointerValidOrNull(v); }
+};
+#define EXPAND_SPECIALIZE_GCPOLICY(Type) \
+ template <> \
+ struct GCPolicy<Type> : public GCPointerPolicy<Type> {}; \
+ template <> \
+ struct GCPolicy<Type const> : public GCPointerPolicy<Type const> {};
+JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(EXPAND_SPECIALIZE_GCPOLICY)
+#undef EXPAND_SPECIALIZE_GCPOLICY
+
+template <typename T>
+struct NonGCPointerPolicy {
+ static void trace(JSTracer* trc, T* vp, const char* name) {
+ if (*vp) {
+ (*vp)->trace(trc);
+ }
+ }
+ static bool needsSweep(T* vp) {
+ if (*vp) {
+ return (*vp)->needsSweep();
+ }
+ return false;
+ }
+ static bool traceWeak(JSTracer* trc, T* vp) {
+ if (*vp) {
+ return (*vp)->traceWeak(trc);
+ }
+ return true;
+ }
+
+ static bool isValid(T v) { return true; }
+};
+
+template <typename T>
+struct GCPolicy<JS::Heap<T>> {
+ static void trace(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
+ TraceEdge(trc, thingp, name);
+ }
+ static bool needsSweep(JS::Heap<T>* thingp) {
+ return *thingp && js::gc::EdgeNeedsSweep(thingp);
+ }
+ static bool traceWeak(JSTracer* trc, JS::Heap<T>* thingp) {
+ if (*thingp) {
+ return js::TraceWeakEdge(trc, thingp, "traceWeak");
+ }
+ return true;
+ }
+};
+
+// GCPolicy<UniquePtr<T>> forwards the contained pointer to GCPolicy<T>.
+template <typename T, typename D>
+struct GCPolicy<mozilla::UniquePtr<T, D>> {
+ static void trace(JSTracer* trc, mozilla::UniquePtr<T, D>* tp,
+ const char* name) {
+ if (tp->get()) {
+ GCPolicy<T>::trace(trc, tp->get(), name);
+ }
+ }
+ static bool needsSweep(mozilla::UniquePtr<T, D>* tp) {
+ if (tp->get()) {
+ return GCPolicy<T>::needsSweep(tp->get());
+ }
+ return false;
+ }
+ static bool traceWeak(JSTracer* trc, mozilla::UniquePtr<T, D>* tp) {
+ if (tp->get()) {
+ return GCPolicy<T>::traceWeak(trc, tp->get());
+ }
+ return true;
+ }
+ static bool isValid(const mozilla::UniquePtr<T, D>& t) {
+ if (t.get()) {
+ return GCPolicy<T>::isValid(*t.get());
+ }
+ return true;
+ }
+};
+
+template <>
+struct GCPolicy<mozilla::Nothing> : public IgnoreGCPolicy<mozilla::Nothing> {};
+
+// GCPolicy<Maybe<T>> forwards tracing/sweeping to GCPolicy<T*> if
+// when the Maybe<T> is full.
+template <typename T>
+struct GCPolicy<mozilla::Maybe<T>> {
+ static void trace(JSTracer* trc, mozilla::Maybe<T>* tp, const char* name) {
+ if (tp->isSome()) {
+ GCPolicy<T>::trace(trc, tp->ptr(), name);
+ }
+ }
+ static bool needsSweep(mozilla::Maybe<T>* tp) {
+ if (tp->isSome()) {
+ return GCPolicy<T>::needsSweep(tp->ptr());
+ }
+ return false;
+ }
+ static bool traceWeak(JSTracer* trc, mozilla::Maybe<T>* tp) {
+ if (tp->isSome()) {
+ return GCPolicy<T>::traceWeak(trc, tp->ptr());
+ }
+ return true;
+ }
+ static bool isValid(const mozilla::Maybe<T>& t) {
+ if (t.isSome()) {
+ return GCPolicy<T>::isValid(t.ref());
+ }
+ return true;
+ }
+};
+
+template <>
+struct GCPolicy<JS::Realm*>; // see Realm.h
+
+} // namespace JS
+
+#endif // GCPolicyAPI_h
diff --git a/js/public/GCTypeMacros.h b/js/public/GCTypeMacros.h
new file mode 100644
index 0000000000..1f59db410a
--- /dev/null
+++ b/js/public/GCTypeMacros.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/. */
+
+/*
+ * Higher-order macros enumerating public untagged and tagged GC pointer types.
+ */
+
+#ifndef GCTypeMacros_h
+#define GCTypeMacros_h
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+class JS_PUBLIC_API JSAtom;
+class JS_PUBLIC_API JSFunction;
+class JS_PUBLIC_API JSObject;
+class JS_PUBLIC_API JSScript;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+class JS_PUBLIC_API BigInt;
+struct JS_PUBLIC_API PropertyKey;
+class JS_PUBLIC_API Symbol;
+class JS_PUBLIC_API Value;
+} // namespace JS
+
+// Expand the given macro D for each public GC pointer.
+#define JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
+ D(JS::BigInt*) \
+ D(JS::Symbol*) \
+ D(JSAtom*) \
+ D(JSFunction*) \
+ D(JSLinearString*) \
+ D(JSObject*) \
+ D(JSScript*) \
+ D(JSString*)
+
+// Expand the given macro D for each public tagged GC pointer type.
+#define JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
+ D(JS::Value) \
+ D(JS::PropertyKey) // i.e. jsid
+
+#endif // GCTypeMacros_h
diff --git a/js/public/GCVariant.h b/js/public/GCVariant.h
new file mode 100644
index 0000000000..0c62f17c22
--- /dev/null
+++ b/js/public/GCVariant.h
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_GCVariant_h
+#define js_GCVariant_h
+
+#include "mozilla/Variant.h"
+
+#include <type_traits>
+
+#include "js/GCPolicyAPI.h"
+#include "js/RootingAPI.h"
+#include "js/TracingAPI.h"
+
+namespace JS {
+
+// These template specializations allow Variant to be used inside GC wrappers.
+//
+// When matching on GC wrappers around Variants, matching should be done on
+// the wrapper itself. The matcher class's methods should take Handles or
+// MutableHandles. For example,
+//
+// struct MyMatcher
+// {
+// using ReturnType = const char*;
+// ReturnType match(HandleObject o) { return "object"; }
+// ReturnType match(HandleScript s) { return "script"; }
+// };
+//
+// Rooted<Variant<JSObject*, JSScript*>> v(cx, someScript);
+// MyMatcher mm;
+// v.match(mm);
+//
+// If you get compile errors about inability to upcast subclasses (e.g., from
+// NativeObject* to JSObject*) and are inside js/src, be sure to also include
+// "gc/Policy.h".
+
+namespace detail {
+
+template <typename... Ts>
+struct GCVariantImplementation;
+
+// The base case.
+template <typename T>
+struct GCVariantImplementation<T> {
+ template <typename ConcreteVariant>
+ static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) {
+ T& thing = v->template as<T>();
+ GCPolicy<T>::trace(trc, &thing, name);
+ }
+
+ template <typename Matcher, typename ConcreteVariant>
+ static typename Matcher::ReturnType match(Matcher& matcher,
+ Handle<ConcreteVariant> v) {
+ const T& thing = v.get().template as<T>();
+ return matcher.match(Handle<T>::fromMarkedLocation(&thing));
+ }
+
+ template <typename Matcher, typename ConcreteVariant>
+ static typename Matcher::ReturnType match(Matcher& matcher,
+ MutableHandle<ConcreteVariant> v) {
+ T& thing = v.get().template as<T>();
+ return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing));
+ }
+};
+
+// The inductive case.
+template <typename T, typename... Ts>
+struct GCVariantImplementation<T, Ts...> {
+ using Next = GCVariantImplementation<Ts...>;
+
+ template <typename ConcreteVariant>
+ static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) {
+ if (v->template is<T>()) {
+ T& thing = v->template as<T>();
+ GCPolicy<T>::trace(trc, &thing, name);
+ } else {
+ Next::trace(trc, v, name);
+ }
+ }
+
+ template <typename Matcher, typename ConcreteVariant>
+ static typename Matcher::ReturnType match(Matcher& matcher,
+ Handle<ConcreteVariant> v) {
+ if (v.get().template is<T>()) {
+ const T& thing = v.get().template as<T>();
+ return matcher.match(Handle<T>::fromMarkedLocation(&thing));
+ }
+ return Next::match(matcher, v);
+ }
+
+ template <typename Matcher, typename ConcreteVariant>
+ static typename Matcher::ReturnType match(Matcher& matcher,
+ MutableHandle<ConcreteVariant> v) {
+ if (v.get().template is<T>()) {
+ T& thing = v.get().template as<T>();
+ return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing));
+ }
+ return Next::match(matcher, v);
+ }
+};
+
+} // namespace detail
+
+template <typename... Ts>
+struct GCPolicy<mozilla::Variant<Ts...>> {
+ using Impl = detail::GCVariantImplementation<Ts...>;
+
+ static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v,
+ const char* name) {
+ Impl::trace(trc, v, name);
+ }
+
+ static bool isValid(const mozilla::Variant<Ts...>& v) {
+ return v.match([](auto& v) {
+ return GCPolicy<std::remove_reference_t<decltype(v)>>::isValid(v);
+ });
+ }
+};
+
+} // namespace JS
+
+namespace js {
+
+template <typename Wrapper, typename... Ts>
+class WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper> {
+ using Impl = JS::detail::GCVariantImplementation<Ts...>;
+ using Variant = mozilla::Variant<Ts...>;
+
+ const Variant& variant() const {
+ return static_cast<const Wrapper*>(this)->get();
+ }
+
+ public:
+ template <typename T>
+ bool is() const {
+ return variant().template is<T>();
+ }
+
+ template <typename T>
+ JS::Handle<T> as() const {
+ return Handle<T>::fromMarkedLocation(&variant().template as<T>());
+ }
+
+ template <typename Matcher>
+ typename Matcher::ReturnType match(Matcher& matcher) const {
+ return Impl::match(matcher,
+ JS::Handle<Variant>::fromMarkedLocation(&variant()));
+ }
+};
+
+template <typename Wrapper, typename... Ts>
+class MutableWrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper>
+ : public WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper> {
+ using Impl = JS::detail::GCVariantImplementation<Ts...>;
+ using Variant = mozilla::Variant<Ts...>;
+
+ const Variant& variant() const {
+ return static_cast<const Wrapper*>(this)->get();
+ }
+ Variant& variant() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+ template <typename T>
+ JS::MutableHandle<T> as() {
+ return JS::MutableHandle<T>::fromMarkedLocation(
+ &variant().template as<T>());
+ }
+
+ template <typename Matcher>
+ typename Matcher::ReturnType match(Matcher& matcher) {
+ return Impl::match(
+ matcher, JS::MutableHandle<Variant>::fromMarkedLocation(&variant()));
+ }
+};
+
+} // namespace js
+
+#endif // js_GCVariant_h
diff --git a/js/public/GCVector.h b/js/public/GCVector.h
new file mode 100644
index 0000000000..a6336b303a
--- /dev/null
+++ b/js/public/GCVector.h
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_GCVector_h
+#define js_GCVector_h
+
+#include "mozilla/Vector.h"
+
+#include "js/AllocPolicy.h"
+#include "js/GCPolicyAPI.h"
+#include "js/RootingAPI.h"
+#include "js/TracingAPI.h"
+#include "js/Vector.h"
+
+namespace JS {
+
+// A GCVector is a Vector with an additional trace method that knows how
+// to visit all of the items stored in the Vector. For vectors that contain GC
+// things, this is usually more convenient than manually iterating and marking
+// the contents.
+//
+// Most types of GC pointers as keys and values can be traced with no extra
+// infrastructure. For structs and non-gc-pointer members, ensure that there is
+// a specialization of GCPolicy<T> with an appropriate trace method available
+// to handle the custom type. Generic helpers can be found in
+// js/public/TracingAPI.h.
+//
+// Note that although this Vector's trace will deal correctly with moved items,
+// it does not itself know when to barrier or trace items. To function properly
+// it must either be used with Rooted, or barriered and traced manually.
+template <typename T, size_t MinInlineCapacity = 0,
+ typename AllocPolicy = js::TempAllocPolicy>
+class GCVector {
+ mozilla::Vector<T, MinInlineCapacity, AllocPolicy> vector;
+
+ public:
+ using ElementType = T;
+
+ explicit GCVector(AllocPolicy alloc = AllocPolicy())
+ : vector(std::move(alloc)) {}
+
+ GCVector(GCVector&& vec) : vector(std::move(vec.vector)) {}
+
+ GCVector& operator=(GCVector&& vec) {
+ vector = std::move(vec.vector);
+ return *this;
+ }
+
+ size_t length() const { return vector.length(); }
+ bool empty() const { return vector.empty(); }
+ size_t capacity() const { return vector.capacity(); }
+
+ T* begin() { return vector.begin(); }
+ const T* begin() const { return vector.begin(); }
+
+ T* end() { return vector.end(); }
+ const T* end() const { return vector.end(); }
+
+ T& operator[](size_t i) { return vector[i]; }
+ const T& operator[](size_t i) const { return vector[i]; }
+
+ T& back() { return vector.back(); }
+ const T& back() const { return vector.back(); }
+
+ bool initCapacity(size_t cap) { return vector.initCapacity(cap); }
+ MOZ_MUST_USE bool reserve(size_t req) { return vector.reserve(req); }
+ void shrinkBy(size_t amount) { return vector.shrinkBy(amount); }
+ void shrinkTo(size_t newLen) { return vector.shrinkTo(newLen); }
+ MOZ_MUST_USE bool growBy(size_t amount) { return vector.growBy(amount); }
+ MOZ_MUST_USE bool resize(size_t newLen) { return vector.resize(newLen); }
+
+ void clear() { return vector.clear(); }
+ void clearAndFree() { return vector.clearAndFree(); }
+
+ template <typename U>
+ bool append(U&& item) {
+ return vector.append(std::forward<U>(item));
+ }
+
+ void erase(T* it) { vector.erase(it); }
+ void erase(T* begin, T* end) { vector.erase(begin, end); }
+ template <typename Pred>
+ void eraseIf(Pred pred) {
+ vector.eraseIf(pred);
+ }
+ template <typename U>
+ void eraseIfEqual(const U& u) {
+ vector.eraseIfEqual(u);
+ }
+
+ template <typename... Args>
+ MOZ_MUST_USE bool emplaceBack(Args&&... args) {
+ return vector.emplaceBack(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void infallibleEmplaceBack(Args&&... args) {
+ vector.infallibleEmplaceBack(std::forward<Args>(args)...);
+ }
+
+ template <typename U>
+ void infallibleAppend(U&& aU) {
+ return vector.infallibleAppend(std::forward<U>(aU));
+ }
+ void infallibleAppendN(const T& aT, size_t aN) {
+ return vector.infallibleAppendN(aT, aN);
+ }
+ template <typename U>
+ void infallibleAppend(const U* aBegin, const U* aEnd) {
+ return vector.infallibleAppend(aBegin, aEnd);
+ }
+ template <typename U>
+ void infallibleAppend(const U* aBegin, size_t aLength) {
+ return vector.infallibleAppend(aBegin, aLength);
+ }
+
+ template <typename U>
+ MOZ_MUST_USE bool appendAll(const U& aU) {
+ return vector.append(aU.begin(), aU.end());
+ }
+ template <typename T2, size_t MinInlineCapacity2, typename AllocPolicy2>
+ MOZ_MUST_USE bool appendAll(
+ GCVector<T2, MinInlineCapacity2, AllocPolicy2>&& aU) {
+ return vector.appendAll(aU.begin(), aU.end());
+ }
+
+ MOZ_MUST_USE bool appendN(const T& val, size_t count) {
+ return vector.appendN(val, count);
+ }
+
+ template <typename U>
+ MOZ_MUST_USE bool append(const U* aBegin, const U* aEnd) {
+ return vector.append(aBegin, aEnd);
+ }
+ template <typename U>
+ MOZ_MUST_USE bool append(const U* aBegin, size_t aLength) {
+ return vector.append(aBegin, aLength);
+ }
+
+ void popBack() { return vector.popBack(); }
+ T popCopy() { return vector.popCopy(); }
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return vector.sizeOfExcludingThis(mallocSizeOf);
+ }
+
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+ return vector.sizeOfIncludingThis(mallocSizeOf);
+ }
+
+ void trace(JSTracer* trc) {
+ for (auto& elem : vector) {
+ GCPolicy<T>::trace(trc, &elem, "vector element");
+ }
+ }
+
+ bool traceWeak(JSTracer* trc) {
+ T* src = begin();
+ T* dst = begin();
+ while (src != end()) {
+ if (GCPolicy<T>::traceWeak(trc, src)) {
+ if (src != dst) {
+ *dst = std::move(*src);
+ }
+ dst++;
+ }
+ src++;
+ }
+
+ MOZ_ASSERT(dst <= end());
+ shrinkBy(end() - dst);
+ return !empty();
+ }
+
+ bool needsSweep() const { return !this->empty(); }
+
+ void sweep() {
+ T* src = begin();
+ T* dst = begin();
+ while (src != end()) {
+ if (!GCPolicy<T>::needsSweep(src)) {
+ if (src != dst) {
+ *dst = std::move(*src);
+ }
+ dst++;
+ }
+ src++;
+ }
+
+ MOZ_ASSERT(dst <= end());
+ shrinkBy(end() - dst);
+ }
+};
+
+// AllocPolicy is optional. It has a default value declared in TypeDecls.h
+template <typename T, typename AllocPolicy>
+class MOZ_STACK_CLASS StackGCVector : public GCVector<T, 8, AllocPolicy> {
+ public:
+ using Base = GCVector<T, 8, AllocPolicy>;
+
+ private:
+ // Inherit constructor from GCVector.
+ using Base::Base;
+};
+
+} // namespace JS
+
+namespace js {
+
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class WrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrapper> {
+ using Vec = JS::GCVector<T, Capacity, AllocPolicy>;
+ const Vec& vec() const { return static_cast<const Wrapper*>(this)->get(); }
+
+ public:
+ const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
+ size_t length() const { return vec().length(); }
+ bool empty() const { return vec().empty(); }
+ size_t capacity() const { return vec().capacity(); }
+ const T* begin() const { return vec().begin(); }
+ const T* end() const { return vec().end(); }
+ const T& back() const { return vec().back(); }
+
+ JS::Handle<T> operator[](size_t aIndex) const {
+ return JS::Handle<T>::fromMarkedLocation(&vec().operator[](aIndex));
+ }
+};
+
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class MutableWrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>,
+ Wrapper>
+ : public WrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>,
+ Wrapper> {
+ using Vec = JS::GCVector<T, Capacity, AllocPolicy>;
+ const Vec& vec() const { return static_cast<const Wrapper*>(this)->get(); }
+ Vec& vec() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+ const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
+ AllocPolicy& allocPolicy() { return vec().allocPolicy(); }
+ const T* begin() const { return vec().begin(); }
+ T* begin() { return vec().begin(); }
+ const T* end() const { return vec().end(); }
+ T* end() { return vec().end(); }
+ const T& back() const { return vec().back(); }
+ T& back() { return vec().back(); }
+
+ JS::Handle<T> operator[](size_t aIndex) const {
+ return JS::Handle<T>::fromMarkedLocation(&vec().operator[](aIndex));
+ }
+ JS::MutableHandle<T> operator[](size_t aIndex) {
+ return JS::MutableHandle<T>::fromMarkedLocation(&vec().operator[](aIndex));
+ }
+
+ MOZ_MUST_USE bool initCapacity(size_t aRequest) {
+ return vec().initCapacity(aRequest);
+ }
+ MOZ_MUST_USE bool reserve(size_t aRequest) { return vec().reserve(aRequest); }
+ void shrinkBy(size_t aIncr) { vec().shrinkBy(aIncr); }
+ MOZ_MUST_USE bool growBy(size_t aIncr) { return vec().growBy(aIncr); }
+ MOZ_MUST_USE bool resize(size_t aNewLength) {
+ return vec().resize(aNewLength);
+ }
+ MOZ_MUST_USE bool growByUninitialized(size_t aIncr) {
+ return vec().growByUninitialized(aIncr);
+ }
+ void infallibleGrowByUninitialized(size_t aIncr) {
+ vec().infallibleGrowByUninitialized(aIncr);
+ }
+ MOZ_MUST_USE bool resizeUninitialized(size_t aNewLength) {
+ return vec().resizeUninitialized(aNewLength);
+ }
+ void clear() { vec().clear(); }
+ void clearAndFree() { vec().clearAndFree(); }
+ template <typename U>
+ MOZ_MUST_USE bool append(U&& aU) {
+ return vec().append(std::forward<U>(aU));
+ }
+ template <typename... Args>
+ MOZ_MUST_USE bool emplaceBack(Args&&... aArgs) {
+ return vec().emplaceBack(std::forward<Args>(aArgs)...);
+ }
+ template <typename... Args>
+ void infallibleEmplaceBack(Args&&... args) {
+ vec().infallibleEmplaceBack(std::forward<Args>(args)...);
+ }
+ template <typename U>
+ MOZ_MUST_USE bool appendAll(U&& aU) {
+ return vec().appendAll(aU);
+ }
+ MOZ_MUST_USE bool appendN(const T& aT, size_t aN) {
+ return vec().appendN(aT, aN);
+ }
+ template <typename U>
+ MOZ_MUST_USE bool append(const U* aBegin, const U* aEnd) {
+ return vec().append(aBegin, aEnd);
+ }
+ template <typename U>
+ MOZ_MUST_USE bool append(const U* aBegin, size_t aLength) {
+ return vec().append(aBegin, aLength);
+ }
+ template <typename U>
+ void infallibleAppend(U&& aU) {
+ vec().infallibleAppend(std::forward<U>(aU));
+ }
+ void infallibleAppendN(const T& aT, size_t aN) {
+ vec().infallibleAppendN(aT, aN);
+ }
+ template <typename U>
+ void infallibleAppend(const U* aBegin, const U* aEnd) {
+ vec().infallibleAppend(aBegin, aEnd);
+ }
+ template <typename U>
+ void infallibleAppend(const U* aBegin, size_t aLength) {
+ vec().infallibleAppend(aBegin, aLength);
+ }
+ void popBack() { vec().popBack(); }
+ T popCopy() { return vec().popCopy(); }
+ template <typename U>
+ T* insert(T* aP, U&& aVal) {
+ return vec().insert(aP, std::forward<U>(aVal));
+ }
+ void erase(T* aT) { vec().erase(aT); }
+ void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
+ template <typename Pred>
+ void eraseIf(Pred pred) {
+ vec().eraseIf(pred);
+ }
+ template <typename U>
+ void eraseIfEqual(const U& u) {
+ vec().eraseIfEqual(u);
+ }
+};
+
+template <typename Wrapper, typename T, typename AllocPolicy>
+class WrappedPtrOperations<JS::StackGCVector<T, AllocPolicy>, Wrapper>
+ : public WrappedPtrOperations<
+ typename JS::StackGCVector<T, AllocPolicy>::Base, Wrapper> {};
+
+template <typename Wrapper, typename T, typename AllocPolicy>
+class MutableWrappedPtrOperations<JS::StackGCVector<T, AllocPolicy>, Wrapper>
+ : public MutableWrappedPtrOperations<
+ typename JS::StackGCVector<T, AllocPolicy>::Base, Wrapper> {};
+
+} // namespace js
+
+namespace JS {
+
+// An automatically rooted GCVector for stack use.
+template <typename T>
+class RootedVector : public Rooted<StackGCVector<T>> {
+ using Vec = StackGCVector<T>;
+ using Base = Rooted<Vec>;
+
+ public:
+ explicit RootedVector(JSContext* cx) : Base(cx, Vec(cx)) {}
+};
+
+// For use in rust code, an analog to RootedVector that doesn't require
+// instances to be destroyed in LIFO order.
+template <typename T>
+class PersistentRootedVector : public PersistentRooted<StackGCVector<T>> {
+ using Vec = StackGCVector<T>;
+ using Base = PersistentRooted<Vec>;
+
+ public:
+ explicit PersistentRootedVector(JSContext* cx) : Base(cx, Vec(cx)) {}
+};
+
+} // namespace JS
+
+#endif // js_GCVector_h
diff --git a/js/public/HashTable.h b/js/public/HashTable.h
new file mode 100644
index 0000000000..3e7d0ffa6a
--- /dev/null
+++ b/js/public/HashTable.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_HashTable_h
+#define js_HashTable_h
+
+#include "mozilla/HashTable.h"
+
+#include "jstypes.h"
+
+namespace js {
+
+using HashNumber = mozilla::HashNumber;
+static const uint32_t kHashNumberBits = mozilla::kHashNumberBits;
+
+class JS_PUBLIC_API TempAllocPolicy;
+
+template <class T>
+using DefaultHasher = mozilla::DefaultHasher<T>;
+
+template <typename Key>
+using PointerHasher = mozilla::PointerHasher<Key>;
+
+template <typename T, class HashPolicy = mozilla::DefaultHasher<T>,
+ class AllocPolicy = TempAllocPolicy>
+using HashSet = mozilla::HashSet<T, HashPolicy, AllocPolicy>;
+
+template <typename Key, typename Value,
+ class HashPolicy = mozilla::DefaultHasher<Key>,
+ class AllocPolicy = TempAllocPolicy>
+using HashMap = mozilla::HashMap<Key, Value, HashPolicy, AllocPolicy>;
+
+} // namespace js
+
+#endif /* js_HashTable_h */
diff --git a/js/public/HeapAPI.h b/js/public/HeapAPI.h
new file mode 100644
index 0000000000..4e01958993
--- /dev/null
+++ b/js/public/HeapAPI.h
@@ -0,0 +1,765 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_HeapAPI_h
+#define js_HeapAPI_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/BitSet.h"
+
+#include <limits.h>
+#include <type_traits>
+
+#include "jspubtd.h"
+
+#include "js/GCAnnotations.h"
+#include "js/shadow/String.h" // JS::shadow::String
+#include "js/shadow/Symbol.h" // JS::shadow::Symbol
+#include "js/shadow/Zone.h" // JS::shadow::Zone
+#include "js/TraceKind.h"
+#include "js/Utility.h"
+
+#ifndef JS_BITS_PER_WORD
+# error \
+ "JS_BITS_PER_WORD must be defined. Did you forget to include js-config.h?"
+#endif
+
+struct JSExternalStringCallbacks;
+
+/* These values are private to the JS engine. */
+namespace js {
+
+class NurseryDecommitTask;
+
+JS_FRIEND_API bool CurrentThreadCanAccessZone(JS::Zone* zone);
+
+namespace gc {
+
+class Arena;
+struct Cell;
+class TenuredChunk;
+class StoreBuffer;
+class TenuredCell;
+
+const size_t ArenaShift = 12;
+const size_t ArenaSize = size_t(1) << ArenaShift;
+const size_t ArenaMask = ArenaSize - 1;
+
+#ifdef JS_GC_SMALL_CHUNK_SIZE
+const size_t ChunkShift = 18;
+#else
+const size_t ChunkShift = 20;
+#endif
+const size_t ChunkSize = size_t(1) << ChunkShift;
+const size_t ChunkMask = ChunkSize - 1;
+
+const size_t CellAlignShift = 3;
+const size_t CellAlignBytes = size_t(1) << CellAlignShift;
+const size_t CellAlignMask = CellAlignBytes - 1;
+
+const size_t CellBytesPerMarkBit = CellAlignBytes;
+const size_t MarkBitsPerCell = 2;
+
+/*
+ * The mark bitmap has one bit per each possible cell start position. This
+ * wastes some space for larger GC things but allows us to avoid division by the
+ * cell's size when accessing the bitmap.
+ */
+const size_t ArenaBitmapBits = ArenaSize / CellBytesPerMarkBit;
+const size_t ArenaBitmapBytes = HowMany(ArenaBitmapBits, 8);
+const size_t ArenaBitmapWords = HowMany(ArenaBitmapBits, JS_BITS_PER_WORD);
+
+// The base class for all GC chunks, either in the nursery or in the tenured
+// heap memory. This structure is locatable from any GC pointer by aligning to
+// the chunk size.
+class alignas(CellAlignBytes) ChunkBase {
+ protected:
+ ChunkBase(JSRuntime* rt, StoreBuffer* sb) : storeBuffer(sb), runtime(rt) {
+ MOZ_ASSERT((uintptr_t(this) & ChunkMask) == 0);
+ }
+
+ public:
+ // The store buffer for pointers from tenured things to things in this
+ // chunk. Will be non-null if and only if this is a nursery chunk.
+ StoreBuffer* storeBuffer;
+
+ // Provide quick access to the runtime from absolutely anywhere.
+ JSRuntime* runtime;
+};
+
+// Information about tenured heap chunks.
+struct TenuredChunkInfo {
+ private:
+ friend class ChunkPool;
+ TenuredChunk* next = nullptr;
+ TenuredChunk* prev = nullptr;
+
+ public:
+ /* Free arenas are linked together with arena.next. */
+ Arena* freeArenasHead;
+
+ /*
+ * Decommitted arenas are tracked by a bitmap in the TenuredChunkBase. We use
+ * this offset to start our search iteration close to a decommitted arena that
+ * we can allocate.
+ */
+ uint32_t lastDecommittedArenaOffset;
+
+ /* Number of free arenas, either committed or decommitted. */
+ uint32_t numArenasFree;
+
+ /* Number of free, committed arenas. */
+ uint32_t numArenasFreeCommitted;
+};
+
+/*
+ * Calculating ArenasPerChunk:
+ *
+ * To figure out how many Arenas will fit in a chunk we need to know how much
+ * extra space is available after we allocate the header data. This is a problem
+ * because the header size depends on the number of arenas in the chunk.
+ *
+ * The two dependent fields are bitmap and decommittedArenas. bitmap needs
+ * ArenaBitmapBytes bytes per arena and decommittedArenas needs one bit per
+ * arena.
+ *
+ * We can calculate an approximate value by dividing the number of bits of free
+ * space in the chunk by the number of bits needed per arena. This is an
+ * approximation because it doesn't take account of the fact that the variable
+ * sized fields must be rounded up to a whole number of words, or any padding
+ * the compiler adds between fields.
+ *
+ * Fortunately, for the chunk and arena size parameters we use this
+ * approximation turns out to be correct. If it were not we might need to adjust
+ * the arena count down by one to allow more space for the padding.
+ */
+const size_t BitsPerArenaWithHeaders =
+ (ArenaSize + ArenaBitmapBytes) * CHAR_BIT + 1;
+const size_t ChunkBitsAvailable =
+ (ChunkSize - sizeof(ChunkBase) - sizeof(TenuredChunkInfo)) * CHAR_BIT;
+const size_t ArenasPerChunk = ChunkBitsAvailable / BitsPerArenaWithHeaders;
+
+const size_t CalculatedChunkSizeRequired =
+ sizeof(ChunkBase) + sizeof(TenuredChunkInfo) +
+ RoundUp(ArenasPerChunk * ArenaBitmapBytes, sizeof(uintptr_t)) +
+ RoundUp(ArenasPerChunk, sizeof(uint32_t) * CHAR_BIT) / CHAR_BIT +
+ ArenasPerChunk * ArenaSize;
+static_assert(CalculatedChunkSizeRequired <= ChunkSize,
+ "Calculated ArenasPerChunk is too large");
+
+const size_t CalculatedChunkPadSize = ChunkSize - CalculatedChunkSizeRequired;
+static_assert(CalculatedChunkPadSize * CHAR_BIT < BitsPerArenaWithHeaders,
+ "Calculated ArenasPerChunk is too small");
+
+// Define a macro for the expected number of arenas so its value appears in the
+// error message if the assertion fails.
+#ifdef JS_GC_SMALL_CHUNK_SIZE
+# define EXPECTED_ARENA_COUNT 63
+#else
+# define EXPECTED_ARENA_COUNT 252
+#endif
+static_assert(ArenasPerChunk == EXPECTED_ARENA_COUNT,
+ "Do not accidentally change our heap's density.");
+#undef EXPECTED_ARENA_COUNT
+
+// Mark bitmaps are atomic because they can be written by gray unmarking on the
+// main thread while read by sweeping on a background thread. The former does
+// not affect the result of the latter.
+using MarkBitmapWord = mozilla::Atomic<uintptr_t, mozilla::Relaxed>;
+
+/*
+ * Live objects are marked black or gray. Everything reachable from a JS root is
+ * marked black. Objects marked gray are eligible for cycle collection.
+ *
+ * BlackBit: GrayOrBlackBit: Color:
+ * 0 0 white
+ * 0 1 gray
+ * 1 0 black
+ * 1 1 black
+ */
+enum class ColorBit : uint32_t { BlackBit = 0, GrayOrBlackBit = 1 };
+
+// Mark colors. Order is important here: the greater value the 'more marked' a
+// cell is.
+enum class MarkColor : uint8_t { Gray = 1, Black = 2 };
+
+// Mark bitmap for a tenured heap chunk.
+struct MarkBitmap {
+ static constexpr size_t WordCount = ArenaBitmapWords * ArenasPerChunk;
+ MarkBitmapWord bitmap[WordCount];
+
+ inline void getMarkWordAndMask(const TenuredCell* cell, ColorBit colorBit,
+ MarkBitmapWord** wordp, uintptr_t* maskp);
+
+ // The following are not exported and are defined in gc/Heap.h:
+ inline bool markBit(const TenuredCell* cell, ColorBit colorBit);
+ inline bool isMarkedAny(const TenuredCell* cell);
+ inline bool isMarkedBlack(const TenuredCell* cell);
+ inline bool isMarkedGray(const TenuredCell* cell);
+ inline bool markIfUnmarked(const TenuredCell* cell, MarkColor color);
+ inline void markBlack(const TenuredCell* cell);
+ inline void copyMarkBit(TenuredCell* dst, const TenuredCell* src,
+ ColorBit colorBit);
+ inline void unmark(const TenuredCell* cell);
+ inline void clear();
+ inline MarkBitmapWord* arenaBits(Arena* arena);
+};
+
+static_assert(ArenaBitmapBytes * ArenasPerChunk == sizeof(MarkBitmap),
+ "Ensure our MarkBitmap actually covers all arenas.");
+
+// Decommit bitmap for a heap chunk.
+using DecommitBitmap = mozilla::BitSet<ArenasPerChunk, uint32_t>;
+
+// Base class containing data members for a tenured heap chunk.
+class TenuredChunkBase : public ChunkBase {
+ public:
+ TenuredChunkInfo info;
+ MarkBitmap markBits;
+ DecommitBitmap decommittedArenas;
+
+ protected:
+ explicit TenuredChunkBase(JSRuntime* runtime) : ChunkBase(runtime, nullptr) {}
+};
+
+/*
+ * We sometimes use an index to refer to a cell in an arena. The index for a
+ * cell is found by dividing by the cell alignment so not all indices refer to
+ * valid cells.
+ */
+const size_t ArenaCellIndexBytes = CellAlignBytes;
+const size_t MaxArenaCellIndex = ArenaSize / CellAlignBytes;
+
+const size_t MarkBitmapWordBits = sizeof(MarkBitmapWord) * CHAR_BIT;
+
+constexpr size_t FirstArenaAdjustmentBits =
+ RoundUp(sizeof(gc::TenuredChunkBase), ArenaSize) / gc::CellBytesPerMarkBit;
+
+static_assert((FirstArenaAdjustmentBits % MarkBitmapWordBits) == 0);
+constexpr size_t FirstArenaAdjustmentWords =
+ FirstArenaAdjustmentBits / MarkBitmapWordBits;
+
+const size_t ChunkRuntimeOffset = offsetof(ChunkBase, runtime);
+const size_t ChunkStoreBufferOffset = offsetof(ChunkBase, storeBuffer);
+const size_t ChunkMarkBitmapOffset = offsetof(TenuredChunkBase, markBits);
+
+// Hardcoded offsets into Arena class.
+const size_t ArenaZoneOffset = sizeof(size_t);
+const size_t ArenaHeaderSize =
+ sizeof(size_t) + 2 * sizeof(uintptr_t) + sizeof(size_t) + sizeof(uintptr_t);
+
+// The first word of a GC thing has certain requirements from the GC and is used
+// to store flags in the low bits.
+const size_t CellFlagBitsReservedForGC = 3;
+
+// The first word can be used to store JSClass pointers for some thing kinds, so
+// these must be suitably aligned.
+const size_t JSClassAlignBytes = size_t(1) << CellFlagBitsReservedForGC;
+
+#ifdef JS_DEBUG
+/* When downcasting, ensure we are actually the right type. */
+extern JS_FRIEND_API void AssertGCThingHasType(js::gc::Cell* cell,
+ JS::TraceKind kind);
+#else
+inline void AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {}
+#endif
+
+MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
+MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::TenuredCell* cell);
+
+} /* namespace gc */
+} /* namespace js */
+
+namespace JS {
+
+enum class HeapState {
+ Idle, // doing nothing with the GC heap
+ Tracing, // tracing the GC heap without collecting, e.g.
+ // IterateCompartments()
+ MajorCollecting, // doing a GC of the major heap
+ MinorCollecting, // doing a GC of the minor heap (nursery)
+ CycleCollecting // in the "Unlink" phase of cycle collection
+};
+
+JS_PUBLIC_API HeapState RuntimeHeapState();
+
+static inline bool RuntimeHeapIsBusy() {
+ return RuntimeHeapState() != HeapState::Idle;
+}
+
+static inline bool RuntimeHeapIsTracing() {
+ return RuntimeHeapState() == HeapState::Tracing;
+}
+
+static inline bool RuntimeHeapIsMajorCollecting() {
+ return RuntimeHeapState() == HeapState::MajorCollecting;
+}
+
+static inline bool RuntimeHeapIsMinorCollecting() {
+ return RuntimeHeapState() == HeapState::MinorCollecting;
+}
+
+static inline bool RuntimeHeapIsCollecting(HeapState state) {
+ return state == HeapState::MajorCollecting ||
+ state == HeapState::MinorCollecting;
+}
+
+static inline bool RuntimeHeapIsCollecting() {
+ return RuntimeHeapIsCollecting(RuntimeHeapState());
+}
+
+static inline bool RuntimeHeapIsCycleCollecting() {
+ return RuntimeHeapState() == HeapState::CycleCollecting;
+}
+
+/*
+ * This list enumerates the different types of conceptual stacks we have in
+ * SpiderMonkey. In reality, they all share the C stack, but we allow different
+ * stack limits depending on the type of code running.
+ */
+enum StackKind {
+ StackForSystemCode, // C++, such as the GC, running on behalf of the VM.
+ StackForTrustedScript, // Script running with trusted principals.
+ StackForUntrustedScript, // Script running with untrusted principals.
+ StackKindCount
+};
+
+/*
+ * Default maximum size for the generational nursery in bytes. This is the
+ * initial value. In the browser this configured by the
+ * javascript.options.mem.nursery.max_kb pref.
+ */
+const uint32_t DefaultNurseryMaxBytes = 16 * js::gc::ChunkSize;
+
+/* Default maximum heap size in bytes to pass to JS_NewContext(). */
+const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
+
+/**
+ * A GC pointer, tagged with the trace kind.
+ *
+ * In general, a GC pointer should be stored with an exact type. This class
+ * is for use when that is not possible because a single pointer must point
+ * to several kinds of GC thing.
+ */
+class JS_FRIEND_API GCCellPtr {
+ public:
+ GCCellPtr() : GCCellPtr(nullptr) {}
+
+ // Construction from a void* and trace kind.
+ GCCellPtr(void* gcthing, JS::TraceKind traceKind)
+ : ptr(checkedCast(gcthing, traceKind)) {}
+
+ // Automatically construct a null GCCellPtr from nullptr.
+ MOZ_IMPLICIT GCCellPtr(decltype(nullptr))
+ : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
+
+ // Construction from an explicit type.
+ template <typename T>
+ explicit GCCellPtr(T* p)
+ : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) {}
+ explicit GCCellPtr(JSFunction* p)
+ : ptr(checkedCast(p, JS::TraceKind::Object)) {}
+ explicit GCCellPtr(JSScript* p)
+ : ptr(checkedCast(p, JS::TraceKind::Script)) {}
+ explicit GCCellPtr(const Value& v);
+
+ JS::TraceKind kind() const {
+ JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
+ if (uintptr_t(traceKind) != OutOfLineTraceKindMask) {
+ return traceKind;
+ }
+ return outOfLineKind();
+ }
+
+ // Allow GCCellPtr to be used in a boolean context.
+ explicit operator bool() const {
+ MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null));
+ return asCell();
+ }
+
+ // Simplify checks to the kind.
+ template <typename T, typename = std::enable_if_t<JS::IsBaseTraceType_v<T>>>
+ bool is() const {
+ return kind() == JS::MapTypeToTraceKind<T>::kind;
+ }
+
+ // Conversions to more specific types must match the kind. Access to
+ // further refined types is not allowed directly from a GCCellPtr.
+ template <typename T, typename = std::enable_if_t<JS::IsBaseTraceType_v<T>>>
+ T& as() const {
+ MOZ_ASSERT(kind() == JS::MapTypeToTraceKind<T>::kind);
+ // We can't use static_cast here, because the fact that JSObject
+ // inherits from js::gc::Cell is not part of the public API.
+ return *reinterpret_cast<T*>(asCell());
+ }
+
+ // Return a pointer to the cell this |GCCellPtr| refers to, or |nullptr|.
+ // (It would be more symmetrical with |to| for this to return a |Cell&|, but
+ // the result can be |nullptr|, and null references are undefined behavior.)
+ js::gc::Cell* asCell() const {
+ return reinterpret_cast<js::gc::Cell*>(ptr & ~OutOfLineTraceKindMask);
+ }
+
+ // The CC's trace logger needs an identity that is XPIDL serializable.
+ uint64_t unsafeAsInteger() const {
+ return static_cast<uint64_t>(unsafeAsUIntPtr());
+ }
+ // Inline mark bitmap access requires direct pointer arithmetic.
+ uintptr_t unsafeAsUIntPtr() const {
+ MOZ_ASSERT(asCell());
+ MOZ_ASSERT(!js::gc::IsInsideNursery(asCell()));
+ return reinterpret_cast<uintptr_t>(asCell());
+ }
+
+ MOZ_ALWAYS_INLINE bool mayBeOwnedByOtherRuntime() const {
+ if (!is<JSString>() && !is<JS::Symbol>()) {
+ return false;
+ }
+ if (is<JSString>()) {
+ return JS::shadow::String::isPermanentAtom(asCell());
+ }
+ MOZ_ASSERT(is<JS::Symbol>());
+ return JS::shadow::Symbol::isWellKnownSymbol(asCell());
+ }
+
+ private:
+ static uintptr_t checkedCast(void* p, JS::TraceKind traceKind) {
+ auto* cell = static_cast<js::gc::Cell*>(p);
+ MOZ_ASSERT((uintptr_t(p) & OutOfLineTraceKindMask) == 0);
+ AssertGCThingHasType(cell, traceKind);
+ // Note: the OutOfLineTraceKindMask bits are set on all out-of-line kinds
+ // so that we can mask instead of branching.
+ MOZ_ASSERT_IF(uintptr_t(traceKind) >= OutOfLineTraceKindMask,
+ (uintptr_t(traceKind) & OutOfLineTraceKindMask) ==
+ OutOfLineTraceKindMask);
+ return uintptr_t(p) | (uintptr_t(traceKind) & OutOfLineTraceKindMask);
+ }
+
+ JS::TraceKind outOfLineKind() const;
+
+ uintptr_t ptr;
+} JS_HAZ_GC_POINTER;
+
+// Unwraps the given GCCellPtr, calls the functor |f| with a template argument
+// of the actual type of the pointer, and returns the result.
+template <typename F>
+auto MapGCThingTyped(GCCellPtr thing, F&& f) {
+ switch (thing.kind()) {
+#define JS_EXPAND_DEF(name, type, _, _1) \
+ case JS::TraceKind::name: \
+ return f(&thing.as<type>());
+ JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+ default:
+ MOZ_CRASH("Invalid trace kind in MapGCThingTyped for GCCellPtr.");
+ }
+}
+
+// Unwraps the given GCCellPtr and calls the functor |f| with a template
+// argument of the actual type of the pointer. Doesn't return anything.
+template <typename F>
+void ApplyGCThingTyped(GCCellPtr thing, F&& f) {
+ // This function doesn't do anything but is supplied for symmetry with other
+ // MapGCThingTyped/ApplyGCThingTyped implementations that have to wrap the
+ // functor to return a dummy value that is ignored.
+ MapGCThingTyped(thing, f);
+}
+
+} /* namespace JS */
+
+// These are defined in the toplevel namespace instead of within JS so that
+// they won't shadow other operator== overloads (see bug 1456512.)
+
+inline bool operator==(const JS::GCCellPtr& ptr1, const JS::GCCellPtr& ptr2) {
+ return ptr1.asCell() == ptr2.asCell();
+}
+
+inline bool operator!=(const JS::GCCellPtr& ptr1, const JS::GCCellPtr& ptr2) {
+ return !(ptr1 == ptr2);
+}
+
+namespace js {
+namespace gc {
+
+/* static */
+MOZ_ALWAYS_INLINE void MarkBitmap::getMarkWordAndMask(const TenuredCell* cell,
+ ColorBit colorBit,
+ MarkBitmapWord** wordp,
+ uintptr_t* maskp) {
+ // Note: the JIT pre-barrier trampolines inline this code. Update
+ // MacroAssembler::emitPreBarrierFastPath code too when making changes here!
+
+ MOZ_ASSERT(size_t(colorBit) < MarkBitsPerCell);
+
+ size_t offset = uintptr_t(cell) & ChunkMask;
+ const size_t bit = offset / CellBytesPerMarkBit + size_t(colorBit);
+ size_t word = bit / MarkBitmapWordBits - FirstArenaAdjustmentWords;
+ MOZ_ASSERT(word < WordCount);
+ *wordp = &bitmap[word];
+ *maskp = uintptr_t(1) << (bit % MarkBitmapWordBits);
+}
+
+namespace detail {
+
+static MOZ_ALWAYS_INLINE ChunkBase* GetCellChunkBase(const Cell* cell) {
+ MOZ_ASSERT(cell);
+ return reinterpret_cast<ChunkBase*>(uintptr_t(cell) & ~ChunkMask);
+}
+
+static MOZ_ALWAYS_INLINE TenuredChunkBase* GetCellChunkBase(
+ const TenuredCell* cell) {
+ MOZ_ASSERT(cell);
+ return reinterpret_cast<TenuredChunkBase*>(uintptr_t(cell) & ~ChunkMask);
+}
+
+static MOZ_ALWAYS_INLINE JS::Zone* GetTenuredGCThingZone(const uintptr_t addr) {
+ MOZ_ASSERT(addr);
+ const uintptr_t zone_addr = (addr & ~ArenaMask) | ArenaZoneOffset;
+ return *reinterpret_cast<JS::Zone**>(zone_addr);
+}
+
+static MOZ_ALWAYS_INLINE bool TenuredCellIsMarkedGray(const TenuredCell* cell) {
+ // Return true if GrayOrBlackBit is set and BlackBit is not set.
+ MOZ_ASSERT(cell);
+ MOZ_ASSERT(!js::gc::IsInsideNursery(cell));
+
+ MarkBitmapWord* grayWord;
+ uintptr_t grayMask;
+ TenuredChunkBase* chunk = GetCellChunkBase(cell);
+ chunk->markBits.getMarkWordAndMask(cell, js::gc::ColorBit::GrayOrBlackBit,
+ &grayWord, &grayMask);
+ if (!(*grayWord & grayMask)) {
+ return false;
+ }
+
+ MarkBitmapWord* blackWord;
+ uintptr_t blackMask;
+ chunk->markBits.getMarkWordAndMask(cell, js::gc::ColorBit::BlackBit,
+ &blackWord, &blackMask);
+ return !(*blackWord & blackMask);
+}
+
+static MOZ_ALWAYS_INLINE bool CellIsMarkedGray(const Cell* cell) {
+ MOZ_ASSERT(cell);
+ if (js::gc::IsInsideNursery(cell)) {
+ return false;
+ }
+ return TenuredCellIsMarkedGray(reinterpret_cast<const TenuredCell*>(cell));
+}
+
+extern JS_PUBLIC_API bool CellIsMarkedGrayIfKnown(const Cell* cell);
+
+#ifdef DEBUG
+extern JS_PUBLIC_API void AssertCellIsNotGray(const Cell* cell);
+
+extern JS_PUBLIC_API bool ObjectIsMarkedBlack(const JSObject* obj);
+#endif
+
+MOZ_ALWAYS_INLINE bool CellHasStoreBuffer(const Cell* cell) {
+ return GetCellChunkBase(cell)->storeBuffer;
+}
+
+} /* namespace detail */
+
+MOZ_ALWAYS_INLINE bool IsInsideNursery(const Cell* cell) {
+ if (!cell) {
+ return false;
+ }
+ return detail::CellHasStoreBuffer(cell);
+}
+
+MOZ_ALWAYS_INLINE bool IsInsideNursery(const TenuredCell* cell) {
+ MOZ_ASSERT_IF(
+ cell, !detail::CellHasStoreBuffer(reinterpret_cast<const Cell*>(cell)));
+ return false;
+}
+
+// Allow use before the compiler knows the derivation of JSObject, JSString, and
+// JS::BigInt.
+MOZ_ALWAYS_INLINE bool IsInsideNursery(const JSObject* obj) {
+ return IsInsideNursery(reinterpret_cast<const Cell*>(obj));
+}
+MOZ_ALWAYS_INLINE bool IsInsideNursery(const JSString* str) {
+ return IsInsideNursery(reinterpret_cast<const Cell*>(str));
+}
+MOZ_ALWAYS_INLINE bool IsInsideNursery(const JS::BigInt* bi) {
+ return IsInsideNursery(reinterpret_cast<const Cell*>(bi));
+}
+
+MOZ_ALWAYS_INLINE bool IsCellPointerValid(const void* ptr) {
+ auto addr = uintptr_t(ptr);
+ if (addr < ChunkSize || addr % CellAlignBytes != 0) {
+ return false;
+ }
+
+ auto* cell = reinterpret_cast<const Cell*>(ptr);
+ if (!IsInsideNursery(cell)) {
+ return detail::GetTenuredGCThingZone(addr) != nullptr;
+ }
+
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool IsCellPointerValidOrNull(const void* cell) {
+ if (!cell) {
+ return true;
+ }
+ return IsCellPointerValid(cell);
+}
+
+} /* namespace gc */
+} /* namespace js */
+
+namespace JS {
+
+static MOZ_ALWAYS_INLINE Zone* GetTenuredGCThingZone(GCCellPtr thing) {
+ MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell()));
+ return js::gc::detail::GetTenuredGCThingZone(thing.unsafeAsUIntPtr());
+}
+
+extern JS_PUBLIC_API Zone* GetNurseryCellZone(js::gc::Cell* cell);
+
+static MOZ_ALWAYS_INLINE Zone* GetGCThingZone(GCCellPtr thing) {
+ if (!js::gc::IsInsideNursery(thing.asCell())) {
+ return js::gc::detail::GetTenuredGCThingZone(thing.unsafeAsUIntPtr());
+ }
+
+ return GetNurseryCellZone(thing.asCell());
+}
+
+static MOZ_ALWAYS_INLINE Zone* GetStringZone(JSString* str) {
+ if (!js::gc::IsInsideNursery(str)) {
+ return js::gc::detail::GetTenuredGCThingZone(
+ reinterpret_cast<uintptr_t>(str));
+ }
+ return GetNurseryCellZone(reinterpret_cast<js::gc::Cell*>(str));
+}
+
+extern JS_PUBLIC_API Zone* GetObjectZone(JSObject* obj);
+
+static MOZ_ALWAYS_INLINE bool GCThingIsMarkedGray(GCCellPtr thing) {
+ if (thing.mayBeOwnedByOtherRuntime()) {
+ return false;
+ }
+ return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell());
+}
+
+extern JS_PUBLIC_API JS::TraceKind GCThingTraceKind(void* thing);
+
+extern JS_PUBLIC_API void EnableNurseryStrings(JSContext* cx);
+
+extern JS_PUBLIC_API void DisableNurseryStrings(JSContext* cx);
+
+extern JS_PUBLIC_API void EnableNurseryBigInts(JSContext* cx);
+
+extern JS_PUBLIC_API void DisableNurseryBigInts(JSContext* cx);
+
+/*
+ * Returns true when writes to GC thing pointers (and reads from weak pointers)
+ * must call an incremental barrier. This is generally only true when running
+ * mutator code in-between GC slices. At other times, the barrier may be elided
+ * for performance.
+ */
+extern JS_PUBLIC_API bool IsIncrementalBarrierNeeded(JSContext* cx);
+
+/*
+ * Notify the GC that a reference to a JSObject is about to be overwritten.
+ * This method must be called if IsIncrementalBarrierNeeded.
+ */
+extern JS_PUBLIC_API void IncrementalPreWriteBarrier(JSObject* obj);
+
+/*
+ * Notify the GC that a reference to a tenured GC cell is about to be
+ * overwritten. This method must be called if IsIncrementalBarrierNeeded.
+ */
+extern JS_PUBLIC_API void IncrementalPreWriteBarrier(GCCellPtr thing);
+
+/**
+ * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
+ * JS::TraceKind::Shape. |thing| should be non-null. The return value indicates
+ * if anything was unmarked.
+ */
+extern JS_FRIEND_API bool UnmarkGrayGCThingRecursively(GCCellPtr thing);
+
+} // namespace JS
+
+namespace js {
+namespace gc {
+
+extern JS_PUBLIC_API void PerformIncrementalReadBarrier(JS::GCCellPtr thing);
+
+static MOZ_ALWAYS_INLINE void ExposeGCThingToActiveJS(JS::GCCellPtr thing) {
+ // TODO: I'd like to assert !RuntimeHeapIsBusy() here but this gets
+ // called while we are tracing the heap, e.g. during memory reporting
+ // (see bug 1313318).
+ MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
+
+ // GC things residing in the nursery cannot be gray: they have no mark bits.
+ // All live objects in the nursery are moved to tenured at the beginning of
+ // each GC slice, so the gray marker never sees nursery things.
+ if (IsInsideNursery(thing.asCell())) {
+ return;
+ }
+
+ auto* cell = reinterpret_cast<TenuredCell*>(thing.asCell());
+
+ // There's nothing to do for permanent GC things that might be owned by
+ // another runtime.
+ if (thing.mayBeOwnedByOtherRuntime()) {
+ return;
+ }
+
+ auto* zone = JS::shadow::Zone::from(JS::GetTenuredGCThingZone(thing));
+ if (zone->needsIncrementalBarrier()) {
+ PerformIncrementalReadBarrier(thing);
+ } else if (!zone->isGCPreparing() && detail::TenuredCellIsMarkedGray(cell)) {
+ MOZ_ALWAYS_TRUE(JS::UnmarkGrayGCThingRecursively(thing));
+ }
+
+ MOZ_ASSERT_IF(!zone->isGCPreparing(), !detail::TenuredCellIsMarkedGray(cell));
+}
+
+template <typename T>
+extern JS_PUBLIC_API bool EdgeNeedsSweepUnbarrieredSlow(T* thingp);
+
+static MOZ_ALWAYS_INLINE bool EdgeNeedsSweepUnbarriered(JSObject** objp) {
+ // This function does not handle updating nursery pointers. Raw JSObject
+ // pointers should be updated separately or replaced with
+ // JS::Heap<JSObject*> which handles this automatically.
+ MOZ_ASSERT(!JS::RuntimeHeapIsMinorCollecting());
+ if (IsInsideNursery(*objp)) {
+ return false;
+ }
+
+ auto zone =
+ JS::shadow::Zone::from(detail::GetTenuredGCThingZone(uintptr_t(*objp)));
+ if (!zone->isGCSweepingOrCompacting()) {
+ return false;
+ }
+
+ return EdgeNeedsSweepUnbarrieredSlow(objp);
+}
+
+} // namespace gc
+} // namespace js
+
+namespace JS {
+
+/*
+ * This should be called when an object that is marked gray is exposed to the JS
+ * engine (by handing it to running JS code or writing it into live JS
+ * data). During incremental GC, since the gray bits haven't been computed yet,
+ * we conservatively mark the object black.
+ */
+static MOZ_ALWAYS_INLINE void ExposeObjectToActiveJS(JSObject* obj) {
+ MOZ_ASSERT(obj);
+ MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&obj));
+ js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj));
+}
+
+} /* namespace JS */
+
+#endif /* js_HeapAPI_h */
diff --git a/js/public/Id.h b/js/public/Id.h
new file mode 100644
index 0000000000..ae4cfb5613
--- /dev/null
+++ b/js/public/Id.h
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Id_h
+#define js_Id_h
+
+// [SMDOC] Property Key / JSID
+//
+// A jsid is an identifier for a property or method of an object which is
+// either a 31-bit unsigned integer, interned string or symbol.
+//
+// Also, there is an additional jsid value, JSID_VOID, which does not occur in
+// JS scripts but may be used to indicate the absence of a valid jsid. A void
+// jsid is not a valid id and only arises as an exceptional API return value,
+// such as in JS_NextProperty. Embeddings must not pass JSID_VOID into JSAPI
+// entry points expecting a jsid and do not need to handle JSID_VOID in hooks
+// receiving a jsid except when explicitly noted in the API contract.
+//
+// A jsid is not implicitly convertible to or from a Value; JS_ValueToId or
+// JS_IdToValue must be used instead.
+
+#include "mozilla/Maybe.h"
+
+#include "jstypes.h"
+
+#include "js/HeapAPI.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+// All jsids with the low bit set are integer ids. This means the other type
+// tags must all be even.
+#define JSID_TYPE_INT_BIT 0x1
+
+// Use 0 for JSID_TYPE_STRING to avoid a bitwise op for atom <-> id conversions.
+#define JSID_TYPE_STRING 0x0
+#define JSID_TYPE_VOID 0x2
+#define JSID_TYPE_SYMBOL 0x4
+#define JSID_TYPE_EMPTY 0x6
+#define JSID_TYPE_MASK 0x7
+
+namespace JS {
+
+enum class SymbolCode : uint32_t;
+
+struct PropertyKey {
+ size_t asBits;
+
+ constexpr PropertyKey() : asBits(JSID_TYPE_VOID) {}
+
+ static constexpr MOZ_ALWAYS_INLINE PropertyKey fromRawBits(size_t bits) {
+ PropertyKey id;
+ id.asBits = bits;
+ return id;
+ }
+
+ bool operator==(const PropertyKey& rhs) const { return asBits == rhs.asBits; }
+ bool operator!=(const PropertyKey& rhs) const { return asBits != rhs.asBits; }
+
+ MOZ_ALWAYS_INLINE bool isInt() const {
+ return !!(asBits & JSID_TYPE_INT_BIT);
+ }
+
+ MOZ_ALWAYS_INLINE bool isString() const {
+ return (asBits & JSID_TYPE_MASK) == JSID_TYPE_STRING;
+ }
+
+ MOZ_ALWAYS_INLINE bool isSymbol() const {
+ return (asBits & JSID_TYPE_MASK) == JSID_TYPE_SYMBOL;
+ }
+
+ MOZ_ALWAYS_INLINE bool isGCThing() const { return isString() || isSymbol(); }
+
+ MOZ_ALWAYS_INLINE int32_t toInt() const {
+ MOZ_ASSERT(isInt());
+ uint32_t bits = static_cast<uint32_t>(asBits) >> 1;
+ return static_cast<int32_t>(bits);
+ }
+
+ MOZ_ALWAYS_INLINE JSString* toString() const {
+ MOZ_ASSERT(isString());
+ // Use XOR instead of `& ~JSID_TYPE_MASK` because small immediates can be
+ // encoded more efficiently on some platorms.
+ return reinterpret_cast<JSString*>(asBits ^ JSID_TYPE_STRING);
+ }
+
+ MOZ_ALWAYS_INLINE JS::Symbol* toSymbol() const {
+ MOZ_ASSERT(isSymbol());
+ return reinterpret_cast<JS::Symbol*>(asBits ^ JSID_TYPE_SYMBOL);
+ }
+
+ js::gc::Cell* toGCThing() const {
+ MOZ_ASSERT(isGCThing());
+ return reinterpret_cast<js::gc::Cell*>(asBits & ~(size_t)JSID_TYPE_MASK);
+ }
+
+ GCCellPtr toGCCellPtr() const {
+ js::gc::Cell* thing = toGCThing();
+ if (isString()) {
+ return JS::GCCellPtr(thing, JS::TraceKind::String);
+ }
+ MOZ_ASSERT(isSymbol());
+ return JS::GCCellPtr(thing, JS::TraceKind::Symbol);
+ }
+
+ bool isPrivateName() const;
+
+ bool isWellKnownSymbol(JS::SymbolCode code) const;
+
+ // This API can be used by embedders to convert pinned (aka interned) strings,
+ // as created by JS_AtomizeAndPinJSString, into PropertyKeys.
+ // This means the string does not have to be explicitly rooted.
+ //
+ // Only use this API when absolutely necessary, otherwise use JS_StringToId.
+ static PropertyKey fromPinnedString(JSString* str);
+
+ // Must not be used on atoms that are representable as integer PropertyKey.
+ // Prefer NameToId or AtomToId over this function:
+ //
+ // A PropertyName is an atom that does not contain an integer in the range
+ // [0, UINT32_MAX]. However, PropertyKey can only hold an integer in the range
+ // [0, JSID_INT_MAX] (where JSID_INT_MAX == 2^31-1). Thus, for the range of
+ // integers (JSID_INT_MAX, UINT32_MAX], to represent as a 'id', it must be
+ // the case id.isString() and id.toString()->isIndex(). In most
+ // cases when creating a PropertyKey, code does not have to care about
+ // this corner case because:
+ //
+ // - When given an arbitrary JSAtom*, AtomToId must be used, which checks for
+ // integer atoms representable as integer PropertyKey, and does this
+ // conversion.
+ //
+ // - When given a PropertyName*, NameToId can be used which does not need
+ // to do any dynamic checks.
+ //
+ // Thus, it is only the rare third case which needs this function, which
+ // handles any JSAtom* that is known not to be representable with an int
+ // PropertyKey.
+ static PropertyKey fromNonIntAtom(JSAtom* atom) {
+ MOZ_ASSERT((size_t(atom) & JSID_TYPE_MASK) == 0);
+ MOZ_ASSERT(PropertyKey::isNonIntAtom(atom));
+ return PropertyKey::fromRawBits(size_t(atom) | JSID_TYPE_STRING);
+ }
+
+ // The JSAtom/JSString type exposed to embedders is opaque.
+ static PropertyKey fromNonIntAtom(JSString* str) {
+ MOZ_ASSERT((size_t(str) & JSID_TYPE_MASK) == 0);
+ MOZ_ASSERT(PropertyKey::isNonIntAtom(str));
+ return PropertyKey::fromRawBits(size_t(str) | JSID_TYPE_STRING);
+ }
+
+ private:
+ static bool isNonIntAtom(JSAtom* atom);
+ static bool isNonIntAtom(JSString* atom);
+} JS_HAZ_GC_POINTER;
+
+} // namespace JS
+
+using jsid = JS::PropertyKey;
+
+#define JSID_BITS(id) (id.asBits)
+
+static MOZ_ALWAYS_INLINE bool JSID_IS_STRING(jsid id) { return id.isString(); }
+
+static MOZ_ALWAYS_INLINE JSString* JSID_TO_STRING(jsid id) {
+ return id.toString();
+}
+
+static MOZ_ALWAYS_INLINE bool JSID_IS_INT(jsid id) { return id.isInt(); }
+
+static MOZ_ALWAYS_INLINE int32_t JSID_TO_INT(jsid id) { return id.toInt(); }
+
+#define JSID_INT_MIN 0
+#define JSID_INT_MAX INT32_MAX
+
+static MOZ_ALWAYS_INLINE bool INT_FITS_IN_JSID(int32_t i) { return i >= 0; }
+
+static MOZ_ALWAYS_INLINE jsid INT_TO_JSID(int32_t i) {
+ jsid id;
+ MOZ_ASSERT(INT_FITS_IN_JSID(i));
+ uint32_t bits = (static_cast<uint32_t>(i) << 1) | JSID_TYPE_INT_BIT;
+ JSID_BITS(id) = static_cast<size_t>(bits);
+ return id;
+}
+
+static MOZ_ALWAYS_INLINE bool JSID_IS_SYMBOL(jsid id) { return id.isSymbol(); }
+
+static MOZ_ALWAYS_INLINE JS::Symbol* JSID_TO_SYMBOL(jsid id) {
+ return id.toSymbol();
+}
+
+static MOZ_ALWAYS_INLINE jsid SYMBOL_TO_JSID(JS::Symbol* sym) {
+ jsid id;
+ MOZ_ASSERT(sym != nullptr);
+ MOZ_ASSERT((size_t(sym) & JSID_TYPE_MASK) == 0);
+ MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym)));
+ JSID_BITS(id) = (size_t(sym) | JSID_TYPE_SYMBOL);
+ return id;
+}
+
+static MOZ_ALWAYS_INLINE bool JSID_IS_VOID(const jsid id) {
+ MOZ_ASSERT_IF((JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_VOID,
+ JSID_BITS(id) == JSID_TYPE_VOID);
+ return JSID_BITS(id) == JSID_TYPE_VOID;
+}
+
+static MOZ_ALWAYS_INLINE bool JSID_IS_EMPTY(const jsid id) {
+ MOZ_ASSERT_IF((JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_EMPTY,
+ JSID_BITS(id) == JSID_TYPE_EMPTY);
+ return JSID_BITS(id) == JSID_TYPE_EMPTY;
+}
+
+constexpr const jsid JSID_VOID;
+constexpr const jsid JSID_EMPTY = jsid::fromRawBits(JSID_TYPE_EMPTY);
+
+extern JS_PUBLIC_DATA const JS::HandleId JSID_VOIDHANDLE;
+extern JS_PUBLIC_DATA const JS::HandleId JSID_EMPTYHANDLE;
+
+namespace JS {
+
+template <>
+struct GCPolicy<jsid> {
+ static void trace(JSTracer* trc, jsid* idp, const char* name) {
+ // It's not safe to trace unbarriered pointers except as part of root
+ // marking.
+ UnsafeTraceRoot(trc, idp, name);
+ }
+ static bool isValid(jsid id) {
+ return !id.isGCThing() ||
+ js::gc::IsCellPointerValid(id.toGCCellPtr().asCell());
+ }
+
+ static bool isTenured(jsid id) {
+ MOZ_ASSERT_IF(id.isGCThing(),
+ !js::gc::IsInsideNursery(id.toGCCellPtr().asCell()));
+ return true;
+ }
+};
+
+#ifdef DEBUG
+MOZ_ALWAYS_INLINE void AssertIdIsNotGray(jsid id) {
+ if (id.isGCThing()) {
+ AssertCellIsNotGray(id.toGCCellPtr().asCell());
+ }
+}
+#endif
+
+} // namespace JS
+
+namespace js {
+
+template <>
+struct BarrierMethods<jsid> {
+ static gc::Cell* asGCThingOrNull(jsid id) {
+ if (JSID_IS_STRING(id)) {
+ return reinterpret_cast<gc::Cell*>(JSID_TO_STRING(id));
+ }
+ if (JSID_IS_SYMBOL(id)) {
+ return reinterpret_cast<gc::Cell*>(JSID_TO_SYMBOL(id));
+ }
+ return nullptr;
+ }
+ static void postWriteBarrier(jsid* idp, jsid prev, jsid next) {
+ MOZ_ASSERT_IF(JSID_IS_STRING(next),
+ !gc::IsInsideNursery(JSID_TO_STRING(next)));
+ }
+ static void exposeToJS(jsid id) {
+ if (id.isGCThing()) {
+ js::gc::ExposeGCThingToActiveJS(id.toGCCellPtr());
+ }
+ }
+};
+
+// If the jsid is a GC pointer type, convert to that type and call |f| with the
+// pointer and return the result wrapped in a Maybe, otherwise return None().
+template <typename F>
+auto MapGCThingTyped(const jsid& id, F&& f) {
+ if (JSID_IS_STRING(id)) {
+ return mozilla::Some(f(JSID_TO_STRING(id)));
+ }
+ if (JSID_IS_SYMBOL(id)) {
+ return mozilla::Some(f(JSID_TO_SYMBOL(id)));
+ }
+ MOZ_ASSERT(!id.isGCThing());
+ using ReturnType = decltype(f(static_cast<JSString*>(nullptr)));
+ return mozilla::Maybe<ReturnType>();
+}
+
+// If the jsid is a GC pointer type, convert to that type and call |f| with the
+// pointer. Return whether this happened.
+template <typename F>
+bool ApplyGCThingTyped(const jsid& id, F&& f) {
+ return MapGCThingTyped(id,
+ [&f](auto t) {
+ f(t);
+ return true;
+ })
+ .isSome();
+}
+
+template <typename Wrapper>
+class WrappedPtrOperations<JS::PropertyKey, Wrapper> {
+ const JS::PropertyKey& id() const {
+ return static_cast<const Wrapper*>(this)->get();
+ }
+
+ public:
+ bool isInt() const { return id().isInt(); }
+ bool isString() const { return id().isString(); }
+ bool isSymbol() const { return id().isSymbol(); }
+ bool isGCThing() const { return id().isGCThing(); }
+
+ int32_t toInt() const { return id().toInt(); }
+ JSString* toString() const { return id().toString(); }
+ JS::Symbol* toSymbol() const { return id().toSymbol(); }
+
+ bool isPrivateName() const { return id().isPrivateName(); }
+
+ bool isWellKnownSymbol(JS::SymbolCode code) const {
+ return id().isWellKnownSymbol(code);
+ }
+};
+
+} // namespace js
+
+#endif /* js_Id_h */
diff --git a/js/public/Initialization.h b/js/public/Initialization.h
new file mode 100644
index 0000000000..e38537fda9
--- /dev/null
+++ b/js/public/Initialization.h
@@ -0,0 +1,115 @@
+/* -*- 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/. */
+
+/* SpiderMonkey initialization and shutdown APIs. */
+
+#ifndef js_Initialization_h
+#define js_Initialization_h
+
+#include "jstypes.h"
+
+namespace JS {
+namespace detail {
+
+enum class InitState { Uninitialized = 0, Initializing, Running, ShutDown };
+
+/**
+ * SpiderMonkey's initialization status is tracked here, and it controls things
+ * that should happen only once across all runtimes. It's an API requirement
+ * that JS_Init (and JS_ShutDown, if called) be called in a thread-aware
+ * manner, so this (internal -- embedders, don't use!) variable doesn't need to
+ * be atomic.
+ */
+extern JS_PUBLIC_DATA InitState libraryInitState;
+
+extern JS_PUBLIC_API const char* InitWithFailureDiagnostic(bool isDebugBuild);
+
+} // namespace detail
+} // namespace JS
+
+// These are equivalent to ICU's |UMemAllocFn|, |UMemReallocFn|, and
+// |UMemFreeFn| types. The first argument (called |context| in the ICU docs)
+// will always be nullptr and should be ignored.
+typedef void* (*JS_ICUAllocFn)(const void*, size_t size);
+typedef void* (*JS_ICUReallocFn)(const void*, void* p, size_t size);
+typedef void (*JS_ICUFreeFn)(const void*, void* p);
+
+/**
+ * This function can be used to track memory used by ICU. If it is called, it
+ * *must* be called before JS_Init. Don't use it unless you know what you're
+ * doing!
+ */
+extern JS_PUBLIC_API bool JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
+ JS_ICUReallocFn reallocFn,
+ JS_ICUFreeFn freeFn);
+
+/**
+ * Initialize SpiderMonkey, returning true only if initialization succeeded.
+ * Once this method has succeeded, it is safe to call JS_NewContext and other
+ * JSAPI methods.
+ *
+ * This method must be called before any other JSAPI method is used on any
+ * thread. Once it has been used, it is safe to call any JSAPI method, and it
+ * remains safe to do so until JS_ShutDown is correctly called.
+ *
+ * It is currently not possible to initialize SpiderMonkey multiple times (that
+ * is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
+ * again). This restriction may eventually be lifted.
+ */
+inline bool JS_Init(void) {
+#ifdef DEBUG
+ return !JS::detail::InitWithFailureDiagnostic(true);
+#else
+ return !JS::detail::InitWithFailureDiagnostic(false);
+#endif
+}
+
+/**
+ * A variant of JS_Init. On success it returns nullptr. On failure it returns a
+ * pointer to a string literal that describes how initialization failed, which
+ * can be useful for debugging purposes.
+ */
+inline const char* JS_InitWithFailureDiagnostic(void) {
+#ifdef DEBUG
+ return JS::detail::InitWithFailureDiagnostic(true);
+#else
+ return JS::detail::InitWithFailureDiagnostic(false);
+#endif
+}
+
+/*
+ * Returns true if SpiderMonkey has been initialized successfully, even if it
+ * has possibly been shut down.
+ *
+ * Note that it is the responsibility of the embedder to call JS_Init() and
+ * JS_ShutDown() at the correct times, and therefore this API should ideally not
+ * be necessary to use. This is only intended to be used in cases where the
+ * embedder isn't in full control of deciding whether to initialize SpiderMonkey
+ * or hand off the task to another consumer.
+ */
+inline bool JS_IsInitialized(void) {
+ return JS::detail::libraryInitState >= JS::detail::InitState::Running;
+}
+
+/**
+ * Destroy free-standing resources allocated by SpiderMonkey, not associated
+ * with any runtime, context, or other structure.
+ *
+ * This method should be called after all other JSAPI data has been properly
+ * cleaned up: every new runtime must have been destroyed, every new context
+ * must have been destroyed, and so on. Calling this method before all other
+ * resources have been destroyed has undefined behavior.
+ *
+ * Failure to call this method, at present, has no adverse effects other than
+ * leaking memory. This may not always be the case; it's recommended that all
+ * embedders call this method when all other JSAPI operations have completed.
+ *
+ * It is currently not possible to initialize SpiderMonkey multiple times (that
+ * is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
+ * again). This restriction may eventually be lifted.
+ */
+extern JS_PUBLIC_API void JS_ShutDown(void);
+
+#endif /* js_Initialization_h */
diff --git a/js/public/JSON.h b/js/public/JSON.h
new file mode 100644
index 0000000000..66d876d729
--- /dev/null
+++ b/js/public/JSON.h
@@ -0,0 +1,95 @@
+/* -*- 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/. */
+
+/*
+ * JSON serialization and deserialization operations.
+ */
+
+#ifndef js_JSON_h
+#define js_JSON_h
+
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+class JS_PUBLIC_API JSString;
+
+using JSONWriteCallback = bool (*)(const char16_t* buf, uint32_t len,
+ void* data);
+
+/**
+ * Performs the JSON.stringify operation, as specified by ECMAScript, except
+ * writing stringified data by repeated calls of |callback|, with each such
+ * call passed |data| as argument.
+ */
+extern JS_PUBLIC_API bool JS_Stringify(JSContext* cx,
+ JS::MutableHandle<JS::Value> value,
+ JS::Handle<JSObject*> replacer,
+ JS::Handle<JS::Value> space,
+ JSONWriteCallback callback, void* data);
+
+namespace JS {
+
+/**
+ * An API akin to JS_Stringify but with the goal of not having observable
+ * side-effects when the stringification is performed. This means it does not
+ * allow a replacer or a custom space and has the following constraints on its
+ * input:
+ *
+ * 1) The input must be a plain object or array, not an abitrary value.
+ * 2) Every value in the graph reached by the algorithm starting with this
+ * object must be one of the following: null, undefined, a string (NOT a
+ * string object!), a boolean, a finite number (i.e. no NaN or Infinity or
+ * -Infinity), a plain object with no accessor properties, or an Array with
+ * no holes.
+ *
+ * The actual behavior differs from JS_Stringify only in asserting the above and
+ * NOT attempting to get the "toJSON" property from things, since that could
+ * clearly have side-effects.
+ */
+extern JS_PUBLIC_API bool ToJSONMaybeSafely(JSContext* cx,
+ JS::Handle<JSObject*> input,
+ JSONWriteCallback callback,
+ void* data);
+
+} /* namespace JS */
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript.
+ */
+extern JS_PUBLIC_API bool JS_ParseJSON(JSContext* cx, const char16_t* chars,
+ uint32_t len,
+ JS::MutableHandle<JS::Value> vp);
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript.
+ */
+extern JS_PUBLIC_API bool JS_ParseJSON(JSContext* cx, JS::Handle<JSString*> str,
+ JS::MutableHandle<JS::Value> vp);
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript, using the
+ * given |reviver| argument as the corresponding optional argument to that
+ * function.
+ */
+extern JS_PUBLIC_API bool JS_ParseJSONWithReviver(
+ JSContext* cx, const char16_t* chars, uint32_t len,
+ JS::Handle<JS::Value> reviver, JS::MutableHandle<JS::Value> vp);
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript, using the
+ * given |reviver| argument as the corresponding optional argument to that
+ * function.
+ */
+extern JS_PUBLIC_API bool JS_ParseJSONWithReviver(
+ JSContext* cx, JS::Handle<JSString*> str, JS::Handle<JS::Value> reviver,
+ JS::MutableHandle<JS::Value> vp);
+
+#endif /* js_JSON_h */
diff --git a/js/public/LocaleSensitive.h b/js/public/LocaleSensitive.h
new file mode 100644
index 0000000000..22a5db04c6
--- /dev/null
+++ b/js/public/LocaleSensitive.h
@@ -0,0 +1,95 @@
+/* -*- 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/. */
+
+/*
+ * Functions and structures related to locale-sensitive behavior, including
+ * exposure of the default locale (used by operations like toLocaleString).
+ */
+
+#ifndef js_LocaleSensitive_h
+#define js_LocaleSensitive_h
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+#include "js/Utility.h" // JS::UniqueChars
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+struct JS_PUBLIC_API JSRuntime;
+class JS_PUBLIC_API JSString;
+
+/**
+ * Set the default locale for the ECMAScript Internationalization API
+ * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat, and others that will
+ * arise as time passes). (Note that the Internationalization API encourages
+ * clients to specify their own locales; this default locale is only used when
+ * no locale is specified, e.g. calling a toLocaleString function without
+ * passing a locale argument to it.)
+ *
+ * The locale string remains owned by the caller.
+ */
+extern JS_PUBLIC_API bool JS_SetDefaultLocale(JSRuntime* rt,
+ const char* locale);
+
+/**
+ * Return a copy of the default locale for the ECMAScript Internationalization
+ * API (and for various ECMAScript functions that will invoke it). The locale
+ * is retrieved from the |JSRuntime| that corresponds to |cx|.
+ *
+ * XXX Bug 1483961 means it's difficult to interpret the meaning of a null
+ * return value for the time being, and we should fix this!
+ */
+extern JS_PUBLIC_API JS::UniqueChars JS_GetDefaultLocale(JSContext* cx);
+
+/** Reset the default locale to OS defaults. */
+extern JS_PUBLIC_API void JS_ResetDefaultLocale(JSRuntime* rt);
+
+using JSLocaleToUpperCase = bool (*)(JSContext* cx, JS::Handle<JSString*> src,
+ JS::MutableHandle<JS::Value> rval);
+
+using JSLocaleToLowerCase = bool (*)(JSContext* cx, JS::Handle<JSString*> src,
+ JS::MutableHandle<JS::Value> rval);
+
+using JSLocaleCompare = bool (*)(JSContext* cx, JS::Handle<JSString*> src1,
+ JS::Handle<JSString*> src2,
+ JS::MutableHandle<JS::Value> rval);
+
+using JSLocaleToUnicode = bool (*)(JSContext* cx, const char* src,
+ JS::MutableHandle<JS::Value> rval);
+
+/**
+ * A suite of locale-specific string conversion and error message callbacks
+ * used to implement locale-sensitive behaviors (such as those performed by
+ * the various toLocaleString and toLocale{Date,Time}String functions).
+ *
+ * If SpiderMonkey is compiled --with-intl-api, then #if JS_HAS_INTL_API. In
+ * this case, SpiderMonkey itself will implement ECMA-402-compliant behavior by
+ * calling on ICU, and none of the fields in this struct will ever be used.
+ * (You'll still be able to call the get/set-callbacks functions; they just
+ * won't affect JavaScript semantics.)
+ */
+struct JSLocaleCallbacks {
+ JSLocaleToUpperCase localeToUpperCase;
+ JSLocaleToLowerCase localeToLowerCase;
+ JSLocaleCompare localeCompare;
+ JSLocaleToUnicode localeToUnicode;
+};
+
+/**
+ * Set locale callbacks to be used in builds not compiled --with-intl-api.
+ * |callbacks| must persist as long as the |JSRuntime|. Pass |nullptr| to
+ * restore default behavior.
+ */
+extern JS_PUBLIC_API void JS_SetLocaleCallbacks(
+ JSRuntime* rt, const JSLocaleCallbacks* callbacks);
+
+/**
+ * Return the current locale callbacks, which may be nullptr.
+ */
+extern JS_PUBLIC_API const JSLocaleCallbacks* JS_GetLocaleCallbacks(
+ JSRuntime* rt);
+
+#endif /* js_LocaleSensitive_h */
diff --git a/js/public/MemoryFunctions.h b/js/public/MemoryFunctions.h
new file mode 100644
index 0000000000..2629207b33
--- /dev/null
+++ b/js/public/MemoryFunctions.h
@@ -0,0 +1,100 @@
+/* -*- 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/. */
+
+/* Low-level memory-allocation functions. */
+
+#ifndef js_MemoryFunctions_h
+#define js_MemoryFunctions_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_MUST_USE
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSFreeOp;
+class JS_PUBLIC_API JSObject;
+struct JS_PUBLIC_API JSRuntime;
+
+extern JS_PUBLIC_API void* JS_malloc(JSContext* cx, size_t nbytes);
+
+extern JS_PUBLIC_API void* JS_realloc(JSContext* cx, void* p, size_t oldBytes,
+ size_t newBytes);
+
+/**
+ * A wrapper for |js_free(p)| that may delay |js_free(p)| invocation as a
+ * performance optimization. |cx| may be nullptr.
+ */
+extern JS_PUBLIC_API void JS_free(JSContext* cx, void* p);
+
+/**
+ * Same as above, but for buffers that will be used with the BYOB
+ * (Bring Your Own Buffer) JSString creation functions, such as
+ * JS_NewLatin1String and JS_NewUCString
+ */
+extern JS_PUBLIC_API void* JS_string_malloc(JSContext* cx, size_t nbytes);
+
+extern JS_PUBLIC_API void* JS_string_realloc(JSContext* cx, void* p,
+ size_t oldBytes, size_t newBytes);
+
+extern JS_PUBLIC_API void JS_string_free(JSContext* cx, void* p);
+
+/**
+ * A wrapper for |js_free(p)| that may delay |js_free(p)| invocation as a
+ * performance optimization as specified by the given JSFreeOp instance.
+ */
+extern JS_PUBLIC_API void JS_freeop(JSFreeOp* fop, void* p);
+
+namespace JS {
+
+/**
+ * The different possible memory uses to pass to Add/RemoveAssociatedMemory.
+ */
+#define JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \
+ _(XPCWrappedNative) \
+ _(DOMBinding) \
+ _(CTypeFFIType) \
+ _(CTypeFFITypeElements) \
+ _(CTypeFunctionInfo) \
+ _(CTypeFieldInfo) \
+ _(CDataBufferPtr) \
+ _(CDataBuffer) \
+ _(CClosureInfo) \
+ _(CTypesInt64) \
+ _(PerfMeasurement) \
+ _(Embedding1) \
+ _(Embedding2) \
+ _(Embedding3) \
+ _(Embedding4) \
+ _(Embedding5)
+
+enum class MemoryUse : uint8_t {
+#define DEFINE_MEMORY_USE(Name) Name,
+ JS_FOR_EACH_PUBLIC_MEMORY_USE(DEFINE_MEMORY_USE)
+#undef DEFINE_MEMORY_USE
+};
+
+/**
+ * Advise the GC of external memory owned by a JSObject. This is used to
+ * determine when to collect zones. Calls must be matched by calls to
+ * RemoveAssociatedMemory() when the memory is deallocated or no longer owned by
+ * the object.
+ */
+extern JS_PUBLIC_API void AddAssociatedMemory(JSObject* obj, size_t nbytes,
+ MemoryUse use);
+
+/**
+ * Advise the GC that external memory reported by JS::AddAssociatedMemory() is
+ * no longer owned by a JSObject. Calls must match those to
+ * AddAssociatedMemory().
+ */
+extern JS_PUBLIC_API void RemoveAssociatedMemory(JSObject* obj, size_t nbytes,
+ MemoryUse use);
+
+} // namespace JS
+
+#endif /* js_MemoryFunctions_h */
diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h
new file mode 100644
index 0000000000..adf68f31cd
--- /dev/null
+++ b/js/public/MemoryMetrics.h
@@ -0,0 +1,902 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_MemoryMetrics_h
+#define js_MemoryMetrics_h
+
+// These declarations are highly likely to change in the future. Depend on them
+// at your own risk.
+
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+
+#include <string.h>
+#include <type_traits>
+
+#include "jspubtd.h"
+
+#include "js/AllocPolicy.h"
+#include "js/GCAPI.h"
+#include "js/HashTable.h"
+#include "js/TracingAPI.h"
+#include "js/Utility.h"
+#include "js/Vector.h"
+
+class nsISupports; // Needed for ObjectPrivateVisitor.
+
+namespace JS {
+
+struct TabSizes {
+ TabSizes() = default;
+
+ enum Kind { Objects, Strings, Private, Other };
+
+ void add(Kind kind, size_t n) {
+ switch (kind) {
+ case Objects:
+ objects_ += n;
+ break;
+ case Strings:
+ strings_ += n;
+ break;
+ case Private:
+ private_ += n;
+ break;
+ case Other:
+ other_ += n;
+ break;
+ default:
+ MOZ_CRASH("bad TabSizes kind");
+ }
+ }
+
+ size_t objects_ = 0;
+ size_t strings_ = 0;
+ size_t private_ = 0;
+ size_t other_ = 0;
+};
+
+/** These are the measurements used by Servo. */
+struct ServoSizes {
+ ServoSizes() = default;
+
+ enum Kind {
+ GCHeapUsed,
+ GCHeapUnused,
+ GCHeapAdmin,
+ GCHeapDecommitted,
+ MallocHeap,
+ NonHeap,
+ Ignore
+ };
+
+ void add(Kind kind, size_t n) {
+ switch (kind) {
+ case GCHeapUsed:
+ gcHeapUsed += n;
+ break;
+ case GCHeapUnused:
+ gcHeapUnused += n;
+ break;
+ case GCHeapAdmin:
+ gcHeapAdmin += n;
+ break;
+ case GCHeapDecommitted:
+ gcHeapDecommitted += n;
+ break;
+ case MallocHeap:
+ mallocHeap += n;
+ break;
+ case NonHeap:
+ nonHeap += n;
+ break;
+ case Ignore: /* do nothing */
+ break;
+ default:
+ MOZ_CRASH("bad ServoSizes kind");
+ }
+ }
+
+ size_t gcHeapUsed = 0;
+ size_t gcHeapUnused = 0;
+ size_t gcHeapAdmin = 0;
+ size_t gcHeapDecommitted = 0;
+ size_t mallocHeap = 0;
+ size_t nonHeap = 0;
+};
+
+} // namespace JS
+
+namespace js {
+
+/**
+ * In memory reporting, we have concept of "sundries", line items which are too
+ * small to be worth reporting individually. Under some circumstances, a memory
+ * reporter gets tossed into the sundries bucket if it's smaller than
+ * MemoryReportingSundriesThreshold() bytes.
+ *
+ * We need to define this value here, rather than in the code which actually
+ * generates the memory reports, because NotableStringInfo uses this value.
+ */
+JS_FRIEND_API size_t MemoryReportingSundriesThreshold();
+
+/**
+ * This hash policy avoids flattening ropes (which perturbs the site being
+ * measured and requires a JSContext) at the expense of doing a FULL ROPE COPY
+ * on every hash and match! Beware.
+ */
+struct InefficientNonFlatteningStringHashPolicy {
+ typedef JSString* Lookup;
+ static HashNumber hash(const Lookup& l);
+ static bool match(const JSString* const& k, const Lookup& l);
+};
+
+// This file features many classes with numerous size_t fields, and each such
+// class has one or more methods that need to operate on all of these fields.
+// Writing these individually is error-prone -- it's easy to add a new field
+// without updating all the required methods. So we define a single macro list
+// in each class to name the fields (and notable characteristics of them), and
+// then use the following macros to transform those lists into the required
+// methods.
+//
+// - The |tabKind| value is used when measuring TabSizes.
+//
+// - The |servoKind| value is used when measuring ServoSizes and also for
+// the various sizeOfLiveGCThings() methods.
+//
+// In some classes, one or more of the macro arguments aren't used. We use '_'
+// for those.
+//
+#define DECL_SIZE_ZERO(tabKind, servoKind, mSize) size_t mSize = 0;
+#define ADD_OTHER_SIZE(tabKind, servoKind, mSize) mSize += other.mSize;
+#define SUB_OTHER_SIZE(tabKind, servoKind, mSize) \
+ MOZ_ASSERT(mSize >= other.mSize); \
+ mSize -= other.mSize;
+#define ADD_SIZE_TO_N(tabKind, servoKind, mSize) n += mSize;
+#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize) \
+ /* Avoid self-comparison warnings by comparing enums indirectly. */ \
+ n += (std::is_same_v<int[ServoSizes::servoKind], \
+ int[ServoSizes::GCHeapUsed]>) \
+ ? mSize \
+ : 0;
+#define ADD_TO_TAB_SIZES(tabKind, servoKind, mSize) \
+ sizes->add(JS::TabSizes::tabKind, mSize);
+#define ADD_TO_SERVO_SIZES(tabKind, servoKind, mSize) \
+ sizes->add(JS::ServoSizes::servoKind, mSize);
+
+} // namespace js
+
+namespace JS {
+
+struct ClassInfo {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(Objects, GCHeapUsed, objectsGCHeap) \
+ MACRO(Objects, MallocHeap, objectsMallocHeapSlots) \
+ MACRO(Objects, MallocHeap, objectsMallocHeapElementsNormal) \
+ MACRO(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \
+ MACRO(Objects, MallocHeap, objectsMallocHeapMisc) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsNormal) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsShared) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsWasm) \
+ MACRO(Objects, NonHeap, objectsNonHeapCodeWasm)
+
+ ClassInfo() = default;
+
+ void add(const ClassInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); }
+
+ void subtract(const ClassInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); }
+
+ size_t sizeOfAllThings() const {
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N);
+ return n;
+ }
+
+ bool isNotable() const {
+ static const size_t NotabilityThreshold = 16 * 1024;
+ return sizeOfAllThings() >= NotabilityThreshold;
+ }
+
+ size_t sizeOfLiveGCThings() const {
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
+ return n;
+ }
+
+ void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ size_t wasmGuardPages = 0;
+
+#undef FOR_EACH_SIZE
+};
+
+struct ShapeInfo {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapTree) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapDict) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapBase) \
+ MACRO(Other, MallocHeap, shapesMallocHeapTreeTables) \
+ MACRO(Other, MallocHeap, shapesMallocHeapDictTables) \
+ MACRO(Other, MallocHeap, shapesMallocHeapTreeChildren)
+
+ ShapeInfo() = default;
+
+ void add(const ShapeInfo& other) { FOR_EACH_SIZE(ADD_OTHER_SIZE); }
+
+ void subtract(const ShapeInfo& other) { FOR_EACH_SIZE(SUB_OTHER_SIZE); }
+
+ size_t sizeOfAllThings() const {
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N);
+ return n;
+ }
+
+ size_t sizeOfLiveGCThings() const {
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
+ return n;
+ }
+
+ void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+#undef FOR_EACH_SIZE
+};
+
+/**
+ * Holds data about a notable class (one whose combined object and shape
+ * instances use more than a certain amount of memory) so we can report it
+ * individually.
+ *
+ * The only difference between this class and ClassInfo is that this class
+ * holds a copy of the filename.
+ */
+struct NotableClassInfo : public ClassInfo {
+ NotableClassInfo() = default;
+ NotableClassInfo(NotableClassInfo&&) = default;
+ NotableClassInfo(const NotableClassInfo& info) = delete;
+
+ NotableClassInfo(const char* className, const ClassInfo& info);
+
+ UniqueChars className_ = nullptr;
+};
+
+/** Data for tracking JIT-code memory usage. */
+struct CodeSizes {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(_, NonHeap, ion) \
+ MACRO(_, NonHeap, baseline) \
+ MACRO(_, NonHeap, regexp) \
+ MACRO(_, NonHeap, other) \
+ MACRO(_, NonHeap, unused)
+
+ CodeSizes() = default;
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+#undef FOR_EACH_SIZE
+};
+
+/** Data for tracking GC memory usage. */
+struct GCSizes {
+ // |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted
+ // because we don't consider the nursery to be part of the GC heap.
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(_, MallocHeap, marker) \
+ MACRO(_, NonHeap, nurseryCommitted) \
+ MACRO(_, MallocHeap, nurseryMallocedBuffers) \
+ MACRO(_, MallocHeap, storeBufferVals) \
+ MACRO(_, MallocHeap, storeBufferCells) \
+ MACRO(_, MallocHeap, storeBufferSlots) \
+ MACRO(_, MallocHeap, storeBufferWholeCells) \
+ MACRO(_, MallocHeap, storeBufferGenerics)
+
+ GCSizes() = default;
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+#undef FOR_EACH_SIZE
+};
+
+/**
+ * This class holds information about the memory taken up by identical copies of
+ * a particular string. Multiple JSStrings may have their sizes aggregated
+ * together into one StringInfo object. Note that two strings with identical
+ * chars will not be aggregated together if one is a short string and the other
+ * is not.
+ */
+struct StringInfo {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(Strings, GCHeapUsed, gcHeapLatin1) \
+ MACRO(Strings, GCHeapUsed, gcHeapTwoByte) \
+ MACRO(Strings, MallocHeap, mallocHeapLatin1) \
+ MACRO(Strings, MallocHeap, mallocHeapTwoByte)
+
+ StringInfo() = default;
+
+ void add(const StringInfo& other) {
+ FOR_EACH_SIZE(ADD_OTHER_SIZE);
+ numCopies++;
+ }
+
+ void subtract(const StringInfo& other) {
+ FOR_EACH_SIZE(SUB_OTHER_SIZE);
+ numCopies--;
+ }
+
+ bool isNotable() const {
+ static const size_t NotabilityThreshold = 16 * 1024;
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N);
+ return n >= NotabilityThreshold;
+ }
+
+ size_t sizeOfLiveGCThings() const {
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
+ return n;
+ }
+
+ void addToTabSizes(TabSizes* sizes) const { FOR_EACH_SIZE(ADD_TO_TAB_SIZES); }
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ uint32_t numCopies = 0; // How many copies of the string have we seen?
+
+#undef FOR_EACH_SIZE
+};
+
+/**
+ * Holds data about a notable string (one which, counting all duplicates, uses
+ * more than a certain amount of memory) so we can report it individually.
+ *
+ * The only difference between this class and StringInfo is that
+ * NotableStringInfo holds a copy of some or all of the string's chars.
+ */
+struct NotableStringInfo : public StringInfo {
+ static const size_t MAX_SAVED_CHARS = 1024;
+
+ NotableStringInfo() = default;
+ NotableStringInfo(NotableStringInfo&&) = default;
+ NotableStringInfo(const NotableStringInfo&) = delete;
+
+ NotableStringInfo(JSString* str, const StringInfo& info);
+
+ UniqueChars buffer = nullptr;
+ size_t length = 0;
+};
+
+/**
+ * This class holds information about the memory taken up by script sources
+ * from a particular file.
+ */
+struct ScriptSourceInfo {
+#define FOR_EACH_SIZE(MACRO) MACRO(_, MallocHeap, misc)
+
+ ScriptSourceInfo() = default;
+
+ void add(const ScriptSourceInfo& other) {
+ FOR_EACH_SIZE(ADD_OTHER_SIZE);
+ numScripts++;
+ }
+
+ void subtract(const ScriptSourceInfo& other) {
+ FOR_EACH_SIZE(SUB_OTHER_SIZE);
+ numScripts--;
+ }
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ }
+
+ bool isNotable() const {
+ static const size_t NotabilityThreshold = 16 * 1024;
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N);
+ return n >= NotabilityThreshold;
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ uint32_t numScripts = 0; // How many ScriptSources come from this file? (It
+ // can be more than one in XML files that have
+ // multiple scripts in CDATA sections.)
+#undef FOR_EACH_SIZE
+};
+
+/**
+ * Holds data about a notable script source file (one whose combined
+ * script sources use more than a certain amount of memory) so we can report it
+ * individually.
+ *
+ * The only difference between this class and ScriptSourceInfo is that this
+ * class holds a copy of the filename.
+ */
+struct NotableScriptSourceInfo : public ScriptSourceInfo {
+ NotableScriptSourceInfo() = default;
+ NotableScriptSourceInfo(NotableScriptSourceInfo&&) = default;
+ NotableScriptSourceInfo(const NotableScriptSourceInfo&) = delete;
+
+ NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info);
+
+ UniqueChars filename_ = nullptr;
+};
+
+struct HelperThreadStats {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(_, MallocHeap, stateData) \
+ MACRO(_, MallocHeap, parseTask) \
+ MACRO(_, MallocHeap, ionCompileTask) \
+ MACRO(_, MallocHeap, wasmCompile) \
+ MACRO(_, MallocHeap, contexts)
+
+ HelperThreadStats() = default;
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ unsigned idleThreadCount = 0;
+ unsigned activeThreadCount = 0;
+
+#undef FOR_EACH_SIZE
+};
+
+/**
+ * Measurements that not associated with any individual runtime.
+ */
+struct GlobalStats {
+#define FOR_EACH_SIZE(MACRO) MACRO(_, MallocHeap, tracelogger)
+
+ explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf)
+ : mallocSizeOf_(mallocSizeOf) {}
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ HelperThreadStats helperThread;
+
+ mozilla::MallocSizeOf mallocSizeOf_;
+
+#undef FOR_EACH_SIZE
+};
+
+/**
+ * These measurements relate directly to the JSRuntime, and not to zones,
+ * compartments, and realms within it.
+ */
+struct RuntimeSizes {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(_, MallocHeap, object) \
+ MACRO(_, MallocHeap, atomsTable) \
+ MACRO(_, MallocHeap, atomsMarkBitmaps) \
+ MACRO(_, MallocHeap, contexts) \
+ MACRO(_, MallocHeap, temporary) \
+ MACRO(_, MallocHeap, interpreterStack) \
+ MACRO(_, MallocHeap, sharedImmutableStringsCache) \
+ MACRO(_, MallocHeap, sharedIntlData) \
+ MACRO(_, MallocHeap, uncompressedSourceCache) \
+ MACRO(_, MallocHeap, scriptData) \
+ MACRO(_, MallocHeap, tracelogger) \
+ MACRO(_, MallocHeap, wasmRuntime) \
+ MACRO(_, MallocHeap, jitLazyLink)
+
+ RuntimeSizes() { allScriptSources.emplace(); }
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ scriptSourceInfo.addToServoSizes(sizes);
+ gc.addToServoSizes(sizes);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ // The script source measurements in |scriptSourceInfo| are initially for
+ // all script sources. At the end, if the measurement granularity is
+ // FineGrained, we subtract the measurements of the notable script sources
+ // and move them into |notableScriptSources|.
+ ScriptSourceInfo scriptSourceInfo;
+ GCSizes gc;
+
+ typedef js::HashMap<const char*, ScriptSourceInfo, mozilla::CStringHasher,
+ js::SystemAllocPolicy>
+ ScriptSourcesHashMap;
+
+ // |allScriptSources| is only used transiently. During the reporting phase
+ // it is filled with info about every script source in the runtime. It's
+ // then used to fill in |notableScriptSources| (which actually gets
+ // reported), and immediately discarded afterwards.
+ mozilla::Maybe<ScriptSourcesHashMap> allScriptSources;
+ js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy>
+ notableScriptSources;
+
+#undef FOR_EACH_SIZE
+};
+
+struct UnusedGCThingSizes {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(Other, GCHeapUnused, object) \
+ MACRO(Other, GCHeapUnused, script) \
+ MACRO(Other, GCHeapUnused, shape) \
+ MACRO(Other, GCHeapUnused, baseShape) \
+ MACRO(Other, GCHeapUnused, objectGroup) \
+ MACRO(Other, GCHeapUnused, string) \
+ MACRO(Other, GCHeapUnused, symbol) \
+ MACRO(Other, GCHeapUnused, bigInt) \
+ MACRO(Other, GCHeapUnused, jitcode) \
+ MACRO(Other, GCHeapUnused, scope) \
+ MACRO(Other, GCHeapUnused, regExpShared)
+
+ UnusedGCThingSizes() = default;
+ UnusedGCThingSizes(UnusedGCThingSizes&& other) = default;
+
+ void addToKind(JS::TraceKind kind, intptr_t n) {
+ switch (kind) {
+ case JS::TraceKind::Object:
+ object += n;
+ break;
+ case JS::TraceKind::String:
+ string += n;
+ break;
+ case JS::TraceKind::Symbol:
+ symbol += n;
+ break;
+ case JS::TraceKind::BigInt:
+ bigInt += n;
+ break;
+ case JS::TraceKind::Script:
+ script += n;
+ break;
+ case JS::TraceKind::Shape:
+ shape += n;
+ break;
+ case JS::TraceKind::BaseShape:
+ baseShape += n;
+ break;
+ case JS::TraceKind::JitCode:
+ jitcode += n;
+ break;
+ case JS::TraceKind::ObjectGroup:
+ objectGroup += n;
+ break;
+ case JS::TraceKind::Scope:
+ scope += n;
+ break;
+ case JS::TraceKind::RegExpShared:
+ regExpShared += n;
+ break;
+ default:
+ MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
+ }
+ }
+
+ void addSizes(const UnusedGCThingSizes& other) {
+ FOR_EACH_SIZE(ADD_OTHER_SIZE);
+ }
+
+ size_t totalSize() const {
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N);
+ return n;
+ }
+
+ void addToTabSizes(JS::TabSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
+ }
+
+ void addToServoSizes(JS::ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+#undef FOR_EACH_SIZE
+};
+
+struct ZoneStats {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(Other, GCHeapUsed, symbolsGCHeap) \
+ MACRO(Other, GCHeapUsed, bigIntsGCHeap) \
+ MACRO(Other, MallocHeap, bigIntsMallocHeap) \
+ MACRO(Other, GCHeapAdmin, gcHeapArenaAdmin) \
+ MACRO(Other, GCHeapUsed, jitCodesGCHeap) \
+ MACRO(Other, GCHeapUsed, objectGroupsGCHeap) \
+ MACRO(Other, GCHeapUsed, scopesGCHeap) \
+ MACRO(Other, MallocHeap, scopesMallocHeap) \
+ MACRO(Other, GCHeapUsed, regExpSharedsGCHeap) \
+ MACRO(Other, MallocHeap, regExpSharedsMallocHeap) \
+ MACRO(Other, MallocHeap, regexpZone) \
+ MACRO(Other, MallocHeap, jitZone) \
+ MACRO(Other, MallocHeap, baselineStubsOptimized) \
+ MACRO(Other, MallocHeap, uniqueIdMap) \
+ MACRO(Other, MallocHeap, shapeTables) \
+ MACRO(Other, MallocHeap, compartmentObjects) \
+ MACRO(Other, MallocHeap, crossCompartmentWrappersTables) \
+ MACRO(Other, MallocHeap, compartmentsPrivateData) \
+ MACRO(Other, MallocHeap, scriptCountsMap)
+
+ ZoneStats() = default;
+ ZoneStats(ZoneStats&& other) = default;
+
+ void initStrings();
+
+ void addSizes(const ZoneStats& other) {
+ MOZ_ASSERT(isTotals);
+ FOR_EACH_SIZE(ADD_OTHER_SIZE);
+ unusedGCThings.addSizes(other.unusedGCThings);
+ stringInfo.add(other.stringInfo);
+ shapeInfo.add(other.shapeInfo);
+ }
+
+ size_t sizeOfLiveGCThings() const {
+ MOZ_ASSERT(isTotals);
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
+ n += stringInfo.sizeOfLiveGCThings();
+ n += shapeInfo.sizeOfLiveGCThings();
+ return n;
+ }
+
+ void addToTabSizes(JS::TabSizes* sizes) const {
+ MOZ_ASSERT(isTotals);
+ FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
+ unusedGCThings.addToTabSizes(sizes);
+ stringInfo.addToTabSizes(sizes);
+ shapeInfo.addToTabSizes(sizes);
+ }
+
+ void addToServoSizes(JS::ServoSizes* sizes) const {
+ MOZ_ASSERT(isTotals);
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ unusedGCThings.addToServoSizes(sizes);
+ stringInfo.addToServoSizes(sizes);
+ shapeInfo.addToServoSizes(sizes);
+ code.addToServoSizes(sizes);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ // These string measurements are initially for all strings. At the end,
+ // if the measurement granularity is FineGrained, we subtract the
+ // measurements of the notable script sources and move them into
+ // |notableStrings|.
+ UnusedGCThingSizes unusedGCThings;
+ StringInfo stringInfo;
+ ShapeInfo shapeInfo;
+ CodeSizes code;
+ void* extra = nullptr; // This field can be used by embedders.
+
+ typedef js::HashMap<JSString*, StringInfo,
+ js::InefficientNonFlatteningStringHashPolicy,
+ js::SystemAllocPolicy>
+ StringsHashMap;
+
+ // |allStrings| is only used transiently. During the zone traversal it is
+ // filled with info about every string in the zone. It's then used to fill
+ // in |notableStrings| (which actually gets reported), and immediately
+ // discarded afterwards.
+ mozilla::Maybe<StringsHashMap> allStrings;
+ js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
+ bool isTotals = true;
+
+#undef FOR_EACH_SIZE
+};
+
+struct RealmStats {
+ // We assume that |objectsPrivate| is on the malloc heap, but it's not
+ // actually guaranteed. But for Servo, at least, it's a moot point because
+ // it doesn't provide an ObjectPrivateVisitor so the value will always be
+ // zero.
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(Private, MallocHeap, objectsPrivate) \
+ MACRO(Other, GCHeapUsed, scriptsGCHeap) \
+ MACRO(Other, MallocHeap, scriptsMallocHeapData) \
+ MACRO(Other, MallocHeap, baselineData) \
+ MACRO(Other, MallocHeap, baselineStubsFallback) \
+ MACRO(Other, MallocHeap, ionData) \
+ MACRO(Other, MallocHeap, jitScripts) \
+ MACRO(Other, MallocHeap, realmObject) \
+ MACRO(Other, MallocHeap, realmTables) \
+ MACRO(Other, MallocHeap, innerViewsTable) \
+ MACRO(Other, MallocHeap, objectMetadataTable) \
+ MACRO(Other, MallocHeap, savedStacksSet) \
+ MACRO(Other, MallocHeap, varNamesSet) \
+ MACRO(Other, MallocHeap, nonSyntacticLexicalScopesTable) \
+ MACRO(Other, MallocHeap, jitRealm)
+
+ RealmStats() = default;
+ RealmStats(RealmStats&& other) = default;
+
+ RealmStats(const RealmStats&) = delete; // disallow copying
+
+ void initClasses();
+
+ void addSizes(const RealmStats& other) {
+ MOZ_ASSERT(isTotals);
+ FOR_EACH_SIZE(ADD_OTHER_SIZE);
+ classInfo.add(other.classInfo);
+ }
+
+ size_t sizeOfLiveGCThings() const {
+ MOZ_ASSERT(isTotals);
+ size_t n = 0;
+ FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING);
+ n += classInfo.sizeOfLiveGCThings();
+ return n;
+ }
+
+ void addToTabSizes(TabSizes* sizes) const {
+ MOZ_ASSERT(isTotals);
+ FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
+ classInfo.addToTabSizes(sizes);
+ }
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ MOZ_ASSERT(isTotals);
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ classInfo.addToServoSizes(sizes);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ // The class measurements in |classInfo| are initially for all classes. At
+ // the end, if the measurement granularity is FineGrained, we subtract the
+ // measurements of the notable classes and move them into |notableClasses|.
+ ClassInfo classInfo;
+ void* extra = nullptr; // This field can be used by embedders.
+
+ typedef js::HashMap<const char*, ClassInfo, mozilla::CStringHasher,
+ js::SystemAllocPolicy>
+ ClassesHashMap;
+
+ // These are similar to |allStrings| and |notableStrings| in ZoneStats.
+ mozilla::Maybe<ClassesHashMap> allClasses;
+ js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
+ bool isTotals = true;
+
+#undef FOR_EACH_SIZE
+};
+
+typedef js::Vector<RealmStats, 0, js::SystemAllocPolicy> RealmStatsVector;
+typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
+
+struct RuntimeStats {
+ // |gcHeapChunkTotal| is ignored because it's the sum of all the other
+ // values. |gcHeapGCThings| is ignored because it's the sum of some of the
+ // values from the zones and compartments. Both of those values are not
+ // reported directly, but are just present for sanity-checking other
+ // values.
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(_, Ignore, gcHeapChunkTotal) \
+ MACRO(_, GCHeapDecommitted, gcHeapDecommittedArenas) \
+ MACRO(_, GCHeapUnused, gcHeapUnusedChunks) \
+ MACRO(_, GCHeapUnused, gcHeapUnusedArenas) \
+ MACRO(_, GCHeapAdmin, gcHeapChunkAdmin) \
+ MACRO(_, Ignore, gcHeapGCThings)
+
+ explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
+ : mallocSizeOf_(mallocSizeOf) {}
+
+ // Here's a useful breakdown of the GC heap.
+ //
+ // - rtStats.gcHeapChunkTotal
+ // - decommitted bytes
+ // - rtStats.gcHeapDecommittedArenas
+ // (decommitted arenas in non-empty chunks)
+ // - unused bytes
+ // - rtStats.gcHeapUnusedChunks (empty chunks)
+ // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
+ // - rtStats.zTotals.unusedGCThings.totalSize()
+ // (empty GC thing slots within non-empty arenas)
+ // - used bytes
+ // - rtStats.gcHeapChunkAdmin
+ // - rtStats.zTotals.gcHeapArenaAdmin
+ // - rtStats.gcHeapGCThings (in-use GC things)
+ // == (rtStats.zTotals.sizeOfLiveGCThings() +
+ // rtStats.cTotals.sizeOfLiveGCThings())
+ //
+ // It's possible that some arenas in empty chunks may be decommitted, but
+ // we don't count those under rtStats.gcHeapDecommittedArenas because (a)
+ // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
+ // multiple of the chunk size, which is good.
+
+ void addToServoSizes(ServoSizes* sizes) const {
+ FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
+ runtime.addToServoSizes(sizes);
+ }
+
+ FOR_EACH_SIZE(DECL_SIZE_ZERO);
+
+ RuntimeSizes runtime;
+
+ RealmStats realmTotals; // The sum of this runtime's realms' measurements.
+ ZoneStats zTotals; // The sum of this runtime's zones' measurements.
+
+ RealmStatsVector realmStatsVector;
+ ZoneStatsVector zoneStatsVector;
+
+ ZoneStats* currZoneStats = nullptr;
+
+ mozilla::MallocSizeOf mallocSizeOf_;
+
+ virtual void initExtraRealmStats(JS::Realm* realm, RealmStats* rstats,
+ const JS::AutoRequireNoGC& nogc) = 0;
+ virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats,
+ const JS::AutoRequireNoGC& nogc) = 0;
+
+#undef FOR_EACH_SIZE
+};
+
+class ObjectPrivateVisitor {
+ public:
+ // Within CollectRuntimeStats, this method is called for each JS object
+ // that has an nsISupports pointer.
+ virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0;
+
+ // A callback that gets a JSObject's nsISupports pointer, if it has one.
+ // Note: this function does *not* addref |iface|.
+ typedef bool (*GetISupportsFun)(JSObject* obj, nsISupports** iface);
+ GetISupportsFun getISupports_;
+
+ explicit ObjectPrivateVisitor(GetISupportsFun getISupports)
+ : getISupports_(getISupports) {}
+};
+
+extern JS_PUBLIC_API bool CollectGlobalStats(GlobalStats* gStats);
+
+extern JS_PUBLIC_API bool CollectRuntimeStats(JSContext* cx,
+ RuntimeStats* rtStats,
+ ObjectPrivateVisitor* opv,
+ bool anonymize);
+
+extern JS_PUBLIC_API size_t SystemCompartmentCount(JSContext* cx);
+extern JS_PUBLIC_API size_t UserCompartmentCount(JSContext* cx);
+
+extern JS_PUBLIC_API size_t SystemRealmCount(JSContext* cx);
+extern JS_PUBLIC_API size_t UserRealmCount(JSContext* cx);
+
+extern JS_PUBLIC_API size_t PeakSizeOfTemporary(const JSContext* cx);
+
+extern JS_PUBLIC_API bool AddSizeOfTab(JSContext* cx, JS::HandleObject obj,
+ mozilla::MallocSizeOf mallocSizeOf,
+ ObjectPrivateVisitor* opv,
+ TabSizes* sizes);
+
+extern JS_PUBLIC_API bool AddServoSizeOf(JSContext* cx,
+ mozilla::MallocSizeOf mallocSizeOf,
+ ObjectPrivateVisitor* opv,
+ ServoSizes* sizes);
+
+} // namespace JS
+
+#undef DECL_SIZE_ZERO
+#undef ADD_OTHER_SIZE
+#undef SUB_OTHER_SIZE
+#undef ADD_SIZE_TO_N
+#undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
+#undef ADD_TO_TAB_SIZES
+
+#endif /* js_MemoryMetrics_h */
diff --git a/js/public/Modules.h b/js/public/Modules.h
new file mode 100644
index 0000000000..e4d9d38c06
--- /dev/null
+++ b/js/public/Modules.h
@@ -0,0 +1,214 @@
+/* -*- 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/. */
+
+/* JavaScript module (as in, the syntactic construct) operations. */
+
+#ifndef js_Modules_h
+#define js_Modules_h
+
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions
+#include "js/RootingAPI.h" // JS::{Mutable,}Handle
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+struct JS_PUBLIC_API JSRuntime;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+template <typename UnitT>
+class SourceText;
+} // namespace JS
+
+namespace JS {
+
+using ModuleResolveHook = JSObject* (*)(JSContext*, Handle<Value>,
+ Handle<JSString*>);
+
+/**
+ * Get the HostResolveImportedModule hook for the runtime.
+ */
+extern JS_PUBLIC_API ModuleResolveHook GetModuleResolveHook(JSRuntime* rt);
+
+/**
+ * Set the HostResolveImportedModule hook for the runtime to the given function.
+ */
+extern JS_PUBLIC_API void SetModuleResolveHook(JSRuntime* rt,
+ ModuleResolveHook func);
+
+using ModuleMetadataHook = bool (*)(JSContext*, Handle<Value>,
+ Handle<JSObject*>);
+
+/**
+ * Get the hook for populating the import.meta metadata object.
+ */
+extern JS_PUBLIC_API ModuleMetadataHook GetModuleMetadataHook(JSRuntime* rt);
+
+/**
+ * Set the hook for populating the import.meta metadata object to the given
+ * function.
+ */
+extern JS_PUBLIC_API void SetModuleMetadataHook(JSRuntime* rt,
+ ModuleMetadataHook func);
+
+using ModuleDynamicImportHook = bool (*)(JSContext* cx,
+ Handle<Value> referencingPrivate,
+ Handle<JSString*> specifier,
+ Handle<JSObject*> promise);
+
+/**
+ * Get the HostImportModuleDynamically hook for the runtime.
+ */
+extern JS_PUBLIC_API ModuleDynamicImportHook
+GetModuleDynamicImportHook(JSRuntime* rt);
+
+/**
+ * Set the HostImportModuleDynamically hook for the runtime to the given
+ * function.
+ *
+ * If this hook is not set (or set to nullptr) then the JS engine will throw an
+ * exception if dynamic module import is attempted.
+ */
+extern JS_PUBLIC_API void SetModuleDynamicImportHook(
+ JSRuntime* rt, ModuleDynamicImportHook func);
+
+/**
+ * Passed to FinishDynamicModuleImport to indicate the result of the dynamic
+ * import operation.
+ */
+enum class DynamicImportStatus { Failed = 0, Ok };
+
+/**
+ * This must be called after a dynamic import operation is complete.
+ *
+ * If |evaluationPromise| is rejected, the rejection reason will be used to
+ * complete the user's promise.
+ */
+extern JS_PUBLIC_API bool FinishDynamicModuleImport(
+ JSContext* cx, Handle<JSObject*> evaluationPromise,
+ Handle<Value> referencingPrivate, Handle<JSString*> specifier,
+ Handle<JSObject*> promise);
+
+/**
+ * This must be called after a dynamic import operation is complete.
+ *
+ * This is used so that Top Level Await functionality can be turned off
+ * entirely. It will be removed in bug#1676612.
+ *
+ * If |status| is Failed, any pending exception on the context will be used to
+ * complete the user's promise.
+ */
+extern JS_PUBLIC_API bool FinishDynamicModuleImport_NoTLA(
+ JSContext* cx, DynamicImportStatus status, Handle<Value> referencingPrivate,
+ Handle<JSString*> specifier, Handle<JSObject*> promise);
+
+/**
+ * Parse the given source buffer as a module in the scope of the current global
+ * of cx and return a source text module record.
+ */
+extern JS_PUBLIC_API JSObject* CompileModule(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf);
+
+/**
+ * Parse the given source buffer as a module in the scope of the current global
+ * of cx and return a source text module record. An error is reported if a
+ * UTF-8 encoding error is encountered.
+ */
+extern JS_PUBLIC_API JSObject* CompileModule(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<mozilla::Utf8Unit>& srcBuf);
+
+/**
+ * Set a private value associated with a source text module record.
+ */
+extern JS_PUBLIC_API void SetModulePrivate(JSObject* module,
+ const Value& value);
+
+/**
+ * Get the private value associated with a source text module record.
+ */
+extern JS_PUBLIC_API Value GetModulePrivate(JSObject* module);
+
+/*
+ * Perform the ModuleInstantiate operation on the given source text module
+ * record.
+ *
+ * This transitively resolves all module dependencies (calling the
+ * HostResolveImportedModule hook) and initializes the environment record for
+ * the module.
+ */
+extern JS_PUBLIC_API bool ModuleInstantiate(JSContext* cx,
+ Handle<JSObject*> moduleRecord);
+
+/*
+ * Perform the ModuleEvaluate operation on the given source text module record
+ * and returns a bool. A result value is returned in result and is either
+ * undefined (and ignored) or a promise (if Top Level Await is enabled).
+ *
+ * If this module has already been evaluated, it returns the evaluation
+ * promise. Otherwise, it transitively evaluates all dependences of this module
+ * and then evaluates this module.
+ *
+ * ModuleInstantiate must have completed prior to calling this.
+ */
+extern JS_PUBLIC_API bool ModuleEvaluate(JSContext* cx,
+ Handle<JSObject*> moduleRecord,
+ MutableHandleValue rval);
+
+/*
+ * If a module evaluation fails, unwrap the resulting evaluation promise
+ * and rethrow.
+ *
+ * This does nothing if this module succeeds in evaluation. Otherwise, it
+ * takes the reason for the module throwing, unwraps it and throws it as a
+ * regular error rather than as an uncaught promise.
+ *
+ * ModuleEvaluate must have completed prior to calling this.
+ */
+extern JS_PUBLIC_API bool ThrowOnModuleEvaluationFailure(
+ JSContext* cx, Handle<JSObject*> evaluationPromise);
+
+/*
+ * Get a list of the module specifiers used by a source text module
+ * record to request importation of modules.
+ *
+ * The result is a JavaScript array of object values. To extract the individual
+ * values use only JS::GetArrayLength and JS_GetElement with indices 0 to length
+ * - 1.
+ *
+ * The element values are objects with the following properties:
+ * - moduleSpecifier: the module specifier string
+ * - lineNumber: the line number of the import in the source text
+ * - columnNumber: the column number of the import in the source text
+ *
+ * These property values can be extracted with GetRequestedModuleSpecifier() and
+ * GetRequestedModuleSourcePos()
+ */
+extern JS_PUBLIC_API JSObject* GetRequestedModules(
+ JSContext* cx, Handle<JSObject*> moduleRecord);
+
+extern JS_PUBLIC_API JSString* GetRequestedModuleSpecifier(
+ JSContext* cx, Handle<Value> requestedModuleObject);
+
+extern JS_PUBLIC_API void GetRequestedModuleSourcePos(
+ JSContext* cx, Handle<Value> requestedModuleObject, uint32_t* lineNumber,
+ uint32_t* columnNumber);
+
+/*
+ * Get the top-level script for a module which has not yet been executed.
+ */
+extern JS_PUBLIC_API JSScript* GetModuleScript(Handle<JSObject*> moduleRecord);
+
+} // namespace JS
+
+#endif // js_Modules_h
diff --git a/js/public/Object.h b/js/public/Object.h
new file mode 100644
index 0000000000..06d1e95f8e
--- /dev/null
+++ b/js/public/Object.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_public_Object_h
+#define js_public_Object_h
+
+#include "js/shadow/Object.h" // JS::shadow::Object
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Class.h" // js::ESClass, JSCLASS_RESERVED_SLOTS, JSCLASS_HAS_PRIVATE
+#include "js/Realm.h" // JS::GetCompartmentForRealm
+#include "js/RootingAPI.h" // JS::{,Mutable}Handle
+#include "js/shadow/ObjectGroup.h" // JS::shadow::ObjectGroup
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API Compartment;
+
+/**
+ * Determine the ECMAScript "class" -- Date, String, RegExp, and all the other
+ * builtin object types (described in ECMAScript in terms of an objecting having
+ * "an [[ArrayBufferData]] internal slot" or similar language for other kinds of
+ * object -- of the provided object.
+ *
+ * If this function is passed a wrapper that can be unwrapped, the determination
+ * is performed on that object. If the wrapper can't be unwrapped, and it's not
+ * a wrapper that prefers to treat this operation as a failure, this function
+ * will indicate that the object is |js::ESClass::Other|.
+ */
+extern JS_PUBLIC_API bool GetBuiltinClass(JSContext* cx, Handle<JSObject*> obj,
+ js::ESClass* cls);
+
+/** Get the |JSClass| of an object. */
+inline const JSClass* GetClass(const JSObject* obj) {
+ return reinterpret_cast<const shadow::Object*>(obj)->group->clasp;
+}
+
+/**
+ * Get the |JS::Compartment*| of an object.
+ *
+ * Note that the compartment of an object in this realm, that is a
+ * cross-compartment wrapper around an object from another realm, is the
+ * compartment of this realm.
+ */
+static MOZ_ALWAYS_INLINE Compartment* GetCompartment(JSObject* obj) {
+ Realm* realm = reinterpret_cast<shadow::Object*>(obj)->group->realm;
+ return GetCompartmentForRealm(realm);
+}
+
+/**
+ * Get the private value stored for an object whose class has a private.
+ *
+ * It is safe to call this function within |obj|'s finalize hook.
+ */
+inline void* GetPrivate(JSObject* obj) {
+ MOZ_ASSERT(GetClass(obj)->flags & JSCLASS_HAS_PRIVATE);
+ const auto* nobj = reinterpret_cast<const shadow::Object*>(obj);
+ return nobj->fixedSlots()[nobj->numFixedSlots()].toPrivate();
+}
+
+/**
+ * Set the private value for |obj|.
+ *
+ * This function may called during the finalization of |obj|.
+ */
+extern JS_PUBLIC_API void SetPrivate(JSObject* obj, void* data);
+
+/**
+ * Get the value stored in a reserved slot in an object.
+ *
+ * If |obj| is known to be a proxy and you're willing to use friend APIs,
+ * |js::GetProxyReservedSlot| in "js/Proxy.h" is very slightly more efficient.
+ */
+inline const Value& GetReservedSlot(JSObject* obj, size_t slot) {
+ MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetClass(obj)));
+ return reinterpret_cast<const shadow::Object*>(obj)->slotRef(slot);
+}
+
+namespace detail {
+
+extern JS_FRIEND_API void SetReservedSlotWithBarrier(JSObject* obj, size_t slot,
+ const Value& value);
+
+} // namespace detail
+
+/**
+ * Store a value in an object's reserved slot.
+ *
+ * This can be used with both native objects and proxies. However, if |obj| is
+ * known to be a proxy, |js::SetProxyReservedSlot| in "js/Proxy.h" is very
+ * slightly more efficient.
+ */
+inline void SetReservedSlot(JSObject* obj, size_t slot, const Value& value) {
+ MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetClass(obj)));
+ auto* sobj = reinterpret_cast<shadow::Object*>(obj);
+ if (sobj->slotRef(slot).isGCThing() || value.isGCThing()) {
+ detail::SetReservedSlotWithBarrier(obj, slot, value);
+ } else {
+ sobj->slotRef(slot) = value;
+ }
+}
+
+} // namespace JS
+
+#endif // js_public_Object_h
diff --git a/js/public/OffThreadScriptCompilation.h b/js/public/OffThreadScriptCompilation.h
new file mode 100644
index 0000000000..32ae4785d9
--- /dev/null
+++ b/js/public/OffThreadScriptCompilation.h
@@ -0,0 +1,183 @@
+/* -*- 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/. */
+
+/*
+ * Types and functions related to the compilation of JavaScript off the
+ * direct JSAPI-using thread.
+ */
+
+#ifndef js_OffThreadScriptCompilation_h
+#define js_OffThreadScriptCompilation_h
+
+#include "mozilla/Range.h" // mozilla::Range
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+#include "mozilla/Vector.h" // mozilla::Vector
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions
+#include "js/GCVector.h" // JS::GCVector
+#include "js/Transcoding.h" // JS::TranscodeSource
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSScript;
+
+namespace JS {
+
+template <typename UnitT>
+class SourceText;
+
+} // namespace JS
+
+namespace JS {
+
+class OffThreadToken;
+
+using OffThreadCompileCallback = void (*)(OffThreadToken* token,
+ void* callbackData);
+
+/*
+ * Off thread compilation control flow.
+ *
+ * After successfully triggering an off thread compile of a script, the
+ * callback will eventually be invoked with the specified data and a token
+ * for the compilation. The callback will be invoked while off thread,
+ * so must ensure that its operations are thread safe. Afterwards, one of the
+ * following functions must be invoked on the runtime's main thread:
+ *
+ * - FinishOffThreadScript, to get the result script (or nullptr on failure).
+ * - CancelOffThreadScript, to free the resources without creating a script.
+ *
+ * The characters passed in to CompileOffThread must remain live until the
+ * callback is invoked, and the resulting script will be rooted until the call
+ * to FinishOffThreadScript.
+ */
+
+extern JS_PUBLIC_API bool CanCompileOffThread(
+ JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
+
+extern JS_PUBLIC_API OffThreadToken* CompileOffThread(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
+ void* callbackData);
+
+// NOTE: Unlike for the normal sync compilation functions, this function NEVER
+// INFLATES TO UTF-16. Therefore, it is ALWAYS invoking experimental
+// UTF-8 support. Inflate to UTF-16 yourself and use the other overload
+// if you're unable to take a risk using unstable functionality.
+extern JS_PUBLIC_API OffThreadToken* CompileOffThread(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<mozilla::Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
+ void* callbackData);
+
+// Finish the off-thread parse/decode task and return the script. Return the
+// script on success, or return null on failure (usually with an error reported)
+extern JS_PUBLIC_API JSScript* FinishOffThreadScript(JSContext* cx,
+ OffThreadToken* token);
+
+// Finish the off-thread parse/decode task and return the script, and register
+// an encoder on its script source, such that all functions can be encoded as
+// they are parsed. This strategy is used to avoid blocking the main thread in
+// a non-interruptible way.
+//
+// See also JS::FinishIncrementalEncoding.
+//
+// Return the script on success, or return null on failure (usually with an
+// error reported)
+extern JS_PUBLIC_API JSScript* FinishOffThreadScriptAndStartIncrementalEncoding(
+ JSContext* cx, OffThreadToken* token);
+
+extern JS_PUBLIC_API void CancelOffThreadScript(JSContext* cx,
+ OffThreadToken* token);
+
+extern JS_PUBLIC_API OffThreadToken* CompileOffThreadModule(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
+ void* callbackData);
+
+// NOTE: Unlike for the normal sync compilation functions, this function NEVER
+// INFLATES TO UTF-16. Therefore, it is ALWAYS invoking experimental
+// UTF-8 support. Inflate to UTF-16 yourself and use the other overload
+// if you're unable to take a risk using unstable functionality.
+extern JS_PUBLIC_API OffThreadToken* CompileOffThreadModule(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<mozilla::Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
+ void* callbackData);
+
+extern JS_PUBLIC_API JSObject* FinishOffThreadModule(JSContext* cx,
+ OffThreadToken* token);
+
+extern JS_PUBLIC_API void CancelOffThreadModule(JSContext* cx,
+ OffThreadToken* token);
+
+extern JS_PUBLIC_API bool CanDecodeOffThread(
+ JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
+
+// If options.useOffThreadParseGlobal is true,
+// decode JSScript from the buffer.
+//
+// If options.useOffThreadParseGlobal is false,
+// decode stencil from the buffer and instantiate JSScript from it.
+//
+// The start of `buffer` and `cursor` should meet
+// IsTranscodingBytecodeAligned and IsTranscodingBytecodeOffsetAligned.
+// (This should be handled while encoding).
+//
+// `buffer` should be alive until the end of `FinishOffThreadScriptDecoder`.
+extern JS_PUBLIC_API OffThreadToken* DecodeOffThreadScript(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
+ OffThreadCompileCallback callback, void* callbackData);
+
+// The start of `range` should be meet IsTranscodingBytecodeAligned and
+// AlignTranscodingBytecodeOffset.
+// (This should be handled while encoding).
+//
+// `range` should be alive until the end of `FinishOffThreadScriptDecoder`.
+extern JS_PUBLIC_API OffThreadToken* DecodeOffThreadScript(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ const mozilla::Range<uint8_t>& range /* TranscodeRange& */,
+ OffThreadCompileCallback callback, void* callbackData);
+
+extern JS_PUBLIC_API JSScript* FinishOffThreadScriptDecoder(
+ JSContext* cx, OffThreadToken* token);
+
+extern JS_PUBLIC_API void CancelOffThreadScriptDecoder(JSContext* cx,
+ OffThreadToken* token);
+
+// Decode multiple JSScript from the sources.
+extern JS_PUBLIC_API OffThreadToken* DecodeMultiOffThreadScripts(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ mozilla::Vector<TranscodeSource>& sources,
+ OffThreadCompileCallback callback, void* callbackData);
+
+extern JS_PUBLIC_API bool FinishMultiOffThreadScriptsDecoder(
+ JSContext* cx, OffThreadToken* token,
+ MutableHandle<GCVector<JSScript*>> scripts);
+
+extern JS_PUBLIC_API void CancelMultiOffThreadScriptsDecoder(
+ JSContext* cx, OffThreadToken* token);
+
+// Tell off-thread compilation to/not to use the parse global.
+// The default value is true.
+//
+// If set to false, off-thread compilation will compile to stencil, and
+// instantiate the stencil on main-thread.
+extern JS_PUBLIC_API void SetUseOffThreadParseGlobal(bool value);
+
+} // namespace JS
+
+namespace js {
+// Returns the value set by JS::SetUseOffThreadParseGlobal.
+// The default value is true.
+//
+// This value is consumed internally, and public API consumer shouldn't
+// directly use this value.
+extern bool UseOffThreadParseGlobal();
+} // namespace js
+
+#endif /* js_OffThreadScriptCompilation_h */
diff --git a/js/public/Principals.h b/js/public/Principals.h
new file mode 100644
index 0000000000..1e251806dc
--- /dev/null
+++ b/js/public/Principals.h
@@ -0,0 +1,160 @@
+/* -*- 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/. */
+
+/* JSPrincipals and related interfaces. */
+
+#ifndef js_Principals_h
+#define js_Principals_h
+
+#include "mozilla/Atomics.h"
+
+#include <stdint.h>
+
+#include "jspubtd.h"
+
+struct JSStructuredCloneReader;
+struct JSStructuredCloneWriter;
+
+struct JSPrincipals {
+ /* Don't call "destroy"; use reference counting macros below. */
+ mozilla::Atomic<int32_t, mozilla::SequentiallyConsistent> refcount{0};
+
+#ifdef JS_DEBUG
+ /* A helper to facilitate principals debugging. */
+ uint32_t debugToken;
+#endif
+
+ JSPrincipals() = default;
+
+ void setDebugToken(uint32_t token) {
+#ifdef JS_DEBUG
+ debugToken = token;
+#endif
+ }
+
+ /*
+ * Write the principals with the given |writer|. Return false on failure,
+ * true on success.
+ */
+ virtual bool write(JSContext* cx, JSStructuredCloneWriter* writer) = 0;
+
+ /*
+ * Whether the principal corresponds to a System or AddOn Principal.
+ * Technically this also checks for an ExpandedAddonPrincipal.
+ */
+ virtual bool isSystemOrAddonPrincipal() = 0;
+
+ /*
+ * This is not defined by the JS engine but should be provided by the
+ * embedding.
+ */
+ JS_PUBLIC_API void dump();
+};
+
+extern JS_PUBLIC_API void JS_HoldPrincipals(JSPrincipals* principals);
+
+extern JS_PUBLIC_API void JS_DropPrincipals(JSContext* cx,
+ JSPrincipals* principals);
+
+// Return whether the first principal subsumes the second. The exact meaning of
+// 'subsumes' is left up to the browser. Subsumption is checked inside the JS
+// engine when determining, e.g., which stack frames to display in a backtrace.
+typedef bool (*JSSubsumesOp)(JSPrincipals* first, JSPrincipals* second);
+
+/*
+ * Used to check if a CSP instance wants to disable eval() and friends.
+ * See GlobalObject::isRuntimeCodeGenEnabled() in vm/GlobalObject.cpp.
+ */
+typedef bool (*JSCSPEvalChecker)(JSContext* cx, JS::HandleString code);
+
+struct JSSecurityCallbacks {
+ JSCSPEvalChecker contentSecurityPolicyAllows;
+ JSSubsumesOp subsumes;
+};
+
+extern JS_PUBLIC_API void JS_SetSecurityCallbacks(
+ JSContext* cx, const JSSecurityCallbacks* callbacks);
+
+extern JS_PUBLIC_API const JSSecurityCallbacks* JS_GetSecurityCallbacks(
+ JSContext* cx);
+
+/*
+ * Code running with "trusted" principals will be given a deeper stack
+ * allocation than ordinary scripts. This allows trusted script to run after
+ * untrusted script has exhausted the stack. This function sets the
+ * runtime-wide trusted principal.
+ *
+ * This principals is not held (via JS_HoldPrincipals/JS_DropPrincipals).
+ * Instead, the caller must ensure that the given principals stays valid for as
+ * long as 'cx' may point to it. If the principals would be destroyed before
+ * 'cx', JS_SetTrustedPrincipals must be called again, passing nullptr for
+ * 'prin'.
+ */
+extern JS_PUBLIC_API void JS_SetTrustedPrincipals(JSContext* cx,
+ JSPrincipals* prin);
+
+typedef void (*JSDestroyPrincipalsOp)(JSPrincipals* principals);
+
+/*
+ * Initialize the callback that is called to destroy JSPrincipals instance
+ * when its reference counter drops to zero. The initialization can be done
+ * only once per JS runtime.
+ */
+extern JS_PUBLIC_API void JS_InitDestroyPrincipalsCallback(
+ JSContext* cx, JSDestroyPrincipalsOp destroyPrincipals);
+
+/*
+ * Read a JSPrincipals instance from the given |reader| and initialize the out
+ * paratemer |outPrincipals| to the JSPrincipals instance read.
+ *
+ * Return false on failure, true on success. The |outPrincipals| parameter
+ * should not be modified if false is returned.
+ *
+ * The caller is not responsible for calling JS_HoldPrincipals on the resulting
+ * JSPrincipals instance, the JSReadPrincipalsOp must increment the refcount of
+ * the resulting JSPrincipals on behalf of the caller.
+ */
+using JSReadPrincipalsOp = bool (*)(JSContext* cx,
+ JSStructuredCloneReader* reader,
+ JSPrincipals** outPrincipals);
+
+/*
+ * Initialize the callback that is called to read JSPrincipals instances from a
+ * buffer. The initialization can be done only once per JS runtime.
+ */
+extern JS_PUBLIC_API void JS_InitReadPrincipalsCallback(
+ JSContext* cx, JSReadPrincipalsOp read);
+
+namespace JS {
+
+class MOZ_RAII AutoHoldPrincipals {
+ JSContext* cx_;
+ JSPrincipals* principals_ = nullptr;
+
+ public:
+ explicit AutoHoldPrincipals(JSContext* cx, JSPrincipals* principals = nullptr)
+ : cx_(cx) {
+ reset(principals);
+ }
+
+ ~AutoHoldPrincipals() { reset(nullptr); }
+
+ void reset(JSPrincipals* principals) {
+ if (principals) {
+ JS_HoldPrincipals(principals);
+ }
+ if (principals_) {
+ JS_DropPrincipals(cx_, principals_);
+ }
+ principals_ = principals;
+ }
+
+ JSPrincipals* get() const { return principals_; }
+};
+
+} // namespace JS
+
+#endif /* js_Principals_h */
diff --git a/js/public/Printf.h b/js/public/Printf.h
new file mode 100644
index 0000000000..aca542d796
--- /dev/null
+++ b/js/public/Printf.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Printf_h
+#define js_Printf_h
+
+#include "mozilla/Printf.h"
+
+#include <stdarg.h>
+
+#include "jstypes.h"
+#include "js/Utility.h"
+
+/* Wrappers for mozilla::Smprintf and friends that are used throughout
+ JS. */
+
+extern JS_PUBLIC_API JS::UniqueChars JS_smprintf(const char* fmt, ...)
+ MOZ_FORMAT_PRINTF(1, 2);
+
+extern JS_PUBLIC_API JS::UniqueChars JS_sprintf_append(JS::UniqueChars&& last,
+ const char* fmt, ...)
+ MOZ_FORMAT_PRINTF(2, 3);
+
+extern JS_PUBLIC_API JS::UniqueChars JS_vsmprintf(const char* fmt, va_list ap)
+ MOZ_FORMAT_PRINTF(1, 0);
+extern JS_PUBLIC_API JS::UniqueChars JS_vsprintf_append(JS::UniqueChars&& last,
+ const char* fmt,
+ va_list ap)
+ MOZ_FORMAT_PRINTF(2, 0);
+
+#endif /* js_Printf_h */
diff --git a/js/public/ProfilingCategory.h b/js/public/ProfilingCategory.h
new file mode 100644
index 0000000000..e922bc5e8c
--- /dev/null
+++ b/js/public/ProfilingCategory.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_ProfilingCategory_h
+#define js_ProfilingCategory_h
+
+#include "jstypes.h" // JS_FRIEND_API
+
+// The source lives in mozglue/baseprofiler/public/ProfilingCategoryList.h
+#include "js/ProfilingCategoryList.h" // MOZ_PROFILING_CATEGORY_LIST
+
+namespace JS {
+
+// clang-format off
+
+// An enum that lists all possible category pairs in one list.
+// This is the enum that is used in profiler stack labels. Having one list that
+// includes subcategories from all categories in one list allows assigning the
+// category pair to a stack label with just one number.
+#define CATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color)
+#define CATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString) name,
+#define CATEGORY_ENUM_END_CATEGORY
+enum class ProfilingCategoryPair : uint32_t {
+ MOZ_PROFILING_CATEGORY_LIST(CATEGORY_ENUM_BEGIN_CATEGORY,
+ CATEGORY_ENUM_SUBCATEGORY,
+ CATEGORY_ENUM_END_CATEGORY)
+ COUNT,
+ LAST = COUNT - 1,
+};
+#undef CATEGORY_ENUM_BEGIN_CATEGORY
+#undef CATEGORY_ENUM_SUBCATEGORY
+#undef CATEGORY_ENUM_END_CATEGORY
+
+// An enum that lists just the categories without their subcategories.
+#define SUPERCATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color) name,
+#define SUPERCATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString)
+#define SUPERCATEGORY_ENUM_END_CATEGORY
+enum class ProfilingCategory : uint32_t {
+ MOZ_PROFILING_CATEGORY_LIST(SUPERCATEGORY_ENUM_BEGIN_CATEGORY,
+ SUPERCATEGORY_ENUM_SUBCATEGORY,
+ SUPERCATEGORY_ENUM_END_CATEGORY)
+ COUNT,
+ LAST = COUNT - 1,
+};
+#undef SUPERCATEGORY_ENUM_BEGIN_CATEGORY
+#undef SUPERCATEGORY_ENUM_SUBCATEGORY
+#undef SUPERCATEGORY_ENUM_END_CATEGORY
+
+// clang-format on
+
+struct ProfilingCategoryPairInfo {
+ ProfilingCategory mCategory;
+ uint32_t mSubcategoryIndex;
+ const char* mLabel;
+};
+
+JS_FRIEND_API const ProfilingCategoryPairInfo& GetProfilingCategoryPairInfo(
+ ProfilingCategoryPair aCategoryPair);
+
+} // namespace JS
+
+#endif /* js_ProfilingCategory_h */
diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h
new file mode 100644
index 0000000000..c231bc140c
--- /dev/null
+++ b/js/public/ProfilingFrameIterator.h
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_ProfilingFrameIterator_h
+#define js_ProfilingFrameIterator_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include "js/GCAPI.h"
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+namespace js {
+class Activation;
+namespace jit {
+class JitActivation;
+class JSJitProfilingFrameIterator;
+class JitcodeGlobalEntry;
+} // namespace jit
+namespace wasm {
+class ProfilingFrameIterator;
+} // namespace wasm
+} // namespace js
+
+namespace JS {
+
+// This iterator can be used to walk the stack of a thread suspended at an
+// arbitrary pc. To provide accurate results, profiling must have been enabled
+// (via EnableRuntimeProfilingStack) before executing the callstack being
+// unwound.
+//
+// Note that the caller must not do anything that could cause GC to happen while
+// the iterator is alive, since this could invalidate Ion code and cause its
+// contents to become out of date.
+class MOZ_NON_PARAM JS_PUBLIC_API ProfilingFrameIterator {
+ public:
+ enum class Kind : bool { JSJit, Wasm };
+
+ private:
+ JSContext* cx_;
+ mozilla::Maybe<uint64_t> samplePositionInProfilerBuffer_;
+ js::Activation* activation_;
+ Kind kind_;
+
+ static const unsigned StorageSpace = 8 * sizeof(void*);
+ alignas(void*) unsigned char storage_[StorageSpace];
+
+ void* storage() { return storage_; }
+ const void* storage() const { return storage_; }
+
+ js::wasm::ProfilingFrameIterator& wasmIter() {
+ MOZ_ASSERT(!done());
+ MOZ_ASSERT(isWasm());
+ return *static_cast<js::wasm::ProfilingFrameIterator*>(storage());
+ }
+ const js::wasm::ProfilingFrameIterator& wasmIter() const {
+ MOZ_ASSERT(!done());
+ MOZ_ASSERT(isWasm());
+ return *static_cast<const js::wasm::ProfilingFrameIterator*>(storage());
+ }
+
+ js::jit::JSJitProfilingFrameIterator& jsJitIter() {
+ MOZ_ASSERT(!done());
+ MOZ_ASSERT(isJSJit());
+ return *static_cast<js::jit::JSJitProfilingFrameIterator*>(storage());
+ }
+
+ const js::jit::JSJitProfilingFrameIterator& jsJitIter() const {
+ MOZ_ASSERT(!done());
+ MOZ_ASSERT(isJSJit());
+ return *static_cast<const js::jit::JSJitProfilingFrameIterator*>(storage());
+ }
+
+ void settleFrames();
+ void settle();
+
+ public:
+ struct RegisterState {
+ RegisterState() : pc(nullptr), sp(nullptr), fp(nullptr), lr(nullptr) {}
+ void* pc;
+ void* sp;
+ void* fp;
+ void* lr;
+ };
+
+ ProfilingFrameIterator(
+ JSContext* cx, const RegisterState& state,
+ const mozilla::Maybe<uint64_t>& samplePositionInProfilerBuffer =
+ mozilla::Nothing());
+ ~ProfilingFrameIterator();
+ void operator++();
+ bool done() const { return !activation_; }
+
+ // Assuming the stack grows down (we do), the return value:
+ // - always points into the stack
+ // - is weakly monotonically increasing (may be equal for successive frames)
+ // - will compare greater than newer native and psuedo-stack frame addresses
+ // and less than older native and psuedo-stack frame addresses
+ void* stackAddress() const;
+
+ enum FrameKind {
+ Frame_BaselineInterpreter,
+ Frame_Baseline,
+ Frame_Ion,
+ Frame_Wasm
+ };
+
+ struct Frame {
+ FrameKind kind;
+ void* stackAddress;
+ union {
+ void* returnAddress_;
+ jsbytecode* interpreterPC_;
+ };
+ void* activation;
+ void* endStackAddress;
+ const char* label;
+ JSScript* interpreterScript;
+ uint64_t realmID;
+
+ public:
+ void* returnAddress() const {
+ MOZ_ASSERT(kind != Frame_BaselineInterpreter);
+ return returnAddress_;
+ }
+ jsbytecode* interpreterPC() const {
+ MOZ_ASSERT(kind == Frame_BaselineInterpreter);
+ return interpreterPC_;
+ }
+ } JS_HAZ_GC_INVALIDATED;
+
+ bool isWasm() const;
+ bool isJSJit() const;
+
+ uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const;
+
+ mozilla::Maybe<Frame> getPhysicalFrameWithoutLabel() const;
+
+ private:
+ mozilla::Maybe<Frame> getPhysicalFrameAndEntry(
+ js::jit::JitcodeGlobalEntry* entry) const;
+
+ void iteratorConstruct(const RegisterState& state);
+ void iteratorConstruct();
+ void iteratorDestroy();
+ bool iteratorDone();
+} JS_HAZ_GC_INVALIDATED;
+
+JS_FRIEND_API bool IsProfilingEnabledForContext(JSContext* cx);
+
+/**
+ * After each sample run, this method should be called with the current buffer
+ * position at which the buffer contents start. This will update the
+ * corresponding field on the JSRuntime.
+ *
+ * See the field |profilerSampleBufferRangeStart| on JSRuntime for documentation
+ * about what this value is used for.
+ */
+JS_FRIEND_API void SetJSContextProfilerSampleBufferRangeStart(
+ JSContext* cx, uint64_t rangeStart);
+
+class ProfiledFrameRange;
+
+// A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
+// lookups on JitcodeGlobalTable.
+class MOZ_STACK_CLASS ProfiledFrameHandle {
+ friend class ProfiledFrameRange;
+
+ JSRuntime* rt_;
+ js::jit::JitcodeGlobalEntry& entry_;
+ void* addr_;
+ void* canonicalAddr_;
+ const char* label_;
+ uint32_t depth_;
+
+ ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry,
+ void* addr, const char* label, uint32_t depth);
+
+ public:
+ const char* label() const { return label_; }
+ uint32_t depth() const { return depth_; }
+ void* canonicalAddress() const { return canonicalAddr_; }
+
+ JS_PUBLIC_API ProfilingFrameIterator::FrameKind frameKind() const;
+
+ JS_PUBLIC_API uint64_t realmID() const;
+};
+
+class ProfiledFrameRange {
+ public:
+ class Iter final {
+ public:
+ Iter(const ProfiledFrameRange& range, uint32_t index)
+ : range_(range), index_(index) {}
+
+ JS_PUBLIC_API ProfiledFrameHandle operator*() const;
+
+ // Provide the bare minimum of iterator methods that are needed for
+ // C++ ranged for loops.
+ Iter& operator++() {
+ ++index_;
+ return *this;
+ }
+ bool operator==(const Iter& rhs) { return index_ == rhs.index_; }
+ bool operator!=(const Iter& rhs) { return !(*this == rhs); }
+
+ private:
+ const ProfiledFrameRange& range_;
+ uint32_t index_;
+ };
+
+ Iter begin() const { return Iter(*this, 0); }
+ Iter end() const { return Iter(*this, depth_); }
+
+ private:
+ friend JS_PUBLIC_API ProfiledFrameRange GetProfiledFrames(JSContext* cx,
+ void* addr);
+
+ ProfiledFrameRange(JSRuntime* rt, void* addr,
+ js::jit::JitcodeGlobalEntry* entry)
+ : rt_(rt), addr_(addr), entry_(entry), depth_(0) {}
+
+ JSRuntime* rt_;
+ void* addr_;
+ js::jit::JitcodeGlobalEntry* entry_;
+ // Assume maximum inlining depth is <64
+ const char* labels_[64];
+ uint32_t depth_;
+};
+
+// Returns a range that can be iterated over using C++ ranged for loops.
+JS_PUBLIC_API ProfiledFrameRange GetProfiledFrames(JSContext* cx, void* addr);
+
+} // namespace JS
+
+#endif /* js_ProfilingFrameIterator_h */
diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h
new file mode 100644
index 0000000000..faca7eb9ed
--- /dev/null
+++ b/js/public/ProfilingStack.h
@@ -0,0 +1,570 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_ProfilingStack_h
+#define js_ProfilingStack_h
+
+#include <algorithm>
+#include <stdint.h>
+
+#include "jstypes.h"
+
+#include "js/ProfilingCategory.h"
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+class JS_PUBLIC_API JSTracer;
+class JS_FRIEND_API ProfilingStack;
+
+// This file defines the classes ProfilingStack and ProfilingStackFrame.
+// The ProfilingStack manages an array of ProfilingStackFrames.
+// It keeps track of the "label stack" and the JS interpreter stack.
+// The two stack types are interleaved.
+//
+// Usage:
+//
+// ProfilingStack* profilingStack = ...;
+//
+// // For label frames:
+// profilingStack->pushLabelFrame(...);
+// // Execute some code. When finished, pop the frame:
+// profilingStack->pop();
+//
+// // For JS stack frames:
+// profilingStack->pushJSFrame(...);
+// // Execute some code. When finished, pop the frame:
+// profilingStack->pop();
+//
+//
+// Concurrency considerations
+//
+// A thread's profiling stack (and the frames inside it) is only modified by
+// that thread. However, the profiling stack can be *read* by a different
+// thread, the sampler thread: Whenever the profiler wants to sample a given
+// thread A, the following happens:
+// (1) Thread A is suspended.
+// (2) The sampler thread (thread S) reads the ProfilingStack of thread A,
+// including all ProfilingStackFrames that are currently in that stack
+// (profilingStack->frames[0..profilingStack->stackSize()]).
+// (3) Thread A is resumed.
+//
+// Thread suspension is achieved using platform-specific APIs; refer to each
+// platform's Sampler::SuspendAndSampleAndResumeThread implementation in
+// platform-*.cpp for details.
+//
+// When the thread is suspended, the values in profilingStack->stackPointer and
+// in the stack frame range
+// profilingStack->frames[0..profilingStack->stackPointer] need to be in a
+// consistent state, so that thread S does not read partially- constructed stack
+// frames. More specifically, we have two requirements:
+// (1) When adding a new frame at the top of the stack, its ProfilingStackFrame
+// data needs to be put in place *before* the stackPointer is incremented,
+// and the compiler + CPU need to know that this order matters.
+// (2) When popping an frame from the stack and then preparing the
+// ProfilingStackFrame data for the next frame that is about to be pushed,
+// the decrement of the stackPointer in pop() needs to happen *before* the
+// ProfilingStackFrame for the new frame is being popuplated, and the
+// compiler + CPU need to know that this order matters.
+//
+// We can express the relevance of these orderings in multiple ways.
+// Option A is to make stackPointer an atomic with SequentiallyConsistent
+// memory ordering. This would ensure that no writes in thread A would be
+// reordered across any writes to stackPointer, which satisfies requirements
+// (1) and (2) at the same time. Option A is the simplest.
+// Option B is to use ReleaseAcquire memory ordering both for writes to
+// stackPointer *and* for writes to ProfilingStackFrame fields. Release-stores
+// ensure that all writes that happened *before this write in program order* are
+// not reordered to happen after this write. ReleaseAcquire ordering places no
+// requirements on the ordering of writes that happen *after* this write in
+// program order.
+// Using release-stores for writes to stackPointer expresses requirement (1),
+// and using release-stores for writes to the ProfilingStackFrame fields
+// expresses requirement (2).
+//
+// Option B is more complicated than option A, but has much better performance
+// on x86/64: In a microbenchmark run on a Macbook Pro from 2017, switching
+// from option A to option B reduced the overhead of pushing+popping a
+// ProfilingStackFrame by 10 nanoseconds.
+// On x86/64, release-stores require no explicit hardware barriers or lock
+// instructions.
+// On ARM/64, option B may be slower than option A, because the compiler will
+// generate hardware barriers for every single release-store instead of just
+// for the writes to stackPointer. However, the actual performance impact of
+// this has not yet been measured on ARM, so we're currently using option B
+// everywhere. This is something that we may want to change in the future once
+// we've done measurements.
+
+namespace js {
+
+// A call stack can be specified to the JS engine such that all JS entry/exits
+// to functions push/pop a stack frame to/from the specified stack.
+//
+// For more detailed information, see vm/GeckoProfiler.h.
+//
+class ProfilingStackFrame {
+ // A ProfilingStackFrame represents either a label frame or a JS frame.
+
+ // WARNING WARNING WARNING
+ //
+ // All the fields below are Atomic<...,ReleaseAcquire>. This is needed so
+ // that writes to these fields are release-writes, which ensures that
+ // earlier writes in this thread don't get reordered after the writes to
+ // these fields. In particular, the decrement of the stack pointer in
+ // ProfilingStack::pop() is a write that *must* happen before the values in
+ // this ProfilingStackFrame are changed. Otherwise, the sampler thread might
+ // see an inconsistent state where the stack pointer still points to a
+ // ProfilingStackFrame which has already been popped off the stack and whose
+ // fields have now been partially repopulated with new values.
+ // See the "Concurrency considerations" paragraph at the top of this file
+ // for more details.
+
+ // Descriptive label for this stack frame. Must be a static string! Can be
+ // an empty string, but not a null pointer.
+ mozilla::Atomic<const char*, mozilla::ReleaseAcquire> label_;
+
+ // An additional descriptive string of this frame which is combined with
+ // |label_| in profiler output. Need not be (and usually isn't) static. Can
+ // be null.
+ mozilla::Atomic<const char*, mozilla::ReleaseAcquire> dynamicString_;
+
+ // Stack pointer for non-JS stack frames, the script pointer otherwise.
+ mozilla::Atomic<void*, mozilla::ReleaseAcquire> spOrScript;
+
+ // ID of the JS Realm for JS stack frames.
+ // Must not be used on non-JS frames; it'll contain either the default 0,
+ // or a leftover value from a previous JS stack frame that was using this
+ // ProfilingStackFrame object.
+ mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> realmID_;
+
+ // The bytecode offset for JS stack frames.
+ // Must not be used on non-JS frames; it'll contain either the default 0,
+ // or a leftover value from a previous JS stack frame that was using this
+ // ProfilingStackFrame object.
+ mozilla::Atomic<int32_t, mozilla::ReleaseAcquire> pcOffsetIfJS_;
+
+ // Bits 0...8 hold the Flags. Bits 9...31 hold the category pair.
+ mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> flagsAndCategoryPair_;
+
+ static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
+
+ public:
+ ProfilingStackFrame() = default;
+ ProfilingStackFrame& operator=(const ProfilingStackFrame& other) {
+ label_ = other.label();
+ dynamicString_ = other.dynamicString();
+ void* spScript = other.spOrScript;
+ spOrScript = spScript;
+ int32_t offsetIfJS = other.pcOffsetIfJS_;
+ pcOffsetIfJS_ = offsetIfJS;
+ uint64_t realmID = other.realmID_;
+ realmID_ = realmID;
+ uint32_t flagsAndCategory = other.flagsAndCategoryPair_;
+ flagsAndCategoryPair_ = flagsAndCategory;
+ return *this;
+ }
+
+ // Reserve up to 16 bits for flags, and 16 for category pair.
+ enum class Flags : uint32_t {
+ // The first three flags describe the kind of the frame and are
+ // mutually exclusive. (We still give them individual bits for
+ // simplicity.)
+
+ // A regular label frame. These usually come from AutoProfilerLabel.
+ IS_LABEL_FRAME = 1 << 0,
+
+ // A special frame indicating the start of a run of JS profiling stack
+ // frames. IS_SP_MARKER_FRAME frames are ignored, except for the sp
+ // field. These frames are needed to get correct ordering between JS
+ // and LABEL frames because JS frames don't carry sp information.
+ // SP is short for "stack pointer".
+ IS_SP_MARKER_FRAME = 1 << 1,
+
+ // A JS frame.
+ IS_JS_FRAME = 1 << 2,
+
+ // An interpreter JS frame that has OSR-ed into baseline. IS_JS_FRAME
+ // frames can have this flag set and unset during their lifetime.
+ // JS_OSR frames are ignored.
+ JS_OSR = 1 << 3,
+
+ // The next three are mutually exclusive.
+ // By default, for profiling stack frames that have both a label and a
+ // dynamic string, the two strings are combined into one string of the
+ // form "<label> <dynamicString>" during JSON serialization. The
+ // following flags can be used to change this preset.
+ STRING_TEMPLATE_METHOD = 1 << 4, // "<label>.<dynamicString>"
+ STRING_TEMPLATE_GETTER = 1 << 5, // "get <label>.<dynamicString>"
+ STRING_TEMPLATE_SETTER = 1 << 6, // "set <label>.<dynamicString>"
+
+ // If set, causes this stack frame to be marked as "relevantForJS" in
+ // the profile JSON, which will make it show up in the "JS only" call
+ // tree view.
+ RELEVANT_FOR_JS = 1 << 7,
+
+ // If set, causes the label on this ProfilingStackFrame to be ignored
+ // and to be replaced by the subcategory's label.
+ LABEL_DETERMINED_BY_CATEGORY_PAIR = 1 << 8,
+
+ // Frame dynamic string does not contain user data.
+ NONSENSITIVE = 1 << 9,
+
+ // A JS Baseline Interpreter frame.
+ IS_BLINTERP_FRAME = 1 << 10,
+
+ FLAGS_BITCOUNT = 16,
+ FLAGS_MASK = (1 << FLAGS_BITCOUNT) - 1
+ };
+
+ static_assert(
+ uint32_t(JS::ProfilingCategoryPair::LAST) <=
+ (UINT32_MAX >> uint32_t(Flags::FLAGS_BITCOUNT)),
+ "Too many category pairs to fit into u32 with together with the "
+ "reserved bits for the flags");
+
+ bool isLabelFrame() const {
+ return uint32_t(flagsAndCategoryPair_) & uint32_t(Flags::IS_LABEL_FRAME);
+ }
+
+ bool isNonsensitive() const {
+ return uint32_t(flagsAndCategoryPair_) & uint32_t(Flags::NONSENSITIVE);
+ }
+
+ bool isSpMarkerFrame() const {
+ return uint32_t(flagsAndCategoryPair_) &
+ uint32_t(Flags::IS_SP_MARKER_FRAME);
+ }
+
+ bool isJsFrame() const {
+ return uint32_t(flagsAndCategoryPair_) & uint32_t(Flags::IS_JS_FRAME);
+ }
+
+ bool isJsBlinterpFrame() const {
+ return uint32_t(flagsAndCategoryPair_) & uint32_t(Flags::IS_BLINTERP_FRAME);
+ }
+
+ bool isOSRFrame() const {
+ return uint32_t(flagsAndCategoryPair_) & uint32_t(Flags::JS_OSR);
+ }
+
+ void setIsOSRFrame(bool isOSR) {
+ if (isOSR) {
+ flagsAndCategoryPair_ =
+ uint32_t(flagsAndCategoryPair_) | uint32_t(Flags::JS_OSR);
+ } else {
+ flagsAndCategoryPair_ =
+ uint32_t(flagsAndCategoryPair_) & ~uint32_t(Flags::JS_OSR);
+ }
+ }
+
+ const char* label() const {
+ uint32_t flagsAndCategoryPair = flagsAndCategoryPair_;
+ if (flagsAndCategoryPair &
+ uint32_t(Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR)) {
+ auto categoryPair = JS::ProfilingCategoryPair(
+ flagsAndCategoryPair >> uint32_t(Flags::FLAGS_BITCOUNT));
+ return JS::GetProfilingCategoryPairInfo(categoryPair).mLabel;
+ }
+ return label_;
+ }
+
+ const char* dynamicString() const { return dynamicString_; }
+
+ void initLabelFrame(const char* aLabel, const char* aDynamicString, void* sp,
+ JS::ProfilingCategoryPair aCategoryPair,
+ uint32_t aFlags) {
+ label_ = aLabel;
+ dynamicString_ = aDynamicString;
+ spOrScript = sp;
+ // pcOffsetIfJS_ is not set and must not be used on label frames.
+ flagsAndCategoryPair_ =
+ uint32_t(Flags::IS_LABEL_FRAME) |
+ (uint32_t(aCategoryPair) << uint32_t(Flags::FLAGS_BITCOUNT)) | aFlags;
+ MOZ_ASSERT(isLabelFrame());
+ }
+
+ void initSpMarkerFrame(void* sp) {
+ label_ = "";
+ dynamicString_ = nullptr;
+ spOrScript = sp;
+ // pcOffsetIfJS_ is not set and must not be used on sp marker frames.
+ flagsAndCategoryPair_ = uint32_t(Flags::IS_SP_MARKER_FRAME) |
+ (uint32_t(JS::ProfilingCategoryPair::OTHER)
+ << uint32_t(Flags::FLAGS_BITCOUNT));
+ MOZ_ASSERT(isSpMarkerFrame());
+ }
+
+ template <JS::ProfilingCategoryPair Category, uint32_t ExtraFlags = 0>
+ void initJsFrame(const char* aLabel, const char* aDynamicString,
+ JSScript* aScript, jsbytecode* aPc, uint64_t aRealmID) {
+ label_ = aLabel;
+ dynamicString_ = aDynamicString;
+ spOrScript = aScript;
+ pcOffsetIfJS_ = pcToOffset(aScript, aPc);
+ realmID_ = aRealmID;
+ flagsAndCategoryPair_ =
+ (uint32_t(Category) << uint32_t(Flags::FLAGS_BITCOUNT)) |
+ uint32_t(Flags::IS_JS_FRAME) | ExtraFlags;
+ MOZ_ASSERT(isJsFrame());
+ }
+
+ uint32_t flags() const {
+ return uint32_t(flagsAndCategoryPair_) & uint32_t(Flags::FLAGS_MASK);
+ }
+
+ JS::ProfilingCategoryPair categoryPair() const {
+ return JS::ProfilingCategoryPair(flagsAndCategoryPair_ >>
+ uint32_t(Flags::FLAGS_BITCOUNT));
+ }
+
+ uint64_t realmID() const { return realmID_; }
+
+ void* stackAddress() const {
+ MOZ_ASSERT(!isJsFrame());
+ return spOrScript;
+ }
+
+ JS_PUBLIC_API JSScript* script() const;
+
+ // Note that the pointer returned might be invalid.
+ JSScript* rawScript() const {
+ MOZ_ASSERT(isJsFrame());
+ void* script = spOrScript;
+ return static_cast<JSScript*>(script);
+ }
+
+ // We can't know the layout of JSScript, so look in vm/GeckoProfiler.cpp.
+ JS_FRIEND_API jsbytecode* pc() const;
+ void setPC(jsbytecode* pc);
+
+ void trace(JSTracer* trc);
+
+ // The offset of a pc into a script's code can actually be 0, so to
+ // signify a nullptr pc, use a -1 index. This is checked against in
+ // pc() and setPC() to set/get the right pc.
+ static const int32_t NullPCOffset = -1;
+};
+
+JS_FRIEND_API void SetContextProfilingStack(JSContext* cx,
+ ProfilingStack* profilingStack);
+
+// GetContextProfilingStack also exists, but it's defined in RootingAPI.h.
+
+JS_FRIEND_API void EnableContextProfilingStack(JSContext* cx, bool enabled);
+
+JS_FRIEND_API void RegisterContextProfilingEventMarker(JSContext* cx,
+ void (*fn)(const char*,
+ const char*));
+
+} // namespace js
+
+namespace JS {
+
+typedef ProfilingStack* (*RegisterThreadCallback)(const char* threadName,
+ void* stackBase);
+
+typedef void (*UnregisterThreadCallback)();
+
+// regiserThread and unregisterThread callbacks are functions which are called
+// by other threads without any locking mechanism.
+JS_FRIEND_API void SetProfilingThreadCallbacks(
+ RegisterThreadCallback registerThread,
+ UnregisterThreadCallback unregisterThread);
+
+} // namespace JS
+
+// Each thread has its own ProfilingStack. That thread modifies the
+// ProfilingStack, pushing and popping elements as necessary.
+//
+// The ProfilingStack is also read periodically by the profiler's sampler
+// thread. This happens only when the thread that owns the ProfilingStack is
+// suspended. So there are no genuine parallel accesses.
+//
+// However, it is possible for pushing/popping to be interrupted by a periodic
+// sample. Because of this, we need pushing/popping to be effectively atomic.
+//
+// - When pushing a new frame, we increment the stack pointer -- making the new
+// frame visible to the sampler thread -- only after the new frame has been
+// fully written. The stack pointer is Atomic<uint32_t,ReleaseAcquire>, so
+// the increment is a release-store, which ensures that this store is not
+// reordered before the writes of the frame.
+//
+// - When popping an old frame, the only operation is the decrementing of the
+// stack pointer, which is obviously atomic.
+//
+class JS_FRIEND_API ProfilingStack final {
+ public:
+ ProfilingStack() = default;
+
+ ~ProfilingStack();
+
+ void pushLabelFrame(const char* label, const char* dynamicString, void* sp,
+ JS::ProfilingCategoryPair categoryPair,
+ uint32_t flags = 0) {
+ // This thread is the only one that ever changes the value of
+ // stackPointer.
+ // Store the value of the atomic in a non-atomic local variable so that
+ // the compiler won't generate two separate loads from the atomic for
+ // the size check and the frames[] array indexing operation.
+ uint32_t stackPointerVal = stackPointer;
+
+ if (MOZ_UNLIKELY(stackPointerVal >= capacity)) {
+ ensureCapacitySlow();
+ }
+ frames[stackPointerVal].initLabelFrame(label, dynamicString, sp,
+ categoryPair, flags);
+
+ // This must happen at the end! The compiler will not reorder this
+ // update because stackPointer is Atomic<..., ReleaseAcquire>, so any
+ // the writes above will not be reordered below the stackPointer store.
+ // Do the read and the write as two separate statements, in order to
+ // make it clear that we don't need an atomic increment, which would be
+ // more expensive on x86 than the separate operations done here.
+ // However, don't use stackPointerVal here; instead, allow the compiler
+ // to turn this store into a non-atomic increment instruction which
+ // takes up less code size.
+ stackPointer = stackPointer + 1;
+ }
+
+ void pushSpMarkerFrame(void* sp) {
+ uint32_t oldStackPointer = stackPointer;
+
+ if (MOZ_UNLIKELY(oldStackPointer >= capacity)) {
+ ensureCapacitySlow();
+ }
+ frames[oldStackPointer].initSpMarkerFrame(sp);
+
+ // This must happen at the end, see the comment in pushLabelFrame.
+ stackPointer = oldStackPointer + 1;
+ }
+
+ void pushJsFrame(const char* label, const char* dynamicString,
+ JSScript* script, jsbytecode* pc, uint64_t aRealmID) {
+ // This thread is the only one that ever changes the value of
+ // stackPointer. Only load the atomic once.
+ uint32_t oldStackPointer = stackPointer;
+
+ if (MOZ_UNLIKELY(oldStackPointer >= capacity)) {
+ ensureCapacitySlow();
+ }
+ frames[oldStackPointer]
+ .initJsFrame<JS::ProfilingCategoryPair::JS_Interpreter>(
+ label, dynamicString, script, pc, aRealmID);
+
+ // This must happen at the end, see the comment in pushLabelFrame.
+ stackPointer = stackPointer + 1;
+ }
+
+ void pop() {
+ MOZ_ASSERT(stackPointer > 0);
+ // Do the read and the write as two separate statements, in order to
+ // make it clear that we don't need an atomic decrement, which would be
+ // more expensive on x86 than the separate operations done here.
+ // This thread is the only one that ever changes the value of
+ // stackPointer.
+ uint32_t oldStackPointer = stackPointer;
+ stackPointer = oldStackPointer - 1;
+ }
+
+ uint32_t stackSize() const { return stackPointer; }
+ uint32_t stackCapacity() const { return capacity; }
+
+ private:
+ // Out of line path for expanding the buffer, since otherwise this would get
+ // inlined in every DOM WebIDL call.
+ MOZ_COLD void ensureCapacitySlow();
+
+ // No copying.
+ ProfilingStack(const ProfilingStack&) = delete;
+ void operator=(const ProfilingStack&) = delete;
+
+ // No moving either.
+ ProfilingStack(ProfilingStack&&) = delete;
+ void operator=(ProfilingStack&&) = delete;
+
+ uint32_t capacity = 0;
+
+ public:
+ // The pointer to the stack frames, this is read from the profiler thread and
+ // written from the current thread.
+ //
+ // This is effectively a unique pointer.
+ mozilla::Atomic<js::ProfilingStackFrame*, mozilla::SequentiallyConsistent>
+ frames{nullptr};
+
+ // This may exceed the capacity, so instead use the stackSize() method to
+ // determine the number of valid frames in stackFrames. When this is less
+ // than stackCapacity(), it refers to the first free stackframe past the top
+ // of the in-use stack (i.e. frames[stackPointer - 1] is the top stack
+ // frame).
+ //
+ // WARNING WARNING WARNING
+ //
+ // This is an atomic variable that uses ReleaseAcquire memory ordering.
+ // See the "Concurrency considerations" paragraph at the top of this file
+ // for more details.
+ mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> stackPointer{0};
+};
+
+namespace js {
+
+class AutoGeckoProfilerEntry;
+class GeckoProfilerEntryMarker;
+class GeckoProfilerBaselineOSRMarker;
+
+class GeckoProfilerThread {
+ friend class AutoGeckoProfilerEntry;
+ friend class GeckoProfilerEntryMarker;
+ friend class GeckoProfilerBaselineOSRMarker;
+
+ ProfilingStack* profilingStack_;
+
+ // Same as profilingStack_ if the profiler is currently active, otherwise
+ // null.
+ ProfilingStack* profilingStackIfEnabled_;
+
+ public:
+ GeckoProfilerThread();
+
+ uint32_t stackPointer() {
+ MOZ_ASSERT(infraInstalled());
+ return profilingStack_->stackPointer;
+ }
+ ProfilingStackFrame* stack() { return profilingStack_->frames; }
+ ProfilingStack* getProfilingStack() { return profilingStack_; }
+ ProfilingStack* getProfilingStackIfEnabled() {
+ return profilingStackIfEnabled_;
+ }
+
+ /*
+ * True if the profiler infrastructure is setup. Should be true in builds
+ * that include profiler support except during early startup or late
+ * shutdown. Unrelated to the presence of the Gecko Profiler addon.
+ */
+ bool infraInstalled() { return profilingStack_ != nullptr; }
+
+ void setProfilingStack(ProfilingStack* profilingStack, bool enabled);
+ void enable(bool enable) {
+ profilingStackIfEnabled_ = enable ? profilingStack_ : nullptr;
+ }
+ void trace(JSTracer* trc);
+
+ /*
+ * Functions which are the actual instrumentation to track run information
+ *
+ * - enter: a function has started to execute
+ * - updatePC: updates the pc information about where a function
+ * is currently executing
+ * - exit: this function has ceased execution, and no further
+ * entries/exits will be made
+ */
+ bool enter(JSContext* cx, JSScript* script);
+ void exit(JSContext* cx, JSScript* script);
+ inline void updatePC(JSContext* cx, JSScript* script, jsbytecode* pc);
+};
+
+} // namespace js
+
+#endif /* js_ProfilingStack_h */
diff --git a/js/public/Promise.h b/js/public/Promise.h
new file mode 100644
index 0000000000..5f444dd2b2
--- /dev/null
+++ b/js/public/Promise.h
@@ -0,0 +1,587 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Promise_h
+#define js_Promise_h
+
+#include "mozilla/Attributes.h"
+
+#include "jspubtd.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/UniquePtr.h"
+
+namespace JS {
+
+class JS_PUBLIC_API AutoDebuggerJobQueueInterruption;
+
+/**
+ * Abstract base class for an ECMAScript Job Queue:
+ * https://www.ecma-international.org/ecma-262/9.0/index.html#sec-jobs-and-job-queues
+ *
+ * SpiderMonkey doesn't schedule Promise resolution jobs itself; instead, the
+ * embedding can provide an instance of this class SpiderMonkey can use to do
+ * that scheduling.
+ *
+ * The JavaScript shell includes a simple implementation adequate for running
+ * tests. Browsers need to augment job handling to meet their own additional
+ * requirements, so they can provide their own implementation.
+ */
+class JS_PUBLIC_API JobQueue {
+ public:
+ virtual ~JobQueue() = default;
+
+ /**
+ * Ask the embedding for the incumbent global.
+ *
+ * SpiderMonkey doesn't itself have a notion of incumbent globals as defined
+ * by the HTML spec, so we need the embedding to provide this. See
+ * dom/script/ScriptSettings.h for details.
+ */
+ virtual JSObject* getIncumbentGlobal(JSContext* cx) = 0;
+
+ /**
+ * Enqueue a reaction job `job` for `promise`, which was allocated at
+ * `allocationSite`. Provide `incumbentGlobal` as the incumbent global for
+ * the reaction job's execution.
+ */
+ virtual bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise,
+ JS::HandleObject job,
+ JS::HandleObject allocationSite,
+ JS::HandleObject incumbentGlobal) = 0;
+
+ /**
+ * Run all jobs in the queue. Running one job may enqueue others; continue to
+ * run jobs until the queue is empty.
+ *
+ * Calling this method at the wrong time can break the web. The HTML spec
+ * indicates exactly when the job queue should be drained (in HTML jargon,
+ * when it should "perform a microtask checkpoint"), and doing so at other
+ * times can incompatibly change the semantics of programs that use promises
+ * or other microtask-based features.
+ *
+ * This method is called only via AutoDebuggerJobQueueInterruption, used by
+ * the Debugger API implementation to ensure that the debuggee's job queue is
+ * protected from the debugger's own activity. See the comments on
+ * AutoDebuggerJobQueueInterruption.
+ */
+ virtual void runJobs(JSContext* cx) = 0;
+
+ /**
+ * Return true if the job queue is empty, false otherwise.
+ */
+ virtual bool empty() const = 0;
+
+ protected:
+ friend class AutoDebuggerJobQueueInterruption;
+
+ /**
+ * A saved job queue, represented however the JobQueue implementation pleases.
+ * Use AutoDebuggerJobQueueInterruption rather than trying to construct one of
+ * these directly; see documentation there.
+ *
+ * Destructing an instance of this class should assert that the current queue
+ * is empty, and then restore the queue the instance captured.
+ */
+ class SavedJobQueue {
+ public:
+ virtual ~SavedJobQueue() = default;
+ };
+
+ /**
+ * Capture this JobQueue's current job queue as a SavedJobQueue and return it,
+ * leaving the JobQueue's job queue empty. Destroying the returned object
+ * should assert that this JobQueue's current job queue is empty, and restore
+ * the original queue.
+ *
+ * On OOM, this should call JS_ReportOutOfMemory on the given JSContext,
+ * and return a null UniquePtr.
+ */
+ virtual js::UniquePtr<SavedJobQueue> saveJobQueue(JSContext*) = 0;
+};
+
+/**
+ * Tell SpiderMonkey to use `queue` to schedule promise reactions.
+ *
+ * SpiderMonkey does not take ownership of the queue; it is the embedding's
+ * responsibility to clean it up after the runtime is destroyed.
+ */
+extern JS_PUBLIC_API void SetJobQueue(JSContext* cx, JobQueue* queue);
+
+/**
+ * [SMDOC] Protecting the debuggee's job/microtask queue from debugger activity.
+ *
+ * When the JavaScript debugger interrupts the execution of some debuggee code
+ * (for a breakpoint, for example), the debuggee's execution must be paused
+ * while the developer takes time to look at it. During this interruption, other
+ * tabs should remain active and usable. If the debuggee shares a main thread
+ * with non-debuggee tabs, that means that the thread will have to process
+ * non-debuggee HTML tasks and microtasks as usual, even as the debuggee's are
+ * on hold until the debugger lets it continue execution. (Letting debuggee
+ * microtasks run during the interruption would mean that, from the debuggee's
+ * point of view, their side effects would take place wherever the breakpoint
+ * was set - in general, not a place other code should ever run, and a violation
+ * of the run-to-completion rule.)
+ *
+ * This means that, even though the timing and ordering of microtasks is
+ * carefully specified by the standard - and important to preserve for
+ * compatibility and predictability - debugger use may, correctly, have the
+ * effect of reordering microtasks. During the interruption, microtasks enqueued
+ * by non-debuggee tabs must run immediately alongside their HTML tasks as
+ * usual, whereas any debuggee microtasks that were in the queue when the
+ * interruption began must wait for the debuggee to be continued - and thus run
+ * after microtasks enqueued after they were.
+ *
+ * Fortunately, this reordering is visible only at the global level: when
+ * implemented correctly, it is not detectable by an individual debuggee. Note
+ * that a debuggee should generally be a complete unit of similar-origin related
+ * browsing contexts. Since non-debuggee activity falls outside that unit, it
+ * should never be visible to the debuggee (except via mechanisms that are
+ * already asynchronous, like events), so the debuggee should be unable to
+ * detect non-debuggee microtasks running when they normally would not. As long
+ * as behavior *visible to the debuggee* is unaffected by the interruption, we
+ * have respected the spirit of the rule.
+ *
+ * Of course, even as we accept the general principle that interrupting the
+ * debuggee should have as little detectable effect as possible, we still permit
+ * the developer to do things like evaluate expressions at the console that have
+ * arbitrary effects on the debuggee's state—effects that could never occur
+ * naturally at that point in the program. But since these are explicitly
+ * requested by the developer, who presumably knows what they're doing, we
+ * support this as best we can. If the developer evaluates an expression in the
+ * console that resolves a promise, it seems most natural for the promise's
+ * reaction microtasks to run immediately, within the interruption. This is an
+ * 'unnatural' time for the microtasks to run, but no more unnatural than the
+ * evaluation that triggered them.
+ *
+ * So the overall behavior we need is as follows:
+ *
+ * - When the debugger interrupts a debuggee, the debuggee's microtask queue
+ * must be saved.
+ *
+ * - When debuggee execution resumes, the debuggee's microtask queue must be
+ * restored exactly as it was when the interruption occurred.
+ *
+ * - Non-debuggee task and microtask execution must take place normally during
+ * the interruption.
+ *
+ * Since each HTML task begins with an empty microtask queue, and it should not
+ * be possible for a task to mix debuggee and non-debuggee code, interrupting a
+ * debuggee should always find a microtask queue containing exclusively debuggee
+ * microtasks, if any. So saving and restoring the microtask queue should affect
+ * only the debuggee, not any non-debuggee content.
+ *
+ * AutoDebuggerJobQueueInterruption
+ * --------------------------------
+ *
+ * AutoDebuggerJobQueueInterruption is an RAII class, meant for use by the
+ * Debugger API implementation, that takes care of saving and restoring the
+ * queue.
+ *
+ * Constructing and initializing an instance of AutoDebuggerJobQueueInterruption
+ * sets aside the given JSContext's job queue, leaving the JSContext's queue
+ * empty. When the AutoDebuggerJobQueueInterruption instance is destroyed, it
+ * asserts that the JSContext's current job queue (holding jobs enqueued while
+ * the AutoDebuggerJobQueueInterruption was alive) is empty, and restores the
+ * saved queue to the JSContext.
+ *
+ * Since the Debugger API's behavior is up to us, we can specify that Debugger
+ * hooks begin execution with an empty job queue, and that we drain the queue
+ * after each hook function has run. This drain will be visible to debugger
+ * hooks, and makes hook calls resemble HTML tasks, with their own automatic
+ * microtask checkpoint. But, the drain will be invisible to the debuggee, as
+ * its queue is preserved across the hook invocation.
+ *
+ * To protect the debuggee's job queue, Debugger takes care to invoke callback
+ * functions only within the scope of an AutoDebuggerJobQueueInterruption
+ * instance.
+ *
+ * Why not let the hook functions themselves take care of this?
+ * ------------------------------------------------------------
+ *
+ * Certainly, we could leave responsibility for saving and restoring the job
+ * queue to the Debugger hook functions themselves.
+ *
+ * In fact, early versions of this change tried making the devtools server save
+ * and restore the queue explicitly, but because hooks are set and changed in
+ * numerous places, it was hard to be confident that every case had been
+ * covered, and it seemed that future changes could easily introduce new holes.
+ *
+ * Later versions of this change modified the accessor properties on the
+ * Debugger objects' prototypes to automatically protect the job queue when
+ * calling hooks, but the effect was essentially a monkeypatch applied to an API
+ * we defined and control, which doesn't make sense.
+ *
+ * In the end, since promises have become such a pervasive part of JavaScript
+ * programming, almost any imaginable use of Debugger would need to provide some
+ * kind of protection for the debuggee's job queue, so it makes sense to simply
+ * handle it once, carefully, in the implementation of Debugger itself.
+ */
+class MOZ_RAII JS_PUBLIC_API AutoDebuggerJobQueueInterruption {
+ public:
+ explicit AutoDebuggerJobQueueInterruption();
+ ~AutoDebuggerJobQueueInterruption();
+
+ bool init(JSContext* cx);
+ bool initialized() const { return !!saved; }
+
+ /**
+ * Drain the job queue. (In HTML terminology, perform a microtask checkpoint.)
+ *
+ * To make Debugger hook calls more like HTML tasks or ECMAScript jobs,
+ * Debugger promises that each hook begins execution with a clean microtask
+ * queue, and that a microtask checkpoint (queue drain) takes place after each
+ * hook returns, successfully or otherwise.
+ *
+ * To ensure these debugger-introduced microtask checkpoints serve only the
+ * hook's microtasks, and never affect the debuggee's, the Debugger API
+ * implementation uses only this method to perform the checkpoints, thereby
+ * statically ensuring that an AutoDebuggerJobQueueInterruption is in scope to
+ * protect the debuggee.
+ *
+ * SavedJobQueue implementations are required to assert that the queue is
+ * empty before restoring the debuggee's queue. If the Debugger API ever fails
+ * to perform a microtask checkpoint after calling a hook, that assertion will
+ * fail, catching the mistake.
+ */
+ void runJobs();
+
+ private:
+ JSContext* cx;
+ js::UniquePtr<JobQueue::SavedJobQueue> saved;
+};
+
+enum class PromiseRejectionHandlingState { Unhandled, Handled };
+
+typedef void (*PromiseRejectionTrackerCallback)(
+ JSContext* cx, bool mutedErrors, JS::HandleObject promise,
+ JS::PromiseRejectionHandlingState state, void* data);
+
+/**
+ * Sets the callback that's invoked whenever a Promise is rejected without
+ * a rejection handler, and when a Promise that was previously rejected
+ * without a handler gets a handler attached.
+ */
+extern JS_PUBLIC_API void SetPromiseRejectionTrackerCallback(
+ JSContext* cx, PromiseRejectionTrackerCallback callback,
+ void* data = nullptr);
+
+/**
+ * Inform the runtime that the job queue is empty and the embedding is going to
+ * execute its last promise job. The runtime may now choose to skip creating
+ * promise jobs for asynchronous execution and instead continue execution
+ * synchronously. More specifically, this optimization is used to skip the
+ * standard job queuing behavior for `await` operations in async functions.
+ *
+ * This function may be called before executing the last job in the job queue.
+ * When it was called, JobQueueMayNotBeEmpty must be called in order to restore
+ * the default job queuing behavior before the embedding enqueues its next job
+ * into the job queue.
+ */
+extern JS_PUBLIC_API void JobQueueIsEmpty(JSContext* cx);
+
+/**
+ * Inform the runtime that job queue is no longer empty. The runtime can now no
+ * longer skip creating promise jobs for asynchronous execution, because
+ * pending jobs in the job queue must be executed first to preserve the FIFO
+ * (first in - first out) property of the queue. This effectively undoes
+ * JobQueueIsEmpty and re-enables the standard job queuing behavior.
+ *
+ * This function must be called whenever enqueuing a job to the job queue when
+ * JobQueueIsEmpty was called previously.
+ */
+extern JS_PUBLIC_API void JobQueueMayNotBeEmpty(JSContext* cx);
+
+/**
+ * Returns a new instance of the Promise builtin class in the current
+ * compartment, with the right slot layout.
+ *
+ * The `executor` can be a `nullptr`. In that case, the only way to resolve or
+ * reject the returned promise is via the `JS::ResolvePromise` and
+ * `JS::RejectPromise` JSAPI functions.
+ */
+extern JS_PUBLIC_API JSObject* NewPromiseObject(JSContext* cx,
+ JS::HandleObject executor);
+
+/**
+ * Returns true if the given object is an unwrapped PromiseObject, false
+ * otherwise.
+ */
+extern JS_PUBLIC_API bool IsPromiseObject(JS::HandleObject obj);
+
+/**
+ * Returns the current compartment's original Promise constructor.
+ */
+extern JS_PUBLIC_API JSObject* GetPromiseConstructor(JSContext* cx);
+
+/**
+ * Returns the current compartment's original Promise.prototype.
+ */
+extern JS_PUBLIC_API JSObject* GetPromisePrototype(JSContext* cx);
+
+// Keep this in sync with the PROMISE_STATE defines in SelfHostingDefines.h.
+enum class PromiseState { Pending, Fulfilled, Rejected };
+
+/**
+ * Returns the given Promise's state as a JS::PromiseState enum value.
+ *
+ * Returns JS::PromiseState::Pending if the given object is a wrapper that
+ * can't safely be unwrapped.
+ */
+extern JS_PUBLIC_API PromiseState GetPromiseState(JS::HandleObject promise);
+
+/**
+ * Returns the given Promise's process-unique ID.
+ */
+JS_PUBLIC_API uint64_t GetPromiseID(JS::HandleObject promise);
+
+/**
+ * Returns the given Promise's result: either the resolution value for
+ * fulfilled promises, or the rejection reason for rejected ones.
+ */
+extern JS_PUBLIC_API JS::Value GetPromiseResult(JS::HandleObject promise);
+
+/**
+ * Returns whether the given promise's rejection is already handled or not.
+ *
+ * The caller must check the given promise is rejected before checking it's
+ * handled or not.
+ */
+extern JS_PUBLIC_API bool GetPromiseIsHandled(JS::HandleObject promise);
+
+/*
+ * Given a settled (i.e. fulfilled or rejected, not pending) promise, sets
+ * |promise.[[PromiseIsHandled]]| to true and removes it from the list of
+ * unhandled rejected promises.
+ */
+extern JS_PUBLIC_API void SetSettledPromiseIsHandled(JSContext* cx,
+ JS::HandleObject promise);
+
+/**
+ * Returns a js::SavedFrame linked list of the stack that lead to the given
+ * Promise's allocation.
+ */
+extern JS_PUBLIC_API JSObject* GetPromiseAllocationSite(
+ JS::HandleObject promise);
+
+extern JS_PUBLIC_API JSObject* GetPromiseResolutionSite(
+ JS::HandleObject promise);
+
+#ifdef DEBUG
+extern JS_PUBLIC_API void DumpPromiseAllocationSite(JSContext* cx,
+ JS::HandleObject promise);
+
+extern JS_PUBLIC_API void DumpPromiseResolutionSite(JSContext* cx,
+ JS::HandleObject promise);
+#endif
+
+/**
+ * Calls the current compartment's original Promise.resolve on the original
+ * Promise constructor, with `resolutionValue` passed as an argument.
+ */
+extern JS_PUBLIC_API JSObject* CallOriginalPromiseResolve(
+ JSContext* cx, JS::HandleValue resolutionValue);
+
+/**
+ * Calls the current compartment's original Promise.reject on the original
+ * Promise constructor, with `resolutionValue` passed as an argument.
+ */
+extern JS_PUBLIC_API JSObject* CallOriginalPromiseReject(
+ JSContext* cx, JS::HandleValue rejectionValue);
+
+/**
+ * Resolves the given Promise with the given `resolutionValue`.
+ *
+ * Calls the `resolve` function that was passed to the executor function when
+ * the Promise was created.
+ */
+extern JS_PUBLIC_API bool ResolvePromise(JSContext* cx,
+ JS::HandleObject promiseObj,
+ JS::HandleValue resolutionValue);
+
+/**
+ * Rejects the given `promise` with the given `rejectionValue`.
+ *
+ * Calls the `reject` function that was passed to the executor function when
+ * the Promise was created.
+ */
+extern JS_PUBLIC_API bool RejectPromise(JSContext* cx,
+ JS::HandleObject promiseObj,
+ JS::HandleValue rejectionValue);
+
+/**
+ * Create a Promise with the given fulfill/reject handlers, that will be
+ * fulfilled/rejected with the value/reason that the promise `promise` is
+ * fulfilled/rejected with.
+ *
+ * This function basically acts like `promise.then(onFulfilled, onRejected)`,
+ * except that its behavior is unaffected by changes to `Promise`,
+ * `Promise[Symbol.species]`, `Promise.prototype.then`, `promise.constructor`,
+ * `promise.then`, and so on.
+ *
+ * This function throws if `promise` is not a Promise from this or another
+ * realm.
+ *
+ * This function will assert if `onFulfilled` or `onRejected` is non-null and
+ * also not IsCallable.
+ */
+extern JS_PUBLIC_API JSObject* CallOriginalPromiseThen(
+ JSContext* cx, JS::HandleObject promise, JS::HandleObject onFulfilled,
+ JS::HandleObject onRejected);
+
+/**
+ * Unforgeable, optimized version of the JS builtin Promise.prototype.then.
+ *
+ * Takes a Promise instance and nullable `onFulfilled`/`onRejected` callables to
+ * enqueue as reactions for that promise. In contrast to Promise.prototype.then,
+ * this doesn't create and return a new Promise instance.
+ *
+ * Throws a TypeError if `promise` isn't a Promise (or possibly a different
+ * error if it's a security wrapper or dead object proxy).
+ */
+extern JS_PUBLIC_API bool AddPromiseReactions(JSContext* cx,
+ JS::HandleObject promise,
+ JS::HandleObject onFulfilled,
+ JS::HandleObject onRejected);
+
+/**
+ * Unforgeable, optimized version of the JS builtin Promise.prototype.then.
+ *
+ * Takes a Promise instance and nullable `onFulfilled`/`onRejected` callables to
+ * enqueue as reactions for that promise. In contrast to Promise.prototype.then,
+ * this doesn't create and return a new Promise instance.
+ *
+ * Throws a TypeError if `promise` isn't a Promise (or possibly a different
+ * error if it's a security wrapper or dead object proxy).
+ *
+ * If `onRejected` is null and `promise` is rejected, this function -- unlike
+ * the function above -- will not report an unhandled rejection.
+ */
+extern JS_PUBLIC_API bool AddPromiseReactionsIgnoringUnhandledRejection(
+ JSContext* cx, JS::HandleObject promise, JS::HandleObject onFulfilled,
+ JS::HandleObject onRejected);
+
+// This enum specifies whether a promise is expected to keep track of
+// information that is useful for embedders to implement user activation
+// behavior handling as specified in the HTML spec:
+// https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-activation
+// By default, promises created by SpiderMonkey do not make any attempt to keep
+// track of information about whether an activation behavior was being processed
+// when the original promise in a promise chain was created. If the embedder
+// sets either of the HadUserInteractionAtCreation or
+// DidntHaveUserInteractionAtCreation flags on a promise after creating it,
+// SpiderMonkey will propagate that flag to newly created promises when
+// processing Promise#then and will make it possible to query this flag off of a
+// promise further down the chain later using the
+// GetPromiseUserInputEventHandlingState() API.
+enum class PromiseUserInputEventHandlingState {
+ // Don't keep track of this state (default for all promises)
+ DontCare,
+ // Keep track of this state, the original promise in the chain was created
+ // while an activation behavior was being processed.
+ HadUserInteractionAtCreation,
+ // Keep track of this state, the original promise in the chain was created
+ // while an activation behavior was not being processed.
+ DidntHaveUserInteractionAtCreation
+};
+
+/**
+ * Returns the given Promise's activation behavior state flag per above as a
+ * JS::PromiseUserInputEventHandlingState value. All promises are created with
+ * the DontCare state by default.
+ *
+ * Returns JS::PromiseUserInputEventHandlingState::DontCare if the given object
+ * is a wrapper that can't safely be unwrapped.
+ */
+extern JS_PUBLIC_API PromiseUserInputEventHandlingState
+GetPromiseUserInputEventHandlingState(JS::HandleObject promise);
+
+/**
+ * Sets the given Promise's activation behavior state flag per above as a
+ * JS::PromiseUserInputEventHandlingState value.
+ *
+ * Returns false if the given object is a wrapper that can't safely be
+ * unwrapped.
+ */
+extern JS_PUBLIC_API bool SetPromiseUserInputEventHandlingState(
+ JS::HandleObject promise, JS::PromiseUserInputEventHandlingState state);
+
+/**
+ * Unforgeable version of the JS builtin Promise.all.
+ *
+ * Takes a HandleObjectVector of Promise objects and returns a promise that's
+ * resolved with an array of resolution values when all those promises have
+ * been resolved, or rejected with the rejection value of the first rejected
+ * promise.
+ *
+ * Asserts that all objects in the `promises` vector are, maybe wrapped,
+ * instances of `Promise` or a subclass of `Promise`.
+ */
+extern JS_PUBLIC_API JSObject* GetWaitForAllPromise(
+ JSContext* cx, JS::HandleObjectVector promises);
+
+/**
+ * The Dispatchable interface allows the embedding to call SpiderMonkey
+ * on a JSContext thread when requested via DispatchToEventLoopCallback.
+ */
+class JS_PUBLIC_API Dispatchable {
+ protected:
+ // Dispatchables are created and destroyed by SpiderMonkey.
+ Dispatchable() = default;
+ virtual ~Dispatchable() = default;
+
+ public:
+ // ShuttingDown indicates that SpiderMonkey should abort async tasks to
+ // expedite shutdown.
+ enum MaybeShuttingDown { NotShuttingDown, ShuttingDown };
+
+ // Called by the embedding after DispatchToEventLoopCallback succeeds.
+ virtual void run(JSContext* cx, MaybeShuttingDown maybeShuttingDown) = 0;
+};
+
+/**
+ * Callback to dispatch a JS::Dispatchable to a JSContext's thread's event loop.
+ *
+ * The DispatchToEventLoopCallback set on a particular JSContext must accept
+ * JS::Dispatchable instances and arrange for their `run` methods to be called
+ * eventually on the JSContext's thread. This is used for cross-thread dispatch,
+ * so the callback itself must be safe to call from any thread.
+ *
+ * If the callback returns `true`, it must eventually run the given
+ * Dispatchable; otherwise, SpiderMonkey may leak memory or hang.
+ *
+ * The callback may return `false` to indicate that the JSContext's thread is
+ * shutting down and is no longer accepting runnables. Shutting down is a
+ * one-way transition: once the callback has rejected a runnable, it must reject
+ * all subsequently submitted runnables as well.
+ *
+ * To establish a DispatchToEventLoopCallback, the embedding may either call
+ * InitDispatchToEventLoop to provide its own, or call js::UseInternalJobQueues
+ * to select a default implementation built into SpiderMonkey. This latter
+ * depends on the embedding to call js::RunJobs on the JavaScript thread to
+ * process queued Dispatchables at appropriate times.
+ */
+
+typedef bool (*DispatchToEventLoopCallback)(void* closure,
+ Dispatchable* dispatchable);
+
+extern JS_PUBLIC_API void InitDispatchToEventLoop(
+ JSContext* cx, DispatchToEventLoopCallback callback, void* closure);
+
+/**
+ * When a JSRuntime is destroyed it implicitly cancels all async tasks in
+ * progress, releasing any roots held by the task. However, this is not soon
+ * enough for cycle collection, which needs to have roots dropped earlier so
+ * that the cycle collector can transitively remove roots for a future GC. For
+ * these and other cases, the set of pending async tasks can be canceled
+ * with this call earlier than JSRuntime destruction.
+ */
+
+extern JS_PUBLIC_API void ShutdownAsyncTasks(JSContext* cx);
+
+} // namespace JS
+
+#endif // js_Promise_h
diff --git a/js/public/PropertyDescriptor.h b/js/public/PropertyDescriptor.h
new file mode 100644
index 0000000000..de70838f61
--- /dev/null
+++ b/js/public/PropertyDescriptor.h
@@ -0,0 +1,383 @@
+/* -*- 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/. */
+
+/* Property descriptors and flags. */
+
+#ifndef js_PropertyDescriptor_h
+#define js_PropertyDescriptor_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF
+
+#include <stdint.h> // uint8_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Class.h" // JS{Getter,Setter}Op
+#include "js/RootingAPI.h" // JS::Handle, js::{,Mutable}WrappedPtrOperations
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+class JS_PUBLIC_API JSTracer;
+
+/* Property attributes, set in JSPropertySpec and passed to API functions.
+ *
+ * The data structure in which some of these values are stored only uses a
+ * uint8_t to store the relevant information. Proceed with caution if trying to
+ * reorder or change the the first byte worth of flags.
+ */
+
+/** The property is visible in for/in loops. */
+static constexpr uint8_t JSPROP_ENUMERATE = 0x01;
+
+/**
+ * The property is non-writable. This flag is only valid when neither
+ * JSPROP_GETTER nor JSPROP_SETTER is set.
+ */
+static constexpr uint8_t JSPROP_READONLY = 0x02;
+
+/**
+ * The property is non-configurable: it can't be deleted, and if it's an
+ * accessor descriptor, its getter and setter can't be changed.
+ */
+static constexpr uint8_t JSPROP_PERMANENT = 0x04;
+
+/* (0x08 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
+
+/** The property has a getter function. */
+static constexpr uint8_t JSPROP_GETTER = 0x10;
+
+/** The property has a setter function. */
+static constexpr uint8_t JSPROP_SETTER = 0x20;
+
+/* (0x40 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
+
+/** A bit for internal JS engine use only. */
+static constexpr uint8_t JSPROP_INTERNAL_USE_BIT = 0x80;
+
+/* (0x1000 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
+
+/**
+ * Resolve hooks and enumerate hooks must pass this flag when calling
+ * JS_Define* APIs to reify lazily-defined properties.
+ *
+ * JSPROP_RESOLVING is used only with property-defining APIs. It tells the
+ * engine to skip the resolve hook when performing the lookup at the beginning
+ * of property definition. This keeps the resolve hook from accidentally
+ * triggering itself: unchecked recursion.
+ *
+ * For enumerate hooks, triggering the resolve hook would be merely silly, not
+ * fatal, except in some cases involving non-configurable properties.
+ */
+static constexpr unsigned JSPROP_RESOLVING = 0x2000;
+
+/**
+ * When redefining an existing property, ignore the value of the
+ * JSPROP_ENUMERATE flag. This flag is ignored in other situations.
+ */
+static constexpr unsigned JSPROP_IGNORE_ENUMERATE = 0x4000;
+
+/**
+ * When redefining an existing property, ignore the value of the JSPROP_READONLY
+ * flag. This flag is ignored in other situations.
+ */
+static constexpr unsigned JSPROP_IGNORE_READONLY = 0x8000;
+
+/**
+ * When redefining an existing property, ignore the value of the
+ * JSPROP_PERMANENT flag. This flag is ignored in other situations.
+ */
+static constexpr unsigned JSPROP_IGNORE_PERMANENT = 0x10000;
+
+/**
+ * When redefining an existing property, ignore the Value in the descriptor.
+ * This flag is ignored in other situations.
+ */
+static constexpr unsigned JSPROP_IGNORE_VALUE = 0x20000;
+
+/* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
+
+static constexpr unsigned JSPROP_FLAGS_MASK =
+ JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_GETTER |
+ JSPROP_SETTER | JSPROP_INTERNAL_USE_BIT | JSPROP_RESOLVING |
+ JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT |
+ JSPROP_IGNORE_VALUE;
+
+namespace JS {
+
+/**
+ * A structure that represents a property on an object, or the absence of a
+ * property. Use {,Mutable}Handle<PropertyDescriptor> to interact with
+ * instances of this structure rather than interacting directly with member
+ * fields.
+ */
+struct JS_PUBLIC_API PropertyDescriptor {
+ JSObject* obj = nullptr;
+ unsigned attrs = 0;
+ JSGetterOp getter = nullptr;
+ JSSetterOp setter = nullptr;
+ Value value;
+
+ PropertyDescriptor() = default;
+
+ void trace(JSTracer* trc);
+};
+
+} // namespace JS
+
+namespace js {
+
+template <typename Wrapper>
+class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
+ const JS::PropertyDescriptor& desc() const {
+ return static_cast<const Wrapper*>(this)->get();
+ }
+
+ bool has(unsigned bit) const {
+ MOZ_ASSERT(bit != 0);
+ MOZ_ASSERT((bit & (bit - 1)) == 0); // only a single bit
+ return (desc().attrs & bit) != 0;
+ }
+
+ bool hasAny(unsigned bits) const { return (desc().attrs & bits) != 0; }
+
+ bool hasAll(unsigned bits) const { return (desc().attrs & bits) == bits; }
+
+ public:
+ // Descriptors with JSGetterOp/JSSetterOp are considered data
+ // descriptors. It's complicated.
+ bool isAccessorDescriptor() const {
+ return hasAny(JSPROP_GETTER | JSPROP_SETTER);
+ }
+ bool isGenericDescriptor() const {
+ return (desc().attrs & (JSPROP_GETTER | JSPROP_SETTER |
+ JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
+ (JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
+ }
+ bool isDataDescriptor() const {
+ return !isAccessorDescriptor() && !isGenericDescriptor();
+ }
+
+ bool hasConfigurable() const { return !has(JSPROP_IGNORE_PERMANENT); }
+ bool configurable() const {
+ MOZ_ASSERT(hasConfigurable());
+ return !has(JSPROP_PERMANENT);
+ }
+
+ bool hasEnumerable() const { return !has(JSPROP_IGNORE_ENUMERATE); }
+ bool enumerable() const {
+ MOZ_ASSERT(hasEnumerable());
+ return has(JSPROP_ENUMERATE);
+ }
+
+ bool hasValue() const {
+ return !isAccessorDescriptor() && !has(JSPROP_IGNORE_VALUE);
+ }
+ JS::HandleValue value() const {
+ return JS::Handle<JS::Value>::fromMarkedLocation(&desc().value);
+ }
+
+ bool hasWritable() const {
+ return !isAccessorDescriptor() && !has(JSPROP_IGNORE_READONLY);
+ }
+ bool writable() const {
+ MOZ_ASSERT(hasWritable());
+ return !has(JSPROP_READONLY);
+ }
+
+ bool hasGetterObject() const { return has(JSPROP_GETTER); }
+ JS::Handle<JSObject*> getterObject() const {
+ MOZ_ASSERT(hasGetterObject());
+ return JS::Handle<JSObject*>::fromMarkedLocation(
+ reinterpret_cast<JSObject* const*>(&desc().getter));
+ }
+ bool hasSetterObject() const { return has(JSPROP_SETTER); }
+ JS::Handle<JSObject*> setterObject() const {
+ MOZ_ASSERT(hasSetterObject());
+ return JS::Handle<JSObject*>::fromMarkedLocation(
+ reinterpret_cast<JSObject* const*>(&desc().setter));
+ }
+
+ bool hasGetterOrSetter() const { return desc().getter || desc().setter; }
+
+ JS::Handle<JSObject*> object() const {
+ return JS::Handle<JSObject*>::fromMarkedLocation(&desc().obj);
+ }
+ unsigned attributes() const { return desc().attrs; }
+ JSGetterOp getter() const { return desc().getter; }
+ JSSetterOp setter() const { return desc().setter; }
+
+ void assertValid() const {
+#ifdef DEBUG
+ MOZ_ASSERT(
+ (attributes() &
+ ~(JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE | JSPROP_PERMANENT |
+ JSPROP_IGNORE_PERMANENT | JSPROP_READONLY | JSPROP_IGNORE_READONLY |
+ JSPROP_IGNORE_VALUE | JSPROP_GETTER | JSPROP_SETTER |
+ JSPROP_RESOLVING | JSPROP_INTERNAL_USE_BIT)) == 0);
+ MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE));
+ MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT));
+ if (isAccessorDescriptor()) {
+ MOZ_ASSERT(!has(JSPROP_READONLY));
+ MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY));
+ MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE));
+ MOZ_ASSERT(!has(JSPROP_INTERNAL_USE_BIT));
+ MOZ_ASSERT(value().isUndefined());
+ MOZ_ASSERT_IF(!has(JSPROP_GETTER), !getter());
+ MOZ_ASSERT_IF(!has(JSPROP_SETTER), !setter());
+ } else {
+ MOZ_ASSERT(!hasAll(JSPROP_IGNORE_READONLY | JSPROP_READONLY));
+ MOZ_ASSERT_IF(has(JSPROP_IGNORE_VALUE), value().isUndefined());
+ }
+
+ MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_ENUMERATE));
+ MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_PERMANENT));
+ MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_READONLY));
+ MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_VALUE));
+#endif
+ }
+
+ void assertComplete() const {
+#ifdef DEBUG
+ assertValid();
+ MOZ_ASSERT(
+ (attributes() & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT |
+ JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER |
+ JSPROP_RESOLVING | JSPROP_INTERNAL_USE_BIT)) == 0);
+ MOZ_ASSERT_IF(isAccessorDescriptor(),
+ has(JSPROP_GETTER) && has(JSPROP_SETTER));
+#endif
+ }
+
+ void assertCompleteIfFound() const {
+#ifdef DEBUG
+ if (object()) {
+ assertComplete();
+ }
+#endif
+ }
+};
+
+template <typename Wrapper>
+class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
+ : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
+ JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+ void clear() {
+ object().set(nullptr);
+ setAttributes(0);
+ setGetter(nullptr);
+ setSetter(nullptr);
+ value().setUndefined();
+ }
+
+ void initFields(JS::Handle<JSObject*> obj, JS::Handle<JS::Value> v,
+ unsigned attrs, JSGetterOp getterOp, JSSetterOp setterOp) {
+ object().set(obj);
+ value().set(v);
+ setAttributes(attrs);
+ setGetter(getterOp);
+ setSetter(setterOp);
+ }
+
+ void assign(JS::PropertyDescriptor& other) {
+ object().set(other.obj);
+ setAttributes(other.attrs);
+ setGetter(other.getter);
+ setSetter(other.setter);
+ value().set(other.value);
+ }
+
+ void setDataDescriptor(JS::Handle<JS::Value> v, unsigned attrs) {
+ MOZ_ASSERT((attrs & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT |
+ JSPROP_READONLY | JSPROP_IGNORE_ENUMERATE |
+ JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_READONLY)) ==
+ 0);
+ object().set(nullptr);
+ setAttributes(attrs);
+ setGetter(nullptr);
+ setSetter(nullptr);
+ value().set(v);
+ }
+
+ JS::MutableHandle<JSObject*> object() {
+ return JS::MutableHandle<JSObject*>::fromMarkedLocation(&desc().obj);
+ }
+ unsigned& attributesRef() { return desc().attrs; }
+ JSGetterOp& getter() { return desc().getter; }
+ JSSetterOp& setter() { return desc().setter; }
+ JS::MutableHandle<JS::Value> value() {
+ return JS::MutableHandle<JS::Value>::fromMarkedLocation(&desc().value);
+ }
+ void setValue(JS::Handle<JS::Value> v) {
+ MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+ attributesRef() &= ~JSPROP_IGNORE_VALUE;
+ value().set(v);
+ }
+
+ void setConfigurable(bool configurable) {
+ setAttributes(
+ (desc().attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
+ (configurable ? 0 : JSPROP_PERMANENT));
+ }
+ void setEnumerable(bool enumerable) {
+ setAttributes(
+ (desc().attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
+ (enumerable ? JSPROP_ENUMERATE : 0));
+ }
+ void setWritable(bool writable) {
+ MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+ setAttributes((desc().attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
+ (writable ? 0 : JSPROP_READONLY));
+ }
+ void setAttributes(unsigned attrs) { desc().attrs = attrs; }
+
+ void setGetter(JSGetterOp op) { desc().getter = op; }
+ void setSetter(JSSetterOp op) { desc().setter = op; }
+ void setGetterObject(JSObject* obj) {
+ desc().getter = reinterpret_cast<JSGetterOp>(obj);
+ desc().attrs &=
+ ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
+ desc().attrs |= JSPROP_GETTER;
+ }
+ void setSetterObject(JSObject* obj) {
+ desc().setter = reinterpret_cast<JSSetterOp>(obj);
+ desc().attrs &=
+ ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
+ desc().attrs |= JSPROP_SETTER;
+ }
+
+ JS::MutableHandle<JSObject*> getterObject() {
+ MOZ_ASSERT(this->hasGetterObject());
+ return JS::MutableHandle<JSObject*>::fromMarkedLocation(
+ reinterpret_cast<JSObject**>(&desc().getter));
+ }
+ JS::MutableHandle<JSObject*> setterObject() {
+ MOZ_ASSERT(this->hasSetterObject());
+ return JS::MutableHandle<JSObject*>::fromMarkedLocation(
+ reinterpret_cast<JSObject**>(&desc().setter));
+ }
+};
+
+} // namespace js
+
+namespace JS {
+
+extern JS_PUBLIC_API bool ObjectToCompletePropertyDescriptor(
+ JSContext* cx, Handle<JSObject*> obj, Handle<Value> descriptor,
+ MutableHandle<PropertyDescriptor> desc);
+
+/*
+ * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
+ *
+ * If desc.object() is null, then vp is set to undefined.
+ */
+extern JS_PUBLIC_API bool FromPropertyDescriptor(
+ JSContext* cx, Handle<PropertyDescriptor> desc, MutableHandle<Value> vp);
+
+} // namespace JS
+
+#endif /* js_PropertyDescriptor_h */
diff --git a/js/public/PropertySpec.h b/js/public/PropertySpec.h
new file mode 100644
index 0000000000..f08a090d66
--- /dev/null
+++ b/js/public/PropertySpec.h
@@ -0,0 +1,428 @@
+/* -*- 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/. */
+
+/* Property descriptors and flags. */
+
+#ifndef js_PropertySpec_h
+#define js_PropertySpec_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF}
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint8_t, uint16_t, int32_t, uint32_t, uintptr_t
+#include <type_traits> // std::enable_if
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/CallArgs.h" // JSNative
+#include "js/PropertyDescriptor.h" // JSPROP_*
+#include "js/RootingAPI.h" // JS::MutableHandle
+#include "js/Symbol.h" // JS::SymbolCode, PropertySpecNameIsSymbol
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JSJitInfo;
+
+/**
+ * Wrapper to relace JSNative for JSPropertySpecs and JSFunctionSpecs. This will
+ * allow us to pass one JSJitInfo per function with the property/function spec,
+ * without additional field overhead.
+ */
+struct JSNativeWrapper {
+ JSNative op = nullptr;
+ const JSJitInfo* info = nullptr;
+
+ JSNativeWrapper() = default;
+
+ JSNativeWrapper(const JSNativeWrapper& other) = default;
+
+ constexpr JSNativeWrapper(JSNative op, const JSJitInfo* info)
+ : op(op), info(info) {}
+};
+
+/**
+ * Description of a property. JS_DefineProperties and JS_InitClass take arrays
+ * of these and define many properties at once. JS_PSG, JS_PSGS and JS_PS_END
+ * are helper macros for defining such arrays.
+ */
+struct JSPropertySpec {
+ struct SelfHostedWrapper {
+ // The same type as JSNativeWrapper's first field, so that the access in
+ // JSPropertySpec::checkAccessorsAreSelfHosted become valid.
+ JSNative unused = nullptr;
+
+ const char* funname;
+
+ SelfHostedWrapper() = delete;
+
+ explicit constexpr SelfHostedWrapper(const char* funname)
+ : funname(funname) {}
+ };
+
+ struct ValueWrapper {
+ uintptr_t type;
+ union {
+ const char* string;
+ int32_t int32;
+ double double_;
+ };
+
+ private:
+ ValueWrapper() = delete;
+
+ explicit constexpr ValueWrapper(int32_t n)
+ : type(JSVAL_TYPE_INT32), int32(n) {}
+
+ explicit constexpr ValueWrapper(const char* s)
+ : type(JSVAL_TYPE_STRING), string(s) {}
+
+ explicit constexpr ValueWrapper(double d)
+ : type(JSVAL_TYPE_DOUBLE), double_(d) {}
+
+ public:
+ ValueWrapper(const ValueWrapper& other) = default;
+
+ static constexpr ValueWrapper int32Value(int32_t n) {
+ return ValueWrapper(n);
+ }
+
+ static constexpr ValueWrapper stringValue(const char* s) {
+ return ValueWrapper(s);
+ }
+
+ static constexpr ValueWrapper doubleValue(double d) {
+ return ValueWrapper(d);
+ }
+ };
+
+ union Accessor {
+ JSNativeWrapper native;
+ SelfHostedWrapper selfHosted;
+
+ private:
+ Accessor() = delete;
+
+ constexpr Accessor(JSNative op, const JSJitInfo* info) : native(op, info) {}
+
+ explicit constexpr Accessor(const char* funname) : selfHosted(funname) {}
+
+ public:
+ Accessor(const Accessor& other) = default;
+
+ static constexpr Accessor nativeAccessor(JSNative op,
+ const JSJitInfo* info = nullptr) {
+ return Accessor(op, info);
+ }
+
+ static constexpr Accessor selfHostedAccessor(const char* funname) {
+ return Accessor(funname);
+ }
+
+ static constexpr Accessor noAccessor() {
+ return Accessor(nullptr, nullptr);
+ }
+ };
+
+ union AccessorsOrValue {
+ struct Accessors {
+ Accessor getter;
+ Accessor setter;
+
+ constexpr Accessors(Accessor getter, Accessor setter)
+ : getter(getter), setter(setter) {}
+ } accessors;
+ ValueWrapper value;
+
+ private:
+ AccessorsOrValue() = delete;
+
+ constexpr AccessorsOrValue(Accessor getter, Accessor setter)
+ : accessors(getter, setter) {}
+
+ explicit constexpr AccessorsOrValue(ValueWrapper value) : value(value) {}
+
+ public:
+ AccessorsOrValue(const AccessorsOrValue& other) = default;
+
+ static constexpr AccessorsOrValue fromAccessors(Accessor getter,
+ Accessor setter) {
+ return AccessorsOrValue(getter, setter);
+ }
+
+ static constexpr AccessorsOrValue fromValue(ValueWrapper value) {
+ return AccessorsOrValue(value);
+ }
+ };
+
+ union Name {
+ private:
+ const char* string_;
+ uintptr_t symbol_;
+
+ public:
+ Name() = delete;
+
+ explicit constexpr Name(const char* str) : string_(str) {}
+ explicit constexpr Name(JS::SymbolCode symbol)
+ : symbol_(uint32_t(symbol) + 1) {}
+
+ explicit operator bool() const { return !!symbol_; }
+
+ bool isSymbol() const { return JS::PropertySpecNameIsSymbol(symbol_); }
+ JS::SymbolCode symbol() const {
+ MOZ_ASSERT(isSymbol());
+ return JS::SymbolCode(symbol_ - 1);
+ }
+
+ bool isString() const { return !isSymbol(); }
+ const char* string() const {
+ MOZ_ASSERT(isString());
+ return string_;
+ }
+ };
+
+ Name name;
+
+ private:
+ uint8_t flags_;
+
+ public:
+ AccessorsOrValue u;
+
+ private:
+ JSPropertySpec() = delete;
+
+ constexpr JSPropertySpec(const char* name, uint8_t flags, AccessorsOrValue u)
+ : name(name), flags_(flags), u(u) {}
+ constexpr JSPropertySpec(JS::SymbolCode name, uint8_t flags,
+ AccessorsOrValue u)
+ : name(name), flags_(flags), u(u) {}
+
+ public:
+ JSPropertySpec(const JSPropertySpec& other) = default;
+
+ static constexpr JSPropertySpec nativeAccessors(
+ const char* name, uint8_t flags, JSNative getter,
+ const JSJitInfo* getterInfo, JSNative setter = nullptr,
+ const JSJitInfo* setterInfo = nullptr) {
+ return JSPropertySpec(
+ name, flags,
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo),
+ JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo)));
+ }
+
+ static constexpr JSPropertySpec nativeAccessors(
+ JS::SymbolCode name, uint8_t flags, JSNative getter,
+ const JSJitInfo* getterInfo, JSNative setter = nullptr,
+ const JSJitInfo* setterInfo = nullptr) {
+ return JSPropertySpec(
+ name, flags,
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo),
+ JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo)));
+ }
+
+ static constexpr JSPropertySpec selfHostedAccessors(
+ const char* name, uint8_t flags, const char* getterName,
+ const char* setterName = nullptr) {
+ return JSPropertySpec(
+ name, flags | JSPROP_GETTER | (setterName ? JSPROP_SETTER : 0),
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::selfHostedAccessor(getterName),
+ setterName
+ ? JSPropertySpec::Accessor::selfHostedAccessor(setterName)
+ : JSPropertySpec::Accessor::noAccessor()));
+ }
+
+ static constexpr JSPropertySpec selfHostedAccessors(
+ JS::SymbolCode name, uint8_t flags, const char* getterName,
+ const char* setterName = nullptr) {
+ return JSPropertySpec(
+ name, flags | JSPROP_GETTER | (setterName ? JSPROP_SETTER : 0),
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::selfHostedAccessor(getterName),
+ setterName
+ ? JSPropertySpec::Accessor::selfHostedAccessor(setterName)
+ : JSPropertySpec::Accessor::noAccessor()));
+ }
+
+ static constexpr JSPropertySpec int32Value(const char* name, uint8_t flags,
+ int32_t n) {
+ return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::int32Value(n)));
+ }
+
+ static constexpr JSPropertySpec int32Value(JS::SymbolCode name, uint8_t flags,
+ int32_t n) {
+ return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::int32Value(n)));
+ }
+
+ static constexpr JSPropertySpec stringValue(const char* name, uint8_t flags,
+ const char* s) {
+ return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::stringValue(s)));
+ }
+
+ static constexpr JSPropertySpec stringValue(JS::SymbolCode name,
+ uint8_t flags, const char* s) {
+ return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::stringValue(s)));
+ }
+
+ static constexpr JSPropertySpec doubleValue(const char* name, uint8_t flags,
+ double d) {
+ return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::doubleValue(d)));
+ }
+
+ static constexpr JSPropertySpec sentinel() {
+ return JSPropertySpec(nullptr, 0,
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::noAccessor(),
+ JSPropertySpec::Accessor::noAccessor()));
+ }
+
+ // JSPROP_* property attributes as defined in PropertyDescriptor.h
+ unsigned attributes() const { return flags_ & ~JSPROP_INTERNAL_USE_BIT; }
+
+ bool isAccessor() const { return !(flags_ & JSPROP_INTERNAL_USE_BIT); }
+
+ JS_PUBLIC_API bool getValue(JSContext* cx,
+ JS::MutableHandle<JS::Value> value) const;
+
+ bool isSelfHosted() const {
+ MOZ_ASSERT(isAccessor());
+
+#ifdef DEBUG
+ // Verify that our accessors match our JSPROP_GETTER flag.
+ if (flags_ & JSPROP_GETTER) {
+ checkAccessorsAreSelfHosted();
+ } else {
+ checkAccessorsAreNative();
+ }
+#endif
+ return (flags_ & JSPROP_GETTER);
+ }
+
+ static_assert(sizeof(SelfHostedWrapper) == sizeof(JSNativeWrapper),
+ "JSPropertySpec::getter/setter must be compact");
+ static_assert(offsetof(SelfHostedWrapper, unused) ==
+ offsetof(JSNativeWrapper, op) &&
+ offsetof(SelfHostedWrapper, funname) ==
+ offsetof(JSNativeWrapper, info),
+ "checkAccessorsAreNative below require that "
+ "SelfHostedWrapper::funname overlay "
+ "JSNativeWrapper::info and "
+ "SelfHostedWrapper::unused overlay "
+ "JSNativeWrapper::op");
+
+ private:
+ void checkAccessorsAreNative() const {
+ // We may have a getter or a setter or both. And whichever ones we have
+ // should not have a SelfHostedWrapper for the accessor.
+ MOZ_ASSERT_IF(u.accessors.getter.native.info, u.accessors.getter.native.op);
+ MOZ_ASSERT_IF(u.accessors.setter.native.info, u.accessors.setter.native.op);
+ }
+
+ void checkAccessorsAreSelfHosted() const {
+ MOZ_ASSERT(!u.accessors.getter.selfHosted.unused);
+ MOZ_ASSERT(!u.accessors.setter.selfHosted.unused);
+ }
+};
+
+#define JS_CHECK_ACCESSOR_FLAGS(flags) \
+ (static_cast<std::enable_if_t<((flags) & ~(JSPROP_ENUMERATE | \
+ JSPROP_PERMANENT)) == 0>>(0), \
+ (flags))
+
+#define JS_PSG(name, getter, flags) \
+ JSPropertySpec::nativeAccessors(name, JS_CHECK_ACCESSOR_FLAGS(flags), \
+ getter, nullptr)
+#define JS_PSGS(name, getter, setter, flags) \
+ JSPropertySpec::nativeAccessors(name, JS_CHECK_ACCESSOR_FLAGS(flags), \
+ getter, nullptr, setter, nullptr)
+#define JS_SYM_GET(symbol, getter, flags) \
+ JSPropertySpec::nativeAccessors(::JS::SymbolCode::symbol, \
+ JS_CHECK_ACCESSOR_FLAGS(flags), getter, \
+ nullptr)
+#define JS_SELF_HOSTED_GET(name, getterName, flags) \
+ JSPropertySpec::selfHostedAccessors(name, JS_CHECK_ACCESSOR_FLAGS(flags), \
+ getterName)
+#define JS_SELF_HOSTED_GETSET(name, getterName, setterName, flags) \
+ JSPropertySpec::selfHostedAccessors(name, JS_CHECK_ACCESSOR_FLAGS(flags), \
+ getterName, setterName)
+#define JS_SELF_HOSTED_SYM_GET(symbol, getterName, flags) \
+ JSPropertySpec::selfHostedAccessors( \
+ ::JS::SymbolCode::symbol, JS_CHECK_ACCESSOR_FLAGS(flags), getterName)
+#define JS_STRING_PS(name, string, flags) \
+ JSPropertySpec::stringValue(name, flags, string)
+#define JS_STRING_SYM_PS(symbol, string, flags) \
+ JSPropertySpec::stringValue(::JS::SymbolCode::symbol, flags, string)
+#define JS_INT32_PS(name, value, flags) \
+ JSPropertySpec::int32Value(name, flags, value)
+#define JS_DOUBLE_PS(name, value, flags) \
+ JSPropertySpec::doubleValue(name, flags, value)
+#define JS_PS_END JSPropertySpec::sentinel()
+
+/**
+ * To define a native function, set call to a JSNativeWrapper. To define a
+ * self-hosted function, set selfHostedName to the name of a function
+ * compiled during JSRuntime::initSelfHosting.
+ */
+struct JSFunctionSpec {
+ using Name = JSPropertySpec::Name;
+
+ Name name;
+ JSNativeWrapper call;
+ uint16_t nargs;
+ uint16_t flags;
+ const char* selfHostedName;
+
+ // JSPROP_* property attributes as defined in PropertyDescriptor.h
+ unsigned attributes() const { return flags; }
+};
+
+/*
+ * Terminating sentinel initializer to put at the end of a JSFunctionSpec array
+ * that's passed to JS_DefineFunctions or JS_InitClass.
+ */
+#define JS_FS_END JS_FN(nullptr, nullptr, 0, 0)
+
+/*
+ * Initializer macros for a JSFunctionSpec array element. JS_FNINFO allows the
+ * simple adding of JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted
+ * function. JS_INLINABLE_FN allows specifying an InlinableNative enum value for
+ * natives inlined or specialized by the JIT. Finally JS_FNSPEC has slots for
+ * all the fields.
+ *
+ * The _SYM variants allow defining a function with a symbol key rather than a
+ * string key. For example, use JS_SYM_FN(iterator, ...) to define an
+ * @@iterator method.
+ */
+#define JS_FN(name, call, nargs, flags) \
+ JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
+#define JS_INLINABLE_FN(name, call, nargs, flags, native) \
+ JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, flags, nullptr)
+#define JS_SYM_FN(symbol, call, nargs, flags) \
+ JS_SYM_FNSPEC(symbol, call, nullptr, nargs, flags, nullptr)
+#define JS_FNINFO(name, call, info, nargs, flags) \
+ JS_FNSPEC(name, call, info, nargs, flags, nullptr)
+#define JS_SELF_HOSTED_FN(name, selfHostedName, nargs, flags) \
+ JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
+#define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \
+ JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName)
+#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \
+ JS_FNSPEC(::JS::SymbolCode::symbol, call, info, nargs, flags, selfHostedName)
+#define JS_FNSPEC(name, call, info, nargs, flags, selfHostedName) \
+ { JSFunctionSpec::Name(name), {call, info}, nargs, flags, selfHostedName }
+
+#endif // js_PropertySpec_h
diff --git a/js/public/ProtoKey.h b/js/public/ProtoKey.h
new file mode 100644
index 0000000000..dd73808289
--- /dev/null
+++ b/js/public/ProtoKey.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_ProtoKey_h
+#define js_ProtoKey_h
+
+/* A higher-order macro for enumerating all JSProtoKey values. */
+/*
+ * Consumers define macros as follows:
+ * MACRO(name, clasp)
+ * name: The canonical name of the class.
+ * clasp: The JSClass for this object, or "dummy" if it doesn't exist.
+ *
+ *
+ * Consumers wishing to iterate over all the JSProtoKey values, can use
+ * JS_FOR_EACH_PROTOTYPE. However, there are certain values that don't
+ * correspond to real constructors, like Null or constructors that are disabled
+ * via preprocessor directives. We still need to include these in the JSProtoKey
+ * list in order to maintain binary XDR compatibility, but we need to provide a
+ * tool to handle them differently. JS_FOR_PROTOTYPES fills this niche.
+ *
+ * Consumers pass two macros to JS_FOR_PROTOTYPES - |REAL| and |IMAGINARY|. The
+ * former is invoked for entries that have real client-exposed constructors, and
+ * the latter is called for the rest. Consumers that don't care about this
+ * distinction can simply pass the same macro to both, which is exactly what
+ * JS_FOR_EACH_PROTOTYPE does.
+ */
+
+#define CLASP(NAME) (&NAME##Class)
+#define OCLASP(NAME) (&NAME##Object::class_)
+#define TYPED_ARRAY_CLASP(TYPE) (&TypedArrayObject::classes[Scalar::TYPE])
+#define ERROR_CLASP(TYPE) (&ErrorObject::classes[TYPE])
+
+#ifdef JS_HAS_INTL_API
+# define IF_INTL(REAL, IMAGINARY) REAL
+#else
+# define IF_INTL(REAL, IMAGINARY) IMAGINARY
+#endif
+
+#define JS_FOR_PROTOTYPES_(REAL, IMAGINARY, REAL_IF_INTL) \
+ IMAGINARY(Null, dummy) \
+ REAL(Object, OCLASP(Plain)) \
+ REAL(Function, &JSFunction::class_) \
+ REAL(Array, OCLASP(Array)) \
+ REAL(Boolean, OCLASP(Boolean)) \
+ REAL(JSON, CLASP(JSON)) \
+ REAL(Date, OCLASP(Date)) \
+ REAL(Math, CLASP(Math)) \
+ REAL(Number, OCLASP(Number)) \
+ REAL(String, OCLASP(String)) \
+ REAL(RegExp, OCLASP(RegExp)) \
+ REAL(Error, ERROR_CLASP(JSEXN_ERR)) \
+ REAL(InternalError, ERROR_CLASP(JSEXN_INTERNALERR)) \
+ REAL(AggregateError, ERROR_CLASP(JSEXN_AGGREGATEERR)) \
+ REAL(EvalError, ERROR_CLASP(JSEXN_EVALERR)) \
+ REAL(RangeError, ERROR_CLASP(JSEXN_RANGEERR)) \
+ REAL(ReferenceError, ERROR_CLASP(JSEXN_REFERENCEERR)) \
+ REAL(SyntaxError, ERROR_CLASP(JSEXN_SYNTAXERR)) \
+ REAL(TypeError, ERROR_CLASP(JSEXN_TYPEERR)) \
+ REAL(URIError, ERROR_CLASP(JSEXN_URIERR)) \
+ REAL(DebuggeeWouldRun, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN)) \
+ REAL(CompileError, ERROR_CLASP(JSEXN_WASMCOMPILEERROR)) \
+ REAL(LinkError, ERROR_CLASP(JSEXN_WASMLINKERROR)) \
+ REAL(RuntimeError, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR)) \
+ REAL(ArrayBuffer, OCLASP(ArrayBuffer)) \
+ REAL(Int8Array, TYPED_ARRAY_CLASP(Int8)) \
+ REAL(Uint8Array, TYPED_ARRAY_CLASP(Uint8)) \
+ REAL(Int16Array, TYPED_ARRAY_CLASP(Int16)) \
+ REAL(Uint16Array, TYPED_ARRAY_CLASP(Uint16)) \
+ REAL(Int32Array, TYPED_ARRAY_CLASP(Int32)) \
+ REAL(Uint32Array, TYPED_ARRAY_CLASP(Uint32)) \
+ REAL(Float32Array, TYPED_ARRAY_CLASP(Float32)) \
+ REAL(Float64Array, TYPED_ARRAY_CLASP(Float64)) \
+ REAL(Uint8ClampedArray, TYPED_ARRAY_CLASP(Uint8Clamped)) \
+ REAL(BigInt64Array, TYPED_ARRAY_CLASP(BigInt64)) \
+ REAL(BigUint64Array, TYPED_ARRAY_CLASP(BigUint64)) \
+ REAL(BigInt, OCLASP(BigInt)) \
+ REAL(Proxy, CLASP(Proxy)) \
+ REAL(WeakMap, OCLASP(WeakMap)) \
+ REAL(Map, OCLASP(Map)) \
+ REAL(Set, OCLASP(Set)) \
+ REAL(DataView, OCLASP(DataView)) \
+ REAL(Symbol, OCLASP(Symbol)) \
+ REAL(SharedArrayBuffer, OCLASP(SharedArrayBuffer)) \
+ REAL_IF_INTL(Intl, CLASP(Intl)) \
+ REAL_IF_INTL(Collator, OCLASP(Collator)) \
+ REAL_IF_INTL(DateTimeFormat, OCLASP(DateTimeFormat)) \
+ REAL_IF_INTL(DisplayNames, OCLASP(DisplayNames)) \
+ REAL_IF_INTL(ListFormat, OCLASP(ListFormat)) \
+ REAL_IF_INTL(Locale, OCLASP(Locale)) \
+ REAL_IF_INTL(NumberFormat, OCLASP(NumberFormat)) \
+ REAL_IF_INTL(PluralRules, OCLASP(PluralRules)) \
+ REAL_IF_INTL(RelativeTimeFormat, OCLASP(RelativeTimeFormat)) \
+ REAL(Reflect, CLASP(Reflect)) \
+ REAL(WeakSet, OCLASP(WeakSet)) \
+ REAL(TypedArray, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
+ REAL(Atomics, OCLASP(Atomics)) \
+ REAL(SavedFrame, &js::SavedFrame::class_) \
+ REAL(Promise, OCLASP(Promise)) \
+ REAL(AsyncFunction, CLASP(AsyncFunction)) \
+ REAL(GeneratorFunction, CLASP(GeneratorFunction)) \
+ REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction)) \
+ REAL(ReadableStream, &js::ReadableStream::class_) \
+ REAL(ReadableStreamDefaultReader, &js::ReadableStreamDefaultReader::class_) \
+ REAL(ReadableStreamDefaultController, \
+ &js::ReadableStreamDefaultController::class_) \
+ REAL(ReadableByteStreamController, \
+ &js::ReadableByteStreamController::class_) \
+ REAL(WritableStream, &js::WritableStream::class_) \
+ REAL(WritableStreamDefaultController, \
+ &js::WritableStreamDefaultController::class_) \
+ REAL(WritableStreamDefaultWriter, &js::WritableStreamDefaultWriter::class_) \
+ REAL(ByteLengthQueuingStrategy, &js::ByteLengthQueuingStrategy::class_) \
+ REAL(CountQueuingStrategy, &js::CountQueuingStrategy::class_) \
+ REAL(WebAssembly, OCLASP(WasmNamespace)) \
+ REAL(WasmModule, OCLASP(WasmModule)) \
+ REAL(WasmInstance, OCLASP(WasmInstance)) \
+ REAL(WasmMemory, OCLASP(WasmMemory)) \
+ REAL(WasmTable, OCLASP(WasmTable)) \
+ REAL(WasmGlobal, OCLASP(WasmGlobal)) \
+ REAL(WasmException, OCLASP(WasmException)) \
+ REAL(FinalizationRegistry, OCLASP(FinalizationRegistry)) \
+ REAL(WeakRef, OCLASP(WeakRef)) \
+ REAL(Iterator, OCLASP(Iterator)) \
+ REAL(AsyncIterator, OCLASP(AsyncIterator))
+
+#define JS_FOR_PROTOTYPES(REAL, IMAGINARY) \
+ JS_FOR_PROTOTYPES_(REAL, IMAGINARY, IF_INTL(REAL, IMAGINARY))
+
+#define JS_FOR_EACH_PROTOTYPE(MACRO) JS_FOR_PROTOTYPES(MACRO, MACRO)
+
+#endif /* js_ProtoKey_h */
diff --git a/js/public/Proxy.h b/js/public/Proxy.h
new file mode 100644
index 0000000000..4c803fa3fd
--- /dev/null
+++ b/js/public/Proxy.h
@@ -0,0 +1,748 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Proxy_h
+#define js_Proxy_h
+
+#include "mozilla/Maybe.h"
+
+#include "jsfriendapi.h"
+
+#include "js/Array.h" // JS::IsArrayAnswer
+#include "js/CallNonGenericMethod.h"
+#include "js/Class.h"
+#include "js/Object.h" // JS::GetClass
+#include "js/shadow/Object.h" // JS::shadow::Object
+
+namespace js {
+
+class RegExpShared;
+
+class JS_FRIEND_API Wrapper;
+
+/*
+ * [SMDOC] Proxy Objects
+ *
+ * A proxy is a JSObject with highly customizable behavior. ES6 specifies a
+ * single kind of proxy, but the customization mechanisms we use to implement
+ * ES6 Proxy objects are also useful wherever an object with weird behavior is
+ * wanted. Proxies are used to implement:
+ *
+ * - the scope objects used by the Debugger's frame.eval() method
+ * (see js::GetDebugEnvironment)
+ *
+ * - the khuey hack, whereby a whole compartment can be blown away
+ * even if other compartments hold references to objects in it
+ * (see js::NukeCrossCompartmentWrappers)
+ *
+ * - XPConnect security wrappers, which protect chrome from malicious content
+ * (js/xpconnect/wrappers)
+ *
+ * - DOM objects with special property behavior, like named getters
+ * (dom/bindings/Codegen.py generates these proxies from WebIDL)
+ *
+ * ### Proxies and internal methods
+ *
+ * ES2019 specifies 13 internal methods. The runtime semantics of just about
+ * everything a script can do to an object is specified in terms of these
+ * internal methods. For example:
+ *
+ * JS code ES6 internal method that gets called
+ * --------------------------- --------------------------------
+ * obj.prop obj.[[Get]](obj, "prop")
+ * "prop" in obj obj.[[HasProperty]]("prop")
+ * new obj() obj.[[Construct]](<empty argument List>)
+ *
+ * With regard to the implementation of these internal methods, there are three
+ * very different kinds of object in SpiderMonkey.
+ *
+ * 1. Native objects cover most objects and contain both internal slots and
+ * properties. JSClassOps and ObjectOps may be used to override certain
+ * default behaviors.
+ *
+ * 2. Proxy objects are composed of internal slots and a ProxyHandler. The
+ * handler contains C++ methods that can implement these standard (and
+ * non-standard) internal methods. JSClassOps and ObjectOps for the base
+ * ProxyObject invoke the handler methods as appropriate.
+ *
+ * 3. Objects with custom layouts like TypedObjects. These rely on JSClassOps
+ * and ObjectOps to implement internal methods.
+ *
+ * Native objects with custom JSClassOps / ObjectOps are used when the object
+ * behaves very similar to a normal object such as the ArrayObject and it's
+ * length property. Most usages wrapping a C++ or other type should prefer
+ * using a Proxy. Using the proxy approach makes it much easier to create an
+ * ECMAScript and JIT compatible object, particularly if using an appropriate
+ * base class.
+ *
+ * Just about anything you do to a proxy will end up going through a C++
+ * virtual method call. Possibly several. There's no reason the JITs and ICs
+ * can't specialize for particular proxies, based on the handler; but currently
+ * we don't do much of this, so the virtual method overhead typically is
+ * actually incurred.
+ *
+ * ### The proxy handler hierarchy
+ *
+ * A major use case for proxies is to forward each internal method call to
+ * another object, known as its target. The target can be an arbitrary JS
+ * object. Not every proxy has the notion of a target, however.
+ *
+ * To minimize code duplication, a set of abstract proxy handler classes is
+ * provided, from which other handlers may inherit. These abstract classes are
+ * organized in the following hierarchy:
+ *
+ * BaseProxyHandler
+ * |
+ * ForwardingProxyHandler // has a target and forwards internal methods
+ * |
+ * Wrapper // can be unwrapped to reveal target
+ * | // (see js::CheckedUnwrap)
+ * |
+ * CrossCompartmentWrapper // target is in another compartment;
+ * // implements membrane between compartments
+ *
+ * Example: Some DOM objects (including all the arraylike DOM objects) are
+ * implemented as proxies. Since these objects don't need to forward operations
+ * to any underlying JS object, BaseDOMProxyHandler directly subclasses
+ * BaseProxyHandler.
+ *
+ * Gecko's security wrappers are examples of cross-compartment wrappers.
+ *
+ * ### Proxy prototype chains
+ *
+ * While most ECMAScript internal methods are handled by simply calling the
+ * handler method, the [[GetPrototypeOf]] / [[SetPrototypeOf]] behaviors may
+ * follow one of two models:
+ *
+ * 1. A concrete prototype object (or null) is passed to object construction
+ * and ordinary prototype read and write applies. The prototype-related
+ * handler hooks are never called in this case. The [[Prototype]] slot is
+ * used to store the current prototype value.
+ *
+ * 2. TaggedProto::LazyProto is passed to NewProxyObject (or the
+ * ProxyOptions::lazyProto flag is set). Each read or write of the
+ * prototype will invoke the handler. This dynamic prototype behavior may
+ * be useful for wrapper-like objects. If this mode is used the
+ * getPrototype handler at a minimum must be implemented.
+ *
+ * NOTE: In this mode the [[Prototype]] internal slot is unavailable and
+ * must be simulated if needed. This is non-standard, but an
+ * appropriate handler can hide this implementation detail.
+ *
+ * One subtlety here is that ECMAScript has a notion of "ordinary" prototypes.
+ * An object that doesn't override [[GetPrototypeOf]] is considered to have an
+ * ordinary prototype. The getPrototypeIfOrdinary handler must be implemented
+ * by you or your base class. Typically model 1 will be considered "ordinary"
+ * and model 2 will not.
+ */
+
+/*
+ * BaseProxyHandler is the most generic kind of proxy handler. It does not make
+ * any assumptions about the target. Consequently, it does not provide any
+ * default implementation for most methods. As a convenience, a few high-level
+ * methods, like get() and set(), are given default implementations that work by
+ * calling the low-level methods, like getOwnPropertyDescriptor().
+ *
+ * Important: If you add a method here, you should probably also add a
+ * Proxy::foo entry point with an AutoEnterPolicy. If you don't, you need an
+ * explicit override for the method in SecurityWrapper. See bug 945826 comment
+ * 0.
+ */
+class JS_FRIEND_API BaseProxyHandler {
+ /*
+ * Sometimes it's desirable to designate groups of proxy handlers as
+ * "similar". For this, we use the notion of a "family": A consumer-provided
+ * opaque pointer that designates the larger group to which this proxy
+ * belongs.
+ *
+ * If it will never be important to differentiate this proxy from others as
+ * part of a distinct group, nullptr may be used instead.
+ */
+ const void* mFamily;
+
+ /*
+ * Proxy handlers can use mHasPrototype to request the following special
+ * treatment from the JS engine:
+ *
+ * - When mHasPrototype is true, the engine never calls these methods:
+ * has, set, enumerate, iterate. Instead, for these operations,
+ * it calls the "own" methods like getOwnPropertyDescriptor, hasOwn,
+ * defineProperty, getOwnEnumerablePropertyKeys, etc.,
+ * and consults the prototype chain if needed.
+ *
+ * - When mHasPrototype is true, the engine calls handler->get() only if
+ * handler->hasOwn() says an own property exists on the proxy. If not,
+ * it consults the prototype chain.
+ *
+ * This is useful because it frees the ProxyHandler from having to implement
+ * any behavior having to do with the prototype chain.
+ */
+ bool mHasPrototype;
+
+ /*
+ * All proxies indicate whether they have any sort of interesting security
+ * policy that might prevent the caller from doing something it wants to
+ * the object. In the case of wrappers, this distinction is used to
+ * determine whether the caller may strip off the wrapper if it so desires.
+ */
+ bool mHasSecurityPolicy;
+
+ public:
+ explicit constexpr BaseProxyHandler(const void* aFamily,
+ bool aHasPrototype = false,
+ bool aHasSecurityPolicy = false)
+ : mFamily(aFamily),
+ mHasPrototype(aHasPrototype),
+ mHasSecurityPolicy(aHasSecurityPolicy) {}
+
+ bool hasPrototype() const { return mHasPrototype; }
+
+ bool hasSecurityPolicy() const { return mHasSecurityPolicy; }
+
+ inline const void* family() const { return mFamily; }
+ static size_t offsetOfFamily() { return offsetof(BaseProxyHandler, mFamily); }
+
+ virtual bool finalizeInBackground(const JS::Value& priv) const {
+ /*
+ * Called on creation of a proxy to determine whether its finalize
+ * method can be finalized on the background thread.
+ */
+ return true;
+ }
+
+ virtual bool canNurseryAllocate() const {
+ /*
+ * Nursery allocation is allowed if and only if it is safe to not
+ * run |finalize| when the ProxyObject dies.
+ */
+ return false;
+ }
+
+ /* Policy enforcement methods.
+ *
+ * enter() allows the policy to specify whether the caller may perform |act|
+ * on the proxy's |id| property. In the case when |act| is CALL, |id| is
+ * generally JSID_VOID. The |mayThrow| parameter indicates whether a
+ * handler that wants to throw custom exceptions when denying should do so
+ * or not.
+ *
+ * The |act| parameter to enter() specifies the action being performed.
+ * If |bp| is false, the method suggests that the caller throw (though it
+ * may still decide to squelch the error).
+ *
+ * We make these OR-able so that assertEnteredPolicy can pass a union of them.
+ * For example, get{,Own}PropertyDescriptor is invoked by calls to ::get()
+ * ::set(), in addition to being invoked on its own, so there are several
+ * valid Actions that could have been entered.
+ */
+ typedef uint32_t Action;
+ enum {
+ NONE = 0x00,
+ GET = 0x01,
+ SET = 0x02,
+ CALL = 0x04,
+ ENUMERATE = 0x08,
+ GET_PROPERTY_DESCRIPTOR = 0x10
+ };
+
+ virtual bool enter(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ Action act, bool mayThrow, bool* bp) const;
+
+ /* Standard internal methods. */
+ virtual bool getOwnPropertyDescriptor(
+ JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const = 0;
+ virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result) const = 0;
+ virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const = 0;
+ virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::ObjectOpResult& result) const = 0;
+
+ /*
+ * These methods are standard, but the engine does not normally call them.
+ * They're opt-in. See "Proxy prototype chains" above.
+ *
+ * getPrototype() crashes if called. setPrototype() throws a TypeError.
+ */
+ virtual bool getPrototype(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleObject protop) const;
+ virtual bool setPrototype(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleObject proto,
+ JS::ObjectOpResult& result) const;
+
+ /* Non-standard but conceptual kin to {g,s}etPrototype, so these live here. */
+ virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy,
+ bool* isOrdinary,
+ JS::MutableHandleObject protop) const = 0;
+ virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject proxy,
+ bool* succeeded) const;
+
+ virtual bool preventExtensions(JSContext* cx, JS::HandleObject proxy,
+ JS::ObjectOpResult& result) const = 0;
+ virtual bool isExtensible(JSContext* cx, JS::HandleObject proxy,
+ bool* extensible) const = 0;
+
+ /*
+ * These standard internal methods are implemented, as a convenience, so
+ * that ProxyHandler subclasses don't have to provide every single method.
+ *
+ * The base-class implementations work by calling getOwnPropertyDescriptor()
+ * and going up the [[Prototype]] chain if necessary. The algorithm for this
+ * follows what is defined for Ordinary Objects in the ES spec.
+ * They do not follow any standard. When in doubt, override them.
+ */
+ virtual bool has(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ bool* bp) const;
+ virtual bool get(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleValue receiver, JS::HandleId id,
+ JS::MutableHandleValue vp) const;
+ virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::HandleValue v, JS::HandleValue receiver,
+ JS::ObjectOpResult& result) const;
+
+ // Use the ProxyExpando object for private fields, rather than taking the
+ // normal get/set/defineField paths.
+ virtual bool useProxyExpandoObjectForPrivateFields() const { return true; }
+
+ /*
+ * [[Call]] and [[Construct]] are standard internal methods but according
+ * to the spec, they are not present on every object.
+ *
+ * SpiderMonkey never calls a proxy's call()/construct() internal method
+ * unless isCallable()/isConstructor() returns true for that proxy.
+ *
+ * BaseProxyHandler::isCallable()/isConstructor() always return false, and
+ * BaseProxyHandler::call()/construct() crash if called. So if you're
+ * creating a kind of that is never callable, you don't have to override
+ * anything, but otherwise you probably want to override all four.
+ */
+ virtual bool call(JSContext* cx, JS::HandleObject proxy,
+ const JS::CallArgs& args) const;
+ virtual bool construct(JSContext* cx, JS::HandleObject proxy,
+ const JS::CallArgs& args) const;
+
+ /* SpiderMonkey extensions. */
+ virtual bool enumerate(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const;
+ virtual bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ bool* bp) const;
+ virtual bool getOwnEnumerablePropertyKeys(
+ JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const;
+ virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test,
+ JS::NativeImpl impl, const JS::CallArgs& args) const;
+ virtual bool hasInstance(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleValue v, bool* bp) const;
+ virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy,
+ ESClass* cls) const;
+ virtual bool isArray(JSContext* cx, JS::HandleObject proxy,
+ JS::IsArrayAnswer* answer) const;
+ virtual const char* className(JSContext* cx, JS::HandleObject proxy) const;
+ virtual JSString* fun_toString(JSContext* cx, JS::HandleObject proxy,
+ bool isToSource) const;
+ virtual RegExpShared* regexp_toShared(JSContext* cx,
+ JS::HandleObject proxy) const;
+ virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleValue vp) const;
+ virtual void trace(JSTracer* trc, JSObject* proxy) const;
+ virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
+ virtual size_t objectMoved(JSObject* proxy, JSObject* old) const;
+
+ // Allow proxies, wrappers in particular, to specify callability at runtime.
+ // Note: These do not take const JSObject*, but they do in spirit.
+ // We are not prepared to do this, as there's little const correctness
+ // in the external APIs that handle proxies.
+ virtual bool isCallable(JSObject* obj) const;
+ virtual bool isConstructor(JSObject* obj) const;
+
+ virtual bool getElements(JSContext* cx, JS::HandleObject proxy,
+ uint32_t begin, uint32_t end,
+ ElementAdder* adder) const;
+
+ virtual bool isScripted() const { return false; }
+};
+
+extern JS_FRIEND_DATA const JSClass ProxyClass;
+
+inline bool IsProxy(const JSObject* obj) {
+ return JS::GetClass(obj)->isProxy();
+}
+
+namespace detail {
+
+// Proxy slot layout
+// -----------------
+//
+// Every proxy has a ProxyValueArray that contains the following Values:
+//
+// - The expando slot. This is used to hold private fields should they be
+// stamped into a non-forwarding proxy type.
+// - The private slot.
+// - The reserved slots. The number of slots is determined by the proxy's Class.
+//
+// Proxy objects store a pointer to the reserved slots (ProxyReservedSlots*).
+// The ProxyValueArray and the private slot can be accessed using
+// ProxyValueArray::fromReservedSlots or ProxyDataLayout::values.
+//
+// Storing a pointer to ProxyReservedSlots instead of ProxyValueArray has a
+// number of advantages. In particular, it means JS::GetReservedSlot and
+// JS::SetReservedSlot can be used with both proxies and native objects. This
+// works because the ProxyReservedSlots* pointer is stored where native objects
+// store their dynamic slots pointer.
+
+struct ProxyReservedSlots {
+ JS::Value slots[1];
+
+ static inline int offsetOfPrivateSlot();
+
+ static inline int offsetOfSlot(size_t slot) {
+ return offsetof(ProxyReservedSlots, slots[0]) + slot * sizeof(JS::Value);
+ }
+
+ void init(size_t nreserved) {
+ for (size_t i = 0; i < nreserved; i++) {
+ slots[i] = JS::UndefinedValue();
+ }
+ }
+
+ ProxyReservedSlots(const ProxyReservedSlots&) = delete;
+ void operator=(const ProxyReservedSlots&) = delete;
+};
+
+struct ProxyValueArray {
+ JS::Value expandoSlot;
+ JS::Value privateSlot;
+ ProxyReservedSlots reservedSlots;
+
+ void init(size_t nreserved) {
+ expandoSlot = JS::ObjectOrNullValue(nullptr);
+ privateSlot = JS::UndefinedValue();
+ reservedSlots.init(nreserved);
+ }
+
+ static size_t sizeOf(size_t nreserved) {
+ return offsetOfReservedSlots() + nreserved * sizeof(JS::Value);
+ }
+ static MOZ_ALWAYS_INLINE ProxyValueArray* fromReservedSlots(
+ ProxyReservedSlots* slots) {
+ uintptr_t p = reinterpret_cast<uintptr_t>(slots);
+ return reinterpret_cast<ProxyValueArray*>(p - offsetOfReservedSlots());
+ }
+ static size_t offsetOfReservedSlots() {
+ return offsetof(ProxyValueArray, reservedSlots);
+ }
+
+ ProxyValueArray(const ProxyValueArray&) = delete;
+ void operator=(const ProxyValueArray&) = delete;
+};
+
+/* static */ inline int ProxyReservedSlots::offsetOfPrivateSlot() {
+ return -int(ProxyValueArray::offsetOfReservedSlots()) +
+ offsetof(ProxyValueArray, privateSlot);
+}
+
+// All proxies share the same data layout. Following the object's shape and
+// type, the proxy has a ProxyDataLayout structure with a pointer to an array
+// of values and the proxy's handler. This is designed both so that proxies can
+// be easily swapped with other objects (via RemapWrapper) and to mimic the
+// layout of other objects (proxies and other objects have the same size) so
+// that common code can access either type of object.
+//
+// See GetReservedOrProxyPrivateSlot below.
+struct ProxyDataLayout {
+ ProxyReservedSlots* reservedSlots;
+ const BaseProxyHandler* handler;
+
+ MOZ_ALWAYS_INLINE ProxyValueArray* values() const {
+ return ProxyValueArray::fromReservedSlots(reservedSlots);
+ }
+};
+
+const uint32_t ProxyDataOffset = 2 * sizeof(void*);
+
+inline ProxyDataLayout* GetProxyDataLayout(JSObject* obj) {
+ MOZ_ASSERT(IsProxy(obj));
+ return reinterpret_cast<ProxyDataLayout*>(reinterpret_cast<uint8_t*>(obj) +
+ ProxyDataOffset);
+}
+
+inline const ProxyDataLayout* GetProxyDataLayout(const JSObject* obj) {
+ MOZ_ASSERT(IsProxy(obj));
+ return reinterpret_cast<const ProxyDataLayout*>(
+ reinterpret_cast<const uint8_t*>(obj) + ProxyDataOffset);
+}
+
+JS_FRIEND_API void SetValueInProxy(JS::Value* slot, const JS::Value& value);
+
+inline void SetProxyReservedSlotUnchecked(JSObject* obj, size_t n,
+ const JS::Value& extra) {
+ MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(JS::GetClass(obj)));
+
+ JS::Value* vp = &GetProxyDataLayout(obj)->reservedSlots->slots[n];
+
+ // Trigger a barrier before writing the slot.
+ if (vp->isGCThing() || extra.isGCThing()) {
+ SetValueInProxy(vp, extra);
+ } else {
+ *vp = extra;
+ }
+}
+
+} // namespace detail
+
+inline const BaseProxyHandler* GetProxyHandler(const JSObject* obj) {
+ return detail::GetProxyDataLayout(obj)->handler;
+}
+
+inline const JS::Value& GetProxyPrivate(const JSObject* obj) {
+ return detail::GetProxyDataLayout(obj)->values()->privateSlot;
+}
+
+inline const JS::Value& GetProxyExpando(const JSObject* obj) {
+ return detail::GetProxyDataLayout(obj)->values()->expandoSlot;
+}
+
+inline JSObject* GetProxyTargetObject(JSObject* obj) {
+ return GetProxyPrivate(obj).toObjectOrNull();
+}
+
+inline const JS::Value& GetProxyReservedSlot(const JSObject* obj, size_t n) {
+ MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(JS::GetClass(obj)));
+ return detail::GetProxyDataLayout(obj)->reservedSlots->slots[n];
+}
+
+inline void SetProxyHandler(JSObject* obj, const BaseProxyHandler* handler) {
+ detail::GetProxyDataLayout(obj)->handler = handler;
+}
+
+inline void SetProxyReservedSlot(JSObject* obj, size_t n,
+ const JS::Value& extra) {
+#ifdef DEBUG
+ if (gc::detail::ObjectIsMarkedBlack(obj)) {
+ JS::AssertValueIsNotGray(extra);
+ }
+#endif
+
+ detail::SetProxyReservedSlotUnchecked(obj, n, extra);
+}
+
+inline void SetProxyPrivate(JSObject* obj, const JS::Value& value) {
+#ifdef DEBUG
+ if (gc::detail::ObjectIsMarkedBlack(obj)) {
+ JS::AssertValueIsNotGray(value);
+ }
+#endif
+
+ JS::Value* vp = &detail::GetProxyDataLayout(obj)->values()->privateSlot;
+
+ // Trigger a barrier before writing the slot.
+ if (vp->isGCThing() || value.isGCThing()) {
+ detail::SetValueInProxy(vp, value);
+ } else {
+ *vp = value;
+ }
+}
+
+inline bool IsScriptedProxy(const JSObject* obj) {
+ return IsProxy(obj) && GetProxyHandler(obj)->isScripted();
+}
+
+class MOZ_STACK_CLASS ProxyOptions {
+ protected:
+ /* protected constructor for subclass */
+ explicit ProxyOptions(bool lazyProtoArg)
+ : lazyProto_(lazyProtoArg), clasp_(&ProxyClass) {}
+
+ public:
+ ProxyOptions() : ProxyOptions(false) {}
+
+ bool lazyProto() const { return lazyProto_; }
+ ProxyOptions& setLazyProto(bool flag) {
+ lazyProto_ = flag;
+ return *this;
+ }
+
+ const JSClass* clasp() const { return clasp_; }
+ ProxyOptions& setClass(const JSClass* claspArg) {
+ clasp_ = claspArg;
+ return *this;
+ }
+
+ private:
+ bool lazyProto_;
+ const JSClass* clasp_;
+};
+
+JS_FRIEND_API JSObject* NewProxyObject(
+ JSContext* cx, const BaseProxyHandler* handler, JS::HandleValue priv,
+ JSObject* proto, const ProxyOptions& options = ProxyOptions());
+
+JSObject* RenewProxyObject(JSContext* cx, JSObject* obj,
+ BaseProxyHandler* handler, const JS::Value& priv);
+
+class JS_FRIEND_API AutoEnterPolicy {
+ public:
+ typedef BaseProxyHandler::Action Action;
+ AutoEnterPolicy(JSContext* cx, const BaseProxyHandler* handler,
+ JS::HandleObject wrapper, JS::HandleId id, Action act,
+ bool mayThrow)
+#ifdef JS_DEBUG
+ : context(nullptr)
+#endif
+ {
+ allow = handler->hasSecurityPolicy()
+ ? handler->enter(cx, wrapper, id, act, mayThrow, &rv)
+ : true;
+ recordEnter(cx, wrapper, id, act);
+ // We want to throw an exception if all of the following are true:
+ // * The policy disallowed access.
+ // * The policy set rv to false, indicating that we should throw.
+ // * The caller did not instruct us to ignore exceptions.
+ // * The policy did not throw itself.
+ if (!allow && !rv && mayThrow) {
+ reportErrorIfExceptionIsNotPending(cx, id);
+ }
+ }
+
+ virtual ~AutoEnterPolicy() { recordLeave(); }
+ inline bool allowed() { return allow; }
+ inline bool returnValue() {
+ MOZ_ASSERT(!allowed());
+ return rv;
+ }
+
+ protected:
+ // no-op constructor for subclass
+ AutoEnterPolicy()
+#ifdef JS_DEBUG
+ : context(nullptr),
+ enteredAction(BaseProxyHandler::NONE)
+#endif
+ {
+ }
+ void reportErrorIfExceptionIsNotPending(JSContext* cx, JS::HandleId id);
+ bool allow;
+ bool rv;
+
+#ifdef JS_DEBUG
+ JSContext* context;
+ mozilla::Maybe<JS::HandleObject> enteredProxy;
+ mozilla::Maybe<JS::HandleId> enteredId;
+ Action enteredAction;
+
+ // NB: We explicitly don't track the entered action here, because sometimes
+ // set() methods do an implicit get() during their implementation, leading
+ // to spurious assertions.
+ AutoEnterPolicy* prev;
+ void recordEnter(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ Action act);
+ void recordLeave();
+
+ friend JS_FRIEND_API void assertEnteredPolicy(JSContext* cx, JSObject* proxy,
+ jsid id, Action act);
+#else
+ inline void recordEnter(JSContext* cx, JSObject* proxy, jsid id, Action act) {
+ }
+ inline void recordLeave() {}
+#endif
+
+ private:
+ // This operator needs to be deleted explicitly, otherwise Visual C++ will
+ // create it automatically when it is part of the export JS API. In that
+ // case, compile would fail because HandleId is not allowed to be assigned
+ // and consequently instantiation of assign operator of mozilla::Maybe
+ // would fail. See bug 1325351 comment 16. Copy constructor is removed at
+ // the same time for consistency.
+ AutoEnterPolicy(const AutoEnterPolicy&) = delete;
+ AutoEnterPolicy& operator=(const AutoEnterPolicy&) = delete;
+};
+
+#ifdef JS_DEBUG
+class JS_FRIEND_API AutoWaivePolicy : public AutoEnterPolicy {
+ public:
+ AutoWaivePolicy(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ BaseProxyHandler::Action act) {
+ allow = true;
+ recordEnter(cx, proxy, id, act);
+ }
+};
+#else
+class JS_FRIEND_API AutoWaivePolicy {
+ public:
+ AutoWaivePolicy(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ BaseProxyHandler::Action act) {}
+};
+#endif
+
+#ifdef JS_DEBUG
+extern JS_FRIEND_API void assertEnteredPolicy(JSContext* cx, JSObject* obj,
+ jsid id,
+ BaseProxyHandler::Action act);
+#else
+inline void assertEnteredPolicy(JSContext* cx, JSObject* obj, jsid id,
+ BaseProxyHandler::Action act) {}
+#endif
+
+extern JS_FRIEND_DATA const JSClassOps ProxyClassOps;
+extern JS_FRIEND_DATA const js::ClassExtension ProxyClassExtension;
+extern JS_FRIEND_DATA const js::ObjectOps ProxyObjectOps;
+
+template <unsigned Flags>
+constexpr unsigned CheckProxyFlags() {
+ constexpr size_t reservedSlots =
+ (Flags >> JSCLASS_RESERVED_SLOTS_SHIFT) & JSCLASS_RESERVED_SLOTS_MASK;
+
+ // For now assert each Proxy Class has at least 1 reserved slot. This is
+ // not a hard requirement, but helps catch Classes that need an explicit
+ // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523.
+ static_assert(reservedSlots > 0,
+ "Proxy Classes must have at least 1 reserved slot");
+
+ constexpr size_t numSlots =
+ offsetof(js::detail::ProxyValueArray, reservedSlots) / sizeof(JS::Value);
+
+ // ProxyValueArray must fit inline in the object, so assert the number of
+ // slots does not exceed MAX_FIXED_SLOTS.
+ static_assert(numSlots + reservedSlots <= JS::shadow::Object::MAX_FIXED_SLOTS,
+ "ProxyValueArray size must not exceed max JSObject size");
+
+ // Proxies must not have the JSCLASS_SKIP_NURSERY_FINALIZE flag set: they
+ // always have finalizers, and whether they can be nursery allocated is
+ // controlled by the canNurseryAllocate() method on the proxy handler.
+ static_assert(!(Flags & JSCLASS_SKIP_NURSERY_FINALIZE),
+ "Proxies must not use JSCLASS_SKIP_NURSERY_FINALIZE; use "
+ "the canNurseryAllocate() proxy handler method instead.");
+ return Flags;
+}
+
+#define PROXY_CLASS_DEF_WITH_CLASS_SPEC(name, flags, classSpec) \
+ { \
+ name, \
+ JSClass::NON_NATIVE | JSCLASS_IS_PROXY | \
+ JSCLASS_DELAY_METADATA_BUILDER | js::CheckProxyFlags<flags>(), \
+ &js::ProxyClassOps, classSpec, &js::ProxyClassExtension, \
+ &js::ProxyObjectOps \
+ }
+
+#define PROXY_CLASS_DEF(name, flags) \
+ PROXY_CLASS_DEF_WITH_CLASS_SPEC(name, flags, JS_NULL_CLASS_SPEC)
+
+// Converts a proxy into a DeadObjectProxy that will throw exceptions on all
+// access. This will run the proxy's finalizer to perform clean-up before the
+// conversion happens.
+JS_FRIEND_API void NukeNonCCWProxy(JSContext* cx, JS::HandleObject proxy);
+
+// This is a variant of js::NukeNonCCWProxy() for CCWs. It should only be called
+// on CCWs that have been removed from CCW tables.
+JS_FRIEND_API void NukeRemovedCrossCompartmentWrapper(JSContext* cx,
+ JSObject* wrapper);
+
+} /* namespace js */
+
+#endif /* js_Proxy_h */
diff --git a/js/public/Realm.h b/js/public/Realm.h
new file mode 100644
index 0000000000..99be81d634
--- /dev/null
+++ b/js/public/Realm.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Realm_h
+#define js_Realm_h
+
+#include "js/shadow/Realm.h" // JS::shadow::Realm
+
+#include "jspubtd.h"
+#include "js/GCAPI.h"
+#include "js/GCPolicyAPI.h"
+#include "js/TypeDecls.h" // forward-declaration of JS::Realm
+
+namespace js {
+namespace gc {
+JS_PUBLIC_API void TraceRealm(JSTracer* trc, JS::Realm* realm,
+ const char* name);
+JS_PUBLIC_API bool RealmNeedsSweep(JS::Realm* realm);
+} // namespace gc
+} // namespace js
+
+namespace JS {
+
+// Each Realm holds a strong reference to its GlobalObject, and vice versa.
+template <>
+struct GCPolicy<Realm*> : public NonGCPointerPolicy<Realm*> {
+ static void trace(JSTracer* trc, Realm** vp, const char* name) {
+ if (*vp) {
+ ::js::gc::TraceRealm(trc, *vp, name);
+ }
+ }
+ static bool needsSweep(Realm** vp) {
+ return *vp && ::js::gc::RealmNeedsSweep(*vp);
+ }
+};
+
+// Get the current realm, if any. The ECMAScript spec calls this "the current
+// Realm Record".
+extern JS_PUBLIC_API Realm* GetCurrentRealmOrNull(JSContext* cx);
+
+// Return the compartment that contains a given realm.
+inline JS::Compartment* GetCompartmentForRealm(Realm* realm) {
+ return shadow::Realm::get(realm)->compartment();
+}
+
+// Return an object's realm. All objects except cross-compartment wrappers are
+// created in a particular realm, which never changes. Returns null if obj is
+// a cross-compartment wrapper.
+extern JS_PUBLIC_API Realm* GetObjectRealmOrNull(JSObject* obj);
+
+// Get the value of the "private data" internal field of the given Realm.
+// This field is initially null and is set using SetRealmPrivate.
+// It's a pointer to embeddding-specific data that SpiderMonkey never uses.
+extern JS_PUBLIC_API void* GetRealmPrivate(Realm* realm);
+
+// Set the "private data" internal field of the given Realm.
+extern JS_PUBLIC_API void SetRealmPrivate(Realm* realm, void* data);
+
+typedef void (*DestroyRealmCallback)(JSFreeOp* fop, Realm* realm);
+
+// Set the callback SpiderMonkey calls just before garbage-collecting a realm.
+// Embeddings can use this callback to free private data associated with the
+// realm via SetRealmPrivate.
+//
+// By the time this is called, the global object for the realm has already been
+// collected.
+extern JS_PUBLIC_API void SetDestroyRealmCallback(
+ JSContext* cx, DestroyRealmCallback callback);
+
+using RealmNameCallback = void (*)(JSContext* cx, Realm* realm, char* buf,
+ size_t bufsize,
+ const JS::AutoRequireNoGC& nogc);
+
+// Set the callback SpiderMonkey calls to get the name of a realm, for
+// diagnostic output.
+extern JS_PUBLIC_API void SetRealmNameCallback(JSContext* cx,
+ RealmNameCallback callback);
+
+// Get the global object for the given realm. This only returns nullptr during
+// GC, between collecting the global object and destroying the Realm.
+extern JS_PUBLIC_API JSObject* GetRealmGlobalOrNull(Realm* realm);
+
+// Initialize standard JS class constructors, prototypes, and any top-level
+// functions and constants associated with the standard classes (e.g. isNaN
+// for Number).
+extern JS_PUBLIC_API bool InitRealmStandardClasses(JSContext* cx);
+
+/*
+ * Ways to get various per-Realm objects. All the getters declared below operate
+ * on the JSContext's current Realm.
+ */
+
+extern JS_PUBLIC_API JSObject* GetRealmObjectPrototype(JSContext* cx);
+
+extern JS_PUBLIC_API JSObject* GetRealmFunctionPrototype(JSContext* cx);
+
+extern JS_PUBLIC_API JSObject* GetRealmArrayPrototype(JSContext* cx);
+
+extern JS_PUBLIC_API JSObject* GetRealmErrorPrototype(JSContext* cx);
+
+extern JS_PUBLIC_API JSObject* GetRealmIteratorPrototype(JSContext* cx);
+
+// Implements https://tc39.github.io/ecma262/#sec-getfunctionrealm
+// 7.3.22 GetFunctionRealm ( obj )
+//
+// WARNING: may return a realm in a different compartment!
+//
+// Will throw an exception and return nullptr when a security wrapper or revoked
+// proxy is encountered.
+extern JS_PUBLIC_API Realm* GetFunctionRealm(JSContext* cx,
+ HandleObject objArg);
+
+} // namespace JS
+
+#endif // js_Realm_h
diff --git a/js/public/RealmOptions.h b/js/public/RealmOptions.h
new file mode 100644
index 0000000000..df6f927618
--- /dev/null
+++ b/js/public/RealmOptions.h
@@ -0,0 +1,399 @@
+/* -*- 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/. */
+
+/*
+ * Options specified when creating a realm to determine its behavior, immutable
+ * options determining the behavior of an existing realm, and mutable options on
+ * an existing realm that may be changed when desired.
+ */
+
+#ifndef js_RealmOptions_h
+#define js_RealmOptions_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Class.h" // JSTraceOp
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API Compartment;
+class JS_PUBLIC_API Realm;
+class JS_PUBLIC_API Zone;
+
+} // namespace JS
+
+namespace JS {
+
+/**
+ * Specification for which compartment/zone a newly created realm should use.
+ */
+enum class CompartmentSpecifier {
+ // Create a new realm and compartment in the single runtime wide system
+ // zone. The meaning of this zone is left to the embedder.
+ NewCompartmentInSystemZone,
+
+ // Create a new realm and compartment in a particular existing zone.
+ NewCompartmentInExistingZone,
+
+ // Create a new zone/compartment.
+ NewCompartmentAndZone,
+
+ // Create a new realm in an existing compartment.
+ ExistingCompartment,
+
+ // Internal use only. Create the self-hosting compartment.
+ NewCompartmentInSelfHostingZone,
+};
+
+/**
+ * Specification for whether weak refs should be enabled and if so whether the
+ * FinalizationRegistry.cleanupSome method should be present.
+ */
+enum class WeakRefSpecifier {
+ Disabled,
+ EnabledWithCleanupSome,
+ EnabledWithoutCleanupSome
+};
+
+/**
+ * RealmCreationOptions specifies options relevant to creating a new realm, that
+ * are either immutable characteristics of that realm or that are discarded
+ * after the realm has been created.
+ *
+ * Access to these options on an existing realm is read-only: if you need
+ * particular selections, you must make them before you create the realm.
+ */
+class JS_PUBLIC_API RealmCreationOptions {
+ public:
+ RealmCreationOptions() : comp_(nullptr) {}
+
+ JSTraceOp getTrace() const { return traceGlobal_; }
+ RealmCreationOptions& setTrace(JSTraceOp op) {
+ traceGlobal_ = op;
+ return *this;
+ }
+
+ Zone* zone() const {
+ MOZ_ASSERT(compSpec_ == CompartmentSpecifier::NewCompartmentInExistingZone);
+ return zone_;
+ }
+ Compartment* compartment() const {
+ MOZ_ASSERT(compSpec_ == CompartmentSpecifier::ExistingCompartment);
+ return comp_;
+ }
+ CompartmentSpecifier compartmentSpecifier() const { return compSpec_; }
+
+ // Set the compartment/zone to use for the realm. See CompartmentSpecifier
+ // above.
+ RealmCreationOptions& setNewCompartmentInSystemZone();
+ RealmCreationOptions& setNewCompartmentInExistingZone(JSObject* obj);
+ RealmCreationOptions& setNewCompartmentAndZone();
+ RealmCreationOptions& setExistingCompartment(JSObject* obj);
+ RealmCreationOptions& setExistingCompartment(Compartment* compartment);
+ RealmCreationOptions& setNewCompartmentInSelfHostingZone();
+
+ // Certain compartments are implementation details of the embedding, and
+ // references to them should never leak out to script. This flag causes this
+ // realm to skip firing onNewGlobalObject and makes addDebuggee a no-op for
+ // this global.
+ //
+ // Debugger visibility is per-compartment, not per-realm (it's only practical
+ // to enforce visibility on compartment boundaries), so if a realm is being
+ // created in an extant compartment, its requested visibility must match that
+ // of the compartment.
+ bool invisibleToDebugger() const { return invisibleToDebugger_; }
+ RealmCreationOptions& setInvisibleToDebugger(bool flag) {
+ invisibleToDebugger_ = flag;
+ return *this;
+ }
+
+ // Realms used for off-thread compilation have their contents merged into a
+ // target realm when the compilation is finished. This is only allowed if
+ // this flag is set. The invisibleToDebugger flag must also be set for such
+ // realms.
+ bool mergeable() const { return mergeable_; }
+ RealmCreationOptions& setMergeable(bool flag) {
+ mergeable_ = flag;
+ return *this;
+ }
+
+ // Determines whether this realm should preserve JIT code on non-shrinking
+ // GCs.
+ bool preserveJitCode() const { return preserveJitCode_; }
+ RealmCreationOptions& setPreserveJitCode(bool flag) {
+ preserveJitCode_ = flag;
+ return *this;
+ }
+
+ // Determines whether 1) the global Atomic property is defined and atomic
+ // operations are supported, and 2) whether shared-memory operations are
+ // supported.
+ bool getSharedMemoryAndAtomicsEnabled() const;
+ RealmCreationOptions& setSharedMemoryAndAtomicsEnabled(bool flag);
+
+ // Determines (if getSharedMemoryAndAtomicsEnabled() is true) whether the
+ // global SharedArrayBuffer property is defined. If the property is not
+ // defined, shared array buffer functionality can only be invoked if the
+ // host/embedding specifically acts to expose it.
+ //
+ // This option defaults to true: embeddings unable to tolerate a global
+ // SharedAraryBuffer property must opt out of it.
+ bool defineSharedArrayBufferConstructor() const {
+ return defineSharedArrayBufferConstructor_;
+ }
+ RealmCreationOptions& setDefineSharedArrayBufferConstructor(bool flag) {
+ defineSharedArrayBufferConstructor_ = flag;
+ return *this;
+ }
+
+ // Structured clone operations support the cloning of shared memory objects
+ // (SharedArrayBuffer or or a shared WASM Memory object) *optionally* -- at
+ // the discretion of the embedder code that performs the cloning. When a
+ // structured clone operation encounters a shared memory object and cloning
+ // shared memory objects has not been enabled, the clone fails and an
+ // error is thrown.
+ //
+ // In the web embedding context, shared memory object cloning is disabled
+ // either because
+ //
+ // 1) *no* way of supporting it is available (because the
+ // Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy HTTP
+ // headers are not respected to force the page into its own process), or
+ // 2) the aforementioned HTTP headers don't specify that the page should be
+ // opened in its own process.
+ //
+ // These two scenarios demand different error messages, and this option can be
+ // used to specify which scenario is in play.
+ //
+ // In the former case, if COOP/COEP support is not enabled, set this option to
+ // false. (This is the default.)
+ //
+ // In the latter case, if COOP/COEP weren't used to force this page into its
+ // own process, set this option to true.
+ //
+ // (Embeddings that are not the web and do not wish to support structured
+ // cloning of shared memory objects will get a "bad" web-centric error message
+ // no matter what. At present, SpiderMonkey does not offer a way for such
+ // embeddings to use an embedding-specific error message.)
+ bool getCoopAndCoepEnabled() const;
+ RealmCreationOptions& setCoopAndCoepEnabled(bool flag);
+
+ bool getStreamsEnabled() const { return streams_; }
+ RealmCreationOptions& setStreamsEnabled(bool flag) {
+ streams_ = flag;
+ return *this;
+ }
+
+ bool getReadableByteStreamsEnabled() const { return readableByteStreams_; }
+ RealmCreationOptions& setReadableByteStreamsEnabled(bool flag) {
+ readableByteStreams_ = flag;
+ return *this;
+ }
+
+ bool getBYOBStreamReadersEnabled() const { return byobStreamReaders_; }
+ RealmCreationOptions& setBYOBStreamReadersEnabled(bool enabled) {
+ byobStreamReaders_ = enabled;
+ return *this;
+ }
+
+ bool getWritableStreamsEnabled() const { return writableStreams_; }
+ RealmCreationOptions& setWritableStreamsEnabled(bool enabled) {
+ writableStreams_ = enabled;
+ return *this;
+ }
+
+ bool getReadableStreamPipeToEnabled() const { return readableStreamPipeTo_; }
+ RealmCreationOptions& setReadableStreamPipeToEnabled(bool enabled) {
+ readableStreamPipeTo_ = enabled;
+ return *this;
+ }
+
+ WeakRefSpecifier getWeakRefsEnabled() const { return weakRefs_; }
+ RealmCreationOptions& setWeakRefsEnabled(WeakRefSpecifier spec) {
+ weakRefs_ = spec;
+ return *this;
+ }
+
+ bool getToSourceEnabled() const { return toSource_; }
+ RealmCreationOptions& setToSourceEnabled(bool flag) {
+ toSource_ = flag;
+ return *this;
+ }
+
+ bool getPropertyErrorMessageFixEnabled() const {
+ return propertyErrorMessageFix_;
+ }
+ RealmCreationOptions& setPropertyErrorMessageFixEnabled(bool flag) {
+ propertyErrorMessageFix_ = flag;
+ return *this;
+ }
+
+ bool getIteratorHelpersEnabled() const { return iteratorHelpers_; }
+ RealmCreationOptions& setIteratorHelpersEnabled(bool flag) {
+ iteratorHelpers_ = flag;
+ return *this;
+ }
+
+ // This flag doesn't affect JS engine behavior. It is used by Gecko to
+ // mark whether content windows and workers are "Secure Context"s. See
+ // https://w3c.github.io/webappsec-secure-contexts/
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1162772#c34
+ bool secureContext() const { return secureContext_; }
+ RealmCreationOptions& setSecureContext(bool flag) {
+ secureContext_ = flag;
+ return *this;
+ }
+
+ uint64_t profilerRealmID() const { return profilerRealmID_; }
+ RealmCreationOptions& setProfilerRealmID(uint64_t id) {
+ profilerRealmID_ = id;
+ return *this;
+ }
+
+ private:
+ JSTraceOp traceGlobal_ = nullptr;
+ CompartmentSpecifier compSpec_ = CompartmentSpecifier::NewCompartmentAndZone;
+ union {
+ Compartment* comp_;
+ Zone* zone_;
+ };
+ uint64_t profilerRealmID_ = 0;
+ WeakRefSpecifier weakRefs_ = WeakRefSpecifier::Disabled;
+ bool invisibleToDebugger_ = false;
+ bool mergeable_ = false;
+ bool preserveJitCode_ = false;
+ bool sharedMemoryAndAtomics_ = false;
+ bool defineSharedArrayBufferConstructor_ = true;
+ bool coopAndCoep_ = false;
+ bool streams_ = false;
+ bool readableByteStreams_ = false;
+ bool byobStreamReaders_ = false;
+ bool writableStreams_ = false;
+ bool readableStreamPipeTo_ = false;
+ bool toSource_ = false;
+ bool propertyErrorMessageFix_ = false;
+ bool iteratorHelpers_ = false;
+ bool secureContext_ = false;
+};
+
+/**
+ * RealmBehaviors specifies behaviors of a realm that can be changed after the
+ * realm's been created.
+ */
+class JS_PUBLIC_API RealmBehaviors {
+ public:
+ RealmBehaviors() = default;
+
+ // For certain globals, we know enough about the code that will run in them
+ // that we can discard script source entirely.
+ bool discardSource() const { return discardSource_; }
+ RealmBehaviors& setDiscardSource(bool flag) {
+ discardSource_ = flag;
+ return *this;
+ }
+
+ bool disableLazyParsing() const { return disableLazyParsing_; }
+ RealmBehaviors& setDisableLazyParsing(bool flag) {
+ disableLazyParsing_ = flag;
+ return *this;
+ }
+
+ bool clampAndJitterTime() const { return clampAndJitterTime_; }
+ RealmBehaviors& setClampAndJitterTime(bool flag) {
+ clampAndJitterTime_ = flag;
+ return *this;
+ }
+
+ class Override {
+ public:
+ Override() : mode_(Default) {}
+
+ bool get(bool defaultValue) const {
+ if (mode_ == Default) {
+ return defaultValue;
+ }
+ return mode_ == ForceTrue;
+ }
+
+ void set(bool overrideValue) {
+ mode_ = overrideValue ? ForceTrue : ForceFalse;
+ }
+
+ void reset() { mode_ = Default; }
+
+ private:
+ enum Mode { Default, ForceTrue, ForceFalse };
+
+ Mode mode_;
+ };
+
+ // A Realm can stop being "live" in all the ways that matter before its global
+ // is actually GCed. Consumers that tear down parts of a Realm or its global
+ // before that point should set isNonLive accordingly.
+ bool isNonLive() const { return isNonLive_; }
+ RealmBehaviors& setNonLive() {
+ isNonLive_ = true;
+ return *this;
+ }
+
+ private:
+ bool discardSource_ = false;
+ bool disableLazyParsing_ = false;
+ bool clampAndJitterTime_ = true;
+ bool isNonLive_ = false;
+};
+
+/**
+ * RealmOptions specifies realm characteristics: both those that can't be
+ * changed on a realm once it's been created (RealmCreationOptions), and those
+ * that can be changed on an existing realm (RealmBehaviors).
+ */
+class JS_PUBLIC_API RealmOptions {
+ public:
+ explicit RealmOptions() : creationOptions_(), behaviors_() {}
+
+ RealmOptions(const RealmCreationOptions& realmCreation,
+ const RealmBehaviors& realmBehaviors)
+ : creationOptions_(realmCreation), behaviors_(realmBehaviors) {}
+
+ // RealmCreationOptions specify fundamental realm characteristics that must
+ // be specified when the realm is created, that can't be changed after the
+ // realm is created.
+ RealmCreationOptions& creationOptions() { return creationOptions_; }
+ const RealmCreationOptions& creationOptions() const {
+ return creationOptions_;
+ }
+
+ // RealmBehaviors specify realm characteristics that can be changed after
+ // the realm is created.
+ RealmBehaviors& behaviors() { return behaviors_; }
+ const RealmBehaviors& behaviors() const { return behaviors_; }
+
+ private:
+ RealmCreationOptions creationOptions_;
+ RealmBehaviors behaviors_;
+};
+
+extern JS_PUBLIC_API const RealmCreationOptions& RealmCreationOptionsRef(
+ Realm* realm);
+
+extern JS_PUBLIC_API const RealmCreationOptions& RealmCreationOptionsRef(
+ JSContext* cx);
+
+extern JS_PUBLIC_API const RealmBehaviors& RealmBehaviorsRef(Realm* realm);
+
+extern JS_PUBLIC_API const RealmBehaviors& RealmBehaviorsRef(JSContext* cx);
+
+extern JS_PUBLIC_API void SetRealmNonLive(Realm* realm);
+
+} // namespace JS
+
+#endif // js_RealmOptions_h
diff --git a/js/public/RefCounted.h b/js/public/RefCounted.h
new file mode 100644
index 0000000000..86fe7eeb13
--- /dev/null
+++ b/js/public/RefCounted.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_RefCounted_h
+#define js_RefCounted_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/RefCountType.h"
+
+#include "js/Utility.h"
+
+// These types implement the same interface as mozilla::(Atomic)RefCounted and
+// must be used instead of mozilla::(Atomic)RefCounted for everything in
+// SpiderMonkey. There are two reasons:
+// - Release() needs to call js_delete, not delete
+// - SpiderMonkey does not have MOZILLA_INTERNAL_API defined which can lead
+// to ODR violations that show up as spurious leak reports when ref-counted
+// types are allocated by SpiderMonkey and released by Gecko (or vice versa).
+
+namespace js {
+
+template <typename T>
+class RefCounted {
+ static const MozRefCountType DEAD = 0xffffdead;
+
+ protected:
+ RefCounted() : mRefCnt(0) {}
+ ~RefCounted() { MOZ_ASSERT(mRefCnt == DEAD); }
+
+ public:
+ void AddRef() const {
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0);
+ ++mRefCnt;
+ }
+
+ void Release() const {
+ MOZ_ASSERT(int32_t(mRefCnt) > 0);
+ MozRefCountType cnt = --mRefCnt;
+ if (0 == cnt) {
+#ifdef DEBUG
+ mRefCnt = DEAD;
+#endif
+ js_delete(const_cast<T*>(static_cast<const T*>(this)));
+ }
+ }
+
+ private:
+ mutable MozRefCountType mRefCnt;
+};
+
+template <typename T>
+class AtomicRefCounted {
+ // On 64-bit systems, if the refcount type is small (say, 32 bits), there's
+ // a risk that it could overflow. So require it to be large enough.
+
+ static_assert(sizeof(MozRefCountType) == sizeof(uintptr_t),
+ "You're at risk for ref count overflow.");
+
+ static const MozRefCountType DEAD = ~MozRefCountType(0xffff) | 0xdead;
+
+ protected:
+ AtomicRefCounted() = default;
+ ~AtomicRefCounted() { MOZ_ASSERT(mRefCnt == DEAD); }
+
+ public:
+ void AddRef() const {
+ ++mRefCnt;
+ MOZ_ASSERT(mRefCnt != DEAD);
+ }
+
+ void Release() const {
+ MOZ_ASSERT(mRefCnt != 0);
+ MozRefCountType cnt = --mRefCnt;
+ if (0 == cnt) {
+#ifdef DEBUG
+ mRefCnt = DEAD;
+#endif
+ js_delete(const_cast<T*>(static_cast<const T*>(this)));
+ }
+ }
+
+ private:
+ mutable mozilla::Atomic<MozRefCountType> mRefCnt{0};
+};
+
+} // namespace js
+
+#endif /* js_RefCounted_h */
diff --git a/js/public/RegExp.h b/js/public/RegExp.h
new file mode 100644
index 0000000000..8e2e8d30a1
--- /dev/null
+++ b/js/public/RegExp.h
@@ -0,0 +1,95 @@
+/* -*- 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. */
+
+#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 JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+
+/**
+ * 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,
+ 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,
+ 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,
+ Handle<JSObject*> reobj,
+ char16_t* chars, size_t length,
+ size_t* indexp, bool test,
+ MutableHandle<Value> rval);
+
+/* RegExp interface for clients without a global object. */
+
+extern JS_PUBLIC_API bool ExecuteRegExpNoStatics(
+ JSContext* cx, Handle<JSObject*> reobj, const char16_t* chars,
+ size_t length, size_t* indexp, bool test, MutableHandle<Value> rval);
+
+/**
+ * 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);
+
+/**
+ * Given a RegExp object (or a wrapper around one), return the set of all
+ * JS::RegExpFlag::* for it.
+ */
+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);
+/**
+ * Check whether the given source is a valid regexp. If the regexp parses
+ * successfully, returns true and sets |error| to undefined. If the regexp
+ * has a syntax error, returns true, sets |error| to that error object, and
+ * clears the exception. Returns false on OOM or over-recursion.
+ */
+extern JS_PUBLIC_API bool CheckRegExpSyntax(JSContext* cx,
+ const char16_t* chars,
+ size_t length, RegExpFlags flags,
+ MutableHandle<Value> error);
+
+} // namespace JS
+
+#endif // js_RegExp_h
diff --git a/js/public/RegExpFlags.h b/js/public/RegExpFlags.h
new file mode 100644
index 0000000000..d75b5f6547
--- /dev/null
+++ b/js/public/RegExpFlags.h
@@ -0,0 +1,152 @@
+/* -*- 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 flags. */
+
+#ifndef js_RegExpFlags_h
+#define js_RegExpFlags_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_IMPLICIT
+
+#include <stdint.h> // uint8_t
+
+namespace JS {
+
+/**
+ * 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, /s, /u, /y.
+ */
+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:
+ /**
+ * Act globally and find *all* matches (rather than stopping after just the
+ * first one), i.e. /g.
+ */
+ static constexpr uint8_t Global = 0b00'0010;
+
+ /**
+ * Interpret regular expression source text case-insensitively by folding
+ * uppercase letters to lowercase, i.e. /i.
+ */
+ static constexpr uint8_t IgnoreCase = 0b00'0001;
+
+ /** Treat ^ and $ as begin and end of line, i.e. /m. */
+ static constexpr uint8_t Multiline = 0b00'0100;
+
+ /* Allow . to match newline characters, i.e. /s. */
+ static constexpr uint8_t DotAll = 0b10'0000;
+
+ /** Use Unicode semantics, i.e. /u. */
+ static constexpr uint8_t Unicode = 0b01'0000;
+
+ /** Only match starting from <regular expression>.lastIndex, i.e. /y. */
+ static constexpr uint8_t Sticky = 0b00'1000;
+
+ /** No regular expression flags. */
+ static constexpr uint8_t NoFlags = 0b00'0000;
+
+ /** All regular expression flags. */
+ static constexpr uint8_t AllFlags = 0b11'1111;
+};
+
+/**
+ * A collection of regular expression flags. Individual flag values may be
+ * combined into a collection using bitwise operators.
+ */
+class RegExpFlags {
+ public:
+ using Flag = uint8_t;
+
+ private:
+ Flag flags_;
+
+ public:
+ RegExpFlags() = default;
+
+ MOZ_IMPLICIT RegExpFlags(Flag flags) : flags_(flags) {
+ MOZ_ASSERT((flags & RegExpFlag::AllFlags) == flags,
+ "flags must not contain unrecognized flags");
+ }
+
+ RegExpFlags(const RegExpFlags&) = default;
+
+ bool operator==(const RegExpFlags& other) const {
+ return flags_ == other.flags_;
+ }
+
+ bool operator!=(const RegExpFlags& other) const { return !(*this == other); }
+
+ RegExpFlags& operator&=(const RegExpFlags& rhs) {
+ flags_ &= rhs.flags_;
+ return *this;
+ }
+
+ RegExpFlags& operator|=(const RegExpFlags& rhs) {
+ flags_ |= rhs.flags_;
+ return *this;
+ }
+
+ RegExpFlags operator&(Flag flag) const { return RegExpFlags(flags_ & flag); }
+
+ RegExpFlags operator|(Flag flag) const { return RegExpFlags(flags_ | flag); }
+
+ RegExpFlags operator^(Flag flag) const { return RegExpFlags(flags_ ^ flag); }
+
+ RegExpFlags operator~() const {
+ return RegExpFlags(~flags_ & RegExpFlag::AllFlags);
+ }
+
+ bool global() const { return flags_ & RegExpFlag::Global; }
+ bool ignoreCase() const { return flags_ & RegExpFlag::IgnoreCase; }
+ bool multiline() const { return flags_ & RegExpFlag::Multiline; }
+ bool dotAll() const { return flags_ & RegExpFlag::DotAll; }
+ 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_; }
+};
+
+inline RegExpFlags& operator&=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+ flags = flags & flag;
+ return flags;
+}
+
+inline RegExpFlags& operator|=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+ flags = flags | flag;
+ return flags;
+}
+
+inline RegExpFlags& operator^=(RegExpFlags& flags, RegExpFlags::Flag flag) {
+ flags = flags ^ flag;
+ return flags;
+}
+
+inline RegExpFlags operator&(const RegExpFlags& lhs, const RegExpFlags& rhs) {
+ RegExpFlags result = lhs;
+ result &= rhs;
+ return lhs;
+}
+
+inline RegExpFlags operator|(const RegExpFlags& lhs, const RegExpFlags& rhs) {
+ RegExpFlags result = lhs;
+ result |= rhs;
+ return result;
+}
+
+} // namespace JS
+
+#endif // js_RegExpFlags_h
diff --git a/js/public/RequiredDefines.h b/js/public/RequiredDefines.h
new file mode 100644
index 0000000000..3ab8fd189e
--- /dev/null
+++ b/js/public/RequiredDefines.h
@@ -0,0 +1,31 @@
+/* -*- 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/. */
+
+/*
+ * Various #defines required to build SpiderMonkey. Embedders should add this
+ * file to the start of the command line via -include or a similar mechanism,
+ * or SpiderMonkey public headers may not work correctly.
+ */
+
+#ifndef js_RequiredDefines_h
+#define js_RequiredDefines_h
+
+/*
+ * The c99 defining the limit macros (UINT32_MAX for example), says:
+ *
+ * C++ implementations should define these macros only when
+ * __STDC_LIMIT_MACROS is defined before <stdint.h> is included.
+ *
+ * The same also occurs with __STDC_CONSTANT_MACROS for the constant macros
+ * (INT8_C for example) used to specify a literal constant of the proper type,
+ * and with __STDC_FORMAT_MACROS for the format macros (PRId32 for example) used
+ * with the fprintf function family.
+ */
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+#define __STDC_FORMAT_MACROS
+
+#endif /* js_RequiredDefines_h */
diff --git a/js/public/Result.h b/js/public/Result.h
new file mode 100644
index 0000000000..cfd68a927c
--- /dev/null
+++ b/js/public/Result.h
@@ -0,0 +1,279 @@
+/* -*- 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/. */
+
+/*
+ * [SMDOC] JS::Result
+ *
+ * `Result` is used as the return type of many SpiderMonkey functions that
+ * can either succeed or fail. See "/mfbt/Result.h".
+ *
+ *
+ * ## Which return type to use
+ *
+ * `Result` is for return values. Obviously, if you're writing a function that
+ * can't fail, don't use Result. Otherwise:
+ *
+ * JS::Result<> - function can fail, doesn't return anything on success
+ * (defaults to `JS::Result<JS::Ok, JS::Error>`)
+ * JS::Result<JS::Ok, JS::OOM> - like JS::Result<>, but fails only on OOM
+ *
+ * JS::Result<Data> - function can fail, returns Data on success
+ * JS::Result<Data, JS::OOM> - returns Data, fails only on OOM
+ *
+ * mozilla::GenericErrorResult<JS::Error> - always fails
+ *
+ * That last type is like a Result with no success type. It's used for
+ * functions like `js::ReportNotFunction` that always return an error
+ * result. `GenericErrorResult<E>` implicitly converts to `Result<V, E>`,
+ * regardless of V.
+ *
+ *
+ * ## Checking Results when your return type is Result
+ *
+ * When you call a function that returns a `Result`, use the `MOZ_TRY` macro to
+ * check for errors:
+ *
+ * MOZ_TRY(DefenestrateObject(cx, obj));
+ *
+ * If `DefenestrateObject` returns a success result, `MOZ_TRY` is done, and
+ * control flows to the next statement. If `DefenestrateObject` returns an
+ * error result, `MOZ_TRY` will immediately return it, propagating the error to
+ * your caller. It's kind of like exceptions, but more explicit -- you can see
+ * in the code exactly where errors can happen.
+ *
+ * You can do a tail call instead of using `MOZ_TRY`:
+ *
+ * return DefenestrateObject(cx, obj);
+ *
+ * Indicate success with `return Ok();`.
+ *
+ * If the function returns a value on success, use `MOZ_TRY_VAR` to get it:
+ *
+ * RootedValue thrug(cx);
+ * MOZ_TRY_VAR(thrug, GetObjectThrug(cx, obj));
+ *
+ * This behaves the same as `MOZ_TRY` on error. On success, the success
+ * value of `GetObjectThrug(cx, obj)` is assigned to the variable `thrug`.
+ *
+ *
+ * ## Checking Results when your return type is not Result
+ *
+ * This header defines alternatives to MOZ_TRY and MOZ_TRY_VAR for when you
+ * need to call a `Result` function from a function that uses false or nullptr
+ * to indicate errors:
+ *
+ * JS_TRY_OR_RETURN_FALSE(cx, DefenestrateObject(cx, obj));
+ * JS_TRY_VAR_OR_RETURN_FALSE(cx, v, GetObjectThrug(cx, obj));
+ *
+ * JS_TRY_OR_RETURN_NULL(cx, DefenestrateObject(cx, obj));
+ * JS_TRY_VAR_OR_RETURN_NULL(cx, v, GetObjectThrug(cx, obj));
+ *
+ * When TRY is not what you want, because you need to do some cleanup or
+ * recovery on error, use this idiom:
+ *
+ * if (!cx->resultToBool(expr_that_is_a_Result)) {
+ * ... your recovery code here ...
+ * }
+ *
+ * In place of a tail call, you can use one of these methods:
+ *
+ * return cx->resultToBool(expr); // false on error
+ * return cx->resultToPtr(expr); // null on error
+ *
+ * Once we are using `Result` everywhere, including in public APIs, all of
+ * these will go away.
+ *
+ *
+ * ## GC safety
+ *
+ * When a function returns a `JS::Result<JSObject*>`, it is the program's
+ * responsibility to check for errors and root the object before continuing:
+ *
+ * RootedObject wrapper(cx);
+ * MOZ_TRY_VAR(wrapper, Enwrapify(cx, thing));
+ *
+ * This is ideal. On error, there is no object to root; on success, the
+ * assignment to wrapper roots it. GC safety is ensured.
+ *
+ * `Result` has methods .isOk(), .isErr(), .unwrap(), and .unwrapErr(), but if
+ * you're actually using them, it's possible to create a GC hazard. The static
+ * analysis will catch it if so, but that's hardly convenient. So try to stick
+ * to the idioms shown above.
+ *
+ *
+ * ## Future directions
+ *
+ * At present, JS::Error and JS::OOM are empty structs. The plan is to make them
+ * GC things that contain the actual error information (including the exception
+ * value and a saved stack).
+ *
+ * The long-term plan is to remove JS_IsExceptionPending and
+ * JS_GetPendingException in favor of JS::Error. Exception state will no longer
+ * exist.
+ */
+
+#ifndef js_Result_h
+#define js_Result_h
+
+#include "mozilla/Result.h"
+
+/**
+ * Evaluate the boolean expression expr. If it's true, do nothing.
+ * If it's false, return an error result.
+ */
+#define JS_TRY_BOOL_TO_RESULT(cx, expr) \
+ do { \
+ bool ok_ = (expr); \
+ if (!ok_) return (cx)->boolToResult(ok_); \
+ } while (0)
+
+/**
+ * JS_TRY_OR_RETURN_FALSE(cx, expr) runs expr to compute a Result value.
+ * On success, nothing happens; on error, it returns false immediately.
+ *
+ * Implementation note: this involves cx because this may eventually
+ * do the work of setting a pending exception or reporting OOM.
+ */
+#define JS_TRY_OR_RETURN_FALSE(cx, expr) \
+ do { \
+ auto tmpResult_ = (expr); \
+ if (tmpResult_.isErr()) return (cx)->resultToBool(tmpResult_); \
+ } while (0)
+
+/**
+ * Like JS_TRY_OR_RETURN_FALSE, but returning nullptr on error,
+ * rather than false.
+ */
+#define JS_TRY_OR_RETURN_NULL(cx, expr) \
+ do { \
+ auto tmpResult_ = (expr); \
+ if (tmpResult_.isErr()) { \
+ MOZ_ALWAYS_FALSE((cx)->resultToBool(tmpResult_)); \
+ return nullptr; \
+ } \
+ } while (0)
+
+#define JS_TRY_VAR_OR_RETURN_FALSE(cx, target, expr) \
+ do { \
+ auto tmpResult_ = (expr); \
+ if (tmpResult_.isErr()) return (cx)->resultToBool(tmpResult_); \
+ (target) = tmpResult_.unwrap(); \
+ } while (0)
+
+#define JS_TRY_VAR_OR_RETURN_NULL(cx, target, expr) \
+ do { \
+ auto tmpResult_ = (expr); \
+ if (tmpResult_.isErr()) { \
+ MOZ_ALWAYS_FALSE((cx)->resultToBool(tmpResult_)); \
+ return nullptr; \
+ } \
+ (target) = tmpResult_.unwrap(); \
+ } while (0)
+
+namespace JS {
+
+using mozilla::Ok;
+
+template <typename T>
+struct UnusedZero;
+
+/**
+ * Type representing a JS error or exception. At the moment this only
+ * "represents" an error in a rather abstract way.
+ */
+struct Error {
+ // Since we claim UnusedZero<Error>::value and HasFreeLSB<Error>::value ==
+ // true below, we must only use positive even enum values.
+ enum class ErrorKind : uintptr_t { Unspecified = 2, OOM = 4 };
+
+ const ErrorKind kind = ErrorKind::Unspecified;
+
+ Error() = default;
+
+ protected:
+ friend struct UnusedZero<Error>;
+
+ constexpr MOZ_IMPLICIT Error(ErrorKind aKind) : kind(aKind) {}
+};
+
+struct OOM : Error {
+ constexpr OOM() : Error(ErrorKind::OOM) {}
+
+ protected:
+ friend struct UnusedZero<OOM>;
+
+ using Error::Error;
+};
+
+template <typename T>
+struct UnusedZero {
+ using StorageType = std::underlying_type_t<Error::ErrorKind>;
+
+ static constexpr bool value = true;
+ static constexpr StorageType nullValue = 0;
+
+ static constexpr void AssertValid(StorageType aValue) {}
+ static constexpr T Inspect(const StorageType& aValue) {
+ return static_cast<Error::ErrorKind>(aValue);
+ }
+ static constexpr T Unwrap(StorageType aValue) {
+ return static_cast<Error::ErrorKind>(aValue);
+ }
+ static constexpr StorageType Store(T aValue) {
+ return static_cast<StorageType>(aValue.kind);
+ }
+};
+
+} // namespace JS
+
+namespace mozilla::detail {
+
+template <>
+struct UnusedZero<JS::Error> : JS::UnusedZero<JS::Error> {};
+
+template <>
+struct UnusedZero<JS::OOM> : JS::UnusedZero<JS::OOM> {};
+
+template <>
+struct HasFreeLSB<JS::Error> {
+ static const bool value = true;
+};
+
+template <>
+struct HasFreeLSB<JS::OOM> {
+ static const bool value = true;
+};
+} // namespace mozilla::detail
+
+namespace JS {
+
+/**
+ * `Result` is intended to be the return type of JSAPI calls and internal
+ * functions that can run JS code or allocate memory from the JS GC heap. Such
+ * functions can:
+ *
+ * - succeed, possibly returning a value;
+ *
+ * - fail with a JS exception (out-of-memory falls in this category); or
+ *
+ * - fail because JS execution was terminated, which occurs when e.g. a
+ * user kills a script from the "slow script" UI. This is also how we
+ * unwind the stack when the debugger forces the current function to
+ * return. JS `catch` blocks can't catch this kind of failure,
+ * and JS `finally` blocks don't execute.
+ */
+template <typename V = Ok, typename E = Error>
+using Result = mozilla::Result<V, E>;
+
+static_assert(sizeof(Result<>) == sizeof(uintptr_t),
+ "Result<> should be pointer-sized");
+
+static_assert(sizeof(Result<int*, Error>) == sizeof(uintptr_t),
+ "Result<V*, Error> should be pointer-sized");
+
+} // namespace JS
+
+#endif // js_Result_h
diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h
new file mode 100644
index 0000000000..fe08687cfd
--- /dev/null
+++ b/js/public/RootingAPI.h
@@ -0,0 +1,1545 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_RootingAPI_h
+#define js_RootingAPI_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Maybe.h"
+
+#include <type_traits>
+#include <utility>
+
+#include "jspubtd.h"
+
+#include "js/ComparisonOperators.h" // JS::detail::DefineComparisonOps
+#include "js/GCAnnotations.h"
+#include "js/GCPolicyAPI.h"
+#include "js/GCTypeMacros.h" // JS_FOR_EACH_PUBLIC_{,TAGGED_}GC_POINTER_TYPE
+#include "js/HashTable.h"
+#include "js/HeapAPI.h"
+#include "js/ProfilingStack.h"
+#include "js/Realm.h"
+#include "js/TypeDecls.h"
+#include "js/UniquePtr.h"
+#include "js/Utility.h"
+
+/*
+ * [SMDOC] Stack Rooting
+ *
+ * Moving GC Stack Rooting
+ *
+ * A moving GC may change the physical location of GC allocated things, even
+ * when they are rooted, updating all pointers to the thing to refer to its new
+ * location. The GC must therefore know about all live pointers to a thing,
+ * not just one of them, in order to behave correctly.
+ *
+ * The |Rooted| and |Handle| classes below are used to root stack locations
+ * whose value may be held live across a call that can trigger GC. For a
+ * code fragment such as:
+ *
+ * JSObject* obj = NewObject(cx);
+ * DoSomething(cx);
+ * ... = obj->lastProperty();
+ *
+ * If |DoSomething()| can trigger a GC, the stack location of |obj| must be
+ * rooted to ensure that the GC does not move the JSObject referred to by
+ * |obj| without updating |obj|'s location itself. This rooting must happen
+ * regardless of whether there are other roots which ensure that the object
+ * itself will not be collected.
+ *
+ * If |DoSomething()| cannot trigger a GC, and the same holds for all other
+ * calls made between |obj|'s definitions and its last uses, then no rooting
+ * is required.
+ *
+ * SpiderMonkey can trigger a GC at almost any time and in ways that are not
+ * always clear. For example, the following innocuous-looking actions can
+ * cause a GC: allocation of any new GC thing; JSObject::hasProperty;
+ * JS_ReportError and friends; and ToNumber, among many others. The following
+ * dangerous-looking actions cannot trigger a GC: js_malloc, cx->malloc_,
+ * rt->malloc_, and friends and JS_ReportOutOfMemory.
+ *
+ * The following family of three classes will exactly root a stack location.
+ * Incorrect usage of these classes will result in a compile error in almost
+ * all cases. Therefore, it is very hard to be incorrectly rooted if you use
+ * these classes exclusively. These classes are all templated on the type T of
+ * the value being rooted.
+ *
+ * - Rooted<T> declares a variable of type T, whose value is always rooted.
+ * Rooted<T> may be automatically coerced to a Handle<T>, below. Rooted<T>
+ * should be used whenever a local variable's value may be held live across a
+ * call which can trigger a GC.
+ *
+ * - Handle<T> is a const reference to a Rooted<T>. Functions which take GC
+ * things or values as arguments and need to root those arguments should
+ * generally use handles for those arguments and avoid any explicit rooting.
+ * This has two benefits. First, when several such functions call each other
+ * then redundant rooting of multiple copies of the GC thing can be avoided.
+ * Second, if the caller does not pass a rooted value a compile error will be
+ * generated, which is quicker and easier to fix than when relying on a
+ * separate rooting analysis.
+ *
+ * - MutableHandle<T> is a non-const reference to Rooted<T>. It is used in the
+ * same way as Handle<T> and includes a |set(const T& v)| method to allow
+ * updating the value of the referenced Rooted<T>. A MutableHandle<T> can be
+ * created with an implicit cast from a Rooted<T>*.
+ *
+ * In some cases the small performance overhead of exact rooting (measured to
+ * be a few nanoseconds on desktop) is too much. In these cases, try the
+ * following:
+ *
+ * - Move all Rooted<T> above inner loops: this allows you to re-use the root
+ * on each iteration of the loop.
+ *
+ * - Pass Handle<T> through your hot call stack to avoid re-rooting costs at
+ * every invocation.
+ *
+ * The following diagram explains the list of supported, implicit type
+ * conversions between classes of this family:
+ *
+ * Rooted<T> ----> Handle<T>
+ * | ^
+ * | |
+ * | |
+ * +---> MutableHandle<T>
+ * (via &)
+ *
+ * All of these types have an implicit conversion to raw pointers.
+ */
+
+namespace js {
+
+template <typename T>
+struct BarrierMethods {};
+
+template <typename Element, typename Wrapper>
+class WrappedPtrOperations {};
+
+template <typename Element, typename Wrapper>
+class MutableWrappedPtrOperations
+ : public WrappedPtrOperations<Element, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class RootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class HandleBase : public WrappedPtrOperations<T, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class MutableHandleBase : public MutableWrappedPtrOperations<T, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class HeapBase : public MutableWrappedPtrOperations<T, Wrapper> {};
+
+// Cannot use FOR_EACH_HEAP_ABLE_GC_POINTER_TYPE, as this would import too many
+// macros into scope
+template <typename T>
+struct IsHeapConstructibleType {
+ static constexpr bool value = false;
+};
+#define DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE(T) \
+ template <> \
+ struct IsHeapConstructibleType<T> { \
+ static constexpr bool value = true; \
+ };
+JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
+JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
+#undef DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
+
+template <typename T, typename Wrapper>
+class PersistentRootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
+
+namespace gc {
+struct Cell;
+template <typename T>
+struct PersistentRootedMarker;
+} /* namespace gc */
+
+// Important: Return a reference so passing a Rooted<T>, etc. to
+// something that takes a |const T&| is not a GC hazard.
+#define DECLARE_POINTER_CONSTREF_OPS(T) \
+ operator const T&() const { return get(); } \
+ const T& operator->() const { return get(); }
+
+// Assignment operators on a base class are hidden by the implicitly defined
+// operator= on the derived class. Thus, define the operator= directly on the
+// class as we would need to manually pass it through anyway.
+#define DECLARE_POINTER_ASSIGN_OPS(Wrapper, T) \
+ Wrapper<T>& operator=(const T& p) { \
+ set(p); \
+ return *this; \
+ } \
+ Wrapper<T>& operator=(T&& p) { \
+ set(std::move(p)); \
+ return *this; \
+ } \
+ Wrapper<T>& operator=(const Wrapper<T>& other) { \
+ set(other.get()); \
+ return *this; \
+ }
+
+#define DELETE_ASSIGNMENT_OPS(Wrapper, T) \
+ template <typename S> \
+ Wrapper<T>& operator=(S) = delete; \
+ Wrapper<T>& operator=(const Wrapper<T>&) = delete;
+
+#define DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr) \
+ const T* address() const { return &(ptr); } \
+ const T& get() const { return (ptr); }
+
+#define DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr) \
+ T* address() { return &(ptr); } \
+ T& get() { return (ptr); }
+
+} /* namespace js */
+
+namespace JS {
+
+JS_FRIEND_API void HeapObjectPostWriteBarrier(JSObject** objp, JSObject* prev,
+ JSObject* next);
+JS_FRIEND_API void HeapStringPostWriteBarrier(JSString** objp, JSString* prev,
+ JSString* next);
+JS_FRIEND_API void HeapBigIntPostWriteBarrier(JS::BigInt** bip,
+ JS::BigInt* prev,
+ JS::BigInt* next);
+JS_FRIEND_API void HeapObjectWriteBarriers(JSObject** objp, JSObject* prev,
+ JSObject* next);
+JS_FRIEND_API void HeapStringWriteBarriers(JSString** objp, JSString* prev,
+ JSString* next);
+JS_FRIEND_API void HeapBigIntWriteBarriers(JS::BigInt** bip, JS::BigInt* prev,
+ JS::BigInt* next);
+JS_FRIEND_API void HeapScriptWriteBarriers(JSScript** objp, JSScript* prev,
+ JSScript* next);
+
+/**
+ * Create a safely-initialized |T|, suitable for use as a default value in
+ * situations requiring a safe but arbitrary |T| value.
+ */
+template <typename T>
+inline T SafelyInitialized() {
+ // This function wants to presume that |T()| -- which value-initializes a
+ // |T| per C++11 [expr.type.conv]p2 -- will produce a safely-initialized,
+ // safely-usable T that it can return.
+
+#if defined(XP_WIN) || defined(XP_MACOSX) || \
+ (defined(XP_UNIX) && !defined(__clang__))
+
+ // That presumption holds for pointers, where value initialization produces
+ // a null pointer.
+ constexpr bool IsPointer = std::is_pointer_v<T>;
+
+ // For classes and unions we *assume* that if |T|'s default constructor is
+ // non-trivial it'll initialize correctly. (This is unideal, but C++
+ // doesn't offer a type trait indicating whether a class's constructor is
+ // user-defined, which better approximates our desired semantics.)
+ constexpr bool IsNonTriviallyDefaultConstructibleClassOrUnion =
+ (std::is_class_v<T> ||
+ std::is_union_v<T>)&&!std::is_trivially_default_constructible_v<T>;
+
+ static_assert(IsPointer || IsNonTriviallyDefaultConstructibleClassOrUnion,
+ "T() must evaluate to a safely-initialized T");
+
+#endif
+
+ return T();
+}
+
+#ifdef JS_DEBUG
+/**
+ * For generational GC, assert that an object is in the tenured generation as
+ * opposed to being in the nursery.
+ */
+extern JS_FRIEND_API void AssertGCThingMustBeTenured(JSObject* obj);
+extern JS_FRIEND_API void AssertGCThingIsNotNurseryAllocable(
+ js::gc::Cell* cell);
+#else
+inline void AssertGCThingMustBeTenured(JSObject* obj) {}
+inline void AssertGCThingIsNotNurseryAllocable(js::gc::Cell* cell) {}
+#endif
+
+/**
+ * The Heap<T> class is a heap-stored reference to a JS GC thing for use outside
+ * the JS engine. All members of heap classes that refer to GC things should use
+ * Heap<T> (or possibly TenuredHeap<T>, described below).
+ *
+ * Heap<T> is an abstraction that hides some of the complexity required to
+ * maintain GC invariants for the contained reference. It uses operator
+ * overloading to provide a normal pointer interface, but adds barriers to
+ * notify the GC of changes.
+ *
+ * Heap<T> implements the following barriers:
+ *
+ * - Post-write barrier (necessary for generational GC).
+ * - Read barrier (necessary for incremental GC and cycle collector
+ * integration).
+ *
+ * Note Heap<T> does not have a pre-write barrier as used internally in the
+ * engine. The read barrier is used to mark anything read from a Heap<T> during
+ * an incremental GC.
+ *
+ * Heap<T> may be moved or destroyed outside of GC finalization and hence may be
+ * used in dynamic storage such as a Vector.
+ *
+ * Heap<T> instances must be traced when their containing object is traced to
+ * keep the pointed-to GC thing alive.
+ *
+ * Heap<T> objects should only be used on the heap. GC references stored on the
+ * C/C++ stack must use Rooted/Handle/MutableHandle instead.
+ *
+ * Type T must be a public GC pointer type.
+ */
+template <typename T>
+class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T, Heap<T>> {
+ // Please note: this can actually also be used by nsXBLMaybeCompiled<T>, for
+ // legacy reasons.
+ static_assert(js::IsHeapConstructibleType<T>::value,
+ "Type T must be a public GC pointer type");
+
+ public:
+ using ElementType = T;
+
+ Heap() : ptr(SafelyInitialized<T>()) {
+ // No barriers are required for initialization to the default value.
+ static_assert(sizeof(T) == sizeof(Heap<T>),
+ "Heap<T> must be binary compatible with T.");
+ }
+ explicit Heap(const T& p) { init(p); }
+
+ /*
+ * For Heap, move semantics are equivalent to copy semantics. In C++, a
+ * copy constructor taking const-ref is the way to get a single function
+ * that will be used for both lvalue and rvalue copies, so we can simply
+ * omit the rvalue variant.
+ */
+ explicit Heap(const Heap<T>& other) { init(other.ptr); }
+
+ Heap& operator=(Heap<T>&& other) {
+ set(other.unbarrieredGet());
+ other.set(SafelyInitialized<T>());
+ return *this;
+ }
+
+ ~Heap() { postWriteBarrier(ptr, SafelyInitialized<T>()); }
+
+ DECLARE_POINTER_CONSTREF_OPS(T);
+ DECLARE_POINTER_ASSIGN_OPS(Heap, T);
+
+ const T* address() const { return &ptr; }
+
+ void exposeToActiveJS() const { js::BarrierMethods<T>::exposeToJS(ptr); }
+ const T& get() const {
+ exposeToActiveJS();
+ return ptr;
+ }
+ const T& unbarrieredGet() const { return ptr; }
+
+ void set(const T& newPtr) {
+ T tmp = ptr;
+ ptr = newPtr;
+ postWriteBarrier(tmp, ptr);
+ }
+
+ T* unsafeGet() { return &ptr; }
+
+ void unbarrieredSet(const T& newPtr) { ptr = newPtr; }
+
+ explicit operator bool() const {
+ return bool(js::BarrierMethods<T>::asGCThingOrNull(ptr));
+ }
+ explicit operator bool() {
+ return bool(js::BarrierMethods<T>::asGCThingOrNull(ptr));
+ }
+
+ private:
+ void init(const T& newPtr) {
+ ptr = newPtr;
+ postWriteBarrier(SafelyInitialized<T>(), ptr);
+ }
+
+ void postWriteBarrier(const T& prev, const T& next) {
+ js::BarrierMethods<T>::postWriteBarrier(&ptr, prev, next);
+ }
+
+ T ptr;
+};
+
+namespace detail {
+
+template <typename T>
+struct DefineComparisonOps<Heap<T>> : std::true_type {
+ static const T& get(const Heap<T>& v) { return v.unbarrieredGet(); }
+};
+
+} // namespace detail
+
+static MOZ_ALWAYS_INLINE bool ObjectIsTenured(JSObject* obj) {
+ return !js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(obj));
+}
+
+static MOZ_ALWAYS_INLINE bool ObjectIsTenured(const Heap<JSObject*>& obj) {
+ return ObjectIsTenured(obj.unbarrieredGet());
+}
+
+static MOZ_ALWAYS_INLINE bool ObjectIsMarkedGray(JSObject* obj) {
+ auto cell = reinterpret_cast<js::gc::Cell*>(obj);
+ return js::gc::detail::CellIsMarkedGrayIfKnown(cell);
+}
+
+static MOZ_ALWAYS_INLINE bool ObjectIsMarkedGray(
+ const JS::Heap<JSObject*>& obj) {
+ return ObjectIsMarkedGray(obj.unbarrieredGet());
+}
+
+// The following *IsNotGray functions take account of the eventual
+// gray marking state at the end of any ongoing incremental GC by
+// delaying the checks if necessary.
+
+#ifdef DEBUG
+
+inline void AssertCellIsNotGray(const js::gc::Cell* maybeCell) {
+ if (maybeCell) {
+ js::gc::detail::AssertCellIsNotGray(maybeCell);
+ }
+}
+
+inline void AssertObjectIsNotGray(JSObject* maybeObj) {
+ AssertCellIsNotGray(reinterpret_cast<js::gc::Cell*>(maybeObj));
+}
+
+inline void AssertObjectIsNotGray(const JS::Heap<JSObject*>& obj) {
+ AssertObjectIsNotGray(obj.unbarrieredGet());
+}
+
+#else
+
+inline void AssertCellIsNotGray(js::gc::Cell* maybeCell) {}
+inline void AssertObjectIsNotGray(JSObject* maybeObj) {}
+inline void AssertObjectIsNotGray(const JS::Heap<JSObject*>& obj) {}
+
+#endif
+
+/**
+ * The TenuredHeap<T> class is similar to the Heap<T> class above in that it
+ * encapsulates the GC concerns of an on-heap reference to a JS object. However,
+ * it has two important differences:
+ *
+ * 1) Pointers which are statically known to only reference "tenured" objects
+ * can avoid the extra overhead of SpiderMonkey's write barriers.
+ *
+ * 2) Objects in the "tenured" heap have stronger alignment restrictions than
+ * those in the "nursery", so it is possible to store flags in the lower
+ * bits of pointers known to be tenured. TenuredHeap wraps a normal tagged
+ * pointer with a nice API for accessing the flag bits and adds various
+ * assertions to ensure that it is not mis-used.
+ *
+ * GC things are said to be "tenured" when they are located in the long-lived
+ * heap: e.g. they have gained tenure as an object by surviving past at least
+ * one GC. For performance, SpiderMonkey allocates some things which are known
+ * to normally be long lived directly into the tenured generation; for example,
+ * global objects. Additionally, SpiderMonkey does not visit individual objects
+ * when deleting non-tenured objects, so object with finalizers are also always
+ * tenured; for instance, this includes most DOM objects.
+ *
+ * The considerations to keep in mind when using a TenuredHeap<T> vs a normal
+ * Heap<T> are:
+ *
+ * - It is invalid for a TenuredHeap<T> to refer to a non-tenured thing.
+ * - It is however valid for a Heap<T> to refer to a tenured thing.
+ * - It is not possible to store flag bits in a Heap<T>.
+ */
+template <typename T>
+class TenuredHeap : public js::HeapBase<T, TenuredHeap<T>> {
+ public:
+ using ElementType = T;
+
+ TenuredHeap() : bits(0) {
+ static_assert(sizeof(T) == sizeof(TenuredHeap<T>),
+ "TenuredHeap<T> must be binary compatible with T.");
+ }
+ explicit TenuredHeap(T p) : bits(0) { setPtr(p); }
+ explicit TenuredHeap(const TenuredHeap<T>& p) : bits(0) {
+ setPtr(p.getPtr());
+ }
+
+ void setPtr(T newPtr) {
+ MOZ_ASSERT((reinterpret_cast<uintptr_t>(newPtr) & flagsMask) == 0);
+ MOZ_ASSERT(js::gc::IsCellPointerValidOrNull(newPtr));
+ if (newPtr) {
+ AssertGCThingMustBeTenured(newPtr);
+ }
+ bits = (bits & flagsMask) | reinterpret_cast<uintptr_t>(newPtr);
+ }
+
+ void setFlags(uintptr_t flagsToSet) {
+ MOZ_ASSERT((flagsToSet & ~flagsMask) == 0);
+ bits |= flagsToSet;
+ }
+
+ void unsetFlags(uintptr_t flagsToUnset) {
+ MOZ_ASSERT((flagsToUnset & ~flagsMask) == 0);
+ bits &= ~flagsToUnset;
+ }
+
+ bool hasFlag(uintptr_t flag) const {
+ MOZ_ASSERT((flag & ~flagsMask) == 0);
+ return (bits & flag) != 0;
+ }
+
+ T unbarrieredGetPtr() const { return reinterpret_cast<T>(bits & ~flagsMask); }
+ uintptr_t getFlags() const { return bits & flagsMask; }
+
+ void exposeToActiveJS() const {
+ js::BarrierMethods<T>::exposeToJS(unbarrieredGetPtr());
+ }
+ T getPtr() const {
+ exposeToActiveJS();
+ return unbarrieredGetPtr();
+ }
+
+ operator T() const { return getPtr(); }
+ T operator->() const { return getPtr(); }
+
+ explicit operator bool() const {
+ return bool(js::BarrierMethods<T>::asGCThingOrNull(unbarrieredGetPtr()));
+ }
+ explicit operator bool() {
+ return bool(js::BarrierMethods<T>::asGCThingOrNull(unbarrieredGetPtr()));
+ }
+
+ TenuredHeap<T>& operator=(T p) {
+ setPtr(p);
+ return *this;
+ }
+
+ TenuredHeap<T>& operator=(const TenuredHeap<T>& other) {
+ bits = other.bits;
+ return *this;
+ }
+
+ private:
+ enum {
+ maskBits = 3,
+ flagsMask = (1 << maskBits) - 1,
+ };
+
+ uintptr_t bits;
+};
+
+namespace detail {
+
+template <typename T>
+struct DefineComparisonOps<TenuredHeap<T>> : std::true_type {
+ static const T get(const TenuredHeap<T>& v) { return v.unbarrieredGetPtr(); }
+};
+
+} // namespace detail
+
+// std::swap uses a stack temporary, which prevents classes like Heap<T>
+// from being declared MOZ_HEAP_CLASS.
+template <typename T>
+void swap(TenuredHeap<T>& aX, TenuredHeap<T>& aY) {
+ T tmp = aX;
+ aX = aY;
+ aY = tmp;
+}
+
+template <typename T>
+void swap(Heap<T>& aX, Heap<T>& aY) {
+ T tmp = aX;
+ aX = aY;
+ aY = tmp;
+}
+
+static MOZ_ALWAYS_INLINE bool ObjectIsMarkedGray(
+ const JS::TenuredHeap<JSObject*>& obj) {
+ return ObjectIsMarkedGray(obj.unbarrieredGetPtr());
+}
+
+template <typename T>
+class MutableHandle;
+template <typename T>
+class Rooted;
+template <typename T>
+class PersistentRooted;
+
+/**
+ * Reference to a T that has been rooted elsewhere. This is most useful
+ * as a parameter type, which guarantees that the T lvalue is properly
+ * rooted. See "Move GC Stack Rooting" above.
+ *
+ * If you want to add additional methods to Handle for a specific
+ * specialization, define a HandleBase<T> specialization containing them.
+ */
+template <typename T>
+class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T, Handle<T>> {
+ friend class MutableHandle<T>;
+
+ public:
+ using ElementType = T;
+
+ /* Creates a handle from a handle of a type convertible to T. */
+ template <typename S>
+ MOZ_IMPLICIT Handle(
+ Handle<S> handle,
+ std::enable_if_t<std::is_convertible_v<S, T>, int> dummy = 0) {
+ static_assert(sizeof(Handle<T>) == sizeof(T*),
+ "Handle must be binary compatible with T*.");
+ ptr = reinterpret_cast<const T*>(handle.address());
+ }
+
+ MOZ_IMPLICIT Handle(decltype(nullptr)) {
+ static_assert(std::is_pointer_v<T>,
+ "nullptr_t overload not valid for non-pointer types");
+ static void* const ConstNullValue = nullptr;
+ ptr = reinterpret_cast<const T*>(&ConstNullValue);
+ }
+
+ MOZ_IMPLICIT Handle(MutableHandle<T> handle) { ptr = handle.address(); }
+
+ /*
+ * Take care when calling this method!
+ *
+ * This creates a Handle from the raw location of a T.
+ *
+ * It should be called only if the following conditions hold:
+ *
+ * 1) the location of the T is guaranteed to be marked (for some reason
+ * other than being a Rooted), e.g., if it is guaranteed to be reachable
+ * from an implicit root.
+ *
+ * 2) the contents of the location are immutable, or at least cannot change
+ * for the lifetime of the handle, as its users may not expect its value
+ * to change underneath them.
+ */
+ static constexpr Handle fromMarkedLocation(const T* p) {
+ return Handle(p, DeliberatelyChoosingThisOverload,
+ ImUsingThisOnlyInFromFromMarkedLocation);
+ }
+
+ /*
+ * Construct a handle from an explicitly rooted location. This is the
+ * normal way to create a handle, and normally happens implicitly.
+ */
+ template <typename S>
+ inline MOZ_IMPLICIT Handle(
+ const Rooted<S>& root,
+ std::enable_if_t<std::is_convertible_v<S, T>, int> dummy = 0);
+
+ template <typename S>
+ inline MOZ_IMPLICIT Handle(
+ const PersistentRooted<S>& root,
+ std::enable_if_t<std::is_convertible_v<S, T>, int> dummy = 0);
+
+ /* Construct a read only handle from a mutable handle. */
+ template <typename S>
+ inline MOZ_IMPLICIT Handle(
+ MutableHandle<S>& root,
+ std::enable_if_t<std::is_convertible_v<S, T>, int> dummy = 0);
+
+ DECLARE_POINTER_CONSTREF_OPS(T);
+ DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr);
+
+ private:
+ Handle() = default;
+ DELETE_ASSIGNMENT_OPS(Handle, T);
+
+ enum Disambiguator { DeliberatelyChoosingThisOverload = 42 };
+ enum CallerIdentity { ImUsingThisOnlyInFromFromMarkedLocation = 17 };
+ constexpr Handle(const T* p, Disambiguator, CallerIdentity) : ptr(p) {}
+
+ const T* ptr;
+};
+
+namespace detail {
+
+template <typename T>
+struct DefineComparisonOps<Handle<T>> : std::true_type {
+ static const T& get(const Handle<T>& v) { return v.get(); }
+};
+
+} // namespace detail
+
+/**
+ * Similar to a handle, but the underlying storage can be changed. This is
+ * useful for outparams.
+ *
+ * If you want to add additional methods to MutableHandle for a specific
+ * specialization, define a MutableHandleBase<T> specialization containing
+ * them.
+ */
+template <typename T>
+class MOZ_STACK_CLASS MutableHandle
+ : public js::MutableHandleBase<T, MutableHandle<T>> {
+ public:
+ using ElementType = T;
+
+ inline MOZ_IMPLICIT MutableHandle(Rooted<T>* root);
+ inline MOZ_IMPLICIT MutableHandle(PersistentRooted<T>* root);
+
+ private:
+ // Disallow nullptr for overloading purposes.
+ MutableHandle(decltype(nullptr)) = delete;
+
+ public:
+ void set(const T& v) {
+ *ptr = v;
+ MOZ_ASSERT(GCPolicy<T>::isValid(*ptr));
+ }
+ void set(T&& v) {
+ *ptr = std::move(v);
+ MOZ_ASSERT(GCPolicy<T>::isValid(*ptr));
+ }
+
+ /*
+ * This may be called only if the location of the T is guaranteed
+ * to be marked (for some reason other than being a Rooted),
+ * e.g., if it is guaranteed to be reachable from an implicit root.
+ *
+ * Create a MutableHandle from a raw location of a T.
+ */
+ static MutableHandle fromMarkedLocation(T* p) {
+ MutableHandle h;
+ h.ptr = p;
+ return h;
+ }
+
+ DECLARE_POINTER_CONSTREF_OPS(T);
+ DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr);
+ DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(*ptr);
+
+ private:
+ MutableHandle() = default;
+ DELETE_ASSIGNMENT_OPS(MutableHandle, T);
+
+ T* ptr;
+};
+
+namespace detail {
+
+template <typename T>
+struct DefineComparisonOps<MutableHandle<T>> : std::true_type {
+ static const T& get(const MutableHandle<T>& v) { return v.get(); }
+};
+
+} // namespace detail
+
+} /* namespace JS */
+
+namespace js {
+
+namespace detail {
+
+// Default implementations for barrier methods on GC thing pointers.
+template <typename T>
+struct PtrBarrierMethodsBase {
+ static T* initial() { return nullptr; }
+ static gc::Cell* asGCThingOrNull(T* v) {
+ if (!v) {
+ return nullptr;
+ }
+ MOZ_ASSERT(uintptr_t(v) > 32);
+ return reinterpret_cast<gc::Cell*>(v);
+ }
+ static void exposeToJS(T* t) {
+ if (t) {
+ js::gc::ExposeGCThingToActiveJS(JS::GCCellPtr(t));
+ }
+ }
+};
+
+} // namespace detail
+
+template <typename T>
+struct BarrierMethods<T*> : public detail::PtrBarrierMethodsBase<T> {
+ static void postWriteBarrier(T** vp, T* prev, T* next) {
+ if (next) {
+ JS::AssertGCThingIsNotNurseryAllocable(
+ reinterpret_cast<js::gc::Cell*>(next));
+ }
+ }
+};
+
+template <>
+struct BarrierMethods<JSObject*>
+ : public detail::PtrBarrierMethodsBase<JSObject> {
+ static void postWriteBarrier(JSObject** vp, JSObject* prev, JSObject* next) {
+ JS::HeapObjectPostWriteBarrier(vp, prev, next);
+ }
+ static void exposeToJS(JSObject* obj) {
+ if (obj) {
+ JS::ExposeObjectToActiveJS(obj);
+ }
+ }
+};
+
+template <>
+struct BarrierMethods<JSFunction*>
+ : public detail::PtrBarrierMethodsBase<JSFunction> {
+ static void postWriteBarrier(JSFunction** vp, JSFunction* prev,
+ JSFunction* next) {
+ JS::HeapObjectPostWriteBarrier(reinterpret_cast<JSObject**>(vp),
+ reinterpret_cast<JSObject*>(prev),
+ reinterpret_cast<JSObject*>(next));
+ }
+ static void exposeToJS(JSFunction* fun) {
+ if (fun) {
+ JS::ExposeObjectToActiveJS(reinterpret_cast<JSObject*>(fun));
+ }
+ }
+};
+
+template <>
+struct BarrierMethods<JSString*>
+ : public detail::PtrBarrierMethodsBase<JSString> {
+ static void postWriteBarrier(JSString** vp, JSString* prev, JSString* next) {
+ JS::HeapStringPostWriteBarrier(vp, prev, next);
+ }
+};
+
+template <>
+struct BarrierMethods<JS::BigInt*>
+ : public detail::PtrBarrierMethodsBase<JS::BigInt> {
+ static void postWriteBarrier(JS::BigInt** vp, JS::BigInt* prev,
+ JS::BigInt* next) {
+ JS::HeapBigIntPostWriteBarrier(vp, prev, next);
+ }
+};
+
+// Provide hash codes for Cell kinds that may be relocated and, thus, not have
+// a stable address to use as the base for a hash code. Instead of the address,
+// this hasher uses Cell::getUniqueId to provide exact matches and as a base
+// for generating hash codes.
+//
+// Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr
+// would not likely be a useful key, there are some cases where being able to
+// hash a nullptr is useful, either on purpose or because of bugs:
+// (1) existence checks where the key may happen to be null and (2) some
+// aggregate Lookup kinds embed a JSObject* that is frequently null and do not
+// null test before dispatching to the hasher.
+template <typename T>
+struct JS_PUBLIC_API MovableCellHasher {
+ using Key = T;
+ using Lookup = T;
+
+ static bool hasHash(const Lookup& l);
+ static bool ensureHash(const Lookup& l);
+ static HashNumber hash(const Lookup& l);
+ static bool match(const Key& k, const Lookup& l);
+ // The rekey hash policy method is not provided since you dont't need to
+ // rekey any more when using this policy.
+};
+
+template <typename T>
+struct JS_PUBLIC_API MovableCellHasher<JS::Heap<T>> {
+ using Key = JS::Heap<T>;
+ using Lookup = T;
+
+ static bool hasHash(const Lookup& l) {
+ return MovableCellHasher<T>::hasHash(l);
+ }
+ static bool ensureHash(const Lookup& l) {
+ return MovableCellHasher<T>::ensureHash(l);
+ }
+ static HashNumber hash(const Lookup& l) {
+ return MovableCellHasher<T>::hash(l);
+ }
+ static bool match(const Key& k, const Lookup& l) {
+ return MovableCellHasher<T>::match(k.unbarrieredGet(), l);
+ }
+};
+
+} // namespace js
+
+namespace mozilla {
+
+template <typename T>
+struct FallibleHashMethods<js::MovableCellHasher<T>> {
+ template <typename Lookup>
+ static bool hasHash(Lookup&& l) {
+ return js::MovableCellHasher<T>::hasHash(std::forward<Lookup>(l));
+ }
+ template <typename Lookup>
+ static bool ensureHash(Lookup&& l) {
+ return js::MovableCellHasher<T>::ensureHash(std::forward<Lookup>(l));
+ }
+};
+
+} // namespace mozilla
+
+namespace js {
+
+struct VirtualTraceable {
+ virtual ~VirtualTraceable() = default;
+ virtual void trace(JSTracer* trc, const char* name) = 0;
+};
+
+template <typename T>
+struct RootedTraceable final : public VirtualTraceable {
+ static_assert(JS::MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
+ "RootedTraceable is intended only for usage with a Traceable");
+
+ T ptr;
+
+ template <typename U>
+ MOZ_IMPLICIT RootedTraceable(U&& initial) : ptr(std::forward<U>(initial)) {}
+
+ operator T&() { return ptr; }
+ operator const T&() const { return ptr; }
+
+ void trace(JSTracer* trc, const char* name) override {
+ JS::GCPolicy<T>::trace(trc, &ptr, name);
+ }
+};
+
+template <typename T>
+struct RootedTraceableTraits {
+ static T* address(RootedTraceable<T>& self) { return &self.ptr; }
+ static const T* address(const RootedTraceable<T>& self) { return &self.ptr; }
+ static void trace(JSTracer* trc, VirtualTraceable* thingp, const char* name);
+};
+
+template <typename T>
+struct RootedGCThingTraits {
+ static T* address(T& self) { return &self; }
+ static const T* address(const T& self) { return &self; }
+ static void trace(JSTracer* trc, T* thingp, const char* name);
+};
+
+} /* namespace js */
+
+namespace JS {
+
+class JS_PUBLIC_API AutoGCRooter;
+
+enum class AutoGCRooterKind : uint8_t {
+ WrapperVector, /* js::AutoWrapperVector */
+ Wrapper, /* js::AutoWrapperRooter */
+ Custom, /* js::CustomAutoRooter */
+
+ Limit
+};
+
+namespace detail {
+// Dummy type to store root list entry pointers as. This code does not just use
+// the actual type, because then eg JSObject* and JSFunction* would be assumed
+// to never alias but they do (they are stored in the same list). Also, do not
+// use `void*` so that `Rooted<void*>` is a compile error.
+struct RootListEntry;
+} // namespace detail
+
+template <>
+struct MapTypeToRootKind<detail::RootListEntry*> {
+ static const RootKind kind = RootKind::Traceable;
+};
+
+using RootedListHeads =
+ mozilla::EnumeratedArray<RootKind, RootKind::Limit,
+ Rooted<detail::RootListEntry*>*>;
+
+using AutoRooterListHeads =
+ mozilla::EnumeratedArray<AutoGCRooterKind, AutoGCRooterKind::Limit,
+ AutoGCRooter*>;
+
+// Superclass of JSContext which can be used for rooting data in use by the
+// current thread but that does not provide all the functions of a JSContext.
+class RootingContext {
+ // Stack GC roots for Rooted GC heap pointers.
+ RootedListHeads stackRoots_;
+ template <typename T>
+ friend class Rooted;
+
+ // Stack GC roots for AutoFooRooter classes.
+ AutoRooterListHeads autoGCRooters_;
+ friend class AutoGCRooter;
+
+ // Gecko profiling metadata.
+ // This isn't really rooting related. It's only here because we want
+ // GetContextProfilingStackIfEnabled to be inlineable into non-JS code, and
+ // we didn't want to add another superclass of JSContext just for this.
+ js::GeckoProfilerThread geckoProfiler_;
+
+ public:
+ RootingContext();
+
+ void traceStackRoots(JSTracer* trc);
+
+ /* Implemented in gc/RootMarking.cpp. */
+ void traceAllGCRooters(JSTracer* trc);
+ void traceWrapperGCRooters(JSTracer* trc);
+ static void traceGCRooterList(JSTracer* trc, AutoGCRooter* head);
+
+ void checkNoGCRooters();
+
+ js::GeckoProfilerThread& geckoProfiler() { return geckoProfiler_; }
+
+ protected:
+ // The remaining members in this class should only be accessed through
+ // JSContext pointers. They are unrelated to rooting and are in place so
+ // that inlined API functions can directly access the data.
+
+ /* The current realm. */
+ Realm* realm_;
+
+ /* The current zone. */
+ Zone* zone_;
+
+ public:
+ /* Limit pointer for checking native stack consumption. */
+ uintptr_t nativeStackLimit[StackKindCount];
+
+ static const RootingContext* get(const JSContext* cx) {
+ return reinterpret_cast<const RootingContext*>(cx);
+ }
+
+ static RootingContext* get(JSContext* cx) {
+ return reinterpret_cast<RootingContext*>(cx);
+ }
+
+ friend JS::Realm* js::GetContextRealm(const JSContext* cx);
+ friend JS::Zone* js::GetContextZone(const JSContext* cx);
+};
+
+class JS_PUBLIC_API AutoGCRooter {
+ public:
+ using Kind = AutoGCRooterKind;
+
+ AutoGCRooter(JSContext* cx, Kind kind)
+ : AutoGCRooter(JS::RootingContext::get(cx), kind) {}
+ AutoGCRooter(RootingContext* cx, Kind kind)
+ : down(cx->autoGCRooters_[kind]),
+ stackTop(&cx->autoGCRooters_[kind]),
+ kind_(kind) {
+ MOZ_ASSERT(this != *stackTop);
+ *stackTop = this;
+ }
+
+ ~AutoGCRooter() {
+ MOZ_ASSERT(this == *stackTop);
+ *stackTop = down;
+ }
+
+ void trace(JSTracer* trc);
+
+ private:
+ friend class RootingContext;
+
+ AutoGCRooter* const down;
+ AutoGCRooter** const stackTop;
+
+ /*
+ * Discriminates actual subclass of this being used. The meaning is
+ * indicated by the corresponding value in the Kind enum.
+ */
+ Kind kind_;
+
+ /* No copy or assignment semantics. */
+ AutoGCRooter(AutoGCRooter& ida) = delete;
+ void operator=(AutoGCRooter& ida) = delete;
+} JS_HAZ_ROOTED_BASE;
+
+namespace detail {
+
+template <typename T>
+using RootedPtr =
+ std::conditional_t<MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
+ js::RootedTraceable<T>, T>;
+
+template <typename T>
+using RootedPtrTraits =
+ std::conditional_t<MapTypeToRootKind<T>::kind == JS::RootKind::Traceable,
+ js::RootedTraceableTraits<T>,
+ js::RootedGCThingTraits<T>>;
+
+// Dummy types to make it easier to understand template overload preference
+// ordering.
+struct FallbackOverload {};
+struct PreferredOverload : FallbackOverload {};
+using OverloadSelector = PreferredOverload;
+
+} /* namespace detail */
+
+/**
+ * Local variable of type T whose value is always rooted. This is typically
+ * used for local variables, or for non-rooted values being passed to a
+ * function that requires a handle, e.g. Foo(Root<T>(cx, x)).
+ *
+ * If you want to add additional methods to Rooted for a specific
+ * specialization, define a RootedBase<T> specialization containing them.
+ */
+template <typename T>
+class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>> {
+ using Ptr = detail::RootedPtr<T>;
+ using PtrTraits = detail::RootedPtrTraits<T>;
+
+ inline void registerWithRootLists(RootedListHeads& roots) {
+ this->stack = &roots[JS::MapTypeToRootKind<T>::kind];
+ this->prev = *stack;
+ *stack = reinterpret_cast<Rooted<detail::RootListEntry*>*>(this);
+ }
+
+ inline RootedListHeads& rootLists(RootingContext* cx) {
+ return cx->stackRoots_;
+ }
+ inline RootedListHeads& rootLists(JSContext* cx) {
+ return rootLists(RootingContext::get(cx));
+ }
+
+ // Define either one or two Rooted(cx) constructors: the fallback one, which
+ // constructs a Rooted holding a SafelyInitialized<T>, and a convenience one
+ // for types that can be constructed with a cx, which will give a Rooted
+ // holding a T(cx).
+
+ // Dummy type to distinguish these constructors from Rooted(cx, initial)
+ struct CtorDispatcher {};
+
+ // Normal case: construct an empty Rooted holding a safely initialized but
+ // empty T.
+ template <typename RootingContext>
+ Rooted(const RootingContext& cx, CtorDispatcher, detail::FallbackOverload)
+ : Rooted(cx, SafelyInitialized<T>()) {}
+
+ // If T can be constructed with a cx, then define another constructor for it
+ // that will be preferred.
+ template <
+ typename RootingContext,
+ typename = std::enable_if_t<std::is_constructible_v<T, RootingContext>>>
+ Rooted(const RootingContext& cx, CtorDispatcher, detail::PreferredOverload)
+ : Rooted(cx, T(cx)) {}
+
+ public:
+ using ElementType = T;
+
+ // Construct an empty Rooted. Delegates to an internal constructor that
+ // chooses a specific meaning of "empty" depending on whether T can be
+ // constructed with a cx.
+ template <typename RootingContext>
+ explicit Rooted(const RootingContext& cx)
+ : Rooted(cx, CtorDispatcher(), detail::OverloadSelector()) {}
+
+ template <typename RootingContext, typename S>
+ Rooted(const RootingContext& cx, S&& initial)
+ : ptr(std::forward<S>(initial)) {
+ MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
+ registerWithRootLists(rootLists(cx));
+ }
+
+ ~Rooted() {
+ MOZ_ASSERT(*stack ==
+ reinterpret_cast<Rooted<detail::RootListEntry*>*>(this));
+ *stack = prev;
+ }
+
+ Rooted<T>* previous() { return reinterpret_cast<Rooted<T>*>(prev); }
+
+ /*
+ * This method is public for Rooted so that Codegen.py can use a Rooted
+ * interchangeably with a MutableHandleValue.
+ */
+ void set(const T& value) {
+ ptr = value;
+ MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
+ }
+ void set(T&& value) {
+ ptr = std::move(value);
+ MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
+ }
+
+ DECLARE_POINTER_CONSTREF_OPS(T);
+ DECLARE_POINTER_ASSIGN_OPS(Rooted, T);
+
+ T& get() { return ptr; }
+ const T& get() const { return ptr; }
+
+ T* address() { return PtrTraits::address(ptr); }
+ const T* address() const { return PtrTraits::address(ptr); }
+
+ void trace(JSTracer* trc, const char* name);
+
+ private:
+ /*
+ * These need to be templated on RootListEntry* to avoid aliasing issues
+ * between, for example, Rooted<JSObject*> and Rooted<JSFunction*>, which use
+ * the same stack head pointer for different classes.
+ */
+ Rooted<detail::RootListEntry*>** stack;
+ Rooted<detail::RootListEntry*>* prev;
+
+ Ptr ptr;
+
+ Rooted(const Rooted&) = delete;
+} JS_HAZ_ROOTED;
+
+namespace detail {
+
+template <typename T>
+struct DefineComparisonOps<Rooted<T>> : std::true_type {
+ static const T& get(const Rooted<T>& v) { return v.get(); }
+};
+
+} // namespace detail
+
+} /* namespace JS */
+
+namespace js {
+
+/*
+ * Inlinable accessors for JSContext.
+ *
+ * - These must not be available on the more restricted superclasses of
+ * JSContext, so we can't simply define them on RootingContext.
+ *
+ * - They're perfectly ordinary JSContext functionality, so ought to be
+ * usable without resorting to jsfriendapi.h, and when JSContext is an
+ * incomplete type.
+ */
+inline JS::Realm* GetContextRealm(const JSContext* cx) {
+ return JS::RootingContext::get(cx)->realm_;
+}
+
+inline JS::Compartment* GetContextCompartment(const JSContext* cx) {
+ if (JS::Realm* realm = GetContextRealm(cx)) {
+ return GetCompartmentForRealm(realm);
+ }
+ return nullptr;
+}
+
+inline JS::Zone* GetContextZone(const JSContext* cx) {
+ return JS::RootingContext::get(cx)->zone_;
+}
+
+inline ProfilingStack* GetContextProfilingStackIfEnabled(JSContext* cx) {
+ return JS::RootingContext::get(cx)
+ ->geckoProfiler()
+ .getProfilingStackIfEnabled();
+}
+
+/**
+ * Augment the generic Rooted<T> interface when T = JSObject* with
+ * class-querying and downcasting operations.
+ *
+ * Given a Rooted<JSObject*> obj, one can view
+ * Handle<StringObject*> h = obj.as<StringObject*>();
+ * as an optimization of
+ * Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>());
+ * Handle<StringObject*> h = rooted;
+ */
+template <typename Container>
+class RootedBase<JSObject*, Container>
+ : public MutableWrappedPtrOperations<JSObject*, Container> {
+ public:
+ template <class U>
+ JS::Handle<U*> as() const;
+};
+
+/**
+ * Augment the generic Handle<T> interface when T = JSObject* with
+ * downcasting operations.
+ *
+ * Given a Handle<JSObject*> obj, one can view
+ * Handle<StringObject*> h = obj.as<StringObject*>();
+ * as an optimization of
+ * Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>());
+ * Handle<StringObject*> h = rooted;
+ */
+template <typename Container>
+class HandleBase<JSObject*, Container>
+ : public WrappedPtrOperations<JSObject*, Container> {
+ public:
+ template <class U>
+ JS::Handle<U*> as() const;
+};
+
+} /* namespace js */
+
+namespace JS {
+
+template <typename T>
+template <typename S>
+inline Handle<T>::Handle(
+ const Rooted<S>& root,
+ std::enable_if_t<std::is_convertible_v<S, T>, int> dummy) {
+ ptr = reinterpret_cast<const T*>(root.address());
+}
+
+template <typename T>
+template <typename S>
+inline Handle<T>::Handle(
+ const PersistentRooted<S>& root,
+ std::enable_if_t<std::is_convertible_v<S, T>, int> dummy) {
+ ptr = reinterpret_cast<const T*>(root.address());
+}
+
+template <typename T>
+template <typename S>
+inline Handle<T>::Handle(
+ MutableHandle<S>& root,
+ std::enable_if_t<std::is_convertible_v<S, T>, int> dummy) {
+ ptr = reinterpret_cast<const T*>(root.address());
+}
+
+template <typename T>
+inline MutableHandle<T>::MutableHandle(Rooted<T>* root) {
+ static_assert(sizeof(MutableHandle<T>) == sizeof(T*),
+ "MutableHandle must be binary compatible with T*.");
+ ptr = root->address();
+}
+
+template <typename T>
+inline MutableHandle<T>::MutableHandle(PersistentRooted<T>* root) {
+ static_assert(sizeof(MutableHandle<T>) == sizeof(T*),
+ "MutableHandle must be binary compatible with T*.");
+ ptr = root->address();
+}
+
+JS_PUBLIC_API void AddPersistentRoot(
+ RootingContext* cx, RootKind kind,
+ PersistentRooted<detail::RootListEntry*>* root);
+
+JS_PUBLIC_API void AddPersistentRoot(
+ JSRuntime* rt, RootKind kind,
+ PersistentRooted<detail::RootListEntry*>* root);
+
+/**
+ * A copyable, assignable global GC root type with arbitrary lifetime, an
+ * infallible constructor, and automatic unrooting on destruction.
+ *
+ * These roots can be used in heap-allocated data structures, so they are not
+ * associated with any particular JSContext or stack. They are registered with
+ * the JSRuntime itself, without locking. Initialization may take place on
+ * construction, or in two phases if the no-argument constructor is called
+ * followed by init().
+ *
+ * Note that you must not use an PersistentRooted in an object owned by a JS
+ * object:
+ *
+ * Whenever one object whose lifetime is decided by the GC refers to another
+ * such object, that edge must be traced only if the owning JS object is traced.
+ * This applies not only to JS objects (which obviously are managed by the GC)
+ * but also to C++ objects owned by JS objects.
+ *
+ * If you put a PersistentRooted in such a C++ object, that is almost certainly
+ * a leak. When a GC begins, the referent of the PersistentRooted is treated as
+ * live, unconditionally (because a PersistentRooted is a *root*), even if the
+ * JS object that owns it is unreachable. If there is any path from that
+ * referent back to the JS object, then the C++ object containing the
+ * PersistentRooted will not be destructed, and the whole blob of objects will
+ * not be freed, even if there are no references to them from the outside.
+ *
+ * In the context of Firefox, this is a severe restriction: almost everything in
+ * Firefox is owned by some JS object or another, so using PersistentRooted in
+ * such objects would introduce leaks. For these kinds of edges, Heap<T> or
+ * TenuredHeap<T> would be better types. It's up to the implementor of the type
+ * containing Heap<T> or TenuredHeap<T> members to make sure their referents get
+ * marked when the object itself is marked.
+ */
+template <typename T>
+class PersistentRooted
+ : public js::RootedBase<T, PersistentRooted<T>>,
+ private mozilla::LinkedListElement<PersistentRooted<T>> {
+ using ListBase = mozilla::LinkedListElement<PersistentRooted<T>>;
+ using Ptr = detail::RootedPtr<T>;
+ using PtrTraits = detail::RootedPtrTraits<T>;
+
+ friend class mozilla::LinkedList<PersistentRooted>;
+ friend class mozilla::LinkedListElement<PersistentRooted>;
+
+ void registerWithRootLists(RootingContext* cx) {
+ MOZ_ASSERT(!initialized());
+ JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
+ AddPersistentRoot(
+ cx, kind,
+ reinterpret_cast<JS::PersistentRooted<detail::RootListEntry*>*>(this));
+ }
+
+ void registerWithRootLists(JSRuntime* rt) {
+ MOZ_ASSERT(!initialized());
+ JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
+ AddPersistentRoot(
+ rt, kind,
+ reinterpret_cast<JS::PersistentRooted<detail::RootListEntry*>*>(this));
+ }
+
+ public:
+ using ElementType = T;
+
+ PersistentRooted() : ptr(SafelyInitialized<T>()) {}
+
+ explicit PersistentRooted(RootingContext* cx) : ptr(SafelyInitialized<T>()) {
+ registerWithRootLists(cx);
+ }
+
+ explicit PersistentRooted(JSContext* cx) : ptr(SafelyInitialized<T>()) {
+ registerWithRootLists(RootingContext::get(cx));
+ }
+
+ template <typename U>
+ PersistentRooted(RootingContext* cx, U&& initial)
+ : ptr(std::forward<U>(initial)) {
+ registerWithRootLists(cx);
+ }
+
+ template <typename U>
+ PersistentRooted(JSContext* cx, U&& initial) : ptr(std::forward<U>(initial)) {
+ registerWithRootLists(RootingContext::get(cx));
+ }
+
+ explicit PersistentRooted(JSRuntime* rt) : ptr(SafelyInitialized<T>()) {
+ registerWithRootLists(rt);
+ }
+
+ template <typename U>
+ PersistentRooted(JSRuntime* rt, U&& initial) : ptr(std::forward<U>(initial)) {
+ registerWithRootLists(rt);
+ }
+
+ PersistentRooted(const PersistentRooted& rhs)
+ : mozilla::LinkedListElement<PersistentRooted<T>>(), ptr(rhs.ptr) {
+ /*
+ * Copy construction takes advantage of the fact that the original
+ * is already inserted, and simply adds itself to whatever list the
+ * original was on - no JSRuntime pointer needed.
+ *
+ * This requires mutating rhs's links, but those should be 'mutable'
+ * anyway. C++ doesn't let us declare mutable base classes.
+ */
+ const_cast<PersistentRooted&>(rhs).setNext(this);
+ }
+
+ bool initialized() const { return ListBase::isInList(); }
+
+ void init(RootingContext* cx) { init(cx, SafelyInitialized<T>()); }
+ void init(JSContext* cx) { init(RootingContext::get(cx)); }
+
+ template <typename U>
+ void init(RootingContext* cx, U&& initial) {
+ ptr = std::forward<U>(initial);
+ registerWithRootLists(cx);
+ }
+ template <typename U>
+ void init(JSContext* cx, U&& initial) {
+ ptr = std::forward<U>(initial);
+ registerWithRootLists(RootingContext::get(cx));
+ }
+
+ void reset() {
+ if (initialized()) {
+ set(SafelyInitialized<T>());
+ ListBase::remove();
+ }
+ }
+
+ DECLARE_POINTER_CONSTREF_OPS(T);
+ DECLARE_POINTER_ASSIGN_OPS(PersistentRooted, T);
+
+ T& get() { return ptr; }
+ const T& get() const { return ptr; }
+
+ T* address() {
+ MOZ_ASSERT(initialized());
+ return PtrTraits::address(ptr);
+ }
+ const T* address() const { return PtrTraits::address(ptr); }
+
+ template <typename U>
+ void set(U&& value) {
+ MOZ_ASSERT(initialized());
+ ptr = std::forward<U>(value);
+ }
+
+ void trace(JSTracer* trc, const char* name);
+
+ private:
+ Ptr ptr;
+} JS_HAZ_ROOTED;
+
+namespace detail {
+
+template <typename T>
+struct DefineComparisonOps<PersistentRooted<T>> : std::true_type {
+ static const T& get(const PersistentRooted<T>& v) { return v.get(); }
+};
+
+} // namespace detail
+
+} /* namespace JS */
+
+namespace js {
+
+template <typename T, typename D, typename Container>
+class WrappedPtrOperations<UniquePtr<T, D>, Container> {
+ const UniquePtr<T, D>& uniquePtr() const {
+ return static_cast<const Container*>(this)->get();
+ }
+
+ public:
+ explicit operator bool() const { return !!uniquePtr(); }
+ T* get() const { return uniquePtr().get(); }
+ T* operator->() const { return get(); }
+ T& operator*() const { return *uniquePtr(); }
+};
+
+template <typename T, typename D, typename Container>
+class MutableWrappedPtrOperations<UniquePtr<T, D>, Container>
+ : public WrappedPtrOperations<UniquePtr<T, D>, Container> {
+ UniquePtr<T, D>& uniquePtr() { return static_cast<Container*>(this)->get(); }
+
+ public:
+ MOZ_MUST_USE typename UniquePtr<T, D>::Pointer release() {
+ return uniquePtr().release();
+ }
+ void reset(T* ptr = T()) { uniquePtr().reset(ptr); }
+};
+
+template <typename T, typename Container>
+class WrappedPtrOperations<mozilla::Maybe<T>, Container> {
+ const mozilla::Maybe<T>& maybe() const {
+ return static_cast<const Container*>(this)->get();
+ }
+
+ public:
+ // This only supports a subset of Maybe's interface.
+ bool isSome() const { return maybe().isSome(); }
+ bool isNothing() const { return maybe().isNothing(); }
+ const T value() const { return maybe().value(); }
+ const T* operator->() const { return maybe().ptr(); }
+ const T& operator*() const { return maybe().ref(); }
+};
+
+template <typename T, typename Container>
+class MutableWrappedPtrOperations<mozilla::Maybe<T>, Container>
+ : public WrappedPtrOperations<mozilla::Maybe<T>, Container> {
+ mozilla::Maybe<T>& maybe() { return static_cast<Container*>(this)->get(); }
+
+ public:
+ // This only supports a subset of Maybe's interface.
+ T* operator->() { return maybe().ptr(); }
+ T& operator*() { return maybe().ref(); }
+ void reset() { return maybe().reset(); }
+};
+
+namespace gc {
+
+template <typename T, typename TraceCallbacks>
+void CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks,
+ const char* aName, void* aClosure) {
+ static_assert(sizeof(T) == sizeof(JS::Heap<T>),
+ "T and Heap<T> must be compatible.");
+ MOZ_ASSERT(v);
+ mozilla::DebugOnly<Cell*> cell = BarrierMethods<T>::asGCThingOrNull(*v);
+ MOZ_ASSERT(cell);
+ MOZ_ASSERT(!IsInsideNursery(cell));
+ JS::Heap<T>* asHeapT = reinterpret_cast<JS::Heap<T>*>(v);
+ aCallbacks.Trace(asHeapT, aName, aClosure);
+}
+
+} /* namespace gc */
+
+} /* namespace js */
+
+#endif /* js_RootingAPI_h */
diff --git a/js/public/SavedFrameAPI.h b/js/public/SavedFrameAPI.h
new file mode 100644
index 0000000000..4883724ff8
--- /dev/null
+++ b/js/public/SavedFrameAPI.h
@@ -0,0 +1,160 @@
+/* -*- 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/. */
+
+/*
+ * Functions and types related to SavedFrame objects created by the Debugger
+ * API.
+ */
+
+#ifndef js_SavedFrameAPI_h
+#define js_SavedFrameAPI_h
+
+#include "jstypes.h" // JS_FRIEND_API, JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+struct JSPrincipals;
+
+namespace JS {
+
+/*
+ * Accessors for working with SavedFrame JSObjects
+ *
+ * Each of these functions assert that if their `HandleObject savedFrame`
+ * argument is non-null, its JSClass is the SavedFrame class (or it is a
+ * cross-compartment or Xray wrapper around an object with the SavedFrame class)
+ * and the object is not the SavedFrame.prototype object.
+ *
+ * Each of these functions will find the first SavedFrame object in the chain
+ * whose underlying stack frame principals are subsumed by the given
+ * |principals|, and operate on that SavedFrame object. This prevents leaking
+ * information about privileged frames to un-privileged callers
+ *
+ * The SavedFrame in parameters do _NOT_ need to be in the same compartment as
+ * the cx, and the various out parameters are _NOT_ guaranteed to be in the same
+ * compartment as cx.
+ *
+ * You may consider or skip over self-hosted frames by passing
+ * `SavedFrameSelfHosted::Include` or `SavedFrameSelfHosted::Exclude`
+ * respectively.
+ *
+ * Additionally, it may be the case that there is no such SavedFrame object
+ * whose captured frame's principals are subsumed by |principals|! If the
+ * `HandleObject savedFrame` argument is null, or the |principals| do not
+ * subsume any of the chained SavedFrame object's principals,
+ * `SavedFrameResult::AccessDenied` is returned and a (hopefully) sane default
+ * value is chosen for the out param.
+ *
+ * See also `js/src/doc/SavedFrame/SavedFrame.md`.
+ */
+
+enum class SavedFrameResult { Ok, AccessDenied };
+
+enum class SavedFrameSelfHosted { Include, Exclude };
+
+/**
+ * Given a SavedFrame JSObject, get its source property. Defaults to the empty
+ * string.
+ */
+extern JS_PUBLIC_API SavedFrameResult GetSavedFrameSource(
+ JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+ MutableHandle<JSString*> sourcep,
+ SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get an ID identifying its ScriptSource.
+ * Defaults to 0.
+ */
+extern JS_PUBLIC_API SavedFrameResult GetSavedFrameSourceId(
+ JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+ uint32_t* sourceIdp,
+ SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its line property. Defaults to 0.
+ */
+extern JS_PUBLIC_API SavedFrameResult GetSavedFrameLine(
+ JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+ uint32_t* linep,
+ SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its column property. Defaults to 0.
+ */
+extern JS_PUBLIC_API SavedFrameResult GetSavedFrameColumn(
+ JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+ uint32_t* columnp,
+ SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its functionDisplayName string, or nullptr
+ * if SpiderMonkey was unable to infer a name for the captured frame's
+ * function. Defaults to nullptr.
+ */
+extern JS_PUBLIC_API SavedFrameResult GetSavedFrameFunctionDisplayName(
+ JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+ MutableHandle<JSString*> namep,
+ SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr.
+ */
+extern JS_PUBLIC_API SavedFrameResult GetSavedFrameAsyncCause(
+ JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+ MutableHandle<JSString*> asyncCausep,
+ SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr
+ * if there is no asyncParent. The `asyncParentp` out parameter is _NOT_
+ * guaranteed to be in the cx's compartment. Defaults to nullptr.
+ */
+extern JS_PUBLIC_API SavedFrameResult GetSavedFrameAsyncParent(
+ JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+ MutableHandle<JSObject*> asyncParentp,
+ SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if
+ * it is the oldest frame in the stack. The `parentp` out parameter is _NOT_
+ * guaranteed to be in the cx's compartment. Defaults to nullptr.
+ */
+extern JS_PUBLIC_API SavedFrameResult GetSavedFrameParent(
+ JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
+ MutableHandle<JSObject*> parentp,
+ SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
+
+/**
+ * Given a SavedFrame object, convert it and its transitive parents to plain
+ * objects. Because SavedFrame objects store their properties on the prototype,
+ * they cannot be usefully stringified to JSON. Assigning their properties to
+ * plain objects allow those objects to be stringified and the saved frame stack
+ * can be encoded as a string.
+ */
+JS_PUBLIC_API JSObject* ConvertSavedFrameToPlainObject(
+ JSContext* cx, JS::HandleObject savedFrame,
+ JS::SavedFrameSelfHosted selfHosted);
+
+} // namespace JS
+
+namespace js {
+
+/**
+ * Get the first SavedFrame object in this SavedFrame stack whose principals are
+ * subsumed by the given |principals|. If there is no such frame, return
+ * nullptr.
+ *
+ * Do NOT pass a non-SavedFrame object here.
+ */
+extern JS_FRIEND_API JSObject* GetFirstSubsumedSavedFrame(
+ JSContext* cx, JSPrincipals* principals, JS::Handle<JSObject*> savedFrame,
+ JS::SavedFrameSelfHosted selfHosted);
+
+} // namespace js
+
+#endif /* js_SavedFrameAPI_h */
diff --git a/js/public/ScalarType.h b/js/public/ScalarType.h
new file mode 100644
index 0000000000..7f53963354
--- /dev/null
+++ b/js/public/ScalarType.h
@@ -0,0 +1,166 @@
+/* -*- 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/. */
+
+/* An enumeration of all possible element types in typed data. */
+
+#ifndef js_ScalarType_h
+#define js_ScalarType_h
+
+#include "mozilla/Assertions.h" // MOZ_CRASH
+
+#include <stddef.h> // size_t
+
+namespace js {
+
+namespace Scalar {
+
+// Scalar types that can appear in typed arrays.
+// The enum values must be kept in sync with:
+// * the TYPEDARRAY_KIND constants
+// * the SCTAG_TYPED_ARRAY constants
+// * JS_FOR_EACH_TYPEDARRAY
+// * JS_FOR_PROTOTYPES_
+// * JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE
+// * JIT compilation
+enum Type {
+ Int8 = 0,
+ Uint8,
+ Int16,
+ Uint16,
+ Int32,
+ Uint32,
+ Float32,
+ Float64,
+
+ /**
+ * Special type that is a uint8_t, but assignments are clamped to [0, 256).
+ * Treat the raw data type as a uint8_t.
+ */
+ Uint8Clamped,
+
+ BigInt64,
+ BigUint64,
+
+ /**
+ * Types that don't have their own TypedArray equivalent, for now.
+ */
+ MaxTypedArrayViewType,
+
+ Int64,
+ Simd128,
+};
+
+static inline size_t byteSize(Type atype) {
+ switch (atype) {
+ case Int8:
+ case Uint8:
+ case Uint8Clamped:
+ return 1;
+ case Int16:
+ case Uint16:
+ return 2;
+ case Int32:
+ case Uint32:
+ case Float32:
+ return 4;
+ case Int64:
+ case Float64:
+ case BigInt64:
+ case BigUint64:
+ return 8;
+ case Simd128:
+ return 16;
+ case MaxTypedArrayViewType:
+ break;
+ }
+ MOZ_CRASH("invalid scalar type");
+}
+
+static inline bool isSignedIntType(Type atype) {
+ switch (atype) {
+ case Int8:
+ case Int16:
+ case Int32:
+ case Int64:
+ case BigInt64:
+ return true;
+ case Uint8:
+ case Uint8Clamped:
+ case Uint16:
+ case Uint32:
+ case Float32:
+ case Float64:
+ case BigUint64:
+ case Simd128:
+ return false;
+ case MaxTypedArrayViewType:
+ break;
+ }
+ MOZ_CRASH("invalid scalar type");
+}
+
+static inline bool isBigIntType(Type atype) {
+ switch (atype) {
+ case BigInt64:
+ case BigUint64:
+ return true;
+ case Int8:
+ case Int16:
+ case Int32:
+ case Int64:
+ case Uint8:
+ case Uint8Clamped:
+ case Uint16:
+ case Uint32:
+ case Float32:
+ case Float64:
+ case Simd128:
+ return false;
+ case MaxTypedArrayViewType:
+ break;
+ }
+ MOZ_CRASH("invalid scalar type");
+}
+
+static inline const char* name(Type atype) {
+ switch (atype) {
+ case Int8:
+ return "Int8";
+ case Uint8:
+ return "Uint8";
+ case Int16:
+ return "Int16";
+ case Uint16:
+ return "Uint16";
+ case Int32:
+ return "Int32";
+ case Uint32:
+ return "Uint32";
+ case Float32:
+ return "Float32";
+ case Float64:
+ return "Float64";
+ case Uint8Clamped:
+ return "Uint8Clamped";
+ case BigInt64:
+ return "BigInt64";
+ case BigUint64:
+ return "BigUint64";
+ case MaxTypedArrayViewType:
+ return "MaxTypedArrayViewType";
+ case Int64:
+ return "Int64";
+ case Simd128:
+ return "Simd128";
+ }
+ MOZ_CRASH("invalid scalar type");
+}
+
+} // namespace Scalar
+
+} // namespace js
+
+#endif // js_ScalarType_h
diff --git a/js/public/SharedArrayBuffer.h b/js/public/SharedArrayBuffer.h
new file mode 100644
index 0000000000..c0a43c9b4d
--- /dev/null
+++ b/js/public/SharedArrayBuffer.h
@@ -0,0 +1,81 @@
+/* -*- 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/. */
+
+/* ArrayBuffer functionality. */
+
+#ifndef js_SharedArrayBuffer_h
+#define js_SharedArrayBuffer_h
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/GCAPI.h" // JS::AutoRequireNoGC
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+// CREATION
+
+/**
+ * Create a new SharedArrayBuffer with the given byte length. This
+ * may only be called if
+ * JS::RealmCreationOptionsRef(cx).getSharedMemoryAndAtomicsEnabled() is
+ * true.
+ */
+extern JS_PUBLIC_API JSObject* NewSharedArrayBuffer(JSContext* cx,
+ uint32_t nbytes);
+
+// TYPE TESTING
+
+/**
+ * Check whether obj supports the JS::GetSharedArrayBuffer* APIs. Note that
+ * this may return false if a security wrapper is encountered that denies the
+ * unwrapping. If this test succeeds, then it is safe to call the various
+ * accessor JSAPI calls defined below.
+ */
+extern JS_PUBLIC_API bool IsSharedArrayBufferObject(JSObject* obj);
+
+// ACCESSORS
+
+extern JS_PUBLIC_API JSObject* UnwrapSharedArrayBuffer(JSObject* obj);
+
+extern JS_PUBLIC_API uint32_t GetSharedArrayBufferByteLength(JSObject* obj);
+
+extern JS_PUBLIC_API uint8_t* GetSharedArrayBufferData(JSObject* obj,
+ bool* isSharedMemory,
+ const AutoRequireNoGC&);
+
+// Ditto for SharedArrayBuffer.
+//
+// There is an isShared out argument for API consistency (eases use from DOM).
+// It will always be set to true.
+extern JS_PUBLIC_API void GetSharedArrayBufferLengthAndData(
+ JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data);
+
+/**
+ * Returns true if there are any live SharedArrayBuffer objects, including those
+ * for wasm memories, associated with the context. This is conservative,
+ * because it does not run GC. Some dead objects may not have been collected
+ * yet and thus will be thought live.
+ */
+extern JS_PUBLIC_API bool ContainsSharedArrayBuffer(JSContext* cx);
+
+/**
+ * Return the isShared flag of a ArrayBufferView subtypes, which denotes whether
+ * the underlying buffer is a SharedArrayBuffer.
+ *
+ * |obj| must have passed a JS_IsArrayBufferViewObject test, or somehow
+ * be known that it would pass such a test: it is a ArrayBufferView subtypes or
+ * a wrapper of a ArrayBufferView subtypes, and the unwrapping will succeed.
+ */
+extern JS_PUBLIC_API bool IsArrayBufferViewShared(JSObject* obj);
+
+} // namespace JS
+
+#endif /* js_SharedArrayBuffer_h */
diff --git a/js/public/SliceBudget.h b/js/public/SliceBudget.h
new file mode 100644
index 0000000000..acfdf34b14
--- /dev/null
+++ b/js/public/SliceBudget.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_SliceBudget_h
+#define js_SliceBudget_h
+
+#include "mozilla/TimeStamp.h"
+
+#include <stdint.h>
+
+#include "jstypes.h"
+
+namespace js {
+
+struct JS_PUBLIC_API TimeBudget {
+ int64_t budget;
+
+ explicit TimeBudget(int64_t milliseconds) { budget = milliseconds; }
+};
+
+struct JS_PUBLIC_API WorkBudget {
+ int64_t budget;
+
+ explicit WorkBudget(int64_t work) { budget = work; }
+};
+
+/*
+ * This class records how much work has been done in a given collection slice,
+ * so that we can return before pausing for too long. Some slices are allowed
+ * to run for unlimited time, and others are bounded. To reduce the number of
+ * gettimeofday calls, we only check the time every 1000 operations.
+ */
+class JS_PUBLIC_API SliceBudget {
+ static mozilla::TimeStamp unlimitedDeadline;
+ static const intptr_t unlimitedStartCounter = INTPTR_MAX;
+
+ bool checkOverBudget();
+
+ SliceBudget();
+
+ public:
+ // Memory of the originally requested budget. If isUnlimited, neither of
+ // these are in use. If deadline==0, then workBudget is valid. Otherwise
+ // timeBudget is valid.
+ TimeBudget timeBudget;
+ WorkBudget workBudget;
+
+ mozilla::TimeStamp deadline;
+ intptr_t counter;
+
+ static const intptr_t CounterReset = 1000;
+
+ static const int64_t UnlimitedTimeBudget = -1;
+ static const int64_t UnlimitedWorkBudget = -1;
+
+ /* Use to create an unlimited budget. */
+ static SliceBudget unlimited() { return SliceBudget(); }
+
+ /* Instantiate as SliceBudget(TimeBudget(n)). */
+ explicit SliceBudget(TimeBudget time);
+
+ /* Instantiate as SliceBudget(WorkBudget(n)). */
+ explicit SliceBudget(WorkBudget work);
+
+ explicit SliceBudget(mozilla::TimeDuration time)
+ : SliceBudget(TimeBudget(time.ToMilliseconds())) {}
+
+ void makeUnlimited() {
+ MOZ_ASSERT(unlimitedDeadline);
+ deadline = unlimitedDeadline;
+ counter = unlimitedStartCounter;
+ }
+
+ void step(intptr_t amt = 1) { counter -= amt; }
+
+ bool isOverBudget() {
+ if (counter > 0) {
+ return false;
+ }
+ return checkOverBudget();
+ }
+
+ bool isWorkBudget() const { return deadline.IsNull(); }
+ bool isTimeBudget() const { return !deadline.IsNull() && !isUnlimited(); }
+ bool isUnlimited() const { return deadline == unlimitedDeadline; }
+
+ int describe(char* buffer, size_t maxlen) const;
+
+ static void Init();
+};
+
+} // namespace js
+
+#endif /* js_SliceBudget_h */
diff --git a/js/public/SourceText.h b/js/public/SourceText.h
new file mode 100644
index 0000000000..118c871095
--- /dev/null
+++ b/js/public/SourceText.h
@@ -0,0 +1,289 @@
+/* -*- 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/. */
+
+/*
+ * SourceText encapsulates a count of char16_t (UTF-16) or Utf8Unit (UTF-8)
+ * code units (note: code *units*, not bytes or code points) and those code
+ * units ("source units"). (Latin-1 is not supported: all places where Latin-1
+ * must be compiled first convert to a supported encoding.)
+ *
+ * A SourceText either observes without owning, or takes ownership of, source
+ * units passed to |SourceText::init|. Thus SourceText can be used to
+ * efficiently avoid copying.
+ *
+ * Rules for use:
+ *
+ * 1) The passed-in source units must be allocated with js_malloc(),
+ * js_calloc(), or js_realloc() if |SourceText::init| is instructed to take
+ * ownership of the source units.
+ * 2) If |SourceText::init| merely borrows the source units, the user must
+ * keep them alive until associated JS compilation is complete.
+ * 3) Code that calls |SourceText::take{Chars,Units}()| must keep the source
+ * units alive until JS compilation completes. Normally only the JS engine
+ * should call |SourceText::take{Chars,Units}()|.
+ * 4) Use the appropriate SourceText parameterization depending on the source
+ * units encoding.
+ *
+ * Example use:
+ *
+ * size_t length = 512;
+ * char16_t* chars = js_pod_malloc<char16_t>(length);
+ * if (!chars) {
+ * JS_ReportOutOfMemory(cx);
+ * return false;
+ * }
+ * JS::SourceText<char16_t> srcBuf;
+ * if (!srcBuf.init(cx, chars, length, JS::SourceOwnership::TakeOwnership)) {
+ * return false;
+ * }
+ * JS::Rooted<JSScript*> script(cx);
+ * if (!JS::Compile(cx, options, srcBuf, &script)) {
+ * return false;
+ * }
+ */
+
+#ifndef js_SourceText_h
+#define js_SourceText_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_COLD, MOZ_IS_CLASS_INIT, MOZ_MUST_USE
+#include "mozilla/Likely.h" // MOZ_UNLIKELY
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+
+#include <stddef.h> // size_t
+#include <stdint.h> // UINT32_MAX
+#include <type_traits> // std::conditional_t, std::is_same_v
+
+#include "js/UniquePtr.h" // js::UniquePtr
+#include "js/Utility.h" // JS::FreePolicy
+
+namespace JS {
+
+namespace detail {
+
+MOZ_COLD extern JS_PUBLIC_API void ReportSourceTooLong(JSContext* cx);
+
+} // namespace detail
+
+enum class SourceOwnership {
+ Borrowed,
+ TakeOwnership,
+};
+
+template <typename Unit>
+class SourceText final {
+ private:
+ static_assert(std::is_same_v<Unit, mozilla::Utf8Unit> ||
+ std::is_same_v<Unit, char16_t>,
+ "Unit must be either char16_t or Utf8Unit for "
+ "SourceText<Unit>");
+
+ /** |char16_t| or |Utf8Unit| source units of uncertain validity. */
+ const Unit* units_ = nullptr;
+
+ /** The length in code units of |units_|. */
+ uint32_t length_ = 0;
+
+ /**
+ * Whether this owns |units_| or merely observes source units owned by some
+ * other object.
+ */
+ bool ownsUnits_ = false;
+
+ public:
+ // A C++ character type that can represent the source units -- suitable for
+ // passing to C++ string functions.
+ using CharT =
+ std::conditional_t<std::is_same_v<Unit, char16_t>, char16_t, char>;
+
+ public:
+ /**
+ * Construct a SourceText. It must be initialized using |init()| before it
+ * can be used as compilation source text.
+ */
+ SourceText() = default;
+
+ /**
+ * Construct a SourceText from contents extracted from |other|. This
+ * SourceText will then act exactly as |other| would have acted, had it
+ * not been passed to this function. |other| will return to its default-
+ * constructed state and must have |init()| called on it to use it.
+ */
+ SourceText(SourceText&& other)
+ : units_(other.units_),
+ length_(other.length_),
+ ownsUnits_(other.ownsUnits_) {
+ other.units_ = nullptr;
+ other.length_ = 0;
+ other.ownsUnits_ = false;
+ }
+
+ ~SourceText() {
+ if (ownsUnits_) {
+ js_free(const_cast<Unit*>(units_));
+ }
+ }
+
+ /**
+ * Initialize this with source unit data: |char16_t| for UTF-16 source
+ * units, or |Utf8Unit| for UTF-8 source units.
+ *
+ * If |ownership == TakeOwnership|, *this function* takes ownership of
+ * |units|, *even if* this function fails, and you MUST NOT free |units|
+ * yourself. This single-owner-friendly approach reduces risk of leaks on
+ * failure.
+ *
+ * |units| may be null if |unitsLength == 0|; if so, this will silently be
+ * initialized using non-null, unowned units.
+ */
+ MOZ_IS_CLASS_INIT MOZ_MUST_USE bool init(JSContext* cx, const Unit* units,
+ size_t unitsLength,
+ SourceOwnership ownership) {
+ MOZ_ASSERT_IF(units == nullptr, unitsLength == 0);
+
+ // Ideally we'd use |Unit| and not cast below, but the risk of a static
+ // initializer is too great.
+ static const CharT emptyString[] = {'\0'};
+
+ // Initialize all fields *before* checking length. This ensures that
+ // if |ownership == SourceOwnership::TakeOwnership|, |units| will be
+ // freed when |this|'s destructor is called.
+ if (units) {
+ units_ = units;
+ length_ = static_cast<uint32_t>(unitsLength);
+ ownsUnits_ = ownership == SourceOwnership::TakeOwnership;
+ } else {
+ units_ = reinterpret_cast<const Unit*>(emptyString);
+ length_ = 0;
+ ownsUnits_ = false;
+ }
+
+ // IMPLEMENTATION DETAIL, DO NOT RELY ON: This limit is used so we can
+ // store offsets in |JSScript|s as |uint32_t|. It could be lifted
+ // fairly easily if desired, as the compiler uses |size_t| internally.
+ if (MOZ_UNLIKELY(unitsLength > UINT32_MAX)) {
+ detail::ReportSourceTooLong(cx);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Exactly identical to the |init()| overload above that accepts
+ * |const Unit*|, but instead takes character data: |const CharT*|.
+ *
+ * (We can't just write this to accept |const CharT*|, because then in the
+ * UTF-16 case this overload and the one above would be identical. So we
+ * use SFINAE to expose the |CharT| overload only if it's different.)
+ */
+ template <typename Char,
+ typename = std::enable_if_t<std::is_same_v<Char, CharT> &&
+ !std::is_same_v<Char, Unit>>>
+ MOZ_IS_CLASS_INIT MOZ_MUST_USE bool init(JSContext* cx, const Char* chars,
+ size_t charsLength,
+ SourceOwnership ownership) {
+ return init(cx, reinterpret_cast<const Unit*>(chars), charsLength,
+ ownership);
+ }
+
+ /**
+ * Initialize this using source units transferred out of |data|.
+ */
+ MOZ_MUST_USE bool init(JSContext* cx,
+ js::UniquePtr<Unit[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return init(cx, data.release(), dataLength, SourceOwnership::TakeOwnership);
+ }
+
+ /**
+ * Exactly identical to the |init()| overload above that accepts
+ * |UniquePtr<Unit[], JS::FreePolicy>|, but instead takes character data:
+ * |UniquePtr<CharT[], JS::FreePolicy>|.
+ *
+ * (We can't just duplicate the signature above with s/Unit/CharT/, because
+ * then in the UTF-16 case this overload and the one above would be identical.
+ * So we use SFINAE to expose the |CharT| overload only if it's different.)
+ */
+ template <typename Char,
+ typename = std::enable_if_t<std::is_same_v<Char, CharT> &&
+ !std::is_same_v<Char, Unit>>>
+ MOZ_MUST_USE bool init(JSContext* cx,
+ js::UniquePtr<Char[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return init(cx, data.release(), dataLength, SourceOwnership::TakeOwnership);
+ }
+
+ /**
+ * Access the encapsulated data using a code unit type.
+ *
+ * This function is useful for code that wants to interact with source text
+ * as *code units*, not as string data. This doesn't matter for UTF-16,
+ * but it's a crucial distinction for UTF-8. When UTF-8 source text is
+ * encapsulated, |Unit| being |mozilla::Utf8Unit| unambiguously indicates
+ * that the code units are UTF-8. In contrast |const char*| returned by
+ * |get()| below could hold UTF-8 (or its ASCII subset) or Latin-1 or (in
+ * particularly cursed embeddings) EBCDIC or some other legacy character
+ * set. Prefer this function to |get()| wherever possible.
+ */
+ const Unit* units() const { return units_; }
+
+ /**
+ * Access the encapsulated data using a character type.
+ *
+ * This function is useful for interactions with character-centric actions
+ * like interacting with UniqueChars/UniqueTwoByteChars or printing out
+ * text in a debugger, that only work with |CharT|. But as |CharT| loses
+ * encoding specificity when UTF-8 source text is encapsulated, prefer
+ * |units()| to this function.
+ */
+ const CharT* get() const { return reinterpret_cast<const CharT*>(units_); }
+
+ /**
+ * Returns true if this owns the source units and will free them on
+ * destruction. If true, it is legal to call |take{Chars,Units}()|.
+ */
+ bool ownsUnits() const { return ownsUnits_; }
+
+ /**
+ * Count of the underlying source units -- code units, not bytes or code
+ * points -- in this.
+ */
+ uint32_t length() const { return length_; }
+
+ /**
+ * Retrieve and take ownership of the underlying source units. The caller
+ * is now responsible for calling js_free() on the returned value, *but
+ * only after JS script compilation has completed*.
+ *
+ * After underlying source units have been taken, this will continue to
+ * refer to the same data -- it just won't own the data. get() and
+ * length() will return the same values, but ownsUnits() will be false.
+ * The taken source units must be kept alive until after JS script
+ * compilation completes, as noted above, for this to be safe.
+ *
+ * The caller must check ownsUnits() before calling takeUnits(). Taking
+ * and then free'ing an unowned buffer will have dire consequences.
+ */
+ Unit* takeUnits() {
+ MOZ_ASSERT(ownsUnits_);
+ ownsUnits_ = false;
+ return const_cast<Unit*>(units_);
+ }
+
+ /**
+ * Akin to |takeUnits()| in all respects, but returns characters rather
+ * than units.
+ */
+ CharT* takeChars() { return reinterpret_cast<CharT*>(takeUnits()); }
+
+ private:
+ SourceText(const SourceText&) = delete;
+ void operator=(const SourceText&) = delete;
+};
+
+} // namespace JS
+
+#endif /* js_SourceText_h */
diff --git a/js/public/StableStringChars.h b/js/public/StableStringChars.h
new file mode 100644
index 0000000000..2ac2e4ec0d
--- /dev/null
+++ b/js/public/StableStringChars.h
@@ -0,0 +1,116 @@
+/* -*- 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/. */
+
+/*
+ * Safely access the contents of a string even as GC can cause the string's
+ * contents to move around in memory.
+ */
+
+#ifndef js_StableStringChars_h
+#define js_StableStringChars_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_INIT_OUTSIDE_CTOR, MOZ_STACK_CLASS, MOZ_MUST_USE
+#include "mozilla/Maybe.h" // mozilla::Maybe
+#include "mozilla/Range.h" // mozilla::Range
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint8_t
+
+#include "jstypes.h" // JS_FRIEND_API
+
+#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
+#include "js/String.h" // JS::GetStringLength
+#include "js/TypeDecls.h" // JSContext, JS::Latin1Char, JSString
+#include "js/Vector.h" // js::Vector
+
+class JSLinearString;
+
+namespace JS {
+
+/**
+ * This class provides safe access to a string's chars across a GC. If we ever
+ * nursery allocate strings' out of line chars, this class will have to make a
+ * copy, so it's best to avoid using this class unless you really need it. It's
+ * usually more efficient to use the latin1Chars/twoByteChars JSString methods
+ * and often the code can be rewritten so that only indexes instead of char
+ * pointers are used in parts of the code that can GC.
+ */
+class MOZ_STACK_CLASS JS_FRIEND_API AutoStableStringChars final {
+ /*
+ * When copying string char, use this many bytes of inline storage. This is
+ * chosen to allow the inline string types to be copied without allocating.
+ * This is asserted in AutoStableStringChars::allocOwnChars.
+ */
+ static const size_t InlineCapacity = 24;
+
+ /* Ensure the string is kept alive while we're using its chars. */
+ Rooted<JSString*> s_;
+ union MOZ_INIT_OUTSIDE_CTOR {
+ const char16_t* twoByteChars_;
+ const Latin1Char* latin1Chars_;
+ };
+ mozilla::Maybe<js::Vector<uint8_t, InlineCapacity>> ownChars_;
+ enum State { Uninitialized, Latin1, TwoByte };
+ State state_;
+
+ public:
+ explicit AutoStableStringChars(JSContext* cx)
+ : s_(cx), state_(Uninitialized) {}
+
+ MOZ_MUST_USE bool init(JSContext* cx, JSString* s);
+
+ /* Like init(), but Latin1 chars are inflated to TwoByte. */
+ MOZ_MUST_USE bool initTwoByte(JSContext* cx, JSString* s);
+
+ bool isLatin1() const { return state_ == Latin1; }
+ bool isTwoByte() const { return state_ == TwoByte; }
+
+ const Latin1Char* latin1Chars() const {
+ MOZ_ASSERT(state_ == Latin1);
+ return latin1Chars_;
+ }
+ const char16_t* twoByteChars() const {
+ MOZ_ASSERT(state_ == TwoByte);
+ return twoByteChars_;
+ }
+
+ mozilla::Range<const Latin1Char> latin1Range() const {
+ MOZ_ASSERT(state_ == Latin1);
+ return mozilla::Range<const Latin1Char>(latin1Chars_, GetStringLength(s_));
+ }
+
+ mozilla::Range<const char16_t> twoByteRange() const {
+ MOZ_ASSERT(state_ == TwoByte);
+ return mozilla::Range<const char16_t>(twoByteChars_, GetStringLength(s_));
+ }
+
+ /* If we own the chars, transfer ownership to the caller. */
+ bool maybeGiveOwnershipToCaller() {
+ MOZ_ASSERT(state_ != Uninitialized);
+ if (!ownChars_.isSome() || !ownChars_->extractRawBuffer()) {
+ return false;
+ }
+ state_ = Uninitialized;
+ ownChars_.reset();
+ return true;
+ }
+
+ private:
+ AutoStableStringChars(const AutoStableStringChars& other) = delete;
+ void operator=(const AutoStableStringChars& other) = delete;
+
+ bool baseIsInline(Handle<JSLinearString*> linearString);
+ template <typename T>
+ T* allocOwnChars(JSContext* cx, size_t count);
+ bool copyLatin1Chars(JSContext* cx, Handle<JSLinearString*> linearString);
+ bool copyTwoByteChars(JSContext* cx, Handle<JSLinearString*> linearString);
+ bool copyAndInflateLatin1Chars(JSContext*,
+ Handle<JSLinearString*> linearString);
+};
+
+} // namespace JS
+
+#endif /* js_StableStringChars_h */
diff --git a/js/public/Stream.h b/js/public/Stream.h
new file mode 100644
index 0000000000..ba4c3c1c49
--- /dev/null
+++ b/js/public/Stream.h
@@ -0,0 +1,542 @@
+/* -*- 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/. */
+
+/*
+ * JSAPI functions and callbacks related to WHATWG Stream objects.
+ *
+ * Much of the API here mirrors the standard algorithms and standard JS methods
+ * of the objects defined in the Streams standard. One difference is that the
+ * functionality of the JS controller object is exposed to C++ as functions
+ * taking ReadableStream instances instead, for convenience.
+ */
+
+#ifndef js_Stream_h
+#define js_Stream_h
+
+#include <stddef.h>
+
+#include "jstypes.h"
+
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+
+struct JSClass;
+
+namespace JS {
+
+/**
+ * Abstract base class for external underlying sources.
+ *
+ * The term "underlying source" is defined in the Streams spec:
+ * https://streams.spec.whatwg.org/#underlying-source
+ *
+ * A `ReadableStreamUnderlyingSource` is an underlying source that is
+ * implemented in C++ rather than JS. It can be passed to
+ * `JS::NewReadableExternalSourceStreamObject` to create a custom,
+ * embedding-defined ReadableStream.
+ *
+ * There are several API difference between this class and the standard API for
+ * underlying sources implemented in JS:
+ *
+ * - JS underlying sources can be either byte sources or non-byte sources.
+ * External underlying source are always byte sources.
+ *
+ * - The C++ API does not bother with controller objects. Instead of using
+ * controller methods, the underlying source directly calls API functions
+ * like JS::ReadableStream{UpdateDataAvailableFromSource,Close,Error}.
+ *
+ * - External readable streams are optimized to allow the embedding to
+ * interact with them with a minimum of overhead: chunks aren't enqueued as
+ * individual typed arrays; instead, the embedding only updates the amount
+ * of data available using
+ * JS::ReadableStreamUpdateDataAvailableFromSource. When JS requests data
+ * from a reader, writeIntoReadRequestBuffer is invoked, asking the
+ * embedding to write data directly into the buffer we're about to hand to
+ * JS.
+ *
+ * - The C++ API provides extra callbacks onClosed() and onErrored().
+ *
+ * - This class has a `finalize()` method, because C++ cares about lifetimes.
+ *
+ * Additionally, ReadableStreamGetExternalUnderlyingSource can be used to get
+ * the pointer to the underlying source. This locks the stream until it is
+ * released again using JS::ReadableStreamReleaseExternalUnderlyingSource.
+ *
+ * Embeddings can use this to optimize away the JS `ReadableStream` overhead
+ * when an embedding-defined C++ stream is passed to an embedding-defined C++
+ * consumer. For example, consider a ServiceWorker piping a `fetch` Response
+ * body to a TextDecoder. Instead of copying chunks of data into JS typed array
+ * buffers and creating a Promise per chunk, only to immediately resolve the
+ * Promises and read the data out again, the embedding can directly feed the
+ * incoming data to the TextDecoder.
+ *
+ * Compartment safety: All methods (except `finalize`) receive `cx` and
+ * `stream` arguments. SpiderMonkey enters the realm of the stream object
+ * before invoking these methods, so `stream` is never a wrapper. Other
+ * arguments may be wrappers.
+ */
+class JS_PUBLIC_API ReadableStreamUnderlyingSource {
+ public:
+ virtual ~ReadableStreamUnderlyingSource() = default;
+
+ /**
+ * Invoked whenever a reader desires more data from this source.
+ *
+ * The given `desiredSize` is the absolute size, not a delta from the
+ * previous desired size.
+ */
+ virtual void requestData(JSContext* cx, HandleObject stream,
+ size_t desiredSize) = 0;
+
+ /**
+ * Invoked to cause the embedding to fill the given `buffer` with data from
+ * this underlying source.
+ *
+ * This is called only after the embedding has updated the amount of data
+ * available using JS::ReadableStreamUpdateDataAvailableFromSource. If at
+ * least one read request is pending when
+ * JS::ReadableStreamUpdateDataAvailableFromSource is called, this method
+ * is invoked immediately from under the call to
+ * JS::ReadableStreamUpdateDataAvailableFromSource. If not, it is invoked
+ * if and when a new read request is made.
+ *
+ * Note: This method *must not cause GC*, because that could potentially
+ * invalidate the `buffer` pointer.
+ */
+ virtual void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream,
+ void* buffer, size_t length,
+ size_t* bytesWritten) = 0;
+
+ /**
+ * Invoked in reaction to the ReadableStream being canceled. This is
+ * equivalent to the `cancel` method on non-external underlying sources
+ * provided to the ReadableStream constructor in JavaScript.
+ *
+ * The underlying source may free up some resources in this method, but
+ * `*this` must not be destroyed until `finalize()` is called.
+ *
+ * The given `reason` is the JS::Value that was passed as an argument to
+ * ReadableStream#cancel().
+ *
+ * The returned JS::Value will be used to resolve the Promise returned by
+ * ReadableStream#cancel().
+ */
+ virtual Value cancel(JSContext* cx, HandleObject stream,
+ HandleValue reason) = 0;
+
+ /**
+ * Invoked when the associated ReadableStream becomes closed.
+ *
+ * The underlying source may free up some resources in this method, but
+ * `*this` must not be destroyed until `finalize()` is called.
+ */
+ virtual void onClosed(JSContext* cx, HandleObject stream) = 0;
+
+ /**
+ * Invoked when the associated ReadableStream becomes errored.
+ *
+ * The underlying source may free up some resources in this method, but
+ * `*this` must not be destroyed until `finalize()` is called.
+ */
+ virtual void onErrored(JSContext* cx, HandleObject stream,
+ HandleValue reason) = 0;
+
+ /**
+ * Invoked when the associated ReadableStream object is finalized. The
+ * stream object is not passed as an argument, as it might not be in a
+ * valid state anymore.
+ *
+ * Note: Finalization can happen on a background thread, so the embedding
+ * must be prepared for `finalize()` to be invoked from any thread.
+ */
+ virtual void finalize() = 0;
+};
+
+/**
+ * Returns a new instance of the ReadableStream builtin class in the current
+ * compartment, configured as a default stream.
+ * If a |proto| is passed, that gets set as the instance's [[Prototype]]
+ * instead of the original value of |ReadableStream.prototype|.
+ */
+extern JS_PUBLIC_API JSObject* NewReadableDefaultStreamObject(
+ JSContext* cx, HandleObject underlyingSource = nullptr,
+ HandleFunction size = nullptr, double highWaterMark = 1,
+ HandleObject proto = nullptr);
+
+/**
+ * Returns a new instance of the ReadableStream builtin class in the current
+ * compartment.
+ *
+ * The instance is a byte stream backed by an embedding-provided underlying
+ * source, using the virtual methods of `underlyingSource` as callbacks. The
+ * embedding must ensure that `*underlyingSource` lives as long as the new
+ * stream object. The JS engine will call the finalize() method when the stream
+ * object is destroyed.
+ *
+ * `nsISupportsObject_alreadyAddreffed` is an optional pointer that can be used
+ * to make the new stream participate in Gecko's cycle collection. Here are the
+ * rules for using this parameter properly:
+ *
+ * - `*underlyingSource` must not be a cycle-collected object. (It would lead
+ * to memory leaks as the cycle collector would not be able to collect
+ * cycles containing that object.)
+ *
+ * - `*underlyingSource` must not contain nsCOMPtrs that point to cycle-
+ * collected objects. (Same reason.)
+ *
+ * - `*underlyingSource` may contain a pointer to a single cycle-collected
+ * object.
+ *
+ * - The pointer may be stored in `*underlyingSource` as a raw pointer.
+ *
+ * - The pointer to the nsISupports interface of the same object must be
+ * passed as the `nsISupportsObject_alreadyAddreffed` parameter to this
+ * function. (This is how the cycle collector knows about it, so omitting
+ * this would again cause leaks.)
+ *
+ * If `proto` is non-null, it is used as the instance's [[Prototype]] instead
+ * of the original value of `ReadableStream.prototype`.
+ */
+extern JS_PUBLIC_API JSObject* NewReadableExternalSourceStreamObject(
+ JSContext* cx, ReadableStreamUnderlyingSource* underlyingSource,
+ void* nsISupportsObject_alreadyAddreffed = nullptr,
+ HandleObject proto = nullptr);
+
+/**
+ * Returns the embedding-provided underlying source of the given |stream|.
+ *
+ * Can be used to optimize operations if both the underlying source and the
+ * intended sink are embedding-provided. In that case it might be
+ * preferrable to pipe data directly from source to sink without interacting
+ * with the stream at all.
+ *
+ * Locks the stream until ReadableStreamReleaseExternalUnderlyingSource is
+ * called.
+ *
+ * Throws an exception if the stream is locked, i.e. if a reader has been
+ * acquired for the stream, or if ReadableStreamGetExternalUnderlyingSource
+ * has been used previously without releasing the external source again.
+ *
+ * Throws an exception if the stream isn't readable, i.e if it is errored or
+ * closed. This is different from ReadableStreamGetReader because we don't
+ * have a Promise to resolve/reject, which a reader provides.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ *
+ * Asserts that the stream has an embedding-provided underlying source.
+ */
+extern JS_PUBLIC_API bool ReadableStreamGetExternalUnderlyingSource(
+ JSContext* cx, HandleObject stream,
+ ReadableStreamUnderlyingSource** source);
+
+/**
+ * Releases the embedding-provided underlying source of the given |stream|,
+ * returning the stream into an unlocked state.
+ *
+ * Asserts that the stream was locked through
+ * ReadableStreamGetExternalUnderlyingSource.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ *
+ * Asserts that the stream has an embedding-provided underlying source.
+ */
+extern JS_PUBLIC_API bool ReadableStreamReleaseExternalUnderlyingSource(
+ JSContext* cx, HandleObject stream);
+
+/**
+ * Update the amount of data available at the underlying source of the given
+ * |stream|.
+ *
+ * Can only be used for streams with an embedding-provided underlying source.
+ * The JS engine will use the given value to satisfy read requests for the
+ * stream by invoking the writeIntoReadRequestBuffer method.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamUpdateDataAvailableFromSource(
+ JSContext* cx, HandleObject stream, uint32_t availableData);
+
+/**
+ * Break the cycle between this object and the
+ * nsISupportsObject_alreadyAddreffed passed in
+ * NewReadableExternalSourceStreamObject().
+ */
+extern JS_PUBLIC_API void ReadableStreamReleaseCCObject(JSObject* stream);
+
+/**
+ * Returns true if the given object is a ReadableStream object or an
+ * unwrappable wrapper for one, false otherwise.
+ */
+extern JS_PUBLIC_API bool IsReadableStream(JSObject* obj);
+
+/**
+ * Returns true if the given object is a ReadableStreamDefaultReader or
+ * ReadableStreamBYOBReader object or an unwrappable wrapper for one, false
+ * otherwise.
+ */
+extern JS_PUBLIC_API bool IsReadableStreamReader(JSObject* obj);
+
+/**
+ * Returns true if the given object is a ReadableStreamDefaultReader object
+ * or an unwrappable wrapper for one, false otherwise.
+ */
+extern JS_PUBLIC_API bool IsReadableStreamDefaultReader(JSObject* obj);
+
+enum class ReadableStreamMode { Default, Byte, ExternalSource };
+
+/**
+ * Returns the stream's ReadableStreamMode. If the mode is |Byte| or
+ * |ExternalSource|, it's possible to acquire a BYOB reader for more optimized
+ * operations.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamGetMode(JSContext* cx,
+ HandleObject stream,
+ ReadableStreamMode* mode);
+
+enum class ReadableStreamReaderMode { Default };
+
+/**
+ * Returns true if the given ReadableStream is readable, false if not.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamIsReadable(JSContext* cx,
+ HandleObject stream,
+ bool* result);
+
+/**
+ * Returns true if the given ReadableStream is locked, false if not.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamIsLocked(JSContext* cx,
+ HandleObject stream,
+ bool* result);
+
+/**
+ * Returns true if the given ReadableStream is disturbed, false if not.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamIsDisturbed(JSContext* cx,
+ HandleObject stream,
+ bool* result);
+
+/**
+ * Cancels the given ReadableStream with the given reason and returns a
+ * Promise resolved according to the result.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API JSObject* ReadableStreamCancel(JSContext* cx,
+ HandleObject stream,
+ HandleValue reason);
+
+/**
+ * Creates a reader of the type specified by the mode option and locks the
+ * stream to the new reader.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one. The returned object will always be created in the
+ * current cx compartment.
+ */
+extern JS_PUBLIC_API JSObject* ReadableStreamGetReader(
+ JSContext* cx, HandleObject stream, ReadableStreamReaderMode mode);
+
+/**
+ * Tees the given ReadableStream and stores the two resulting streams in
+ * outparams. Returns false if the operation fails, e.g. because the stream is
+ * locked.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamTee(JSContext* cx, HandleObject stream,
+ MutableHandleObject branch1Stream,
+ MutableHandleObject branch2Stream);
+
+/**
+ * Retrieves the desired combined size of additional chunks to fill the given
+ * ReadableStream's queue. Stores the result in |value| and sets |hasValue| to
+ * true on success, returns false on failure.
+ *
+ * If the stream is errored, the call will succeed but no value will be stored
+ * in |value| and |hasValue| will be set to false.
+ *
+ * Note: This is semantically equivalent to the |desiredSize| getter on
+ * the stream controller's prototype in JS. We expose it with the stream
+ * itself as a target for simplicity.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamGetDesiredSize(JSContext* cx,
+ JSObject* stream,
+ bool* hasValue,
+ double* value);
+
+/**
+ * Close the given ReadableStream. This is equivalent to `controller.close()`
+ * in JS.
+ *
+ * This can fail with or without an exception pending under a variety of
+ * circumstances. On failure, the stream may or may not be closed, and
+ * downstream consumers may or may not have been notified.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamClose(JSContext* cx,
+ HandleObject stream);
+
+/**
+ * Returns true if the given ReadableStream reader is locked, false otherwise.
+ *
+ * Asserts that |reader| is a ReadableStreamDefaultReader or
+ * ReadableStreamBYOBReader object or an unwrappable wrapper for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamReaderIsClosed(JSContext* cx,
+ HandleObject reader,
+ bool* result);
+
+/**
+ * Enqueues the given chunk in the given ReadableStream.
+ *
+ * Throws a TypeError and returns false if the enqueing operation fails.
+ *
+ * Note: This is semantically equivalent to the |enqueue| method on
+ * the stream controller's prototype in JS. We expose it with the stream
+ * itself as a target for simplicity.
+ *
+ * If the ReadableStream has an underlying byte source, the given chunk must
+ * be a typed array or a DataView. Consider using
+ * ReadableByteStreamEnqueueBuffer.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamEnqueue(JSContext* cx,
+ HandleObject stream,
+ HandleValue chunk);
+
+/**
+ * Errors the given ReadableStream, causing all future interactions to fail
+ * with the given error value.
+ *
+ * Throws a TypeError and returns false if the erroring operation fails.
+ *
+ * Note: This is semantically equivalent to the |error| method on
+ * the stream controller's prototype in JS. We expose it with the stream
+ * itself as a target for simplicity.
+ *
+ * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
+ * for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamError(JSContext* cx,
+ HandleObject stream,
+ HandleValue error);
+
+/**
+ * C++ equivalent of `reader.cancel(reason)`
+ * (both <https://streams.spec.whatwg.org/#default-reader-cancel> and
+ * <https://streams.spec.whatwg.org/#byob-reader-cancel>).
+ *
+ * `reader` must be a stream reader created using `JS::ReadableStreamGetReader`
+ * or an unwrappable wrapper for one. (This function is meant to support using
+ * C++ to read from streams. It's not meant to allow C++ code to operate on
+ * readers created by scripts.)
+ */
+extern JS_PUBLIC_API bool ReadableStreamReaderCancel(JSContext* cx,
+ HandleObject reader,
+ HandleValue reason);
+
+/**
+ * C++ equivalent of `reader.releaseLock()`
+ * (both <https://streams.spec.whatwg.org/#default-reader-release-lock> and
+ * <https://streams.spec.whatwg.org/#byob-reader-release-lock>).
+ *
+ * `reader` must be a stream reader created using `JS::ReadableStreamGetReader`
+ * or an unwrappable wrapper for one.
+ */
+extern JS_PUBLIC_API bool ReadableStreamReaderReleaseLock(JSContext* cx,
+ HandleObject reader);
+
+/**
+ * C++ equivalent of the `reader.read()` method on default readers
+ * (<https://streams.spec.whatwg.org/#default-reader-read>).
+ *
+ * The result is a new Promise object, or null on OOM.
+ *
+ * `reader` must be the result of calling `JS::ReadableStreamGetReader` with
+ * `ReadableStreamReaderMode::Default` mode, or an unwrappable wrapper for such
+ * a reader.
+ */
+extern JS_PUBLIC_API JSObject* ReadableStreamDefaultReaderRead(
+ JSContext* cx, HandleObject reader);
+
+class JS_PUBLIC_API WritableStreamUnderlyingSink {
+ public:
+ virtual ~WritableStreamUnderlyingSink() = default;
+
+ /**
+ * Invoked when the associated WritableStream object is finalized. The
+ * stream object is not passed as an argument, as it might not be in a
+ * valid state anymore.
+ *
+ * Note: Finalization can happen on a background thread, so the embedding
+ * must be prepared for `finalize()` to be invoked from any thread.
+ */
+ virtual void finalize() = 0;
+};
+
+// ReadableStream.prototype.pipeTo SUPPORT
+
+/**
+ * The signature of a function that, when passed an |AbortSignal| instance, will
+ * return the value of its "aborted" flag.
+ *
+ * This function will be called while |signal|'s realm has been entered.
+ */
+using AbortSignalIsAborted = bool (*)(JSObject* signal);
+
+/**
+ * Dictate embedder-specific details necessary to implement certain aspects of
+ * the |ReadableStream.prototype.pipeTo| function. This should be performed
+ * exactly once, for a single context associated with a |JSRuntime|.
+ *
+ * The |ReadableStream.prototype.pipeTo| function accepts a |signal| argument
+ * that may be used to abort the piping operation. This argument must be either
+ * |undefined| (in other words, the piping operation can't be aborted) or an
+ * |AbortSignal| instance (that may be aborted using the signal's associated
+ * |AbortController|). |AbortSignal| is defined by WebIDL and the DOM in the
+ * web embedding. Therefore, embedders must use this function to specify how
+ * such objects can be recognized and how to perform various essential actions
+ * upon them.
+ *
+ * The provided |isAborted| function will be called with an unwrapped
+ * |AbortSignal| instance, while that instance's realm has been entered.
+ *
+ * If this function isn't called, and a situation arises where an "is this an
+ * |AbortSignal|?" question must be asked, that question will simply be answered
+ * "no".
+ */
+extern JS_PUBLIC_API void InitPipeToHandling(const JSClass* abortSignalClass,
+ AbortSignalIsAborted isAborted,
+ JSContext* cx);
+
+} // namespace JS
+
+#endif // js_Stream_h
diff --git a/js/public/String.h b/js/public/String.h
new file mode 100644
index 0000000000..c7fbb18288
--- /dev/null
+++ b/js/public/String.h
@@ -0,0 +1,233 @@
+/* -*- 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/. */
+
+/* JavaScript string operations. */
+
+#ifndef js_String_h
+#define js_String_h
+
+#include "js/shadow/String.h" // JS::shadow::String
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_ALWAYS_INLINE, MOZ_MUST_USE
+#include "mozilla/Likely.h" // MOZ_LIKELY
+
+#include <algorithm> // std::copy_n
+#include <stddef.h> // size_t
+#include <stdint.h> // uint32_t, uint64_t, INT32_MAX
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/TypeDecls.h" // JS::Latin1Char
+
+class JS_PUBLIC_API JSAtom;
+class JSLinearString;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+
+class JS_PUBLIC_API AutoRequireNoGC;
+
+/**
+ * Maximum length of a JS string. This is chosen so that the number of bytes
+ * allocated for a null-terminated TwoByte string still fits in int32_t.
+ */
+static constexpr uint32_t MaxStringLength = (1 << 30) - 2;
+
+static_assert((uint64_t(MaxStringLength) + 1) * sizeof(char16_t) <= INT32_MAX,
+ "size of null-terminated JSString char buffer must fit in "
+ "INT32_MAX");
+
+/** Compute the length of a string. */
+MOZ_ALWAYS_INLINE size_t GetStringLength(JSString* s) {
+ return shadow::AsShadowString(s)->length();
+}
+
+/** Compute the length of a linear string. */
+MOZ_ALWAYS_INLINE size_t GetLinearStringLength(JSLinearString* s) {
+ return shadow::AsShadowString(s)->length();
+}
+
+/** Return true iff the given linear string uses Latin-1 storage. */
+MOZ_ALWAYS_INLINE bool LinearStringHasLatin1Chars(JSLinearString* s) {
+ return shadow::AsShadowString(s)->hasLatin1Chars();
+}
+
+/** Return true iff the given string uses Latin-1 storage. */
+MOZ_ALWAYS_INLINE bool StringHasLatin1Chars(JSString* s) {
+ return shadow::AsShadowString(s)->hasLatin1Chars();
+}
+
+/**
+ * Given a linear string known to use Latin-1 storage, return a pointer to that
+ * storage. This pointer remains valid only as long as no GC occurs.
+ */
+MOZ_ALWAYS_INLINE const Latin1Char* GetLatin1LinearStringChars(
+ const AutoRequireNoGC& nogc, JSLinearString* linear) {
+ return shadow::AsShadowString(linear)->latin1LinearChars();
+}
+
+/**
+ * Given a linear string known to use two-byte storage, return a pointer to that
+ * storage. This pointer remains valid only as long as no GC occurs.
+ */
+MOZ_ALWAYS_INLINE const char16_t* GetTwoByteLinearStringChars(
+ const AutoRequireNoGC& nogc, JSLinearString* linear) {
+ return shadow::AsShadowString(linear)->twoByteLinearChars();
+}
+
+/**
+ * Given an in-range index into the provided string, return the character at
+ * that index.
+ */
+MOZ_ALWAYS_INLINE char16_t GetLinearStringCharAt(JSLinearString* linear,
+ size_t index) {
+ shadow::String* s = shadow::AsShadowString(linear);
+ MOZ_ASSERT(index < s->length());
+
+ return s->hasLatin1Chars() ? s->latin1LinearChars()[index]
+ : s->twoByteLinearChars()[index];
+}
+
+/**
+ * Convert an atom to a linear string. All atoms are linear, so this
+ * operation is infallible.
+ */
+MOZ_ALWAYS_INLINE JSLinearString* AtomToLinearString(JSAtom* atom) {
+ return reinterpret_cast<JSLinearString*>(atom);
+}
+
+/**
+ * If the provided string uses externally-managed storage, return true and set
+ * |*callbacks| to the external-string callbacks used to create it and |*chars|
+ * to a pointer to its two-byte storage. (These pointers remain valid as long
+ * as the provided string is kept alive.)
+ */
+MOZ_ALWAYS_INLINE bool IsExternalString(
+ JSString* str, const JSExternalStringCallbacks** callbacks,
+ const char16_t** chars) {
+ shadow::String* s = shadow::AsShadowString(str);
+
+ if (!s->isExternal()) {
+ return false;
+ }
+
+ *callbacks = s->externalCallbacks;
+ *chars = s->nonInlineCharsTwoByte;
+ return true;
+}
+
+namespace detail {
+
+extern JS_FRIEND_API JSLinearString* StringToLinearStringSlow(JSContext* cx,
+ JSString* str);
+
+} // namespace detail
+
+/** Convert a string to a linear string. */
+MOZ_ALWAYS_INLINE JSLinearString* StringToLinearString(JSContext* cx,
+ JSString* str) {
+ if (MOZ_LIKELY(shadow::AsShadowString(str)->isLinear())) {
+ return reinterpret_cast<JSLinearString*>(str);
+ }
+
+ return detail::StringToLinearStringSlow(cx, str);
+}
+
+/** Copy characters in |s[start..start + len]| to |dest[0..len]|. */
+MOZ_ALWAYS_INLINE void CopyLinearStringChars(char16_t* dest, JSLinearString* s,
+ size_t len, size_t start = 0) {
+#ifdef DEBUG
+ size_t stringLen = GetLinearStringLength(s);
+ MOZ_ASSERT(start <= stringLen);
+ MOZ_ASSERT(len <= stringLen - start);
+#endif
+
+ shadow::String* str = shadow::AsShadowString(s);
+
+ if (str->hasLatin1Chars()) {
+ const Latin1Char* src = str->latin1LinearChars();
+ for (size_t i = 0; i < len; i++) {
+ dest[i] = src[start + i];
+ }
+ } else {
+ const char16_t* src = str->twoByteLinearChars();
+ std::copy_n(src + start, len, dest);
+ }
+}
+
+/**
+ * Copy characters in |s[start..start + len]| to |dest[0..len]|, lossily
+ * truncating 16-bit values to |char| if necessary.
+ */
+MOZ_ALWAYS_INLINE void LossyCopyLinearStringChars(char* dest, JSLinearString* s,
+ size_t len,
+ size_t start = 0) {
+#ifdef DEBUG
+ size_t stringLen = GetLinearStringLength(s);
+ MOZ_ASSERT(start <= stringLen);
+ MOZ_ASSERT(len <= stringLen - start);
+#endif
+
+ shadow::String* str = shadow::AsShadowString(s);
+
+ if (LinearStringHasLatin1Chars(s)) {
+ const Latin1Char* src = str->latin1LinearChars();
+ for (size_t i = 0; i < len; i++) {
+ dest[i] = char(src[start + i]);
+ }
+ } else {
+ const char16_t* src = str->twoByteLinearChars();
+ for (size_t i = 0; i < len; i++) {
+ dest[i] = char(src[start + i]);
+ }
+ }
+}
+
+/**
+ * Copy characters in |s[start..start + len]| to |dest[0..len]|.
+ *
+ * This function is fallible. If you already have a linear string, use the
+ * infallible |JS::CopyLinearStringChars| above instead.
+ */
+inline MOZ_MUST_USE bool CopyStringChars(JSContext* cx, char16_t* dest,
+ JSString* s, size_t len,
+ size_t start = 0) {
+ JSLinearString* linear = StringToLinearString(cx, s);
+ if (!linear) {
+ return false;
+ }
+
+ CopyLinearStringChars(dest, linear, len, start);
+ return true;
+}
+
+/**
+ * Copy characters in |s[start..start + len]| to |dest[0..len]|, lossily
+ * truncating 16-bit values to |char| if necessary.
+ *
+ * This function is fallible. If you already have a linear string, use the
+ * infallible |JS::LossyCopyLinearStringChars| above instead.
+ */
+inline MOZ_MUST_USE bool LossyCopyStringChars(JSContext* cx, char* dest,
+ JSString* s, size_t len,
+ size_t start = 0) {
+ JSLinearString* linear = StringToLinearString(cx, s);
+ if (!linear) {
+ return false;
+ }
+
+ LossyCopyLinearStringChars(dest, linear, len, start);
+ return true;
+}
+
+} // namespace JS
+
+/** DO NOT USE, only present for Rust bindings as a temporary hack */
+[[deprecated]] extern JS_PUBLIC_API bool JS_DeprecatedStringHasLatin1Chars(
+ JSString* str);
+
+#endif // js_String_h
diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h
new file mode 100644
index 0000000000..79dddfd0ac
--- /dev/null
+++ b/js/public/StructuredClone.h
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_StructuredClone_h
+#define js_StructuredClone_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/BufferList.h"
+#include "mozilla/MemoryReporting.h"
+
+#include <stdint.h>
+#include <utility>
+
+#include "jstypes.h"
+
+#include "js/AllocPolicy.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/Value.h"
+#include "js/Vector.h"
+
+/*
+ * API for safe passing of structured data, HTML 2018 Feb 21 section 2.7.
+ * <https://html.spec.whatwg.org/multipage/structured-data.html>
+ *
+ * This is a serialization scheme for JS values, somewhat like JSON. It
+ * preserves some aspects of JS objects (strings, numbers, own data properties
+ * with string keys, array elements) but not others (methods, getters and
+ * setters, prototype chains). Unlike JSON, structured data:
+ *
+ * - can contain cyclic references.
+ *
+ * - handles Maps, Sets, and some other object types.
+ *
+ * - supports *transferring* objects of certain types from one realm to
+ * another, rather than cloning them.
+ *
+ * - is specified by a living standard, and continues to evolve.
+ *
+ * - is encoded in a nonstandard binary format, and is never exposed to Web
+ * content in its serialized form. It's used internally by the browser to
+ * send data from one thread/realm/domain to another, not across the
+ * network.
+ */
+
+struct JSStructuredCloneReader;
+struct JSStructuredCloneWriter;
+
+/**
+ * The structured-clone serialization format version number.
+ *
+ * When serialized data is stored as bytes, e.g. in your Firefox profile, later
+ * versions of the engine may have to read it. When you upgrade Firefox, we
+ * don't crawl through your whole profile converting all saved data from the
+ * previous version of the serialization format to the latest version. So it is
+ * normal to have data in old formats stored in your profile.
+ *
+ * The JS engine can *write* data only in the current format version.
+ *
+ * It can *read* any data written in the current version, and data written for
+ * DifferentProcess scope in earlier versions.
+ *
+ *
+ * ## When to bump this version number
+ *
+ * When making a change so drastic that the JS engine needs to know whether
+ * it's reading old or new serialized data in order to handle both correctly,
+ * increment this version number. Make sure the engine can still read all
+ * old data written with previous versions.
+ *
+ * If StructuredClone.cpp doesn't contain code that distinguishes between
+ * version 8 and version 9, there should not be a version 9.
+ *
+ * Do not increment for changes that only affect SameProcess encoding.
+ *
+ * Increment only for changes that would otherwise break old serialized data.
+ * Do not increment for new data types. (Rationale: Modulo bugs, older versions
+ * of the JS engine can already correctly throw errors when they encounter new,
+ * unrecognized features. A version number bump does not actually help them.)
+ */
+#define JS_STRUCTURED_CLONE_VERSION 8
+
+namespace JS {
+
+/**
+ * Indicates the "scope of validity" of serialized data.
+ *
+ * Writing plain JS data produces an array of bytes that can be copied and
+ * read in another process or whatever. The serialized data is Plain Old Data.
+ * However, HTML also supports `Transferable` objects, which, when cloned, can
+ * be moved from the source object into the clone, like when you take a
+ * photograph of someone and it steals their soul.
+ * See <https://developer.mozilla.org/en-US/docs/Web/API/Transferable>.
+ * We support cloning and transferring objects of many types.
+ *
+ * For example, when we transfer an ArrayBuffer (within a process), we "detach"
+ * the ArrayBuffer, embed the raw buffer pointer in the serialized data, and
+ * later install it in a new ArrayBuffer in the destination realm. Ownership
+ * of that buffer memory is transferred from the original ArrayBuffer to the
+ * serialized data and then to the clone.
+ *
+ * This only makes sense within a single address space. When we transfer an
+ * ArrayBuffer to another process, the contents of the buffer must be copied
+ * into the serialized data. (The original ArrayBuffer is still detached,
+ * though, for consistency; in some cases the caller shouldn't know or care if
+ * the recipient is in the same process.)
+ *
+ * ArrayBuffers are actually a lucky case; some objects (like MessagePorts)
+ * can't reasonably be stored by value in serialized data -- it's pointers or
+ * nothing.
+ *
+ * So there is a tradeoff between scope of validity -- how far away the
+ * serialized data may be sent and still make sense -- and efficiency or
+ * features. The read and write algorithms therefore take an argument of this
+ * type, allowing the user to control those trade-offs.
+ */
+enum class StructuredCloneScope : uint32_t {
+ /**
+ * The most restrictive scope, with greatest efficiency and features.
+ *
+ * When writing, this means: The caller promises that the serialized data
+ * will **not** be shipped off to a different process or stored in a
+ * database. However, it may be shipped to another thread. It's OK to
+ * produce serialized data that contains pointers to data that is safe to
+ * send across threads, such as array buffers. In Rust terms, the
+ * serialized data will be treated as `Send` but not `Copy`.
+ *
+ * When reading, this means: Accept transferred objects and buffers
+ * (pointers). The caller promises that the serialized data was written
+ * using this API (otherwise, the serialized data may contain bogus
+ * pointers, leading to undefined behavior).
+ *
+ * Starts from 1 because there used to be a SameProcessSameThread enum value
+ * of 0 and these values are encoded into the structured serialization format
+ * as part of the SCTAG_HEADER, and IndexedDB persists the representation to
+ * disk.
+ */
+ SameProcess = 1,
+
+ /**
+ * When writing, this means we're writing for an audience in a different
+ * process. Produce serialized data that can be sent to other processes,
+ * bitwise copied, or even stored as bytes in a database and read by later
+ * versions of Firefox years from now. The HTML5 spec refers to this as
+ * "ForStorage" as in StructuredSerializeForStorage, though we use
+ * DifferentProcess for IPC as well as storage.
+ *
+ * Transferable objects are limited to ArrayBuffers, whose contents are
+ * copied into the serialized data (rather than just writing a pointer).
+ *
+ * When reading, this means: Do not accept pointers.
+ */
+ DifferentProcess,
+
+ /**
+ * Handle a backwards-compatibility case with IndexedDB (bug 1434308): when
+ * reading, this means to treat legacy SameProcess data as if it were
+ * DifferentProcess.
+ *
+ * Do not use this for writing; use DifferentProcess instead.
+ */
+ DifferentProcessForIndexedDB,
+
+ /**
+ * Existing code wants to be able to create an uninitialized
+ * JSStructuredCloneData without knowing the scope, then populate it with
+ * data (at which point the scope *is* known.)
+ */
+ Unassigned,
+
+ /**
+ * This scope is used when the deserialization context is unknown. When
+ * writing, DifferentProcess or SameProcess scope is chosen based on the
+ * nature of the object.
+ */
+ UnknownDestination,
+};
+
+enum TransferableOwnership {
+ /** Transferable data has not been filled in yet */
+ SCTAG_TMO_UNFILLED = 0,
+
+ /** Structured clone buffer does not yet own the data */
+ SCTAG_TMO_UNOWNED = 1,
+
+ /** All values at least this large are owned by the clone buffer */
+ SCTAG_TMO_FIRST_OWNED = 2,
+
+ /** Data is a pointer that can be freed */
+ SCTAG_TMO_ALLOC_DATA = 2,
+
+ /** Data is a memory mapped pointer */
+ SCTAG_TMO_MAPPED_DATA = 3,
+
+ /**
+ * Data is embedding-specific. The engine can free it by calling the
+ * freeTransfer op. The embedding can also use SCTAG_TMO_USER_MIN and
+ * greater, up to 32 bits, to distinguish specific ownership variants.
+ */
+ SCTAG_TMO_CUSTOM = 4,
+
+ SCTAG_TMO_USER_MIN
+};
+
+class CloneDataPolicy {
+ bool allowIntraClusterClonableSharedObjects_;
+ bool allowSharedMemoryObjects_;
+
+ public:
+ // The default is to deny all policy-controlled aspects.
+
+ CloneDataPolicy()
+ : allowIntraClusterClonableSharedObjects_(false),
+ allowSharedMemoryObjects_(false) {}
+
+ // SharedArrayBuffers and WASM modules can only be cloned intra-process
+ // because the shared memory areas are allocated in process-private memory or
+ // because there are security issues of sharing them cross agent clusters.
+ // y default, we don't allow shared-memory and intra-cluster objects. Clients
+ // should therefore enable these 2 clone features when needed.
+
+ void allowIntraClusterClonableSharedObjects() {
+ allowIntraClusterClonableSharedObjects_ = true;
+ }
+
+ bool areIntraClusterClonableSharedObjectsAllowed() const {
+ return allowIntraClusterClonableSharedObjects_;
+ }
+
+ void allowSharedMemoryObjects() { allowSharedMemoryObjects_ = true; }
+
+ bool areSharedMemoryObjectsAllowed() const {
+ return allowSharedMemoryObjects_;
+ }
+};
+
+} /* namespace JS */
+
+/**
+ * Read structured data from the reader r. This hook is used to read a value
+ * previously serialized by a call to the WriteStructuredCloneOp hook.
+ *
+ * tag and data are the pair of uint32_t values from the header. The callback
+ * may use the JS_Read* APIs to read any other relevant parts of the object
+ * from the reader r. closure is any value passed to the JS_ReadStructuredClone
+ * function. Return the new object on success, nullptr on error/exception.
+ */
+typedef JSObject* (*ReadStructuredCloneOp)(
+ JSContext* cx, JSStructuredCloneReader* r,
+ const JS::CloneDataPolicy& cloneDataPolicy, uint32_t tag, uint32_t data,
+ void* closure);
+
+/**
+ * Structured data serialization hook. The engine can write primitive values,
+ * Objects, Arrays, Dates, RegExps, TypedArrays, ArrayBuffers, Sets, Maps,
+ * and SharedTypedArrays. Any other type of object requires application support.
+ * This callback must first use the JS_WriteUint32Pair API to write an object
+ * header, passing a value greater than JS_SCTAG_USER to the tag parameter.
+ * Then it can use the JS_Write* APIs to write any other relevant parts of
+ * the value v to the writer w. closure is any value passed to the
+ * JS_WriteStructuredClone function.
+ *
+ * Return true on success, false on error/exception.
+ */
+typedef bool (*WriteStructuredCloneOp)(JSContext* cx,
+ JSStructuredCloneWriter* w,
+ JS::HandleObject obj,
+ bool* sameProcessScopeRequired,
+ void* closure);
+
+/**
+ * This is called when JS_WriteStructuredClone is given an invalid transferable.
+ * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
+ * with error set to one of the JS_SCERR_* values.
+ */
+typedef void (*StructuredCloneErrorOp)(JSContext* cx, uint32_t errorid,
+ void* closure, const char* errorMessage);
+
+/**
+ * This is called when JS_ReadStructuredClone receives a transferable object
+ * not known to the engine. If this hook does not exist or returns false, the
+ * JS engine calls the reportError op if set, otherwise it throws a
+ * DATA_CLONE_ERR DOM Exception. This method is called before any other
+ * callback and must return a non-null object in returnObject on success.
+ */
+typedef bool (*ReadTransferStructuredCloneOp)(
+ JSContext* cx, JSStructuredCloneReader* r, uint32_t tag, void* content,
+ uint64_t extraData, void* closure, JS::MutableHandleObject returnObject);
+
+/**
+ * Called when JS_WriteStructuredClone receives a transferable object not
+ * handled by the engine. If this hook does not exist or returns false, the JS
+ * engine will call the reportError hook or fall back to throwing a
+ * DATA_CLONE_ERR DOM Exception. This method is called before any other
+ * callback.
+ *
+ * tag: indicates what type of transferable this is. Must be greater than
+ * 0xFFFF0201 (value of the internal SCTAG_TRANSFER_MAP_PENDING_ENTRY)
+ *
+ * ownership: see TransferableOwnership, above. Used to communicate any needed
+ * ownership info to the FreeTransferStructuredCloneOp.
+ *
+ * content, extraData: what the ReadTransferStructuredCloneOp will receive
+ */
+typedef bool (*TransferStructuredCloneOp)(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ void* closure,
+ // Output:
+ uint32_t* tag,
+ JS::TransferableOwnership* ownership,
+ void** content, uint64_t* extraData);
+
+/**
+ * Called when freeing an unknown transferable object. Note that it
+ * should never trigger a garbage collection (and will assert in a
+ * debug build if it does.)
+ */
+typedef void (*FreeTransferStructuredCloneOp)(
+ uint32_t tag, JS::TransferableOwnership ownership, void* content,
+ uint64_t extraData, void* closure);
+
+/**
+ * Called when the transferring objects are checked. If this function returns
+ * false, the serialization ends throwing a DataCloneError exception.
+ */
+typedef bool (*CanTransferStructuredCloneOp)(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ bool* sameProcessScopeRequired,
+ void* closure);
+
+/**
+ * Called when a SharedArrayBuffer (including one owned by a Wasm memory object)
+ * has been processed in context `cx` by structured cloning. If `receiving` is
+ * true then the SAB has been received from a channel and a new SAB object has
+ * been created; if false then an existing SAB has been serialized onto a
+ * channel.
+ *
+ * If the callback returns false then the clone operation (read or write) will
+ * signal a failure.
+ */
+typedef bool (*SharedArrayBufferClonedOp)(JSContext* cx, bool receiving,
+ void* closure);
+
+struct JSStructuredCloneCallbacks {
+ ReadStructuredCloneOp read;
+ WriteStructuredCloneOp write;
+ StructuredCloneErrorOp reportError;
+ ReadTransferStructuredCloneOp readTransfer;
+ TransferStructuredCloneOp writeTransfer;
+ FreeTransferStructuredCloneOp freeTransfer;
+ CanTransferStructuredCloneOp canTransfer;
+ SharedArrayBufferClonedOp sabCloned;
+};
+
+enum OwnTransferablePolicy {
+ /**
+ * The buffer owns any Transferables that it might contain, and should
+ * properly release them upon destruction.
+ */
+ OwnsTransferablesIfAny,
+
+ /**
+ * Do not free any Transferables within this buffer when deleting it. This
+ * is used to mark as clone buffer as containing data from another process,
+ * and so it can't legitimately contain pointers. If the buffer claims to
+ * have transferables, it's a bug or an attack. This is also used for
+ * abandon(), where a buffer still contains raw data but the ownership has
+ * been given over to some other entity.
+ */
+ IgnoreTransferablesIfAny,
+
+ /**
+ * A buffer that cannot contain Transferables at all. This usually means
+ * the buffer is empty (not yet filled in, or having been cleared).
+ */
+ NoTransferables
+};
+
+namespace js {
+class SharedArrayRawBuffer;
+
+class SharedArrayRawBufferRefs {
+ public:
+ SharedArrayRawBufferRefs() = default;
+ SharedArrayRawBufferRefs(SharedArrayRawBufferRefs&& other) = default;
+ SharedArrayRawBufferRefs& operator=(SharedArrayRawBufferRefs&& other);
+ ~SharedArrayRawBufferRefs();
+
+ MOZ_MUST_USE bool acquire(JSContext* cx, SharedArrayRawBuffer* rawbuf);
+ MOZ_MUST_USE bool acquireAll(JSContext* cx,
+ const SharedArrayRawBufferRefs& that);
+ void takeOwnership(SharedArrayRawBufferRefs&&);
+ void releaseAll();
+
+ private:
+ js::Vector<js::SharedArrayRawBuffer*, 0, js::SystemAllocPolicy> refs_;
+};
+
+template <typename T, typename AllocPolicy>
+struct BufferIterator;
+} // namespace js
+
+/**
+ * JSStructuredCloneData represents structured clone data together with the
+ * information needed to read/write/transfer/free the records within it, in the
+ * form of a set of callbacks.
+ */
+class MOZ_NON_MEMMOVABLE JS_PUBLIC_API JSStructuredCloneData {
+ public:
+ using BufferList = mozilla::BufferList<js::SystemAllocPolicy>;
+ using Iterator = BufferList::IterImpl;
+
+ private:
+ static const size_t kStandardCapacity = 4096;
+
+ BufferList bufList_;
+
+ // The (address space, thread) scope within which this clone is valid. Note
+ // that this must be either set during construction, or start out as
+ // Unassigned and transition once to something else.
+ JS::StructuredCloneScope scope_;
+
+ const JSStructuredCloneCallbacks* callbacks_ = nullptr;
+ void* closure_ = nullptr;
+ OwnTransferablePolicy ownTransferables_ =
+ OwnTransferablePolicy::NoTransferables;
+ js::SharedArrayRawBufferRefs refsHeld_;
+
+ friend struct JSStructuredCloneWriter;
+ friend class JS_PUBLIC_API JSAutoStructuredCloneBuffer;
+ template <typename T, typename AllocPolicy>
+ friend struct js::BufferIterator;
+
+ public:
+ // The constructor must be infallible but SystemAllocPolicy is not, so both
+ // the initial size and initial capacity of the BufferList must be zero.
+ explicit JSStructuredCloneData(JS::StructuredCloneScope scope)
+ : bufList_(0, 0, kStandardCapacity, js::SystemAllocPolicy()),
+ scope_(scope),
+ callbacks_(nullptr),
+ closure_(nullptr),
+ ownTransferables_(OwnTransferablePolicy::NoTransferables) {}
+
+ // Steal the raw data from a BufferList. In this case, we don't know the
+ // scope and none of the callback info is assigned yet.
+ JSStructuredCloneData(BufferList&& buffers, JS::StructuredCloneScope scope)
+ : bufList_(std::move(buffers)),
+ scope_(scope),
+ callbacks_(nullptr),
+ closure_(nullptr),
+ ownTransferables_(OwnTransferablePolicy::NoTransferables) {}
+ MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers)
+ : JSStructuredCloneData(std::move(buffers),
+ JS::StructuredCloneScope::Unassigned) {}
+ JSStructuredCloneData(JSStructuredCloneData&& other) = default;
+ JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default;
+ ~JSStructuredCloneData() { discardTransferables(); }
+
+ void setCallbacks(const JSStructuredCloneCallbacks* callbacks, void* closure,
+ OwnTransferablePolicy policy) {
+ callbacks_ = callbacks;
+ closure_ = closure;
+ ownTransferables_ = policy;
+ }
+
+ MOZ_MUST_USE bool Init(size_t initialCapacity = 0) {
+ return bufList_.Init(0, initialCapacity);
+ }
+
+ JS::StructuredCloneScope scope() const {
+ if (scope_ == JS::StructuredCloneScope::UnknownDestination) {
+ return JS::StructuredCloneScope::DifferentProcess;
+ }
+ return scope_;
+ }
+
+ void sameProcessScopeRequired() {
+ if (scope_ == JS::StructuredCloneScope::UnknownDestination) {
+ scope_ = JS::StructuredCloneScope::SameProcess;
+ }
+ }
+
+ void initScope(JS::StructuredCloneScope newScope) {
+ MOZ_ASSERT(Size() == 0, "initScope() of nonempty JSStructuredCloneData");
+ if (scope() != JS::StructuredCloneScope::Unassigned) {
+ MOZ_ASSERT(scope() == newScope,
+ "Cannot change scope after it has been initialized");
+ }
+ scope_ = newScope;
+ }
+
+ size_t Size() const { return bufList_.Size(); }
+
+ const Iterator Start() const { return bufList_.Iter(); }
+
+ MOZ_MUST_USE bool Advance(Iterator& iter, size_t distance) const {
+ return iter.AdvanceAcrossSegments(bufList_, distance);
+ }
+
+ MOZ_MUST_USE bool ReadBytes(Iterator& iter, char* buffer, size_t size) const {
+ return bufList_.ReadBytes(iter, buffer, size);
+ }
+
+ // Append new data to the end of the buffer.
+ MOZ_MUST_USE bool AppendBytes(const char* data, size_t size) {
+ MOZ_ASSERT(scope() != JS::StructuredCloneScope::Unassigned);
+ return bufList_.WriteBytes(data, size);
+ }
+
+ // Update data stored within the existing buffer. There must be at least
+ // 'size' bytes between the position of 'iter' and the end of the buffer.
+ MOZ_MUST_USE bool UpdateBytes(Iterator& iter, const char* data,
+ size_t size) const {
+ MOZ_ASSERT(scope() != JS::StructuredCloneScope::Unassigned);
+ while (size > 0) {
+ size_t remaining = iter.RemainingInSegment();
+ size_t nbytes = std::min(remaining, size);
+ memcpy(iter.Data(), data, nbytes);
+ data += nbytes;
+ size -= nbytes;
+ iter.Advance(bufList_, nbytes);
+ }
+ return true;
+ }
+
+ char* AllocateBytes(size_t maxSize, size_t* size) {
+ return bufList_.AllocateBytes(maxSize, size);
+ }
+
+ void Clear() {
+ discardTransferables();
+ bufList_.Clear();
+ }
+
+ // Return a new read-only JSStructuredCloneData that "borrows" the contents
+ // of |this|. Its lifetime should not exceed the donor's. This is only
+ // allowed for DifferentProcess clones, so finalization of the borrowing
+ // clone will do nothing.
+ JSStructuredCloneData Borrow(Iterator& iter, size_t size,
+ bool* success) const {
+ MOZ_ASSERT(scope() == JS::StructuredCloneScope::DifferentProcess);
+ return JSStructuredCloneData(
+ bufList_.Borrow<js::SystemAllocPolicy>(iter, size, success), scope());
+ }
+
+ // Iterate over all contained data, one BufferList segment's worth at a
+ // time, and invoke the given FunctionToApply with the data pointer and
+ // size. The function should return a bool value, and this loop will exit
+ // with false if the function ever returns false.
+ template <typename FunctionToApply>
+ bool ForEachDataChunk(FunctionToApply&& function) const {
+ Iterator iter = bufList_.Iter();
+ while (!iter.Done()) {
+ if (!function(iter.Data(), iter.RemainingInSegment())) {
+ return false;
+ }
+ iter.Advance(bufList_, iter.RemainingInSegment());
+ }
+ return true;
+ }
+
+ // Append the entire contents of other's bufList_ to our own.
+ MOZ_MUST_USE bool Append(const JSStructuredCloneData& other) {
+ MOZ_ASSERT(scope() == other.scope());
+ return other.ForEachDataChunk(
+ [&](const char* data, size_t size) { return AppendBytes(data, size); });
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+ return bufList_.SizeOfExcludingThis(mallocSizeOf);
+ }
+
+ void discardTransferables();
+
+ private:
+ // This internal method exposes the real value of scope_. It's meant to be
+ // used only when starting the writing.
+ JS::StructuredCloneScope scopeForInternalWriting() const { return scope_; }
+};
+
+/**
+ * Implements StructuredDeserialize and StructuredDeserializeWithTransfer.
+ *
+ * Note: If `data` contains transferable objects, it can be read only once.
+ */
+JS_PUBLIC_API bool JS_ReadStructuredClone(
+ JSContext* cx, const JSStructuredCloneData& data, uint32_t version,
+ JS::StructuredCloneScope scope, JS::MutableHandleValue vp,
+ const JS::CloneDataPolicy& cloneDataPolicy,
+ const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
+
+/**
+ * Implements StructuredSerialize, StructuredSerializeForStorage, and
+ * StructuredSerializeWithTransfer.
+ *
+ * Note: If the scope is DifferentProcess then the cloneDataPolicy must deny
+ * shared-memory objects, or an error will be signaled if a shared memory object
+ * is seen.
+ */
+JS_PUBLIC_API bool JS_WriteStructuredClone(
+ JSContext* cx, JS::HandleValue v, JSStructuredCloneData* data,
+ JS::StructuredCloneScope scope, const JS::CloneDataPolicy& cloneDataPolicy,
+ const JSStructuredCloneCallbacks* optionalCallbacks, void* closure,
+ JS::HandleValue transferable);
+
+JS_PUBLIC_API bool JS_StructuredCloneHasTransferables(
+ JSStructuredCloneData& data, bool* hasTransferable);
+
+JS_PUBLIC_API bool JS_StructuredClone(
+ JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
+ const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
+
+/**
+ * The C-style API calls to read and write structured clones are fragile --
+ * they rely on the caller to properly handle ownership of the clone data, and
+ * the handling of the input data as well as the interpretation of the contents
+ * of the clone buffer are dependent on the callbacks passed in. If you
+ * serialize and deserialize with different callbacks, the results are
+ * questionable.
+ *
+ * JSAutoStructuredCloneBuffer wraps things up in an RAII class for data
+ * management, and uses the same callbacks for both writing and reading
+ * (serializing and deserializing).
+ */
+class JS_PUBLIC_API JSAutoStructuredCloneBuffer {
+ JSStructuredCloneData data_;
+ uint32_t version_;
+
+ public:
+ JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
+ const JSStructuredCloneCallbacks* callbacks,
+ void* closure)
+ : data_(scope), version_(JS_STRUCTURED_CLONE_VERSION) {
+ data_.setCallbacks(callbacks, closure,
+ OwnTransferablePolicy::NoTransferables);
+ }
+
+ JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
+ JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other);
+
+ ~JSAutoStructuredCloneBuffer() { clear(); }
+
+ JSStructuredCloneData& data() { return data_; }
+ bool empty() const { return !data_.Size(); }
+
+ void clear();
+
+ JS::StructuredCloneScope scope() const { return data_.scope(); }
+
+ /**
+ * Adopt some memory. It will be automatically freed by the destructor.
+ * data must have been allocated by the JS engine (e.g., extracted via
+ * JSAutoStructuredCloneBuffer::steal).
+ */
+ void adopt(JSStructuredCloneData&& data,
+ uint32_t version = JS_STRUCTURED_CLONE_VERSION,
+ const JSStructuredCloneCallbacks* callbacks = nullptr,
+ void* closure = nullptr);
+
+ /**
+ * Release the buffer and transfer ownership to the caller.
+ */
+ void steal(JSStructuredCloneData* data, uint32_t* versionp = nullptr,
+ const JSStructuredCloneCallbacks** callbacks = nullptr,
+ void** closure = nullptr);
+
+ /**
+ * Abandon ownership of any transferable objects stored in the buffer,
+ * without freeing the buffer itself. Useful when copying the data out into
+ * an external container, though note that you will need to use adopt() to
+ * properly release that data eventually.
+ */
+ void abandon() {
+ data_.ownTransferables_ = OwnTransferablePolicy::IgnoreTransferablesIfAny;
+ }
+
+ bool read(JSContext* cx, JS::MutableHandleValue vp,
+ const JS::CloneDataPolicy& cloneDataPolicy = JS::CloneDataPolicy(),
+ const JSStructuredCloneCallbacks* optionalCallbacks = nullptr,
+ void* closure = nullptr);
+
+ bool write(JSContext* cx, JS::HandleValue v,
+ const JSStructuredCloneCallbacks* optionalCallbacks = nullptr,
+ void* closure = nullptr);
+
+ bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable,
+ const JS::CloneDataPolicy& cloneDataPolicy,
+ const JSStructuredCloneCallbacks* optionalCallbacks = nullptr,
+ void* closure = nullptr);
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+ return data_.SizeOfExcludingThis(mallocSizeOf);
+ }
+
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+ return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+ }
+
+ private:
+ // Copy and assignment are not supported.
+ JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& other) =
+ delete;
+ JSAutoStructuredCloneBuffer& operator=(
+ const JSAutoStructuredCloneBuffer& other) = delete;
+};
+
+// The range of tag values the application may use for its own custom object
+// types.
+#define JS_SCTAG_USER_MIN ((uint32_t)0xFFFF8000)
+#define JS_SCTAG_USER_MAX ((uint32_t)0xFFFFFFFF)
+
+#define JS_SCERR_RECURSION 0
+#define JS_SCERR_TRANSFERABLE 1
+#define JS_SCERR_DUP_TRANSFERABLE 2
+#define JS_SCERR_UNSUPPORTED_TYPE 3
+#define JS_SCERR_SHMEM_TRANSFERABLE 4
+#define JS_SCERR_TYPED_ARRAY_DETACHED 5
+#define JS_SCERR_WASM_NO_TRANSFER 6
+#define JS_SCERR_NOT_CLONABLE 7
+#define JS_SCERR_NOT_CLONABLE_WITH_COOP_COEP 8
+
+JS_PUBLIC_API bool JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1,
+ uint32_t* p2);
+
+JS_PUBLIC_API bool JS_ReadBytes(JSStructuredCloneReader* r, void* p,
+ size_t len);
+
+JS_PUBLIC_API bool JS_ReadTypedArray(JSStructuredCloneReader* r,
+ JS::MutableHandleValue vp);
+
+JS_PUBLIC_API bool JS_WriteUint32Pair(JSStructuredCloneWriter* w, uint32_t tag,
+ uint32_t data);
+
+JS_PUBLIC_API bool JS_WriteBytes(JSStructuredCloneWriter* w, const void* p,
+ size_t len);
+
+JS_PUBLIC_API bool JS_WriteString(JSStructuredCloneWriter* w,
+ JS::HandleString str);
+
+JS_PUBLIC_API bool JS_WriteTypedArray(JSStructuredCloneWriter* w,
+ JS::HandleValue v);
+
+JS_PUBLIC_API bool JS_ObjectNotWritten(JSStructuredCloneWriter* w,
+ JS::HandleObject obj);
+
+JS_PUBLIC_API JS::StructuredCloneScope JS_GetStructuredCloneScope(
+ JSStructuredCloneWriter* w);
+
+#endif /* js_StructuredClone_h */
diff --git a/js/public/SweepingAPI.h b/js/public/SweepingAPI.h
new file mode 100644
index 0000000000..25be71890f
--- /dev/null
+++ b/js/public/SweepingAPI.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_SweepingAPI_h
+#define js_SweepingAPI_h
+
+#include "js/HeapAPI.h"
+
+namespace js {
+namespace gc {
+
+class StoreBuffer;
+
+JS_PUBLIC_API void LockStoreBuffer(StoreBuffer* sb);
+JS_PUBLIC_API void UnlockStoreBuffer(StoreBuffer* sb);
+
+class AutoLockStoreBuffer {
+ StoreBuffer* sb;
+
+ public:
+ explicit AutoLockStoreBuffer(StoreBuffer* sb) : sb(sb) {
+ LockStoreBuffer(sb);
+ }
+ ~AutoLockStoreBuffer() { UnlockStoreBuffer(sb); }
+};
+
+} // namespace gc
+} // namespace js
+
+namespace JS {
+namespace detail {
+class WeakCacheBase;
+} // namespace detail
+
+namespace shadow {
+JS_PUBLIC_API void RegisterWeakCache(JS::Zone* zone,
+ JS::detail::WeakCacheBase* cachep);
+JS_PUBLIC_API void RegisterWeakCache(JSRuntime* rt,
+ JS::detail::WeakCacheBase* cachep);
+} // namespace shadow
+
+namespace detail {
+class WeakCacheBase : public mozilla::LinkedListElement<WeakCacheBase> {
+ WeakCacheBase() = delete;
+ explicit WeakCacheBase(const WeakCacheBase&) = delete;
+
+ public:
+ explicit WeakCacheBase(Zone* zone) { shadow::RegisterWeakCache(zone, this); }
+ explicit WeakCacheBase(JSRuntime* rt) { shadow::RegisterWeakCache(rt, this); }
+ WeakCacheBase(WeakCacheBase&& other) = default;
+ virtual ~WeakCacheBase() = default;
+
+ virtual size_t sweep(js::gc::StoreBuffer* sbToLock) = 0;
+ virtual bool needsSweep() = 0;
+
+ virtual bool setNeedsIncrementalBarrier(bool needs) {
+ // Derived classes do not support incremental barriers by default.
+ return false;
+ }
+ virtual bool needsIncrementalBarrier() const {
+ // Derived classes do not support incremental barriers by default.
+ return false;
+ }
+};
+} // namespace detail
+
+// A WeakCache stores the given Sweepable container and links itself into a
+// list of such caches that are swept during each GC. A WeakCache can be
+// specific to a zone, or across a whole runtime, depending on which
+// constructor is used.
+template <typename T>
+class WeakCache : protected detail::WeakCacheBase,
+ public js::MutableWrappedPtrOperations<T, WeakCache<T>> {
+ T cache;
+
+ public:
+ using Type = T;
+
+ template <typename... Args>
+ explicit WeakCache(Zone* zone, Args&&... args)
+ : WeakCacheBase(zone), cache(std::forward<Args>(args)...) {}
+ template <typename... Args>
+ explicit WeakCache(JSRuntime* rt, Args&&... args)
+ : WeakCacheBase(rt), cache(std::forward<Args>(args)...) {}
+
+ const T& get() const { return cache; }
+ T& get() { return cache; }
+
+ size_t sweep(js::gc::StoreBuffer* sbToLock) override {
+ // Take the store buffer lock in case sweeping triggers any generational
+ // post barriers. This is not always required and WeakCache specializations
+ // may delay or skip taking the lock as appropriate.
+ mozilla::Maybe<js::gc::AutoLockStoreBuffer> lock;
+ if (sbToLock) {
+ lock.emplace(sbToLock);
+ }
+
+ GCPolicy<T>::sweep(&cache);
+ return 0;
+ }
+
+ bool needsSweep() override { return cache.needsSweep(); }
+} JS_HAZ_NON_GC_POINTER;
+
+} // namespace JS
+
+#endif // js_SweepingAPI_h
diff --git a/js/public/Symbol.h b/js/public/Symbol.h
new file mode 100644
index 0000000000..1e16552b36
--- /dev/null
+++ b/js/public/Symbol.h
@@ -0,0 +1,110 @@
+/* -*- 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/. */
+
+/* Symbols. */
+
+#ifndef js_Symbol_h
+#define js_Symbol_h
+
+#include "js/shadow/Symbol.h" // JS::shadow::Symbol::WellKnownAPILimit
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uintptr_t, uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+class JS_PUBLIC_API Symbol;
+
+/**
+ * Create a new Symbol with the given description. This function never returns
+ * a Symbol that is in the Runtime-wide symbol registry.
+ *
+ * If description is null, the new Symbol's [[Description]] attribute is
+ * undefined.
+ */
+extern JS_PUBLIC_API Symbol* NewSymbol(JSContext* cx,
+ Handle<JSString*> description);
+
+/**
+ * Symbol.for as specified in ES6.
+ *
+ * Get a Symbol with the description 'key' from the Runtime-wide symbol
+ * registry. If there is not already a Symbol with that description in the
+ * registry, a new Symbol is created and registered. 'key' must not be null.
+ */
+extern JS_PUBLIC_API Symbol* GetSymbolFor(JSContext* cx, Handle<JSString*> key);
+
+/**
+ * Get the [[Description]] attribute of the given symbol.
+ *
+ * This function is infallible. If it returns null, that means the symbol's
+ * [[Description]] is undefined.
+ */
+extern JS_PUBLIC_API JSString* GetSymbolDescription(Handle<Symbol*> symbol);
+
+/* Well-known symbols. */
+#define JS_FOR_EACH_WELL_KNOWN_SYMBOL(MACRO) \
+ MACRO(isConcatSpreadable) \
+ MACRO(iterator) \
+ MACRO(match) \
+ MACRO(replace) \
+ MACRO(search) \
+ MACRO(species) \
+ MACRO(hasInstance) \
+ MACRO(split) \
+ MACRO(toPrimitive) \
+ MACRO(toStringTag) \
+ MACRO(unscopables) \
+ MACRO(asyncIterator) \
+ MACRO(matchAll)
+
+enum class SymbolCode : uint32_t {
+// There is one SymbolCode for each well-known symbol.
+#define JS_DEFINE_SYMBOL_ENUM(name) name,
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(
+ JS_DEFINE_SYMBOL_ENUM) // SymbolCode::iterator, etc.
+#undef JS_DEFINE_SYMBOL_ENUM
+ Limit,
+ WellKnownAPILimit = JS::shadow::Symbol::WellKnownAPILimit,
+ PrivateNameSymbol = 0xfffffffd, // created by the #PrivateName syntax.
+ InSymbolRegistry =
+ 0xfffffffe, // created by Symbol.for() or JS::GetSymbolFor()
+ UniqueSymbol = 0xffffffff // created by Symbol() or JS::NewSymbol()
+};
+
+/* For use in loops that iterate over the well-known symbols. */
+const size_t WellKnownSymbolLimit = size_t(SymbolCode::Limit);
+
+/**
+ * Return the SymbolCode telling what sort of symbol `symbol` is.
+ *
+ * A symbol's SymbolCode never changes once it is created.
+ */
+extern JS_PUBLIC_API SymbolCode GetSymbolCode(Handle<Symbol*> symbol);
+
+/**
+ * Get one of the well-known symbols defined by ES6. A single set of well-known
+ * symbols is shared by all compartments in a JSRuntime.
+ *
+ * `which` must be in the range [0, WellKnownSymbolLimit).
+ */
+extern JS_PUBLIC_API Symbol* GetWellKnownSymbol(JSContext* cx,
+ SymbolCode which);
+
+/**
+ * Return true if the given JSPropertySpec::name or JSFunctionSpec::name value
+ * is actually a symbol code and not a string. See JS_SYM_FN.
+ */
+inline bool PropertySpecNameIsSymbol(uintptr_t name) {
+ return name != 0 && name - 1 < WellKnownSymbolLimit;
+}
+
+} // namespace JS
+
+#endif /* js_Symbol_h */
diff --git a/js/public/TraceKind.h b/js/public/TraceKind.h
new file mode 100644
index 0000000000..d59cc5c92a
--- /dev/null
+++ b/js/public/TraceKind.h
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_TraceKind_h
+#define js_TraceKind_h
+
+#include "mozilla/UniquePtr.h"
+
+#include "js/TypeDecls.h"
+
+// Forward declarations of all the types a TraceKind can denote.
+class JSLinearString;
+
+namespace js {
+class BaseScript;
+class BaseShape;
+class ObjectGroup;
+class RegExpShared;
+class Shape;
+class Scope;
+namespace jit {
+class JitCode;
+} // namespace jit
+} // namespace js
+
+namespace JS {
+
+// When tracing a thing, the GC needs to know about the layout of the object it
+// is looking at. There are a fixed number of different layouts that the GC
+// knows about. The "trace kind" is a static map which tells which layout a GC
+// thing has.
+//
+// Although this map is public, the details are completely hidden. Not all of
+// the matching C++ types are exposed, and those that are, are opaque.
+//
+// See Value::gcKind() and JSTraceCallback in Tracer.h for more details.
+enum class TraceKind {
+ // These trace kinds have a publicly exposed, although opaque, C++ type.
+ // Note: The order here is determined by our Value packing. Other users
+ // should sort alphabetically, for consistency.
+ Object = 0x00,
+ BigInt = 0x01,
+ String = 0x02,
+ Symbol = 0x03,
+
+ // Shape details are exposed through JS_TraceShapeCycleCollectorChildren.
+ Shape = 0x04,
+
+ // ObjectGroup details are exposed through
+ // JS_TraceObjectGroupCycleCollectorChildren.
+ ObjectGroup = 0x05,
+
+ // The kind associated with a nullptr.
+ Null = 0x06,
+
+ // The following kinds do not have an exposed C++ idiom.
+ BaseShape = 0x0F,
+ JitCode = 0x1F,
+ Script = 0x2F,
+ Scope = 0x3F,
+ RegExpShared = 0x4F
+};
+const static uintptr_t OutOfLineTraceKindMask = 0x07;
+
+#define ASSERT_TRACE_KIND(tk) \
+ static_assert( \
+ (uintptr_t(tk) & OutOfLineTraceKindMask) == OutOfLineTraceKindMask, \
+ "mask bits are set")
+ASSERT_TRACE_KIND(JS::TraceKind::BaseShape);
+ASSERT_TRACE_KIND(JS::TraceKind::JitCode);
+ASSERT_TRACE_KIND(JS::TraceKind::Script);
+ASSERT_TRACE_KIND(JS::TraceKind::Scope);
+ASSERT_TRACE_KIND(JS::TraceKind::RegExpShared);
+#undef ASSERT_TRACE_KIND
+
+// When this header is imported inside SpiderMonkey, the class definitions are
+// available and we can query those definitions to find the correct kind
+// directly from the class hierarchy.
+template <typename T>
+struct MapTypeToTraceKind {
+ static const JS::TraceKind kind = T::TraceKind;
+};
+
+// When this header is used outside SpiderMonkey, the class definitions are not
+// available, so the following table containing all public GC types is used.
+//
+// canBeGray: GC can mark things of this kind gray. The cycle collector
+// traverses gray GC things when looking for cycles.
+// inCCGraph: Things of this kind are represented as nodes in the CC graph. This
+// also means they can be used as a keys in WeakMap.
+
+// clang-format off
+#define JS_FOR_EACH_TRACEKIND(D) \
+ /* name type canBeGray inCCGraph */ \
+ D(BaseShape, js::BaseShape, true, false) \
+ D(JitCode, js::jit::JitCode, true, false) \
+ D(Scope, js::Scope, true, true) \
+ D(Object, JSObject, true, true) \
+ D(ObjectGroup, js::ObjectGroup, true, false) \
+ D(Script, js::BaseScript, true, true) \
+ D(Shape, js::Shape, true, false) \
+ D(String, JSString, false, false) \
+ D(Symbol, JS::Symbol, false, false) \
+ D(BigInt, JS::BigInt, false, false) \
+ D(RegExpShared, js::RegExpShared, true, true)
+// clang-format on
+
+// Returns true if the JS::TraceKind is represented as a node in cycle collector
+// graph.
+inline constexpr bool IsCCTraceKind(JS::TraceKind aKind) {
+ switch (aKind) {
+#define JS_EXPAND_DEF(name, _1, _2, inCCGraph) \
+ case JS::TraceKind::name: \
+ return inCCGraph;
+ JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+ default:
+ return false;
+ }
+}
+
+// Helper for SFINAE to ensure certain methods are only used on appropriate base
+// types. This avoids common footguns such as `Cell::is<JSFunction>()` which
+// match any type of JSObject.
+template <typename T>
+struct IsBaseTraceType : std::false_type {};
+
+#define JS_EXPAND_DEF(_, type, _1, _2) \
+ template <> \
+ struct IsBaseTraceType<type> : std::true_type {};
+JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+
+template <typename T>
+inline constexpr bool IsBaseTraceType_v = IsBaseTraceType<T>::value;
+
+// Map from all public types to their trace kind.
+#define JS_EXPAND_DEF(name, type, _, _1) \
+ template <> \
+ struct MapTypeToTraceKind<type> { \
+ static const JS::TraceKind kind = JS::TraceKind::name; \
+ };
+JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+
+template <>
+struct MapTypeToTraceKind<JSLinearString> {
+ static const JS::TraceKind kind = JS::TraceKind::String;
+};
+template <>
+struct MapTypeToTraceKind<JSFunction> {
+ static const JS::TraceKind kind = JS::TraceKind::Object;
+};
+template <>
+struct MapTypeToTraceKind<JSScript> {
+ static const JS::TraceKind kind = JS::TraceKind::Script;
+};
+
+// RootKind is closely related to TraceKind. Whereas TraceKind's indices are
+// laid out for convenient embedding as a pointer tag, the indicies of RootKind
+// are designed for use as array keys via EnumeratedArray.
+enum class RootKind : int8_t {
+// These map 1:1 with trace kinds.
+#define EXPAND_ROOT_KIND(name, _0, _1, _2) name,
+ JS_FOR_EACH_TRACEKIND(EXPAND_ROOT_KIND)
+#undef EXPAND_ROOT_KIND
+
+ // These tagged pointers are special-cased for performance.
+ Id,
+ Value,
+
+ // Everything else.
+ Traceable,
+
+ Limit
+};
+
+// Most RootKind correspond directly to a trace kind.
+template <TraceKind traceKind>
+struct MapTraceKindToRootKind {};
+#define JS_EXPAND_DEF(name, _0, _1, _2) \
+ template <> \
+ struct MapTraceKindToRootKind<JS::TraceKind::name> { \
+ static const JS::RootKind kind = JS::RootKind::name; \
+ };
+JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF)
+#undef JS_EXPAND_DEF
+
+// Specify the RootKind for all types. Value and jsid map to special cases;
+// Cell pointer types we can derive directly from the TraceKind; everything else
+// should go in the Traceable list and use GCPolicy<T>::trace for tracing.
+template <typename T>
+struct MapTypeToRootKind {
+ static const JS::RootKind kind = JS::RootKind::Traceable;
+};
+template <typename T>
+struct MapTypeToRootKind<T*> {
+ static const JS::RootKind kind =
+ JS::MapTraceKindToRootKind<JS::MapTypeToTraceKind<T>::kind>::kind;
+};
+template <>
+struct MapTypeToRootKind<JS::Realm*> {
+ // Not a pointer to a GC cell. Use GCPolicy.
+ static const JS::RootKind kind = JS::RootKind::Traceable;
+};
+template <typename T>
+struct MapTypeToRootKind<mozilla::UniquePtr<T>> {
+ static const JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
+};
+template <>
+struct MapTypeToRootKind<JS::Value> {
+ static const JS::RootKind kind = JS::RootKind::Value;
+};
+template <>
+struct MapTypeToRootKind<jsid> {
+ static const JS::RootKind kind = JS::RootKind::Id;
+};
+
+// Fortunately, few places in the system need to deal with fully abstract
+// cells. In those places that do, we generally want to move to a layout
+// templated function as soon as possible. This template wraps the upcast
+// for that dispatch.
+//
+// Given a call:
+//
+// DispatchTraceKindTyped(f, thing, traceKind, ... args)
+//
+// Downcast the |void *thing| to the specific type designated by |traceKind|,
+// and pass it to the functor |f| along with |... args|, forwarded. Pass the
+// type designated by |traceKind| as the functor's template argument. The
+// |thing| parameter is optional; without it, we simply pass through |... args|.
+template <typename F, typename... Args>
+auto DispatchTraceKindTyped(F f, JS::TraceKind traceKind, Args&&... args) {
+ switch (traceKind) {
+#define JS_EXPAND_DEF(name, type, _, _1) \
+ case JS::TraceKind::name: \
+ return f.template operator()<type>(std::forward<Args>(args)...);
+ JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+ default:
+ MOZ_CRASH("Invalid trace kind in DispatchTraceKindTyped.");
+ }
+}
+
+// Given a GC thing specified by pointer and trace kind, calls the functor |f|
+// with a template argument of the actual type of the pointer and returns the
+// result.
+template <typename F>
+auto MapGCThingTyped(void* thing, JS::TraceKind traceKind, F&& f) {
+ switch (traceKind) {
+#define JS_EXPAND_DEF(name, type, _, _1) \
+ case JS::TraceKind::name: \
+ return f(static_cast<type*>(thing));
+ JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+ default:
+ MOZ_CRASH("Invalid trace kind in MapGCThingTyped.");
+ }
+}
+
+// Given a GC thing specified by pointer and trace kind, calls the functor |f|
+// with a template argument of the actual type of the pointer and ignores the
+// result.
+template <typename F>
+void ApplyGCThingTyped(void* thing, JS::TraceKind traceKind, F&& f) {
+ // This function doesn't do anything but is supplied for symmetry with other
+ // MapGCThingTyped/ApplyGCThingTyped implementations that have to wrap the
+ // functor to return a dummy value that is ignored.
+ MapGCThingTyped(thing, traceKind, std::move(f));
+}
+
+} // namespace JS
+
+#endif // js_TraceKind_h
diff --git a/js/public/TraceLoggerAPI.h b/js/public/TraceLoggerAPI.h
new file mode 100644
index 0000000000..b26c9118b6
--- /dev/null
+++ b/js/public/TraceLoggerAPI.h
@@ -0,0 +1,217 @@
+/* -*- 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/. */
+
+/* SpiderMonkey TraceLogger APIs. */
+
+#ifndef js_TraceLoggerAPI_h
+#define js_TraceLoggerAPI_h
+
+#include "jstypes.h"
+
+namespace mozilla {
+class JSONWriteFunc;
+class TimeStamp;
+}; // namespace mozilla
+
+namespace JS {
+
+// Used to lock any tracelogger activities, and consequently, will also block
+// any further JS execution when a thread hits an atomic tracelogger activity
+// such as payload creation.
+class AutoTraceLoggerLockGuard {
+ public:
+ AutoTraceLoggerLockGuard();
+ ~AutoTraceLoggerLockGuard();
+};
+
+// An implementation type must be defined in order to gather data using the
+// TraceLoggerCollectorBuffer. Each implementation must define the type that is
+// being collected in the buffer, along with a static method that is used to
+// actually write into the buffer from the tracelogger.
+struct TraceLoggerDictionaryImpl {
+ using ImplType = char;
+ static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
+ size_t bufferSize);
+};
+
+struct TraceLoggerIdImpl {
+ using ImplType = uint32_t;
+ static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
+ size_t bufferSize);
+};
+
+struct TraceLoggerLineNoImpl {
+ using ImplType = int32_t;
+ static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
+ size_t bufferSize);
+};
+
+struct TraceLoggerColNoImpl {
+ using ImplType = int32_t;
+ static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
+ size_t bufferSize);
+};
+
+struct TraceLoggerTimeStampImpl {
+ using ImplType = mozilla::TimeStamp;
+ static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
+ size_t bufferSize);
+};
+
+struct TraceLoggerDurationImpl {
+ using ImplType = double;
+ static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
+ size_t bufferSize);
+};
+
+// Buffer that is used to retrieve tracelogger data in fixed size chunks so that
+// allocation of a large array is not necessary. The TraceLoggerCollectorBuffer
+// class will manage an internal state which points to the next data index being
+// collected. Each call to NextChunk will also clobber the internal buffer used
+// to store the data.
+template <class T>
+class TraceLoggerCollectorBuffer {
+ using ImplType = typename T::ImplType;
+
+ public:
+ class Iterator {
+ public:
+ Iterator(ImplType* buffer, size_t index)
+ : iteratorIndex(index), buf(buffer) {}
+
+ Iterator operator++() {
+ iteratorIndex++;
+ return *this;
+ }
+
+ bool operator!=(const Iterator& other) const {
+ return iteratorIndex != other.iteratorIndex;
+ }
+
+ ImplType operator*() const { return buf[iteratorIndex]; }
+
+ private:
+ size_t iteratorIndex;
+ ImplType* buf;
+ };
+
+ explicit TraceLoggerCollectorBuffer(AutoTraceLoggerLockGuard& lockGuard,
+ JSContext* cx = nullptr,
+ size_t length = 4096)
+ : cx_(cx), length_(length), dataIndex_(0), bufferIndex_(0) {
+ buffer_ = js_pod_malloc<ImplType>(length);
+ }
+
+ ~TraceLoggerCollectorBuffer() { js_free(buffer_); }
+
+ Iterator begin() const { return Iterator(buffer_, 0); }
+
+ Iterator end() const { return Iterator(buffer_, bufferIndex_); }
+
+ ImplType* internalBuffer() const { return buffer_; }
+
+ bool NextChunk() {
+ bufferIndex_ = T::NextChunk(cx_, &dataIndex_, buffer_, length_);
+ return (bufferIndex_ != 0) ? true : false;
+ }
+
+ private:
+ JSContext* cx_;
+ size_t length_;
+ size_t dataIndex_;
+ size_t bufferIndex_;
+ ImplType* buffer_;
+};
+
+#ifdef JS_TRACE_LOGGING
+
+// Initialize the trace logger. This must be called before using any of the
+// other trace logging functions.
+extern JS_PUBLIC_API bool InitTraceLogger();
+
+// Return whether the trace logger is supported in this browser session.
+extern JS_PUBLIC_API bool TraceLoggerSupported();
+
+// Begin trace logging events. This will activate some of the
+// textId's for various events and set the global option
+// JSJITCOMPILER_ENABLE_TRACELOGGER to true.
+// This does nothing except return if the trace logger is already active.
+extern JS_PUBLIC_API void StartTraceLogger(JSContext* cx);
+
+// Stop trace logging events. All textId's will be set to false, and the
+// global JSJITCOMPILER_ENABLE_TRACELOGGER will be set to false.
+// This does nothing except return if the trace logger is not active.
+extern JS_PUBLIC_API void StopTraceLogger(JSContext* cx);
+
+// Clear and free any event data that was recorded by the trace logger.
+extern JS_PUBLIC_API void ResetTraceLogger(void);
+
+// Spew trace logger statistics.
+extern JS_PUBLIC_API void SpewTraceLoggerThread(JSContext* cx);
+
+// Spew trace logger statistics.
+extern JS_PUBLIC_API void SpewTraceLoggerForCurrentProcess();
+
+#else
+// Define empty inline functions for when trace logging compilation is not
+// enabled. TraceLogging.cpp will not be built in that case so we need to
+// provide something for any routines that reference these.
+inline bool InitTraceLogger() { return true; }
+inline bool TraceLoggerSupported() { return false; }
+inline void StartTraceLogger(JSContext* cx) {}
+inline void StopTraceLogger(JSContext* cx) {}
+inline void ResetTraceLogger(void) {}
+inline void SpewTraceLoggerThread(JSContext* cx) {}
+inline void SpewTraceLoggerForCurrentProcess() {}
+inline size_t TraceLoggerDictionaryImpl::NextChunk(JSContext* cx,
+ size_t* dataIndex,
+ ImplType buffer[],
+ size_t bufferSize) {
+ return 0;
+}
+inline size_t TraceLoggerIdImpl::NextChunk(JSContext* cx, size_t* dataIndex,
+ ImplType buffer[],
+ size_t bufferSize) {
+ return 0;
+}
+inline size_t TraceLoggerTimeStampImpl::NextChunk(JSContext* cx,
+ size_t* dataIndex,
+ ImplType buffer[],
+ size_t bufferSize) {
+ return 0;
+}
+inline size_t TraceLoggerDurationImpl::NextChunk(JSContext* cx,
+ size_t* dataIndex,
+ ImplType buffer[],
+ size_t bufferSize) {
+ return 0;
+}
+inline size_t TraceLoggerLineNoImpl::NextChunk(JSContext* cx, size_t* dataIndex,
+ ImplType buffer[],
+ size_t bufferSize) {
+ return 0;
+}
+inline size_t TraceLoggerColNoImpl::NextChunk(JSContext* cx, size_t* dataIndex,
+ ImplType buffer[],
+ size_t bufferSize) {
+ return 0;
+}
+inline AutoTraceLoggerLockGuard::AutoTraceLoggerLockGuard() {}
+inline AutoTraceLoggerLockGuard::~AutoTraceLoggerLockGuard() {}
+#endif
+using TraceLoggerDictionaryBuffer =
+ TraceLoggerCollectorBuffer<JS::TraceLoggerDictionaryImpl>;
+using TraceLoggerIdBuffer = TraceLoggerCollectorBuffer<JS::TraceLoggerIdImpl>;
+using TraceLoggerTimeStampBuffer =
+ TraceLoggerCollectorBuffer<JS::TraceLoggerTimeStampImpl>;
+using TraceLoggerDurationBuffer =
+ TraceLoggerCollectorBuffer<JS::TraceLoggerDurationImpl>;
+using TraceLoggerLineNoBuffer =
+ TraceLoggerCollectorBuffer<JS::TraceLoggerLineNoImpl>;
+using TraceLoggerColNoBuffer =
+ TraceLoggerCollectorBuffer<JS::TraceLoggerColNoImpl>;
+}; // namespace JS
+
+#endif /* js_TraceLoggerAPI_h */
diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h
new file mode 100644
index 0000000000..b09d9864c8
--- /dev/null
+++ b/js/public/TracingAPI.h
@@ -0,0 +1,546 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_TracingAPI_h
+#define js_TracingAPI_h
+
+#include "js/GCTypeMacros.h"
+#include "js/HeapAPI.h"
+#include "js/TraceKind.h"
+
+class JS_PUBLIC_API JSTracer;
+
+namespace JS {
+class JS_PUBLIC_API CallbackTracer;
+template <typename T>
+class Heap;
+template <typename T>
+class TenuredHeap;
+
+/** Returns a static string equivalent of |kind|. */
+JS_FRIEND_API const char* GCTraceKindToAscii(JS::TraceKind kind);
+
+/** Returns the base size in bytes of the GC thing of kind |kind|. */
+JS_FRIEND_API size_t GCTraceKindSize(JS::TraceKind kind);
+
+// Kinds of JSTracer.
+enum class TracerKind {
+ // Marking path: a tracer used only for marking liveness of cells, not
+ // for moving them.
+ Marking,
+
+ // Generic tracers: Internal tracers that have a different virtual method
+ // called for each edge kind.
+ //
+ // Order is important. All generic kinds must follow this one.
+ Generic,
+
+ // Specific kinds of generic tracer.
+ Tenuring,
+ Moving,
+ GrayBuffering,
+ ClearEdges,
+ Sweeping,
+
+ // Callback tracers: General-purpose tracers that have a single virtual
+ // method called on every edge.
+ //
+ // Order is important. All callback kinds must follow this one.
+ Callback,
+
+ // Specific kinds of callback tracer.
+ UnmarkGray,
+ VerifyTraceProtoAndIface
+};
+
+enum class WeakMapTraceAction {
+ /**
+ * Do not trace into weak map keys or values during traversal. Users must
+ * handle weak maps manually.
+ */
+ Skip,
+
+ /**
+ * Do true ephemeron marking with a weak key lookup marking phase. This is
+ * the default for GCMarker.
+ */
+ Expand,
+
+ /**
+ * Trace through to all values, irrespective of whether the keys are live
+ * or not. Used for non-marking tracers.
+ */
+ TraceValues,
+
+ /**
+ * Trace through to all keys and values, irrespective of whether the keys
+ * are live or not. Used for non-marking tracers.
+ */
+ TraceKeysAndValues
+};
+
+// Whether a tracer should trace weak edges. GCMarker sets this to Skip.
+enum class WeakEdgeTraceAction { Skip, Trace };
+
+// Whether a tracer can skip tracing JS::Ids. This is needed by the cycle
+// collector to skip some Ids for performance reasons. Not all Ids are skipped.
+enum class IdTraceAction { CanSkip, Trace };
+
+struct TraceOptions {
+ JS::WeakMapTraceAction weakMapAction = WeakMapTraceAction::TraceValues;
+ JS::WeakEdgeTraceAction weakEdgeAction = WeakEdgeTraceAction::Trace;
+ JS::IdTraceAction idAction = IdTraceAction::Trace;
+
+ TraceOptions() = default;
+ TraceOptions(JS::WeakMapTraceAction weakMapActionArg,
+ JS::WeakEdgeTraceAction weakEdgeActionArg,
+ JS::IdTraceAction idActionArg = IdTraceAction::Trace)
+ : weakMapAction(weakMapActionArg),
+ weakEdgeAction(weakEdgeActionArg),
+ idAction(idActionArg) {}
+ MOZ_IMPLICIT TraceOptions(JS::WeakMapTraceAction weakMapActionArg)
+ : weakMapAction(weakMapActionArg) {}
+ MOZ_IMPLICIT TraceOptions(JS::WeakEdgeTraceAction weakEdgeActionArg)
+ : weakEdgeAction(weakEdgeActionArg) {}
+ MOZ_IMPLICIT TraceOptions(JS::IdTraceAction idActionArg)
+ : idAction(idActionArg) {}
+};
+
+class AutoTracingName;
+class AutoTracingIndex;
+class AutoTracingCallback;
+
+// Optional context information that can be used to construct human readable
+// descriptions of what is being traced.
+class TracingContext {
+ public:
+ // Access to the tracing context: When tracing with a JS::CallbackTracer, we
+ // invoke the callback with the edge location and the type of target. This is
+ // useful for operating on the edge in the abstract or on the target thing,
+ // satisfying most common use cases. However, some tracers need additional
+ // detail about the specific edge that is being traced in order to be
+ // useful. Unfortunately, the raw pointer to the edge that we provide is not
+ // enough information to infer much of anything useful about that edge.
+ //
+ // In order to better support use cases that care in particular about edges --
+ // as opposed to the target thing -- tracing implementations are responsible
+ // for providing extra context information about each edge they trace, as it
+ // is traced. This contains, at a minimum, an edge name and, when tracing an
+ // array, the index. Further specialization can be achieved (with some
+ // complexity), by associating a functor with the tracer so that, when
+ // requested, the user can generate totally custom edge descriptions.
+
+ // Returns the current edge's name. It is only valid to call this when
+ // inside the trace callback, however, the edge name will always be set.
+ const char* name() const {
+ MOZ_ASSERT(name_);
+ return name_;
+ }
+
+ // Returns the current edge's index, if marked as part of an array of edges.
+ // This must be called only inside the trace callback. When not tracing an
+ // array, the value will be InvalidIndex.
+ constexpr static size_t InvalidIndex = size_t(-1);
+ size_t index() const { return index_; }
+
+ // Build a description of this edge in the heap graph. This call may invoke
+ // the context functor, if set, which may inspect arbitrary areas of the
+ // heap. On the other hand, the description provided by this method may be
+ // substantially more accurate and useful than those provided by only the
+ // name and index.
+ void getEdgeName(char* buffer, size_t bufferSize);
+
+ // The trace implementation may associate a callback with one or more edges
+ // using AutoTracingDetails. This functor is called by getEdgeName and
+ // is responsible for providing a textual representation of the edge currently
+ // being traced. The callback has access to the full heap, including the
+ // currently set tracing context.
+ class Functor {
+ public:
+ virtual void operator()(TracingContext* tcx, char* buf, size_t bufsize) = 0;
+ };
+
+ private:
+ friend class AutoTracingName;
+ const char* name_ = nullptr;
+
+ friend class AutoTracingIndex;
+ size_t index_ = InvalidIndex;
+
+ friend class AutoTracingDetails;
+ Functor* functor_ = nullptr;
+};
+
+} // namespace JS
+
+namespace js {
+class GenericTracer;
+} // namespace js
+
+class JS_PUBLIC_API JSTracer {
+ public:
+ // Return the runtime set on the tracer.
+ JSRuntime* runtime() const { return runtime_; }
+
+ JS::TracingContext* maybeContext() { return maybeContext_; }
+
+ JS::TracerKind kind() const { return kind_; }
+ bool isMarkingTracer() const { return kind_ == JS::TracerKind::Marking; }
+ bool isTenuringTracer() const { return kind_ == JS::TracerKind::Tenuring; }
+ bool isGenericTracer() const { return kind_ >= JS::TracerKind::Generic; }
+ bool isCallbackTracer() const { return kind_ >= JS::TracerKind::Callback; }
+
+ inline js::GenericTracer* asGenericTracer();
+ inline JS::CallbackTracer* asCallbackTracer();
+
+ JS::WeakMapTraceAction weakMapAction() const {
+ return options_.weakMapAction;
+ }
+ bool traceWeakEdges() const {
+ return options_.weakEdgeAction == JS::WeakEdgeTraceAction::Trace;
+ }
+ bool canSkipJsids() const {
+ return options_.idAction == JS::IdTraceAction::CanSkip;
+ }
+
+ // Get the current GC number. Only call this method if |isMarkingTracer()|
+ // is true.
+ uint32_t gcNumberForMarking() const;
+
+ protected:
+ JSTracer(JSRuntime* rt, JS::TracerKind kind,
+ JS::TraceOptions options = JS::TraceOptions())
+ : runtime_(rt), kind_(kind), options_(options) {}
+
+ void setContext(JS::TracingContext* tcx) { maybeContext_ = tcx; }
+
+ private:
+ JSRuntime* const runtime_;
+ JS::TracingContext* maybeContext_ = nullptr;
+ const JS::TracerKind kind_;
+ const JS::TraceOptions options_;
+};
+
+namespace js {
+
+class GenericTracer : public JSTracer {
+ public:
+ GenericTracer(JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Generic,
+ JS::TraceOptions options = JS::TraceOptions())
+ : JSTracer(rt, kind, options) {
+ MOZ_ASSERT(isGenericTracer());
+ }
+
+ // These methods are called when the tracer encounters an edge. Clients should
+ // override them to receive notifications when an edge of each type is
+ // visited.
+ //
+ // The caller updates the edge with the return value (if different).
+ //
+ // In C++, overriding a method hides all methods in the base class with that
+ // name, not just methods with that signature. Thus, the typed edge methods
+ // have to have distinct names to allow us to override them individually,
+ // which is freqently useful if, for example, we only want to process one type
+ // of edge.
+ virtual JSObject* onObjectEdge(JSObject* obj) = 0;
+ virtual JSString* onStringEdge(JSString* str) = 0;
+ virtual JS::Symbol* onSymbolEdge(JS::Symbol* sym) = 0;
+ virtual JS::BigInt* onBigIntEdge(JS::BigInt* bi) = 0;
+ virtual js::BaseScript* onScriptEdge(js::BaseScript* script) = 0;
+ virtual js::Shape* onShapeEdge(js::Shape* shape) = 0;
+ virtual js::RegExpShared* onRegExpSharedEdge(js::RegExpShared* shared) = 0;
+ virtual js::ObjectGroup* onObjectGroupEdge(js::ObjectGroup* group) = 0;
+ virtual js::BaseShape* onBaseShapeEdge(js::BaseShape* base) = 0;
+ virtual js::jit::JitCode* onJitCodeEdge(js::jit::JitCode* code) = 0;
+ virtual js::Scope* onScopeEdge(js::Scope* scope) = 0;
+};
+
+} // namespace js
+
+namespace JS {
+
+class JS_PUBLIC_API CallbackTracer : public js::GenericTracer {
+ public:
+ CallbackTracer(JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Callback,
+ JS::TraceOptions options = JS::TraceOptions())
+ : GenericTracer(rt, kind, options) {
+ MOZ_ASSERT(isCallbackTracer());
+ setContext(&context_);
+ }
+ CallbackTracer(JSContext* cx, JS::TracerKind kind = JS::TracerKind::Callback,
+ JS::TraceOptions options = JS::TraceOptions());
+
+ TracingContext& context() { return context_; }
+
+ // Override this method to receive notification when a node in the GC
+ // heap graph is visited.
+ virtual void onChild(const JS::GCCellPtr& thing) = 0;
+
+ private:
+ // This class implements the GenericTracer interface to dispatches to onChild.
+ virtual JSObject* onObjectEdge(JSObject* obj) {
+ onChild(JS::GCCellPtr(obj));
+ return obj;
+ }
+ virtual JSString* onStringEdge(JSString* str) {
+ onChild(JS::GCCellPtr(str));
+ return str;
+ }
+ virtual JS::Symbol* onSymbolEdge(JS::Symbol* sym) {
+ onChild(JS::GCCellPtr(sym));
+ return sym;
+ }
+ virtual JS::BigInt* onBigIntEdge(JS::BigInt* bi) {
+ onChild(JS::GCCellPtr(bi));
+ return bi;
+ }
+ virtual js::BaseScript* onScriptEdge(js::BaseScript* script) {
+ onChild(JS::GCCellPtr(script));
+ return script;
+ }
+ virtual js::Shape* onShapeEdge(js::Shape* shape) {
+ onChild(JS::GCCellPtr(shape, JS::TraceKind::Shape));
+ return shape;
+ }
+ virtual js::ObjectGroup* onObjectGroupEdge(js::ObjectGroup* group) {
+ onChild(JS::GCCellPtr(group, JS::TraceKind::ObjectGroup));
+ return group;
+ }
+ virtual js::BaseShape* onBaseShapeEdge(js::BaseShape* base) {
+ onChild(JS::GCCellPtr(base, JS::TraceKind::BaseShape));
+ return base;
+ }
+ virtual js::jit::JitCode* onJitCodeEdge(js::jit::JitCode* code) {
+ onChild(JS::GCCellPtr(code, JS::TraceKind::JitCode));
+ return code;
+ }
+ virtual js::Scope* onScopeEdge(js::Scope* scope) {
+ onChild(JS::GCCellPtr(scope, JS::TraceKind::Scope));
+ return scope;
+ }
+ virtual js::RegExpShared* onRegExpSharedEdge(js::RegExpShared* shared) {
+ onChild(JS::GCCellPtr(shared, JS::TraceKind::RegExpShared));
+ return shared;
+ }
+
+ TracingContext context_;
+};
+
+// Set the name portion of the tracer's context for the current edge.
+class MOZ_RAII AutoTracingName {
+ TracingContext* tcx_ = nullptr;
+ const char* prior_ = nullptr;
+
+ public:
+ AutoTracingName(JSTracer* trc, const char* name) {
+ MOZ_ASSERT(name);
+ if (TracingContext* tcx = trc->maybeContext()) {
+ tcx_ = tcx;
+ prior_ = tcx_->name_;
+ tcx_->name_ = name;
+ }
+ }
+ ~AutoTracingName() {
+ if (tcx_) {
+ MOZ_ASSERT(tcx_->name_);
+ tcx_->name_ = prior_;
+ }
+ }
+};
+
+// Set the index portion of the tracer's context for the current range.
+class MOZ_RAII AutoTracingIndex {
+ TracingContext* tcx_ = nullptr;
+
+ public:
+ explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) {
+ if (TracingContext* tcx = trc->maybeContext()) {
+ tcx_ = tcx;
+ MOZ_ASSERT(tcx_->index_ == TracingContext::InvalidIndex);
+ tcx_->index_ = initial;
+ }
+ }
+ ~AutoTracingIndex() {
+ if (tcx_) {
+ MOZ_ASSERT(tcx_->index_ != TracingContext::InvalidIndex);
+ tcx_->index_ = TracingContext::InvalidIndex;
+ }
+ }
+
+ void operator++() {
+ if (tcx_) {
+ MOZ_ASSERT(tcx_->index_ != TracingContext::InvalidIndex);
+ ++tcx_->index_;
+ }
+ }
+};
+
+// Set a context callback for the trace callback to use, if it needs a detailed
+// edge description.
+class MOZ_RAII AutoTracingDetails {
+ TracingContext* tcx_ = nullptr;
+
+ public:
+ AutoTracingDetails(JSTracer* trc, TracingContext::Functor& func) {
+ if (TracingContext* tcx = trc->maybeContext()) {
+ tcx_ = tcx;
+ MOZ_ASSERT(tcx_->functor_ == nullptr);
+ tcx_->functor_ = &func;
+ }
+ }
+ ~AutoTracingDetails() {
+ if (tcx_) {
+ MOZ_ASSERT(tcx_->functor_);
+ tcx_->functor_ = nullptr;
+ }
+ }
+};
+
+} // namespace JS
+
+js::GenericTracer* JSTracer::asGenericTracer() {
+ MOZ_ASSERT(isGenericTracer());
+ return static_cast<js::GenericTracer*>(this);
+}
+
+JS::CallbackTracer* JSTracer::asCallbackTracer() {
+ MOZ_ASSERT(isCallbackTracer());
+ return static_cast<JS::CallbackTracer*>(this);
+}
+
+namespace js {
+
+class AbstractGeneratorObject;
+class SavedFrame;
+
+namespace gc {
+
+#define JS_DECLARE_TRACE_EXTERNAL_EDGE(type) \
+ extern JS_PUBLIC_API void TraceExternalEdge(JSTracer* trc, type* thingp, \
+ const char* name);
+
+// Declare edge-tracing function overloads for public GC pointer types.
+JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE)
+JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE)
+
+#undef JS_DECLARE_TRACE_EXTERNAL_EDGE
+
+} // namespace gc
+} // namespace js
+
+namespace JS {
+
+// The JS::TraceEdge family of functions traces the given GC thing reference.
+// This performs the tracing action configured on the given JSTracer: typically
+// calling the JSTracer::callback or marking the thing as live.
+//
+// The argument to JS::TraceEdge is an in-out param: when the function returns,
+// the garbage collector might have moved the GC thing. In this case, the
+// reference passed to JS::TraceEdge will be updated to the thing's new
+// location. Callers of this method are responsible for updating any state that
+// is dependent on the object's address. For example, if the object's address
+// is used as a key in a hashtable, then the object must be removed and
+// re-inserted with the correct hash.
+//
+// Note that while |edgep| must never be null, it is fine for |*edgep| to be
+// nullptr.
+
+template <typename T>
+inline void TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
+ MOZ_ASSERT(thingp);
+ if (*thingp) {
+ js::gc::TraceExternalEdge(trc, thingp->unsafeGet(), name);
+ }
+}
+
+template <typename T>
+inline void TraceEdge(JSTracer* trc, JS::TenuredHeap<T>* thingp,
+ const char* name) {
+ MOZ_ASSERT(thingp);
+ if (T ptr = thingp->unbarrieredGetPtr()) {
+ js::gc::TraceExternalEdge(trc, &ptr, name);
+ thingp->setPtr(ptr);
+ }
+}
+
+// Edges that are always traced as part of root marking do not require
+// incremental barriers. |JS::UnsafeTraceRoot| overloads allow for marking
+// non-barriered pointers but assert that this happens during root marking.
+//
+// Note that while |edgep| must never be null, it is fine for |*edgep| to be
+// nullptr.
+#define JS_DECLARE_UNSAFE_TRACE_ROOT(type) \
+ extern JS_PUBLIC_API void UnsafeTraceRoot(JSTracer* trc, type* edgep, \
+ const char* name);
+
+// Declare edge-tracing function overloads for public GC pointer types.
+JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_UNSAFE_TRACE_ROOT)
+JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_UNSAFE_TRACE_ROOT)
+
+// We also require overloads for these purely-internal types. These overloads
+// ought not be in public headers, and they should use a different name in order
+// to not be *actual* overloads, but for the moment we still declare them here.
+JS_DECLARE_UNSAFE_TRACE_ROOT(js::AbstractGeneratorObject*)
+JS_DECLARE_UNSAFE_TRACE_ROOT(js::SavedFrame*)
+
+#undef JS_DECLARE_UNSAFE_TRACE_ROOT
+
+extern JS_PUBLIC_API void TraceChildren(JSTracer* trc, GCCellPtr thing);
+
+} // namespace JS
+
+namespace js {
+
+inline bool IsTracerKind(JSTracer* trc, JS::TracerKind kind) {
+ return trc->kind() == kind;
+}
+
+// Trace an edge that is not a GC root and is not wrapped in a barriered
+// wrapper for some reason.
+//
+// This method does not check if |*edgep| is non-null before tracing through
+// it, so callers must check any nullable pointer before calling this method.
+extern JS_PUBLIC_API void UnsafeTraceManuallyBarrieredEdge(JSTracer* trc,
+ JSObject** edgep,
+ const char* name);
+
+// Not part of the public API, but declared here so we can use it in
+// GCPolicyAPI.h
+template <typename T>
+inline bool TraceManuallyBarrieredWeakEdge(JSTracer* trc, T* thingp,
+ const char* name);
+
+template <typename T>
+class BarrieredBase;
+
+template <typename T>
+inline bool TraceWeakEdge(JSTracer* trc, BarrieredBase<T>* thingp,
+ const char* name);
+
+namespace gc {
+
+// Return true if the given edge is not live and is about to be swept.
+template <typename T>
+extern JS_PUBLIC_API bool EdgeNeedsSweep(JS::Heap<T>* edgep);
+
+// Not part of the public API, but declared here so we can use it in GCPolicy
+// which is.
+template <typename T>
+bool IsAboutToBeFinalizedUnbarriered(T* thingp);
+
+} // namespace gc
+
+#ifdef DEBUG
+/*
+ * Return whether the runtime is currently being destroyed, for use in
+ * assertions.
+ */
+extern JS_FRIEND_API bool RuntimeIsBeingDestroyed();
+#endif
+
+} // namespace js
+
+#endif /* js_TracingAPI_h */
diff --git a/js/public/Transcoding.h b/js/public/Transcoding.h
new file mode 100644
index 0000000000..c386fc2539
--- /dev/null
+++ b/js/public/Transcoding.h
@@ -0,0 +1,190 @@
+/* -*- 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/. */
+
+/*
+ * Structures and functions for transcoding compiled scripts and functions to
+ * and from memory.
+ */
+
+#ifndef js_Transcoding_h
+#define js_Transcoding_h
+
+#include "mozilla/Range.h" // mozilla::Range
+#include "mozilla/Vector.h" // mozilla::Vector
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint8_t, uint32_t
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+class ReadOnlyCompileOptions;
+
+using TranscodeBuffer = mozilla::Vector<uint8_t>;
+using TranscodeRange = mozilla::Range<uint8_t>;
+
+struct TranscodeSource final {
+ TranscodeSource(const TranscodeRange& range_, const char* file, uint32_t line)
+ : range(range_), filename(file), lineno(line) {}
+
+ const TranscodeRange range;
+ const char* filename;
+ const uint32_t lineno;
+};
+
+using TranscodeSources = mozilla::Vector<TranscodeSource>;
+
+enum TranscodeResult : uint8_t {
+ // Successful encoding / decoding.
+ TranscodeResult_Ok = 0,
+
+ // A warning message, is set to the message out-param.
+ TranscodeResult_Failure = 0x10,
+ TranscodeResult_Failure_BadBuildId = TranscodeResult_Failure | 0x1,
+ TranscodeResult_Failure_RunOnceNotSupported = TranscodeResult_Failure | 0x2,
+ TranscodeResult_Failure_AsmJSNotSupported = TranscodeResult_Failure | 0x3,
+ TranscodeResult_Failure_BadDecode = TranscodeResult_Failure | 0x4,
+ TranscodeResult_Failure_WrongCompileOption = TranscodeResult_Failure | 0x5,
+ TranscodeResult_Failure_NotInterpretedFun = TranscodeResult_Failure | 0x6,
+
+ // There is a pending exception on the context.
+ TranscodeResult_Throw = 0x20
+};
+
+static constexpr size_t BytecodeOffsetAlignment = 4;
+static_assert(BytecodeOffsetAlignment <= alignof(std::max_align_t),
+ "Alignment condition requires a custom allocator.");
+
+// Align the bytecode offset for transcoding for the requirement.
+inline size_t AlignTranscodingBytecodeOffset(size_t offset) {
+ size_t extra = offset % BytecodeOffsetAlignment;
+ if (extra == 0) {
+ return offset;
+ }
+ size_t padding = BytecodeOffsetAlignment - extra;
+ return offset + padding;
+}
+
+inline bool IsTranscodingBytecodeOffsetAligned(size_t offset) {
+ return offset % BytecodeOffsetAlignment == 0;
+}
+
+inline bool IsTranscodingBytecodeAligned(void* offset) {
+ return IsTranscodingBytecodeOffsetAligned(size_t(offset));
+}
+
+// Encode JSScript into the buffer.
+//
+// If the `buffer` isn't empty, the start of the `buffer` should meet
+// IsTranscodingBytecodeAligned, and the length should meet
+// IsTranscodingBytecodeOffsetAligned.
+//
+// NOTE: As long as IsTranscodingBytecodeOffsetAligned is met, that means
+// there's JS::BytecodeOffsetAlignment+extra bytes in the buffer,
+// IsTranscodingBytecodeAligned should be guaranteed to meet by
+// malloc, used by MallocAllocPolicy in mozilla::Vector.
+extern JS_PUBLIC_API TranscodeResult EncodeScript(JSContext* cx,
+ TranscodeBuffer& buffer,
+ Handle<JSScript*> script);
+
+// Decode JSScript from the buffer.
+//
+// The start of `buffer` and `cursorIndex` should meet
+// IsTranscodingBytecodeAligned and IsTranscodingBytecodeOffsetAligned.
+// (This should be handled while encoding).
+extern JS_PUBLIC_API TranscodeResult
+DecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+ TranscodeBuffer& buffer, MutableHandle<JSScript*> scriptp,
+ size_t cursorIndex = 0);
+
+// Decode JSScript from the range.
+//
+// The start of `range` should meet IsTranscodingBytecodeAligned and
+// IsTranscodingBytecodeOffsetAligned.
+// (This should be handled while encoding).
+extern JS_PUBLIC_API TranscodeResult
+DecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+ const TranscodeRange& range, MutableHandle<JSScript*> scriptp);
+
+// If js::UseOffThreadParseGlobal is true, decode JSScript from the buffer.
+//
+// If js::UseOffThreadParseGlobal is false, decode CompilationStencil from the
+// buffer and instantiate JSScript from it.
+//
+// options.useOffThreadParseGlobal should match JS::SetUseOffThreadParseGlobal.
+//
+// The start of `buffer` and `cursorIndex` should meet
+// IsTranscodingBytecodeAligned and IsTranscodingBytecodeOffsetAligned.
+// (This should be handled while encoding).
+extern JS_PUBLIC_API TranscodeResult DecodeScriptMaybeStencil(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ TranscodeBuffer& buffer, MutableHandle<JSScript*> scriptp,
+ size_t cursorIndex = 0);
+
+// If js::UseOffThreadParseGlobal is true, decode JSScript from the buffer.
+//
+// If js::UseOffThreadParseGlobal is false, decode CompilationStencil from the
+// buffer and instantiate JSScript from it.
+//
+// And then register an encoder on its script source, such that all functions
+// can be encoded as they are parsed. This strategy is used to avoid blocking
+// the main thread in a non-interruptible way.
+//
+// See also JS::FinishIncrementalEncoding.
+//
+// options.useOffThreadParseGlobal should match JS::SetUseOffThreadParseGlobal.
+//
+// The start of `buffer` and `cursorIndex` should meet
+// IsTranscodingBytecodeAligned and IsTranscodingBytecodeOffsetAligned.
+// (This should be handled while encoding).
+extern JS_PUBLIC_API TranscodeResult DecodeScriptAndStartIncrementalEncoding(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ TranscodeBuffer& buffer, MutableHandle<JSScript*> scriptp,
+ size_t cursorIndex = 0);
+
+// Finish incremental encoding started by one of:
+// * JS::CompileAndStartIncrementalEncoding
+// * JS::FinishOffThreadScriptAndStartIncrementalEncoding
+// * JS::DecodeScriptAndStartIncrementalEncoding
+//
+// The |script| argument of |FinishIncrementalEncoding| should be the top-level
+// script returned from one of the above.
+//
+// The |buffer| argument of |FinishIncrementalEncoding| is used for appending
+// the encoded bytecode into the buffer. If any of these functions failed, the
+// content of |buffer| would be undefined.
+//
+// If js::UseOffThreadParseGlobal is true, |buffer| contains encoded JSScript.
+//
+// If js::UseOffThreadParseGlobal is false, |buffer| contains encoded
+// CompilationStencil.
+//
+// If the `buffer` isn't empty, the start of the `buffer` should meet
+// IsTranscodingBytecodeAligned, and the length should meet
+// IsTranscodingBytecodeOffsetAligned.
+//
+// NOTE: As long as IsTranscodingBytecodeOffsetAligned is met, that means
+// there's JS::BytecodeOffsetAlignment+extra bytes in the buffer,
+// IsTranscodingBytecodeAligned should be guaranteed to meet by
+// malloc, used by MallocAllocPolicy in mozilla::Vector.
+extern JS_PUBLIC_API bool FinishIncrementalEncoding(JSContext* cx,
+ Handle<JSScript*> script,
+ TranscodeBuffer& buffer);
+
+// Check if the compile options and script's flag matches.
+//
+// JS::DecodeScript* and JS::DecodeOffThreadScript internally check this.
+//
+// JS::DecodeMultiOffThreadScripts checks some options shared across multiple
+// scripts. Caller is responsible for checking each script with this API when
+// using the decoded script instead of compiling a new script wiht the given
+// options.
+extern JS_PUBLIC_API bool CheckCompileOptionsMatch(
+ const ReadOnlyCompileOptions& options, JSScript* script);
+
+} // namespace JS
+
+#endif /* js_Transcoding_h */
diff --git a/js/public/TypeDecls.h b/js/public/TypeDecls.h
new file mode 100644
index 0000000000..e59c2aa27c
--- /dev/null
+++ b/js/public/TypeDecls.h
@@ -0,0 +1,129 @@
+/* -*- 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/. */
+
+// This file contains public type declarations that are used *frequently*. If
+// it doesn't occur at least 10 times in Gecko, it probably shouldn't be in
+// here.
+//
+// It includes only:
+// - forward declarations of structs and classes;
+// - typedefs;
+// - enums (maybe).
+// It does *not* contain any struct or class definitions.
+
+#ifndef js_TypeDecls_h
+#define js_TypeDecls_h
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "js-config.h"
+#include "jstypes.h"
+
+typedef uint8_t jsbytecode;
+
+class JS_PUBLIC_API JSAtom;
+struct JS_PUBLIC_API JSContext;
+struct JSClass;
+class JS_PUBLIC_API JSFunction;
+class JS_PUBLIC_API JSFreeOp;
+class JS_PUBLIC_API JSObject;
+struct JS_PUBLIC_API JSRuntime;
+class JS_PUBLIC_API JSScript;
+class JS_PUBLIC_API JSString;
+
+namespace js {
+class JS_PUBLIC_API TempAllocPolicy;
+}; // namespace js
+
+namespace JS {
+
+struct JS_PUBLIC_API PropertyKey;
+
+typedef unsigned char Latin1Char;
+
+class JS_PUBLIC_API Symbol;
+class JS_PUBLIC_API BigInt;
+class JS_PUBLIC_API Value;
+
+class JS_PUBLIC_API Compartment;
+class JS_PUBLIC_API Realm;
+struct JS_PUBLIC_API Runtime;
+class JS_PUBLIC_API Zone;
+
+template <typename T>
+class Handle;
+template <typename T>
+class MutableHandle;
+template <typename T>
+class Rooted;
+template <typename T>
+class PersistentRooted;
+template <typename T>
+class RootedVector;
+template <typename T>
+class PersistentRootedVector;
+template <typename T, typename AllocPolicy = js::TempAllocPolicy>
+class StackGCVector;
+
+typedef Handle<JSFunction*> HandleFunction;
+typedef Handle<PropertyKey> HandleId;
+typedef Handle<JSObject*> HandleObject;
+typedef Handle<JSScript*> HandleScript;
+typedef Handle<JSString*> HandleString;
+typedef Handle<JS::Symbol*> HandleSymbol;
+typedef Handle<JS::BigInt*> HandleBigInt;
+typedef Handle<Value> HandleValue;
+typedef Handle<StackGCVector<Value>> HandleValueVector;
+typedef Handle<StackGCVector<JSObject*>> HandleObjectVector;
+typedef Handle<StackGCVector<JS::PropertyKey>> HandleIdVector;
+
+typedef MutableHandle<JSFunction*> MutableHandleFunction;
+typedef MutableHandle<PropertyKey> MutableHandleId;
+typedef MutableHandle<JSObject*> MutableHandleObject;
+typedef MutableHandle<JSScript*> MutableHandleScript;
+typedef MutableHandle<JSString*> MutableHandleString;
+typedef MutableHandle<JS::Symbol*> MutableHandleSymbol;
+typedef MutableHandle<JS::BigInt*> MutableHandleBigInt;
+typedef MutableHandle<Value> MutableHandleValue;
+typedef MutableHandle<StackGCVector<Value>> MutableHandleValueVector;
+typedef MutableHandle<StackGCVector<JSObject*>> MutableHandleObjectVector;
+typedef MutableHandle<StackGCVector<JS::PropertyKey>> MutableHandleIdVector;
+
+typedef Rooted<JSObject*> RootedObject;
+typedef Rooted<JSFunction*> RootedFunction;
+typedef Rooted<JSScript*> RootedScript;
+typedef Rooted<JSString*> RootedString;
+typedef Rooted<JS::Symbol*> RootedSymbol;
+typedef Rooted<JS::BigInt*> RootedBigInt;
+typedef Rooted<PropertyKey> RootedId;
+typedef Rooted<JS::Value> RootedValue;
+
+typedef RootedVector<JS::Value> RootedValueVector;
+typedef RootedVector<JSObject*> RootedObjectVector;
+typedef RootedVector<JS::PropertyKey> RootedIdVector;
+
+typedef PersistentRooted<JSFunction*> PersistentRootedFunction;
+typedef PersistentRooted<PropertyKey> PersistentRootedId;
+typedef PersistentRooted<JSObject*> PersistentRootedObject;
+typedef PersistentRooted<JSScript*> PersistentRootedScript;
+typedef PersistentRooted<JSString*> PersistentRootedString;
+typedef PersistentRooted<JS::Symbol*> PersistentRootedSymbol;
+typedef PersistentRooted<JS::BigInt*> PersistentRootedBigInt;
+typedef PersistentRooted<Value> PersistentRootedValue;
+
+typedef PersistentRootedVector<PropertyKey> PersistentRootedIdVector;
+typedef PersistentRootedVector<JSObject*> PersistentRootedObjectVector;
+
+template <typename T>
+using HandleVector = Handle<StackGCVector<T>>;
+template <typename T>
+using MutableHandleVector = MutableHandle<StackGCVector<T>>;
+} // namespace JS
+
+using jsid = JS::PropertyKey;
+
+#endif /* js_TypeDecls_h */
diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h
new file mode 100644
index 0000000000..f4bd4e3734
--- /dev/null
+++ b/js/public/UbiNode.h
@@ -0,0 +1,1209 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UbiNode_h
+#define js_UbiNode_h
+
+#include "mozilla/Alignment.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/RangedPtr.h"
+#include "mozilla/Variant.h"
+
+#include <utility>
+
+#include "jspubtd.h"
+
+#include "js/AllocPolicy.h"
+#include "js/GCAPI.h"
+#include "js/HashTable.h"
+#include "js/RootingAPI.h"
+#include "js/TracingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/UniquePtr.h"
+#include "js/Value.h"
+#include "js/Vector.h"
+
+// [SMDOC] ubi::Node (Heap Analysis framework)
+//
+// JS::ubi::Node is a pointer-like type designed for internal use by heap
+// analysis tools. A ubi::Node can refer to:
+//
+// - a JS value, like a string, object, or symbol;
+// - an internal SpiderMonkey structure, like a shape or a scope chain object
+// - an instance of some embedding-provided type: in Firefox, an XPCOM
+// object, or an internal DOM node class instance
+//
+// A ubi::Node instance provides metadata about its referent, and can
+// enumerate its referent's outgoing edges, so you can implement heap analysis
+// algorithms that walk the graph - finding paths between objects, or
+// computing heap dominator trees, say - using ubi::Node, while remaining
+// ignorant of the details of the types you're operating on.
+//
+// Of course, when it comes to presenting the results in a developer-facing
+// tool, you'll need to stop being ignorant of those details, because you have
+// to discuss the ubi::Nodes' referents with the developer. Here, ubi::Node
+// can hand you dynamically checked, properly typed pointers to the original
+// objects via the as<T> method, or generate descriptions of the referent
+// itself.
+//
+// ubi::Node instances are lightweight (two-word) value types. Instances:
+// - compare equal if and only if they refer to the same object;
+// - have hash values that respect their equality relation; and
+// - have serializations that are only equal if the ubi::Nodes are equal.
+//
+// A ubi::Node is only valid for as long as its referent is alive; if its
+// referent goes away, the ubi::Node becomes a dangling pointer. A ubi::Node
+// that refers to a GC-managed object is not automatically a GC root; if the
+// GC frees or relocates its referent, the ubi::Node becomes invalid. A
+// ubi::Node that refers to a reference-counted object does not bump the
+// reference count.
+//
+// ubi::Node values require no supporting data structures, making them
+// feasible for use in memory-constrained devices --- ideally, the memory
+// requirements of the algorithm which uses them will be the limiting factor,
+// not the demands of ubi::Node itself.
+//
+// One can construct a ubi::Node value given a pointer to a type that ubi::Node
+// supports. In the other direction, one can convert a ubi::Node back to a
+// pointer; these downcasts are checked dynamically. In particular, one can
+// convert a 'JSContext*' to a ubi::Node, yielding a node with an outgoing edge
+// for every root registered with the runtime; starting from this, one can walk
+// the entire heap. (Of course, one could also start traversal at any other kind
+// of type to which one has a pointer.)
+//
+//
+// Extending ubi::Node To Handle Your Embedding's Types
+//
+// To add support for a new ubi::Node referent type R, you must define a
+// specialization of the ubi::Concrete template, ubi::Concrete<R>, which
+// inherits from ubi::Base. ubi::Node itself uses the specialization for
+// compile-time information (i.e. the checked conversions between R * and
+// ubi::Node), and the inheritance for run-time dispatching.
+//
+//
+// ubi::Node Exposes Implementation Details
+//
+// In many cases, a JavaScript developer's view of their data differs
+// substantially from its actual implementation. For example, while the
+// ECMAScript specification describes objects as maps from property names to
+// sets of attributes (like ECMAScript's [[Value]]), in practice many objects
+// have only a pointer to a shape, shared with other similar objects, and
+// indexed slots that contain the [[Value]] attributes. As another example, a
+// string produced by concatenating two other strings may sometimes be
+// represented by a "rope", a structure that points to the two original
+// strings.
+//
+// We intend to use ubi::Node to write tools that report memory usage, so it's
+// important that ubi::Node accurately portray how much memory nodes consume.
+// Thus, for example, when data that apparently belongs to multiple nodes is
+// in fact shared in a common structure, ubi::Node's graph uses a separate
+// node for that shared structure, and presents edges to it from the data's
+// apparent owners. For example, ubi::Node exposes SpiderMonkey objects'
+// shapes and base shapes, and exposes rope string and substring structure,
+// because these optimizations become visible when a tool reports how much
+// memory a structure consumes.
+//
+// However, fine granularity is not a goal. When a particular object is the
+// exclusive owner of a separate block of memory, ubi::Node may present the
+// object and its block as a single node, and add their sizes together when
+// reporting the node's size, as there is no meaningful loss of data in this
+// case. Thus, for example, a ubi::Node referring to a JavaScript object, when
+// asked for the object's size in bytes, includes the object's slot and
+// element arrays' sizes in the total. There is no separate ubi::Node value
+// representing the slot and element arrays, since they are owned exclusively
+// by the object.
+//
+//
+// Presenting Analysis Results To JavaScript Developers
+//
+// If an analysis provides its results in terms of ubi::Node values, a user
+// interface presenting those results will generally need to clean them up
+// before they can be understood by JavaScript developers. For example,
+// JavaScript developers should not need to understand shapes, only JavaScript
+// objects. Similarly, they should not need to understand the distinction
+// between DOM nodes and the JavaScript shadow objects that represent them.
+//
+//
+// Rooting Restrictions
+//
+// At present there is no way to root ubi::Node instances, so instances can't be
+// live across any operation that might GC. Analyses using ubi::Node must either
+// run to completion and convert their results to some other rootable type, or
+// save their intermediate state in some rooted structure if they must GC before
+// they complete. (For algorithms like path-finding and dominator tree
+// computation, we implement the algorithm avoiding any operation that could
+// cause a GC --- and use AutoCheckCannotGC to verify this.)
+//
+// If this restriction prevents us from implementing interesting tools, we may
+// teach the GC how to root ubi::Nodes, fix up hash tables that use them as
+// keys, etc.
+//
+//
+// Hostile Graph Structure
+//
+// Analyses consuming ubi::Node graphs must be robust when presented with graphs
+// that are deliberately constructed to exploit their weaknesses. When operating
+// on live graphs, web content has control over the object graph, and less
+// direct control over shape and string structure, and analyses should be
+// prepared to handle extreme cases gracefully. For example, if an analysis were
+// to use the C++ stack in a depth-first traversal, carefully constructed
+// content could cause the analysis to overflow the stack.
+//
+// When ubi::Nodes refer to nodes deserialized from a heap snapshot, analyses
+// must be even more careful: since snapshots often come from potentially
+// compromised e10s content processes, even properties normally guaranteed by
+// the platform (the proper linking of DOM nodes, for example) might be
+// corrupted. While it is the deserializer's responsibility to check the basic
+// structure of the snapshot file, the analyses should be prepared for ubi::Node
+// graphs constructed from snapshots to be even more bizarre.
+
+namespace js {
+class BaseScript;
+} // namespace js
+
+namespace JS {
+
+using ZoneSet =
+ js::HashSet<Zone*, js::DefaultHasher<Zone*>, js::SystemAllocPolicy>;
+
+using CompartmentSet =
+ js::HashSet<Compartment*, js::DefaultHasher<Compartment*>,
+ js::SystemAllocPolicy>;
+
+namespace ubi {
+
+class Edge;
+class EdgeRange;
+class StackFrame;
+
+using mozilla::Maybe;
+using mozilla::RangedPtr;
+using mozilla::Variant;
+
+template <typename T>
+using Vector = mozilla::Vector<T, 0, js::SystemAllocPolicy>;
+
+/*** ubi::StackFrame **********************************************************/
+
+// Concrete JS::ubi::StackFrame instances backed by a live SavedFrame object
+// store their strings as JSAtom*, while deserialized stack frames from offline
+// heap snapshots store their strings as const char16_t*. In order to provide
+// zero-cost accessors to these strings in a single interface that works with
+// both cases, we use this variant type.
+class JS_PUBLIC_API AtomOrTwoByteChars
+ : public Variant<JSAtom*, const char16_t*> {
+ using Base = Variant<JSAtom*, const char16_t*>;
+
+ public:
+ template <typename T>
+ MOZ_IMPLICIT AtomOrTwoByteChars(T&& rhs) : Base(std::forward<T>(rhs)) {}
+
+ template <typename T>
+ AtomOrTwoByteChars& operator=(T&& rhs) {
+ MOZ_ASSERT(this != &rhs, "self-move disallowed");
+ this->~AtomOrTwoByteChars();
+ new (this) AtomOrTwoByteChars(std::forward<T>(rhs));
+ return *this;
+ }
+
+ // Return the length of the given AtomOrTwoByteChars string.
+ size_t length();
+
+ // Copy the given AtomOrTwoByteChars string into the destination buffer,
+ // inflating if necessary. Does NOT null terminate. Returns the number of
+ // characters written to destination.
+ size_t copyToBuffer(RangedPtr<char16_t> destination, size_t length);
+};
+
+// The base class implemented by each ConcreteStackFrame<T> type. Subclasses
+// must not add data members to this class.
+class BaseStackFrame {
+ friend class StackFrame;
+
+ BaseStackFrame(const StackFrame&) = delete;
+ BaseStackFrame& operator=(const StackFrame&) = delete;
+
+ protected:
+ void* ptr;
+ explicit BaseStackFrame(void* ptr) : ptr(ptr) {}
+
+ public:
+ // This is a value type that should not have a virtual destructor. Don't add
+ // destructors in subclasses!
+
+ // Get a unique identifier for this StackFrame. The identifier is not valid
+ // across garbage collections.
+ virtual uint64_t identifier() const { return uint64_t(uintptr_t(ptr)); }
+
+ // Get this frame's parent frame.
+ virtual StackFrame parent() const = 0;
+
+ // Get this frame's line number.
+ virtual uint32_t line() const = 0;
+
+ // Get this frame's column number.
+ virtual uint32_t column() const = 0;
+
+ // Get this frame's source name. Never null.
+ virtual AtomOrTwoByteChars source() const = 0;
+
+ // Get a unique per-process ID for this frame's source. Defaults to zero.
+ virtual uint32_t sourceId() const = 0;
+
+ // Return this frame's function name if named, otherwise the inferred
+ // display name. Can be null.
+ virtual AtomOrTwoByteChars functionDisplayName() const = 0;
+
+ // Returns true if this frame's function is system JavaScript running with
+ // trusted principals, false otherwise.
+ virtual bool isSystem() const = 0;
+
+ // Return true if this frame's function is a self-hosted JavaScript builtin,
+ // false otherwise.
+ virtual bool isSelfHosted(JSContext* cx) const = 0;
+
+ // Construct a SavedFrame stack for the stack starting with this frame and
+ // containing all of its parents. The SavedFrame objects will be placed into
+ // cx's current compartment.
+ //
+ // Note that the process of
+ //
+ // SavedFrame
+ // |
+ // V
+ // JS::ubi::StackFrame
+ // |
+ // V
+ // offline heap snapshot
+ // |
+ // V
+ // JS::ubi::StackFrame
+ // |
+ // V
+ // SavedFrame
+ //
+ // is lossy because we cannot serialize and deserialize the SavedFrame's
+ // principals in the offline heap snapshot, so JS::ubi::StackFrame
+ // simplifies the principals check into the boolean isSystem() state. This
+ // is fine because we only expose JS::ubi::Stack to devtools and chrome
+ // code, and not to the web platform.
+ virtual MOZ_MUST_USE bool constructSavedFrameStack(
+ JSContext* cx, MutableHandleObject outSavedFrameStack) const = 0;
+
+ // Trace the concrete implementation of JS::ubi::StackFrame.
+ virtual void trace(JSTracer* trc) = 0;
+};
+
+// A traits template with a specialization for each backing type that implements
+// the ubi::BaseStackFrame interface. Each specialization must be the a subclass
+// of ubi::BaseStackFrame.
+template <typename T>
+class ConcreteStackFrame;
+
+// A JS::ubi::StackFrame represents a frame in a recorded stack. It can be
+// backed either by a live SavedFrame object or by a structure deserialized from
+// an offline heap snapshot.
+//
+// It is a value type that may be memcpy'd hither and thither without worrying
+// about constructors or destructors, similar to POD types.
+//
+// Its lifetime is the same as the lifetime of the graph that is being analyzed
+// by the JS::ubi::Node that the JS::ubi::StackFrame came from. That is, if the
+// graph being analyzed is the live heap graph, the JS::ubi::StackFrame is only
+// valid within the scope of an AutoCheckCannotGC; if the graph being analyzed
+// is an offline heap snapshot, the JS::ubi::StackFrame is valid as long as the
+// offline heap snapshot is alive.
+class StackFrame {
+ // Storage in which we allocate BaseStackFrame subclasses.
+ mozilla::AlignedStorage2<BaseStackFrame> storage;
+
+ BaseStackFrame* base() { return storage.addr(); }
+ const BaseStackFrame* base() const { return storage.addr(); }
+
+ template <typename T>
+ void construct(T* ptr) {
+ static_assert(std::is_base_of_v<BaseStackFrame, ConcreteStackFrame<T>>,
+ "ConcreteStackFrame<T> must inherit from BaseStackFrame");
+ static_assert(
+ sizeof(ConcreteStackFrame<T>) == sizeof(*base()),
+ "ubi::ConcreteStackFrame<T> specializations must be the same size as "
+ "ubi::BaseStackFrame");
+ ConcreteStackFrame<T>::construct(base(), ptr);
+ }
+ struct ConstructFunctor;
+
+ public:
+ StackFrame() { construct<void>(nullptr); }
+
+ template <typename T>
+ MOZ_IMPLICIT StackFrame(T* ptr) {
+ construct(ptr);
+ }
+
+ template <typename T>
+ StackFrame& operator=(T* ptr) {
+ construct(ptr);
+ return *this;
+ }
+
+ // Constructors accepting SpiderMonkey's generic-pointer-ish types.
+
+ template <typename T>
+ explicit StackFrame(const JS::Handle<T*>& handle) {
+ construct(handle.get());
+ }
+
+ template <typename T>
+ StackFrame& operator=(const JS::Handle<T*>& handle) {
+ construct(handle.get());
+ return *this;
+ }
+
+ template <typename T>
+ explicit StackFrame(const JS::Rooted<T*>& root) {
+ construct(root.get());
+ }
+
+ template <typename T>
+ StackFrame& operator=(const JS::Rooted<T*>& root) {
+ construct(root.get());
+ return *this;
+ }
+
+ // Because StackFrame is just a vtable pointer and an instance pointer, we
+ // can memcpy everything around instead of making concrete classes define
+ // virtual constructors. See the comment above Node's copy constructor for
+ // more details; that comment applies here as well.
+ StackFrame(const StackFrame& rhs) {
+ memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
+ }
+
+ StackFrame& operator=(const StackFrame& rhs) {
+ memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
+ return *this;
+ }
+
+ bool operator==(const StackFrame& rhs) const {
+ return base()->ptr == rhs.base()->ptr;
+ }
+ bool operator!=(const StackFrame& rhs) const { return !(*this == rhs); }
+
+ explicit operator bool() const { return base()->ptr != nullptr; }
+
+ // Copy this StackFrame's source name into the given |destination|
+ // buffer. Copy no more than |length| characters. The result is *not* null
+ // terminated. Returns how many characters were written into the buffer.
+ size_t source(RangedPtr<char16_t> destination, size_t length) const;
+
+ // Copy this StackFrame's function display name into the given |destination|
+ // buffer. Copy no more than |length| characters. The result is *not* null
+ // terminated. Returns how many characters were written into the buffer.
+ size_t functionDisplayName(RangedPtr<char16_t> destination,
+ size_t length) const;
+
+ // Get the size of the respective strings. 0 is returned for null strings.
+ size_t sourceLength();
+ size_t functionDisplayNameLength();
+
+ // Methods that forward to virtual calls through BaseStackFrame.
+
+ void trace(JSTracer* trc) { base()->trace(trc); }
+ uint64_t identifier() const {
+ auto id = base()->identifier();
+ MOZ_ASSERT(JS::Value::isNumberRepresentable(id));
+ return id;
+ }
+ uint32_t line() const { return base()->line(); }
+ uint32_t column() const { return base()->column(); }
+ AtomOrTwoByteChars source() const { return base()->source(); }
+ uint32_t sourceId() const { return base()->sourceId(); }
+ AtomOrTwoByteChars functionDisplayName() const {
+ return base()->functionDisplayName();
+ }
+ StackFrame parent() const { return base()->parent(); }
+ bool isSystem() const { return base()->isSystem(); }
+ bool isSelfHosted(JSContext* cx) const { return base()->isSelfHosted(cx); }
+ MOZ_MUST_USE bool constructSavedFrameStack(
+ JSContext* cx, MutableHandleObject outSavedFrameStack) const {
+ return base()->constructSavedFrameStack(cx, outSavedFrameStack);
+ }
+
+ struct HashPolicy {
+ using Lookup = JS::ubi::StackFrame;
+
+ static js::HashNumber hash(const Lookup& lookup) {
+ return mozilla::HashGeneric(lookup.identifier());
+ }
+
+ static bool match(const StackFrame& key, const Lookup& lookup) {
+ return key == lookup;
+ }
+
+ static void rekey(StackFrame& k, const StackFrame& newKey) { k = newKey; }
+ };
+};
+
+// The ubi::StackFrame null pointer. Any attempt to operate on a null
+// ubi::StackFrame crashes.
+template <>
+class ConcreteStackFrame<void> : public BaseStackFrame {
+ explicit ConcreteStackFrame(void* ptr) : BaseStackFrame(ptr) {}
+
+ public:
+ static void construct(void* storage, void*) {
+ new (storage) ConcreteStackFrame(nullptr);
+ }
+
+ uint64_t identifier() const override { return 0; }
+ void trace(JSTracer* trc) override {}
+ MOZ_MUST_USE bool constructSavedFrameStack(
+ JSContext* cx, MutableHandleObject out) const override {
+ out.set(nullptr);
+ return true;
+ }
+
+ uint32_t line() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
+ uint32_t column() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
+ AtomOrTwoByteChars source() const override {
+ MOZ_CRASH("null JS::ubi::StackFrame");
+ }
+ uint32_t sourceId() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
+ AtomOrTwoByteChars functionDisplayName() const override {
+ MOZ_CRASH("null JS::ubi::StackFrame");
+ }
+ StackFrame parent() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
+ bool isSystem() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
+ bool isSelfHosted(JSContext* cx) const override {
+ MOZ_CRASH("null JS::ubi::StackFrame");
+ }
+};
+
+MOZ_MUST_USE JS_PUBLIC_API bool ConstructSavedFrameStackSlow(
+ JSContext* cx, JS::ubi::StackFrame& frame,
+ MutableHandleObject outSavedFrameStack);
+
+/*** ubi::Node
+ * ************************************************************************************/
+
+// A concrete node specialization can claim its referent is a member of a
+// particular "coarse type" which is less specific than the actual
+// implementation type but generally more palatable for web developers. For
+// example, JitCode can be considered to have a coarse type of "Script". This is
+// used by some analyses for putting nodes into different buckets. The default,
+// if a concrete specialization does not provide its own mapping to a CoarseType
+// variant, is "Other".
+//
+// NB: the values associated with a particular enum variant must not change or
+// be reused for new variants. Doing so will cause inspecting ubi::Nodes backed
+// by an offline heap snapshot from an older SpiderMonkey/Firefox version to
+// break. Consider this enum append only.
+enum class CoarseType : uint32_t {
+ Other = 0,
+ Object = 1,
+ Script = 2,
+ String = 3,
+ DOMNode = 4,
+
+ FIRST = Other,
+ LAST = DOMNode
+};
+
+/**
+ * Convert a CoarseType enum into a string. The string is statically allocated.
+ */
+JS_PUBLIC_API const char* CoarseTypeToString(CoarseType type);
+
+inline uint32_t CoarseTypeToUint32(CoarseType type) {
+ return static_cast<uint32_t>(type);
+}
+
+inline bool Uint32IsValidCoarseType(uint32_t n) {
+ auto first = static_cast<uint32_t>(CoarseType::FIRST);
+ auto last = static_cast<uint32_t>(CoarseType::LAST);
+ MOZ_ASSERT(first < last);
+ return first <= n && n <= last;
+}
+
+inline CoarseType Uint32ToCoarseType(uint32_t n) {
+ MOZ_ASSERT(Uint32IsValidCoarseType(n));
+ return static_cast<CoarseType>(n);
+}
+
+// The base class implemented by each ubi::Node referent type. Subclasses must
+// not add data members to this class.
+class JS_PUBLIC_API Base {
+ friend class Node;
+
+ // For performance's sake, we'd prefer to avoid a virtual destructor; and
+ // an empty constructor seems consistent with the 'lightweight value type'
+ // visible behavior we're trying to achieve. But if the destructor isn't
+ // virtual, and a subclass overrides it, the subclass's destructor will be
+ // ignored. Is there a way to make the compiler catch that error?
+
+ protected:
+ // Space for the actual pointer. Concrete subclasses should define a
+ // properly typed 'get' member function to access this.
+ void* ptr;
+
+ explicit Base(void* ptr) : ptr(ptr) {}
+
+ public:
+ bool operator==(const Base& rhs) const {
+ // Some compilers will indeed place objects of different types at
+ // the same address, so technically, we should include the vtable
+ // in this comparison. But it seems unlikely to cause problems in
+ // practice.
+ return ptr == rhs.ptr;
+ }
+ bool operator!=(const Base& rhs) const { return !(*this == rhs); }
+
+ // An identifier for this node, guaranteed to be stable and unique for as
+ // long as this ubi::Node's referent is alive and at the same address.
+ //
+ // This is probably suitable for use in serializations, as it is an integral
+ // type. It may also help save memory when constructing HashSets of
+ // ubi::Nodes: since a uint64_t will always be smaller-or-equal-to the size
+ // of a ubi::Node, a HashSet<ubi::Node::Id> may use less space per element
+ // than a HashSet<ubi::Node>.
+ //
+ // (Note that 'unique' only means 'up to equality on ubi::Node'; see the
+ // caveats about multiple objects allocated at the same address for
+ // 'ubi::Node::operator=='.)
+ using Id = uint64_t;
+ virtual Id identifier() const { return Id(uintptr_t(ptr)); }
+
+ // Returns true if this node is pointing to something on the live heap, as
+ // opposed to something from a deserialized core dump. Returns false,
+ // otherwise.
+ virtual bool isLive() const { return true; };
+
+ // Return the coarse-grained type-of-thing that this node represents.
+ virtual CoarseType coarseType() const { return CoarseType::Other; }
+
+ // Return a human-readable name for the referent's type. The result should
+ // be statically allocated. (You can use u"strings" for this.)
+ //
+ // This must always return Concrete<T>::concreteTypeName; we use that
+ // pointer as a tag for this particular referent type.
+ virtual const char16_t* typeName() const = 0;
+
+ // Return the size of this node, in bytes. Include any structures that this
+ // node owns exclusively that are not exposed as their own ubi::Nodes.
+ // |mallocSizeOf| should be a malloc block sizing function; see
+ // |mfbt/MemoryReporting.h|.
+ //
+ // Because we can use |JS::ubi::Node|s backed by a snapshot that was taken
+ // on a 64-bit platform when we are currently on a 32-bit platform, we
+ // cannot rely on |size_t| for node sizes. Instead, |Size| is uint64_t on
+ // all platforms.
+ using Size = uint64_t;
+ virtual Size size(mozilla::MallocSizeOf mallocSizeof) const { return 1; }
+
+ // Return an EdgeRange that initially contains all the referent's outgoing
+ // edges. The caller takes ownership of the EdgeRange.
+ //
+ // If wantNames is true, compute names for edges. Doing so can be expensive
+ // in time and memory.
+ virtual js::UniquePtr<EdgeRange> edges(JSContext* cx,
+ bool wantNames) const = 0;
+
+ // Return the Zone to which this node's referent belongs, or nullptr if the
+ // referent is not of a type allocated in SpiderMonkey Zones.
+ virtual JS::Zone* zone() const { return nullptr; }
+
+ // Return the compartment for this node. Some ubi::Node referents are not
+ // associated with Compartments, such as JSStrings (which are associated
+ // with Zones). When the referent is not associated with a compartment,
+ // nullptr is returned.
+ virtual JS::Compartment* compartment() const { return nullptr; }
+
+ // Return the realm for this node. Some ubi::Node referents are not
+ // associated with Realms, such as JSStrings (which are associated
+ // with Zones) or cross-compartment wrappers (which are associated with
+ // compartments). When the referent is not associated with a realm,
+ // nullptr is returned.
+ virtual JS::Realm* realm() const { return nullptr; }
+
+ // Return whether this node's referent's allocation stack was captured.
+ virtual bool hasAllocationStack() const { return false; }
+
+ // Get the stack recorded at the time this node's referent was
+ // allocated. This must only be called when hasAllocationStack() is true.
+ virtual StackFrame allocationStack() const {
+ MOZ_CRASH(
+ "Concrete classes that have an allocation stack must override both "
+ "hasAllocationStack and allocationStack.");
+ }
+
+ // In some cases, Concrete<T> can return a more descriptive
+ // referent type name than simply `T`. This method returns an
+ // identifier as specific as is efficiently available.
+ // The string returned is borrowed from the ubi::Node's referent.
+ // If nothing more specific than typeName() is available, return nullptr.
+ virtual const char16_t* descriptiveTypeName() const { return nullptr; }
+
+ // Methods for JSObject Referents
+ //
+ // These methods are only semantically valid if the referent is either a
+ // JSObject in the live heap, or represents a previously existing JSObject
+ // from some deserialized heap snapshot.
+
+ // Return the object's [[Class]]'s name.
+ virtual const char* jsObjectClassName() const { return nullptr; }
+
+ // Methods for CoarseType::Script referents
+
+ // Return the script's source's filename if available. If unavailable,
+ // return nullptr.
+ virtual const char* scriptFilename() const { return nullptr; }
+
+ private:
+ Base(const Base& rhs) = delete;
+ Base& operator=(const Base& rhs) = delete;
+};
+
+// A traits template with a specialization for each referent type that
+// ubi::Node supports. The specialization must be the concrete subclass of Base
+// that represents a pointer to the referent type. It must include these
+// members:
+//
+// // The specific char16_t array returned by Concrete<T>::typeName().
+// static const char16_t concreteTypeName[];
+//
+// // Construct an instance of this concrete class in |storage| referring
+// // to |referent|. Implementations typically use a placement 'new'.
+// //
+// // In some cases, |referent| will contain dynamic type information that
+// // identifies it a some more specific subclass of |Referent|. For
+// // example, when |Referent| is |JSObject|, then |referent->getClass()|
+// // could tell us that it's actually a JSFunction. Similarly, if
+// // |Referent| is |nsISupports|, we would like a ubi::Node that knows its
+// // final implementation type.
+// //
+// // So we delegate the actual construction to this specialization, which
+// // knows Referent's details.
+// static void construct(void* storage, Referent* referent);
+template <typename Referent>
+class Concrete;
+
+// A container for a Base instance; all members simply forward to the contained
+// instance. This container allows us to pass ubi::Node instances by value.
+class Node {
+ // Storage in which we allocate Base subclasses.
+ mozilla::AlignedStorage2<Base> storage;
+ Base* base() { return storage.addr(); }
+ const Base* base() const { return storage.addr(); }
+
+ template <typename T>
+ void construct(T* ptr) {
+ static_assert(
+ sizeof(Concrete<T>) == sizeof(*base()),
+ "ubi::Base specializations must be the same size as ubi::Base");
+ static_assert(std::is_base_of_v<Base, Concrete<T>>,
+ "ubi::Concrete<T> must inherit from ubi::Base");
+ Concrete<T>::construct(base(), ptr);
+ }
+ struct ConstructFunctor;
+
+ public:
+ Node() { construct<void>(nullptr); }
+
+ template <typename T>
+ MOZ_IMPLICIT Node(T* ptr) {
+ construct(ptr);
+ }
+ template <typename T>
+ Node& operator=(T* ptr) {
+ construct(ptr);
+ return *this;
+ }
+
+ // We can construct and assign from rooted forms of pointers.
+ template <typename T>
+ MOZ_IMPLICIT Node(const Rooted<T*>& root) {
+ construct(root.get());
+ }
+ template <typename T>
+ Node& operator=(const Rooted<T*>& root) {
+ construct(root.get());
+ return *this;
+ }
+
+ // Constructors accepting SpiderMonkey's other generic-pointer-ish types.
+ // Note that we *do* want an implicit constructor here: JS::Value and
+ // JS::ubi::Node are both essentially tagged references to other sorts of
+ // objects, so letting conversions happen automatically is appropriate.
+ MOZ_IMPLICIT Node(JS::HandleValue value);
+ explicit Node(const JS::GCCellPtr& thing);
+
+ // copy construction and copy assignment just use memcpy, since we know
+ // instances contain nothing but a vtable pointer and a data pointer.
+ //
+ // To be completely correct, concrete classes could provide a virtual
+ // 'construct' member function, which we could invoke on rhs to construct an
+ // instance in our storage. But this is good enough; there's no need to jump
+ // through vtables for copying and assignment that are just going to move
+ // two words around. The compiler knows how to optimize memcpy.
+ Node(const Node& rhs) {
+ memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
+ }
+
+ Node& operator=(const Node& rhs) {
+ memcpy(storage.u.mBytes, rhs.storage.u.mBytes, sizeof(storage.u));
+ return *this;
+ }
+
+ bool operator==(const Node& rhs) const { return *base() == *rhs.base(); }
+ bool operator!=(const Node& rhs) const { return *base() != *rhs.base(); }
+
+ explicit operator bool() const { return base()->ptr != nullptr; }
+
+ bool isLive() const { return base()->isLive(); }
+
+ // Get the canonical type name for the given type T.
+ template <typename T>
+ static const char16_t* canonicalTypeName() {
+ return Concrete<T>::concreteTypeName;
+ }
+
+ template <typename T>
+ bool is() const {
+ return base()->typeName() == canonicalTypeName<T>();
+ }
+
+ template <typename T>
+ T* as() const {
+ MOZ_ASSERT(isLive());
+ MOZ_ASSERT(this->is<T>());
+ return static_cast<T*>(base()->ptr);
+ }
+
+ template <typename T>
+ T* asOrNull() const {
+ MOZ_ASSERT(isLive());
+ return this->is<T>() ? static_cast<T*>(base()->ptr) : nullptr;
+ }
+
+ // If this node refers to something that can be represented as a JavaScript
+ // value that is safe to expose to JavaScript code, return that value.
+ // Otherwise return UndefinedValue(). JSStrings, JS::Symbols, and some (but
+ // not all!) JSObjects can be exposed.
+ JS::Value exposeToJS() const;
+
+ CoarseType coarseType() const { return base()->coarseType(); }
+ const char16_t* typeName() const { return base()->typeName(); }
+ JS::Zone* zone() const { return base()->zone(); }
+ JS::Compartment* compartment() const { return base()->compartment(); }
+ JS::Realm* realm() const { return base()->realm(); }
+ const char* jsObjectClassName() const { return base()->jsObjectClassName(); }
+ const char16_t* descriptiveTypeName() const {
+ return base()->descriptiveTypeName();
+ }
+
+ const char* scriptFilename() const { return base()->scriptFilename(); }
+
+ using Size = Base::Size;
+ Size size(mozilla::MallocSizeOf mallocSizeof) const {
+ auto size = base()->size(mallocSizeof);
+ MOZ_ASSERT(
+ size > 0,
+ "C++ does not have zero-sized types! Choose 1 if you just need a "
+ "conservative default.");
+ return size;
+ }
+
+ js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames = true) const {
+ return base()->edges(cx, wantNames);
+ }
+
+ bool hasAllocationStack() const { return base()->hasAllocationStack(); }
+ StackFrame allocationStack() const { return base()->allocationStack(); }
+
+ using Id = Base::Id;
+ Id identifier() const {
+ auto id = base()->identifier();
+ MOZ_ASSERT(JS::Value::isNumberRepresentable(id));
+ return id;
+ }
+
+ // A hash policy for ubi::Nodes.
+ // This simply uses the stock PointerHasher on the ubi::Node's pointer.
+ // We specialize DefaultHasher below to make this the default.
+ class HashPolicy {
+ typedef js::PointerHasher<void*> PtrHash;
+
+ public:
+ typedef Node Lookup;
+
+ static js::HashNumber hash(const Lookup& l) {
+ return PtrHash::hash(l.base()->ptr);
+ }
+ static bool match(const Node& k, const Lookup& l) { return k == l; }
+ static void rekey(Node& k, const Node& newKey) { k = newKey; }
+ };
+};
+
+using NodeSet =
+ js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
+using NodeSetPtr = mozilla::UniquePtr<NodeSet, JS::DeletePolicy<NodeSet>>;
+
+/*** Edge and EdgeRange *******************************************************/
+
+using EdgeName = UniqueTwoByteChars;
+
+// An outgoing edge to a referent node.
+class Edge {
+ public:
+ Edge() = default;
+
+ // Construct an initialized Edge, taking ownership of |name|.
+ Edge(char16_t* name, const Node& referent) : name(name), referent(referent) {}
+
+ // Move construction and assignment.
+ Edge(Edge&& rhs) : name(std::move(rhs.name)), referent(rhs.referent) {}
+
+ Edge& operator=(Edge&& rhs) {
+ MOZ_ASSERT(&rhs != this);
+ this->~Edge();
+ new (this) Edge(std::move(rhs));
+ return *this;
+ }
+
+ Edge(const Edge&) = delete;
+ Edge& operator=(const Edge&) = delete;
+
+ // This edge's name. This may be nullptr, if Node::edges was called with
+ // false as the wantNames parameter.
+ //
+ // The storage is owned by this Edge, and will be freed when this Edge is
+ // destructed. You may take ownership of the name by `std::move`ing it
+ // out of the edge; it is just a UniquePtr.
+ //
+ // (In real life we'll want a better representation for names, to avoid
+ // creating tons of strings when the names follow a pattern; and we'll need
+ // to think about lifetimes carefully to ensure traversal stays cheap.)
+ EdgeName name = nullptr;
+
+ // This edge's referent.
+ Node referent;
+};
+
+// EdgeRange is an abstract base class for iterating over a node's outgoing
+// edges. (This is modeled after js::HashTable<K,V>::Range.)
+//
+// Concrete instances of this class need not be as lightweight as Node itself,
+// since they're usually only instantiated while iterating over a particular
+// object's edges. For example, a dumb implementation for JS Cells might use
+// JS::TraceChildren to to get the outgoing edges, and then store them in an
+// array internal to the EdgeRange.
+class EdgeRange {
+ protected:
+ // The current front edge of this range, or nullptr if this range is empty.
+ Edge* front_;
+
+ EdgeRange() : front_(nullptr) {}
+
+ public:
+ virtual ~EdgeRange() = default;
+
+ // True if there are no more edges in this range.
+ bool empty() const { return !front_; }
+
+ // The front edge of this range. This is owned by the EdgeRange, and is
+ // only guaranteed to live until the next call to popFront, or until
+ // the EdgeRange is destructed.
+ const Edge& front() const { return *front_; }
+ Edge& front() { return *front_; }
+
+ // Remove the front edge from this range. This should only be called if
+ // !empty().
+ virtual void popFront() = 0;
+
+ private:
+ EdgeRange(const EdgeRange&) = delete;
+ EdgeRange& operator=(const EdgeRange&) = delete;
+};
+
+typedef mozilla::Vector<Edge, 8, js::SystemAllocPolicy> EdgeVector;
+
+// An EdgeRange concrete class that holds a pre-existing vector of
+// Edges. A PreComputedEdgeRange does not take ownership of its
+// EdgeVector; it is up to the PreComputedEdgeRange's consumer to manage
+// that lifetime.
+class PreComputedEdgeRange : public EdgeRange {
+ EdgeVector& edges;
+ size_t i;
+
+ void settle() { front_ = i < edges.length() ? &edges[i] : nullptr; }
+
+ public:
+ explicit PreComputedEdgeRange(EdgeVector& edges) : edges(edges), i(0) {
+ settle();
+ }
+
+ void popFront() override {
+ MOZ_ASSERT(!empty());
+ i++;
+ settle();
+ }
+};
+
+/*** RootList *****************************************************************/
+
+// RootList is a class that can be pointed to by a |ubi::Node|, creating a
+// fictional root-of-roots which has edges to every GC root in the JS
+// runtime. Having a single root |ubi::Node| is useful for algorithms written
+// with the assumption that there aren't multiple roots (such as computing
+// dominator trees) and you want a single point of entry. It also ensures that
+// the roots themselves get visited by |ubi::BreadthFirst| (they would otherwise
+// only be used as starting points).
+//
+// RootList::init itself causes a minor collection, but once the list of roots
+// has been created, GC must not occur, as the referent ubi::Nodes are not
+// stable across GC. The init calls emplace on |noGC|'s AutoCheckCannotGC, whose
+// lifetime must extend at least as long as the RootList itself.
+//
+// Example usage:
+//
+// {
+// mozilla::Maybe<JS::AutoCheckCannotGC> maybeNoGC;
+// JS::ubi::RootList rootList(cx, maybeNoGC);
+// if (!rootList.init()) {
+// return false;
+// }
+//
+// // The AutoCheckCannotGC is guaranteed to exist if init returned true.
+// MOZ_ASSERT(maybeNoGC.isSome());
+//
+// JS::ubi::Node root(&rootList);
+//
+// ...
+// }
+class MOZ_STACK_CLASS JS_PUBLIC_API RootList {
+ Maybe<AutoCheckCannotGC>& noGC;
+
+ public:
+ JSContext* cx;
+ EdgeVector edges;
+ bool wantNames;
+
+ RootList(JSContext* cx, Maybe<AutoCheckCannotGC>& noGC,
+ bool wantNames = false);
+
+ // Find all GC roots.
+ MOZ_MUST_USE bool init();
+ // Find only GC roots in the provided set of |JS::Compartment|s. Note: it's
+ // important to take a CompartmentSet and not a RealmSet: objects in
+ // same-compartment realms can reference each other directly, without going
+ // through CCWs, so if we used a RealmSet here we would miss edges.
+ MOZ_MUST_USE bool init(CompartmentSet& debuggees);
+ // Find only GC roots in the given Debugger object's set of debuggee
+ // compartments.
+ MOZ_MUST_USE bool init(HandleObject debuggees);
+
+ // Returns true if the RootList has been initialized successfully, false
+ // otherwise.
+ bool initialized() { return noGC.isSome(); }
+
+ // Explicitly add the given Node as a root in this RootList. If wantNames is
+ // true, you must pass an edgeName. The RootList does not take ownership of
+ // edgeName.
+ MOZ_MUST_USE bool addRoot(Node node, const char16_t* edgeName = nullptr);
+};
+
+/*** Concrete classes for ubi::Node referent types ****************************/
+
+template <>
+class JS_PUBLIC_API Concrete<RootList> : public Base {
+ protected:
+ explicit Concrete(RootList* ptr) : Base(ptr) {}
+ RootList& get() const { return *static_cast<RootList*>(ptr); }
+
+ public:
+ static void construct(void* storage, RootList* ptr) {
+ new (storage) Concrete(ptr);
+ }
+
+ js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames) const override;
+
+ const char16_t* typeName() const override { return concreteTypeName; }
+ static const char16_t concreteTypeName[];
+};
+
+// A reusable ubi::Concrete specialization base class for types supported by
+// JS::TraceChildren.
+template <typename Referent>
+class JS_PUBLIC_API TracerConcrete : public Base {
+ JS::Zone* zone() const override;
+
+ public:
+ js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames) const override;
+
+ protected:
+ explicit TracerConcrete(Referent* ptr) : Base(ptr) {}
+ Referent& get() const { return *static_cast<Referent*>(ptr); }
+};
+
+// For JS::TraceChildren-based types that have 'realm' and 'compartment'
+// methods.
+template <typename Referent>
+class JS_PUBLIC_API TracerConcreteWithRealm : public TracerConcrete<Referent> {
+ typedef TracerConcrete<Referent> TracerBase;
+ JS::Compartment* compartment() const override;
+ JS::Realm* realm() const override;
+
+ protected:
+ explicit TracerConcreteWithRealm(Referent* ptr) : TracerBase(ptr) {}
+};
+
+// Define specializations for some commonly-used public JSAPI types.
+// These can use the generic templates above.
+template <>
+class JS_PUBLIC_API Concrete<JS::Symbol> : TracerConcrete<JS::Symbol> {
+ protected:
+ explicit Concrete(JS::Symbol* ptr) : TracerConcrete(ptr) {}
+
+ public:
+ static void construct(void* storage, JS::Symbol* ptr) {
+ new (storage) Concrete(ptr);
+ }
+
+ Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+ const char16_t* typeName() const override { return concreteTypeName; }
+ static const char16_t concreteTypeName[];
+};
+
+template <>
+class JS_PUBLIC_API Concrete<JS::BigInt> : TracerConcrete<JS::BigInt> {
+ protected:
+ explicit Concrete(JS::BigInt* ptr) : TracerConcrete(ptr) {}
+
+ public:
+ static void construct(void* storage, JS::BigInt* ptr) {
+ new (storage) Concrete(ptr);
+ }
+
+ Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+ const char16_t* typeName() const override { return concreteTypeName; }
+ static const char16_t concreteTypeName[];
+};
+
+template <>
+class JS_PUBLIC_API Concrete<js::BaseScript>
+ : TracerConcreteWithRealm<js::BaseScript> {
+ protected:
+ explicit Concrete(js::BaseScript* ptr)
+ : TracerConcreteWithRealm<js::BaseScript>(ptr) {}
+
+ public:
+ static void construct(void* storage, js::BaseScript* ptr) {
+ new (storage) Concrete(ptr);
+ }
+
+ CoarseType coarseType() const final { return CoarseType::Script; }
+ Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+ const char* scriptFilename() const final;
+
+ const char16_t* typeName() const override { return concreteTypeName; }
+ static const char16_t concreteTypeName[];
+};
+
+// The JSObject specialization.
+template <>
+class JS_PUBLIC_API Concrete<JSObject> : public TracerConcrete<JSObject> {
+ protected:
+ explicit Concrete(JSObject* ptr) : TracerConcrete<JSObject>(ptr) {}
+
+ public:
+ static void construct(void* storage, JSObject* ptr);
+
+ JS::Compartment* compartment() const override;
+ JS::Realm* realm() const override;
+
+ const char* jsObjectClassName() const override;
+ Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+ bool hasAllocationStack() const override;
+ StackFrame allocationStack() const override;
+
+ CoarseType coarseType() const final { return CoarseType::Object; }
+
+ const char16_t* typeName() const override { return concreteTypeName; }
+ static const char16_t concreteTypeName[];
+};
+
+// For JSString, we extend the generic template with a 'size' implementation.
+template <>
+class JS_PUBLIC_API Concrete<JSString> : TracerConcrete<JSString> {
+ protected:
+ explicit Concrete(JSString* ptr) : TracerConcrete<JSString>(ptr) {}
+
+ public:
+ static void construct(void* storage, JSString* ptr) {
+ new (storage) Concrete(ptr);
+ }
+
+ Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+ CoarseType coarseType() const final { return CoarseType::String; }
+
+ const char16_t* typeName() const override { return concreteTypeName; }
+ static const char16_t concreteTypeName[];
+};
+
+// The ubi::Node null pointer. Any attempt to operate on a null ubi::Node
+// asserts.
+template <>
+class JS_PUBLIC_API Concrete<void> : public Base {
+ const char16_t* typeName() const override;
+ Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+ js::UniquePtr<EdgeRange> edges(JSContext* cx, bool wantNames) const override;
+ JS::Zone* zone() const override;
+ JS::Compartment* compartment() const override;
+ JS::Realm* realm() const override;
+ CoarseType coarseType() const final;
+
+ explicit Concrete(void* ptr) : Base(ptr) {}
+
+ public:
+ static void construct(void* storage, void* ptr) {
+ new (storage) Concrete(ptr);
+ }
+};
+
+// The |callback| callback is much like the |Concrete<T>::construct| method: a
+// call to |callback| should construct an instance of the most appropriate
+// JS::ubi::Base subclass for |obj| in |storage|. The callback may assume that
+// |obj->getClass()->isDOMClass()|, and that |storage| refers to the
+// sizeof(JS::ubi::Base) bytes of space that all ubi::Base implementations
+// should require.
+
+// Set |cx|'s runtime hook for constructing ubi::Nodes for DOM classes to
+// |callback|.
+void SetConstructUbiNodeForDOMObjectCallback(JSContext* cx,
+ void (*callback)(void*,
+ JSObject*));
+
+} // namespace ubi
+} // namespace JS
+
+namespace mozilla {
+
+// Make ubi::Node::HashPolicy the default hash policy for ubi::Node.
+template <>
+struct DefaultHasher<JS::ubi::Node> : JS::ubi::Node::HashPolicy {};
+template <>
+struct DefaultHasher<JS::ubi::StackFrame> : JS::ubi::StackFrame::HashPolicy {};
+
+} // namespace mozilla
+
+#endif // js_UbiNode_h
diff --git a/js/public/UbiNodeBreadthFirst.h b/js/public/UbiNodeBreadthFirst.h
new file mode 100644
index 0000000000..2f269fbf37
--- /dev/null
+++ b/js/public/UbiNodeBreadthFirst.h
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UbiNodeBreadthFirst_h
+#define js_UbiNodeBreadthFirst_h
+
+#include "js/UbiNode.h"
+#include "js/Utility.h"
+#include "js/Vector.h"
+
+namespace JS {
+namespace ubi {
+
+// A breadth-first traversal template for graphs of ubi::Nodes.
+//
+// No GC may occur while an instance of this template is live.
+//
+// The provided Handler type should have two members:
+//
+// typename NodeData;
+//
+// The value type of |BreadthFirst<Handler>::visited|, the HashMap of
+// ubi::Nodes that have been visited so far. Since the algorithm needs a
+// hash table like this for its own use anyway, it is simple to let
+// Handler store its own metadata about each node in the same table.
+//
+// For example, if you want to find a shortest path to each node from any
+// traversal starting point, your |NodeData| type could record the first
+// edge to reach each node, and the node from which it originates. Then,
+// when the traversal is complete, you can walk backwards from any node
+// to some starting point, and the path recorded will be a shortest path.
+//
+// This type must have a default constructor. If this type owns any other
+// resources, move constructors and assignment operators are probably a
+// good idea, too.
+//
+// bool operator() (BreadthFirst& traversal,
+// Node origin, const Edge& edge,
+// Handler::NodeData* referentData, bool first);
+//
+// The visitor function, called to report that we have traversed
+// |edge| from |origin|. This is called once for each edge we traverse.
+// As this is a breadth-first search, any prior calls to the visitor
+// function were for origin nodes not further from the start nodes than
+// |origin|.
+//
+// |traversal| is this traversal object, passed along for convenience.
+//
+// |referentData| is a pointer to the value of the entry in
+// |traversal.visited| for |edge.referent|; the visitor function can
+// store whatever metadata it likes about |edge.referent| there.
+//
+// |first| is true if this is the first time we have visited an edge
+// leading to |edge.referent|. This could be stored in NodeData, but
+// the algorithm knows whether it has just created the entry in
+// |traversal.visited|, so it passes it along for convenience.
+//
+// The visitor function may call |traversal.abandonReferent()| if it
+// doesn't want to traverse the outgoing edges of |edge.referent|. You can
+// use this to limit the traversal to a given portion of the graph: it will
+// never visit nodes reachable only through nodes that you have abandoned.
+// Note that |abandonReferent| must be called the first time the given node
+// is reached; that is, |first| must be true.
+//
+// The visitor function may call |doNotMarkReferentAsVisited()| if it
+// does not want a node to be considered 'visited' (and added to the
+// 'visited' set). This is useful when the visitor has custom logic to
+// determine whether an edge is 'interesting'.
+//
+// The visitor function may call |traversal.stop()| if it doesn't want
+// to visit any more nodes at all.
+//
+// The visitor function may consult |traversal.visited| for information
+// about other nodes, but it should not add or remove entries.
+//
+// The visitor function should return true on success, or false if an
+// error occurs. A false return value terminates the traversal
+// immediately, and causes BreadthFirst<Handler>::traverse to return
+// false.
+template <typename Handler>
+struct BreadthFirst {
+ // Construct a breadth-first traversal object that reports the nodes it
+ // reaches to |handler|. The traversal asserts that no GC happens in its
+ // runtime during its lifetime.
+ //
+ // We do nothing with noGC, other than require it to exist, with a lifetime
+ // that encloses our own.
+ BreadthFirst(JSContext* cx, Handler& handler, const JS::AutoRequireNoGC& noGC)
+ : wantNames(true),
+ cx(cx),
+ visited(),
+ handler(handler),
+ pending(),
+ traversalBegun(false),
+ stopRequested(false),
+ abandonRequested(false),
+ markReferentAsVisited(false) {}
+
+ // Add |node| as a starting point for the traversal. You may add
+ // as many starting points as you like. Return false on OOM.
+ bool addStart(Node node) { return pending.append(node); }
+
+ // Add |node| as a starting point for the traversal (see addStart) and also
+ // add it to the |visited| set. Return false on OOM.
+ bool addStartVisited(Node node) {
+ typename NodeMap::AddPtr ptr = visited.lookupForAdd(node);
+ if (!ptr && !visited.add(ptr, node, typename Handler::NodeData())) {
+ return false;
+ }
+ return addStart(node);
+ }
+
+ // True if the handler wants us to compute edge names; doing so can be
+ // expensive in time and memory. True by default.
+ bool wantNames;
+
+ // Traverse the graph in breadth-first order, starting at the given
+ // start nodes, applying |handler::operator()| for each edge traversed
+ // as described above.
+ //
+ // This should be called only once per instance of this class.
+ //
+ // Return false on OOM or error return from |handler::operator()|.
+ bool traverse() {
+ MOZ_ASSERT(!traversalBegun);
+ traversalBegun = true;
+
+ // While there are pending nodes, visit them.
+ while (!pending.empty()) {
+ Node origin = pending.front();
+ pending.popFront();
+
+ // Get a range containing all origin's outgoing edges.
+ auto range = origin.edges(cx, wantNames);
+ if (!range) {
+ return false;
+ }
+
+ // Traverse each edge.
+ for (; !range->empty(); range->popFront()) {
+ MOZ_ASSERT(!stopRequested);
+
+ Edge& edge = range->front();
+ typename NodeMap::AddPtr a = visited.lookupForAdd(edge.referent);
+ bool first = !a;
+
+ // Pass a pointer to a stack-allocated NodeData if the referent is not
+ // in |visited|.
+ typename Handler::NodeData nodeData;
+ typename Handler::NodeData* nodeDataPtr =
+ first ? &nodeData : &a->value();
+
+ // Report this edge to the visitor function.
+ markReferentAsVisited = true;
+ if (!handler(*this, origin, edge, nodeDataPtr, first)) {
+ return false;
+ }
+
+ if (first && markReferentAsVisited) {
+ // This is the first time we've reached |edge.referent| and the
+ // handler wants it marked as visited.
+ if (!visited.add(a, edge.referent, std::move(nodeData))) {
+ return false;
+ }
+ }
+
+ if (stopRequested) {
+ return true;
+ }
+
+ // Arrange to traverse this edge's referent's outgoing edges
+ // later --- unless |handler| asked us not to.
+ if (abandonRequested) {
+ // Skip the enqueue; reset flag for future iterations.
+ abandonRequested = false;
+ } else if (first) {
+ if (!pending.append(edge.referent)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ // Stop traversal, and return true from |traverse| without visiting any
+ // more nodes. Only |handler::operator()| should call this function; it
+ // may do so to stop the traversal early, without returning false and
+ // then making |traverse|'s caller disambiguate that result from a real
+ // error.
+ void stop() { stopRequested = true; }
+
+ // Request that the current edge's referent's outgoing edges not be
+ // traversed. This must be called the first time that referent is reached.
+ // Other edges *to* that referent will still be traversed.
+ void abandonReferent() { abandonRequested = true; }
+
+ // Request the the current edge's referent not be added to the |visited| set
+ // if this is the first time we're visiting it.
+ void doNotMarkReferentAsVisited() { markReferentAsVisited = false; }
+
+ // The context with which we were constructed.
+ JSContext* cx;
+
+ // A map associating each node N that we have reached with a
+ // Handler::NodeData, for |handler|'s use. This is public, so that
+ // |handler| can access it to see the traversal thus far.
+ using NodeMap = js::HashMap<Node, typename Handler::NodeData,
+ js::DefaultHasher<Node>, js::SystemAllocPolicy>;
+ NodeMap visited;
+
+ private:
+ // Our handler object.
+ Handler& handler;
+
+ // A queue template. Appending and popping the front are constant time.
+ // Wasted space is never more than some recent actual population plus the
+ // current population.
+ template <typename T>
+ class Queue {
+ js::Vector<T, 0, js::SystemAllocPolicy> head, tail;
+ size_t frontIndex;
+
+ public:
+ Queue() : head(), tail(), frontIndex(0) {}
+ bool empty() { return frontIndex >= head.length(); }
+ T& front() {
+ MOZ_ASSERT(!empty());
+ return head[frontIndex];
+ }
+ void popFront() {
+ MOZ_ASSERT(!empty());
+ frontIndex++;
+ if (frontIndex >= head.length()) {
+ head.clearAndFree();
+ head.swap(tail);
+ frontIndex = 0;
+ }
+ }
+ bool append(const T& elt) {
+ return frontIndex == 0 ? head.append(elt) : tail.append(elt);
+ }
+ };
+
+ // A queue of nodes that we have reached, but whose outgoing edges we
+ // have not yet traversed. Nodes reachable in fewer edges are enqueued
+ // earlier.
+ Queue<Node> pending;
+
+ // True if our traverse function has been called.
+ bool traversalBegun;
+
+ // True if we've been asked to stop the traversal.
+ bool stopRequested;
+
+ // True if we've been asked to abandon the current edge's referent.
+ bool abandonRequested;
+
+ // True if the node should be added to the |visited| set after calling the
+ // handler.
+ bool markReferentAsVisited;
+};
+
+} // namespace ubi
+} // namespace JS
+
+#endif // js_UbiNodeBreadthFirst_h
diff --git a/js/public/UbiNodeCensus.h b/js/public/UbiNodeCensus.h
new file mode 100644
index 0000000000..d247b4abf0
--- /dev/null
+++ b/js/public/UbiNodeCensus.h
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UbiNodeCensus_h
+#define js_UbiNodeCensus_h
+
+#include "mozilla/Attributes.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "jsapi.h"
+
+#include "js/UbiNode.h"
+#include "js/UbiNodeBreadthFirst.h"
+
+// A census is a ubi::Node traversal that assigns each node to one or more
+// buckets, and returns a report with the size of each bucket.
+//
+// We summarize the results of a census with counts broken down according to
+// criteria selected by the API consumer code that is requesting the census. For
+// example, the following breakdown might give an interesting overview of the
+// heap:
+//
+// - all nodes
+// - objects
+// - objects with a specific [[Class]] *
+// - strings
+// - scripts
+// - DOM nodes
+// - nsINodes with a specific name (found in nsINode::NodeName()) *
+// - all other Node types
+// - nodes with a specific ubi::Node::typeName *
+//
+// Obviously, the parts of this tree marked with * represent many separate
+// counts, depending on how many distinct [[Class]] values and ubi::Node type
+// names we encounter.
+//
+// The supported types of breakdowns are documented in
+// js/src/doc/Debugger/Debugger.Memory.md.
+//
+// When we parse the 'breakdown' argument to takeCensus, we build a tree of
+// CountType nodes. For example, for the breakdown shown in the
+// Debugger.Memory.prototype.takeCensus, documentation:
+//
+// {
+// by: "coarseType",
+// objects: { by: "objectClass" },
+// other: { by: "internalType" },
+// domNode: { by: "descriptiveType" }
+// }
+//
+// we would build the following tree of CountType subclasses:
+//
+// ByCoarseType
+// objects: ByObjectClass
+// each class: SimpleCount
+// scripts: SimpleCount
+// strings: SimpleCount
+// other: ByUbinodeType
+// each type: SimpleCount
+// domNode: ByDomObjectClass
+// each type: SimpleCount
+//
+// The interior nodes are all breakdown types that categorize nodes according to
+// one characteristic or another; and the leaf nodes are all SimpleType.
+//
+// Each CountType has its own concrete C++ type that holds the counts it
+// produces. SimpleCount::Count just holds totals. ByObjectClass::Count has a
+// hash table whose keys are object class names and whose values are counts of
+// some other type (in the example above, SimpleCount).
+//
+// To keep actual count nodes small, they have no vtable. Instead, each count
+// points to its CountType, which knows how to carry out all the operations we
+// need on a Count. A CountType can produce new count nodes; process nodes as we
+// visit them; build a JS object reporting the results; and destruct count
+// nodes.
+
+namespace JS {
+namespace ubi {
+
+struct Census;
+
+class CountBase;
+
+struct CountDeleter {
+ JS_PUBLIC_API void operator()(CountBase*);
+};
+
+using CountBasePtr = js::UniquePtr<CountBase, CountDeleter>;
+
+// Abstract base class for CountType nodes.
+struct CountType {
+ explicit CountType() = default;
+ virtual ~CountType() = default;
+
+ // Destruct a count tree node that this type instance constructed.
+ virtual void destructCount(CountBase& count) = 0;
+
+ // Return a fresh node for the count tree that categorizes nodes according
+ // to this type. Return a nullptr on OOM.
+ virtual CountBasePtr makeCount() = 0;
+
+ // Trace |count| and all its children, for garbage collection.
+ virtual void traceCount(CountBase& count, JSTracer* trc) = 0;
+
+ // Implement the 'count' method for counts returned by this CountType
+ // instance's 'newCount' method.
+ virtual MOZ_MUST_USE bool count(CountBase& count,
+ mozilla::MallocSizeOf mallocSizeOf,
+ const Node& node) = 0;
+
+ // Implement the 'report' method for counts returned by this CountType
+ // instance's 'newCount' method.
+ virtual MOZ_MUST_USE bool report(JSContext* cx, CountBase& count,
+ MutableHandleValue report) = 0;
+};
+
+using CountTypePtr = js::UniquePtr<CountType>;
+
+// An abstract base class for count tree nodes.
+class CountBase {
+ // In lieu of a vtable, each CountBase points to its type, which
+ // carries not only the implementations of the CountBase methods, but also
+ // additional parameters for the type's behavior, as specified in the
+ // breakdown argument passed to takeCensus.
+ CountType& type;
+
+ protected:
+ ~CountBase() = default;
+
+ public:
+ explicit CountBase(CountType& type)
+ : type(type), total_(0), smallestNodeIdCounted_(SIZE_MAX) {}
+
+ // Categorize and count |node| as appropriate for this count's type.
+ MOZ_MUST_USE bool count(mozilla::MallocSizeOf mallocSizeOf,
+ const Node& node) {
+ total_++;
+
+ auto id = node.identifier();
+ if (id < smallestNodeIdCounted_) {
+ smallestNodeIdCounted_ = id;
+ }
+
+#ifdef DEBUG
+ size_t oldTotal = total_;
+#endif
+
+ bool ret = type.count(*this, mallocSizeOf, node);
+
+ MOZ_ASSERT(total_ == oldTotal,
+ "CountType::count should not increment total_, CountBase::count "
+ "handles that");
+
+ return ret;
+ }
+
+ // Construct a JavaScript object reporting the counts recorded in this
+ // count, and store it in |report|. Return true on success, or false on
+ // failure.
+ MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
+ return type.report(cx, *this, report);
+ }
+
+ // Down-cast this CountBase to its true type, based on its 'type' member,
+ // and run its destructor.
+ void destruct() { return type.destructCount(*this); }
+
+ // Trace this count for garbage collection.
+ void trace(JSTracer* trc) { type.traceCount(*this, trc); }
+
+ size_t total_;
+
+ // The smallest JS::ubi::Node::identifier() passed to this instance's
+ // count() method. This provides a stable way to sort sets.
+ Node::Id smallestNodeIdCounted_;
+};
+
+using RootedCount = JS::Rooted<CountBasePtr>;
+
+// Common data for a census traversal, shared across all CountType nodes.
+struct Census {
+ JSContext* const cx;
+ // If the targetZones set is non-empty, then only consider nodes whose zone
+ // is an element of the set. If the targetZones set is empty, then nodes in
+ // all zones are considered.
+ JS::ZoneSet targetZones;
+
+ explicit Census(JSContext* cx) : cx(cx) {}
+};
+
+// A BreadthFirst handler type that conducts a census, using a CountBase to
+// categorize and count each node.
+class CensusHandler {
+ Census& census;
+ JS::Handle<CountBasePtr> rootCount;
+ mozilla::MallocSizeOf mallocSizeOf;
+
+ public:
+ CensusHandler(Census& census, JS::Handle<CountBasePtr> rootCount,
+ mozilla::MallocSizeOf mallocSizeOf)
+ : census(census), rootCount(rootCount), mallocSizeOf(mallocSizeOf) {}
+
+ MOZ_MUST_USE bool report(JSContext* cx, MutableHandleValue report) {
+ return rootCount->report(cx, report);
+ }
+
+ // This class needs to retain no per-node data.
+ class NodeData {};
+
+ MOZ_MUST_USE JS_PUBLIC_API bool operator()(
+ BreadthFirst<CensusHandler>& traversal, Node origin, const Edge& edge,
+ NodeData* referentData, bool first);
+};
+
+using CensusTraversal = BreadthFirst<CensusHandler>;
+
+// Examine the census options supplied by the API consumer, and (among other
+// things) use that to build a CountType tree.
+MOZ_MUST_USE JS_PUBLIC_API bool ParseCensusOptions(JSContext* cx,
+ Census& census,
+ HandleObject options,
+ CountTypePtr& outResult);
+
+// Parse the breakdown language (as described in
+// js/src/doc/Debugger/Debugger.Memory.md) into a CountTypePtr. A null pointer
+// is returned on error and is reported to the cx.
+JS_PUBLIC_API CountTypePtr ParseBreakdown(JSContext* cx,
+ HandleValue breakdownValue);
+
+} // namespace ubi
+} // namespace JS
+
+#endif // js_UbiNodeCensus_h
diff --git a/js/public/UbiNodeDominatorTree.h b/js/public/UbiNodeDominatorTree.h
new file mode 100644
index 0000000000..199b59a992
--- /dev/null
+++ b/js/public/UbiNodeDominatorTree.h
@@ -0,0 +1,691 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UbiNodeDominatorTree_h
+#define js_UbiNodeDominatorTree_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+
+#include <utility>
+
+#include "js/AllocPolicy.h"
+#include "js/UbiNode.h"
+#include "js/UbiNodePostOrder.h"
+#include "js/Utility.h"
+#include "js/Vector.h"
+
+namespace JS {
+namespace ubi {
+
+/**
+ * In a directed graph with a root node `R`, a node `A` is said to "dominate" a
+ * node `B` iff every path from `R` to `B` contains `A`. A node `A` is said to
+ * be the "immediate dominator" of a node `B` iff it dominates `B`, is not `B`
+ * itself, and does not dominate any other nodes which also dominate `B` in
+ * turn.
+ *
+ * If we take every node from a graph `G` and create a new graph `T` with edges
+ * to each node from its immediate dominator, then `T` is a tree (each node has
+ * only one immediate dominator, or none if it is the root). This tree is called
+ * a "dominator tree".
+ *
+ * This class represents a dominator tree constructed from a `JS::ubi::Node`
+ * heap graph. The domination relationship and dominator trees are useful tools
+ * for analyzing heap graphs because they tell you:
+ *
+ * - Exactly what could be reclaimed by the GC if some node `A` became
+ * unreachable: those nodes which are dominated by `A`,
+ *
+ * - The "retained size" of a node in the heap graph, in contrast to its
+ * "shallow size". The "shallow size" is the space taken by a node itself,
+ * not counting anything it references. The "retained size" of a node is its
+ * shallow size plus the size of all the things that would be collected if
+ * the original node wasn't (directly or indirectly) referencing them. In
+ * other words, the retained size is the shallow size of a node plus the
+ * shallow sizes of every other node it dominates. For example, the root
+ * node in a binary tree might have a small shallow size that does not take
+ * up much space itself, but it dominates the rest of the binary tree and
+ * its retained size is therefore significant (assuming no external
+ * references into the tree).
+ *
+ * The simple, engineered algorithm presented in "A Simple, Fast Dominance
+ * Algorithm" by Cooper el al[0] is used to find dominators and construct the
+ * dominator tree. This algorithm runs in O(n^2) time, but is faster in practice
+ * than alternative algorithms with better theoretical running times, such as
+ * Lengauer-Tarjan which runs in O(e * log(n)). The big caveat to that statement
+ * is that Cooper et al found it is faster in practice *on control flow graphs*
+ * and I'm not convinced that this property also holds on *heap* graphs. That
+ * said, the implementation of this algorithm is *much* simpler than
+ * Lengauer-Tarjan and has been found to be fast enough at least for the time
+ * being.
+ *
+ * [0]: http://www.cs.rice.edu/~keith/EMBED/dom.pdf
+ */
+class JS_PUBLIC_API DominatorTree {
+ private:
+ // Types.
+
+ using PredecessorSets = js::HashMap<Node, NodeSetPtr, js::DefaultHasher<Node>,
+ js::SystemAllocPolicy>;
+ using NodeToIndexMap = js::HashMap<Node, uint32_t, js::DefaultHasher<Node>,
+ js::SystemAllocPolicy>;
+ class DominatedSets;
+
+ public:
+ class DominatedSetRange;
+
+ /**
+ * A pointer to an immediately dominated node.
+ *
+ * Don't use this type directly; it is no safer than regular pointers. This
+ * is only for use indirectly with range-based for loops and
+ * `DominatedSetRange`.
+ *
+ * @see JS::ubi::DominatorTree::getDominatedSet
+ */
+ class DominatedNodePtr {
+ friend class DominatedSetRange;
+
+ const JS::ubi::Vector<Node>& postOrder;
+ const uint32_t* ptr;
+
+ DominatedNodePtr(const JS::ubi::Vector<Node>& postOrder,
+ const uint32_t* ptr)
+ : postOrder(postOrder), ptr(ptr) {}
+
+ public:
+ bool operator!=(const DominatedNodePtr& rhs) const {
+ return ptr != rhs.ptr;
+ }
+ void operator++() { ptr++; }
+ const Node& operator*() const { return postOrder[*ptr]; }
+ };
+
+ /**
+ * A range of immediately dominated `JS::ubi::Node`s for use with
+ * range-based for loops.
+ *
+ * @see JS::ubi::DominatorTree::getDominatedSet
+ */
+ class DominatedSetRange {
+ friend class DominatedSets;
+
+ const JS::ubi::Vector<Node>& postOrder;
+ const uint32_t* beginPtr;
+ const uint32_t* endPtr;
+
+ DominatedSetRange(JS::ubi::Vector<Node>& postOrder, const uint32_t* begin,
+ const uint32_t* end)
+ : postOrder(postOrder), beginPtr(begin), endPtr(end) {
+ MOZ_ASSERT(begin <= end);
+ }
+
+ public:
+ DominatedNodePtr begin() const {
+ MOZ_ASSERT(beginPtr <= endPtr);
+ return DominatedNodePtr(postOrder, beginPtr);
+ }
+
+ DominatedNodePtr end() const { return DominatedNodePtr(postOrder, endPtr); }
+
+ size_t length() const {
+ MOZ_ASSERT(beginPtr <= endPtr);
+ return endPtr - beginPtr;
+ }
+
+ /**
+ * Safely skip ahead `n` dominators in the range, in O(1) time.
+ *
+ * Example usage:
+ *
+ * mozilla::Maybe<DominatedSetRange> range =
+ * myDominatorTree.getDominatedSet(myNode);
+ * if (range.isNothing()) {
+ * // Handle unknown nodes however you see fit...
+ * return false;
+ * }
+ *
+ * // Don't care about the first ten, for whatever reason.
+ * range->skip(10);
+ * for (const JS::ubi::Node& dominatedNode : *range) {
+ * // ...
+ * }
+ */
+ void skip(size_t n) {
+ beginPtr += n;
+ if (beginPtr > endPtr) {
+ beginPtr = endPtr;
+ }
+ }
+ };
+
+ private:
+ /**
+ * The set of all dominated sets in a dominator tree.
+ *
+ * Internally stores the sets in a contiguous array, with a side table of
+ * indices into that contiguous array to denote the start index of each
+ * individual set.
+ */
+ class DominatedSets {
+ JS::ubi::Vector<uint32_t> dominated;
+ JS::ubi::Vector<uint32_t> indices;
+
+ DominatedSets(JS::ubi::Vector<uint32_t>&& dominated,
+ JS::ubi::Vector<uint32_t>&& indices)
+ : dominated(std::move(dominated)), indices(std::move(indices)) {}
+
+ public:
+ // DominatedSets is not copy-able.
+ DominatedSets(const DominatedSets& rhs) = delete;
+ DominatedSets& operator=(const DominatedSets& rhs) = delete;
+
+ // DominatedSets is move-able.
+ DominatedSets(DominatedSets&& rhs)
+ : dominated(std::move(rhs.dominated)), indices(std::move(rhs.indices)) {
+ MOZ_ASSERT(this != &rhs, "self-move not allowed");
+ }
+ DominatedSets& operator=(DominatedSets&& rhs) {
+ this->~DominatedSets();
+ new (this) DominatedSets(std::move(rhs));
+ return *this;
+ }
+
+ /**
+ * Create the DominatedSets given the mapping of a node index to its
+ * immediate dominator. Returns `Some` on success, `Nothing` on OOM
+ * failure.
+ */
+ static mozilla::Maybe<DominatedSets> Create(
+ const JS::ubi::Vector<uint32_t>& doms) {
+ auto length = doms.length();
+ MOZ_ASSERT(length < UINT32_MAX);
+
+ // Create a vector `dominated` holding a flattened set of buckets of
+ // immediately dominated children nodes, with a lookup table
+ // `indices` mapping from each node to the beginning of its bucket.
+ //
+ // This has three phases:
+ //
+ // 1. Iterate over the full set of nodes and count up the size of
+ // each bucket. These bucket sizes are temporarily stored in the
+ // `indices` vector.
+ //
+ // 2. Convert the `indices` vector to store the cumulative sum of
+ // the sizes of all buckets before each index, resulting in a
+ // mapping from node index to one past the end of that node's
+ // bucket.
+ //
+ // 3. Iterate over the full set of nodes again, filling in bucket
+ // entries from the end of the bucket's range to its
+ // beginning. This decrements each index as a bucket entry is
+ // filled in. After having filled in all of a bucket's entries,
+ // the index points to the start of the bucket.
+
+ JS::ubi::Vector<uint32_t> dominated;
+ JS::ubi::Vector<uint32_t> indices;
+ if (!dominated.growBy(length) || !indices.growBy(length)) {
+ return mozilla::Nothing();
+ }
+
+ // 1
+ memset(indices.begin(), 0, length * sizeof(uint32_t));
+ for (uint32_t i = 0; i < length; i++) {
+ indices[doms[i]]++;
+ }
+
+ // 2
+ uint32_t sumOfSizes = 0;
+ for (uint32_t i = 0; i < length; i++) {
+ sumOfSizes += indices[i];
+ MOZ_ASSERT(sumOfSizes <= length);
+ indices[i] = sumOfSizes;
+ }
+
+ // 3
+ for (uint32_t i = 0; i < length; i++) {
+ auto idxOfDom = doms[i];
+ indices[idxOfDom]--;
+ dominated[indices[idxOfDom]] = i;
+ }
+
+#ifdef DEBUG
+ // Assert that our buckets are non-overlapping and don't run off the
+ // end of the vector.
+ uint32_t lastIndex = 0;
+ for (uint32_t i = 0; i < length; i++) {
+ MOZ_ASSERT(indices[i] >= lastIndex);
+ MOZ_ASSERT(indices[i] < length);
+ lastIndex = indices[i];
+ }
+#endif
+
+ return mozilla::Some(
+ DominatedSets(std::move(dominated), std::move(indices)));
+ }
+
+ /**
+ * Get the set of nodes immediately dominated by the node at
+ * `postOrder[nodeIndex]`.
+ */
+ DominatedSetRange dominatedSet(JS::ubi::Vector<Node>& postOrder,
+ uint32_t nodeIndex) const {
+ MOZ_ASSERT(postOrder.length() == indices.length());
+ MOZ_ASSERT(nodeIndex < indices.length());
+ auto end = nodeIndex == indices.length() - 1
+ ? dominated.end()
+ : &dominated[indices[nodeIndex + 1]];
+ return DominatedSetRange(postOrder, &dominated[indices[nodeIndex]], end);
+ }
+ };
+
+ private:
+ // Data members.
+ JS::ubi::Vector<Node> postOrder;
+ NodeToIndexMap nodeToPostOrderIndex;
+ JS::ubi::Vector<uint32_t> doms;
+ DominatedSets dominatedSets;
+ mozilla::Maybe<JS::ubi::Vector<JS::ubi::Node::Size>> retainedSizes;
+
+ private:
+ // We use `UNDEFINED` as a sentinel value in the `doms` vector to signal
+ // that we haven't found any dominators for the node at the corresponding
+ // index in `postOrder` yet.
+ static const uint32_t UNDEFINED = UINT32_MAX;
+
+ DominatorTree(JS::ubi::Vector<Node>&& postOrder,
+ NodeToIndexMap&& nodeToPostOrderIndex,
+ JS::ubi::Vector<uint32_t>&& doms, DominatedSets&& dominatedSets)
+ : postOrder(std::move(postOrder)),
+ nodeToPostOrderIndex(std::move(nodeToPostOrderIndex)),
+ doms(std::move(doms)),
+ dominatedSets(std::move(dominatedSets)),
+ retainedSizes(mozilla::Nothing()) {}
+
+ static uint32_t intersect(JS::ubi::Vector<uint32_t>& doms, uint32_t finger1,
+ uint32_t finger2) {
+ while (finger1 != finger2) {
+ if (finger1 < finger2) {
+ finger1 = doms[finger1];
+ } else if (finger2 < finger1) {
+ finger2 = doms[finger2];
+ }
+ }
+ return finger1;
+ }
+
+ // Do the post order traversal of the heap graph and populate our
+ // predecessor sets.
+ static MOZ_MUST_USE bool doTraversal(JSContext* cx, AutoCheckCannotGC& noGC,
+ const Node& root,
+ JS::ubi::Vector<Node>& postOrder,
+ PredecessorSets& predecessorSets) {
+ uint32_t nodeCount = 0;
+ auto onNode = [&](const Node& node) {
+ nodeCount++;
+ if (MOZ_UNLIKELY(nodeCount == UINT32_MAX)) {
+ return false;
+ }
+ return postOrder.append(node);
+ };
+
+ auto onEdge = [&](const Node& origin, const Edge& edge) {
+ auto p = predecessorSets.lookupForAdd(edge.referent);
+ if (!p) {
+ mozilla::UniquePtr<NodeSet, DeletePolicy<NodeSet>> set(
+ js_new<NodeSet>());
+ if (!set || !predecessorSets.add(p, edge.referent, std::move(set))) {
+ return false;
+ }
+ }
+ MOZ_ASSERT(p && p->value());
+ return p->value()->put(origin);
+ };
+
+ PostOrder traversal(cx, noGC);
+ return traversal.addStart(root) && traversal.traverse(onNode, onEdge);
+ }
+
+ // Populates the given `map` with an entry for each node to its index in
+ // `postOrder`.
+ static MOZ_MUST_USE bool mapNodesToTheirIndices(
+ JS::ubi::Vector<Node>& postOrder, NodeToIndexMap& map) {
+ MOZ_ASSERT(map.empty());
+ MOZ_ASSERT(postOrder.length() < UINT32_MAX);
+ uint32_t length = postOrder.length();
+ if (!map.reserve(length)) {
+ return false;
+ }
+ for (uint32_t i = 0; i < length; i++) {
+ map.putNewInfallible(postOrder[i], i);
+ }
+ return true;
+ }
+
+ // Convert the Node -> NodeSet predecessorSets to a index -> Vector<index>
+ // form.
+ static MOZ_MUST_USE bool convertPredecessorSetsToVectors(
+ const Node& root, JS::ubi::Vector<Node>& postOrder,
+ PredecessorSets& predecessorSets, NodeToIndexMap& nodeToPostOrderIndex,
+ JS::ubi::Vector<JS::ubi::Vector<uint32_t>>& predecessorVectors) {
+ MOZ_ASSERT(postOrder.length() < UINT32_MAX);
+ uint32_t length = postOrder.length();
+
+ MOZ_ASSERT(predecessorVectors.length() == 0);
+ if (!predecessorVectors.growBy(length)) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < length - 1; i++) {
+ auto& node = postOrder[i];
+ MOZ_ASSERT(node != root,
+ "Only the last node should be root, since this was a post "
+ "order traversal.");
+
+ auto ptr = predecessorSets.lookup(node);
+ MOZ_ASSERT(ptr,
+ "Because this isn't the root, it had better have "
+ "predecessors, or else how "
+ "did we even find it.");
+
+ auto& predecessors = ptr->value();
+ if (!predecessorVectors[i].reserve(predecessors->count())) {
+ return false;
+ }
+ for (auto range = predecessors->all(); !range.empty(); range.popFront()) {
+ auto ptr = nodeToPostOrderIndex.lookup(range.front());
+ MOZ_ASSERT(ptr);
+ predecessorVectors[i].infallibleAppend(ptr->value());
+ }
+ }
+ predecessorSets.clearAndCompact();
+ return true;
+ }
+
+ // Initialize `doms` such that the immediate dominator of the `root` is the
+ // `root` itself and all others are `UNDEFINED`.
+ static MOZ_MUST_USE bool initializeDominators(JS::ubi::Vector<uint32_t>& doms,
+ uint32_t length) {
+ MOZ_ASSERT(doms.length() == 0);
+ if (!doms.growByUninitialized(length)) {
+ return false;
+ }
+ doms[length - 1] = length - 1;
+ for (uint32_t i = 0; i < length - 1; i++) {
+ doms[i] = UNDEFINED;
+ }
+ return true;
+ }
+
+ void assertSanity() const {
+ MOZ_ASSERT(postOrder.length() == doms.length());
+ MOZ_ASSERT(postOrder.length() == nodeToPostOrderIndex.count());
+ MOZ_ASSERT_IF(retainedSizes.isSome(),
+ postOrder.length() == retainedSizes->length());
+ }
+
+ MOZ_MUST_USE bool computeRetainedSizes(mozilla::MallocSizeOf mallocSizeOf) {
+ MOZ_ASSERT(retainedSizes.isNothing());
+ auto length = postOrder.length();
+
+ retainedSizes.emplace();
+ if (!retainedSizes->growBy(length)) {
+ retainedSizes = mozilla::Nothing();
+ return false;
+ }
+
+ // Iterate in forward order so that we know all of a node's children in
+ // the dominator tree have already had their retained size
+ // computed. Then we can simply say that the retained size of a node is
+ // its shallow size (JS::ubi::Node::size) plus the retained sizes of its
+ // immediate children in the tree.
+
+ for (uint32_t i = 0; i < length; i++) {
+ auto size = postOrder[i].size(mallocSizeOf);
+
+ for (const auto& dominated : dominatedSets.dominatedSet(postOrder, i)) {
+ // The root node dominates itself, but shouldn't contribute to
+ // its own retained size.
+ if (dominated == postOrder[length - 1]) {
+ MOZ_ASSERT(i == length - 1);
+ continue;
+ }
+
+ auto ptr = nodeToPostOrderIndex.lookup(dominated);
+ MOZ_ASSERT(ptr);
+ auto idxOfDominated = ptr->value();
+ MOZ_ASSERT(idxOfDominated < i);
+ size += retainedSizes.ref()[idxOfDominated];
+ }
+
+ retainedSizes.ref()[i] = size;
+ }
+
+ return true;
+ }
+
+ public:
+ // DominatorTree is not copy-able.
+ DominatorTree(const DominatorTree&) = delete;
+ DominatorTree& operator=(const DominatorTree&) = delete;
+
+ // DominatorTree is move-able.
+ DominatorTree(DominatorTree&& rhs)
+ : postOrder(std::move(rhs.postOrder)),
+ nodeToPostOrderIndex(std::move(rhs.nodeToPostOrderIndex)),
+ doms(std::move(rhs.doms)),
+ dominatedSets(std::move(rhs.dominatedSets)),
+ retainedSizes(std::move(rhs.retainedSizes)) {
+ MOZ_ASSERT(this != &rhs, "self-move is not allowed");
+ }
+ DominatorTree& operator=(DominatorTree&& rhs) {
+ this->~DominatorTree();
+ new (this) DominatorTree(std::move(rhs));
+ return *this;
+ }
+
+ /**
+ * Construct a `DominatorTree` of the heap graph visible from `root`. The
+ * `root` is also used as the root of the resulting dominator tree.
+ *
+ * The resulting `DominatorTree` instance must not outlive the
+ * `JS::ubi::Node` graph it was constructed from.
+ *
+ * - For `JS::ubi::Node` graphs backed by the live heap graph, this means
+ * that the `DominatorTree`'s lifetime _must_ be contained within the
+ * scope of the provided `AutoCheckCannotGC` reference because a GC will
+ * invalidate the nodes.
+ *
+ * - For `JS::ubi::Node` graphs backed by some other offline structure
+ * provided by the embedder, the resulting `DominatorTree`'s lifetime is
+ * bounded by that offline structure's lifetime.
+ *
+ * In practice, this means that within SpiderMonkey we must treat
+ * `DominatorTree` as if it were backed by the live heap graph and trust
+ * that embedders with knowledge of the graph's implementation will do the
+ * Right Thing.
+ *
+ * Returns `mozilla::Nothing()` on OOM failure. It is the caller's
+ * responsibility to handle and report the OOM.
+ */
+ static mozilla::Maybe<DominatorTree> Create(JSContext* cx,
+ AutoCheckCannotGC& noGC,
+ const Node& root) {
+ JS::ubi::Vector<Node> postOrder;
+ PredecessorSets predecessorSets;
+ if (!doTraversal(cx, noGC, root, postOrder, predecessorSets)) {
+ return mozilla::Nothing();
+ }
+
+ MOZ_ASSERT(postOrder.length() < UINT32_MAX);
+ uint32_t length = postOrder.length();
+ MOZ_ASSERT(postOrder[length - 1] == root);
+
+ // From here on out we wish to avoid hash table lookups, and we use
+ // indices into `postOrder` instead of actual nodes wherever
+ // possible. This greatly improves the performance of this
+ // implementation, but we have to pay a little bit of upfront cost to
+ // convert our data structures to play along first.
+
+ NodeToIndexMap nodeToPostOrderIndex(postOrder.length());
+ if (!mapNodesToTheirIndices(postOrder, nodeToPostOrderIndex)) {
+ return mozilla::Nothing();
+ }
+
+ JS::ubi::Vector<JS::ubi::Vector<uint32_t>> predecessorVectors;
+ if (!convertPredecessorSetsToVectors(root, postOrder, predecessorSets,
+ nodeToPostOrderIndex,
+ predecessorVectors))
+ return mozilla::Nothing();
+
+ JS::ubi::Vector<uint32_t> doms;
+ if (!initializeDominators(doms, length)) {
+ return mozilla::Nothing();
+ }
+
+ bool changed = true;
+ while (changed) {
+ changed = false;
+
+ // Iterate over the non-root nodes in reverse post order.
+ for (uint32_t indexPlusOne = length - 1; indexPlusOne > 0;
+ indexPlusOne--) {
+ MOZ_ASSERT(postOrder[indexPlusOne - 1] != root);
+
+ // Take the intersection of every predecessor's dominator set;
+ // that is the current best guess at the immediate dominator for
+ // this node.
+
+ uint32_t newIDomIdx = UNDEFINED;
+
+ auto& predecessors = predecessorVectors[indexPlusOne - 1];
+ auto range = predecessors.all();
+ for (; !range.empty(); range.popFront()) {
+ auto idx = range.front();
+ if (doms[idx] != UNDEFINED) {
+ newIDomIdx = idx;
+ break;
+ }
+ }
+
+ MOZ_ASSERT(newIDomIdx != UNDEFINED,
+ "Because the root is initialized to dominate itself and is "
+ "the first "
+ "node in every path, there must exist a predecessor to this "
+ "node that "
+ "also has a dominator.");
+
+ for (; !range.empty(); range.popFront()) {
+ auto idx = range.front();
+ if (doms[idx] != UNDEFINED) {
+ newIDomIdx = intersect(doms, newIDomIdx, idx);
+ }
+ }
+
+ // If the immediate dominator changed, we will have to do
+ // another pass of the outer while loop to continue the forward
+ // dataflow.
+ if (newIDomIdx != doms[indexPlusOne - 1]) {
+ doms[indexPlusOne - 1] = newIDomIdx;
+ changed = true;
+ }
+ }
+ }
+
+ auto maybeDominatedSets = DominatedSets::Create(doms);
+ if (maybeDominatedSets.isNothing()) {
+ return mozilla::Nothing();
+ }
+
+ return mozilla::Some(
+ DominatorTree(std::move(postOrder), std::move(nodeToPostOrderIndex),
+ std::move(doms), std::move(*maybeDominatedSets)));
+ }
+
+ /**
+ * Get the root node for this dominator tree.
+ */
+ const Node& root() const { return postOrder[postOrder.length() - 1]; }
+
+ /**
+ * Return the immediate dominator of the given `node`. If `node` was not
+ * reachable from the `root` that this dominator tree was constructed from,
+ * then return the null `JS::ubi::Node`.
+ */
+ Node getImmediateDominator(const Node& node) const {
+ assertSanity();
+ auto ptr = nodeToPostOrderIndex.lookup(node);
+ if (!ptr) {
+ return Node();
+ }
+
+ auto idx = ptr->value();
+ MOZ_ASSERT(idx < postOrder.length());
+ return postOrder[doms[idx]];
+ }
+
+ /**
+ * Get the set of nodes immediately dominated by the given `node`. If `node`
+ * is not a member of this dominator tree, return `Nothing`.
+ *
+ * Example usage:
+ *
+ * mozilla::Maybe<DominatedSetRange> range =
+ * myDominatorTree.getDominatedSet(myNode);
+ * if (range.isNothing()) {
+ * // Handle unknown node however you see fit...
+ * return false;
+ * }
+ *
+ * for (const JS::ubi::Node& dominatedNode : *range) {
+ * // Do something with each immediately dominated node...
+ * }
+ */
+ mozilla::Maybe<DominatedSetRange> getDominatedSet(const Node& node) {
+ assertSanity();
+ auto ptr = nodeToPostOrderIndex.lookup(node);
+ if (!ptr) {
+ return mozilla::Nothing();
+ }
+
+ auto idx = ptr->value();
+ MOZ_ASSERT(idx < postOrder.length());
+ return mozilla::Some(dominatedSets.dominatedSet(postOrder, idx));
+ }
+
+ /**
+ * Get the retained size of the given `node`. The size is placed in
+ * `outSize`, or 0 if `node` is not a member of the dominator tree. Returns
+ * false on OOM failure, leaving `outSize` unchanged.
+ */
+ MOZ_MUST_USE bool getRetainedSize(const Node& node,
+ mozilla::MallocSizeOf mallocSizeOf,
+ Node::Size& outSize) {
+ assertSanity();
+ auto ptr = nodeToPostOrderIndex.lookup(node);
+ if (!ptr) {
+ outSize = 0;
+ return true;
+ }
+
+ if (retainedSizes.isNothing() && !computeRetainedSizes(mallocSizeOf)) {
+ return false;
+ }
+
+ auto idx = ptr->value();
+ MOZ_ASSERT(idx < postOrder.length());
+ outSize = retainedSizes.ref()[idx];
+ return true;
+ }
+};
+
+} // namespace ubi
+} // namespace JS
+
+#endif // js_UbiNodeDominatorTree_h
diff --git a/js/public/UbiNodePostOrder.h b/js/public/UbiNodePostOrder.h
new file mode 100644
index 0000000000..ee385eaec4
--- /dev/null
+++ b/js/public/UbiNodePostOrder.h
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UbiNodePostOrder_h
+#define js_UbiNodePostOrder_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include <utility>
+
+#include "js/AllocPolicy.h"
+#include "js/UbiNode.h"
+#include "js/Utility.h"
+#include "js/Vector.h"
+
+namespace JS {
+namespace ubi {
+
+/**
+ * A post-order depth-first traversal of `ubi::Node` graphs.
+ *
+ * No GC may occur while an instance of `PostOrder` is live.
+ *
+ * The `NodeVisitor` type provided to `PostOrder::traverse` must have the
+ * following member:
+ *
+ * bool operator()(Node& node)
+ *
+ * The node visitor method. This method is called once for each `node`
+ * reachable from the start set in post-order.
+ *
+ * This visitor function should return true on success, or false if an error
+ * occurs. A false return value terminates the traversal immediately, and
+ * causes `PostOrder::traverse` to return false.
+ *
+ * The `EdgeVisitor` type provided to `PostOrder::traverse` must have the
+ * following member:
+ *
+ * bool operator()(Node& origin, Edge& edge)
+ *
+ * The edge visitor method. This method is called once for each outgoing
+ * `edge` from `origin` that is reachable from the start set.
+ *
+ * NB: UNLIKE NODES, THERE IS NO GUARANTEED ORDER IN WHICH EDGES AND THEIR
+ * ORIGINS ARE VISITED!
+ *
+ * This visitor function should return true on success, or false if an error
+ * occurs. A false return value terminates the traversal immediately, and
+ * causes `PostOrder::traverse` to return false.
+ */
+struct PostOrder {
+ private:
+ struct OriginAndEdges {
+ Node origin;
+ EdgeVector edges;
+
+ OriginAndEdges(const Node& node, EdgeVector&& edges)
+ : origin(node), edges(std::move(edges)) {}
+
+ OriginAndEdges(const OriginAndEdges& rhs) = delete;
+ OriginAndEdges& operator=(const OriginAndEdges& rhs) = delete;
+
+ OriginAndEdges(OriginAndEdges&& rhs)
+ : origin(rhs.origin), edges(std::move(rhs.edges)) {
+ MOZ_ASSERT(&rhs != this, "self-move disallowed");
+ }
+
+ OriginAndEdges& operator=(OriginAndEdges&& rhs) {
+ this->~OriginAndEdges();
+ new (this) OriginAndEdges(std::move(rhs));
+ return *this;
+ }
+ };
+
+ using Stack = js::Vector<OriginAndEdges, 256, js::SystemAllocPolicy>;
+ using Set = js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
+
+ JSContext* cx;
+ Set seen;
+ Stack stack;
+#ifdef DEBUG
+ bool traversed;
+#endif
+
+ private:
+ MOZ_MUST_USE bool fillEdgesFromRange(EdgeVector& edges,
+ js::UniquePtr<EdgeRange>& range) {
+ MOZ_ASSERT(range);
+ for (; !range->empty(); range->popFront()) {
+ if (!edges.append(std::move(range->front()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ MOZ_MUST_USE bool pushForTraversing(const Node& node) {
+ EdgeVector edges;
+ auto range = node.edges(cx, /* wantNames */ false);
+ return range && fillEdgesFromRange(edges, range) &&
+ stack.append(OriginAndEdges(node, std::move(edges)));
+ }
+
+ public:
+ // Construct a post-order traversal object.
+ //
+ // The traversal asserts that no GC happens in its runtime during its
+ // lifetime via the `AutoCheckCannotGC&` parameter. We do nothing with it,
+ // other than require it to exist with a lifetime that encloses our own.
+ PostOrder(JSContext* cx, AutoCheckCannotGC&)
+ : cx(cx),
+ seen(),
+ stack()
+#ifdef DEBUG
+ ,
+ traversed(false)
+#endif
+ {
+ }
+
+ // Add `node` as a starting point for the traversal. You may add
+ // as many starting points as you like. Returns false on OOM.
+ MOZ_MUST_USE bool addStart(const Node& node) {
+ if (!seen.put(node)) {
+ return false;
+ }
+ return pushForTraversing(node);
+ }
+
+ // Traverse the graph in post-order, starting with the set of nodes passed
+ // to `addStart` and applying `onNode::operator()` for each node in the
+ // graph and `onEdge::operator()` for each edge in the graph, as described
+ // above.
+ //
+ // This should be called only once per instance of this class.
+ //
+ // Return false on OOM or error return from `onNode::operator()` or
+ // `onEdge::operator()`.
+ template <typename NodeVisitor, typename EdgeVisitor>
+ MOZ_MUST_USE bool traverse(NodeVisitor onNode, EdgeVisitor onEdge) {
+#ifdef DEBUG
+ MOZ_ASSERT(!traversed, "Can only traverse() once!");
+ traversed = true;
+#endif
+
+ while (!stack.empty()) {
+ auto& origin = stack.back().origin;
+ auto& edges = stack.back().edges;
+
+ if (edges.empty()) {
+ if (!onNode(origin)) {
+ return false;
+ }
+ stack.popBack();
+ continue;
+ }
+
+ Edge edge = std::move(edges.back());
+ edges.popBack();
+
+ if (!onEdge(origin, edge)) {
+ return false;
+ }
+
+ auto ptr = seen.lookupForAdd(edge.referent);
+ // We've already seen this node, don't follow its edges.
+ if (ptr) {
+ continue;
+ }
+
+ // Mark the referent as seen and follow its edges.
+ if (!seen.add(ptr, edge.referent) || !pushForTraversing(edge.referent)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+} // namespace ubi
+} // namespace JS
+
+#endif // js_UbiNodePostOrder_h
diff --git a/js/public/UbiNodeShortestPaths.h b/js/public/UbiNodeShortestPaths.h
new file mode 100644
index 0000000000..964dd87b0c
--- /dev/null
+++ b/js/public/UbiNodeShortestPaths.h
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UbiNodeShortestPaths_h
+#define js_UbiNodeShortestPaths_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include <utility>
+
+#include "js/AllocPolicy.h"
+#include "js/UbiNodeBreadthFirst.h"
+#include "js/UniquePtr.h"
+#include "js/Vector.h"
+
+namespace JS {
+namespace ubi {
+
+/**
+ * A back edge along a path in the heap graph.
+ */
+struct JS_PUBLIC_API BackEdge {
+ private:
+ Node predecessor_;
+ EdgeName name_;
+
+ public:
+ using Ptr = js::UniquePtr<BackEdge>;
+
+ BackEdge() : predecessor_(), name_(nullptr) {}
+
+ MOZ_MUST_USE bool init(const Node& predecessor, Edge& edge) {
+ MOZ_ASSERT(!predecessor_);
+ MOZ_ASSERT(!name_);
+
+ predecessor_ = predecessor;
+ name_ = std::move(edge.name);
+ return true;
+ }
+
+ BackEdge(const BackEdge&) = delete;
+ BackEdge& operator=(const BackEdge&) = delete;
+
+ BackEdge(BackEdge&& rhs)
+ : predecessor_(rhs.predecessor_), name_(std::move(rhs.name_)) {
+ MOZ_ASSERT(&rhs != this);
+ }
+
+ BackEdge& operator=(BackEdge&& rhs) {
+ this->~BackEdge();
+ new (this) BackEdge(std::move(rhs));
+ return *this;
+ }
+
+ Ptr clone() const;
+
+ const EdgeName& name() const { return name_; }
+ EdgeName& name() { return name_; }
+
+ const JS::ubi::Node& predecessor() const { return predecessor_; }
+};
+
+/**
+ * A path is a series of back edges from which we discovered a target node.
+ */
+using Path = JS::ubi::Vector<BackEdge*>;
+
+/**
+ * The `JS::ubi::ShortestPaths` type represents a collection of up to N shortest
+ * retaining paths for each of a target set of nodes, starting from the same
+ * root node.
+ */
+struct JS_PUBLIC_API ShortestPaths {
+ private:
+ // Types, type aliases, and data members.
+
+ using BackEdgeVector = JS::ubi::Vector<BackEdge::Ptr>;
+ using NodeToBackEdgeVectorMap =
+ js::HashMap<Node, BackEdgeVector, js::DefaultHasher<Node>,
+ js::SystemAllocPolicy>;
+
+ struct Handler;
+ using Traversal = BreadthFirst<Handler>;
+
+ /**
+ * A `JS::ubi::BreadthFirst` traversal handler that records back edges for
+ * how we reached each node, allowing us to reconstruct the shortest
+ * retaining paths after the traversal.
+ */
+ struct Handler {
+ using NodeData = BackEdge;
+
+ ShortestPaths& shortestPaths;
+ size_t totalMaxPathsToRecord;
+ size_t totalPathsRecorded;
+
+ explicit Handler(ShortestPaths& shortestPaths)
+ : shortestPaths(shortestPaths),
+ totalMaxPathsToRecord(shortestPaths.targets_.count() *
+ shortestPaths.maxNumPaths_),
+ totalPathsRecorded(0) {}
+
+ bool operator()(Traversal& traversal, const JS::ubi::Node& origin,
+ JS::ubi::Edge& edge, BackEdge* back, bool first) {
+ MOZ_ASSERT(back);
+ MOZ_ASSERT(origin == shortestPaths.root_ ||
+ traversal.visited.has(origin));
+ MOZ_ASSERT(totalPathsRecorded < totalMaxPathsToRecord);
+
+ if (first && !back->init(origin, edge)) {
+ return false;
+ }
+
+ if (!shortestPaths.targets_.has(edge.referent)) {
+ return true;
+ }
+
+ // If `first` is true, then we moved the edge's name into `back` in
+ // the above call to `init`. So clone that back edge to get the
+ // correct edge name. If `first` is not true, then our edge name is
+ // still in `edge`. This accounts for the asymmetry between
+ // `back->clone()` in the first branch, and the `init` call in the
+ // second branch.
+
+ if (first) {
+ BackEdgeVector paths;
+ if (!paths.reserve(shortestPaths.maxNumPaths_)) {
+ return false;
+ }
+ auto cloned = back->clone();
+ if (!cloned) {
+ return false;
+ }
+ paths.infallibleAppend(std::move(cloned));
+ if (!shortestPaths.paths_.putNew(edge.referent, std::move(paths))) {
+ return false;
+ }
+ totalPathsRecorded++;
+ } else {
+ auto ptr = shortestPaths.paths_.lookup(edge.referent);
+ MOZ_ASSERT(ptr,
+ "This isn't the first time we have seen the target node "
+ "`edge.referent`. "
+ "We should have inserted it into shortestPaths.paths_ the "
+ "first time we "
+ "saw it.");
+
+ if (ptr->value().length() < shortestPaths.maxNumPaths_) {
+ auto thisBackEdge = js::MakeUnique<BackEdge>();
+ if (!thisBackEdge || !thisBackEdge->init(origin, edge)) {
+ return false;
+ }
+ ptr->value().infallibleAppend(std::move(thisBackEdge));
+ totalPathsRecorded++;
+ }
+ }
+
+ MOZ_ASSERT(totalPathsRecorded <= totalMaxPathsToRecord);
+ if (totalPathsRecorded == totalMaxPathsToRecord) {
+ traversal.stop();
+ }
+
+ return true;
+ }
+ };
+
+ // The maximum number of paths to record for each node.
+ uint32_t maxNumPaths_;
+
+ // The root node we are starting the search from.
+ Node root_;
+
+ // The set of nodes we are searching for paths to.
+ NodeSet targets_;
+
+ // The resulting paths.
+ NodeToBackEdgeVectorMap paths_;
+
+ // Need to keep alive the traversal's back edges so we can walk them later
+ // when the traversal is over when recreating the shortest paths.
+ Traversal::NodeMap backEdges_;
+
+ private:
+ // Private methods.
+
+ ShortestPaths(uint32_t maxNumPaths, const Node& root, NodeSet&& targets)
+ : maxNumPaths_(maxNumPaths),
+ root_(root),
+ targets_(std::move(targets)),
+ paths_(targets_.count()),
+ backEdges_() {
+ MOZ_ASSERT(maxNumPaths_ > 0);
+ MOZ_ASSERT(root_);
+ }
+
+ public:
+ // Public methods.
+
+ ShortestPaths(ShortestPaths&& rhs)
+ : maxNumPaths_(rhs.maxNumPaths_),
+ root_(rhs.root_),
+ targets_(std::move(rhs.targets_)),
+ paths_(std::move(rhs.paths_)),
+ backEdges_(std::move(rhs.backEdges_)) {
+ MOZ_ASSERT(this != &rhs, "self-move is not allowed");
+ }
+
+ ShortestPaths& operator=(ShortestPaths&& rhs) {
+ this->~ShortestPaths();
+ new (this) ShortestPaths(std::move(rhs));
+ return *this;
+ }
+
+ ShortestPaths(const ShortestPaths&) = delete;
+ ShortestPaths& operator=(const ShortestPaths&) = delete;
+
+ /**
+ * Construct a new `JS::ubi::ShortestPaths`, finding up to `maxNumPaths`
+ * shortest retaining paths for each target node in `targets` starting from
+ * `root`.
+ *
+ * The resulting `ShortestPaths` instance must not outlive the
+ * `JS::ubi::Node` graph it was constructed from.
+ *
+ * - For `JS::ubi::Node` graphs backed by the live heap graph, this means
+ * that the `ShortestPaths`'s lifetime _must_ be contained within the
+ * scope of the provided `AutoCheckCannotGC` reference because a GC will
+ * invalidate the nodes.
+ *
+ * - For `JS::ubi::Node` graphs backed by some other offline structure
+ * provided by the embedder, the resulting `ShortestPaths`'s lifetime is
+ * bounded by that offline structure's lifetime.
+ *
+ * Returns `mozilla::Nothing()` on OOM failure. It is the caller's
+ * responsibility to handle and report the OOM.
+ */
+ static mozilla::Maybe<ShortestPaths> Create(JSContext* cx,
+ AutoCheckCannotGC& noGC,
+ uint32_t maxNumPaths,
+ const Node& root,
+ NodeSet&& targets) {
+ MOZ_ASSERT(targets.count() > 0);
+ MOZ_ASSERT(maxNumPaths > 0);
+
+ ShortestPaths paths(maxNumPaths, root, std::move(targets));
+
+ Handler handler(paths);
+ Traversal traversal(cx, handler, noGC);
+ traversal.wantNames = true;
+ if (!traversal.addStart(root) || !traversal.traverse()) {
+ return mozilla::Nothing();
+ }
+
+ // Take ownership of the back edges we created while traversing the
+ // graph so that we can follow them from `paths_` and don't
+ // use-after-free.
+ paths.backEdges_ = std::move(traversal.visited);
+
+ return mozilla::Some(std::move(paths));
+ }
+
+ /**
+ * Get an iterator over each target node we searched for retaining paths
+ * for. The returned iterator must not outlive the `ShortestPaths`
+ * instance.
+ */
+ NodeSet::Iterator targetIter() const { return targets_.iter(); }
+
+ /**
+ * Invoke the provided functor/lambda/callable once for each retaining path
+ * discovered for `target`. The `func` is passed a single `JS::ubi::Path&`
+ * argument, which contains each edge along the path ordered starting from
+ * the root and ending at the target, and must not outlive the scope of the
+ * call.
+ *
+ * Note that it is possible that we did not find any paths from the root to
+ * the given target, in which case `func` will not be invoked.
+ */
+ template <class Func>
+ MOZ_MUST_USE bool forEachPath(const Node& target, Func func) {
+ MOZ_ASSERT(targets_.has(target));
+
+ auto ptr = paths_.lookup(target);
+
+ // We didn't find any paths to this target, so nothing to do here.
+ if (!ptr) {
+ return true;
+ }
+
+ MOZ_ASSERT(ptr->value().length() <= maxNumPaths_);
+
+ Path path;
+ for (const auto& backEdge : ptr->value()) {
+ path.clear();
+
+ if (!path.append(backEdge.get())) {
+ return false;
+ }
+
+ Node here = backEdge->predecessor();
+ MOZ_ASSERT(here);
+
+ while (here != root_) {
+ auto p = backEdges_.lookup(here);
+ MOZ_ASSERT(p);
+ if (!path.append(&p->value())) {
+ return false;
+ }
+ here = p->value().predecessor();
+ MOZ_ASSERT(here);
+ }
+
+ path.reverse();
+
+ if (!func(path)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+#ifdef DEBUG
+// A helper function to dump the first `maxNumPaths` shortest retaining paths to
+// `node` from the GC roots. Useful when GC things you expect to have been
+// reclaimed by the collector haven't been!
+//
+// Usage:
+//
+// JSObject* foo = ...;
+// JS::ubi::dumpPaths(rt, JS::ubi::Node(foo));
+JS_PUBLIC_API void dumpPaths(JSRuntime* rt, Node node,
+ uint32_t maxNumPaths = 10);
+#endif
+
+} // namespace ubi
+} // namespace JS
+
+#endif // js_UbiNodeShortestPaths_h
diff --git a/js/public/UbiNodeUtils.h b/js/public/UbiNodeUtils.h
new file mode 100644
index 0000000000..bd48086af8
--- /dev/null
+++ b/js/public/UbiNodeUtils.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UbiNodeUtils_h
+#define js_UbiNodeUtils_h
+
+#include "jspubtd.h"
+
+#include "js/UbiNode.h"
+#include "js/UniquePtr.h"
+
+using JS::ubi::Edge;
+using JS::ubi::EdgeRange;
+using JS::ubi::EdgeVector;
+
+namespace JS {
+namespace ubi {
+// An EdgeRange concrete class that simply holds a vector of Edges,
+// populated by the addTracerEdges method.
+class SimpleEdgeRange : public EdgeRange {
+ EdgeVector edges;
+ size_t i;
+
+ protected:
+ void settle() { front_ = i < edges.length() ? &edges[i] : nullptr; }
+
+ public:
+ explicit SimpleEdgeRange() : edges(), i(0) {}
+
+ bool addTracerEdges(JSRuntime* rt, void* thing, JS::TraceKind kind,
+ bool wantNames);
+
+ bool addEdge(Edge edge) {
+ if (!edges.append(std::move(edge))) return false;
+ settle();
+ return true;
+ }
+
+ void popFront() override {
+ i++;
+ settle();
+ }
+};
+
+} // namespace ubi
+} // namespace JS
+
+#endif // js_UbiNodeUtils_h
diff --git a/js/public/UniquePtr.h b/js/public/UniquePtr.h
new file mode 100644
index 0000000000..d6bbb9ae61
--- /dev/null
+++ b/js/public/UniquePtr.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_UniquePtr_h
+#define js_UniquePtr_h
+
+#include "mozilla/UniquePtr.h"
+
+#include "js/Utility.h"
+
+namespace js {
+
+// Replacement for mozilla::UniquePtr that defaults to JS::DeletePolicy.
+template <typename T, typename D = JS::DeletePolicy<T>>
+using UniquePtr = mozilla::UniquePtr<T, D>;
+
+namespace detail {
+
+template <typename T>
+struct UniqueSelector {
+ typedef UniquePtr<T> SingleObject;
+};
+
+template <typename T>
+struct UniqueSelector<T[]> {
+ typedef UniquePtr<T[]> UnknownBound;
+};
+
+template <typename T, decltype(sizeof(int)) N>
+struct UniqueSelector<T[N]> {
+ typedef UniquePtr<T[N]> KnownBound;
+};
+
+} // namespace detail
+
+// Replacement for mozilla::MakeUnique that correctly calls js_new and produces
+// a js::UniquePtr.
+template <typename T, typename... Args>
+typename detail::UniqueSelector<T>::SingleObject MakeUnique(Args&&... aArgs) {
+ return UniquePtr<T>(js_new<T>(std::forward<Args>(aArgs)...));
+}
+
+template <typename T>
+typename detail::UniqueSelector<T>::UnknownBound MakeUnique(
+ decltype(sizeof(int)) aN) = delete;
+
+template <typename T, typename... Args>
+typename detail::UniqueSelector<T>::KnownBound MakeUnique(Args&&... aArgs) =
+ delete;
+
+} // namespace js
+
+#endif /* js_UniquePtr_h */
diff --git a/js/public/Utility.h b/js/public/Utility.h
new file mode 100644
index 0000000000..3e366a159c
--- /dev/null
+++ b/js/public/Utility.h
@@ -0,0 +1,690 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Utility_h
+#define js_Utility_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Compiler.h"
+#include "mozilla/TemplateLib.h"
+#include "mozilla/UniquePtr.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <type_traits>
+#include <utility>
+
+#include "jstypes.h"
+#include "mozmemory.h"
+#include "js/TypeDecls.h"
+
+/* The public JS engine namespace. */
+namespace JS {}
+
+/* The mozilla-shared reusable template/utility namespace. */
+namespace mozilla {}
+
+/* The private JS engine namespace. */
+namespace js {}
+
+extern MOZ_NORETURN MOZ_COLD JS_PUBLIC_API void JS_Assert(const char* s,
+ const char* file,
+ int ln);
+
+/*
+ * Custom allocator support for SpiderMonkey
+ */
+#if defined JS_USE_CUSTOM_ALLOCATOR
+# include "jscustomallocator.h"
+#else
+
+namespace js {
+
+/*
+ * Thread types are used to tag threads for certain kinds of testing (see
+ * below), and also used to characterize threads in the thread scheduler (see
+ * js/src/vm/HelperThreads.cpp).
+ *
+ * Please update oom::FirstThreadTypeToTest and oom::LastThreadTypeToTest when
+ * adding new thread types.
+ */
+enum ThreadType {
+ THREAD_TYPE_NONE = 0, // 0
+ THREAD_TYPE_MAIN, // 1
+ THREAD_TYPE_WASM, // 2
+ THREAD_TYPE_ION, // 3
+ THREAD_TYPE_PARSE, // 4
+ THREAD_TYPE_COMPRESS, // 5
+ THREAD_TYPE_GCPARALLEL, // 6
+ THREAD_TYPE_PROMISE_TASK, // 7
+ THREAD_TYPE_ION_FREE, // 8
+ THREAD_TYPE_WASM_TIER2, // 9
+ THREAD_TYPE_WORKER, // 10
+ THREAD_TYPE_MAX // Used to check shell function arguments
+};
+
+/*
+ * Threads need a universal way to dispatch from xpcom thread pools,
+ * so having objects inherit from this struct enables
+ * mozilla::HelperThreadPool's runnable handler to call runTask() on each type.
+ */
+struct RunnableTask {
+ virtual ThreadType threadType() = 0;
+ virtual void runTask() = 0;
+ virtual ~RunnableTask() = default;
+};
+
+namespace oom {
+
+/*
+ * Theads are tagged only in certain debug contexts. Notably, to make testing
+ * OOM in certain helper threads more effective, we allow restricting the OOM
+ * testing to a certain helper thread type. This allows us to fail e.g. in
+ * off-thread script parsing without causing an OOM in the active thread first.
+ *
+ * Getter/Setter functions to encapsulate mozilla::ThreadLocal, implementation
+ * is in util/Utility.cpp.
+ */
+# if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+
+// Define the range of threads tested by simulated OOM testing and the
+// like. Testing worker threads is not supported.
+const ThreadType FirstThreadTypeToTest = THREAD_TYPE_MAIN;
+const ThreadType LastThreadTypeToTest = THREAD_TYPE_WASM_TIER2;
+
+extern bool InitThreadType(void);
+extern void SetThreadType(ThreadType);
+extern JS_FRIEND_API uint32_t GetThreadType(void);
+
+# else
+
+inline bool InitThreadType(void) { return true; }
+inline void SetThreadType(ThreadType t){};
+inline uint32_t GetThreadType(void) { return 0; }
+inline uint32_t GetAllocationThreadType(void) { return 0; }
+inline uint32_t GetStackCheckThreadType(void) { return 0; }
+inline uint32_t GetInterruptCheckThreadType(void) { return 0; }
+
+# endif
+
+} /* namespace oom */
+} /* namespace js */
+
+# if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+
+# ifdef JS_OOM_BREAKPOINT
+# if defined(_MSC_VER)
+static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() {
+ __asm {}
+ ;
+}
+# else
+static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
+# endif
+# define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
+# else
+# define JS_OOM_CALL_BP_FUNC() \
+ do { \
+ } while (0)
+# endif
+
+namespace js {
+namespace oom {
+
+/*
+ * Out of memory testing support. We provide various testing functions to
+ * simulate OOM conditions and so we can test that they are handled correctly.
+ */
+class FailureSimulator {
+ public:
+ enum class Kind : uint8_t { Nothing, OOM, StackOOM, Interrupt };
+
+ private:
+ Kind kind_ = Kind::Nothing;
+ uint32_t targetThread_ = 0;
+ uint64_t maxChecks_ = UINT64_MAX;
+ uint64_t counter_ = 0;
+ bool failAlways_ = true;
+ bool inUnsafeRegion_ = false;
+
+ public:
+ uint64_t maxChecks() const { return maxChecks_; }
+ uint64_t counter() const { return counter_; }
+ void setInUnsafeRegion(bool b) {
+ MOZ_ASSERT(inUnsafeRegion_ != b);
+ inUnsafeRegion_ = b;
+ }
+ uint32_t targetThread() const { return targetThread_; }
+ bool isThreadSimulatingAny() const {
+ return targetThread_ && targetThread_ == js::oom::GetThreadType() &&
+ !inUnsafeRegion_;
+ }
+ bool isThreadSimulating(Kind kind) const {
+ return kind_ == kind && isThreadSimulatingAny();
+ }
+ bool isSimulatedFailure(Kind kind) const {
+ if (!isThreadSimulating(kind)) {
+ return false;
+ }
+ return counter_ == maxChecks_ || (counter_ > maxChecks_ && failAlways_);
+ }
+ bool hadFailure(Kind kind) const {
+ return kind_ == kind && counter_ >= maxChecks_;
+ }
+ bool shouldFail(Kind kind) {
+ if (!isThreadSimulating(kind)) {
+ return false;
+ }
+ counter_++;
+ if (isSimulatedFailure(kind)) {
+ JS_OOM_CALL_BP_FUNC();
+ return true;
+ }
+ return false;
+ }
+
+ void simulateFailureAfter(Kind kind, uint64_t checks, uint32_t thread,
+ bool always);
+ void reset();
+};
+extern JS_PUBLIC_DATA FailureSimulator simulator;
+
+inline bool IsSimulatedOOMAllocation() {
+ return simulator.isSimulatedFailure(FailureSimulator::Kind::OOM);
+}
+
+inline bool ShouldFailWithOOM() {
+ return simulator.shouldFail(FailureSimulator::Kind::OOM);
+}
+
+inline bool HadSimulatedOOM() {
+ return simulator.hadFailure(FailureSimulator::Kind::OOM);
+}
+
+/*
+ * Out of stack space testing support, similar to OOM testing functions.
+ */
+
+inline bool IsSimulatedStackOOMCheck() {
+ return simulator.isSimulatedFailure(FailureSimulator::Kind::StackOOM);
+}
+
+inline bool ShouldFailWithStackOOM() {
+ return simulator.shouldFail(FailureSimulator::Kind::StackOOM);
+}
+
+inline bool HadSimulatedStackOOM() {
+ return simulator.hadFailure(FailureSimulator::Kind::StackOOM);
+}
+
+/*
+ * Interrupt testing support, similar to OOM testing functions.
+ */
+
+inline bool IsSimulatedInterruptCheck() {
+ return simulator.isSimulatedFailure(FailureSimulator::Kind::Interrupt);
+}
+
+inline bool ShouldFailWithInterrupt() {
+ return simulator.shouldFail(FailureSimulator::Kind::Interrupt);
+}
+
+inline bool HadSimulatedInterrupt() {
+ return simulator.hadFailure(FailureSimulator::Kind::Interrupt);
+}
+
+} /* namespace oom */
+} /* namespace js */
+
+# define JS_OOM_POSSIBLY_FAIL() \
+ do { \
+ if (js::oom::ShouldFailWithOOM()) return nullptr; \
+ } while (0)
+
+# define JS_OOM_POSSIBLY_FAIL_BOOL() \
+ do { \
+ if (js::oom::ShouldFailWithOOM()) return false; \
+ } while (0)
+
+# define JS_STACK_OOM_POSSIBLY_FAIL() \
+ do { \
+ if (js::oom::ShouldFailWithStackOOM()) return false; \
+ } while (0)
+
+# define JS_STACK_OOM_POSSIBLY_FAIL_REPORT() \
+ do { \
+ if (js::oom::ShouldFailWithStackOOM()) { \
+ ReportOverRecursed(cx); \
+ return false; \
+ } \
+ } while (0)
+
+# define JS_INTERRUPT_POSSIBLY_FAIL() \
+ do { \
+ if (MOZ_UNLIKELY(js::oom::ShouldFailWithInterrupt())) { \
+ cx->requestInterrupt(js::InterruptReason::CallbackUrgent); \
+ return cx->handleInterrupt(); \
+ } \
+ } while (0)
+
+# else
+
+# define JS_OOM_POSSIBLY_FAIL() \
+ do { \
+ } while (0)
+# define JS_OOM_POSSIBLY_FAIL_BOOL() \
+ do { \
+ } while (0)
+# define JS_STACK_OOM_POSSIBLY_FAIL() \
+ do { \
+ } while (0)
+# define JS_STACK_OOM_POSSIBLY_FAIL_REPORT() \
+ do { \
+ } while (0)
+# define JS_INTERRUPT_POSSIBLY_FAIL() \
+ do { \
+ } while (0)
+namespace js {
+namespace oom {
+static inline bool IsSimulatedOOMAllocation() { return false; }
+static inline bool ShouldFailWithOOM() { return false; }
+} /* namespace oom */
+} /* namespace js */
+
+# endif /* DEBUG || JS_OOM_BREAKPOINT */
+
+# ifdef FUZZING
+namespace js {
+namespace oom {
+extern JS_PUBLIC_DATA size_t largeAllocLimit;
+extern void InitLargeAllocLimit();
+} /* namespace oom */
+} /* namespace js */
+
+# define JS_CHECK_LARGE_ALLOC(x) \
+ do { \
+ if (js::oom::largeAllocLimit && x > js::oom::largeAllocLimit) { \
+ if (getenv("MOZ_FUZZ_CRASH_ON_LARGE_ALLOC")) { \
+ MOZ_CRASH("Large allocation"); \
+ } else { \
+ return nullptr; \
+ } \
+ } \
+ } while (0)
+# else
+# define JS_CHECK_LARGE_ALLOC(x) \
+ do { \
+ } while (0)
+# endif
+
+namespace js {
+
+/* Disable OOM testing in sections which are not OOM safe. */
+struct MOZ_RAII JS_PUBLIC_DATA AutoEnterOOMUnsafeRegion {
+ MOZ_NORETURN MOZ_COLD void crash(const char* reason);
+ MOZ_NORETURN MOZ_COLD void crash(size_t size, const char* reason);
+
+ using AnnotateOOMAllocationSizeCallback = void (*)(size_t);
+ static mozilla::Atomic<AnnotateOOMAllocationSizeCallback, mozilla::Relaxed>
+ annotateOOMSizeCallback;
+ static void setAnnotateOOMAllocationSizeCallback(
+ AnnotateOOMAllocationSizeCallback callback) {
+ annotateOOMSizeCallback = callback;
+ }
+
+# if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+ AutoEnterOOMUnsafeRegion()
+ : oomEnabled_(oom::simulator.isThreadSimulatingAny()) {
+ if (oomEnabled_) {
+ MOZ_ALWAYS_TRUE(owner_.compareExchange(nullptr, this));
+ oom::simulator.setInUnsafeRegion(true);
+ }
+ }
+
+ ~AutoEnterOOMUnsafeRegion() {
+ if (oomEnabled_) {
+ oom::simulator.setInUnsafeRegion(false);
+ MOZ_ALWAYS_TRUE(owner_.compareExchange(this, nullptr));
+ }
+ }
+
+ private:
+ // Used to catch concurrent use from other threads.
+ static mozilla::Atomic<AutoEnterOOMUnsafeRegion*> owner_;
+
+ bool oomEnabled_;
+# endif
+};
+
+} /* namespace js */
+
+// Malloc allocation.
+
+namespace js {
+
+extern JS_PUBLIC_DATA arena_id_t MallocArena;
+extern JS_PUBLIC_DATA arena_id_t ArrayBufferContentsArena;
+extern JS_PUBLIC_DATA arena_id_t StringBufferArena;
+
+extern void InitMallocAllocator();
+extern void ShutDownMallocAllocator();
+
+// This is a no-op if built without MOZ_MEMORY and MOZ_DEBUG.
+extern void AssertJSStringBufferInCorrectArena(const void* ptr);
+
+} /* namespace js */
+
+static inline void* js_arena_malloc(arena_id_t arena, size_t bytes) {
+ JS_OOM_POSSIBLY_FAIL();
+ JS_CHECK_LARGE_ALLOC(bytes);
+ return moz_arena_malloc(arena, bytes);
+}
+
+static inline void* js_malloc(size_t bytes) {
+ return js_arena_malloc(js::MallocArena, bytes);
+}
+
+static inline void* js_arena_calloc(arena_id_t arena, size_t bytes) {
+ JS_OOM_POSSIBLY_FAIL();
+ JS_CHECK_LARGE_ALLOC(bytes);
+ return moz_arena_calloc(arena, bytes, 1);
+}
+
+static inline void* js_arena_calloc(arena_id_t arena, size_t nmemb,
+ size_t size) {
+ JS_OOM_POSSIBLY_FAIL();
+ JS_CHECK_LARGE_ALLOC(nmemb * size);
+ return moz_arena_calloc(arena, nmemb, size);
+}
+
+static inline void* js_calloc(size_t bytes) {
+ return js_arena_calloc(js::MallocArena, bytes);
+}
+
+static inline void* js_calloc(size_t nmemb, size_t size) {
+ return js_arena_calloc(js::MallocArena, nmemb, size);
+}
+
+static inline void* js_arena_realloc(arena_id_t arena, void* p, size_t bytes) {
+ // realloc() with zero size is not portable, as some implementations may
+ // return nullptr on success and free |p| for this. We assume nullptr
+ // indicates failure and that |p| is still valid.
+ MOZ_ASSERT(bytes != 0);
+
+ JS_OOM_POSSIBLY_FAIL();
+ JS_CHECK_LARGE_ALLOC(bytes);
+ return moz_arena_realloc(arena, p, bytes);
+}
+
+static inline void* js_realloc(void* p, size_t bytes) {
+ return js_arena_realloc(js::MallocArena, p, bytes);
+}
+
+static inline void js_free(void* p) {
+ // TODO: This should call |moz_arena_free(js::MallocArena, p)| but we
+ // currently can't enforce that all memory freed here was allocated by
+ // js_malloc().
+ free(p);
+}
+#endif /* JS_USE_CUSTOM_ALLOCATOR */
+
+#include <new>
+
+/*
+ * [SMDOC] Low-level memory management in SpiderMonkey
+ *
+ * ** Do not use the standard malloc/free/realloc: SpiderMonkey allows these
+ * to be redefined (via JS_USE_CUSTOM_ALLOCATOR) and Gecko even #define's
+ * these symbols.
+ *
+ * ** Do not use the builtin C++ operator new and delete: these throw on
+ * error and we cannot override them not to.
+ *
+ * Allocation:
+ *
+ * - If the lifetime of the allocation is tied to the lifetime of a GC-thing
+ * (that is, finalizing the GC-thing will free the allocation), call one of
+ * the following functions:
+ *
+ * JSContext::{pod_malloc,pod_calloc,pod_realloc}
+ * Zone::{pod_malloc,pod_calloc,pod_realloc}
+ *
+ * These functions accumulate the number of bytes allocated which is used as
+ * part of the GC-triggering heuristics.
+ *
+ * The difference between the JSContext and Zone versions is that the
+ * cx version report an out-of-memory error on OOM. (This follows from the
+ * general SpiderMonkey idiom that a JSContext-taking function reports its
+ * own errors.)
+ *
+ * If you don't want to report an error on failure, there are maybe_ versions
+ * of these methods available too, e.g. maybe_pod_malloc.
+ *
+ * The methods above use templates to allow allocating memory suitable for an
+ * array of a given type and number of elements. There are _with_extra
+ * versions to allow allocating an area of memory which is larger by a
+ * specified number of bytes, e.g. pod_malloc_with_extra.
+ *
+ * These methods are available on a JSRuntime, but calling them is
+ * discouraged. Memory attributed to a runtime can only be reclaimed by full
+ * GCs, and we try to avoid those where possible.
+ *
+ * - Otherwise, use js_malloc/js_realloc/js_calloc/js_new
+ *
+ * Deallocation:
+ *
+ * - Ordinarily, use js_free/js_delete.
+ *
+ * - For deallocations during GC finalization, use one of the following
+ * operations on the JSFreeOp provided to the finalizer:
+ *
+ * JSFreeOp::{free_,delete_}
+ */
+
+/*
+ * Given a class which should provide a 'new' method, add
+ * JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example).
+ *
+ * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
+ * or the build will break.
+ */
+#define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS) \
+ template <class T, typename... Args> \
+ QUALIFIERS T* MOZ_HEAP_ALLOCATOR NEWNAME(Args&&... args) { \
+ void* memory = ALLOCATOR(sizeof(T)); \
+ return MOZ_LIKELY(memory) ? new (memory) T(std::forward<Args>(args)...) \
+ : nullptr; \
+ }
+
+/*
+ * Given a class which should provide a 'new' method that takes an arena as
+ * its first argument, add JS_DECLARE_NEW_ARENA_METHODS
+ * (see js::MallocProvider for an example).
+ *
+ * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_ARENA_METHODS,
+ * or the build will break.
+ */
+#define JS_DECLARE_NEW_ARENA_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS) \
+ template <class T, typename... Args> \
+ QUALIFIERS T* MOZ_HEAP_ALLOCATOR NEWNAME(arena_id_t arena, Args&&... args) { \
+ void* memory = ALLOCATOR(arena, sizeof(T)); \
+ return MOZ_LIKELY(memory) ? new (memory) T(std::forward<Args>(args)...) \
+ : nullptr; \
+ }
+
+/*
+ * Given a class which should provide 'make' methods, add
+ * JS_DECLARE_MAKE_METHODS (see js::MallocProvider for an example). This
+ * method is functionally the same as JS_DECLARE_NEW_METHODS: it just declares
+ * methods that return mozilla::UniquePtr instances that will singly-manage
+ * ownership of the created object.
+ *
+ * Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS,
+ * or the build will break.
+ */
+#define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS) \
+ template <class T, typename... Args> \
+ QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> MOZ_HEAP_ALLOCATOR \
+ MAKENAME(Args&&... args) { \
+ T* ptr = NEWNAME<T>(std::forward<Args>(args)...); \
+ return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \
+ }
+
+JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
+JS_DECLARE_NEW_ARENA_METHODS(js_arena_new, js_arena_malloc,
+ static MOZ_ALWAYS_INLINE)
+
+namespace js {
+
+/*
+ * Calculate the number of bytes needed to allocate |numElems| contiguous
+ * instances of type |T|. Return false if the calculation overflowed.
+ */
+template <typename T>
+MOZ_MUST_USE inline bool CalculateAllocSize(size_t numElems, size_t* bytesOut) {
+ *bytesOut = numElems * sizeof(T);
+ return (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) == 0;
+}
+
+/*
+ * Calculate the number of bytes needed to allocate a single instance of type
+ * |T| followed by |numExtra| contiguous instances of type |Extra|. Return
+ * false if the calculation overflowed.
+ */
+template <typename T, typename Extra>
+MOZ_MUST_USE inline bool CalculateAllocSizeWithExtra(size_t numExtra,
+ size_t* bytesOut) {
+ *bytesOut = sizeof(T) + numExtra * sizeof(Extra);
+ return (numExtra & mozilla::tl::MulOverflowMask<sizeof(Extra)>::value) == 0 &&
+ *bytesOut >= sizeof(T);
+}
+
+} /* namespace js */
+
+template <class T>
+static MOZ_ALWAYS_INLINE void js_delete(const T* p) {
+ if (p) {
+ p->~T();
+ js_free(const_cast<T*>(p));
+ }
+}
+
+template <class T>
+static MOZ_ALWAYS_INLINE void js_delete_poison(const T* p) {
+ if (p) {
+ p->~T();
+ memset(static_cast<void*>(const_cast<T*>(p)), 0x3B, sizeof(T));
+ js_free(const_cast<T*>(p));
+ }
+}
+
+template <class T>
+static MOZ_ALWAYS_INLINE T* js_pod_arena_malloc(arena_id_t arena,
+ size_t numElems) {
+ size_t bytes;
+ if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
+ return nullptr;
+ }
+ return static_cast<T*>(js_arena_malloc(arena, bytes));
+}
+
+template <class T>
+static MOZ_ALWAYS_INLINE T* js_pod_malloc(size_t numElems) {
+ return js_pod_arena_malloc<T>(js::MallocArena, numElems);
+}
+
+template <class T>
+static MOZ_ALWAYS_INLINE T* js_pod_arena_calloc(arena_id_t arena,
+ size_t numElems) {
+ size_t bytes;
+ if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
+ return nullptr;
+ }
+ return static_cast<T*>(js_arena_calloc(arena, bytes, 1));
+}
+
+template <class T>
+static MOZ_ALWAYS_INLINE T* js_pod_calloc(size_t numElems) {
+ return js_pod_arena_calloc<T>(js::MallocArena, numElems);
+}
+
+template <class T>
+static MOZ_ALWAYS_INLINE T* js_pod_arena_realloc(arena_id_t arena, T* prior,
+ size_t oldSize,
+ size_t newSize) {
+ MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
+ size_t bytes;
+ if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes))) {
+ return nullptr;
+ }
+ return static_cast<T*>(js_arena_realloc(arena, prior, bytes));
+}
+
+template <class T>
+static MOZ_ALWAYS_INLINE T* js_pod_realloc(T* prior, size_t oldSize,
+ size_t newSize) {
+ return js_pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize);
+}
+
+namespace JS {
+
+template <typename T>
+struct DeletePolicy {
+ constexpr DeletePolicy() = default;
+
+ template <typename U>
+ MOZ_IMPLICIT DeletePolicy(
+ DeletePolicy<U> other,
+ std::enable_if_t<std::is_convertible_v<U*, T*>, int> dummy = 0) {}
+
+ void operator()(const T* ptr) { js_delete(const_cast<T*>(ptr)); }
+};
+
+struct FreePolicy {
+ void operator()(const void* ptr) { js_free(const_cast<void*>(ptr)); }
+};
+
+typedef mozilla::UniquePtr<char[], JS::FreePolicy> UniqueChars;
+typedef mozilla::UniquePtr<char16_t[], JS::FreePolicy> UniqueTwoByteChars;
+typedef mozilla::UniquePtr<JS::Latin1Char[], JS::FreePolicy> UniqueLatin1Chars;
+
+} // namespace JS
+
+/* sixgill annotation defines */
+#ifndef HAVE_STATIC_ANNOTATIONS
+# define HAVE_STATIC_ANNOTATIONS
+# ifdef XGILL_PLUGIN
+# define STATIC_PRECONDITION(COND) __attribute__((precondition(# COND)))
+# define STATIC_PRECONDITION_ASSUME(COND) \
+ __attribute__((precondition_assume(#COND)))
+# define STATIC_POSTCONDITION(COND) __attribute__((postcondition(# COND)))
+# define STATIC_POSTCONDITION_ASSUME(COND) \
+ __attribute__((postcondition_assume(#COND)))
+# define STATIC_INVARIANT(COND) __attribute__((invariant(# COND)))
+# define STATIC_INVARIANT_ASSUME(COND) \
+ __attribute__((invariant_assume(#COND)))
+# define STATIC_ASSUME(COND) \
+ JS_BEGIN_MACRO \
+ __attribute__((assume_static(#COND), unused)) int STATIC_PASTE1( \
+ assume_static_, __COUNTER__); \
+ JS_END_MACRO
+# else /* XGILL_PLUGIN */
+# define STATIC_PRECONDITION(COND) /* nothing */
+# define STATIC_PRECONDITION_ASSUME(COND) /* nothing */
+# define STATIC_POSTCONDITION(COND) /* nothing */
+# define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */
+# define STATIC_INVARIANT(COND) /* nothing */
+# define STATIC_INVARIANT_ASSUME(COND) /* nothing */
+# define STATIC_ASSUME(COND) \
+ JS_BEGIN_MACRO /* nothing */ \
+ JS_END_MACRO
+# endif /* XGILL_PLUGIN */
+# define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference())
+#endif /* HAVE_STATIC_ANNOTATIONS */
+
+#endif /* js_Utility_h */
diff --git a/js/public/Value.h b/js/public/Value.h
new file mode 100644
index 0000000000..51293b8c91
--- /dev/null
+++ b/js/public/Value.h
@@ -0,0 +1,1323 @@
+/* -*- 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/. */
+
+/* JS::Value implementation. */
+
+#ifndef js_Value_h
+#define js_Value_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Casting.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Maybe.h"
+
+#include <limits> /* for std::numeric_limits */
+
+#include "js-config.h"
+#include "jstypes.h"
+
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+
+namespace JS {
+class JS_PUBLIC_API Value;
+}
+
+/* JS::Value can store a full int32_t. */
+#define JSVAL_INT_BITS 32
+#define JSVAL_INT_MIN ((int32_t)0x80000000)
+#define JSVAL_INT_MAX ((int32_t)0x7fffffff)
+
+#if defined(JS_NUNBOX32)
+# define JSVAL_TAG_SHIFT 32
+#elif defined(JS_PUNBOX64)
+# define JSVAL_TAG_SHIFT 47
+#endif
+
+// Use enums so that printing a JS::Value in the debugger shows nice
+// symbolic type tags.
+
+enum JSValueType : uint8_t {
+ JSVAL_TYPE_DOUBLE = 0x00,
+ JSVAL_TYPE_INT32 = 0x01,
+ JSVAL_TYPE_BOOLEAN = 0x02,
+ JSVAL_TYPE_UNDEFINED = 0x03,
+ JSVAL_TYPE_NULL = 0x04,
+ JSVAL_TYPE_MAGIC = 0x05,
+ JSVAL_TYPE_STRING = 0x06,
+ JSVAL_TYPE_SYMBOL = 0x07,
+ JSVAL_TYPE_PRIVATE_GCTHING = 0x08,
+ JSVAL_TYPE_BIGINT = 0x09,
+ JSVAL_TYPE_OBJECT = 0x0c,
+
+ // This type never appears in a Value; it's only an out-of-band value.
+ JSVAL_TYPE_UNKNOWN = 0x20
+};
+
+namespace JS {
+enum class ValueType : uint8_t {
+ Double = JSVAL_TYPE_DOUBLE,
+ Int32 = JSVAL_TYPE_INT32,
+ Boolean = JSVAL_TYPE_BOOLEAN,
+ Undefined = JSVAL_TYPE_UNDEFINED,
+ Null = JSVAL_TYPE_NULL,
+ Magic = JSVAL_TYPE_MAGIC,
+ String = JSVAL_TYPE_STRING,
+ Symbol = JSVAL_TYPE_SYMBOL,
+ PrivateGCThing = JSVAL_TYPE_PRIVATE_GCTHING,
+ BigInt = JSVAL_TYPE_BIGINT,
+ Object = JSVAL_TYPE_OBJECT,
+};
+}
+
+static_assert(sizeof(JSValueType) == 1,
+ "compiler typed enum support is apparently buggy");
+
+#if defined(JS_NUNBOX32)
+
+enum JSValueTag : uint32_t {
+ JSVAL_TAG_CLEAR = 0xFFFFFF80,
+ JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
+ JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED,
+ JSVAL_TAG_NULL = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
+ JSVAL_TAG_BOOLEAN = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
+ JSVAL_TAG_MAGIC = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
+ JSVAL_TAG_STRING = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
+ JSVAL_TAG_SYMBOL = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
+ JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING,
+ JSVAL_TAG_BIGINT = JSVAL_TAG_CLEAR | JSVAL_TYPE_BIGINT,
+ JSVAL_TAG_OBJECT = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT
+};
+
+static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
+ "compiler typed enum support is apparently buggy");
+
+#elif defined(JS_PUNBOX64)
+
+enum JSValueTag : uint32_t {
+ JSVAL_TAG_MAX_DOUBLE = 0x1FFF0,
+ JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
+ JSVAL_TAG_UNDEFINED = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED,
+ JSVAL_TAG_NULL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
+ JSVAL_TAG_BOOLEAN = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
+ JSVAL_TAG_MAGIC = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
+ JSVAL_TAG_STRING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
+ JSVAL_TAG_SYMBOL = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
+ JSVAL_TAG_PRIVATE_GCTHING = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING,
+ JSVAL_TAG_BIGINT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BIGINT,
+ JSVAL_TAG_OBJECT = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT
+};
+
+static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
+ "compiler typed enum support is apparently buggy");
+
+enum JSValueShiftedTag : uint64_t {
+ // See Bug 584653 for why we include 0xFFFFFFFF.
+ JSVAL_SHIFTED_TAG_MAX_DOUBLE =
+ ((uint64_t(JSVAL_TAG_MAX_DOUBLE) << JSVAL_TAG_SHIFT) | 0xFFFFFFFF),
+ JSVAL_SHIFTED_TAG_INT32 = (uint64_t(JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_UNDEFINED =
+ (uint64_t(JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_NULL = (uint64_t(JSVAL_TAG_NULL) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_BOOLEAN = (uint64_t(JSVAL_TAG_BOOLEAN) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_MAGIC = (uint64_t(JSVAL_TAG_MAGIC) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_STRING = (uint64_t(JSVAL_TAG_STRING) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_SYMBOL = (uint64_t(JSVAL_TAG_SYMBOL) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_PRIVATE_GCTHING =
+ (uint64_t(JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_BIGINT = (uint64_t(JSVAL_TAG_BIGINT) << JSVAL_TAG_SHIFT),
+ JSVAL_SHIFTED_TAG_OBJECT = (uint64_t(JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT)
+};
+
+static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
+ "compiler typed enum support is apparently buggy");
+
+#endif
+
+namespace JS {
+namespace detail {
+
+#if defined(JS_NUNBOX32)
+
+constexpr JSValueTag ValueTypeToTag(JSValueType type) {
+ return static_cast<JSValueTag>(JSVAL_TAG_CLEAR | type);
+}
+
+constexpr bool ValueIsDouble(uint64_t bits) {
+ return uint32_t(bits >> JSVAL_TAG_SHIFT) <= uint32_t(JSVAL_TAG_CLEAR);
+}
+
+constexpr JSValueTag ValueUpperExclPrimitiveTag = JSVAL_TAG_OBJECT;
+constexpr JSValueTag ValueUpperInclNumberTag = JSVAL_TAG_INT32;
+constexpr JSValueTag ValueLowerInclGCThingTag = JSVAL_TAG_STRING;
+
+#elif defined(JS_PUNBOX64)
+
+constexpr JSValueTag ValueTypeToTag(JSValueType type) {
+ return static_cast<JSValueTag>(JSVAL_TAG_MAX_DOUBLE | type);
+}
+
+constexpr bool ValueIsDouble(uint64_t bits) {
+ return bits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
+}
+
+constexpr uint64_t ValueTagMask = 0xFFFF'8000'0000'0000;
+
+// This should only be used in toGCThing. See the 'Spectre mitigations' comment.
+constexpr uint64_t ValueGCThingPayloadMask = 0x0000'7FFF'FFFF'FFFF;
+
+constexpr uint64_t ValueTypeToShiftedTag(JSValueType type) {
+ return static_cast<uint64_t>(ValueTypeToTag(type)) << JSVAL_TAG_SHIFT;
+}
+# define JSVAL_TYPE_TO_SHIFTED_TAG(type) \
+ (JS::detail::ValueTypeToShiftedTag(type))
+
+constexpr JSValueTag ValueUpperExclPrimitiveTag = JSVAL_TAG_OBJECT;
+constexpr JSValueTag ValueUpperInclNumberTag = JSVAL_TAG_INT32;
+constexpr JSValueTag ValueLowerInclGCThingTag = JSVAL_TAG_STRING;
+
+constexpr uint64_t ValueUpperExclShiftedPrimitiveTag = JSVAL_SHIFTED_TAG_OBJECT;
+constexpr uint64_t ValueUpperExclShiftedNumberTag = JSVAL_SHIFTED_TAG_BOOLEAN;
+constexpr uint64_t ValueLowerInclShiftedGCThingTag = JSVAL_SHIFTED_TAG_STRING;
+
+// JSVAL_TYPE_OBJECT and JSVAL_TYPE_NULL differ by one bit. We can use this to
+// implement toObjectOrNull more efficiently.
+constexpr uint64_t ValueObjectOrNullBit = 0x8ULL << JSVAL_TAG_SHIFT;
+static_assert(
+ (JSVAL_SHIFTED_TAG_NULL ^ JSVAL_SHIFTED_TAG_OBJECT) == ValueObjectOrNullBit,
+ "ValueObjectOrNullBit must be consistent with object and null tags");
+
+constexpr uint64_t IsValidUserModePointer(uint64_t bits) {
+ // All 64-bit platforms that we support actually have a 48-bit address space
+ // for user-mode pointers, with the top 16 bits all set to zero.
+ return (bits & 0xFFFF'0000'0000'0000) == 0;
+}
+
+#endif /* JS_PUNBOX64 */
+
+} // namespace detail
+} // namespace JS
+
+#define JSVAL_TYPE_TO_TAG(type) (JS::detail::ValueTypeToTag(type))
+
+enum JSWhyMagic {
+ /** a hole in a native object's elements */
+ JS_ELEMENTS_HOLE,
+
+ /** there is not a pending iterator value */
+ JS_NO_ITER_VALUE,
+
+ /** exception value thrown when closing a generator */
+ JS_GENERATOR_CLOSING,
+
+ /** used in debug builds to catch tracing errors */
+ JS_ARG_POISON,
+
+ /** an empty subnode in the AST serializer */
+ JS_SERIALIZE_NO_NODE,
+
+ /** optimized-away 'arguments' value */
+ JS_OPTIMIZED_ARGUMENTS,
+
+ /** magic value passed to natives to indicate construction */
+ JS_IS_CONSTRUCTING,
+
+ /** see class js::HashableValue */
+ JS_HASH_KEY_EMPTY,
+
+ /** error while running Ion code */
+ JS_ION_ERROR,
+
+ /** missing recover instruction result */
+ JS_ION_BAILOUT,
+
+ /** optimized out slot */
+ JS_OPTIMIZED_OUT,
+
+ /** uninitialized lexical bindings that produce ReferenceError on touch. */
+ JS_UNINITIALIZED_LEXICAL,
+
+ /** standard constructors are not created for off-thread parsing. */
+ JS_OFF_THREAD_CONSTRUCTOR,
+
+ /** for local use */
+ JS_GENERIC_MAGIC,
+
+ /**
+ * Write records queued up in WritableStreamDefaultController.[[queue]] in the
+ * spec are either "close" (a String) or Record { [[chunk]]: chunk }, where
+ * chunk is an arbitrary user-provided (and therefore non-magic) value.
+ * Represent "close" the String as this magic value; represent Record records
+ * as the |chunk| value within each of them.
+ */
+ JS_WRITABLESTREAM_CLOSE_RECORD,
+
+ /**
+ * The ReadableStream pipe-to operation concludes with a "finalize" operation
+ * that accepts an optional |error| argument. In certain cases that optional
+ * |error| must be stored in a handler function, for use after a promise has
+ * settled. We represent the argument not being provided, in those cases,
+ * using this magic value.
+ */
+ JS_READABLESTREAM_PIPETO_FINALIZE_WITHOUT_ERROR,
+
+ JS_WHY_MAGIC_COUNT
+};
+
+namespace js {
+static inline JS::Value PoisonedObjectValue(uintptr_t poison);
+} // namespace js
+
+namespace JS {
+
+namespace detail {
+
+// IEEE-754 bit pattern for double-precision positive infinity.
+constexpr int InfinitySignBit = 0;
+constexpr uint64_t InfinityBits =
+ mozilla::InfinityBits<double, detail::InfinitySignBit>::value;
+
+// This is a quiet NaN on IEEE-754[2008] compatible platforms, including X86,
+// ARM, SPARC and modern MIPS.
+//
+// Note: The default sign bit for a hardware sythesized NaN differs between X86
+// and ARM. Both values are considered compatible values on both
+// platforms.
+constexpr int CanonicalizedNaNSignBit = 0;
+constexpr uint64_t CanonicalizedNaNSignificand = 0x8000000000000;
+
+#if defined(__sparc__)
+// Some architectures (not to name names) generate NaNs with bit patterns that
+// are incompatible with JS::Value's bit pattern restrictions. Instead we must
+// canonicalize all hardware values before storing in JS::Value.
+# define JS_NONCANONICAL_HARDWARE_NAN
+#endif
+
+#if defined(__mips__) && !defined(__mips_nan_2008)
+// These builds may run on hardware that has differing polarity of the signaling
+// NaN bit. While the kernel may handle the trap for us, it is a performance
+// issue so instead we compute the NaN to use on startup. The runtime value must
+// still meet `ValueIsDouble` requirements which are checked on startup.
+
+// In particular, we expect one of the following values on MIPS:
+// - 0x7FF7FFFFFFFFFFFF Legacy
+// - 0x7FF8000000000000 IEEE-754[2008]
+# define JS_RUNTIME_CANONICAL_NAN
+#endif
+
+#if defined(JS_RUNTIME_CANONICAL_NAN)
+extern uint64_t CanonicalizedNaNBits;
+#else
+constexpr uint64_t CanonicalizedNaNBits =
+ mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit,
+ detail::CanonicalizedNaNSignificand>::value;
+#endif
+} // namespace detail
+
+// Return a quiet NaN that is compatible with JS::Value restrictions.
+static MOZ_ALWAYS_INLINE double GenericNaN() {
+#if !defined(JS_RUNTIME_CANONICAL_NAN)
+ static_assert(detail::ValueIsDouble(detail::CanonicalizedNaNBits),
+ "Canonical NaN must be compatible with JS::Value");
+#endif
+
+ return mozilla::BitwiseCast<double>(detail::CanonicalizedNaNBits);
+}
+
+// Convert an arbitrary double to one compatible with JS::Value representation
+// by replacing any NaN value with a canonical one.
+static MOZ_ALWAYS_INLINE double CanonicalizeNaN(double d) {
+ if (MOZ_UNLIKELY(mozilla::IsNaN(d))) {
+ return GenericNaN();
+ }
+ return d;
+}
+
+/**
+ * [SMDOC] JS::Value type
+ *
+ * JS::Value is the interface for a single JavaScript Engine value. A few
+ * general notes on JS::Value:
+ *
+ * - JS::Value has setX() and isX() members for X in
+ *
+ * { Int32, Double, String, Symbol, BigInt, Boolean, Undefined, Null,
+ * Object, Magic }
+ *
+ * JS::Value also contains toX() for each of the non-singleton types.
+ *
+ * - Magic is a singleton type whose payload contains either a JSWhyMagic
+ * "reason" for the magic value or a uint32_t value. By providing JSWhyMagic
+ * values when creating and checking for magic values, it is possible to
+ * assert, at runtime, that only magic values with the expected reason flow
+ * through a particular value. For example, if cx->exception has a magic
+ * value, the reason must be JS_GENERATOR_CLOSING.
+ *
+ * - The JS::Value operations are preferred. The JSVAL_* operations remain for
+ * compatibility; they may be removed at some point. These operations mostly
+ * provide similar functionality. But there are a few key differences. One
+ * is that JS::Value gives null a separate type.
+ * Also, to help prevent mistakenly boxing a nullable JSObject* as an object,
+ * Value::setObject takes a JSObject&. (Conversely, Value::toObject returns a
+ * JSObject&.) A convenience member Value::setObjectOrNull is provided.
+ *
+ * - Note that JS::Value is 8 bytes on 32 and 64-bit architectures. Thus, on
+ * 32-bit user code should avoid copying jsval/JS::Value as much as possible,
+ * preferring to pass by const Value&.
+ *
+ * Spectre mitigations
+ * ===================
+ * To mitigate Spectre attacks, we do the following:
+ *
+ * - On 64-bit platforms, when unboxing a Value, we XOR the bits with the
+ * expected type tag (instead of masking the payload bits). This guarantees
+ * that toString, toObject, toSymbol will return an invalid pointer (because
+ * some high bits will be set) when called on a Value with a different type
+ * tag.
+ *
+ * - On 32-bit platforms,when unboxing an object/string/symbol Value, we use a
+ * conditional move (not speculated) to zero the payload register if the type
+ * doesn't match.
+ */
+class alignas(8) Value {
+ private:
+ uint64_t asBits_;
+
+ public:
+ constexpr Value() : asBits_(bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0)) {}
+ Value(const Value& v) = default;
+
+ private:
+ explicit constexpr Value(uint64_t asBits) : asBits_(asBits) {}
+
+ static uint64_t bitsFromDouble(double d) {
+#if defined(JS_NONCANONICAL_HARDWARE_NAN)
+ d = CanonicalizeNaN(d);
+#endif
+ return mozilla::BitwiseCast<uint64_t>(d);
+ }
+
+ static_assert(sizeof(JSValueType) == 1,
+ "type bits must fit in a single byte");
+ static_assert(sizeof(JSValueTag) == 4,
+ "32-bit Value's tag_ must have size 4 to complement the "
+ "payload union's size 4");
+ static_assert(sizeof(JSWhyMagic) <= 4,
+ "32-bit Value's JSWhyMagic payload field must not inflate "
+ "the payload beyond 4 bytes");
+
+ public:
+#if defined(JS_NUNBOX32)
+ using PayloadType = uint32_t;
+#elif defined(JS_PUNBOX64)
+ using PayloadType = uint64_t;
+#endif
+
+ static constexpr uint64_t bitsFromTagAndPayload(JSValueTag tag,
+ PayloadType payload) {
+ return (uint64_t(tag) << JSVAL_TAG_SHIFT) | payload;
+ }
+
+ static constexpr Value fromTagAndPayload(JSValueTag tag,
+ PayloadType payload) {
+ return fromRawBits(bitsFromTagAndPayload(tag, payload));
+ }
+
+ static constexpr Value fromRawBits(uint64_t asBits) { return Value(asBits); }
+
+ static constexpr Value fromInt32(int32_t i) {
+ return fromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i));
+ }
+
+ static Value fromDouble(double d) { return fromRawBits(bitsFromDouble(d)); }
+
+ /**
+ * Returns false if creating a NumberValue containing the given type would
+ * be lossy, true otherwise.
+ */
+ template <typename T>
+ static bool isNumberRepresentable(const T t) {
+ return T(double(t)) == t;
+ }
+
+ /*** Mutators ***/
+
+ void setNull() {
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_NULL, 0);
+ MOZ_ASSERT(isNull());
+ }
+
+ void setUndefined() {
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_UNDEFINED, 0);
+ MOZ_ASSERT(isUndefined());
+ }
+
+ void setInt32(int32_t i) {
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i));
+ MOZ_ASSERT(toInt32() == i);
+ }
+
+ void setDouble(double d) {
+ asBits_ = bitsFromDouble(d);
+ MOZ_ASSERT(isDouble());
+ }
+
+ void setString(JSString* str) {
+ MOZ_ASSERT(js::gc::IsCellPointerValid(str));
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str));
+ MOZ_ASSERT(toString() == str);
+ }
+
+ void setSymbol(JS::Symbol* sym) {
+ MOZ_ASSERT(js::gc::IsCellPointerValid(sym));
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym));
+ MOZ_ASSERT(toSymbol() == sym);
+ }
+
+ void setBigInt(JS::BigInt* bi) {
+ MOZ_ASSERT(js::gc::IsCellPointerValid(bi));
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BIGINT, PayloadType(bi));
+ MOZ_ASSERT(toBigInt() == bi);
+ }
+
+ void setObject(JSObject& obj) {
+ MOZ_ASSERT(js::gc::IsCellPointerValid(&obj));
+ setObjectNoCheck(&obj);
+ MOZ_ASSERT(&toObject() == &obj);
+ }
+
+ private:
+ void setObjectNoCheck(JSObject* obj) {
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_OBJECT, PayloadType(obj));
+ }
+
+ friend inline Value js::PoisonedObjectValue(uintptr_t poison);
+
+ public:
+ void setBoolean(bool b) {
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(b));
+ MOZ_ASSERT(toBoolean() == b);
+ }
+
+ void setMagic(JSWhyMagic why) {
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, uint32_t(why));
+ MOZ_ASSERT(whyMagic() == why);
+ }
+
+ void setMagicUint32(uint32_t payload) {
+ MOZ_ASSERT(payload >= JS_WHY_MAGIC_COUNT,
+ "This should only be used for non-standard magic values");
+ asBits_ = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, payload);
+ MOZ_ASSERT(magicUint32() == payload);
+ }
+
+ void setNumber(float f) {
+ int32_t i;
+ if (mozilla::NumberIsInt32(f, &i)) {
+ setInt32(i);
+ return;
+ }
+
+ setDouble(double(f));
+ }
+
+ void setNumber(double d) {
+ int32_t i;
+ if (mozilla::NumberIsInt32(d, &i)) {
+ setInt32(i);
+ return;
+ }
+
+ setDouble(d);
+ }
+
+ template <typename T>
+ void setNumber(const T t) {
+ static_assert(std::is_integral<T>::value, "must be integral type");
+ MOZ_ASSERT(isNumberRepresentable(t), "value creation would be lossy");
+
+ if constexpr (std::numeric_limits<T>::is_signed) {
+ if constexpr (sizeof(t) <= sizeof(int32_t)) {
+ setInt32(int32_t(t));
+ } else {
+ if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX) {
+ setInt32(int32_t(t));
+ } else {
+ setDouble(double(t));
+ }
+ }
+ } else {
+ if constexpr (sizeof(t) <= sizeof(uint16_t)) {
+ setInt32(int32_t(t));
+ } else {
+ if (t <= JSVAL_INT_MAX) {
+ setInt32(int32_t(t));
+ } else {
+ setDouble(double(t));
+ }
+ }
+ }
+ }
+
+ void setObjectOrNull(JSObject* arg) {
+ if (arg) {
+ setObject(*arg);
+ } else {
+ setNull();
+ }
+ }
+
+ void swap(Value& rhs) {
+ uint64_t tmp = rhs.asBits_;
+ rhs.asBits_ = asBits_;
+ asBits_ = tmp;
+ }
+
+ private:
+ JSValueTag toTag() const { return JSValueTag(asBits_ >> JSVAL_TAG_SHIFT); }
+
+ template <typename T, JSValueTag Tag>
+ T* unboxGCPointer() const {
+ MOZ_ASSERT((asBits_ & js::gc::CellAlignMask) == 0,
+ "GC pointer is not aligned. Is this memory corruption?");
+#if defined(JS_NUNBOX32)
+ uintptr_t payload = uint32_t(asBits_);
+ return reinterpret_cast<T*>(payload);
+#elif defined(JS_PUNBOX64)
+ // Note: the 'Spectre mitigations' comment at the top of this class
+ // explains why we use XOR here.
+ constexpr uint64_t shiftedTag = uint64_t(Tag) << JSVAL_TAG_SHIFT;
+ return reinterpret_cast<T*>(uintptr_t(asBits_ ^ shiftedTag));
+#endif
+ }
+
+ public:
+ /*** JIT-only interfaces to interact with and create raw Values ***/
+#if defined(JS_NUNBOX32)
+ PayloadType toNunboxPayload() const { return uint32_t(asBits_); }
+
+ JSValueTag toNunboxTag() const { return toTag(); }
+#elif defined(JS_PUNBOX64)
+ const void* bitsAsPunboxPointer() const {
+ return reinterpret_cast<void*>(asBits_);
+ }
+#endif
+
+ /*** Value type queries ***/
+
+ /*
+ * N.B. GCC, in some but not all cases, chooses to emit signed comparison
+ * of JSValueTag even though its underlying type has been forced to be
+ * uint32_t. Thus, all comparisons should explicitly cast operands to
+ * uint32_t.
+ */
+
+ bool isUndefined() const {
+#if defined(JS_NUNBOX32)
+ return toTag() == JSVAL_TAG_UNDEFINED;
+#elif defined(JS_PUNBOX64)
+ return asBits_ == JSVAL_SHIFTED_TAG_UNDEFINED;
+#endif
+ }
+
+ bool isNull() const {
+#if defined(JS_NUNBOX32)
+ return toTag() == JSVAL_TAG_NULL;
+#elif defined(JS_PUNBOX64)
+ return asBits_ == JSVAL_SHIFTED_TAG_NULL;
+#endif
+ }
+
+ bool isNullOrUndefined() const { return isNull() || isUndefined(); }
+
+ bool isInt32() const { return toTag() == JSVAL_TAG_INT32; }
+
+ bool isInt32(int32_t i32) const {
+ return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_INT32, uint32_t(i32));
+ }
+
+ bool isDouble() const { return detail::ValueIsDouble(asBits_); }
+
+ bool isNumber() const {
+#if defined(JS_NUNBOX32)
+ MOZ_ASSERT(toTag() != JSVAL_TAG_CLEAR);
+ return uint32_t(toTag()) <= uint32_t(detail::ValueUpperInclNumberTag);
+#elif defined(JS_PUNBOX64)
+ return asBits_ < detail::ValueUpperExclShiftedNumberTag;
+#endif
+ }
+
+ bool isString() const { return toTag() == JSVAL_TAG_STRING; }
+
+ bool isSymbol() const { return toTag() == JSVAL_TAG_SYMBOL; }
+
+ bool isBigInt() const { return toTag() == JSVAL_TAG_BIGINT; }
+
+ bool isObject() const {
+#if defined(JS_NUNBOX32)
+ return toTag() == JSVAL_TAG_OBJECT;
+#elif defined(JS_PUNBOX64)
+ MOZ_ASSERT((asBits_ >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
+ return asBits_ >= JSVAL_SHIFTED_TAG_OBJECT;
+#endif
+ }
+
+ bool isPrimitive() const {
+#if defined(JS_NUNBOX32)
+ return uint32_t(toTag()) < uint32_t(detail::ValueUpperExclPrimitiveTag);
+#elif defined(JS_PUNBOX64)
+ return asBits_ < detail::ValueUpperExclShiftedPrimitiveTag;
+#endif
+ }
+
+ bool isObjectOrNull() const { return isObject() || isNull(); }
+
+ bool isNumeric() const { return isNumber() || isBigInt(); }
+
+ bool isGCThing() const {
+#if defined(JS_NUNBOX32)
+ /* gcc sometimes generates signed < without explicit casts. */
+ return uint32_t(toTag()) >= uint32_t(detail::ValueLowerInclGCThingTag);
+#elif defined(JS_PUNBOX64)
+ return asBits_ >= detail::ValueLowerInclShiftedGCThingTag;
+#endif
+ }
+
+ bool isBoolean() const { return toTag() == JSVAL_TAG_BOOLEAN; }
+
+ bool isTrue() const {
+ return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(true));
+ }
+
+ bool isFalse() const {
+ return asBits_ == bitsFromTagAndPayload(JSVAL_TAG_BOOLEAN, uint32_t(false));
+ }
+
+ bool isMagic() const { return toTag() == JSVAL_TAG_MAGIC; }
+
+ bool isMagic(JSWhyMagic why) const {
+ if (!isMagic()) {
+ return false;
+ }
+ MOZ_RELEASE_ASSERT(whyMagic() == why);
+ return true;
+ }
+
+ JS::TraceKind traceKind() const {
+ MOZ_ASSERT(isGCThing());
+ static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
+ "Value type tags must correspond with JS::TraceKinds.");
+ static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
+ "Value type tags must correspond with JS::TraceKinds.");
+ static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
+ "Value type tags must correspond with JS::TraceKinds.");
+ static_assert((JSVAL_TAG_BIGINT & 0x03) == size_t(JS::TraceKind::BigInt),
+ "Value type tags must correspond with JS::TraceKinds.");
+ if (MOZ_UNLIKELY(isPrivateGCThing())) {
+ return JS::GCThingTraceKind(toGCThing());
+ }
+ return JS::TraceKind(toTag() & 0x03);
+ }
+
+ JSWhyMagic whyMagic() const {
+ MOZ_ASSERT(magicUint32() < JS_WHY_MAGIC_COUNT);
+ return static_cast<JSWhyMagic>(magicUint32());
+ }
+
+ uint32_t magicUint32() const {
+ MOZ_ASSERT(isMagic());
+ return uint32_t(asBits_);
+ }
+
+ /*** Comparison ***/
+
+ bool operator==(const Value& rhs) const { return asBits_ == rhs.asBits_; }
+
+ bool operator!=(const Value& rhs) const { return asBits_ != rhs.asBits_; }
+
+ friend inline bool SameType(const Value& lhs, const Value& rhs);
+
+ /*** Extract the value's typed payload ***/
+
+ int32_t toInt32() const {
+ MOZ_ASSERT(isInt32());
+ return int32_t(asBits_);
+ }
+
+ double toDouble() const {
+ MOZ_ASSERT(isDouble());
+ return mozilla::BitwiseCast<double>(asBits_);
+ }
+
+ double toNumber() const {
+ MOZ_ASSERT(isNumber());
+ return isDouble() ? toDouble() : double(toInt32());
+ }
+
+ JSString* toString() const {
+ MOZ_ASSERT(isString());
+ return unboxGCPointer<JSString, JSVAL_TAG_STRING>();
+ }
+
+ JS::Symbol* toSymbol() const {
+ MOZ_ASSERT(isSymbol());
+ return unboxGCPointer<JS::Symbol, JSVAL_TAG_SYMBOL>();
+ }
+
+ JS::BigInt* toBigInt() const {
+ MOZ_ASSERT(isBigInt());
+ return unboxGCPointer<JS::BigInt, JSVAL_TAG_BIGINT>();
+ }
+
+ JSObject& toObject() const {
+ MOZ_ASSERT(isObject());
+#if defined(JS_PUNBOX64)
+ MOZ_ASSERT((asBits_ & detail::ValueGCThingPayloadMask) != 0);
+#endif
+ return *unboxGCPointer<JSObject, JSVAL_TAG_OBJECT>();
+ }
+
+ JSObject* toObjectOrNull() const {
+ MOZ_ASSERT(isObjectOrNull());
+#if defined(JS_NUNBOX32)
+ return reinterpret_cast<JSObject*>(uintptr_t(asBits_));
+#elif defined(JS_PUNBOX64)
+ // Note: the 'Spectre mitigations' comment at the top of this class
+ // explains why we use XOR here and in other to* methods.
+ uint64_t ptrBits =
+ (asBits_ ^ JSVAL_SHIFTED_TAG_OBJECT) & ~detail::ValueObjectOrNullBit;
+ MOZ_ASSERT((ptrBits & 0x7) == 0);
+ return reinterpret_cast<JSObject*>(ptrBits);
+#endif
+ }
+
+ js::gc::Cell* toGCThing() const {
+ MOZ_ASSERT(isGCThing());
+#if defined(JS_NUNBOX32)
+ return reinterpret_cast<js::gc::Cell*>(uintptr_t(asBits_));
+#elif defined(JS_PUNBOX64)
+ uint64_t ptrBits = asBits_ & detail::ValueGCThingPayloadMask;
+ MOZ_ASSERT((ptrBits & 0x7) == 0);
+ return reinterpret_cast<js::gc::Cell*>(ptrBits);
+#endif
+ }
+
+ GCCellPtr toGCCellPtr() const { return GCCellPtr(toGCThing(), traceKind()); }
+
+ bool toBoolean() const {
+ MOZ_ASSERT(isBoolean());
+#if defined(JS_NUNBOX32)
+ return bool(toNunboxPayload());
+#elif defined(JS_PUNBOX64)
+ return bool(asBits_ & 0x1);
+#endif
+ }
+
+ uint32_t payloadAsRawUint32() const {
+ MOZ_ASSERT(!isDouble());
+ return uint32_t(asBits_);
+ }
+
+ constexpr uint64_t asRawBits() const { return asBits_; }
+
+ JSValueType extractNonDoubleType() const {
+ uint32_t type = toTag() & 0xF;
+ MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
+ return JSValueType(type);
+ }
+
+ JS::ValueType type() const {
+ if (isDouble()) {
+ return JS::ValueType::Double;
+ }
+
+ JSValueType type = extractNonDoubleType();
+ MOZ_ASSERT(type <= JSVAL_TYPE_OBJECT);
+ return JS::ValueType(type);
+ }
+
+ /*
+ * Private API
+ *
+ * Private setters/getters allow the caller to read/write arbitrary
+ * word-size pointers or uint32s. After storing to a value with
+ * setPrivateX, it is the caller's responsibility to only read using
+ * toPrivateX. Private values are given a type which ensures they
+ * aren't marked by the GC.
+ */
+
+ void setPrivate(void* ptr) {
+#if defined(JS_PUNBOX64)
+ MOZ_ASSERT(detail::IsValidUserModePointer(uintptr_t(ptr)));
+#endif
+ asBits_ = uintptr_t(ptr);
+ MOZ_ASSERT(isDouble());
+ }
+
+ void* toPrivate() const {
+ MOZ_ASSERT(isDouble());
+#if defined(JS_PUNBOX64)
+ MOZ_ASSERT(detail::IsValidUserModePointer(asBits_));
+#endif
+ return reinterpret_cast<void*>(uintptr_t(asBits_));
+ }
+
+ void setPrivateUint32(uint32_t ui) {
+ MOZ_ASSERT(uint32_t(int32_t(ui)) == ui);
+ setInt32(int32_t(ui));
+ }
+
+ uint32_t toPrivateUint32() const { return uint32_t(toInt32()); }
+
+ /*
+ * Private GC Thing API
+ *
+ * Non-JSObject, JSString, and JS::Symbol cells may be put into the 64-bit
+ * payload as private GC things. Such Values are considered isGCThing(), and
+ * as such, automatically marked. Their traceKind() is gotten via their
+ * cells.
+ */
+
+ void setPrivateGCThing(js::gc::Cell* cell) {
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
+ "Private GC thing Values must not be strings. Make a "
+ "StringValue instead.");
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
+ "Private GC thing Values must not be symbols. Make a "
+ "SymbolValue instead.");
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::BigInt,
+ "Private GC thing Values must not be BigInts. Make a "
+ "BigIntValue instead.");
+ MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
+ "Private GC thing Values must not be objects. Make an "
+ "ObjectValue instead.");
+
+ MOZ_ASSERT(js::gc::IsCellPointerValid(cell));
+#if defined(JS_PUNBOX64)
+ // VisualStudio cannot contain parenthesized C++ style cast and shift
+ // inside decltype in template parameter:
+ // AssertionConditionType<decltype((uintptr_t(x) >> 1))>
+ // It throws syntax error.
+ MOZ_ASSERT((((uintptr_t)cell) >> JSVAL_TAG_SHIFT) == 0);
+#endif
+ asBits_ =
+ bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell));
+ }
+
+ bool isPrivateGCThing() const { return toTag() == JSVAL_TAG_PRIVATE_GCTHING; }
+} JS_HAZ_GC_POINTER MOZ_NON_PARAM;
+
+static_assert(sizeof(Value) == 8,
+ "Value size must leave three tag bits, be a binary power, and "
+ "is ubiquitously depended upon everywhere");
+
+inline bool IsOptimizedPlaceholderMagicValue(const Value& v) {
+ if (v.isMagic()) {
+ MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS ||
+ v.whyMagic() == JS_OPTIMIZED_OUT);
+ return true;
+ }
+ return false;
+}
+
+static MOZ_ALWAYS_INLINE void ExposeValueToActiveJS(const Value& v) {
+#ifdef DEBUG
+ Value tmp = v;
+ MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp));
+#endif
+ if (v.isGCThing()) {
+ js::gc::ExposeGCThingToActiveJS(GCCellPtr(v));
+ }
+}
+
+/************************************************************************/
+
+static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value NullValue() {
+ Value v;
+ v.setNull();
+ return v;
+}
+
+static constexpr Value UndefinedValue() { return Value(); }
+
+static constexpr Value Int32Value(int32_t i32) { return Value::fromInt32(i32); }
+
+static inline Value DoubleValue(double dbl) {
+ Value v;
+ v.setDouble(dbl);
+ return v;
+}
+
+static inline Value CanonicalizedDoubleValue(double d) {
+ return Value::fromDouble(CanonicalizeNaN(d));
+}
+
+static inline Value NaNValue() {
+ return Value::fromRawBits(detail::CanonicalizedNaNBits);
+}
+
+static inline Value InfinityValue() {
+ return Value::fromRawBits(detail::InfinityBits);
+}
+
+static inline Value Float32Value(float f) {
+ Value v;
+ v.setDouble(f);
+ return v;
+}
+
+static inline Value StringValue(JSString* str) {
+ Value v;
+ v.setString(str);
+ return v;
+}
+
+static inline Value SymbolValue(JS::Symbol* sym) {
+ Value v;
+ v.setSymbol(sym);
+ return v;
+}
+
+static inline Value BigIntValue(JS::BigInt* bi) {
+ Value v;
+ v.setBigInt(bi);
+ return v;
+}
+
+static inline Value BooleanValue(bool boo) {
+ Value v;
+ v.setBoolean(boo);
+ return v;
+}
+
+static inline Value TrueValue() {
+ Value v;
+ v.setBoolean(true);
+ return v;
+}
+
+static inline Value FalseValue() {
+ Value v;
+ v.setBoolean(false);
+ return v;
+}
+
+static inline Value ObjectValue(JSObject& obj) {
+ Value v;
+ v.setObject(obj);
+ return v;
+}
+
+static inline Value MagicValue(JSWhyMagic why) {
+ Value v;
+ v.setMagic(why);
+ return v;
+}
+
+static inline Value MagicValueUint32(uint32_t payload) {
+ Value v;
+ v.setMagicUint32(payload);
+ return v;
+}
+
+static constexpr Value NumberValue(uint32_t i) {
+ return i <= JSVAL_INT_MAX ? Int32Value(int32_t(i))
+ : Value::fromDouble(double(i));
+}
+
+template <typename T>
+static inline Value NumberValue(const T t) {
+ Value v;
+ v.setNumber(t);
+ return v;
+}
+
+static inline Value ObjectOrNullValue(JSObject* obj) {
+ Value v;
+ v.setObjectOrNull(obj);
+ return v;
+}
+
+static inline Value PrivateValue(void* ptr) {
+ Value v;
+ v.setPrivate(ptr);
+ return v;
+}
+
+static inline Value PrivateValue(uintptr_t ptr) {
+ return PrivateValue(reinterpret_cast<void*>(ptr));
+}
+
+static inline Value PrivateUint32Value(uint32_t ui) {
+ Value v;
+ v.setPrivateUint32(ui);
+ return v;
+}
+
+static inline Value PrivateGCThingValue(js::gc::Cell* cell) {
+ Value v;
+ v.setPrivateGCThing(cell);
+ return v;
+}
+
+inline bool SameType(const Value& lhs, const Value& rhs) {
+#if defined(JS_NUNBOX32)
+ JSValueTag ltag = lhs.toTag(), rtag = rhs.toTag();
+ return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR);
+#elif defined(JS_PUNBOX64)
+ return (lhs.isDouble() && rhs.isDouble()) ||
+ (((lhs.asBits_ ^ rhs.asBits_) & 0xFFFF800000000000ULL) == 0);
+#endif
+}
+
+} // namespace JS
+
+/************************************************************************/
+
+namespace JS {
+JS_PUBLIC_API void HeapValuePostWriteBarrier(Value* valuep, const Value& prev,
+ const Value& next);
+JS_PUBLIC_API void HeapValueWriteBarriers(Value* valuep, const Value& prev,
+ const Value& next);
+
+template <>
+struct GCPolicy<JS::Value> {
+ static void trace(JSTracer* trc, Value* v, const char* name) {
+ // It's not safe to trace unbarriered pointers except as part of root
+ // marking.
+ UnsafeTraceRoot(trc, v, name);
+ }
+ static bool isTenured(const Value& thing) {
+ return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing());
+ }
+ static bool isValid(const Value& value) {
+ return !value.isGCThing() || js::gc::IsCellPointerValid(value.toGCThing());
+ }
+};
+
+} // namespace JS
+
+namespace js {
+
+template <>
+struct BarrierMethods<JS::Value> {
+ static gc::Cell* asGCThingOrNull(const JS::Value& v) {
+ return v.isGCThing() ? v.toGCThing() : nullptr;
+ }
+ static void postWriteBarrier(JS::Value* v, const JS::Value& prev,
+ const JS::Value& next) {
+ JS::HeapValuePostWriteBarrier(v, prev, next);
+ }
+ static void exposeToJS(const JS::Value& v) { JS::ExposeValueToActiveJS(v); }
+};
+
+template <class Wrapper>
+class MutableValueOperations;
+
+/**
+ * A class designed for CRTP use in implementing the non-mutating parts of the
+ * Value interface in Value-like classes. Wrapper must be a class inheriting
+ * ValueOperations<Wrapper> with a visible get() method returning a const
+ * reference to the Value abstracted by Wrapper.
+ */
+template <class Wrapper>
+class WrappedPtrOperations<JS::Value, Wrapper> {
+ const JS::Value& value() const {
+ return static_cast<const Wrapper*>(this)->get();
+ }
+
+ public:
+ bool isUndefined() const { return value().isUndefined(); }
+ bool isNull() const { return value().isNull(); }
+ bool isBoolean() const { return value().isBoolean(); }
+ bool isTrue() const { return value().isTrue(); }
+ bool isFalse() const { return value().isFalse(); }
+ bool isNumber() const { return value().isNumber(); }
+ bool isInt32() const { return value().isInt32(); }
+ bool isInt32(int32_t i32) const { return value().isInt32(i32); }
+ bool isDouble() const { return value().isDouble(); }
+ bool isString() const { return value().isString(); }
+ bool isSymbol() const { return value().isSymbol(); }
+ bool isBigInt() const { return value().isBigInt(); }
+ bool isObject() const { return value().isObject(); }
+ bool isMagic() const { return value().isMagic(); }
+ bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
+ bool isGCThing() const { return value().isGCThing(); }
+ bool isPrimitive() const { return value().isPrimitive(); }
+
+ bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
+ bool isObjectOrNull() const { return value().isObjectOrNull(); }
+ bool isNumeric() const { return value().isNumeric(); }
+
+ bool toBoolean() const { return value().toBoolean(); }
+ double toNumber() const { return value().toNumber(); }
+ int32_t toInt32() const { return value().toInt32(); }
+ double toDouble() const { return value().toDouble(); }
+ JSString* toString() const { return value().toString(); }
+ JS::Symbol* toSymbol() const { return value().toSymbol(); }
+ JS::BigInt* toBigInt() const { return value().toBigInt(); }
+ JSObject& toObject() const { return value().toObject(); }
+ JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
+ gc::Cell* toGCThing() const { return value().toGCThing(); }
+ JS::TraceKind traceKind() const { return value().traceKind(); }
+ void* toPrivate() const { return value().toPrivate(); }
+ uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
+
+ uint64_t asRawBits() const { return value().asRawBits(); }
+ JSValueType extractNonDoubleType() const {
+ return value().extractNonDoubleType();
+ }
+ JS::ValueType type() const { return value().type(); }
+
+ JSWhyMagic whyMagic() const { return value().whyMagic(); }
+ uint32_t magicUint32() const { return value().magicUint32(); }
+};
+
+/**
+ * A class designed for CRTP use in implementing all the mutating parts of the
+ * Value interface in Value-like classes. Wrapper must be a class inheriting
+ * MutableWrappedPtrOperations<Wrapper> with visible get() methods returning
+ * const and non-const references to the Value abstracted by Wrapper.
+ */
+template <class Wrapper>
+class MutableWrappedPtrOperations<JS::Value, Wrapper>
+ : public WrappedPtrOperations<JS::Value, Wrapper> {
+ protected:
+ void set(const JS::Value& v) {
+ // Call Wrapper::set to trigger any barriers.
+ static_cast<Wrapper*>(this)->set(v);
+ }
+
+ public:
+ void setNull() { set(JS::NullValue()); }
+ void setUndefined() { set(JS::UndefinedValue()); }
+ void setInt32(int32_t i) { set(JS::Int32Value(i)); }
+ void setDouble(double d) { set(JS::DoubleValue(d)); }
+ void setNaN() { set(JS::NaNValue()); }
+ void setInfinity() { set(JS::InfinityValue()); }
+ void setBoolean(bool b) { set(JS::BooleanValue(b)); }
+ void setMagic(JSWhyMagic why) { set(JS::MagicValue(why)); }
+ template <typename T>
+ void setNumber(T t) {
+ set(JS::NumberValue(t));
+ }
+ void setString(JSString* str) { set(JS::StringValue(str)); }
+ void setSymbol(JS::Symbol* sym) { set(JS::SymbolValue(sym)); }
+ void setBigInt(JS::BigInt* bi) { set(JS::BigIntValue(bi)); }
+ void setObject(JSObject& obj) { set(JS::ObjectValue(obj)); }
+ void setObjectOrNull(JSObject* arg) { set(JS::ObjectOrNullValue(arg)); }
+ void setPrivate(void* ptr) { set(JS::PrivateValue(ptr)); }
+ void setPrivateUint32(uint32_t ui) { set(JS::PrivateUint32Value(ui)); }
+ void setPrivateGCThing(js::gc::Cell* cell) {
+ set(JS::PrivateGCThingValue(cell));
+ }
+};
+
+/*
+ * Augment the generic Heap<T> interface when T = Value with
+ * type-querying, value-extracting, and mutating operations.
+ */
+template <typename Wrapper>
+class HeapBase<JS::Value, Wrapper>
+ : public MutableWrappedPtrOperations<JS::Value, Wrapper> {};
+
+MOZ_HAVE_NORETURN MOZ_COLD MOZ_NEVER_INLINE void ReportBadValueTypeAndCrash(
+ const JS::Value& val);
+
+// If the Value is a GC pointer type, call |f| with the pointer cast to that
+// type and return the result wrapped in a Maybe, otherwise return None().
+template <typename F>
+auto MapGCThingTyped(const JS::Value& val, F&& f) {
+ switch (val.type()) {
+ case JS::ValueType::String: {
+ JSString* str = val.toString();
+ MOZ_ASSERT(gc::IsCellPointerValid(str));
+ return mozilla::Some(f(str));
+ }
+ case JS::ValueType::Object: {
+ JSObject* obj = &val.toObject();
+ MOZ_ASSERT(gc::IsCellPointerValid(obj));
+ return mozilla::Some(f(obj));
+ }
+ case JS::ValueType::Symbol: {
+ JS::Symbol* sym = val.toSymbol();
+ MOZ_ASSERT(gc::IsCellPointerValid(sym));
+ return mozilla::Some(f(sym));
+ }
+ case JS::ValueType::BigInt: {
+ JS::BigInt* bi = val.toBigInt();
+ MOZ_ASSERT(gc::IsCellPointerValid(bi));
+ return mozilla::Some(f(bi));
+ }
+ case JS::ValueType::PrivateGCThing: {
+ MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing()));
+ return mozilla::Some(MapGCThingTyped(val.toGCCellPtr(), std::move(f)));
+ }
+ case JS::ValueType::Double:
+ case JS::ValueType::Int32:
+ case JS::ValueType::Boolean:
+ case JS::ValueType::Undefined:
+ case JS::ValueType::Null:
+ case JS::ValueType::Magic: {
+ MOZ_ASSERT(!val.isGCThing());
+ using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
+ return mozilla::Maybe<ReturnType>();
+ }
+ }
+
+ ReportBadValueTypeAndCrash(val);
+}
+
+// If the Value is a GC pointer type, call |f| with the pointer cast to that
+// type. Return whether this happened.
+template <typename F>
+bool ApplyGCThingTyped(const JS::Value& val, F&& f) {
+ return MapGCThingTyped(val,
+ [&f](auto t) {
+ f(t);
+ return true;
+ })
+ .isSome();
+}
+
+static inline JS::Value PoisonedObjectValue(uintptr_t poison) {
+ JS::Value v;
+ v.setObjectNoCheck(reinterpret_cast<JSObject*>(poison));
+ return v;
+}
+
+} // namespace js
+
+#ifdef DEBUG
+namespace JS {
+
+MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Value& value) {
+ if (value.isGCThing()) {
+ AssertCellIsNotGray(value.toGCThing());
+ }
+}
+
+MOZ_ALWAYS_INLINE void AssertValueIsNotGray(const Heap<Value>& value) {
+ AssertValueIsNotGray(value.unbarrieredGet());
+}
+
+} // namespace JS
+#endif
+
+/************************************************************************/
+
+namespace JS {
+
+extern JS_PUBLIC_DATA const HandleValue NullHandleValue;
+extern JS_PUBLIC_DATA const HandleValue UndefinedHandleValue;
+extern JS_PUBLIC_DATA const HandleValue TrueHandleValue;
+extern JS_PUBLIC_DATA const HandleValue FalseHandleValue;
+extern JS_PUBLIC_DATA const Handle<mozilla::Maybe<Value>> NothingHandleValue;
+
+} // namespace JS
+
+#endif /* js_Value_h */
diff --git a/js/public/ValueArray.h b/js/public/ValueArray.h
new file mode 100644
index 0000000000..0d520fb9b6
--- /dev/null
+++ b/js/public/ValueArray.h
@@ -0,0 +1,130 @@
+/* -*- 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/. */
+
+/** GC-safe representations of consecutive JS::Value in memory. */
+
+#ifndef js_ValueArray_h
+#define js_ValueArray_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_IMPLICIT, MOZ_RAII
+
+#include <stdint.h> // size_t
+
+#include "js/CallArgs.h" // JS::CallArgs
+#include "js/GCVector.h" // JS::RootedVector
+#include "js/RootingAPI.h" // JS::AutoGCRooter, JS::{,Mutable}Handle
+#include "js/Value.h" // JS::Value
+
+namespace js {
+JS_PUBLIC_API void TraceValueArray(JSTracer* trc, size_t length,
+ JS::Value* elements);
+} // namespace js
+
+namespace JS {
+
+/* A fixed-size array of values, for use inside Rooted<>. */
+template <size_t N>
+struct ValueArray {
+ Value elements[N];
+ void trace(JSTracer* trc) { js::TraceValueArray(trc, N, elements); }
+};
+
+/** RootedValueArray roots an internal fixed-size array of Values. */
+template <size_t N>
+using RootedValueArray = Rooted<ValueArray<N>>;
+
+/**
+ * A generic handle to an array of rooted values.
+ *
+ * The rooted array refernced can take several forms, therfore this is not the
+ * same as Handle<js::ValueArray>.
+ */
+class HandleValueArray {
+ const size_t length_;
+ const Value* const elements_;
+
+ HandleValueArray(size_t len, const Value* elements)
+ : length_(len), elements_(elements) {}
+
+ public:
+ explicit HandleValueArray(Handle<Value> value)
+ : length_(1), elements_(value.address()) {}
+
+ MOZ_IMPLICIT HandleValueArray(const RootedVector<Value>& values)
+ : length_(values.length()), elements_(values.begin()) {}
+
+ template <size_t N>
+ MOZ_IMPLICIT HandleValueArray(const RootedValueArray<N>& values)
+ : length_(N), elements_(values.begin()) {}
+
+ /** CallArgs must already be rooted somewhere up the stack. */
+ MOZ_IMPLICIT HandleValueArray(const JS::CallArgs& args)
+ : length_(args.length()), elements_(args.array()) {}
+
+ /** Use with care! Only call this if the data is guaranteed to be marked. */
+ static HandleValueArray fromMarkedLocation(size_t len,
+ const Value* elements) {
+ return HandleValueArray(len, elements);
+ }
+
+ static HandleValueArray subarray(const HandleValueArray& values,
+ size_t startIndex, size_t len) {
+ MOZ_ASSERT(startIndex + len <= values.length());
+ return HandleValueArray(len, values.begin() + startIndex);
+ }
+
+ static HandleValueArray empty() { return HandleValueArray(0, nullptr); }
+
+ size_t length() const { return length_; }
+ const Value* begin() const { return elements_; }
+
+ Handle<Value> operator[](size_t i) const {
+ MOZ_ASSERT(i < length_);
+ return Handle<Value>::fromMarkedLocation(&elements_[i]);
+ }
+};
+
+} // namespace JS
+
+namespace js {
+
+template <size_t N, typename Container>
+class WrappedPtrOperations<JS::ValueArray<N>, Container> {
+ const JS::ValueArray<N>& array() const {
+ return static_cast<const Container*>(this)->get();
+ }
+
+ public:
+ size_t length() const { return N; }
+ const JS::Value* begin() const { return array().elements; }
+
+ JS::HandleValue operator[](size_t i) const {
+ MOZ_ASSERT(i < N);
+ return JS::HandleValue::fromMarkedLocation(&array().elements[i]);
+ }
+};
+
+template <size_t N, typename Container>
+class MutableWrappedPtrOperations<JS::ValueArray<N>, Container>
+ : public WrappedPtrOperations<JS::ValueArray<N>, Container> {
+ using Base = WrappedPtrOperations<JS::ValueArray<N>, Container>;
+ JS::ValueArray<N>& array() { return static_cast<Container*>(this)->get(); }
+
+ public:
+ using Base::begin;
+ JS::Value* begin() { return array().elements; }
+
+ using Base::operator[];
+ JS::MutableHandleValue operator[](size_t i) {
+ MOZ_ASSERT(i < N);
+ return JS::MutableHandleValue::fromMarkedLocation(&array().elements[i]);
+ }
+};
+
+} // namespace js
+
+#endif // js_ValueArray_h
diff --git a/js/public/Vector.h b/js/public/Vector.h
new file mode 100644
index 0000000000..afaa55ca01
--- /dev/null
+++ b/js/public/Vector.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Vector_h
+#define js_Vector_h
+
+#include "mozilla/Vector.h"
+
+#include <type_traits>
+
+#include "js/TypeDecls.h"
+
+namespace js {
+
+class JS_PUBLIC_API TempAllocPolicy;
+
+namespace detail {
+
+template <typename T>
+struct TypeIsGCThing : std::false_type {};
+
+template <>
+struct TypeIsGCThing<JS::Value> : std::true_type {};
+
+} // namespace detail
+
+template <typename T, size_t MinInlineCapacity = 0,
+ class AllocPolicy = TempAllocPolicy,
+ // Don't use this with JS::Value! Use JS::RootedValueVector instead.
+ typename = std::enable_if_t<!detail::TypeIsGCThing<T>::value>>
+using Vector = mozilla::Vector<T, MinInlineCapacity, AllocPolicy>;
+
+} // namespace js
+
+#endif /* js_Vector_h */
diff --git a/js/public/Warnings.h b/js/public/Warnings.h
new file mode 100644
index 0000000000..c00cd099b6
--- /dev/null
+++ b/js/public/Warnings.h
@@ -0,0 +1,98 @@
+/* -*- 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/. */
+
+/*
+ * Functionality for issuing and handling warnings.
+ *
+ * Warnings are situations that aren't inherently full-blown errors (and perhaps
+ * for spec compliance *can't* be), but that may represent dubious programming
+ * practice that embeddings may wish to know about.
+ *
+ * SpiderMonkey recognizes an unspecified set of syntactic patterns and runtime
+ * behaviors as triggering a warning. Embeddings may also recognize and report
+ * additional warnings.
+ */
+
+#ifndef js_Warnings_h
+#define js_Warnings_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_FORMAT_PRINTF, MOZ_RAII
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JS_PUBLIC_API JSContext;
+class JSErrorReport;
+
+namespace JS {
+
+/**
+ * Report a warning represented by the sprintf-like conversion of ASCII format
+ * filled from trailing ASCII arguments.
+ *
+ * Return true iff the warning was successfully reported without reporting an
+ * error (or being upgraded into one).
+ */
+extern JS_PUBLIC_API bool WarnASCII(JSContext* cx, const char* format, ...)
+ MOZ_FORMAT_PRINTF(2, 3);
+
+/**
+ * Report a warning represented by the sprintf-like conversion of Latin-1 format
+ * filled from trailing Latin-1 arguments.
+ *
+ * Return true iff the warning was successfully reported without reporting an
+ * error (or being upgraded into one).
+ */
+extern JS_PUBLIC_API bool WarnLatin1(JSContext* cx, const char* format, ...)
+ MOZ_FORMAT_PRINTF(2, 3);
+
+/**
+ * Report a warning represented by the sprintf-like conversion of UTF-8 format
+ * filled from trailing UTF-8 arguments.
+ *
+ * Return true iff the warning was successfully reported without reporting an
+ * error (or being upgraded into one).
+ */
+extern JS_PUBLIC_API bool WarnUTF8(JSContext* cx, const char* format, ...)
+ MOZ_FORMAT_PRINTF(2, 3);
+
+using WarningReporter = void (*)(JSContext* cx, JSErrorReport* report);
+
+extern JS_PUBLIC_API WarningReporter GetWarningReporter(JSContext* cx);
+
+extern JS_PUBLIC_API WarningReporter
+SetWarningReporter(JSContext* cx, WarningReporter reporter);
+
+/**
+ * A simple RAII class that clears the registered warning reporter on
+ * construction and restores it on destruction.
+ *
+ * A fresh warning reporter *may* be set while an instance of this class is
+ * live, but it must be unset in LIFO fashion by the time that instance is
+ * destroyed.
+ */
+class MOZ_RAII JS_PUBLIC_API AutoSuppressWarningReporter {
+ JSContext* context_;
+ WarningReporter prevReporter_;
+
+ public:
+ explicit AutoSuppressWarningReporter(JSContext* cx) : context_(cx) {
+ prevReporter_ = SetWarningReporter(context_, nullptr);
+ }
+
+ ~AutoSuppressWarningReporter() {
+#ifdef DEBUG
+ WarningReporter reporter =
+#endif
+ SetWarningReporter(context_, prevReporter_);
+ MOZ_ASSERT(reporter == nullptr, "Unexpected WarningReporter active");
+ SetWarningReporter(context_, prevReporter_);
+ }
+};
+
+} // namespace JS
+
+#endif // js_Warnings_h
diff --git a/js/public/WasmModule.h b/js/public/WasmModule.h
new file mode 100644
index 0000000000..a4d386ce61
--- /dev/null
+++ b/js/public/WasmModule.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_WasmModule_h
+#define js_WasmModule_h
+
+#include "mozilla/RefPtr.h" // RefPtr
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RefCounted.h" // AtomicRefCounted
+#include "js/TypeDecls.h" // HandleObject
+
+namespace JS {
+
+/**
+ * The WasmModule interface allows the embedding to hold a reference to the
+ * underying C++ implementation of a JS WebAssembly.Module object for purposes
+ * of efficient postMessage() and (de)serialization from a random thread.
+ *
+ * In particular, this allows postMessage() of a WebAssembly.Module:
+ * GetWasmModule() is called when making a structured clone of a payload
+ * containing a WebAssembly.Module object. The structured clone buffer holds a
+ * refcount of the JS::WasmModule until createObject() is called in the target
+ * agent's JSContext. The new WebAssembly.Module object continues to hold the
+ * JS::WasmModule and thus the final reference of a JS::WasmModule may be
+ * dropped from any thread and so the virtual destructor (and all internal
+ * methods of the C++ module) must be thread-safe.
+ */
+
+struct WasmModule : js::AtomicRefCounted<WasmModule> {
+ virtual ~WasmModule() = default;
+ virtual JSObject* createObject(JSContext* cx) const = 0;
+ virtual JSObject* createObjectForAsmJS(JSContext* cx) const = 0;
+};
+
+extern JS_PUBLIC_API bool IsWasmModuleObject(HandleObject obj);
+
+extern JS_PUBLIC_API RefPtr<WasmModule> GetWasmModule(HandleObject obj);
+
+} // namespace JS
+
+#endif /* js_WasmModule_h */
diff --git a/js/public/WeakMapPtr.h b/js/public/WeakMapPtr.h
new file mode 100644
index 0000000000..0d731e20f0
--- /dev/null
+++ b/js/public/WeakMapPtr.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_WeakMapPtr_h
+#define js_WeakMapPtr_h
+
+#include "jspubtd.h"
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+// A wrapper around the internal C++ representation of SpiderMonkey WeakMaps,
+// usable outside the engine.
+//
+// The supported template specializations are enumerated in gc/WeakMapPtr.cpp.
+// If you want to use this class for a different key/value combination, add it
+// to the list and the compiler will generate the relevant machinery.
+template <typename K, typename V>
+class JS_PUBLIC_API WeakMapPtr {
+ public:
+ WeakMapPtr() : ptr(nullptr) {}
+ bool init(JSContext* cx);
+ bool initialized() { return ptr != nullptr; }
+ void destroy();
+ virtual ~WeakMapPtr() { MOZ_ASSERT(!initialized()); }
+ void trace(JSTracer* tracer);
+
+ V lookup(const K& key);
+ bool put(JSContext* cx, const K& key, const V& value);
+ V removeValue(const K& key);
+
+ private:
+ void* ptr;
+
+ // WeakMapPtr is neither copyable nor assignable.
+ WeakMapPtr(const WeakMapPtr& wmp) = delete;
+ WeakMapPtr& operator=(const WeakMapPtr& wmp) = delete;
+};
+
+} /* namespace JS */
+
+#endif /* js_WeakMapPtr_h */
diff --git a/js/public/Wrapper.h b/js/public/Wrapper.h
new file mode 100644
index 0000000000..66e06c886a
--- /dev/null
+++ b/js/public/Wrapper.h
@@ -0,0 +1,511 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Wrapper_h
+#define js_Wrapper_h
+
+#include "mozilla/Attributes.h"
+
+#include "js/Proxy.h"
+
+namespace js {
+
+/*
+ * Helper for Wrapper::New default options.
+ *
+ * Callers of Wrapper::New() who wish to specify a prototype for the created
+ * Wrapper, *MUST* construct a WrapperOptions with a JSContext.
+ */
+class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions {
+ public:
+ WrapperOptions() : ProxyOptions(false), proto_() {}
+
+ explicit WrapperOptions(JSContext* cx) : ProxyOptions(false), proto_() {
+ proto_.emplace(cx);
+ }
+
+ inline JSObject* proto() const;
+ WrapperOptions& setProto(JSObject* protoArg) {
+ MOZ_ASSERT(proto_);
+ *proto_ = protoArg;
+ return *this;
+ }
+
+ private:
+ mozilla::Maybe<JS::RootedObject> proto_;
+};
+
+// Base class for proxy handlers that want to forward all operations to an
+// object stored in the proxy's private slot.
+class JS_FRIEND_API ForwardingProxyHandler : public BaseProxyHandler {
+ public:
+ using BaseProxyHandler::BaseProxyHandler;
+
+ /* Standard internal methods. */
+ virtual bool getOwnPropertyDescriptor(
+ JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
+ virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result) const override;
+ virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::ObjectOpResult& result) const override;
+ virtual bool enumerate(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool getPrototype(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleObject protop) const override;
+ virtual bool setPrototype(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleObject proto,
+ JS::ObjectOpResult& result) const override;
+ virtual bool getPrototypeIfOrdinary(
+ JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
+ JS::MutableHandleObject protop) const override;
+ virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject proxy,
+ bool* succeeded) const override;
+ virtual bool preventExtensions(JSContext* cx, JS::HandleObject proxy,
+ JS::ObjectOpResult& result) const override;
+ virtual bool isExtensible(JSContext* cx, JS::HandleObject proxy,
+ bool* extensible) const override;
+ virtual bool has(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ bool* bp) const override;
+ virtual bool get(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleValue receiver, JS::HandleId id,
+ JS::MutableHandleValue vp) const override;
+ virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::HandleValue v, JS::HandleValue receiver,
+ JS::ObjectOpResult& result) const override;
+ virtual bool call(JSContext* cx, JS::HandleObject proxy,
+ const JS::CallArgs& args) const override;
+ virtual bool construct(JSContext* cx, JS::HandleObject proxy,
+ const JS::CallArgs& args) const override;
+
+ /* SpiderMonkey extensions. */
+ virtual bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ bool* bp) const override;
+ virtual bool getOwnEnumerablePropertyKeys(
+ JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test,
+ JS::NativeImpl impl,
+ const JS::CallArgs& args) const override;
+ virtual bool hasInstance(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleValue v, bool* bp) const override;
+ virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy,
+ ESClass* cls) const override;
+ virtual bool isArray(JSContext* cx, JS::HandleObject proxy,
+ JS::IsArrayAnswer* answer) const override;
+ virtual const char* className(JSContext* cx,
+ JS::HandleObject proxy) const override;
+ virtual JSString* fun_toString(JSContext* cx, JS::HandleObject proxy,
+ bool isToSource) const override;
+ virtual RegExpShared* regexp_toShared(JSContext* cx,
+ JS::HandleObject proxy) const override;
+ virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleValue vp) const override;
+ virtual bool isCallable(JSObject* obj) const override;
+ virtual bool isConstructor(JSObject* obj) const override;
+
+ // Use the target object for private fields.
+ virtual bool useProxyExpandoObjectForPrivateFields() const override {
+ return false;
+ }
+};
+
+/*
+ * A wrapper is a proxy with a target object to which it generally forwards
+ * operations, but may restrict access to certain operations or augment those
+ * operations in various ways.
+ *
+ * A wrapper can be "unwrapped" in C++, exposing the underlying object.
+ * Callers should be careful to avoid unwrapping security wrappers in the wrong
+ * context.
+ *
+ * Important: If you add a method implementation here, you probably also need
+ * to add an override in CrossCompartmentWrapper. If you don't, you risk
+ * compartment mismatches. See bug 945826 comment 0.
+ */
+class JS_FRIEND_API Wrapper : public ForwardingProxyHandler {
+ unsigned mFlags;
+
+ public:
+ explicit constexpr Wrapper(unsigned aFlags, bool aHasPrototype = false,
+ bool aHasSecurityPolicy = false)
+ : ForwardingProxyHandler(&family, aHasPrototype, aHasSecurityPolicy),
+ mFlags(aFlags) {}
+
+ virtual bool finalizeInBackground(const JS::Value& priv) const override;
+
+ /**
+ * A hook subclasses can override to implement CheckedUnwrapDynamic
+ * behavior. The JSContext represents the "who is trying to unwrap?" Realm.
+ * The JSObject is the wrapper that the caller is trying to unwrap.
+ */
+ virtual bool dynamicCheckedUnwrapAllowed(JS::HandleObject obj,
+ JSContext* cx) const {
+ MOZ_ASSERT(hasSecurityPolicy(), "Why are you asking?");
+ return false;
+ }
+
+ using BaseProxyHandler::Action;
+
+ enum Flags { CROSS_COMPARTMENT = 1 << 0, LAST_USED_FLAG = CROSS_COMPARTMENT };
+
+ static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
+ const WrapperOptions& options = WrapperOptions());
+
+ static JSObject* NewSingleton(
+ JSContext* cx, JSObject* obj, const Wrapper* handler,
+ const WrapperOptions& options = WrapperOptions());
+
+ static JSObject* Renew(JSObject* existing, JSObject* obj,
+ const Wrapper* handler);
+
+ static inline const Wrapper* wrapperHandler(const JSObject* wrapper);
+
+ static JSObject* wrappedObject(JSObject* wrapper);
+
+ unsigned flags() const { return mFlags; }
+
+ bool isCrossCompartmentWrapper() const {
+ return !!(mFlags & CROSS_COMPARTMENT);
+ }
+
+ static const char family;
+ static const Wrapper singleton;
+ static const Wrapper singletonWithPrototype;
+
+ static JSObject* const defaultProto;
+};
+
+inline JSObject* WrapperOptions::proto() const {
+ return proto_ ? *proto_ : Wrapper::defaultProto;
+}
+
+/* Base class for all cross compartment wrapper handlers. */
+class JS_FRIEND_API CrossCompartmentWrapper : public Wrapper {
+ public:
+ explicit constexpr CrossCompartmentWrapper(unsigned aFlags,
+ bool aHasPrototype = false,
+ bool aHasSecurityPolicy = false)
+ : Wrapper(CROSS_COMPARTMENT | aFlags, aHasPrototype, aHasSecurityPolicy) {
+ }
+
+ /* Standard internal methods. */
+ virtual bool getOwnPropertyDescriptor(
+ JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
+ virtual bool defineProperty(JSContext* cx, JS::HandleObject wrapper,
+ JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result) const override;
+ virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject wrapper,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ JS::ObjectOpResult& result) const override;
+ virtual bool enumerate(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool getPrototype(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleObject protop) const override;
+ virtual bool setPrototype(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleObject proto,
+ JS::ObjectOpResult& result) const override;
+
+ virtual bool getPrototypeIfOrdinary(
+ JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
+ JS::MutableHandleObject protop) const override;
+ virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject proxy,
+ bool* succeeded) const override;
+ virtual bool preventExtensions(JSContext* cx, JS::HandleObject wrapper,
+ JS::ObjectOpResult& result) const override;
+ virtual bool isExtensible(JSContext* cx, JS::HandleObject wrapper,
+ bool* extensible) const override;
+ virtual bool has(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ bool* bp) const override;
+ virtual bool get(JSContext* cx, JS::HandleObject wrapper,
+ JS::HandleValue receiver, JS::HandleId id,
+ JS::MutableHandleValue vp) const override;
+ virtual bool set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ JS::HandleValue v, JS::HandleValue receiver,
+ JS::ObjectOpResult& result) const override;
+ virtual bool call(JSContext* cx, JS::HandleObject wrapper,
+ const JS::CallArgs& args) const override;
+ virtual bool construct(JSContext* cx, JS::HandleObject wrapper,
+ const JS::CallArgs& args) const override;
+
+ /* SpiderMonkey extensions. */
+ virtual bool hasOwn(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ bool* bp) const override;
+ virtual bool getOwnEnumerablePropertyKeys(
+ JSContext* cx, JS::HandleObject wrapper,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test,
+ JS::NativeImpl impl,
+ const JS::CallArgs& args) const override;
+ virtual bool hasInstance(JSContext* cx, JS::HandleObject wrapper,
+ JS::MutableHandleValue v, bool* bp) const override;
+ virtual const char* className(JSContext* cx,
+ JS::HandleObject proxy) const override;
+ virtual JSString* fun_toString(JSContext* cx, JS::HandleObject wrapper,
+ bool isToSource) const override;
+ virtual RegExpShared* regexp_toShared(JSContext* cx,
+ JS::HandleObject proxy) const override;
+ virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleValue vp) const override;
+
+ // Allocate CrossCompartmentWrappers in the nursery.
+ virtual bool canNurseryAllocate() const override { return true; }
+
+ static const CrossCompartmentWrapper singleton;
+ static const CrossCompartmentWrapper singletonWithPrototype;
+};
+
+class JS_FRIEND_API OpaqueCrossCompartmentWrapper
+ : public CrossCompartmentWrapper {
+ public:
+ explicit constexpr OpaqueCrossCompartmentWrapper()
+ : CrossCompartmentWrapper(0) {}
+
+ /* Standard internal methods. */
+ virtual bool getOwnPropertyDescriptor(
+ JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
+ virtual bool defineProperty(JSContext* cx, JS::HandleObject wrapper,
+ JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result) const override;
+ virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject wrapper,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ JS::ObjectOpResult& result) const override;
+ virtual bool enumerate(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
+ JS::MutableHandleObject protop) const override;
+ virtual bool setPrototype(JSContext* cx, JS::HandleObject wrapper,
+ JS::HandleObject proto,
+ JS::ObjectOpResult& result) const override;
+ virtual bool getPrototypeIfOrdinary(
+ JSContext* cx, JS::HandleObject wrapper, bool* isOrdinary,
+ JS::MutableHandleObject protop) const override;
+ virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject wrapper,
+ bool* succeeded) const override;
+ virtual bool preventExtensions(JSContext* cx, JS::HandleObject wrapper,
+ JS::ObjectOpResult& result) const override;
+ virtual bool isExtensible(JSContext* cx, JS::HandleObject wrapper,
+ bool* extensible) const override;
+ virtual bool has(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ bool* bp) const override;
+ virtual bool get(JSContext* cx, JS::HandleObject wrapper,
+ JS::HandleValue receiver, JS::HandleId id,
+ JS::MutableHandleValue vp) const override;
+ virtual bool set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ JS::HandleValue v, JS::HandleValue receiver,
+ JS::ObjectOpResult& result) const override;
+ virtual bool call(JSContext* cx, JS::HandleObject wrapper,
+ const JS::CallArgs& args) const override;
+ virtual bool construct(JSContext* cx, JS::HandleObject wrapper,
+ const JS::CallArgs& args) const override;
+
+ /* SpiderMonkey extensions. */
+ virtual bool hasOwn(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ bool* bp) const override;
+ virtual bool getOwnEnumerablePropertyKeys(
+ JSContext* cx, JS::HandleObject wrapper,
+ JS::MutableHandleIdVector props) const override;
+ virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper,
+ ESClass* cls) const override;
+ virtual bool isArray(JSContext* cx, JS::HandleObject obj,
+ JS::IsArrayAnswer* answer) const override;
+ virtual bool hasInstance(JSContext* cx, JS::HandleObject wrapper,
+ JS::MutableHandleValue v, bool* bp) const override;
+ virtual const char* className(JSContext* cx,
+ JS::HandleObject wrapper) const override;
+ virtual JSString* fun_toString(JSContext* cx, JS::HandleObject proxy,
+ bool isToSource) const override;
+
+ static const OpaqueCrossCompartmentWrapper singleton;
+};
+
+/*
+ * Base class for security wrappers. A security wrapper is potentially hiding
+ * all or part of some wrapped object thus SecurityWrapper defaults to denying
+ * access to the wrappee. This is the opposite of Wrapper which tries to be
+ * completely transparent.
+ *
+ * NB: Currently, only a few ProxyHandler operations are overridden to deny
+ * access, relying on derived SecurityWrapper to block access when necessary.
+ */
+template <class Base>
+class JS_FRIEND_API SecurityWrapper : public Base {
+ public:
+ explicit constexpr SecurityWrapper(unsigned flags, bool hasPrototype = false)
+ : Base(flags, hasPrototype, /* hasSecurityPolicy = */ true) {}
+
+ virtual bool enter(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+ Wrapper::Action act, bool mayThrow,
+ bool* bp) const override;
+
+ virtual bool defineProperty(JSContext* cx, JS::HandleObject wrapper,
+ JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result) const override;
+ virtual bool isExtensible(JSContext* cx, JS::HandleObject wrapper,
+ bool* extensible) const override;
+ virtual bool preventExtensions(JSContext* cx, JS::HandleObject wrapper,
+ JS::ObjectOpResult& result) const override;
+ virtual bool setPrototype(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleObject proto,
+ JS::ObjectOpResult& result) const override;
+ virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject proxy,
+ bool* succeeded) const override;
+
+ virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test,
+ JS::NativeImpl impl,
+ const JS::CallArgs& args) const override;
+ virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper,
+ ESClass* cls) const override;
+ virtual bool isArray(JSContext* cx, JS::HandleObject wrapper,
+ JS::IsArrayAnswer* answer) const override;
+ virtual RegExpShared* regexp_toShared(JSContext* cx,
+ JS::HandleObject proxy) const override;
+ virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleValue vp) const override;
+
+ // Allow isCallable and isConstructor. They used to be class-level, and so
+ // could not be guarded against.
+
+ /*
+ * Allow our subclasses to select the superclass behavior they want without
+ * needing to specify an exact superclass.
+ */
+ typedef Base Permissive;
+ typedef SecurityWrapper<Base> Restrictive;
+};
+
+typedef SecurityWrapper<CrossCompartmentWrapper>
+ CrossCompartmentSecurityWrapper;
+
+extern JSObject* TransparentObjectWrapper(JSContext* cx,
+ JS::HandleObject existing,
+ JS::HandleObject obj);
+
+inline bool IsWrapper(const JSObject* obj) {
+ return IsProxy(obj) && GetProxyHandler(obj)->family() == &Wrapper::family;
+}
+
+inline bool IsCrossCompartmentWrapper(const JSObject* obj) {
+ return IsWrapper(obj) &&
+ (Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
+}
+
+/* static */ inline const Wrapper* Wrapper::wrapperHandler(
+ const JSObject* wrapper) {
+ MOZ_ASSERT(IsWrapper(wrapper));
+ return static_cast<const Wrapper*>(GetProxyHandler(wrapper));
+}
+
+// Given a JSObject, returns that object stripped of wrappers. If
+// stopAtWindowProxy is true, then this returns the WindowProxy if it was
+// previously wrapped. Otherwise, this returns the first object for which
+// JSObject::isWrapper returns false.
+//
+// ExposeToActiveJS is called on wrapper targets to allow gray marking
+// assertions to work while an incremental GC is in progress, but this means
+// that this cannot be called from the GC or off the main thread.
+JS_FRIEND_API JSObject* UncheckedUnwrap(JSObject* obj,
+ bool stopAtWindowProxy = true,
+ unsigned* flagsp = nullptr);
+
+// Given a JSObject, returns that object stripped of wrappers, except
+// WindowProxy wrappers. At each stage, the wrapper has the opportunity to veto
+// the unwrap. Null is returned if there are security wrappers that can't be
+// unwrapped.
+//
+// This does a static-only unwrap check: it basically checks whether _all_
+// globals in the wrapper's source compartment should be able to access the
+// wrapper target. This won't necessarily return the right thing for the HTML
+// spec's cross-origin objects (WindowProxy and Location), but is fine to use
+// when failure to unwrap one of those objects wouldn't be a problem. For
+// example, if you want to test whether your target object is a specific class
+// that's not WindowProxy or Location, you can use this.
+//
+// ExposeToActiveJS is called on wrapper targets to allow gray marking
+// assertions to work while an incremental GC is in progress, but this means
+// that this cannot be called from the GC or off the main thread.
+JS_FRIEND_API JSObject* CheckedUnwrapStatic(JSObject* obj);
+
+// Unwrap only the outermost security wrapper, with the same semantics as
+// above. This is the checked version of Wrapper::wrappedObject.
+JS_FRIEND_API JSObject* UnwrapOneCheckedStatic(JSObject* obj);
+
+// Given a JSObject, returns that object stripped of wrappers. At each stage,
+// the security wrapper has the opportunity to veto the unwrap. If
+// stopAtWindowProxy is true, then this returns the WindowProxy if it was
+// previously wrapped. Null is returned if there are security wrappers that
+// can't be unwrapped.
+//
+// ExposeToActiveJS is called on wrapper targets to allow gray marking
+// assertions to work while an incremental GC is in progress, but this means
+// that this cannot be called from the GC or off the main thread.
+//
+// The JSContext argument will be used for dynamic checks (needed by WindowProxy
+// and Location) and should represent the Realm doing the unwrapping. It is not
+// used to throw exceptions; this function never throws.
+//
+// This function may be able to GC (and the static analysis definitely thinks it
+// can), but it still takes a JSObject* argument, because some of its callers
+// would actually have a bit of a hard time producing a Rooted. And it ends up
+// having to root internally anyway, because it wants to use the value in a loop
+// and you can't assign to a HandleObject. What this means is that callers who
+// plan to use the argument object after they have called this function will
+// need to root it to avoid hazard failures, even though this function doesn't
+// require a Handle.
+JS_FRIEND_API JSObject* CheckedUnwrapDynamic(JSObject* obj, JSContext* cx,
+ bool stopAtWindowProxy = true);
+
+// Unwrap only the outermost security wrapper, with the same semantics as
+// above. This is the checked version of Wrapper::wrappedObject.
+JS_FRIEND_API JSObject* UnwrapOneCheckedDynamic(JS::HandleObject obj,
+ JSContext* cx,
+ bool stopAtWindowProxy = true);
+
+// Given a JSObject, returns that object stripped of wrappers. This returns the
+// WindowProxy if it was previously wrapped.
+//
+// ExposeToActiveJS is not called on wrapper targets so this can be called from
+// the GC or off the main thread.
+JS_FRIEND_API JSObject* UncheckedUnwrapWithoutExpose(JSObject* obj);
+
+void ReportAccessDenied(JSContext* cx);
+
+JS_FRIEND_API void NukeCrossCompartmentWrapper(JSContext* cx,
+ JSObject* wrapper);
+
+// If a cross-compartment wrapper source => target exists, nuke it.
+JS_FRIEND_API void NukeCrossCompartmentWrapperIfExists(JSContext* cx,
+ JS::Compartment* source,
+ JSObject* target);
+
+void RemapWrapper(JSContext* cx, JSObject* wobj, JSObject* newTarget);
+void RemapDeadWrapper(JSContext* cx, JS::HandleObject wobj,
+ JS::HandleObject newTarget);
+
+JS_FRIEND_API bool RemapAllWrappersForObject(JSContext* cx,
+ JS::HandleObject oldTarget,
+ JS::HandleObject newTarget);
+
+// API to recompute all cross-compartment wrappers whose source and target
+// match the given filters.
+JS_FRIEND_API bool RecomputeWrappers(JSContext* cx,
+ const CompartmentFilter& sourceFilter,
+ const CompartmentFilter& targetFilter);
+
+} /* namespace js */
+
+#endif /* js_Wrapper_h */
diff --git a/js/public/experimental/CTypes.h b/js/public/experimental/CTypes.h
new file mode 100644
index 0000000000..f7f6bace34
--- /dev/null
+++ b/js/public/experimental/CTypes.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_experimental_CTypes_h
+#define js_experimental_CTypes_h
+
+#include "mozilla/Attributes.h" // MOZ_RAII
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API, JS_FRIEND_API
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+#ifdef JS_HAS_CTYPES
+
+/**
+ * Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes'
+ * object will be sealed.
+ */
+extern JS_PUBLIC_API bool InitCTypesClass(JSContext* cx,
+ Handle<JSObject*> global);
+
+#endif // JS_HAS_CTYPES
+
+/**
+ * The type of ctypes activity that is occurring.
+ */
+enum class CTypesActivityType {
+ BeginCall,
+ EndCall,
+ BeginCallback,
+ EndCallback,
+};
+
+/**
+ * The signature of a function invoked at the leading or trailing edge of ctypes
+ * activity.
+ */
+using CTypesActivityCallback = void (*)(JSContext*, CTypesActivityType);
+
+/**
+ * Sets a callback that is run whenever js-ctypes is about to be used when
+ * calling into C.
+ */
+extern JS_FRIEND_API void SetCTypesActivityCallback(JSContext* cx,
+ CTypesActivityCallback cb);
+
+class MOZ_RAII JS_FRIEND_API AutoCTypesActivityCallback {
+ private:
+ JSContext* cx;
+ CTypesActivityCallback callback;
+ CTypesActivityType endType;
+
+ public:
+ AutoCTypesActivityCallback(JSContext* cx, CTypesActivityType beginType,
+ CTypesActivityType endType);
+
+ ~AutoCTypesActivityCallback() { DoEndCallback(); }
+
+ void DoEndCallback() {
+ if (callback) {
+ callback(cx, endType);
+ callback = nullptr;
+ }
+ }
+};
+
+#ifdef JS_HAS_CTYPES
+
+/**
+ * Convert a unicode string 'source' of length 'slen' to the platform native
+ * charset, returning a null-terminated string allocated with JS_malloc. On
+ * failure, this function should report an error.
+ */
+using CTypesUnicodeToNativeFun = char* (*)(JSContext*, const char16_t*, size_t);
+
+/**
+ * Set of function pointers that ctypes can use for various internal functions.
+ * See JS::SetCTypesCallbacks below. Providing nullptr for a function is safe
+ * and will result in the applicable ctypes functionality not being available.
+ */
+struct CTypesCallbacks {
+ CTypesUnicodeToNativeFun unicodeToNative;
+};
+
+/**
+ * Set the callbacks on the provided 'ctypesObj' object. 'callbacks' should be a
+ * pointer to static data that exists for the lifetime of 'ctypesObj', but it
+ * may safely be altered after calling this function and without having
+ * to call this function again.
+ */
+extern JS_PUBLIC_API void SetCTypesCallbacks(JSObject* ctypesObj,
+ const CTypesCallbacks* callbacks);
+
+#endif // JS_HAS_CTYPES
+
+} // namespace JS
+
+#endif // js_experimental_CTypes_h
diff --git a/js/public/experimental/CodeCoverage.h b/js/public/experimental/CodeCoverage.h
new file mode 100644
index 0000000000..768d702106
--- /dev/null
+++ b/js/public/experimental/CodeCoverage.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_experimental_CodeCoverage_h
+#define js_experimental_CodeCoverage_h
+
+#include "jstypes.h" // JS_FRIEND_API
+#include "js/Utility.h" // JS::UniqueChars
+
+struct JS_PUBLIC_API JSContext;
+
+namespace js {
+
+/**
+ * Enable the collection of lcov code coverage metrics.
+ * Must be called before a runtime is created and before any calls to
+ * GetCodeCoverageSummary.
+ */
+extern JS_FRIEND_API void EnableCodeCoverage();
+
+/**
+ * Generate lcov trace file content for the current realm, and allocate a new
+ * buffer and return the content in it, the size of the newly allocated content
+ * within the buffer would be set to the length out-param. The 'All' variant
+ * will collect data for all realms in the runtime.
+ *
+ * In case of out-of-memory, this function returns nullptr. The length
+ * out-param is undefined on failure.
+ */
+extern JS_FRIEND_API JS::UniqueChars GetCodeCoverageSummary(JSContext* cx,
+ size_t* length);
+extern JS_FRIEND_API JS::UniqueChars GetCodeCoverageSummaryAll(JSContext* cx,
+ size_t* length);
+
+} // namespace js
+
+#endif // js_experimental_CodeCoverage_h
diff --git a/js/public/experimental/Intl.h b/js/public/experimental/Intl.h
new file mode 100644
index 0000000000..052bffb7ed
--- /dev/null
+++ b/js/public/experimental/Intl.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_experimental_Intl_h
+#define js_experimental_Intl_h
+
+#include "jstypes.h" // JS_FRIEND_API
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+/**
+ * Create and add the Intl.MozDateTimeFormat constructor function to the
+ * provided object.
+ *
+ * This custom date/time formatter constructor gives users the ability to
+ * specify a custom format pattern. This pattern is passed *directly* to ICU
+ * with NO SYNTAX PARSING OR VALIDATION WHATSOEVER. ICU appears to have a
+ * modicum of testing of this, and it won't fall over completely if passed bad
+ * input. But the current behavior is entirely under-specified and emphatically
+ * not shippable on the web, and it *must* be fixed before this functionality
+ * can be exposed in the real world. (There are also some questions about
+ * whether the format exposed here is the *right* one to standardize, that will
+ * also need to be resolved to ship this.)
+ *
+ * If JS was built without JS_HAS_INTL_API, this function will throw an
+ * exception.
+ */
+extern JS_FRIEND_API bool AddMozDateTimeFormatConstructor(
+ JSContext* cx, Handle<JSObject*> intl);
+
+/**
+ * Create and add the Intl.MozDisplayNames constructor function to the
+ * provided object. This constructor acts like the standard |Intl.DisplayNames|
+ * but accepts certain additional syntax that isn't standardized to the point of
+ * being shippable.
+ *
+ * If JS was built without JS_HAS_INTL_API, this function will throw an
+ * exception.
+ */
+extern JS_FRIEND_API bool AddMozDisplayNamesConstructor(JSContext* cx,
+ Handle<JSObject*> intl);
+
+} // namespace JS
+
+#endif // js_experimental_Intl_h
diff --git a/js/public/experimental/JitInfo.h b/js/public/experimental/JitInfo.h
new file mode 100644
index 0000000000..1b070021bd
--- /dev/null
+++ b/js/public/experimental/JitInfo.h
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_experimental_JitInfo_h
+#define js_experimental_JitInfo_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint16_t, uint32_t
+
+#include "js/CallArgs.h" // JS::CallArgs, JS::detail::CallArgsBase, JSNative
+#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted
+#include "js/Value.h" // JS::Value, JSValueType
+
+namespace js {
+
+namespace jit {
+
+enum class InlinableNative : uint16_t;
+
+} // namespace jit
+
+} // namespace js
+
+/**
+ * A class, expected to be passed by value, which represents the CallArgs for a
+ * JSJitGetterOp.
+ */
+class JSJitGetterCallArgs : protected JS::MutableHandle<JS::Value> {
+ public:
+ explicit JSJitGetterCallArgs(const JS::CallArgs& args)
+ : JS::MutableHandle<JS::Value>(args.rval()) {}
+
+ explicit JSJitGetterCallArgs(JS::Rooted<JS::Value>* rooted)
+ : JS::MutableHandle<JS::Value>(rooted) {}
+
+ explicit JSJitGetterCallArgs(JS::MutableHandle<JS::Value> handle)
+ : JS::MutableHandle<JS::Value>(handle) {}
+
+ JS::MutableHandle<JS::Value> rval() { return *this; }
+};
+
+/**
+ * A class, expected to be passed by value, which represents the CallArgs for a
+ * JSJitSetterOp.
+ */
+class JSJitSetterCallArgs : protected JS::MutableHandle<JS::Value> {
+ public:
+ explicit JSJitSetterCallArgs(const JS::CallArgs& args)
+ : JS::MutableHandle<JS::Value>(args[0]) {}
+
+ explicit JSJitSetterCallArgs(JS::Rooted<JS::Value>* rooted)
+ : JS::MutableHandle<JS::Value>(rooted) {}
+
+ JS::MutableHandle<JS::Value> operator[](unsigned i) {
+ MOZ_ASSERT(i == 0);
+ return *this;
+ }
+
+ unsigned length() const { return 1; }
+
+ // Add get() or maybe hasDefined() as needed
+};
+
+struct JSJitMethodCallArgsTraits;
+
+/**
+ * A class, expected to be passed by reference, which represents the CallArgs
+ * for a JSJitMethodOp.
+ */
+class JSJitMethodCallArgs
+ : protected JS::detail::CallArgsBase<JS::detail::NoUsedRval> {
+ private:
+ using Base = JS::detail::CallArgsBase<JS::detail::NoUsedRval>;
+ friend struct JSJitMethodCallArgsTraits;
+
+ public:
+ explicit JSJitMethodCallArgs(const JS::CallArgs& args) {
+ argv_ = args.array();
+ argc_ = args.length();
+ }
+
+ JS::MutableHandle<JS::Value> rval() const { return Base::rval(); }
+
+ unsigned length() const { return Base::length(); }
+
+ JS::MutableHandle<JS::Value> operator[](unsigned i) const {
+ return Base::operator[](i);
+ }
+
+ bool hasDefined(unsigned i) const { return Base::hasDefined(i); }
+
+ JSObject& callee() const {
+ // We can't use Base::callee() because that will try to poke at
+ // this->usedRval_, which we don't have.
+ return argv_[-2].toObject();
+ }
+
+ JS::Handle<JS::Value> get(unsigned i) const { return Base::get(i); }
+
+ bool requireAtLeast(JSContext* cx, const char* fnname,
+ unsigned required) const {
+ // Can just forward to Base, since it only needs the length and we
+ // forward that already.
+ return Base::requireAtLeast(cx, fnname, required);
+ }
+};
+
+struct JSJitMethodCallArgsTraits {
+ static constexpr size_t offsetOfArgv = offsetof(JSJitMethodCallArgs, argv_);
+ static constexpr size_t offsetOfArgc = offsetof(JSJitMethodCallArgs, argc_);
+};
+
+using JSJitGetterOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
+ JSJitGetterCallArgs);
+using JSJitSetterOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
+ JSJitSetterCallArgs);
+using JSJitMethodOp = bool (*)(JSContext*, JS::Handle<JSObject*>, void*,
+ const JSJitMethodCallArgs&);
+
+/**
+ * This struct contains metadata passed from the DOM to the JS Engine for JIT
+ * optimizations on DOM property accessors.
+ *
+ * Eventually, this should be made available to general JSAPI users as *not*
+ * experimental and *not* a friend API, but we're not ready to do so yet.
+ */
+class JSJitInfo {
+ public:
+ enum OpType {
+ Getter,
+ Setter,
+ Method,
+ StaticMethod,
+ InlinableNative,
+ IgnoresReturnValueNative,
+ // Must be last
+ OpTypeCount
+ };
+
+ enum ArgType {
+ // Basic types
+ String = (1 << 0),
+ Integer = (1 << 1), // Only 32-bit or less
+ Double = (1 << 2), // Maybe we want to add Float sometime too
+ Boolean = (1 << 3),
+ Object = (1 << 4),
+ Null = (1 << 5),
+
+ // And derived types
+ Numeric = Integer | Double,
+ // Should "Primitive" use the WebIDL definition, which
+ // excludes string and null, or the typical JS one that includes them?
+ Primitive = Numeric | Boolean | Null | String,
+ ObjectOrNull = Object | Null,
+ Any = ObjectOrNull | Primitive,
+
+ // Our sentinel value.
+ ArgTypeListEnd = (1 << 31)
+ };
+
+ static_assert(Any & String, "Any must include String");
+ static_assert(Any & Integer, "Any must include Integer");
+ static_assert(Any & Double, "Any must include Double");
+ static_assert(Any & Boolean, "Any must include Boolean");
+ static_assert(Any & Object, "Any must include Object");
+ static_assert(Any & Null, "Any must include Null");
+
+ /**
+ * An enum that describes what this getter/setter/method aliases. This
+ * determines what things can be hoisted past this call, and if this
+ * call is movable what it can be hoisted past.
+ */
+ enum AliasSet {
+ /**
+ * Alias nothing: a constant value, getting it can't affect any other
+ * values, nothing can affect it.
+ */
+ AliasNone,
+
+ /**
+ * Alias things that can modify the DOM but nothing else. Doing the
+ * call can't affect the behavior of any other function.
+ */
+ AliasDOMSets,
+
+ /**
+ * Alias the world. Calling this can change arbitrary values anywhere
+ * in the system. Most things fall in this bucket.
+ */
+ AliasEverything,
+
+ /** Must be last. */
+ AliasSetCount
+ };
+
+ bool needsOuterizedThisObject() const {
+ return type() != Getter && type() != Setter;
+ }
+
+ bool isTypedMethodJitInfo() const { return isTypedMethod; }
+
+ OpType type() const { return OpType(type_); }
+
+ AliasSet aliasSet() const { return AliasSet(aliasSet_); }
+
+ JSValueType returnType() const { return JSValueType(returnType_); }
+
+ union {
+ JSJitGetterOp getter;
+ JSJitSetterOp setter;
+ JSJitMethodOp method;
+ /** A DOM static method, used for Promise wrappers */
+ JSNative staticMethod;
+ JSNative ignoresReturnValueMethod;
+ };
+
+ static unsigned offsetOfIgnoresReturnValueNative() {
+ return offsetof(JSJitInfo, ignoresReturnValueMethod);
+ }
+
+ union {
+ uint16_t protoID;
+ js::jit::InlinableNative inlinableNative;
+ };
+
+ union {
+ uint16_t depth;
+
+ // Additional opcode for some InlinableNative functions.
+ uint16_t nativeOp;
+ };
+
+ // These fields are carefully packed to take up 4 bytes. If you need more
+ // bits for whatever reason, please see if you can steal bits from existing
+ // fields before adding more members to this structure.
+ static constexpr size_t OpTypeBits = 4;
+ static constexpr size_t AliasSetBits = 4;
+ static constexpr size_t ReturnTypeBits = 8;
+ static constexpr size_t SlotIndexBits = 10;
+
+ /** The OpType that says what sort of function we are. */
+ uint32_t type_ : OpTypeBits;
+
+ /**
+ * The alias set for this op. This is a _minimal_ alias set; in
+ * particular for a method it does not include whatever argument
+ * conversions might do. That's covered by argTypes and runtime
+ * analysis of the actual argument types being passed in.
+ */
+ uint32_t aliasSet_ : AliasSetBits;
+
+ /** The return type tag. Might be JSVAL_TYPE_UNKNOWN. */
+ uint32_t returnType_ : ReturnTypeBits;
+
+ static_assert(OpTypeCount <= (1 << OpTypeBits),
+ "Not enough space for OpType");
+ static_assert(AliasSetCount <= (1 << AliasSetBits),
+ "Not enough space for AliasSet");
+ static_assert((sizeof(JSValueType) * 8) <= ReturnTypeBits,
+ "Not enough space for JSValueType");
+
+ /** Is op fallible? False in setters. */
+ uint32_t isInfallible : 1;
+
+ /**
+ * Is op movable? To be movable the op must
+ * not AliasEverything, but even that might
+ * not be enough (e.g. in cases when it can
+ * throw or is explicitly not movable).
+ */
+ uint32_t isMovable : 1;
+
+ /**
+ * Can op be dead-code eliminated? Again, this
+ * depends on whether the op can throw, in
+ * addition to the alias set.
+ */
+ uint32_t isEliminatable : 1;
+
+ // XXXbz should we have a JSValueType for the type of the member?
+ /**
+ * True if this is a getter that can always
+ * get the value from a slot of the "this" object.
+ */
+ uint32_t isAlwaysInSlot : 1;
+
+ /**
+ * True if this is a getter that can sometimes (if the slot doesn't contain
+ * UndefinedValue()) get the value from a slot of the "this" object.
+ */
+ uint32_t isLazilyCachedInSlot : 1;
+
+ /** True if this is an instance of JSTypedMethodJitInfo. */
+ uint32_t isTypedMethod : 1;
+
+ /**
+ * If isAlwaysInSlot or isSometimesInSlot is true,
+ * the index of the slot to get the value from.
+ * Otherwise 0.
+ */
+ uint32_t slotIndex : SlotIndexBits;
+
+ static constexpr size_t maxSlotIndex = (1 << SlotIndexBits) - 1;
+};
+
+static_assert(sizeof(JSJitInfo) == (sizeof(void*) + 2 * sizeof(uint32_t)),
+ "There are several thousand instances of JSJitInfo stored in "
+ "a binary. Please don't increase its space requirements without "
+ "verifying that there is no other way forward (better packing, "
+ "smaller datatypes for fields, subclassing, etc.).");
+
+struct JSTypedMethodJitInfo {
+ // We use C-style inheritance here, rather than C++ style inheritance
+ // because not all compilers support brace-initialization for non-aggregate
+ // classes. Using C++ style inheritance and constructors instead of
+ // brace-initialization would also force the creation of static
+ // constructors (on some compilers) when JSJitInfo and JSTypedMethodJitInfo
+ // structures are declared. Since there can be several thousand of these
+ // structures present and we want to have roughly equivalent performance
+ // across a range of compilers, we do things manually.
+ JSJitInfo base;
+
+ const JSJitInfo::ArgType* const argTypes; /* For a method, a list of sets of
+ types that the function
+ expects. This can be used,
+ for example, to figure out
+ when argument coercions can
+ have side-effects. */
+};
+
+#endif // js_experimental_JitInfo_h
diff --git a/js/public/experimental/SourceHook.h b/js/public/experimental/SourceHook.h
new file mode 100644
index 0000000000..a908800dfd
--- /dev/null
+++ b/js/public/experimental/SourceHook.h
@@ -0,0 +1,99 @@
+/* -*- 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 context-wide source hook allows the source of scripts/functions to be
+ * discarded, if that source is constant and readily-reloadable if it's needed
+ * in the future.
+ *
+ * Ordinarily, functions and scripts store a copy of their underlying source, to
+ * support |Function.prototype.toString| and debuggers. Some scripts, however,
+ * might be constant and retrievable on demand -- perhaps burned into the binary
+ * or in a readonly file provided by the embedding. Why not just ask the
+ * embedding for a copy of the source?
+ *
+ * The context-wide |SourceHook| gives embedders a way to respond to these
+ * requests. The source of scripts/functions compiled with the compile option
+ * |JS::CompileOptions::setSourceIsLazy(true)| is eligible to be discarded.
+ * (The exact conditions under which source is discarded are unspecified.) *If*
+ * source is discarded, performing an operation that requires source uses the
+ * source hook to load the source.
+ *
+ * The source hook must return the *exact* same source for every call. (This is
+ * why the source hook is unsuitable for use with scripts loaded from the web:
+ * in general, their contents can change over time.) If the source hook doesn't
+ * return the exact same source, Very Bad Things may happen. (For example,
+ * previously-valid indexes into the source will no longer be coherent: they
+ * might index out of bounds, into the middle of multi-unit code points, &c.)
+ *
+ * These APIs are experimental because they shouldn't provide a per-*context*
+ * mechanism, rather something that's per-compilation.
+ */
+
+#ifndef js_experimental_SourceHook_h
+#define js_experimental_SourceHook_h
+
+#include "mozilla/UniquePtr.h" // mozilla::UniquePtr
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_FRIEND_API
+
+struct JS_PUBLIC_API JSContext;
+
+namespace js {
+
+/**
+ * A class of objects that return source code on demand.
+ *
+ * When code is compiled with setSourceIsLazy(true), SpiderMonkey doesn't
+ * retain the source code (and doesn't do lazy bytecode generation). If we ever
+ * need the source code, say, in response to a call to Function.prototype.
+ * toSource or Debugger.Source.prototype.text, then we call the 'load' member
+ * function of the instance of this class that has hopefully been registered
+ * with the runtime, passing the code's URL, and hope that it will be able to
+ * find the source.
+ */
+class SourceHook {
+ public:
+ virtual ~SourceHook() = default;
+
+ /**
+ * Attempt to load the source for |filename|.
+ *
+ * On success, return true and store an owning pointer to the UTF-8 or UTF-16
+ * contents of the file in whichever of |twoByteSource| or |utf8Source| is
+ * non-null. (Exactly one of these will be non-null.) If the stored pointer
+ * is non-null, source was loaded and must be |js_free|'d when it's no longer
+ * needed. If the stored pointer is null, the JS engine will simply act as if
+ * source was unavailable, and users like |Function.prototype.toString| will
+ * produce fallback results, e.g. "[native code]".
+ *
+ * On failure, return false. The contents of whichever of |twoByteSource| or
+ * |utf8Source| was initially non-null are unspecified and must not be
+ * |js_free|'d.
+ */
+ virtual bool load(JSContext* cx, const char* filename,
+ char16_t** twoByteSource, char** utf8Source,
+ size_t* length) = 0;
+};
+
+/**
+ * Have |cx| use |hook| to retrieve lazily-retrieved source code. See the
+ * comments for SourceHook. The context takes ownership of the hook, and
+ * will delete it when the context itself is deleted, or when a new hook is
+ * set.
+ */
+extern JS_FRIEND_API void SetSourceHook(JSContext* cx,
+ mozilla::UniquePtr<SourceHook> hook);
+
+/** Remove |cx|'s source hook, and return it. The caller now owns the hook. */
+extern JS_FRIEND_API mozilla::UniquePtr<SourceHook> ForgetSourceHook(
+ JSContext* cx);
+
+} // namespace js
+
+#endif // js_experimental_SourceHook_h
diff --git a/js/public/experimental/TypedData.h b/js/public/experimental/TypedData.h
new file mode 100644
index 0000000000..405d5dfe55
--- /dev/null
+++ b/js/public/experimental/TypedData.h
@@ -0,0 +1,422 @@
+/* -*- 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/. */
+
+/*
+ * Typed array, ArrayBuffer, and DataView creation, predicate, and accessor
+ * functions.
+ */
+
+#ifndef js_experimental_TypedData_h
+#define js_experimental_TypedData_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_CRASH
+#include "mozilla/Casting.h" // mozilla::AssertedCast
+
+#include <stddef.h> // size_t
+#include <stdint.h> // {,u}int8_t, {,u}int16_t, {,u}int32_t
+
+#include "jstypes.h" // JS_FRIEND_API
+
+#include "js/Object.h" // JS::GetClass, JS::GetPrivate, JS::GetReservedSlot
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/ScalarType.h" // js::Scalar::Type
+
+struct JSClass;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API AutoRequireNoGC;
+
+} // namespace JS
+
+/*
+ * Create a new typed array with nelements elements.
+ *
+ * These functions (except the WithBuffer variants) fill in the array with
+ * zeros.
+ */
+
+extern JS_FRIEND_API JSObject* JS_NewInt8Array(JSContext* cx,
+ uint32_t nelements);
+extern JS_FRIEND_API JSObject* JS_NewUint8Array(JSContext* cx,
+ uint32_t nelements);
+extern JS_FRIEND_API JSObject* JS_NewUint8ClampedArray(JSContext* cx,
+ uint32_t nelements);
+extern JS_FRIEND_API JSObject* JS_NewInt16Array(JSContext* cx,
+ uint32_t nelements);
+extern JS_FRIEND_API JSObject* JS_NewUint16Array(JSContext* cx,
+ uint32_t nelements);
+extern JS_FRIEND_API JSObject* JS_NewInt32Array(JSContext* cx,
+ uint32_t nelements);
+extern JS_FRIEND_API JSObject* JS_NewUint32Array(JSContext* cx,
+ uint32_t nelements);
+extern JS_FRIEND_API JSObject* JS_NewFloat32Array(JSContext* cx,
+ uint32_t nelements);
+extern JS_FRIEND_API JSObject* JS_NewFloat64Array(JSContext* cx,
+ uint32_t nelements);
+
+/*
+ * Create a new typed array and copy in values from the given object. The
+ * object is used as if it were an array; that is, the new array (if
+ * successfully created) will have length given by array.length, and its
+ * elements will be those specified by array[0], array[1], and so on, after
+ * conversion to the typed array element type.
+ */
+
+extern JS_FRIEND_API JSObject* JS_NewInt8ArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+extern JS_FRIEND_API JSObject* JS_NewUint8ArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+extern JS_FRIEND_API JSObject* JS_NewUint8ClampedArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+extern JS_FRIEND_API JSObject* JS_NewInt16ArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+extern JS_FRIEND_API JSObject* JS_NewUint16ArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+extern JS_FRIEND_API JSObject* JS_NewInt32ArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+extern JS_FRIEND_API JSObject* JS_NewUint32ArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+extern JS_FRIEND_API JSObject* JS_NewFloat32ArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+extern JS_FRIEND_API JSObject* JS_NewFloat64ArrayFromArray(
+ JSContext* cx, JS::Handle<JSObject*> array);
+
+/*
+ * Create a new typed array using the given ArrayBuffer or
+ * SharedArrayBuffer for storage. The length value is optional; if -1
+ * is passed, enough elements to use up the remainder of the byte
+ * array is used as the default value.
+ */
+
+extern JS_FRIEND_API JSObject* JS_NewInt8ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewUint8ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewUint8ClampedArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewInt16ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewUint16ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewInt32ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewUint32ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewBigInt64ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewBigUint64ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewFloat32ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+extern JS_FRIEND_API JSObject* JS_NewFloat64ArrayWithBuffer(
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset,
+ int64_t length);
+
+/**
+ * Check whether obj supports JS_GetTypedArray* APIs. Note that this may return
+ * false if a security wrapper is encountered that denies the unwrapping. If
+ * this test or one of the JS_Is*Array tests succeeds, then it is safe to call
+ * the various accessor JSAPI calls defined below.
+ */
+extern JS_FRIEND_API bool JS_IsTypedArrayObject(JSObject* obj);
+
+/**
+ * Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may
+ * return false if a security wrapper is encountered that denies the
+ * unwrapping. If this test or one of the more specific tests succeeds, then it
+ * is safe to call the various ArrayBufferView accessor JSAPI calls defined
+ * below.
+ */
+extern JS_FRIEND_API bool JS_IsArrayBufferViewObject(JSObject* obj);
+
+/*
+ * Test for specific typed array types (ArrayBufferView subtypes)
+ */
+
+extern JS_FRIEND_API bool JS_IsInt8Array(JSObject* obj);
+extern JS_FRIEND_API bool JS_IsUint8Array(JSObject* obj);
+extern JS_FRIEND_API bool JS_IsUint8ClampedArray(JSObject* obj);
+extern JS_FRIEND_API bool JS_IsInt16Array(JSObject* obj);
+extern JS_FRIEND_API bool JS_IsUint16Array(JSObject* obj);
+extern JS_FRIEND_API bool JS_IsInt32Array(JSObject* obj);
+extern JS_FRIEND_API bool JS_IsUint32Array(JSObject* obj);
+extern JS_FRIEND_API bool JS_IsFloat32Array(JSObject* obj);
+extern JS_FRIEND_API bool JS_IsFloat64Array(JSObject* obj);
+
+/**
+ * Return the isShared flag of a typed array, which denotes whether
+ * the underlying buffer is a SharedArrayBuffer.
+ *
+ * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is a typed array or a wrapper of
+ * a typed array, and the unwrapping will succeed.
+ */
+extern JS_FRIEND_API bool JS_GetTypedArraySharedness(JSObject* obj);
+
+/*
+ * Test for specific typed array types (ArrayBufferView subtypes) and return
+ * the unwrapped object if so, else nullptr. Never throws.
+ */
+
+namespace js {
+
+extern JS_FRIEND_API JSObject* UnwrapInt8Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapUint8Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapUint8ClampedArray(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapInt16Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapUint16Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapInt32Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapUint32Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapBigInt64Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapBigUint64Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapFloat32Array(JSObject* obj);
+extern JS_FRIEND_API JSObject* UnwrapFloat64Array(JSObject* obj);
+
+extern JS_FRIEND_API JSObject* UnwrapArrayBufferView(JSObject* obj);
+
+extern JS_FRIEND_API JSObject* UnwrapReadableStream(JSObject* obj);
+
+namespace detail {
+
+extern JS_FRIEND_DATA const JSClass* const Int8ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const Uint8ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const Uint8ClampedArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const Int16ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const Uint16ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const Int32ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const Uint32ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const BigInt64ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const BigUint64ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const Float32ArrayClassPtr;
+extern JS_FRIEND_DATA const JSClass* const Float64ArrayClassPtr;
+
+const size_t TypedArrayLengthSlot = 1;
+
+} // namespace detail
+
+#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type) \
+ inline void Get##Type##ArrayLengthAndData( \
+ JSObject* obj, uint32_t* length, bool* isSharedMemory, type** data) { \
+ MOZ_ASSERT(JS::GetClass(obj) == detail::Type##ArrayClassPtr); \
+ const JS::Value& lenSlot = \
+ JS::GetReservedSlot(obj, detail::TypedArrayLengthSlot); \
+ *length = mozilla::AssertedCast<uint32_t>(size_t(lenSlot.toPrivate())); \
+ *isSharedMemory = JS_GetTypedArraySharedness(obj); \
+ *data = static_cast<type*>(JS::GetPrivate(obj)); \
+ }
+
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double)
+
+#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR
+
+// This one isn't inlined because it's rather tricky (by dint of having to deal
+// with a dozen-plus classes and varying slot layouts.
+extern JS_FRIEND_API void GetArrayBufferViewLengthAndData(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ uint8_t** data);
+
+} // namespace js
+
+/*
+ * Unwrap Typed arrays all at once. Return nullptr without throwing if the
+ * object cannot be viewed as the correct typed array, or the typed array
+ * object on success, filling both outparameters.
+ */
+extern JS_FRIEND_API JSObject* JS_GetObjectAsInt8Array(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ int8_t** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsUint8Array(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ uint8_t** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsUint8ClampedArray(
+ JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsInt16Array(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ int16_t** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsUint16Array(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ uint16_t** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsInt32Array(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ int32_t** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsUint32Array(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ uint32_t** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsFloat32Array(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ float** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsFloat64Array(JSObject* obj,
+ uint32_t* length,
+ bool* isSharedMemory,
+ double** data);
+extern JS_FRIEND_API JSObject* JS_GetObjectAsArrayBufferView(
+ JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data);
+
+/*
+ * Get the type of elements in a typed array, or MaxTypedArrayViewType if a
+ * DataView.
+ *
+ * |obj| must have passed a JS_IsArrayBufferView/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is an ArrayBufferView or a
+ * wrapper of an ArrayBufferView, and the unwrapping will succeed.
+ */
+extern JS_FRIEND_API js::Scalar::Type JS_GetArrayBufferViewType(JSObject* obj);
+
+/**
+ * Return the number of elements in a typed array.
+ *
+ * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is a typed array or a wrapper of
+ * a typed array, and the unwrapping will succeed.
+ */
+extern JS_FRIEND_API uint32_t JS_GetTypedArrayLength(JSObject* obj);
+
+/**
+ * Return the byte offset from the start of an ArrayBuffer to the start of a
+ * typed array view.
+ *
+ * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is a typed array or a wrapper of
+ * a typed array, and the unwrapping will succeed.
+ */
+extern JS_FRIEND_API uint32_t JS_GetTypedArrayByteOffset(JSObject* obj);
+
+/**
+ * Return the byte length of a typed array.
+ *
+ * |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
+ * be known that it would pass such a test: it is a typed array or a wrapper of
+ * a typed array, and the unwrapping will succeed.
+ */
+extern JS_FRIEND_API uint32_t JS_GetTypedArrayByteLength(JSObject* obj);
+
+/**
+ * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well
+ */
+extern JS_FRIEND_API uint32_t JS_GetArrayBufferViewByteLength(JSObject* obj);
+
+/**
+ * More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well
+ */
+extern JS_FRIEND_API uint32_t JS_GetArrayBufferViewByteOffset(JSObject* obj);
+
+/*
+ * Return a pointer to the start of the data referenced by a typed array. The
+ * data is still owned by the typed array, and should not be modified on
+ * another thread. Furthermore, the pointer can become invalid on GC (if the
+ * data is small and fits inside the array's GC header), so callers must take
+ * care not to hold on across anything that could GC.
+ *
+ * |obj| must have passed a JS_Is*Array test, or somehow be known that it would
+ * pass such a test: it is a typed array or a wrapper of a typed array, and the
+ * unwrapping will succeed.
+ *
+ * |*isSharedMemory| will be set to true if the typed array maps a
+ * SharedArrayBuffer, otherwise to false.
+ */
+
+extern JS_FRIEND_API int8_t* JS_GetInt8ArrayData(JSObject* obj,
+ bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+extern JS_FRIEND_API uint8_t* JS_GetUint8ArrayData(JSObject* obj,
+ bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+extern JS_FRIEND_API uint8_t* JS_GetUint8ClampedArrayData(
+ JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
+extern JS_FRIEND_API int16_t* JS_GetInt16ArrayData(JSObject* obj,
+ bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+extern JS_FRIEND_API uint16_t* JS_GetUint16ArrayData(
+ JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
+extern JS_FRIEND_API int32_t* JS_GetInt32ArrayData(JSObject* obj,
+ bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+extern JS_FRIEND_API uint32_t* JS_GetUint32ArrayData(
+ JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
+extern JS_FRIEND_API float* JS_GetFloat32ArrayData(JSObject* obj,
+ bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+extern JS_FRIEND_API double* JS_GetFloat64ArrayData(JSObject* obj,
+ bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+
+/**
+ * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific
+ * versions when possible.
+ */
+extern JS_FRIEND_API void* JS_GetArrayBufferViewData(
+ JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
+
+/**
+ * Return a "fixed" pointer (one that will not move during a GC) to the
+ * ArrayBufferView's data. Note that this will not keep the object alive; the
+ * holding object should be rooted or traced. If the view is storing the data
+ * inline, this will copy the data to the provided buffer, returning nullptr if
+ * bufSize is inadequate.
+ *
+ * Avoid using this unless necessary. JS_GetArrayBufferViewData is simpler and
+ * more efficient because it requires the caller to ensure that a GC will not
+ * occur and thus does not need to handle movable data.
+ */
+extern JS_FRIEND_API uint8_t* JS_GetArrayBufferViewFixedData(JSObject* obj,
+ uint8_t* buffer,
+ size_t bufSize);
+
+/**
+ * If the bufSize passed to JS_GetArrayBufferViewFixedData is at least this
+ * many bytes, then any copied data is guaranteed to fit into the provided
+ * buffer.
+ */
+extern JS_FRIEND_API size_t JS_MaxMovableTypedArraySize();
+
+/**
+ * Return the ArrayBuffer or SharedArrayBuffer underlying an ArrayBufferView.
+ * This may return a detached buffer. |obj| must be an object that would
+ * return true for JS_IsArrayBufferViewObject().
+ */
+extern JS_FRIEND_API JSObject* JS_GetArrayBufferViewBuffer(
+ JSContext* cx, JS::Handle<JSObject*> obj, bool* isSharedMemory);
+
+/**
+ * Create a new DataView using the given buffer for storage. The given buffer
+ * must be an ArrayBuffer or SharedArrayBuffer (or a cross-compartment wrapper
+ * of either type), and the offset and length must fit within the bounds of the
+ * buffer. Currently, nullptr will be returned and an exception will be thrown
+ * if these conditions do not hold, but do not depend on that behavior.
+ */
+JS_FRIEND_API JSObject* JS_NewDataView(JSContext* cx,
+ JS::Handle<JSObject*> buffer,
+ size_t byteOffset, size_t byteLength);
+
+#endif // js_experimental_TypedData_h
diff --git a/js/public/friend/DOMProxy.h b/js/public/friend/DOMProxy.h
new file mode 100644
index 0000000000..e009550138
--- /dev/null
+++ b/js/public/friend/DOMProxy.h
@@ -0,0 +1,91 @@
+/* -*- 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/. */
+
+/*
+ * Specify information about DOMProxy proxies in the DOM, for use by ICs.
+ *
+ * Embedders who don't need to define particularly high-performance proxies that
+ * can have random properties added to them can ignore this header.
+ */
+
+#ifndef js_friend_DOMProxy_h
+#define js_friend_DOMProxy_h
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint64_t
+
+#include "jstypes.h" // JS_FRIEND_API
+
+#include "js/Id.h" // JS::PropertyKey
+#include "js/RootingAPI.h" // JS::Handle, JS::Heap
+#include "js/Value.h" // JS::UndefinedValue, JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+/*
+ * The DOMProxyShadowsCheck function will be called to check if the property for
+ * id should be gotten from the prototype, or if there is an own property that
+ * shadows it.
+ * * If ShadowsViaDirectExpando is returned, then the slot at
+ * listBaseExpandoSlot contains an expando object which has the property in
+ * question.
+ * * If ShadowsViaIndirectExpando is returned, then the slot at
+ * listBaseExpandoSlot contains a private pointer to an ExpandoAndGeneration
+ * and the expando object in the ExpandoAndGeneration has the property in
+ * question.
+ * * If DoesntShadow is returned then the slot at listBaseExpandoSlot should
+ * either be undefined or point to an expando object that would contain the
+ * own property.
+ * * If DoesntShadowUnique is returned then the slot at listBaseExpandoSlot
+ * should contain a private pointer to a ExpandoAndGeneration, which contains
+ * a JS::Value that should either be undefined or point to an expando object,
+ * and a uint64 value. If that value changes then the IC for getting a
+ * property will be invalidated.
+ * * If Shadows is returned, that means the property is an own property of the
+ * proxy but doesn't live on the expando object.
+ */
+
+struct ExpandoAndGeneration {
+ ExpandoAndGeneration() : expando(JS::UndefinedValue()), generation(0) {}
+
+ void OwnerUnlinked() { ++generation; }
+
+ static constexpr size_t offsetOfExpando() {
+ return offsetof(ExpandoAndGeneration, expando);
+ }
+
+ static constexpr size_t offsetOfGeneration() {
+ return offsetof(ExpandoAndGeneration, generation);
+ }
+
+ Heap<Value> expando;
+ uint64_t generation;
+};
+
+enum class DOMProxyShadowsResult {
+ ShadowCheckFailed,
+ Shadows,
+ DoesntShadow,
+ DoesntShadowUnique,
+ ShadowsViaDirectExpando,
+ ShadowsViaIndirectExpando
+};
+
+using DOMProxyShadowsCheck = DOMProxyShadowsResult (*)(JSContext*,
+ Handle<JSObject*>,
+ Handle<JS::PropertyKey>);
+
+extern JS_FRIEND_API void SetDOMProxyInformation(
+ const void* domProxyHandlerFamily,
+ DOMProxyShadowsCheck domProxyShadowsCheck,
+ const void* domRemoteProxyHandlerFamily);
+
+} // namespace JS
+
+#endif // js_friend_DOMProxy_h
diff --git a/js/public/friend/DumpFunctions.h b/js/public/friend/DumpFunctions.h
new file mode 100644
index 0000000000..6582de7ca5
--- /dev/null
+++ b/js/public/friend/DumpFunctions.h
@@ -0,0 +1,113 @@
+/* -*- 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/. */
+
+/* Functions to print out values during debugging. */
+
+#ifndef js_friend_DumpFunctions_h
+#define js_friend_DumpFunctions_h
+
+#include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
+
+#include <stddef.h> // size_t
+#include <stdio.h> // FILE
+
+#include "jstypes.h" // JS_FRIEND_API
+
+#include "js/Utility.h" // JS::UniqueChars
+
+class JS_PUBLIC_API JSAtom;
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+class JS_PUBLIC_API JSScript;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+
+struct JS_PUBLIC_API PropertyKey;
+class JS_PUBLIC_API Value;
+
+} // namespace JS
+
+namespace js {
+
+class InterpreterFrame;
+
+} // namespace js
+
+namespace JS {
+
+/** Exposed for DumpJSStack */
+extern JS_FRIEND_API JS::UniqueChars FormatStackDump(JSContext* cx,
+ bool showArgs,
+ bool showLocals,
+ bool showThisProps);
+
+} // namespace JS
+
+namespace js {
+
+/*
+ * These functions are FRIEND_API to help the debugger find them and to support
+ * temporarily hacking js::Dump* calls into other code. Note that there are
+ * overloads that do not require the FILE* parameter, which will default to
+ * stderr.
+ *
+ * These functions are no-ops unless built with DEBUG or JS_JITSPEW.
+ */
+
+extern JS_FRIEND_API void DumpString(JSString* str, FILE* fp);
+
+extern JS_FRIEND_API void DumpAtom(JSAtom* atom, FILE* fp);
+
+extern JS_FRIEND_API void DumpObject(JSObject* obj, FILE* fp);
+
+extern JS_FRIEND_API void DumpChars(const char16_t* s, size_t n, FILE* fp);
+
+extern JS_FRIEND_API void DumpValue(const JS::Value& val, FILE* fp);
+
+extern JS_FRIEND_API void DumpId(JS::PropertyKey id, FILE* fp);
+
+extern JS_FRIEND_API bool DumpPC(JSContext* cx, FILE* fp);
+
+extern JS_FRIEND_API bool DumpScript(JSContext* cx, JSScript* scriptArg,
+ FILE* fp);
+
+// Versions for use directly in a debugger (default parameters are not handled
+// well in gdb; built-in handles like stderr are not handled well in lldb.)
+extern JS_FRIEND_API void DumpString(JSString* str);
+extern JS_FRIEND_API void DumpAtom(JSAtom* atom);
+extern JS_FRIEND_API void DumpObject(JSObject* obj);
+extern JS_FRIEND_API void DumpChars(const char16_t* s, size_t n);
+extern JS_FRIEND_API void DumpValue(const JS::Value& val);
+extern JS_FRIEND_API void DumpId(JS::PropertyKey id);
+extern JS_FRIEND_API void DumpInterpreterFrame(
+ JSContext* cx, InterpreterFrame* start = nullptr);
+extern JS_FRIEND_API bool DumpPC(JSContext* cx);
+extern JS_FRIEND_API bool DumpScript(JSContext* cx, JSScript* scriptArg);
+
+// DumpBacktrace(), unlike the other dump functions, always dumps a backtrace --
+// regardless of DEBUG or JS_JITSPEW.
+
+extern JS_FRIEND_API void DumpBacktrace(JSContext* cx, FILE* fp);
+
+extern JS_FRIEND_API void DumpBacktrace(JSContext* cx);
+
+enum DumpHeapNurseryBehaviour {
+ CollectNurseryBeforeDump,
+ IgnoreNurseryObjects
+};
+
+/**
+ * Dump the complete object graph of heap-allocated things.
+ * fp is the file for the dump output.
+ */
+extern JS_FRIEND_API void DumpHeap(
+ JSContext* cx, FILE* fp, DumpHeapNurseryBehaviour nurseryBehaviour,
+ mozilla::MallocSizeOf mallocSizeOf = nullptr);
+
+} // namespace js
+
+#endif // js_friend_DumpFunctions_h
diff --git a/js/public/friend/ErrorMessages.h b/js/public/friend/ErrorMessages.h
new file mode 100644
index 0000000000..b9ea939195
--- /dev/null
+++ b/js/public/friend/ErrorMessages.h
@@ -0,0 +1,47 @@
+/* -*- 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/. */
+
+/*
+ * SpiderMonkey internal error numbering and error-formatting functionality
+ * (also for warnings).
+ *
+ * This functionality is moderately stable. JSErrNum and js::GetErrorMessage
+ * are widely used inside SpiderMonkey, and Gecko uses them to produce errors
+ * identical to those SpiderMonkey itself would produce, in various situations.
+ * However, the set of error numbers is not stable, error number values are not
+ * stable, error types are not stable, etc. Use your own error reporting code
+ * if you can.
+ */
+
+#ifndef js_friend_ErrorMessages_h
+#define js_friend_ErrorMessages_h
+
+#include "jstypes.h" // JS_FRIEND_API
+
+struct JSErrorFormatString;
+
+enum JSErrNum {
+#define MSG_DEF(name, count, exception, format) name,
+#include "js/friend/ErrorNumbers.msg"
+#undef MSG_DEF
+ JSErr_Limit
+};
+
+namespace js {
+
+/**
+ * A JSErrorCallback suitable for passing to |JS_ReportErrorNumberASCII| and
+ * similar functions in concert with one of the |JSErrNum| error numbers.
+ *
+ * This function is a function only of |errorNumber|: |userRef| and ambient
+ * state have no effect on its behavior.
+ */
+extern JS_FRIEND_API const JSErrorFormatString* GetErrorMessage(
+ void* userRef, unsigned errorNumber);
+
+} // namespace js
+
+#endif // js_friend_ErrorMessages_h
diff --git a/js/public/friend/ErrorNumbers.msg b/js/public/friend/ErrorNumbers.msg
new file mode 100644
index 0000000000..69c5181912
--- /dev/null
+++ b/js/public/friend/ErrorNumbers.msg
@@ -0,0 +1,782 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * SpiderMonkey error messages.
+ *
+ * These are largely for internal use, but they're exposed publicly as a
+ * "friend" interface for embedders that want to replicate SpiderMonkey's exact
+ * error message behavior in particular circumstances. All names, arities,
+ * types, and messages are subject to change or removal. However, the
+ * longer-lived the error message, the less likely it is to change.
+ */
+
+/*
+ * This is the JavaScript error message file.
+ *
+ * The format for each JS error message is:
+ *
+ * MSG_DEF(<SYMBOLIC_NAME>, <ARGUMENT_COUNT>, <EXCEPTION_NAME>,
+ * <FORMAT_STRING>)
+ *
+ * where ;
+ * <SYMBOLIC_NAME> is a legal C identifer that will be used in the
+ * JS engine source.
+ *
+ * <ARGUMENT_COUNT> is an integer literal specifying the total number of
+ * replaceable arguments in the following format string.
+ *
+ * <EXCEPTION_NAME> is an enum JSExnType value, defined in js/ErrorReport.h.
+ *
+ * <FORMAT_STRING> is a string literal, optionally containing sequences
+ * {X} where X is an integer representing the argument number that will
+ * be replaced with a string value when the error is reported.
+ *
+ * e.g.
+ *
+ * MSG_DEF(JSMSG_NOT_A_SUBSPECIES, 2, JSEXN_TYPEERROR,
+ * "{0} is not a member of the {1} family")
+ *
+ * can be used:
+ *
+ * JS_ReportErrorNumberASCII(JSMSG_NOT_A_SUBSPECIES, "Rhino", "Monkey");
+ *
+ * to report:
+ *
+ * "TypeError: Rhino is not a member of the Monkey family"
+ */
+
+// clang-format off
+MSG_DEF(JSMSG_NOT_AN_ERROR, 0, JSEXN_ERR, "<Error #0 is reserved>")
+MSG_DEF(JSMSG_NOT_DEFINED, 1, JSEXN_REFERENCEERR, "{0} is not defined")
+MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 4, JSEXN_TYPEERR, "{0}: At least {1} argument{2} required, but only {3} passed")
+MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}")
+MSG_DEF(JSMSG_INCOMPATIBLE_PROTO2, 3, JSEXN_TYPEERR, "{0}.prototype[{1}] called on incompatible {2}")
+MSG_DEF(JSMSG_NO_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} has no constructor")
+MSG_DEF(JSMSG_BAD_SORT_ARG, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")
+MSG_DEF(JSMSG_READ_ONLY, 1, JSEXN_TYPEERR, "{0} is read-only")
+MSG_DEF(JSMSG_CANT_DELETE, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
+MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY, 0, JSEXN_TYPEERR, "can't delete non-configurable array element")
+MSG_DEF(JSMSG_NOT_FUNCTION, 1, JSEXN_TYPEERR, "{0} is not a function")
+MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} is not a constructor")
+MSG_DEF(JSMSG_BOGUS_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} constructor can't be used directly")
+MSG_DEF(JSMSG_CANT_CONVERT_TO, 2, JSEXN_TYPEERR, "can't convert {0} to {1}")
+MSG_DEF(JSMSG_TOPRIMITIVE_NOT_CALLABLE, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] property is not a function")
+MSG_DEF(JSMSG_TOPRIMITIVE_RETURNED_OBJECT, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] method returned an object")
+MSG_DEF(JSMSG_NO_PROPERTIES, 1, JSEXN_TYPEERR, "{0} has no properties")
+MSG_DEF(JSMSG_PROPERTY_FAIL, 2, JSEXN_TYPEERR, "can't access property {0} of {1}")
+MSG_DEF(JSMSG_PROPERTY_FAIL_EXPR, 3, JSEXN_TYPEERR, "can't access property {0}, {1} is {2}")
+MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}")
+MSG_DEF(JSMSG_INVALID_DATA_VIEW_LENGTH, 0, JSEXN_RANGEERR, "invalid data view length")
+MSG_DEF(JSMSG_OFFSET_LARGER_THAN_FILESIZE, 0, JSEXN_RANGEERR, "offset is larger than filesize")
+MSG_DEF(JSMSG_OFFSET_OUT_OF_BUFFER, 0, JSEXN_RANGEERR, "start offset is outside the bounds of the buffer")
+MSG_DEF(JSMSG_OFFSET_OUT_OF_DATAVIEW, 0, JSEXN_RANGEERR, "offset is outside the bounds of the DataView")
+MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 0, JSEXN_RANGEERR, "array too large due to spread operand(s)")
+MSG_DEF(JSMSG_BAD_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
+MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 1, JSEXN_TYPEERR, "invalid {0} usage")
+MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 0, JSEXN_RANGEERR, "invalid array length")
+MSG_DEF(JSMSG_SOURCE_ARRAY_TOO_LONG, 0, JSEXN_RANGEERR, "source array is too long")
+MSG_DEF(JSMSG_REDECLARED_PREV, 2, JSEXN_NOTE, "Previously declared at line {0}, column {1}")
+MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}")
+MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
+MSG_DEF(JSMSG_UNDECLARED_PRIVATE, 0, JSEXN_TYPEERR, "Trying to read undeclared field")
+MSG_DEF(JSMSG_GETTER_ONLY, 1, JSEXN_TYPEERR, "setting getter-only property {0}")
+MSG_DEF(JSMSG_PRIVATE_SETTER_ONLY, 0, JSEXN_TYPEERR, "getting private setter-only property")
+MSG_DEF(JSMSG_OVERWRITING_ACCESSOR, 1, JSEXN_TYPEERR, "can't overwrite accessor property {0}")
+MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects")
+MSG_DEF(JSMSG_NESTING_GENERATOR, 0, JSEXN_TYPEERR, "already executing generator")
+MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
+MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 1, JSEXN_TYPEERR, "bad surrogate character {0}")
+MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
+MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
+MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW, 1, JSEXN_TYPEERR, "calling a builtin {0} constructor without new is forbidden")
+MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
+MSG_DEF(JSMSG_EMPTY_ITERATOR_REDUCE, 0, JSEXN_TYPEERR, "reduce of empty iterator with no initial value")
+MSG_DEF(JSMSG_UNEXPECTED_TYPE, 2, JSEXN_TYPEERR, "{0} is {1}")
+MSG_DEF(JSMSG_MISSING_FUN_ARG, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
+MSG_DEF(JSMSG_OBJECT_REQUIRED, 1, JSEXN_TYPEERR, "{0} is not a non-null object")
+MSG_DEF(JSMSG_OBJECT_REQUIRED_ARG, 3, JSEXN_TYPEERR, "{0} argument of {1} must be an object, got {2}")
+MSG_DEF(JSMSG_OBJECT_REQUIRED_WEAKMAP_KEY, 1, JSEXN_TYPEERR, "WeakMap key must be an object, got {0}")
+MSG_DEF(JSMSG_OBJECT_REQUIRED_WEAKSET_VAL, 1, JSEXN_TYPEERR, "WeakSet value must be an object, got {0}")
+MSG_DEF(JSMSG_OBJECT_REQUIRED_PROP_DESC, 1, JSEXN_TYPEERR, "Property descriptor must be an object, got {0}")
+MSG_DEF(JSMSG_OBJECT_REQUIRED_RET_OWNKEYS, 1, JSEXN_TYPEERR, "ownKeys trap must be an object, got {0}")
+MSG_DEF(JSMSG_WRONG_TYPE_ARG, 4, JSEXN_TYPEERR, "argument {0} to {1} must be an object of type {2}, got {3}")
+MSG_DEF(JSMSG_SET_NON_OBJECT_RECEIVER, 2, JSEXN_TYPEERR, "can't assign to property {1} on {0}: not an object")
+MSG_DEF(JSMSG_INVALID_DESCRIPTOR, 0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified")
+MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 1, JSEXN_TYPEERR, "{0}: Object is not extensible")
+MSG_DEF(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE, 2, JSEXN_TYPEERR, "can't define property {1}: {0} is not extensible")
+MSG_DEF(JSMSG_CANT_REDEFINE_PROP, 1, JSEXN_TYPEERR, "can't redefine non-configurable property {0}")
+MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't redefine array length")
+MSG_DEF(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't define array index property past the end of an array with non-writable length")
+MSG_DEF(JSMSG_BAD_GET_SET_FIELD, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
+MSG_DEF(JSMSG_THROW_TYPE_ERROR, 0, JSEXN_TYPEERR, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
+MSG_DEF(JSMSG_NOT_EXPECTED_TYPE, 3, JSEXN_TYPEERR, "{0}: expected {1}, got {2}")
+MSG_DEF(JSMSG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable")
+MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 2, JSEXN_WARN, "{0} is being assigned a {1}, but already has one")
+MSG_DEF(JSMSG_GET_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.iterator]() returned a non-object value")
+MSG_DEF(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, 1, JSEXN_TYPEERR, "iterator.{0}() returned a non-object value")
+MSG_DEF(JSMSG_CANT_SET_PROTO, 0, JSEXN_TYPEERR, "can't set prototype of this object")
+MSG_DEF(JSMSG_CANT_SET_PROTO_OF, 1, JSEXN_TYPEERR, "can't set prototype of {0}")
+MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
+MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
+MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
+MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with 'new'")
+MSG_DEF(JSMSG_UNINITIALIZED_THIS, 0, JSEXN_REFERENCEERR, "must call super constructor before using 'this' in derived class constructor")
+MSG_DEF(JSMSG_BAD_DERIVED_RETURN, 1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
+MSG_DEF(JSMSG_BAD_HERITAGE, 2, JSEXN_TYPEERR, "class heritage {0} is {1}")
+MSG_DEF(JSMSG_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0} is not an object or null")
+MSG_DEF(JSMSG_CONSTRUCTOR_DISABLED, 1, JSEXN_TYPEERR, "{0} is disabled")
+
+// JSON
+MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
+MSG_DEF(JSMSG_JSON_CYCLIC_VALUE, 0, JSEXN_TYPEERR, "cyclic object value")
+
+// Runtime errors
+MSG_DEF(JSMSG_ASSIGN_TO_CALL, 0, JSEXN_REFERENCEERR, "cannot assign to function call")
+MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}")
+MSG_DEF(JSMSG_BAD_PROTOTYPE, 1, JSEXN_TYPEERR, "'prototype' property of {0} is not an object")
+MSG_DEF(JSMSG_IN_NOT_OBJECT, 1, JSEXN_TYPEERR, "right-hand side of 'in' should be an object, got {0}")
+MSG_DEF(JSMSG_IN_STRING, 2, JSEXN_TYPEERR, "cannot use 'in' operator to search for {0} in {1}")
+MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 0, JSEXN_RANGEERR, "too many constructor arguments")
+MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 0, JSEXN_RANGEERR, "too many function arguments")
+MSG_DEF(JSMSG_UNINITIALIZED_LEXICAL, 1, JSEXN_REFERENCEERR, "can't access lexical declaration '{0}' before initialization")
+MSG_DEF(JSMSG_BAD_CONST_ASSIGN, 1, JSEXN_TYPEERR, "invalid assignment to const '{0}'")
+MSG_DEF(JSMSG_CANT_DECLARE_GLOBAL_BINDING, 2, JSEXN_TYPEERR, "cannot declare global binding '{0}': {1}")
+
+// Date
+MSG_DEF(JSMSG_INVALID_DATE, 0, JSEXN_RANGEERR, "invalid date")
+MSG_DEF(JSMSG_BAD_TOISOSTRING_PROP, 0, JSEXN_TYPEERR, "toISOString property is not callable")
+
+// String
+MSG_DEF(JSMSG_BAD_URI, 0, JSEXN_URIERR, "malformed URI sequence")
+MSG_DEF(JSMSG_INVALID_NORMALIZE_FORM, 0, JSEXN_RANGEERR, "form must be one of 'NFC', 'NFD', 'NFKC', or 'NFKD'")
+MSG_DEF(JSMSG_NEGATIVE_REPETITION_COUNT, 0, JSEXN_RANGEERR, "repeat count must be non-negative")
+MSG_DEF(JSMSG_NOT_A_CODEPOINT, 1, JSEXN_RANGEERR, "{0} is not a valid code point")
+MSG_DEF(JSMSG_RESULTING_STRING_TOO_LARGE, 0, JSEXN_RANGEERR, "repeat count must be less than infinity and not overflow maximum string size")
+MSG_DEF(JSMSG_FLAGS_UNDEFINED_OR_NULL, 0, JSEXN_TYPEERR, "'flags' property must neither be undefined nor null")
+MSG_DEF(JSMSG_REQUIRES_GLOBAL_REGEXP, 1, JSEXN_TYPEERR, "{0} must be called with a global RegExp")
+
+// Number
+MSG_DEF(JSMSG_BAD_RADIX, 0, JSEXN_RANGEERR, "radix must be an integer at least 2 and no greater than 36")
+MSG_DEF(JSMSG_PRECISION_RANGE, 1, JSEXN_RANGEERR, "precision {0} out of range")
+
+// Function
+MSG_DEF(JSMSG_BAD_APPLY_ARGS, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array")
+MSG_DEF(JSMSG_BAD_FORMAL, 0, JSEXN_SYNTAXERR, "malformed formal parameter")
+MSG_DEF(JSMSG_CALLER_IS_STRICT, 0, JSEXN_TYPEERR, "access to strict mode caller function is censored")
+MSG_DEF(JSMSG_DEPRECATED_USAGE, 1, JSEXN_REFERENCEERR, "deprecated {0} usage")
+MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 1, JSEXN_TYPEERR, "{0} is not a scripted function")
+MSG_DEF(JSMSG_NO_REST_NAME, 0, JSEXN_SYNTAXERR, "no parameter name after ...")
+MSG_DEF(JSMSG_PARAMETER_AFTER_REST, 0, JSEXN_SYNTAXERR, "parameter after rest parameter")
+MSG_DEF(JSMSG_TOO_MANY_ARGUMENTS, 0, JSEXN_RANGEERR, "too many arguments provided for a function call")
+
+// CSP
+MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 0, JSEXN_EVALERR, "call to eval() blocked by CSP")
+MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 0, JSEXN_EVALERR, "call to Function() blocked by CSP")
+
+// Wrappers
+MSG_DEF(JSMSG_ACCESSOR_DEF_DENIED, 1, JSEXN_ERR, "Permission denied to define accessor property {0}")
+MSG_DEF(JSMSG_DEAD_OBJECT, 0, JSEXN_TYPEERR, "can't access dead object")
+MSG_DEF(JSMSG_OBJECT_ACCESS_DENIED, 0, JSEXN_ERR, "Permission denied to access object")
+MSG_DEF(JSMSG_PROPERTY_ACCESS_DENIED, 1, JSEXN_ERR, "Permission denied to access property {0}")
+
+// JSAPI-only (Not thrown as JS exceptions)
+MSG_DEF(JSMSG_CANT_CLONE_OBJECT, 0, JSEXN_TYPEERR, "can't clone object")
+MSG_DEF(JSMSG_CANT_OPEN, 2, JSEXN_ERR, "can't open {0}: {1}")
+MSG_DEF(JSMSG_SUPPORT_NOT_ENABLED, 1, JSEXN_ERR, "support for {0} is not enabled")
+MSG_DEF(JSMSG_USER_DEFINED_ERROR, 0, JSEXN_ERR, "JS_ReportError was called")
+
+// Internal errors
+MSG_DEF(JSMSG_ALLOC_OVERFLOW, 0, JSEXN_INTERNALERR, "allocation size overflow")
+MSG_DEF(JSMSG_BAD_BYTECODE, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}")
+MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 0, JSEXN_INTERNALERR, "buffer too small")
+MSG_DEF(JSMSG_BUILD_ID_NOT_AVAILABLE, 0, JSEXN_INTERNALERR, "build ID is not available")
+MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})")
+MSG_DEF(JSMSG_NEED_DIET, 1, JSEXN_INTERNALERR, "{0} too large")
+MSG_DEF(JSMSG_OUT_OF_MEMORY, 0, JSEXN_INTERNALERR, "out of memory")
+MSG_DEF(JSMSG_OVER_RECURSED, 0, JSEXN_INTERNALERR, "too much recursion")
+MSG_DEF(JSMSG_TOO_BIG_TO_ENCODE, 0, JSEXN_INTERNALERR, "data are to big to encode")
+MSG_DEF(JSMSG_TOO_DEEP, 1, JSEXN_INTERNALERR, "{0} nested too deeply")
+MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 1, JSEXN_INTERNALERR, "uncaught exception: {0}")
+MSG_DEF(JSMSG_UNKNOWN_FORMAT, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}")
+MSG_DEF(JSMSG_UNSAFE_FILENAME, 1, JSEXN_INTERNALERR, "unsafe filename: {0}")
+
+// Frontend
+MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}")
+MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initializer too large")
+MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *")
+MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
+MSG_DEF(JSMSG_AWAIT_IN_PARAMETER, 0, JSEXN_SYNTAXERR, "await expression can't be used in parameter")
+MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid in async functions and async generators")
+MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC_OR_MODULE, 0, JSEXN_SYNTAXERR, "await is only valid in async functions, async generators and modules")
+MSG_DEF(JSMSG_TOP_LEVEL_AWAIT_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "top level await is not currently supported")
+MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
+MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
+MSG_DEF(JSMSG_BAD_COALESCE_MIXING, 0, JSEXN_SYNTAXERR, "cannot use `??` unparenthesized within `||` and `&&` expressions")
+MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration")
+MSG_DEF(JSMSG_BAD_CONTINUE, 0, JSEXN_SYNTAXERR, "continue must be inside loop")
+MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 0, JSEXN_SYNTAXERR, "invalid destructuring assignment operator")
+MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET, 0, JSEXN_SYNTAXERR, "invalid destructuring target")
+MSG_DEF(JSMSG_BAD_DESTRUCT_PARENS, 0, JSEXN_SYNTAXERR, "destructuring patterns in assignments can't be parenthesized")
+MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration")
+MSG_DEF(JSMSG_BAD_DUP_ARGS, 0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context")
+MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loop")
+MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side")
+MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding")
+MSG_DEF(JSMSG_BAD_STARTING_FOROF_LHS, 1, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with '{0}'")
+MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand")
+MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS, 0, JSEXN_SYNTAXERR, "invalid assignment left-hand side")
+MSG_DEF(JSMSG_BAD_METHOD_DEF, 0, JSEXN_SYNTAXERR, "bad method definition")
+MSG_DEF(JSMSG_BAD_POW_LEFTSIDE, 0, JSEXN_SYNTAXERR, "unparenthesized unary expression can't appear on the left-hand side of '**'")
+MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id")
+MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS, 0, JSEXN_SYNTAXERR, "'arguments' can't be defined or assigned to in strict mode code")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN_EVAL, 0, JSEXN_SYNTAXERR, "'eval' can't be defined or assigned to in strict mode code")
+MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch statement")
+MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
+MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
+MSG_DEF(JSMSG_BAD_SUPERPRIVATE, 0, JSEXN_SYNTAXERR, "invalid access of private field on 'super'")
+MSG_DEF(JSMSG_BAD_SUPERCALL, 0, JSEXN_SYNTAXERR, "super() is only valid in derived class constructors")
+MSG_DEF(JSMSG_BAD_ARGUMENTS, 0, JSEXN_SYNTAXERR, "arguments is not valid in fields")
+MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list")
+MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
+MSG_DEF(JSMSG_BRACKET_OPENED, 2, JSEXN_NOTE, "[ opened at line {0}, column {1}")
+MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 0, JSEXN_SYNTAXERR, "catch after unconditional catch")
+MSG_DEF(JSMSG_CATCH_IDENTIFIER, 0, JSEXN_SYNTAXERR, "missing identifier in catch")
+MSG_DEF(JSMSG_CATCH_OR_FINALLY, 0, JSEXN_SYNTAXERR, "missing catch or finally after try")
+MSG_DEF(JSMSG_CATCH_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "catch without try")
+MSG_DEF(JSMSG_COLON_AFTER_CASE, 0, JSEXN_SYNTAXERR, "missing : after case label")
+MSG_DEF(JSMSG_COLON_AFTER_ID, 0, JSEXN_SYNTAXERR, "missing : after property id")
+MSG_DEF(JSMSG_COLON_IN_COND, 0, JSEXN_SYNTAXERR, "missing : in conditional expression")
+MSG_DEF(JSMSG_COMP_PROP_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing ] in computed property name")
+MSG_DEF(JSMSG_CURLY_AFTER_BODY, 0, JSEXN_SYNTAXERR, "missing } after function body")
+MSG_DEF(JSMSG_CURLY_OPENED, 2, JSEXN_NOTE, "{ opened at line {0}, column {1}")
+MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 0, JSEXN_SYNTAXERR, "missing } after catch block")
+MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 0, JSEXN_SYNTAXERR, "missing } after finally block")
+MSG_DEF(JSMSG_CURLY_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing } after property list")
+MSG_DEF(JSMSG_CURLY_AFTER_TRY, 0, JSEXN_SYNTAXERR, "missing } after try block")
+MSG_DEF(JSMSG_CURLY_BEFORE_BODY, 0, JSEXN_SYNTAXERR, "missing { before function body")
+MSG_DEF(JSMSG_CURLY_BEFORE_CATCH, 0, JSEXN_SYNTAXERR, "missing { before catch block")
+MSG_DEF(JSMSG_CURLY_BEFORE_CLASS, 0, JSEXN_SYNTAXERR, "missing { before class body")
+MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY, 0, JSEXN_SYNTAXERR, "missing { before finally block")
+MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH, 0, JSEXN_SYNTAXERR, "missing { before switch body")
+MSG_DEF(JSMSG_CURLY_BEFORE_TRY, 0, JSEXN_SYNTAXERR, "missing { before try block")
+MSG_DEF(JSMSG_CURLY_IN_COMPOUND, 0, JSEXN_SYNTAXERR, "missing } in compound statement")
+MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT,0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword")
+MSG_DEF(JSMSG_DECLARATION_AFTER_IMPORT,0, JSEXN_SYNTAXERR, "missing declaration after 'import' keyword")
+MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 0, JSEXN_SYNTAXERR, "applying the 'delete' operator to an unqualified name is deprecated")
+MSG_DEF(JSMSG_DEPRECATED_OCTAL, 0, JSEXN_SYNTAXERR, "\"0\"-prefixed octal literals and octal escape sequences are deprecated; for octal literals use the \"0o\" prefix instead")
+MSG_DEF(JSMSG_DEPRECATED_PRAGMA, 1, JSEXN_WARN, "Using //@ to indicate {0} pragmas is deprecated. Use //# instead")
+MSG_DEF(JSMSG_DUPLICATE_EXPORT_NAME, 1, JSEXN_SYNTAXERR, "duplicate export name '{0}'")
+MSG_DEF(JSMSG_DUPLICATE_FORMAL, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}")
+MSG_DEF(JSMSG_DUPLICATE_LABEL, 0, JSEXN_SYNTAXERR, "duplicate label")
+MSG_DEF(JSMSG_DUPLICATE_PROPERTY, 1, JSEXN_SYNTAXERR, "property name {0} appears more than once in object literal")
+MSG_DEF(JSMSG_DUPLICATE_PROTO_PROPERTY, 0, JSEXN_SYNTAXERR, "property name __proto__ appears more than once in object literal")
+MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?")
+MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?")
+MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0, JSEXN_SYNTAXERR, "export declarations may only appear at top level of a module")
+MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "finally without try")
+MSG_DEF(JSMSG_FORBIDDEN_AS_STATEMENT, 1, JSEXN_SYNTAXERR, "{0} can't appear in single-statement context")
+MSG_DEF(JSMSG_FOR_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "for await (... of ...) is only valid in async functions and async generators")
+MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause")
+MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
+MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
+MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
+MSG_DEF(JSMSG_BAD_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid escape sequence")
+MSG_DEF(JSMSG_MISSING_PRIVATE_NAME, 0, JSEXN_SYNTAXERR, "'#' not followed by identifier")
+MSG_DEF(JSMSG_PRIVATE_DELETE, 0, JSEXN_SYNTAXERR, "private fields can't be deleted")
+MSG_DEF(JSMSG_MISSING_PRIVATE_DECL, 1, JSEXN_SYNTAXERR, "reference to undeclared private field or method {0}")
+MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 1, JSEXN_SYNTAXERR, "illegal character {0}")
+MSG_DEF(JSMSG_IMPORT_META_OUTSIDE_MODULE, 0, JSEXN_SYNTAXERR, "import.meta may only appear in a module")
+MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
+MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL, 0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer")
+MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer")
+MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
+MSG_DEF(JSMSG_INVALID_ID, 1, JSEXN_SYNTAXERR, "{0} is an invalid identifier")
+MSG_DEF(JSMSG_SEPARATOR_IN_ZERO_PREFIXED_NUMBER, 0, JSEXN_SYNTAXERR, "numeric separators '_' are not allowed in numbers that start with '0'")
+MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found")
+MSG_DEF(JSMSG_GENERATOR_LABEL, 0, JSEXN_SYNTAXERR, "generator functions cannot be labelled")
+MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled")
+MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
+MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
+MSG_DEF(JSMSG_LINE_BREAK_BEFORE_ARROW, 0, JSEXN_SYNTAXERR, "no line break is allowed before '=>'")
+MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
+MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
+MSG_DEF(JSMSG_MISSING_EXPONENT, 0, JSEXN_SYNTAXERR, "missing exponent")
+MSG_DEF(JSMSG_MISSING_EXPR_AFTER_THROW,0, JSEXN_SYNTAXERR, "throw statement is missing an expression")
+MSG_DEF(JSMSG_MISSING_FORMAL, 0, JSEXN_SYNTAXERR, "missing formal parameter")
+MSG_DEF(JSMSG_MISSING_HEXDIGITS, 0, JSEXN_SYNTAXERR, "missing hexadecimal digits after '0x'")
+MSG_DEF(JSMSG_MISSING_OCTAL_DIGITS, 0, JSEXN_SYNTAXERR, "missing octal digits after '0o'")
+MSG_DEF(JSMSG_NUMBER_END_WITH_UNDERSCORE, 0, JSEXN_SYNTAXERR, "underscore can appear only between digits, not after the last digit in a number")
+MSG_DEF(JSMSG_NUMBER_MULTIPLE_ADJACENT_UNDERSCORES, 0, JSEXN_SYNTAXERR, "number cannot contain multiple adjacent underscores")
+MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM, 0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword")
+MSG_DEF(JSMSG_NAME_AFTER_DOT, 0, JSEXN_SYNTAXERR, "missing name after . operator")
+MSG_DEF(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT, 0, JSEXN_SYNTAXERR, "expected named imports or namespace import after comma")
+MSG_DEF(JSMSG_NO_BINDING_NAME, 0, JSEXN_SYNTAXERR, "missing binding name")
+MSG_DEF(JSMSG_NO_EXPORT_NAME, 0, JSEXN_SYNTAXERR, "missing export name")
+MSG_DEF(JSMSG_NO_IMPORT_NAME, 0, JSEXN_SYNTAXERR, "missing import name")
+MSG_DEF(JSMSG_NO_VARIABLE_NAME, 0, JSEXN_SYNTAXERR, "missing variable name")
+MSG_DEF(JSMSG_OF_AFTER_FOR_NAME, 0, JSEXN_SYNTAXERR, "missing 'of' after for")
+MSG_DEF(JSMSG_PAREN_AFTER_ARGS, 0, JSEXN_SYNTAXERR, "missing ) after argument list")
+MSG_DEF(JSMSG_PAREN_AFTER_CATCH, 0, JSEXN_SYNTAXERR, "missing ) after catch")
+MSG_DEF(JSMSG_PAREN_AFTER_COND, 0, JSEXN_SYNTAXERR, "missing ) after condition")
+MSG_DEF(JSMSG_PAREN_AFTER_FOR, 0, JSEXN_SYNTAXERR, "missing ( after for")
+MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters")
+MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL, 0, JSEXN_SYNTAXERR, "missing ) after for-loop control")
+MSG_DEF(JSMSG_PAREN_AFTER_FOR_OF_ITERABLE, 0, JSEXN_SYNTAXERR, "missing ) after for-of iterable")
+MSG_DEF(JSMSG_PAREN_AFTER_SWITCH, 0, JSEXN_SYNTAXERR, "missing ) after switch expression")
+MSG_DEF(JSMSG_PAREN_AFTER_WITH, 0, JSEXN_SYNTAXERR, "missing ) after with-statement object")
+MSG_DEF(JSMSG_PAREN_BEFORE_CATCH, 0, JSEXN_SYNTAXERR, "missing ( before catch")
+MSG_DEF(JSMSG_PAREN_BEFORE_COND, 0, JSEXN_SYNTAXERR, "missing ( before condition")
+MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters")
+MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH, 0, JSEXN_SYNTAXERR, "missing ( before switch expression")
+MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 0, JSEXN_SYNTAXERR, "missing ( before with-statement object")
+MSG_DEF(JSMSG_PAREN_IN_PAREN, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical")
+MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list")
+MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after module specifier list")
+MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_SYNTAXERR, "redeclaration of identifier '{0}' in catch")
+MSG_DEF(JSMSG_RESERVED_ID, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier")
+MSG_DEF(JSMSG_REST_WITH_COMMA, 0, JSEXN_SYNTAXERR, "rest element may not have a trailing comma")
+MSG_DEF(JSMSG_REST_WITH_DEFAULT, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
+MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, 1, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level {0} declarations")
+MSG_DEF(JSMSG_SELFHOSTED_METHOD_CALL, 0, JSEXN_SYNTAXERR, "self-hosted code may not contain direct method calls. Use callFunction() or callContentFunction()")
+MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
+MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition")
+MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")
+MSG_DEF(JSMSG_SOURCE_TOO_LONG, 0, JSEXN_RANGEERR, "source is too long")
+MSG_DEF(JSMSG_STMT_AFTER_RETURN, 0, JSEXN_WARN, "unreachable code after return statement")
+MSG_DEF(JSMSG_STRICT_CODE_WITH, 0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements")
+MSG_DEF(JSMSG_STRICT_NON_SIMPLE_PARAMS, 1, JSEXN_SYNTAXERR, "\"use strict\" not allowed in function with {0} parameter")
+MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing } in template string")
+MSG_DEF(JSMSG_TOO_MANY_CASES, 0, JSEXN_INTERNALERR, "too many switch cases")
+MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 0, JSEXN_SYNTAXERR, "too many catch variables")
+MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 0, JSEXN_SYNTAXERR, "too many constructor arguments")
+MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 0, JSEXN_SYNTAXERR, "more than one switch default")
+MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 0, JSEXN_SYNTAXERR, "too many function arguments")
+MSG_DEF(JSMSG_TOO_MANY_LOCALS, 0, JSEXN_SYNTAXERR, "too many local variables")
+MSG_DEF(JSMSG_TOO_MANY_RESUME_INDEXES, 0, JSEXN_SYNTAXERR, "too many yield/await/finally/case locations")
+MSG_DEF(JSMSG_TOUGH_BREAK, 0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch")
+MSG_DEF(JSMSG_UNEXPECTED_TOKEN, 2, JSEXN_SYNTAXERR, "expected {0}, got {1}")
+MSG_DEF(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, 1, JSEXN_SYNTAXERR, "unexpected token: {0}")
+MSG_DEF(JSMSG_UNEXPECTED_PARAMLIST_END,0, JSEXN_SYNTAXERR, "unexpected end of function parameter list")
+MSG_DEF(JSMSG_UNNAMED_CLASS_STMT, 0, JSEXN_SYNTAXERR, "class statement requires a name")
+MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 0, JSEXN_SYNTAXERR, "function statement requires a name")
+MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 0, JSEXN_SYNTAXERR, "unterminated comment")
+MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal")
+MSG_DEF(JSMSG_EOF_BEFORE_END_OF_LITERAL,1,JSEXN_SYNTAXERR, "{0} literal not terminated before end of script")
+MSG_DEF(JSMSG_EOL_BEFORE_END_OF_STRING,1, JSEXN_SYNTAXERR, "{0} string literal contains an unescaped line break")
+MSG_DEF(JSMSG_EOF_IN_ESCAPE_IN_LITERAL,1, JSEXN_SYNTAXERR, "reached end of script in the middle of an escape sequence in a {0} literal")
+MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 0, JSEXN_SYNTAXERR, "\"use asm\" is only meaningful in the Directive Prologue of a function body")
+MSG_DEF(JSMSG_VAR_HIDES_ARG, 1, JSEXN_TYPEERR, "variable {0} redeclares argument")
+MSG_DEF(JSMSG_WHILE_AFTER_DO, 0, JSEXN_SYNTAXERR, "missing while after do-loop body")
+MSG_DEF(JSMSG_YIELD_IN_PARAMETER, 0, JSEXN_SYNTAXERR, "yield expression can't be used in parameter")
+MSG_DEF(JSMSG_YIELD_OUTSIDE_GENERATOR, 0, JSEXN_SYNTAXERR, "yield expression is only valid in generators")
+MSG_DEF(JSMSG_BAD_COLUMN_NUMBER, 0, JSEXN_RANGEERR, "column number out of range")
+MSG_DEF(JSMSG_BAD_LINE_NUMBER, 0, JSEXN_RANGEERR, "line number out of range")
+MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property names aren't supported in this destructuring declaration")
+MSG_DEF(JSMSG_DEFAULT_IN_PATTERN, 0, JSEXN_SYNTAXERR, "destructuring defaults aren't supported in this destructuring declaration")
+MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allowed within functions")
+MSG_DEF(JSMSG_BAD_NEW_OPTIONAL, 0, JSEXN_SYNTAXERR, "new keyword cannot be used with an optional chain")
+MSG_DEF(JSMSG_BAD_OPTIONAL_TEMPLATE, 0, JSEXN_SYNTAXERR, "tagged template cannot be used with optional chain")
+MSG_DEF(JSMSG_ESCAPED_KEYWORD, 0, JSEXN_SYNTAXERR, "keywords must be written literally, without embedded escapes")
+MSG_DEF(JSMSG_PRIVATE_FIELDS_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "private fields are not currently supported")
+MSG_DEF(JSMSG_ILLEGAL_PRIVATE_FIELD, 0, JSEXN_SYNTAXERR, "private fields aren't valid in this context")
+MSG_DEF(JSMSG_PRIVATE_FIELD_DOUBLE, 0, JSEXN_TYPEERR, "Initializing an object twice is an error with private fields")
+
+// UTF-8 source text encoding errors
+MSG_DEF(JSMSG_BAD_LEADING_UTF8_UNIT, 1, JSEXN_SYNTAXERR, "{0} byte doesn't begin a valid UTF-8 code point")
+MSG_DEF(JSMSG_NOT_ENOUGH_CODE_UNITS, 5, JSEXN_SYNTAXERR, "{0} byte in UTF-8 must be followed by {1} byte{2}, but {3} byte{4} present")
+MSG_DEF(JSMSG_BAD_TRAILING_UTF8_UNIT, 1, JSEXN_SYNTAXERR, "bad trailing UTF-8 byte {0} doesn't match the pattern 0b10xxxxxx")
+MSG_DEF(JSMSG_FORBIDDEN_UTF8_CODE_POINT,2,JSEXN_SYNTAXERR, "{0} isn't a valid code point because {1}")
+MSG_DEF(JSMSG_BAD_CODE_UNITS, 1, JSEXN_NOTE, "the code units comprising this invalid code point were: {0}")
+
+// SmooshMonkey
+MSG_DEF(JSMSG_SMOOSH_COMPILE_ERROR, 1, JSEXN_SYNTAXERR, "{0}")
+MSG_DEF(JSMSG_SMOOSH_UNIMPLEMENTED, 1, JSEXN_INTERNALERR, "{0}")
+
+// asm.js
+MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
+MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
+MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 1, JSEXN_WARN, "Successfully compiled asm.js code (total compilation time {0}ms)")
+MSG_DEF(JSMSG_USE_ASM_TYPE_OK_NO_TIME, 0, JSEXN_WARN, "Successfully compiled asm.js code ()")
+
+// wasm
+MSG_DEF(JSMSG_WASM_VERBOSE, 1, JSEXN_WARN, "WebAssembly verbose: {0}")
+MSG_DEF(JSMSG_WASM_COMPILE_WARNING, 1, JSEXN_WARN, "WebAssembly module validated with warning: {0}")
+MSG_DEF(JSMSG_WASM_HUGE_MEMORY_FAILED, 0, JSEXN_WARN, "WebAssembly.Memory failed to reserve a large virtual memory region. This may be due to low configured virtual memory limits on this system.")
+MSG_DEF(JSMSG_WASM_COMPILE_ERROR, 1, JSEXN_WASMCOMPILEERROR, "{0}")
+MSG_DEF(JSMSG_WASM_NO_SHMEM_COMPILE, 0, JSEXN_WASMCOMPILEERROR, "shared memory is disabled")
+MSG_DEF(JSMSG_WASM_BAD_IMPORT_TYPE, 2, JSEXN_WASMLINKERROR, "import object field '{0}' is not a {1}")
+MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG, 2, JSEXN_WASMLINKERROR, "imported function '{0}.{1}' signature mismatch")
+MSG_DEF(JSMSG_WASM_BAD_EXN_SIG, 2, JSEXN_WASMLINKERROR, "imported exception '{0}.{1}' signature mismatch")
+MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE, 1, JSEXN_WASMLINKERROR, "imported {0} with incompatible size")
+MSG_DEF(JSMSG_WASM_BAD_IMP_MAX, 1, JSEXN_WASMLINKERROR, "imported {0} with incompatible maximum size")
+MSG_DEF(JSMSG_WASM_IMP_SHARED_REQD, 0, JSEXN_WASMLINKERROR, "imported unshared memory but shared required")
+MSG_DEF(JSMSG_WASM_IMP_SHARED_BANNED, 0, JSEXN_WASMLINKERROR, "imported shared memory but unshared required")
+MSG_DEF(JSMSG_WASM_BAD_FIT, 2, JSEXN_WASMLINKERROR, "{0} segment does not fit in {1}")
+MSG_DEF(JSMSG_WASM_NO_SHMEM_LINK, 0, JSEXN_WASMLINKERROR, "shared memory is disabled")
+MSG_DEF(JSMSG_WASM_BAD_GLOB_MUT_LINK, 0, JSEXN_WASMLINKERROR, "imported global mutability mismatch")
+MSG_DEF(JSMSG_WASM_BAD_GLOB_TYPE_LINK, 0, JSEXN_WASMLINKERROR, "imported global type mismatch")
+MSG_DEF(JSMSG_WASM_BAD_TBL_TYPE_LINK, 0, JSEXN_WASMLINKERROR, "imported table type mismatch")
+MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL, 0, JSEXN_WASMRUNTIMEERROR, "indirect call to null")
+MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG, 0, JSEXN_WASMRUNTIMEERROR, "indirect call signature mismatch")
+MSG_DEF(JSMSG_WASM_UNREACHABLE, 0, JSEXN_WASMRUNTIMEERROR, "unreachable executed")
+MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW, 0, JSEXN_WASMRUNTIMEERROR, "integer overflow")
+MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_WASMRUNTIMEERROR, "invalid conversion to integer")
+MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_WASMRUNTIMEERROR, "integer divide by zero")
+MSG_DEF(JSMSG_WASM_OUT_OF_BOUNDS, 0, JSEXN_WASMRUNTIMEERROR, "index out of bounds")
+MSG_DEF(JSMSG_WASM_UNALIGNED_ACCESS, 0, JSEXN_WASMRUNTIMEERROR, "unaligned memory access")
+MSG_DEF(JSMSG_WASM_WAKE_OVERFLOW, 0, JSEXN_WASMRUNTIMEERROR, "too many woken agents")
+MSG_DEF(JSMSG_WASM_DEREF_NULL, 0, JSEXN_WASMRUNTIMEERROR, "dereferencing null pointer")
+MSG_DEF(JSMSG_WASM_MEM_IMP_LIMIT, 0, JSEXN_WASMRUNTIMEERROR, "too many memory pages")
+MSG_DEF(JSMSG_WASM_TABLE_IMP_LIMIT, 0, JSEXN_WASMRUNTIMEERROR, "too many table elements")
+MSG_DEF(JSMSG_WASM_BAD_RANGE , 2, JSEXN_RANGEERR, "bad {0} {1}")
+MSG_DEF(JSMSG_WASM_BAD_GROW, 1, JSEXN_RANGEERR, "failed to grow {0}")
+MSG_DEF(JSMSG_WASM_TABLE_OUT_OF_BOUNDS, 0, JSEXN_WASMRUNTIMEERROR, "table index out of bounds")
+MSG_DEF(JSMSG_WASM_BAD_UINT32, 2, JSEXN_TYPEERR, "bad {0} {1}")
+MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument must be an ArrayBuffer or typed array object")
+MSG_DEF(JSMSG_WASM_BAD_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module")
+MSG_DEF(JSMSG_WASM_BAD_BUF_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module, ArrayBuffer or typed array object")
+MSG_DEF(JSMSG_WASM_BAD_DESC_ARG, 1, JSEXN_TYPEERR, "first argument must be a {0} descriptor")
+MSG_DEF(JSMSG_WASM_BAD_ELEMENT, 0, JSEXN_TYPEERR, "\"element\" property of table descriptor must be \"funcref\"")
+MSG_DEF(JSMSG_WASM_BAD_ELEMENT_GENERALIZED, 0, JSEXN_TYPEERR, "\"element\" property of table descriptor must be \"funcref\" or \"externref\"")
+MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument must be an object")
+MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD, 1, JSEXN_TYPEERR, "import object field '{0}' is not an Object")
+MSG_DEF(JSMSG_WASM_BAD_REF_NONNULLABLE_VALUE, 0, JSEXN_TYPEERR, "cannot pass null to non-nullable WebAssembly reference")
+MSG_DEF(JSMSG_WASM_BAD_FUNCREF_VALUE, 0, JSEXN_TYPEERR, "can only pass WebAssembly exported functions to funcref")
+MSG_DEF(JSMSG_WASM_BAD_EQREF_VALUE, 0, JSEXN_TYPEERR, "can only pass a TypedObject to an eqref")
+MSG_DEF(JSMSG_WASM_BAD_VAL_TYPE, 0, JSEXN_TYPEERR, "cannot pass v128 to or from JS")
+MSG_DEF(JSMSG_WASM_BAD_GLOBAL_TYPE, 0, JSEXN_TYPEERR, "bad type for a WebAssembly.Global")
+MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
+MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
+MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
+MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE, 0, JSEXN_TYPEERR, "can't set value of immutable global")
+MSG_DEF(JSMSG_WASM_WRONG_NUMBER_OF_VALUES, 2, JSEXN_TYPEERR, "wrong number of values returned by JavaScript to WebAssembly (expected {0}, got {1})")
+MSG_DEF(JSMSG_WASM_NONSHARED_WAIT , 0, JSEXN_WASMRUNTIMEERROR, "atomic wait on non-shared memory")
+MSG_DEF(JSMSG_WASM_NULL_REQUIRED, 0, JSEXN_TYPEERR, "nullref requires a null value")
+MSG_DEF(JSMSG_WASM_SUPPLY_ONLY_ONE, 2, JSEXN_TYPEERR, "exactly one of {0} and {1} must be supplied")
+MSG_DEF(JSMSG_WASM_MISSING_REQUIRED, 1, JSEXN_TYPEERR, "Missing required argument {0}")
+MSG_DEF(JSMSG_WASM_EXN_CONSTRUCTOR, 0, JSEXN_WASMRUNTIMEERROR, "cannot call WebAssembly.Exception")
+
+// Proxy
+MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
+MSG_DEF(JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler returned a non-object, non-null value")
+MSG_DEF(JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler didn't return the target object's prototype")
+MSG_DEF(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy setPrototypeOf handler returned false")
+MSG_DEF(JSMSG_PROXY_ISEXTENSIBLE_RETURNED_FALSE,0,JSEXN_TYPEERR,"proxy isExtensible handler must return the same extensibility as target")
+MSG_DEF(JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy setPrototypeOf handler returned true, even though the target's prototype is immutable because the target is non-extensible")
+MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 0, JSEXN_TYPEERR, "can't change object's extensibility")
+MSG_DEF(JSMSG_CANT_DEFINE_INVALID, 2, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor ('{0}', {1})")
+MSG_DEF(JSMSG_CANT_DEFINE_NEW, 1, JSEXN_TYPEERR, "proxy can't define a new property '{0}' on a non-extensible object")
+MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC, 1, JSEXN_TYPEERR, "proxy can't define a non-existent '{0}' property as non-configurable")
+MSG_DEF(JSMSG_PROXY_DEFINE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy defineProperty handler returned false for property '{0}'")
+MSG_DEF(JSMSG_PROXY_DELETE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "can't delete property '{0}': proxy deleteProperty handler returned false")
+MSG_DEF(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy preventExtensions handler returned false")
+MSG_DEF(JSMSG_PROXY_SET_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy set handler returned false for property '{0}'")
+MSG_DEF(JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE, 0, JSEXN_TYPEERR, "proxy can't report an extensible object as non-extensible")
+MSG_DEF(JSMSG_CANT_DELETE_NON_EXTENSIBLE, 1, JSEXN_TYPEERR, "proxy can't delete property '{0}' on a non-extensible object")
+MSG_DEF(JSMSG_CANT_REPORT_C_AS_NC, 1, JSEXN_TYPEERR, "proxy can't report existing configurable property '{0}' as non-configurable")
+MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 1, JSEXN_TYPEERR, "proxy can't report an existing own property '{0}' as non-existent on a non-extensible object")
+MSG_DEF(JSMSG_CANT_REPORT_INVALID, 2, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor ('{0}', {1})")
+MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 1, JSEXN_TYPEERR, "proxy can't report a non-configurable own property '{0}' as non-existent")
+MSG_DEF(JSMSG_CANT_REPORT_NEW, 1, JSEXN_TYPEERR, "proxy can't report a new property '{0}' on a non-extensible object")
+MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC, 1, JSEXN_TYPEERR, "proxy can't report a non-existent property '{0}' as non-configurable")
+MSG_DEF(JSMSG_CANT_REPORT_W_AS_NW, 1, JSEXN_TYPEERR, "proxy can't report existing writable property '{0}' as non-writable")
+MSG_DEF(JSMSG_CANT_SET_NW_NC, 1, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property '{0}'")
+MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 1, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property '{0}' without a setter")
+MSG_DEF(JSMSG_CANT_SKIP_NC, 1, JSEXN_TYPEERR, "proxy can't skip a non-configurable property '{0}'")
+MSG_DEF(JSMSG_OWNKEYS_STR_SYM, 0, JSEXN_TYPEERR, "proxy [[OwnPropertyKeys]] must return an array with only string and symbol elements")
+MSG_DEF(JSMSG_OWNKEYS_DUPLICATE, 1, JSEXN_TYPEERR, "proxy [[OwnPropertyKeys]] can't report property '{0}' more than once")
+MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 1, JSEXN_TYPEERR, "proxy must report the same value for the non-writable, non-configurable property '{0}'")
+MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 1, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property '{0}' without a getter")
+MSG_DEF(JSMSG_PROXY_CONSTRUCT_OBJECT, 0, JSEXN_TYPEERR, "proxy [[Construct]] must return an object")
+MSG_DEF(JSMSG_PROXY_EXTENSIBILITY, 0, JSEXN_TYPEERR, "proxy must report same extensiblitity as target")
+MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF, 1, JSEXN_TYPEERR, "proxy [[GetOwnProperty]] must return an object or undefined for property '{0}'")
+MSG_DEF(JSMSG_PROXY_REVOKED, 0, JSEXN_TYPEERR, "illegal operation attempted on a revoked proxy")
+MSG_DEF(JSMSG_PROXY_ARG_REVOKED, 1, JSEXN_TYPEERR, "argument {0} cannot be a revoked proxy")
+MSG_DEF(JSMSG_BAD_TRAP, 1, JSEXN_TYPEERR, "proxy handler's {0} trap wasn't undefined, null, or callable")
+
+// Structured cloning
+MSG_DEF(JSMSG_SC_BAD_CLONE_VERSION, 0, JSEXN_ERR, "unsupported structured clone version")
+MSG_DEF(JSMSG_SC_BAD_SERIALIZED_DATA, 1, JSEXN_INTERNALERR, "bad serialized structured data ({0})")
+MSG_DEF(JSMSG_SC_DUP_TRANSFERABLE, 0, JSEXN_TYPEERR, "duplicate transferable for structured clone")
+MSG_DEF(JSMSG_SC_NOT_TRANSFERABLE, 0, JSEXN_TYPEERR, "invalid transferable array for structured clone")
+MSG_DEF(JSMSG_SC_UNSUPPORTED_TYPE, 0, JSEXN_TYPEERR, "unsupported type for structured data")
+MSG_DEF(JSMSG_SC_NOT_CLONABLE, 1, JSEXN_TYPEERR, "The {0} object cannot be serialized. The Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy HTTP headers will enable this in the future.")
+MSG_DEF(JSMSG_SC_NOT_CLONABLE_WITH_COOP_COEP, 1, JSEXN_TYPEERR, "The {0} object cannot be serialized. The Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy HTTP headers can be used to enable this.")
+MSG_DEF(JSMSG_SC_SAB_DISABLED, 0, JSEXN_TYPEERR, "SharedArrayBuffer not cloned - shared memory disabled in receiver")
+MSG_DEF(JSMSG_SC_SAB_REFCNT_OFLO, 0, JSEXN_TYPEERR, "SharedArrayBuffer has too many references")
+MSG_DEF(JSMSG_SC_SHMEM_TRANSFERABLE, 0, JSEXN_TYPEERR, "Shared memory objects must not be in the transfer list")
+MSG_DEF(JSMSG_SC_SHMEM_POLICY, 0, JSEXN_TYPEERR, "Policy object must forbid cloning shared memory objects cross-process")
+
+// Debugger
+MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")
+MSG_DEF(JSMSG_DEBUG_BAD_LINE, 0, JSEXN_TYPEERR, "invalid line number")
+MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 0, JSEXN_TYPEERR, "invalid script offset")
+MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
+MSG_DEF(JSMSG_DEBUG_BAD_RESUMPTION, 0, JSEXN_TYPEERR, "debugger resumption value must be undefined, {throw: val}, {return: val}, or null")
+MSG_DEF(JSMSG_DEBUG_RESUMPTION_CONFLICT, 0, JSEXN_TYPEERR, "debugger hook returned a resumption, but an earlier hook already did")
+MSG_DEF(JSMSG_DEBUG_BAD_YIELD, 0, JSEXN_TYPEERR, "generator yielded invalid value")
+MSG_DEF(JSMSG_DEBUG_CANT_DEBUG_GLOBAL, 0, JSEXN_TYPEERR, "passing non-debuggable global to addDebuggee")
+MSG_DEF(JSMSG_DEBUG_SAME_COMPARTMENT, 0, JSEXN_TYPEERR, "debugger and debuggee must be in different compartments")
+MSG_DEF(JSMSG_DEBUG_CCW_REQUIRED, 1, JSEXN_TYPEERR, "{0}: argument must be an object from a different compartment")
+MSG_DEF(JSMSG_DEBUG_COMPARTMENT_MISMATCH, 2, JSEXN_TYPEERR, "{0}: descriptor .{1} property is an object in a different compartment than the target object")
+MSG_DEF(JSMSG_DEBUG_LOOP, 0, JSEXN_TYPEERR, "cannot debug an object in same compartment as debugger or a compartment that is already debugging the debugger")
+MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGEE, 2, JSEXN_ERR, "{0} is not a debuggee {1}")
+MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGING, 0, JSEXN_ERR, "can't set breakpoint: script global is not a debuggee")
+MSG_DEF(JSMSG_DEBUG_NOT_IDLE, 0, JSEXN_ERR, "can't start debugging: a debuggee script is on the stack")
+MSG_DEF(JSMSG_DEBUG_NOT_ON_STACK, 1, JSEXN_ERR, "{0} is not on stack")
+MSG_DEF(JSMSG_DEBUG_NOT_ON_STACK_OR_SUSPENDED, 1, JSEXN_ERR, "{0} is not on stack or suspended")
+MSG_DEF(JSMSG_DEBUG_NO_ENV_OBJECT, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects")
+MSG_DEF(JSMSG_DEBUG_PROTO, 2, JSEXN_TYPEERR, "{0}.prototype is not a valid {1} instance")
+MSG_DEF(JSMSG_DEBUG_WRONG_OWNER, 1, JSEXN_TYPEERR, "{0} belongs to a different Debugger")
+MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT, 1, JSEXN_ERR, "variable '{0}' has been optimized out")
+MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT_FUN, 0, JSEXN_ERR, "function is optimized out")
+MSG_DEF(JSMSG_DEBUG_FORCED_RETURN_DISALLOWED, 0, JSEXN_TYPEERR, "can't force return from a generator before the initial yield")
+MSG_DEF(JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED, 0, JSEXN_TYPEERR, "resumption values are disallowed in this hook")
+MSG_DEF(JSMSG_DEBUG_VARIABLE_NOT_FOUND,0, JSEXN_TYPEERR, "variable not found in environment")
+MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY, 3, JSEXN_TYPEERR, "{0} is {1}{2}a global object, but a direct reference is required")
+MSG_DEF(JSMSG_DEBUGGEE_WOULD_RUN, 2, JSEXN_DEBUGGEEWOULDRUN, "debuggee '{0}:{1}' would run")
+MSG_DEF(JSMSG_NOT_CALLABLE_OR_UNDEFINED, 0, JSEXN_TYPEERR, "value is not a function or undefined")
+MSG_DEF(JSMSG_NOT_TRACKING_ALLOCATIONS, 1, JSEXN_ERR, "Cannot call {0} without setting trackingAllocationSites to true")
+MSG_DEF(JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET, 0, JSEXN_ERR, "Cannot track object allocation, because other tools are already doing so")
+MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 0, JSEXN_TYPEERR, "findScripts query object with 'innermost' property must have 'line' and either 'displayURL', 'url', or 'source'")
+MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL, 0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'displayURL', 'url', or 'source' property")
+MSG_DEF(JSMSG_DEBUG_CANT_SET_OPT_ENV, 1, JSEXN_REFERENCEERR, "can't set '{0}' in an optimized-out environment")
+MSG_DEF(JSMSG_DEBUG_INVISIBLE_COMPARTMENT, 0, JSEXN_TYPEERR, "object in compartment marked as invisible to Debugger")
+MSG_DEF(JSMSG_DEBUG_CENSUS_BREAKDOWN, 1, JSEXN_TYPEERR, "unrecognized 'by' value in takeCensus breakdown: {0}")
+MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_RESOLVED, 0, JSEXN_TYPEERR, "Promise hasn't been resolved")
+MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_FULFILLED, 0, JSEXN_TYPEERR, "Promise hasn't been fulfilled")
+MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_REJECTED, 0, JSEXN_TYPEERR, "Promise hasn't been rejected")
+MSG_DEF(JSMSG_DEBUG_NO_BINARY_SOURCE, 0, JSEXN_ERR, "WebAssembly binary source is not available")
+
+// Testing functions
+MSG_DEF(JSMSG_TESTING_SCRIPTS_ONLY, 0, JSEXN_TYPEERR, "only works on scripts")
+
+// Tracelogger
+MSG_DEF(JSMSG_TRACELOGGER_ENABLE_FAIL, 1, JSEXN_ERR, "enabling tracelogger failed: {0}")
+
+// Intl
+MSG_DEF(JSMSG_DATE_NOT_FINITE, 2, JSEXN_RANGEERR, "date value is not finite in {0}.{1}()")
+MSG_DEF(JSMSG_DUPLICATE_VARIANT_SUBTAG, 1, JSEXN_RANGEERR, "duplicate variant subtag: {0}")
+MSG_DEF(JSMSG_INTERNAL_INTL_ERROR, 0, JSEXN_ERR, "internal error while computing Intl data")
+MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
+MSG_DEF(JSMSG_INVALID_UNIT_IDENTIFIER, 1, JSEXN_RANGEERR, "invalid unit identifier in NumberFormat(): {0}")
+MSG_DEF(JSMSG_INVALID_DIGITS_VALUE, 1, JSEXN_RANGEERR, "invalid digits value: {0}")
+MSG_DEF(JSMSG_INVALID_KEYS_TYPE, 0, JSEXN_TYPEERR, "calendar info keys must be an object or undefined")
+MSG_DEF(JSMSG_INVALID_KEY, 1, JSEXN_RANGEERR, "invalid key: {0}")
+MSG_DEF(JSMSG_INVALID_LANGUAGE_TAG, 1, JSEXN_RANGEERR, "invalid language tag: {0}")
+MSG_DEF(JSMSG_INVALID_LOCALES_ELEMENT, 0, JSEXN_TYPEERR, "invalid element in locales argument")
+MSG_DEF(JSMSG_INVALID_LOCALE_MATCHER, 1, JSEXN_RANGEERR, "invalid locale matcher in supportedLocalesOf(): {0}")
+MSG_DEF(JSMSG_INVALID_OPTION_VALUE, 2, JSEXN_RANGEERR, "invalid value {1} for option {0}")
+MSG_DEF(JSMSG_INVALID_TIME_ZONE, 1, JSEXN_RANGEERR, "invalid time zone in DateTimeFormat(): {0}")
+MSG_DEF(JSMSG_INVALID_DATETIME_OPTION, 2, JSEXN_TYPEERR, "can't set option {0} when {1} is used")
+MSG_DEF(JSMSG_INVALID_DATETIME_STYLE, 2, JSEXN_TYPEERR, "can't set option {0} in Date.{1}()")
+MSG_DEF(JSMSG_UNDEFINED_CURRENCY, 0, JSEXN_TYPEERR, "undefined currency in NumberFormat() with currency style")
+MSG_DEF(JSMSG_UNDEFINED_UNIT, 0, JSEXN_TYPEERR, "undefined unit in NumberFormat() with unit style")
+MSG_DEF(JSMSG_UNDEFINED_DATE, 2, JSEXN_TYPEERR, "undefined {0}-date in DateTimeFormat.{1}()")
+MSG_DEF(JSMSG_UNDEFINED_TYPE, 0, JSEXN_TYPEERR, "missing \"type\" option in DisplayNames()")
+MSG_DEF(JSMSG_START_AFTER_END_DATE, 1, JSEXN_RANGEERR, "start-date after end-date in DateTimeFormat.{0}()")
+
+// RegExp
+MSG_DEF(JSMSG_BAD_CLASS_RANGE, 0, JSEXN_SYNTAXERR, "invalid range in character class")
+MSG_DEF(JSMSG_ESCAPE_AT_END_OF_REGEXP, 0, JSEXN_SYNTAXERR, "\\ at end of pattern")
+MSG_DEF(JSMSG_EXEC_NOT_OBJORNULL, 0, JSEXN_TYPEERR, "RegExp exec method should return object or null")
+MSG_DEF(JSMSG_INVALID_DECIMAL_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid decimal escape in regular expression")
+MSG_DEF(JSMSG_INVALID_GROUP, 0, JSEXN_SYNTAXERR, "invalid regexp group")
+MSG_DEF(JSMSG_INVALID_IDENTITY_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid identity escape in regular expression")
+MSG_DEF(JSMSG_INVALID_UNICODE_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid unicode escape in regular expression")
+MSG_DEF(JSMSG_MISSING_PAREN, 0, JSEXN_SYNTAXERR, "unterminated parenthetical")
+MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another")
+MSG_DEF(JSMSG_NOTHING_TO_REPEAT, 0, JSEXN_SYNTAXERR, "nothing to repeat")
+MSG_DEF(JSMSG_NUMBERS_OUT_OF_ORDER, 0, JSEXN_SYNTAXERR, "numbers out of order in {} quantifier.")
+MSG_DEF(JSMSG_RANGE_WITH_CLASS_ESCAPE, 0, JSEXN_SYNTAXERR, "character class escape cannot be used in class range in regular expression")
+MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEXP, 0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag")
+MSG_DEF(JSMSG_TOO_MANY_PARENS, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
+MSG_DEF(JSMSG_UNICODE_OVERFLOW, 1, JSEXN_SYNTAXERR, "Unicode codepoint must not be greater than 0x10FFFF in {0}")
+MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
+MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated character class")
+MSG_DEF(JSMSG_INVALID_PROPERTY_NAME, 0, JSEXN_SYNTAXERR, "invalid property name in regular expression")
+MSG_DEF(JSMSG_INVALID_CLASS_PROPERTY_NAME, 0, JSEXN_SYNTAXERR, "invalid class property name in regular expression")
+MSG_DEF(JSMSG_INCOMPLETE_QUANTIFIER, 0, JSEXN_SYNTAXERR, "incomplete quantifier in regular expression")
+MSG_DEF(JSMSG_INVALID_QUANTIFIER, 0, JSEXN_SYNTAXERR, "invalid quantifier in regular expression")
+MSG_DEF(JSMSG_INVALID_CAPTURE_NAME, 0, JSEXN_SYNTAXERR, "invalid capture group name in regular expression")
+MSG_DEF(JSMSG_DUPLICATE_CAPTURE_NAME, 0, JSEXN_SYNTAXERR, "duplicate capture group name in regular expression")
+MSG_DEF(JSMSG_INVALID_NAMED_REF, 0, JSEXN_SYNTAXERR, "invalid named reference in regular expression")
+MSG_DEF(JSMSG_INVALID_NAMED_CAPTURE_REF, 0, JSEXN_SYNTAXERR, "invalid named capture reference in regular expression")
+MSG_DEF(JSMSG_INCOMPATIBLE_REGEXP_GETTER, 2, JSEXN_TYPEERR, "RegExp.prototype.{0} getter called on non-RegExp object: {1}")
+
+// Self-hosting
+MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR, 0, JSEXN_ERR, "internal error getting the default locale")
+MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1, JSEXN_ERR, "No such property on self-hosted object: {0}")
+
+// Typed object
+MSG_DEF(JSMSG_TYPEDOBJECT_SETTING_IMMUTABLE, 0, JSEXN_ERR, "setting immutable field")
+
+// Array
+MSG_DEF(JSMSG_TOO_LONG_ARRAY, 0, JSEXN_TYPEERR, "Too long array")
+
+// Typed array
+MSG_DEF(JSMSG_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
+MSG_DEF(JSMSG_DEFINE_BAD_INDEX, 0, JSEXN_TYPEERR, "can't define element for invalid or out-of-range index")
+MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
+MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")
+MSG_DEF(JSMSG_SHORT_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected ArrayBuffer with at least {0} bytes, but species constructor returns ArrayBuffer with {1} bytes")
+MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 0, JSEXN_TYPEERR, "invalid arguments")
+MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
+MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED, 0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
+MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS, 0, JSEXN_RANGEERR, "attempting to construct out-of-bounds TypedArray on ArrayBuffer")
+MSG_DEF(JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT, 1, JSEXN_TYPEERR, "cannot directly {0} builtin %TypedArray%")
+MSG_DEF(JSMSG_NON_TYPED_ARRAY_RETURNED, 0, JSEXN_TYPEERR, "constructor didn't return TypedArray object")
+MSG_DEF(JSMSG_SHORT_TYPED_ARRAY_RETURNED, 2, JSEXN_TYPEERR, "expected TypedArray of at least length {0}, but constructor returned TypedArray of length {1}")
+MSG_DEF(JSMSG_TYPED_ARRAY_NOT_COMPATIBLE, 2, JSEXN_TYPEERR, "{0} elements are incompatible with {1}")
+MSG_DEF(JSMSG_ARRAYBUFFER_REQUIRED, 0, JSEXN_TYPEERR, "ArrayBuffer object required")
+
+// Shared array buffer
+MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH, 0, JSEXN_RANGEERR, "length argument out of range")
+MSG_DEF(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected SharedArrayBuffer, but species constructor returned non-SharedArrayBuffer")
+MSG_DEF(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different SharedArrayBuffer, but species constructor returned same SharedArrayBuffer")
+MSG_DEF(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected SharedArrayBuffer with at least {0} bytes, but species constructor returns SharedArrayBuffer with {1} bytes")
+
+// Reflect
+MSG_DEF(JSMSG_BAD_PARSE_NODE, 0, JSEXN_INTERNALERR, "bad parse node")
+
+// Symbol
+MSG_DEF(JSMSG_SYMBOL_TO_STRING, 0, JSEXN_TYPEERR, "can't convert symbol to string")
+MSG_DEF(JSMSG_SYMBOL_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert symbol to number")
+
+// Atomics and futexes
+MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY, 0, JSEXN_TYPEERR, "invalid array type for the operation")
+MSG_DEF(JSMSG_ATOMICS_TOO_LONG, 0, JSEXN_RANGEERR, "timeout value too large")
+MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED, 0, JSEXN_TYPEERR, "waiting is not allowed on this thread")
+
+// XPConnect wrappers and DOM bindings
+MSG_DEF(JSMSG_CANT_SET_INTERPOSED, 1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
+MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't define elements on a Window object")
+MSG_DEF(JSMSG_CANT_DELETE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't delete elements from a Window object")
+MSG_DEF(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't delete property {0} from window's named properties object")
+MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS, 0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
+MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_NC, 0, JSEXN_TYPEERR, "can't define non-configurable property on WindowProxy")
+MSG_DEF(JSMSG_NO_NAMED_SETTER, 2, JSEXN_TYPEERR, "{0} doesn't have a named property setter for '{1}'")
+MSG_DEF(JSMSG_NO_INDEXED_SETTER, 2, JSEXN_TYPEERR, "{0} doesn't have an indexed property setter for '{1}'")
+MSG_DEF(JSMSG_NOT_DATA_DESCRIPTOR, 2, JSEXN_TYPEERR, "can't define a getter/setter for element '{1}' of {0} object")
+
+// Super
+MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
+MSG_DEF(JSMSG_REINIT_THIS, 0, JSEXN_REFERENCEERR, "super() called twice in derived class constructor")
+
+// Modules
+MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT, 0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
+MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "indirect export not found")
+MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "ambiguous indirect export")
+MSG_DEF(JSMSG_MISSING_IMPORT, 0, JSEXN_SYNTAXERR, "import not found")
+MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 0, JSEXN_SYNTAXERR, "ambiguous import")
+MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found for namespace")
+MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
+MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
+MSG_DEF(JSMSG_DYNAMIC_IMPORT_FAILED, 0, JSEXN_TYPEERR, "error loading dynamically imported module")
+
+// Promise
+MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.")
+MSG_DEF(JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.")
+MSG_DEF(JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
+MSG_DEF(JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
+MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "Promise rejection value is a non-unwrappable cross-compartment wrapper.")
+MSG_DEF(JSMSG_PROMISE_ANY_REJECTION, 0, JSEXN_AGGREGATEERR, "No Promise in Promise.any was resolved")
+
+// Iterator
+MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of iterator is not callable")
+MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
+
+// Async Function
+MSG_DEF(JSMSG_UNHANDLABLE_PROMISE_REJECTION_WARNING, 0, JSEXN_WARN, "unhandlable error after resolving async function's promise")
+
+// Async Iteration
+MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_SYNTAXERR, "'for await' loop should be used with 'of'")
+MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator")
+MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator")
+MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
+
+// ReadableStream
+MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSOURCE_TYPE_WRONG,0, JSEXN_RANGEERR,"'underlyingSource.type' must be \"bytes\" or undefined.")
+MSG_DEF(JSMSG_READABLESTREAM_BYTES_TYPE_NOT_IMPLEMENTED, 0, JSEXN_RANGEERR,"support for 'new ReadableStream({ type: \"bytes\" })' is not yet implemented")
+MSG_DEF(JSMSG_READABLESTREAM_BYOB_READER_FOR_NON_BYTE_STREAM,0,JSEXN_TYPEERR,"can't get a BYOB reader for a non-byte stream")
+MSG_DEF(JSMSG_READABLESTREAM_INVALID_READER_MODE, 0, JSEXN_RANGEERR,"'mode' must be \"byob\" or undefined.")
+MSG_DEF(JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, 1, JSEXN_RANGEERR, "'{0}' must be a finite, non-negative number.")
+MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_BYTESWRITTEN, 0, JSEXN_RANGEERR, "'bytesWritten' exceeds remaining length.")
+MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_SIZE, 0, JSEXN_RANGEERR, "view size does not match requested data.")
+MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_OFFSET, 0, JSEXN_RANGEERR, "view offset does not match requested position.")
+MSG_DEF(JSMSG_READABLESTREAM_LOCKED_METHOD, 1, JSEXN_TYPEERR, "'{0}' can't be called on a locked stream.")
+MSG_DEF(JSMSG_READABLESTREAM_LOCKED, 0, JSEXN_TYPEERR, "A Reader may only be created for an unlocked ReadableStream.")
+MSG_DEF(JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableByteStreamController.")
+MSG_DEF(JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableStreamDefaultController.")
+MSG_DEF(JSMSG_READABLESTREAM_CONTROLLER_SET, 0, JSEXN_TYPEERR, "The ReadableStream already has a controller defined.")
+MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_OWNED, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may only be called on a reader owned by a stream.")
+MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_EMPTY, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may not be called on a reader with read requests.")
+MSG_DEF(JSMSG_READABLESTREAMBYOBREADER_READ_EMPTY_VIEW, 0, JSEXN_TYPEERR, "ReadableStreamBYOBReader.read() was passed an empty TypedArrayBuffer view.")
+MSG_DEF(JSMSG_READABLESTREAMREADER_RELEASED, 0, JSEXN_TYPEERR, "The ReadableStream reader was released.")
+MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_CLOSED, 1, JSEXN_TYPEERR, "'{0}' called on a stream already closing.")
+MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, 1, JSEXN_TYPEERR, "'{0}' may only be called on a stream in the 'readable' state.")
+MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE,0, JSEXN_RANGEERR, "ReadableByteStreamController requires a positive integer or undefined for 'autoAllocateChunkSize'.")
+MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, 1, JSEXN_TYPEERR, "{0} passed a bad chunk.")
+MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL, 0, JSEXN_TYPEERR, "The ReadableByteStreamController cannot be closed while the buffer is being filled.")
+MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_NO_CONTROLLER, 1, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method '{0}' called on a request with no controller.")
+MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_RESPOND_CLOSED, 0, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method 'respond' called with non-zero number of bytes with a closed controller.")
+MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "ReadableStream method {0} not yet implemented")
+MSG_DEF(JSMSG_READABLESTREAM_PIPETO_BAD_SIGNAL, 0, JSEXN_TYPEERR, "signal must be either undefined or an AbortSignal")
+
+// WritableStream
+MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSINK_TYPE_WRONG, 0, JSEXN_RANGEERR,"'underlyingSink.type' must be undefined.")
+MSG_DEF(JSMSG_WRITABLESTREAMWRITER_NOT_OWNED, 1, JSEXN_TYPEERR, "the WritableStream writer method '{0}' may only be called on a writer owned by a stream")
+MSG_DEF(JSMSG_WRITABLESTREAM_CLOSED_OR_ERRORED, 0, JSEXN_TYPEERR, "writable stream is already closed or errored")
+MSG_DEF(JSMSG_WRITABLESTREAM_RELEASED_DURING_WRITE, 0, JSEXN_TYPEERR, "writer's lock on the stream was released before writing completed")
+MSG_DEF(JSMSG_WRITABLESTREAM_WRITE_CLOSING_OR_CLOSED, 0, JSEXN_TYPEERR, "can't write to a stream that's currently closing or already closed")
+MSG_DEF(JSMSG_CANT_USE_LOCKED_WRITABLESTREAM, 1, JSEXN_TYPEERR, "can't {0} a WritableStream that's locked to a writer")
+MSG_DEF(JSMSG_WRITABLESTREAM_CLOSE_CLOSING_OR_CLOSED, 0, JSEXN_TYPEERR, "can't close a stream that's currently closing or already closed")
+MSG_DEF(JSMSG_WRITABLESTREAM_CANT_RELEASE_ALREADY_CLOSED,0, JSEXN_TYPEERR, "writer has already been released and can't be closed")
+MSG_DEF(JSMSG_WRITABLESTREAM_ALREADY_LOCKED, 0, JSEXN_TYPEERR, "writable stream is already locked by another writer")
+MSG_DEF(JSMSG_READABLESTREAM_NYI, 0, JSEXN_ERR, "full WritableStream support is not yet implemented")
+
+// Other Stream-related
+MSG_DEF(JSMSG_STREAM_INVALID_HIGHWATERMARK, 0, JSEXN_RANGEERR, "'highWaterMark' must be a non-negative, non-NaN number.")
+MSG_DEF(JSMSG_STREAM_CONSUME_ERROR, 0, JSEXN_TYPEERR, "error consuming stream body")
+
+// (wasm) Response-related
+MSG_DEF(JSMSG_WASM_ERROR_CONSUMING_RESPONSE, 0, JSEXN_TYPEERR, "WebAssembly: There was an error consuming the Response")
+MSG_DEF(JSMSG_WASM_BAD_RESPONSE_VALUE, 0, JSEXN_TYPEERR, "WebAssembly: Expected Response or Promise resolving to Response")
+MSG_DEF(JSMSG_WASM_BAD_RESPONSE_MIME_TYPE, 2, JSEXN_TYPEERR, "WebAssembly: Response has unsupported MIME type '{0}' expected '{1}'")
+MSG_DEF(JSMSG_WASM_BAD_RESPONSE_CORS_SAME_ORIGIN, 0, JSEXN_TYPEERR, "WebAssembly: Response.type must be 'basic', 'cors' or 'default'")
+MSG_DEF(JSMSG_WASM_BAD_RESPONSE_STATUS, 0, JSEXN_TYPEERR, "WebAssembly: Response does not have ok status")
+MSG_DEF(JSMSG_WASM_RESPONSE_ALREADY_CONSUMED, 0, JSEXN_TYPEERR, "WebAssembly: Response already consumed")
+
+// BigInt
+MSG_DEF(JSMSG_BIGINT_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert BigInt to number")
+MSG_DEF(JSMSG_NONINTEGER_NUMBER_TO_BIGINT, 1, JSEXN_RANGEERR, "{0} can't be converted to BigInt because it isn't an integer")
+MSG_DEF(JSMSG_BIGINT_TOO_LARGE, 0, JSEXN_RANGEERR, "BigInt is too large to allocate")
+MSG_DEF(JSMSG_BIGINT_DIVISION_BY_ZERO, 0, JSEXN_RANGEERR, "BigInt division by zero")
+MSG_DEF(JSMSG_BIGINT_NEGATIVE_EXPONENT, 0, JSEXN_RANGEERR, "BigInt negative exponent")
+MSG_DEF(JSMSG_BIGINT_INVALID_SYNTAX, 0, JSEXN_SYNTAXERR, "invalid BigInt syntax")
+MSG_DEF(JSMSG_BIGINT_NOT_SERIALIZABLE, 0, JSEXN_TYPEERR, "BigInt value can't be serialized in JSON")
+MSG_DEF(JSMSG_SC_BIGINT_DISABLED, 0, JSEXN_ERR, "BigInt not cloned - feature disabled in receiver")
+
+// FinalizationRegistry
+MSG_DEF(JSMSG_NOT_A_FINALIZATION_REGISTRY, 1, JSEXN_TYPEERR, "{0} is not a FinalizationRegistry")
+MSG_DEF(JSMSG_NOT_A_FINALIZATION_ITERATOR, 1, JSEXN_TYPEERR, "{0} is not a FinalizationRegistryCleanupIterator")
+MSG_DEF(JSMSG_BAD_HELD_VALUE, 0, JSEXN_TYPEERR, "The heldValue parameter passed to FinalizationRegistry.register must not be the same as the target parameter")
+MSG_DEF(JSMSG_BAD_UNREGISTER_TOKEN, 1, JSEXN_TYPEERR, "Invalid unregister token passed to {0}")
+MSG_DEF(JSMSG_STALE_FINALIZATION_REGISTRY_ITERATOR, 0, JSEXN_TYPEERR, "Can't use stale finalization registry iterator")
+MSG_DEF(JSMSG_BAD_CLEANUP_STATE, 0, JSEXN_TYPEERR, "Can't call FinalizeRegistry.cleanupSome while cleanup is in progress")
+MSG_DEF(JSMSG_BAD_FINALIZATION_REGISTRY_OBJECT, 0, JSEXN_TYPEERR, "cannot register the given object with a FinalizationRegistry")
+
+// WeakRef
+MSG_DEF(JSMSG_NOT_A_WEAK_REF, 1, JSEXN_TYPEERR, "{0} is not a WeakRef")
+MSG_DEF(JSMSG_BAD_WEAKREF_TARGET, 0, JSEXN_TYPEERR, "cannot use the given object as the target of a WeakRef")
+
+// Iterator Helpers
+MSG_DEF(JSMSG_NEGATIVE_LIMIT, 0, JSEXN_RANGEERR, "Iterator limits cannot be negative")
+
+//clang-format on
diff --git a/js/public/friend/JSMEnvironment.h b/js/public/friend/JSMEnvironment.h
new file mode 100644
index 0000000000..cdbb7ec359
--- /dev/null
+++ b/js/public/friend/JSMEnvironment.h
@@ -0,0 +1,93 @@
+/* -*- 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/. */
+
+/*
+ * Functionality provided for the JSM component loader in Gecko, that requires
+ * its own unique manner of global environment and currently requires assistance
+ * from SpiderMonkey to do so.
+ *
+ * Embedders who aren't Gecko can ignore this header.
+ */
+
+#ifndef js_friend_JSMEnvironment_h
+#define js_friend_JSMEnvironment_h
+
+#include "jstypes.h" // JS_FRIEND_API, JS_PUBLIC_API
+
+#include "js/GCVector.h" // JS::StackGCVector
+#include "js/TypeDecls.h"
+
+// A 'JSMEnvironment' refers to an environment chain constructed for JSM loading
+// in a shared global. Internally it is a NonSyntacticVariablesObject with a
+// corresponding extensible LexicalEnvironmentObject that is accessible by
+// JS_ExtensibleLexicalEnvironment. The |this| value of that lexical environment
+// is the NSVO itself.
+//
+// Normal global environment (ES6): JSM "global" environment:
+//
+// * - extensible lexical environment
+// | (code runs in this environment;
+// | `let/const` bindings go here)
+// |
+// * - JSMEnvironment (=== `this`)
+// | (`var` bindings go here)
+// |
+// * - extensible lexical environment * - extensible lexical environment
+// | (code runs in this environment; | (empty)
+// | `let/const` bindings go here) |
+// | |
+// * - actual global (=== `this`) * - shared JSM global
+// (var bindings go here; and (Object, Math, etc. live here)
+// Object, Math, etc. live here)
+
+namespace JS {
+
+/**
+ * Allocate a new environment in the current compartment that is compatible with
+ * JSM shared loading.
+ */
+extern JS_FRIEND_API JSObject* NewJSMEnvironment(JSContext* cx);
+
+/**
+ * Execute the given script (copied into the current compartment if necessary)
+ * in the given JSMEnvironment. The script must have been compiled for
+ * hasNonSyntacticScope. The |jsmEnv| must have been previously allocated by
+ * |NewJSMEnvironment|.
+ *
+ * NOTE: The associated extensible lexical environment is reused.
+ */
+extern JS_FRIEND_API bool ExecuteInJSMEnvironment(JSContext* cx,
+ Handle<JSScript*> script,
+ Handle<JSObject*> jsmEnv);
+
+// Additionally, target objects may be specified as required by the Gecko
+// subscript loader. These are wrapped in non-syntactic WithEnvironments and
+// temporarily placed on the environment chain.
+//
+// See also: JS::CloneAndExecuteScript(...)
+extern JS_FRIEND_API bool ExecuteInJSMEnvironment(
+ JSContext* cx, Handle<JSScript*> script, Handle<JSObject*> jsmEnv,
+ Handle<StackGCVector<JSObject*>> targetObj);
+
+// Used by native methods to determine the JSMEnvironment of caller if possible
+// by looking at stack frames. Returns nullptr if top frame isn't a scripted
+// caller in a JSM.
+//
+// NOTE: This may find NonSyntacticVariablesObject generated by other embedding
+// such as a Gecko FrameScript. Caller can check the compartment if needed.
+extern JS_FRIEND_API JSObject* GetJSMEnvironmentOfScriptedCaller(JSContext* cx);
+
+/**
+ * Determine if obj is a JSMEnvironment
+ *
+ * NOTE: This may return true for an NonSyntacticVariablesObject generated by
+ * other embedding such as a Gecko FrameScript. Caller can check compartment.
+ */
+extern JS_FRIEND_API bool IsJSMEnvironment(JSObject* obj);
+
+} // namespace JS
+
+#endif // js_friend_JSMEnvironment_h
diff --git a/js/public/friend/StackLimits.h b/js/public/friend/StackLimits.h
new file mode 100644
index 0000000000..57168d222f
--- /dev/null
+++ b/js/public/friend/StackLimits.h
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_friend_StackLimits_h
+#define js_friend_StackLimits_h
+
+#include "mozilla/Attributes.h" // MOZ_ALWAYS_INLINE, MOZ_COLD
+#include "mozilla/Likely.h" // MOZ_LIKELY
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uintptr_t
+
+#include "jstypes.h" // JS_FRIEND_API
+
+#include "js/HeapAPI.h" // JS::StackKind, JS::StackForTrustedScript, JS::StackForUntrustedScript
+#include "js/RootingAPI.h" // JS::RootingContext
+#include "js/Utility.h" // JS_STACK_OOM_POSSIBLY_FAIL_REPORT, JS_STACK_OOM_POSSIBLY_FAIL
+
+struct JS_PUBLIC_API JSContext;
+
+#ifndef JS_STACK_GROWTH_DIRECTION
+# ifdef __hppa
+# define JS_STACK_GROWTH_DIRECTION (1)
+# else
+# define JS_STACK_GROWTH_DIRECTION (-1)
+# endif
+#endif
+
+#if JS_STACK_GROWTH_DIRECTION > 0
+# define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) < (limit)))
+#else
+# define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) > (limit)))
+#endif
+
+namespace js {
+
+namespace detail {
+
+extern JS_FRIEND_API bool RunningWithTrustedPrincipals(JSContext* cx);
+
+MOZ_ALWAYS_INLINE uintptr_t GetNativeStackLimitHelper(JSContext* cx,
+ JS::StackKind kind,
+ int extraAllowance) {
+ uintptr_t limit = JS::RootingContext::get(cx)->nativeStackLimit[kind];
+#if JS_STACK_GROWTH_DIRECTION > 0
+ limit += extraAllowance;
+#else
+ limit -= extraAllowance;
+#endif
+ return limit;
+}
+
+} // namespace detail
+
+extern MOZ_COLD JS_FRIEND_API void ReportOverRecursed(JSContext* maybecx);
+
+MOZ_ALWAYS_INLINE uintptr_t GetNativeStackLimit(JSContext* cx) {
+ JS::StackKind kind = detail::RunningWithTrustedPrincipals(cx)
+ ? JS::StackForTrustedScript
+ : JS::StackForUntrustedScript;
+ return detail::GetNativeStackLimitHelper(cx, kind, 0);
+}
+
+/*
+ * These functions return |false| if we are close to using up the C++ stack.
+ * They also report an overrecursion error, except for the DontReport variants.
+ * The CheckSystemRecursionLimit variant gives us a little extra space so we
+ * can ensure that crucial code is able to run. CheckRecursionLimitConservative
+ * allows less space than any other check, including a safety buffer (as in, it
+ * uses the untrusted limit and subtracts a little more from it).
+ */
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimit(JSContext* cx, uintptr_t limit) {
+ int stackDummy;
+
+ JS_STACK_OOM_POSSIBLY_FAIL_REPORT();
+
+ if (!JS_CHECK_STACK_SIZE(limit, &stackDummy)) {
+ ReportOverRecursed(cx);
+ return false;
+ }
+
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimitDontReport(uintptr_t limit) {
+ int stackDummy;
+
+ JS_STACK_OOM_POSSIBLY_FAIL();
+
+ return JS_CHECK_STACK_SIZE(limit, &stackDummy);
+}
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimit(JSContext* cx) {
+ JS_STACK_OOM_POSSIBLY_FAIL_REPORT();
+
+ // GetNativeStackLimit(cx) is pretty slow because it has to do an uninlined
+ // call to RunningWithTrustedPrincipals to determine which stack limit to
+ // use. To work around this, check the untrusted limit first to avoid the
+ // overhead in most cases.
+ uintptr_t untrustedLimit =
+ detail::GetNativeStackLimitHelper(cx, JS::StackForUntrustedScript, 0);
+ if (MOZ_LIKELY(CheckRecursionLimitDontReport(untrustedLimit))) {
+ return true;
+ }
+ return CheckRecursionLimit(cx, GetNativeStackLimit(cx));
+}
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimitDontReport(JSContext* cx) {
+ return CheckRecursionLimitDontReport(GetNativeStackLimit(cx));
+}
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimitWithStackPointerDontReport(
+ JSContext* cx, void* sp) {
+ JS_STACK_OOM_POSSIBLY_FAIL();
+
+ return JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), sp);
+}
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimitWithStackPointer(JSContext* cx,
+ void* sp) {
+ JS_STACK_OOM_POSSIBLY_FAIL_REPORT();
+
+ if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), sp)) {
+ ReportOverRecursed(cx);
+ return false;
+ }
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimitWithExtra(JSContext* cx,
+ size_t extra) {
+ char stackDummy;
+ char* sp = &stackDummy;
+#if JS_STACK_GROWTH_DIRECTION > 0
+ sp += extra;
+#else
+ sp -= extra;
+#endif
+ return CheckRecursionLimitWithStackPointer(cx, sp);
+}
+
+MOZ_ALWAYS_INLINE bool CheckSystemRecursionLimit(JSContext* cx) {
+ return CheckRecursionLimit(
+ cx, detail::GetNativeStackLimitHelper(cx, JS::StackForSystemCode, 0));
+}
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimitConservative(JSContext* cx) {
+ return CheckRecursionLimit(
+ cx, detail::GetNativeStackLimitHelper(cx, JS::StackForUntrustedScript,
+ -1024 * int(sizeof(size_t))));
+}
+
+MOZ_ALWAYS_INLINE bool CheckRecursionLimitConservativeDontReport(
+ JSContext* cx) {
+ return CheckRecursionLimitDontReport(detail::GetNativeStackLimitHelper(
+ cx, JS::StackForUntrustedScript, -1024 * int(sizeof(size_t))));
+}
+
+} // namespace js
+
+#endif // js_friend_StackLimits_h
diff --git a/js/public/friend/UsageStatistics.h b/js/public/friend/UsageStatistics.h
new file mode 100644
index 0000000000..904d277d3a
--- /dev/null
+++ b/js/public/friend/UsageStatistics.h
@@ -0,0 +1,93 @@
+/* -*- 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/. */
+
+/* Telemetry and use counter functionality. */
+
+#ifndef js_friend_UsageStatistics_h
+#define js_friend_UsageStatistics_h
+
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_FRIEND_API
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+/*
+ * Telemetry reasons passed to the accumulate telemetry callback.
+ *
+ * It's OK for these enum values to change as they will be mapped to a fixed
+ * member of the mozilla::Telemetry::HistogramID enum by the callback.
+ */
+#define MAP_JS_TELEMETRY(_) \
+ _(JS_TELEMETRY_GC_REASON) \
+ _(JS_TELEMETRY_GC_IS_ZONE_GC) \
+ _(JS_TELEMETRY_GC_MS) \
+ _(JS_TELEMETRY_GC_BUDGET_MS_2) \
+ _(JS_TELEMETRY_GC_BUDGET_OVERRUN) \
+ _(JS_TELEMETRY_GC_ANIMATION_MS) \
+ _(JS_TELEMETRY_GC_MAX_PAUSE_MS_2) \
+ _(JS_TELEMETRY_GC_PREPARE_MS) \
+ _(JS_TELEMETRY_GC_MARK_MS) \
+ _(JS_TELEMETRY_GC_SWEEP_MS) \
+ _(JS_TELEMETRY_GC_COMPACT_MS) \
+ _(JS_TELEMETRY_GC_MARK_ROOTS_US) \
+ _(JS_TELEMETRY_GC_MARK_GRAY_MS_2) \
+ _(JS_TELEMETRY_GC_MARK_WEAK_MS) \
+ _(JS_TELEMETRY_GC_SLICE_MS) \
+ _(JS_TELEMETRY_GC_SLOW_PHASE) \
+ _(JS_TELEMETRY_GC_SLOW_TASK) \
+ _(JS_TELEMETRY_GC_MMU_50) \
+ _(JS_TELEMETRY_GC_RESET) \
+ _(JS_TELEMETRY_GC_RESET_REASON) \
+ _(JS_TELEMETRY_GC_NON_INCREMENTAL) \
+ _(JS_TELEMETRY_GC_NON_INCREMENTAL_REASON) \
+ _(JS_TELEMETRY_GC_MINOR_REASON) \
+ _(JS_TELEMETRY_GC_MINOR_REASON_LONG) \
+ _(JS_TELEMETRY_GC_MINOR_US) \
+ _(JS_TELEMETRY_GC_NURSERY_BYTES) \
+ _(JS_TELEMETRY_GC_PRETENURE_COUNT_2) \
+ _(JS_TELEMETRY_GC_NURSERY_PROMOTION_RATE) \
+ _(JS_TELEMETRY_GC_TENURED_SURVIVAL_RATE) \
+ _(JS_TELEMETRY_GC_MARK_RATE_2) \
+ _(JS_TELEMETRY_GC_TIME_BETWEEN_S) \
+ _(JS_TELEMETRY_GC_TIME_BETWEEN_SLICES_MS) \
+ _(JS_TELEMETRY_GC_SLICE_COUNT) \
+ _(JS_TELEMETRY_GC_EFFECTIVENESS) \
+ _(JS_TELEMETRY_RUN_TIME_US) \
+ _(JS_TELEMETRY_WASM_COMPILE_TIME_BASELINE_US) \
+ _(JS_TELEMETRY_WASM_COMPILE_TIME_ION_US) \
+ _(JS_TELEMETRY_WASM_COMPILE_TIME_CRANELIFT_US)
+
+// clang-format off
+#define ENUM_DEF(NAME) NAME ,
+enum {
+ MAP_JS_TELEMETRY(ENUM_DEF)
+ JS_TELEMETRY_END
+};
+#undef ENUM_DEF
+// clang-format on
+
+using JSAccumulateTelemetryDataCallback = void (*)(int, uint32_t, const char*);
+
+extern JS_FRIEND_API void JS_SetAccumulateTelemetryCallback(
+ JSContext* cx, JSAccumulateTelemetryDataCallback callback);
+
+/*
+ * Use counter names passed to the accumulate use counter callback.
+ *
+ * It's OK to for these enum values to change as they will be mapped to a
+ * fixed member of the mozilla::UseCounter enum by the callback.
+ */
+
+enum class JSUseCounter { ASMJS, WASM, WASM_DUPLICATE_IMPORTS };
+
+using JSSetUseCounterCallback = void (*)(JSObject*, JSUseCounter);
+
+extern JS_FRIEND_API void JS_SetSetUseCounterCallback(
+ JSContext* cx, JSSetUseCounterCallback callback);
+
+#endif // js_friend_UsageStatistics_h
diff --git a/js/public/friend/WindowProxy.h b/js/public/friend/WindowProxy.h
new file mode 100644
index 0000000000..95a293f617
--- /dev/null
+++ b/js/public/friend/WindowProxy.h
@@ -0,0 +1,106 @@
+/* -*- 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/. */
+
+/*
+ * Window and WindowProxy.
+ *
+ * For silly obscure reasons embedders are better off not knowing, the web wants
+ * every global object to exist as two linked components: a Window component
+ * that stores global variables and appears in environment chains but can't be
+ * directly referred to by any script, and a WindowProxy component that
+ * intermediates access to its Window that *can* be directly referred to by
+ * script. (Thus the global |window| and |globalThis| properties, |this| in
+ * global code, the value of |(function() { return this; })()| in non-strict
+ * mode code, and similar values are WindowProxy objects, not Windows.)
+ *
+ * Maintaining an invariant of never exposing a Window to script requires
+ * substituting in its WindowProxy in a variety of apparently arbitrary (but
+ * actually *very* carefully and nervously selected) places throughout the
+ * engine and indeed the universe.
+ *
+ * This header defines functions that let embeddings convert from a WindowProxy
+ * to its Window and vice versa.
+ *
+ * If you're not embedding SpiderMonkey in a web browser, you can almost
+ * certainly ignore this header.
+ */
+
+#ifndef js_friend_WindowProxy_h
+#define js_friend_WindowProxy_h
+
+#include "jstypes.h" // JS_FRIEND_API, JS_PUBLIC_API
+
+#include "js/Class.h" // JSCLASS_IS_GLOBAL
+#include "js/Object.h" // JS::GetClass
+#include "js/RootingAPI.h" // JS::Handle
+
+struct JSClass;
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace js {
+
+/**
+ * Tell the JS engine which Class is used for WindowProxy objects. Used by the
+ * functions below.
+ */
+extern JS_FRIEND_API void SetWindowProxyClass(JSContext* cx,
+ const JSClass* clasp);
+
+/**
+ * Associates a WindowProxy with a Window (global object). `windowProxy` must
+ * have the Class set by SetWindowProxyClass.
+ */
+extern JS_FRIEND_API void SetWindowProxy(JSContext* cx,
+ JS::Handle<JSObject*> global,
+ JS::Handle<JSObject*> windowProxy);
+
+namespace detail {
+
+extern JS_FRIEND_API bool IsWindowSlow(JSObject* obj);
+
+extern JS_FRIEND_API JSObject* ToWindowProxyIfWindowSlow(JSObject* obj);
+
+} // namespace detail
+
+/**
+ * Returns true iff `obj` is a global object with an associated WindowProxy,
+ * see SetWindowProxy.
+ */
+inline bool IsWindow(JSObject* obj) {
+ if (JS::GetClass(obj)->flags & JSCLASS_IS_GLOBAL) {
+ return detail::IsWindowSlow(obj);
+ }
+ return false;
+}
+
+/**
+ * Returns true iff `obj` has the WindowProxy Class (see SetWindowProxyClass).
+ */
+extern JS_FRIEND_API bool IsWindowProxy(JSObject* obj);
+
+/**
+ * If `obj` is a Window, get its associated WindowProxy (or a CCW or dead
+ * wrapper if the page was navigated away from), else return `obj`. This
+ * function is infallible and never returns nullptr.
+ */
+MOZ_ALWAYS_INLINE JSObject* ToWindowProxyIfWindow(JSObject* obj) {
+ if (JS::GetClass(obj)->flags & JSCLASS_IS_GLOBAL) {
+ return detail::ToWindowProxyIfWindowSlow(obj);
+ }
+ return obj;
+}
+
+/**
+ * If `obj` is a WindowProxy, get its associated Window (the compartment's
+ * global), else return `obj`. This function is infallible and never returns
+ * nullptr.
+ */
+extern JS_FRIEND_API JSObject* ToWindowIfWindowProxy(JSObject* obj);
+
+} // namespace js
+
+#endif // js_friend_WindowProxy_h
diff --git a/js/public/friend/XrayJitInfo.h b/js/public/friend/XrayJitInfo.h
new file mode 100644
index 0000000000..50344ce327
--- /dev/null
+++ b/js/public/friend/XrayJitInfo.h
@@ -0,0 +1,57 @@
+/* -*- 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/. */
+
+/*
+ * JIT info so SpiderMonkey can efficiently work with Gecko XrayWrapper
+ * instances.
+ *
+ * This header is completely irrelevant to non-Gecko embedders.
+ */
+
+#ifndef js_friend_XrayJitInfo_h
+#define js_friend_XrayJitInfo_h
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_FRIEND_API, JS_PUBLIC_API
+
+class JS_PUBLIC_API JSObject;
+
+namespace js {
+
+class JS_FRIEND_API BaseProxyHandler;
+
+} // namespace js
+
+namespace JS {
+
+// Callbacks and other information for use by the JITs when optimizing accesses
+// on xray wrappers.
+struct XrayJitInfo {
+ // Test whether a proxy handler is a cross compartment xray with no
+ // security checks.
+ bool (*isCrossCompartmentXray)(const js::BaseProxyHandler* handler);
+
+ // Test whether xrays in |obj|'s compartment have expandos of their own,
+ // instead of sharing them with Xrays from other compartments.
+ bool (*compartmentHasExclusiveExpandos)(JSObject* obj);
+
+ // Proxy reserved slot used by xrays in sandboxes to store their holder
+ // object.
+ size_t xrayHolderSlot;
+
+ // Reserved slot used by xray holders to store the xray's expando object.
+ size_t holderExpandoSlot;
+
+ // Reserved slot used by xray expandos to store a custom prototype.
+ size_t expandoProtoSlot;
+};
+
+extern JS_FRIEND_API void SetXrayJitInfo(XrayJitInfo* info);
+
+} // namespace JS
+
+#endif // js_friend_XrayJitInfo_h
diff --git a/js/public/shadow/Function.h b/js/public/shadow/Function.h
new file mode 100644
index 0000000000..0b9d4ac214
--- /dev/null
+++ b/js/public/shadow/Function.h
@@ -0,0 +1,48 @@
+/* -*- 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/. */
+
+/* Shadow definition of |JSFunction| innards. Do not use this directly! */
+
+#ifndef js_shadow_Function_h
+#define js_shadow_Function_h
+
+#include <stdint.h> // uint16_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/CallArgs.h" // JSNative
+#include "js/shadow/Object.h" // JS::shadow::Object
+
+class JS_PUBLIC_API JSFunction;
+class JSJitInfo;
+
+namespace JS {
+
+namespace shadow {
+
+struct Function {
+ shadow::Object base;
+ uint16_t nargs;
+ uint16_t flags;
+ /* Used only for natives */
+ JSNative native;
+ const JSJitInfo* jitinfo;
+ void* _1;
+};
+
+inline Function* AsShadowFunction(JSFunction* fun) {
+ return reinterpret_cast<Function*>(fun);
+}
+
+inline const Function* AsShadowFunction(const JSFunction* fun) {
+ return reinterpret_cast<const Function*>(fun);
+}
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_Function_h
diff --git a/js/public/shadow/Object.h b/js/public/shadow/Object.h
new file mode 100644
index 0000000000..68badf9b2b
--- /dev/null
+++ b/js/public/shadow/Object.h
@@ -0,0 +1,67 @@
+/* -*- 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/. */
+
+/*
+ * Shadow definition of |JSObject| innards. (|js::NativeObject| is more
+ * accurate, but portions can sometimes be used in some non-native objects.) Do
+ * not use this directly!
+ */
+
+#ifndef js_shadow_Object_h
+#define js_shadow_Object_h
+
+#include <stddef.h> // size_t
+
+#include "js/shadow/Shape.h" // JS::shadow::Shape
+#include "js/Value.h" // JS::Value
+
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API Value;
+
+namespace shadow {
+
+struct ObjectGroup;
+
+/**
+ * This layout is shared by all native objects. For non-native objects, the
+ * group may always be accessed safely, and other members may be as well,
+ * depending on the object's specific layout.
+ */
+struct Object {
+ shadow::ObjectGroup* group;
+ shadow::Shape* shape;
+ Value* slots;
+ void* _1;
+
+ static constexpr size_t MAX_FIXED_SLOTS = 16;
+
+ size_t numFixedSlots() const {
+ return (shape->immutableFlags & shadow::Shape::FIXED_SLOTS_MASK) >>
+ shadow::Shape::FIXED_SLOTS_SHIFT;
+ }
+
+ Value* fixedSlots() const {
+ auto address = reinterpret_cast<uintptr_t>(this);
+ return reinterpret_cast<JS::Value*>(address + sizeof(shadow::Object));
+ }
+
+ Value& slotRef(size_t slot) const {
+ size_t nfixed = numFixedSlots();
+ if (slot < nfixed) {
+ return fixedSlots()[slot];
+ }
+ return slots[slot - nfixed];
+ }
+};
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_Object_h
diff --git a/js/public/shadow/ObjectGroup.h b/js/public/shadow/ObjectGroup.h
new file mode 100644
index 0000000000..f95f60ced1
--- /dev/null
+++ b/js/public/shadow/ObjectGroup.h
@@ -0,0 +1,33 @@
+/* -*- 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/. */
+
+/* Shadow definition of |js::ObjectGroup| innards. Do not use this directly! */
+
+#ifndef js_shadow_ObjectGroup_h
+#define js_shadow_ObjectGroup_h
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JSClass;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API Realm;
+
+namespace shadow {
+
+struct ObjectGroup {
+ const JSClass* clasp;
+ JSObject* proto;
+ JS::Realm* realm;
+};
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_ObjectGroup_h
diff --git a/js/public/shadow/Realm.h b/js/public/shadow/Realm.h
new file mode 100644
index 0000000000..e3243d3483
--- /dev/null
+++ b/js/public/shadow/Realm.h
@@ -0,0 +1,38 @@
+/* -*- 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/. */
+
+/* Shadow definition of |JS::Realm| innards. Do not use this directly! */
+
+#ifndef js_shadow_Realm_h
+#define js_shadow_Realm_h
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+namespace JS {
+
+class JS_PUBLIC_API Compartment;
+class JS_PUBLIC_API Realm;
+
+namespace shadow {
+
+class Realm {
+ protected:
+ JS::Compartment* compartment_;
+
+ explicit Realm(JS::Compartment* comp) : compartment_(comp) {}
+
+ public:
+ JS::Compartment* compartment() { return compartment_; }
+ static shadow::Realm* get(JS::Realm* realm) {
+ return reinterpret_cast<shadow::Realm*>(realm);
+ }
+};
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_Realm_h
diff --git a/js/public/shadow/Shape.h b/js/public/shadow/Shape.h
new file mode 100644
index 0000000000..754f465162
--- /dev/null
+++ b/js/public/shadow/Shape.h
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+/*
+ * Shadow definition of |js::BaseShape| and |js::Shape| innards. Do not use
+ * this directly!
+ */
+
+#ifndef js_shadow_Shape_h
+#define js_shadow_Shape_h
+
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Id.h" // JS::PropertyKey
+
+struct JSClass;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+namespace shadow {
+
+struct BaseShape {
+ const JSClass* clasp_;
+};
+
+class Shape {
+ public:
+ shadow::BaseShape* base;
+ JS::PropertyKey _1;
+ uint32_t immutableFlags;
+
+ static constexpr uint32_t FIXED_SLOTS_SHIFT = 24;
+ static constexpr uint32_t FIXED_SLOTS_MASK = 0x1f << FIXED_SLOTS_SHIFT;
+};
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_Shape_h
diff --git a/js/public/shadow/String.h b/js/public/shadow/String.h
new file mode 100644
index 0000000000..570c9f189b
--- /dev/null
+++ b/js/public/shadow/String.h
@@ -0,0 +1,120 @@
+/* -*- 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/. */
+
+/* Shadow definition of |JSString| innards. Don't use this directly! */
+
+#ifndef js_shadow_String_h
+#define js_shadow_String_h
+
+#include <stdint.h> // uint32_t, uintptr_t
+
+#include "jstypes.h" // JS_PUBLIC_API, js::Bit, js::BitMask, JS_BITS_PER_WORD
+
+#include "js/TypeDecls.h" // JS::Latin1Char
+
+class JS_PUBLIC_API JSAtom;
+struct JSExternalStringCallbacks;
+class JSLinearString;
+class JS_PUBLIC_API JSString;
+
+namespace js {
+namespace gc {
+struct Cell;
+} // namespace gc
+} // namespace js
+
+namespace JS {
+
+namespace shadow {
+
+struct String {
+ static constexpr uint32_t ATOM_BIT = js::Bit(3);
+ static constexpr uint32_t LINEAR_BIT = js::Bit(4);
+ static constexpr uint32_t INLINE_CHARS_BIT = js::Bit(6);
+ static constexpr uint32_t LATIN1_CHARS_BIT = js::Bit(9);
+ static constexpr uint32_t EXTERNAL_FLAGS = LINEAR_BIT | js::Bit(8);
+ static constexpr uint32_t TYPE_FLAGS_MASK = js::BitMask(9) - js::BitMask(3);
+ static constexpr uint32_t PERMANENT_ATOM_MASK = ATOM_BIT | js::Bit(8);
+
+ uintptr_t flags_;
+#if JS_BITS_PER_WORD == 32
+ uint32_t length_;
+#endif
+
+ union {
+ const JS::Latin1Char* nonInlineCharsLatin1;
+ const char16_t* nonInlineCharsTwoByte;
+ JS::Latin1Char inlineStorageLatin1[1];
+ char16_t inlineStorageTwoByte[1];
+ };
+ const JSExternalStringCallbacks* externalCallbacks;
+
+ uint32_t flags() const { return static_cast<uint32_t>(flags_); }
+ uint32_t length() const {
+#if JS_BITS_PER_WORD == 32
+ return length_;
+#else
+ return static_cast<uint32_t>(flags_ >> 32);
+#endif
+ }
+
+ static bool isPermanentAtom(const js::gc::Cell* cell) {
+ uint32_t flags = reinterpret_cast<const String*>(cell)->flags();
+ return (flags & PERMANENT_ATOM_MASK) == PERMANENT_ATOM_MASK;
+ }
+
+ bool isLinear() const { return flags() & LINEAR_BIT; }
+ bool hasLatin1Chars() const { return flags() & LATIN1_CHARS_BIT; }
+
+ // For hot code, prefer other type queries.
+ bool isExternal() const {
+ return (flags() & TYPE_FLAGS_MASK) == EXTERNAL_FLAGS;
+ }
+
+ const JS::Latin1Char* latin1LinearChars() const {
+ MOZ_ASSERT(isLinear());
+ MOZ_ASSERT(hasLatin1Chars());
+ return (flags() & String::INLINE_CHARS_BIT) ? inlineStorageLatin1
+ : nonInlineCharsLatin1;
+ }
+
+ const char16_t* twoByteLinearChars() const {
+ MOZ_ASSERT(isLinear());
+ MOZ_ASSERT(!hasLatin1Chars());
+ return (flags() & String::INLINE_CHARS_BIT) ? inlineStorageTwoByte
+ : nonInlineCharsTwoByte;
+ }
+};
+
+inline const String* AsShadowString(const JSString* str) {
+ return reinterpret_cast<const String*>(str);
+}
+
+inline String* AsShadowString(JSString* str) {
+ return reinterpret_cast<String*>(str);
+}
+
+inline const String* AsShadowString(const JSLinearString* str) {
+ return reinterpret_cast<const String*>(str);
+}
+
+inline String* AsShadowString(JSLinearString* str) {
+ return reinterpret_cast<String*>(str);
+}
+
+inline const String* AsShadowString(const JSAtom* str) {
+ return reinterpret_cast<const String*>(str);
+}
+
+inline String* AsShadowString(JSAtom* str) {
+ return reinterpret_cast<String*>(str);
+}
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_String_h
diff --git a/js/public/shadow/Symbol.h b/js/public/shadow/Symbol.h
new file mode 100644
index 0000000000..a4b40139cc
--- /dev/null
+++ b/js/public/shadow/Symbol.h
@@ -0,0 +1,38 @@
+/* -*- 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/. */
+
+/* Shadow definition of |JS::Symbol| innards. Do not use this directly! */
+
+#ifndef js_shadow_Symbol_h
+#define js_shadow_Symbol_h
+
+#include <stdint.h> // uint32_t
+
+namespace js {
+namespace gc {
+struct Cell;
+} // namespace gc
+} // namespace js
+
+namespace JS {
+
+namespace shadow {
+
+struct Symbol {
+ void* _1;
+ uint32_t code_;
+ static constexpr uint32_t WellKnownAPILimit = 0x80000000;
+
+ static bool isWellKnownSymbol(const js::gc::Cell* cell) {
+ return reinterpret_cast<const Symbol*>(cell)->code_ < WellKnownAPILimit;
+ }
+};
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_Symbol_h
diff --git a/js/public/shadow/Zone.h b/js/public/shadow/Zone.h
new file mode 100644
index 0000000000..a8d9c70dc4
--- /dev/null
+++ b/js/public/shadow/Zone.h
@@ -0,0 +1,97 @@
+/* -*- 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/. */
+
+/* Shadow definition of |JS::Zone| innards. Do not use this directly! */
+
+#ifndef js_shadow_Zone_h
+#define js_shadow_Zone_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include <stdint.h> // uint8_t, uint32_t
+
+#include "jspubtd.h" // js::CurrentThreadCanAccessRuntime
+
+struct JS_PUBLIC_API JSRuntime;
+class JS_PUBLIC_API JSTracer;
+
+namespace JS {
+
+namespace shadow {
+
+struct Zone {
+ enum GCState : uint8_t {
+ NoGC,
+ Prepare,
+ MarkBlackOnly,
+ MarkBlackAndGray,
+ Sweep,
+ Finished,
+ Compact
+ };
+
+ enum Kind : uint8_t { NormalZone, AtomsZone, SelfHostingZone, SystemZone };
+
+ protected:
+ JSRuntime* const runtime_;
+ JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|.
+ uint32_t needsIncrementalBarrier_ = 0;
+ GCState gcState_ = NoGC;
+ const Kind kind_;
+
+ Zone(JSRuntime* runtime, JSTracer* barrierTracerArg, Kind kind)
+ : runtime_(runtime), barrierTracer_(barrierTracerArg), kind_(kind) {}
+
+ public:
+ bool needsIncrementalBarrier() const { return needsIncrementalBarrier_; }
+
+ JSTracer* barrierTracer() {
+ MOZ_ASSERT(needsIncrementalBarrier_);
+ MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
+ return barrierTracer_;
+ }
+
+ JSRuntime* runtimeFromMainThread() const {
+ MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
+ return runtime_;
+ }
+
+ // Note: Unrestricted access to the zone's runtime from an arbitrary
+ // thread can easily lead to races. Use this method very carefully.
+ JSRuntime* runtimeFromAnyThread() const { return runtime_; }
+
+ GCState gcState() const { return gcState_; }
+ bool wasGCStarted() const { return gcState_ != NoGC; }
+ bool isGCPreparing() const { return gcState_ == Prepare; }
+ bool isGCMarkingBlackOnly() const { return gcState_ == MarkBlackOnly; }
+ bool isGCMarkingBlackAndGray() const { return gcState_ == MarkBlackAndGray; }
+ bool isGCSweeping() const { return gcState_ == Sweep; }
+ bool isGCFinished() const { return gcState_ == Finished; }
+ bool isGCCompacting() const { return gcState_ == Compact; }
+ bool isGCMarking() const {
+ return isGCMarkingBlackOnly() || isGCMarkingBlackAndGray();
+ }
+ bool isGCMarkingOrSweeping() const {
+ return gcState_ >= MarkBlackOnly && gcState_ <= Sweep;
+ }
+ bool isGCSweepingOrCompacting() const {
+ return gcState_ == Sweep || gcState_ == Compact;
+ }
+
+ bool isAtomsZone() const { return kind_ == AtomsZone; }
+ bool isSelfHostingZone() const { return kind_ == SelfHostingZone; }
+ bool isSystemZone() const { return kind_ == SystemZone; }
+
+ static shadow::Zone* from(JS::Zone* zone) {
+ return reinterpret_cast<shadow::Zone*>(zone);
+ }
+};
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_Zone_h