summaryrefslogtreecommitdiffstats
path: root/js/public
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/public/AllocPolicy.h243
-rw-r--r--js/public/AllocationLogging.h75
-rw-r--r--js/public/AllocationRecording.h73
-rw-r--r--js/public/Array.h120
-rw-r--r--js/public/ArrayBuffer.h380
-rw-r--r--js/public/ArrayBufferMaybeShared.h109
-rw-r--r--js/public/BigInt.h229
-rw-r--r--js/public/BuildId.h82
-rw-r--r--js/public/CallAndConstruct.h143
-rw-r--r--js/public/CallArgs.h364
-rw-r--r--js/public/CallNonGenericMethod.h123
-rw-r--r--js/public/CharacterEncoding.h448
-rw-r--r--js/public/Class.h837
-rw-r--r--js/public/ColumnNumber.h418
-rw-r--r--js/public/ComparisonOperators.h237
-rw-r--r--js/public/CompilationAndEvaluation.h233
-rw-r--r--js/public/CompileOptions.h864
-rw-r--r--js/public/Context.h107
-rw-r--r--js/public/ContextOptions.h255
-rw-r--r--js/public/Conversions.h601
-rw-r--r--js/public/Date.h222
-rw-r--r--js/public/Debug.h354
-rw-r--r--js/public/Equality.h65
-rw-r--r--js/public/ErrorInterceptor.h45
-rw-r--r--js/public/ErrorReport.h545
-rw-r--r--js/public/Exception.h196
-rw-r--r--js/public/ForOfIterator.h118
-rw-r--r--js/public/GCAPI.h1361
-rw-r--r--js/public/GCAnnotations.h116
-rw-r--r--js/public/GCHashTable.h370
-rw-r--r--js/public/GCPolicyAPI.h235
-rw-r--r--js/public/GCTypeMacros.h45
-rw-r--r--js/public/GCVariant.h182
-rw-r--r--js/public/GCVector.h366
-rw-r--r--js/public/GlobalObject.h99
-rw-r--r--js/public/HashTable.h38
-rw-r--r--js/public/HeapAPI.h838
-rw-r--r--js/public/HelperThreadAPI.h40
-rw-r--r--js/public/Id.h371
-rw-r--r--js/public/Initialization.h210
-rw-r--r--js/public/Interrupt.h41
-rw-r--r--js/public/Iterator.h39
-rw-r--r--js/public/JSON.h188
-rw-r--r--js/public/JitCodeAPI.h97
-rw-r--r--js/public/LocaleSensitive.h95
-rw-r--r--js/public/MapAndSet.h85
-rw-r--r--js/public/MemoryCallbacks.h47
-rw-r--r--js/public/MemoryFunctions.h91
-rw-r--r--js/public/MemoryMetrics.h915
-rw-r--r--js/public/Modules.h294
-rw-r--r--js/public/Object.h147
-rw-r--r--js/public/Prefs.h131
-rw-r--r--js/public/Principals.h173
-rw-r--r--js/public/Printer.h565
-rw-r--r--js/public/Printf.h34
-rw-r--r--js/public/ProfilingCategory.h65
-rw-r--r--js/public/ProfilingFrameIterator.h280
-rw-r--r--js/public/ProfilingStack.h578
-rw-r--r--js/public/Promise.h599
-rw-r--r--js/public/PropertyAndElement.h486
-rw-r--r--js/public/PropertyDescriptor.h503
-rw-r--r--js/public/PropertySpec.h447
-rw-r--r--js/public/ProtoKey.h158
-rw-r--r--js/public/Proxy.h768
-rw-r--r--js/public/Realm.h202
-rw-r--r--js/public/RealmIterators.h81
-rw-r--r--js/public/RealmOptions.h364
-rw-r--r--js/public/RefCounted.h96
-rw-r--r--js/public/RegExp.h106
-rw-r--r--js/public/RegExpFlags.h163
-rw-r--r--js/public/Result.h198
-rw-r--r--js/public/RootingAPI.h1609
-rw-r--r--js/public/SavedFrameAPI.h159
-rw-r--r--js/public/ScalarType.h232
-rw-r--r--js/public/ScriptPrivate.h51
-rw-r--r--js/public/ShadowRealmCallbacks.h50
-rw-r--r--js/public/SharedArrayBuffer.h80
-rw-r--r--js/public/SliceBudget.h145
-rw-r--r--js/public/SourceText.h353
-rw-r--r--js/public/StableStringChars.h118
-rw-r--r--js/public/Stack.h234
-rw-r--r--js/public/StreamConsumer.h114
-rw-r--r--js/public/String.h556
-rw-r--r--js/public/StructuredClone.h783
-rw-r--r--js/public/Symbol.h110
-rw-r--r--js/public/TelemetryTimers.h34
-rw-r--r--js/public/TraceKind.h273
-rw-r--r--js/public/TracingAPI.h425
-rw-r--r--js/public/Transcoding.h136
-rw-r--r--js/public/TypeDecls.h151
-rw-r--r--js/public/UbiNode.h1213
-rw-r--r--js/public/UbiNodeBreadthFirst.h271
-rw-r--r--js/public/UbiNodeCensus.h231
-rw-r--r--js/public/UbiNodeDominatorTree.h691
-rw-r--r--js/public/UbiNodePostOrder.h191
-rw-r--r--js/public/UbiNodeShortestPaths.h351
-rw-r--r--js/public/UbiNodeUtils.h51
-rw-r--r--js/public/UniquePtr.h56
-rw-r--r--js/public/Utility.h684
-rw-r--r--js/public/Value.h1553
-rw-r--r--js/public/ValueArray.h130
-rw-r--r--js/public/Vector.h38
-rw-r--r--js/public/WaitCallbacks.h54
-rw-r--r--js/public/Warnings.h98
-rw-r--r--js/public/WasmFeatures.h274
-rw-r--r--js/public/WasmModule.h46
-rw-r--r--js/public/WeakMap.h34
-rw-r--r--js/public/WeakMapPtr.h48
-rw-r--r--js/public/Wrapper.h505
-rw-r--r--js/public/WrapperCallbacks.h40
-rw-r--r--js/public/Zone.h86
-rw-r--r--js/public/experimental/CTypes.h105
-rw-r--r--js/public/experimental/CodeCoverage.h40
-rw-r--r--js/public/experimental/CompileScript.h180
-rw-r--r--js/public/experimental/Intl.h50
-rw-r--r--js/public/experimental/JSStencil.h217
-rw-r--r--js/public/experimental/JitInfo.h336
-rw-r--r--js/public/experimental/PCCountProfiling.h162
-rw-r--r--js/public/experimental/SourceHook.h99
-rw-r--r--js/public/experimental/TypedData.h748
-rw-r--r--js/public/friend/DOMProxy.h91
-rw-r--r--js/public/friend/DumpFunctions.h127
-rw-r--r--js/public/friend/ErrorMessages.h47
-rw-r--r--js/public/friend/ErrorNumbers.msg920
-rw-r--r--js/public/friend/JSMEnvironment.h91
-rw-r--r--js/public/friend/PerformanceHint.h29
-rw-r--r--js/public/friend/StackLimits.h335
-rw-r--r--js/public/friend/UsageStatistics.h101
-rw-r--r--js/public/friend/WindowProxy.h105
-rw-r--r--js/public/friend/XrayJitInfo.h57
-rw-r--r--js/public/shadow/Function.h51
-rw-r--r--js/public/shadow/Object.h67
-rw-r--r--js/public/shadow/Realm.h38
-rw-r--r--js/public/shadow/Shape.h65
-rw-r--r--js/public/shadow/String.h120
-rw-r--r--js/public/shadow/Symbol.h38
-rw-r--r--js/public/shadow/Zone.h126
137 files changed, 36396 insertions, 0 deletions
diff --git a/js/public/AllocPolicy.h b/js/public/AllocPolicy.h
new file mode 100644
index 0000000000..72c449293c
--- /dev/null
+++ b/js/public/AllocPolicy.h
@@ -0,0 +1,243 @@
+/* -*- 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 {
+
+class FrontendContext;
+
+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_PUBLIC_API void ReportOutOfMemory(JSContext* cx);
+MOZ_COLD JS_PUBLIC_API void ReportOutOfMemory(FrontendContext* fc);
+
+// An out of memory condition which is easily user generatable and should
+// be specially handled to try and avoid a tab crash.
+MOZ_COLD JS_PUBLIC_API void ReportLargeOutOfMemory(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 JS_PUBLIC_API TempAllocPolicy : public AllocPolicyBase {
+ // Type tag for context_bits_
+ static constexpr uintptr_t JsContextTag = 0x1;
+
+ // Either a JSContext* (if JsContextTag is set), or FrontendContext*
+ uintptr_t const context_bits_;
+
+ MOZ_ALWAYS_INLINE bool hasJSContext() const {
+ return (context_bits_ & JsContextTag) == JsContextTag;
+ }
+
+ MOZ_ALWAYS_INLINE JSContext* cx() const {
+ MOZ_ASSERT(hasJSContext());
+ return reinterpret_cast<JSContext*>(context_bits_ ^ JsContextTag);
+ }
+
+ MOZ_ALWAYS_INLINE FrontendContext* fc() const {
+ MOZ_ASSERT(!hasJSContext());
+ return reinterpret_cast<FrontendContext*>(context_bits_);
+ }
+
+ /*
+ * Non-inline helper to call JSRuntime::onOutOfMemory with minimal
+ * code bloat.
+ */
+ 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)
+ : context_bits_(uintptr_t(cx) | JsContextTag) {
+ MOZ_ASSERT((uintptr_t(cx) & JsContextTag) == 0);
+ }
+ MOZ_IMPLICIT TempAllocPolicy(FrontendContext* fc)
+ : context_bits_(uintptr_t(fc)) {
+ MOZ_ASSERT((uintptr_t(fc) & JsContextTag) == 0);
+ }
+
+ 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);
+ }
+
+ void reportAllocOverflow() const;
+
+ bool checkSimulatedOOM() const {
+ if (js::oom::ShouldFailWithOOM()) {
+ if (hasJSContext()) {
+ ReportOutOfMemory(cx());
+ } else {
+ ReportOutOfMemory(fc());
+ }
+ 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 {}
+
+ [[nodiscard]] 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..a714819344
--- /dev/null
+++ b/js/public/AllocationLogging.h
@@ -0,0 +1,75 @@
+/* -*- 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 <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..942d94b08e
--- /dev/null
+++ b/js/public/AllocationRecording.h
@@ -0,0 +1,73 @@
+/* -*- 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 <stdint.h>
+#include "jstypes.h"
+#include "js/TypeDecls.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_PUBLIC_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_PUBLIC_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..f019090167
--- /dev/null
+++ b/js/public/Array.h
@@ -0,0 +1,120 @@
+/* -*- 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/TypeDecls.h"
+
+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.
+ *
+ * If the length does not fit in |uint32_t|, an exception is reported and false
+ * is returned.
+ *
+ * |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..ddb8ba3932
--- /dev/null
+++ b/js/public/ArrayBuffer.h
@@ -0,0 +1,380 @@
+/* -*- 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 "mozilla/UniquePtr.h"
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API AutoRequireNoGC;
+
+// CREATION
+
+/**
+ * Create a new ArrayBuffer with the given byte length.
+ */
+extern JS_PUBLIC_API JSObject* NewArrayBuffer(JSContext* cx, size_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|.
+ *
+ * Care must be taken that |nbytes| bytes of |contents| 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,
+ mozilla::UniquePtr<void, JS::FreePolicy> contents);
+
+/**
+ * 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|.
+ *
+ * Care must be taken that |nbytes| bytes of |contents| 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.
+ */
+inline JS_PUBLIC_API JSObject* NewArrayBufferWithContents(
+ JSContext* cx, size_t nbytes,
+ mozilla::UniquePtr<char[], JS::FreePolicy> contents) {
+ // As a convenience, provide an overload for UniquePtr<char[]>.
+ mozilla::UniquePtr<void, JS::FreePolicy> ptr{contents.release()};
+ return NewArrayBufferWithContents(cx, nbytes, std::move(ptr));
+}
+
+/**
+ * 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|.
+ *
+ * Care must be taken that |nbytes| bytes of |contents| 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.
+ */
+inline JS_PUBLIC_API JSObject* NewArrayBufferWithContents(
+ JSContext* cx, size_t nbytes,
+ mozilla::UniquePtr<uint8_t[], JS::FreePolicy> contents) {
+ // As a convenience, provide an overload for UniquePtr<uint8_t[]>.
+ mozilla::UniquePtr<void, JS::FreePolicy> ptr{contents.release()};
+ return NewArrayBufferWithContents(cx, nbytes, std::move(ptr));
+}
+
+/**
+ * Marker enum to notify callers that the buffer contents must be freed manually
+ * when the ArrayBuffer allocation failed.
+ */
+enum class NewArrayBufferOutOfMemory { CallerMustFreeMemory };
+
+/**
+ * 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|.
+ *
+ * !!! IMPORTANT !!!
+ * 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 |contents| 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, NewArrayBufferOutOfMemory);
+
+/**
+ * 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);
+
+/**
+ * UniquePtr deleter for external buffer contents.
+ */
+class JS_PUBLIC_API BufferContentsDeleter {
+ BufferContentsFreeFunc freeFunc_ = nullptr;
+ void* userData_ = nullptr;
+
+ public:
+ MOZ_IMPLICIT BufferContentsDeleter(BufferContentsFreeFunc freeFunc,
+ void* userData = nullptr)
+ : freeFunc_(freeFunc), userData_(userData) {}
+
+ void operator()(void* contents) const { freeFunc_(contents, userData_); }
+
+ BufferContentsFreeFunc freeFunc() const { return freeFunc_; }
+ void* userData() const { return 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,
+ mozilla::UniquePtr<void, BufferContentsDeleter> contents);
+
+/**
+ * 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,
+ size_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 size_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,
+ size_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);
+
+// Indicates if an object has a defined [[ArrayBufferDetachKey]] internal slot,
+// which indicates an ArrayBuffer cannot be detached
+extern JS_PUBLIC_API bool HasDefinedArrayBufferDetachKey(JSContext* cx,
+ Handle<JSObject*> obj,
+ bool* isDefined);
+
+/**
+ * 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);
+
+/**
+ * Copy data from one array buffer to another.
+ *
+ * Both fromBuffer and toBuffer must be (possibly wrapped)
+ * ArrayBufferObjectMaybeShared.
+ *
+ * This method may throw if the sizes don't match, or if unwrapping fails.
+ *
+ * The API for this is modelled on CopyDataBlockBytes from the spec:
+ * https://tc39.es/ecma262/#sec-copydatablockbytes
+ */
+[[nodiscard]] extern JS_PUBLIC_API bool ArrayBufferCopyData(
+ JSContext* cx, Handle<JSObject*> toBlock, size_t toIndex,
+ Handle<JSObject*> fromBlock, size_t fromIndex, size_t count);
+
+/**
+ * Copy data from one array buffer to another.
+ *
+ * srcBuffer must be a (possibly wrapped) ArrayBufferObjectMaybeShared.
+ *
+ * This method may throw if unwrapping or allocation fails.
+ *
+ * The API for this is modelled on CloneArrayBuffer from the spec:
+ * https://tc39.es/ecma262/#sec-clonearraybuffer
+ */
+extern JS_PUBLIC_API JSObject* ArrayBufferClone(JSContext* cx,
+ Handle<JSObject*> srcBuffer,
+ size_t srcByteOffset,
+ size_t srcLength);
+
+} // namespace JS
+
+#endif /* js_ArrayBuffer_h */
diff --git a/js/public/ArrayBufferMaybeShared.h b/js/public/ArrayBufferMaybeShared.h
new file mode 100644
index 0000000000..2a972c96a4
--- /dev/null
+++ b/js/public/ArrayBufferMaybeShared.h
@@ -0,0 +1,109 @@
+/* -*- 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, size_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&);
+
+/**
+ * Returns whether the passed array buffer is 'large': its byteLength >= 2 GB.
+ *
+ * |obj| must pass a JS::IsArrayBufferObjectMaybeShared test.
+ */
+extern JS_PUBLIC_API bool IsLargeArrayBufferMaybeShared(JSObject* obj);
+
+/**
+ * Returns whether the passed array buffer is resizable or growable for shared
+ * array buffers.
+ *
+ * |obj| must pass a JS::IsArrayBufferObjectMaybeShared test.
+ */
+extern JS_PUBLIC_API bool IsResizableArrayBufferMaybeShared(JSObject* obj);
+
+} // namespace JS
+
+#endif /* js_ArrayBufferMaybeShared_h */
diff --git a/js/public/BigInt.h b/js/public/BigInt.h
new file mode 100644
index 0000000000..4cf1a10140
--- /dev/null
+++ b/js/public/BigInt.h
@@ -0,0 +1,229 @@
+/* -*- 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/Span.h" // mozilla::Span
+
+#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 "jstypes.h" // JS_PUBLIC_API
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+template <typename T>
+class Range;
+}
+
+namespace JS {
+
+class JS_PUBLIC_API BigInt;
+
+namespace detail {
+
+using Int64Limits = std::numeric_limits<int64_t>;
+using Uint64Limits = std::numeric_limits<uint64_t>;
+
+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> &&
+ Int64Limits::min() <= std::numeric_limits<SignedIntT>::min() &&
+ std::numeric_limits<SignedIntT>::max() <= Int64Limits::max()>> {
+ 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>::max() <= Uint64Limits::max()>> {
+ 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);
+ }
+};
+
+extern JS_PUBLIC_API bool BigIntIsInt64(const BigInt* bi, int64_t* result);
+extern JS_PUBLIC_API bool BigIntIsUint64(const BigInt* bi, uint64_t* result);
+
+template <typename T, typename = void>
+struct BigIntToNumberChecker;
+
+template <typename SignedIntT>
+struct BigIntToNumberChecker<
+ SignedIntT,
+ std::enable_if_t<
+ std::is_integral_v<SignedIntT> && std::is_signed_v<SignedIntT> &&
+ Int64Limits::min() <= std::numeric_limits<SignedIntT>::min() &&
+ std::numeric_limits<SignedIntT>::max() <= Int64Limits::max()>> {
+ using TypeLimits = std::numeric_limits<SignedIntT>;
+
+ static bool fits(const BigInt* bi, SignedIntT* result) {
+ int64_t innerResult;
+ if (!BigIntIsInt64(bi, &innerResult)) {
+ return false;
+ }
+ if (TypeLimits::min() <= innerResult && innerResult <= TypeLimits::max()) {
+ *result = SignedIntT(innerResult);
+ return true;
+ }
+ return false;
+ }
+};
+
+template <typename UnsignedIntT>
+struct BigIntToNumberChecker<
+ UnsignedIntT,
+ std::enable_if_t<
+ std::is_integral_v<UnsignedIntT> && std::is_unsigned_v<UnsignedIntT> &&
+ std::numeric_limits<UnsignedIntT>::max() <= Uint64Limits::max()>> {
+ static bool fits(const BigInt* bi, UnsignedIntT* result) {
+ uint64_t innerResult;
+ if (!BigIntIsUint64(bi, &innerResult)) {
+ return false;
+ }
+ if (innerResult <= std::numeric_limits<UnsignedIntT>::max()) {
+ *result = UnsignedIntT(innerResult);
+ return true;
+ }
+ return false;
+ }
+};
+
+} // namespace detail
+
+/**
+ * Create a BigInt from an integer value. All integral types not larger than 64
+ * bits in size are supported.
+ */
+template <typename NumericT>
+static inline 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, const mozilla::Range<const Latin1Char>& chars);
+
+extern JS_PUBLIC_API BigInt* StringToBigInt(
+ JSContext* cx, const 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, uint8_t 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(const BigInt* bi);
+
+/**
+ * Convert the given BigInt, modulo 2**64, to an unsigned 64-bit integer.
+ */
+extern JS_PUBLIC_API uint64_t ToBigUint64(const BigInt* bi);
+
+/**
+ * Convert the given BigInt to a Number value as if calling the Number
+ * constructor on it
+ * (https://tc39.es/ecma262/#sec-number-constructor-number-value). The value
+ * may be rounded if it doesn't fit without loss of precision.
+ */
+extern JS_PUBLIC_API double BigIntToNumber(const BigInt* bi);
+
+/**
+ * Return true if the given BigInt is negative.
+ */
+extern JS_PUBLIC_API bool BigIntIsNegative(const BigInt* bi);
+
+/**
+ * Return true if the given BigInt fits inside the given NumericT type without
+ * loss of precision, and store the value in the out parameter. Otherwise return
+ * false and leave the value of the out parameter unspecified.
+ */
+template <typename NumericT>
+static inline bool BigIntFits(const BigInt* bi, NumericT* out) {
+ return detail::BigIntToNumberChecker<NumericT>::fits(bi, out);
+}
+
+/**
+ * Same as BigIntFits(), but checks if the value fits inside a JS Number value.
+ */
+extern JS_PUBLIC_API bool BigIntFitsNumber(const BigInt* bi, double* out);
+
+/**
+ * Convert the given BigInt to a String value as if toString() were called on
+ * it.
+ *
+ * If the radix is not in the range [2, 36], then this function returns null and
+ * throws an exception.
+ */
+extern JS_PUBLIC_API JSString* BigIntToString(JSContext* cx, Handle<BigInt*> bi,
+ uint8_t radix);
+
+} // namespace JS
+
+#endif /* js_BigInt_h */
diff --git a/js/public/BuildId.h b/js/public/BuildId.h
new file mode 100644
index 0000000000..4f090dacc5
--- /dev/null
+++ b/js/public/BuildId.h
@@ -0,0 +1,82 @@
+/* -*- 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 "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.
+ */
+[[nodiscard]] extern 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.
+ */
+[[nodiscard]] extern JS_PUBLIC_API bool GetScriptTranscodingBuildId(
+ BuildIdCharVector* buildId);
+
+} // namespace JS
+
+#endif /* js_BuildId_h */
diff --git a/js/public/CallAndConstruct.h b/js/public/CallAndConstruct.h
new file mode 100644
index 0000000000..cd3007cbb9
--- /dev/null
+++ b/js/public/CallAndConstruct.h
@@ -0,0 +1,143 @@
+/* -*- 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/. */
+
+/* Call and construct API. */
+
+#ifndef js_CallAndConstruct_h
+#define js_CallAndConstruct_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+#include "js/Value.h" // JS::Value, JS::ObjectValue
+#include "js/ValueArray.h" // JS::HandleValueArray
+
+struct JSContext;
+class JSObject;
+class JSFunction;
+
+/*
+ * API for determining callability and constructability. [[Call]] and
+ * [[Construct]] are internal methods that aren't present on all objects, so it
+ * is useful to ask if they are there or not. The standard itself asks these
+ * questions routinely.
+ */
+namespace JS {
+
+/**
+ * Return true if the given object is callable. In ES6 terms, an object is
+ * callable if it has a [[Call]] internal method.
+ *
+ * Implements: ES6 7.2.3 IsCallable(argument).
+ *
+ * Functions are callable. A scripted proxy or wrapper is callable if its
+ * target is callable. Most other objects aren't callable.
+ */
+extern JS_PUBLIC_API bool IsCallable(JSObject* obj);
+
+/**
+ * Return true if the given object is a constructor. In ES6 terms, an object is
+ * a constructor if it has a [[Construct]] internal method. The expression
+ * `new obj()` throws a TypeError if obj is not a constructor.
+ *
+ * Implements: ES6 7.2.4 IsConstructor(argument).
+ *
+ * JS functions and classes are constructors. Arrow functions and most builtin
+ * functions are not. A scripted proxy or wrapper is a constructor if its
+ * target is a constructor.
+ */
+extern JS_PUBLIC_API bool IsConstructor(JSObject* obj);
+
+} /* namespace JS */
+
+/**
+ * Call a function, passing a this-value and arguments. This is the C++
+ * equivalent of `rval = Reflect.apply(fun, obj, args)`.
+ *
+ * Implements: ES6 7.3.12 Call(F, V, [argumentsList]).
+ * Use this function to invoke the [[Call]] internal method.
+ */
+extern JS_PUBLIC_API bool JS_CallFunctionValue(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<JS::Value> fval,
+ const JS::HandleValueArray& args, JS::MutableHandle<JS::Value> rval);
+
+extern JS_PUBLIC_API bool JS_CallFunction(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<JSFunction*> fun,
+ const JS::HandleValueArray& args,
+ JS::MutableHandle<JS::Value> rval);
+
+/**
+ * Perform the method call `rval = obj[name](args)`.
+ */
+extern JS_PUBLIC_API bool JS_CallFunctionName(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
+ const JS::HandleValueArray& args, JS::MutableHandle<JS::Value> rval);
+
+namespace JS {
+
+static inline bool Call(JSContext* cx, Handle<JSObject*> thisObj,
+ Handle<JSFunction*> fun, const HandleValueArray& args,
+ MutableHandle<Value> rval) {
+ return !!JS_CallFunction(cx, thisObj, fun, args, rval);
+}
+
+static inline bool Call(JSContext* cx, Handle<JSObject*> thisObj,
+ Handle<Value> fun, const HandleValueArray& args,
+ MutableHandle<Value> rval) {
+ return !!JS_CallFunctionValue(cx, thisObj, fun, args, rval);
+}
+
+static inline bool Call(JSContext* cx, Handle<JSObject*> thisObj,
+ const char* name, const HandleValueArray& args,
+ MutableHandle<Value> rval) {
+ return !!JS_CallFunctionName(cx, thisObj, name, args, rval);
+}
+
+extern JS_PUBLIC_API bool Call(JSContext* cx, Handle<Value> thisv,
+ Handle<Value> fun, const HandleValueArray& args,
+ MutableHandle<Value> rval);
+
+static inline bool Call(JSContext* cx, Handle<Value> thisv,
+ Handle<JSObject*> funObj, const HandleValueArray& args,
+ MutableHandle<Value> rval) {
+ MOZ_ASSERT(funObj);
+ Rooted<Value> fun(cx, ObjectValue(*funObj));
+ return Call(cx, thisv, fun, args, rval);
+}
+
+/**
+ * Invoke a constructor. This is the C++ equivalent of
+ * `rval = Reflect.construct(fun, args, newTarget)`.
+ *
+ * Construct() takes a `newTarget` argument that most callers don't need.
+ * Consider using the four-argument Construct signature instead. (But if you're
+ * implementing a subclass or a proxy handler's construct() method, this is the
+ * right function to call.)
+ *
+ * Implements: ES6 7.3.13 Construct(F, [argumentsList], [newTarget]).
+ * Use this function to invoke the [[Construct]] internal method.
+ */
+extern JS_PUBLIC_API bool Construct(JSContext* cx, Handle<Value> fun,
+ Handle<JSObject*> newTarget,
+ const HandleValueArray& args,
+ MutableHandle<JSObject*> objp);
+
+/**
+ * Invoke a constructor. This is the C++ equivalent of
+ * `rval = new fun(...args)`.
+ *
+ * Implements: ES6 7.3.13 Construct(F, [argumentsList], [newTarget]), when
+ * newTarget is omitted.
+ */
+extern JS_PUBLIC_API bool Construct(JSContext* cx, Handle<Value> fun,
+ const HandleValueArray& args,
+ MutableHandle<JSObject*> objp);
+
+} /* namespace JS */
+
+#endif /* js_CallAndConstruct_h */
diff --git a/js/public/CallArgs.h b/js/public/CallArgs.h
new file mode 100644
index 0000000000..16f8d6f904
--- /dev/null
+++ b/js/public/CallArgs.h
@@ -0,0 +1,364 @@
+/* -*- 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_ = false;
+
+ 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_ = nullptr;
+ unsigned argc_ = 0;
+ 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
+
+ CallArgsBase() : constructing_(false), ignoresReturnValue_(false) {}
+
+ 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].isMagicNoReleaseCheck(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..9d1df4664b
--- /dev/null
+++ b/js/public/CharacterEncoding.h
@@ -0,0 +1,448 @@
+/* -*- 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 "js/TypeDecls.h"
+#include "js/Utility.h"
+
+class JSLinearString;
+
+namespace mozilla {
+union Utf8Unit;
+}
+
+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) {}
+};
+
+/*
+ * 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) {}
+
+ explicit ConstUTF8CharsZ(const char* aBytes) : data_(aBytes) {
+#ifdef DEBUG
+ if (aBytes) {
+ validateWithoutLength();
+ }
+#endif
+ }
+
+ 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);
+ void validateWithoutLength();
+#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, typename Allocator>
+extern UTF8CharsZ CharsToNewUTF8CharsZ(Allocator* alloc,
+ const mozilla::Range<CharT>& chars);
+
+JS_PUBLIC_API char32_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 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(const 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);
+
+/**
+ * Encode a narrow multibyte character string to a UTF-8 string.
+ *
+ * NOTE: Should only be used when interacting with POSIX/OS functions and not
+ * for encoding ASCII/Latin-1/etc. strings to UTF-8.
+ */
+extern JS_PUBLIC_API JS::UniqueChars EncodeNarrowToUtf8(JSContext* cx,
+ const char* chars);
+
+/**
+ * Encode a wide string to a UTF-8 string.
+ *
+ * NOTE: Should only be used when interacting with Windows API functions.
+ */
+extern JS_PUBLIC_API JS::UniqueChars EncodeWideToUtf8(JSContext* cx,
+ const wchar_t* chars);
+
+/**
+ * Encode a UTF-8 string to a narrow multibyte character string.
+ *
+ * NOTE: Should only be used when interacting with POSIX/OS functions and not
+ * for encoding UTF-8 to ASCII/Latin-1/etc. strings.
+ */
+extern JS_PUBLIC_API JS::UniqueChars EncodeUtf8ToNarrow(JSContext* cx,
+ const char* chars);
+
+/**
+ * Encode a UTF-8 string to a wide string.
+ *
+ * NOTE: Should only be used when interacting with Windows API functions.
+ */
+extern JS_PUBLIC_API JS::UniqueWideChars EncodeUtf8ToWide(JSContext* cx,
+ const char* chars);
+
+} // 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..5664198584
--- /dev/null
+++ b/js/public/Class.h
@@ -0,0 +1,837 @@
+/* -*- 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 "mozilla/Maybe.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 PropertyResult;
+
+// These are equal to js::FunctionClass / js::ExtendedFunctionClass.
+extern JS_PUBLIC_DATA const JSClass* const FunctionClassPtr;
+extern JS_PUBLIC_DATA const JSClass* const FunctionExtendedClassPtr;
+
+} // 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 failCantDefineWindowNamedProperty();
+ 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();
+ JS_PUBLIC_API bool failInvalidDescriptor();
+
+ // Careful: This case has special handling in Object.defineProperty.
+ JS_PUBLIC_API bool failCantDefineWindowNonConfigurable();
+
+ JS_PUBLIC_API bool failBadArrayLength();
+ JS_PUBLIC_API bool failBadIndex();
+
+ 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);
+ }
+};
+
+} // namespace JS
+
+// JSClass operation signatures.
+
+/** Add a property named by id to obj. */
+typedef bool (*JSAddPropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, JS::HandleValue v);
+
+/**
+ * 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)(JS::GCContext* gcx, JSObject* obj);
+
+/**
+ * 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,
+ 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<mozilla::Maybe<JS::PropertyDescriptor>> desc);
+typedef bool (*DeletePropertyOp)(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, JS::ObjectOpResult& result);
+
+class JS_PUBLIC_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.
+
+// (1 << 0 is unused)
+
+// 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;
+
+// First reserved slot is `PrivateValue(nsISupports*)` or `UndefinedValue`.
+static constexpr uint32_t JSCLASS_SLOT0_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 + 1;
+
+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;
+ 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; }
+ 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(JS::GCContext* gcx, JSObject* obj) const {
+ MOZ_ASSERT(cOps && cOps->finalize);
+ cOps->finalize(gcx, 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;
+
+ // A JSObject created from a JSClass extends from one of:
+ // - js::NativeObject
+ // - js::ProxyObject
+ //
+ // While it is possible to introduce new families of objects, it is strongly
+ // discouraged. The JITs would be entirely unable to optimize them and testing
+ // coverage is low. The existing NativeObject and ProxyObject are extremely
+ // flexible and are able to represent the entire Gecko embedding requirements.
+ //
+ // NOTE: Internal to SpiderMonkey, there is an experimental js::TypedObject
+ // object family for future WASM features.
+ bool isNativeObject() const { return !(flags & NON_NATIVE); }
+ bool isProxyObject() const { return flags & JSCLASS_IS_PROXY; }
+
+ bool emulatesUndefined() const { return flags & JSCLASS_EMULATES_UNDEFINED; }
+
+ bool isJSFunction() const {
+ return this == js::FunctionClassPtr || this == js::FunctionExtendedClassPtr;
+ }
+
+ bool nonProxyCallable() const {
+ MOZ_ASSERT(!isProxyObject());
+ return isJSFunction() || getCall();
+ }
+
+ bool isGlobal() const { return flags & JSCLASS_IS_GLOBAL; }
+
+ 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; }
+
+ bool slot0IsISupports() const { return flags & JSCLASS_SLOT0_IS_NSISUPPORTS; }
+
+ 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.
+
+#ifdef ENABLE_RECORD_TUPLE
+ Record,
+ Tuple,
+#endif
+
+ /** 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);
+
+// Classes with JSCLASS_SKIP_NURSERY_FINALIZE or Wrapper classes with
+// CROSS_COMPARTMENT flags will not have their finalizer called if they are
+// nursery allocated and not promoted to the tenured heap. The finalizers for
+// these classes must do nothing except free data which was allocated via
+// Nursery::allocateBuffer.
+inline bool CanNurseryAllocateFinalizedClass(const JSClass* const clasp) {
+ MOZ_ASSERT(clasp->hasFinalize());
+ return clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE;
+}
+
+#ifdef DEBUG
+JS_PUBLIC_API bool HasObjectMovedOp(JSObject* obj);
+#endif
+
+} /* namespace js */
+
+#endif /* js_Class_h */
diff --git a/js/public/ColumnNumber.h b/js/public/ColumnNumber.h
new file mode 100644
index 0000000000..9fd007f4bb
--- /dev/null
+++ b/js/public/ColumnNumber.h
@@ -0,0 +1,418 @@
+/* -*- 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/. */
+
+// [SMDOC] Column numbers
+//
+// Inside SpiderMonkey, column numbers are represented as 1-origin 32-bit
+// unsigned integers. Some parts of the engine use the highest bit of a column
+// number as a tag to indicate Wasm frame.
+//
+// These classes help clarifying the origin of the column number, and also
+// figuring out whether the column number uses the wasm's tag or not, and also
+// help converting between them.
+//
+// Also these classes support converting from 0-origin column number.
+//
+// In a 0-origin context, column 0 is the first character of the line.
+// In a 1-origin context, column 1 is the first character of the line,
+// for example:
+//
+// function foo() { ... }
+// ^ ^
+// 0-origin: 0 15
+// 1-origin: 1 16
+
+#ifndef js_ColumnNumber_h
+#define js_ColumnNumber_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_IMPLICIT
+
+#include <limits> // std::numeric_limits
+#include <stdint.h> // uint32_t
+
+namespace JS {
+
+// Wasm function index.
+//
+// This class is used as parameter or return type of
+// TaggedColumnNumberOneOrigin class below.
+struct WasmFunctionIndex {
+ // TaggedColumnNumberOneOrigin uses the highest bit as a tag.
+ static constexpr uint32_t Limit = std::numeric_limits<int32_t>::max() / 2;
+
+ // For wasm frames, the function index is returned as the column with the
+ // high bit set. In paths that format error stacks into strings, this
+ // information can be used to synthesize a proper wasm frame. But when raw
+ // column numbers are handed out, we just fix them to the first column to
+ // avoid confusion.
+ static constexpr uint32_t DefaultBinarySourceColumnNumberOneOrigin = 1;
+
+ private:
+ uint32_t value_ = 0;
+
+ public:
+ constexpr WasmFunctionIndex() = default;
+ constexpr WasmFunctionIndex(const WasmFunctionIndex& other) = default;
+
+ inline explicit WasmFunctionIndex(uint32_t value) : value_(value) {
+ MOZ_ASSERT(valid());
+ }
+
+ uint32_t value() const { return value_; }
+
+ bool valid() const { return value_ <= Limit; }
+};
+
+// The offset between 2 column numbers.
+struct ColumnNumberOffset {
+ private:
+ int32_t value_ = 0;
+
+ public:
+ constexpr ColumnNumberOffset() = default;
+ constexpr ColumnNumberOffset(const ColumnNumberOffset& other) = default;
+
+ inline explicit ColumnNumberOffset(int32_t value) : value_(value) {}
+
+ static constexpr ColumnNumberOffset zero() { return ColumnNumberOffset(); }
+
+ bool operator==(const ColumnNumberOffset& rhs) const {
+ return value_ == rhs.value_;
+ }
+
+ bool operator!=(const ColumnNumberOffset& rhs) const {
+ return !(*this == rhs);
+ }
+
+ int32_t value() const { return value_; }
+};
+
+// The positive offset from certain column number.
+struct ColumnNumberUnsignedOffset {
+ private:
+ uint32_t value_ = 0;
+
+ public:
+ constexpr ColumnNumberUnsignedOffset() = default;
+ constexpr ColumnNumberUnsignedOffset(
+ const ColumnNumberUnsignedOffset& other) = default;
+
+ inline explicit ColumnNumberUnsignedOffset(uint32_t value) : value_(value) {}
+
+ static constexpr ColumnNumberUnsignedOffset zero() {
+ return ColumnNumberUnsignedOffset();
+ }
+
+ ColumnNumberUnsignedOffset operator+(
+ const ColumnNumberUnsignedOffset& offset) const {
+ return ColumnNumberUnsignedOffset(value_ + offset.value());
+ }
+
+ ColumnNumberUnsignedOffset& operator+=(
+ const ColumnNumberUnsignedOffset& offset) {
+ value_ += offset.value();
+ return *this;
+ }
+
+ bool operator==(const ColumnNumberUnsignedOffset& rhs) const {
+ return value_ == rhs.value_;
+ }
+
+ bool operator!=(const ColumnNumberUnsignedOffset& rhs) const {
+ return !(*this == rhs);
+ }
+
+ uint32_t value() const { return value_; }
+
+ uint32_t* addressOfValueForTranscode() { return &value_; }
+};
+
+struct TaggedColumnNumberOneOrigin;
+
+namespace detail {
+
+// Shared implementation of {,Limited}ColumnNumberOneOrigin classes.
+//
+// LimitValue being 0 means there's no limit.
+template <uint32_t LimitValue = 0>
+struct MaybeLimitedColumnNumber {
+ public:
+ static constexpr uint32_t OriginValue = 1;
+
+ protected:
+ uint32_t value_ = OriginValue;
+
+ friend struct ::JS::TaggedColumnNumberOneOrigin;
+
+ public:
+ constexpr MaybeLimitedColumnNumber() = default;
+ MaybeLimitedColumnNumber(const MaybeLimitedColumnNumber& other) = default;
+ MaybeLimitedColumnNumber& operator=(const MaybeLimitedColumnNumber& other) =
+ default;
+
+ explicit MaybeLimitedColumnNumber(uint32_t value) : value_(value) {
+ MOZ_ASSERT(valid());
+ }
+
+ bool operator==(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
+ return value_ == rhs.value_;
+ }
+
+ bool operator!=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
+ return !(*this == rhs);
+ }
+
+ MaybeLimitedColumnNumber<LimitValue> operator+(
+ const ColumnNumberOffset& offset) const {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0);
+ return MaybeLimitedColumnNumber<LimitValue>(value_ + offset.value());
+ }
+
+ MaybeLimitedColumnNumber<LimitValue> operator+(
+ const ColumnNumberUnsignedOffset& offset) const {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0);
+ return MaybeLimitedColumnNumber<LimitValue>(value_ + offset.value());
+ }
+
+ MaybeLimitedColumnNumber<LimitValue> operator-(
+ const ColumnNumberOffset& offset) const {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(ptrdiff_t(value_) - offset.value() >= 0);
+ return MaybeLimitedColumnNumber<LimitValue>(value_ - offset.value());
+ }
+ ColumnNumberOffset operator-(
+ const MaybeLimitedColumnNumber<LimitValue>& other) const {
+ MOZ_ASSERT(valid());
+ return ColumnNumberOffset(int32_t(value_) - int32_t(other.value_));
+ }
+
+ MaybeLimitedColumnNumber<LimitValue>& operator+=(
+ const ColumnNumberOffset& offset) {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0);
+ value_ += offset.value();
+ MOZ_ASSERT(valid());
+ return *this;
+ }
+ MaybeLimitedColumnNumber<LimitValue>& operator-=(
+ const ColumnNumberOffset& offset) {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(ptrdiff_t(value_) - offset.value() >= 0);
+ value_ -= offset.value();
+ MOZ_ASSERT(valid());
+ return *this;
+ }
+
+ bool operator<(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(rhs.valid());
+ return value_ < rhs.value_;
+ }
+ bool operator<=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(rhs.valid());
+ return value_ <= rhs.value_;
+ }
+ bool operator>(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(rhs.valid());
+ return value_ > rhs.value_;
+ }
+ bool operator>=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
+ MOZ_ASSERT(valid());
+ MOZ_ASSERT(rhs.valid());
+ return value_ >= rhs.value_;
+ }
+
+ uint32_t oneOriginValue() const {
+ MOZ_ASSERT(valid());
+
+ return value_;
+ }
+
+ uint32_t* addressOfValueForTranscode() { return &value_; }
+
+ bool valid() const {
+ if constexpr (LimitValue == 0) {
+ return true;
+ }
+
+ MOZ_ASSERT(value_ != 0);
+
+ return value_ <= LimitValue;
+ }
+};
+
+// See the comment for LimitedColumnNumberOneOrigin below
+static constexpr uint32_t ColumnNumberOneOriginLimit =
+ std::numeric_limits<int32_t>::max() / 2;
+
+} // namespace detail
+
+// Column number in 1-origin with 31-bit limit.
+//
+// Various parts of the engine requires the column number be represented in
+// 31 bits.
+//
+// See:
+// * TaggedColumnNumberOneOrigin
+// * TokenStreamAnyChars::checkOptions
+// * SourceNotes::isRepresentable
+// * WasmFrameIter::computeLine
+struct LimitedColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<
+ detail::ColumnNumberOneOriginLimit> {
+ private:
+ using Base =
+ detail::MaybeLimitedColumnNumber<detail::ColumnNumberOneOriginLimit>;
+
+ public:
+ static constexpr uint32_t Limit = detail::ColumnNumberOneOriginLimit;
+
+ static_assert(uint32_t(Limit + Limit) > Limit,
+ "Adding Limit should not overflow");
+
+ using Base::Base;
+
+ LimitedColumnNumberOneOrigin() = default;
+ LimitedColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other) =
+ default;
+ MOZ_IMPLICIT LimitedColumnNumberOneOrigin(const Base& other) : Base(other) {}
+
+ static LimitedColumnNumberOneOrigin limit() {
+ return LimitedColumnNumberOneOrigin(Limit);
+ }
+
+ static LimitedColumnNumberOneOrigin fromUnlimited(uint32_t value) {
+ if (value > Limit) {
+ return LimitedColumnNumberOneOrigin(Limit);
+ }
+ return LimitedColumnNumberOneOrigin(value);
+ }
+ static LimitedColumnNumberOneOrigin fromUnlimited(
+ const MaybeLimitedColumnNumber<0>& value) {
+ return fromUnlimited(value.oneOriginValue());
+ }
+
+ static LimitedColumnNumberOneOrigin fromZeroOrigin(uint32_t value) {
+ return LimitedColumnNumberOneOrigin(value + 1);
+ }
+};
+
+// Column number in 1-origin.
+struct ColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<0> {
+ private:
+ using Base = detail::MaybeLimitedColumnNumber<0>;
+
+ public:
+ using Base::Base;
+ using Base::operator=;
+
+ ColumnNumberOneOrigin() = default;
+ ColumnNumberOneOrigin(const ColumnNumberOneOrigin& other) = default;
+ ColumnNumberOneOrigin& operator=(ColumnNumberOneOrigin&) = default;
+
+ MOZ_IMPLICIT ColumnNumberOneOrigin(const Base& other) : Base(other) {}
+
+ explicit ColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other)
+ : Base(other.oneOriginValue()) {}
+
+ static ColumnNumberOneOrigin fromZeroOrigin(uint32_t value) {
+ return ColumnNumberOneOrigin(value + 1);
+ }
+};
+
+// Either LimitedColumnNumberOneOrigin, or WasmFunctionIndex.
+//
+// In order to pass the Wasm frame's (url, bytecode-offset, func-index) tuple
+// through the existing (url, line, column) tuple, it tags the highest bit of
+// the column to indicate "this is a wasm frame".
+//
+// When knowing clients see this bit, they shall render the tuple
+// (url, line, column|bit) as "url:wasm-function[column]:0xline" according
+// to the WebAssembly Web API's Developer-Facing Display Conventions.
+// https://webassembly.github.io/spec/web-api/index.html#conventions
+// The wasm bytecode offset continues to be passed as the JS line to avoid
+// breaking existing devtools code written when this used to be the case.
+//
+// 0b0YYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY LimitedColumnNumberOneOrigin
+// 0b1YYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY WasmFunctionIndex
+//
+// The tagged colum number shouldn't escape the JS engine except for the
+// following places:
+// * SavedFrame API which can directly access WASM frame's info
+// * ubi::Node API which can also directly access WASM frame's info
+struct TaggedColumnNumberOneOrigin {
+ static constexpr uint32_t WasmFunctionTag = 1u << 31;
+
+ static_assert((WasmFunctionIndex::Limit & WasmFunctionTag) == 0);
+ static_assert((LimitedColumnNumberOneOrigin::Limit & WasmFunctionTag) == 0);
+
+ protected:
+ uint32_t value_ = LimitedColumnNumberOneOrigin::OriginValue;
+
+ explicit TaggedColumnNumberOneOrigin(uint32_t value) : value_(value) {}
+
+ public:
+ constexpr TaggedColumnNumberOneOrigin() = default;
+ TaggedColumnNumberOneOrigin(const TaggedColumnNumberOneOrigin& other) =
+ default;
+
+ explicit TaggedColumnNumberOneOrigin(
+ const LimitedColumnNumberOneOrigin& other)
+ : value_(other.value_) {
+ MOZ_ASSERT(isLimitedColumnNumber());
+ }
+ explicit TaggedColumnNumberOneOrigin(const WasmFunctionIndex& other)
+ : value_(other.value() | WasmFunctionTag) {
+ MOZ_ASSERT(isWasmFunctionIndex());
+ }
+
+ static TaggedColumnNumberOneOrigin fromRaw(uint32_t value) {
+ return TaggedColumnNumberOneOrigin(value);
+ }
+
+ static TaggedColumnNumberOneOrigin forDifferentialTesting() {
+ return TaggedColumnNumberOneOrigin(LimitedColumnNumberOneOrigin());
+ }
+
+ bool operator==(const TaggedColumnNumberOneOrigin& rhs) const {
+ return value_ == rhs.value_;
+ }
+
+ bool operator!=(const TaggedColumnNumberOneOrigin& rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool isLimitedColumnNumber() const { return !isWasmFunctionIndex(); }
+
+ bool isWasmFunctionIndex() const { return !!(value_ & WasmFunctionTag); }
+
+ LimitedColumnNumberOneOrigin toLimitedColumnNumber() const {
+ MOZ_ASSERT(isLimitedColumnNumber());
+ return LimitedColumnNumberOneOrigin(value_);
+ }
+
+ WasmFunctionIndex toWasmFunctionIndex() const {
+ MOZ_ASSERT(isWasmFunctionIndex());
+ return WasmFunctionIndex(value_ & ~WasmFunctionTag);
+ }
+
+ uint32_t oneOriginValue() const {
+ return isWasmFunctionIndex()
+ ? WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin
+ : toLimitedColumnNumber().oneOriginValue();
+ }
+
+ uint32_t rawValue() const { return value_; }
+
+ uint32_t* addressOfValueForTranscode() { return &value_; }
+};
+
+} // namespace JS
+
+#endif /* js_ColumnNumber_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..ce0090dea9
--- /dev/null
+++ b/js/public/CompilationAndEvaluation.h
@@ -0,0 +1,233 @@
+/* -*- 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 <stddef.h> // size_t
+#include <stdio.h> // FILE
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+#include "js/TypeDecls.h"
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSFunction;
+class JS_PUBLIC_API JSObject;
+class JS_PUBLIC_API JSScript;
+
+namespace mozilla {
+union Utf8Unit;
+}
+
+namespace JS {
+
+class JS_PUBLIC_API InstantiateOptions;
+class JS_PUBLIC_API ReadOnlyCompileOptions;
+
+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 {
+
+/**
+ * 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 UTF-8 encoded, too.) 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 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);
+
+/*
+ * JSScripts have associated with them (via their ScriptSourceObjects) some
+ * metadata used by the debugger. The following API functions are used to set
+ * that metadata on scripts, functions and modules.
+ *
+ * The metadata consists of:
+ * - A privateValue, which is used to keep some object value associated
+ * with the script.
+ * - The elementAttributeName is used by Gecko
+ * - The introductionScript is used by the debugger to identify which
+ * script created which. Only set for dynamicaly generated scripts.
+ * - scriptOrModule is used to transfer private value metadata from
+ * script to script
+ *
+ * Callers using UpdateDebugMetaData need to have set deferDebugMetadata
+ * in the compile options; this hides the script from the debugger until
+ * the debug metadata is provided by the UpdateDebugMetadata call.
+ */
+extern JS_PUBLIC_API bool UpdateDebugMetadata(
+ JSContext* cx, Handle<JSScript*> script, const InstantiateOptions& options,
+ HandleValue privateValue, HandleString elementAttributeName,
+ HandleScript introScript, HandleScript scriptOrModule);
+
+} /* namespace JS */
+
+#endif /* js_CompilationAndEvaluation_h */
diff --git a/js/public/CompileOptions.h b/js/public/CompileOptions.h
new file mode 100644
index 0000000000..01a84b2398
--- /dev/null
+++ b/js/public/CompileOptions.h
@@ -0,0 +1,864 @@
+/* -*- 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/Assertions.h" // MOZ_ASSERT
+#include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint8_t, uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
+#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
+#include "js/TypeDecls.h" // JS::MutableHandle (fwd)
+
+namespace js {
+class FrontendContext;
+} // namespace js
+
+namespace JS {
+using FrontendContext = js::FrontendContext;
+
+enum class AsmJSOption : uint8_t {
+ Enabled,
+ DisabledByAsmJSPref,
+ DisabledByLinker,
+ DisabledByNoWasmCompiler,
+ DisabledByDebugger,
+};
+
+#define FOREACH_DELAZIFICATION_STRATEGY(_) \
+ /* Do not delazify anything eagerly. */ \
+ _(OnDemandOnly) \
+ \
+ /* \
+ * Compare the stencil produced by concurrent depth first delazification and \
+ * on-demand delazification. Any differences would crash SpiderMonkey with \
+ * an assertion. \
+ */ \
+ _(CheckConcurrentWithOnDemand) \
+ \
+ /* \
+ * Delazifiy functions in a depth first traversal of the functions. \
+ */ \
+ _(ConcurrentDepthFirst) \
+ \
+ /* \
+ * Delazifiy functions strating with the largest function first. \
+ */ \
+ _(ConcurrentLargeFirst) \
+ \
+ /* \
+ * Parse everything eagerly, from the first parse. \
+ * \
+ * NOTE: Either the Realm configuration or specialized VM operating modes \
+ * may disallow syntax-parse altogether. These conditions are checked in the \
+ * CompileOptions constructor. \
+ */ \
+ _(ParseEverythingEagerly)
+
+enum class DelazificationOption : uint8_t {
+#define _ENUM_ENTRY(Name) Name,
+ FOREACH_DELAZIFICATION_STRATEGY(_ENUM_ENTRY)
+#undef _ENUM_ENTRY
+};
+
+class JS_PUBLIC_API InstantiateOptions;
+class JS_PUBLIC_API ReadOnlyDecodeOptions;
+
+// Compilation-specific part of JS::ContextOptions which is supposed to be
+// configured by user prefs.
+class JS_PUBLIC_API PrefableCompileOptions {
+ public:
+ PrefableCompileOptions()
+ : importAttributes_(false),
+ importAttributesAssertSyntax_(false),
+ sourcePragmas_(true),
+ throwOnAsmJSValidationFailure_(false) {}
+
+ bool importAttributes() const { return importAttributes_; }
+ PrefableCompileOptions& setImportAttributes(bool enabled) {
+ importAttributes_ = enabled;
+ return *this;
+ }
+ bool importAttributesAssertSyntax() const {
+ return importAttributesAssertSyntax_;
+ }
+ PrefableCompileOptions& setImportAttributesAssertSyntax(bool enabled) {
+ importAttributesAssertSyntax_ = enabled;
+ return *this;
+ }
+
+ // Enable/disable support for parsing '//(#@) source(Mapping)?URL=' pragmas.
+ bool sourcePragmas() const { return sourcePragmas_; }
+ PrefableCompileOptions& setSourcePragmas(bool flag) {
+ sourcePragmas_ = flag;
+ return *this;
+ }
+
+ AsmJSOption asmJSOption() const { return asmJSOption_; }
+ PrefableCompileOptions& setAsmJS(bool flag) {
+ asmJSOption_ =
+ flag ? AsmJSOption::Enabled : AsmJSOption::DisabledByAsmJSPref;
+ return *this;
+ }
+ PrefableCompileOptions& setAsmJSOption(AsmJSOption option) {
+ asmJSOption_ = option;
+ return *this;
+ }
+
+ bool throwOnAsmJSValidationFailure() const {
+ return throwOnAsmJSValidationFailure_;
+ }
+ PrefableCompileOptions& setThrowOnAsmJSValidationFailure(bool flag) {
+ throwOnAsmJSValidationFailure_ = flag;
+ return *this;
+ }
+ PrefableCompileOptions& toggleThrowOnAsmJSValidationFailure() {
+ throwOnAsmJSValidationFailure_ = !throwOnAsmJSValidationFailure_;
+ return *this;
+ }
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+ template <typename Printer>
+ void dumpWith(Printer& print) const {
+# define PrintFields_(Name) print(#Name, Name)
+ PrintFields_(importAttributes_);
+ PrintFields_(importAttributesAssertSyntax_);
+ PrintFields_(sourcePragmas_);
+ PrintFields_(throwOnAsmJSValidationFailure_);
+# undef PrintFields_
+
+ switch (asmJSOption_) {
+ case AsmJSOption::Enabled:
+ print("asmJSOption_", "AsmJSOption::Enabled");
+ break;
+ case AsmJSOption::DisabledByAsmJSPref:
+ print("asmJSOption_", "AsmJSOption::DisabledByAsmJSPref");
+ break;
+ case AsmJSOption::DisabledByLinker:
+ print("asmJSOption_", "AsmJSOption::DisabledByLinker");
+ break;
+ case AsmJSOption::DisabledByNoWasmCompiler:
+ print("asmJSOption_", "AsmJSOption::DisabledByNoWasmCompiler");
+ break;
+ case AsmJSOption::DisabledByDebugger:
+ print("asmJSOption_", "AsmJSOption::DisabledByDebugger");
+ break;
+ }
+ }
+#endif // defined(DEBUG) || defined(JS_JITSPEW)
+
+ private:
+ // ==== Syntax-related options. ====
+ bool importAttributes_ : 1;
+ bool importAttributesAssertSyntax_ : 1;
+
+ // The context has specified that source pragmas should be parsed.
+ bool sourcePragmas_ : 1;
+
+ // ==== asm.js options. ====
+ bool throwOnAsmJSValidationFailure_ : 1;
+
+ AsmJSOption asmJSOption_ = AsmJSOption::DisabledByAsmJSPref;
+};
+
+/**
+ * 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 {
+ friend class JS_PUBLIC_API ReadOnlyDecodeOptions;
+
+ protected:
+ // non-POD options:
+
+ JS::ConstUTF8CharsZ filename_;
+
+ JS::ConstUTF8CharsZ introducerFilename_;
+
+ const char16_t* sourceMapURL_ = nullptr;
+
+ // POD options:
+ // WARNING: When adding new fields, don't forget to add them to
+ // copyPODTransitiveOptions.
+
+ /**
+ * 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 the compile request may force
+ // strict-mode.
+ bool forceStrictMode_ = false;
+
+ // The Realm of this script is configured to use fdlibm math library.
+ bool alwaysUseFdlibm_ = false;
+
+ // Flag used to bypass the filename validation callback.
+ // See also SetFilenameValidationCallback.
+ bool skipFilenameValidation_ = false;
+
+ bool hideScriptFromDebugger_ = false;
+
+ // If set, this script will be hidden from the debugger. The requirement
+ // is that once compilation is finished, a call to UpdateDebugMetadata will
+ // be made, which will update the SSO with the appropiate debug metadata,
+ // and expose the script to the debugger (if hideScriptFromDebugger_ isn't
+ // set)
+ bool deferDebugMetadata_ = false;
+
+ // Off-thread delazification strategy is used to tell off-thread tasks how the
+ // delazification should be performed. Multiple strategies are available in
+ // order to test different approaches to the concurrent delazification.
+ DelazificationOption eagerDelazificationStrategy_ =
+ DelazificationOption::OnDemandOnly;
+
+ friend class JS_PUBLIC_API InstantiateOptions;
+
+ public:
+ bool selfHostingMode = false;
+ bool discardSource = false;
+ bool sourceIsLazy = false;
+ bool allowHTMLComments = true;
+ bool nonSyntacticScope = false;
+
+ // Top-level await is enabled by default but is not supported for chrome
+ // modules loaded with ChromeUtils.importModule.
+ bool topLevelAwait = true;
+
+ // When decoding from XDR into a Stencil, directly reference data in the
+ // buffer (where possible) instead of copying it. This is an optional
+ // performance optimization, and may also reduce memory if the buffer is going
+ // remain alive anyways.
+ //
+ // NOTE: The XDR buffer must remain alive as long as the Stencil does. Special
+ // care must be taken that there are no addition shared references to
+ // the Stencil.
+ //
+ // NOTE: Instantiated GC things may still outlive the buffer as long as the
+ // Stencil was cleaned up. This is covers a typical case where a decoded
+ // Stencil is instantiated once and then thrown away.
+ bool borrowBuffer = false;
+
+ // Similar to `borrowBuffer`, but additionally the JSRuntime may directly
+ // reference data in the buffer for JS bytecode. The `borrowBuffer` flag must
+ // be set if this is set. This can be a memory optimization in multi-process
+ // architectures where a (read-only) XDR buffer is mapped into multiple
+ // processes.
+ //
+ // NOTE: When using this mode, the XDR buffer must live until JS_Shutdown is
+ // called. There is currently no mechanism to release the data sooner.
+ bool usePinnedBytecode = false;
+
+ // De-optimize ES module's top-level `var`s, in order to define all of them
+ // on the ModuleEnvironmentObject, instead of local slot.
+ //
+ // This is used for providing all global variables in Cu.import return value
+ // (see bug 1766761 for more details), and this is temporary solution until
+ // ESM-ification finishes.
+ //
+ // WARNING: This option will eventually be removed.
+ bool deoptimizeModuleGlobalVars = false;
+
+ PrefableCompileOptions prefableOptions_;
+
+ /**
+ * |introductionType| is a statically allocated C string. See JSScript.h
+ * for more information.
+ */
+ const char* introductionType = nullptr;
+
+ unsigned introductionLineno = 0;
+ uint32_t introductionOffset = 0;
+ bool hasIntroductionInfo = false;
+
+ // WARNING: When adding new fields, don't forget to add them to
+ // copyPODTransitiveOptions.
+
+ 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);
+
+ bool isEagerDelazificationEqualTo(DelazificationOption val) const {
+ return eagerDelazificationStrategy() == val;
+ }
+
+ template <DelazificationOption... Values>
+ bool eagerDelazificationIsOneOf() const {
+ return (isEagerDelazificationEqualTo(Values) || ...);
+ }
+
+ 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 alwaysUseFdlibm() const { return alwaysUseFdlibm_; }
+ bool forceFullParse() const {
+ return eagerDelazificationIsOneOf<
+ DelazificationOption::ParseEverythingEagerly>();
+ }
+ bool forceStrictMode() const { return forceStrictMode_; }
+ bool consumeDelazificationCache() const {
+ return eagerDelazificationIsOneOf<
+ DelazificationOption::ConcurrentDepthFirst,
+ DelazificationOption::ConcurrentLargeFirst>();
+ }
+ bool populateDelazificationCache() const {
+ return eagerDelazificationIsOneOf<
+ DelazificationOption::CheckConcurrentWithOnDemand,
+ DelazificationOption::ConcurrentDepthFirst,
+ DelazificationOption::ConcurrentLargeFirst>();
+ }
+ bool waitForDelazificationCache() const {
+ return eagerDelazificationIsOneOf<
+ DelazificationOption::CheckConcurrentWithOnDemand>();
+ }
+ bool checkDelazificationCache() const {
+ return eagerDelazificationIsOneOf<
+ DelazificationOption::CheckConcurrentWithOnDemand>();
+ }
+ DelazificationOption eagerDelazificationStrategy() const {
+ return eagerDelazificationStrategy_;
+ }
+
+ bool importAttributes() const { return prefableOptions_.importAttributes(); }
+ bool importAttributesAssertSyntax() const {
+ return prefableOptions_.importAttributesAssertSyntax();
+ }
+ bool sourcePragmas() const { return prefableOptions_.sourcePragmas(); }
+ bool throwOnAsmJSValidationFailure() const {
+ return prefableOptions_.throwOnAsmJSValidationFailure();
+ }
+ AsmJSOption asmJSOption() const { return prefableOptions_.asmJSOption(); }
+ void setAsmJSOption(AsmJSOption option) {
+ prefableOptions_.setAsmJSOption(option);
+ }
+
+ JS::ConstUTF8CharsZ filename() const { return filename_; }
+ JS::ConstUTF8CharsZ introducerFilename() const { return introducerFilename_; }
+ const char16_t* sourceMapURL() const { return sourceMapURL_; }
+
+ const PrefableCompileOptions& prefableOptions() const {
+ return prefableOptions_;
+ }
+
+ TransitiveCompileOptions(const TransitiveCompileOptions&) = delete;
+ TransitiveCompileOptions& operator=(const TransitiveCompileOptions&) = delete;
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+ template <typename Printer>
+ void dumpWith(Printer& print) const {
+# define PrintFields_(Name) print(#Name, Name)
+ PrintFields_(filename_);
+ PrintFields_(introducerFilename_);
+ PrintFields_(sourceMapURL_);
+ PrintFields_(mutedErrors_);
+ PrintFields_(forceStrictMode_);
+ PrintFields_(alwaysUseFdlibm_);
+ PrintFields_(skipFilenameValidation_);
+ PrintFields_(hideScriptFromDebugger_);
+ PrintFields_(deferDebugMetadata_);
+ PrintFields_(eagerDelazificationStrategy_);
+ PrintFields_(selfHostingMode);
+ PrintFields_(discardSource);
+ PrintFields_(sourceIsLazy);
+ PrintFields_(allowHTMLComments);
+ PrintFields_(nonSyntacticScope);
+ PrintFields_(topLevelAwait);
+ PrintFields_(borrowBuffer);
+ PrintFields_(usePinnedBytecode);
+ PrintFields_(deoptimizeModuleGlobalVars);
+ PrintFields_(introductionType);
+ PrintFields_(introductionLineno);
+ PrintFields_(introductionOffset);
+ PrintFields_(hasIntroductionInfo);
+# undef PrintFields_
+
+ prefableOptions_.dumpWith(print);
+ }
+#endif // defined(DEBUG) || defined(JS_JITSPEW)
+};
+
+/**
+ * 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.
+
+ // Line number of the first character (1-origin).
+ uint32_t lineno = 1;
+ // Column number of the first character in UTF-16 code units.
+ JS::ColumnNumberOneOrigin column;
+
+ // 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;
+
+ public:
+#if defined(DEBUG) || defined(JS_JITSPEW)
+ template <typename Printer>
+ void dumpWith(Printer& print) const {
+ this->TransitiveCompileOptions::dumpWith(print);
+# define PrintFields_(Name) print(#Name, Name)
+ PrintFields_(lineno);
+ print("column", column.oneOriginValue());
+ PrintFields_(scriptSourceOffset);
+ PrintFields_(isRunOnce);
+ PrintFields_(noScriptRval);
+# undef PrintFields_
+ }
+#endif // defined(DEBUG) || defined(JS_JITSPEW)
+};
+
+class JS_PUBLIC_API OwningDecodeOptions;
+
+/**
+ * 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 {
+ public:
+ // A minimal constructor, for use with OwningCompileOptions::copy.
+ explicit OwningCompileOptions(JSContext* cx);
+
+ struct ForFrontendContext {};
+ explicit OwningCompileOptions(const ForFrontendContext&)
+ : ReadOnlyCompileOptions() {}
+
+ ~OwningCompileOptions();
+
+ private:
+ template <typename ContextT>
+ bool copyImpl(ContextT* cx, const ReadOnlyCompileOptions& rhs);
+
+ public:
+ /** Set this to a copy of |rhs|. Return false on OOM. */
+ bool copy(JSContext* cx, const ReadOnlyCompileOptions& rhs);
+ bool copy(JS::FrontendContext* fc, const ReadOnlyCompileOptions& rhs);
+
+ void steal(OwningCompileOptions&& rhs);
+ void steal(OwningDecodeOptions&& 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 {
+ 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() {
+ copyPODNonTransitiveOptions(rhs);
+ copyPODTransitiveOptions(rhs);
+
+ filename_ = rhs.filename();
+ introducerFilename_ = rhs.introducerFilename();
+ sourceMapURL_ = rhs.sourceMapURL();
+ }
+
+ // Construct a CompileOption in the context where JSContext is not available.
+ // prefableOptions should reflect the compilation-specific user prefs.
+ explicit CompileOptions(const PrefableCompileOptions& prefableOptions)
+ : ReadOnlyCompileOptions() {
+ prefableOptions_ = prefableOptions;
+ }
+
+ CompileOptions& setFile(const char* f) {
+ filename_ = JS::ConstUTF8CharsZ(f);
+ return *this;
+ }
+
+ CompileOptions& setLine(uint32_t l) {
+ lineno = l;
+ return *this;
+ }
+
+ CompileOptions& setFileAndLine(const char* f, uint32_t l) {
+ filename_ = JS::ConstUTF8CharsZ(f);
+ lineno = l;
+ return *this;
+ }
+
+ CompileOptions& setSourceMapURL(const char16_t* s) {
+ sourceMapURL_ = s;
+ return *this;
+ }
+
+ CompileOptions& setMutedErrors(bool mute) {
+ mutedErrors_ = mute;
+ return *this;
+ }
+
+ CompileOptions& setColumn(JS::ColumnNumberOneOrigin 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& setDeferDebugMetadata(bool v = true) {
+ deferDebugMetadata_ = v;
+ return *this;
+ }
+
+ CompileOptions& setHideScriptFromDebugger(bool v = true) {
+ hideScriptFromDebugger_ = v;
+ return *this;
+ }
+
+ CompileOptions& setIntroductionInfo(const char* introducerFn,
+ const char* intro, uint32_t line,
+ uint32_t offset) {
+ introducerFilename_ = JS::ConstUTF8CharsZ(introducerFn);
+ introductionType = intro;
+ introductionLineno = line;
+ introductionOffset = offset;
+ hasIntroductionInfo = true;
+ return *this;
+ }
+
+ // Set introduction information according to any currently executing script.
+ CompileOptions& setIntroductionInfoToCaller(
+ JSContext* cx, const char* introductionType,
+ JS::MutableHandle<JSScript*> introductionScript);
+
+ CompileOptions& setDiscardSource() {
+ discardSource = true;
+ return *this;
+ }
+
+ CompileOptions& setForceFullParse() {
+ eagerDelazificationStrategy_ = DelazificationOption::ParseEverythingEagerly;
+ return *this;
+ }
+
+ CompileOptions& setEagerDelazificationStrategy(
+ DelazificationOption strategy) {
+ // forceFullParse is at the moment considered as a non-overridable strategy.
+ MOZ_RELEASE_ASSERT(eagerDelazificationStrategy_ !=
+ DelazificationOption::ParseEverythingEagerly ||
+ strategy ==
+ DelazificationOption::ParseEverythingEagerly);
+ eagerDelazificationStrategy_ = strategy;
+ 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;
+};
+
+/**
+ * Subset of CompileOptions fields used while instantiating Stencils.
+ */
+class JS_PUBLIC_API InstantiateOptions {
+ public:
+ bool skipFilenameValidation = false;
+ bool hideScriptFromDebugger = false;
+ bool deferDebugMetadata = false;
+
+ InstantiateOptions() = default;
+
+ explicit InstantiateOptions(const ReadOnlyCompileOptions& options)
+ : skipFilenameValidation(options.skipFilenameValidation_),
+ hideScriptFromDebugger(options.hideScriptFromDebugger_),
+ deferDebugMetadata(options.deferDebugMetadata_) {}
+
+ void copyTo(CompileOptions& options) const {
+ options.skipFilenameValidation_ = skipFilenameValidation;
+ options.hideScriptFromDebugger_ = hideScriptFromDebugger;
+ options.deferDebugMetadata_ = deferDebugMetadata;
+ }
+
+ bool hideFromNewScriptInitial() const {
+ return deferDebugMetadata || hideScriptFromDebugger;
+ }
+
+#ifdef DEBUG
+ // Assert that all fields have default value.
+ //
+ // This can be used when instantiation is performed as separate step than
+ // compile-to-stencil, and CompileOptions isn't available there.
+ void assertDefault() const {
+ MOZ_ASSERT(skipFilenameValidation == false);
+ MOZ_ASSERT(hideScriptFromDebugger == false);
+ MOZ_ASSERT(deferDebugMetadata == false);
+ }
+#endif
+};
+
+/**
+ * Subset of CompileOptions fields used while decoding Stencils.
+ */
+class JS_PUBLIC_API ReadOnlyDecodeOptions {
+ public:
+ bool borrowBuffer = false;
+ bool usePinnedBytecode = false;
+
+ protected:
+ JS::ConstUTF8CharsZ introducerFilename_;
+
+ public:
+ // See `TransitiveCompileOptions::introductionType` field for details.
+ const char* introductionType = nullptr;
+
+ uint32_t introductionLineno = 0;
+ uint32_t introductionOffset = 0;
+
+ protected:
+ ReadOnlyDecodeOptions() = default;
+
+ ReadOnlyDecodeOptions(const ReadOnlyDecodeOptions&) = delete;
+ ReadOnlyDecodeOptions& operator=(const ReadOnlyDecodeOptions&) = delete;
+
+ template <typename T>
+ void copyPODOptionsFrom(const T& options) {
+ borrowBuffer = options.borrowBuffer;
+ usePinnedBytecode = options.usePinnedBytecode;
+ introductionType = options.introductionType;
+ introductionLineno = options.introductionLineno;
+ introductionOffset = options.introductionOffset;
+ }
+
+ template <typename T>
+ void copyPODOptionsTo(T& options) const {
+ options.borrowBuffer = borrowBuffer;
+ options.usePinnedBytecode = usePinnedBytecode;
+ options.introductionType = introductionType;
+ options.introductionLineno = introductionLineno;
+ options.introductionOffset = introductionOffset;
+ }
+
+ public:
+ void copyTo(CompileOptions& options) const {
+ copyPODOptionsTo(options);
+ options.introducerFilename_ = introducerFilename_;
+ }
+
+ JS::ConstUTF8CharsZ introducerFilename() const { return introducerFilename_; }
+};
+
+class MOZ_STACK_CLASS JS_PUBLIC_API DecodeOptions final
+ : public ReadOnlyDecodeOptions {
+ public:
+ DecodeOptions() = default;
+
+ explicit DecodeOptions(const ReadOnlyCompileOptions& options) {
+ copyPODOptionsFrom(options);
+
+ introducerFilename_ = options.introducerFilename();
+ }
+};
+
+class JS_PUBLIC_API OwningDecodeOptions final : public ReadOnlyDecodeOptions {
+ friend class OwningCompileOptions;
+
+ public:
+ OwningDecodeOptions() = default;
+
+ ~OwningDecodeOptions();
+
+ bool copy(JS::FrontendContext* maybeFc, const ReadOnlyDecodeOptions& rhs);
+ void infallibleCopy(const ReadOnlyDecodeOptions& rhs);
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ private:
+ void release();
+
+ OwningDecodeOptions(const OwningDecodeOptions&) = delete;
+ OwningDecodeOptions& operator=(const OwningDecodeOptions&) = delete;
+};
+
+} // namespace JS
+
+#endif /* js_CompileOptions_h */
diff --git a/js/public/Context.h b/js/public/Context.h
new file mode 100644
index 0000000000..13a18e3e05
--- /dev/null
+++ b/js/public/Context.h
@@ -0,0 +1,107 @@
+/* -*- 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_Context_h
+#define js_Context_h
+
+#include "jspubtd.h"
+// [SMDOC] Nested Thread Data Structures (JSContext, JSRuntime)
+//
+// Spidermonkey has two nested data structures for representing threads,
+// JSContext and JSRuntime. All JS threads are represented by a context.
+// Contexts can contain runtimes. A runtime however is not present for
+// all threads. Threads also interact with the GC. See "Nested GC
+// DataStructures" for more info.
+//
+// Context
+// -------
+// JSContext represents a thread: there must be exactly one JSContext for each
+// thread running JS/Wasm.
+//
+// Runtime
+// -------
+// JSRuntime is very similar to JSContext: each runtime belongs to one context
+// (thread), but helper threads don't have their own runtimes (they're shared by
+// all runtimes in the process and use the runtime of the task they're
+// executing).
+//
+// Note:
+// Locking, contexts, and memory allocation.
+//
+// It is important that SpiderMonkey be initialized, and the first context
+// be created, in a single-threaded fashion. Otherwise the behavior of the
+// library is undefined.
+// See:
+// https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference
+
+// Create a new context (and runtime) for this thread.
+extern JS_PUBLIC_API JSContext* JS_NewContext(
+ uint32_t maxbytes, JSRuntime* parentRuntime = nullptr);
+
+// Destroy a context allocated with JS_NewContext. Must be called on the thread
+// that called JS_NewContext.
+extern JS_PUBLIC_API void JS_DestroyContext(JSContext* cx);
+
+JS_PUBLIC_API void* JS_GetContextPrivate(JSContext* cx);
+
+JS_PUBLIC_API void JS_SetContextPrivate(JSContext* cx, void* data);
+
+extern JS_PUBLIC_API JSRuntime* JS_GetParentRuntime(JSContext* cx);
+
+extern JS_PUBLIC_API JSRuntime* JS_GetRuntime(JSContext* cx);
+
+extern JS_PUBLIC_API void JS_SetFutexCanWait(JSContext* cx);
+
+namespace js {
+
+void AssertHeapIsIdle();
+
+} /* namespace js */
+
+namespace JS {
+
+/**
+ * Asserts (in debug and release builds) that `obj` belongs to the current
+ * thread's context.
+ */
+JS_PUBLIC_API void AssertObjectBelongsToCurrentThread(JSObject* obj);
+
+/**
+ * Install a process-wide callback to validate script filenames. The JS engine
+ * will invoke this callback for each JS script it parses or XDR decodes.
+ *
+ * If the callback returns |false|, an exception is thrown and parsing/decoding
+ * will be aborted.
+ *
+ * See also CompileOptions::setSkipFilenameValidation to opt-out of the callback
+ * for specific parse jobs.
+ */
+using FilenameValidationCallback = bool (*)(JSContext* cx,
+ const char* filename);
+JS_PUBLIC_API void SetFilenameValidationCallback(FilenameValidationCallback cb);
+
+/**
+ * Install an context wide callback that implements the ECMA262 specification
+ * host hook `HostEnsureCanAddPrivateElement`.
+ *
+ * This hook, which should only be overriden for Web Browsers, examines the
+ * provided object to determine if the addition of a private field is allowed,
+ * throwing an exception and returning false if not.
+ *
+ * The default implementation of this hook, which will be used unless overriden,
+ * examines only proxy objects, and throws if the proxy handler returns true
+ * from the handler method `throwOnPrivateField()`.
+ */
+using EnsureCanAddPrivateElementOp = bool (*)(JSContext* cx, HandleValue val);
+
+JS_PUBLIC_API void SetHostEnsureCanAddPrivateElementHook(
+ JSContext* cx, EnsureCanAddPrivateElementOp op);
+
+} /* namespace JS */
+
+#endif // js_Context_h
diff --git a/js/public/ContextOptions.h b/js/public/ContextOptions.h
new file mode 100644
index 0000000000..25744ce9ea
--- /dev/null
+++ b/js/public/ContextOptions.h
@@ -0,0 +1,255 @@
+/* -*- 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
+
+#include "js/CompileOptions.h" // PrefableCompileOptions
+#include "js/WasmFeatures.h"
+
+struct JS_PUBLIC_API JSContext;
+
+namespace JS {
+
+class JS_PUBLIC_API ContextOptions {
+ public:
+ // clang-format off
+ ContextOptions()
+ : wasm_(true),
+ wasmForTrustedPrinciples_(true),
+ wasmVerbose_(false),
+ wasmBaseline_(true),
+ wasmIon_(true),
+#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, ...) wasm##NAME##_(STAGE == WasmFeatureStage::Default),
+ JS_FOR_WASM_FEATURES(WASM_FEATURE)
+#undef WASM_FEATURE
+ testWasmAwaitTier2_(false),
+ disableIon_(false),
+ disableEvalSecurityChecks_(false),
+ asyncStack_(true),
+ asyncStackCaptureDebuggeeOnly_(false),
+ throwOnDebuggeeWouldRun_(true),
+ dumpStackOnDebuggeeWouldRun_(false),
+#ifdef JS_ENABLE_SMOOSH
+ trackNotImplemented_(false),
+ trySmoosh_(false),
+#endif
+ fuzzing_(false) {
+ }
+ // clang-format on
+
+ bool asmJS() const {
+ return compileOptions_.asmJSOption() == AsmJSOption::Enabled;
+ }
+ AsmJSOption asmJSOption() const { return compileOptions_.asmJSOption(); }
+ ContextOptions& setAsmJS(bool flag) {
+ compileOptions_.setAsmJS(flag);
+ return *this;
+ }
+ ContextOptions& setAsmJSOption(AsmJSOption option) {
+ compileOptions_.setAsmJSOption(option);
+ 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 testWasmAwaitTier2() const { return testWasmAwaitTier2_; }
+ ContextOptions& setTestWasmAwaitTier2(bool flag) {
+ testWasmAwaitTier2_ = flag;
+ return *this;
+ }
+
+#define WASM_FEATURE(NAME, ...) \
+ bool wasm##NAME() const { return wasm##NAME##_; } \
+ ContextOptions& setWasm##NAME(bool flag) { \
+ wasm##NAME##_ = flag; \
+ return *this; \
+ }
+ JS_FOR_WASM_FEATURES(WASM_FEATURE)
+#undef WASM_FEATURE
+
+ bool throwOnAsmJSValidationFailure() const {
+ return compileOptions_.throwOnAsmJSValidationFailure();
+ }
+ ContextOptions& setThrowOnAsmJSValidationFailure(bool flag) {
+ compileOptions_.setThrowOnAsmJSValidationFailure(flag);
+ return *this;
+ }
+ ContextOptions& toggleThrowOnAsmJSValidationFailure() {
+ compileOptions_.toggleThrowOnAsmJSValidationFailure();
+ 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 importAttributes() const { return compileOptions_.importAttributes(); }
+ ContextOptions& setImportAttributes(bool enabled) {
+ compileOptions_.setImportAttributes(enabled);
+ return *this;
+ }
+
+ bool importAttributesAssertSyntax() const {
+ return compileOptions_.importAttributesAssertSyntax();
+ }
+ ContextOptions& setImportAttributesAssertSyntax(bool enabled) {
+ compileOptions_.setImportAttributesAssertSyntax(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 compileOptions_.sourcePragmas(); }
+ ContextOptions& setSourcePragmas(bool flag) {
+ compileOptions_.setSourcePragmas(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;
+ }
+
+#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() {
+ setAsmJSOption(AsmJSOption::DisabledByAsmJSPref);
+ setWasmBaseline(false);
+ }
+
+ PrefableCompileOptions& compileOptions() { return compileOptions_; }
+ const PrefableCompileOptions& compileOptions() const {
+ return compileOptions_;
+ }
+
+ private:
+ // WASM options.
+ bool wasm_ : 1;
+ bool wasmForTrustedPrinciples_ : 1;
+ bool wasmVerbose_ : 1;
+ bool wasmBaseline_ : 1;
+ bool wasmIon_ : 1;
+#define WASM_FEATURE(NAME, ...) bool wasm##NAME##_ : 1;
+ JS_FOR_WASM_FEATURES(WASM_FEATURE)
+#undef WASM_FEATURE
+ bool testWasmAwaitTier2_ : 1;
+
+ // JIT options.
+ bool disableIon_ : 1;
+ bool disableEvalSecurityChecks_ : 1;
+
+ // Runtime options.
+ bool asyncStack_ : 1;
+ bool asyncStackCaptureDebuggeeOnly_ : 1;
+ bool throwOnDebuggeeWouldRun_ : 1;
+ bool dumpStackOnDebuggeeWouldRun_ : 1;
+#ifdef JS_ENABLE_SMOOSH
+ bool trackNotImplemented_ : 1;
+ bool trySmoosh_ : 1;
+#endif
+ bool fuzzing_ : 1;
+
+ // Compile options.
+ PrefableCompileOptions compileOptions_;
+};
+
+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..63a9c5111e
--- /dev/null
+++ b/js/public/Conversions.h
@@ -0,0 +1,601 @@
+/* -*- 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 !std::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 (!std::isfinite(d)) {
+ if (std::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);
+}
+
+#ifdef ENABLE_RECORD_TUPLE
+inline JSObject* ToObjectOrGetObjectPayload(JSContext* cx, HandleValue v) {
+ detail::AssertArgumentsAreSane(cx, v);
+
+ if (v.hasObjectPayload()) {
+ return &v.getObjectPayload();
+ }
+ return js::ToObjectSlow(cx, v, false);
+}
+#endif
+
+/**
+ * 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..523e84c8c9
--- /dev/null
+++ b/js/public/Date.h
@@ -0,0 +1,222 @@
+/* -*- 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,}, mozilla::UnspecifiedNaN
+#include "mozilla/MathAlgorithms.h" // mozilla::Abs
+
+#include "js/CharacterEncoding.h" // JS::Latin1Chars
+#include "js/Conversions.h" // JS::ToInteger
+#include "js/RealmOptions.h" // JS::RTPCallerTypeToken
+#include "js/TypeDecls.h"
+#include "js/Value.h" // JS::CanonicalizeNaN, JS::DoubleValue, JS::Value
+
+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 !std::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 (!std::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 double (the time
+// to clamp and jitter) and a JS::RTPCallerTypeToken (a wrapper for
+// mozilla::RTPCallerType) that can be used to decide the proper clamping
+// behavior to use. 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, JS::RTPCallerTypeToken, JSContext*);
+
+// Set a callback into the toolkit/components/resistfingerprinting function that
+// will centralize time resolution and jitter into one place.
+// Defining such a callback requires all Realms that are created afterwards
+// to have a set JS::RTPCallerTypeToken, via RealmBehaviors or
+// JS::SetRealmReduceTimerPrecisionCallerType.
+JS_PUBLIC_API void SetReduceMicrosecondTimePrecisionCallback(
+ ReduceMicrosecondTimePrecisionCallback callback);
+
+// Get the previously set ReduceMicrosecondTimePrecisionCallback callback or
+// nullptr.
+JS_PUBLIC_API ReduceMicrosecondTimePrecisionCallback
+GetReduceMicrosecondTimePrecisionCallback();
+
+// 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);
+
+// Returns whether a given string follows the Date Time String Format.
+JS_PUBLIC_API bool IsISOStyleDate(JSContext* cx, const JS::Latin1Chars& str);
+
+} // namespace JS
+
+#endif /* js_Date_h */
diff --git a/js/public/Debug.h b/js/public/Debug.h
new file mode 100644
index 0000000000..6d7fd8a4be
--- /dev/null
+++ b/js/public/Debug.h
@@ -0,0 +1,354 @@
+/* -*- 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 <utility>
+
+#include "jstypes.h"
+
+#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+
+namespace js {
+class Debugger;
+} // namespace js
+
+/* Defined in vm/Debugger.cpp. */
+extern JS_PUBLIC_API bool JS_DefineDebuggerObject(JSContext* cx,
+ JS::HandleObject obj);
+
+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.
+#ifdef 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>::create())
+ : 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..7460aebe16
--- /dev/null
+++ b/js/public/Equality.h
@@ -0,0 +1,65 @@
+/* -*- 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/TypeDecls.h"
+
+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/ErrorInterceptor.h b/js/public/ErrorInterceptor.h
new file mode 100644
index 0000000000..7481c48fcb
--- /dev/null
+++ b/js/public/ErrorInterceptor.h
@@ -0,0 +1,45 @@
+/* -*- 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/. */
+
+#ifndef js_ErrorInterceptor_h
+#define js_ErrorInterceptor_h
+
+#include "jstypes.h"
+
+#include "js/TypeDecls.h"
+
+/**
+ * Callback used to intercept JavaScript errors.
+ */
+struct JSErrorInterceptor {
+ /**
+ * This method is called whenever an error has been raised from JS code.
+ *
+ * This method MUST be infallible.
+ */
+ virtual void interceptError(JSContext* cx, JS::HandleValue error) = 0;
+};
+
+// Set a callback that will be called whenever an error
+// is thrown in this runtime. This is designed as a mechanism
+// for logging errors. Note that the VM makes no attempt to sanitize
+// the contents of the error (so it may contain private data)
+// or to sort out among errors (so it may not be the error you
+// are interested in or for the component in which you are
+// interested).
+//
+// If the callback sets a new error, this new error
+// will replace the original error.
+//
+// May be `nullptr`.
+// This is a no-op if built without NIGHTLY_BUILD.
+extern JS_PUBLIC_API void JS_SetErrorInterceptorCallback(
+ JSRuntime*, JSErrorInterceptor* callback);
+
+// This returns nullptr if built without NIGHTLY_BUILD.
+extern JS_PUBLIC_API JSErrorInterceptor* JS_GetErrorInterceptorCallback(
+ JSRuntime*);
+
+#endif // js_ErrorInterceptor_h
diff --git a/js/public/ErrorReport.h b/js/public/ErrorReport.h
new file mode 100644
index 0000000000..9bf824dea2
--- /dev/null
+++ b/js/public/ErrorReport.h
@@ -0,0 +1,545 @@
+/* -*- 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 APIs.
+ *
+ * Despite the types and structures defined here 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 "mozilla/Maybe.h" // mozilla::Maybe
+
+#include <cstdarg>
+#include <iterator> // std::input_iterator_tag, std::iterator
+#include <stdarg.h>
+#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"
+#include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
+#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
+#include "js/RootingAPI.h" // JS::HandleObject, JS::RootedObject
+#include "js/UniquePtr.h" // js::UniquePtr
+#include "js/Value.h" // JS::Value
+#include "js/Vector.h" // js::Vector
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+class ExceptionStack;
+}
+namespace js {
+class SystemAllocPolicy;
+
+enum ErrorArgumentsType {
+ ArgumentsAreUnicode,
+ ArgumentsAreASCII,
+ ArgumentsAreLatin1,
+ ArgumentsAreUTF8
+};
+} // namespace js
+
+/**
+ * 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.
+ JS::ConstUTF8CharsZ filename;
+
+ // Unique identifier for the script source.
+ unsigned sourceId;
+
+ // Source line number (1-origin).
+ uint32_t lineno;
+
+ // Column number in line in UTF-16 code units.
+ JS::ColumnNumberOneOrigin 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),
+ errorNumber(0),
+ errorMessageName(nullptr),
+ ownsMessage_(false) {}
+ JSErrorBase(JSErrorBase&& other) noexcept
+ : message_(other.message_),
+ filename(other.filename),
+ sourceId(other.sourceId),
+ lineno(other.lineno),
+ column(other.column),
+ errorNumber(other.errorNumber),
+ errorMessageName(other.errorMessageName),
+ ownsMessage_(other.ownsMessage_) {
+ if (ownsMessage_) {
+ other.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_;
+
+ bool addNoteVA(js::FrontendContext* fc, const char* filename,
+ unsigned sourceId, uint32_t lineno,
+ JS::ColumnNumberOneOrigin column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber,
+ js::ErrorArgumentsType argumentsType, va_list ap);
+
+ public:
+ JSErrorNotes();
+ ~JSErrorNotes();
+
+ // Add a note to the given position.
+ bool addNoteASCII(JSContext* cx, const char* filename, unsigned sourceId,
+ uint32_t lineno, JS::ColumnNumberOneOrigin column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteASCII(js::FrontendContext* fc, const char* filename,
+ unsigned sourceId, uint32_t lineno,
+ JS::ColumnNumberOneOrigin column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteLatin1(JSContext* cx, const char* filename, unsigned sourceId,
+ uint32_t lineno, JS::ColumnNumberOneOrigin column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteLatin1(js::FrontendContext* fc, const char* filename,
+ unsigned sourceId, uint32_t lineno,
+ JS::ColumnNumberOneOrigin column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteUTF8(JSContext* cx, const char* filename, unsigned sourceId,
+ uint32_t lineno, JS::ColumnNumberOneOrigin column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteUTF8(js::FrontendContext* fc, const char* filename,
+ unsigned sourceId, uint32_t lineno,
+ JS::ColumnNumberOneOrigin 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(JSErrorReport&& other) noexcept
+ : JSErrorBase(std::move(other)),
+ linebuf_(other.linebuf_),
+ linebufLength_(other.linebufLength_),
+ tokenOffset_(other.tokenOffset_),
+ notes(std::move(other.notes)),
+ exnType(other.exnType),
+ isMuted(other.isMuted),
+ isWarning_(other.isWarning_),
+ ownsLinebuf_(other.ownsLinebuf_) {
+ if (ownsLinebuf_) {
+ other.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(FILE* file, JSErrorReport* report,
+ bool reportWarnings);
+
+extern JS_PUBLIC_API void PrintError(FILE* file,
+ const JS::ErrorReportBuilder& builder,
+ bool reportWarnings);
+
+} // namespace JS
+
+/*
+ * There are four encoding variants for the error reporting API:
+ * UTF-8
+ * JSAPI's default encoding for error handling. Use this when the encoding
+ * of the error message, format string, and arguments is UTF-8.
+ * ASCII
+ * Equivalent to UTF-8, but also asserts that the error message, format
+ * string, and arguments are all ASCII. Because ASCII is a subset of UTF-8,
+ * any use of this encoding variant *could* be replaced with use of the
+ * UTF-8 variant. This variant exists solely to double-check the
+ * developer's assumption that all these strings truly are ASCII, given that
+ * UTF-8 and ASCII strings regrettably have the same C++ type.
+ * UC = UTF-16
+ * Use this when arguments are UTF-16. The format string must be UTF-8.
+ * Latin1 (planned to be removed)
+ * In this variant, all strings are interpreted byte-for-byte as the
+ * corresponding Unicode codepoint. This encoding may *safely* be used on
+ * any null-terminated string, regardless of its encoding. (You shouldn't
+ * *actually* be uncertain, but in the real world, a string's encoding -- if
+ * promised at all -- may be more...aspirational...than reality.) This
+ * encoding variant will eventually be removed -- work to convert your uses
+ * to UTF-8 as you're able.
+ */
+
+namespace JS {
+const uint16_t MaxNumErrorArguments = 10;
+};
+
+/**
+ * Report an exception represented by the sprintf-like conversion of format
+ * and its arguments.
+ */
+extern JS_PUBLIC_API void JS_ReportErrorASCII(JSContext* cx, const char* format,
+ ...) MOZ_FORMAT_PRINTF(2, 3);
+
+extern JS_PUBLIC_API void JS_ReportErrorLatin1(JSContext* cx,
+ const char* format, ...)
+ MOZ_FORMAT_PRINTF(2, 3);
+
+extern JS_PUBLIC_API void JS_ReportErrorUTF8(JSContext* cx, const char* format,
+ ...) MOZ_FORMAT_PRINTF(2, 3);
+
+/*
+ * Use an errorNumber to retrieve the format string, args are char*
+ */
+extern JS_PUBLIC_API void JS_ReportErrorNumberASCII(
+ JSContext* cx, JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+
+extern JS_PUBLIC_API void JS_ReportErrorNumberASCIIVA(
+ JSContext* cx, JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, va_list ap);
+
+extern JS_PUBLIC_API void JS_ReportErrorNumberLatin1(
+ JSContext* cx, JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+
+#ifdef va_start
+extern JS_PUBLIC_API void JS_ReportErrorNumberLatin1VA(
+ JSContext* cx, JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, va_list ap);
+#endif
+
+extern JS_PUBLIC_API void JS_ReportErrorNumberUTF8(
+ JSContext* cx, JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+
+#ifdef va_start
+extern JS_PUBLIC_API void JS_ReportErrorNumberUTF8VA(
+ JSContext* cx, JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, va_list ap);
+#endif
+
+/*
+ * args is null-terminated. That is, a null char* means there are no
+ * more args. The number of args must match the number expected for
+ * errorNumber for the given JSErrorCallback.
+ */
+extern JS_PUBLIC_API void JS_ReportErrorNumberUTF8Array(
+ JSContext* cx, JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, const char** args);
+
+/*
+ * Use an errorNumber to retrieve the format string, args are char16_t*
+ */
+extern JS_PUBLIC_API void JS_ReportErrorNumberUC(JSContext* cx,
+ JSErrorCallback errorCallback,
+ void* userRef,
+ const unsigned errorNumber,
+ ...);
+
+extern JS_PUBLIC_API void JS_ReportErrorNumberUCArray(
+ JSContext* cx, JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, const char16_t** args);
+
+/**
+ * Complain when out of memory.
+ */
+extern MOZ_COLD JS_PUBLIC_API void JS_ReportOutOfMemory(JSContext* cx);
+
+extern JS_PUBLIC_API bool JS_ExpandErrorArgumentsASCII(
+ JSContext* cx, JSErrorCallback errorCallback, const unsigned errorNumber,
+ JSErrorReport* reportp, ...);
+
+/**
+ * Complain when an allocation size overflows the maximum supported limit.
+ */
+extern JS_PUBLIC_API void JS_ReportAllocationOverflow(JSContext* cx);
+
+namespace JS {
+
+extern JS_PUBLIC_API bool CreateError(
+ JSContext* cx, JSExnType type, HandleObject stack, HandleString fileName,
+ uint32_t lineNumber, JS::ColumnNumberOneOrigin column,
+ JSErrorReport* report, HandleString message,
+ Handle<mozilla::Maybe<Value>> cause, MutableHandleValue rval);
+
+} /* namespace JS */
+
+#endif /* js_ErrorReport_h */
diff --git a/js/public/Exception.h b/js/public/Exception.h
new file mode 100644
index 0000000000..649c40ac47
--- /dev/null
+++ b/js/public/Exception.h
@@ -0,0 +1,196 @@
+/* -*- 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 "mozilla/Maybe.h"
+
+#include "jstypes.h"
+
+#include "js/RootingAPI.h" // JS::{Handle,Rooted}
+#include "js/TypeDecls.h"
+#include "js/Value.h" // JS::Value, JS::Handle<JS::Value>
+
+class JSErrorReport;
+
+namespace JS {
+enum class ExceptionStackBehavior : bool {
+ // Do not capture any stack.
+ DoNotCapture,
+
+ // Capture the current JS stack when setting the exception. It may be
+ // retrieved by JS::GetPendingExceptionStack.
+ Capture
+};
+} // namespace JS
+
+extern JS_PUBLIC_API bool JS_IsExceptionPending(JSContext* cx);
+
+extern JS_PUBLIC_API bool JS_IsThrowingOutOfMemory(JSContext* cx);
+
+extern JS_PUBLIC_API bool JS_GetPendingException(JSContext* cx,
+ JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API void JS_SetPendingException(
+ JSContext* cx, JS::HandleValue v,
+ JS::ExceptionStackBehavior behavior = JS::ExceptionStackBehavior::Capture);
+
+extern JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx);
+
+/**
+ * If the given object is an exception object, the exception will have (or be
+ * able to lazily create) an error report struct, and this function will return
+ * the address of that struct. Otherwise, it returns nullptr. The lifetime
+ * of the error report struct that might be returned is the same as the
+ * lifetime of the exception object.
+ */
+extern JS_PUBLIC_API JSErrorReport* JS_ErrorFromException(JSContext* cx,
+ JS::HandleObject obj);
+
+namespace JS {
+
+// When propagating an exception up the call stack, we store the underlying
+// reason on the JSContext as one of the following enum values.
+//
+// TODO: Track uncatchable exceptions explicitly.
+enum class ExceptionStatus {
+ // No exception status.
+ None,
+
+ // Used by debugger when forcing an early return from a frame. This uses
+ // exception machinery, but at the right time is turned back into a normal
+ // non-error completion.
+ ForcedReturn,
+
+ // Throwing a (catchable) exception. Certain well-known exceptions are
+ // explicitly tracked for convenience.
+ Throwing,
+ OutOfMemory,
+ OverRecursed,
+};
+
+// Returns true if the status is a catchable exception. Formerly this was
+// indicated by the `JSContext::throwing` flag.
+static MOZ_ALWAYS_INLINE bool IsCatchableExceptionStatus(
+ ExceptionStatus status) {
+ return status >= ExceptionStatus::Throwing;
+}
+
+// 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_; }
+};
+
+/**
+ * Save and later restore the current exception state of a given JSContext.
+ * This is useful for implementing behavior in C++ that's like try/catch
+ * or try/finally in JS.
+ *
+ * Typical usage:
+ *
+ * bool ok = JS::Evaluate(cx, ...);
+ * AutoSaveExceptionState savedExc(cx);
+ * ... cleanup that might re-enter JS ...
+ * return ok;
+ */
+class JS_PUBLIC_API AutoSaveExceptionState {
+ private:
+ JSContext* context;
+ ExceptionStatus status;
+ RootedValue exceptionValue;
+ RootedObject exceptionStack;
+
+ public:
+ /*
+ * Take a snapshot of cx's current exception state. Then clear any current
+ * pending exception in cx.
+ */
+ explicit AutoSaveExceptionState(JSContext* cx);
+
+ /*
+ * If neither drop() nor restore() was called, restore the exception
+ * state only if no exception is currently pending on cx.
+ */
+ ~AutoSaveExceptionState();
+
+ /*
+ * Discard any stored exception state.
+ * If this is called, the destructor is a no-op.
+ */
+ void drop();
+
+ /*
+ * Replace cx's exception state with the stored exception state. Then
+ * discard the stored exception state. If this is called, the
+ * destructor is a no-op.
+ */
+ void restore();
+};
+
+// 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);
+
+/**
+ * If the given object is an exception object (or an unwrappable
+ * cross-compartment wrapper for one), return the stack for that exception, if
+ * any. Will return null if the given object is not an exception object
+ * (including if it's null or a security wrapper that can't be unwrapped) or if
+ * the exception has no stack.
+ */
+extern JS_PUBLIC_API JSObject* ExceptionStackOrNull(JS::HandleObject obj);
+
+/**
+ * If the given object is an exception object, return the error cause for that
+ * exception, if any, or mozilla::Nothing.
+ */
+extern JS_PUBLIC_API mozilla::Maybe<JS::Value> GetExceptionCause(JSObject* exc);
+
+} // namespace JS
+
+#endif // js_Exception_h
diff --git a/js/public/ForOfIterator.h b/js/public/ForOfIterator.h
new file mode 100644
index 0000000000..db0a099e5e
--- /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_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.
+ */
+ [[nodiscard]] 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.
+ */
+ [[nodiscard]] 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..b0f1325a1e
--- /dev/null
+++ b/js/public/GCAPI.h
@@ -0,0 +1,1361 @@
+/* -*- 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/CharacterEncoding.h" // JS::UTF8Chars
+#include "js/GCAnnotations.h"
+#include "js/shadow/Zone.h"
+#include "js/SliceBudget.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
+class JS_PUBLIC_API SliceBudget;
+namespace gcstats {
+struct Statistics;
+} // namespace gcstats
+} // namespace js
+
+namespace JS {
+
+// Options used when starting a GC.
+enum class GCOptions : uint32_t {
+ // Normal GC invocation.
+ //
+ // Some objects that are unreachable from the program may still be alive after
+ // collection because of internal references
+ Normal = 0,
+
+ // A shrinking GC.
+ //
+ // Try to release as much memory as possible by clearing internal caches,
+ // aggressively discarding JIT code and decommitting unused chunks. This
+ // ensures all unreferenced objects are removed from the system.
+ //
+ // Finally, compact the GC heap.
+ Shrink = 1,
+
+ // A shutdown GC.
+ //
+ // This does more drastic cleanup as part of system shutdown, including:
+ // - clearing WeakRef kept object sets
+ // - not marking FinalizationRegistry roots
+ // - repeating collection if JS::NotifyGCRootsRemoved was called
+ // - skipping scheduling of various future work that won't be needed
+ //
+ // Note that this assumes that no JS will run after this point!
+ Shutdown = 2
+};
+
+} // namespace JS
+
+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.
+ *
+ * A value of zero means there is no maximum.
+ *
+ * Pref: javascript.options.mem.gc_incremental_slice_ms
+ * Default: DefaultTimeBudgetMS.
+ */
+ JSGC_SLICE_TIME_BUDGET_MS = 9,
+
+ /**
+ * 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,
+
+ /**
+ * Whether balanced heap limits are enabled.
+ *
+ * If this is set to true then heap limits are calculated in a way designed to
+ * balance memory usage optimally between many heaps.
+ *
+ * Otherwise, heap limits are set based on a linear multiple of the retained
+ * size after the last collection.
+ *
+ * Pref: javascript.options.mem.gc_balanced_heap_limits
+ * Default: BalancedHeapLimitsEnabled
+ */
+ JSGC_BALANCED_HEAP_LIMITS_ENABLED = 17,
+
+ /**
+ * Heap growth parameter for balanced heap limit calculation.
+ *
+ * This parameter trades off GC time for memory usage. Smaller values result
+ * in lower memory use and larger values result in less time spent collecting.
+ *
+ * Heap limits are set to the heap's retained size plus some extra space. The
+ * extra space is calculated based on several factors but is scaled
+ * proportionally to this parameter.
+ *
+ * Pref: javascript.options.mem.gc_heap_growth_factor
+ * Default: HeapGrowthFactor
+ */
+ JSGC_HEAP_GROWTH_FACTOR = 18,
+
+ /**
+ * Lower limit for collecting a zone (MB).
+ *
+ * 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_max_empty_chunk_count
+ * Default: MaxEmptyChunkCount
+ */
+ JSGC_MAX_EMPTY_CHUNK_COUNT = 22,
+
+ /**
+ * Whether compacting GC is enabled.
+ *
+ * Pref: javascript.options.mem.gc_compacting
+ * Default: CompactingEnabled
+ */
+ JSGC_COMPACTING_ENABLED = 23,
+
+ /**
+ * Whether parallel marking is enabled.
+ *
+ * Pref: javascript.options.mem.gc_parallel_marking
+ * Default: ParallelMarkingEnabled
+ */
+ JSGC_PARALLEL_MARKING_ENABLED = 24,
+
+ /**
+ * 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,
+
+ /**
+ * 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,
+
+ /**
+ * 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,
+
+ /**
+ * 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,
+
+ /**
+ * JS::MaybeRunNurseryCollection will collect the nursery if it hasn't been
+ * collected in this many milliseconds.
+ *
+ * Default: 5000
+ * Pref: None
+ */
+ JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS = 46,
+
+ /**
+ * The system page size in KB.
+ *
+ * This parameter is read-only.
+ */
+ JSGC_SYSTEM_PAGE_SIZE_KB = 47,
+
+ /**
+ * In an incremental GC, this determines the point at which to start
+ * increasing the slice budget and frequency of allocation triggered slices to
+ * try to avoid reaching the incremental limit and finishing the collection
+ * synchronously.
+ *
+ * The threshold is calculated by subtracting this value from the heap's
+ * incremental limit.
+ */
+ JSGC_URGENT_THRESHOLD_MB = 48,
+
+ /**
+ * Set the number of threads to use for parallel marking, or zero to use the
+ * default.
+ *
+ * The actual number used is capped to the number of available helper threads.
+ *
+ * This is provided for testing purposes.
+ *
+ * Pref: None.
+ * Default: 0 (no effect).
+ */
+ JSGC_MARKING_THREAD_COUNT = 49,
+
+ /**
+ * The heap size above which to use parallel marking.
+ *
+ * Default: ParallelMarkingThresholdMB
+ */
+ JSGC_PARALLEL_MARKING_THRESHOLD_MB = 50,
+} JSGCParamKey;
+
+/*
+ * Generic trace operation that calls JS::TraceEdge on each traceable thing's
+ * location reachable from data.
+ */
+typedef void (*JSTraceDataOp)(JSTracer* trc, void* data);
+
+/*
+ * Trace hook used to trace gray roots incrementally.
+ *
+ * This should return whether tracing is finished. It will be called repeatedly
+ * in subsequent GC slices until it returns true.
+ *
+ * While tracing this should check the budget and return false if it has been
+ * exceeded. When passed an unlimited budget it should always return true.
+ */
+typedef bool (*JSGrayRootsTracer)(JSTracer* trc, js::SliceBudget& budget,
+ 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)(JS::GCContext* gcx, JSFinalizeStatus status,
+ void* data);
+
+typedef void (*JSWeakPointerZonesCallback)(JSTracer* trc, void* data);
+
+typedef void (*JSWeakPointerCompartmentCallback)(JSTracer* trc,
+ 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_NewExternalStringLatin1 or
+ * JS_NewExternalUCString. The finalizer can be called off the main
+ * thread.
+ */
+ virtual void finalize(JS::Latin1Char* chars) const = 0;
+ 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 JS::Latin1Char* chars,
+ mozilla::MallocSizeOf mallocSizeOf) const = 0;
+ 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(SHARED_MEMORY_LIMIT, 13) \
+ D(EAGER_NURSERY_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(FULL_WASM_ANYREF_BUFFER, 27) \
+ D(FULL_CELL_PTR_STR_BUFFER, 28) \
+ D(TOO_MUCH_JIT_CODE, 29) \
+ D(FULL_CELL_PTR_BIGINT_BUFFER, 30) \
+ D(NURSERY_TRAILERS, 31) \
+ D(NURSERY_MALLOC_BUFFERS, 32) \
+ \
+ /* \
+ * Reasons from Firefox. \
+ * \
+ * The JS engine attaches special meanings to some of these reasons. \
+ */ \
+ 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) \
+ D(DOM_TESTUTILS, 56) \
+ D(PREPARE_FOR_PAGELOAD, 57) \
+ \
+ /* Reasons reserved for embeddings. */ \
+ D(RESERVED1, FIRST_RESERVED_REASON) \
+ D(RESERVED2, 91) \
+ D(RESERVED3, 92) \
+ D(RESERVED4, 93) \
+ D(RESERVED5, 94) \
+ D(RESERVED6, 95) \
+ D(RESERVED7, 96) \
+ D(RESERVED8, 97) \
+ D(RESERVED9, 98)
+
+enum class GCReason {
+ FIRST_FIREFOX_REASON = 33,
+ FIRST_RESERVED_REASON = 90,
+
+#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.
+ */
+extern JS_PUBLIC_API void NonIncrementalGC(JSContext* cx, JS::GCOptions options,
+ 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,
+ JS::GCOptions options,
+ GCReason reason,
+ const js::SliceBudget& budget);
+
+/**
+ * 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,
+ const js::SliceBudget& budget);
+
+/**
+ * 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_;
+ JS::GCOptions options_;
+ GCReason reason_;
+
+ GCDescription(bool isZone, bool isComplete, JS::GCOptions options,
+ GCReason reason)
+ : isZone_(isZone),
+ isComplete_(isComplete),
+ options_(options),
+ 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, void* data);
+
+/**
+ * Add and remove nursery collection callbacks for the given runtime. These will
+ * be called at the start and end of every nursery collection.
+ */
+extern JS_PUBLIC_API bool AddGCNurseryCollectionCallback(
+ JSContext* cx, GCNurseryCollectionCallback callback, void* data);
+extern JS_PUBLIC_API void RemoveGCNurseryCollectionCallback(
+ JSContext* cx, GCNurseryCollectionCallback callback, void* data);
+
+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);
+
+using CreateSliceBudgetCallback = js::SliceBudget (*)(JS::GCReason reason,
+ int64_t millis);
+
+/**
+ * Called when generating a GC slice budget. It allows the embedding to control
+ * the duration of slices and potentially check an interrupt flag as well. For
+ * internally triggered GCs, the given millis parameter is the JS engine's
+ * internal scheduling decision, which the embedding can choose to ignore.
+ * (Otherwise, it will be the value that was passed to eg
+ * JS::IncrementalGCSlice()).
+ */
+extern JS_PUBLIC_API void SetCreateGCSliceBudgetCallback(
+ JSContext* cx, CreateSliceBudgetCallback cb);
+
+/**
+ * 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:
+ */
+
+/**
+ * Ensure that generational GC is disabled within some scope.
+ *
+ * This evicts the nursery and discards JIT code so it is not a lightweight
+ * operation.
+ */
+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
+ protected:
+ JSContext* cx_; // nullptr if inactive.
+
+ public:
+ // Nullptr here means get the context from TLS. It does not mean inactive
+ // (though cx_ may end up nullptr, and thus inactive, if TLS has not yet been
+ // initialized.)
+ explicit AutoAssertNoGC(JSContext* cx = nullptr);
+ AutoAssertNoGC(AutoAssertNoGC&& other) : cx_(other.cx_) {
+ other.cx_ = nullptr;
+ }
+ ~AutoAssertNoGC();
+
+ void reset();
+#else
+ public:
+ explicit AutoAssertNoGC(JSContext* cx = nullptr) {}
+ ~AutoAssertNoGC() {}
+
+ void reset() {}
+#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) {}
+# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ AutoCheckCannotGC(const AutoCheckCannotGC& other)
+ : AutoCheckCannotGC(other.cx_) {}
+# else
+ AutoCheckCannotGC(const AutoCheckCannotGC& other) : AutoCheckCannotGC() {}
+# endif
+ AutoCheckCannotGC(AutoCheckCannotGC&& other)
+ : AutoAssertNoGC(std::forward<AutoAssertNoGC>(other)) {}
+#else
+class JS_PUBLIC_API AutoCheckCannotGC : public AutoRequireNoGC {
+ public:
+ explicit AutoCheckCannotGC(JSContext* cx = nullptr) {}
+ AutoCheckCannotGC(const AutoCheckCannotGC& other) : AutoCheckCannotGC() {}
+ AutoCheckCannotGC(AutoCheckCannotGC&& other) : AutoCheckCannotGC() {}
+ void reset() {}
+#endif
+} JS_HAZ_GC_INVALIDATED JS_HAZ_GC_REF;
+
+extern JS_PUBLIC_API void SetLowMemoryState(JSContext* cx, bool newState);
+
+/*
+ * Internal to Firefox.
+ */
+extern JS_PUBLIC_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.
+ *
+ * The return value of JS_UpdateWeakPointerAfterGC() indicates whether the
+ * referent is still alive. If the referent is is about to be finalized, this
+ * will return false.
+ *
+ * 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 bool JS_UpdateWeakPointerAfterGC(
+ JSTracer* trc, JS::Heap<JSObject*>* objp);
+
+extern JS_PUBLIC_API bool JS_UpdateWeakPointerAfterGCUnbarriered(
+ JSTracer* trc, 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 availMemMB);
+
+/**
+ * Create a new JSString whose chars member refers to external memory, i.e.,
+ * memory requiring application-specific finalization.
+ */
+extern JS_PUBLIC_API JSString* JS_NewExternalStringLatin1(
+ JSContext* cx, const JS::Latin1Char* chars, size_t length,
+ const JSExternalStringCallbacks* callbacks);
+extern JS_PUBLIC_API JSString* JS_NewExternalUCString(
+ 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_NewMaybeExternalStringLatin1(
+ JSContext* cx, const JS::Latin1Char* chars, size_t length,
+ const JSExternalStringCallbacks* callbacks, bool* allocatedExternal);
+extern JS_PUBLIC_API JSString* JS_NewMaybeExternalUCString(
+ JSContext* cx, const char16_t* chars, size_t length,
+ const JSExternalStringCallbacks* callbacks, bool* allocatedExternal);
+
+/**
+ * Similar to JS_NewMaybeExternalStringLatin1.
+ *
+ * Create an external Latin1 string if the utf8 buffer contains only ASCII
+ * chars, otherwise copy the chars into a non-external string.
+ */
+extern JS_PUBLIC_API JSString* JS_NewMaybeExternalStringUTF8(
+ JSContext* cx, const JS::UTF8Chars& utf8,
+ const JSExternalStringCallbacks* callbacks, bool* allocatedExternal);
+
+/**
+ * Return the 'callbacks' arg passed to JS_NewExternalStringLatin1,
+ * JS_NewExternalUCString, JS_NewMaybeExternalStringLatin1,
+ * or JS_NewMaybeExternalUCString.
+ */
+extern JS_PUBLIC_API const JSExternalStringCallbacks*
+JS_GetExternalStringCallbacks(JSString* str);
+
+namespace JS {
+
+extern JS_PUBLIC_API GCReason WantEagerMinorGC(JSRuntime* rt);
+
+extern JS_PUBLIC_API GCReason WantEagerMajorGC(JSRuntime* rt);
+
+extern JS_PUBLIC_API void MaybeRunNurseryCollection(JSRuntime* rt,
+ JS::GCReason reason);
+
+extern JS_PUBLIC_API void RunNurseryCollection(
+ JSRuntime* rt, JS::GCReason reason,
+ mozilla::TimeDuration aSinceLastMinorGC);
+
+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);
+
+inline JS_PUBLIC_API bool NeedGrayRootsForZone(Zone* zoneArg) {
+ shadow::Zone* zone = shadow::Zone::from(zoneArg);
+ return zone->isGCMarkingBlackAndGray() || zone->isGCCompacting();
+}
+
+extern JS_PUBLIC_API bool AtomsZoneIsCollecting(JSRuntime* runtime);
+extern JS_PUBLIC_API bool IsAtomsZone(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 */
+
+#ifdef JS_GC_ZEAL
+
+# define JS_DEFAULT_ZEAL_FREQ 100
+
+extern JS_PUBLIC_API void JS_GetGCZealBits(JSContext* cx, uint32_t* zealBits,
+ uint32_t* frequency,
+ uint32_t* nextScheduled);
+
+extern JS_PUBLIC_API void JS_SetGCZeal(JSContext* cx, uint8_t zeal,
+ uint32_t frequency);
+
+extern JS_PUBLIC_API void JS_UnsetGCZeal(JSContext* cx, uint8_t zeal);
+
+extern JS_PUBLIC_API void JS_ScheduleGC(JSContext* cx, uint32_t count);
+
+#endif
+
+#endif /* js_GCAPI_h */
diff --git a/js/public/GCAnnotations.h b/js/public/GCAnnotations.h
new file mode 100644
index 0000000000..b952bae3ff
--- /dev/null
+++ b/js/public/GCAnnotations.h
@@ -0,0 +1,116 @@
+/* -*- 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")))
+
+// Same as JS_HAZ_GC_POINTER, except additionally treat pointers to these
+// as GC pointers themselves in order to check references to them, since
+// the analysis cannot distinguish between pointers and references.
+# define JS_HAZ_GC_REF __attribute__((annotate("GC Pointer or Reference")))
+
+// 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")))
+
+// Mark a variable as being "GC safe", i.e., it does not contain any
+// invalidatable pointers at the current point in the code. A typical
+// example might be a collection containing GC pointers, which at the
+// present time is empty. This property is only temporary; the next use
+// of the variable will invalidate it (on the assumption that a GC pointer
+// might be added to it.) Try to use this as early as possible, probably
+// immediately after construction, so that if future mutations through
+// the variable are added, they won't be covered by the annotation.
+# define JS_HAZ_VALUE_IS_GC_SAFE(var) JS::detail::MarkVariableAsGCSafe(var)
+
+#else
+
+# define JS_EXPECT_HAZARDS
+# define JS_HAZ_GC_THING
+# define JS_HAZ_GC_POINTER
+# define JS_HAZ_GC_REF
+# 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
+# define JS_HAZ_VALUE_IS_GC_SAFE(var)
+
+#endif
+
+#ifdef XGILL_PLUGIN
+
+// Implemented by passing variable to a dummy function so that it shows up
+// in the control flow graph.
+namespace JS {
+namespace detail {
+
+template <typename T>
+static inline void MarkVariableAsGCSafe(T& var) {
+ asm("");
+}
+
+} // namespace detail
+} // namespace JS
+
+#endif
+
+#endif /* js_GCAnnotations_h */
diff --git a/js/public/GCHashTable.h b/js/public/GCHashTable.h
new file mode 100644
index 0000000000..69487b97a3
--- /dev/null
+++ b/js/public/GCHashTable.h
@@ -0,0 +1,370 @@
+/* -*- 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/TypeDecls.h"
+
+class JSTracer;
+
+namespace JS {
+
+// Define a reasonable default GC policy for GC-aware Maps.
+template <typename Key, typename Value>
+struct DefaultMapEntryGCPolicy {
+ 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
+// 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 function should work as well. If you need to
+// define your own GCPolicy specialization, generic helpers can be found in
+// js/public/TracingAPI.h.
+//
+// The MapEntryGCPolicy template parameter controls how the table drops entries
+// when edges are weakly held. GCHashMap::traceWeak applies the
+// MapEntryGCPolicy's traceWeak method to each table entry; if it returns true,
+// the entry is dropped. The default MapEntryGCPolicy drops the entry if either
+// the key or value is about to be finalized, according to its
+// GCPolicy<T>::traceWeak 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, but it does not itself
+// cause tracing to be invoked. For tracing, it must be used as
+// Rooted<GCHashMap> or PersistentRooted<GCHashMap>, or barriered and traced
+// manually.
+template <typename Key, typename Value,
+ typename HashPolicy = js::DefaultHasher<Key>,
+ typename AllocPolicy = js::TempAllocPolicy,
+ typename MapEntryGCPolicy = DefaultMapEntryGCPolicy<Key, Value>>
+class GCHashMap : public js::HashMap<Key, Value, HashPolicy, AllocPolicy> {
+ using Base = js::HashMap<Key, Value, HashPolicy, AllocPolicy>;
+
+ public:
+ using EntryGCPolicy = MapEntryGCPolicy;
+
+ 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 traceWeak(JSTracer* trc) {
+ typename Base::Enum e(*this);
+ traceWeakEntries(trc, e);
+ return !this->empty();
+ }
+
+ void traceWeakEntries(JSTracer* trc, typename Base::Enum& e) {
+ for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+ if (!MapEntryGCPolicy::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 StableCellHasher, 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 MapEntryGCPolicy = JS::DefaultMapEntryGCPolicy<Key, Value>>
+class GCRekeyableHashMap : public JS::GCHashMap<Key, Value, HashPolicy,
+ AllocPolicy, MapEntryGCPolicy> {
+ 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) {}
+
+ bool traceWeak(JSTracer* trc) {
+ for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+ Key key(e.front().key());
+ if (!MapEntryGCPolicy::traceWeak(trc, &key, &e.front().value())) {
+ e.removeFront();
+ } else if (!HashPolicy::match(key, e.front().key())) {
+ e.rekeyFront(key);
+ }
+ }
+ return !this->empty();
+ }
+
+ // 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 traceWeak(JSTracer* trc) {
+ typename Base::Enum e(*this);
+ traceWeakEntries(trc, e);
+ return !this->empty();
+ }
+
+ void traceWeakEntries(JSTracer* trc, typename Base::Enum& e) {
+ for (; !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(); }
+ [[nodiscard]] 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>
+ void replaceKey(Ptr p, const Lookup& l, TInput&& newValue) {
+ set().replaceKey(p, l, std::forward<TInput>(newValue));
+ }
+
+ 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 */
+
+#endif /* GCHashTable_h */
diff --git a/js/public/GCPolicyAPI.h b/js/public/GCPolicyAPI.h
new file mode 100644
index 0000000000..eef7ee7623
--- /dev/null
+++ b/js/public/GCPolicyAPI.h
@@ -0,0 +1,235 @@
+/* -*- 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 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. Note that
+// currently a type can define only the subset of the methods (trace and/or
+// traceWeak) if it is never used in a context that requires the other.
+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 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 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 <>
+struct GCPolicy<bool> : public IgnoreGCPolicy<bool> {};
+
+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) {
+ // This should only be called as part of root marking since that's the only
+ // time we should trace unbarriered GC thing pointers. This will assert if
+ // called at other times.
+ TraceRoot(trc, vp, name);
+ }
+ static bool isTenured(T v) { return !v || !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 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 traceWeak(JSTracer* trc, JS::Heap<T>* thingp) {
+ return !*thingp || js::gc::TraceWeakEdge(trc, thingp);
+ }
+};
+
+// 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 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
+// the Maybe<T> is filled and T* can be traced via GCPolicy<T*>.
+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 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 <typename T1, typename T2>
+struct GCPolicy<std::pair<T1, T2>> {
+ static void trace(JSTracer* trc, std::pair<T1, T2>* tp, const char* name) {
+ GCPolicy<T1>::trace(trc, &tp->first, name);
+ GCPolicy<T2>::trace(trc, &tp->second, name);
+ }
+ static bool traceWeak(JSTracer* trc, std::pair<T1, T2>* tp) {
+ return GCPolicy<T1>::traceWeak(trc, &tp->first) &&
+ GCPolicy<T2>::traceWeak(trc, &tp->second);
+ }
+ static bool isValid(const std::pair<T1, T2>& t) {
+ return GCPolicy<T1>::isValid(t.first) && GCPolicy<T2>::isValid(t.second);
+ }
+};
+
+template <>
+struct GCPolicy<JS::Realm*>; // see Realm.h
+
+template <>
+struct GCPolicy<mozilla::Ok> : public IgnoreGCPolicy<mozilla::Ok> {};
+
+template <typename V, typename E>
+struct GCPolicy<mozilla::Result<V, E>> {
+ static void trace(JSTracer* trc, mozilla::Result<V, E>* tp,
+ const char* name) {
+ if (tp->isOk()) {
+ V tmp = tp->unwrap();
+ JS::GCPolicy<V>::trace(trc, &tmp, "Result value");
+ tp->updateAfterTracing(std::move(tmp));
+ }
+
+ if (tp->isErr()) {
+ E tmp = tp->unwrapErr();
+ JS::GCPolicy<E>::trace(trc, &tmp, "Result error");
+ tp->updateErrorAfterTracing(std::move(tmp));
+ }
+ }
+
+ static bool isValid(const mozilla::Result<V, E>& t) { return true; }
+};
+
+} // namespace JS
+
+#endif // GCPolicyAPI_h
diff --git a/js/public/GCTypeMacros.h b/js/public/GCTypeMacros.h
new file mode 100644
index 0000000000..fafe62d57f
--- /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;
+class 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..43610b898b
--- /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 JS::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..424f689a91
--- /dev/null
+++ b/js/public/GCVector.h
@@ -0,0 +1,366 @@
+/* -*- 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/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_STACK_CLASS
+#include "mozilla/MemoryReporting.h" // MallocSizeOf
+#include "mozilla/Span.h"
+#include "mozilla/Vector.h"
+
+#include <stddef.h> // size_t
+#include <utility> // forward, move
+
+#include "js/AllocPolicy.h"
+#include "js/GCPolicyAPI.h"
+#include "js/RootingAPI.h"
+
+class JSTracer;
+struct JSContext;
+
+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) : vector(std::move(alloc)) {}
+ GCVector() : GCVector(AllocPolicy()) {}
+
+ 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(); }
+
+ operator mozilla::Span<T>() { return vector; }
+ operator mozilla::Span<const T>() const { return vector; }
+
+ bool initCapacity(size_t cap) { return vector.initCapacity(cap); }
+ [[nodiscard]] 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); }
+ [[nodiscard]] bool growBy(size_t amount) { return vector.growBy(amount); }
+ [[nodiscard]] 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>
+ [[nodiscard]] 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>
+ [[nodiscard]] bool appendAll(const U& aU) {
+ return vector.append(aU.begin(), aU.end());
+ }
+ template <typename T2, size_t MinInlineCapacity2, typename AllocPolicy2>
+ [[nodiscard]] bool appendAll(
+ GCVector<T2, MinInlineCapacity2, AllocPolicy2>&& aU) {
+ return vector.appendAll(aU.begin(), aU.end());
+ }
+
+ [[nodiscard]] bool appendN(const T& val, size_t count) {
+ return vector.appendN(val, count);
+ }
+
+ template <typename U>
+ [[nodiscard]] bool append(const U* aBegin, const U* aEnd) {
+ return vector.append(aBegin, aEnd);
+ }
+ template <typename U>
+ [[nodiscard]] 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) {
+ mutableEraseIf(
+ [trc](T& elem) { return !GCPolicy<T>::traceWeak(trc, &elem); });
+ return !empty();
+ }
+
+ // Like eraseIf, but may mutate the contents of the vector. Iterates from
+ // |startIndex| to the last element of the vector.
+ template <typename Pred>
+ void mutableEraseIf(Pred pred, size_t startIndex = 0) {
+ MOZ_ASSERT(startIndex <= length());
+
+ T* src = begin() + startIndex;
+ T* dst = src;
+ while (src != end()) {
+ if (!pred(*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));
+ }
+
+ [[nodiscard]] bool initCapacity(size_t aRequest) {
+ return vec().initCapacity(aRequest);
+ }
+ [[nodiscard]] bool reserve(size_t aRequest) {
+ return vec().reserve(aRequest);
+ }
+ void shrinkBy(size_t aIncr) { vec().shrinkBy(aIncr); }
+ [[nodiscard]] bool growBy(size_t aIncr) { return vec().growBy(aIncr); }
+ [[nodiscard]] bool resize(size_t aNewLength) {
+ return vec().resize(aNewLength);
+ }
+ void clear() { vec().clear(); }
+ void clearAndFree() { vec().clearAndFree(); }
+ template <typename U>
+ [[nodiscard]] bool append(U&& aU) {
+ return vec().append(std::forward<U>(aU));
+ }
+ template <typename... Args>
+ [[nodiscard]] 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>
+ [[nodiscard]] bool appendAll(U&& aU) {
+ return vec().appendAll(aU);
+ }
+ [[nodiscard]] bool appendN(const T& aT, size_t aN) {
+ return vec().appendN(aT, aN);
+ }
+ template <typename U>
+ [[nodiscard]] bool append(const U* aBegin, const U* aEnd) {
+ return vec().append(aBegin, aEnd);
+ }
+ template <typename U>
+ [[nodiscard]] 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(); }
+ 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/GlobalObject.h b/js/public/GlobalObject.h
new file mode 100644
index 0000000000..22da1fc28b
--- /dev/null
+++ b/js/public/GlobalObject.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/. */
+
+#ifndef js_GlobalObject_h
+#define js_GlobalObject_h
+
+#include "mozilla/Attributes.h"
+
+#include "jstypes.h"
+
+#include "js/TypeDecls.h"
+
+class JS_PUBLIC_API JSTracer;
+
+struct JSClassOps;
+
+extern JS_PUBLIC_API bool JS_IsGlobalObject(JSObject* obj);
+
+namespace JS {
+
+class JS_PUBLIC_API RealmOptions;
+
+/**
+ * Get the current realm's global. Returns nullptr if no realm has been
+ * entered.
+ */
+extern JS_PUBLIC_API JSObject* CurrentGlobalOrNull(JSContext* cx);
+
+/**
+ * Get the global object associated with an object's realm. The object must not
+ * be a cross-compartment wrapper (because CCWs are shared by all realms in the
+ * compartment).
+ */
+extern JS_PUBLIC_API JSObject* GetNonCCWObjectGlobal(JSObject* obj);
+
+/**
+ * During global creation, we fire notifications to callbacks registered
+ * via the Debugger API. These callbacks are arbitrary script, and can touch
+ * the global in arbitrary ways. When that happens, the global should not be
+ * in a half-baked state. But this creates a problem for consumers that need
+ * to set slots on the global to put it in a consistent state.
+ *
+ * This API provides a way for consumers to set slots atomically (immediately
+ * after the global is created), before any debugger hooks are fired. It's
+ * unfortunately on the clunky side, but that's the way the cookie crumbles.
+ *
+ * If callers have no additional state on the global to set up, they may pass
+ * |FireOnNewGlobalHook| to JS_NewGlobalObject, which causes that function to
+ * fire the hook as its final act before returning. Otherwise, callers should
+ * pass |DontFireOnNewGlobalHook|, which means that they are responsible for
+ * invoking JS_FireOnNewGlobalObject upon successfully creating the global. If
+ * an error occurs and the operation aborts, callers should skip firing the
+ * hook. But otherwise, callers must take care to fire the hook exactly once
+ * before compiling any script in the global's scope (we have assertions in
+ * place to enforce this). This lets us be sure that debugger clients never miss
+ * breakpoints.
+ */
+enum OnNewGlobalHookOption { FireOnNewGlobalHook, DontFireOnNewGlobalHook };
+
+} // namespace JS
+
+extern JS_PUBLIC_API JSObject* JS_NewGlobalObject(
+ JSContext* cx, const JSClass* clasp, JSPrincipals* principals,
+ JS::OnNewGlobalHookOption hookOption, const JS::RealmOptions& options);
+/**
+ * Spidermonkey does not have a good way of keeping track of what compartments
+ * should be marked on their own. We can mark the roots unconditionally, but
+ * marking GC things only relevant in live compartments is hard. To mitigate
+ * this, we create a static trace hook, installed on each global object, from
+ * which we can be sure the compartment is relevant, and mark it.
+ *
+ * It is still possible to specify custom trace hooks for global object classes.
+ * They can be provided via the RealmOptions passed to JS_NewGlobalObject.
+ */
+extern JS_PUBLIC_API void JS_GlobalObjectTraceHook(JSTracer* trc,
+ JSObject* global);
+
+namespace JS {
+
+/**
+ * This allows easily constructing a global object without having to deal with
+ * JSClassOps, forgetting to add JS_GlobalObjectTraceHook, or forgetting to call
+ * JS::InitRealmStandardClasses(). Example:
+ *
+ * const JSClass globalClass = { "MyGlobal", JSCLASS_GLOBAL_FLAGS,
+ * &JS::DefaultGlobalClassOps };
+ * JS_NewGlobalObject(cx, &globalClass, ...);
+ */
+extern JS_PUBLIC_DATA const JSClassOps DefaultGlobalClassOps;
+
+} // namespace JS
+
+extern JS_PUBLIC_API void JS_FireOnNewGlobalObject(JSContext* cx,
+ JS::HandleObject global);
+
+#endif // js_GlobalObject_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..3dfe00bd0d
--- /dev/null
+++ b/js/public/HeapAPI.h
@@ -0,0 +1,838 @@
+/* -*- 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 "js/AllocPolicy.h"
+#include "js/GCAnnotations.h"
+#include "js/HashTable.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/TypeDecls.h"
+
+/* These values are private to the JS engine. */
+namespace js {
+
+class NurseryDecommitTask;
+
+JS_PUBLIC_API bool CurrentThreadCanAccessZone(JS::Zone* zone);
+
+// To prevent false sharing, some data structures are aligned to a typical cache
+// line size.
+static constexpr size_t TypicalCacheLineSize = 64;
+
+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;
+
+#if defined(XP_MACOSX) && defined(__aarch64__)
+const size_t PageShift = 14;
+#else
+const size_t PageShift = 12;
+#endif
+// Expected page size, so we could initialze ArenasPerPage at compile-time.
+// The actual system page size should be queried by SystemPageSize().
+const size_t PageSize = size_t(1) << PageShift;
+constexpr size_t ArenasPerPage = PageSize / ArenaSize;
+
+const size_t ChunkShift = 20;
+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 minimum cell size ends up as twice the cell alignment because the mark
+ * bitmap contains one bit per CellBytesPerMarkBit bytes (which is equal to
+ * CellAlignBytes) and we need two mark bits per cell.
+ */
+const size_t MinCellSize = CellBytesPerMarkBit * MarkBitsPerCell;
+
+/*
+ * 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) {
+ MOZ_ASSERT((uintptr_t(this) & ChunkMask) == 0);
+ initBase(rt, sb);
+ }
+
+ void initBase(JSRuntime* rt, StoreBuffer* sb) {
+ runtime = rt;
+ storeBuffer = sb;
+ }
+
+ 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:
+ /* 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 dependent fields are markBits, decommittedPages and
+ * freeCommittedArenas. markBits needs ArenaBitmapBytes bytes per arena,
+ * decommittedPages needs one bit per page and freeCommittedArenas 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 BitsPerPageWithHeaders =
+ (ArenaSize + ArenaBitmapBytes) * ArenasPerPage * CHAR_BIT + ArenasPerPage +
+ 1;
+const size_t ChunkBitsAvailable =
+ (ChunkSize - sizeof(ChunkBase) - sizeof(TenuredChunkInfo)) * CHAR_BIT;
+const size_t PagesPerChunk = ChunkBitsAvailable / BitsPerPageWithHeaders;
+const size_t ArenasPerChunk = PagesPerChunk * ArenasPerPage;
+const size_t FreeCommittedBits = ArenasPerChunk;
+const size_t DecommitBits = PagesPerChunk;
+const size_t BitsPerArenaWithHeaders =
+ (ArenaSize + ArenaBitmapBytes) * CHAR_BIT +
+ (DecommitBits / ArenasPerChunk) + 1;
+
+const size_t CalculatedChunkSizeRequired =
+ sizeof(ChunkBase) + sizeof(TenuredChunkInfo) +
+ RoundUp(ArenasPerChunk * ArenaBitmapBytes, sizeof(uintptr_t)) +
+ RoundUp(FreeCommittedBits, sizeof(uint32_t) * CHAR_BIT) / CHAR_BIT +
+ RoundUp(DecommitBits, 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");
+
+static_assert(ArenasPerChunk == 252,
+ "Do not accidentally change our heap's density.");
+
+// 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 alignas(TypicalCacheLineSize) 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 bool markIfUnmarkedAtomic(const TenuredCell* cell, MarkColor color);
+ inline void markBlack(const TenuredCell* cell);
+ inline void markBlackAtomic(const TenuredCell* cell);
+ inline void copyMarkBit(TenuredCell* dst, const TenuredCell* src,
+ ColorBit colorBit);
+ inline void unmark(const TenuredCell* cell);
+ inline MarkBitmapWord* arenaBits(Arena* arena);
+};
+
+static_assert(ArenaBitmapBytes * ArenasPerChunk == sizeof(MarkBitmap),
+ "Ensure our MarkBitmap actually covers all arenas.");
+
+// Bitmap with one bit per page used for decommitted page set.
+using ChunkPageBitmap = mozilla::BitSet<PagesPerChunk, uint32_t>;
+
+// Bitmap with one bit per arena used for free committed arena set.
+using ChunkArenaBitmap = mozilla::BitSet<ArenasPerChunk, uint32_t>;
+
+// Base class containing data members for a tenured heap chunk.
+class TenuredChunkBase : public ChunkBase {
+ public:
+ TenuredChunkInfo info;
+ MarkBitmap markBits;
+ ChunkArenaBitmap freeCommittedArenas;
+ ChunkPageBitmap decommittedPages;
+
+ protected:
+ explicit TenuredChunkBase(JSRuntime* runtime) : ChunkBase(runtime, nullptr) {
+ info.numArenasFree = ArenasPerChunk;
+ }
+
+ void initAsDecommitted();
+};
+
+/*
+ * 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 ChunkStoreBufferOffset = offsetof(ChunkBase, storeBuffer);
+const size_t ChunkMarkBitmapOffset = offsetof(TenuredChunkBase, markBits);
+
+// Hardcoded offsets into Arena class.
+const size_t ArenaZoneOffset = 2 * sizeof(uint32_t);
+const size_t ArenaHeaderSize = ArenaZoneOffset + 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_PUBLIC_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_PUBLIC_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 {
+ uintptr_t kindBits = ptr & OutOfLineTraceKindMask;
+ if (kindBits != OutOfLineTraceKindMask) {
+ return JS::TraceKind(kindBits);
+ }
+ 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);
+ // Store trace in the bottom bits of pointer for common kinds.
+ uintptr_t kindBits = uintptr_t(traceKind);
+ if (kindBits >= OutOfLineTraceKindMask) {
+ kindBits = OutOfLineTraceKindMask;
+ }
+ return uintptr_t(p) | kindBits;
+ }
+
+ 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==(JS::GCCellPtr ptr1, JS::GCCellPtr ptr2) {
+ return ptr1.asCell() == ptr2.asCell();
+}
+
+inline bool operator!=(JS::GCCellPtr ptr1, 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);
+ auto* chunk = reinterpret_cast<ChunkBase*>(uintptr_t(cell) & ~ChunkMask);
+ MOZ_ASSERT(chunk->runtime);
+ return chunk;
+}
+
+static MOZ_ALWAYS_INLINE TenuredChunkBase* GetCellChunkBase(
+ const TenuredCell* cell) {
+ MOZ_ASSERT(cell);
+ auto* chunk =
+ reinterpret_cast<TenuredChunkBase*>(uintptr_t(cell) & ~ChunkMask);
+ MOZ_ASSERT(chunk->runtime);
+ return chunk;
+}
+
+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 TenuredCellIsMarkedBlack(
+ const TenuredCell* cell) {
+ // Return true if BlackBit is set.
+
+ MOZ_ASSERT(cell);
+ MOZ_ASSERT(!js::gc::IsInsideNursery(cell));
+
+ MarkBitmapWord* blackWord;
+ uintptr_t blackMask;
+ TenuredChunkBase* chunk = GetCellChunkBase(cell);
+ chunk->markBits.getMarkWordAndMask(cell, js::gc::ColorBit::BlackBit,
+ &blackWord, &blackMask);
+ return *blackWord & blackMask;
+}
+
+static MOZ_ALWAYS_INLINE bool NonBlackCellIsMarkedGray(
+ const TenuredCell* cell) {
+ // Return true if GrayOrBlackBit is set. Callers should check BlackBit first.
+
+ MOZ_ASSERT(cell);
+ MOZ_ASSERT(!js::gc::IsInsideNursery(cell));
+ MOZ_ASSERT(!TenuredCellIsMarkedBlack(cell));
+
+ MarkBitmapWord* grayWord;
+ uintptr_t grayMask;
+ TenuredChunkBase* chunk = GetCellChunkBase(cell);
+ chunk->markBits.getMarkWordAndMask(cell, js::gc::ColorBit::GrayOrBlackBit,
+ &grayWord, &grayMask);
+ return *grayWord & grayMask;
+}
+
+static MOZ_ALWAYS_INLINE bool TenuredCellIsMarkedGray(const TenuredCell* cell) {
+ return !TenuredCellIsMarkedBlack(cell) && NonBlackCellIsMarkedGray(cell);
+}
+
+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 CanCheckGrayBits(const TenuredCell* cell);
+
+extern JS_PUBLIC_API bool CellIsMarkedGrayIfKnown(const TenuredCell* 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) {
+ MOZ_ASSERT(cell);
+ return detail::CellHasStoreBuffer(cell);
+}
+
+MOZ_ALWAYS_INLINE bool IsInsideNursery(const TenuredCell* cell) {
+ MOZ_ASSERT(cell);
+ MOZ_ASSERT(!IsInsideNursery(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) {
+ js::gc::Cell* cell = thing.asCell();
+ if (IsInsideNursery(cell)) {
+ return false;
+ }
+
+ auto* tenuredCell = reinterpret_cast<js::gc::TenuredCell*>(cell);
+ return js::gc::detail::CellIsMarkedGrayIfKnown(tenuredCell);
+}
+
+// Specialised gray marking check for use by the cycle collector. This is not
+// called during incremental GC or when the gray bits are invalid.
+static MOZ_ALWAYS_INLINE bool GCThingIsMarkedGrayInCC(GCCellPtr thing) {
+ js::gc::Cell* cell = thing.asCell();
+ if (IsInsideNursery(cell)) {
+ return false;
+ }
+
+ auto* tenuredCell = reinterpret_cast<js::gc::TenuredCell*>(cell);
+ if (!js::gc::detail::TenuredCellIsMarkedGray(tenuredCell)) {
+ return false;
+ }
+
+ MOZ_ASSERT(js::gc::detail::CanCheckGrayBits(tenuredCell));
+
+ return true;
+}
+
+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_PUBLIC_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());
+ if (detail::TenuredCellIsMarkedBlack(cell)) {
+ return;
+ }
+
+ // GC things owned by other runtimes are always black.
+ MOZ_ASSERT(!thing.mayBeOwnedByOtherRuntime());
+
+ auto* zone = JS::shadow::Zone::from(JS::GetTenuredGCThingZone(thing));
+ if (zone->needsIncrementalBarrier()) {
+ PerformIncrementalReadBarrier(thing);
+ } else if (!zone->isGCPreparing() && detail::NonBlackCellIsMarkedGray(cell)) {
+ MOZ_ALWAYS_TRUE(JS::UnmarkGrayGCThingRecursively(thing));
+ }
+
+ MOZ_ASSERT_IF(!zone->isGCPreparing(), !detail::TenuredCellIsMarkedGray(cell));
+}
+
+static MOZ_ALWAYS_INLINE void IncrementalReadBarrier(JS::GCCellPtr thing) {
+ // This is a lighter version of ExposeGCThingToActiveJS that doesn't do gray
+ // unmarking.
+
+ if (IsInsideNursery(thing.asCell())) {
+ return;
+ }
+
+ auto* zone = JS::shadow::Zone::from(JS::GetTenuredGCThingZone(thing));
+ auto* cell = reinterpret_cast<TenuredCell*>(thing.asCell());
+ if (zone->needsIncrementalBarrier() &&
+ !detail::TenuredCellIsMarkedBlack(cell)) {
+ // GC things owned by other runtimes are always black.
+ MOZ_ASSERT(!thing.mayBeOwnedByOtherRuntime());
+ PerformIncrementalReadBarrier(thing);
+ }
+}
+
+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/HelperThreadAPI.h b/js/public/HelperThreadAPI.h
new file mode 100644
index 0000000000..c877fbb49b
--- /dev/null
+++ b/js/public/HelperThreadAPI.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/. */
+
+/*
+ * API for supplying an external thread pool to run internal work off the main
+ * thread.
+ */
+
+#ifndef js_HelperThreadAPI_h
+#define js_HelperThreadAPI_h
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+namespace JS {
+
+// Argument passed to the task callback to indicate whether we're invoking it
+// because a new task was added by the JS engine or because we're on a helper
+// thread that just finished a task and there are other tasks pending.
+enum class DispatchReason { NewTask, FinishedTask };
+
+/**
+ * Set callback to dispatch a tasks to an external thread pool.
+ *
+ * When the task runs it should call JS::RunHelperThreadTask.
+ */
+using HelperThreadTaskCallback = void (*)(DispatchReason reason);
+extern JS_PUBLIC_API void SetHelperThreadTaskCallback(
+ HelperThreadTaskCallback callback, size_t threadCount, size_t stackSize);
+
+// Function to call from external thread pool to run a helper thread task.
+extern JS_PUBLIC_API void RunHelperThreadTask();
+
+} // namespace JS
+
+#endif // js_HelperThreadAPI_h
diff --git a/js/public/Id.h b/js/public/Id.h
new file mode 100644
index 0000000000..087f9e61c9
--- /dev/null
+++ b/js/public/Id.h
@@ -0,0 +1,371 @@
+/* -*- 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] PropertyKey / jsid
+//
+// A PropertyKey is an identifier for a property of an object which is either a
+// 31-bit unsigned integer, interned string or symbol.
+//
+// Also, there is an additional PropertyKey value, PropertyKey::Void(), which
+// does not occur in JS scripts but may be used to indicate the absence of a
+// valid key. A void PropertyKey is not a valid key and only arises as an
+// exceptional API return value. Embeddings must not pass a void PropertyKey
+// into JSAPI entry points expecting a PropertyKey and do not need to handle
+// void keys in hooks receiving a PropertyKey except when explicitly noted in
+// the API contract.
+//
+// A PropertyKey is not implicitly convertible to or from a Value; JS_ValueToId
+// or JS_IdToValue must be used instead.
+//
+// jsid is an alias for JS::PropertyKey. New code should use PropertyKey instead
+// of jsid.
+
+#include "mozilla/Maybe.h"
+
+#include "jstypes.h"
+
+#include "js/GCAnnotations.h"
+#include "js/HeapAPI.h"
+#include "js/RootingAPI.h"
+#include "js/TraceKind.h"
+#include "js/TracingAPI.h"
+#include "js/TypeDecls.h"
+
+namespace js {
+class JS_PUBLIC_API GenericPrinter;
+class JSONPrinter;
+} // namespace js
+
+namespace JS {
+
+enum class SymbolCode : uint32_t;
+
+class PropertyKey {
+ uintptr_t asBits_;
+
+ public:
+ // All keys with the low bit set are integer keys. This means the other type
+ // tags must all be even. These constants are public only for the JITs.
+ static constexpr uintptr_t IntTagBit = 0x1;
+ // Use 0 for StringTypeTag to avoid a bitwise op for atom <-> id conversions.
+ static constexpr uintptr_t StringTypeTag = 0x0;
+ static constexpr uintptr_t VoidTypeTag = 0x2;
+ static constexpr uintptr_t SymbolTypeTag = 0x4;
+ // (0x6 is unused)
+ static constexpr uintptr_t TypeMask = 0x7;
+
+ static constexpr uint32_t IntMin = 0;
+ static constexpr uint32_t IntMax = INT32_MAX;
+
+ constexpr PropertyKey() : asBits_(VoidTypeTag) {}
+
+ static constexpr MOZ_ALWAYS_INLINE PropertyKey fromRawBits(uintptr_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 isVoid() const {
+ MOZ_ASSERT_IF((asBits_ & TypeMask) == VoidTypeTag, asBits_ == VoidTypeTag);
+ return asBits_ == VoidTypeTag;
+ }
+
+ MOZ_ALWAYS_INLINE bool isInt() const { return !!(asBits_ & IntTagBit); }
+
+ MOZ_ALWAYS_INLINE bool isString() const {
+ return (asBits_ & TypeMask) == StringTypeTag;
+ }
+
+ MOZ_ALWAYS_INLINE bool isSymbol() const {
+ return (asBits_ & TypeMask) == SymbolTypeTag;
+ }
+
+ MOZ_ALWAYS_INLINE bool isGCThing() const { return isString() || isSymbol(); }
+
+ constexpr uintptr_t asRawBits() const { return asBits_; }
+
+ 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 `& ~TypeMask` because small immediates can be
+ // encoded more efficiently on some platorms.
+ return reinterpret_cast<JSString*>(asBits_ ^ StringTypeTag);
+ }
+
+ MOZ_ALWAYS_INLINE JS::Symbol* toSymbol() const {
+ MOZ_ASSERT(isSymbol());
+ return reinterpret_cast<JS::Symbol*>(asBits_ ^ SymbolTypeTag);
+ }
+
+ js::gc::Cell* toGCThing() const {
+ MOZ_ASSERT(isGCThing());
+ return reinterpret_cast<js::gc::Cell*>(asBits_ & ~TypeMask);
+ }
+
+ 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;
+
+ // A void PropertyKey. This is equivalent to a PropertyKey created by the
+ // default constructor.
+ static constexpr PropertyKey Void() { return PropertyKey(); }
+
+ static constexpr bool fitsInInt(int32_t i) { return i >= 0; }
+
+ static constexpr PropertyKey Int(int32_t i) {
+ MOZ_ASSERT(fitsInInt(i));
+ uint32_t bits = (static_cast<uint32_t>(i) << 1) | IntTagBit;
+ return PropertyKey::fromRawBits(bits);
+ }
+
+ static PropertyKey Symbol(JS::Symbol* sym) {
+ MOZ_ASSERT(sym != nullptr);
+ MOZ_ASSERT((uintptr_t(sym) & TypeMask) == 0);
+ MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym)));
+ return PropertyKey::fromRawBits(uintptr_t(sym) | SymbolTypeTag);
+ }
+
+ // 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, IntMax] (where IntMax == 2^31-1). Thus, for the range of integers
+ // (IntMax, 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 NonIntAtom(JSAtom* atom) {
+ MOZ_ASSERT((uintptr_t(atom) & TypeMask) == 0);
+ MOZ_ASSERT(PropertyKey::isNonIntAtom(atom));
+ return PropertyKey::fromRawBits(uintptr_t(atom) | StringTypeTag);
+ }
+
+ // The JSAtom/JSString type exposed to embedders is opaque.
+ static PropertyKey NonIntAtom(JSString* str) {
+ MOZ_ASSERT((uintptr_t(str) & TypeMask) == 0);
+ MOZ_ASSERT(PropertyKey::isNonIntAtom(str));
+ return PropertyKey::fromRawBits(uintptr_t(str) | StringTypeTag);
+ }
+
+ // This API can be used by embedders to convert pinned (aka interned) strings,
+ // as created by JS_AtomizeAndPinString, 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);
+
+ // Internal API!
+ // All string PropertyKeys are actually atomized.
+ MOZ_ALWAYS_INLINE bool isAtom() const { return isString(); }
+
+ MOZ_ALWAYS_INLINE bool isAtom(JSAtom* atom) const {
+ MOZ_ASSERT(PropertyKey::isNonIntAtom(atom));
+ return isAtom() && toAtom() == atom;
+ }
+
+ MOZ_ALWAYS_INLINE JSAtom* toAtom() const {
+ return reinterpret_cast<JSAtom*>(toString());
+ }
+ MOZ_ALWAYS_INLINE JSLinearString* toLinearString() const {
+ return reinterpret_cast<JSLinearString*>(toString());
+ }
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+ void dump() const;
+ void dump(js::GenericPrinter& out) const;
+ void dump(js::JSONPrinter& json) const;
+
+ void dumpFields(js::JSONPrinter& json) const;
+ void dumpPropertyName(js::GenericPrinter& out) const;
+ void dumpStringContent(js::GenericPrinter& out) const;
+#endif
+
+ private:
+ static bool isNonIntAtom(JSAtom* atom);
+ static bool isNonIntAtom(JSString* atom);
+} JS_HAZ_GC_POINTER;
+
+} // namespace JS
+
+using jsid = JS::PropertyKey;
+
+namespace JS {
+
+// Handle<PropertyKey> version of PropertyKey::Void().
+extern JS_PUBLIC_DATA const JS::HandleId VoidHandlePropertyKey;
+
+template <>
+struct GCPolicy<jsid> {
+ static void trace(JSTracer* trc, jsid* idp, const char* name) {
+ // This should only be called as part of root marking since that's the only
+ // time we should trace unbarriered GC thing pointers. This will assert if
+ // called at other times.
+ TraceRoot(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
+
+/**
+ * Get one of the well-known symbols defined by ES6 as PropertyKey. This is
+ * equivalent to calling JS::GetWellKnownSymbol and then creating a PropertyKey.
+ *
+ * `which` must be in the range [0, WellKnownSymbolLimit).
+ */
+extern JS_PUBLIC_API PropertyKey GetWellKnownSymbolKey(JSContext* cx,
+ SymbolCode which);
+
+/**
+ * Generate getter/setter id for given id, by adding "get " or "set " prefix.
+ */
+extern JS_PUBLIC_API bool ToGetterId(
+ JSContext* cx, JS::Handle<JS::PropertyKey> id,
+ JS::MutableHandle<JS::PropertyKey> getterId);
+extern JS_PUBLIC_API bool ToSetterId(
+ JSContext* cx, JS::Handle<JS::PropertyKey> id,
+ JS::MutableHandle<JS::PropertyKey> setterId);
+
+} // namespace JS
+
+namespace js {
+
+template <>
+struct BarrierMethods<jsid> {
+ static gc::Cell* asGCThingOrNull(jsid id) {
+ if (id.isGCThing()) {
+ return id.toGCThing();
+ }
+ return nullptr;
+ }
+ static void postWriteBarrier(jsid* idp, jsid prev, jsid next) {
+ MOZ_ASSERT_IF(next.isString(), !gc::IsInsideNursery(next.toString()));
+ }
+ static void exposeToJS(jsid id) {
+ if (id.isGCThing()) {
+ js::gc::ExposeGCThingToActiveJS(id.toGCCellPtr());
+ }
+ }
+ static void readBarrier(jsid id) {
+ if (id.isGCThing()) {
+ js::gc::IncrementalReadBarrier(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 (id.isString()) {
+ return mozilla::Some(f(id.toString()));
+ }
+ if (id.isSymbol()) {
+ return mozilla::Some(f(id.toSymbol()));
+ }
+ 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 isVoid() const { return id().isVoid(); }
+ 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);
+ }
+
+ uintptr_t asRawBits() const { return id().asRawBits(); }
+
+ // Internal API
+ bool isAtom() const { return id().isAtom(); }
+ bool isAtom(JSAtom* atom) const { return id().isAtom(atom); }
+ JSAtom* toAtom() const { return id().toAtom(); }
+ JSLinearString* toLinearString() const { return id().toLinearString(); }
+};
+
+} // namespace js
+
+#endif /* js_Id_h */
diff --git a/js/public/Initialization.h b/js/public/Initialization.h
new file mode 100644
index 0000000000..46715b6c4f
--- /dev/null
+++ b/js/public/Initialization.h
@@ -0,0 +1,210 @@
+/* -*- 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 "mozilla/Span.h"
+
+#include "jstypes.h"
+
+struct JS_PUBLIC_API JSContext;
+
+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;
+
+enum class FrontendOnly { No, Yes };
+
+extern JS_PUBLIC_API const char* InitWithFailureDiagnostic(
+ bool isDebugBuild, FrontendOnly frontendOnly = FrontendOnly::No);
+
+} // 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
+}
+
+/**
+ * A lightweight variant of JS_Init, which skips initializing runtime-specific
+ * part.
+ * Suitable for processes where only JSContext-free stencil-APIs are used.
+ */
+inline bool JS_FrontendOnlyInit(void) {
+#ifdef DEBUG
+ return !JS::detail::InitWithFailureDiagnostic(true,
+ JS::detail::FrontendOnly::Yes);
+#else
+ return !JS::detail::InitWithFailureDiagnostic(false,
+ JS::detail::FrontendOnly::Yes);
+#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;
+}
+
+namespace JS {
+
+// Reference to a sequence of bytes.
+// TODO: This type should be Span<cont uint8_t> (Bug 1709135)
+using SelfHostedCache = mozilla::Span<const uint8_t>;
+
+// Callback function used to copy the SelfHosted content to memory or to disk.
+using SelfHostedWriter = bool (*)(JSContext*, SelfHostedCache);
+
+/*
+ * Initialize the runtime's self-hosted code. Embeddings should call this
+ * exactly once per runtime/context, before the first JS_NewGlobalObject
+ * call.
+ *
+ * This function parses the self-hosted code, except if the provided cache span
+ * is not empty, in which case the self-hosted content is decoded from the span.
+ *
+ * The cached content provided as argument, when non-empty, should come from the
+ * a previous execution of JS::InitSelfHostedCode where a writer was registered.
+ * The content should come from the same version of the binary, otherwise this
+ * would cause an error.
+ *
+ * The cached content provided with the Span should remain alive until
+ * JS_Shutdown is called.
+ *
+ * The writer callback given as argument would be called by when the result of
+ * the parser is ready to be cached. The writer is in charge of saving the
+ * content in memory or on disk. The span given as argument of the writer only
+ * last for the time of the call, and contains the content to be saved.
+ *
+ * The writer is not called if the cached content given as argument of
+ * InitSelfHostedCode is non-empty.
+ *
+ * Errors returned by the writer callback would bubble up through
+ * JS::InitSelfHostedCode.
+ *
+ * The cached content provided by the writer callback is safe to reuse across
+ * threads, and even across multiple executions as long as the executable is
+ * identical.
+ *
+ * NOTE: This may not set a pending exception in the case of OOM since this
+ * runs very early in startup.
+ */
+JS_PUBLIC_API bool InitSelfHostedCode(JSContext* cx,
+ SelfHostedCache cache = nullptr,
+ SelfHostedWriter writer = nullptr);
+
+/*
+ * Permanently disable the JIT backend for this process. This disables the JS
+ * Baseline Interpreter, JIT compilers, regular expression JIT and support for
+ * WebAssembly.
+ *
+ * If called, this *must* be called before JS_Init.
+ */
+JS_PUBLIC_API void DisableJitBackend();
+
+} // namespace JS
+
+/**
+ * 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);
+
+/**
+ * A variant of JS_ShutDown for process which used JS_FrontendOnlyInit instead
+ * of JS_Init.
+ */
+extern JS_PUBLIC_API void JS_FrontendOnlyShutDown(void);
+
+#if defined(ENABLE_WASM_SIMD) && \
+ (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86))
+namespace JS {
+// Enable support for AVX instructions in the JIT/Wasm backend on x86/x64
+// platforms. Must be called before JS_Init*.
+void SetAVXEnabled(bool enabled);
+} // namespace JS
+#endif
+
+#endif /* js_Initialization_h */
diff --git a/js/public/Interrupt.h b/js/public/Interrupt.h
new file mode 100644
index 0000000000..3e7ead7b28
--- /dev/null
+++ b/js/public/Interrupt.h
@@ -0,0 +1,41 @@
+/* -*- 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/. */
+
+#ifndef js_Interrupt_h
+#define js_Interrupt_h
+
+#include "jstypes.h"
+
+struct JS_PUBLIC_API JSContext;
+
+using JSInterruptCallback = bool (*)(JSContext*);
+
+extern JS_PUBLIC_API bool JS_CheckForInterrupt(JSContext* cx);
+
+/*
+ * These functions allow setting an interrupt callback that will be called
+ * from the JS thread some time after any thread triggered the callback using
+ * JS_RequestInterruptCallback(cx).
+ *
+ * To schedule the GC and for other activities the engine internally triggers
+ * interrupt callbacks. The embedding should thus not rely on callbacks being
+ * triggered through the external API only.
+ *
+ * Important note: Additional callbacks can occur inside the callback handler
+ * if it re-enters the JS engine. The embedding must ensure that the callback
+ * is disconnected before attempting such re-entry.
+ */
+extern JS_PUBLIC_API bool JS_AddInterruptCallback(JSContext* cx,
+ JSInterruptCallback callback);
+
+extern JS_PUBLIC_API bool JS_DisableInterruptCallback(JSContext* cx);
+
+extern JS_PUBLIC_API void JS_ResetInterruptCallback(JSContext* cx, bool enable);
+
+extern JS_PUBLIC_API void JS_RequestInterruptCallback(JSContext* cx);
+
+extern JS_PUBLIC_API void JS_RequestInterruptCallbackCanWait(JSContext* cx);
+
+#endif // js_Interrupt_h
diff --git a/js/public/Iterator.h b/js/public/Iterator.h
new file mode 100644
index 0000000000..8a0a708ced
--- /dev/null
+++ b/js/public/Iterator.h
@@ -0,0 +1,39 @@
+/* -*- 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_Iterator_h
+#define js_Iterator_h
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+// https://tc39.es/ecma262/#sec-getiterator
+// GetIterator
+JSObject* GetIteratorObject(JSContext* cx, Handle<Value> obj, bool isAsync);
+
+// https://tc39.es/ecma262/#sec-iteratornext
+bool IteratorNext(JSContext* cx, Handle<JSObject*> iteratorRecord,
+ MutableHandle<Value> result);
+
+// https://tc39.es/ecma262/#sec-iteratorcomplete
+bool IteratorComplete(JSContext* cx, Handle<JSObject*> iterResult, bool* done);
+
+// https://tc39.es/ecma262/#sec-iteratorvalue
+bool IteratorValue(JSContext* cx, Handle<JSObject*> iterResult,
+ MutableHandle<Value> value);
+
+// Implements iteratorRecord.[[Iterator]]
+bool GetIteratorRecordIterator(JSContext* cx, Handle<JSObject*> iteratorRecord,
+ MutableHandle<Value> iterator);
+
+// Implements GetMethod(iterator, "return").
+bool GetReturnMethod(JSContext* cx, Handle<Value> iterator,
+ MutableHandle<Value> result);
+
+} // namespace JS
+
+#endif /* js_Iterator_h */
diff --git a/js/public/JSON.h b/js/public/JSON.h
new file mode 100644
index 0000000000..feef53f755
--- /dev/null
+++ b/js/public/JSON.h
@@ -0,0 +1,188 @@
+/* -*- 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/TypeDecls.h"
+
+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 exactly one call of |callback|, passing |data| as
+ * argument.
+ *
+ * In cases where JSON.stringify would return undefined, this function calls
+ * |callback| with the string "null".
+ */
+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);
+
+/**
+ * Performs the JSON.stringify operation, as specified by ECMAScript, except
+ * writing stringified data by one call of |callback|, passing |data| as
+ * argument.
+ *
+ * In cases where JSON.stringify would return undefined, this function does not
+ * call |callback| at all.
+ */
+extern JS_PUBLIC_API bool ToJSON(JSContext* cx, Handle<Value> value,
+ Handle<JSObject*> replacer,
+ Handle<Value> space,
+ 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.
+ */
+extern JS_PUBLIC_API bool JS_ParseJSON(JSContext* cx,
+ const JS::Latin1Char* chars,
+ uint32_t len,
+ 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);
+
+namespace JS {
+
+/**
+ * Returns true if the given text is valid JSON.
+ */
+extern JS_PUBLIC_API bool IsValidJSON(const JS::Latin1Char* chars,
+ uint32_t len);
+extern JS_PUBLIC_API bool IsValidJSON(const char16_t* chars, uint32_t len);
+
+/**
+ * Handler with callbacks for JS::ParseJSONWithHandler.
+ *
+ * Each method is called during parsing the JSON string. If the method returns
+ * true, the parsing keeps going. If the method returns false, the parsing
+ * stops and fails.
+ *
+ * The error method is called when syntax error happens while parsing the input.
+ * This method is not called when handler's method returns false.
+ */
+class JSONParseHandler {
+ public:
+ JSONParseHandler() {}
+ virtual ~JSONParseHandler() {}
+
+ // Called when '{' is found for an object.
+ virtual bool startObject() = 0;
+
+ // Called when a property name is found for an object.
+ // The character type depends on the input type and also the content of the
+ // property name. The consumer should implement both methods.
+ virtual bool propertyName(const JS::Latin1Char* name, size_t length) = 0;
+ virtual bool propertyName(const char16_t* name, size_t length) = 0;
+
+ // Called when '}' is found for an object.
+ virtual bool endObject() = 0;
+
+ // Called when '[' is found for an array.
+ virtual bool startArray() = 0;
+
+ // Called when ']' is found for an array.
+ virtual bool endArray() = 0;
+
+ // Called when a string is found.
+ // The character type depends on the input type and also the content of the
+ // string. The consumer should implement both methods.
+ virtual bool stringValue(const JS::Latin1Char* str, size_t length) = 0;
+ virtual bool stringValue(const char16_t* str, size_t length) = 0;
+
+ // Called when a number is found.
+ virtual bool numberValue(double d) = 0;
+
+ // Called when a boolean is found.
+ virtual bool booleanValue(bool v) = 0;
+
+ // Called when null is found.
+ virtual bool nullValue() = 0;
+
+ // Called when syntax error happens.
+ virtual void error(const char* msg, uint32_t line, uint32_t column) = 0;
+};
+
+/**
+ * Performs the JSON.parse operation as specified by ECMAScript, and call
+ * callbacks defined by the handler.
+ */
+extern JS_PUBLIC_API bool ParseJSONWithHandler(const JS::Latin1Char* chars,
+ uint32_t len,
+ JSONParseHandler* handler);
+extern JS_PUBLIC_API bool ParseJSONWithHandler(const char16_t* chars,
+ uint32_t len,
+ JSONParseHandler* handler);
+
+} // namespace JS
+
+#endif /* js_JSON_h */
diff --git a/js/public/JitCodeAPI.h b/js/public/JitCodeAPI.h
new file mode 100644
index 0000000000..e1cdad2083
--- /dev/null
+++ b/js/public/JitCodeAPI.h
@@ -0,0 +1,97 @@
+/* -*- 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 API for obtaining JitCode information. */
+
+#ifndef js_JitCodeAPI_h
+#define js_JitCodeAPI_h
+
+#include "js/AllocPolicy.h"
+#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
+#include "js/Initialization.h"
+#include "js/Printf.h"
+#include "js/Vector.h"
+
+namespace JS {
+
+enum class JitTier { Baseline, IC, Ion, Other };
+
+class JitOpcodeDictionary {
+ typedef js::Vector<UniqueChars, 0, js::SystemAllocPolicy> StringVector;
+
+ public:
+ JitOpcodeDictionary();
+
+ StringVector& GetBaselineDictionary() { return baselineDictionary; }
+ StringVector& GetIonDictionary() { return ionDictionary; }
+ StringVector& GetInlineCacheDictionary() { return icDictionary; }
+
+ private:
+ StringVector baselineDictionary;
+ StringVector icDictionary;
+ StringVector ionDictionary;
+};
+
+struct JitCodeSourceInfo {
+ UniqueChars filename;
+ uint32_t offset = 0;
+
+ // Line number (1-origin).
+ uint32_t lineno = 0;
+ // Column number in UTF-16 code units.
+ JS::LimitedColumnNumberOneOrigin colno;
+};
+
+struct JitCodeIRInfo {
+ uint32_t offset = 0;
+ uint32_t opcode = 0;
+ UniqueChars str;
+};
+
+typedef js::Vector<JitCodeSourceInfo, 0, js::SystemAllocPolicy>
+ SourceInfoVector;
+typedef js::Vector<JitCodeIRInfo, 0, js::SystemAllocPolicy> IRInfoVector;
+
+struct JitCodeRecord {
+ UniqueChars functionName;
+ uint64_t code_addr = 0;
+ uint32_t instructionSize = 0;
+ JitTier tier = JitTier::Other;
+
+ SourceInfoVector sourceInfo;
+ IRInfoVector irInfo;
+};
+
+class JitCodeIterator {
+ void getDataForIndex(size_t iteratorIndex);
+
+ public:
+ JitCodeIterator();
+ ~JitCodeIterator();
+
+ void operator++(int) {
+ iteratorIndex++;
+ getDataForIndex(iteratorIndex);
+ }
+
+ explicit operator bool() const { return data != nullptr; }
+
+ SourceInfoVector& sourceData() { return data->sourceInfo; }
+
+ IRInfoVector& irData() { return data->irInfo; }
+
+ const char* functionName() const { return data->functionName.get(); }
+ uint64_t code_addr() const { return data->code_addr; }
+ uint32_t instructionSize() { return data->instructionSize; }
+ JitTier jit_tier() const { return data->tier; }
+
+ private:
+ JitCodeRecord* data = nullptr;
+ size_t iteratorIndex = 0;
+};
+
+} // namespace JS
+
+#endif /* js_JitCodeAPI_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/MapAndSet.h b/js/public/MapAndSet.h
new file mode 100644
index 0000000000..754379dc82
--- /dev/null
+++ b/js/public/MapAndSet.h
@@ -0,0 +1,85 @@
+/* -*- 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/. */
+
+/*
+ * Maps and Sets.
+ */
+
+#ifndef js_MapAndSet_h
+#define js_MapAndSet_h
+
+#include "jspubtd.h"
+
+namespace JS {
+
+/*
+ * Map
+ */
+extern JS_PUBLIC_API JSObject* NewMapObject(JSContext* cx);
+
+extern JS_PUBLIC_API uint32_t MapSize(JSContext* cx, HandleObject obj);
+
+extern JS_PUBLIC_API bool MapGet(JSContext* cx, HandleObject obj,
+ HandleValue key, MutableHandleValue rval);
+
+extern JS_PUBLIC_API bool MapHas(JSContext* cx, HandleObject obj,
+ HandleValue key, bool* rval);
+
+extern JS_PUBLIC_API bool MapSet(JSContext* cx, HandleObject obj,
+ HandleValue key, HandleValue val);
+
+extern JS_PUBLIC_API bool MapDelete(JSContext* cx, HandleObject obj,
+ HandleValue key, bool* rval);
+
+extern JS_PUBLIC_API bool MapClear(JSContext* cx, HandleObject obj);
+
+extern JS_PUBLIC_API bool MapKeys(JSContext* cx, HandleObject obj,
+ MutableHandleValue rval);
+
+extern JS_PUBLIC_API bool MapValues(JSContext* cx, HandleObject obj,
+ MutableHandleValue rval);
+
+extern JS_PUBLIC_API bool MapEntries(JSContext* cx, HandleObject obj,
+ MutableHandleValue rval);
+
+extern JS_PUBLIC_API bool MapForEach(JSContext* cx, HandleObject obj,
+ HandleValue callbackFn,
+ HandleValue thisVal);
+
+/*
+ * Set
+ */
+extern JS_PUBLIC_API JSObject* NewSetObject(JSContext* cx);
+
+extern JS_PUBLIC_API uint32_t SetSize(JSContext* cx, HandleObject obj);
+
+extern JS_PUBLIC_API bool SetHas(JSContext* cx, HandleObject obj,
+ HandleValue key, bool* rval);
+
+extern JS_PUBLIC_API bool SetDelete(JSContext* cx, HandleObject obj,
+ HandleValue key, bool* rval);
+
+extern JS_PUBLIC_API bool SetAdd(JSContext* cx, HandleObject obj,
+ HandleValue key);
+
+extern JS_PUBLIC_API bool SetClear(JSContext* cx, HandleObject obj);
+
+extern JS_PUBLIC_API bool SetKeys(JSContext* cx, HandleObject obj,
+ MutableHandleValue rval);
+
+extern JS_PUBLIC_API bool SetValues(JSContext* cx, HandleObject obj,
+ MutableHandleValue rval);
+
+extern JS_PUBLIC_API bool SetEntries(JSContext* cx, HandleObject obj,
+ MutableHandleValue rval);
+
+extern JS_PUBLIC_API bool SetForEach(JSContext* cx, HandleObject obj,
+ HandleValue callbackFn,
+ HandleValue thisVal);
+
+} // namespace JS
+
+#endif // js_MapAndSet_h
diff --git a/js/public/MemoryCallbacks.h b/js/public/MemoryCallbacks.h
new file mode 100644
index 0000000000..dad63915d4
--- /dev/null
+++ b/js/public/MemoryCallbacks.h
@@ -0,0 +1,47 @@
+/* -*- 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/. */
+
+#ifndef js_MemoryCallbacks_h
+#define js_MemoryCallbacks_h
+
+#include "jstypes.h"
+
+struct JS_PUBLIC_API JSContext;
+
+namespace JS {
+
+/**
+ * If a large allocation fails when calling pod_{calloc,realloc}CanGC, the JS
+ * engine may call the large-allocation-failure callback, if set, to allow the
+ * embedding to flush caches, possibly perform shrinking GCs, etc. to make some
+ * room. The allocation will then be retried (and may still fail.) This callback
+ * can be called on any thread and must be set at most once in a process.
+ */
+
+using LargeAllocationFailureCallback = void (*)();
+
+extern JS_PUBLIC_API void SetProcessLargeAllocationFailureCallback(
+ LargeAllocationFailureCallback afc);
+
+/**
+ * Unlike the error reporter, which is only called if the exception for an OOM
+ * bubbles up and is not caught, the OutOfMemoryCallback is called immediately
+ * at the OOM site to allow the embedding to capture the current state of heap
+ * allocation before anything is freed. If the large-allocation-failure callback
+ * is called at all (not all allocation sites call the large-allocation-failure
+ * callback on failure), it is called before the out-of-memory callback; the
+ * out-of-memory callback is only called if the allocation still fails after the
+ * large-allocation-failure callback has returned.
+ */
+
+using OutOfMemoryCallback = void (*)(JSContext*, void*);
+
+extern JS_PUBLIC_API void SetOutOfMemoryCallback(JSContext* cx,
+ OutOfMemoryCallback cb,
+ void* data);
+
+} // namespace JS
+
+#endif // js_MemoryCallbacks_h
diff --git a/js/public/MemoryFunctions.h b/js/public/MemoryFunctions.h
new file mode 100644
index 0000000000..f0417f5eb3
--- /dev/null
+++ b/js/public/MemoryFunctions.h
@@ -0,0 +1,91 @@
+/* -*- 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 <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JS_PUBLIC_API JSContext;
+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);
+
+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) \
+ _(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..f30844d8aa
--- /dev/null
+++ b/js/public/MemoryMetrics.h
@@ -0,0 +1,915 @@
+/* -*- 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 "jstypes.h"
+
+#include "js/AllocPolicy.h"
+#include "js/HashTable.h"
+#include "js/TraceKind.h"
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+#include "js/Vector.h"
+
+class nsISupports; // Needed for ObjectPrivateVisitor.
+
+namespace js {
+class SystemAllocPolicy;
+}
+
+namespace mozilla {
+struct CStringHasher;
+}
+
+namespace JS {
+class JS_PUBLIC_API AutoRequireNoGC;
+
+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_PUBLIC_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, objectsMallocHeapGlobalData) \
+ MACRO(Objects, MallocHeap, objectsMallocHeapGlobalVarNamesSet) \
+ MACRO(Objects, MallocHeap, objectsMallocHeapMisc) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsNormal) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsShared) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsWasm) \
+ MACRO(Objects, NonHeap, objectsNonHeapElementsWasmShared) \
+ 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);
+
+#undef FOR_EACH_SIZE
+};
+
+struct ShapeInfo {
+#define FOR_EACH_SIZE(MACRO) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapShared) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapDict) \
+ MACRO(Other, GCHeapUsed, shapesGCHeapBase) \
+ MACRO(Other, MallocHeap, shapesMallocHeapCache)
+
+ 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, nurseryMallocedBlockCache) \
+ MACRO(_, MallocHeap, nurseryTrailerBlockSets) \
+ MACRO(_, MallocHeap, storeBufferVals) \
+ MACRO(_, MallocHeap, storeBufferCells) \
+ MACRO(_, MallocHeap, storeBufferSlots) \
+ MACRO(_, MallocHeap, storeBufferWasmAnyRefs) \
+ 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, 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 {
+ explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf)
+ : mallocSizeOf_(mallocSizeOf) {}
+
+ HelperThreadStats helperThread;
+
+ mozilla::MallocSizeOf mallocSizeOf_;
+};
+
+/**
+ * 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, selfHostStencil) \
+ MACRO(_, MallocHeap, contexts) \
+ MACRO(_, MallocHeap, temporary) \
+ MACRO(_, MallocHeap, interpreterStack) \
+ MACRO(_, MallocHeap, sharedImmutableStringsCache) \
+ MACRO(_, MallocHeap, sharedIntlData) \
+ MACRO(_, MallocHeap, uncompressedSourceCache) \
+ MACRO(_, MallocHeap, scriptData) \
+ MACRO(_, MallocHeap, wasmRuntime) \
+ MACRO(_, Ignore, wasmGuardPages) \
+ 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, getterSetter) \
+ MACRO(Other, GCHeapUnused, propMap) \
+ 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::GetterSetter:
+ getterSetter += n;
+ break;
+ case JS::TraceKind::PropMap:
+ propMap += n;
+ break;
+ case JS::TraceKind::JitCode:
+ jitcode += 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, getterSettersGCHeap) \
+ MACRO(Other, GCHeapUsed, compactPropMapsGCHeap) \
+ MACRO(Other, GCHeapUsed, normalPropMapsGCHeap) \
+ MACRO(Other, GCHeapUsed, dictPropMapsGCHeap) \
+ MACRO(Other, MallocHeap, propMapChildren) \
+ MACRO(Other, MallocHeap, propMapTables) \
+ 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, cacheIRStubs) \
+ MACRO(Other, MallocHeap, uniqueIdMap) \
+ MACRO(Other, MallocHeap, initialPropMapTable) \
+ 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, allocSites) \
+ 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, nonSyntacticLexicalScopesTable)
+
+ 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, gcHeapDecommittedPages) \
+ 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.gcHeapDecommittedPages
+ // (decommitted pages 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 pages in empty chunks may be decommitted, but
+ // we don't count those under rtStats.gcHeapDecommittedPages 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..580962235f
--- /dev/null
+++ b/js/public/Modules.h
@@ -0,0 +1,294 @@
+/* -*- 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 <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/AllocPolicy.h" // js::SystemAllocPolicy
+#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
+#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions
+#include "js/RootingAPI.h" // JS::{Mutable,}Handle
+#include "js/Value.h" // JS::Value
+#include "js/Vector.h" // js::Vector
+
+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 mozilla {
+union Utf8Unit;
+}
+
+namespace JS {
+
+/**
+ * The HostResolveImportedModule hook.
+ *
+ * See: https://tc39.es/ecma262/#sec-hostresolveimportedmodule
+ *
+ * This embedding-defined hook is used to implement module loading. It is called
+ * to get or create a module object corresponding to |moduleRequest| occurring
+ * in the context of the script or module with private value
+ * |referencingPrivate|.
+ *
+ * The module specifier string for the request can be obtained by calling
+ * JS::GetModuleRequestSpecifier.
+ *
+ * The private value for a script or module is set with JS::SetScriptPrivate or
+ * JS::SetModulePrivate. It's assumed that the embedding can handle receiving
+ * either here.
+ *
+ * This hook must obey the restrictions defined in the spec:
+ * - Each time the hook is called with the same arguemnts, the same module must
+ * be returned.
+ * - If a module cannot be created for the given arguments, an exception must
+ * be thrown.
+ *
+ * This is a synchronous operation.
+ */
+using ModuleResolveHook = JSObject* (*)(JSContext* cx,
+ Handle<Value> referencingPrivate,
+ Handle<JSObject*> moduleRequest);
+
+/**
+ * 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);
+
+/**
+ * The module metadata hook.
+ *
+ * See: https://tc39.es/ecma262/#sec-hostgetimportmetaproperties
+ *
+ * Populate the |metaObject| object returned when import.meta is evaluated in
+ * the context of the script or module with private value |privateValue|.
+ *
+ * This is based on the spec's HostGetImportMetaProperties hook but defines
+ * properties on the meta object directly rather than returning a list.
+ */
+using ModuleMetadataHook = bool (*)(JSContext* cx, Handle<Value> privateValue,
+ Handle<JSObject*> metaObject);
+
+/**
+ * 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);
+
+/**
+ * The HostImportModuleDynamically hook.
+ *
+ * See https://tc39.es/ecma262/#sec-hostimportmoduledynamically
+ *
+ * Used to implement dynamic module import. Called when evaluating import()
+ * expressions.
+ *
+ * This starts an asynchronous operation. Some time after this hook is called
+ * the embedding must call JS::FinishDynamicModuleImport() passing the
+ * |referencingPrivate|, |moduleRequest| and |promise| arguments from the
+ * call. This must happen for both success and failure cases.
+ *
+ * In the meantime the embedding can take whatever steps it needs to make the
+ * module available. If successful, after calling FinishDynamicModuleImport()
+ * the module should be returned by the resolve hook when passed
+ * |referencingPrivate| and |moduleRequest|.
+ */
+using ModuleDynamicImportHook = bool (*)(JSContext* cx,
+ Handle<Value> referencingPrivate,
+ Handle<JSObject*> moduleRequest,
+ 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);
+
+/**
+ * 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<JSObject*> moduleRequest,
+ 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);
+/**
+ * Clear the private value associated with a source text module record.
+ *
+ * This is used during unlinking and can be called on a gray module, skipping
+ * the usual checks.
+ */
+extern JS_PUBLIC_API void ClearModulePrivate(JSObject* module);
+
+/**
+ * Get the private value associated with a source text module record.
+ */
+extern JS_PUBLIC_API Value GetModulePrivate(JSObject* module);
+
+/*
+ * Perform the ModuleLink 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 ModuleLink(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.
+ *
+ * ModuleLink must have completed prior to calling this.
+ */
+extern JS_PUBLIC_API bool ModuleEvaluate(JSContext* cx,
+ Handle<JSObject*> moduleRecord,
+ MutableHandleValue rval);
+
+enum ModuleErrorBehaviour {
+ // Report module evaluation errors asynchronously when the evaluation promise
+ // is rejected. This is used for web content.
+ ReportModuleErrorsAsync,
+
+ // Throw module evaluation errors synchronously by setting an exception on the
+ // context. Does not support modules that use top-level await.
+ ThrowModuleErrorsSync
+};
+
+/*
+ * 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,
+ ModuleErrorBehaviour errorBehaviour = ReportModuleErrorsAsync);
+
+/*
+ * Functions to access the module specifiers of a source text module record used
+ * to request module imports.
+ *
+ * Clients can use GetRequestedModulesCount() to get the number of specifiers
+ * and GetRequestedModuleSpecifier() / GetRequestedModuleSourcePos() to get the
+ * individual elements.
+ */
+extern JS_PUBLIC_API uint32_t
+GetRequestedModulesCount(JSContext* cx, Handle<JSObject*> moduleRecord);
+
+extern JS_PUBLIC_API JSString* GetRequestedModuleSpecifier(
+ JSContext* cx, Handle<JSObject*> moduleRecord, uint32_t index);
+
+/*
+ * Get the position of a requested module's name in the source.
+ */
+extern JS_PUBLIC_API void GetRequestedModuleSourcePos(
+ JSContext* cx, Handle<JSObject*> moduleRecord, uint32_t index,
+ uint32_t* lineNumber, JS::ColumnNumberOneOrigin* columnNumber);
+
+/*
+ * Get the top-level script for a module which has not yet been executed.
+ */
+extern JS_PUBLIC_API JSScript* GetModuleScript(Handle<JSObject*> moduleRecord);
+
+extern JS_PUBLIC_API JSObject* CreateModuleRequest(
+ JSContext* cx, Handle<JSString*> specifierArg);
+extern JS_PUBLIC_API JSString* GetModuleRequestSpecifier(
+ JSContext* cx, Handle<JSObject*> moduleRequestArg);
+
+/*
+ * Get the module record for a module script.
+ */
+extern JS_PUBLIC_API JSObject* GetModuleObject(Handle<JSScript*> moduleScript);
+
+/*
+ * Get the namespace object for a module.
+ */
+extern JS_PUBLIC_API JSObject* GetModuleNamespace(
+ JSContext* cx, Handle<JSObject*> moduleRecord);
+
+extern JS_PUBLIC_API JSObject* GetModuleForNamespace(
+ JSContext* cx, Handle<JSObject*> moduleNamespace);
+
+extern JS_PUBLIC_API JSObject* GetModuleEnvironment(
+ JSContext* cx, Handle<JSObject*> moduleObj);
+
+/*
+ * Clear all bindings in a module's environment. Used during shutdown.
+ */
+extern JS_PUBLIC_API void ClearModuleEnvironment(JSObject* moduleObj);
+
+} // namespace JS
+
+#endif // js_Modules_h
diff --git a/js/public/Object.h b/js/public/Object.h
new file mode 100644
index 0000000000..c7c5e3c617
--- /dev/null
+++ b/js/public/Object.h
@@ -0,0 +1,147 @@
+/* -*- 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
+#include "js/Realm.h" // JS::GetCompartmentForRealm
+#include "js/RootingAPI.h" // JS::{,Mutable}Handle
+#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)->shape->base->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)->shape->base->realm;
+ return GetCompartmentForRealm(realm);
+}
+
+/**
+ * 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_PUBLIC_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;
+ }
+}
+
+/**
+ * Helper function to get the pointer value (or nullptr if not set) from an
+ * object's reserved slot. The slot must contain either a PrivateValue(T*) or
+ * UndefinedValue.
+ */
+template <typename T>
+inline T* GetMaybePtrFromReservedSlot(JSObject* obj, size_t slot) {
+ Value v = GetReservedSlot(obj, slot);
+ return v.isUndefined() ? nullptr : static_cast<T*>(v.toPrivate());
+}
+
+/**
+ * Helper function to get the pointer value (or nullptr if not set) from the
+ * object's first reserved slot. Must only be used for objects with a JSClass
+ * that has the JSCLASS_SLOT0_IS_NSISUPPORTS flag.
+ */
+template <typename T>
+inline T* GetObjectISupports(JSObject* obj) {
+ MOZ_ASSERT(GetClass(obj)->slot0IsISupports());
+ return GetMaybePtrFromReservedSlot<T>(obj, 0);
+}
+
+/**
+ * Helper function to store |PrivateValue(nsISupportsValue)| in the object's
+ * first reserved slot. Must only be used for objects with a JSClass that has
+ * the JSCLASS_SLOT0_IS_NSISUPPORTS flag.
+ *
+ * Note: the pointer is opaque to the JS engine (including the GC) so it's the
+ * embedding's responsibility to trace or free this value.
+ */
+inline void SetObjectISupports(JSObject* obj, void* nsISupportsValue) {
+ MOZ_ASSERT(GetClass(obj)->slot0IsISupports());
+ SetReservedSlot(obj, 0, PrivateValue(nsISupportsValue));
+}
+
+} // namespace JS
+
+// JSObject* is an aligned pointer, but this information isn't available in the
+// public header. We specialize HasFreeLSB here so that JS::Result<JSObject*>
+// compiles.
+
+namespace mozilla {
+namespace detail {
+template <>
+struct HasFreeLSB<JSObject*> {
+ static constexpr bool value = true;
+};
+} // namespace detail
+} // namespace mozilla
+
+#endif // js_public_Object_h
diff --git a/js/public/Prefs.h b/js/public/Prefs.h
new file mode 100644
index 0000000000..374b5bdbee
--- /dev/null
+++ b/js/public/Prefs.h
@@ -0,0 +1,131 @@
+/* -*- 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/. */
+
+#ifndef js_Prefs_h
+#define js_Prefs_h
+
+#include "js/PrefsGenerated.h"
+
+// [SMDOC] Prefs
+//
+// JS::Prefs is used to make JS preferences defined in StaticPrefList.yaml
+// available to SpiderMonkey code.
+//
+// Adding a Pref
+// =============
+// Adding a new pref is easy. For example, if you're adding a new JS feature,
+// you could add the following to StaticPrefList.yaml:
+//
+// - name: javascript.options.experimental.my_new_feature
+// type: bool
+// value: false
+// mirror: always
+// set_spidermonkey_pref: startup
+//
+// The value of this pref can then be accessed in SpiderMonkey code with
+// |JS::Prefs::experimental_my_new_feature()|.
+//
+// The default pref value in the YAML file applies to all SpiderMonkey builds
+// (browser, JS shell, jsapi-tests, etc), so by default this feature will be
+// disabled everywhere.
+//
+// To enable your feature, use the |--setpref experimental.my_new_feature=true|
+// JS shell command line argument, or set the browser pref in about:config.
+// Because this is a 'startup' pref, a browser restart is required for this to
+// take effect.
+//
+// The rest of this comment describes more advanced use cases.
+//
+// Non-startup prefs
+// =================
+// Setting |set_spidermonkey_pref = startup| is recommended for most prefs.
+// In this case the pref is only set during startup so we don't have to worry
+// about the pref value changing at runtime.
+//
+// However, for some prefs this doesn't work. For instance, the WPT test harness
+// can set test-specific prefs after startup. To properly update the JS pref in
+// this case, |set_spidermonkey_pref = always| must be used. This means the
+// SpiderMonkey pref will be updated whenever it's changed in the browser.
+//
+// Setting Prefs
+// =============
+// Embedders can override pref values. For startup prefs, this should only be
+// done during startup (before calling JS_Init*) to avoid races with worker
+// threads and to avoid confusing code with unexpected pref changes:
+//
+// JS::Prefs::setAtStartup_experimental_my_new_feature(true);
+//
+// Non-startup prefs can also be changed after startup:
+//
+// JS::Prefs::set_experimental_my_new_feature(true);
+//
+// JS Shell Prefs
+// ==============
+// The JS shell |--list-prefs| command line flag will print a list of all of the
+// available JS prefs and their current values.
+//
+// To change a pref, use |--setpref name=value|, for example
+// |--setpref experimental.my_new_feature=true|.
+//
+// It's also possible to add a custom shell flag. In this case you have to
+// override the pref value yourself based on this flag.
+//
+// Testing Functions
+// =================
+// The |getAllPrefNames()| function will return an array with all JS pref names.
+//
+// The |getPrefValue(name)| function can be used to look up the value of the
+// given pref. For example, use |getPrefValue("experimental.my_new_feature")|
+// for the pref defined above.
+
+namespace JS {
+
+class Prefs {
+ // For each pref, define a static |pref_| member.
+ JS_PREF_CLASS_FIELDS;
+
+#ifdef DEBUG
+ static void assertCanSetStartupPref();
+#else
+ static void assertCanSetStartupPref() {}
+#endif
+
+ public:
+ // For each pref, define static getter/setter accessors.
+#define DEF_GETSET(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
+ static TYPE CPP_NAME() { return CPP_NAME##_; } \
+ static void SETTER(TYPE value) { \
+ if (IS_STARTUP_PREF) { \
+ assertCanSetStartupPref(); \
+ } \
+ CPP_NAME##_ = value; \
+ }
+ FOR_EACH_JS_PREF(DEF_GETSET)
+#undef DEF_GETSET
+};
+
+/**
+ * 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
+};
+
+inline WeakRefSpecifier GetWeakRefsEnabled() {
+ if (!Prefs::weakrefs()) {
+ return WeakRefSpecifier::Disabled;
+ }
+ if (Prefs::experimental_weakrefs_expose_cleanupSome()) {
+ return WeakRefSpecifier::EnabledWithCleanupSome;
+ }
+ return WeakRefSpecifier::EnabledWithoutCleanupSome;
+}
+
+}; // namespace JS
+
+#endif /* js_Prefs_h */
diff --git a/js/public/Principals.h b/js/public/Principals.h
new file mode 100644
index 0000000000..609b1ecff6
--- /dev/null
+++ b/js/public/Principals.h
@@ -0,0 +1,173 @@
+/* -*- 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 "jstypes.h"
+
+#include "js/TypeDecls.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);
+
+namespace JS {
+enum class RuntimeCode { JS, WASM };
+} // namespace JS
+
+/*
+ * Used to check if a CSP instance wants to disable eval() and friends.
+ * See JSContext::isRuntimeCodeGenEnabled() in vm/JSContext.cpp.
+ *
+ * `code` is the JavaScript source code passed to eval/Function, but nullptr
+ * for Wasm.
+ *
+ * Returning `false` from this callback will prevent the execution/compilation
+ * of the code.
+ */
+typedef bool (*JSCSPEvalChecker)(JSContext* cx, JS::RuntimeCode kind,
+ 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/Printer.h b/js/public/Printer.h
new file mode 100644
index 0000000000..644ffa9176
--- /dev/null
+++ b/js/public/Printer.h
@@ -0,0 +1,565 @@
+/* -*- 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_Printer_h
+#define js_Printer_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/glue/Debug.h"
+#include "mozilla/Range.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "js/TypeDecls.h"
+#include "js/Utility.h"
+
+// [SMDOC] *Printer, Sprinter, Fprinter, ...
+//
+// # Motivation
+//
+// In many places, we want to have functions which are capable of logging
+// various data structures. Previously, we had logging functions for each
+// storage, such as using `fwrite`, `printf` or `snprintf`. In additional cases,
+// many of these logging options were using a string serializing logging
+// function, only to discard the allocated string after it had been copied to a
+// file.
+//
+// GenericPrinter is an answer to avoid excessive amount of temporary
+// allocations which are used once, and a way to make logging functions work
+// independently of the backend they are used with.
+//
+// # Design
+//
+// The GenericPrinter implements most of `put`, `printf`, `vprintf` and
+// `putChar` functions, which are implemented using `put` and `putChar`
+// functions in the derivative classes. Thus, one does not have to reimplement
+// `putString` nor `printf` for each printer.
+//
+// // Logging the value N to whatever printer is provided such as
+// // a file or a string.
+// void logN(GenericPrinter& out) {
+// out.printf("[Logging] %d\n", this->n);
+// }
+//
+// The printing functions are infallible, from the logging functions
+// perspective. If an issue happens while printing, this would be recorded by
+// the Printer, and this can be tested using `hadOutOfMemory` function by the
+// owner of the Printer instance.
+//
+// Even in case of failure, printing functions should remain safe to use. Thus
+// calling `put` twice in a row is safe even if no check for `hadOutOfMemory` is
+// performed. This is necessary to simplify the control flow and avoid bubble up
+// failures out of logging functions.
+//
+// Note, being safe to use does not imply correctness. In case of failure the
+// correctness of the printed characters is no longer guarantee. One should use
+// `hadOutOfMemory` function to know if any failure happened which might have
+// caused incorrect content to be saved. In some cases, such as `Sprinter`,
+// where the string buffer can be extracted, the returned value would account
+// for checking `hadOutOfMemory`.
+//
+// # Implementations
+//
+// The GenericPrinter is a base class where the derivative classes are providing
+// different implementations which have their own advantages and disadvantages:
+//
+// - Fprinter: FILE* printer. Write the content directly to a file.
+//
+// - Sprinter: System allocator C-string buffer. Write the content to a buffer
+// which is reallocated as more content is added. The buffer can then be
+// extracted into a C-string or a JSString, respectively using `release` and
+// `releaseJS`.
+//
+// - LSprinter: LifoAlloc C-string rope. Write the content to a list of chunks
+// in a LifoAlloc buffer, no-reallocation occur but one should use
+// `exportInto` to serialize its content to a Sprinter or a Fprinter. This is
+// useful to avoid reallocation copies, while using an existing LifoAlloc.
+//
+// - SEPrinter: Roughly the same as Fprinter for stderr, except it goes through
+// printf_stderr, which makes sure the output goes to a useful place: the
+// Android log or the Windows debug output.
+//
+// - EscapePrinter: Wrapper around other printers, to escape characters when
+// necessary.
+//
+// # Print UTF-16
+//
+// The GenericPrinter only handle `char` inputs, which is good enough for ASCII
+// and Latin1 character sets. However, to handle UTF-16, one should use an
+// EscapePrinter as well as a policy for escaping characters.
+//
+// One might require different escaping policies based on the escape sequences
+// and based on the set of accepted character for the content generated. For
+// example, JSON does not specify \x<XX> escape sequences.
+//
+// Today the following escape policies exists:
+//
+// - StringEscape: Produce C-like escape sequences: \<c>, \x<XX> and \u<XXXX>.
+// - JSONEscape: Produce JSON escape sequences: \<c> and \u<XXXX>.
+//
+// An escape policy is defined by 2 functions:
+//
+// bool isSafeChar(char16_t c):
+// Returns whether a character can be printed without being escaped.
+//
+// void convertInto(GenericPrinter& out, char16_t c):
+// Calls the printer with the escape sequence for the character given as
+// argument.
+//
+// To use an escape policy, the printer should be wrapped using an EscapePrinter
+// as follows:
+//
+// {
+// // The escaped string is surrounded by double-quotes, escape the double
+// // quotes as well.
+// StringEscape esc('"');
+//
+// // Wrap our existing `GenericPrinter& out` using the `EscapePrinter`.
+// EscapePrinter ep(out, esc);
+//
+// // Append a sequence of characters which might contain UTF-16 characters.
+// ep.put(chars);
+// }
+//
+
+namespace js {
+
+class LifoAlloc;
+
+// Generic printf interface, similar to an ostream in the standard library.
+//
+// This class is useful to make generic printers which can work either with a
+// file backend, with a buffer allocated with an JSContext or a link-list
+// of chunks allocated with a LifoAlloc.
+class JS_PUBLIC_API GenericPrinter {
+ protected:
+ bool hadOOM_; // whether reportOutOfMemory() has been called.
+
+ constexpr GenericPrinter() : hadOOM_(false) {}
+
+ public:
+ // Puts |len| characters from |s| at the current position. This function might
+ // silently fail and the error can be tested using `hadOutOfMemory()`. Calling
+ // this function or any other printing functions after a failures is accepted,
+ // but the outcome would still remain incorrect and `hadOutOfMemory()` would
+ // still report any of the previous errors.
+ virtual void put(const char* s, size_t len) = 0;
+ inline void put(const char* s) { put(s, strlen(s)); }
+
+ // Put a mozilla::Span / mozilla::Range of Latin1Char or char16_t characters
+ // in the output.
+ //
+ // Note that the char16_t variant is expected to crash unless putChar is
+ // overriden to handle properly the full set of WTF-16 character set.
+ virtual void put(mozilla::Span<const JS::Latin1Char> str);
+ virtual void put(mozilla::Span<const char16_t> str);
+
+ // Same as the various put function but only appending a single character.
+ //
+ // Note that the char16_t variant is expected to crash unless putChar is
+ // overriden to handle properly the full set of WTF-16 character set.
+ virtual inline void putChar(const char c) { put(&c, 1); }
+ virtual inline void putChar(const JS::Latin1Char c) { putChar(char(c)); }
+ virtual inline void putChar(const char16_t c) {
+ MOZ_CRASH("Use an EscapePrinter to handle all characters");
+ }
+
+ virtual void putString(JSContext* cx, JSString* str);
+
+ // Prints a formatted string into the buffer.
+ void printf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
+ void vprintf(const char* fmt, va_list ap) MOZ_FORMAT_PRINTF(2, 0);
+
+ // In some cases, such as handling JSRopes in a less-quadratic worse-case,
+ // it might be useful to copy content which has already been generated.
+ //
+ // If the buffer is back-readable, then this function should return `true`
+ // and `putFromIndex` should be implemented to delegate to a `put` call at
+ // the matching index and the corresponding length. To provide the index
+ // argument of `putFromIndex`, the `index` method should also be implemented
+ // to return the index within the inner buffer used by the printer.
+ virtual bool canPutFromIndex() const { return false; }
+
+ // Append to the current buffer, bytes which have previously been appended
+ // before.
+ virtual void putFromIndex(size_t index, size_t length) {
+ MOZ_CRASH("Calls to putFromIndex should be guarded by canPutFromIndex.");
+ }
+
+ // When the printer has a seekable buffer and `canPutFromIndex` returns
+ // `true`, this function can return the `index` of the next character to be
+ // added to the buffer.
+ //
+ // This function is monotonic. Thus, if the printer encounter an
+ // Out-Of-Memory issue, then the returned index should be the maximal value
+ // ever returned.
+ virtual size_t index() const { return 0; }
+
+ // In some printers, this ensure that the content is fully written.
+ virtual void flush() { /* Do nothing */
+ }
+
+ // Report that a string operation failed to get the memory it requested.
+ virtual void reportOutOfMemory();
+
+ // Return true if this Sprinter ran out of memory.
+ virtual bool hadOutOfMemory() const { return hadOOM_; }
+};
+
+// Sprintf / JSSprintf, but with unlimited and automatically allocated
+// buffering.
+class JS_PUBLIC_API StringPrinter : public GenericPrinter {
+ public:
+ // Check that the invariant holds at the entry and exit of a scope.
+ struct InvariantChecker {
+ const StringPrinter* parent;
+
+ explicit InvariantChecker(const StringPrinter* p) : parent(p) {
+ parent->checkInvariants();
+ }
+
+ ~InvariantChecker() { parent->checkInvariants(); }
+ };
+
+ JSContext* maybeCx;
+
+ private:
+ static const size_t DefaultSize;
+#ifdef DEBUG
+ bool initialized; // true if this is initialized, use for debug builds
+#endif
+ bool shouldReportOOM; // whether to report OOM to the maybeCx
+ char* base; // malloc'd buffer address
+ size_t size; // size of buffer allocated at base
+ ptrdiff_t offset; // offset of next free char in buffer
+
+ // The arena to be used by jemalloc to allocate the string into. This is
+ // selected by the child classes when calling the constructor. JSStrings have
+ // a different arena than strings which do not belong to the JS engine, and as
+ // such when building a JSString with the intent of avoiding reallocation, the
+ // destination arena has to be selected upfront.
+ arena_id_t arena;
+
+ private:
+ [[nodiscard]] bool realloc_(size_t newSize);
+
+ protected:
+ // JSContext* parameter is optional and can be omitted if the following
+ // are not used.
+ // * putString method with JSString
+ // * QuoteString function with JSString
+ // * JSONQuoteString function with JSString
+ //
+ // If JSContext* parameter is not provided, or shouldReportOOM is false,
+ // the consumer should manually report OOM on any failure.
+ explicit StringPrinter(arena_id_t arena, JSContext* maybeCx = nullptr,
+ bool shouldReportOOM = true);
+ ~StringPrinter();
+
+ JS::UniqueChars releaseChars();
+ JSString* releaseJS(JSContext* cx);
+
+ public:
+ // Initialize this sprinter, returns false on error.
+ [[nodiscard]] bool init();
+
+ void checkInvariants() const;
+
+ // Attempt to reserve len + 1 space (for a trailing nullptr byte). If the
+ // attempt succeeds, return a pointer to the start of that space and adjust
+ // the internal content. The caller *must* completely fill this space on
+ // success.
+ char* reserve(size_t len);
+
+ // Puts |len| characters from |s| at the current position. May OOM, which must
+ // be checked by testing the return value of releaseJS() at the end of
+ // printing.
+ virtual void put(const char* s, size_t len) final;
+ using GenericPrinter::put; // pick up |put(const char* s);|
+
+ virtual bool canPutFromIndex() const final { return true; }
+ virtual void putFromIndex(size_t index, size_t length) final {
+ MOZ_ASSERT(index <= this->index());
+ MOZ_ASSERT(index + length <= this->index());
+ put(base + index, length);
+ }
+ virtual size_t index() const final { return length(); }
+
+ virtual void putString(JSContext* cx, JSString* str) final;
+
+ size_t length() const;
+
+ // When an OOM has already been reported on the Sprinter, this function will
+ // forward this error to the JSContext given in the Sprinter initialization.
+ //
+ // If no JSContext had been provided or the Sprinter is configured to not
+ // report OOM, then nothing happens.
+ void forwardOutOfMemory();
+};
+
+class JS_PUBLIC_API Sprinter : public StringPrinter {
+ public:
+ explicit Sprinter(JSContext* maybeCx = nullptr, bool shouldReportOOM = true)
+ : StringPrinter(js::MallocArena, maybeCx, shouldReportOOM) {}
+ ~Sprinter() {}
+
+ JS::UniqueChars release() { return releaseChars(); }
+};
+
+class JS_PUBLIC_API JSSprinter : public StringPrinter {
+ public:
+ explicit JSSprinter(JSContext* cx)
+ : StringPrinter(js::StringBufferArena, cx, true) {}
+ ~JSSprinter() {}
+
+ JSString* release(JSContext* cx) { return releaseJS(cx); }
+};
+
+// Fprinter, print a string directly into a file.
+class JS_PUBLIC_API Fprinter final : public GenericPrinter {
+ private:
+ FILE* file_;
+ bool init_;
+
+ public:
+ explicit Fprinter(FILE* fp);
+
+ constexpr Fprinter() : file_(nullptr), init_(false) {}
+
+#ifdef DEBUG
+ ~Fprinter();
+#endif
+
+ // Initialize this printer, returns false on error.
+ [[nodiscard]] bool init(const char* path);
+ void init(FILE* fp);
+ bool isInitialized() const { return file_ != nullptr; }
+ void flush() override;
+ void finish();
+
+ // Puts |len| characters from |s| at the current position. Errors may be
+ // detected with hadOutOfMemory() (which will be set for any fwrite() error,
+ // not just OOM.)
+ void put(const char* s, size_t len) override;
+ using GenericPrinter::put; // pick up |put(const char* s);|
+};
+
+// SEprinter, print using printf_stderr (goes to Android log, Windows debug,
+// else just stderr).
+class SEprinter final : public GenericPrinter {
+ public:
+ constexpr SEprinter() {}
+
+ // Puts |len| characters from |s| at the current position. Ignores errors.
+ virtual void put(const char* s, size_t len) override {
+ printf_stderr("%.*s", int(len), s);
+ }
+ using GenericPrinter::put; // pick up |put(const char* s);|
+};
+
+// LSprinter, is similar to Sprinter except that instead of using an
+// JSContext to allocate strings, it use a LifoAlloc as a backend for the
+// allocation of the chunk of the string.
+class JS_PUBLIC_API LSprinter final : public GenericPrinter {
+ private:
+ struct Chunk {
+ Chunk* next;
+ size_t length;
+
+ char* chars() { return reinterpret_cast<char*>(this + 1); }
+ char* end() { return chars() + length; }
+ };
+
+ private:
+ LifoAlloc* alloc_; // LifoAlloc used as a backend of chunk allocations.
+ Chunk* head_;
+ Chunk* tail_;
+ size_t unused_;
+
+ public:
+ explicit LSprinter(LifoAlloc* lifoAlloc);
+ ~LSprinter();
+
+ // Copy the content of the chunks into another printer, such that we can
+ // flush the content of this printer to a file.
+ void exportInto(GenericPrinter& out) const;
+
+ // Drop the current string, and let them be free with the LifoAlloc.
+ void clear();
+
+ // Puts |len| characters from |s| at the current position.
+ virtual void put(const char* s, size_t len) override;
+ using GenericPrinter::put; // pick up |put(const char* s);|
+};
+
+// Escaping printers work like any other printer except that any added character
+// are checked for escaping sequences. This one would escape a string such that
+// it can safely be embedded in a JS string.
+template <typename Delegate, typename Escape>
+class JS_PUBLIC_API EscapePrinter final : public GenericPrinter {
+ size_t lengthOfSafeChars(const char* s, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ if (!esc.isSafeChar(uint8_t(s[i]))) {
+ return i;
+ }
+ }
+ return len;
+ }
+
+ private:
+ Delegate& out;
+ Escape& esc;
+
+ public:
+ EscapePrinter(Delegate& out, Escape& esc) : out(out), esc(esc) {}
+ ~EscapePrinter() {}
+
+ using GenericPrinter::put;
+ void put(const char* s, size_t len) override {
+ const char* b = s;
+ while (len) {
+ size_t index = lengthOfSafeChars(b, len);
+ if (index) {
+ out.put(b, index);
+ len -= index;
+ b += index;
+ }
+ if (len) {
+ esc.convertInto(out, char16_t(uint8_t(*b)));
+ len -= 1;
+ b += 1;
+ }
+ }
+ }
+
+ inline void putChar(const char c) override {
+ if (esc.isSafeChar(char16_t(uint8_t(c)))) {
+ out.putChar(char(c));
+ return;
+ }
+ esc.convertInto(out, char16_t(uint8_t(c)));
+ }
+
+ inline void putChar(const JS::Latin1Char c) override {
+ if (esc.isSafeChar(char16_t(c))) {
+ out.putChar(char(c));
+ return;
+ }
+ esc.convertInto(out, char16_t(c));
+ }
+
+ inline void putChar(const char16_t c) override {
+ if (esc.isSafeChar(c)) {
+ out.putChar(char(c));
+ return;
+ }
+ esc.convertInto(out, c);
+ }
+
+ // Forward calls to delegated printer.
+ bool canPutFromIndex() const override { return out.canPutFromIndex(); }
+ void putFromIndex(size_t index, size_t length) final {
+ out.putFromIndex(index, length);
+ }
+ size_t index() const final { return out.index(); }
+ void flush() final { out.flush(); }
+ void reportOutOfMemory() final { out.reportOutOfMemory(); }
+ bool hadOutOfMemory() const final { return out.hadOutOfMemory(); }
+};
+
+class JS_PUBLIC_API JSONEscape {
+ public:
+ bool isSafeChar(char16_t c);
+ void convertInto(GenericPrinter& out, char16_t c);
+};
+
+class JS_PUBLIC_API StringEscape {
+ private:
+ const char quote = '\0';
+
+ public:
+ explicit StringEscape(const char quote = '\0') : quote(quote) {}
+
+ bool isSafeChar(char16_t c);
+ void convertInto(GenericPrinter& out, char16_t c);
+};
+
+// A GenericPrinter that formats everything at a nested indentation level.
+class JS_PUBLIC_API IndentedPrinter final : public GenericPrinter {
+ GenericPrinter& out_;
+ // The number of indents to insert at the beginning of each line.
+ uint32_t indentLevel_;
+ // The number of spaces to insert for each indent.
+ uint32_t indentAmount_;
+ // Whether we have seen a line ending and should insert an indent at the
+ // next line fragment.
+ bool pendingIndent_;
+
+ // Put an indent to `out_`
+ void putIndent();
+ // Put `s` to `out_`, inserting an indent if we need to
+ void putWithMaybeIndent(const char* s, size_t len);
+
+ public:
+ explicit IndentedPrinter(GenericPrinter& out, uint32_t indentLevel = 0,
+ uint32_t indentAmount = 2)
+ : out_(out),
+ indentLevel_(indentLevel),
+ indentAmount_(indentAmount),
+ pendingIndent_(false) {}
+
+ // Automatically insert and remove and indent for a scope
+ class AutoIndent {
+ IndentedPrinter& printer_;
+
+ public:
+ explicit AutoIndent(IndentedPrinter& printer) : printer_(printer) {
+ printer_.setIndentLevel(printer_.indentLevel() + 1);
+ }
+ ~AutoIndent() { printer_.setIndentLevel(printer_.indentLevel() - 1); }
+ };
+
+ uint32_t indentLevel() const { return indentLevel_; }
+ void setIndentLevel(uint32_t indentLevel) { indentLevel_ = indentLevel; }
+
+ virtual void put(const char* s, size_t len) override;
+ using GenericPrinter::put; // pick up |inline void put(const char* s);|
+};
+
+// Map escaped code to the letter/symbol escaped with a backslash.
+extern const char js_EscapeMap[];
+
+// Return a C-string containing the chars in str, with any non-printing chars
+// escaped. If the optional quote parameter is present and is not '\0', quotes
+// (as specified by the quote argument) are also escaped, and the quote
+// character is appended at the beginning and end of the result string.
+// The returned string is guaranteed to contain only ASCII characters.
+extern JS_PUBLIC_API JS::UniqueChars QuoteString(JSContext* cx, JSString* str,
+ char quote = '\0');
+
+// Appends the quoted string to the given Sprinter. Follows the same semantics
+// as QuoteString from above.
+extern JS_PUBLIC_API void QuoteString(Sprinter* sp, JSString* str,
+ char quote = '\0');
+
+// Appends the quoted string to the given Sprinter. Follows the same
+// Appends the JSON quoted string to the given Sprinter.
+extern JS_PUBLIC_API void JSONQuoteString(StringPrinter* sp, JSString* str);
+
+// Internal implementation code for QuoteString methods above.
+enum class QuoteTarget { String, JSON };
+
+template <QuoteTarget target, typename CharT>
+void JS_PUBLIC_API QuoteString(Sprinter* sp,
+ const mozilla::Range<const CharT>& chars,
+ char quote = '\0');
+
+} // namespace js
+
+#endif // js_Printer_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..5cf247abc1
--- /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_PUBLIC_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_PUBLIC_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..886ed806e9
--- /dev/null
+++ b/js/public/ProfilingFrameIterator.h
@@ -0,0 +1,280 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include "jstypes.h"
+
+#include "js/GCAnnotations.h"
+#include "js/TypeDecls.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_;
+ // For each JitActivation, this records the lowest (most recent) stack
+ // address. This will usually be either the exitFP of the activation or the
+ // frame or stack pointer of currently executing JIT/Wasm code. The Gecko
+ // profiler uses this to skip native frames between the activation and
+ // endStackAddress_.
+ void* endStackAddress_ = nullptr;
+ 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 maybeSetEndStackAddress(void* addr) {
+ // If endStackAddress_ has already been set, don't change it because we
+ // want this to correspond to the most recent frame.
+ if (!endStackAddress_) {
+ endStackAddress_ = addr;
+ }
+ }
+
+ void settleFrames();
+ void settle();
+
+ public:
+ struct RegisterState {
+ RegisterState()
+ : pc(nullptr),
+ sp(nullptr),
+ fp(nullptr),
+ unused1(nullptr),
+ unused2(nullptr) {}
+ void* pc;
+ void* sp;
+ void* fp;
+ union {
+ // Value of the LR register on ARM platforms.
+ void* lr;
+ // The return address during a tail call operation.
+ // Note that for ARM is still the value of LR register.
+ void* tempRA;
+ // Undefined on non-ARM plaforms outside tail calls operations.
+ void* unused1;
+ };
+ union {
+ // The FP reference during a tail call operation.
+ void* tempFP;
+ // Undefined outside tail calls operations.
+ void* unused2;
+ };
+ };
+
+ 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;
+
+ // Return the registers from the native caller frame.
+ // Nothing{} if this iterator is NOT pointing at a native-to-JIT entry frame,
+ // or if the information is not accessible/implemented on this platform.
+ mozilla::Maybe<RegisterState> getCppEntryRegisters() const;
+
+ private:
+ mozilla::Maybe<Frame> getPhysicalFrameAndEntry(
+ const js::jit::JitcodeGlobalEntry** entry) const;
+
+ void iteratorConstruct(const RegisterState& state);
+ void iteratorConstruct();
+ void iteratorDestroy();
+ bool iteratorDone();
+} JS_HAZ_GC_INVALIDATED;
+
+JS_PUBLIC_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_PUBLIC_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) const { return index_ == rhs.index_; }
+ bool operator!=(const Iter& rhs) const { 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..b9b96c27dc
--- /dev/null
+++ b/js/public/ProfilingStack.h
@@ -0,0 +1,578 @@
+/* -*- 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 "mozilla/Atomics.h"
+
+#include <stdint.h>
+
+#include "jstypes.h"
+
+#include "js/ProfilingCategory.h"
+#include "js/TypeDecls.h"
+
+class JS_PUBLIC_API JSTracer;
+class JS_PUBLIC_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);
+ }
+ }
+
+ void setLabelCategory(JS::ProfilingCategoryPair aCategoryPair) {
+ MOZ_ASSERT(isLabelFrame());
+ flagsAndCategoryPair_ =
+ (uint32_t(aCategoryPair) << uint32_t(Flags::FLAGS_BITCOUNT)) | flags();
+ }
+
+ 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;
+
+ JS_PUBLIC_API JSFunction* function() 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_PUBLIC_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_PUBLIC_API void SetContextProfilingStack(JSContext* cx,
+ ProfilingStack* profilingStack);
+
+// GetContextProfilingStack also exists, but it's defined in RootingAPI.h.
+
+JS_PUBLIC_API void EnableContextProfilingStack(JSContext* cx, bool enabled);
+
+JS_PUBLIC_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_PUBLIC_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_PUBLIC_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..221d6fdfee
--- /dev/null
+++ b/js/public/Promise.h
@@ -0,0 +1,599 @@
+/* -*- 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 "jstypes.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.
+ *
+ * `promise` can be null if the promise is optimized out.
+ * `promise` is guaranteed not to be optimized out if the promise has
+ * non-default user-interaction flag.
+ */
+ 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 bool SetSettledPromiseIsHandled(JSContext* cx,
+ JS::HandleObject promise);
+
+/*
+ * Given a promise (settled or not), sets |promise.[[PromiseIsHandled]]| to true
+ * and removes it from the list of unhandled rejected promises if it's settled.
+ */
+[[nodiscard]] extern JS_PUBLIC_API bool SetAnyPromiseIsHandled(
+ 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/PropertyAndElement.h b/js/public/PropertyAndElement.h
new file mode 100644
index 0000000000..48a7532fa4
--- /dev/null
+++ b/js/public/PropertyAndElement.h
@@ -0,0 +1,486 @@
+/* -*- 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 and element API. */
+
+#ifndef js_PropertyAndElement_h
+#define js_PropertyAndElement_h
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/CallArgs.h" // JSNative
+#include "js/GCVector.h" // JS::GCVector
+#include "js/Id.h" // jsid
+#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle
+
+struct JSContext;
+class JSFunction;
+class JSObject;
+class JSString;
+
+namespace JS {
+
+class ObjectOpResult;
+class JS_PUBLIC_API PropertyDescriptor;
+
+using IdVector = JS::GCVector<jsid>;
+
+} /* namespace JS */
+
+/**
+ * Define a property on obj.
+ *
+ * This function uses JS::ObjectOpResult to indicate conditions that ES6
+ * specifies as non-error failures. This is inconvenient at best, so use this
+ * function only if you are implementing a proxy handler's defineProperty()
+ * method. For all other purposes, use one of the many DefineProperty functions
+ * below that throw an exception in all failure cases.
+ *
+ * Implements: ES6 [[DefineOwnProperty]] internal method.
+ */
+extern JS_PUBLIC_API bool JS_DefinePropertyById(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result);
+
+/**
+ * Define a property on obj, throwing a TypeError if the attempt fails.
+ * This is the C++ equivalent of `Object.defineProperty(obj, id, desc)`.
+ */
+extern JS_PUBLIC_API bool JS_DefinePropertyById(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::Handle<JS::PropertyDescriptor> desc);
+
+extern JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ JS::Handle<JS::Value> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefinePropertyById(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JSNative getter, JSNative setter, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefinePropertyById(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::Handle<JSObject*> getter, JS::Handle<JSObject*> setter, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ JS::Handle<JSObject*> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ JS::Handle<JSString*> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ int32_t value, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ uint32_t value, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ double value, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name,
+ JS::Handle<JS::Value> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name, JSNative getter,
+ JSNative setter, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineProperty(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
+ JS::Handle<JSObject*> getter, JS::Handle<JSObject*> setter, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name,
+ JS::Handle<JSObject*> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name,
+ JS::Handle<JSString*> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name, int32_t value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name, uint32_t value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name, double value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen, JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen, JS::Handle<JS::PropertyDescriptor> desc);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen, JS::Handle<JS::Value> value, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen, JS::Handle<JSObject*> getter, JS::Handle<JSObject*> setter,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen, JS::Handle<JSObject*> value, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen, JS::Handle<JSString*> value, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char16_t* name,
+ size_t namelen, int32_t value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char16_t* name,
+ size_t namelen, uint32_t value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char16_t* name,
+ size_t namelen, double value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::Handle<JS::Value> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineElement(
+ JSContext* cx, JS::Handle<JSObject*> obj, uint32_t index,
+ JS::Handle<JSObject*> getter, JS::Handle<JSObject*> setter, unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::Handle<JSObject*> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::Handle<JSString*> value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index, int32_t value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index, uint32_t value,
+ unsigned attrs);
+
+extern JS_PUBLIC_API bool JS_DefineElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index, double value,
+ unsigned attrs);
+
+/**
+ * Compute the expression `id in obj`.
+ *
+ * If obj has an own or inherited property obj[id], set *foundp = true and
+ * return true. If not, set *foundp = false and return true. On error, return
+ * false with an exception pending.
+ *
+ * Implements: ES6 [[Has]] internal method.
+ */
+extern JS_PUBLIC_API bool JS_HasPropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id, bool* foundp);
+
+extern JS_PUBLIC_API bool JS_HasProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name, bool* foundp);
+
+extern JS_PUBLIC_API bool JS_HasUCProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char16_t* name, size_t namelen,
+ bool* vp);
+
+extern JS_PUBLIC_API bool JS_HasElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index, bool* foundp);
+
+/**
+ * Determine whether obj has an own property with the key `id`.
+ *
+ * Implements: ES6 7.3.11 HasOwnProperty(O, P).
+ */
+extern JS_PUBLIC_API bool JS_HasOwnPropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ bool* foundp);
+
+extern JS_PUBLIC_API bool JS_HasOwnProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name, bool* foundp);
+
+/**
+ * Get the value of the property `obj[id]`, or undefined if no such property
+ * exists. This is the C++ equivalent of `vp = Reflect.get(obj, id, receiver)`.
+ *
+ * Most callers don't need the `receiver` argument. Consider using
+ * JS_GetProperty instead. (But if you're implementing a proxy handler's set()
+ * method, it's often correct to call this function and pass the receiver
+ * through.)
+ *
+ * Implements: ES6 [[Get]] internal method.
+ */
+extern JS_PUBLIC_API bool JS_ForwardGetPropertyTo(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::Handle<JS::Value> receiver, JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API bool JS_ForwardGetElementTo(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::Handle<JSObject*> receiver,
+ JS::MutableHandleValue vp);
+
+/**
+ * Get the value of the property `obj[id]`, or undefined if no such property
+ * exists. The result is stored in vp.
+ *
+ * Implements: ES6 7.3.1 Get(O, P).
+ */
+extern JS_PUBLIC_API bool JS_GetPropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API bool JS_GetProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name,
+ JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API bool JS_GetUCProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char16_t* name, size_t namelen,
+ JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API bool JS_GetElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::MutableHandleValue vp);
+
+/**
+ * Perform the same property assignment as `Reflect.set(obj, id, v, receiver)`.
+ *
+ * This function has a `receiver` argument that most callers don't need.
+ * Consider using JS_SetProperty instead.
+ *
+ * Implements: ES6 [[Set]] internal method.
+ */
+extern JS_PUBLIC_API bool JS_ForwardSetPropertyTo(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
+ JS::ObjectOpResult& result);
+
+/**
+ * Perform the assignment `obj[id] = v`.
+ *
+ * This function performs non-strict assignment, so if the property is
+ * read-only, nothing happens and no error is thrown.
+ */
+extern JS_PUBLIC_API bool JS_SetPropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ JS::Handle<JS::Value> v);
+
+extern JS_PUBLIC_API bool JS_SetProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name,
+ JS::Handle<JS::Value> v);
+
+extern JS_PUBLIC_API bool JS_SetUCProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char16_t* name, size_t namelen,
+ JS::Handle<JS::Value> v);
+
+extern JS_PUBLIC_API bool JS_SetElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::Handle<JS::Value> v);
+
+extern JS_PUBLIC_API bool JS_SetElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::Handle<JSObject*> v);
+
+extern JS_PUBLIC_API bool JS_SetElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::Handle<JSString*> v);
+
+extern JS_PUBLIC_API bool JS_SetElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index, int32_t v);
+
+extern JS_PUBLIC_API bool JS_SetElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index, uint32_t v);
+
+extern JS_PUBLIC_API bool JS_SetElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index, double v);
+
+/**
+ * Delete a property. This is the C++ equivalent of
+ * `result = Reflect.deleteProperty(obj, id)`.
+ *
+ * This function has a `result` out parameter that most callers don't need.
+ * Unless you can pass through an ObjectOpResult provided by your caller, it's
+ * probably best to use the JS_DeletePropertyById signature with just 3
+ * arguments.
+ *
+ * Implements: ES6 [[Delete]] internal method.
+ */
+extern JS_PUBLIC_API bool JS_DeletePropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ JS::ObjectOpResult& result);
+
+extern JS_PUBLIC_API bool JS_DeleteProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name,
+ JS::ObjectOpResult& result);
+
+extern JS_PUBLIC_API bool JS_DeleteUCProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char16_t* name,
+ size_t namelen,
+ JS::ObjectOpResult& result);
+
+extern JS_PUBLIC_API bool JS_DeleteElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index,
+ JS::ObjectOpResult& result);
+
+/**
+ * Delete a property, ignoring strict failures. This is the C++ equivalent of
+ * the JS `delete obj[id]` in non-strict mode code.
+ */
+extern JS_PUBLIC_API bool JS_DeletePropertyById(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id);
+
+extern JS_PUBLIC_API bool JS_DeleteProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name);
+
+extern JS_PUBLIC_API bool JS_DeleteElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index);
+
+/**
+ * Get an array of the non-symbol enumerable properties of obj.
+ * This function is roughly equivalent to:
+ *
+ * var result = [];
+ * for (key in obj) {
+ * result.push(key);
+ * }
+ * return result;
+ *
+ * This is the closest thing we currently have to the ES6 [[Enumerate]]
+ * internal method.
+ *
+ * The array of ids returned by JS_Enumerate must be rooted to protect its
+ * contents from garbage collection. Use JS::Rooted<JS::IdVector>.
+ */
+extern JS_PUBLIC_API bool JS_Enumerate(JSContext* cx, JS::Handle<JSObject*> obj,
+ JS::MutableHandle<JS::IdVector> props);
+
+/*** Other property-defining functions **************************************/
+
+extern JS_PUBLIC_API JSObject* JS_DefineObject(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name,
+ const JSClass* clasp = nullptr,
+ unsigned attrs = 0);
+
+extern JS_PUBLIC_API bool JS_DefineProperties(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const JSPropertySpec* ps);
+
+/* * */
+
+extern JS_PUBLIC_API bool JS_AlreadyHasOwnPropertyById(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ bool* foundp);
+
+extern JS_PUBLIC_API bool JS_AlreadyHasOwnProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char* name,
+ bool* foundp);
+
+extern JS_PUBLIC_API bool JS_AlreadyHasOwnUCProperty(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const char16_t* name,
+ size_t namelen,
+ bool* foundp);
+
+extern JS_PUBLIC_API bool JS_AlreadyHasOwnElement(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ uint32_t index, bool* foundp);
+
+extern JS_PUBLIC_API bool JS_DefineFunctions(JSContext* cx,
+ JS::Handle<JSObject*> obj,
+ const JSFunctionSpec* fs);
+
+extern JS_PUBLIC_API JSFunction* JS_DefineFunction(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char* name, JSNative call,
+ unsigned nargs, unsigned attrs);
+
+extern JS_PUBLIC_API JSFunction* JS_DefineUCFunction(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen, JSNative call, unsigned nargs, unsigned attrs);
+
+extern JS_PUBLIC_API JSFunction* JS_DefineFunctionById(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JSNative call, unsigned nargs, unsigned attrs);
+
+#endif /* js_PropertyAndElement_h */
diff --git a/js/public/PropertyDescriptor.h b/js/public/PropertyDescriptor.h
new file mode 100644
index 0000000000..d298aa4274
--- /dev/null
+++ b/js/public/PropertyDescriptor.h
@@ -0,0 +1,503 @@
+/* -*- 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 "mozilla/EnumSet.h" // mozilla::EnumSet
+#include "mozilla/Maybe.h" // mozilla::Maybe
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint8_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Id.h" // jsid
+#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 for data properties.
+ */
+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;
+
+/**
+ * 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 = 0x08;
+
+/* (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_RESOLVING;
+
+namespace JS {
+
+// 6.1.7.1 Property Attributes
+enum class PropertyAttribute : uint8_t {
+ // The descriptor is [[Configurable]] := true.
+ Configurable,
+
+ // The descriptor is [[Enumerable]] := true.
+ Enumerable,
+
+ // The descriptor is [[Writable]] := true. Only valid for data descriptors.
+ Writable
+};
+
+class PropertyAttributes : public mozilla::EnumSet<PropertyAttribute> {
+ // Re-use all EnumSet constructors.
+ using mozilla::EnumSet<PropertyAttribute>::EnumSet;
+
+ public:
+ bool configurable() const {
+ return contains(PropertyAttribute::Configurable);
+ }
+ bool enumerable() const { return contains(PropertyAttribute::Enumerable); }
+ bool writable() const { return contains(PropertyAttribute::Writable); }
+};
+
+/**
+ * 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.
+ */
+class JS_PUBLIC_API PropertyDescriptor {
+ private:
+ bool hasConfigurable_ : 1;
+ bool configurable_ : 1;
+
+ bool hasEnumerable_ : 1;
+ bool enumerable_ : 1;
+
+ bool hasWritable_ : 1;
+ bool writable_ : 1;
+
+ bool hasValue_ : 1;
+ bool hasGetter_ : 1;
+ bool hasSetter_ : 1;
+
+ bool resolving_ : 1;
+
+ JSObject* getter_;
+ JSObject* setter_;
+ Value value_;
+
+ public:
+ PropertyDescriptor()
+ : hasConfigurable_(false),
+ configurable_(false),
+ hasEnumerable_(false),
+ enumerable_(false),
+ hasWritable_(false),
+ writable_(false),
+ hasValue_(false),
+ hasGetter_(false),
+ hasSetter_(false),
+ resolving_(false),
+ getter_(nullptr),
+ setter_(nullptr),
+ value_(UndefinedValue()) {}
+
+ void trace(JSTracer* trc);
+
+ // Construct a new complete DataDescriptor.
+ static PropertyDescriptor Data(const Value& value,
+ PropertyAttributes attributes = {}) {
+ PropertyDescriptor desc;
+ desc.setConfigurable(attributes.configurable());
+ desc.setEnumerable(attributes.enumerable());
+ desc.setWritable(attributes.writable());
+ desc.setValue(value);
+ desc.assertComplete();
+ return desc;
+ }
+
+ // This constructor is only provided for legacy code!
+ static PropertyDescriptor Data(const Value& value, unsigned attrs) {
+ MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
+ JSPROP_READONLY | JSPROP_RESOLVING)) == 0);
+
+ PropertyDescriptor desc;
+ desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
+ desc.setEnumerable(attrs & JSPROP_ENUMERATE);
+ desc.setWritable(!(attrs & JSPROP_READONLY));
+ desc.setValue(value);
+ desc.setResolving(attrs & JSPROP_RESOLVING);
+ desc.assertComplete();
+ return desc;
+ }
+
+ // Construct a new complete AccessorDescriptor.
+ // Note: This means JSPROP_GETTER and JSPROP_SETTER are always set.
+ static PropertyDescriptor Accessor(JSObject* getter, JSObject* setter,
+ PropertyAttributes attributes = {}) {
+ MOZ_ASSERT(!attributes.writable());
+
+ PropertyDescriptor desc;
+ desc.setConfigurable(attributes.configurable());
+ desc.setEnumerable(attributes.enumerable());
+ desc.setGetter(getter);
+ desc.setSetter(setter);
+ desc.assertComplete();
+ return desc;
+ }
+
+ // This constructor is only provided for legacy code!
+ static PropertyDescriptor Accessor(JSObject* getter, JSObject* setter,
+ unsigned attrs) {
+ MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
+ JSPROP_RESOLVING)) == 0);
+
+ PropertyDescriptor desc;
+ desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
+ desc.setEnumerable(attrs & JSPROP_ENUMERATE);
+ desc.setGetter(getter);
+ desc.setSetter(setter);
+ desc.setResolving(attrs & JSPROP_RESOLVING);
+ desc.assertComplete();
+ return desc;
+ }
+
+ static PropertyDescriptor Accessor(mozilla::Maybe<JSObject*> getter,
+ mozilla::Maybe<JSObject*> setter,
+ unsigned attrs) {
+ MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
+ JSPROP_RESOLVING)) == 0);
+
+ PropertyDescriptor desc;
+ desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
+ desc.setEnumerable(attrs & JSPROP_ENUMERATE);
+ if (getter) {
+ desc.setGetter(*getter);
+ }
+ if (setter) {
+ desc.setSetter(*setter);
+ }
+ desc.setResolving(attrs & JSPROP_RESOLVING);
+ desc.assertValid();
+ return desc;
+ }
+
+ // Construct a new incomplete empty PropertyDescriptor.
+ // Using the spec syntax this would be { }. Specific fields like [[Value]]
+ // can be added with e.g., setValue.
+ static PropertyDescriptor Empty() {
+ PropertyDescriptor desc;
+ desc.assertValid();
+ MOZ_ASSERT(!desc.hasConfigurable() && !desc.hasEnumerable() &&
+ !desc.hasWritable() && !desc.hasValue() && !desc.hasGetter() &&
+ !desc.hasSetter());
+ return desc;
+ }
+
+ public:
+ bool isAccessorDescriptor() const {
+ MOZ_ASSERT_IF(hasGetter_ || hasSetter_, !isDataDescriptor());
+ return hasGetter_ || hasSetter_;
+ }
+ bool isGenericDescriptor() const {
+ return !isAccessorDescriptor() && !isDataDescriptor();
+ }
+ bool isDataDescriptor() const {
+ MOZ_ASSERT_IF(hasWritable_ || hasValue_, !isAccessorDescriptor());
+ return hasWritable_ || hasValue_;
+ }
+
+ bool hasConfigurable() const { return hasConfigurable_; }
+ bool configurable() const {
+ MOZ_ASSERT(hasConfigurable());
+ return configurable_;
+ }
+ void setConfigurable(bool configurable) {
+ hasConfigurable_ = true;
+ configurable_ = configurable;
+ }
+
+ bool hasEnumerable() const { return hasEnumerable_; }
+ bool enumerable() const {
+ MOZ_ASSERT(hasEnumerable());
+ return enumerable_;
+ }
+ void setEnumerable(bool enumerable) {
+ hasEnumerable_ = true;
+ enumerable_ = enumerable;
+ }
+
+ bool hasValue() const { return hasValue_; }
+ Value value() const {
+ MOZ_ASSERT(hasValue());
+ return value_;
+ }
+ void setValue(const Value& v) {
+ MOZ_ASSERT(!isAccessorDescriptor());
+ hasValue_ = true;
+ value_ = v;
+ }
+
+ bool hasWritable() const { return hasWritable_; }
+ bool writable() const {
+ MOZ_ASSERT(hasWritable());
+ return writable_;
+ }
+ void setWritable(bool writable) {
+ MOZ_ASSERT(!isAccessorDescriptor());
+ hasWritable_ = true;
+ writable_ = writable;
+ }
+
+ bool hasGetter() const { return hasGetter_; }
+ JSObject* getter() const {
+ MOZ_ASSERT(hasGetter());
+ return getter_;
+ }
+ void setGetter(JSObject* obj) {
+ MOZ_ASSERT(!isDataDescriptor());
+ hasGetter_ = true;
+ getter_ = obj;
+ }
+
+ bool hasSetter() const { return hasSetter_; }
+ JSObject* setter() const {
+ MOZ_ASSERT(hasSetter());
+ return setter_;
+ }
+ void setSetter(JSObject* obj) {
+ MOZ_ASSERT(!isDataDescriptor());
+ hasSetter_ = true;
+ setter_ = obj;
+ }
+
+ // Non-standard flag, which is set when defining properties in a resolve hook.
+ bool resolving() const { return resolving_; }
+ void setResolving(bool resolving) { resolving_ = resolving; }
+
+ Value* valueDoNotUse() { return &value_; }
+ Value const* valueDoNotUse() const { return &value_; }
+ JSObject** getterDoNotUse() { return &getter_; }
+ JSObject* const* getterDoNotUse() const { return &getter_; }
+ void setGetterDoNotUse(JSObject* obj) { getter_ = obj; }
+ JSObject** setterDoNotUse() { return &setter_; }
+ JSObject* const* setterDoNotUse() const { return &setter_; }
+ void setSetterDoNotUse(JSObject* obj) { setter_ = obj; }
+
+ void assertValid() const {
+#ifdef DEBUG
+ if (isAccessorDescriptor()) {
+ MOZ_ASSERT(!hasWritable_);
+ MOZ_ASSERT(!hasValue_);
+ } else {
+ MOZ_ASSERT(isGenericDescriptor() || isDataDescriptor());
+ MOZ_ASSERT(!hasGetter_);
+ MOZ_ASSERT(!hasSetter_);
+ }
+
+ MOZ_ASSERT_IF(!hasConfigurable_, !configurable_);
+ MOZ_ASSERT_IF(!hasEnumerable_, !enumerable_);
+ MOZ_ASSERT_IF(!hasWritable_, !writable_);
+ MOZ_ASSERT_IF(!hasValue_, value_.isUndefined());
+ MOZ_ASSERT_IF(!hasGetter_, !getter_);
+ MOZ_ASSERT_IF(!hasSetter_, !setter_);
+
+ MOZ_ASSERT_IF(resolving_, !isGenericDescriptor());
+#endif
+ }
+
+ void assertComplete() const {
+#ifdef DEBUG
+ assertValid();
+ MOZ_ASSERT(hasConfigurable());
+ MOZ_ASSERT(hasEnumerable());
+ MOZ_ASSERT(!isGenericDescriptor());
+ MOZ_ASSERT_IF(isDataDescriptor(), hasValue() && hasWritable());
+ MOZ_ASSERT_IF(isAccessorDescriptor(), hasGetter() && hasSetter());
+#endif
+ }
+};
+
+} // namespace JS
+
+namespace js {
+
+template <typename Wrapper>
+class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
+ const JS::PropertyDescriptor& desc() const {
+ return static_cast<const Wrapper*>(this)->get();
+ }
+
+ public:
+ bool isAccessorDescriptor() const { return desc().isAccessorDescriptor(); }
+ bool isGenericDescriptor() const { return desc().isGenericDescriptor(); }
+ bool isDataDescriptor() const { return desc().isDataDescriptor(); }
+
+ bool hasConfigurable() const { return desc().hasConfigurable(); }
+ bool configurable() const { return desc().configurable(); }
+
+ bool hasEnumerable() const { return desc().hasEnumerable(); }
+ bool enumerable() const { return desc().enumerable(); }
+
+ bool hasValue() const { return desc().hasValue(); }
+ JS::Handle<JS::Value> value() const {
+ MOZ_ASSERT(hasValue());
+ return JS::Handle<JS::Value>::fromMarkedLocation(desc().valueDoNotUse());
+ }
+
+ bool hasWritable() const { return desc().hasWritable(); }
+ bool writable() const { return desc().writable(); }
+
+ bool hasGetter() const { return desc().hasGetter(); }
+ JS::Handle<JSObject*> getter() const {
+ MOZ_ASSERT(hasGetter());
+ return JS::Handle<JSObject*>::fromMarkedLocation(desc().getterDoNotUse());
+ }
+ bool hasSetter() const { return desc().hasSetter(); }
+ JS::Handle<JSObject*> setter() const {
+ MOZ_ASSERT(hasSetter());
+ return JS::Handle<JSObject*>::fromMarkedLocation(desc().setterDoNotUse());
+ }
+
+ bool resolving() const { return desc().resolving(); }
+
+ void assertValid() const { desc().assertValid(); }
+ void assertComplete() const { desc().assertComplete(); }
+};
+
+template <typename Wrapper>
+class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
+ : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
+ JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+ JS::MutableHandle<JS::Value> value() {
+ MOZ_ASSERT(desc().hasValue());
+ return JS::MutableHandle<JS::Value>::fromMarkedLocation(
+ desc().valueDoNotUse());
+ }
+ void setValue(JS::Handle<JS::Value> v) { desc().setValue(v); }
+
+ void setConfigurable(bool configurable) {
+ desc().setConfigurable(configurable);
+ }
+ void setEnumerable(bool enumerable) { desc().setEnumerable(enumerable); }
+ void setWritable(bool writable) { desc().setWritable(writable); }
+
+ void setGetter(JSObject* obj) { desc().setGetter(obj); }
+ void setSetter(JSObject* obj) { desc().setSetter(obj); }
+
+ JS::MutableHandle<JSObject*> getter() {
+ MOZ_ASSERT(desc().hasGetter());
+ return JS::MutableHandle<JSObject*>::fromMarkedLocation(
+ desc().getterDoNotUse());
+ }
+ JS::MutableHandle<JSObject*> setter() {
+ MOZ_ASSERT(desc().hasSetter());
+ return JS::MutableHandle<JSObject*>::fromMarkedLocation(
+ desc().setterDoNotUse());
+ }
+
+ void setResolving(bool resolving) { desc().setResolving(resolving); }
+};
+
+} // namespace js
+
+/**
+ * Get a description of one of obj's own properties. If no such property exists
+ * on obj, return true with desc.object() set to null.
+ *
+ * Implements: ES6 [[GetOwnProperty]] internal method.
+ */
+extern JS_PUBLIC_API bool JS_GetOwnPropertyDescriptorById(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
+
+extern JS_PUBLIC_API bool JS_GetOwnPropertyDescriptor(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
+ JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
+
+extern JS_PUBLIC_API bool JS_GetOwnUCPropertyDescriptor(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen,
+ JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
+
+/**
+ * DEPRECATED
+ *
+ * Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain
+ * if no own property is found directly on obj. The object on which the
+ * property is found is returned in holder. If the property is not found
+ * on the prototype chain, then desc is Nothing.
+ */
+extern JS_PUBLIC_API bool JS_GetPropertyDescriptorById(
+ JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
+ JS::MutableHandle<JSObject*> holder);
+
+extern JS_PUBLIC_API bool JS_GetPropertyDescriptor(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
+ JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
+ JS::MutableHandle<JSObject*> holder);
+
+extern JS_PUBLIC_API bool JS_GetUCPropertyDescriptor(
+ JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
+ size_t namelen,
+ JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
+ JS::MutableHandle<JSObject*> holder);
+
+namespace JS {
+
+// https://tc39.es/ecma262/#sec-topropertydescriptor
+// https://tc39.es/ecma262/#sec-completepropertydescriptor
+//
+// Implements ToPropertyDescriptor combined with CompletePropertyDescriptor,
+// if the former is successful.
+extern JS_PUBLIC_API bool ToCompletePropertyDescriptor(
+ JSContext* cx, Handle<Value> descriptor,
+ MutableHandle<PropertyDescriptor> desc);
+
+/*
+ * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
+ *
+ * If desc.isNothing(), then vp is set to undefined.
+ */
+extern JS_PUBLIC_API bool FromPropertyDescriptor(
+ JSContext* cx, Handle<mozilla::Maybe<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..9d8115508d
--- /dev/null
+++ b/js/public/PropertySpec.h
@@ -0,0 +1,447 @@
+/* -*- 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 {
+ enum class Type : uint8_t { String, Int32, Double };
+ Type type;
+ union {
+ const char* string;
+ int32_t int32;
+ double double_;
+ };
+
+ private:
+ ValueWrapper() = delete;
+
+ explicit constexpr ValueWrapper(int32_t n) : type(Type::Int32), int32(n) {}
+
+ explicit constexpr ValueWrapper(const char* s)
+ : type(Type::String), string(s) {}
+
+ explicit constexpr ValueWrapper(double d)
+ : type(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:
+ // JSPROP_* property attributes as defined in PropertyDescriptor.h.
+ uint8_t attributes_;
+
+ // Whether AccessorsOrValue below stores a value, JSNative accessors, or
+ // self-hosted accessors.
+ enum class Kind : uint8_t { Value, SelfHostedAccessor, NativeAccessor };
+ Kind kind_;
+
+ public:
+ AccessorsOrValue u;
+
+ private:
+ JSPropertySpec() = delete;
+
+ constexpr JSPropertySpec(const char* name, uint8_t attributes, Kind kind,
+ AccessorsOrValue u)
+ : name(name), attributes_(attributes), kind_(kind), u(u) {}
+ constexpr JSPropertySpec(JS::SymbolCode name, uint8_t attributes, Kind kind,
+ AccessorsOrValue u)
+ : name(name), attributes_(attributes), kind_(kind), u(u) {}
+
+ public:
+ JSPropertySpec(const JSPropertySpec& other) = default;
+
+ static constexpr JSPropertySpec nativeAccessors(
+ const char* name, uint8_t attributes, JSNative getter,
+ const JSJitInfo* getterInfo, JSNative setter = nullptr,
+ const JSJitInfo* setterInfo = nullptr) {
+ return JSPropertySpec(
+ name, attributes, Kind::NativeAccessor,
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo),
+ JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo)));
+ }
+
+ static constexpr JSPropertySpec nativeAccessors(
+ JS::SymbolCode name, uint8_t attributes, JSNative getter,
+ const JSJitInfo* getterInfo, JSNative setter = nullptr,
+ const JSJitInfo* setterInfo = nullptr) {
+ return JSPropertySpec(
+ name, attributes, Kind::NativeAccessor,
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo),
+ JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo)));
+ }
+
+ static constexpr JSPropertySpec selfHostedAccessors(
+ const char* name, uint8_t attributes, const char* getterName,
+ const char* setterName = nullptr) {
+ return JSPropertySpec(
+ name, attributes, Kind::SelfHostedAccessor,
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::selfHostedAccessor(getterName),
+ setterName
+ ? JSPropertySpec::Accessor::selfHostedAccessor(setterName)
+ : JSPropertySpec::Accessor::noAccessor()));
+ }
+
+ static constexpr JSPropertySpec selfHostedAccessors(
+ JS::SymbolCode name, uint8_t attributes, const char* getterName,
+ const char* setterName = nullptr) {
+ return JSPropertySpec(
+ name, attributes, Kind::SelfHostedAccessor,
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::selfHostedAccessor(getterName),
+ setterName
+ ? JSPropertySpec::Accessor::selfHostedAccessor(setterName)
+ : JSPropertySpec::Accessor::noAccessor()));
+ }
+
+ static constexpr JSPropertySpec int32Value(const char* name,
+ uint8_t attributes, int32_t n) {
+ return JSPropertySpec(name, attributes, Kind::Value,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::int32Value(n)));
+ }
+
+ static constexpr JSPropertySpec int32Value(JS::SymbolCode name,
+ uint8_t attributes, int32_t n) {
+ return JSPropertySpec(name, attributes, Kind::Value,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::int32Value(n)));
+ }
+
+ static constexpr JSPropertySpec stringValue(const char* name,
+ uint8_t attributes,
+ const char* s) {
+ return JSPropertySpec(name, attributes, Kind::Value,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::stringValue(s)));
+ }
+
+ static constexpr JSPropertySpec stringValue(JS::SymbolCode name,
+ uint8_t attributes,
+ const char* s) {
+ return JSPropertySpec(name, attributes, Kind::Value,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::stringValue(s)));
+ }
+
+ static constexpr JSPropertySpec doubleValue(const char* name,
+ uint8_t attributes, double d) {
+ return JSPropertySpec(name, attributes, Kind::Value,
+ AccessorsOrValue::fromValue(
+ JSPropertySpec::ValueWrapper::doubleValue(d)));
+ }
+
+ static constexpr JSPropertySpec sentinel() {
+ return JSPropertySpec(nullptr, 0, Kind::NativeAccessor,
+ AccessorsOrValue::fromAccessors(
+ JSPropertySpec::Accessor::noAccessor(),
+ JSPropertySpec::Accessor::noAccessor()));
+ }
+
+ unsigned attributes() const { return attributes_; }
+
+ bool isAccessor() const {
+ return (kind_ == Kind::NativeAccessor || kind_ == Kind::SelfHostedAccessor);
+ }
+
+ 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 Kind.
+ if (kind_ == Kind::SelfHostedAccessor) {
+ checkAccessorsAreSelfHosted();
+ } else {
+ checkAccessorsAreNative();
+ }
+#endif
+ return kind_ == Kind::SelfHostedAccessor;
+ }
+
+ 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);
+ }
+};
+
+// There can be many JSPropertySpec instances so verify the size is what we
+// expect:
+//
+// - Name (1 word)
+// - attributes_ + isAccessor_ (1 word)
+// - AccessorsOrValue (4 words, native + JSJitInfo for both getter and setter)
+static_assert(sizeof(JSPropertySpec) == 6 * sizeof(uintptr_t));
+
+template <unsigned Attributes>
+constexpr uint8_t CheckAccessorAttrs() {
+ static_assert((Attributes & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)) == 0,
+ "Unexpected flag (not JSPROP_ENUMERATE or JSPROP_PERMANENT)");
+ return uint8_t(Attributes);
+}
+
+#define JS_PSG(name, getter, attributes) \
+ JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \
+ getter, nullptr)
+#define JS_PSGS(name, getter, setter, attributes) \
+ JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \
+ getter, nullptr, setter, nullptr)
+#define JS_SYM_GET(symbol, getter, attributes) \
+ JSPropertySpec::nativeAccessors(::JS::SymbolCode::symbol, \
+ CheckAccessorAttrs<attributes>(), getter, \
+ nullptr)
+#define JS_SELF_HOSTED_GET(name, getterName, attributes) \
+ JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs<attributes>(), \
+ getterName)
+#define JS_SELF_HOSTED_GETSET(name, getterName, setterName, attributes) \
+ JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs<attributes>(), \
+ getterName, setterName)
+#define JS_SELF_HOSTED_SYM_GET(symbol, getterName, attributes) \
+ JSPropertySpec::selfHostedAccessors( \
+ ::JS::SymbolCode::symbol, CheckAccessorAttrs<attributes>(), getterName)
+#define JS_STRING_PS(name, string, attributes) \
+ JSPropertySpec::stringValue(name, attributes, string)
+#define JS_STRING_SYM_PS(symbol, string, attributes) \
+ JSPropertySpec::stringValue(::JS::SymbolCode::symbol, attributes, string)
+#define JS_INT32_PS(name, value, attributes) \
+ JSPropertySpec::int32Value(name, attributes, value)
+#define JS_DOUBLE_PS(name, value, attributes) \
+ JSPropertySpec::doubleValue(name, attributes, 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..31dfae927c
--- /dev/null
+++ b/js/public/ProtoKey.h
@@ -0,0 +1,158 @@
+/* -*- 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::fixedLengthClasses[JS::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
+
+#ifdef JS_HAS_TEMPORAL_API
+# define IF_TEMPORAL(REAL, IMAGINARY) REAL
+#else
+# define IF_TEMPORAL(REAL, IMAGINARY) IMAGINARY
+#endif
+
+#ifdef ENABLE_WASM_TYPE_REFLECTIONS
+# define IF_WASM_TYPE(REAL, IMAGINARY) REAL
+#else
+# define IF_WASM_TYPE(REAL, IMAGINARY) IMAGINARY
+#endif
+
+#define JS_FOR_PROTOTYPES_(REAL, IMAGINARY, REAL_IF_INTL, REAL_IF_TEMPORAL, \
+ REAL_IF_WASM_TYPE) \
+ IMAGINARY(Null, dummy) \
+ REAL(Object, OCLASP(Plain)) \
+ REAL(Function, &FunctionClass) \
+ IMAGINARY(BoundFunction, OCLASP(BoundFunction)) \
+ 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(FixedLengthArrayBuffer)) \
+ 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(FixedLengthDataView)) \
+ REAL(Symbol, OCLASP(Symbol)) \
+ REAL(ShadowRealm, OCLASP(ShadowRealm)) \
+ REAL(SharedArrayBuffer, OCLASP(FixedLengthSharedArrayBuffer)) \
+ 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_IF_INTL(Segmenter, OCLASP(Segmenter)) \
+ 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(WebAssembly, OCLASP(WasmNamespace)) \
+ REAL(WasmModule, OCLASP(WasmModule)) \
+ REAL(WasmInstance, OCLASP(WasmInstance)) \
+ REAL(WasmMemory, OCLASP(WasmMemory)) \
+ REAL(WasmTable, OCLASP(WasmTable)) \
+ REAL(WasmGlobal, OCLASP(WasmGlobal)) \
+ REAL(WasmTag, OCLASP(WasmTag)) \
+ REAL_IF_WASM_TYPE(WasmFunction, CLASP(WasmFunction)) \
+ REAL(WasmException, OCLASP(WasmException)) \
+ REAL(FinalizationRegistry, OCLASP(FinalizationRegistry)) \
+ REAL(WeakRef, OCLASP(WeakRef)) \
+ REAL(Iterator, OCLASP(Iterator)) \
+ REAL(AsyncIterator, OCLASP(AsyncIterator)) \
+ REAL_IF_TEMPORAL(Temporal, OCLASP(temporal::Temporal)) \
+ REAL_IF_TEMPORAL(Calendar, OCLASP(temporal::Calendar)) \
+ REAL_IF_TEMPORAL(Duration, OCLASP(temporal::Duration)) \
+ REAL_IF_TEMPORAL(Instant, OCLASP(temporal::Instant)) \
+ REAL_IF_TEMPORAL(PlainDate, OCLASP(temporal::PlainDate)) \
+ REAL_IF_TEMPORAL(PlainDateTime, OCLASP(temporal::PlainDateTime)) \
+ REAL_IF_TEMPORAL(PlainMonthDay, OCLASP(temporal::PlainMonthDay)) \
+ REAL_IF_TEMPORAL(PlainYearMonth, OCLASP(temporal::PlainYearMonth)) \
+ REAL_IF_TEMPORAL(PlainTime, OCLASP(temporal::PlainTime)) \
+ REAL_IF_TEMPORAL(TemporalNow, OCLASP(temporal::TemporalNow)) \
+ REAL_IF_TEMPORAL(TimeZone, OCLASP(temporal::TimeZone)) \
+ REAL_IF_TEMPORAL(ZonedDateTime, OCLASP(temporal::ZonedDateTime)) \
+ IF_RECORD_TUPLE(REAL(Record, (&RecordType::class_))) \
+ IF_RECORD_TUPLE(REAL(Tuple, (&TupleType::class_)))
+
+#define JS_FOR_PROTOTYPES(REAL, IMAGINARY) \
+ JS_FOR_PROTOTYPES_(REAL, IMAGINARY, IF_INTL(REAL, IMAGINARY), \
+ IF_TEMPORAL(REAL, IMAGINARY), \
+ IF_WASM_TYPE(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..45ba4a1376
--- /dev/null
+++ b/js/public/Proxy.h
@@ -0,0 +1,768 @@
+/* -*- 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 "jstypes.h" // for JS_PUBLIC_API, JS_PUBLIC_DATA
+
+#include "js/Array.h" // JS::IsArrayAnswer
+#include "js/CallNonGenericMethod.h"
+#include "js/Class.h"
+#include "js/HeapAPI.h" // for ObjectIsMarkedBlack
+#include "js/Id.h" // for jsid
+#include "js/Object.h" // JS::GetClass
+#include "js/RootingAPI.h" // for Handle, MutableHandle (ptr only)
+#include "js/shadow/Object.h" // JS::shadow::Object
+#include "js/TypeDecls.h" // for HandleObject, HandleId, HandleValue, MutableHandleIdVector, MutableHandleValue, MutableHand...
+#include "js/Value.h" // for Value, AssertValueIsNotGray, UndefinedValue, ObjectOrNullValue
+
+namespace js {
+
+class RegExpShared;
+
+class JS_PUBLIC_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_PUBLIC_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 JS::PropertyKey::isVoid. 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<mozilla::Maybe<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; }
+
+ // For some exotic objects (WindowProxy, Location), we want to be able to
+ // throw rather than allow private fields on these objects.
+ //
+ // As a simplfying assumption, if throwOnPrivateFields returns true,
+ // we should also return true to useProxyExpandoObjectForPrivateFields.
+ virtual bool throwOnPrivateField() const { return false; }
+
+ /*
+ * [[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 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(JS::GCContext* gcx, 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_PUBLIC_DATA const JSClass ProxyClass;
+
+inline bool IsProxy(const JSObject* obj) {
+ return reinterpret_cast<const JS::shadow::Object*>(obj)->shape->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 constexpr ptrdiff_t 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 MOZ_ALWAYS_INLINE ProxyValueArray* fromReservedSlots(
+ ProxyReservedSlots* slots) {
+ uintptr_t p = reinterpret_cast<uintptr_t>(slots);
+ return reinterpret_cast<ProxyValueArray*>(p - offsetOfReservedSlots());
+ }
+ static constexpr size_t offsetOfReservedSlots() {
+ return offsetof(ProxyValueArray, reservedSlots);
+ }
+
+ static size_t allocCount(size_t nreserved) {
+ static_assert(offsetOfReservedSlots() % sizeof(JS::Value) == 0);
+ return offsetOfReservedSlots() / sizeof(JS::Value) + nreserved;
+ }
+ static size_t sizeOf(size_t nreserved) {
+ return allocCount(nreserved) * sizeof(JS::Value);
+ }
+
+ ProxyValueArray(const ProxyValueArray&) = delete;
+ void operator=(const ProxyValueArray&) = delete;
+};
+
+/* static */
+constexpr ptrdiff_t ProxyReservedSlots::offsetOfPrivateSlot() {
+ return -ptrdiff_t(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);
+ }
+};
+
+#ifdef JS_64BIT
+constexpr uint32_t ProxyDataOffset = 1 * sizeof(void*);
+#else
+constexpr uint32_t ProxyDataOffset = 2 * sizeof(void*);
+#endif
+
+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_PUBLIC_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(const 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_PUBLIC_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_PUBLIC_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_PUBLIC_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_PUBLIC_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_PUBLIC_API AutoWaivePolicy {
+ public:
+ AutoWaivePolicy(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ BaseProxyHandler::Action act) {}
+};
+#endif
+
+#ifdef JS_DEBUG
+extern JS_PUBLIC_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_PUBLIC_DATA const JSClassOps ProxyClassOps;
+extern JS_PUBLIC_DATA const js::ClassExtension ProxyClassExtension;
+extern JS_PUBLIC_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_PUBLIC_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_PUBLIC_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..3421a9fed3
--- /dev/null
+++ b/js/public/Realm.h
@@ -0,0 +1,202 @@
+/* -*- 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 "js/GCPolicyAPI.h"
+#include "js/TypeDecls.h" // forward-declaration of JS::Realm
+
+/************************************************************************/
+
+// [SMDOC] Realms
+//
+// Data associated with a global object. In the browser each frame has its
+// own global/realm.
+
+namespace js {
+namespace gc {
+JS_PUBLIC_API void TraceRealm(JSTracer* trc, JS::Realm* realm,
+ const char* name);
+} // namespace gc
+} // namespace js
+
+namespace JS {
+class JS_PUBLIC_API AutoRequireNoGC;
+
+// Each Realm holds a weak reference to its GlobalObject.
+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);
+ }
+ }
+};
+
+// 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)(JS::GCContext* gcx, 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);
+
+// If the current realm has the non-standard freezeBuiltins option set to true,
+// freeze the constructor object and seal the prototype.
+extern JS_PUBLIC_API bool MaybeFreezeCtorAndPrototype(JSContext* cx,
+ HandleObject ctor,
+ HandleObject maybeProto);
+
+/*
+ * 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 JS::Handle<JSObject*> GetRealmObjectPrototypeHandle(
+ 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);
+
+extern JS_PUBLIC_API JSObject* GetRealmAsyncIteratorPrototype(JSContext* cx);
+
+// Returns an object that represents the realm, that can be referred from
+// other realm/compartment.
+// See the consumer in `MaybeCrossOriginObjectMixins::EnsureHolder` for details.
+extern JS_PUBLIC_API JSObject* GetRealmKeyObject(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);
+
+/** NB: This API is infallible; a nullptr return value does not indicate error.
+ *
+ * |target| must not be a cross-compartment wrapper because CCWs are not
+ * associated with a single realm.
+ *
+ * Entering a realm roots the realm and its global object until the matching
+ * JS::LeaveRealm() call.
+ */
+extern JS_PUBLIC_API JS::Realm* EnterRealm(JSContext* cx, JSObject* target);
+
+extern JS_PUBLIC_API void LeaveRealm(JSContext* cx, JS::Realm* oldRealm);
+
+} // namespace JS
+
+/*
+ * At any time, a JSContext has a current (possibly-nullptr) realm. The
+ * preferred way to change the current realm is with JSAutoRealm:
+ *
+ * void foo(JSContext* cx, JSObject* obj) {
+ * // in some realm 'r'
+ * {
+ * JSAutoRealm ar(cx, obj); // constructor enters
+ * // in the realm of 'obj'
+ * } // destructor leaves
+ * // back in realm 'r'
+ * }
+ *
+ * The object passed to JSAutoRealm must *not* be a cross-compartment wrapper,
+ * because CCWs are not associated with a single realm.
+ *
+ * For more complicated uses that don't neatly fit in a C++ stack frame, the
+ * realm can be entered and left using separate function calls:
+ *
+ * void foo(JSContext* cx, JSObject* obj) {
+ * // in 'oldRealm'
+ * JS::Realm* oldRealm = JS::EnterRealm(cx, obj);
+ * // in the realm of 'obj'
+ * JS::LeaveRealm(cx, oldRealm);
+ * // back in 'oldRealm'
+ * }
+ *
+ * Note: these calls must still execute in a LIFO manner w.r.t all other
+ * enter/leave calls on the context. Furthermore, only the return value of a
+ * JS::EnterRealm call may be passed as the 'oldRealm' argument of
+ * the corresponding JS::LeaveRealm call.
+ *
+ * Entering a realm roots the realm and its global object for the lifetime of
+ * the JSAutoRealm.
+ */
+
+class MOZ_RAII JS_PUBLIC_API JSAutoRealm {
+ JSContext* cx_;
+ JS::Realm* oldRealm_;
+
+ public:
+ JSAutoRealm(JSContext* cx, JSObject* target);
+ JSAutoRealm(JSContext* cx, JSScript* target);
+ ~JSAutoRealm();
+};
+
+class MOZ_RAII JS_PUBLIC_API JSAutoNullableRealm {
+ JSContext* cx_;
+ JS::Realm* oldRealm_;
+
+ public:
+ explicit JSAutoNullableRealm(JSContext* cx, JSObject* targetOrNull);
+ ~JSAutoNullableRealm();
+};
+
+#endif // js_Realm_h
diff --git a/js/public/RealmIterators.h b/js/public/RealmIterators.h
new file mode 100644
index 0000000000..13f19dbb8f
--- /dev/null
+++ b/js/public/RealmIterators.h
@@ -0,0 +1,81 @@
+/* -*- 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 interfaces to iterate over the Realms given various context such as
+ * principals, compartments and GC zones.
+ */
+
+#ifndef js_RealmIterators_h
+#define js_RealmIterators_h
+
+#include "js/GCAPI.h"
+#include "js/TypeDecls.h"
+
+struct JSPrincipals;
+
+namespace JS {
+
+class JS_PUBLIC_API AutoRequireNoGC;
+
+using IterateRealmCallback = void (*)(JSContext* cx, void* data, Realm* realm,
+ const AutoRequireNoGC& nogc);
+
+/**
+ * This function calls |realmCallback| on every realm. Beware that there is no
+ * guarantee that the realm will survive after the callback returns. Also,
+ * barriers are disabled via the TraceSession.
+ */
+extern JS_PUBLIC_API void IterateRealms(JSContext* cx, void* data,
+ IterateRealmCallback realmCallback);
+
+/**
+ * Like IterateRealms, but only call the callback for realms using |principals|.
+ */
+extern JS_PUBLIC_API void IterateRealmsWithPrincipals(
+ JSContext* cx, JSPrincipals* principals, void* data,
+ IterateRealmCallback realmCallback);
+
+/**
+ * Like IterateRealms, but only iterates realms in |compartment|.
+ */
+extern JS_PUBLIC_API void IterateRealmsInCompartment(
+ JSContext* cx, JS::Compartment* compartment, void* data,
+ IterateRealmCallback realmCallback);
+
+/**
+ * An enum that JSIterateCompartmentCallback can return to indicate
+ * whether to keep iterating.
+ */
+enum class CompartmentIterResult { KeepGoing, Stop };
+
+} // namespace JS
+
+using JSIterateCompartmentCallback =
+ JS::CompartmentIterResult (*)(JSContext*, void*, JS::Compartment*);
+
+/**
+ * This function calls |compartmentCallback| on every compartment until either
+ * all compartments have been iterated or CompartmentIterResult::Stop is
+ * returned. Beware that there is no guarantee that the compartment will survive
+ * after the callback returns. Also, barriers are disabled via the TraceSession.
+ */
+extern JS_PUBLIC_API void JS_IterateCompartments(
+ JSContext* cx, void* data,
+ JSIterateCompartmentCallback compartmentCallback);
+
+/**
+ * This function calls |compartmentCallback| on every compartment in the given
+ * zone until either all compartments have been iterated or
+ * CompartmentIterResult::Stop is returned. Beware that there is no guarantee
+ * that the compartment will survive after the callback returns. Also, barriers
+ * are disabled via the TraceSession.
+ */
+extern JS_PUBLIC_API void JS_IterateCompartmentsInZone(
+ JSContext* cx, JS::Zone* zone, void* data,
+ JSIterateCompartmentCallback compartmentCallback);
+
+#endif /* js_RealmIterators_h */
diff --git a/js/public/RealmOptions.h b/js/public/RealmOptions.h
new file mode 100644
index 0000000000..0a0c1faeff
--- /dev/null
+++ b/js/public/RealmOptions.h
@@ -0,0 +1,364 @@
+/* -*- 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 "mozilla/Maybe.h"
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Class.h" // JSTraceOp
+#include "js/RefCounted.h"
+
+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,
+};
+
+struct LocaleString : js::RefCounted<LocaleString> {
+ const char* chars_;
+
+ explicit LocaleString(const char* chars) : chars_(chars) {}
+
+ auto chars() const { return chars_; }
+};
+
+/**
+ * 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);
+
+ // 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;
+ }
+
+ // 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 getToSourceEnabled() const { return toSource_; }
+ RealmCreationOptions& setToSourceEnabled(bool flag) {
+ toSource_ = flag;
+ return *this;
+ }
+
+#ifdef ENABLE_JSON_PARSE_WITH_SOURCE
+ bool getJSONParseWithSource() const { return jsonParseWithSource; }
+ RealmCreationOptions& setJSONParseWithSource(bool flag) {
+ jsonParseWithSource = flag;
+ return *this;
+ }
+#endif
+
+ // 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;
+ }
+
+ // Non-standard option to freeze certain builtin constructors and seal their
+ // prototypes. Also defines these constructors on the global as non-writable
+ // and non-configurable.
+ bool freezeBuiltins() const { return freezeBuiltins_; }
+ RealmCreationOptions& setFreezeBuiltins(bool flag) {
+ freezeBuiltins_ = flag;
+ return *this;
+ }
+
+ // Force all date/time methods in JavaScript to use the UTC timezone for
+ // fingerprinting protection.
+ bool forceUTC() const { return forceUTC_; }
+ RealmCreationOptions& setForceUTC(bool flag) {
+ forceUTC_ = flag;
+ return *this;
+ }
+
+ RefPtr<LocaleString> locale() const { return locale_; }
+ RealmCreationOptions& setLocaleCopyZ(const char* locale);
+
+ // Always use the fdlibm implementation of math functions instead of the
+ // platform native libc implementations. Useful for fingerprinting protection
+ // and cross-platform consistency.
+ bool alwaysUseFdlibm() const { return alwaysUseFdlibm_; }
+ RealmCreationOptions& setAlwaysUseFdlibm(bool flag) {
+ alwaysUseFdlibm_ = 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;
+ RefPtr<LocaleString> locale_;
+ bool invisibleToDebugger_ = false;
+ bool preserveJitCode_ = false;
+ bool sharedMemoryAndAtomics_ = false;
+ bool defineSharedArrayBufferConstructor_ = true;
+ bool coopAndCoep_ = false;
+ bool toSource_ = false;
+#ifdef ENABLE_JSON_PARSE_WITH_SOURCE
+ bool jsonParseWithSource = false;
+#endif
+
+ bool secureContext_ = false;
+ bool freezeBuiltins_ = false;
+ bool forceUTC_ = false;
+ bool alwaysUseFdlibm_ = false;
+};
+
+// This is a wrapper for mozilla::RTPCallerType, that can't easily
+// be exposed to the JS engine for layering reasons.
+struct RTPCallerTypeToken {
+ uint8_t value;
+};
+
+/**
+ * RealmBehaviors specifies behaviors of a realm that can be changed after the
+ * realm's been created.
+ */
+class JS_PUBLIC_API RealmBehaviors {
+ public:
+ RealmBehaviors() = default;
+
+ // When a JS::ReduceMicrosecondTimePrecisionCallback callback is defined via
+ // JS::SetReduceMicrosecondTimePrecisionCallback, a JS::RTPCallerTypeToken (a
+ // wrapper for mozilla::RTPCallerType) needs to be set for every Realm.
+ mozilla::Maybe<RTPCallerTypeToken> reduceTimerPrecisionCallerType() const {
+ return rtpCallerType;
+ }
+ RealmBehaviors& setReduceTimerPrecisionCallerType(RTPCallerTypeToken type) {
+ rtpCallerType = mozilla::Some(type);
+ return *this;
+ }
+
+ // 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 clampAndJitterTime() const { return clampAndJitterTime_; }
+ RealmBehaviors& setClampAndJitterTime(bool flag) {
+ clampAndJitterTime_ = flag;
+ return *this;
+ }
+
+ // 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:
+ mozilla::Maybe<RTPCallerTypeToken> rtpCallerType;
+ bool discardSource_ = 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);
+
+// This behaves like RealmBehaviors::setReduceTimerPrecisionCallerType, but
+// can be used even after the Realm has already been created.
+extern JS_PUBLIC_API void SetRealmReduceTimerPrecisionCallerType(
+ Realm* realm, RTPCallerTypeToken type);
+
+} // namespace JS
+
+#endif // js_RealmOptions_h
diff --git a/js/public/RefCounted.h b/js/public/RefCounted.h
new file mode 100644
index 0000000000..de5a72f2d8
--- /dev/null
+++ b/js/public/RefCounted.h
@@ -0,0 +1,96 @@
+/* -*- 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)));
+ }
+ }
+
+ bool hasOneRef() const {
+ MOZ_ASSERT(mRefCnt > 0);
+ return mRefCnt == 1;
+ }
+
+ 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..eda8cdb6fa
--- /dev/null
+++ b/js/public/RegExp.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/. */
+
+/* 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/TypeDecls.h"
+
+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);
+
+/**
+ * Execute a regexp on a given input, starting from |indexp|.
+ * Returns false on OOM or over-recursion.
+ *
+ * On no match, |rval| is set to Null.
+ * On a match, |indexp| and the RegExp statics are updated.
+ * Then, if |test| is true, |rval| is set to true.
+ * Otherwise, |rval| is set to a match result object.
+ */
+extern JS_PUBLIC_API bool ExecuteRegExp(JSContext* cx, Handle<JSObject*> obj,
+ Handle<JSObject*> reobj,
+ const char16_t* chars, size_t length,
+ size_t* indexp, bool test,
+ MutableHandle<Value> rval);
+
+/**
+ * Execute a regexp on a given input, starting from |indexp|.
+ * This is the same as ExecuteRegExp, except it does not update the RegExp
+ * statics and can be called 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..36a2e76c29
--- /dev/null
+++ b/js/public/RegExpFlags.h
@@ -0,0 +1,163 @@
+/* -*- 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 -- /d, /g, /i, /m, /s, /u, /v, /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:
+ /**
+ * Add .indices property to the match result, i.e. /d
+ */
+ static constexpr uint8_t HasIndices = 0b0100'0000;
+
+ /**
+ * Act globally and find *all* matches (rather than stopping after just the
+ * first one), i.e. /g.
+ */
+ static constexpr uint8_t Global = 0b0000'0010;
+
+ /**
+ * Interpret regular expression source text case-insensitively by folding
+ * uppercase letters to lowercase, i.e. /i.
+ */
+ static constexpr uint8_t IgnoreCase = 0b0000'0001;
+
+ /** Treat ^ and $ as begin and end of line, i.e. /m. */
+ static constexpr uint8_t Multiline = 0b0000'0100;
+
+ /* Allow . to match newline characters, i.e. /s. */
+ static constexpr uint8_t DotAll = 0b0010'0000;
+
+ /** Use Unicode semantics, i.e. /u. */
+ static constexpr uint8_t Unicode = 0b0001'0000;
+
+ /** Use Unicode Sets semantics, i.e. /v. */
+ static constexpr uint8_t UnicodeSets = 0b1000'0000;
+
+ /** Only match starting from <regular expression>.lastIndex, i.e. /y. */
+ static constexpr uint8_t Sticky = 0b0000'1000;
+
+ /** No regular expression flags. */
+ static constexpr uint8_t NoFlags = 0b0000'0000;
+
+ /** All regular expression flags. */
+ static constexpr uint8_t AllFlags = 0b1111'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;
+ RegExpFlags& operator=(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 hasIndices() const { return flags_ & RegExpFlag::HasIndices; }
+ 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 unicodeSets() const { return flags_ & RegExpFlag::UnicodeSets; }
+ 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/Result.h b/js/public/Result.h
new file mode 100644
index 0000000000..f8f6c2f650
--- /dev/null
+++ b/js/public/Result.h
@@ -0,0 +1,198 @@
+/* -*- 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`.
+ *
+ *
+ * ## 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"
+
+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..471c72dc42
--- /dev/null
+++ b/js/public/RootingAPI.h
@@ -0,0 +1,1609 @@
+/* -*- 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" // StackKindCount
+#include "js/ProfilingStack.h"
+#include "js/Realm.h"
+#include "js/Stack.h" // JS::NativeStackLimit
+#include "js/TypeDecls.h"
+#include "js/UniquePtr.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 {
+
+class Nursery;
+
+// The defaulted Enable parameter for the following two types is for restricting
+// specializations with std::enable_if.
+template <typename T, typename Enable = void>
+struct BarrierMethods {};
+
+template <typename Element, typename Wrapper, typename Enable = void>
+class WrappedPtrOperations {};
+
+template <typename Element, typename Wrapper>
+class MutableWrappedPtrOperations
+ : public WrappedPtrOperations<Element, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class RootedOperations : public MutableWrappedPtrOperations<T, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class HandleOperations : public WrappedPtrOperations<T, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class MutableHandleOperations : public MutableWrappedPtrOperations<T, Wrapper> {
+};
+
+template <typename T, typename Wrapper>
+class HeapOperations : public MutableWrappedPtrOperations<T, Wrapper> {};
+
+// Cannot use FOR_EACH_HEAP_ABLE_GC_POINTER_TYPE, as this would import too many
+// macros into scope
+
+// Add a 2nd template parameter to allow conditionally enabling partial
+// specializations via std::enable_if.
+template <typename T, typename Enable = void>
+struct IsHeapConstructibleType : public std::false_type {};
+
+#define JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE(T) \
+ template <> \
+ struct IsHeapConstructibleType<T> : public std::true_type {};
+JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
+JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
+// Note that JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE is left defined, to allow
+// declaring other types (eg from js/public/experimental/TypedData.h) to
+// be used with Heap<>.
+
+namespace gc {
+struct Cell;
+} /* 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_PUBLIC_API void HeapObjectPostWriteBarrier(JSObject** objp, JSObject* prev,
+ JSObject* next);
+JS_PUBLIC_API void HeapStringPostWriteBarrier(JSString** objp, JSString* prev,
+ JSString* next);
+JS_PUBLIC_API void HeapBigIntPostWriteBarrier(JS::BigInt** bip,
+ JS::BigInt* prev,
+ JS::BigInt* next);
+JS_PUBLIC_API void HeapObjectWriteBarriers(JSObject** objp, JSObject* prev,
+ JSObject* next);
+JS_PUBLIC_API void HeapStringWriteBarriers(JSString** objp, JSString* prev,
+ JSString* next);
+JS_PUBLIC_API void HeapBigIntWriteBarriers(JS::BigInt** bip, JS::BigInt* prev,
+ JS::BigInt* next);
+JS_PUBLIC_API void HeapScriptWriteBarriers(JSScript** objp, JSScript* prev,
+ JSScript* next);
+
+/**
+ * SafelyInitialized<T>::create() creates a safely-initialized |T|, suitable for
+ * use as a default value in situations requiring a safe but arbitrary |T|
+ * value. Implemented as a static method of a struct to allow partial
+ * specialization for subclasses via the Enable template parameter.
+ */
+template <typename T, typename Enable = void>
+struct SafelyInitialized {
+ static T create() {
+ // 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_PUBLIC_API void AssertGCThingMustBeTenured(JSObject* obj);
+extern JS_PUBLIC_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::HeapOperations<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>::create()) {
+ // 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) : ptr(p) {
+ postWriteBarrier(SafelyInitialized<T>::create(), ptr);
+ }
+
+ /*
+ * For Heap, move semantics are equivalent to copy semantics. However, we want
+ * the copy constructor to be explicit, and an explicit move constructor
+ * breaks common usage of move semantics, so we need to define both, even
+ * though they are equivalent.
+ */
+ explicit Heap(const Heap<T>& other) : ptr(other.getWithoutExpose()) {
+ postWriteBarrier(SafelyInitialized<T>::create(), ptr);
+ }
+ Heap(Heap<T>&& other) : ptr(other.getWithoutExpose()) {
+ postWriteBarrier(SafelyInitialized<T>::create(), ptr);
+ }
+
+ Heap& operator=(Heap<T>&& other) {
+ set(other.getWithoutExpose());
+ other.set(SafelyInitialized<T>::create());
+ return *this;
+ }
+
+ ~Heap() { postWriteBarrier(ptr, SafelyInitialized<T>::create()); }
+
+ 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& getWithoutExpose() const {
+ js::BarrierMethods<T>::readBarrier(ptr);
+ 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 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);
+ if (js::gc::IsInsideNursery(cell)) {
+ return false;
+ }
+
+ auto tenuredCell = reinterpret_cast<js::gc::TenuredCell*>(cell);
+ return js::gc::detail::CellIsMarkedGrayIfKnown(tenuredCell);
+}
+
+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::HeapOperations<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 HandleOperations<T> specialization containing them.
+ */
+template <typename T>
+class MOZ_NONHEAP_CLASS Handle : public js::HandleOperations<T, Handle<T>> {
+ friend class MutableHandle<T>;
+
+ public:
+ using ElementType = T;
+
+ Handle(const Handle<T>&) = default;
+
+ /* 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 MutableHandleOperations<T> specialization containing
+ * them.
+ */
+template <typename T>
+class MOZ_STACK_CLASS MutableHandle
+ : public js::MutableHandleOperations<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:
+ MutableHandle(const MutableHandle<T>&) = default;
+ 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));
+ }
+ }
+ static void readBarrier(T* t) {
+ if (t) {
+ js::gc::IncrementalReadBarrier(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 StableCellHasher {
+ using Key = T;
+ using Lookup = T;
+
+ static bool maybeGetHash(const Lookup& l, mozilla::HashNumber* hashOut);
+ static bool ensureHash(const Lookup& l, HashNumber* hashOut);
+ 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 StableCellHasher<JS::Heap<T>> {
+ using Key = JS::Heap<T>;
+ using Lookup = T;
+
+ static bool maybeGetHash(const Lookup& l, HashNumber* hashOut) {
+ return StableCellHasher<T>::maybeGetHash(l, hashOut);
+ }
+ static bool ensureHash(const Lookup& l, HashNumber* hashOut) {
+ return StableCellHasher<T>::ensureHash(l, hashOut);
+ }
+ static HashNumber hash(const Lookup& l) {
+ return StableCellHasher<T>::hash(l);
+ }
+ static bool match(const Key& k, const Lookup& l) {
+ return StableCellHasher<T>::match(k.unbarrieredGet(), l);
+ }
+};
+
+} // namespace js
+
+namespace mozilla {
+
+template <typename T>
+struct FallibleHashMethods<js::StableCellHasher<T>> {
+ template <typename Lookup>
+ static bool maybeGetHash(Lookup&& l, HashNumber* hashOut) {
+ return js::StableCellHasher<T>::maybeGetHash(std::forward<Lookup>(l),
+ hashOut);
+ }
+ template <typename Lookup>
+ static bool ensureHash(Lookup&& l, HashNumber* hashOut) {
+ return js::StableCellHasher<T>::ensureHash(std::forward<Lookup>(l),
+ hashOut);
+ }
+};
+
+} // namespace mozilla
+
+namespace js {
+
+struct VirtualTraceable {
+ virtual ~VirtualTraceable() = default;
+ virtual void trace(JSTracer* trc, const char* name) = 0;
+};
+
+class StackRootedBase {
+ public:
+ StackRootedBase* previous() { return prev; }
+
+ protected:
+ StackRootedBase** stack;
+ StackRootedBase* prev;
+
+ template <typename T>
+ auto* derived() {
+ return static_cast<JS::Rooted<T>*>(this);
+ }
+};
+
+class PersistentRootedBase
+ : protected mozilla::LinkedListElement<PersistentRootedBase> {
+ protected:
+ friend class mozilla::LinkedList<PersistentRootedBase>;
+ friend class mozilla::LinkedListElement<PersistentRootedBase>;
+
+ template <typename T>
+ auto* derived() {
+ return static_cast<JS::PersistentRooted<T>*>(this);
+ }
+};
+
+struct StackRootedTraceableBase : public StackRootedBase,
+ public VirtualTraceable {};
+
+class PersistentRootedTraceableBase : public PersistentRootedBase,
+ public VirtualTraceable {};
+
+template <typename Base, typename T>
+class TypedRootedGCThingBase : public Base {
+ public:
+ void trace(JSTracer* trc, const char* name);
+};
+
+template <typename Base, typename T>
+class TypedRootedTraceableBase : public Base {
+ public:
+ void trace(JSTracer* trc, const char* name) override {
+ auto* self = this->template derived<T>();
+ JS::GCPolicy<T>::trace(trc, self->address(), name);
+ }
+};
+
+template <typename T>
+struct RootedTraceableTraits {
+ using StackBase = TypedRootedTraceableBase<StackRootedTraceableBase, T>;
+ using PersistentBase =
+ TypedRootedTraceableBase<PersistentRootedTraceableBase, T>;
+};
+
+template <typename T>
+struct RootedGCThingTraits {
+ using StackBase = TypedRootedGCThingBase<StackRootedBase, T>;
+ using PersistentBase = TypedRootedGCThingBase<PersistentRootedBase, T>;
+};
+
+} /* namespace js */
+
+namespace JS {
+
+class JS_PUBLIC_API AutoGCRooter;
+
+enum class AutoGCRooterKind : uint8_t {
+ WrapperVector, /* js::AutoWrapperVector */
+ Wrapper, /* js::AutoWrapperRooter */
+ Custom, /* js::CustomAutoRooter */
+
+ Limit
+};
+
+using RootedListHeads =
+ mozilla::EnumeratedArray<RootKind, RootKind::Limit, js::StackRootedBase*>;
+
+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:
+ explicit RootingContext(js::Nursery* nursery);
+
+ 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_; }
+
+ js::Nursery& nursery() const {
+ MOZ_ASSERT(nursery_);
+ return *nursery_;
+ }
+
+ 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 nursery. Null for non-main-thread contexts. */
+ js::Nursery* nursery_;
+
+ /* The current zone. */
+ Zone* zone_;
+
+ /* The current realm. */
+ Realm* realm_;
+
+ public:
+ /* Limit pointer for checking native stack consumption. */
+ JS::NativeStackLimit nativeStackLimit[StackKindCount];
+
+#ifdef __wasi__
+ // For WASI we can't catch call-stack overflows with stack-pointer checks, so
+ // we count recursion depth with RAII based AutoCheckRecursionLimit.
+ uint32_t wasiRecursionDepth = 0u;
+
+ static constexpr uint32_t wasiRecursionDepthLimit = 350u;
+#endif // __wasi__
+
+ 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;
+
+/**
+ * Custom rooting behavior for internal and external clients.
+ *
+ * Deprecated. Where possible, use Rooted<> instead.
+ */
+class MOZ_RAII JS_PUBLIC_API CustomAutoRooter : private AutoGCRooter {
+ public:
+ template <typename CX>
+ explicit CustomAutoRooter(const CX& cx)
+ : AutoGCRooter(cx, AutoGCRooter::Kind::Custom) {}
+
+ friend void AutoGCRooter::trace(JSTracer* trc);
+
+ protected:
+ virtual ~CustomAutoRooter() = default;
+
+ /** Supplied by derived class to trace roots. */
+ virtual void trace(JSTracer* trc) = 0;
+};
+
+namespace detail {
+
+template <typename T>
+constexpr bool IsTraceable_v =
+ MapTypeToRootKind<T>::kind == JS::RootKind::Traceable;
+
+template <typename T>
+using RootedTraits =
+ std::conditional_t<IsTraceable_v<T>, js::RootedTraceableTraits<T>,
+ js::RootedGCThingTraits<T>>;
+
+} /* 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 RootedOperations<T> specialization containing them.
+ */
+template <typename T>
+class MOZ_RAII Rooted : public detail::RootedTraits<T>::StackBase,
+ public js::RootedOperations<T, Rooted<T>> {
+ inline void registerWithRootLists(RootedListHeads& roots) {
+ this->stack = &roots[JS::MapTypeToRootKind<T>::kind];
+ this->prev = *this->stack;
+ *this->stack = this;
+ }
+
+ inline RootedListHeads& rootLists(RootingContext* cx) {
+ return cx->stackRoots_;
+ }
+ inline RootedListHeads& rootLists(JSContext* cx) {
+ return rootLists(RootingContext::get(cx));
+ }
+
+ public:
+ using ElementType = T;
+
+ // Construct an empty Rooted holding a safely initialized but empty T.
+ // Requires T to have a copy constructor in order to copy the safely
+ // initialized value.
+ //
+ // Note that for SFINAE to reject this method, the 2nd template parameter must
+ // depend on RootingContext somehow even though we really only care about T.
+ template <typename RootingContext,
+ typename = std::enable_if_t<std::is_copy_constructible_v<T>,
+ RootingContext>>
+ explicit Rooted(const RootingContext& cx)
+ : ptr(SafelyInitialized<T>::create()) {
+ registerWithRootLists(rootLists(cx));
+ }
+
+ // Provide an initial value. Requires T to be constructible from the given
+ // argument.
+ 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));
+ }
+
+ // (Traceables only) Construct the contained value from the given arguments.
+ // Constructs in-place, so T does not need to be copyable or movable.
+ //
+ // Note that a copyable Traceable passed only a RootingContext will
+ // choose the above SafelyInitialized<T> constructor, because otherwise
+ // identical functions with parameter packs are considered less specialized.
+ //
+ // The SFINAE type must again depend on an inferred template parameter.
+ template <
+ typename RootingContext, typename... CtorArgs,
+ typename = std::enable_if_t<detail::IsTraceable_v<T>, RootingContext>>
+ explicit Rooted(const RootingContext& cx, CtorArgs... args)
+ : ptr(std::forward<CtorArgs>(args)...) {
+ MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
+ registerWithRootLists(rootLists(cx));
+ }
+
+ ~Rooted() {
+ MOZ_ASSERT(*this->stack == this);
+ *this->stack = this->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 &ptr; }
+ const T* address() const { return &ptr; }
+
+ private:
+ T 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 RootedOperations<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 HandleOperations<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,
+ js::PersistentRootedBase* root);
+
+JS_PUBLIC_API void AddPersistentRoot(JSRuntime* rt, RootKind kind,
+ js::PersistentRootedBase* 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 detail::RootedTraits<T>::PersistentBase,
+ public js::RootedOperations<T, PersistentRooted<T>> {
+ void registerWithRootLists(RootingContext* cx) {
+ MOZ_ASSERT(!initialized());
+ JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
+ AddPersistentRoot(cx, kind, this);
+ }
+
+ void registerWithRootLists(JSRuntime* rt) {
+ MOZ_ASSERT(!initialized());
+ JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
+ AddPersistentRoot(rt, kind, this);
+ }
+
+ // Used when JSContext type is incomplete and so it is not known to inherit
+ // from RootingContext.
+ void registerWithRootLists(JSContext* cx) {
+ registerWithRootLists(RootingContext::get(cx));
+ }
+
+ public:
+ using ElementType = T;
+
+ PersistentRooted() : ptr(SafelyInitialized<T>::create()) {}
+
+ template <
+ typename RootHolder,
+ typename = std::enable_if_t<std::is_copy_constructible_v<T>, RootHolder>>
+ explicit PersistentRooted(const RootHolder& cx)
+ : ptr(SafelyInitialized<T>::create()) {
+ registerWithRootLists(cx);
+ }
+
+ template <
+ typename RootHolder, typename U,
+ typename = std::enable_if_t<std::is_constructible_v<T, U>, RootHolder>>
+ PersistentRooted(const RootHolder& cx, U&& initial)
+ : ptr(std::forward<U>(initial)) {
+ registerWithRootLists(cx);
+ }
+
+ template <typename RootHolder, typename... CtorArgs,
+ typename = std::enable_if_t<detail::IsTraceable_v<T>, RootHolder>>
+ explicit PersistentRooted(const RootHolder& cx, CtorArgs... args)
+ : ptr(std::forward<CtorArgs>(args)...) {
+ registerWithRootLists(cx);
+ }
+
+ PersistentRooted(const PersistentRooted& rhs) : 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 this->isInList(); }
+
+ void init(RootingContext* cx) { init(cx, SafelyInitialized<T>::create()); }
+ 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>::create());
+ this->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 &ptr;
+ }
+ const T* address() const { return &ptr; }
+
+ template <typename U>
+ void set(U&& value) {
+ MOZ_ASSERT(initialized());
+ ptr = std::forward<U>(value);
+ }
+
+ private:
+ T 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:
+ [[nodiscard]] 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 */
+
+template <typename Wrapper, typename T1, typename T2>
+class WrappedPtrOperations<std::pair<T1, T2>, Wrapper> {
+ const std::pair<T1, T2>& pair() const {
+ return static_cast<const Wrapper*>(this)->get();
+ }
+
+ public:
+ const T1& first() const { return pair().first; }
+ const T2& second() const { return pair().second; }
+};
+
+template <typename Wrapper, typename T1, typename T2>
+class MutableWrappedPtrOperations<std::pair<T1, T2>, Wrapper>
+ : public WrappedPtrOperations<std::pair<T1, T2>, Wrapper> {
+ std::pair<T1, T2>& pair() { return static_cast<Wrapper*>(this)->get(); }
+
+ public:
+ T1& first() { return pair().first; }
+ T2& second() { return pair().second; }
+};
+
+} /* namespace js */
+
+#endif /* js_RootingAPI_h */
diff --git a/js/public/SavedFrameAPI.h b/js/public/SavedFrameAPI.h
new file mode 100644
index 0000000000..02bc65a0ea
--- /dev/null
+++ b/js/public/SavedFrameAPI.h
@@ -0,0 +1,159 @@
+/* -*- 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_PUBLIC_API
+
+#include "js/ColumnNumber.h" // JS::TaggedColumnNumberOneOrigin
+#include "js/TypeDecls.h"
+
+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 (1-origin).
+ * 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,
+ JS::TaggedColumnNumberOneOrigin* 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_PUBLIC_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..3c98fa204c
--- /dev/null
+++ b/js/public/ScalarType.h
@@ -0,0 +1,232 @@
+/* -*- 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_TYPED_ARRAY
+// * JS_FOR_PROTOTYPES_
+// * JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE
+// * JIT compilation
+//
+// and the existing entries here must not be renumbered, since they are
+// necessary for backwards compatibility with structured clones from previous
+// versions. (It is fine to add new entries and increment
+// MaxTypedArrayViewType, or change anything at or after
+// MaxTypedArrayViewType.)
+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.
+ * E.g. DataView
+ */
+ 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 bool isFloatingType(Type atype) {
+ switch (atype) {
+ case Int8:
+ case Uint8:
+ case Uint8Clamped:
+ case Int16:
+ case Uint16:
+ case Int32:
+ case Uint32:
+ case Int64:
+ case BigInt64:
+ case BigUint64:
+ return false;
+ case Float32:
+ case Float64:
+ case Simd128:
+ return true;
+ 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");
+}
+
+static inline const char* byteSizeString(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");
+}
+
+} // namespace Scalar
+
+} // namespace JS
+
+namespace js {
+
+// This is aliased in NamespaceImports.h, but that is internal-only and
+// inaccessible to Gecko code, which uses this type fairly heavily. Until such
+// uses are changed, we need the alias here as well.
+namespace Scalar = JS::Scalar;
+
+} // namespace js
+
+#endif // js_ScalarType_h
diff --git a/js/public/ScriptPrivate.h b/js/public/ScriptPrivate.h
new file mode 100644
index 0000000000..1c7f1801fe
--- /dev/null
+++ b/js/public/ScriptPrivate.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_ScriptPrivate_h
+#define js_ScriptPrivate_h
+
+#include "jstypes.h"
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+/**
+ * Set a private value associated with a script. Note that this value is shared
+ * by all nested scripts compiled from a single source file.
+ */
+extern JS_PUBLIC_API void SetScriptPrivate(JSScript* script,
+ const JS::Value& value);
+
+/**
+ * Get the private value associated with a script. Note that this value is
+ * shared by all nested scripts compiled from a single source file.
+ */
+extern JS_PUBLIC_API JS::Value GetScriptPrivate(JSScript* script);
+
+/**
+ * Return the private value associated with currently executing script or
+ * module, or undefined if there is no such script.
+ */
+extern JS_PUBLIC_API JS::Value GetScriptedCallerPrivate(JSContext* cx);
+
+/**
+ * Hooks called when references to a script private value are created or
+ * destroyed. This allows use of a reference counted object as the
+ * script private.
+ */
+using ScriptPrivateReferenceHook = void (*)(const JS::Value&);
+
+/**
+ * Set the script private finalize hook for the runtime to the given function.
+ */
+extern JS_PUBLIC_API void SetScriptPrivateReferenceHooks(
+ JSRuntime* rt, ScriptPrivateReferenceHook addRefHook,
+ ScriptPrivateReferenceHook releaseHook);
+
+} // namespace JS
+
+#endif // js_ScriptPrivate_h
diff --git a/js/public/ShadowRealmCallbacks.h b/js/public/ShadowRealmCallbacks.h
new file mode 100644
index 0000000000..bbd01b9d8c
--- /dev/null
+++ b/js/public/ShadowRealmCallbacks.h
@@ -0,0 +1,50 @@
+/* -*- 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/. */
+
+#ifndef js_ShadowReamCallbacks_h
+#define js_ShadowReamCallbacks_h
+
+#include "jstypes.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+
+struct JS_PUBLIC_API JSContext;
+
+namespace JS {
+
+class RealmOptions;
+
+using GlobalInitializeCallback = bool (*)(JSContext*, JS::Handle<JSObject*>);
+
+// Install the HostInitializeShadowRealm callback that will be invoked when
+// creating a shadow realm.
+//
+// The callback will be passed the realm's global object, so that it is possible
+// for the embedding to make any host-determined manipulations to the global,
+// such as installing interfaces or helpers that should exist even within
+// ShadowRealms. (For example, in the web platform, WebIDL with the
+// [Exposed=*] attribute should be installed within a shadow realm.)
+extern JS_PUBLIC_API void SetShadowRealmInitializeGlobalCallback(
+ JSContext* cx, GlobalInitializeCallback callback);
+
+using GlobalCreationCallback =
+ JSObject* (*)(JSContext* cx, JS::RealmOptions& creationOptions,
+ JSPrincipals* principals,
+ JS::Handle<JSObject*> enclosingGlobal);
+
+// Create the Global object for a ShadowRealm.
+//
+// This isn't directly specified, however at least in Gecko, in order to
+// correctly implement HostInitializeShadowRealm, there are requirements
+// placed on the global for the ShadowRealm.
+//
+// This callback should return a Global object compatible with the
+// callback installed by SetShadowRealmInitializeGlobalCallback
+extern JS_PUBLIC_API void SetShadowRealmGlobalCreationCallback(
+ JSContext* cx, GlobalCreationCallback callback);
+
+} // namespace JS
+
+#endif // js_ShadowReamCallbacks_h
diff --git a/js/public/SharedArrayBuffer.h b/js/public/SharedArrayBuffer.h
new file mode 100644
index 0000000000..f427b7c806
--- /dev/null
+++ b/js/public/SharedArrayBuffer.h
@@ -0,0 +1,80 @@
+/* -*- 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> // uint8_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+class JS_PUBLIC_API AutoRequireNoGC;
+
+// 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,
+ size_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 size_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, size_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..4445020484
--- /dev/null
+++ b/js/public/SliceBudget.h
@@ -0,0 +1,145 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Variant.h"
+
+#include <stdint.h>
+
+#include "jstypes.h"
+
+namespace js {
+
+struct JS_PUBLIC_API TimeBudget {
+ const mozilla::TimeDuration budget;
+ mozilla::TimeStamp deadline; // Calculated when SliceBudget is constructed.
+
+ explicit TimeBudget(mozilla::TimeDuration duration) : budget(duration) {}
+ explicit TimeBudget(int64_t milliseconds)
+ : budget(mozilla::TimeDuration::FromMilliseconds(milliseconds)) {}
+
+ void setDeadlineFromNow();
+};
+
+struct JS_PUBLIC_API WorkBudget {
+ const int64_t budget;
+
+ explicit WorkBudget(int64_t work) : budget(work) {}
+};
+
+struct UnlimitedBudget {};
+
+/*
+ * This class describes a limit to the amount of work to be performed in a GC
+ * slice, so that we can return to the mutator without pausing for too long. The
+ * budget may be based on a deadline time or an amount of work to be performed,
+ * or may be unlimited.
+ *
+ * To reduce the number of gettimeofday calls, we only check the time every 1000
+ * operations.
+ */
+class JS_PUBLIC_API SliceBudget {
+ public:
+ using InterruptRequestFlag = mozilla::Atomic<bool, mozilla::Relaxed>;
+
+ private:
+ static constexpr int64_t UnlimitedCounter = INT64_MAX;
+
+ // Most calls to isOverBudget will only check the counter value. Every N
+ // steps, do a more "expensive" check -- look at the current time and/or
+ // check the atomic interrupt flag.
+ static constexpr int64_t StepsPerExpensiveCheck = 1000;
+
+ // How many steps to count before checking the time and possibly the interrupt
+ // flag.
+ int64_t counter = StepsPerExpensiveCheck;
+
+ // External flag to request the current slice to be interrupted
+ // (and return isOverBudget() early.) Applies only to time-based budgets.
+ InterruptRequestFlag* interruptRequested = nullptr;
+
+ // Configuration
+ mozilla::Variant<TimeBudget, WorkBudget, UnlimitedBudget> budget;
+
+ // This SliceBudget is considered interrupted from the time isOverBudget()
+ // finds the interrupt flag set.
+ bool interrupted = false;
+
+ public:
+ // Whether this slice is running in (predicted to be) idle time.
+ // Only used for recording in the profile.
+ bool idle = false;
+
+ // Whether this slice was given an extended budget, larger than
+ // the predicted idle time.
+ bool extended = false;
+
+ private:
+ explicit SliceBudget(InterruptRequestFlag* irqPtr)
+ : counter(irqPtr ? StepsPerExpensiveCheck : UnlimitedCounter),
+ interruptRequested(irqPtr),
+ budget(UnlimitedBudget()) {}
+
+ bool checkOverBudget();
+
+ public:
+ // Use to create an unlimited budget.
+ static SliceBudget unlimited() { return SliceBudget(nullptr); }
+
+ // Instantiate as SliceBudget(TimeBudget(n)).
+ explicit SliceBudget(TimeBudget time,
+ InterruptRequestFlag* interrupt = nullptr);
+
+ explicit SliceBudget(mozilla::TimeDuration duration,
+ InterruptRequestFlag* interrupt = nullptr)
+ : SliceBudget(TimeBudget(duration.ToMilliseconds()), interrupt) {}
+
+ // Instantiate as SliceBudget(WorkBudget(n)).
+ explicit SliceBudget(WorkBudget work);
+
+ // Register having performed the given number of steps (counted against a
+ // work budget, or progress towards the next time or callback check).
+ void step(uint64_t steps = 1) {
+ MOZ_ASSERT(steps > 0);
+ counter -= steps;
+ }
+
+ // Force an "expensive" (time) check on the next call to isOverBudget. Useful
+ // when switching between major phases of an operation like a cycle
+ // collection.
+ void forceCheck() {
+ if (isTimeBudget()) {
+ counter = 0;
+ }
+ }
+
+ bool isOverBudget() { return counter <= 0 && checkOverBudget(); }
+
+ bool isWorkBudget() const { return budget.is<WorkBudget>(); }
+ bool isTimeBudget() const { return budget.is<TimeBudget>(); }
+ bool isUnlimited() const { return budget.is<UnlimitedBudget>(); }
+
+ mozilla::TimeDuration timeBudgetDuration() const {
+ return budget.as<TimeBudget>().budget;
+ }
+ int64_t timeBudget() const { return timeBudgetDuration().ToMilliseconds(); }
+ int64_t workBudget() const { return budget.as<WorkBudget>().budget; }
+
+ mozilla::TimeStamp deadline() const {
+ return budget.as<TimeBudget>().deadline;
+ }
+
+ int describe(char* buffer, size_t maxlen) const;
+};
+
+} // namespace js
+
+#endif /* js_SliceBudget_h */
diff --git a/js/public/SourceText.h b/js/public/SourceText.h
new file mode 100644
index 0000000000..88592be875
--- /dev/null
+++ b/js/public/SourceText.h
@@ -0,0 +1,353 @@
+/* -*- 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
+#include "mozilla/Likely.h" // MOZ_UNLIKELY
+
+#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 mozilla {
+union Utf8Unit;
+}
+
+namespace js {
+class FrontendContext;
+} // namespace js
+
+namespace JS {
+
+class JS_PUBLIC_API AutoStableStringChars;
+using FrontendContext = js::FrontendContext;
+
+namespace detail {
+
+MOZ_COLD extern JS_PUBLIC_API void ReportSourceTooLong(JSContext* cx);
+MOZ_COLD extern JS_PUBLIC_API void ReportSourceTooLong(JS::FrontendContext* fc);
+
+} // 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_));
+ }
+ }
+
+ private:
+ template <typename ContextT>
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool initImpl(ContextT* context,
+ 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(context);
+ return false;
+ }
+
+ return true;
+ }
+
+ public:
+ /**
+ * 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.
+ */
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool init(JSContext* cx, const Unit* units,
+ size_t unitsLength,
+ SourceOwnership ownership) {
+ return initImpl(cx, units, unitsLength, ownership);
+ }
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool init(JS::FrontendContext* fc,
+ const Unit* units,
+ size_t unitsLength,
+ SourceOwnership ownership) {
+ return initImpl(fc, units, unitsLength, ownership);
+ }
+
+ /**
+ * 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>>>
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool init(JSContext* cx, const Char* chars,
+ size_t charsLength,
+ SourceOwnership ownership) {
+ return initImpl(cx, reinterpret_cast<const Unit*>(chars), charsLength,
+ ownership);
+ }
+ template <typename Char,
+ typename = std::enable_if_t<std::is_same_v<Char, CharT> &&
+ !std::is_same_v<Char, Unit>>>
+ [[nodiscard]] MOZ_IS_CLASS_INIT bool init(JS::FrontendContext* fc,
+ const Char* chars,
+ size_t charsLength,
+ SourceOwnership ownership) {
+ return initImpl(fc, reinterpret_cast<const Unit*>(chars), charsLength,
+ ownership);
+ }
+
+ /**
+ * Initialize this using source units transferred out of |data|.
+ */
+ [[nodiscard]] bool init(JSContext* cx,
+ js::UniquePtr<Unit[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return initImpl(cx, data.release(), dataLength,
+ SourceOwnership::TakeOwnership);
+ }
+ [[nodiscard]] bool init(JS::FrontendContext* fc,
+ js::UniquePtr<Unit[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return initImpl(fc, 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>>>
+ [[nodiscard]] bool init(JSContext* cx,
+ js::UniquePtr<Char[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return init(cx, data.release(), dataLength, SourceOwnership::TakeOwnership);
+ }
+ template <typename Char,
+ typename = std::enable_if_t<std::is_same_v<Char, CharT> &&
+ !std::is_same_v<Char, Unit>>>
+ [[nodiscard]] bool init(JS::FrontendContext* fc,
+ js::UniquePtr<Char[], JS::FreePolicy> data,
+ size_t dataLength) {
+ return init(fc, data.release(), dataLength, SourceOwnership::TakeOwnership);
+ }
+
+ /**
+ * Initialize this using an AutoStableStringChars. Transfers the code units if
+ * they are owned by the AutoStableStringChars, otherwise borrow directly from
+ * the underlying JSString. The AutoStableStringChars must outlive this
+ * SourceText and must be explicitly configured to the same unit type as this
+ * SourceText.
+ */
+ [[nodiscard]] bool initMaybeBorrowed(JSContext* cx,
+ AutoStableStringChars& linearChars);
+ [[nodiscard]] bool initMaybeBorrowed(JS::FrontendContext* fc,
+ AutoStableStringChars& linearChars);
+
+ /**
+ * 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..5c50df18cb
--- /dev/null
+++ b/js/public/StableStringChars.h
@@ -0,0 +1,118 @@
+/* -*- 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
+#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_PUBLIC_API
+
+#include "js/AllocPolicy.h"
+#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. When it
+ * has nursery-allocated out of lines 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_PUBLIC_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) {}
+
+ [[nodiscard]] bool init(JSContext* cx, JSString* s);
+
+ /* Like init(), but Latin1 chars are inflated to TwoByte. */
+ [[nodiscard]] 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_, length());
+ }
+
+ mozilla::Range<const char16_t> twoByteRange() const {
+ MOZ_ASSERT(state_ == TwoByte);
+ return mozilla::Range<const char16_t>(twoByteChars_, length());
+ }
+
+ /* 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;
+ }
+
+ size_t length() const { return GetStringLength(s_); }
+
+ private:
+ AutoStableStringChars(const AutoStableStringChars& other) = delete;
+ void operator=(const AutoStableStringChars& other) = delete;
+
+ 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/Stack.h b/js/public/Stack.h
new file mode 100644
index 0000000000..6f01b2b728
--- /dev/null
+++ b/js/public/Stack.h
@@ -0,0 +1,234 @@
+/* -*- 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_Stack_h
+#define js_Stack_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Maybe.h" // mozilla::Maybe
+#include "mozilla/Variant.h" // mozilla::Variant
+
+#include <stddef.h> // size_t
+#include <stdint.h> // uint32_t, uintptr_t, UINTPTR_MAX
+#include <utility> // std::move
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Principals.h" // JSPrincipals, JS_HoldPrincipals, JS_DropPrincipals
+#include "js/TypeDecls.h" // JSContext, Handle*, MutableHandle*
+
+namespace JS {
+
+using NativeStackSize = size_t;
+
+using NativeStackBase = uintptr_t;
+
+using NativeStackLimit = uintptr_t;
+
+#if JS_STACK_GROWTH_DIRECTION > 0
+constexpr NativeStackLimit NativeStackLimitMin = 0;
+constexpr NativeStackLimit NativeStackLimitMax = UINTPTR_MAX;
+#else
+constexpr NativeStackLimit NativeStackLimitMin = UINTPTR_MAX;
+constexpr NativeStackLimit NativeStackLimitMax = 0;
+#endif
+
+#ifdef __wasi__
+// We build with the "stack-first" wasm-ld option, so the stack grows downward
+// toward zero. Let's set a limit just a bit above this so that we catch an
+// overflow before a Wasm trap occurs.
+constexpr NativeStackLimit WASINativeStackLimit = 1024;
+#endif // __wasi__
+
+inline NativeStackLimit GetNativeStackLimit(NativeStackBase base,
+ NativeStackSize size) {
+#if JS_STACK_GROWTH_DIRECTION > 0
+ MOZ_ASSERT(base <= size_t(-1) - size);
+ return base + size - 1;
+#else // stack grows up
+ MOZ_ASSERT(base >= size);
+ return base - (size - 1);
+#endif // stack grows down
+}
+
+} // namespace JS
+
+/**
+ * Set the size of the native stack that should not be exceed. To disable
+ * stack size checking pass 0.
+ *
+ * SpiderMonkey allows for a distinction between system code (such as GCs, which
+ * may incidentally be triggered by script but are not strictly performed on
+ * behalf of such script), trusted script (as determined by
+ * JS_SetTrustedPrincipals), and untrusted script. Each kind of code may have a
+ * different stack quota, allowing embedders to keep higher-priority machinery
+ * running in the face of scripted stack exhaustion by something else.
+ *
+ * The stack quotas for each kind of code should be monotonically descending,
+ * and may be specified with this function. If 0 is passed for a given kind
+ * of code, it defaults to the value of the next-highest-priority kind.
+ *
+ * This function may only be called immediately after the runtime is initialized
+ * and before any code is executed and/or interrupts requested.
+ */
+extern JS_PUBLIC_API void JS_SetNativeStackQuota(
+ JSContext* cx, JS::NativeStackSize systemCodeStackSize,
+ JS::NativeStackSize trustedScriptStackSize = 0,
+ JS::NativeStackSize untrustedScriptStackSize = 0);
+
+namespace js {
+
+enum class StackFormat { SpiderMonkey, V8, Default };
+
+/*
+ * Sets the format used for stringifying Error stacks.
+ *
+ * The default format is StackFormat::SpiderMonkey. Use StackFormat::V8
+ * in order to emulate V8's stack formatting. StackFormat::Default can't be
+ * used here.
+ */
+extern JS_PUBLIC_API void SetStackFormat(JSContext* cx, StackFormat format);
+
+extern JS_PUBLIC_API StackFormat GetStackFormat(JSContext* cx);
+
+} // namespace js
+
+namespace JS {
+
+/**
+ * Capture all frames.
+ */
+struct AllFrames {};
+
+/**
+ * Capture at most this many frames.
+ */
+struct MaxFrames {
+ uint32_t maxFrames;
+
+ explicit MaxFrames(uint32_t max) : maxFrames(max) { MOZ_ASSERT(max > 0); }
+};
+
+/**
+ * Capture the first frame with the given principals. By default, do not
+ * consider self-hosted frames with the given principals as satisfying the stack
+ * capture.
+ */
+struct JS_PUBLIC_API FirstSubsumedFrame {
+ JSContext* cx;
+ JSPrincipals* principals;
+ bool ignoreSelfHosted;
+
+ /**
+ * Use the cx's current compartment's principals.
+ */
+ explicit FirstSubsumedFrame(JSContext* cx,
+ bool ignoreSelfHostedFrames = true);
+
+ explicit FirstSubsumedFrame(JSContext* ctx, JSPrincipals* p,
+ bool ignoreSelfHostedFrames = true)
+ : cx(ctx), principals(p), ignoreSelfHosted(ignoreSelfHostedFrames) {
+ if (principals) {
+ JS_HoldPrincipals(principals);
+ }
+ }
+
+ // No copying because we want to avoid holding and dropping principals
+ // unnecessarily.
+ FirstSubsumedFrame(const FirstSubsumedFrame&) = delete;
+ FirstSubsumedFrame& operator=(const FirstSubsumedFrame&) = delete;
+
+ FirstSubsumedFrame(FirstSubsumedFrame&& rhs)
+ : principals(rhs.principals), ignoreSelfHosted(rhs.ignoreSelfHosted) {
+ MOZ_ASSERT(this != &rhs, "self move disallowed");
+ rhs.principals = nullptr;
+ }
+
+ FirstSubsumedFrame& operator=(FirstSubsumedFrame&& rhs) {
+ new (this) FirstSubsumedFrame(std::move(rhs));
+ return *this;
+ }
+
+ ~FirstSubsumedFrame() {
+ if (principals) {
+ JS_DropPrincipals(cx, principals);
+ }
+ }
+};
+
+using StackCapture = mozilla::Variant<AllFrames, MaxFrames, FirstSubsumedFrame>;
+
+/**
+ * Capture the current call stack as a chain of SavedFrame JSObjects, and set
+ * |stackp| to the SavedFrame for the youngest stack frame, or nullptr if there
+ * are no JS frames on the stack.
+ *
+ * The |capture| parameter describes the portion of the JS stack to capture:
+ *
+ * * |JS::AllFrames|: Capture all frames on the stack.
+ *
+ * * |JS::MaxFrames|: Capture no more than |JS::MaxFrames::maxFrames| from the
+ * stack.
+ *
+ * * |JS::FirstSubsumedFrame|: Capture the first frame whose principals are
+ * subsumed by |JS::FirstSubsumedFrame::principals|. By default, do not
+ * consider self-hosted frames; this can be controlled via the
+ * |JS::FirstSubsumedFrame::ignoreSelfHosted| flag. Do not capture any async
+ * stack.
+ */
+extern JS_PUBLIC_API bool CaptureCurrentStack(
+ JSContext* cx, MutableHandleObject stackp,
+ StackCapture&& capture = StackCapture(AllFrames()));
+
+/**
+ * Returns true if capturing stack trace data to associate with an asynchronous
+ * operation is currently enabled for the current context realm.
+ *
+ * Users should check this state before capturing a stack that will be passed
+ * back to AutoSetAsyncStackForNewCalls later, in order to avoid capturing a
+ * stack for async use when we don't actually want to capture it.
+ */
+extern JS_PUBLIC_API bool IsAsyncStackCaptureEnabledForRealm(JSContext* cx);
+
+/*
+ * This is a utility function for preparing an async stack to be used
+ * by some other object. This may be used when you need to treat a
+ * given stack trace as an async parent. If you just need to capture
+ * the current stack, async parents and all, use CaptureCurrentStack
+ * instead.
+ *
+ * Here |asyncStack| is the async stack to prepare. It is copied into
+ * |cx|'s current compartment, and the newest frame is given
+ * |asyncCause| as its asynchronous cause. If |maxFrameCount| is
+ * |Some(n)|, capture at most the youngest |n| frames. The
+ * new stack object is written to |stackp|. Returns true on success,
+ * or sets an exception and returns |false| on error.
+ */
+extern JS_PUBLIC_API bool CopyAsyncStack(
+ JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
+ MutableHandleObject stackp, const mozilla::Maybe<size_t>& maxFrameCount);
+
+/**
+ * Given a SavedFrame JSObject stack, stringify it in the same format as
+ * Error.prototype.stack. The stringified stack out parameter is placed in the
+ * cx's compartment. Defaults to the empty string.
+ *
+ * The same notes above about SavedFrame accessors applies here as well: cx
+ * doesn't need to be in stack's compartment, and stack can be null, a
+ * SavedFrame object, or a wrapper (CCW or Xray) around a SavedFrame object.
+ * SavedFrames not subsumed by |principals| are skipped.
+ *
+ * Optional indent parameter specifies the number of white spaces to indent
+ * each line.
+ */
+extern JS_PUBLIC_API bool BuildStackString(
+ JSContext* cx, JSPrincipals* principals, HandleObject stack,
+ MutableHandleString stringp, size_t indent = 0,
+ js::StackFormat stackFormat = js::StackFormat::Default);
+
+} // namespace JS
+
+#endif // js_Stack_h
diff --git a/js/public/StreamConsumer.h b/js/public/StreamConsumer.h
new file mode 100644
index 0000000000..c05f20ae7e
--- /dev/null
+++ b/js/public/StreamConsumer.h
@@ -0,0 +1,114 @@
+/* -*- 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/. */
+
+#ifndef js_StreamConsumer_h
+#define js_StreamConsumer_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/RefCountType.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "jstypes.h"
+
+#include "js/AllocPolicy.h"
+#include "js/TypeDecls.h"
+#include "js/UniquePtr.h"
+#include "js/Vector.h"
+
+namespace JS {
+
+/**
+ * The ConsumeStreamCallback is called from an active JSContext, passing a
+ * StreamConsumer that wishes to consume the given host object as a stream of
+ * bytes with the given MIME type. On failure, the embedding must report the
+ * appropriate error on 'cx'. On success, the embedding must call
+ * consumer->consumeChunk() repeatedly on any thread until exactly one of:
+ * - consumeChunk() returns false
+ * - the embedding calls consumer->streamEnd()
+ * - the embedding calls consumer->streamError()
+ * before JS_DestroyContext(cx) or JS::ShutdownAsyncTasks(cx) is called.
+ *
+ * Note: consumeChunk(), streamEnd() and streamError() may be called
+ * synchronously by ConsumeStreamCallback.
+ *
+ * When streamEnd() is called, the embedding may optionally pass an
+ * OptimizedEncodingListener*, indicating that there is a cache entry associated
+ * with this stream that can store an optimized encoding of the bytes that were
+ * just streamed at some point in the future by having SpiderMonkey call
+ * storeOptimizedEncoding(). Until the optimized encoding is ready, SpiderMonkey
+ * will hold an outstanding refcount to keep the listener alive.
+ *
+ * After storeOptimizedEncoding() is called, on cache hit, the embedding
+ * may call consumeOptimizedEncoding() instead of consumeChunk()/streamEnd().
+ * The embedding must ensure that the GetOptimizedEncodingBuildId() (see
+ * js/BuildId.h) at the time when an optimized encoding is created is the same
+ * as when it is later consumed.
+ */
+
+class OptimizedEncodingListener {
+ protected:
+ virtual ~OptimizedEncodingListener() = default;
+
+ public:
+ // SpiderMonkey will hold an outstanding reference count as long as it holds
+ // a pointer to OptimizedEncodingListener.
+ virtual MozExternalRefCountType MOZ_XPCOM_ABI AddRef() = 0;
+ virtual MozExternalRefCountType MOZ_XPCOM_ABI Release() = 0;
+
+ // SpiderMonkey may optionally call storeOptimizedEncoding() after it has
+ // finished processing a streamed resource.
+ virtual void storeOptimizedEncoding(const uint8_t* bytes, size_t length) = 0;
+};
+
+class JS_PUBLIC_API StreamConsumer {
+ protected:
+ // AsyncStreamConsumers are created and destroyed by SpiderMonkey.
+ StreamConsumer() = default;
+ virtual ~StreamConsumer() = default;
+
+ public:
+ // Called by the embedding as each chunk of bytes becomes available.
+ // If this function returns 'false', the stream must drop all pointers to
+ // this StreamConsumer.
+ virtual bool consumeChunk(const uint8_t* begin, size_t length) = 0;
+
+ // Called by the embedding when the stream reaches end-of-file, passing the
+ // listener described above.
+ virtual void streamEnd(OptimizedEncodingListener* listener = nullptr) = 0;
+
+ // Called by the embedding when there is an error during streaming. The
+ // given error code should be passed to the ReportStreamErrorCallback on the
+ // main thread to produce the semantically-correct rejection value.
+ virtual void streamError(size_t errorCode) = 0;
+
+ // Called by the embedding *instead of* consumeChunk()/streamEnd() if an
+ // optimized encoding is available from a previous streaming of the same
+ // contents with the same optimized build id.
+ virtual void consumeOptimizedEncoding(const uint8_t* begin,
+ size_t length) = 0;
+
+ // Provides optional stream attributes such as base or source mapping URLs.
+ // Necessarily called before consumeChunk(), streamEnd(), streamError() or
+ // consumeOptimizedEncoding(). The caller retains ownership of the strings.
+ virtual void noteResponseURLs(const char* maybeUrl,
+ const char* maybeSourceMapUrl) = 0;
+};
+
+enum class MimeType { Wasm };
+
+using ConsumeStreamCallback = bool (*)(JSContext*, JS::HandleObject, MimeType,
+ StreamConsumer*);
+
+using ReportStreamErrorCallback = void (*)(JSContext*, size_t);
+
+extern JS_PUBLIC_API void InitConsumeStreamCallback(
+ JSContext* cx, ConsumeStreamCallback consume,
+ ReportStreamErrorCallback report);
+
+} // namespace JS
+
+#endif // js_StreamConsumer_h
diff --git a/js/public/String.h b/js/public/String.h
new file mode 100644
index 0000000000..3e003f291c
--- /dev/null
+++ b/js/public/String.h
@@ -0,0 +1,556 @@
+/* -*- 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
+#include "mozilla/Likely.h" // MOZ_LIKELY
+#include "mozilla/Maybe.h" // mozilla::Maybe
+#include "mozilla/Range.h" // mozilla::Range
+#include "mozilla/Span.h" // mozilla::Span
+ // std::tuple
+
+#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/CharacterEncoding.h" // JS::UTF8Chars, JS::ConstUTF8CharsZ
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/TypeDecls.h" // JS::Latin1Char
+#include "js/UniquePtr.h" // JS::UniquePtr
+#include "js/Utility.h" // JS::FreePolicy, JS::UniqueTwoByteChars
+#include "js/Value.h" // JS::Value
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSAtom;
+class JSLinearString;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+
+class JS_PUBLIC_API AutoRequireNoGC;
+
+} // namespace JS
+
+extern JS_PUBLIC_API JSString* JS_GetEmptyString(JSContext* cx);
+
+// Don't want to export data, so provide accessors for non-inline Values.
+extern JS_PUBLIC_API JS::Value JS_GetEmptyStringValue(JSContext* cx);
+
+/*
+ * String creation.
+ *
+ * NB: JS_NewUCString takes ownership of bytes on success, avoiding a copy;
+ * but on error (signified by null return), it leaves chars owned by the
+ * caller. So the caller must free bytes in the error case, if it has no use
+ * for them. In contrast, all the JS_New*StringCopy* functions do not take
+ * ownership of the character memory passed to them -- they copy it.
+ */
+
+extern JS_PUBLIC_API JSString* JS_NewStringCopyN(JSContext* cx, const char* s,
+ size_t n);
+
+extern JS_PUBLIC_API JSString* JS_NewStringCopyZ(JSContext* cx, const char* s);
+
+extern JS_PUBLIC_API JSString* JS_NewStringCopyUTF8Z(
+ JSContext* cx, const JS::ConstUTF8CharsZ s);
+
+extern JS_PUBLIC_API JSString* JS_NewStringCopyUTF8N(JSContext* cx,
+ const JS::UTF8Chars& s);
+
+extern JS_PUBLIC_API JSString* JS_AtomizeStringN(JSContext* cx, const char* s,
+ size_t length);
+
+extern JS_PUBLIC_API JSString* JS_AtomizeString(JSContext* cx, const char* s);
+
+// Note: unlike the non-pinning JS_Atomize* functions, this can be called
+// without entering a realm/zone.
+extern JS_PUBLIC_API JSString* JS_AtomizeAndPinStringN(JSContext* cx,
+ const char* s,
+ size_t length);
+
+// Note: unlike the non-pinning JS_Atomize* functions, this can be called
+// without entering a realm/zone.
+extern JS_PUBLIC_API JSString* JS_AtomizeAndPinString(JSContext* cx,
+ const char* s);
+
+extern JS_PUBLIC_API JSString* JS_NewLatin1String(
+ JSContext* cx, js::UniquePtr<JS::Latin1Char[], JS::FreePolicy> chars,
+ size_t length);
+
+extern JS_PUBLIC_API JSString* JS_NewUCString(JSContext* cx,
+ JS::UniqueTwoByteChars chars,
+ size_t length);
+
+extern JS_PUBLIC_API JSString* JS_NewUCStringDontDeflate(
+ JSContext* cx, JS::UniqueTwoByteChars chars, size_t length);
+
+extern JS_PUBLIC_API JSString* JS_NewUCStringCopyN(JSContext* cx,
+ const char16_t* s, size_t n);
+
+extern JS_PUBLIC_API JSString* JS_NewUCStringCopyZ(JSContext* cx,
+ const char16_t* s);
+
+extern JS_PUBLIC_API JSString* JS_AtomizeUCStringN(JSContext* cx,
+ const char16_t* s,
+ size_t length);
+
+extern JS_PUBLIC_API JSString* JS_AtomizeUCString(JSContext* cx,
+ const char16_t* s);
+
+extern JS_PUBLIC_API bool JS_CompareStrings(JSContext* cx, JSString* str1,
+ JSString* str2, int32_t* result);
+
+[[nodiscard]] extern JS_PUBLIC_API bool JS_StringEqualsAscii(
+ JSContext* cx, JSString* str, const char* asciiBytes, bool* match);
+
+// Same as above, but when the length of asciiBytes (excluding the
+// trailing null, if any) is known.
+[[nodiscard]] extern JS_PUBLIC_API bool JS_StringEqualsAscii(
+ JSContext* cx, JSString* str, const char* asciiBytes, size_t length,
+ bool* match);
+
+template <size_t N>
+[[nodiscard]] bool JS_StringEqualsLiteral(JSContext* cx, JSString* str,
+ const char (&asciiBytes)[N],
+ bool* match) {
+ MOZ_ASSERT(asciiBytes[N - 1] == '\0');
+ return JS_StringEqualsAscii(cx, str, asciiBytes, N - 1, match);
+}
+
+extern JS_PUBLIC_API size_t JS_PutEscapedString(JSContext* cx, char* buffer,
+ size_t size, JSString* str,
+ char quote);
+
+/*
+ * Extracting string characters and length.
+ *
+ * While getting the length of a string is infallible, getting the chars can
+ * fail. As indicated by the lack of a JSContext parameter, there are two
+ * special cases where getting the chars is infallible:
+ *
+ * The first case is for strings that have been atomized, e.g. directly by
+ * JS_AtomizeAndPinString or implicitly because it is stored in a jsid.
+ *
+ * The second case is "linear" strings that have been explicitly prepared in a
+ * fallible context by JS_EnsureLinearString. To catch errors, a separate opaque
+ * JSLinearString type is returned by JS_EnsureLinearString and expected by
+ * JS_Get{Latin1,TwoByte}StringCharsAndLength. Note, though, that this is purely
+ * a syntactic distinction: the input and output of JS_EnsureLinearString are
+ * the same actual GC-thing. If a JSString is known to be linear,
+ * JS_ASSERT_STRING_IS_LINEAR can be used to make a debug-checked cast. Example:
+ *
+ * // In a fallible context.
+ * JSLinearString* lstr = JS_EnsureLinearString(cx, str);
+ * if (!lstr) {
+ * return false;
+ * }
+ * MOZ_ASSERT(lstr == JS_ASSERT_STRING_IS_LINEAR(str));
+ *
+ * // In an infallible context, for the same 'str'.
+ * AutoCheckCannotGC nogc;
+ * const char16_t* chars = JS::GetTwoByteLinearStringChars(nogc, lstr)
+ * MOZ_ASSERT(chars);
+ *
+ * Note: JS strings (including linear strings and atoms) are not
+ * null-terminated!
+ *
+ * Additionally, string characters are stored as either Latin1Char (8-bit)
+ * or char16_t (16-bit). Clients can use JS::StringHasLatin1Chars and can then
+ * call either the Latin1* or TwoByte* functions. Some functions like
+ * JS_CopyStringChars and JS_GetStringCharAt accept both Latin1 and TwoByte
+ * strings.
+ */
+
+extern JS_PUBLIC_API size_t JS_GetStringLength(JSString* str);
+
+extern JS_PUBLIC_API bool JS_StringIsLinear(JSString* str);
+
+extern JS_PUBLIC_API const JS::Latin1Char* JS_GetLatin1StringCharsAndLength(
+ JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
+ size_t* length);
+
+extern JS_PUBLIC_API const char16_t* JS_GetTwoByteStringCharsAndLength(
+ JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
+ size_t* length);
+
+extern JS_PUBLIC_API bool JS_GetStringCharAt(JSContext* cx, JSString* str,
+ size_t index, char16_t* res);
+
+extern JS_PUBLIC_API const char16_t* JS_GetTwoByteExternalStringChars(
+ JSString* str);
+
+extern JS_PUBLIC_API bool JS_CopyStringChars(
+ JSContext* cx, const mozilla::Range<char16_t>& dest, JSString* str);
+
+/**
+ * Copies the string's characters to a null-terminated char16_t buffer.
+ *
+ * Returns nullptr on OOM.
+ */
+extern JS_PUBLIC_API JS::UniqueTwoByteChars JS_CopyStringCharsZ(JSContext* cx,
+ JSString* str);
+
+extern JS_PUBLIC_API JSLinearString* JS_EnsureLinearString(JSContext* cx,
+ JSString* str);
+
+static MOZ_ALWAYS_INLINE JSLinearString* JS_ASSERT_STRING_IS_LINEAR(
+ JSString* str) {
+ MOZ_ASSERT(JS_StringIsLinear(str));
+ return reinterpret_cast<JSLinearString*>(str);
+}
+
+static MOZ_ALWAYS_INLINE JSString* JS_FORGET_STRING_LINEARNESS(
+ JSLinearString* str) {
+ return reinterpret_cast<JSString*>(str);
+}
+
+/*
+ * Additional APIs that avoid fallibility when given a linear string.
+ */
+
+extern JS_PUBLIC_API bool JS_LinearStringEqualsAscii(JSLinearString* str,
+ const char* asciiBytes);
+extern JS_PUBLIC_API bool JS_LinearStringEqualsAscii(JSLinearString* str,
+ const char* asciiBytes,
+ size_t length);
+
+template <size_t N>
+bool JS_LinearStringEqualsLiteral(JSLinearString* str,
+ const char (&asciiBytes)[N]) {
+ MOZ_ASSERT(asciiBytes[N - 1] == '\0');
+ return JS_LinearStringEqualsAscii(str, asciiBytes, N - 1);
+}
+
+extern JS_PUBLIC_API size_t JS_PutEscapedLinearString(char* buffer, size_t size,
+ JSLinearString* str,
+ char quote);
+
+/**
+ * Create a dependent string, i.e., a string that owns no character storage,
+ * but that refers to a slice of another string's chars. Dependent strings
+ * are mutable by definition, so the thread safety comments above apply.
+ */
+extern JS_PUBLIC_API JSString* JS_NewDependentString(JSContext* cx,
+ JS::Handle<JSString*> str,
+ size_t start,
+ size_t length);
+
+/**
+ * Concatenate two strings, possibly resulting in a rope.
+ * See above for thread safety comments.
+ */
+extern JS_PUBLIC_API JSString* JS_ConcatStrings(JSContext* cx,
+ JS::Handle<JSString*> left,
+ JS::Handle<JSString*> right);
+
+/**
+ * For JS_DecodeBytes, set *dstlenp to the size of the destination buffer before
+ * the call; on return, *dstlenp contains the number of characters actually
+ * stored. To determine the necessary destination buffer size, make a sizing
+ * call that passes nullptr for dst.
+ *
+ * On errors, the functions report the error. In that case, *dstlenp contains
+ * the number of characters or bytes transferred so far. If cx is nullptr, no
+ * error is reported on failure, and the functions simply return false.
+ *
+ * NB: This function does not store an additional zero byte or char16_t after
+ * the transcoded string.
+ */
+JS_PUBLIC_API bool JS_DecodeBytes(JSContext* cx, const char* src, size_t srclen,
+ char16_t* dst, size_t* dstlenp);
+
+/**
+ * Get number of bytes in the string encoding (without accounting for a
+ * terminating zero bytes. The function returns (size_t) -1 if the string
+ * can not be encoded into bytes and reports an error using cx accordingly.
+ */
+JS_PUBLIC_API size_t JS_GetStringEncodingLength(JSContext* cx, JSString* str);
+
+/**
+ * Encode string into a buffer. The function does not stores an additional
+ * zero byte. The function returns (size_t) -1 if the string can not be
+ * encoded into bytes with no error reported. Otherwise it returns the number
+ * of bytes that are necessary to encode the string. If that exceeds the
+ * length parameter, the string will be cut and only length bytes will be
+ * written into the buffer.
+ */
+[[nodiscard]] JS_PUBLIC_API bool JS_EncodeStringToBuffer(JSContext* cx,
+ JSString* str,
+ char* buffer,
+ size_t length);
+
+/**
+ * Encode as many scalar values of the string as UTF-8 as can fit
+ * into the caller-provided buffer replacing unpaired surrogates
+ * with the REPLACEMENT CHARACTER.
+ *
+ * If JS::StringHasLatin1Chars(str) returns true, the function
+ * is guaranteed to convert the entire string if
+ * buffer.Length() >= 2 * JS_GetStringLength(str). Otherwise,
+ * the function is guaranteed to convert the entire string if
+ * buffer.Length() >= 3 * JS_GetStringLength(str).
+ *
+ * This function does not alter the representation of |str| or
+ * any |JSString*| substring that is a constituent part of it.
+ * Returns mozilla::Nothing() on OOM, without reporting an error;
+ * some data may have been written to |buffer| when this happens.
+ *
+ * If there's no OOM, returns the number of code units read and
+ * the number of code units written.
+ *
+ * The semantics of this method match the semantics of
+ * TextEncoder.encodeInto().
+ *
+ * The function does not store an additional zero byte.
+ */
+JS_PUBLIC_API mozilla::Maybe<std::tuple<size_t, size_t>>
+JS_EncodeStringToUTF8BufferPartial(JSContext* cx, JSString* str,
+ mozilla::Span<char> buffer);
+
+namespace JS {
+
+/**
+ * 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 latin-1 storage, return true
+ * and set |*callbacks| to the external-string callbacks used to create it and
+ * |*chars| to a pointer to its latin1 storage. (These pointers remain valid
+ * as long as the provided string is kept alive.)
+ */
+MOZ_ALWAYS_INLINE bool IsExternalStringLatin1(
+ JSString* str, const JSExternalStringCallbacks** callbacks,
+ const JS::Latin1Char** chars) {
+ shadow::String* s = shadow::AsShadowString(str);
+
+ if (!s->isExternal() || !s->hasLatin1Chars()) {
+ return false;
+ }
+
+ *callbacks = s->externalCallbacks;
+ *chars = s->nonInlineCharsLatin1;
+ return true;
+}
+
+/**
+ * If the provided string uses externally-managed two-byte 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 IsExternalUCString(
+ JSString* str, const JSExternalStringCallbacks** callbacks,
+ const char16_t** chars) {
+ shadow::String* s = shadow::AsShadowString(str);
+
+ if (!s->isExternal() || s->hasLatin1Chars()) {
+ return false;
+ }
+
+ *callbacks = s->externalCallbacks;
+ *chars = s->nonInlineCharsTwoByte;
+ return true;
+}
+
+namespace detail {
+
+extern JS_PUBLIC_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.
+ */
+[[nodiscard]] inline 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.
+ */
+[[nodiscard]] inline 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);
+
+// JSString* is an aligned pointer, but this information isn't available in the
+// public header. We specialize HasFreeLSB here so that JS::Result<JSString*>
+// compiles.
+
+namespace mozilla {
+namespace detail {
+template <>
+struct HasFreeLSB<JSString*> {
+ static constexpr bool value = true;
+};
+} // namespace detail
+} // namespace mozilla
+
+#endif // js_String_h
diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h
new file mode 100644
index 0000000000..7fef48ee24
--- /dev/null
+++ b/js/public/StructuredClone.h
@@ -0,0 +1,783 @@
+/* -*- 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/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,
+};
+
+/** Values used to describe the ownership individual Transferables.
+ *
+ * Note that these *can* show up in DifferentProcess clones, since
+ * DifferentProcess ArrayBuffers can be Transferred. In that case, this will
+ * distinguish the specific ownership mechanism: is it a malloc pointer or a
+ * memory mapping? */
+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 enum 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 = SCTAG_TMO_FIRST_OWNED,
+
+ /** 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. */
+ SCTAG_TMO_CUSTOM = 4,
+
+ /**
+ * Same as SCTAG_TMO_CUSTOM, but the embedding can also use
+ * SCTAG_TMO_USER_MIN and greater, up to 2^32-1, to distinguish specific
+ * ownership variants.
+ */
+ 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, or raise an exception and return nullptr on
+ * error.
+ */
+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. On error, an exception should
+ * normally be set.
+ */
+typedef bool (*WriteStructuredCloneOp)(JSContext* cx,
+ JSStructuredCloneWriter* w,
+ JS::HandleObject obj,
+ bool* sameProcessScopeRequired,
+ void* closure);
+
+/**
+ * This is called when serialization or deserialization encounters an error.
+ * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
+ * with error set to one of the JS_SCERR_* values.
+ *
+ * Note that if the .reportError field of the JSStructuredCloneCallbacks is
+ * set (to a function with this signature), then an exception will *not* be
+ * set on the JSContext when an error is encountered. The clone operation
+ * will still be aborted and will return false, however, so it is up to the
+ * embedding to do what it needs to for the error.
+ *
+ * Example: for the DOM, mozilla::dom::StructuredCloneHolder will save away
+ * the error message during its reportError callback. Then when the overall
+ * operation fails, it will clear any exception that might have been set
+ * from other ways to fail and pass the saved error message to
+ * ErrorResult::ThrowDataCloneError().
+ */
+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.
+ *
+ * If this readTransfer() hook is called and produces an object, then the
+ * read() hook will *not* be called for the same object, since the main data
+ * will only contain a backreference to the already-read object.
+ */
+typedef bool (*ReadTransferStructuredCloneOp)(
+ JSContext* cx, JSStructuredCloneReader* r,
+ const JS::CloneDataPolicy& aCloneDataPolicy, 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 a transferable handled by the embedding. Note that it
+ * should never trigger a garbage collection (and will assert in a
+ * debug build if it does.)
+ *
+ * This callback will be used to release ownership in three situations:
+ *
+ * 1. During serialization: an object is Transferred from, then an error is
+ * encountered later and the incomplete serialization is discarded.
+ *
+ * 2. During deserialization: before an object is Transferred to, an error
+ * is encountered and the incompletely deserialized clone is discarded.
+ *
+ * 3. Serialized data that includes Transferring is never deserialized (eg when
+ * the receiver disappears before reading in the message), and the clone data
+ * is destroyed.
+ *
+ */
+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 a 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();
+
+ [[nodiscard]] bool acquire(JSContext* cx, SharedArrayRawBuffer* rawbuf);
+ [[nodiscard]] 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) {}
+
+ // 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,
+ OwnTransferablePolicy ownership)
+ : bufList_(std::move(buffers)),
+ scope_(scope),
+ ownTransferables_(ownership) {}
+ JSStructuredCloneData(JSStructuredCloneData&& other) = default;
+ JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default;
+ ~JSStructuredCloneData();
+
+ void setCallbacks(const JSStructuredCloneCallbacks* callbacks, void* closure,
+ OwnTransferablePolicy policy) {
+ callbacks_ = callbacks;
+ closure_ = closure;
+ ownTransferables_ = policy;
+ }
+
+ [[nodiscard]] 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(); }
+
+ [[nodiscard]] bool Advance(Iterator& iter, size_t distance) const {
+ return iter.AdvanceAcrossSegments(bufList_, distance);
+ }
+
+ [[nodiscard]] 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.
+ [[nodiscard]] 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.
+ [[nodiscard]] 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(),
+ IgnoreTransferablesIfAny);
+ }
+
+ // 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.
+ [[nodiscard]] 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 ownership of the buffer and assign it and ownership of it to
+ * `data`.
+ */
+ void giveTo(JSStructuredCloneData* data);
+
+ 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_ReadString(JSStructuredCloneReader* r,
+ JS::MutableHandleString str);
+
+JS_PUBLIC_API bool JS_ReadDouble(JSStructuredCloneReader* r, double* v);
+
+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_WriteDouble(JSStructuredCloneWriter* w, double v);
+
+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/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/TelemetryTimers.h b/js/public/TelemetryTimers.h
new file mode 100644
index 0000000000..a58f9efcad
--- /dev/null
+++ b/js/public/TelemetryTimers.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_TelemetryTimers_h
+#define js_TelemetryTimers_h
+
+#include "mozilla/TimeStamp.h"
+
+#include "jstypes.h"
+
+struct JS_PUBLIC_API JSContext;
+
+namespace JS {
+
+/** Timing information for telemetry purposes **/
+struct JSTimers {
+ mozilla::TimeDuration executionTime; // Total time spent executing
+ mozilla::TimeDuration delazificationTime; // Total time spent delazifying
+ mozilla::TimeDuration xdrEncodingTime; // Total time spent XDR encoding
+ mozilla::TimeDuration gcTime; // Total time spent in GC
+ mozilla::TimeDuration
+ protectTime; // Total time spent protecting JIT executable memory
+ mozilla::TimeDuration
+ baselineCompileTime; // Total time spent in baseline compiler
+};
+
+extern JS_PUBLIC_API JSTimers GetJSTimers(JSContext* cx);
+
+} // namespace JS
+
+#endif // js_TelemetryTimers_h
diff --git a/js/public/TraceKind.h b/js/public/TraceKind.h
new file mode 100644
index 0000000000..268db56957
--- /dev/null
+++ b/js/public/TraceKind.h
@@ -0,0 +1,273 @@
+/* -*- 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 GetterSetter;
+class PropMap;
+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.
+ // Note: Nursery allocatable kinds go first. See js::gc::NurseryTraceKinds.
+ Object = 0x00,
+ BigInt = 0x01,
+ String = 0x02,
+ Symbol = 0x03,
+
+ // Shape details are exposed through JS_TraceShapeCycleCollectorChildren.
+ Shape = 0x04,
+
+ BaseShape = 0x05,
+
+ // The kind associated with a nullptr.
+ Null = 0x06,
+
+ // The following kinds do not have an exposed C++ idiom.
+ JitCode,
+ Script,
+ Scope,
+ RegExpShared,
+ GetterSetter,
+ PropMap,
+};
+
+// GCCellPtr packs the trace kind into the low bits of the pointer for common
+// kinds.
+const static uintptr_t OutOfLineTraceKindMask = 0x07;
+static_assert(uintptr_t(JS::TraceKind::Null) < OutOfLineTraceKindMask,
+ "GCCellPtr requires an inline representation for nullptr");
+
+// 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(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) \
+ D(GetterSetter, js::GetterSetter, true, true) \
+ D(PropMap, js::PropMap, false, false)
+// 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/TracingAPI.h b/js/public/TracingAPI.h
new file mode 100644
index 0000000000..4db1b0a30c
--- /dev/null
+++ b/js/public/TracingAPI.h
@@ -0,0 +1,425 @@
+/* -*- 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_PUBLIC_API const char* GCTraceKindToAscii(JS::TraceKind kind);
+
+/** Returns the base size in bytes of the GC thing of kind |kind|. */
+JS_PUBLIC_API size_t GCTraceKindSize(JS::TraceKind kind);
+
+// Kinds of JSTracer.
+enum class TracerKind {
+ // Generic tracers: Internal tracers that have a different virtual method
+ // called for each edge kind.
+ Marking,
+ Tenuring,
+ Moving,
+ ClearEdges,
+ Sweeping,
+ MinorSweeping,
+ Barrier,
+
+ // 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,
+ CompartmentCheck,
+};
+
+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 };
+
+struct TraceOptions {
+ JS::WeakMapTraceAction weakMapAction = WeakMapTraceAction::TraceValues;
+ JS::WeakEdgeTraceAction weakEdgeAction = WeakEdgeTraceAction::Trace;
+
+ TraceOptions() = default;
+ TraceOptions(JS::WeakMapTraceAction weakMapActionArg,
+ JS::WeakEdgeTraceAction weakEdgeActionArg)
+ : weakMapAction(weakMapActionArg), weakEdgeAction(weakEdgeActionArg) {}
+ MOZ_IMPLICIT TraceOptions(JS::WeakMapTraceAction weakMapActionArg)
+ : weakMapAction(weakMapActionArg) {}
+ MOZ_IMPLICIT TraceOptions(JS::WeakEdgeTraceAction weakEdgeActionArg)
+ : weakEdgeAction(weakEdgeActionArg) {}
+};
+
+class AutoTracingIndex;
+
+// 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 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(const char* name, 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 AutoTracingIndex;
+ size_t index_ = InvalidIndex;
+
+ friend class AutoTracingDetails;
+ Functor* functor_ = nullptr;
+};
+
+} // namespace JS
+
+class JS_PUBLIC_API JSTracer {
+ public:
+ // Return the runtime set on the tracer.
+ JSRuntime* runtime() const { return runtime_; }
+
+ JS::TracerKind kind() const { return kind_; }
+ bool isGenericTracer() const { return kind_ < JS::TracerKind::Callback; }
+ bool isCallbackTracer() const { return kind_ >= JS::TracerKind::Callback; }
+ bool isMarkingTracer() const { return kind_ == JS::TracerKind::Marking; }
+ bool isTenuringTracer() const { return kind_ == JS::TracerKind::Tenuring; }
+
+ inline JS::CallbackTracer* asCallbackTracer();
+
+ JS::WeakMapTraceAction weakMapAction() const {
+ return options_.weakMapAction;
+ }
+ bool traceWeakEdges() const {
+ return options_.weakEdgeAction == JS::WeakEdgeTraceAction::Trace;
+ }
+
+ JS::TracingContext& context() { return context_; }
+
+ // 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.
+#define DEFINE_ON_EDGE_METHOD(name, type, _1, _2) \
+ virtual void on##name##Edge(type** thingp, const char* name) = 0;
+ JS_FOR_EACH_TRACEKIND(DEFINE_ON_EDGE_METHOD)
+#undef DEFINE_ON_EDGE_METHOD
+
+ protected:
+ JSTracer(JSRuntime* rt, JS::TracerKind kind,
+ JS::TraceOptions options = JS::TraceOptions())
+ : runtime_(rt), kind_(kind), options_(options) {}
+
+ private:
+ JSRuntime* const runtime_;
+ const JS::TracerKind kind_;
+ const JS::TraceOptions options_;
+ JS::TracingContext context_;
+};
+
+namespace js {
+
+// A CRTP helper class that implements a JSTracer by calling a template method
+// on the derived tracer type for each edge kind.
+template <typename T>
+class GenericTracerImpl : public JSTracer {
+ public:
+ GenericTracerImpl(JSRuntime* rt, JS::TracerKind kind,
+ JS::TraceOptions options)
+ : JSTracer(rt, kind, options) {}
+
+ private:
+ T* derived() { return static_cast<T*>(this); }
+
+#define DEFINE_ON_EDGE_METHOD(name, type, _1, _2) \
+ void on##name##Edge(type** thingp, const char* name) final { \
+ derived()->onEdge(thingp, name); \
+ }
+ JS_FOR_EACH_TRACEKIND(DEFINE_ON_EDGE_METHOD)
+#undef DEFINE_ON_EDGE_METHOD
+};
+
+} // namespace js
+
+namespace JS {
+
+class JS_PUBLIC_API CallbackTracer
+ : public js::GenericTracerImpl<CallbackTracer> {
+ public:
+ CallbackTracer(JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Callback,
+ JS::TraceOptions options = JS::TraceOptions())
+ : GenericTracerImpl(rt, kind, options) {
+ MOZ_ASSERT(isCallbackTracer());
+ }
+ CallbackTracer(JSContext* cx, JS::TracerKind kind = JS::TracerKind::Callback,
+ JS::TraceOptions options = JS::TraceOptions());
+
+ // Override this method to receive notification when a node in the GC
+ // heap graph is visited.
+ virtual void onChild(JS::GCCellPtr thing, const char* name) = 0;
+
+ private:
+ template <typename T>
+ void onEdge(T** thingp, const char* name) {
+ onChild(JS::GCCellPtr(*thingp), name);
+ }
+ friend class js::GenericTracerImpl<CallbackTracer>;
+};
+
+// Set the index portion of the tracer's context for the current range.
+class MOZ_RAII AutoTracingIndex {
+ JSTracer* trc_;
+
+ public:
+ explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(trc) {
+ MOZ_ASSERT(trc_->context().index_ == TracingContext::InvalidIndex);
+ trc_->context().index_ = initial;
+ }
+ ~AutoTracingIndex() {
+ MOZ_ASSERT(trc_->context().index_ != TracingContext::InvalidIndex);
+ trc_->context().index_ = TracingContext::InvalidIndex;
+ }
+
+ void operator++() {
+ MOZ_ASSERT(trc_->context().index_ != TracingContext::InvalidIndex);
+ ++trc_->context().index_;
+ }
+};
+
+// Set a context callback for the trace callback to use, if it needs a detailed
+// edge description.
+class MOZ_RAII AutoTracingDetails {
+ JSTracer* trc_;
+
+ public:
+ AutoTracingDetails(JSTracer* trc, TracingContext::Functor& func) : trc_(trc) {
+ MOZ_ASSERT(trc_->context().functor_ == nullptr);
+ trc_->context().functor_ = &func;
+ }
+ ~AutoTracingDetails() {
+ MOZ_ASSERT(trc_->context().functor_);
+ trc_->context().functor_ = nullptr;
+ }
+};
+
+// Save and clear tracing context when performing nested tracing.
+class MOZ_RAII AutoClearTracingContext {
+ JSTracer* trc_;
+ TracingContext prev_;
+
+ public:
+ explicit AutoClearTracingContext(JSTracer* trc)
+ : trc_(trc), prev_(trc->context()) {
+ trc_->context() = TracingContext();
+ }
+
+ ~AutoClearTracingContext() { trc_->context() = prev_; }
+};
+
+} // namespace JS
+
+JS::CallbackTracer* JSTracer::asCallbackTracer() {
+ MOZ_ASSERT(isCallbackTracer());
+ return static_cast<JS::CallbackTracer*>(this);
+}
+
+namespace js {
+
+class AbstractGeneratorObject;
+class SavedFrame;
+namespace wasm {
+class AnyRef;
+} // namespace wasm
+
+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::TraceRoot| 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_TRACE_ROOT(type) \
+ extern JS_PUBLIC_API void TraceRoot(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_TRACE_ROOT)
+JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_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_TRACE_ROOT(js::AbstractGeneratorObject*)
+JS_DECLARE_TRACE_ROOT(js::SavedFrame*)
+JS_DECLARE_TRACE_ROOT(js::wasm::AnyRef)
+
+#undef JS_DECLARE_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** 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 TraceWeakEdge(JSTracer* trc, JS::Heap<T>* thingp);
+
+} // namespace gc
+
+#ifdef DEBUG
+/*
+ * Return whether the runtime is currently being destroyed, for use in
+ * assertions.
+ */
+extern JS_PUBLIC_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..9a5c6102ad
--- /dev/null
+++ b/js/public/Transcoding.h
@@ -0,0 +1,136 @@
+/* -*- 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 JS_PUBLIC_API ReadOnlyCompileOptions;
+
+using TranscodeBuffer = mozilla::Vector<uint8_t>;
+using TranscodeRange = mozilla::Range<const 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;
+};
+
+enum class TranscodeResult : uint8_t {
+ // Successful encoding / decoding.
+ Ok = 0,
+
+ // A warning message, is set to the message out-param.
+ Failure = 0x10,
+ Failure_BadBuildId = Failure | 0x1,
+ Failure_AsmJSNotSupported = Failure | 0x2,
+ Failure_BadDecode = Failure | 0x3,
+
+ // There is a pending exception on the context.
+ Throw = 0x20
+};
+
+inline bool IsTranscodeFailureResult(const TranscodeResult result) {
+ uint8_t raw_result = static_cast<uint8_t>(result);
+ uint8_t raw_failure = static_cast<uint8_t>(TranscodeResult::Failure);
+ TranscodeResult masked =
+ static_cast<TranscodeResult>(raw_result & raw_failure);
+ return masked == TranscodeResult::Failure;
+}
+
+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(const void* offset) {
+ return IsTranscodingBytecodeOffsetAligned(size_t(offset));
+}
+
+// Finish incremental encoding started by JS::StartIncrementalEncoding.
+//
+// * Regular script case
+// the |script| argument must be the top-level script returned from
+// |JS::InstantiateGlobalStencil| with the same stencil
+//
+// * Module script case
+// the |script| argument must be the script returned by
+// |JS::GetModuleScript| called on the module returned by
+// |JS::InstantiateModuleStencil| with the same stencil
+//
+// NOTE: |JS::GetModuleScript| doesn't work after evaluating the
+// module script. For the case, use Handle<JSObject*> variant of
+// this function below.
+//
+// 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.
+//
+// |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);
+
+// Similar to |JS::FinishIncrementalEncoding|, but receives module obect.
+//
+// The |module| argument must be the module returned by
+// |JS::InstantiateModuleStencil| with the same stencil that's passed to
+// |JS::StartIncrementalEncoding|.
+extern JS_PUBLIC_API bool FinishIncrementalEncoding(JSContext* cx,
+ Handle<JSObject*> module,
+ TranscodeBuffer& buffer);
+
+// Abort incremental encoding started by JS::StartIncrementalEncoding.
+extern JS_PUBLIC_API void AbortIncrementalEncoding(Handle<JSScript*> script);
+extern JS_PUBLIC_API void AbortIncrementalEncoding(Handle<JSObject*> module);
+
+// Check if the compile options and script's flag matches.
+//
+// JS::DecodeScript* and JS::DecodeOffThreadScript internally check this.
+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..1686b19173
--- /dev/null
+++ b/js/public/TypeDecls.h
@@ -0,0 +1,151 @@
+/* -*- 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 <stdint.h> // uint8_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+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 JSObject;
+struct JS_PUBLIC_API JSRuntime;
+class JS_PUBLIC_API JSScript;
+class JS_PUBLIC_API JSString;
+
+struct JSPrincipals;
+
+namespace js {
+class JS_PUBLIC_API TempAllocPolicy;
+}; // namespace js
+
+namespace JS {
+
+class JS_PUBLIC_API GCContext;
+class JS_PUBLIC_API PropertyKey;
+
+typedef unsigned char Latin1Char;
+
+class JS_PUBLIC_API Symbol;
+class JS_PUBLIC_API BigInt;
+#ifdef ENABLE_RECORD_TUPLE
+class JS_PUBLIC_API RecordType;
+class JS_PUBLIC_API TupleType;
+#endif
+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;
+
+#ifdef ENABLE_RECORD_TUPLE
+// This takes 1 or 2 parameters. ... is just used so that
+// it's possible to omit the comma when passing a single
+// param:
+// IF_RECORD_TUPLE(doThis)
+// IF_RECORD_TUPLE(doThis, elseThis)
+# define IF_RECORD_TUPLE(x, ...) x
+#else
+# define IF_RECORD_TUPLE(x, ...) __VA_ARGS__
+#endif
+
+// Follows the same pattern as IF_RECORD_TUPLE
+#ifdef ENABLE_DECORATORS
+# define IF_DECORATORS(x, ...) x
+#else
+# define IF_DECORATORS(x, ...) __VA_ARGS__
+#endif
+
+#endif /* js_TypeDecls_h */
diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h
new file mode 100644
index 0000000000..15edea070e
--- /dev/null
+++ b/js/public/UbiNode.h
@@ -0,0 +1,1213 @@
+/* -*- 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 "mozilla/Vector.h"
+
+#include <utility>
+
+#include "jspubtd.h"
+
+#include "js/AllocPolicy.h"
+#include "js/ColumnNumber.h" // JS::TaggedColumnNumberOneOrigin
+#include "js/HashTable.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/UniquePtr.h"
+#include "js/Value.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 {
+
+class JS_PUBLIC_API AutoCheckCannotGC;
+
+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 (1-origin).
+ virtual uint32_t line() const = 0;
+
+ // Get this frame's column number in UTF-16 code units.
+ virtual JS::TaggedColumnNumberOneOrigin 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.
+ [[nodiscard]] virtual 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(); }
+ JS::TaggedColumnNumberOneOrigin 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); }
+ [[nodiscard]] 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 {}
+ [[nodiscard]] bool constructSavedFrameStack(
+ JSContext* cx, MutableHandleObject out) const override {
+ out.set(nullptr);
+ return true;
+ }
+
+ uint32_t line() const override { MOZ_CRASH("null JS::ubi::StackFrame"); }
+ JS::TaggedColumnNumberOneOrigin 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");
+ }
+};
+
+[[nodiscard]] 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(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. It returns a [[nodiscard]] AutoCheckCannotGC token in order
+// to enforce this. The token's lifetime must extend at least as long as the
+// RootList itself. Note that the RootList does not itself contain a nogc field,
+// which means that it is possible to store it somewhere that it can escape
+// the init()'s nogc scope. Don't do that. (Or you could call some function
+// and pass in the RootList and GC, but that would be caught.)
+//
+// Example usage:
+//
+// {
+// JS::ubi::RootList rootList(cx);
+// auto [ok, nogc] = rootList.init();
+// if (!ok()) {
+// return false;
+// }
+//
+// JS::ubi::Node root(&rootList);
+//
+// ...
+// }
+class MOZ_STACK_CLASS JS_PUBLIC_API RootList {
+ public:
+ JSContext* cx;
+ EdgeVector edges;
+ bool wantNames;
+ bool inited;
+
+ explicit RootList(JSContext* cx, bool wantNames = false);
+
+ // Find all GC roots.
+ [[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> 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.
+ [[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> init(
+ CompartmentSet& debuggees);
+ // Find only GC roots in the given Debugger object's set of debuggee
+ // compartments.
+ [[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> init(
+ HandleObject debuggees);
+
+ // Returns true if the RootList has been initialized successfully, false
+ // otherwise.
+ bool initialized() { return inited; }
+
+ // 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.
+ [[nodiscard]] 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..fc2318a153
--- /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/HashTable.h"
+#include "js/UbiNode.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..4086300580
--- /dev/null
+++ b/js/public/UbiNodeCensus.h
@@ -0,0 +1,231 @@
+/* -*- 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 "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.
+ [[nodiscard]] virtual 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.
+ [[nodiscard]] virtual 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.
+ [[nodiscard]] 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.
+ [[nodiscard]] 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) {}
+
+ [[nodiscard]] bool report(JSContext* cx, MutableHandleValue report) {
+ return rootCount->report(cx, report);
+ }
+
+ // This class needs to retain no per-node data.
+ class NodeData {};
+
+ [[nodiscard]] 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.
+[[nodiscard]] 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..7b534aa11c
--- /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.
+ [[nodiscard]] static 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`.
+ [[nodiscard]] static 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.
+ [[nodiscard]] static 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`.
+ [[nodiscard]] static 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());
+ }
+
+ [[nodiscard]] 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.
+ */
+ [[nodiscard]] 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..10d8835be9
--- /dev/null
+++ b/js/public/UbiNodePostOrder.h
@@ -0,0 +1,191 @@
+/* -*- 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 <utility>
+
+#include "js/UbiNode.h"
+#include "js/Vector.h"
+
+namespace js {
+class SystemAllocPolicy;
+}
+
+namespace JS {
+class AutoCheckCannotGC;
+
+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:
+ [[nodiscard]] 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;
+ }
+
+ [[nodiscard]] 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.
+ [[nodiscard]] 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>
+ [[nodiscard]] 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..c5bd84d4e4
--- /dev/null
+++ b/js/public/UbiNodeShortestPaths.h
@@ -0,0 +1,351 @@
+/* -*- 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/CheckedInt.h"
+#include "mozilla/Maybe.h"
+
+#include <utility>
+
+#include "js/AllocPolicy.h"
+#include "js/GCAPI.h"
+#include "js/UbiNode.h"
+#include "js/UbiNodeBreadthFirst.h"
+#include "js/UniquePtr.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) {}
+
+ [[nodiscard]] 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);
+
+ mozilla::CheckedInt<uint32_t> max = maxNumPaths;
+ max *= targets.count();
+ if (!max.isValid()) {
+ return mozilla::Nothing();
+ }
+
+ 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>
+ [[nodiscard]] 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..25e351d740
--- /dev/null
+++ b/js/public/Utility.h
@@ -0,0 +1,684 @@
+/* -*- 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_COMPILE_TIER1, // 2
+ THREAD_TYPE_WASM_COMPILE_TIER2, // 3
+ THREAD_TYPE_ION, // 4
+ THREAD_TYPE_COMPRESS, // 5
+ THREAD_TYPE_GCPARALLEL, // 6
+ THREAD_TYPE_PROMISE_TASK, // 7
+ THREAD_TYPE_ION_FREE, // 8
+ THREAD_TYPE_WASM_GENERATOR_TIER2, // 9
+ THREAD_TYPE_WORKER, // 10
+ THREAD_TYPE_DELAZIFY, // 11
+ THREAD_TYPE_DELAZIFY_FREE, // 12
+ THREAD_TYPE_MAX // Used to check shell function arguments
+};
+
+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_GENERATOR_TIER2;
+
+extern bool InitThreadType(void);
+extern void SetThreadType(ThreadType);
+extern JS_PUBLIC_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_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_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) { crash_impl(reason); }
+ MOZ_NORETURN MOZ_COLD void crash(size_t size, const char* reason) {
+ crash_impl(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
+ private:
+ static MOZ_NORETURN MOZ_COLD void crash_impl(const char* reason);
+ static MOZ_NORETURN MOZ_COLD void crash_impl(size_t size, const char* reason);
+};
+
+} /* 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) {
+ // Bug 1784164: 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(). All other memory should go through a different allocator and
+ // deallocator.
+ 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 JS::GCContext provided to the finalizer:
+ *
+ * JS::GCContext::{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) { \
+ static_assert( \
+ alignof(T) <= alignof(max_align_t), \
+ "over-aligned type is not supported by JS_DECLARE_NEW_METHODS"); \
+ 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) { \
+ static_assert( \
+ alignof(T) <= alignof(max_align_t), \
+ "over-aligned type is not supported by JS_DECLARE_NEW_ARENA_METHODS"); \
+ 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>
+[[nodiscard]] 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>
+[[nodiscard]] 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)); }
+};
+
+using UniqueChars = mozilla::UniquePtr<char[], JS::FreePolicy>;
+using UniqueTwoByteChars = mozilla::UniquePtr<char16_t[], JS::FreePolicy>;
+using UniqueLatin1Chars = mozilla::UniquePtr<JS::Latin1Char[], JS::FreePolicy>;
+using UniqueWideChars = mozilla::UniquePtr<wchar_t[], JS::FreePolicy>;
+
+} // 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..59f71da4f1
--- /dev/null
+++ b/js/public/Value.h
@@ -0,0 +1,1553 @@
+/* -*- 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 <type_traits>
+
+#include "jstypes.h"
+
+#include "js/HeapAPI.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+
+namespace JS {
+class JS_PUBLIC_API Value;
+}
+
+// [SMDOC] JS::Value Boxing Formats
+//
+// JS::Value is a 64-bit value, on all architectures. It is conceptually a
+// discriminated union of all the types of values that can be represented in SM:
+// - Object Pointers
+// - 64 bit IEEE 754 floats
+// - 32-bit integer values
+// - and quite a few more (see JSValueType)
+//
+// The ECMAScript standard specifies that ECMAScript numbers are IEEE 64-bit
+// floating-point values. A JS::Value can represent any JavaScript number
+// value directly, without referring to additional storage, or represent an
+// object, string, or other ECMAScript value, and remember which type it is.
+//
+// This may seem surprising: how can a 64-bit type hold all the 64-bit IEEE
+// values, and still distinguish them from objects, strings, and so on,
+// which have 64-bit addresses ?
+//
+// This is possible for two reasons:
+//
+// - First, ECMAScript implementations aren't required to distinguish all
+// the values the IEEE 64-bit format can represent.
+//
+// The IEEE 754 format for floating point numbers specifies that every
+// floating-point value whose 11-bit exponent field is all ones, and whose
+// 52-bit fraction field is non-zero, has the value NaN. EMCAScript requires
+// only one NaN value. This means we can use one IEEE NaN to represent
+// ECMAScript's NaN, and use all the other 2^52-2 NaN bitstrings to
+// represent the other ECMAScript values.
+//
+// - Second, on the 64 bit architectures we suppport, only the
+// lower 48 bits of an address are currently significant. The upper sixteen
+// bits are required to be the sign-extension of bit 48. Furthermore, user
+// code always runs in "positive addresses": those in which bit 48 is zero. So
+// we only actually need 47 bits to store all possible object or string
+// addresses, even on 64-bit platforms.
+//
+// Our memory initialization system ensures that all pointers we will store in
+// objects use only 47 bits. See js::gc::MapAlignedPagesRandom.
+//
+// The introduction of 5-level page tables, supporting 57-bit virtual
+// addresses, is a potential complication. For now, large addresses are
+// opt-in, and we simply don't use them.
+//
+// With a 52-bit fraction field, and 47 bits needed for the 'payload', we
+// have up to five bits left to store a 'tag' value, to indicate which
+// branch of our discriminated union is live. (In practice, one of those
+// bits is used up to simplify NaN representation; see micro-optimization 5
+// below.)
+//
+// Thus, we define JS::Value representations in terms of the IEEE 64-bit
+// floating-point format:
+//
+// - Any bitstring that IEEE calls a number or an infinity represents that
+// ECMAScript number.
+//
+// - Any bitstring that IEEE calls a NaN represents either an ECMAScript NaN
+// or a non-number ECMAScript value, as determined by a tag field stored
+// towards the most significant end of the fraction field (exactly where
+// depends on the address size). If the tag field indicates that this
+// JS::Value is an object, the fraction field's least significant end
+// holds the address of a JSObject; if a string, the address of a
+// JSString; and so on.
+//
+// To enforce this invariant, anywhere that may provide a numerical value
+// which may have a non-canonical NaN value (NaN, but not the one we've chosen
+// for ECMAScript) we must convert that to the canonical NaN. See
+// JS::CanonicalizeNaN.
+//
+// We have two boxing modes defined: NUNBOX32 and PUNBOX64.The first is
+// "NaN unboxed boxing" (or Nunboxing), as non-Number payload are stored
+// unaltered in the lower bits. The second is "Packed NaN boxing" (or
+// punboxing), which is 'logically like nunboxing, but with all the unused bits
+// sucked out' [1], as we rely on unused bits of the payload to pack the
+// payload in the lower bits using Nunboxing.
+//
+// - In NUNBOX32 the tag is stored in the least-significant bits of the high
+// word of the NaN. Since it's used on 32-bit systems, this has the nice
+// property that boxed values are simply stored in the low-word of the 8-byte
+// NaN.
+//
+// - In PUNBOX64, since we need to store more pointer bits (47, see above), the
+// tag is stored in the 5 most significant bits of the fraction adjacent to
+// the exponent.
+//
+// Tag values are carefully ordered to support a set of micro-optimizations. In
+// particular:
+//
+// 1. Object is the highest tag, to simplify isPrimitive checks. (See
+// ValueUpperExclPrimitiveTag)
+// 2. Numbers (Double and Int32) are the lowest tags, to simplify isNumber
+// checks. (See ValueUpperInclNumberTag)
+// 3. Non-GC tags are ordered before GC-tags, to simplify isGCThing checks. (See
+// ValueLowerInclGCThingTag)
+// 4. The tags for Object and Null differ by a single flipped bit, to simplify
+// toObjectOrNull. (See ValueObjectOrNullBit)
+// 5. In PUNBOX64, the most significant bit of every non-Double tag is always
+// set. This is to simplify isDouble checks. Note that the highest bitstring
+// that corresponds to a non-NaN double is -Infinity:
+// 0xfff0_0000_0000_0000
+// But the canonical hardware NaN (produced by, for example, 0/0) is:
+// 0x?ff8_0000_0000_0000
+// on all platforms with JIT support*. (The most significant bit is the sign
+// bit; it is 1 on x86, but 0 on ARM.) The most significant bit of the
+// fraction field is set, which corresponds to the most significant of the 5
+// tag bits. Because we only use tags that have the high bit set, any Value
+// represented by a bitstring less than or equal to 0xfff8_..._0000 is a
+// Double. (If we wanted to use all five bits, we could define 0x10 as
+// JSVAL_TYPE_NAN, and mask off the most significant bit of the tag for
+// IsDouble checks. This is not yet necessary, because we still have room
+// left to allocate new tags.)
+//
+// * But see JS_NONCANONICAL_HARDWARE_NAN below.
+//
+// [1]:
+// https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations#969f63bbe4eb912778c9da85feb0f5763e7a7862
+
+/* 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,
+#ifdef ENABLE_RECORD_TUPLE
+ JSVAL_TYPE_EXTENDED_PRIMITIVE = 0x0b,
+#endif
+ 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,
+#ifdef ENABLE_RECORD_TUPLE
+ ExtendedPrimitive = JSVAL_TYPE_EXTENDED_PRIMITIVE,
+#endif
+ Object = JSVAL_TYPE_OBJECT,
+};
+} // namespace JS
+
+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,
+# ifdef ENABLE_RECORD_TUPLE
+ JSVAL_TAG_EXTENDED_PRIMITIVE =
+ JSVAL_TAG_CLEAR | JSVAL_TYPE_EXTENDED_PRIMITIVE,
+# endif
+ 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,
+# ifdef ENABLE_RECORD_TUPLE
+ JSVAL_TAG_EXTENDED_PRIMITIVE =
+ JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_EXTENDED_PRIMITIVE,
+# endif
+ 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),
+# ifdef ENABLE_RECORD_TUPLE
+ JSVAL_SHIFTED_TAG_EXTENDED_PRIMITIVE =
+ (uint64_t(JSVAL_TYPE_EXTENDED_PRIMITIVE) << JSVAL_TAG_SHIFT),
+# endif
+ JSVAL_SHIFTED_TAG_OBJECT = (uint64_t(JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT)
+};
+
+static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
+ "compiler typed enum support is apparently buggy");
+
+#endif
+
+namespace JS {
+namespace detail {
+
+#if defined(JS_NUNBOX32)
+
+constexpr JSValueTag ValueTypeToTag(JSValueType type) {
+ return static_cast<JSValueTag>(JSVAL_TAG_CLEAR |
+ std::underlying_type_t<JSValueType>(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 |
+ std::underlying_type_t<JSValueType>(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;
+
+// Mask used to combine an unbox operation with getting the chunk base.
+constexpr uint64_t ValueGCThingPayloadChunkMask =
+ ValueGCThingPayloadMask & ~js::gc::ChunkMask;
+
+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,
+
+ /** 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,
+
+ /** arguments object can't be created because environment is dead. */
+ JS_MISSING_ARGUMENTS,
+
+ /** for local use */
+ JS_GENERIC_MAGIC,
+
+ /**
+ * When an error object is created without the error cause argument, we set
+ * the error's cause slot to this magic value.
+ */
+ JS_ERROR_WITHOUT_CAUSE,
+
+ JS_WHY_MAGIC_COUNT
+};
+
+namespace js {
+class JS_PUBLIC_API GenericPrinter;
+class JSONPrinter;
+
+static inline JS::Value PoisonedObjectValue(uintptr_t poison);
+#ifdef ENABLE_RECORD_TUPLE
+// Re-defined in vm/RecordTupleBoxShared.h. We cannot include that
+// file because it circularly includes this one.
+bool IsExtendedPrimitive(const JSObject& obj);
+namespace gc {
+bool MaybeForwardedIsExtendedPrimitive(const JSObject& obj);
+} // namespace gc
+#endif
+} // 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, RISC-V and modern MIPS.
+//
+// Note: The default sign bit for a hardware synthesized 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);
+}
+
+// Return the infinity the engine uses
+static MOZ_ALWAYS_INLINE double Infinity() {
+ return mozilla::BitwiseCast<double>(detail::InfinityBits);
+}
+
+// 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(std::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)) {}
+
+ 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));
+#ifdef ENABLE_RECORD_TUPLE
+ MOZ_ASSERT(!js::gc::MaybeForwardedIsExtendedPrimitive(obj));
+#endif
+ setObjectNoCheck(&obj);
+ MOZ_ASSERT(&toObject() == &obj);
+ }
+
+#ifdef ENABLE_RECORD_TUPLE
+ void setExtendedPrimitive(JSObject& obj) {
+ MOZ_ASSERT(js::gc::IsCellPointerValid(&obj));
+ MOZ_ASSERT(js::gc::MaybeForwardedIsExtendedPrimitive(obj));
+ asBits_ =
+ bitsFromTagAndPayload(JSVAL_TAG_EXTENDED_PRIMITIVE, PayloadType(&obj));
+ MOZ_ASSERT(&toExtendedPrimitive() == &obj);
+ }
+#endif
+
+ 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
+ }
+
+#ifdef ENABLE_RECORD_TUPLE
+ bool isExtendedPrimitive() const {
+ return toTag() == JSVAL_TAG_EXTENDED_PRIMITIVE;
+ }
+#endif
+
+ bool hasObjectPayload() const {
+ return isObject() || IF_RECORD_TUPLE(isExtendedPrimitive(), false);
+ }
+
+ 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;
+ }
+
+ // Like isMagic, but without the release assertion.
+ bool isMagicNoReleaseCheck(JSWhyMagic why) const {
+ if (!isMagic()) {
+ return false;
+ }
+ MOZ_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());
+ }
+#ifdef ENABLE_RECORD_TUPLE
+ if (isExtendedPrimitive()) {
+ return JS::TraceKind::Object;
+ }
+#endif
+ 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
+ }
+
+#ifdef ENABLE_RECORD_TUPLE
+ JSObject& toExtendedPrimitive() const {
+ MOZ_ASSERT(isExtendedPrimitive());
+# if defined(JS_PUNBOX64)
+ MOZ_ASSERT((asBits_ & detail::ValueGCThingPayloadMask) != 0);
+# endif
+ return *unboxGCPointer<JSObject, JSVAL_TAG_EXTENDED_PRIMITIVE>();
+ }
+#endif
+
+ JSObject& getObjectPayload() const {
+#ifdef ENABLE_RECORD_TUPLE
+ return isExtendedPrimitive() ? toExtendedPrimitive() : toObject();
+#else
+ return toObject();
+#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
+ }
+
+ 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* toPrivateUnchecked() const {
+ 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; }
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+ void dump() const;
+ void dump(js::GenericPrinter& out) const;
+ void dump(js::JSONPrinter& json) const;
+
+ void dumpFields(js::JSONPrinter& json) const;
+ void dumpStringContent(js::GenericPrinter& out) const;
+#endif
+} 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");
+
+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(v.toGCCellPtr());
+ }
+}
+
+/************************************************************************/
+
+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;
+}
+
+#ifdef ENABLE_RECORD_TUPLE
+static inline Value ExtendedPrimitiveValue(JSObject& obj) {
+ Value v;
+ v.setExtendedPrimitive(obj);
+ return v;
+}
+#endif
+
+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) {
+ // This should only be called as part of root marking since that's the only
+ // time we should trace unbarriered GC thing pointers. This will assert if
+ // called at other times.
+ TraceRoot(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); }
+ static void readBarrier(const JS::Value& v) {
+ if (v.isGCThing()) {
+ js::gc::IncrementalReadBarrier(v.toGCCellPtr());
+ }
+ }
+};
+
+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(); }
+#ifdef ENABLE_RECORD_TUPLE
+ bool isExtendedPrimitive() const { return value().isExtendedPrimitive(); }
+#endif
+ bool hasObjectPayload() const { return value().hasObjectPayload(); }
+ bool isMagic() const { return value().isMagic(); }
+ bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
+ bool isGCThing() const { return value().isGCThing(); }
+ bool isPrivateGCThing() const { return value().isPrivateGCThing(); }
+ 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(); }
+#ifdef ENABLE_RECORD_TUPLE
+ JSObject& toExtendedPrimitive() const {
+ return value().toExtendedPrimitive();
+ }
+#endif
+ JSObject& getObjectPayload() const { return value().getObjectPayload(); }
+ JS::GCCellPtr toGCCellPtr() const { return value().toGCCellPtr(); }
+ 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)); }
+#ifdef ENABLE_RECORD_TUPLE
+ void setExtendedPrimitive(JSObject& obj) {
+ return set(JS::ExtendedPrimitiveValue(obj));
+ }
+#endif
+ 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 HeapOperations<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));
+ }
+#ifdef ENABLE_RECORD_TUPLE
+ case JS::ValueType::ExtendedPrimitive:
+#endif
+ case JS::ValueType::Object: {
+ JSObject* obj = &val.getObjectPayload();
+ 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/WaitCallbacks.h b/js/public/WaitCallbacks.h
new file mode 100644
index 0000000000..b79172e6c8
--- /dev/null
+++ b/js/public/WaitCallbacks.h
@@ -0,0 +1,54 @@
+/* -*- 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_WaitCallbacks_h
+#define js_WaitCallbacks_h
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "jstypes.h"
+
+struct JS_PUBLIC_API JSRuntime;
+
+namespace JS {
+
+/**
+ * When the JSRuntime is about to block in an Atomics.wait() JS call or in a
+ * `wait` instruction in WebAssembly, it can notify the host by means of a call
+ * to BeforeWaitCallback. After the wait, it can notify the host by means of a
+ * call to AfterWaitCallback. Both callbacks must be null, or neither.
+ *
+ * (If you change the callbacks from null to not-null or vice versa while some
+ * thread on the runtime is in a wait, you will be sorry.)
+ *
+ * The argument to the BeforeWaitCallback is a pointer to uninitialized
+ * stack-allocated working memory of size WAIT_CALLBACK_CLIENT_MAXMEM bytes.
+ * The caller of SetWaitCallback() must pass the amount of memory it will need,
+ * and this amount will be checked against that limit and the process will crash
+ * reliably if the check fails.
+ *
+ * The value returned by the BeforeWaitCallback will be passed to the
+ * AfterWaitCallback.
+ *
+ * The AfterWaitCallback will be called even if the wakeup is spurious and the
+ * thread goes right back to waiting again. Of course the thread will call the
+ * BeforeWaitCallback once more before it goes to sleep in this situation.
+ */
+
+static constexpr size_t WAIT_CALLBACK_CLIENT_MAXMEM = 32;
+
+using BeforeWaitCallback = void* (*)(uint8_t* memory);
+using AfterWaitCallback = void (*)(void* cookie);
+
+extern JS_PUBLIC_API void SetWaitCallback(JSRuntime* rt,
+ BeforeWaitCallback beforeWait,
+ AfterWaitCallback afterWait,
+ size_t requiredMemory);
+
+} // namespace JS
+
+#endif // js_WaitCallbacks_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/WasmFeatures.h b/js/public/WasmFeatures.h
new file mode 100644
index 0000000000..f2089f7fba
--- /dev/null
+++ b/js/public/WasmFeatures.h
@@ -0,0 +1,274 @@
+/* -*- 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_WasmFeatures_h
+#define js_WasmFeatures_h
+
+// [SMDOC] WebAssembly feature gating
+//
+// Declarative listing of WebAssembly optional features. This macro is used to
+// generate most of the feature gating code in a centralized manner. See
+// 'Adding a feature' below for the exact steps needed to add a new feature.
+//
+// Each feature is either `DEFAULT`, `TENTATIVE`, or `EXPERIMENTAL`:
+//
+// Default features are enabled by default in ContextOptions and in the
+// JS-shell, and are given a `--no-wasm-FEATURE` shell flag to disable. The
+// `--wasm-FEATURE` flag is rejected.
+//
+// Tentative features are like Default features, but the `--wasm-FEATURE` flag
+// is silently ignored.
+//
+// Experimental features are disabled by default in ContextOptions and in the
+// JS-shell, and are given a `--wasm-FEATURE` shell flag to enable. The
+// `--no-wasm-FEATURE` flag is silently ignored.
+//
+// The browser pref is `javascript.options.wasm-FEATURE` for default, tentative,
+// and experimental features alike.
+//
+// # Adding a feature
+//
+// 1. Add a configure switch for the feature in js/moz.configure
+// 2. Add a WASM_FEATURE_ENABLED #define below
+// 3. Add the feature to JS_FOR_WASM_FEATURES
+// a. capitalized name: Used for naming of feature functions, including
+// wasmFeatureEnabled shell function.
+// b. lower case name: Used for naming of feature flag variables, including
+// in wasm::FeatureArgs.
+// c. compile predicate: Set to WASM_FEATURE_ENABLED
+// d. compiler predicate: Expression of compilers that this feature depends
+// on.
+// e. flag predicate: Expression used to predicate enablement of feature
+// flag. Useful for disabling a feature when dependent feature is not
+// enabled or if we are fuzzing.
+// f. shell flag: The stem of the JS-shell flag. Will be expanded to
+// --no-wasm-FEATURE or --wasm-FEATURE as explained above.
+// g. preference name: The stem of the browser preference. Will be expanded
+// to `javascript.options.wasm-FEATURE`.
+// 4. Add the preference to module/libpref/init/StaticPrefList.yaml
+// a. Use conditionally compiled flag
+// b. Set value to 'true' for default features, @IS_NIGHTLY_BUILD@ for
+// tentative features, and 'false' for experimental features.
+// 5. [fuzzing] Add the feature to gluesmith/src/lib.rs, if wasm-smith has
+// support for it.
+
+#ifdef ENABLE_WASM_RELAXED_SIMD
+# define WASM_RELAXED_SIMD_ENABLED 1
+#else
+# define WASM_RELAXED_SIMD_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_EXTENDED_CONST
+# define WASM_EXTENDED_CONST_ENABLED 1
+#else
+# define WASM_EXTENDED_CONST_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_FUNCTION_REFERENCES
+# define WASM_FUNCTION_REFERENCES_ENABLED 1
+#else
+# define WASM_FUNCTION_REFERENCES_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_GC
+# define WASM_GC_ENABLED 1
+#else
+# define WASM_GC_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_MEMORY64
+# define WASM_MEMORY64_ENABLED 1
+#else
+# define WASM_MEMORY64_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_MEMORY_CONTROL
+# define WASM_MEMORY_CONTROL_ENABLED 1
+#else
+# define WASM_MEMORY_CONTROL_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_TAIL_CALLS
+# define WASM_TAIL_CALLS_ENABLED 1
+#else
+# define WASM_TAIL_CALLS_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_MOZ_INTGEMM
+# define WASM_MOZ_INTGEMM_ENABLED 1
+#else
+# define WASM_MOZ_INTGEMM_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_MULTI_MEMORY
+# define WASM_MULTI_MEMORY_ENABLED 1
+#else
+# define WASM_MULTI_MEMORY_ENABLED 0
+#endif
+#ifdef ENABLE_WASM_JS_STRING_BUILTINS
+# define WASM_JS_STRING_BUILTINS_ENABLED 1
+#else
+# define WASM_JS_STRING_BUILTINS_ENABLED 0
+#endif
+
+enum class WasmFeatureStage {
+ Experimental = 0,
+ Tentative,
+ Default,
+};
+
+// clang-format off
+#define JS_FOR_WASM_FEATURES(FEATURE) \
+ FEATURE( \
+ /* capitalized name */ ExtendedConst, \
+ /* lower case name */ extendedConst, \
+ /* stage */ WasmFeatureStage::Tentative, \
+ /* compile predicate */ WASM_EXTENDED_CONST_ENABLED, \
+ /* compiler predicate */ true, \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ true, \
+ /* shell flag */ "extended-const", \
+ /* preference name */ "extended_const") \
+ FEATURE( \
+ /* capitalized name */ Exceptions, \
+ /* lower case name */ exceptions, \
+ /* stage */ WasmFeatureStage::Default, \
+ /* compile predicate */ true, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ WasmExnRefFlag(cx), \
+ /* flag fuzz enable */ true, \
+ /* shell flag */ "exceptions", \
+ /* preference name */ "exceptions") \
+ FEATURE( \
+ /* capitalized name */ ExnRef, \
+ /* lower case name */ exnref, \
+ /* stage */ WasmFeatureStage::Experimental, \
+ /* compile predicate */ true, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ true, \
+ /* shell flag */ "exnref", \
+ /* preference name */ "exnref ") \
+ FEATURE( \
+ /* capitalized name */ FunctionReferences, \
+ /* lower case name */ functionReferences, \
+ /* stage */ WasmFeatureStage::Tentative, \
+ /* compile predicate */ WASM_FUNCTION_REFERENCES_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ WasmGcFlag(cx), \
+ /* flag fuzz enable */ false, \
+ /* shell flag */ "function-references", \
+ /* preference name */ "function_references") \
+ FEATURE( \
+ /* capitalized name */ Gc, \
+ /* lower case name */ gc, \
+ /* stage */ WasmFeatureStage::Tentative, \
+ /* compile predicate */ WASM_GC_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ false, \
+ /* shell flag */ "gc", \
+ /* preference name */ "gc") \
+ FEATURE( \
+ /* capitalized name */ JSStringBuiltins, \
+ /* lower case name */ jsStringBuiltins, \
+ /* stage */ WasmFeatureStage::Experimental, \
+ /* compile predicate */ WASM_JS_STRING_BUILTINS_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ true, \
+ /* shell flag */ "js-string-builtins", \
+ /* preference name */ "js_string_builtins") \
+ FEATURE( \
+ /* capitalized name */ RelaxedSimd, \
+ /* lower case name */ v128Relaxed, \
+ /* stage */ WasmFeatureStage::Tentative, \
+ /* compile predicate */ WASM_RELAXED_SIMD_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ js::jit::JitSupportsWasmSimd(), \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ true, \
+ /* shell flag */ "relaxed-simd", \
+ /* preference name */ "relaxed_simd") \
+ FEATURE( \
+ /* capitalized name */ Memory64, \
+ /* lower case name */ memory64, \
+ /* stage */ WasmFeatureStage::Tentative, \
+ /* compile predicate */ WASM_MEMORY64_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ true, \
+ /* shell flag */ "memory64", \
+ /* preference name */ "memory64") \
+ FEATURE( \
+ /* capitalized name */ MemoryControl, \
+ /* lower case name */ memoryControl, \
+ /* stage */ WasmFeatureStage::Experimental, \
+ /* compile predicate */ WASM_MEMORY_CONTROL_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ false, \
+ /* shell flag */ "memory-control", \
+ /* preference name */ "memory_control") \
+ FEATURE( \
+ /* capitalized name */ MultiMemory, \
+ /* lower case name */ multiMemory, \
+ /* stage */ WasmFeatureStage::Experimental, \
+ /* compile predicate */ WASM_MULTI_MEMORY_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ false, \
+ /* shell flag */ "multi-memory", \
+ /* preference name */ "multi_memory") \
+ FEATURE( \
+ /* capitalized name */ TailCalls, \
+ /* lower case name */ tailCalls, \
+ /* stage */ WasmFeatureStage::Tentative, \
+ /* compile predicate */ WASM_TAIL_CALLS_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ true, \
+ /* shell flag */ "tail-calls", \
+ /* preference name */ "tail_calls") \
+ FEATURE( \
+ /* capitalized name */ MozIntGemm, \
+ /* lower case name */ mozIntGemm, \
+ /* stage */ WasmFeatureStage::Experimental, \
+ /* compile predicate */ WASM_MOZ_INTGEMM_ENABLED, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ IsSimdPrivilegedContext(cx), \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ false, \
+ /* shell flag */ "moz-intgemm", \
+ /* preference name */ "moz_intgemm") \
+ FEATURE( \
+ /* capitalized name */ TestSerialization, \
+ /* lower case name */ testSerialization, \
+ /* stage */ WasmFeatureStage::Experimental, \
+ /* compile predicate */ 1, \
+ /* compiler predicate */ IonAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ false, \
+ /* shell flag */ "test-serialization", \
+ /* preference name */ "test-serialization") \
+ FEATURE( \
+ /* capitalized name */ TestMetadata, \
+ /* lower case name */ testMetadata, \
+ /* stage */ WasmFeatureStage::Experimental, \
+ /* compile predicate */ 1, \
+ /* compiler predicate */ AnyCompilerAvailable(cx), \
+ /* flag predicate */ true, \
+ /* flag force enable */ false, \
+ /* flag fuzz enable */ false, \
+ /* shell flag */ "test-metadata", \
+ /* preference name */ "test_metadata")
+
+// clang-format on
+
+#endif // js_WasmFeatures_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/WeakMap.h b/js/public/WeakMap.h
new file mode 100644
index 0000000000..f76522c3ba
--- /dev/null
+++ b/js/public/WeakMap.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/. */
+
+/*
+ * Weak Maps.
+ */
+
+#ifndef js_WeakMap_h
+#define js_WeakMap_h
+
+#include "jspubtd.h"
+
+namespace JS {
+
+extern JS_PUBLIC_API JSObject* NewWeakMapObject(JSContext* cx);
+
+extern JS_PUBLIC_API bool IsWeakMapObject(JSObject* obj);
+
+extern JS_PUBLIC_API bool GetWeakMapEntry(JSContext* cx,
+ JS::HandleObject mapObj,
+ JS::HandleValue key,
+ JS::MutableHandleValue val);
+
+extern JS_PUBLIC_API bool SetWeakMapEntry(JSContext* cx,
+ JS::HandleObject mapObj,
+ JS::HandleValue key,
+ JS::HandleValue val);
+
+} // namespace JS
+
+#endif // js_WeakMap_h
diff --git a/js/public/WeakMapPtr.h b/js/public/WeakMapPtr.h
new file mode 100644
index 0000000000..f3fd5135d8
--- /dev/null
+++ b/js/public/WeakMapPtr.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/. */
+
+#ifndef js_WeakMapPtr_h
+#define js_WeakMapPtr_h
+
+#include "jstypes.h"
+
+#include "js/TypeDecls.h"
+
+class JS_PUBLIC_API JSTracer;
+
+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* trc);
+
+ 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..197d76f11f
--- /dev/null
+++ b/js/public/Wrapper.h
@@ -0,0 +1,505 @@
+/* -*- 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 {
+struct CompartmentFilter;
+
+/*
+ * 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_PUBLIC_API ForwardingProxyHandler : public BaseProxyHandler {
+ public:
+ using BaseProxyHandler::BaseProxyHandler;
+
+ /* Standard internal methods. */
+ virtual bool getOwnPropertyDescriptor(
+ JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::MutableHandle<mozilla::Maybe<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 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_PUBLIC_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* 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_PUBLIC_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<mozilla::Maybe<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 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_PUBLIC_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<mozilla::Maybe<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 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_PUBLIC_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_PUBLIC_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_PUBLIC_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_PUBLIC_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_PUBLIC_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_PUBLIC_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_PUBLIC_API JSObject* UncheckedUnwrapWithoutExpose(JSObject* obj);
+
+void ReportAccessDenied(JSContext* cx);
+
+JS_PUBLIC_API void NukeCrossCompartmentWrapper(JSContext* cx,
+ JSObject* wrapper);
+
+// If a cross-compartment wrapper source => target exists, nuke it.
+JS_PUBLIC_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_PUBLIC_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_PUBLIC_API bool RecomputeWrappers(JSContext* cx,
+ const CompartmentFilter& sourceFilter,
+ const CompartmentFilter& targetFilter);
+
+} /* namespace js */
+
+#endif /* js_Wrapper_h */
diff --git a/js/public/WrapperCallbacks.h b/js/public/WrapperCallbacks.h
new file mode 100644
index 0000000000..24f5aa7272
--- /dev/null
+++ b/js/public/WrapperCallbacks.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_WrapperCallbacks_h
+#define js_WrapperCallbacks_h
+
+#include "js/TypeDecls.h"
+
+/**
+ * Callback used to ask the embedding for the cross compartment wrapper handler
+ * that implements the desired prolicy for this kind of object in the
+ * destination compartment. |obj| is the object to be wrapped. If |existing| is
+ * non-nullptr, it will point to an existing wrapper object that should be
+ * re-used if possible. |existing| is guaranteed to be a cross-compartment
+ * wrapper with a lazily-defined prototype and the correct global. It is
+ * guaranteed not to wrap a function.
+ */
+using JSWrapObjectCallback = JSObject* (*)(JSContext*, JS::HandleObject,
+ JS::HandleObject);
+
+/**
+ * Callback used by the wrap hook to ask the embedding to prepare an object
+ * for wrapping in a context. This might include unwrapping other wrappers
+ * or even finding a more suitable object for the new compartment. If |origObj|
+ * is non-null, then it is the original object we are going to swap into during
+ * a transplant.
+ */
+using JSPreWrapCallback = void (*)(JSContext*, JS::HandleObject,
+ JS::HandleObject, JS::HandleObject,
+ JS::HandleObject, JS::MutableHandleObject);
+
+struct JSWrapObjectCallbacks {
+ JSWrapObjectCallback wrap;
+ JSPreWrapCallback preWrap;
+};
+
+#endif // js_WrapperCallbacks_h
diff --git a/js/public/Zone.h b/js/public/Zone.h
new file mode 100644
index 0000000000..452d7dec5e
--- /dev/null
+++ b/js/public/Zone.h
@@ -0,0 +1,86 @@
+/* -*- 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_Zone_h
+#define js_Zone_h
+
+#include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/TypeDecls.h" // JSContext, JSObject, jsid, JS::Compartment, JS::GCContext, JS::Value, JS::Zone
+
+// [SMDOC] Nested GC Data Structures (Compartments and Zones)
+//
+// The GC has two nested data structures, Zones and Compartents. Each has
+// distint responsibilities. Zones contain compartments, along with other GC
+// resources used by a tab. Compartments contain realms, which are specification
+// defined.
+//
+// See also the SMDoc on Realms.
+//
+// Compartment
+// -----------
+// Security membrane; when an object from compartment A is used in compartment
+// B, a cross-compartment wrapper (a kind of proxy) is used. In the browser,
+// same-origin realms can share a compartment.
+//
+// Zone
+// ----
+// A Zone is a group of compartments that share GC resources (arenas, strings,
+// etc) for memory usage and performance reasons. Zone is the GC unit: the GC
+// can operate on one or more zones at a time. The browser uses roughly one zone
+// per tab.
+
+using JSDestroyZoneCallback = void (*)(JS::GCContext*, JS::Zone*);
+
+using JSDestroyCompartmentCallback = void (*)(JS::GCContext*, JS::Compartment*);
+
+using JSSizeOfIncludingThisCompartmentCallback =
+ size_t (*)(mozilla::MallocSizeOf, JS::Compartment*);
+
+extern JS_PUBLIC_API void JS_SetDestroyZoneCallback(
+ JSContext* cx, JSDestroyZoneCallback callback);
+
+extern JS_PUBLIC_API void JS_SetDestroyCompartmentCallback(
+ JSContext* cx, JSDestroyCompartmentCallback callback);
+
+extern JS_PUBLIC_API void JS_SetSizeOfIncludingThisCompartmentCallback(
+ JSContext* cx, JSSizeOfIncludingThisCompartmentCallback callback);
+
+extern JS_PUBLIC_API void JS_SetCompartmentPrivate(JS::Compartment* compartment,
+ void* data);
+
+extern JS_PUBLIC_API void* JS_GetCompartmentPrivate(
+ JS::Compartment* compartment);
+
+extern JS_PUBLIC_API void JS_SetZoneUserData(JS::Zone* zone, void* data);
+
+extern JS_PUBLIC_API void* JS_GetZoneUserData(JS::Zone* zone);
+
+extern JS_PUBLIC_API bool JS_RefreshCrossCompartmentWrappers(
+ JSContext* cx, JS::Handle<JSObject*> obj);
+
+/**
+ * Mark a jsid after entering a new compartment. Different zones separately
+ * mark the ids in a runtime, and this must be used any time an id is obtained
+ * from one compartment and then used in another compartment, unless the two
+ * compartments are guaranteed to be in the same zone.
+ */
+extern JS_PUBLIC_API void JS_MarkCrossZoneId(JSContext* cx, jsid id);
+
+/**
+ * If value stores a jsid (an atomized string or symbol), mark that id as for
+ * JS_MarkCrossZoneId.
+ */
+extern JS_PUBLIC_API void JS_MarkCrossZoneIdValue(JSContext* cx,
+ const JS::Value& value);
+
+#endif // js_Zone_h
diff --git a/js/public/experimental/CTypes.h b/js/public/experimental/CTypes.h
new file mode 100644
index 0000000000..bcd3b28775
--- /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
+
+#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_PUBLIC_API void SetCTypesActivityCallback(JSContext* cx,
+ CTypesActivityCallback cb);
+
+class MOZ_RAII JS_PUBLIC_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..32729c792d
--- /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_PUBLIC_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_PUBLIC_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_PUBLIC_API JS::UniqueChars GetCodeCoverageSummary(JSContext* cx,
+ size_t* length);
+extern JS_PUBLIC_API JS::UniqueChars GetCodeCoverageSummaryAll(JSContext* cx,
+ size_t* length);
+
+} // namespace js
+
+#endif // js_experimental_CodeCoverage_h
diff --git a/js/public/experimental/CompileScript.h b/js/public/experimental/CompileScript.h
new file mode 100644
index 0000000000..308a5848d9
--- /dev/null
+++ b/js/public/experimental/CompileScript.h
@@ -0,0 +1,180 @@
+/* -*- 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 for compiling scripts to stencil without depending on
+ * JSContext. */
+
+#ifndef js_experimental_CompileScript_h
+#define js_experimental_CompileScript_h
+
+#include "jspubtd.h"
+#include "js/ErrorReport.h" // JSErrorReport
+#include "js/experimental/JSStencil.h"
+#include "js/GCAnnotations.h"
+#include "js/Modules.h"
+#include "js/Stack.h"
+#include "js/UniquePtr.h"
+
+namespace js {
+class FrontendContext;
+namespace frontend {
+struct CompilationInput;
+} // namespace frontend
+} // namespace js
+
+namespace JS {
+using FrontendContext = js::FrontendContext;
+
+// Create a new front-end context.
+JS_PUBLIC_API JS::FrontendContext* NewFrontendContext();
+
+// Destroy a front-end context allocated with NewFrontendContext.
+JS_PUBLIC_API void DestroyFrontendContext(JS::FrontendContext* fc);
+
+// Set the size of the native stack that should not be exceed. To disable
+// stack size checking pass 0.
+//
+// WARNING: When the stack size checking is enabled, the JS::FrontendContext
+// can be used only in the thread where JS::SetNativeStackQuota is called.
+JS_PUBLIC_API void SetNativeStackQuota(JS::FrontendContext* fc,
+ JS::NativeStackSize stackSize);
+
+// Return the stack quota that can be passed to SetNativeStackQuota, for given
+// stack size.
+// This subtracts a margin from given stack size, to make sure the stack quota
+// check performed internally is sufficient.
+JS_PUBLIC_API JS::NativeStackSize ThreadStackQuotaForSize(size_t stackSize);
+
+// Returns true if there was any error reported to given FrontendContext.
+JS_PUBLIC_API bool HadFrontendErrors(JS::FrontendContext* fc);
+
+// Convert the error reported to FrontendContext into runtime error in
+// JSContext. Returns false if the error cannot be converted (such as due to
+// OOM). An error might still be reported to the given JSContext. Also, returns
+// false when OOM is converted. Returns true otherwise.
+//
+// The options parameter isn't actually used, but the CompileOptions
+// provided to the compile/decode operation owns the filename pointer
+// that the error and warnings reported to FrontendContext point to,
+// so the CompileOptions must be alive until this call.
+JS_PUBLIC_API bool ConvertFrontendErrorsToRuntimeErrors(
+ JSContext* cx, JS::FrontendContext* fc,
+ const JS::ReadOnlyCompileOptions& options);
+
+// Returns an error report if given JS::FrontendContext had error and it has
+// an error report associated.
+//
+// This can be nullptr even if JS::HadFrontendErrors returned true, if
+// the error is one of:
+// * over recursed
+// * out of memory
+// * allocation overflow
+//
+// The returned pointer is valid only while the given JS::FrontendContext is
+// alive.
+//
+// See ConvertFrontendErrorsToRuntimeErrors for options parameter.
+JS_PUBLIC_API const JSErrorReport* GetFrontendErrorReport(
+ JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options);
+
+// Returns true if the JS::FrontendContext had over recuresed error.
+JS_PUBLIC_API bool HadFrontendOverRecursed(JS::FrontendContext* fc);
+
+// Returns true if the JS::FrontendContext had out of memory error.
+JS_PUBLIC_API bool HadFrontendOutOfMemory(JS::FrontendContext* fc);
+
+// Returns true if the JS::FrontendContext had allocation overflow error.
+JS_PUBLIC_API bool HadFrontendAllocationOverflow(JS::FrontendContext* fc);
+
+// Clear errors reported to the JS::FrontendContext.
+// No-op when there's no errors.
+JS_PUBLIC_API void ClearFrontendErrors(JS::FrontendContext* fc);
+
+// Returns the number of warnings reported to the JS::FrontendContext.
+JS_PUBLIC_API size_t GetFrontendWarningCount(JS::FrontendContext* fc);
+
+// Returns an error report represents the index-th warning.
+//
+// The returned pointer is valid only while the JS::FrontendContext is alive.
+//
+// See ConvertFrontendErrorsToRuntimeErrors for options parameter.
+JS_PUBLIC_API const JSErrorReport* GetFrontendWarningAt(
+ JS::FrontendContext* fc, size_t index,
+ const JS::ReadOnlyCompileOptions& options);
+
+// Temporary storage used during compiling and preparing to instantiate a
+// Stencil.
+//
+// Off-thread consumers can allocate this instance off main thread, and pass it
+// back to the main thread, in order to reduce the main thread allocation.
+struct CompilationStorage {
+ private:
+ // Owned CompilationInput.
+ //
+ // This uses raw pointer instead of UniquePtr because CompilationInput
+ // is opaque.
+ JS_HAZ_NON_GC_POINTER js::frontend::CompilationInput* input_ = nullptr;
+ bool isBorrowed_ = false;
+
+ public:
+ CompilationStorage() = default;
+ explicit CompilationStorage(js::frontend::CompilationInput* input)
+ : input_(input), isBorrowed_(true) {}
+ CompilationStorage(CompilationStorage&& other)
+ : input_(other.input_), isBorrowed_(other.isBorrowed_) {
+ other.input_ = nullptr;
+ }
+
+ ~CompilationStorage();
+
+ private:
+ CompilationStorage(const CompilationStorage& other) = delete;
+ void operator=(const CompilationStorage& aOther) = delete;
+
+ public:
+ bool hasInput() { return !!input_; }
+
+ // Internal function that initializes the CompilationInput. It should only be
+ // called once.
+ bool allocateInput(FrontendContext* fc,
+ const JS::ReadOnlyCompileOptions& options);
+
+ js::frontend::CompilationInput& getInput() {
+ MOZ_ASSERT(hasInput());
+ return *input_;
+ }
+
+ // Size of dynamic data. Note that GC data is counted by GC and not here.
+ size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ void trace(JSTracer* trc);
+};
+
+extern JS_PUBLIC_API already_AddRefed<JS::Stencil> CompileGlobalScriptToStencil(
+ JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+ JS::CompilationStorage& compileStorage);
+
+extern JS_PUBLIC_API already_AddRefed<JS::Stencil> CompileGlobalScriptToStencil(
+ JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf, JS::CompilationStorage& compileStorage);
+
+extern JS_PUBLIC_API already_AddRefed<JS::Stencil> CompileModuleScriptToStencil(
+ JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+ JS::CompilationStorage& compileStorage);
+
+extern JS_PUBLIC_API already_AddRefed<JS::Stencil> CompileModuleScriptToStencil(
+ JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options,
+ JS::SourceText<char16_t>& srcBuf, JS::CompilationStorage& compileStorage);
+
+extern JS_PUBLIC_API bool PrepareForInstantiate(
+ JS::FrontendContext* fc, JS::Stencil& stencil,
+ JS::InstantiationStorage& storage);
+
+} // namespace JS
+
+#endif // js_experimental_CompileScript_h
diff --git a/js/public/experimental/Intl.h b/js/public/experimental/Intl.h
new file mode 100644
index 0000000000..a3d1c5c171
--- /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_PUBLIC_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_PUBLIC_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_PUBLIC_API bool AddMozDisplayNamesConstructor(JSContext* cx,
+ Handle<JSObject*> intl);
+
+} // namespace JS
+
+#endif // js_experimental_Intl_h
diff --git a/js/public/experimental/JSStencil.h b/js/public/experimental/JSStencil.h
new file mode 100644
index 0000000000..8a79687898
--- /dev/null
+++ b/js/public/experimental/JSStencil.h
@@ -0,0 +1,217 @@
+/* -*- 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_JSStencil_h
+#define js_experimental_JSStencil_h
+
+/* The `JS::Stencil` type holds the output of the JS Parser before it is
+ * allocated on the GC heap as a `JSScript`. This form may be serialized as
+ * part of building a bytecode cache. This `Stencil` is not associated with any
+ * particular Realm and may be generated off-main-thread, making it useful for
+ * building script loaders.
+ */
+
+#include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
+#include "mozilla/RefPtr.h" // RefPtr, already_AddRefed
+#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, JS::InstantiateOptions, JS::ReadOnlyDecodeOptions
+#include "js/SourceText.h" // JS::SourceText
+#include "js/Transcoding.h" // JS::TranscodeBuffer, JS::TranscodeRange
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSTracer;
+
+// Underlying opaque type.
+namespace js {
+class FrontendContext;
+namespace frontend {
+struct CompilationStencil;
+struct CompilationGCOutput;
+struct CompilationInput;
+struct PreallocatedCompilationGCOutput;
+} // namespace frontend
+} // namespace js
+
+// ************************************************************************
+// Types
+// ************************************************************************
+
+namespace JS {
+
+struct CompilationStorage;
+
+using Stencil = js::frontend::CompilationStencil;
+using FrontendContext = js::FrontendContext;
+
+// Temporary storage used during instantiating Stencil.
+//
+// Off-thread APIs can allocate this instance off main thread, and pass it back
+// to the main thread, in order to reduce the main thread allocation.
+struct InstantiationStorage {
+ private:
+ // Owned CompilationGCOutput.
+ //
+ // This uses raw pointer instead of UniquePtr because
+ // PreallocatedCompilationGCOutput is opaque.
+ js::frontend::PreallocatedCompilationGCOutput* gcOutput_ = nullptr;
+
+ friend JS_PUBLIC_API JSScript* InstantiateGlobalStencil(
+ JSContext* cx, const InstantiateOptions& options, Stencil* stencil,
+ InstantiationStorage* storage);
+
+ friend JS_PUBLIC_API JSObject* InstantiateModuleStencil(
+ JSContext* cx, const InstantiateOptions& options, Stencil* stencil,
+ InstantiationStorage* storage);
+
+ friend JS_PUBLIC_API bool PrepareForInstantiate(
+ JS::FrontendContext* fc, JS::Stencil& stencil,
+ JS::InstantiationStorage& storage);
+
+ public:
+ InstantiationStorage() = default;
+ InstantiationStorage(InstantiationStorage&& other)
+ : gcOutput_(other.gcOutput_) {
+ other.gcOutput_ = nullptr;
+ }
+
+ ~InstantiationStorage();
+
+ void operator=(InstantiationStorage&& other) {
+ gcOutput_ = other.gcOutput_;
+ other.gcOutput_ = nullptr;
+ }
+
+ private:
+ InstantiationStorage(const InstantiationStorage& other) = delete;
+ void operator=(const InstantiationStorage& aOther) = delete;
+
+ public:
+ bool isValid() const { return !!gcOutput_; }
+};
+
+} // namespace JS
+
+// ************************************************************************
+// Reference Count
+// ************************************************************************
+
+namespace JS {
+
+// These non-member functions let us manipulate the ref counts of the opaque
+// Stencil type. The RefPtrTraits below calls these for use when using the
+// RefPtr type.
+JS_PUBLIC_API void StencilAddRef(Stencil* stencil);
+JS_PUBLIC_API void StencilRelease(Stencil* stencil);
+
+} // namespace JS
+
+namespace mozilla {
+template <>
+struct RefPtrTraits<JS::Stencil> {
+ static void AddRef(JS::Stencil* stencil) { JS::StencilAddRef(stencil); }
+ static void Release(JS::Stencil* stencil) { JS::StencilRelease(stencil); }
+};
+} // namespace mozilla
+
+// ************************************************************************
+// Properties
+// ************************************************************************
+
+namespace JS {
+
+// Return true if the stencil relies on external data as a result of XDR
+// decoding.
+extern JS_PUBLIC_API bool StencilIsBorrowed(Stencil* stencil);
+
+extern JS_PUBLIC_API size_t SizeOfStencil(Stencil* stencil,
+ mozilla::MallocSizeOf mallocSizeOf);
+
+} // namespace JS
+
+// ************************************************************************
+// Compilation
+// ************************************************************************
+
+namespace JS {
+
+// Compile the source text into a JS::Stencil using the provided options. The
+// resulting stencil may be instantiated into any Realm on the current runtime
+// and may be used multiple times.
+//
+// NOTE: On error, a null will be returned and an exception will be set on the
+// JSContext.
+extern JS_PUBLIC_API already_AddRefed<Stencil> CompileGlobalScriptToStencil(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<mozilla::Utf8Unit>& srcBuf);
+extern JS_PUBLIC_API already_AddRefed<Stencil> CompileGlobalScriptToStencil(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf);
+
+// Compile the source text into a JS::Stencil using "module" parse goal. The
+// ECMAScript spec defines special semantics so we use a seperate entry point
+// here for clarity. The result is still a JS::Stencil, but should use the
+// appropriate instantiate API below.
+extern JS_PUBLIC_API already_AddRefed<Stencil> CompileModuleScriptToStencil(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<mozilla::Utf8Unit>& srcBuf);
+extern JS_PUBLIC_API already_AddRefed<Stencil> CompileModuleScriptToStencil(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf);
+
+} // namespace JS
+
+// ************************************************************************
+// Instantiation
+// ************************************************************************
+
+namespace JS {
+
+// Instantiate the Stencil into current Realm and return the JSScript.
+extern JS_PUBLIC_API JSScript* InstantiateGlobalStencil(
+ JSContext* cx, const InstantiateOptions& options, Stencil* stencil,
+ InstantiationStorage* storage = nullptr);
+
+// Instantiate a module Stencil and return the associated object. Inside the
+// engine this is a js::ModuleObject.
+extern JS_PUBLIC_API JSObject* InstantiateModuleStencil(
+ JSContext* cx, const InstantiateOptions& options, Stencil* stencil,
+ InstantiationStorage* storage = nullptr);
+
+} // namespace JS
+
+// ************************************************************************
+// Transcoding
+// ************************************************************************
+
+namespace JS {
+
+// Serialize the Stencil into the transcode buffer.
+extern JS_PUBLIC_API TranscodeResult EncodeStencil(JSContext* cx,
+ Stencil* stencil,
+ TranscodeBuffer& buffer);
+
+// Deserialize data and create a new Stencil.
+extern JS_PUBLIC_API TranscodeResult
+DecodeStencil(JSContext* cx, const ReadOnlyDecodeOptions& options,
+ const TranscodeRange& range, Stencil** stencilOut);
+extern JS_PUBLIC_API TranscodeResult
+DecodeStencil(JS::FrontendContext* fc, const ReadOnlyDecodeOptions& options,
+ const TranscodeRange& range, Stencil** stencilOut);
+
+// Register an encoder on its script source, such that all functions can be
+// encoded as they are delazified.
+extern JS_PUBLIC_API bool StartIncrementalEncoding(JSContext* cx,
+ RefPtr<Stencil>&& stencil);
+
+} // namespace JS
+
+#endif // js_experimental_JSStencil_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/PCCountProfiling.h b/js/public/experimental/PCCountProfiling.h
new file mode 100644
index 0000000000..132d48bcae
--- /dev/null
+++ b/js/public/experimental/PCCountProfiling.h
@@ -0,0 +1,162 @@
+/* -*- 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/. */
+
+/*
+ * Bytecode-level profiling support based on program counter offset within
+ * functions and overall scripts.
+ *
+ * SpiderMonkey compiles functions and scripts into bytecode representation.
+ * During execution, SpiderMonkey can keep a counter of how often each bytecode
+ * (specifically, bytecodes that are the targets of jumps) is reached, to track
+ * basic information about how many times any particular bytecode in a script
+ * or function is executed. These functions allow this counting to be started
+ * and stopped, and the results of such counting to be examined.
+ *
+ * Accumulated counting data is tracked per "script" (where "script" is either
+ * a JavaScript function from source text or a top-level script or module).
+ * Accumulated PCCount data thus prevents the particular scripts that were
+ * executed from being GC'd. Once PCCount profiling has been stopped, you
+ * should examine and then purge the accumulated data as soon as possible.
+ *
+ * Much of the data tracked here is pretty internal and may only have meaning if
+ * you're familiar with bytecode and Ion details. Hence why these APIs are
+ * "experimental".
+ */
+
+#ifndef js_experimental_PCCountProfiling_h
+#define js_experimental_PCCountProfiling_h
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+struct JS_PUBLIC_API JSContext;
+class JS_PUBLIC_API JSString;
+
+namespace JS {
+
+/**
+ * Start PCCount-based profiling if it hasn't already been started.
+ *
+ * This discards previously-accumulated PCCount profiling data from a prior
+ * PCCount profiling session. It also discards all active JIT code (as such
+ * code was compiled to *not* perform PCCount-based profiling), so a brief
+ * performance hit to code execution can be expected after calling this.
+ */
+extern JS_PUBLIC_API void StartPCCountProfiling(JSContext* cx);
+
+/**
+ * Stop PCCount-based profiling if it has been started.
+ *
+ * Internally, this accumulates PCCount profiling results into an array of
+ * (script, script count info) for future consumption. If accumulation fails,
+ * this array will just be empty.
+ *
+ * This also discard all active JIT code (as such code was compiled to perform
+ * PCCount-based profiling), so a brief performance hit to code execution can be
+ * expected after calling this.
+ */
+extern JS_PUBLIC_API void StopPCCountProfiling(JSContext* cx);
+
+/**
+ * Get a count of all scripts for which PCCount profiling data was accumulated,
+ * after PCCount profiling has been stopped.
+ *
+ * As noted above, if an internal error occurred when profiling was stopped,
+ * this function will return 0.
+ */
+extern JS_PUBLIC_API size_t GetPCCountScriptCount(JSContext* cx);
+
+/**
+ * Get a JSON string summarizing PCCount data for the given script, by index
+ * within the internal array computed when PCCount profiling was stopped, of
+ * length indicated by |JS::GetPCCountScriptCount|.
+ *
+ * The JSON string will encode an object of this form:
+ *
+ * {
+ * "file": filename for the script,
+ * "line": line number within the script,
+ *
+ * // OPTIONAL: only if this script is a function
+ * "name": function name
+ *
+ * "totals":
+ * {
+ * "interp": sum of execution counts at all bytecode locations,
+ * "ion": sum of Ion activity counts,
+ * }
+ * }
+ *
+ * There is no inherent ordering to the (script, script count info) pairs within
+ * the array. Callers generally should expect to call this function in a loop
+ * to get summaries for all executed scripts.
+ */
+extern JS_PUBLIC_API JSString* GetPCCountScriptSummary(JSContext* cx,
+ size_t script);
+
+/**
+ * Get a JSON string encoding detailed PCCount data for the given script, by
+ * index within the internal array computed when PCCount profiling was stopped,
+ * of length indicated by |JS::GetPCCountScriptCount|.
+ *
+ * The JSON string will encode an object of this form:
+ *
+ * {
+ * "text": a string containg (possibly decompiled) source of the script,
+ * "line": line number within the script,
+ *
+ * "opcodes":
+ * [
+ * // array elements of this form, corresponding to the script's
+ * // bytecodes
+ * {
+ * "id": offset of bytecode within script
+ * "line": line number for bytecode,
+ * "name": the name of the bytecode in question,
+ * "text": text of the expression being evaluated,
+ * "counts":
+ * {
+ * // OPTIONAL: only if the count is nonzero
+ * "interp": count of times executed,
+ * },
+ * ],
+ *
+ * // OPTIONAL: only if the script is executed under Ion
+ * "ion":
+ * [
+ * // array of elements of this form, corresponding to Ion basic blocks
+ * {
+ * "id": Ion block id,
+ * "offset": Ion block offset,
+ * "successors":
+ * [
+ * // list of Ion block ids from which execution may proceed after
+ * // this one
+ * ],
+ * "hits": count of times this block was hit during execution,
+ * "code": a string containing the code in this Ion basic block,
+ * }
+ * ],
+ * }
+ *
+ * There is no inherent ordering to the (script, script count info) pairs within
+ * the array. Callers generally should expect to call this function in a loop
+ * to get summaries for all executed scripts.
+ */
+extern JS_PUBLIC_API JSString* GetPCCountScriptContents(JSContext* cx,
+ size_t script);
+
+/**
+ * Purge all accumulated PCCount profiling data, particularly the internal array
+ * that |JS::GetPCCountScript{Count,Summary,Contents}| exposes -- and all that
+ * array's references to previously-executed scripts.
+ */
+extern JS_PUBLIC_API void PurgePCCounts(JSContext* cx);
+
+} // namespace JS
+
+#endif // js_experimental_PCCountProfiling_h
diff --git a/js/public/experimental/SourceHook.h b/js/public/experimental/SourceHook.h
new file mode 100644
index 0000000000..dc32fabbaf
--- /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_PUBLIC_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_PUBLIC_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_PUBLIC_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..abb6bf81f4
--- /dev/null
+++ b/js/public/experimental/TypedData.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/. */
+
+/*
+ * 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 "mozilla/Span.h"
+
+#include <stddef.h> // size_t
+#include <stdint.h> // {,u}int8_t, {,u}int16_t, {,u}int32_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Object.h" // JS::GetClass, JS::GetReservedSlot, JS::GetMaybePtrFromReservedSlot
+#include "js/RootingAPI.h" // JS::Handle, JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
+#include "js/ScalarType.h" // JS::Scalar::Type
+#include "js/Wrapper.h" // js::CheckedUnwrapStatic
+
+struct JSClass;
+class JS_PUBLIC_API JSObject;
+
+namespace JS {
+
+class JS_PUBLIC_API AutoRequireNoGC;
+
+} // namespace JS
+
+// JS_FOR_EACH_TYPED_ARRAY(MACRO) expands MACRO once for each specific
+// typed array subtype (Int8Array, Float64Array, ...), passing arguments
+// as MACRO(ExternalT, NativeT, Name) where
+//
+// ExternalT - externally-exposed element type (eg uint8_t)
+//
+// NativeT - element type used for the implementation (eg js::uint8_clamped_t)
+// Note that this type is not exposed publicly. Internal files need to
+// #include <vm/Uint8Clamped.h> to see it.
+//
+// Name - a name usable as both a JS::Scalar::Type value (eg
+// JS::Scalar::Uint8Clamped) or the stem of a full typed array name (eg
+// Uint8ClampedArray)
+//
+#define JS_FOR_EACH_TYPED_ARRAY(MACRO) \
+ MACRO(int8_t, int8_t, Int8) \
+ MACRO(uint8_t, uint8_t, Uint8) \
+ MACRO(int16_t, int16_t, Int16) \
+ MACRO(uint16_t, uint16_t, Uint16) \
+ MACRO(int32_t, int32_t, Int32) \
+ MACRO(uint32_t, uint32_t, Uint32) \
+ MACRO(float, float, Float32) \
+ MACRO(double, double, Float64) \
+ MACRO(uint8_t, js::uint8_clamped, Uint8Clamped) \
+ MACRO(int64_t, int64_t, BigInt64) \
+ MACRO(uint64_t, uint64_t, BigUint64)
+
+/*
+ * JS_New(type)Array:
+ *
+ * Create a new typed array with nelements elements.
+ *
+ * These functions (except the WithBuffer variants) fill in the array with
+ * zeros.
+ *
+ * JS_New(type)ArrayFromArray:
+ *
+ * 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.
+ *
+ * JS_New(type)ArrayWithBuffer:
+ *
+ * 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.
+ */
+
+#define DECLARE_TYPED_ARRAY_CREATION_API(ExternalType, NativeType, Name) \
+ extern JS_PUBLIC_API JSObject* JS_New##Name##Array(JSContext* cx, \
+ size_t nelements); \
+ extern JS_PUBLIC_API JSObject* JS_New##Name##ArrayFromArray( \
+ JSContext* cx, JS::Handle<JSObject*> array); \
+ extern JS_PUBLIC_API JSObject* JS_New##Name##ArrayWithBuffer( \
+ JSContext* cx, JS::Handle<JSObject*> arrayBuffer, size_t byteOffset, \
+ int64_t length);
+
+JS_FOR_EACH_TYPED_ARRAY(DECLARE_TYPED_ARRAY_CREATION_API)
+#undef DECLARE_TYPED_ARRAY_CREATION_API
+
+/**
+ * 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_PUBLIC_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_PUBLIC_API bool JS_IsArrayBufferViewObject(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_PUBLIC_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_PUBLIC_API JSObject* UnwrapArrayBufferView(JSObject* obj);
+
+extern JS_PUBLIC_API JSObject* UnwrapReadableStream(JSObject* obj);
+
+namespace detail {
+
+constexpr size_t TypedArrayLengthSlot = 1;
+constexpr size_t TypedArrayDataSlot = 3;
+
+} // namespace detail
+
+// 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_PUBLIC_API void GetArrayBufferViewLengthAndData(JSObject* obj,
+ size_t* length,
+ bool* isSharedMemory,
+ uint8_t** data);
+
+} // namespace js
+
+/*
+ * JS_GetObjectAs(type)Array(JSObject* maybeWrapped, size_t* length, bool*
+ * isSharedMemory, element_type** data)
+ *
+ * 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.
+ */
+#define DECLARE_GET_OBJECT_AS(ExternalType, NativeType, Name) \
+ extern JS_PUBLIC_API JSObject* JS_GetObjectAs##Name##Array( \
+ JSObject* maybeWrapped, size_t* length, bool* isSharedMemory, \
+ ExternalType** data);
+JS_FOR_EACH_TYPED_ARRAY(DECLARE_GET_OBJECT_AS)
+#undef DECLARE_GET_OBJECT_AS
+
+extern JS_PUBLIC_API JSObject* JS_GetObjectAsArrayBufferView(
+ JSObject* obj, size_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_PUBLIC_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_PUBLIC_API size_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_PUBLIC_API size_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_PUBLIC_API size_t JS_GetTypedArrayByteLength(JSObject* obj);
+
+/**
+ * More generic name for JS_GetTypedArrayByteLength to cover DataViews as well
+ */
+extern JS_PUBLIC_API size_t JS_GetArrayBufferViewByteLength(JSObject* obj);
+
+/**
+ * More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well
+ */
+extern JS_PUBLIC_API size_t JS_GetArrayBufferViewByteOffset(JSObject* obj);
+
+/**
+ * Same as above, but for any kind of ArrayBufferView. Prefer the type-specific
+ * versions when possible.
+ */
+extern JS_PUBLIC_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_PUBLIC_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_PUBLIC_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_PUBLIC_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_PUBLIC_API JSObject* JS_NewDataView(JSContext* cx,
+ JS::Handle<JSObject*> buffer,
+ size_t byteOffset, size_t byteLength);
+
+namespace JS {
+
+/*
+ * Returns whether the passed array buffer view is 'large': its byteLength >= 2
+ * GB.
+ *
+ * |obj| must pass a JS_IsArrayBufferViewObject test.
+ */
+JS_PUBLIC_API bool IsLargeArrayBufferView(JSObject* obj);
+
+/*
+ * Returns whether the passed array buffer view has a resizable or growable
+ * array buffer.
+ *
+ * |obj| must pass a JS_IsArrayBufferViewObject test.
+ */
+JS_PUBLIC_API bool IsResizableArrayBufferView(JSObject* obj);
+
+/*
+ * Given an ArrayBuffer or view, prevent the length of the underlying
+ * ArrayBuffer from changing (with pin=true) until unfrozen (with
+ * pin=false). Note that some objects (eg SharedArrayBuffers) cannot change
+ * length to begin with, and are treated as always pinned.
+ *
+ * ArrayBuffers and their views can change length by being detached, or
+ * if they are ResizableArrayBuffers or (shared) GrowableArrayBuffers.
+ *
+ * Returns whether the pinned status changed.
+ */
+JS_PUBLIC_API bool PinArrayBufferOrViewLength(JSObject* obj, bool pin);
+
+/*
+ * Given an ArrayBuffer or view, make sure its contents are not stored inline
+ * so that the data is safe for use even if a GC moves the owning object.
+ *
+ * Note that this by itself does not make it safe to use the data pointer
+ * if JS can run or the ArrayBuffer can be detached in any way. Consider using
+ * this in conjunction with PinArrayBufferOrViewLength, which will cause any
+ * potentially invalidating operations to fail.
+ */
+JS_PUBLIC_API bool EnsureNonInlineArrayBufferOrView(JSContext* cx,
+ JSObject* obj);
+
+namespace detail {
+
+// Map from eg Uint8Clamped -> uint8_t, Uint8 -> uint8_t, or Float64 ->
+// double. Used as the DataType within a JS::TypedArray specialization.
+template <JS::Scalar::Type ArrayType>
+struct ExternalTypeOf {};
+
+#define DEFINE_ELEMENT_TYPES(ExternalT, NativeT, Name) \
+ template <> \
+ struct ExternalTypeOf<JS::Scalar::Name> { \
+ using Type = ExternalT; \
+ };
+JS_FOR_EACH_TYPED_ARRAY(DEFINE_ELEMENT_TYPES)
+#undef DEFINE_ELEMENT_TYPES
+
+template <JS::Scalar::Type ArrayType>
+using ExternalTypeOf_t = typename ExternalTypeOf<ArrayType>::Type;
+
+} // namespace detail
+
+// A class holding a JSObject referring to a buffer of data. Either an
+// ArrayBufferObject or some sort of ArrayBufferViewObject (see below).
+// Note that this will always hold an unwrapped object.
+class JS_PUBLIC_API ArrayBufferOrView {
+ public:
+ // Typed Arrays will set this to their specific element type.
+ // Everything else just claims to expose things as uint8_t*.
+ using DataType = uint8_t;
+
+ protected:
+ JSObject* obj;
+
+ explicit ArrayBufferOrView(JSObject* unwrapped) : obj(unwrapped) {}
+
+ public:
+ // ArrayBufferOrView subclasses will set `obj` to nullptr if wrapping an
+ // object of the wrong type. So this allows:
+ //
+ // auto view = JS::TypedArray<JS::Scalar::Int8>::fromObject(obj);
+ // if (!view) { ... }
+ //
+ explicit operator bool() const { return !!obj; }
+
+ // `obj` must be an unwrapped ArrayBuffer or view, or nullptr.
+ static inline ArrayBufferOrView fromObject(JSObject* unwrapped);
+
+ // Unwrap an ArrayBuffer or view. Returns ArrayBufferOrView(nullptr) if
+ // `maybeWrapped` is the wrong type or fails unwrapping. Never throw.
+ static ArrayBufferOrView unwrap(JSObject* maybeWrapped);
+
+ // Allow use as Rooted<JS::ArrayBufferOrView>.
+ void trace(JSTracer* trc) {
+ if (obj) {
+ js::gc::TraceExternalEdge(trc, &obj, "ArrayBufferOrView object");
+ }
+ }
+
+ bool isDetached() const;
+ bool isResizable() const;
+
+ void exposeToActiveJS() const {
+ if (obj) {
+ js::BarrierMethods<JSObject*>::exposeToJS(obj);
+ }
+ }
+
+ JSObject* asObject() const {
+ exposeToActiveJS();
+ return obj;
+ }
+
+ JSObject* asObjectUnbarriered() const { return obj; }
+
+ JSObject** addressOfObject() { return &obj; }
+
+ bool operator==(const ArrayBufferOrView& other) const {
+ return obj == other.asObjectUnbarriered();
+ }
+ bool operator!=(const ArrayBufferOrView& other) const {
+ return obj != other.asObjectUnbarriered();
+ }
+};
+
+class JS_PUBLIC_API ArrayBuffer : public ArrayBufferOrView {
+ static const JSClass* const FixedLengthUnsharedClass;
+ static const JSClass* const ResizableUnsharedClass;
+ static const JSClass* const FixedLengthSharedClass;
+ static const JSClass* const GrowableSharedClass;
+
+ protected:
+ explicit ArrayBuffer(JSObject* unwrapped) : ArrayBufferOrView(unwrapped) {}
+
+ public:
+ static ArrayBuffer fromObject(JSObject* unwrapped) {
+ if (unwrapped) {
+ const JSClass* clasp = GetClass(unwrapped);
+ if (clasp == FixedLengthUnsharedClass ||
+ clasp == ResizableUnsharedClass || clasp == FixedLengthSharedClass ||
+ clasp == GrowableSharedClass) {
+ return ArrayBuffer(unwrapped);
+ }
+ }
+ return ArrayBuffer(nullptr);
+ }
+ static ArrayBuffer unwrap(JSObject* maybeWrapped);
+
+ static ArrayBuffer create(JSContext* cx, size_t nbytes);
+
+ mozilla::Span<uint8_t> getData(bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+};
+
+// A view into an ArrayBuffer, either a DataViewObject or a Typed Array variant.
+class JS_PUBLIC_API ArrayBufferView : public ArrayBufferOrView {
+ protected:
+ explicit ArrayBufferView(JSObject* unwrapped)
+ : ArrayBufferOrView(unwrapped) {}
+
+ public:
+ static inline ArrayBufferView fromObject(JSObject* unwrapped);
+ static ArrayBufferView unwrap(JSObject* maybeWrapped) {
+ if (!maybeWrapped) {
+ return ArrayBufferView(nullptr);
+ }
+ ArrayBufferView view = fromObject(maybeWrapped);
+ if (view) {
+ return view;
+ }
+ return fromObject(js::CheckedUnwrapStatic(maybeWrapped));
+ }
+
+ bool isDetached() const;
+ bool isResizable() const;
+
+ mozilla::Span<uint8_t> getData(bool* isSharedMemory,
+ const JS::AutoRequireNoGC&);
+
+ // Must only be called if !isDetached().
+ size_t getByteLength(const JS::AutoRequireNoGC&);
+};
+
+class JS_PUBLIC_API DataView : public ArrayBufferView {
+ static const JSClass* const FixedLengthClassPtr;
+ static const JSClass* const ResizableClassPtr;
+
+ protected:
+ explicit DataView(JSObject* unwrapped) : ArrayBufferView(unwrapped) {}
+
+ public:
+ static DataView fromObject(JSObject* unwrapped) {
+ if (unwrapped) {
+ const JSClass* clasp = GetClass(unwrapped);
+ if (clasp == FixedLengthClassPtr || clasp == ResizableClassPtr) {
+ return DataView(unwrapped);
+ }
+ }
+ return DataView(nullptr);
+ }
+
+ static DataView unwrap(JSObject* maybeWrapped) {
+ if (!maybeWrapped) {
+ return DataView(nullptr);
+ }
+ DataView view = fromObject(maybeWrapped);
+ if (view) {
+ return view;
+ }
+ return fromObject(js::CheckedUnwrapStatic(maybeWrapped));
+ }
+};
+
+// Base type of all Typed Array variants.
+class JS_PUBLIC_API TypedArray_base : public ArrayBufferView {
+ protected:
+ explicit TypedArray_base(JSObject* unwrapped) : ArrayBufferView(unwrapped) {}
+
+ static const JSClass* const fixedLengthClasses;
+ static const JSClass* const resizableClasses;
+
+ public:
+ static TypedArray_base fromObject(JSObject* unwrapped);
+
+ static TypedArray_base unwrap(JSObject* maybeWrapped) {
+ if (!maybeWrapped) {
+ return TypedArray_base(nullptr);
+ }
+ TypedArray_base view = fromObject(maybeWrapped);
+ if (view) {
+ return view;
+ }
+ return fromObject(js::CheckedUnwrapStatic(maybeWrapped));
+ }
+};
+
+template <JS::Scalar::Type TypedArrayElementType>
+class JS_PUBLIC_API TypedArray : public TypedArray_base {
+ // This cannot be a static data member because on Windows,
+ // __declspec(dllexport) causes the class to be instantiated immediately,
+ // leading to errors when later explicit specializations of inline member
+ // functions are encountered ("error: explicit specialization of 'ClassPtr'
+ // after instantiation"). And those inlines need to be defined outside of the
+ // class due to order dependencies. This is the only way I could get it to
+ // work on both Windows and POSIX.
+ static const JSClass* fixedLengthClasp() {
+ return &TypedArray_base::fixedLengthClasses[static_cast<int>(
+ TypedArrayElementType)];
+ }
+ static const JSClass* resizableClasp() {
+ return &TypedArray_base::resizableClasses[static_cast<int>(
+ TypedArrayElementType)];
+ }
+
+ protected:
+ explicit TypedArray(JSObject* unwrapped) : TypedArray_base(unwrapped) {}
+
+ public:
+ using DataType = detail::ExternalTypeOf_t<TypedArrayElementType>;
+
+ static constexpr JS::Scalar::Type Scalar = TypedArrayElementType;
+
+ static TypedArray create(JSContext* cx, size_t nelements);
+ static TypedArray fromArray(JSContext* cx, HandleObject other);
+ static TypedArray fromBuffer(JSContext* cx, HandleObject arrayBuffer,
+ size_t byteOffset, int64_t length);
+
+ // Return an interface wrapper around `obj`, or around nullptr if `obj` is not
+ // an unwrapped typed array of the correct type.
+ static TypedArray fromObject(JSObject* unwrapped) {
+ if (unwrapped) {
+ const JSClass* clasp = GetClass(unwrapped);
+ if (clasp == fixedLengthClasp() || clasp == resizableClasp()) {
+ return TypedArray(unwrapped);
+ }
+ }
+ return TypedArray(nullptr);
+ }
+
+ static TypedArray unwrap(JSObject* maybeWrapped) {
+ if (!maybeWrapped) {
+ return TypedArray(nullptr);
+ }
+ TypedArray view = fromObject(maybeWrapped);
+ if (view) {
+ return view;
+ }
+ return fromObject(js::CheckedUnwrapStatic(maybeWrapped));
+ }
+
+ // 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.
+ //
+ mozilla::Span<DataType> getData(bool* isSharedMemory,
+ const JS::AutoRequireNoGC& nogc);
+};
+
+ArrayBufferOrView ArrayBufferOrView::fromObject(JSObject* unwrapped) {
+ if (ArrayBuffer::fromObject(unwrapped) ||
+ ArrayBufferView::fromObject(unwrapped)) {
+ return ArrayBufferOrView(unwrapped);
+ }
+ return ArrayBufferOrView(nullptr);
+}
+
+ArrayBufferView ArrayBufferView::fromObject(JSObject* unwrapped) {
+ if (TypedArray_base::fromObject(unwrapped) ||
+ DataView::fromObject(unwrapped)) {
+ return ArrayBufferView(unwrapped);
+ }
+ return ArrayBufferView(nullptr);
+}
+
+} /* namespace JS */
+
+/*
+ * JS_Get(type)ArrayData(JSObject* obj,
+ * bool* isSharedMemory,
+ * const JS::AutoRequireNoGC&)
+ *
+ * js::Get(type)ArrayLengthAndData(JSObject* obj,
+ * size_t* length,
+ * bool* isSharedMemory,
+ * const JS::AutoRequireNoGC&)
+ *
+ * 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.
+ */
+
+#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(ExternalType, NativeType, Name) \
+ extern JS_PUBLIC_API ExternalType* JS_Get##Name##ArrayData( \
+ JSObject* maybeWrapped, bool* isSharedMemory, \
+ const JS::AutoRequireNoGC&); \
+ \
+ namespace js { \
+ inline void Get##Name##ArrayLengthAndData(JSObject* unwrapped, \
+ size_t* length, \
+ bool* isSharedMemory, \
+ ExternalType** data) { \
+ MOZ_ASSERT(JS::TypedArray<JS::Scalar::Name>::fromObject(unwrapped)); \
+ const JS::Value& lenSlot = \
+ JS::GetReservedSlot(unwrapped, detail::TypedArrayLengthSlot); \
+ *length = size_t(lenSlot.toPrivate()); \
+ *isSharedMemory = JS_GetTypedArraySharedness(unwrapped); \
+ *data = JS::GetMaybePtrFromReservedSlot<ExternalType>( \
+ unwrapped, detail::TypedArrayDataSlot); \
+ } \
+ \
+ JS_PUBLIC_API JSObject* Unwrap##Name##Array(JSObject* maybeWrapped); \
+ } /* namespace js */
+
+JS_FOR_EACH_TYPED_ARRAY(JS_DEFINE_DATA_AND_LENGTH_ACCESSOR)
+#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR
+
+namespace JS {
+
+#define IMPL_TYPED_ARRAY_CLASS(ExternalType, NativeType, Name) \
+ template <> \
+ inline JS::TypedArray<JS::Scalar::Name> \
+ JS::TypedArray<JS::Scalar::Name>::create(JSContext* cx, size_t nelements) { \
+ return fromObject(JS_New##Name##Array(cx, nelements)); \
+ }; \
+ \
+ template <> \
+ inline JS::TypedArray<JS::Scalar::Name> \
+ JS::TypedArray<JS::Scalar::Name>::fromArray(JSContext* cx, \
+ HandleObject other) { \
+ return fromObject(JS_New##Name##ArrayFromArray(cx, other)); \
+ }; \
+ \
+ template <> \
+ inline JS::TypedArray<JS::Scalar::Name> \
+ JS::TypedArray<JS::Scalar::Name>::fromBuffer( \
+ JSContext* cx, HandleObject arrayBuffer, size_t byteOffset, \
+ int64_t length) { \
+ return fromObject( \
+ JS_New##Name##ArrayWithBuffer(cx, arrayBuffer, byteOffset, length)); \
+ };
+
+JS_FOR_EACH_TYPED_ARRAY(IMPL_TYPED_ARRAY_CLASS)
+#undef IMPL_TYPED_ARRAY_CLASS
+
+// Create simple names like Int8Array, Float32Array, etc.
+#define JS_DECLARE_CLASS_ALIAS(ExternalType, NativeType, Name) \
+ using Name##Array = TypedArray<js::Scalar::Name>;
+JS_FOR_EACH_TYPED_ARRAY(JS_DECLARE_CLASS_ALIAS)
+#undef JS_DECLARE_CLASS_ALIAS
+
+} // namespace JS
+
+namespace js {
+
+template <typename T>
+using EnableIfABOVType =
+ std::enable_if_t<std::is_base_of_v<JS::ArrayBufferOrView, T>>;
+
+template <typename T, typename Wrapper>
+class WrappedPtrOperations<T, Wrapper, EnableIfABOVType<T>> {
+ auto get() const { return static_cast<const Wrapper*>(this)->get(); }
+
+ public:
+ explicit operator bool() const { return bool(get()); }
+ JSObject* asObject() const { return get().asObject(); }
+ bool isDetached() const { return get().isDetached(); }
+ bool isSharedMemory() const { return get().isSharedMemory(); }
+
+ mozilla::Span<typename T::DataType> getData(bool* isSharedMemory,
+ const JS::AutoRequireNoGC& nogc) {
+ return get().getData(isSharedMemory, nogc);
+ }
+};
+
+// Allow usage within Heap<T>.
+template <typename T>
+struct IsHeapConstructibleType<T, EnableIfABOVType<T>> : public std::true_type {
+};
+
+template <typename T>
+struct BarrierMethods<T, EnableIfABOVType<T>> {
+ static gc::Cell* asGCThingOrNull(T view) {
+ return reinterpret_cast<gc::Cell*>(view.asObjectUnbarriered());
+ }
+ static void postWriteBarrier(T* viewp, T prev, T next) {
+ BarrierMethods<JSObject*>::postWriteBarrier(viewp->addressOfObject(),
+ prev.asObjectUnbarriered(),
+ next.asObjectUnbarriered());
+ }
+ static void exposeToJS(T view) { view.exposeToActiveJS(); }
+ static void readBarrier(T view) {
+ JSObject* obj = view.asObjectUnbarriered();
+ if (obj) {
+ js::gc::IncrementalReadBarrier(JS::GCCellPtr(obj));
+ }
+ }
+};
+
+} // namespace js
+
+namespace JS {
+template <typename T>
+struct SafelyInitialized<T, js::EnableIfABOVType<T>> {
+ static T create() { return T::fromObject(nullptr); }
+};
+} // namespace JS
+
+/*
+ * JS_Is(type)Array(JSObject* maybeWrapped)
+ *
+ * Test for specific typed array types.
+ */
+
+#define DECLARE_IS_ARRAY_TEST(_1, _2, Name) \
+ inline JS_PUBLIC_API bool JS_Is##Name##Array(JSObject* maybeWrapped) { \
+ return JS::TypedArray<js::Scalar::Name>::unwrap(maybeWrapped).asObject(); \
+ }
+JS_FOR_EACH_TYPED_ARRAY(DECLARE_IS_ARRAY_TEST)
+#undef DECLARE_IS_ARRAY_TEST
+
+#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..533c327adb
--- /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_PUBLIC_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_PUBLIC_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..3b69277edc
--- /dev/null
+++ b/js/public/friend/DumpFunctions.h
@@ -0,0 +1,127 @@
+/* -*- 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/Attributes.h"
+#include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
+
+#include <stddef.h> // size_t
+#include <stdio.h> // FILE
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Printer.h" // js::GenericPrinter
+#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 {
+
+class JS_PUBLIC_API BigInt;
+class JS_PUBLIC_API PropertyKey;
+class JS_PUBLIC_API Value;
+
+} // namespace JS
+
+namespace js {
+
+class InterpreterFrame;
+
+} // namespace js
+
+namespace JS {
+
+/** Exposed for DumpJSStack */
+extern JS_PUBLIC_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_PUBLIC_API void DumpString(JSString* str, FILE* fp);
+
+extern JS_PUBLIC_API void DumpAtom(JSAtom* atom, FILE* fp);
+
+extern JS_PUBLIC_API void DumpObject(JSObject* obj, FILE* fp);
+
+extern JS_PUBLIC_API void DumpChars(const char16_t* s, size_t n, FILE* fp);
+
+// DumpBigInt() outputs the value in decimal if it fits within a 64-bit int, and
+// otherwise in hex, prefixed with "0x". In both cases the "n" is appended.
+extern JS_PUBLIC_API void DumpBigInt(JS::BigInt* bi, FILE* fp);
+
+extern JS_PUBLIC_API void DumpValue(const JS::Value& val, FILE* fp);
+
+extern JS_PUBLIC_API void DumpId(JS::PropertyKey id, FILE* fp);
+
+extern JS_PUBLIC_API bool DumpPC(JSContext* cx, FILE* fp);
+
+extern JS_PUBLIC_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_PUBLIC_API void DumpString(JSString* str);
+extern JS_PUBLIC_API void DumpAtom(JSAtom* atom);
+extern JS_PUBLIC_API void DumpObject(JSObject* obj);
+extern JS_PUBLIC_API void DumpChars(const char16_t* s, size_t n);
+extern JS_PUBLIC_API void DumpBigInt(JS::BigInt* bi);
+extern JS_PUBLIC_API void DumpValue(const JS::Value& val);
+extern JS_PUBLIC_API void DumpId(JS::PropertyKey id);
+extern JS_PUBLIC_API void DumpInterpreterFrame(
+ JSContext* cx, InterpreterFrame* start = nullptr);
+extern JS_PUBLIC_API bool DumpPC(JSContext* cx);
+extern JS_PUBLIC_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_PUBLIC_API void DumpBacktrace(JSContext* cx, FILE* fp);
+
+extern JS_PUBLIC_API void DumpBacktrace(JSContext* cx, GenericPrinter& out);
+
+extern JS_PUBLIC_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_PUBLIC_API void DumpHeap(
+ JSContext* cx, FILE* fp, DumpHeapNurseryBehaviour nurseryBehaviour,
+ mozilla::MallocSizeOf mallocSizeOf = nullptr);
+
+extern JS_PUBLIC_API void DumpFmt(FILE* fp, const char* fmt, ...)
+ MOZ_FORMAT_PRINTF(2, 3);
+extern JS_PUBLIC_API void DumpFmt(const char* fmt, ...) MOZ_FORMAT_PRINTF(1, 2);
+
+} // 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..1615c8e052
--- /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_PUBLIC_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_PUBLIC_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..5daa625ff9
--- /dev/null
+++ b/js/public/friend/ErrorNumbers.msg
@@ -0,0 +1,920 @@
+/* -*- 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_BAD_TOSORTED_ARG, 0, JSEXN_TYPEERR, "non-function passed to Array.prototype.toSorted")
+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_PROPERTY_NOT_CALLABLE, 1, JSEXN_TYPEERR, "{0} property is not callable")
+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_WEAKMAP_KEY_CANT_BE_HELD_WEAKLY, 1, JSEXN_TYPEERR, "WeakMap key {0} must be an object or an unregistered symbol")
+MSG_DEF(JSMSG_WEAKSET_VAL_CANT_BE_HELD_WEAKLY, 1, JSEXN_TYPEERR, "WeakSet value {0} must be an object or an unregistered symbol")
+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_PREV_DECLARATION, 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_MISMATCHED_PLACEMENT, 2, JSEXN_SYNTAXERR, "getter and setter for {0} {1} should either be both static or non-static")
+MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
+MSG_DEF(JSMSG_GET_MISSING_PRIVATE, 0, JSEXN_TYPEERR, "can't access private field or method: object is not the right class")
+MSG_DEF(JSMSG_SET_MISSING_PRIVATE, 0, JSEXN_TYPEERR, "can't set private field: object is not the right class")
+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_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_ASSIGN_TO_PRIVATE_METHOD, 0, JSEXN_TYPEERR, "cannot assign to private method")
+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")
+MSG_DEF(JSMSG_DEPRECATED_LATE_WEEKDAY, 0, JSEXN_WARN, "day of week after day of month in date format is deprecated")
+
+// 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_DEPRECATED_USAGE, 1, JSEXN_REFERENCEERR, "deprecated {0} usage")
+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")
+MSG_DEF(JSMSG_CSP_BLOCKED_WASM, 1, JSEXN_WASMCOMPILEERROR, "call to {0}() blocked by CSP")
+MSG_DEF(JSMSG_CSP_BLOCKED_SHADOWREALM, 0, JSEXN_EVALERR, "call to ShadowRealm.prototype.evaluate 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_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_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_AS_AFTER_STRING, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after string literal")
+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 supported in this context")
+MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
+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_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_LOCAL_STRING_EXPORT, 0, JSEXN_SYNTAXERR, "string exports can't be used without 'from'")
+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_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_LITERAL,0, JSEXN_SYNTAXERR, "\"0\"-prefixed octal literals are deprecated; use the \"0o\" prefix instead")
+MSG_DEF(JSMSG_DEPRECATED_OCTAL_ESCAPE ,0, JSEXN_SYNTAXERR, "octal escape sequences can't be used in untagged template literals or in strict mode code")
+MSG_DEF(JSMSG_DEPRECATED_EIGHT_OR_NINE_ESCAPE, 0, JSEXN_SYNTAXERR, "the escapes \\8 and \\9 can't be used in untagged template literals or in strict mode code")
+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_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_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_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_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_PAREN_AFTER_DECORATOR, 0, JSEXN_SYNTAXERR, "missing ) in decorator expression")
+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_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_METHOD_CALL, 0, JSEXN_SYNTAXERR, "self-hosted code may not contain direct method calls. Use callFunction() or callContentFunction()")
+MSG_DEF(JSMSG_SELFHOSTED_LEXICAL, 0, JSEXN_SYNTAXERR, "self-hosted code may not contain 'let' or 'const', use 'var'")
+MSG_DEF(JSMSG_SELFHOSTED_CLASS, 0, JSEXN_SYNTAXERR, "self-hosted code may not contain a class")
+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_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_UNPAIRED_SURROGATE_EXPORT, 0, JSEXN_SYNTAXERR, "module export name contains unpaired surrogate")
+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_UNTERMINATED_STATIC_CLASS_BLOCK, 0, JSEXN_SYNTAXERR, "unterminated static class block")
+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_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_IMPORT_ASSERTIONS_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "import assertions are not currently supported")
+MSG_DEF(JSMSG_ILLEGAL_PRIVATE_FIELD, 0, JSEXN_SYNTAXERR, "private fields aren't valid in this context")
+MSG_DEF(JSMSG_ILLEGAL_PRIVATE_NAME, 0, JSEXN_SYNTAXERR, "private names aren't valid in this context")
+MSG_DEF(JSMSG_INVALID_PRIVATE_NAME_PRECEDENCE, 0, JSEXN_SYNTAXERR, "invalid use of private name due to operator precedence")
+MSG_DEF(JSMSG_INVALID_PRIVATE_NAME_IN_UNARY_EXPR, 0, JSEXN_SYNTAXERR, "invalid use of private name in unary expression without object reference")
+MSG_DEF(JSMSG_ILLEGAL_PRIVATE_EXOTIC, 0, JSEXN_TYPEERR, "private fields or private methods aren't allowed on this exotic object")
+MSG_DEF(JSMSG_PRIVATE_FIELD_DOUBLE, 0, JSEXN_TYPEERR, "Initializing an object twice is an error with private fields")
+MSG_DEF(JSMSG_PRIVATE_BRAND_DOUBLE, 0, JSEXN_TYPEERR, "Initializing an object twice is an error with private methods")
+MSG_DEF(JSMSG_CURLY_AFTER_ASSERT, 0, JSEXN_SYNTAXERR, "missing '{' after assert")
+MSG_DEF(JSMSG_DUPLICATE_ASSERT_KEY, 1, JSEXN_SYNTAXERR, "duplicate assert key '{0}'")
+MSG_DEF(JSMSG_COLON_AFTER_ASSERT_KEY, 0, JSEXN_SYNTAXERR, "missing : after assert key")
+MSG_DEF(JSMSG_ASSERT_STRING_LITERAL, 0, JSEXN_SYNTAXERR, "expected string literal")
+MSG_DEF(JSMSG_ASSERT_KEY_EXPECTED, 0, JSEXN_SYNTAXERR, "expected assertion key")
+MSG_DEF(JSMSG_DECORATOR_NAME_EXPECTED, 0, JSEXN_SYNTAXERR, "expected property name in decorator expression")
+MSG_DEF(JSMSG_CLASS_EXPECTED, 0, JSEXN_SYNTAXERR, "expected class")
+
+// 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}")
+
+// System encoding errors
+MSG_DEF(JSMSG_CANT_CONVERT_TO_NARROW, 0, JSEXN_ERR, "can't convert to narrow string")
+MSG_DEF(JSMSG_CANT_CONVERT_TO_WIDE, 0, JSEXN_ERR, "can't convert to wide string")
+MSG_DEF(JSMSG_CANT_CONVERT_WIDE_TO_UTF8, 0, JSEXN_ERR, "can't convert wide string to UTF-8")
+MSG_DEF(JSMSG_CANT_CONVERT_UTF8_TO_WIDE, 0, JSEXN_ERR, "can't convert UTF-8 to wide string")
+
+// 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_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_TAG_SIG, 2, JSEXN_WASMLINKERROR, "imported tag '{0}.{1}' signature mismatch")
+MSG_DEF(JSMSG_WASM_BAD_IMP_INDEX, 1, JSEXN_WASMLINKERROR, "imported {0} with incompatible index type")
+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_NO_SHMEM_LINK, 0, JSEXN_WASMLINKERROR, "shared memory is disabled")
+MSG_DEF(JSMSG_WASM_NO_MEM64_LINK, 0, JSEXN_WASMLINKERROR, "memory64 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_BAD_CAST, 0, JSEXN_WASMRUNTIMEERROR, "bad cast")
+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_ARRAY_IMP_LIMIT, 0, JSEXN_WASMRUNTIMEERROR, "too many array elements")
+MSG_DEF(JSMSG_WASM_ARRAY_NEW_ELEM_NOT_IMPLEMENTED, 0, JSEXN_WASMRUNTIMEERROR, "array.new_elem does not yet support the expression encoding of element segments")
+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_ENFORCE_RANGE, 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_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_NULL_EXTERNREF_VALUE, 0, JSEXN_TYPEERR, "can only pass null to nullexternref")
+MSG_DEF(JSMSG_WASM_BAD_NULL_FUNCREF_VALUE, 0, JSEXN_TYPEERR, "can only pass null to nullfuncref")
+MSG_DEF(JSMSG_WASM_BAD_NULL_ANYREF_VALUE, 0, JSEXN_TYPEERR, "can only pass null to nullref")
+MSG_DEF(JSMSG_WASM_BAD_EQREF_VALUE, 0, JSEXN_TYPEERR, "can only pass a WebAssembly GC object to an eqref")
+MSG_DEF(JSMSG_WASM_BAD_I31REF_VALUE, 0, JSEXN_TYPEERR, "can only pass a 31-bit number to an i31ref")
+MSG_DEF(JSMSG_WASM_BAD_STRUCTREF_VALUE, 0, JSEXN_TYPEERR, "can only pass a WebAssembly struct object to a structref")
+MSG_DEF(JSMSG_WASM_BAD_ARRAYREF_VALUE, 0, JSEXN_TYPEERR, "can only pass a WebAssembly array object to an arrayref")
+MSG_DEF(JSMSG_WASM_BAD_TYPEREF_VALUE, 0, JSEXN_TYPEERR, "bad type")
+MSG_DEF(JSMSG_WASM_BAD_VAL_TYPE, 0, JSEXN_TYPEERR, "cannot pass value to or from JS")
+MSG_DEF(JSMSG_WASM_BAD_STRING_VAL_TYPE, 0, JSEXN_TYPEERR, "bad value type")
+MSG_DEF(JSMSG_WASM_BAD_STRING_IDX_TYPE, 0, JSEXN_TYPEERR, "bad index type")
+MSG_DEF(JSMSG_WASM_BAD_EXN_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Tag")
+MSG_DEF(JSMSG_WASM_BAD_EXN_PAYLOAD, 0, JSEXN_TYPEERR, "second argument must be an object")
+MSG_DEF(JSMSG_WASM_BAD_EXN_PAYLOAD_LEN, 2, JSEXN_TYPEERR, "expected {0} values but got {1}")
+MSG_DEF(JSMSG_WASM_BAD_EXN_TAG, 0, JSEXN_TYPEERR, "exception's tag did not match the provided exception tag")
+MSG_DEF(JSMSG_WASM_BAD_EXN_OPTIONS, 0, JSEXN_TYPEERR, "argument cannot be converted to an ExceptionOptions")
+MSG_DEF(JSMSG_WASM_BAD_FUNCTION_VALUE, 0, JSEXN_TYPEERR, "second argument must be a function")
+MSG_DEF(JSMSG_WASM_BAD_COMPILE_OPTIONS, 0, JSEXN_TYPEERR, "argument cannot be converted to a CompileOptions")
+MSG_DEF(JSMSG_WASM_UNKNOWN_BUILTIN, 0, JSEXN_TYPEERR, "unknown builtin")
+MSG_DEF(JSMSG_WASM_DUPLICATE_BUILTIN, 0, JSEXN_TYPEERR, "duplicate builtin")
+MSG_DEF(JSMSG_WASM_BAD_CODEPOINT, 0, JSEXN_WASMRUNTIMEERROR, "bad codepoint")
+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_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_MODIFIED_GC_OBJECT, 0, JSEXN_TYPEERR, "can't modify WebAssembly GC objects")
+
+// Proxy
+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_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_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_BREAKPOINT_NOT_ALLOWED, 0, JSEXN_ERR, "breakpoint is not allowed for this opcode")
+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_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")
+MSG_DEF(JSMSG_DEBUG_EXCLUSIVE_FRAME_COVERAGE, 0, JSEXN_ERR, "onEnterFrame and collectCoverageInfo cannot be active at the same time")
+
+// Testing functions
+MSG_DEF(JSMSG_TESTING_SCRIPTS_ONLY, 0, JSEXN_TYPEERR, "only works on scripts")
+MSG_DEF(JSMSG_INVALID_ARGS, 1, JSEXN_ERR, "{0}: invalid arguments")
+
+// 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, 0, JSEXN_RANGEERR, "duplicate variant subtag")
+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_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_NUMBER, 3, JSEXN_TYPEERR, "undefined {0} number in {1}.{2}()")
+MSG_DEF(JSMSG_UNDEFINED_TYPE, 0, JSEXN_TYPEERR, "missing \"type\" option in DisplayNames()")
+MSG_DEF(JSMSG_NAN_NUMBER_RANGE, 3, JSEXN_RANGEERR, "range can't {0} with NaN in {1}.{2}()")
+MSG_DEF(JSMSG_INVALID_NUMBER_OPTION, 2, JSEXN_TYPEERR, "can't set option {0} when {1} is used")
+MSG_DEF(JSMSG_UNEQUAL_FRACTION_DIGITS, 0, JSEXN_RANGEERR, "fraction digits must be the same when roundingIncrement is used")
+
+// 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}")
+MSG_DEF(JSMSG_INVALID_CLASS_SET_OP, 0, JSEXN_SYNTAXERR, "invalid class set operation in regular expression")
+MSG_DEF(JSMSG_INVALID_CHAR_IN_CLASS, 0, JSEXN_SYNTAXERR, "invalid character in class in regular expression")
+MSG_DEF(JSMSG_NEGATED_CLASS_WITH_STR, 0, JSEXN_SYNTAXERR, "negated character class with strings in regular expression")
+
+// 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_DETACHED, 0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
+MSG_DEF(JSMSG_ARRAYBUFFER_LENGTH_PINNED, 0, JSEXN_RANGEERR, "attempting to change pinned length of ArrayBuffer")
+MSG_DEF(JSMSG_TYPED_ARRAY_RESIZED_BOUNDS, 0, JSEXN_TYPEERR, "attempting to access outside the bounds of resized ArrayBuffer")
+MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_BOUNDS, 2, JSEXN_RANGEERR, "start offset of {0}Array should be a multiple of {1}")
+MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_MISALIGNED, 2, JSEXN_RANGEERR, "buffer length for {0}Array should be a multiple of {1}")
+MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_OFFSET_LENGTH_BOUNDS, 1, JSEXN_RANGEERR, "size of buffer is too small for {0}Array with byteOffset")
+MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_ARRAY_LENGTH_BOUNDS, 1, JSEXN_RANGEERR, "attempting to construct out-of-bounds {0}Array on ArrayBuffer")
+MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_TOO_LARGE, 1, JSEXN_RANGEERR, "{0}Array too large")
+
+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")
+MSG_DEF(JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM, 0, JSEXN_RANGEERR, "byte length is larger than maximum byte length")
+MSG_DEF(JSMSG_ARRAYBUFFER_NON_RESIZABLE, 0, JSEXN_TYPEERR, "can't resize non-resizable ArrayBuffer")
+MSG_DEF(JSMSG_ARRAYBUFFER_COPY_RANGE, 0, JSEXN_RANGEERR, "ArrayBuffer range incorrect for copying")
+
+// 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")
+MSG_DEF(JSMSG_SHARED_ARRAY_LENGTH_SMALLER_THAN_CURRENT, 0, JSEXN_RANGEERR, "byte length is smaller than current byte length")
+
+// 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_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_DEFINE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't define property {0} on window's named properties 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_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_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
+MSG_DEF(JSMSG_BAD_MODULE_STATUS, 1, JSEXN_INTERNALERR, "module record has unexpected status: {0}")
+MSG_DEF(JSMSG_DYNAMIC_IMPORT_FAILED, 1, JSEXN_TYPEERR, "error loading dynamically imported module: {0}")
+MSG_DEF(JSMSG_DYNAMIC_IMPORT_NOT_SUPPORTED, 0, JSEXN_TYPEERR, "Dynamic import not supported in this context")
+
+// Import maps
+MSG_DEF(JSMSG_IMPORT_MAPS_PARSE_FAILED, 1, JSEXN_SYNTAXERR, "Failed to parse import map: Invalid JSON format. {0}")
+MSG_DEF(JSMSG_IMPORT_MAPS_NOT_A_MAP, 0, JSEXN_TYPEERR, "the top-level value needs to be a JSON object")
+MSG_DEF(JSMSG_IMPORT_MAPS_IMPORTS_NOT_A_MAP, 0, JSEXN_TYPEERR, "the imports top-level key needs to be a JSON object")
+MSG_DEF(JSMSG_IMPORT_MAPS_SCOPES_NOT_A_MAP, 0, JSEXN_TYPEERR, "the scopes top-level key needs to be a JSON object")
+MSG_DEF(JSMSG_IMPORT_MAPS_SCOPE_VALUE_NOT_A_MAP, 1, JSEXN_TYPEERR, "the value of the scope with prefix '{0}' needs to be a JSON object")
+
+// 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_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
+MSG_DEF(JSMSG_SUSPENDED_QUEUE_NOT_EMPTY, 0, JSEXN_INTERNALERR, "Async generator is in invalid state due to debugger interaction")
+
+// 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_TYPEERR,"'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_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_DEFAULT_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableStreamDefaultController.")
+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_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.")
+
+// Other Stream-related
+MSG_DEF(JSMSG_STREAM_MISSING_HIGHWATERMARK, 0, JSEXN_TYPEERR, "'highWaterMark' must not be undefined.")
+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")
+
+// FinalizationRegistry
+MSG_DEF(JSMSG_NOT_A_FINALIZATION_REGISTRY, 1, JSEXN_TYPEERR, "{0} is not a FinalizationRegistry")
+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_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")
+
+// Set
+MSG_DEF(JSMSG_SET_NEGATIVE_SIZE, 0, JSEXN_RANGEERR, "Set size must be non-negative")
+
+// Record and Tuple
+MSG_DEF(JSMSG_RECORD_TUPLE_NO_OBJECT, 0, JSEXN_TYPEERR, "Record and Tuple can only contain primitive values")
+MSG_DEF(JSMSG_RECORD_NO_PROTO, 0, JSEXN_SYNTAXERR, "__proto__ is not a valid literal key in records")
+MSG_DEF(JSMSG_RECORD_NO_SYMBOL_KEY, 0, JSEXN_TYPEERR, "Symbols cannot be used as record keys")
+MSG_DEF(JSMSG_BAD_TUPLE_INDEX, 0, JSEXN_RANGEERR, "index out of range for tuple")
+MSG_DEF(JSMSG_BAD_TUPLE_OBJECT, 0, JSEXN_TYPEERR, "value of TupleObject must be a Tuple")
+MSG_DEF(JSMSG_RECORD_TUPLE_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert Record or Tuple to number")
+
+
+// Shadow Realms
+MSG_DEF(JSMSG_NOT_SHADOW_REALM, 0, JSEXN_TYPEERR, "Object is not a ShadowRealm")
+MSG_DEF(JSMSG_SHADOW_REALM_EVALUATE_NOT_STRING, 0, JSEXN_TYPEERR, "a ShadowRealm can only evaluate a string")
+MSG_DEF(JSMSG_SHADOW_REALM_INVALID_RETURN, 0, JSEXN_TYPEERR, "return value not primitive or callable")
+MSG_DEF(JSMSG_SHADOW_REALM_WRAP_FAILURE, 0, JSEXN_TYPEERR, "unable to wrap callable return object")
+MSG_DEF(JSMSG_SHADOW_REALM_EVALUATE_FAILURE, 0, JSEXN_TYPEERR, "evaluate failed.")
+MSG_DEF(JSMSG_SHADOW_REALM_EVALUATE_FAILURE_DETAIL, 1, JSEXN_TYPEERR, "evaluate failed, error was {0}")
+MSG_DEF(JSMSG_SHADOW_REALM_WRAPPED_EXECUTION_FAILURE, 0, JSEXN_TYPEERR, "wrapped function threw.")
+MSG_DEF(JSMSG_SHADOW_REALM_WRAPPED_EXECUTION_FAILURE_DETAIL, 1, JSEXN_TYPEERR, "wrapped function threw, error was {0}")
+
+MSG_DEF(JSMSG_SHADOW_REALM_EXPORT_NOT_STRING, 0, JSEXN_TYPEERR, "exportName must be a string")
+MSG_DEF(JSMSG_SHADOW_REALM_IMPORTVALUE_FAILED, 0, JSEXN_TYPEERR, "import value failed")
+MSG_DEF(JSMSG_SHADOW_REALM_VALUE_NOT_EXPORTED, 0, JSEXN_TYPEERR, "value not exported")
+
+// Decorators
+MSG_DEF(JSMSG_DECORATOR_INVALID_RETURN_TYPE, 0, JSEXN_TYPEERR, "Invalid value returned from decorator")
+
+// Temporal
+MSG_DEF(JSMSG_TEMPORAL_INVALID_UNIT_RANGE, 0, JSEXN_RANGEERR, "smallestUnit must not be larger than largestUnit")
+MSG_DEF(JSMSG_TEMPORAL_INVALID_UNIT_OPTION, 2, JSEXN_RANGEERR, "{0} is not a valid {1} option in this context")
+MSG_DEF(JSMSG_TEMPORAL_INVALID_NUMBER, 1, JSEXN_RANGEERR, "{0} must be larger than zero")
+MSG_DEF(JSMSG_TEMPORAL_INVALID_INTEGER, 1, JSEXN_RANGEERR, "{0} must be an integer")
+MSG_DEF(JSMSG_TEMPORAL_INVALID_OBJECT, 2, JSEXN_TYPEERR, "{0} must not be a {1} object")
+MSG_DEF(JSMSG_TEMPORAL_MISSING_OPTION, 1, JSEXN_RANGEERR, "undefined {0} option")
+MSG_DEF(JSMSG_TEMPORAL_MISSING_PROPERTY, 1, JSEXN_TYPEERR, "{0} property is undefined")
+MSG_DEF(JSMSG_TEMPORAL_UNEXPECTED_PROPERTY, 1, JSEXN_TYPEERR, "{0} property is not undefined")
+MSG_DEF(JSMSG_TEMPORAL_MISSING_TEMPORAL_FIELDS, 0, JSEXN_TYPEERR, "object must have at least one temporal property")
+MSG_DEF(JSMSG_TEMPORAL_DUPLICATE_PROPERTY, 1, JSEXN_RANGEERR, "duplicate property name \"{0}\"")
+MSG_DEF(JSMSG_TEMPORAL_INVALID_PROPERTY, 1, JSEXN_RANGEERR, "invalid property name \"{0}\"")
+MSG_DEF(JSMSG_TEMPORAL_INSTANT_INVALID, 0, JSEXN_RANGEERR, "epoch nanoseconds too large")
+MSG_DEF(JSMSG_TEMPORAL_INSTANT_NONINTEGER, 1, JSEXN_RANGEERR, "Instant must be an integer, but received {0}")
+MSG_DEF(JSMSG_TEMPORAL_INSTANT_BAD_DURATION, 1, JSEXN_RANGEERR, "duration \"{0}\" property must be zero")
+MSG_DEF(JSMSG_TEMPORAL_TIMEZONE_INVALID_IDENTIFIER, 1, JSEXN_RANGEERR, "invalid time zone: {0}")
+MSG_DEF(JSMSG_TEMPORAL_TIMEZONE_NANOS_RANGE, 1, JSEXN_RANGEERR, "nanoseconds out of range: {0}")
+MSG_DEF(JSMSG_TEMPORAL_TIMEZONE_INCOMPATIBLE, 2, JSEXN_RANGEERR, "time zones \"{0}\" and \"{1}\" aren't compatible")
+MSG_DEF(JSMSG_TEMPORAL_TIMEZONE_INSTANT_AMBIGUOUS, 0, JSEXN_RANGEERR, "instant is ambiguous")
+MSG_DEF(JSMSG_TEMPORAL_DURATION_INVALID_SIGN, 2, JSEXN_RANGEERR, "duration value \"{0}\" has a mismatched sign: {1}")
+MSG_DEF(JSMSG_TEMPORAL_DURATION_INVALID_NON_FINITE, 2, JSEXN_RANGEERR, "duration value \"{0}\" is {1}")
+MSG_DEF(JSMSG_TEMPORAL_DURATION_MISSING_UNIT, 0, JSEXN_TYPEERR, "Duration-like objects must have at least one duration unit")
+MSG_DEF(JSMSG_TEMPORAL_DURATION_NOT_INTEGER, 2, JSEXN_RANGEERR, "{0} isn't a valid {1} duration because it isn't an integer")
+MSG_DEF(JSMSG_TEMPORAL_DURATION_MISSING_UNIT_SPECIFIER, 0, JSEXN_RANGEERR, "at least one of \"smallestUnit\" and \"largestUnit\" must be present")
+MSG_DEF(JSMSG_TEMPORAL_DURATION_UNCOMPARABLE, 1, JSEXN_RANGEERR, "can't compare durations when \"{0}\" is undefined")
+MSG_DEF(JSMSG_TEMPORAL_CALENDAR_INVALID_ID, 1, JSEXN_RANGEERR, "invalid calendar: {0}")
+MSG_DEF(JSMSG_TEMPORAL_CALENDAR_MISSING_FIELD, 1, JSEXN_TYPEERR, "missing \"{0}\" calendar field")
+MSG_DEF(JSMSG_TEMPORAL_CALENDAR_INVALID_FIELD, 1, JSEXN_RANGEERR, "invalid calendar field name \"{0}\"")
+MSG_DEF(JSMSG_TEMPORAL_CALENDAR_DUPLICATE_FIELD, 1, JSEXN_RANGEERR, "duplicate calendar field \"{0}\"")
+MSG_DEF(JSMSG_TEMPORAL_CALENDAR_INVALID_MONTHCODE, 1, JSEXN_RANGEERR, "invalid \"monthCode\" calendar field: {0}")
+MSG_DEF(JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE, 2, JSEXN_RANGEERR, "calendars \"{0}\" and \"{1}\" aren't compatible")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_DATE_INVALID, 0, JSEXN_RANGEERR, "year-month-day must be valid iso dates")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_DATE_INVALID_VALUE, 4, JSEXN_RANGEERR, "date value \"{0}\" not in {1}..{2}: {3}")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID, 0, JSEXN_RANGEERR, "arguments must be valid ISO date-time values")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_TIME_INVALID, 0, JSEXN_RANGEERR, "arguments must be valid ISO time values")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_TIME_INVALID_VALUE, 4, JSEXN_RANGEERR, "time value \"{0}\" not in {1}..{2}: {3}")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_TIME_MISSING_UNIT, 0, JSEXN_TYPEERR, "Time-like objects must have at least one time unit")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_TIME_CALENDAR_NOT_ISO8601, 1, JSEXN_RANGEERR, "PlainTime only supports the \"iso8601\" calendar: {0}")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_MONTH_DAY_INVALID, 0, JSEXN_RANGEERR, "year-month-day must be valid iso dates")
+MSG_DEF(JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID, 0, JSEXN_RANGEERR, "year-month-day must be valid iso dates")
+MSG_DEF(JSMSG_TEMPORAL_ZONED_DATE_TIME_NO_TIME_FOUND, 0, JSEXN_RANGEERR, "date-time can't be represented in the given time zone")
+MSG_DEF(JSMSG_TEMPORAL_ZONED_DATE_TIME_NON_POSITIVE_DAY_LENGTH, 0, JSEXN_RANGEERR, "day length must be positive number")
+MSG_DEF(JSMSG_TEMPORAL_ZONED_DATE_TIME_INCORRECT_SIGN, 1, JSEXN_RANGEERR, "computed {0} has an incorrect sign")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_NEGATIVE_ZERO_YEAR, 0, JSEXN_RANGEERR, "year 0 must not start with \"-\"")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_EXTENDED_YEAR, 0, JSEXN_RANGEERR, "signed year must be followed by six digits")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_YEAR, 0, JSEXN_RANGEERR, "missing four digit year")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_MONTH, 0, JSEXN_RANGEERR, "missing two digit month")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_DAY, 0, JSEXN_RANGEERR, "missing two digit day")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_HOUR, 0, JSEXN_RANGEERR, "missing two digit hour")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_MINUTE, 0, JSEXN_RANGEERR, "missing two digit minute")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_SECOND, 0, JSEXN_RANGEERR, "missing two digit second")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE_SIGN, 0, JSEXN_RANGEERR, "missing time zone sign")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_MONTH, 0, JSEXN_RANGEERR, "month must be a number from 1 to 12")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_DAY, 0, JSEXN_RANGEERR, "day must be a number from 1 to 31")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_HOUR, 0, JSEXN_RANGEERR, "hour must be a number from 0 to 23")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_MINUTE, 0, JSEXN_RANGEERR, "minute must be a number from 0 to 59")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_SECOND, 0, JSEXN_RANGEERR, "second must be a number from 0 to 59")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_LEAPSECOND, 0, JSEXN_RANGEERR, "second must be a number from 0 to 60")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_BRACKET_BEFORE_TIMEZONE, 0, JSEXN_RANGEERR, "missing '[' before time zone annotation")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_BRACKET_AFTER_TIMEZONE, 0, JSEXN_RANGEERR, "missing ']' after time zone annotation")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE, 0, JSEXN_RANGEERR, "missing time zone")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_TIMEZONE_NAME, 0, JSEXN_RANGEERR, "missing time zone name")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT, 0, JSEXN_RANGEERR, "unexpected garbage after end of input")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DESIGNATOR, 0, JSEXN_RANGEERR, "missing duration designator 'P'")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_TIME_DESIGNATOR, 0, JSEXN_RANGEERR, "missing time designator 'T'")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_DURATION_DIGITS, 0, JSEXN_RANGEERR, "missing duration digits")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_DURATION_MINUTES, 0, JSEXN_RANGEERR, "invalid duration minutes after fractional hours")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_DURATION_SECONDS, 0, JSEXN_RANGEERR, "invalid duration seconds after fractional hours or minutes")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_ANNOTATION_KEY, 0, JSEXN_RANGEERR, "invalid annotation key")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_ANNOTATION_VALUE, 0, JSEXN_RANGEERR, "invalid annotation value")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_CALENDAR_NAME, 0, JSEXN_RANGEERR, "invalid calendar name")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_BRACKET_BEFORE_ANNOTATION, 0, JSEXN_RANGEERR, "missing ']' before annotation")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_BRACKET_AFTER_ANNOTATION, 0, JSEXN_RANGEERR, "missing ']' after annotation")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_ASSIGNMENT_IN_ANNOTATION, 0, JSEXN_RANGEERR, "missing '=' in annotation")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_CRITICAL_ANNOTATION, 0, JSEXN_RANGEERR, "unexpected critical annotation")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MISSING_DATE_TIME_SEPARATOR, 0, JSEXN_RANGEERR, "missing date-time separator 'T'")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_AMBIGUOUS_TIME_MONTH_DAY, 0, JSEXN_RANGEERR, "time is ambiguous with a month-day")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_AMBIGUOUS_TIME_YEAR_MONTH, 0, JSEXN_RANGEERR, "time is ambiguous with a year-month")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR, 0, JSEXN_RANGEERR, "unexpected UTC designator 'Z'")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_UTC_DESIGNATOR_WITHOUT_NAME, 0, JSEXN_RANGEERR, "unexpected UTC designator 'Z' without a bracketed time zone")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_MONTH_DAY_CALENDAR_NOT_ISO8601, 0, JSEXN_RANGEERR, "Month-Day formats only support the \"iso8601\" calendar")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_YEAR_MONTH_CALENDAR_NOT_ISO8601, 0, JSEXN_RANGEERR, "Year-Month formats only support the \"iso8601\" calendar")
+MSG_DEF(JSMSG_TEMPORAL_PARSER_INVALID_SUBMINUTE_TIMEZONE, 0, JSEXN_RANGEERR, "time zone offset must not contain seconds precision")
+
+//clang-format on
diff --git a/js/public/friend/JSMEnvironment.h b/js/public/friend/JSMEnvironment.h
new file mode 100644
index 0000000000..ad16f2ba62
--- /dev/null
+++ b/js/public/friend/JSMEnvironment.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/. */
+
+/*
+ * 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_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_PUBLIC_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_PUBLIC_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.
+extern JS_PUBLIC_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_PUBLIC_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_PUBLIC_API bool IsJSMEnvironment(JSObject* obj);
+
+} // namespace JS
+
+#endif // js_friend_JSMEnvironment_h
diff --git a/js/public/friend/PerformanceHint.h b/js/public/friend/PerformanceHint.h
new file mode 100644
index 0000000000..ca743e3ab7
--- /dev/null
+++ b/js/public/friend/PerformanceHint.h
@@ -0,0 +1,29 @@
+/* -*- 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_PerformanceHint_h
+#define js_friend_PerformanceHint_h
+
+#include "jstypes.h" // JS_PUBLIC_API
+#include "js/TypeDecls.h" // JSContext
+
+namespace js {
+namespace gc {
+
+// API to let the DOM tell us whether we're currently in pageload.
+//
+// This currently affects nursery sizing; we tolerate large nursery sizes (and
+// hence longer minor GC pauses) during pageload so as not to limit performance.
+
+enum class PerformanceHint { Normal, InPageLoad };
+
+extern JS_PUBLIC_API void SetPerformanceHint(JSContext* cx,
+ PerformanceHint hint);
+
+} /* namespace gc */
+} /* namespace js */
+
+#endif // js_friend_PerformanceHint_h
diff --git a/js/public/friend/StackLimits.h b/js/public/friend/StackLimits.h
new file mode 100644
index 0000000000..b4f1e010a8
--- /dev/null
+++ b/js/public/friend/StackLimits.h
@@ -0,0 +1,335 @@
+/* -*- 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 "mozilla/Variant.h" // mozilla::Variant, mozilla::AsVariant
+
+#include <stddef.h> // size_t
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/HeapAPI.h" // JS::StackKind, JS::StackForTrustedScript, JS::StackForUntrustedScript
+#include "js/RootingAPI.h" // JS::RootingContext
+#include "js/Stack.h" // JS::NativeStackLimit
+#include "js/Utility.h" // 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
+
+namespace js {
+
+class FrontendContext;
+
+#ifdef __wasi__
+extern MOZ_COLD JS_PUBLIC_API void IncWasiRecursionDepth(JSContext* cx);
+extern MOZ_COLD JS_PUBLIC_API void DecWasiRecursionDepth(JSContext* cx);
+extern MOZ_COLD JS_PUBLIC_API bool CheckWasiRecursionLimit(JSContext* cx);
+
+extern MOZ_COLD JS_PUBLIC_API void IncWasiRecursionDepth(FrontendContext* fc);
+extern MOZ_COLD JS_PUBLIC_API void DecWasiRecursionDepth(FrontendContext* fc);
+extern MOZ_COLD JS_PUBLIC_API bool CheckWasiRecursionLimit(FrontendContext* fc);
+#endif // __wasi__
+
+// The minimum margin for stack limit to ensure that the periodic
+// AutoCheckRecursionLimit operation is sufficient.
+//
+// See FrontendContext::checkAndUpdateFrontendContextRecursionLimit and
+// JS::ThreadStackQuotaForSize.
+static constexpr size_t MinimumStackLimitMargin = 32 * 1024;
+
+// AutoCheckRecursionLimit can be used to check whether we're close to using up
+// the C++ stack.
+//
+// Typical usage is like this:
+//
+// AutoCheckRecursionLimit recursion(cx);
+// if (!recursion.check(cx)) {
+// return false;
+// }
+//
+// The check* functions return |false| if we are close to the stack limit.
+// They also report an overrecursion error, except for the DontReport variants.
+//
+// The checkSystem variant gives us a little extra space so we can ensure that
+// crucial code is able to run.
+//
+// checkConservative 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).
+class MOZ_RAII AutoCheckRecursionLimit {
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkLimitImpl(
+ JS::NativeStackLimit limit, void* sp) const;
+
+ MOZ_ALWAYS_INLINE JS::NativeStackLimit getStackLimitSlow(JSContext* cx) const;
+ MOZ_ALWAYS_INLINE JS::NativeStackLimit getStackLimitHelper(
+ JSContext* cx, JS::StackKind kind, int extraAllowance) const;
+
+ JS::NativeStackLimit getStackLimit(FrontendContext* fc) const;
+
+ JS_PUBLIC_API JS::StackKind stackKindForCurrentPrincipal(JSContext* cx) const;
+
+#ifdef __wasi__
+ // The JSContext outlives AutoCheckRecursionLimit so it is safe to use raw
+ // pointer here.
+ mozilla::Variant<JSContext*, FrontendContext*> context_;
+#endif // __wasi__
+
+ public:
+ explicit MOZ_ALWAYS_INLINE AutoCheckRecursionLimit(JSContext* cx)
+#ifdef __wasi__
+ : context_(mozilla::AsVariant(cx))
+#endif // __wasi__
+ {
+#ifdef __wasi__
+ incWasiRecursionDepth();
+#endif // __wasi__
+ }
+
+ explicit MOZ_ALWAYS_INLINE AutoCheckRecursionLimit(FrontendContext* fc)
+#ifdef __wasi__
+ : context_(mozilla::AsVariant(fc))
+#endif // __wasi__
+ {
+#ifdef __wasi__
+ incWasiRecursionDepth();
+#endif // __wasi__
+ }
+
+ MOZ_ALWAYS_INLINE ~AutoCheckRecursionLimit() {
+#ifdef __wasi__
+ decWasiRecursionDepth();
+#endif // __wasi__
+ }
+
+#ifdef __wasi__
+ MOZ_ALWAYS_INLINE void incWasiRecursionDepth() {
+ if (context_.is<JSContext*>()) {
+ JSContext* cx = context_.as<JSContext*>();
+ IncWasiRecursionDepth(cx);
+ } else {
+ FrontendContext* fc = context_.as<FrontendContext*>();
+ IncWasiRecursionDepth(fc);
+ }
+ }
+
+ MOZ_ALWAYS_INLINE void decWasiRecursionDepth() {
+ if (context_.is<JSContext*>()) {
+ JSContext* cx = context_.as<JSContext*>();
+ DecWasiRecursionDepth(cx);
+ } else {
+ FrontendContext* fc = context_.as<FrontendContext*>();
+ DecWasiRecursionDepth(fc);
+ }
+ }
+
+ MOZ_ALWAYS_INLINE bool checkWasiRecursionLimit() const {
+ if (context_.is<JSContext*>()) {
+ JSContext* cx = context_.as<JSContext*>();
+ if (!CheckWasiRecursionLimit(cx)) {
+ return false;
+ }
+ } else {
+ FrontendContext* fc = context_.as<FrontendContext*>();
+ if (!CheckWasiRecursionLimit(fc)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+#endif // __wasi__
+
+ AutoCheckRecursionLimit(const AutoCheckRecursionLimit&) = delete;
+ void operator=(const AutoCheckRecursionLimit&) = delete;
+
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool check(JSContext* cx) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool check(FrontendContext* fc) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkDontReport(JSContext* cx) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkDontReport(
+ FrontendContext* fc) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkWithExtra(JSContext* cx,
+ size_t extra) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkWithStackPointerDontReport(
+ JSContext* cx, void* sp) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkWithStackPointerDontReport(
+ FrontendContext* fc, void* sp) const;
+
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkConservative(JSContext* cx) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkConservativeDontReport(
+ JSContext* cx) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkConservativeDontReport(
+ JS::NativeStackLimit limit) const;
+
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkSystem(JSContext* cx) const;
+ [[nodiscard]] MOZ_ALWAYS_INLINE bool checkSystemDontReport(
+ JSContext* cx) const;
+};
+
+extern MOZ_COLD JS_PUBLIC_API void ReportOverRecursed(JSContext* maybecx);
+extern MOZ_COLD JS_PUBLIC_API void ReportOverRecursed(FrontendContext* fc);
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkLimitImpl(
+ JS::NativeStackLimit limit, void* sp) const {
+ JS_STACK_OOM_POSSIBLY_FAIL();
+
+#ifdef __wasi__
+ if (!checkWasiRecursionLimit()) {
+ return false;
+ }
+#endif // __wasi__
+
+#if JS_STACK_GROWTH_DIRECTION > 0
+ return MOZ_LIKELY(JS::NativeStackLimit(sp) < limit);
+#else
+ return MOZ_LIKELY(JS::NativeStackLimit(sp) > limit);
+#endif
+}
+
+MOZ_ALWAYS_INLINE JS::NativeStackLimit
+AutoCheckRecursionLimit::getStackLimitSlow(JSContext* cx) const {
+ JS::StackKind kind = stackKindForCurrentPrincipal(cx);
+ return getStackLimitHelper(cx, kind, 0);
+}
+
+MOZ_ALWAYS_INLINE JS::NativeStackLimit
+AutoCheckRecursionLimit::getStackLimitHelper(JSContext* cx, JS::StackKind kind,
+ int extraAllowance) const {
+ JS::NativeStackLimit limit =
+ JS::RootingContext::get(cx)->nativeStackLimit[kind];
+#if JS_STACK_GROWTH_DIRECTION > 0
+ limit += extraAllowance;
+#else
+ limit -= extraAllowance;
+#endif
+ return limit;
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::check(JSContext* cx) const {
+ if (MOZ_UNLIKELY(!checkDontReport(cx))) {
+ ReportOverRecursed(cx);
+ return false;
+ }
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::check(
+ FrontendContext* fc) const {
+ if (MOZ_UNLIKELY(!checkDontReport(fc))) {
+ ReportOverRecursed(fc);
+ return false;
+ }
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkDontReport(
+ JSContext* cx) const {
+ int stackDummy;
+ return checkWithStackPointerDontReport(cx, &stackDummy);
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkDontReport(
+ FrontendContext* fc) const {
+ int stackDummy;
+ return checkWithStackPointerDontReport(fc, &stackDummy);
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkWithStackPointerDontReport(
+ JSContext* cx, void* sp) const {
+ // getStackLimitSlow(cx) is pretty slow because it has to do an uninlined
+ // call to stackKindForCurrentPrincipal to determine which stack limit to
+ // use. To work around this, check the untrusted limit first to avoid the
+ // overhead in most cases.
+ JS::NativeStackLimit untrustedLimit =
+ getStackLimitHelper(cx, JS::StackForUntrustedScript, 0);
+ if (MOZ_LIKELY(checkLimitImpl(untrustedLimit, sp))) {
+ return true;
+ }
+ return checkLimitImpl(getStackLimitSlow(cx), sp);
+}
+
+#ifdef DEBUG
+extern void CheckAndUpdateFrontendContextRecursionLimit(FrontendContext* fc,
+ void* sp);
+#endif
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkWithStackPointerDontReport(
+ FrontendContext* fc, void* sp) const {
+#ifdef DEBUG
+ CheckAndUpdateFrontendContextRecursionLimit(fc, sp);
+#endif
+ return checkLimitImpl(getStackLimit(fc), sp);
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkWithExtra(
+ JSContext* cx, size_t extra) const {
+ char stackDummy;
+ char* sp = &stackDummy;
+#if JS_STACK_GROWTH_DIRECTION > 0
+ sp += extra;
+#else
+ sp -= extra;
+#endif
+ if (MOZ_UNLIKELY(!checkWithStackPointerDontReport(cx, sp))) {
+ ReportOverRecursed(cx);
+ return false;
+ }
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkSystem(
+ JSContext* cx) const {
+ if (MOZ_UNLIKELY(!checkSystemDontReport(cx))) {
+ ReportOverRecursed(cx);
+ return false;
+ }
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkSystemDontReport(
+ JSContext* cx) const {
+ JS::NativeStackLimit limit =
+ getStackLimitHelper(cx, JS::StackForSystemCode, 0);
+ int stackDummy;
+ return checkLimitImpl(limit, &stackDummy);
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkConservative(
+ JSContext* cx) const {
+ if (MOZ_UNLIKELY(!checkConservativeDontReport(cx))) {
+ ReportOverRecursed(cx);
+ return false;
+ }
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkConservativeDontReport(
+ JSContext* cx) const {
+ JS::NativeStackLimit limit = getStackLimitHelper(
+ cx, JS::StackForUntrustedScript, -4096 * int(sizeof(size_t)));
+ int stackDummy;
+ return checkLimitImpl(limit, &stackDummy);
+}
+
+MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkConservativeDontReport(
+ JS::NativeStackLimit limit) const {
+ int stackDummy;
+ return checkLimitImpl(limit, &stackDummy);
+}
+
+} // 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..27fea9b1a8
--- /dev/null
+++ b/js/public/friend/UsageStatistics.h
@@ -0,0 +1,101 @@
+/* -*- 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_PUBLIC_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 FOR_EACH_JS_METRIC(_) \
+ _(GC_REASON_2, Enumeration) \
+ _(GC_IS_COMPARTMENTAL, Boolean) \
+ _(GC_ZONE_COUNT, QuantityDistribution) \
+ _(GC_ZONES_COLLECTED, QuantityDistribution) \
+ _(GC_MS, TimeDuration_MS) \
+ _(GC_BUDGET_MS_2, TimeDuration_MS) \
+ _(GC_BUDGET_WAS_INCREASED, Boolean) \
+ _(GC_SLICE_WAS_LONG, Boolean) \
+ _(GC_BUDGET_OVERRUN, TimeDuration_US) \
+ _(GC_ANIMATION_MS, TimeDuration_MS) \
+ _(GC_MAX_PAUSE_MS_2, TimeDuration_MS) \
+ _(GC_PREPARE_MS, TimeDuration_MS) \
+ _(GC_MARK_MS, TimeDuration_MS) \
+ _(GC_SWEEP_MS, TimeDuration_MS) \
+ _(GC_COMPACT_MS, TimeDuration_MS) \
+ _(GC_MARK_ROOTS_US, TimeDuration_US) \
+ _(GC_MARK_GRAY_MS_2, TimeDuration_MS) \
+ _(GC_MARK_WEAK_MS, TimeDuration_MS) \
+ _(GC_SLICE_MS, TimeDuration_MS) \
+ _(GC_SLOW_PHASE, Enumeration) \
+ _(GC_SLOW_TASK, Enumeration) \
+ _(GC_MMU_50, Percentage) \
+ _(GC_RESET, Boolean) \
+ _(GC_RESET_REASON, Enumeration) \
+ _(GC_NON_INCREMENTAL, Boolean) \
+ _(GC_NON_INCREMENTAL_REASON, Enumeration) \
+ _(GC_MINOR_REASON, Enumeration) \
+ _(GC_MINOR_REASON_LONG, Enumeration) \
+ _(GC_MINOR_US, TimeDuration_US) \
+ _(GC_NURSERY_BYTES_2, MemoryDistribution) \
+ _(GC_PRETENURE_COUNT_2, QuantityDistribution) \
+ _(GC_NURSERY_PROMOTION_RATE, Percentage) \
+ _(GC_TENURED_SURVIVAL_RATE, Percentage) \
+ _(GC_MARK_RATE_2, QuantityDistribution) \
+ _(GC_TIME_BETWEEN_S, TimeDuration_S) \
+ _(GC_TIME_BETWEEN_SLICES_MS, TimeDuration_MS) \
+ _(GC_SLICE_COUNT, QuantityDistribution) \
+ _(DESERIALIZE_BYTES, MemoryDistribution) \
+ _(DESERIALIZE_ITEMS, QuantityDistribution) \
+ _(DESERIALIZE_US, TimeDuration_US) \
+ _(GC_EFFECTIVENESS, MemoryDistribution) \
+ _(GC_PARALLEL_MARK, Boolean) \
+ _(GC_PARALLEL_MARK_SPEEDUP, Integer) \
+ _(GC_PARALLEL_MARK_UTILIZATION, Percentage) \
+ _(GC_PARALLEL_MARK_INTERRUPTIONS, Integer) \
+ _(GC_TASK_START_DELAY_US, TimeDuration_US)
+
+// clang-format off
+#define ENUM_DEF(NAME, _) NAME,
+enum class JSMetric {
+ FOR_EACH_JS_METRIC(ENUM_DEF)
+ Count
+};
+#undef ENUM_DEF
+// clang-format on
+
+using JSAccumulateTelemetryDataCallback = void (*)(JSMetric, uint32_t);
+
+extern JS_PUBLIC_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_LEGACY_EXCEPTIONS, LATE_WEEKDAY };
+
+using JSSetUseCounterCallback = void (*)(JSObject*, JSUseCounter);
+
+extern JS_PUBLIC_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..9023e9e96b
--- /dev/null
+++ b/js/public/friend/WindowProxy.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/. */
+
+/*
+ * 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_PUBLIC_API
+
+#include "js/Class.h" // JSCLASS_IS_GLOBAL
+#include "js/Object.h" // JS::GetClass
+#include "js/RootingAPI.h" // JS::Handle
+
+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_PUBLIC_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_PUBLIC_API void SetWindowProxy(JSContext* cx,
+ JS::Handle<JSObject*> global,
+ JS::Handle<JSObject*> windowProxy);
+
+namespace detail {
+
+extern JS_PUBLIC_API bool IsWindowSlow(JSObject* obj);
+
+extern JS_PUBLIC_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_PUBLIC_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_PUBLIC_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..ddeec6a44f
--- /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_PUBLIC_API
+
+class JS_PUBLIC_API JSObject;
+
+namespace js {
+
+class JS_PUBLIC_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_PUBLIC_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..a2c4dcec67
--- /dev/null
+++ b/js/public/shadow/Function.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/. */
+
+/* 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
+#include "js/Value.h" // JS::Value
+
+class JS_PUBLIC_API JSFunction;
+class JSJitInfo;
+
+namespace JS {
+
+namespace shadow {
+
+struct Function : shadow::Object {
+ enum {
+ FlagsAndArgCountSlot,
+ NativeFuncOrInterpretedEnvSlot,
+ NativeJitInfoOrInterpretedScriptSlot,
+ AtomSlot
+ };
+ uint32_t flagsAndArgCount() const {
+ return fixedSlots()[FlagsAndArgCountSlot].toPrivateUint32();
+ }
+
+ void* jitInfoOrScript() const {
+ return fixedSlots()[NativeJitInfoOrInterpretedScriptSlot].toPrivate();
+ }
+
+ void setJitInfoOrScript(void* ptr) {
+ fixedSlots()[NativeJitInfoOrInterpretedScriptSlot] = JS::PrivateValue(ptr);
+ }
+};
+
+} // 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..6f77de71ba
--- /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 {
+
+/**
+ * This layout is shared by all native objects. For non-native objects, the
+ * shape may always be accessed safely, and other members may be as well,
+ * depending on the object's specific layout.
+ */
+struct Object {
+ shadow::Shape* shape;
+#ifndef JS_64BIT
+ uint32_t padding_;
+#endif
+ 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/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..c83d320bf2
--- /dev/null
+++ b/js/public/shadow/Shape.h
@@ -0,0 +1,65 @@
+/* -*- 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;
+ JS::Realm* realm;
+};
+
+class Shape {
+ public:
+ shadow::BaseShape* base;
+ uint32_t immutableFlags;
+
+ enum class Kind : uint8_t {
+ // SharedShape or DictionaryShape for NativeObject.
+ // (Only) these two kinds must have the low bit set, to allow for fast
+ // is-native checking.
+ Shared = 1,
+ Dictionary = 3,
+ // ProxyShape for ProxyObject.
+ Proxy = 0,
+ // WasmGCShape for WasmGCObject.
+ WasmGC = 2,
+ };
+
+ static constexpr uint32_t KIND_SHIFT = 4;
+ static constexpr uint32_t KIND_MASK = 0b11;
+
+ static constexpr uint32_t FIXED_SLOTS_SHIFT = 6;
+ static constexpr uint32_t FIXED_SLOTS_MASK = 0x1f << FIXED_SLOTS_SHIFT;
+
+ bool isProxy() const {
+ return Kind((immutableFlags >> KIND_SHIFT) & KIND_MASK) == Kind::Proxy;
+ }
+};
+
+} // 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..02cd57a14b
--- /dev/null
+++ b/js/public/shadow/Zone.h
@@ -0,0 +1,126 @@
+/* -*- 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 "mozilla/Atomics.h"
+
+#include <stdint.h> // uint8_t, uint32_t
+
+#include "jspubtd.h" // js::CurrentThreadCanAccessRuntime
+#include "jstypes.h" // js::Bit
+
+struct JS_PUBLIC_API JSRuntime;
+class JS_PUBLIC_API JSTracer;
+
+namespace JS {
+
+namespace shadow {
+
+struct Zone {
+ enum GCState : uint32_t {
+ NoGC = 0,
+ Prepare,
+ MarkBlackOnly,
+ MarkBlackAndGray,
+ Sweep,
+ Finished,
+ Compact,
+ VerifyPreBarriers,
+
+ Limit
+ };
+
+ using BarrierState = mozilla::Atomic<uint32_t, mozilla::Relaxed>;
+
+ enum Kind : uint8_t { NormalZone, AtomsZone, SystemZone };
+
+ protected:
+ JSRuntime* const runtime_;
+ JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|.
+ BarrierState needsIncrementalBarrier_;
+ GCState gcState_ = NoGC;
+ const Kind kind_;
+
+ Zone(JSRuntime* runtime, JSTracer* barrierTracerArg, Kind kind)
+ : runtime_(runtime), barrierTracer_(barrierTracerArg), kind_(kind) {
+ MOZ_ASSERT(!needsIncrementalBarrier());
+ }
+
+ 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(uint32_t(gcState_)); }
+
+ static constexpr uint32_t gcStateMask(GCState state) {
+ static_assert(uint32_t(Limit) < 32);
+ return js::Bit(state);
+ }
+
+ bool hasAnyGCState(uint32_t stateMask) const {
+ return js::Bit(gcState_) & stateMask;
+ }
+
+ 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 hasAnyGCState(gcStateMask(MarkBlackOnly) |
+ gcStateMask(MarkBlackAndGray));
+ }
+ bool isGCMarkingOrSweeping() const {
+ return hasAnyGCState(gcStateMask(MarkBlackOnly) |
+ gcStateMask(MarkBlackAndGray) | gcStateMask(Sweep));
+ }
+ bool isGCMarkingOrVerifyingPreBarriers() const {
+ return hasAnyGCState(gcStateMask(MarkBlackOnly) |
+ gcStateMask(MarkBlackAndGray) |
+ gcStateMask(VerifyPreBarriers));
+ }
+ bool isGCSweepingOrCompacting() const {
+ return hasAnyGCState(gcStateMask(Sweep) | gcStateMask(Compact));
+ }
+ bool isVerifyingPreBarriers() const { return gcState() == VerifyPreBarriers; }
+
+ bool isAtomsZone() const { return kind_ == AtomsZone; }
+ bool isSystemZone() const { return kind_ == SystemZone; }
+
+ static shadow::Zone* from(JS::Zone* zone) {
+ return reinterpret_cast<shadow::Zone*>(zone);
+ }
+ static const shadow::Zone* from(const JS::Zone* zone) {
+ return reinterpret_cast<const shadow::Zone*>(zone);
+ }
+};
+
+} // namespace shadow
+
+} // namespace JS
+
+#endif // js_shadow_Zone_h