gecko-dev/js/public/Promise.h

353 строки
14 KiB
C++

/* -*- 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 "jspubtd.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
namespace JS {
typedef JSObject* (*GetIncumbentGlobalCallback)(JSContext* cx);
typedef bool (*EnqueuePromiseJobCallback)(JSContext* cx,
JS::HandleObject promise,
JS::HandleObject job,
JS::HandleObject allocationSite,
JS::HandleObject incumbentGlobal,
void* data);
enum class PromiseRejectionHandlingState { Unhandled, Handled };
typedef void (*PromiseRejectionTrackerCallback)(
JSContext* cx, JS::HandleObject promise,
JS::PromiseRejectionHandlingState state, void* data);
/**
* Sets the callback that's invoked whenever an incumbent global is required.
*
* 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/base/ScriptSettings.h for details.
*/
extern JS_PUBLIC_API void SetGetIncumbentGlobalCallback(
JSContext* cx, GetIncumbentGlobalCallback callback);
/**
* Sets the callback that's invoked whenever a Promise job should be enqeued.
*
* SpiderMonkey doesn't schedule Promise resolution jobs itself; instead,
* using this function the embedding can provide a callback to do that
* scheduling. The provided `callback` is invoked with the promise job,
* the corresponding Promise's allocation stack, and the `data` pointer
* passed here as arguments.
*/
extern JS_PUBLIC_API void SetEnqueuePromiseJobCallback(
JSContext* cx, EnqueuePromiseJobCallback callback, void* data = nullptr);
/**
* 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.
*
* If a `proto` is passed, that gets set as the instance's [[Prototype]]
* instead of the original value of `Promise.prototype`.
*/
extern JS_PUBLIC_API JSObject* NewPromiseObject(
JSContext* cx, JS::HandleObject executor, JS::HandleObject proto = nullptr);
/**
* 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);
/**
* 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);
/**
* Calls the current compartment's original Promise.prototype.then on the
* given `promise`, with `onResolve` and `onReject` passed as arguments.
*
* Throws a TypeError if `promise` isn't a Promise (or possibly a different
* error if it's a security wrapper or dead object proxy).
*
* Asserts that `onFulfilled` and `onRejected` are each either callable or
* null.
*/
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 `onResolve`, `onReject` callables to enqueue
* as reactions for that promise. In difference 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).
*
* Asserts that `onFulfilled` and `onRejected` are each either callable or
* null.
*/
extern JS_PUBLIC_API bool AddPromiseReactions(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 an AutoObjectVector 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, const JS::AutoObjectVector& 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