/* -*- 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 builtin_Promise_h
#define builtin_Promise_h

#include "js/Promise.h"

#include "mozilla/Attributes.h"  // MOZ_MUST_USE

#include "jsapi.h"    // js::CompletionKind
#include "jstypes.h"  // JS_PUBLIC_API

#include "js/CallArgs.h"    // JS::CallArgs
#include "js/RootingAPI.h"  // JS::{,Mutable}Handle
#include "js/TypeDecls.h"   // JS::HandleObjectVector
#include "js/Value.h"       // JS::Value

struct JS_PUBLIC_API JSContext;
class JS_PUBLIC_API JSObject;

namespace js {

class AsyncFunctionGeneratorObject;
class AsyncGeneratorObject;
class PromiseObject;

// Promise.prototype.then.
extern bool Promise_then(JSContext* cx, unsigned argc, JS::Value* vp);

// Promise[Symbol.species].
extern bool Promise_static_species(JSContext* cx, unsigned argc, JS::Value* vp);

// Promise.resolve.
extern bool Promise_static_resolve(JSContext* cx, unsigned argc, JS::Value* vp);

/**
 * Unforgeable version of the JS builtin Promise.all.
 *
 * Takes a HandleValueVector 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`.
 */
MOZ_MUST_USE JSObject* GetWaitForAllPromise(JSContext* cx,
                                            JS::HandleObjectVector promises);

/**
 * Enqueues resolve/reject reactions in the given Promise's reactions lists
 * as though by calling the original value of Promise.prototype.then, and
 * without regard to any Promise subclassing used in `promiseObj` itself.
 */
MOZ_MUST_USE PromiseObject* OriginalPromiseThen(
    JSContext* cx, JS::Handle<JSObject*> promiseObj,
    JS::Handle<JSObject*> onFulfilled, JS::Handle<JSObject*> onRejected);

enum class UnhandledRejectionBehavior { Ignore, Report };

/**
 * React to[0] `unwrappedPromise` (which may not be from the current realm) as
 * if by using a fresh promise created for the provided nullable fulfill/reject
 * IsCallable objects.
 *
 * However, no dependent Promise will be created, and mucking with `Promise`,
 * `Promise.prototype.then`, and `Promise[Symbol.species]` will not alter this
 * function's behavior.
 *
 * If `unwrappedPromise` rejects and `onRejected_` is null, handling is
 * determined by `behavior`.  If `behavior == Report`, a fresh Promise will be
 * constructed and rejected on the fly (and thus will be reported as unhandled).
 * But if `behavior == Ignore`, the rejection is ignored and is not reported as
 * unhandled.
 *
 * Note: Reactions pushed using this function contain a null `promise` field.
 * That field is only ever used by devtools, which have to treat these reactions
 * specially.
 *
 * 0. The sense of "react" here is the sense of the term as defined by Web IDL:
 *    https://heycam.github.io/webidl/#dfn-perform-steps-once-promise-is-settled
 */
extern MOZ_MUST_USE bool ReactToUnwrappedPromise(
    JSContext* cx, JS::Handle<PromiseObject*> unwrappedPromise,
    JS::Handle<JSObject*> onFulfilled_, JS::Handle<JSObject*> onRejected_,
    UnhandledRejectionBehavior behavior);

/**
 * PromiseResolve ( C, x )
 *
 * The abstract operation PromiseResolve, given a constructor and a value,
 * returns a new promise resolved with that value.
 */
MOZ_MUST_USE JSObject* PromiseResolve(JSContext* cx,
                                      JS::Handle<JSObject*> constructor,
                                      JS::Handle<JS::Value> value);

/**
 * Reject |promise| with the value of the current pending exception.
 *
 * |promise| must be from the current realm.  Callers must enter the realm of
 * |promise| if they are not already in it.
 */
MOZ_MUST_USE bool RejectPromiseWithPendingError(
    JSContext* cx, JS::Handle<PromiseObject*> promise);

/**
 * Create the promise object which will be used as the return value of an async
 * function.
 */
MOZ_MUST_USE PromiseObject* CreatePromiseObjectForAsync(JSContext* cx);

/**
 * Returns true if the given object is a promise created by
 * either CreatePromiseObjectForAsync function or async generator's method.
 */
MOZ_MUST_USE bool IsPromiseForAsyncFunctionOrGenerator(JSObject* promise);

