Skip to content

Instantly share code, notes, and snippets.

@lexborisov
Created December 6, 2019 14:40
Show Gist options
  • Save lexborisov/087962d930332750f2fe66bf152f87fa to your computer and use it in GitHub Desktop.
Save lexborisov/087962d930332750f2fe66bf152f87fa to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Alexander Borisov <alexander.borisov@nginx.com>
# Date 1575643153 -10800
# Fri Dec 06 17:39:13 2019 +0300
# Node ID adf24ac3d53087e6590055c81ab3395e54066f46
# Parent 242395b814bbfadbf957ccb18037f28628ce3bf0
Introduced the Promise object.
The current implementation without three methods: all, allSettled, race.
This closes #39 issue on GitHub.
diff -r 242395b814bb -r adf24ac3d530 auto/sources
--- a/auto/sources Fri Dec 06 14:59:48 2019 +0300
+++ b/auto/sources Fri Dec 06 17:39:13 2019 +0300
@@ -51,6 +51,7 @@ NJS_LIB_SRCS=" \
src/njs_parser_expression.c \
src/njs_generator.c \
src/njs_disassembler.c \
+ src/njs_promise.c \
"
NJS_LIB_TEST_SRCS=" \
diff -r 242395b814bb -r adf24ac3d530 src/njs_builtin.c
--- a/src/njs_builtin.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_builtin.c Fri Dec 06 17:39:13 2019 +0300
@@ -65,6 +65,7 @@ static const njs_object_type_init_t *con
&njs_function_type_init,
&njs_regexp_type_init,
&njs_date_type_init,
+ &njs_promise_type_init,
/* Hidden types. */
@@ -1162,6 +1163,15 @@ static const njs_object_prop_t njs_glob
{
.type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("Promise"),
+ .value = njs_prop_handler2(njs_top_level_constructor,
+ NJS_OBJ_TYPE_PROMISE, NJS_PROMISE_HASH),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
.name = njs_string("Error"),
.value = njs_prop_handler2(njs_top_level_constructor,
NJS_OBJ_TYPE_ERROR, NJS_ERROR_HASH),
diff -r 242395b814bb -r adf24ac3d530 src/njs_date.c
--- a/src/njs_date.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_date.c Fri Dec 06 17:39:13 2019 +0300
@@ -1840,3 +1840,4 @@ const njs_object_type_init_t njs_date_t
.prototype_props = &njs_date_prototype_init,
.prototype_value = { .object = { .type = NJS_OBJECT } },
};
+
diff -r 242395b814bb -r adf24ac3d530 src/njs_event.h
--- a/src/njs_event.h Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_event.h Fri Dec 06 17:39:13 2019 +0300
@@ -16,6 +16,8 @@
#define njs_posted_events(vm) (!njs_queue_is_empty(&(vm)->posted_events))
+#define njs_promise_events(vm) (!njs_queue_is_empty(&(vm)->promise_events))
+
typedef struct {
njs_function_t *function;
diff -r 242395b814bb -r adf24ac3d530 src/njs_function.c
--- a/src/njs_function.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_function.c Fri Dec 06 17:39:13 2019 +0300
@@ -539,14 +539,14 @@ njs_function_frame_alloc(njs_vm_t *vm, s
njs_int_t
-njs_function_call(njs_vm_t *vm, njs_function_t *function,
+njs_function_call2(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *this, const njs_value_t *args,
- njs_uint_t nargs, njs_value_t *retval)
+ njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor)
{
njs_int_t ret;
njs_value_t dst njs_aligned(16);
- ret = njs_function_frame(vm, function, this, args, nargs, 0);
+ ret = njs_function_frame(vm, function, this, args, nargs, ctor);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
diff -r 242395b814bb -r adf24ac3d530 src/njs_function.h
--- a/src/njs_function.h Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_function.h Fri Dec 06 17:39:13 2019 +0300
@@ -115,9 +115,9 @@ njs_int_t njs_function_native_frame(njs_
njs_int_t njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs,
njs_bool_t ctor);
-njs_int_t njs_function_call(njs_vm_t *vm, njs_function_t *function,
- const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs,
- njs_value_t *retval);
+njs_int_t njs_function_call2(njs_vm_t *vm, njs_function_t *function,
+ const njs_value_t *this, const njs_value_t *args,
+ njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor);
njs_int_t njs_function_lambda_call(njs_vm_t *vm);
njs_int_t njs_function_native_call(njs_vm_t *vm);
void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame);
@@ -186,11 +186,20 @@ njs_function_frame_invoke(njs_vm_t *vm,
njs_inline njs_int_t
+njs_function_call(njs_vm_t *vm, njs_function_t *function,
+ const njs_value_t *this, const njs_value_t *args,
+ njs_uint_t nargs, njs_value_t *retval)
+{
+ return njs_function_call2(vm, function, this, args, nargs, retval, 0);
+}
+
+
+njs_inline njs_int_t
njs_function_apply(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval)
{
- return njs_function_call(vm, function, &args[0], &args[1], nargs - 1,
- retval);
+ return njs_function_call2(vm, function, &args[0], &args[1], nargs - 1,
+ retval, 0);
}
diff -r 242395b814bb -r adf24ac3d530 src/njs_json.c
--- a/src/njs_json.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_json.c Fri Dec 06 17:39:13 2019 +0300
@@ -2077,6 +2077,10 @@ njs_dump_value(njs_json_stringify_t *str
return njs_json_buf_append(stringify, (char *) str.start, str.length);
+ case NJS_PROMISE:
+ njs_dump("[object Promise]");
+ break;
+
default:
p = njs_sprintf(buf, buf + njs_length(buf), "[Unknown value type:%uD]",
value->type);
diff -r 242395b814bb -r adf24ac3d530 src/njs_main.h
--- a/src/njs_main.h Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_main.h Fri Dec 06 17:39:13 2019 +0300
@@ -63,6 +63,7 @@
#include <njs_regexp.h>
#include <njs_regexp_pattern.h>
#include <njs_date.h>
+#include <njs_promise.h>
#include <njs_math.h>
#include <njs_json.h>
diff -r 242395b814bb -r adf24ac3d530 src/njs_object.c
--- a/src/njs_object.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_object.c Fri Dec 06 17:39:13 2019 +0300
@@ -2408,6 +2408,7 @@ njs_object_prototype_to_string(njs_vm_t
&njs_object_regexp_string,
&njs_object_date_string,
&njs_object_object_string,
+ &njs_object_object_string,
};
value = njs_argument(args, 0);
diff -r 242395b814bb -r adf24ac3d530 src/njs_object_hash.h
--- a/src/njs_object_hash.h Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_object_hash.h Fri Dec 06 17:39:13 2019 +0300
@@ -88,6 +88,17 @@
'D'), 'a'), 't'), 'e')
+#define NJS_PROMISE_HASH \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add(NJS_DJB_HASH_INIT, \
+ 'P'), 'r'), 'o'), 'm'), 'i'), 's'), 'e')
+
+
#define NJS_ENUMERABLE_HASH \
njs_djb_hash_add( \
njs_djb_hash_add( \
diff -r 242395b814bb -r adf24ac3d530 src/njs_promise.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_promise.c Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,1216 @@
+
+/*
+ * 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 struct {
+ njs_promise_type_t state;
+ njs_value_t result;
+ njs_queue_t fulfill_queue;
+ njs_queue_t reject_queue;
+ njs_arr_t reactions;
+ 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_promise_context_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_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_catch_finally_function(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_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)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ 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.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_PROMISE].object;
+
+ data = (njs_promise_data_t *) ((uint8_t *) promise + sizeof(njs_promise_t));
+
+ data->state = NJS_PROMISE_PENDING;
+ data->is_handled = 0;
+
+ (void) njs_arr_init(vm->mem_pool, &data->reactions, NULL, 12,
+ sizeof(njs_promise_reaction_t));
+ if (njs_slow_path(data->reactions.start == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ njs_queue_init(&data->fulfill_queue);
+ njs_queue_init(&data->reject_queue);
+
+ njs_set_promise(&vm->retval, promise);
+ njs_value_data_set(&promise->value, data);
+
+ return promise;
+}
+
+
+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, "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;
+}
+
+
+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)
+{
+ 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)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ context = njs_mp_zalloc(vm->mem_pool, sizeof(njs_promise_context_t));
+ if (njs_slow_path(context == NULL)) {
+ njs_memory_error(vm);
+ return 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.shared = 0;
+ function->object.extensible = 1;
+ function->args_offset = 1;
+ function->native = 1;
+ function->context = context;
+
+ return function;
+}
+
+
+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);
+ 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);
+ 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);
+ function->u.native = njs_promise_reaction_job;
+
+ njs_set_data(&arguments[0], reaction);
+ 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_value_data(&promise->value);
+
+ data->result = *value;
+ data->state = NJS_PROMISE_FULFILL;
+
+ if (data->fulfill_queue.head.next == &data->fulfill_queue.head) {
+ 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_queue_t queue;
+ njs_promise_data_t *data;
+
+ data = njs_value_data(&promise->value);
+
+ data->result = *reason;
+ data->state = NJS_PROMISE_REJECTED;
+
+ if (data->reject_queue.head.next == &data->reject_queue.head) {
+ 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_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_frame_t *active_frame;
+ njs_value_t *resolution, error, then, arguments[3];
+ njs_promise_t *promise;
+ njs_function_t *function;
+ njs_promise_context_t *context;
+
+ static const njs_value_t string_then = njs_string("then");
+
+ active_frame = (njs_frame_t *) vm->top_frame;
+ context = active_frame->native.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);
+ 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_frame_t *active_frame;
+ njs_value_t *value;
+ njs_promise_context_t *context;
+
+ active_frame = (njs_frame_t *) vm->top_frame;
+ context = active_frame->native.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);
+ 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_value_data(&promise->value);
+
+ fulfilled_reaction = njs_arr_add(&data->reactions);
+ 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_arr_add(&data->reactions);
+ if (njs_slow_path(fulfilled_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);
+ function->u.native = njs_promise_reaction_job;
+
+ if (data->state == NJS_PROMISE_REJECTED) {
+ njs_set_data(&arguments[0], rejected_reaction);
+
+ /* TODO: HostPromiseRejectionTracker */
+
+ } else {
+ njs_set_data(&arguments[0], fulfilled_reaction);
+ }
+
+ 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);
+ 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);
+ 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;
+
+ njs_set_function(&arguments[0], function);
+
+ function = njs_promise_create_function(vm);
+ if (njs_slow_path(function == NULL)) {
+ return NJS_ERROR;
+ }
+
+ function->u.native = njs_promise_catch_finally_function;
+ function->args_count = 1;
+
+ context = function->context;
+ context->constructor = constructor;
+ context->finally = *finally;
+
+ 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;
+ njs_frame_t *frame;
+ njs_promise_t *promise;
+ njs_promise_context_t *context;
+
+ frame = (njs_frame_t *) vm->top_frame;
+ context = frame->native.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);
+
+ return njs_promise_invoke_then(vm, &value, njs_arg(args, nargs, 1), 1);
+}
+
+
+static njs_int_t
+njs_promise_catch_finally_function(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused)
+{
+ return njs_promise_then_finally_function(vm, args, nargs, 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)
+{
+ 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_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_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 } },
+};
diff -r 242395b814bb -r adf24ac3d530 src/njs_promise.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_promise.h Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,18 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+#ifndef _NJS_PROMISE_H_INCLUDED_
+#define _NJS_PROMISE_H_INCLUDED_
+
+
+njs_int_t
+njs_promise_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused);
+
+
+extern const njs_object_type_init_t njs_promise_type_init;
+
+
+#endif /* _NJS_PROMISE_H_INCLUDED_ */
diff -r 242395b814bb -r adf24ac3d530 src/njs_shell.c
--- a/src/njs_shell.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_shell.c Fri Dec 06 17:39:13 2019 +0300
@@ -109,6 +109,7 @@ static njs_int_t njs_ext_console_time_en
static njs_host_event_t njs_console_set_timer(njs_external_ptr_t external,
uint64_t delay, njs_vm_event_t vm_event);
+
static void njs_console_clear_timer(njs_external_ptr_t external,
njs_host_event_t event);
diff -r 242395b814bb -r adf24ac3d530 src/njs_value.c
--- a/src/njs_value.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_value.c Fri Dec 06 17:39:13 2019 +0300
@@ -359,6 +359,9 @@ njs_type_string(njs_value_type_t type)
case NJS_DATE:
return "date";
+ case NJS_PROMISE:
+ return "promise";
+
default:
return NULL;
}
@@ -553,6 +556,7 @@ njs_property_query(njs_vm_t *vm, njs_pro
case NJS_OBJECT_STRING:
case NJS_REGEXP:
case NJS_DATE:
+ case NJS_PROMISE:
case NJS_OBJECT_VALUE:
obj = njs_object(value);
break;
@@ -1252,6 +1256,7 @@ njs_value_to_object(njs_vm_t *vm, njs_va
}
+
void
njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string)
{
diff -r 242395b814bb -r adf24ac3d530 src/njs_value.h
--- a/src/njs_value.h Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_value.h Fri Dec 06 17:39:13 2019 +0300
@@ -67,7 +67,8 @@ typedef enum {
NJS_FUNCTION = 0x16,
NJS_REGEXP = 0x17,
NJS_DATE = 0x18,
- NJS_OBJECT_VALUE = 0x19,
+ NJS_PROMISE = 0x19,
+ NJS_OBJECT_VALUE = 0x1A,
NJS_VALUE_TYPE_MAX
} njs_value_type_t;
@@ -81,6 +82,7 @@ typedef struct njs_regexp_pattern_s nj
typedef struct njs_array_s njs_array_t;
typedef struct njs_regexp_s njs_regexp_t;
typedef struct njs_date_s njs_date_t;
+typedef struct njs_object_value_s njs_promise_t;
typedef struct njs_property_next_s njs_property_next_t;
typedef struct njs_object_init_s njs_object_init_t;
@@ -143,6 +145,7 @@ union njs_value_s {
njs_function_lambda_t *lambda;
njs_regexp_t *regexp;
njs_date_t *date;
+ njs_promise_t *promise;
njs_prop_handler_t prop_handler;
njs_value_t *value;
njs_property_next_t *next;
@@ -260,6 +263,8 @@ struct njs_function_s {
njs_function_t *bound_target;
} u;
+ void *context;
+
njs_value_t *bound;
};
@@ -289,6 +294,7 @@ typedef union {
njs_function_t function;
njs_regexp_t regexp;
njs_date_t date;
+ njs_promise_t promise;
} njs_object_prototype_t;
@@ -617,6 +623,10 @@ typedef struct {
((value)->type == NJS_DATE)
+#define njs_is_promise(value) \
+ ((value)->type == NJS_PROMISE)
+
+
#define njs_is_error(value) \
((value)->type == NJS_OBJECT && njs_object(value)->error_data)
@@ -677,6 +687,10 @@ typedef struct {
((value)->data.u.date)
+#define njs_promise(value) \
+ ((value)->data.u.promise)
+
+
#define njs_regexp(value) \
((value)->data.u.regexp)
@@ -835,6 +849,15 @@ njs_set_date(njs_value_t *value, njs_dat
njs_inline void
+njs_set_promise(njs_value_t *value, njs_promise_t *promise)
+{
+ value->data.u.promise = promise;
+ value->type = NJS_PROMISE;
+ value->data.truth = 1;
+}
+
+
+njs_inline void
njs_set_regexp(njs_value_t *value, njs_regexp_t *regexp)
{
value->data.u.regexp = regexp;
diff -r 242395b814bb -r adf24ac3d530 src/njs_vm.c
--- a/src/njs_vm.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_vm.c Fri Dec 06 17:39:13 2019 +0300
@@ -304,6 +304,7 @@ njs_vm_init(njs_vm_t *vm)
njs_lvlhsh_init(&vm->events_hash);
njs_queue_init(&vm->posted_events);
+ njs_queue_init(&vm->promise_events);
return NJS_OK;
}
@@ -435,7 +436,7 @@ njs_vm_waiting(njs_vm_t *vm)
njs_int_t
njs_vm_posted(njs_vm_t *vm)
{
- return njs_posted_events(vm);
+ return njs_posted_events(vm) || njs_promise_events(vm);
}
@@ -495,6 +496,26 @@ njs_vm_handle_events(njs_vm_t *vm)
njs_queue_t *events;
njs_queue_link_t *link;
+ events = &vm->promise_events;
+
+ for ( ;; ) {
+ link = njs_queue_first(events);
+
+ if (link == njs_queue_tail(events)) {
+ break;
+ }
+
+ ev = njs_queue_link_data(link, njs_event_t, link);
+
+ ev->posted = 0;
+ njs_queue_remove(&ev->link);
+
+ ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+ }
+
events = &vm->posted_events;
for ( ;; ) {
diff -r 242395b814bb -r adf24ac3d530 src/njs_vm.h
--- a/src/njs_vm.h Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_vm.h Fri Dec 06 17:39:13 2019 +0300
@@ -89,6 +89,7 @@ typedef enum {
NJS_OBJ_TYPE_FUNCTION,
NJS_OBJ_TYPE_REGEXP,
NJS_OBJ_TYPE_DATE,
+ NJS_OBJ_TYPE_PROMISE,
NJS_OBJ_TYPE_CRYPTO_HASH,
#define NJS_OBJ_TYPE_HIDDEN_MIN (NJS_OBJ_TYPE_CRYPTO_HASH)
NJS_OBJ_TYPE_CRYPTO_HMAC,
@@ -186,6 +187,7 @@ struct njs_vm_s {
uint32_t event_id;
njs_lvlhsh_t events_hash;
njs_queue_t posted_events;
+ njs_queue_t promise_events;
njs_vm_opt_t options;
diff -r 242395b814bb -r adf24ac3d530 src/njs_vmcode.c
--- a/src/njs_vmcode.c Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_vmcode.c Fri Dec 06 17:39:13 2019 +0300
@@ -66,8 +66,6 @@ static njs_jump_off_t njs_primitive_valu
static njs_jump_off_t njs_function_frame_create(njs_vm_t *vm,
njs_value_t *value, const njs_value_t *this, uintptr_t nargs,
njs_bool_t ctor);
-static njs_object_t *njs_function_new_object(njs_vm_t *vm,
- njs_value_t *constructor);
/*
* The nJSVM is optimized for an ABIs where the first several arguments
@@ -1416,6 +1414,7 @@ njs_vmcode_typeof(njs_vm_t *vm, njs_valu
&njs_string_object,
&njs_string_object,
&njs_string_object,
+ &njs_string_object,
};
vm->retval = *types[value->type];
@@ -1619,7 +1618,7 @@ njs_function_frame_create(njs_vm_t *vm,
}
-static njs_object_t *
+njs_object_t *
njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor)
{
njs_value_t proto, bound;
diff -r 242395b814bb -r adf24ac3d530 src/njs_vmcode.h
--- a/src/njs_vmcode.h Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_vmcode.h Fri Dec 06 17:39:13 2019 +0300
@@ -392,5 +392,7 @@ typedef struct {
njs_int_t njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc);
+njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor);
+
#endif /* _NJS_VMCODE_H_INCLUDED_ */
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s1.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s1.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,15 @@
+var promise = new Promise(function(resolve, reject) {
+ console.log('One');
+
+ reject(123);
+}).catch((v) => {console.log(v)});
+
+console.log('Two');
+
+promise.then(() => {console.log('Four'); return {num: 'Five'}})
+.then((obj) => {console.log(obj.num); return {num: 'Six'}})
+.then((obj) => {console.log(obj.num); return {num: 'Seven'}})
+.then((obj) => {console.log(obj.num); return {num: 'Eight'}})
+.then((obj) => {console.log(obj.num)});
+
+console.log('Three');
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s10.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s10.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,11 @@
+var promise = new Promise(function(resolve, reject) {
+ console.log('One');
+ reject('Oh no');
+});
+
+console.log('Two');
+
+promise.then(() => {console.log('Three')})
+.catch((v) => {console.log(v)});
+
+console.log('Three');
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s11.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s11.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,13 @@
+var promise = new Promise((resolve, reject) => resolve('all'));
+
+promise.then( function f1(result) {
+ console.log('S: ' + result);
+ return 'f1';
+});
+
+promise.then( function f2(result) {
+ console.log('R: ' + result);
+ return 'f2';
+});
+
+console.log('end')
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s12.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s12.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,10 @@
+var thenable = new Promise(function() {});
+
+var p = new Promise(function(resolve) {
+ resolve();
+ throw thenable;
+});
+
+p.then(function() {
+ console.log('Done');
+});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s13.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s13.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,21 @@
+var thenable = Promise.resolve();
+var p = new Promise(function(a,b) {
+ throw thenable;
+});
+
+p.then(function() {
+ console.log('The promise should not be fulfilled.');
+})
+.then(
+ function() {
+ console.log('The promise should not be fulfilled.');
+ },
+ function(x) {
+ if (x !== thenable) {
+ console.log('The promise should be rejected with the resolution value.');
+ return;
+ }
+
+ console.log('Done');
+ }
+);
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s14.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s14.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,9 @@
+var isLoading = true;
+var promise = new Promise((a, b) => {a()} );
+
+promise.then(function(response) {
+ throw new TypeError('oops');
+})
+.then(function(json) { })
+.catch(function(error) { console.log(error); })
+.finally(function() { console.log('Done') });
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s15.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s15.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,10 @@
+var obj = {};
+var p = Promise.resolve(obj);
+
+p.then(undefined, undefined)
+.then(function(arg) {
+ if (arg !== obj) {
+ console.log('Expected resolution object to be passed through, got ' + arg);
+ }
+})
+.then(() => {console.log('Done')}, () => {console.log('Error')});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s16.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s16.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,10 @@
+var obj = {};
+var p = Promise.reject(obj);
+
+p.then(undefined, undefined).then(function() {
+ console.log('Should not be called -- promise was rejected.');
+}, function(arg) {
+ if (arg !== obj) {
+ console.log('Expected resolution object to be passed through, got ' + arg);
+ }
+}).then(() => {console.log('Done')}, () => {console.log('Error')});
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s17.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s17.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,10 @@
+var obj = {};
+var p = Promise.reject(obj);
+
+p.then(undefined, undefined).then(function() {
+ console.log('Should not be called -- promise was rejected.');
+}, function(arg) {
+ if (arg !== obj) {
+ console.log('Expected resolution object to be passed through, got ' + arg);
+ }
+}).then(() => {console.log('Done')}, () => {console.log('Error')});
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s18.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s18.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,23 @@
+var thenable = {
+ then: function(resolve) {
+ resolve();
+ console.log('State 5');
+ }
+};
+
+var thenableWithError = {
+ then: function(resolve) {
+ console.log('State 3');
+ resolve(thenable);
+ console.log('State 4');
+ throw new Error('ignored exception');
+ }
+};
+
+function executor(resolve, reject) {
+ console.log('State 1');
+ resolve(thenableWithError);
+ console.log('State 2');
+}
+
+new Promise(executor).then(() => {console.log('Done')}, () => {console.log('Error')});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s19.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s19.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,33 @@
+var returnValue = null;
+var value = {};
+var resolve;
+
+var poisonedThen = Object.defineProperty({}, 'then', {
+ get: function() {
+ console.log('Throw!');
+ throw value;
+ }
+});
+
+var promise = new Promise(function(_resolve) {
+ resolve = _resolve;
+});
+
+promise.then(
+function() {
+ console.log('Resolve!');
+ console.log('The promise should not be fulfilled.');
+},
+function(val) {
+ console.log('Reject!');
+ if (val !== value) {
+ console.log('The promise should be fulfilled with the provided value.');
+ return;
+ }
+
+ console.log('Done');
+});
+
+returnValue = resolve(poisonedThen);
+
+console.log(returnValue);
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s2.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s2.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,14 @@
+var promise = new Promise(function(resolve, reject) {
+ console.log('One');
+ reject(123);
+});
+
+console.log('Two');
+
+promise.then(() => {console.log('Four'); return {num: 'Five'}})
+.then((obj) => {console.log(obj.num); return {num: 'Six'}})
+.then((obj) => {console.log(obj.num); return {num: 'Seven'}})
+.then((obj) => {console.log(obj.num); return {num: 'Eight'}})
+.then((obj) => {console.log(obj.num)});
+
+console.log('Three');
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s20.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s20.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,23 @@
+var returnValue = null;
+var value = {};
+var poisonedThen = Object.defineProperty({}, 'then', {
+ get: function() {
+ throw value;
+ }
+});
+var promise = new Promise(function(resolve) {
+ returnValue = resolve(poisonedThen);
+});
+
+console.log(returnValue == undefined);
+
+promise.then(function() {
+ console.log('The promise should not be fulfilled.');
+}, function(val) {
+ if (val !== value) {
+ console.log('The promise should be fulfilled with the provided value.');
+ return;
+ }
+
+ console.log('Done');
+});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s21.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s21.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,30 @@
+var value = {};
+var resolve;
+var poisonedThen = Object.defineProperty({}, 'then', {
+ get: function() {
+ throw value;
+ }
+});
+
+var p1 = new Promise(function(_resolve) {
+ resolve = _resolve;
+});
+
+var p2;
+
+p2 = p1.then(function() {
+ return poisonedThen;
+});
+
+p2.then(function(x) {
+ console.log('The promise should not be fulfilled.');
+}, function(x) {
+ if (x !== value) {
+ console.log('The promise should be rejected with the thrown exception.');
+ return;
+ }
+
+ console.log('Done');
+});
+
+resolve();
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s22.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s22.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,32 @@
+var returnValue = null;
+var value = {};
+var resolve;
+
+var thenable = new Promise(function(resolve) {
+ resolve();
+});
+
+var promise = new Promise(function(_resolve) {
+ resolve = _resolve;
+});
+
+thenable.then = function(resolve) {
+ resolve(value);
+};
+
+promise.then(
+function(val) {
+ if (val !== value) {
+ console.log('The promise should be fulfilled with the provided value.');
+ return;
+ }
+
+ console.log('Done');
+},
+function() {
+ console.log('The promise should not be rejected.');
+});
+
+returnValue = resolve(thenable);
+
+console.log(returnValue == undefined);
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s23.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s23.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,28 @@
+var returnValue = null;
+var resolve;
+
+var promise = new Promise(function(_resolve) {
+ resolve = _resolve;
+});
+
+promise.then(
+function() {
+ console.log('The promise should not be fulfilled.');
+},
+function(reason) {
+ if (!reason) {
+ console.log('The promise should be rejected with a value.');
+ return;
+ }
+
+ if (reason.constructor !== TypeError) {
+ console.log('The promise should be rejected with a TypeError instance.');
+ return;
+ }
+
+ console.log('Done');
+});
+
+returnValue = resolve(promise);
+
+console.log(returnValue == undefined)
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s24.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s24.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,13 @@
+var checkPoint = '';
+
+Promise.reject.call(function(executor) {
+ checkPoint += 'a';
+ executor();
+
+ checkPoint += 'b';
+ executor(function() {}, function() {});
+
+ checkPoint += 'c';
+}, {});
+
+console.log(checkPoint == 'abc');
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s25.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s25.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,29 @@
+var resolve, reject;
+var promise = new Promise(function(_resolve, _reject) {
+ resolve = _resolve;
+ reject = _reject;
+});
+
+var P = function(executor) {
+ executor(resolve, reject);
+ return promise;
+};
+
+Promise.resolve.call(P, promise)
+.then(
+function() {
+ console.log('The promise should not be fulfilled.');
+},
+function(value) {
+ if (!value) {
+ console.log('The promise should be rejected with a value.');
+ return;
+ }
+
+ if (value.constructor !== TypeError) {
+ console.log('The promise should be rejected with a TypeError instance.');
+ return;
+ }
+
+ console.log('Done');
+});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s26.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s26.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,76 @@
+var inherits = function(child, parent) {
+ child.prototype = Object.create(parent.prototype, {
+ constructor: {
+ value: child,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ Object.setPrototypeOf(child, parent);
+};
+
+function BoxedPromise(executor) {
+ console.log('ctor hacked');
+
+ if (!(this instanceof BoxedPromise)) {
+ return Promise(executor);
+ }
+
+ if (typeof executor !== 'function') {
+ return new Promise(executor);
+ }
+
+ var context, args;
+ var promise = new Promise(wrappedExecutor);
+ this.boxed = promise;
+
+ try {
+ executor.apply(context, args);
+
+ } catch (e) {
+ args[1](e);
+ }
+
+ function wrappedExecutor(resolve, reject) {
+ context = this;
+ args = [wrappedResolve, wrappedReject];
+ function wrappedResolve(val) {
+ return resolve(val);
+ }
+ function wrappedReject(val) {
+ return reject(val);
+ }
+ }
+}
+
+inherits(BoxedPromise, Promise);
+
+BoxedPromise.prototype.then = function(res, rej) {
+ console.log('then hacked');
+ var rs = Object();
+ Object.setPrototypeOf(rs, Object.getPrototypeOf(this));
+ rs.boxed = this.boxed.then(res, rej);
+ return rs;
+};
+
+
+var reject = BoxedPromise.reject('test4').catch((x) => console.log('reject - OK', x));
+
+console.log(reject instanceof BoxedPromise, reject);
+
+var resolve = BoxedPromise.resolve('test3').then((x) => console.log('resolve - OK', x));
+
+console.log(resolve instanceof BoxedPromise, resolve);
+
+
+var work = new BoxedPromise((resolve) => {
+ setImmediate(() => resolve('test5'));
+});
+
+var x = work
+ .then((x) => { console.log(x); return x; })
+ .then((x) => { console.log(x); return x; })
+ .finally(() => console.log('done5 finally:'));
+
+console.log(x instanceof BoxedPromise, x);
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s3.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s3.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,11 @@
+
+var promise = new Promise(function(resolve, reject) {
+ console.log('One');
+ reject(new Error('Blah'));
+});
+
+console.log('Two');
+
+promise.then((response) => console.log(`Fulfilled: ${response}`), (error) => console.log(`Rejected: ${error}`));
+
+console.log('Three');
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s4.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s4.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,6 @@
+Promise.reject(new Error('Oh my')).then(function(success) {
+},
+function(error) {
+ console.log(error);
+ throw error;
+});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s5.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s5.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,7 @@
+
+Promise.resolve('Success').then(function(value) {
+ console.log(value);
+},
+function(value) {
+ console.log('ignored');
+});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s6.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s6.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,4 @@
+var p = Promise.resolve([1,2,3]);
+p.then(function(v) {
+ console.log(v[0]);
+});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s7.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s7.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,12 @@
+var p1 = Promise.resolve({
+ then: function(onFulfill, onReject) { onFulfill('fulfilled!'); }
+});
+
+console.log(p1 instanceof Promise);
+
+p1.then(function(v) {
+ console.log(v);
+},
+function(e) {
+ console.log('ignored');
+});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s8.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s8.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,13 @@
+var thenable = {
+ then: function(resolve) {
+ console.log('Ok')
+ resolve();
+ }
+};
+
+function executor(resolve, reject) {
+ resolve(thenable);
+ throw new Error('ignored');
+}
+
+new Promise(executor).then(() => {});
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_s9.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_s9.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,10 @@
+var executorFunction;
+
+function NotPromise(executor) {
+ executorFunction = executor;
+ executor(function() {console.log('S')}, function() {console.log('R')});
+}
+
+Promise.resolve.call(NotPromise);
+
+console.log(Object.isExtensible(executorFunction));
\ No newline at end of file
diff -r 242395b814bb -r adf24ac3d530 test/js/promise_set_timeout.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/promise_set_timeout.js Fri Dec 06 17:39:13 2019 +0300
@@ -0,0 +1,17 @@
+var res = [];
+function abc() {
+ var promise = new Promise(function(resolve, reject) {
+ res.push('One');
+ resolve();
+ });
+ res.push('Two');
+ promise.then(() => {res.push('Four'); return {num: 'Five'}})
+ .then((obj) => {res.push(obj.num); return {num: 'Six'}})
+ .then((obj) => {res.push(obj.num); return {num: 'Seven'}})
+ .then((obj) => {res.push(obj.num); return {num: 'Eight'}})
+ .then((obj) => {res.push(obj.num)});
+ res.push('Three');
+}
+abc();
+console.log(res.join(','));
+setTimeout(() => console.log(res.join(',')), 0);
diff -r 242395b814bb -r adf24ac3d530 test/njs_expect_test.exp
--- a/test/njs_expect_test.exp Fri Dec 06 14:59:48 2019 +0300
+++ b/test/njs_expect_test.exp Fri Dec 06 17:39:13 2019 +0300
@@ -860,3 +860,141 @@ njs_test {
njs_run {"-v"} "\\d+\.\\d+\.\\d+"
+
+# Promise
+
+njs_run {"./test/js/promise_set_timeout.js"} \
+"One,Two,Three
+One,Two,Three,Four,Five,Six,Seven,Eight"
+
+njs_run {"./test/js/promise_s1.js"} \
+"One
+Two
+Three
+123
+Four
+Five
+Six
+Seven
+Eight"
+
+njs_run {"./test/js/promise_s2.js"} \
+"One
+Two
+Three"
+
+njs_run {"./test/js/promise_s3.js"} \
+"One
+Two
+Three
+Rejected: Error: Blah"
+
+njs_run {"./test/js/promise_s4.js"} \
+"Error: Oh my"
+
+njs_run {"./test/js/promise_s5.js"} \
+"Success"
+
+njs_run {"./test/js/promise_s6.js"} \
+"1"
+
+njs_run {"./test/js/promise_s7.js"} \
+"true
+fulfilled!"
+
+njs_run {"./test/js/promise_s8.js"} \
+"Ok"
+
+njs_run {"./test/js/promise_s9.js"} \
+"S
+true"
+
+njs_run {"./test/js/promise_s10.js"} \
+"One
+Two
+Three
+Oh no"
+
+njs_run {"./test/js/promise_s11.js"} \
+"end
+S: all
+R: all"
+
+njs_run {"./test/js/promise_s12.js"} \
+"Done"
+
+njs_run {"./test/js/promise_s13.js"} \
+"Done"
+
+njs_run {"./test/js/promise_s14.js"} \
+"TypeError: oops
+ at anonymous \\\(promise_s14.js:4\\\)
+ at native \\\(native\\\)
+ at main \\\(native\\\)
+
+Done"
+
+njs_run {"./test/js/promise_s15.js"} \
+"Done"
+
+njs_run {"./test/js/promise_s16.js"} \
+"Done"
+
+njs_run {"./test/js/promise_s17.js"} \
+"Done"
+
+njs_run {"./test/js/promise_s18.js"} \
+"State 1
+State 2
+State 3
+State 4
+State 5
+Done"
+
+njs_run {"./test/js/promise_s19.js"} \
+"Throw!
+undefined
+Reject!
+Done"
+
+njs_run {"./test/js/promise_s20.js"} \
+"true
+Done"
+
+njs_run {"./test/js/promise_s21.js"} \
+"Done"
+
+njs_run {"./test/js/promise_s22.js"} \
+"true
+Done"
+
+njs_run {"./test/js/promise_s23.js"} \
+"true
+Done"
+
+njs_run {"./test/js/promise_s24.js"} \
+"true"
+
+njs_run {"./test/js/promise_s25.js"} \
+"Done"
+
+njs_run {"./test/js/promise_s26.js"} \
+"ctor hacked
+then hacked
+true Promise {boxed:\\\[object Promise\\\]}
+ctor hacked
+then hacked
+true Promise {boxed:\\\[object Promise\\\]}
+ctor hacked
+then hacked
+then hacked
+then hacked
+true Promise {boxed:\\\[object Promise\\\]}
+reject - OK test4
+resolve - OK test3
+test5
+test5
+done5 finally:
+ctor hacked
+then hacked
+then hacked"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment