summaryrefslogtreecommitdiffstats
path: root/js/public/experimental
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/public/experimental
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-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
9 files changed, 1937 insertions, 0 deletions
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