blob: 34e48e10b3d71dee9b71d3ca51eec90a16eaf40a [file] [log] [blame]
/*
* Copyright (C) Alexander Borisov
* Copyright (C) Nginx, Inc.
*/
#include <njs_main.h>
typedef enum {
NJS_PROMISE_PENDING = 0,
NJS_PROMISE_FULFILL,
NJS_PROMISE_REJECTED
} njs_promise_type_t;
typedef enum {
NJS_PROMISE_HANDLE = 0,
NJS_PROMISE_REJECT
} njs_promise_rejection_type_t;
typedef enum {
NJS_PROMISE_ALL = 0,
NJS_PROMISE_ALL_SETTLED,
NJS_PROMISE_ANY
} njs_promise_function_type_t;
typedef struct {
njs_promise_type_t state;
njs_value_t result;
njs_queue_t fulfill_queue;
njs_queue_t reject_queue;
njs_bool_t is_handled;
} njs_promise_data_t;
typedef struct {
njs_value_t promise;
njs_value_t resolve;
njs_value_t reject;
} njs_promise_capability_t;
typedef struct {
njs_promise_capability_t *capability;
njs_promise_type_t type;
njs_queue_link_t link;
njs_value_t handler;
} njs_promise_reaction_t;
typedef struct {
njs_value_t promise;
njs_value_t finally;
njs_value_t constructor;
njs_bool_t resolved;
njs_bool_t *resolved_ref;
njs_promise_capability_t *capability;
njs_function_native_t handler;
} njs_promise_context_t;
typedef struct {
njs_bool_t already_called;
uint32_t index;
uint32_t *remaining_elements;
njs_array_t *values;
njs_promise_capability_t *capability;
} njs_promise_all_context_t;
typedef struct {
njs_iterator_args_t args;
uint32_t *remaining;
njs_value_t *constructor;
njs_function_t *function;
njs_promise_capability_t *capability;
} njs_promise_iterator_args_t;
static njs_promise_t *njs_promise_constructor_call(njs_vm_t *vm,
njs_function_t *function);
static njs_int_t njs_promise_create_resolving_functions(njs_vm_t *vm,
njs_promise_t *promise, njs_value_t *dst);
static njs_int_t njs_promise_value_constructor(njs_vm_t *vm, njs_value_t *value,
njs_value_t *dst);
static njs_int_t njs_promise_capability_executor(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t retval);
static njs_int_t njs_promise_host_rejection_tracker(njs_vm_t *vm,
njs_promise_t *promise, njs_promise_rejection_type_t operation);
static njs_int_t njs_promise_resolve_function(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t retval);
static njs_promise_t *njs_promise_resolve(njs_vm_t *vm,
njs_value_t *constructor, njs_value_t *x);
static njs_int_t njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t retval);
static njs_int_t njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value,
njs_value_t *fulfilled, njs_value_t *rejected,
njs_promise_capability_t *capability);
static njs_int_t njs_promise_then_finally_function(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_promise_then_finally_return(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_promise_catch_finally_return(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_promise_reaction_job(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_promise_resolve_thenable_job(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_promise_perform_all(njs_vm_t *vm, njs_value_t *iterator,
njs_promise_iterator_args_t *pargs, njs_iterator_handler_t handler,
njs_value_t *retval);
static njs_int_t njs_promise_perform_all_handler(njs_vm_t *vm,
njs_iterator_args_t *args, njs_value_t *value, int64_t index);
static njs_int_t njs_promise_all_resolve_element_functions(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_promise_perform_all_settled_handler(njs_vm_t *vm,
njs_iterator_args_t *args, njs_value_t *value, int64_t index);
static njs_int_t njs_promise_all_settled_element_functions(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t rejected);
static njs_int_t njs_promise_perform_any_handler(njs_vm_t *vm,
njs_iterator_args_t *args, njs_value_t *value, int64_t index);
static njs_int_t njs_promise_any_reject_element_functions(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_promise_perform_race_handler(njs_vm_t *vm,
njs_iterator_args_t *args, njs_value_t *value, int64_t index);
static const njs_value_t string_resolve = njs_string("resolve");
static const njs_value_t string_any_rejected =
njs_long_string("All promises were rejected");
static njs_promise_t *
njs_promise_alloc(njs_vm_t *vm)
{
njs_promise_t *promise;
njs_promise_data_t *data;
promise = njs_mp_alloc(vm->mem_pool, sizeof(njs_promise_t)
+ sizeof(njs_promise_data_t));
if (njs_slow_path(promise == NULL)) {
goto memory_error;
}
njs_lvlhsh_init(&promise->object.hash);
njs_lvlhsh_init(&promise->object.shared_hash);
promise->object.type = NJS_PROMISE;
promise->object.shared = 0;
promise->object.extensible = 1;
promise->object.error_data = 0;
promise->object.fast_array = 0;
promise->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_PROMISE].object;
promise->object.slots = NULL;
data = (njs_promise_data_t *) ((uint8_t *) promise + sizeof(njs_promise_t));
data->state = NJS_PROMISE_PENDING;
data->is_handled = 0;
njs_queue_init(&data->fulfill_queue);
njs_queue_init(&data->reject_queue);
njs_set_data(&promise->value, data, 0);
return promise;
memory_error:
njs_memory_error(vm);
return NULL;
}
njs_int_t
njs_promise_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_promise_t *promise;
njs_function_t *function;
if (njs_slow_path(!vm->top_frame->ctor)) {
njs_type_error(vm, "the Promise constructor must be called with new");
return NJS_ERROR;
}
if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) {
njs_type_error(vm, "unexpected arguments");
return NJS_ERROR;
}
function = njs_function(njs_argument(args, 1));
promise = njs_promise_constructor_call(vm, function);
if (njs_slow_path(promise == NULL)) {
return NJS_ERROR;
}
njs_set_promise(&vm->retval, promise);
return NJS_OK;
}
njs_int_t
njs_vm_promise_create(njs_vm_t *vm, njs_value_t *retval, njs_value_t *callbacks)
{
njs_int_t ret;
njs_promise_t *promise;
promise = njs_promise_alloc(vm);
if (njs_slow_path(promise == NULL)) {
return NJS_ERROR;
}
ret = njs_promise_create_resolving_functions(vm, promise, callbacks);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
njs_set_promise(retval, promise);
return NJS_OK;
}
static njs_promise_t *
njs_promise_constructor_call(njs_vm_t *vm, njs_function_t *function)
{
njs_int_t ret;
njs_value_t retval, arguments[2];
njs_promise_t *promise;
promise = njs_promise_alloc(vm);
if (njs_slow_path(promise == NULL)) {
return NULL;
}
ret = njs_promise_create_resolving_functions(vm, promise, arguments);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
ret = njs_function_call(vm, function, &njs_value_undefined, arguments, 2,
&retval);
if (njs_slow_path(ret != NJS_OK)) {
if (njs_slow_path(njs_is_memory_error(vm, &vm->retval))) {
return NULL;
}
ret = njs_function_call(vm, njs_function(&arguments[1]),
&njs_value_undefined, &vm->retval, 1, &retval);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
}
return promise;
}
static njs_function_t *
njs_promise_create_function(njs_vm_t *vm, size_t context_size)
{
njs_function_t *function;
njs_promise_context_t *context;
function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t));
if (njs_slow_path(function == NULL)) {
goto memory_error;
}
if (context_size > 0) {
context = njs_mp_zalloc(vm->mem_pool, context_size);
if (njs_slow_path(context == NULL)) {
njs_mp_free(vm->mem_pool, function);
goto memory_error;
}
} else {
context = NULL;
}
function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
function->object.shared_hash = vm->shared->arrow_instance_hash;
function->object.type = NJS_FUNCTION;
function->object.extensible = 1;
function->args_offset = 1;
function->native = 1;
function->context = context;
return function;
memory_error:
njs_memory_error(vm);
return NULL;
}
static njs_int_t
njs_promise_create_resolving_functions(njs_vm_t *vm, njs_promise_t *promise,
njs_value_t *dst)
{
unsigned i;
njs_function_t *function;
njs_promise_context_t *context, *resolve_context;
i = 0;
/* Some compilers give at error an uninitialized context if using for. */
do {
function = njs_promise_create_function(vm,
sizeof(njs_promise_context_t));
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
function->args_count = 1;
context = function->context;
context->resolved_ref = &context->resolved;
njs_set_promise(&context->promise, promise);
njs_set_function(&dst[i], function);
} while (++i < 2);
njs_function(&dst[0])->u.native = njs_promise_resolve_function;
njs_function(&dst[1])->u.native = njs_promise_reject_function;
resolve_context = njs_function(&dst[0])->context;
resolve_context->resolved_ref = &context->resolved;
return NJS_OK;
}
static njs_promise_capability_t *
njs_promise_new_capability(njs_vm_t *vm, njs_value_t *constructor)
{
njs_int_t ret;
njs_value_t argument, this;
njs_object_t *object;
njs_function_t *function;
njs_promise_context_t *context;
njs_promise_capability_t *capability;
object = NULL;
function = NULL;
ret = njs_promise_value_constructor(vm, constructor, constructor);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
capability = njs_mp_zalloc(vm->mem_pool, sizeof(njs_promise_capability_t));
if (njs_slow_path(capability == NULL)) {
njs_memory_error(vm);
return NULL;
}
function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
if (njs_slow_path(function == NULL)) {
return NULL;
}
njs_set_undefined(&capability->resolve);
njs_set_undefined(&capability->reject);
function->u.native = njs_promise_capability_executor;
function->args_count = 2;
context = function->context;
context->capability = capability;
njs_set_function(&argument, function);
object = njs_function_new_object(vm, constructor);
if (njs_slow_path(object == NULL)) {
return NULL;
}
njs_set_object(&this, object);
ret = njs_function_call2(vm, njs_function(constructor), &this,
&argument, 1, &capability->promise, 1);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
if (njs_slow_path(!njs_is_function(&capability->resolve))) {
njs_type_error(vm, "capability resolve slot is not callable");
return NULL;
}
if (njs_slow_path(!njs_is_function(&capability->reject))) {
njs_type_error(vm, "capability reject slot is not callable");
return NULL;
}
return capability;
}
static njs_int_t
njs_promise_value_constructor(njs_vm_t *vm, njs_value_t *value,
njs_value_t *dst)
{
njs_int_t ret;
static const njs_value_t string_constructor = njs_string("constructor");
if (njs_is_function(value)) {
*dst = *value;
return NJS_OK;
}
ret = njs_value_property(vm, value, njs_value_arg(&string_constructor),
dst);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
if (!njs_is_function(dst)) {
njs_type_error(vm, "the object does not contain a constructor");
return NJS_ERROR;
}
return NJS_OK;
}
static njs_int_t
njs_promise_capability_executor(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
njs_promise_context_t *context;
njs_promise_capability_t *capability;
context = vm->top_frame->function->context;
capability = context->capability;
if (njs_slow_path(capability == NULL)) {
njs_type_error(vm, "failed to get function capability");
return NJS_ERROR;
}
if (!njs_is_undefined(&capability->resolve)) {
njs_type_error(vm, "capability resolve slot is not undefined");
return NJS_ERROR;
}
if (!njs_is_undefined(&capability->reject)) {
njs_type_error(vm, "capability reject slot is not undefined");
return NJS_ERROR;
}
capability->resolve = *njs_arg(args, nargs, 1);
capability->reject = *njs_arg(args, nargs, 2);
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
njs_inline njs_int_t
njs_promise_add_event(njs_vm_t *vm, njs_function_t *function, njs_value_t *args,
njs_uint_t nargs)
{
njs_event_t *event;
event = njs_mp_zalloc(vm->mem_pool, sizeof(njs_event_t));
if (njs_slow_path(event == NULL)) {
njs_memory_error(vm);
return NJS_ERROR;
}
event->function = function;
event->once = 1;
if (nargs != 0) {
event->args = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs);
if (njs_slow_path(event->args == NULL)) {
njs_memory_error(vm);
return NJS_ERROR;
}
memcpy(event->args, args, sizeof(njs_value_t) * nargs);
event->nargs = nargs;
}
njs_queue_insert_tail(&vm->promise_events, &event->link);
return NJS_OK;
}
njs_inline njs_value_t *
njs_promise_trigger_reactions(njs_vm_t *vm, njs_value_t *value,
njs_queue_t *queue)
{
njs_int_t ret;
njs_value_t arguments[2];
njs_function_t *function;
njs_queue_link_t *link;
njs_promise_reaction_t *reaction;
for (link = njs_queue_first(queue);
link != njs_queue_tail(queue);
link = njs_queue_next(link))
{
reaction = njs_queue_link_data(link, njs_promise_reaction_t, link);
function = njs_promise_create_function(vm,
sizeof(njs_promise_context_t));
function->u.native = njs_promise_reaction_job;
njs_set_data(&arguments[0], reaction, 0);
arguments[1] = *value;
ret = njs_promise_add_event(vm, function, arguments, 2);
if (njs_slow_path(ret != NJS_OK)) {
return njs_value_arg(&njs_value_null);
}
}
return njs_value_arg(&njs_value_undefined);
}
njs_inline njs_value_t *
njs_promise_fulfill(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *value)
{
njs_queue_t queue;
njs_promise_data_t *data;
data = njs_data(&promise->value);
data->result = *value;
data->state = NJS_PROMISE_FULFILL;
if (njs_queue_is_empty(&data->fulfill_queue)) {
return njs_value_arg(&njs_value_undefined);
} else {
queue = data->fulfill_queue;
queue.head.prev->next = &queue.head;
queue.head.next->prev = &queue.head;
}
njs_queue_init(&data->fulfill_queue);
njs_queue_init(&data->reject_queue);
return njs_promise_trigger_reactions(vm, value, &queue);
}
njs_inline njs_value_t *
njs_promise_reject(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *reason)
{
njs_int_t ret;
njs_queue_t queue;
njs_promise_data_t *data;
data = njs_data(&promise->value);
data->result = *reason;
data->state = NJS_PROMISE_REJECTED;
if (!data->is_handled) {
ret = njs_promise_host_rejection_tracker(vm, promise,
NJS_PROMISE_REJECT);
if (njs_slow_path(ret != NJS_OK)) {
return njs_value_arg(&njs_value_null);
}
}
if (njs_queue_is_empty(&data->reject_queue)) {
return njs_value_arg(&njs_value_undefined);
} else {
queue = data->reject_queue;
queue.head.prev->next = &queue.head;
queue.head.next->prev = &queue.head;
}
njs_queue_init(&data->fulfill_queue);
njs_queue_init(&data->reject_queue);
return njs_promise_trigger_reactions(vm, reason, &queue);
}
static njs_int_t
njs_promise_host_rejection_tracker(njs_vm_t *vm, njs_promise_t *promise,
njs_promise_rejection_type_t operation)
{
uint32_t i, length;
njs_value_t *value;
njs_promise_data_t *data;
if (vm->options.unhandled_rejection
== NJS_VM_OPT_UNHANDLED_REJECTION_IGNORE)
{
return NJS_OK;
}
if (vm->promise_reason == NULL) {
vm->promise_reason = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
if (njs_slow_path(vm->promise_reason == NULL)) {
return NJS_ERROR;
}
}
data = njs_data(&promise->value);
if (operation == NJS_PROMISE_REJECT) {
if (vm->promise_reason != NULL) {
return njs_array_add(vm, vm->promise_reason, &data->result);
}
} else {
value = vm->promise_reason->start;
length = vm->promise_reason->length;
for (i = 0; i < length; i++) {
if (njs_values_same(&value[i], &data->result)) {
length--;
if (i < length) {
memmove(&value[i], &value[i + 1],
sizeof(njs_value_t) * (length - i));
}
break;
}
}
vm->promise_reason->length = length;
}
return NJS_OK;
}
static njs_int_t
njs_promise_invoke_then(njs_vm_t *vm, njs_value_t *promise, njs_value_t *args,
njs_int_t nargs)
{
njs_int_t ret;
njs_value_t function;
static const njs_value_t string_then = njs_string("then");
ret = njs_value_property(vm, promise, njs_value_arg(&string_then),
&function);
if (njs_slow_path(ret != NJS_OK)) {
if (ret == NJS_DECLINED) {
goto failed;
}
return NJS_ERROR;
}
if (njs_fast_path(njs_is_function(&function))) {
return njs_function_call(vm, njs_function(&function), promise, args,
nargs, &vm->retval);
}
failed:
njs_type_error(vm, "is not a function");
return NJS_ERROR;
}
static njs_int_t
njs_promise_resolve_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_int_t ret;
njs_value_t *resolution, error, then, arguments[3];
njs_promise_t *promise;
njs_function_t *function;
njs_native_frame_t *active_frame;
njs_promise_context_t *context;
static const njs_value_t string_then = njs_string("then");
active_frame = vm->top_frame;
context = active_frame->function->context;
promise = njs_promise(&context->promise);
if (*context->resolved_ref) {
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
*context->resolved_ref = 1;
resolution = njs_arg(args, nargs, 1);
if (njs_values_same(resolution, &context->promise)) {
njs_error_fmt_new(vm, &error, NJS_OBJ_TYPE_TYPE_ERROR,
"promise self resolution");
if (njs_slow_path(!njs_is_error(&error))) {
return NJS_ERROR;
}
njs_vm_retval_set(vm, njs_promise_reject(vm, promise, &error));
return NJS_OK;
}
if (!njs_is_object(resolution)) {
goto fulfill;
}
ret = njs_value_property(vm, resolution, njs_value_arg(&string_then),
&then);
if (njs_slow_path(ret == NJS_ERROR)) {
if (njs_slow_path(njs_is_memory_error(vm, &vm->retval))) {
return NJS_ERROR;
}
njs_vm_retval_set(vm, njs_promise_reject(vm, promise, &vm->retval));
if (njs_slow_path(njs_vm_retval(vm)->type == NJS_NULL)) {
return NJS_ERROR;
}
return NJS_OK;
}
if (!njs_is_function(&then)) {
goto fulfill;
}
arguments[0] = context->promise;
arguments[1] = *resolution;
arguments[2] = then;
function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
function->u.native = njs_promise_resolve_thenable_job;
ret = njs_promise_add_event(vm, function, arguments, 3);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
fulfill:
njs_vm_retval_set(vm, njs_promise_fulfill(vm, promise, resolution));
if (njs_slow_path(njs_vm_retval(vm)->type == NJS_NULL)) {
return NJS_ERROR;
}
return NJS_OK;
}
static njs_int_t
njs_promise_object_resolve(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_promise_t *promise;
if (njs_slow_path(!njs_is_object(njs_arg(args, nargs, 0)))) {
njs_type_error(vm, "this value is not an object");
return NJS_ERROR;
}
promise = njs_promise_resolve(vm, njs_argument(args, 0),
njs_arg(args, nargs, 1));
if (njs_slow_path(promise == NULL)) {
return NJS_ERROR;
}
njs_set_promise(&vm->retval, promise);
return NJS_OK;
}
static njs_promise_t *
njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x)
{
njs_int_t ret;
njs_value_t value;
njs_object_t *object;
njs_promise_capability_t *capability;
static const njs_value_t string_constructor = njs_string("constructor");
if (njs_is_object(x)) {
object = njs_object_proto_lookup(njs_object(x), NJS_PROMISE,
njs_object_t);
if (object != NULL) {
ret = njs_value_property(vm, x, njs_value_arg(&string_constructor),
&value);
if (njs_slow_path(ret == NJS_ERROR)) {
return NULL;
}
if (njs_values_same(&value, constructor)) {
return njs_promise(x);
}
}
}
capability = njs_promise_new_capability(vm, constructor);
if (njs_slow_path(capability == NULL)) {
return NULL;
}
ret = njs_function_call(vm, njs_function(&capability->resolve),
&njs_value_undefined, x, 1, &value);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
return njs_promise(&capability->promise);
}
static njs_int_t
njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_value_t *value;
njs_native_frame_t *active_frame;
njs_promise_context_t *context;
active_frame = vm->top_frame;
context = active_frame->function->context;
if (*context->resolved_ref) {
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
*context->resolved_ref = 1;
value = njs_promise_reject(vm, njs_promise(&context->promise),
njs_arg(args, nargs, 1));
if (njs_slow_path(value->type == NJS_NULL)) {
return NJS_ERROR;
}
njs_vm_retval_set(vm, value);
return NJS_OK;
}
static njs_int_t
njs_promise_object_reject(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_int_t ret;
njs_value_t value;
njs_promise_capability_t *capability;
if (njs_slow_path(!njs_is_object(njs_arg(args, nargs, 0)))) {
njs_type_error(vm, "this value is not an object");
return NJS_ERROR;
}
capability = njs_promise_new_capability(vm, njs_argument(args, 0));
if (njs_slow_path(capability == NULL)) {
return NJS_ERROR;
}
ret = njs_function_call(vm, njs_function(&capability->reject),
&njs_value_undefined, njs_arg(args, nargs, 1),
1, &value);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
njs_vm_retval_set(vm, &capability->promise);
return NJS_OK;
}
static njs_int_t
njs_promise_prototype_then(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_int_t ret;
njs_value_t *promise, *fulfilled, *rejected, constructor;
njs_object_t *object;
njs_function_t *function;
njs_promise_capability_t *capability;
promise = njs_arg(args, nargs, 0);
if (njs_slow_path(!njs_is_object(promise))) {
goto failed;
}
object = njs_object_proto_lookup(njs_object(promise), NJS_PROMISE,
njs_object_t);
if (njs_slow_path(object == NULL)) {
goto failed;
}
function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
function->u.native = njs_promise_constructor;
njs_set_function(&constructor, function);
ret = njs_value_species_constructor(vm, promise, &constructor,
&constructor);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
capability = njs_promise_new_capability(vm, &constructor);
if (njs_slow_path(capability == NULL)) {
return NJS_ERROR;
}
fulfilled = njs_arg(args, nargs, 1);
rejected = njs_arg(args, nargs, 2);
return njs_promise_perform_then(vm, promise, fulfilled, rejected,
capability);
failed:
njs_type_error(vm, "required a promise object");
return NJS_ERROR;
}
static njs_int_t
njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value,
njs_value_t *fulfilled, njs_value_t *rejected,
njs_promise_capability_t *capability)
{
njs_int_t ret;
njs_value_t arguments[2];
njs_promise_t *promise;
njs_function_t *function;
njs_promise_data_t *data;
njs_promise_reaction_t *fulfilled_reaction, *rejected_reaction;
if (!njs_is_function(fulfilled)) {
fulfilled = njs_value_arg(&njs_value_undefined);
}
if (!njs_is_function(rejected)) {
rejected = njs_value_arg(&njs_value_undefined);
}
promise = njs_promise(value);
data = njs_data(&promise->value);
fulfilled_reaction = njs_mp_alloc(vm->mem_pool,
sizeof(njs_promise_reaction_t));
if (njs_slow_path(fulfilled_reaction == NULL)) {
njs_memory_error(vm);
return NJS_ERROR;
}
fulfilled_reaction->capability = capability;
fulfilled_reaction->handler = *fulfilled;
fulfilled_reaction->type = NJS_PROMISE_FULFILL;
rejected_reaction = njs_mp_alloc(vm->mem_pool,
sizeof(njs_promise_reaction_t));
if (njs_slow_path(rejected_reaction == NULL)) {
njs_memory_error(vm);
return NJS_ERROR;
}
rejected_reaction->capability = capability;
rejected_reaction->handler = *rejected;
rejected_reaction->type = NJS_PROMISE_REJECTED;
if (data->state == NJS_PROMISE_PENDING) {
njs_queue_insert_tail(&data->fulfill_queue, &fulfilled_reaction->link);
njs_queue_insert_tail(&data->reject_queue, &rejected_reaction->link);
} else {
function = njs_promise_create_function(vm,
sizeof(njs_promise_context_t));
function->u.native = njs_promise_reaction_job;
if (data->state == NJS_PROMISE_REJECTED) {
njs_set_data(&arguments[0], rejected_reaction, 0);
ret = njs_promise_host_rejection_tracker(vm, promise,
NJS_PROMISE_HANDLE);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
} else {
njs_set_data(&arguments[0], fulfilled_reaction, 0);
}
arguments[1] = data->result;
ret = njs_promise_add_event(vm, function, arguments, 2);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
}
data->is_handled = 1;
if (capability == NULL) {
njs_vm_retval_set(vm, &njs_value_undefined);
} else {
njs_vm_retval_set(vm, &capability->promise);
}
return NJS_OK;
}
static njs_int_t
njs_promise_prototype_catch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_value_t arguments[2];
arguments[0] = njs_value_undefined;
arguments[1] = *njs_arg(args, nargs, 1);
return njs_promise_invoke_then(vm, njs_arg(args, nargs, 0), arguments, 2);
}
static njs_int_t
njs_promise_prototype_finally(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_int_t ret;
njs_value_t *promise, *finally, constructor, arguments[2];
njs_function_t *function;
njs_promise_context_t *context;
promise = njs_arg(args, nargs, 0);
if (njs_slow_path(!njs_is_object(promise))) {
njs_type_error(vm, "required a object");
return NJS_ERROR;
}
finally = njs_arg(args, nargs, 1);
function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
function->u.native = njs_promise_constructor;
njs_set_function(&constructor, function);
ret = njs_value_species_constructor(vm, promise, &constructor,
&constructor);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
if (!njs_is_function(finally)) {
arguments[0] = *finally;
arguments[1] = *finally;
return njs_promise_invoke_then(vm, promise, arguments, 2);
}
function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
function->u.native = njs_promise_then_finally_function;
function->args_count = 1;
context = function->context;
context->constructor = constructor;
context->finally = *finally;
context->handler = njs_promise_then_finally_return;
njs_set_function(&arguments[0], function);
function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
if (njs_slow_path(function == NULL)) {
njs_mp_free(vm->mem_pool, njs_function(&arguments[0]));
return NJS_ERROR;
}
function->u.native = njs_promise_then_finally_function;
function->args_count = 1;
context = function->context;
context->constructor = constructor;
context->finally = *finally;
context->handler = njs_promise_catch_finally_return;
njs_set_function(&arguments[1], function);
return njs_promise_invoke_then(vm, promise, arguments, 2);
}
static njs_int_t
njs_promise_then_finally_function(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
njs_int_t ret;
njs_value_t value, retval, argument;
njs_promise_t *promise;
njs_function_t *function;
njs_native_frame_t *frame;
njs_promise_context_t *context;
frame = vm->top_frame;
context = frame->function->context;
ret = njs_function_call(vm, njs_function(&context->finally),
&njs_value_undefined, args, 0, &retval);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
promise = njs_promise_resolve(vm, &context->constructor, &retval);
if (njs_slow_path(promise == NULL)) {
return NJS_ERROR;
}
njs_set_promise(&value, promise);
function = njs_promise_create_function(vm, sizeof(njs_value_t));
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
function->u.native = context->handler;
*((njs_value_t *) function->context) = *njs_arg(args, nargs, 1);
njs_set_function(&argument, function);
return njs_promise_invoke_then(vm, &value, &argument, 1);
}
static njs_int_t
njs_promise_then_finally_return(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
njs_vm_retval_set(vm, vm->top_frame->function->context);
return NJS_OK;
}
static njs_int_t
njs_promise_catch_finally_return(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
njs_vm_retval_set(vm, vm->top_frame->function->context);
return NJS_ERROR;
}
static njs_int_t
njs_promise_reaction_job(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_int_t ret;
njs_bool_t is_error;
njs_value_t *value, *argument, retval;
njs_promise_reaction_t *reaction;
njs_promise_capability_t *capability;
value = njs_arg(args, nargs, 1);
argument = njs_arg(args, nargs, 2);
reaction = njs_data(value);
capability = reaction->capability;
is_error = 0;
if (njs_is_undefined(&reaction->handler)) {
if (reaction->type == NJS_PROMISE_REJECTED) {
is_error = 1;
}
retval = *argument;
} else {
ret = njs_function_call(vm, njs_function(&reaction->handler),
&njs_value_undefined, argument, 1, &retval);
if (njs_slow_path(ret != NJS_OK)) {
if (njs_slow_path(njs_is_memory_error(vm, &vm->retval))) {
return NJS_ERROR;
}
retval = vm->retval;
is_error = 1;
}
}
if (capability == NULL) {
njs_vm_retval_set(vm, &retval);
return NJS_OK;
}
if (is_error) {
ret = njs_function_call(vm, njs_function(&capability->reject),
&njs_value_undefined, &retval, 1, &vm->retval);
} else {
ret = njs_function_call(vm, njs_function(&capability->resolve),
&njs_value_undefined, &retval, 1, &vm->retval);
}
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
return NJS_OK;
}
static njs_int_t
njs_promise_resolve_thenable_job(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
njs_int_t ret;
njs_value_t *promise, retval, arguments[2];
promise = njs_arg(args, nargs, 1);
ret = njs_promise_create_resolving_functions(vm, njs_promise(promise),
arguments);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
ret = njs_function_call(vm, njs_function(njs_arg(args, nargs, 3)),
njs_arg(args, nargs, 2), arguments, 2, &retval);
if (njs_slow_path(ret != NJS_OK)) {
if (njs_slow_path(njs_is_memory_error(vm, &vm->retval))) {
return NJS_ERROR;
}
ret = njs_function_call(vm, njs_function(&arguments[1]),
&njs_value_undefined, &vm->retval, 1,
&vm->retval);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
}
return NJS_OK;
}
static njs_int_t
njs_promise_all(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t function_type)
{
njs_int_t ret;
njs_value_t *promise, resolve;
njs_iterator_handler_t handler;
njs_promise_iterator_args_t pargs;
promise = njs_argument(args, 0);
pargs.capability = njs_promise_new_capability(vm, promise);
if (njs_slow_path(pargs.capability == NULL)) {
return NJS_ERROR;
}
ret = njs_value_property(vm, promise, njs_value_arg(&string_resolve),
&resolve);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
if (njs_slow_path(!njs_is_function(&resolve))) {
njs_type_error(vm, "resolve is not callable");
return NJS_ERROR;
}
pargs.function = njs_function(&resolve);
pargs.constructor = promise;
switch (function_type) {
case NJS_PROMISE_ALL_SETTLED:
handler = njs_promise_perform_all_settled_handler;
break;
case NJS_PROMISE_ANY:
handler = njs_promise_perform_any_handler;
break;
default:
handler = njs_promise_perform_all_handler;
break;
}
return njs_promise_perform_all(vm, njs_arg(args, nargs, 1), &pargs,
handler, &vm->retval);
}
static njs_int_t
njs_promise_perform_all(njs_vm_t *vm, njs_value_t *iterator,
njs_promise_iterator_args_t *pargs, njs_iterator_handler_t handler,
njs_value_t *retval)
{
int64_t length;
njs_int_t ret;
njs_value_t argument;
njs_object_t *error;
if (njs_slow_path(!njs_is_object(pargs->constructor))) {
njs_type_error(vm, "constructor is not object");
return NJS_ERROR;
}
njs_memzero(&pargs->args, sizeof(njs_iterator_args_t));
ret = njs_object_length(vm, iterator, &length);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
pargs->args.data = njs_array_alloc(vm, 1, length, 0);
if (njs_slow_path(pargs->args.data == NULL)) {
return NJS_ERROR;
}
pargs->remaining = njs_mp_alloc(vm->mem_pool, sizeof(uint32_t));
if (njs_slow_path(pargs->remaining == NULL)) {
njs_memory_error(vm);
return NJS_ERROR;
}
(*pargs->remaining) = 1;
pargs->args.value = iterator;
pargs->args.to = length;
ret = njs_object_iterate(vm, &pargs->args, handler);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
if (--(*pargs->remaining) == 0) {
njs_mp_free(vm->mem_pool, pargs->remaining);
njs_set_array(&argument, pargs->args.data);
if (handler == njs_promise_perform_any_handler) {
error = njs_error_alloc(vm, NJS_OBJ_TYPE_AGGREGATE_ERROR,
NULL, &string_any_rejected, &argument);
if (njs_slow_path(error == NULL)) {
return NJS_ERROR;
}
njs_set_object(&argument, error);
}
ret = njs_function_call(vm, njs_function(&pargs->capability->resolve),
&njs_value_undefined, &argument, 1, retval);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
}
*retval = pargs->capability->promise;
return NJS_OK;
}
static njs_int_t
njs_promise_perform_all_handler(njs_vm_t *vm, njs_iterator_args_t *args,
njs_value_t *value, int64_t index)
{
njs_int_t ret;
njs_array_t *array;
njs_value_t arguments[2], next;
njs_function_t *on_fulfilled;
njs_promise_capability_t *capability;
njs_promise_all_context_t *context;
njs_promise_iterator_args_t *pargs;
pargs = (njs_promise_iterator_args_t *) args;
capability = pargs->capability;
array = args->data;
njs_set_undefined(&array->start[index]);
ret = njs_function_call(vm, pargs->function, pargs->constructor, value,
1, &next);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
on_fulfilled = njs_promise_create_function(vm,
sizeof(njs_promise_all_context_t));
if (njs_slow_path(on_fulfilled == NULL)) {
return NJS_ERROR;
}
on_fulfilled->u.native = njs_promise_all_resolve_element_functions;
on_fulfilled->args_count = 1;
context = on_fulfilled->context;
context->already_called = 0;
context->index = (uint32_t) index;
context->values = pargs->args.data;
context->capability = capability;
context->remaining_elements = pargs->remaining;
(*pargs->remaining)++;
njs_set_function(&arguments[0], on_fulfilled);
arguments[1] = capability->reject;
ret = njs_promise_invoke_then(vm, &next, arguments, 2);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
return NJS_OK;
}
static njs_int_t
njs_promise_all_resolve_element_functions(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
njs_value_t arguments;
njs_promise_all_context_t *context;
context = vm->top_frame->function->context;
if (context->already_called) {
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
context->already_called = 1;
context->values->start[context->index] = *njs_arg(args, nargs, 1);
if (--(*context->remaining_elements) == 0) {
njs_mp_free(vm->mem_pool, context->remaining_elements);
njs_set_array(&arguments, context->values);
return njs_function_call(vm,
njs_function(&context->capability->resolve),
&njs_value_undefined, &arguments, 1,
&vm->retval);
}
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
static njs_int_t
njs_promise_perform_all_settled_handler(njs_vm_t *vm, njs_iterator_args_t *args,
njs_value_t *value, int64_t index)
{
njs_int_t ret;
njs_array_t *array;
njs_value_t arguments[2], next;
njs_function_t *on_fulfilled, *on_rejected;
njs_promise_capability_t *capability;
njs_promise_all_context_t *context;
njs_promise_iterator_args_t *pargs;
pargs = (njs_promise_iterator_args_t *) args;
capability = pargs->capability;
array = args->data;
njs_set_undefined(&array->start[index]);
ret = njs_function_call(vm, pargs->function, pargs->constructor, value,
1, &next);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
on_fulfilled = njs_promise_create_function(vm,
sizeof(njs_promise_all_context_t));
if (njs_slow_path(on_fulfilled == NULL)) {
return NJS_ERROR;
}
context = on_fulfilled->context;
context->already_called = 0;
context->index = (uint32_t) index;
context->values = pargs->args.data;
context->capability = capability;
context->remaining_elements = pargs->remaining;
on_rejected = njs_promise_create_function(vm, 0);
if (njs_slow_path(on_rejected == NULL)) {
return NJS_ERROR;
}
on_fulfilled->u.native = njs_promise_all_settled_element_functions;
on_rejected->u.native = njs_promise_all_settled_element_functions;
on_rejected->magic8 = 1; /* rejected. */
on_fulfilled->args_count = 1;
on_rejected->args_count = 1;
on_rejected->context = context;
(*pargs->remaining)++;
njs_set_function(&arguments[0], on_fulfilled);
njs_set_function(&arguments[1], on_rejected);
ret = njs_promise_invoke_then(vm, &next, arguments, 2);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
return NJS_OK;
}
static njs_int_t
njs_promise_all_settled_element_functions(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t rejected)
{
njs_int_t ret;
njs_value_t arguments, *value;
njs_object_t *obj;
const njs_value_t *status, *set;
njs_promise_all_context_t *context;
static const njs_value_t string_status = njs_string("status");
static const njs_value_t string_fulfilled = njs_string("fulfilled");
static const njs_value_t string_value = njs_string("value");
static const njs_value_t string_rejected = njs_string("rejected");
static const njs_value_t string_reason = njs_string("reason");
context = vm->top_frame->function->context;
if (context->already_called) {
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
context->already_called = 1;
obj = njs_object_alloc(vm);
if (njs_slow_path(obj == NULL)) {
return NJS_ERROR;
}
value = &context->values->start[context->index];
njs_set_object(value, obj);
if (rejected) {
status = &string_rejected;
set = &string_reason;
} else {
status = &string_fulfilled;
set = &string_value;
}
ret = njs_value_property_set(vm, value, njs_value_arg(&string_status),
njs_value_arg(status));
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
ret = njs_value_property_set(vm, value, njs_value_arg(set),
njs_arg(args, nargs, 1));
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
if (--(*context->remaining_elements) == 0) {
njs_mp_free(vm->mem_pool, context->remaining_elements);
njs_set_array(&arguments, context->values);
return njs_function_call(vm,
njs_function(&context->capability->resolve),
&njs_value_undefined, &arguments, 1,
&vm->retval);
}
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
static njs_int_t
njs_promise_perform_any_handler(njs_vm_t *vm, njs_iterator_args_t *args,
njs_value_t *value, int64_t index)
{
njs_int_t ret;
njs_array_t *array;
njs_value_t arguments[2], next;
njs_function_t *on_rejected;
njs_promise_capability_t *capability;
njs_promise_all_context_t *context;
njs_promise_iterator_args_t *pargs;
pargs = (njs_promise_iterator_args_t *) args;
capability = pargs->capability;
array = pargs->args.data;
njs_set_undefined(&array->start[index]);
ret = njs_function_call(vm, pargs->function, pargs->constructor, value, 1,
&next);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
on_rejected = njs_promise_create_function(vm,
sizeof(njs_promise_all_context_t));
if (njs_slow_path(on_rejected == NULL)) {
return NJS_ERROR;
}
on_rejected->u.native = njs_promise_any_reject_element_functions;
on_rejected->args_count = 1;
context = on_rejected->context;
context->already_called = 0;
context->index = (uint32_t) index;
context->values = pargs->args.data;
context->capability = capability;
context->remaining_elements = pargs->remaining;
(*pargs->remaining)++;
arguments[0] = capability->resolve;
njs_set_function(&arguments[1], on_rejected);
ret = njs_promise_invoke_then(vm, &next, arguments, 2);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
return NJS_OK;
}
static njs_int_t
njs_promise_any_reject_element_functions(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused)
{
njs_value_t argument;
njs_object_t *error;
njs_promise_all_context_t *context;
context = vm->top_frame->function->context;
if (context->already_called) {
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
context->already_called = 1;
context->values->start[context->index] = *njs_arg(args, nargs, 1);
if (--(*context->remaining_elements) == 0) {
njs_mp_free(vm->mem_pool, context->remaining_elements);
njs_set_array(&argument, context->values);
error = njs_error_alloc(vm, NJS_OBJ_TYPE_AGGREGATE_ERROR,
NULL, &string_any_rejected, &argument);
if (njs_slow_path(error == NULL)) {
return NJS_ERROR;
}
njs_set_object(&argument, error);
return njs_function_call(vm, njs_function(&context->capability->reject),
&njs_value_undefined, &argument, 1,
&vm->retval);
}
njs_vm_retval_set(vm, &njs_value_undefined);
return NJS_OK;
}
static njs_int_t
njs_promise_race(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
int64_t length;
njs_int_t ret;
njs_value_t *promise, *iterator, resolve;
njs_promise_iterator_args_t pargs;
promise = njs_argument(args, 0);
iterator = njs_arg(args, nargs, 1);
pargs.capability = njs_promise_new_capability(vm, promise);
if (njs_slow_path(pargs.capability == NULL)) {
return NJS_ERROR;
}
ret = njs_value_property(vm, promise, njs_value_arg(&string_resolve),
&resolve);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
if (njs_slow_path(!njs_is_function(&resolve))) {
njs_type_error(vm, "resolve is not callable");
return NJS_ERROR;
}
ret = njs_object_length(vm, iterator, &length);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
njs_memzero(&pargs.args, sizeof(njs_iterator_args_t));
pargs.function = njs_function(&resolve);
pargs.constructor = promise;
pargs.args.value = iterator;
pargs.args.to = length;
ret = njs_object_iterate(vm, &pargs.args, njs_promise_perform_race_handler);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
vm->retval = pargs.capability->promise;
return NJS_OK;
}
static njs_int_t
njs_promise_perform_race_handler(njs_vm_t *vm, njs_iterator_args_t *args,
njs_value_t *value, int64_t index)
{
njs_int_t ret;
njs_value_t arguments[2], next;
njs_promise_capability_t *capability;
njs_promise_iterator_args_t *pargs;
pargs = (njs_promise_iterator_args_t *) args;
ret = njs_function_call(vm, pargs->function, pargs->constructor, value,
1, &next);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
capability = pargs->capability;
arguments[0] = capability->resolve;
arguments[1] = capability->reject;
(void) njs_promise_invoke_then(vm, &next, arguments, 2);
return NJS_OK;
}
static njs_int_t
njs_promise_species(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
njs_vm_retval_set(vm, njs_arg(args, nargs, 0));
return NJS_OK;
}
static const njs_object_prop_t njs_promise_constructor_properties[] =
{
{
.type = NJS_PROPERTY,
.name = njs_string("name"),
.value = njs_string("Promise"),
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("length"),
.value = njs_value(NJS_NUMBER, 1, 1.0),
.configurable = 1,
},
{
.type = NJS_PROPERTY_HANDLER,
.name = njs_string("prototype"),
.value = njs_prop_handler(njs_object_prototype_create),
},
{
.type = NJS_PROPERTY,
.name = njs_string("resolve"),
.value = njs_native_function(njs_promise_object_resolve, 1),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("reject"),
.value = njs_native_function(njs_promise_object_reject, 1),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("all"),
.value = njs_native_function2(njs_promise_all, 1, NJS_PROMISE_ALL),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("allSettled"),
.value = njs_native_function2(njs_promise_all, 1,
NJS_PROMISE_ALL_SETTLED),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("any"),
.value = njs_native_function2(njs_promise_all, 1, NJS_PROMISE_ANY),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("race"),
.value = njs_native_function(njs_promise_race, 1),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES),
.value = njs_value(NJS_INVALID, 1, NAN),
.getter = njs_native_function(njs_promise_species, 0),
.setter = njs_value(NJS_UNDEFINED, 0, NAN),
.writable = NJS_ATTRIBUTE_UNSET,
.configurable = 1,
.enumerable = 0,
},
};
const njs_object_init_t njs_promise_constructor_init = {
njs_promise_constructor_properties,
njs_nitems(njs_promise_constructor_properties),
};
static const njs_object_prop_t njs_promise_prototype_properties[] =
{
{
.type = NJS_PROPERTY_HANDLER,
.name = njs_string("constructor"),
.value = njs_prop_handler(njs_object_prototype_create_constructor),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
.value = njs_string("Promise"),
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("then"),
.value = njs_native_function(njs_promise_prototype_then, 2),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("catch"),
.value = njs_native_function(njs_promise_prototype_catch, 1),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("finally"),
.value = njs_native_function(njs_promise_prototype_finally, 1),
.writable = 1,
.configurable = 1,
},
};
const njs_object_init_t njs_promise_prototype_init = {
njs_promise_prototype_properties,
njs_nitems(njs_promise_prototype_properties),
};
const njs_object_type_init_t njs_promise_type_init = {
.constructor = njs_native_ctor(njs_promise_constructor, 1, 0),
.prototype_props = &njs_promise_prototype_init,
.constructor_props = &njs_promise_constructor_init,
.prototype_value = { .object = { .type = NJS_OBJECT } },
};