MOZ_MUST_USE bool AsyncFunctionReturned(
    JSContext* cx, JS::Handle<PromiseObject*> resultPromise,
    JS::Handle<JS::Value> value);

MOZ_MUST_USE bool AsyncFunctionThrown(JSContext* cx,
                                      JS::Handle<PromiseObject*> resultPromise,
                                      JS::Handle<JS::Value> reason);

// Start awaiting `value` in an async function (, but doesn't suspend the
// async function's execution!). Returns the async function's result promise.
MOZ_MUST_USE JSObject* AsyncFunctionAwait(
    JSContext* cx, JS::Handle<AsyncFunctionGeneratorObject*> genObj,
    JS::Handle<JS::Value> value);

// If the await operation can be skipped and the resolution value for `val` can
// be acquired, stored the resolved value to `resolved` and `true` to
// `*canSkip`.  Otherwise, stores `false` to `*canSkip`.
MOZ_MUST_USE bool TrySkipAwait(JSContext* cx, JS::Handle<JS::Value> val,
                               bool* canSkip,
                               JS::MutableHandle<JS::Value> resolved);

MOZ_MUST_USE bool AsyncGeneratorAwait(
    JSContext* cx, JS::Handle<AsyncGeneratorObject*> asyncGenObj,
    JS::Handle<JS::Value> value);

MOZ_MUST_USE bool AsyncGeneratorResolve(
    JSContext* cx, JS::Handle<AsyncGeneratorObject*> asyncGenObj,
    JS::Handle<JS::Value> value, bool done);

MOZ_MUST_USE bool AsyncGeneratorReject(
    JSContext* cx, JS::Handle<AsyncGeneratorObject*> asyncGenObj,
    JS::Handle<JS::Value> exception);

MOZ_MUST_USE bool AsyncGeneratorEnqueue(JSContext* cx,
                                        JS::Handle<JS::Value> asyncGenVal,
                                        CompletionKind completionKind,
                                        JS::Handle<JS::Value> completionValue,
                                        JS::MutableHandle<JS::Value> result);

bool AsyncFromSyncIteratorMethod(JSContext* cx, JS::CallArgs& args,
                                 CompletionKind completionKind);

// Callback for describing promise reaction records, for use with
// PromiseObject::getReactionRecords.
struct PromiseReactionRecordBuilder {
  // A reaction record created by a call to 'then' or 'catch', with functions to
  // call on resolution or rejection, and the promise that will be settled
  // according to the result of calling them.
  //
  // Note that resolve, reject, and result may not be same-compartment with cx,
  // or with the promise we're inspecting. This function presents the values
  // exactly as they appear in the reaction record. They may also be wrapped or
  // unwrapped.
  //
  // Some reaction records refer to internal resolution or rejection functions
  // that are not naturally represented as debuggee JavaScript functions. In
  // this case, resolve and reject may be nullptr.
  virtual MOZ_MUST_USE bool then(JSContext* cx, JS::Handle<JSObject*> resolve,
                                 JS::Handle<JSObject*> reject,
                                 JS::Handle<JSObject*> result) = 0;

  // A reaction record created when one native promise is resolved to another.
  // The 'promise' argument is the promise that will be settled in the same way
  // the promise this reaction record is attached to is settled.
  //
  // Note that promise may not be same-compartment with cx. This function
  // presents the promise exactly as it appears in the reaction record.
  virtual MOZ_MUST_USE bool direct(
      JSContext* cx, JS::Handle<PromiseObject*> unwrappedPromise) = 0;

  // A reaction record that resumes an asynchronous function suspended at an
  // await expression. The 'generator' argument is the generator object
  // representing the call.
  //
  // Note that generator may not be same-compartment with cx. This function
  // presents the generator exactly as it appears in the reaction record.
  virtual MOZ_MUST_USE bool asyncFunction(
      JSContext* cx,
      JS::Handle<AsyncFunctionGeneratorObject*> unwrappedGenerator) = 0;

  // A reaction record that resumes an asynchronous generator suspended at an
  // await expression. The 'generator' argument is the generator object
  // representing the call.
  //
  // Note that generator may not be same-compartment with cx. This function
  // presents the generator exactly as it appears in the reaction record.
  virtual MOZ_MUST_USE bool asyncGenerator(
      JSContext* cx, JS::Handle<AsyncGeneratorObject*> unwrappedGenerator) = 0;
};

}  // namespace js

#endif  // builtin_Promise_h
