Created
November 29, 2019 15:16
-
-
Save lexborisov/a8b4593514f3d7cf8f7700d4e8f8df9b 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 1575040496 -10800 | |
# Fri Nov 29 18:14:56 2019 +0300 | |
# Node ID c06acccab6a1765544a7c243c9cb23ddd7e296d3 | |
# Parent 01c7375c9b5ca47e0eda9a7bddf6aacbefbebaff | |
[mq]: Great_Promise | |
diff -r 01c7375c9b5c -r c06acccab6a1 auto/sources | |
--- a/auto/sources Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/auto/sources Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_builtin.c | |
--- a/src/njs_builtin.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_builtin.c Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_date.c | |
--- a/src/njs_date.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_date.c Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_error.c | |
--- a/src/njs_error.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_error.c Fri Nov 29 18:14:56 2019 +0300 | |
@@ -647,6 +647,27 @@ njs_memory_error(njs_vm_t *vm) | |
} | |
+njs_int_t | |
+njs_is_memory_error(njs_vm_t *vm) | |
+{ | |
+ njs_object_t *object; | |
+ | |
+ if (!njs_is_object(&vm->retval)) { | |
+ return 0; | |
+ } | |
+ | |
+ object = njs_object(&vm->retval); | |
+ | |
+ if (object->__proto__->type == NJS_OBJ_TYPE_INTERNAL_ERROR | |
+ && object->extensible == 0) | |
+ { | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
static njs_int_t | |
njs_memory_error_constructor(njs_vm_t *vm, njs_value_t *args, | |
njs_uint_t nargs, njs_index_t unused) | |
diff -r 01c7375c9b5c -r c06acccab6a1 src/njs_error.h | |
--- a/src/njs_error.h Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_error.h Fri Nov 29 18:14:56 2019 +0300 | |
@@ -39,6 +39,7 @@ void njs_noinline njs_error_fmt_new(njs_ | |
void njs_memory_error(njs_vm_t *vm); | |
void njs_memory_error_set(njs_vm_t *vm, njs_value_t *value); | |
+njs_int_t njs_is_memory_error(njs_vm_t *vm); | |
njs_object_t *njs_error_alloc(njs_vm_t *vm, njs_object_type_t type, | |
const njs_value_t *name, const njs_value_t *message); | |
diff -r 01c7375c9b5c -r c06acccab6a1 src/njs_event.h | |
--- a/src/njs_event.h Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_event.h Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_function.c | |
--- a/src/njs_function.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_function.c Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_function.h | |
--- a/src/njs_function.h Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_function.h Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_json.c | |
--- a/src/njs_json.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_json.c Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_main.h | |
--- a/src/njs_main.h Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_main.h Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_object.c | |
--- a/src/njs_object.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_object.c Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_object_hash.h | |
--- a/src/njs_object_hash.h Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_object_hash.h Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/njs_promise.c | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/njs_promise.c Fri Nov 29 18:14:56 2019 +0300 | |
@@ -0,0 +1,1209 @@ | |
+ | |
+/* | |
+ * Copyright (C) Igor Sysoev | |
+ * Copyright (C) Nginx, Inc. | |
+ */ | |
+ | |
+#include <njs_main.h> | |
+ | |
+#include "njs_error.h" | |
+#include "njs_promise.h" | |
+ | |
+ | |
+typedef enum { | |
+ NJS_PROMISE_PENDING = 0x00, | |
+ 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; | |
+ | |
+ | |
+static njs_promise_t *njs_promise_constructor_call(njs_vm_t *vm, | |
+ njs_function_t *function); | |
+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_species_constructor(njs_vm_t *vm, njs_value_t *object, | |
+ njs_value_t *default_constructor, 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_function_t *njs_promise_create_resolve_function(njs_vm_t *vm, | |
+ njs_promise_t *promise); | |
+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_function_t *njs_promise_create_reject_function(njs_vm_t *vm, | |
+ njs_promise_t *promise); | |
+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 (!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; | |
+ njs_function_t *resolve, *reject; | |
+ | |
+ promise = njs_promise_alloc(vm); | |
+ if (njs_slow_path(promise == NULL)) { | |
+ return NULL; | |
+ } | |
+ | |
+ resolve = njs_promise_create_resolve_function(vm, promise); | |
+ if (njs_slow_path(resolve == NULL)) { | |
+ return NULL; | |
+ } | |
+ | |
+ reject = njs_promise_create_reject_function(vm, promise); | |
+ if (njs_slow_path(reject == NULL)) { | |
+ return NULL; | |
+ } | |
+ | |
+ reject->resolved_ref = &resolve->resolved; | |
+ | |
+ njs_set_function(&arguments[0], resolve); | |
+ njs_set_function(&arguments[1], reject); | |
+ | |
+ 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))) { | |
+ return NULL; | |
+ } | |
+ | |
+ ret = njs_function_call(vm, reject, &njs_value_undefined, &vm->retval, | |
+ 1, &retval); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return NULL; | |
+ } | |
+ } | |
+ | |
+ return promise; | |
+} | |
+ | |
+ | |
+njs_inline njs_function_t * | |
+njs_promise_create_function(njs_vm_t *vm) | |
+{ | |
+ njs_function_t *function; | |
+ | |
+ function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t)); | |
+ if (njs_slow_path(function == 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; | |
+ | |
+ return function; | |
+} | |
+ | |
+ | |
+njs_inline njs_promise_capability_t * | |
+njs_promise_new_capability(njs_vm_t *vm, njs_value_t *constructor) | |
+{ | |
+ njs_int_t ret; | |
+ njs_value_t argument; | |
+ njs_function_t *function; | |
+ 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->capability = capability; | |
+ function->args_count = 2; | |
+ | |
+ njs_set_function(&argument, function); | |
+ | |
+ ret = njs_function_call2(vm, njs_function(constructor), constructor, | |
+ &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; | |
+} | |
+ | |
+ | |
+/* TODO: need move the function to good place. */ | |
+static njs_int_t | |
+njs_species_constructor(njs_vm_t *vm, njs_value_t *object, | |
+ njs_value_t *default_constructor, njs_value_t *dst) | |
+{ | |
+ njs_int_t ret; | |
+ njs_value_t constructor; | |
+ | |
+ static const njs_value_t string_constructor = njs_string("constructor"); | |
+ | |
+ ret = njs_value_property(vm, object, njs_value_arg(&string_constructor), | |
+ &constructor); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ goto done_default; | |
+ } | |
+ | |
+ if (!njs_is_function(&constructor)) { | |
+ njs_type_error(vm, "the object does not contain a constructor"); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ /* TODO: Let S be ? Get(C, @@species). */ | |
+ goto done_default; | |
+ | |
+ njs_type_error(vm, "get species constructor"); | |
+ | |
+ return NJS_ERROR; | |
+ | |
+done_default: | |
+ | |
+ *dst = *default_constructor; | |
+ | |
+ 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_capability_t *capability; | |
+ | |
+ capability = vm->top_frame->function->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_function_t * | |
+njs_promise_create_resolve_function(njs_vm_t *vm, njs_promise_t *promise) | |
+{ | |
+ njs_function_t *function; | |
+ | |
+ function = njs_promise_create_function(vm); | |
+ if (njs_slow_path(function == NULL)) { | |
+ return NULL; | |
+ } | |
+ | |
+ function->u.native = njs_promise_resolve_function; | |
+ function->resolved_ref = &function->resolved; | |
+ function->args_count = 1; | |
+ | |
+ njs_set_promise(&function->promise, promise); | |
+ | |
+ return function; | |
+} | |
+ | |
+ | |
+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; | |
+ | |
+ static const njs_value_t string_then = njs_string("then"); | |
+ | |
+ active_frame = (njs_frame_t *) vm->top_frame; | |
+ function = active_frame->native.function; | |
+ promise = njs_promise(&function->promise); | |
+ | |
+ if (*function->resolved_ref) { | |
+ njs_vm_retval_set(vm, &njs_value_undefined); | |
+ return NJS_OK; | |
+ } | |
+ | |
+ *function->resolved_ref = 1; | |
+ | |
+ resolution = njs_arg(args, nargs, 1); | |
+ | |
+ if (njs_values_same(resolution, &function->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))) { | |
+ 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] = function->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); | |
+ 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_function_t * | |
+njs_promise_create_reject_function(njs_vm_t *vm, njs_promise_t *promise) | |
+{ | |
+ njs_function_t *function; | |
+ | |
+ function = njs_promise_create_function(vm); | |
+ if (njs_slow_path(function == NULL)) { | |
+ return NULL; | |
+ } | |
+ | |
+ function->u.native = njs_promise_reject_function; | |
+ function->resolved_ref = &function->resolved; | |
+ function->args_count = 1; | |
+ | |
+ njs_set_promise(&function->promise, promise); | |
+ | |
+ return function; | |
+} | |
+ | |
+ | |
+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_function_t *function; | |
+ | |
+ active_frame = (njs_frame_t *) vm->top_frame; | |
+ function = active_frame->native.function; | |
+ | |
+ if (*function->resolved_ref) { | |
+ njs_vm_retval_set(vm, &njs_value_undefined); | |
+ return NJS_OK; | |
+ } | |
+ | |
+ *function->resolved_ref = 1; | |
+ | |
+ value = njs_promise_reject(vm, njs_promise(&function->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_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; | |
+ } | |
+ | |
+ /* TODO: need optimization */ | |
+ function = njs_promise_create_function(vm); | |
+ function->u.native = njs_promise_constructor; | |
+ | |
+ njs_set_function(&constructor, function); | |
+ | |
+ ret = njs_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); | |
+} | |
+ | |
+ | |
+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; | |
+ | |
+ 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_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->constructor = constructor; | |
+ function->finally = *finally; | |
+ function->args_count = 1; | |
+ | |
+ 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->constructor = constructor; | |
+ function->finally = *finally; | |
+ function->args_count = 1; | |
+ | |
+ 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_function_t *function; | |
+ | |
+ frame = (njs_frame_t *) vm->top_frame; | |
+ function = frame->native.function; | |
+ | |
+ ret = njs_function_call(vm, njs_function(&function->finally), | |
+ &njs_value_undefined, args, 0, &retval); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ promise = njs_promise_resolve(vm, &function->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))) { | |
+ 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]; | |
+ njs_function_t *resolve, *reject; | |
+ | |
+ promise = njs_arg(args, nargs, 1); | |
+ | |
+ resolve = njs_promise_create_resolve_function(vm, njs_promise(promise)); | |
+ if (njs_slow_path(resolve == NULL)) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ reject = njs_promise_create_reject_function(vm, njs_promise(promise)); | |
+ if (njs_slow_path(reject == NULL)) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ reject->resolved_ref = &resolve->resolved; | |
+ | |
+ njs_set_function(&arguments[0], resolve); | |
+ njs_set_function(&arguments[1], reject); | |
+ | |
+ 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))) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ ret = njs_function_call(vm, reject, &njs_value_undefined, &vm->retval, | |
+ 1, &vm->retval); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return NJS_ERROR; | |
+ } | |
+ } | |
+ | |
+ 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, | |
+ }, | |
+}; | |
+ | |
+ | |
+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 01c7375c9b5c -r c06acccab6a1 src/njs_promise.h | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/src/njs_promise.h Fri Nov 29 18:14:56 2019 +0300 | |
@@ -0,0 +1,19 @@ | |
+ | |
+/* | |
+ * Copyright (C) Igor Sysoev | |
+ * 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 01c7375c9b5c -r c06acccab6a1 src/njs_shell.c | |
--- a/src/njs_shell.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_shell.c Fri Nov 29 18:14:56 2019 +0300 | |
@@ -88,6 +88,12 @@ static njs_int_t njs_process_script(njs_ | |
#ifndef NJS_FUZZER_TARGET | |
+njs_host_event_t njs_console_set_timer(njs_external_ptr_t external, | |
+ uint64_t delay, njs_vm_event_t vm_event); | |
+ | |
+void njs_console_clear_timer(njs_external_ptr_t external, | |
+ njs_host_event_t event); | |
+ | |
static njs_int_t njs_get_options(njs_opts_t *opts, int argc, char **argv); | |
static njs_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options); | |
static njs_int_t njs_interactive_shell(njs_opts_t *opts, | |
@@ -106,11 +112,6 @@ static njs_int_t njs_ext_console_time(nj | |
static njs_int_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, | |
njs_uint_t nargs, njs_index_t unused); | |
-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); | |
- | |
static njs_int_t njs_timelabel_hash_test(njs_lvlhsh_query_t *lhq, void *data); | |
static njs_int_t lvlhsh_key_test(njs_lvlhsh_query_t *lhq, void *data); | |
@@ -590,7 +591,7 @@ njs_console_init(njs_vm_t *vm, njs_conso | |
console->vm = vm; | |
njs_lvlhsh_init(&console->events); | |
- njs_queue_init(&console->posted_events); | |
+ njs_queue_init(&console->posted_events);; | |
njs_lvlhsh_init(&console->labels); | |
@@ -1252,7 +1253,7 @@ njs_ext_console_time_end(njs_vm_t *vm, n | |
} | |
-static njs_host_event_t | |
+njs_host_event_t | |
njs_console_set_timer(njs_external_ptr_t external, uint64_t delay, | |
njs_vm_event_t vm_event) | |
{ | |
@@ -1297,7 +1298,7 @@ njs_console_set_timer(njs_external_ptr_t | |
} | |
-static void | |
+void | |
njs_console_clear_timer(njs_external_ptr_t external, njs_host_event_t event) | |
{ | |
njs_vm_t *vm; | |
diff -r 01c7375c9b5c -r c06acccab6a1 src/njs_value.c | |
--- a/src/njs_value.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_value.c Fri Nov 29 18:14:56 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; | |
@@ -1251,6 +1255,7 @@ njs_value_to_object(njs_vm_t *vm, njs_va | |
return NJS_ERROR; | |
} | |
+ | |
void | |
njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string) | |
{ | |
diff -r 01c7375c9b5c -r c06acccab6a1 src/njs_value.h | |
--- a/src/njs_value.h Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_value.h Fri Nov 29 18:14:56 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,13 @@ struct njs_function_s { | |
njs_function_t *bound_target; | |
} u; | |
+ njs_value_t promise; | |
+ njs_value_t finally; | |
+ njs_value_t constructor; | |
+ njs_bool_t resolved; | |
+ njs_bool_t *resolved_ref; | |
+ void *capability; | |
+ | |
njs_value_t *bound; | |
#if (NJS_SUNC) | |
njs_closure_t *closures[1]; | |
@@ -285,6 +295,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 +615,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 +679,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 +841,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 01c7375c9b5c -r c06acccab6a1 src/njs_vm.c | |
--- a/src/njs_vm.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_vm.c Fri Nov 29 18:14:56 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; | |
} | |
@@ -375,7 +376,7 @@ njs_vm_scopes_restore(njs_vm_t *vm, njs_ | |
nesting = (function != NULL) ? function->u.lambda->nesting : 0; | |
for (n = 0; n <= nesting; n++) { | |
- vm->scopes[NJS_SCOPE_CLOSURE + n] = &frame->closures[n]->u.values; | |
+ vm->scopes[NJS_SCOPE_CLOSURE + n] = &frame->closures[n]->u.values; | |
} | |
while (n < NJS_MAX_NESTING) { | |
@@ -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 01c7375c9b5c -r c06acccab6a1 src/njs_vm.h | |
--- a/src/njs_vm.h Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_vm.h Fri Nov 29 18:14:56 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, | |
@@ -123,7 +124,6 @@ enum njs_object_e { | |
#define NJS_OBJECT_MAX (NJS_OBJECT_JSON + 1) | |
}; | |
- | |
#define njs_scope_index(value, type) \ | |
((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | (type))) | |
@@ -186,6 +186,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 01c7375c9b5c -r c06acccab6a1 src/njs_vmcode.c | |
--- a/src/njs_vmcode.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/njs_vmcode.c Fri Nov 29 18:14:56 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 01c7375c9b5c -r c06acccab6a1 src/test/njs_interactive_test.c | |
--- a/src/test/njs_interactive_test.c Fri Nov 29 12:53:33 2019 +0300 | |
+++ b/src/test/njs_interactive_test.c Fri Nov 29 18:14:56 2019 +0300 | |
@@ -267,7 +267,6 @@ static njs_interactive_test_t njs_test[ | |
njs_str("Error\n" | |
" at anonymous (:2)\n" | |
" at main (native)\n") }, | |
- | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment