]> git.kaiwu.me - njs.git/commitdiff
Introduced AggregateError implementation.
authorAlexander Borisov <alexander.borisov@nginx.com>
Wed, 11 Aug 2021 18:48:51 +0000 (21:48 +0300)
committerAlexander Borisov <alexander.borisov@nginx.com>
Wed, 11 Aug 2021 18:48:51 +0000 (21:48 +0300)
src/njs_builtin.c
src/njs_error.c
src/njs_error.h
src/njs_fs.c
src/njs_iterator.c
src/njs_iterator.h
src/njs_object_hash.h
src/njs_vm.h
src/test/njs_unit_test.c

index eee44c6a42b97dbed2eaecf30e9133f781fdb05e..b873f066258161a3f2415af004eda7e9adf59383 100644 (file)
@@ -114,6 +114,7 @@ static const njs_object_type_init_t *const
     &njs_type_error_type_init,
     &njs_uri_error_type_init,
     &njs_memory_error_type_init,
+    &njs_aggregate_error_type_init,
 };
 
 
@@ -1676,6 +1677,16 @@ static const njs_object_prop_t  njs_global_this_object_properties[] =
         .writable = 1,
         .configurable = 1,
     },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("AggregateError"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_AGGREGATE_ERROR,
+                                   NJS_AGGREGATE_ERROR_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
 };
 
 
index 0fe92c250c9854f8c3675dee202a382d194347e4..518836bf64a933f07ec5c7ade59221c50455a667 100644 (file)
@@ -24,6 +24,7 @@ static njs_int_t njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace,
 static const njs_value_t  njs_error_message_string = njs_string("message");
 static const njs_value_t  njs_error_name_string = njs_string("name");
 static const njs_value_t  njs_error_stack_string = njs_string("stack");
+static const njs_value_t  njs_error_errors_string = njs_string("errors");
 
 
 void
@@ -45,7 +46,7 @@ njs_error_new(njs_vm_t *vm, njs_value_t *dst, njs_object_type_t type,
         return;
     }
 
-    error = njs_error_alloc(vm, type, NULL, &string);
+    error = njs_error_alloc(vm, type, NULL, &string, NULL);
     if (njs_slow_path(error == NULL)) {
         return;
     }
@@ -198,7 +199,7 @@ njs_error_stack(njs_vm_t *vm, njs_value_t *value, njs_value_t *stack)
 
 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)
+    const njs_value_t *message, const njs_value_t *errors)
 {
     njs_int_t           ret;
     njs_object_t        *error;
@@ -262,6 +263,26 @@ njs_error_alloc(njs_vm_t *vm, njs_object_type_t type, const njs_value_t *name,
         }
     }
 
+    if (errors != NULL) {
+        lhq.key = njs_str_value("errors");
+        lhq.key_hash = NJS_ERRORS_HASH;
+
+        prop = njs_object_prop_alloc(vm, &njs_error_errors_string, errors, 1);
+        if (njs_slow_path(prop == NULL)) {
+            goto memory_error;
+        }
+
+        prop->enumerable = 0;
+
+        lhq.value = prop;
+
+        ret = njs_lvlhsh_insert(&error->hash, &lhq);
+        if (njs_slow_path(ret != NJS_OK)) {
+            njs_internal_error(vm, "lvlhsh insert failed");
+            return NULL;
+        }
+    }
+
     return error;
 
 memory_error:
