Created
December 3, 2019 17:09
-
-
Save lexborisov/76ce1e6722323efd1cbc574efab6faaf 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 1575386163 -10800 | |
# Tue Dec 03 18:16:03 2019 +0300 | |
# Node ID eeed4c5ffca03e1749cfe60e5d4af6cb4259c5ac | |
# Parent ef64784b77f667f9c16905dc50fb62203390c32f | |
Introduced the Promise object. | |
The current implementation without three methods: all, allSettled, race. | |
This closes #39 issue on GitHub. | |
diff -r ef64784b77f6 -r eeed4c5ffca0 auto/sources | |
--- a/auto/sources Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/auto/sources Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_builtin.c | |
--- a/src/njs_builtin.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_builtin.c Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_date.c | |
--- a/src/njs_date.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_date.c Tue Dec 03 18:16:03 2019 +0300 | |
@@ -1840,3 +1840,4 @@ const njs_object_type_init_t njs_date_t | |
.constructor_props = &njs_date_constructor_init, | |
.value = { .object = { .type = NJS_OBJECT } }, | |
}; | |
+ | |
diff -r ef64784b77f6 -r eeed4c5ffca0 src/njs_event.h | |
--- a/src/njs_event.h Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_event.h Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_function.c | |
--- a/src/njs_function.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_function.c Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_function.h | |
--- a/src/njs_function.h Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_function.h Tue Dec 03 18:16:03 2019 +0300 | |
@@ -117,9 +117,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); | |
@@ -188,11 +188,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 ef64784b77f6 -r eeed4c5ffca0 src/njs_json.c | |
--- a/src/njs_json.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_json.c Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_main.h | |
--- a/src/njs_main.h Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_main.h Tue Dec 03 18:16:03 2019 +0300 | |
@@ -62,6 +62,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 ef64784b77f6 -r eeed4c5ffca0 src/njs_object.c | |
--- a/src/njs_object.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_object.c Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_object_hash.h | |
--- a/src/njs_object_hash.h Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_object_hash.h Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_promise.c | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/njs_promise.c Tue Dec 03 18:16:03 2019 +0300 | |
@@ -0,0 +1,1194 @@ | |
+ | |
+/* | |
+ * 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 *obj, | |
+ njs_value_t *constructor) | |
+{ | |
+ njs_int_t ret; | |
+ njs_value_t argument; | |
+ njs_function_t *function; | |
+ njs_promise_context_t *context; | |
+ njs_promise_capability_t *capability; | |
+ | |
+ 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); | |
+ | |
+ ret = njs_function_call2(vm, njs_function(constructor), obj, | |
+ &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_promise_capability_t *capability; | |
+ | |
+ static const njs_value_t string_constructor = njs_string("constructor"); | |
+ | |
+ if (njs_is_promise(x)) { | |
+ 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, 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), | |
+ 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_function_t *function; | |
+ njs_promise_capability_t *capability; | |
+ | |
+ promise = njs_arg(args, nargs, 0); | |
+ | |
+ if (njs_slow_path(!njs_is_promise(promise))) { | |
+ njs_type_error(vm, "required a promise object"); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ 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, promise, &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); | |
+} | |
+ | |
+ | |
+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_slow_path(!njs_is_promise(value))) { | |
+ njs_type_error(vm, "required a promise object"); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ 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_promise(promise))) { | |
+ njs_type_error(vm, "required a promise 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_promise_constructor, | |
+ .prototype_props = &njs_promise_prototype_init, | |
+ .constructor_props = &njs_promise_constructor_init, | |
+ .value = { .object = { .type = NJS_OBJECT } }, | |
+}; | |
diff -r ef64784b77f6 -r eeed4c5ffca0 src/njs_promise.h | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/njs_promise.h Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_shell.c | |
--- a/src/njs_shell.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_shell.c Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_value.c | |
--- a/src/njs_value.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_value.c Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_value.h | |
--- a/src/njs_value.h Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_value.h Tue Dec 03 18:16:03 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; | |
@@ -251,6 +254,8 @@ struct njs_function_s { | |
njs_function_t *bound_target; | |
} u; | |
+ void *context; | |
+ | |
njs_value_t *bound; | |
#if (NJS_SUNC) | |
njs_closure_t *closures[1]; | |
@@ -285,6 +290,7 @@ typedef union { | |
njs_function_t function; | |
njs_regexp_t regexp; | |
njs_date_t date; | |
+ njs_promise_t promise; | |
} njs_object_prototype_t; | |
@@ -604,6 +610,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) | |
@@ -664,6 +674,10 @@ typedef struct { | |
((value)->data.u.date) | |
+#define njs_promise(value) \ | |
+ ((value)->data.u.promise) | |
+ | |
+ | |
#define njs_regexp(value) \ | |
((value)->data.u.regexp) | |
@@ -822,6 +836,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 ef64784b77f6 -r eeed4c5ffca0 src/njs_vm.c | |
--- a/src/njs_vm.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_vm.c Tue Dec 03 18:16:03 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; | |
} | |
@@ -433,7 +434,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); | |
} | |
@@ -493,6 +494,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 ef64784b77f6 -r eeed4c5ffca0 src/njs_vm.h | |
--- a/src/njs_vm.h Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_vm.h Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 src/njs_vmcode.c | |
--- a/src/njs_vmcode.c Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/src/njs_vmcode.c Tue Dec 03 18:16:03 2019 +0300 | |
@@ -1415,6 +1415,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]; | |
diff -r ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s1.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s1.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s10.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s10.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s11.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s11.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s12.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s12.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s13.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s13.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s14.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s14.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s15.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s15.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s16.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s16.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s17.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s17.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s18.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s18.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s19.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s19.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s2.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s2.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s20.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s20.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s21.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s21.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s22.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s22.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s23.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s23.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s24.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s24.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s25.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s25.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s26.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s26.js Tue Dec 03 18:16:03 2019 +0300 | |
@@ -0,0 +1,67 @@ | |
+var inherits = function(child, parent) { | |
+ child.prototype = Object.create(parent.prototype, { | |
+ constructor: { | |
+ value: child, | |
+ enumerable: false, | |
+ writable: true, | |
+ configurable: true | |
+ } | |
+ }); | |
+ Object.setPrototypeOf(child, parent); | |
+}; | |
+ | |
+// inspired by https://blog.izs.me/2013/08/designing-apis-for-asynchrony | |
+function Zalgo(executor) { | |
+ if (!(this instanceof Zalgo)) { | |
+ return Promise(executor); | |
+ } | |
+ | |
+ if (typeof executor !== 'function') { | |
+ return new Promise(executor); | |
+ } | |
+ | |
+ var context, args; | |
+ var promise = new Promise(wrappedExecutor); | |
+ Object.setPrototypeOf(promise, Zalgo.prototype); | |
+ | |
+ try { | |
+ executor.apply(context, args); | |
+ | |
+ } catch (e) { | |
+ args[1](e); | |
+ } | |
+ | |
+ return promise; | |
+ | |
+ function wrappedExecutor(resolve, reject) { | |
+ context = this; | |
+ args = [wrappedResolve, wrappedReject]; | |
+ function wrappedResolve(val) { | |
+ return resolve(val); | |
+ } | |
+ function wrappedReject(val) { | |
+ return resolve(val); | |
+ } | |
+ } | |
+} | |
+ | |
+inherits(Zalgo, Promise); | |
+ | |
+var summon = new Zalgo((resolve) => { | |
+ setImmediate(() => resolve(chant)); | |
+}); | |
+ | |
+console.log(summon instanceof Zalgo); | |
+ | |
+var z = summon | |
+ .then((x) => { console.log(x); return x; }) | |
+ .then((x) => { console.log(x); return x; }) | |
+ .finally(() => console.log('HE IS COMING')); | |
+console.log(z instanceof Zalgo); | |
+ | |
+var z = summon | |
+ .then((x) => { console.log(x); return x; }) | |
+ .then((x) => { console.log(x); return x; }); | |
+console.log(z instanceof Zalgo); | |
+ | |
+var chant = "Text"; | |
diff -r ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s3.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s3.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s4.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s4.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s5.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s5.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s6.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s6.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s7.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s7.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s8.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s8.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_s9.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_s9.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/js/promise_set_timeout.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/promise_set_timeout.js Tue Dec 03 18:16:03 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 ef64784b77f6 -r eeed4c5ffca0 test/njs_expect_test.exp | |
--- a/test/njs_expect_test.exp Tue Dec 03 18:02:40 2019 +0300 | |
+++ b/test/njs_expect_test.exp Tue Dec 03 18:16:03 2019 +0300 | |
@@ -860,3 +860,130 @@ 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"} \ | |
+"true | |
+true | |
+true | |
+Text | |
+Text | |
+Text | |
+Text | |
+HE IS COMING" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment