Created
December 6, 2019 14:40
-
-
Save lexborisov/087962d930332750f2fe66bf152f87fa to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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