@@ -277,10 +298,32 @@ njs_error_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t type)
 {
     njs_int_t     ret;
-    njs_value_t   *value;
+    njs_value_t   *iterator, *value, list;
+    njs_array_t   *array;
     njs_object_t  *error;
 
-    value = njs_arg(args, nargs, 1);
+    if (type != NJS_OBJ_TYPE_AGGREGATE_ERROR) {
+        iterator = NULL;
+        value = njs_arg(args, nargs, 1);
+
+        njs_set_undefined(&list);
+
+    } else {
+        iterator = njs_arg(args, nargs, 1);
+        value = njs_arg(args, nargs, 2);
+
+        if (njs_slow_path(iterator->type < NJS_STRING)) {
+            njs_type_error(vm, "first argument is not iterable");
+            return NJS_ERROR;
+        }
+
+        array = njs_iterator_to_array(vm, iterator);
+        if (njs_slow_path(array == NULL)) {
+            return NJS_ERROR;
+        }
+
+        njs_set_array(&list, array);
+    }
 
     if (njs_slow_path(!njs_is_string(value))) {
         if (!njs_is_undefined(value)) {
@@ -292,7 +335,8 @@ njs_error_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     error = njs_error_alloc(vm, type, NULL,
-                            njs_is_defined(value) ? value : NULL);
+                            njs_is_defined(value) ? value : NULL,
+                            njs_is_defined(&list) ? &list : NULL);
     if (njs_slow_path(error == NULL)) {
         return NJS_ERROR;
     }
@@ -543,6 +587,36 @@ const njs_object_init_t  njs_uri_error_constructor_init = {
 };
 
 
+static const njs_object_prop_t  njs_aggregate_error_constructor_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("AggregateError"),
+        .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),
+    },
+};
+
+
+const njs_object_init_t  njs_aggregate_error_constructor_init = {
+    njs_aggregate_error_constructor_properties,
+    njs_nitems(njs_aggregate_error_constructor_properties),
+};
+
+
 void
 njs_memory_error_set(njs_vm_t *vm, njs_value_t *value)
 {
@@ -1164,6 +1238,49 @@ const njs_object_type_init_t  njs_uri_error_type_init = {
 };
 
 
+static const njs_object_prop_t  njs_aggregate_error_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("AggregateError"),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("message"),
+        .value = njs_string(""),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+};
+
+
+const njs_object_init_t  njs_aggregate_error_prototype_init = {
+    njs_aggregate_error_prototype_properties,
+    njs_nitems(njs_aggregate_error_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_aggregate_error_type_init = {
+    .constructor = njs_native_ctor(njs_error_constructor, 1,
+                                   NJS_OBJ_TYPE_AGGREGATE_ERROR),
+    .constructor_props = &njs_aggregate_error_constructor_init,
+    .prototype_props = &njs_aggregate_error_prototype_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
 static njs_int_t
 njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
     njs_native_frame_t *native_frame)
index 0cfd687fbedde25897152169cb7ebf4884723d29..c0a42dc59f3d53d0c3431cd8f2cd2fec17411328 100644 (file)
@@ -41,7 +41,8 @@ void njs_memory_error(njs_vm_t *vm);
 void njs_memory_error_set(njs_vm_t *vm, njs_value_t *value);
 
 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);
+    const njs_value_t *name, const njs_value_t *message,
+    const njs_value_t *errors);
 njs_int_t njs_error_to_string(njs_vm_t *vm, njs_value_t *retval,
     const njs_value_t *error);
 njs_int_t njs_error_stack(njs_vm_t *vm, njs_value_t *value, njs_value_t *stack);
@@ -57,6 +58,7 @@ extern const njs_object_type_init_t  njs_syntax_error_type_init;
 extern const njs_object_type_init_t  njs_type_error_type_init;
 extern const njs_object_type_init_t  njs_uri_error_type_init;
 extern const njs_object_type_init_t  njs_memory_error_type_init;
+extern const njs_object_type_init_t  njs_aggregate_error_type_init;
 
 
 njs_inline njs_int_t
index 9f0447c2c7bff1cf4c71f1576a9b0baa0872c6ea..8ed51852893b25e52ad21f8054145d803cde7c44 100644 (file)
@@ -1491,7 +1491,7 @@ njs_fs_error(njs_vm_t *vm, const char *syscall, const char *description,
         return NJS_ERROR;
     }
 
-    error = njs_error_alloc(vm, NJS_OBJ_TYPE_ERROR, NULL, &value);
+    error = njs_error_alloc(vm, NJS_OBJ_TYPE_ERROR, NULL, &value, NULL);
     if (njs_slow_path(error == NULL)) {
         return NJS_ERROR;
     }
index 4f85d48783ee4bceee84265d6e2936a43ded0423..954f5eba8603d0f232955b105b7a3038426f40db 100644 (file)
@@ -26,6 +26,9 @@ static njs_int_t njs_iterator_object_handler(njs_vm_t *vm,
     njs_iterator_handler_t handler, njs_iterator_args_t *args,
     njs_value_t *key, int64_t i);
 
+static njs_int_t njs_iterator_to_array_handler(njs_vm_t *vm,
+    njs_iterator_args_t *args, njs_value_t *value, int64_t index);
+
 
 njs_int_t
 njs_array_iterator_create(njs_vm_t *vm, const njs_value_t *target,
@@ -676,3 +679,45 @@ njs_iterator_object_handler(njs_vm_t *vm, njs_iterator_handler_t handler,
 
     return ret;
 }
+
+
+njs_array_t *
+njs_iterator_to_array(njs_vm_t *vm, njs_value_t *iterator)
+{
+    int64_t              length;
+    njs_int_t            ret;
+    njs_iterator_args_t  args;
+
+    njs_memzero(&args, sizeof(njs_iterator_args_t));
+
+    ret = njs_object_length(vm, iterator, &length);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NULL;
+    }
+
+    args.array = njs_array_alloc(vm, 1, length, 0);
+    if (njs_slow_path(args.array == NULL)) {
+        return NULL;
+    }
+
+    args.value = iterator;
+    args.to = length;
+
+    ret = njs_object_iterate(vm, &args, njs_iterator_to_array_handler);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        njs_mp_free(vm->mem_pool, args.array);
+        return NULL;
+    }
+
+    return args.array;
+}
+
+
+static njs_int_t
+njs_iterator_to_array_handler(njs_vm_t *vm, njs_iterator_args_t *args,
+    njs_value_t *value, int64_t index)
+{
+    args->array->start[index] = *value;
+
+    return NJS_OK;
+}
index d561b348e1f743464586038c75075f3019018ac6..b5d4b8ec93b01cd2dbd95fa97928141d73b0161a 100644 (file)
@@ -36,6 +36,8 @@ njs_int_t njs_object_iterate(njs_vm_t *vm, njs_iterator_args_t *args,
 njs_int_t njs_object_iterate_reverse(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_iterator_handler_t handler);
 
+njs_array_t *njs_iterator_to_array(njs_vm_t *vm, njs_value_t *iterator);
+
 
 extern const njs_object_type_init_t  njs_iterator_type_init;
 extern const njs_object_type_init_t  njs_array_iterator_type_init;
index a5cc0210950067ce6e0bf99f6864fa87dcec6689..163c814ca1875f002c7c755b20b7a4dbbf5c17ab 100644 (file)
         'E'), 'r'), 'r'), 'o'), 'r')
 
 
+#define NJS_AGGREGATE_ERROR_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_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,                                       \
+        'A'), 'g'), 'g'), 'r'), 'e'), 'g'), 'a'), 't'), 'e'),                 \
+        'E'), 'r'), 'r'), 'o'), 'r')
+
+
 #define NJS_MESSAGE_HASH                                                      \
     njs_djb_hash_add(                                                         \
     njs_djb_hash_add(                                                         \
         'm'), 'e'), 's'), 's'), 'a'), 'g'), 'e')
 
 
+#define NJS_ERRORS_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_INIT,                                       \
+        'e'), 'r'), 'r'), 'o'), 'r'), 's')
+
+
 #define NJS_MODE_HASH                                                         \
     njs_djb_hash_add(                                                         \
     njs_djb_hash_add(                                                         \
index 5883b5d4e6d8fc031d541e787956026583f26f61..1539a285015022b51a37719349fcad36abedbef4 100644 (file)
@@ -86,6 +86,7 @@ typedef enum {
     NJS_OBJ_TYPE_TYPE_ERROR,
     NJS_OBJ_TYPE_URI_ERROR,
     NJS_OBJ_TYPE_MEMORY_ERROR,
+    NJS_OBJ_TYPE_AGGREGATE_ERROR,
 
     NJS_OBJ_TYPE_MAX,
 } njs_object_type_t;
index 7fc215d74596ebc59b9fc64db0adbccc4a090c41..66c80022dbecdb28faa030e3ea97aaf2af0f8724 100644 (file)
@@ -11134,6 +11134,26 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("var e = RangeError('e'); Object.preventExtensions(e);e"),
       njs_str("RangeError: e") },
 
+    /* AggregateError. */
+
+    { njs_str("AggregateError()"),
+      njs_str("TypeError: first argument is not iterable") },
+
+    { njs_str("AggregateError([1, 2, 3])"),
+      njs_str("AggregateError") },
+
+    { njs_str("let e = AggregateError([1, 2, 3], 'm'); e.message"),
+      njs_str("m") },
+
+    { njs_str("let e = AggregateError([1, 2, 3], 'm'); e.errors"),
+      njs_str("1,2,3") },
+
+    { njs_str("let e = AggregateError('abc'); e.errors"),
+      njs_str("a,b,c") },
+
+    { njs_str("let e = AggregateError([1, 2, 3], 'm'); e"),
+      njs_str("AggregateError: m") },
+
     /* Memory object is immutable. */
 
     { njs_str("var e = MemoryError('e'); e.name = 'E'"),