]> git.kaiwu.me - njs.git/commitdiff
Added initial version of Symbol primitive type.
authorArtem S. Povalyukhin <artem.povaluhin@gmail.com>
Tue, 19 Nov 2019 17:45:22 +0000 (20:45 +0300)
committerArtem S. Povalyukhin <artem.povaluhin@gmail.com>
Tue, 19 Nov 2019 17:45:22 +0000 (20:45 +0300)
This closes #249 issue on Github.

15 files changed:
auto/sources
src/njs_builtin.c
src/njs_json.c
src/njs_main.h
src/njs_object.c
src/njs_object_hash.h
src/njs_string.c
src/njs_symbol.c [new file with mode: 0644]
src/njs_symbol.h [new file with mode: 0644]
src/njs_value.c
src/njs_value.h
src/njs_vm.c
src/njs_vm.h
src/njs_vmcode.c
src/test/njs_unit_test.c

index 175400d0e60189b48ff160c62dbed646fe8c1930..675e45c63e45bb4215b389302cf71dda6bdff334 100644 (file)
@@ -25,6 +25,7 @@ NJS_LIB_SRCS=" \
    src/njs_vmcode.c \
    src/njs_boolean.c \
    src/njs_number.c \
+   src/njs_symbol.c \
    src/njs_string.c \
    src/njs_object.c \
    src/njs_object_prop.c \
index a07c7f89e8c53ea8bbcd75c0a6c14d944ac34518..5d11f3d07a318c3f3ba69a34d744dbf02fe5a71d 100644 (file)
@@ -60,6 +60,7 @@ static const njs_object_type_init_t *const
     &njs_array_type_init,
     &njs_boolean_type_init,
     &njs_number_type_init,
+    &njs_symbol_type_init,
     &njs_string_type_init,
     &njs_function_type_init,
     &njs_regexp_type_init,
@@ -1167,6 +1168,15 @@ static const njs_object_prop_t  njs_global_this_object_properties[] =
         .configurable = 1,
     },
 
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("Symbol"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_SYMBOL, NJS_SYMBOL_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
     {
         .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("String"),
index 3f948f420006fff84d4bd28248d974b0345d7d80..2314267301b83ff21368a838b85acecd371b50cf 100644 (file)
@@ -1933,6 +1933,33 @@ njs_dump_value(njs_json_stringify_t *stringify, const njs_value_t *value,
 
         break;
 
+    case NJS_OBJECT_SYMBOL:
+        value = njs_object_value(value);
+
+        ret = njs_symbol_to_string(stringify->vm, &str_val, value);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        njs_string_get(&str_val, &str);
+
+        njs_dump("[Symbol: ");
+        njs_json_buf_append(stringify, (char *) str.start, str.length);
+        njs_dump("]");
+
+        break;
+
+    case NJS_SYMBOL:
+        ret = njs_symbol_to_string(stringify->vm, &str_val, value);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        njs_string_get(&str_val, &str);
+        njs_json_buf_append(stringify, (char *) str.start, str.length);
+
+        break;
+
     case NJS_OBJECT_NUMBER:
         value = njs_object_value(value);
 
index 7ecb8b7dc3e168f642f6fdda966b47352d71ac0b..4b9c3d170b60fc2c2f655a781161d0f5317706ec 100644 (file)
@@ -52,6 +52,7 @@
 
 #include <njs_boolean.h>
 #include <njs_number.h>
+#include <njs_symbol.h>
 #include <njs_string.h>
 #include <njs_object.h>
 #include <njs_object_hash.h>
index 864298f7ea67acc1f677252e6995cb86fce17ff4..2a5a87d036af8012b3a8f90ac7c601f14d671855 100644 (file)
@@ -1387,7 +1387,7 @@ static njs_int_t
 njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    uint32_t     index;
+    uint32_t     index, type;
     njs_value_t  *value;
 
     value = njs_arg(args, nargs, 1);
@@ -1399,9 +1399,10 @@ njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     if (!njs_is_null_or_undefined(value)) {
         index = njs_primitive_prototype_index(value->type);
+        type = njs_is_symbol(value) ? NJS_OBJECT
+                                    : njs_object_value_type(value->type);
 
-        njs_set_type_object(&vm->retval, &vm->prototypes[index].object,
-                            njs_object_value_type(value->type));
+        njs_set_type_object(&vm->retval, &vm->prototypes[index].object, type);
 
         return NJS_OK;
     }
@@ -2189,6 +2190,8 @@ static const njs_value_t  njs_object_boolean_string =
                                      njs_long_string("[object Boolean]");
 static const njs_value_t  njs_object_number_string =
                                      njs_long_string("[object Number]");
+static const njs_value_t  njs_object_symbol_string =
+                                     njs_long_string("[object Symbol]");
 static const njs_value_t  njs_object_string_string =
                                      njs_long_string("[object String]");
 static const njs_value_t  njs_object_data_string =
@@ -2220,6 +2223,7 @@ njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
         &njs_object_undefined_string,
         &njs_object_boolean_string,
         &njs_object_number_string,
+        &njs_object_symbol_string,
         &njs_object_string_string,
 
         &njs_object_data_string,
@@ -2232,13 +2236,13 @@ njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
         NULL,
         NULL,
         NULL,
-        NULL,
 
         /* Objects. */
         &njs_object_object_string,
         &njs_object_array_string,
         &njs_object_boolean_string,
         &njs_object_number_string,
+        &njs_object_symbol_string,
         &njs_object_string_string,
         &njs_object_function_string,
         &njs_object_regexp_string,
index 98e41a51c8b1225bb9f398038421369972b75c4b..e5df7a3549e31e876f699bff56abf222a8785450 100644 (file)
         'S'), 't'), 'r'), 'i'), 'n'), 'g')
 
 
+#define NJS_SYMBOL_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,                                       \
+        'S'), 'y'), 'm'), 'b'), 'o'), 'l')
+
+
 #define NJS_SYNTAX_ERROR_HASH                                                 \
     njs_djb_hash_add(                                                         \
     njs_djb_hash_add(                                                         \
index 698c3450d0b49794f73cec348e8a7e1d2b48bbf2..c13e1bb05a296b16c9d9e4ab332d40b3a7aae396 100644 (file)
@@ -552,6 +552,10 @@ njs_string_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         value = &args[1];
 
         if (njs_slow_path(!njs_is_string(value))) {
+            if (!vm->top_frame->ctor && njs_is_symbol(value)) {
+                return njs_symbol_to_string(vm, &vm->retval, value);
+            }
+
             ret = njs_value_to_string(vm, value, value);
             if (njs_slow_path(ret != NJS_OK)) {
                 return ret;
@@ -4273,6 +4277,10 @@ njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
     case NJS_NUMBER:
         return njs_number_to_string(vm, dst, src);
 
+    case NJS_SYMBOL:
+        njs_symbol_conversion_failed(vm, 1);
+        return NJS_ERROR;
+
     case NJS_STRING:
         /* GC: njs_retain(src); */
         value = src;
diff --git a/src/njs_symbol.c b/src/njs_symbol.c
new file mode 100644 (file)
index 0000000..015f63c
--- /dev/null
@@ -0,0 +1,441 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+static const njs_value_t  njs_symbol_async_iterator_name =
+                            njs_long_string("Symbol.asyncIterator");
+static const njs_value_t  njs_symbol_has_instance_name =
+                            njs_long_string("Symbol.hasInstance");
+static const njs_value_t  njs_symbol_is_concat_spreadable_name =
+                            njs_long_string("Symbol.isConcatSpreadable");
+static const njs_value_t  njs_symbol_iterator_name =
+                            njs_long_string("Symbol.iterator");
+static const njs_value_t  njs_symbol_match_name =
+                            njs_string("Symbol.match");
+static const njs_value_t  njs_symbol_match_all_name =
+                            njs_long_string("Symbol.matchAll");
+static const njs_value_t  njs_symbol_replace_name =
+                            njs_string("Symbol.replace");
+static const njs_value_t  njs_symbol_search_name =
+                            njs_string("Symbol.search");
+static const njs_value_t  njs_symbol_species_name =
+                            njs_string("Symbol.species");
+static const njs_value_t  njs_symbol_split_name =
+                            njs_string("Symbol.split");
+static const njs_value_t  njs_symbol_to_primitive_name =
+                            njs_long_string("Symbol.toPrimitive");
+static const njs_value_t  njs_symbol_to_string_tag_name =
+                            njs_long_string("Symbol.toStringTag");
+static const njs_value_t  njs_symbol_unscopables_name =
+                            njs_long_string("Symbol.unscopables");
+
+
+static const njs_value_t  *njs_symbol_names[NJS_SYMBOL_KNOWN_MAX] = {
+    &njs_string_invalid,
+    &njs_symbol_async_iterator_name,
+    &njs_symbol_has_instance_name,
+    &njs_symbol_is_concat_spreadable_name,
+    &njs_symbol_iterator_name,
+    &njs_symbol_match_name,
+    &njs_symbol_match_all_name,
+    &njs_symbol_replace_name,
+    &njs_symbol_search_name,
+    &njs_symbol_species_name,
+    &njs_symbol_split_name,
+    &njs_symbol_to_primitive_name,
+    &njs_symbol_to_string_tag_name,
+    &njs_symbol_unscopables_name,
+};
+
+
+njs_int_t
+njs_symbol_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *value)
+{
+    u_char             *start;
+    const njs_value_t  *name;
+    njs_string_prop_t  string;
+
+    static const njs_value_t  string_symbol = njs_string("Symbol()");
+
+    name = value->data.u.value;
+
+    if (name == NULL) {
+        if (njs_fast_path(njs_symbol_key(value) < NJS_SYMBOL_KNOWN_MAX)) {
+
+            name = njs_symbol_names[njs_symbol_key(value)];
+
+        } else {
+            *dst = string_symbol;
+
+            return NJS_OK;
+        }
+    }
+
+    (void) njs_string_prop(&string, name);
+    string.length += njs_length("Symbol()");
+
+    start = njs_string_alloc(vm, dst, string.size + 8, string.length);
+    if (njs_slow_path(start == NULL)) {
+        return NJS_ERROR;
+    }
+
+    start = njs_cpymem(start, "Symbol(", 7);
+    start = njs_cpymem(start, string.start, string.size);
+    *start = ')';
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_symbol_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
+{
+    njs_int_t    ret;
+    njs_value_t  *value, *name;
+    uint64_t     key;
+
+    if (njs_slow_path(vm->top_frame->ctor)) {
+        njs_type_error(vm, "Symbol is not a constructor");
+        return NJS_ERROR;
+    }
+
+    value = njs_arg(args, nargs, 1);
+
+    if (njs_is_undefined(value)) {
+        name = NULL;
+
+    } else {
+        if (njs_slow_path(!njs_is_string(value))) {
+            ret = njs_value_to_string(vm, value, value);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+        }
+
+        name = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+        if (njs_slow_path(name == NULL)) {
+            njs_memory_error(vm);
+            return NJS_ERROR;
+        }
+
+        /* GC: retain */
+        *name = *value;
+    }
+
+    key = ++vm->symbol_generator;
+
+    if (njs_slow_path(key >= UINT32_MAX)) {
+        njs_internal_error(vm, "Symbol generator overflow");
+        return NJS_ERROR;
+    }
+
+    vm->retval.type = NJS_SYMBOL;
+    vm->retval.data.truth = 1;
+    vm->retval.data.magic32 = key;
+    vm->retval.data.u.value = name;
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_symbol_for(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_internal_error(vm, "not implemented");
+
+    return NJS_ERROR;
+}
+
+
+static njs_int_t
+njs_symbol_key_for(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_internal_error(vm, "not implemented");
+
+    return NJS_ERROR;
+}
+
+
+static const njs_object_prop_t  njs_symbol_constructor_properties[] =
+{
+    /* Symbol.name == "Symbol". */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Symbol"),
+        .configurable = 1,
+    },
+
+    /* Symbol.length == 0. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 0, 0.0),
+        .configurable = 1,
+    },
+
+    /* Symbol.prototype. */
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    /* Symbol.for(). */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("for"),
+        .value = njs_native_function(njs_symbol_for, 1),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    /* Symbol.keyFor(). */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("keyFor"),
+        .value = njs_native_function(njs_symbol_key_for, 1),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    /* Symbol.asyncIterator. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("asyncIterator"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_ASYNC_ITERATOR),
+    },
+
+    /* Symbol.hasInstance. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("hasInstance"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_HAS_INSTANCE),
+    },
+
+    /* Symbol.isConcatSpreadable. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("isConcatSpreadable"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_IS_CONCAT_SPREADABLE),
+    },
+
+    /* Symbol.iterator. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("iterator"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR),
+    },
+
+    /* Symbol.match. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("match"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_MATCH),
+    },
+
+    /* Symbol.matchAll. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("matchAll"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_MATCH_ALL),
+    },
+
+    /* Symbol.replace. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("replace"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_REPLACE),
+    },
+
+    /* Symbol.search. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("search"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_SEARCH),
+    },
+
+    /* Symbol.species. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("species"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_SPECIES),
+    },
+
+    /* Symbol.split. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("split"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_SPLIT),
+    },
+
+    /* Symbol.toPrimitive. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("toPrimitive"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_TO_PRIMITIVE),
+    },
+
+    /* Symbol.toStringTag. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("toStringTag"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+    },
+
+    /* Symbol.unscopables. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("unscopables"),
+        .value = njs_wellknown_symbol(NJS_SYMBOL_UNSCOPABLES),
+    },
+
+};
+
+
+const njs_object_init_t  njs_symbol_constructor_init = {
+    njs_symbol_constructor_properties,
+    njs_nitems(njs_symbol_constructor_properties),
+};
+
+
+static njs_int_t
+njs_symbol_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
+{
+    njs_value_t  *value;
+
+    value = &args[0];
+
+    if (value->type != NJS_SYMBOL) {
+
+        if (value->type == NJS_OBJECT_SYMBOL) {
+            value = njs_object_value(value);
+
+        } else {
+            njs_type_error(vm, "unexpected value type:%s",
+                           njs_type_string(value->type));
+
+            return NJS_ERROR;
+        }
+    }
+
+    vm->retval = *value;
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_symbol_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_int_t  ret;
+
+    ret = njs_symbol_prototype_value_of(vm, args, nargs, unused);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return njs_symbol_to_string(vm, &vm->retval, &vm->retval);
+}
+
+
+static njs_int_t
+njs_symbol_prototype_description(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_int_t          ret;
+    const njs_value_t  *value, *name;
+
+    ret = njs_symbol_prototype_value_of(vm, args, nargs, unused);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    value = &vm->retval;
+
+    name = value->data.u.value;
+
+    if (name == NULL) {
+        if (njs_fast_path(njs_symbol_key(value) < NJS_SYMBOL_KNOWN_MAX)) {
+            name = njs_symbol_names[njs_symbol_key(value)];
+
+        } else {
+            name = &njs_value_undefined;
+        }
+    }
+
+    vm->retval = *name;
+
+    return NJS_OK;
+}
+
+
+static const njs_object_prop_t  njs_symbol_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("__proto__"),
+        .value = njs_prop_handler(njs_primitive_prototype_get_proto),
+        .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,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("valueOf"),
+        .value = njs_native_function(njs_symbol_prototype_value_of, 0),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("toString"),
+        .value = njs_native_function(njs_symbol_prototype_to_string, 0),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("description"),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_symbol_prototype_description, 0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+    },
+};
+
+
+const njs_object_init_t  njs_symbol_prototype_init = {
+    njs_symbol_prototype_properties,
+    njs_nitems(njs_symbol_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_symbol_type_init = {
+   .constructor = njs_symbol_constructor,
+   .prototype_props = &njs_symbol_prototype_init,
+   .constructor_props = &njs_symbol_constructor_init,
+   .value = { .object = { .type = NJS_OBJECT } },
+};
diff --git a/src/njs_symbol.h b/src/njs_symbol.h
new file mode 100644 (file)
index 0000000..22f60d2
--- /dev/null
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_SYMBOL_H_INCLUDED_
+#define _NJS_SYMBOL_H_INCLUDED_
+
+typedef enum {
+    NJS_SYMBOL_INVALID                  = 0,
+    NJS_SYMBOL_ASYNC_ITERATOR           = 1,
+    NJS_SYMBOL_HAS_INSTANCE             = 2,
+    NJS_SYMBOL_IS_CONCAT_SPREADABLE     = 3,
+    NJS_SYMBOL_ITERATOR                 = 4,
+    NJS_SYMBOL_MATCH                    = 5,
+    NJS_SYMBOL_MATCH_ALL                = 6,
+    NJS_SYMBOL_REPLACE                  = 7,
+    NJS_SYMBOL_SEARCH                   = 8,
+    NJS_SYMBOL_SPECIES                  = 9,
+    NJS_SYMBOL_SPLIT                    = 10,
+    NJS_SYMBOL_TO_PRIMITIVE             = 11,
+    NJS_SYMBOL_TO_STRING_TAG            = 12,
+    NJS_SYMBOL_UNSCOPABLES              = 13,
+#define NJS_SYMBOL_KNOWN_MAX (NJS_SYMBOL_UNSCOPABLES + 1)
+} njs_wellknown_symbol_t;
+
+
+njs_int_t njs_symbol_to_string(njs_vm_t *vm, njs_value_t *dst,
+    const njs_value_t *value);
+
+
+extern const njs_object_type_init_t  njs_symbol_type_init;
+
+
+#endif /* _NJS_SYMBOL_H_INCLUDED_ */
index 3dd425f1cb41541b99399ccb8c47e3d2f5e7fa8e..f102a33b56125c0a0e890a23a251a9ea2bc7cfd6 100644 (file)
@@ -47,6 +47,7 @@ const njs_value_t  njs_string_minus_infinity =
 const njs_value_t  njs_string_plus_infinity =
                                             njs_string("Infinity");
 const njs_value_t  njs_string_nan =         njs_string("NaN");
+const njs_value_t  njs_string_symbol =      njs_string("symbol");
 const njs_value_t  njs_string_string =      njs_string("string");
 const njs_value_t  njs_string_name =        njs_string("name");
 const njs_value_t  njs_string_data =        njs_string("data");
@@ -312,6 +313,9 @@ njs_type_string(njs_value_type_t type)
     case NJS_NUMBER:
         return "number";
 
+    case NJS_SYMBOL:
+        return "symbol";
+
     case NJS_STRING:
         return "string";
 
@@ -333,6 +337,9 @@ njs_type_string(njs_value_type_t type)
     case NJS_OBJECT_NUMBER:
         return "object number";
 
+    case NJS_OBJECT_SYMBOL:
+        return "object symbol";
+
     case NJS_OBJECT_STRING:
         return "object string";
 
@@ -514,6 +521,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
 
     case NJS_BOOLEAN:
     case NJS_NUMBER:
+    case NJS_SYMBOL:
         index = njs_primitive_prototype_index(value->type);
         obj = &vm->prototypes[index].object;
         break;
@@ -534,6 +542,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
     case NJS_ARRAY:
     case NJS_OBJECT_BOOLEAN:
     case NJS_OBJECT_NUMBER:
+    case NJS_OBJECT_SYMBOL:
     case NJS_OBJECT_STRING:
     case NJS_REGEXP:
     case NJS_DATE:
@@ -1206,3 +1215,11 @@ njs_value_to_object(njs_vm_t *vm, njs_value_t *value)
 
     return NJS_ERROR;
 }
+
+void
+njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string)
+{
+    njs_type_error(vm, to_string
+        ? "Cannot convert a Symbol value to a string"
+        : "Cannot convert a Symbol value to a number");
+}
index ed909f3fa8ab57cb29bc39d1d6e393f858608baa..d464b839189c8684097bb0f3d0c50c745fbb3148 100644 (file)
@@ -32,14 +32,16 @@ typedef enum {
      *   a numeric value of the null and false values is zero,
      *   a numeric value of the void value is NaN.
      */
-    NJS_STRING                = 0x04,
+    NJS_SYMBOL                = 0x04,
+
+    NJS_STRING                = 0x05,
 
     /* The order of the above type is used in njs_is_primitive(). */
 
-    NJS_DATA                  = 0x05,
+    NJS_DATA                  = 0x06,
 
     /* The type is external code. */
-    NJS_EXTERNAL              = 0x06,
+    NJS_EXTERNAL              = 0x07,
 
     /*
      * The invalid value type is used:
@@ -47,7 +49,7 @@ typedef enum {
      *   to detect non-declared explicitly or implicitly variables,
      *   for native property getters.
      */
-    NJS_INVALID               = 0x07,
+    NJS_INVALID               = 0x08,
 
     /*
      * The object types are >= NJS_OBJECT, this is used in njs_is_object().
@@ -60,12 +62,13 @@ typedef enum {
     NJS_ARRAY                 = 0x11,
     NJS_OBJECT_BOOLEAN        = 0x12,
     NJS_OBJECT_NUMBER         = 0x13,
-    NJS_OBJECT_STRING         = 0x14,
-    NJS_FUNCTION              = 0x15,
-    NJS_REGEXP                = 0x16,
-    NJS_DATE                  = 0x17,
-    NJS_OBJECT_VALUE          = 0x18,
-#define NJS_VALUE_TYPE_MAX    (NJS_OBJECT_VALUE + 1)
+    NJS_OBJECT_SYMBOL         = 0x14,
+    NJS_OBJECT_STRING         = 0x15,
+    NJS_FUNCTION              = 0x16,
+    NJS_REGEXP                = 0x17,
+    NJS_DATE                  = 0x18,
+    NJS_OBJECT_VALUE          = 0x19,
+    NJS_VALUE_TYPE_MAX
 } njs_value_type_t;
 
 
@@ -367,6 +370,16 @@ typedef struct {
 }
 
 
+#define njs_wellknown_symbol(key) {                                           \
+    .data = {                                                                 \
+        .type = NJS_SYMBOL,                                                   \
+        .truth = 1,                                                           \
+        .magic32 = key,                                                       \
+        .u = { .value = NULL }                                                \
+    }                                                                         \
+}
+
+
 #define njs_string(s) {                                                       \
     .short_string = {                                                         \
         .type = NJS_STRING,                                                   \
@@ -480,6 +493,10 @@ typedef struct {
     ((value)->type <= NJS_NUMBER)
 
 
+#define njs_is_symbol(value)                                                  \
+    ((value)->type == NJS_SYMBOL)
+
+
 #define njs_is_string(value)                                                  \
     ((value)->type == NJS_STRING)
 
@@ -661,6 +678,14 @@ typedef struct {
     *(value) = njs_value_false
 
 
+#define njs_symbol_key(value)                                                 \
+    ((value)->data.magic32)
+
+
+#define njs_symbol_eq(value1, value2)                                         \
+    (njs_symbol_key(value1) == njs_symbol_key(value2))
+
+
 extern const njs_value_t  njs_value_null;
 extern const njs_value_t  njs_value_undefined;
 extern const njs_value_t  njs_value_false;
@@ -681,6 +706,7 @@ extern const njs_value_t  njs_string_minus_zero;
 extern const njs_value_t  njs_string_minus_infinity;
 extern const njs_value_t  njs_string_plus_infinity;
 extern const njs_value_t  njs_string_nan;
+extern const njs_value_t  njs_string_symbol;
 extern const njs_value_t  njs_string_string;
 extern const njs_value_t  njs_string_data;
 extern const njs_value_t  njs_string_name;
@@ -874,6 +900,10 @@ njs_int_t njs_value_to_object(njs_vm_t *vm, njs_value_t *value);
 #include "njs_number.h"
 
 
+void
+njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string);
+
+
 njs_inline njs_int_t
 njs_value_to_number(njs_vm_t *vm, njs_value_t *value, double *dst)
 {
@@ -890,6 +920,12 @@ njs_value_to_number(njs_vm_t *vm, njs_value_t *value, double *dst)
     }
 
     if (njs_slow_path(!njs_is_numeric(value))) {
+
+        if (njs_slow_path(njs_is_symbol(value))) {
+            njs_symbol_conversion_failed(vm, 0);
+            return NJS_ERROR;
+        }
+
         *dst = NAN;
 
         if (njs_is_string(value)) {
@@ -1014,12 +1050,18 @@ njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value)
     njs_value_t  primitive;
 
     if (njs_slow_path(!njs_is_primitive(value))) {
-        ret = njs_value_to_primitive(vm, &primitive, value, 1);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
+        if (njs_slow_path(value->type == NJS_OBJECT_SYMBOL)) {
+            /* should fail */
+            value = njs_object_value(value);
 
-        value = &primitive;
+        } else {
+            ret = njs_value_to_primitive(vm, &primitive, value, 1);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+
+            value = &primitive;
+        }
     }
 
     return njs_primitive_value_to_string(vm, dst, value);
@@ -1047,6 +1089,10 @@ njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2)
         return njs_string_eq(val1, val2);
     }
 
+    if (njs_is_symbol(val1)) {
+        return njs_symbol_eq(val1, val2);
+    }
+
     return (njs_object(val1) == njs_object(val2));
 }
 
index d4c56358751f01c64b5d20835ea16d2e893a1b5a..354a49da440e459788c23ae2bffd2b8c7e79ed1a 100644 (file)
@@ -92,6 +92,8 @@ njs_vm_create(njs_vm_opt_t *options)
         }
     }
 
+    vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX;
+
     return vm;
 }
 
index 5b3398b8891523046a20bf6caa591bd634b1559d..748f678ee5ef96a57506ba838143d40037a675e8 100644 (file)
@@ -84,6 +84,7 @@ typedef enum {
     NJS_OBJ_TYPE_ARRAY,
     NJS_OBJ_TYPE_BOOLEAN,
     NJS_OBJ_TYPE_NUMBER,
+    NJS_OBJ_TYPE_SYMBOL,
     NJS_OBJ_TYPE_STRING,
     NJS_OBJ_TYPE_FUNCTION,
     NJS_OBJ_TYPE_REGEXP,
@@ -232,6 +233,8 @@ struct njs_vm_s {
      * and NJS_PROPERTY_QUERY_DELETE modes.
      */
     uintptr_t                stash; /* njs_property_query_t * */
+
+    uint64_t                 symbol_generator;
 };
 
 
index af8ef290b48d3c7229295262fc9c790477583105..925b3307ef5ce57f77c1c8942d775221ce671b2e 100644 (file)
@@ -261,6 +261,16 @@ next:
                     value2 = &primitive2;
                 }
 
+                if (njs_slow_path(njs_is_symbol(value1)
+                                  || njs_is_symbol(value2)))
+                {
+                    njs_symbol_conversion_failed(vm,
+                        (op == NJS_VMCODE_ADDITION) &&
+                        (njs_is_string(value1) || njs_is_string(value2)));
+
+                    goto error;
+                }
+
                 retval = njs_vmcode_operand(vm, vmcode->operand1);
 
                 if (op == NJS_VMCODE_ADDITION) {
@@ -1397,6 +1407,7 @@ njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
         &njs_string_undefined,
         &njs_string_boolean,
         &njs_string_number,
+        &njs_string_symbol,
         &njs_string_string,
         &njs_string_data,
         &njs_string_external,
@@ -1408,13 +1419,13 @@ njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
         &njs_string_undefined,
         &njs_string_undefined,
         &njs_string_undefined,
-        &njs_string_undefined,
 
         &njs_string_object,
         &njs_string_object,
         &njs_string_object,
         &njs_string_object,
         &njs_string_object,
+        &njs_string_object,
         &njs_string_function,
         &njs_string_object,
         &njs_string_object,
@@ -1494,10 +1505,14 @@ again:
             return njs_string_eq(val1, val2);
         }
 
+        if (njs_is_symbol(val1)) {
+            return njs_symbol_eq(val1, val2);
+        }
+
         return (njs_object(val1) == njs_object(val2));
     }
 
-    /* Sort values as: numeric < string < objects. */
+    /* Sort values as: numeric < symbol < string < objects. */
 
     if (val1->type > val2->type) {
         hv = val1;
@@ -1513,12 +1528,18 @@ again:
         return 0;
     }
 
-    /* If "hv" is a string then "lv" can only be a numeric. */
+    /* If "hv" is a symbol then "lv" can only be a numeric. */
+    if (njs_is_symbol(hv)) {
+        return 0;
+    }
+
+    /* If "hv" is a string then "lv" can be a numeric or symbol. */
     if (njs_is_string(hv)) {
-        return (njs_number(lv) == njs_string_to_number(hv, 0));
+        return !njs_is_symbol(lv)
+            && (njs_number(lv) == njs_string_to_number(hv, 0));
     }
 
-    /* "hv" is an object and "lv" is either a string or a numeric. */
+    /* "hv" is an object and "lv" is either a string or a symbol or a numeric. */
 
     ret = njs_value_to_primitive(vm, &primitive, hv, 0);
     if (ret != NJS_OK) {
index b06426908380e8f31ced2a295f60f89461f224e4..f1b8f59e91718d2a6499a8fd40fcb92bb5897c3a 100644 (file)
@@ -9996,6 +9996,265 @@ static njs_unit_test_t  njs_test[] =
       njs_str("true") },
 #endif
 
+    /* Symbol */
+
+    { njs_str("typeof Symbol"),
+      njs_str("function") },
+
+    { njs_str("this.Symbol === Symbol"),
+      njs_str("true") },
+
+    { njs_str("Symbol.name"),
+      njs_str("Symbol") },
+
+    { njs_str("Object.getOwnPropertyDescriptor(Symbol, 'name').configurable"),
+      njs_str("true") },
+
+    { njs_str("Symbol.length"),
+      njs_str("0") },
+
+    { njs_str("Object.getOwnPropertyDescriptor(Symbol, 'length').configurable"),
+      njs_str("true") },
+
+    { njs_str("typeof Symbol.for"),
+      njs_str("function") },
+
+    { njs_str("Symbol.for.length"),
+      njs_str("1") },
+
+    { njs_str("typeof Symbol.keyFor"),
+      njs_str("function") },
+
+    { njs_str("Symbol.keyFor.length"),
+      njs_str("1") },
+
+    { njs_str("Symbol.prototype.constructor === Symbol"),
+      njs_str("true") },
+
+    { njs_str("Symbol.prototype.__proto__ === Object.prototype"),
+      njs_str("true") },
+
+    { njs_str("Object.prototype.toString.call(Symbol.prototype)"),
+      njs_str("[object Object]") },
+
+    { njs_str("Symbol.prototype.toString()"),
+      njs_str("TypeError: unexpected value type:object") },
+
+    { njs_str("new Symbol()"),
+      njs_str("TypeError: Symbol is not a constructor") },
+
+    { njs_str("typeof Symbol()"),
+      njs_str("symbol") },
+
+    { njs_str("typeof Symbol('desc')"),
+      njs_str("symbol") },
+
+    { njs_str("Symbol() === Symbol()"),
+      njs_str("false") },
+
+    { njs_str("Symbol('desc') === Symbol('desc')"),
+      njs_str("false") },
+
+    { njs_str("Symbol() == Symbol()"),
+      njs_str("false") },
+
+    { njs_str("Symbol('desc') == Symbol('desc')"),
+      njs_str("false") },
+
+    { njs_str("Symbol() == true"),
+      njs_str("false") },
+
+    { njs_str("Symbol() == false"),
+      njs_str("false") },
+
+    { njs_str("Symbol() != true"),
+      njs_str("true") },
+
+    { njs_str("Symbol() != 0"),
+      njs_str("true") },
+
+    { njs_str("Symbol() != ''"),
+      njs_str("true") },
+
+    { njs_str("Symbol() != undefined"),
+      njs_str("true") },
+
+    { njs_str("typeof Object(Symbol())"),
+      njs_str("object") },
+
+    { njs_str("typeof Object(Symbol('desc'))"),
+      njs_str("object") },
+
+    { njs_str("var x = Symbol(), o = Object(x); x == o"),
+      njs_str("true") },
+
+    { njs_str("var x = Symbol(), o = Object(x); o == x"),
+      njs_str("true") },
+
+    { njs_str("var x = Symbol(), o = Object(x); x !== o"),
+      njs_str("true") },
+
+    { njs_str("var x = Symbol(), o = Object(x); x === o.valueOf()"),
+      njs_str("true") },
+
+    { njs_str("var x = Symbol(); Object(x) == Object(x)"),
+      njs_str("false") },
+
+    { njs_str("!Symbol()"),
+      njs_str("false") },
+
+    { njs_str("!!Symbol()"),
+      njs_str("true") },
+
+    { njs_str("(Symbol('a') && Symbol('b')).toString()"),
+      njs_str("Symbol(b)") },
+
+    { njs_str("(Symbol('a') || Symbol('b')).toString()"),
+      njs_str("Symbol(a)") },
+
+    { njs_str("+Symbol()"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("-Symbol()"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("~Symbol()"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("var x = Symbol(); ++x"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("var x = Symbol(); x++"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("var x = Symbol(); --x"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("var x = Symbol(); x--"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("var msg = 'Cannot convert a Symbol value to a number',"
+              "    ops = ['+', '-', '*', '**', '/', '%', '>>>', '>>', '<<',"
+              "           '&', '|', '^', '<', '<=', '>', '>='],"
+              "    test = (lhs, rhs, cls, msg, stop, op) => {"
+              "        if (stop) {"
+              "            return stop;"
+              "        }"
+              "        var op = `${lhs} ${op} ${rhs}`;"
+              "        try {"
+              "            (new Function(op))();"
+              "        } catch (e) {"
+              "            if (e instanceof cls && msg == e.message) {"
+              "                return '';"
+              "            }"
+              "        }"
+              "        return `'${op}' - failed`;"
+              "   };"
+              "ops.reduce(test.bind({}, 'Symbol()', '42', TypeError, msg), '') ||"
+              "ops.reduce(test.bind({}, '42', 'Symbol()', TypeError, msg), '');"),
+      njs_str("") },
+
+    { njs_str("Symbol() > 'abc'"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("'abc' > Symbol()"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("'abc' + Symbol()"),
+      njs_str("TypeError: Cannot convert a Symbol value to a string") },
+
+    { njs_str("Symbol() + 'abc'"),
+      njs_str("TypeError: Cannot convert a Symbol value to a string") },
+
+    { njs_str("Math.min(Symbol())"),
+      njs_str("TypeError: Cannot convert a Symbol value to a number") },
+
+    { njs_str("[Symbol(), Symbol()].join()"),
+      njs_str("TypeError: Cannot convert a Symbol value to a string") },
+
+    { njs_str("Symbol().toString()"),
+      njs_str("Symbol()") },
+
+    { njs_str("Symbol('desc').toString()"),
+      njs_str("Symbol(desc)") },
+
+    { njs_str("Symbol('α'.repeat(16)).toString()"),
+      njs_str("Symbol(αααααααααααααααα)") },
+
+    { njs_str("Symbol(undefined).toString()"),
+      njs_str("Symbol()") },
+
+    { njs_str("Symbol(null).toString()"),
+      njs_str("Symbol(null)") },
+
+    { njs_str("Symbol(123).toString()"),
+      njs_str("Symbol(123)") },
+
+    { njs_str("Symbol(false).toString()"),
+      njs_str("Symbol(false)") },
+
+    { njs_str("Symbol(Symbol()).toString()"),
+      njs_str("TypeError: Cannot convert a Symbol value to a string") },
+
+    { njs_str("var all = [ 'asyncIterator',"
+              "            'hasInstance',"
+              "            'isConcatSpreadable',"
+              "            'iterator',"
+              "            'match',"
+              "            'matchAll',"
+              "            'replace',"
+              "            'search',"
+              "            'species',"
+              "            'split',"
+              "            'toPrimitive',"
+              "            'toStringTag',"
+              "            'unscopables' ]; "
+              "Object.getOwnPropertyNames(Symbol)"
+              ".filter((x) => typeof Symbol[x] == 'symbol')"
+              ".length == all.length"),
+      njs_str("true") },
+
+    { njs_str("var all = [ 'asyncIterator',"
+              "            'hasInstance',"
+              "            'isConcatSpreadable',"
+              "            'iterator',"
+              "            'match',"
+              "            'matchAll',"
+              "            'replace',"
+              "            'search',"
+              "            'species',"
+              "            'split',"
+              "            'toPrimitive',"
+              "            'toStringTag',"
+              "            'unscopables' ]; "
+              "Object.getOwnPropertyNames(Symbol)"
+              ".filter((x) => typeof Symbol[x] == 'symbol')"
+              ".filter((x, i) => !all.includes(x))"),
+      njs_str("") },
+
+    { njs_str("Object.getOwnPropertyNames(Symbol)"
+              ".filter((x) => typeof Symbol[x] == 'symbol')"
+              ".map(x => ({ k: x, v: Symbol[x] }))"
+              ".every((x) => 'Symbol(Symbol.' + x.k + ')' == String(x.v))"),
+      njs_str("true") },
+
+    { njs_str("typeof Symbol.prototype.description"),
+      njs_str("TypeError: unexpected value type:object") },
+
+    { njs_str("Symbol.prototype.description = 1"),
+      njs_str("TypeError: Cannot set property \"description\" of object which has only a getter") },
+
+    { njs_str("typeof Symbol().description"),
+      njs_str("undefined") },
+
+    { njs_str("Symbol('desc').description"),
+      njs_str("desc") },
+
+    { njs_str("Symbol.iterator.description"),
+      njs_str("Symbol.iterator") },
+
+    /* String */
+
     { njs_str("String()"),
       njs_str("") },
 
@@ -10011,6 +10270,18 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("new String(123)"),
       njs_str("123") },
 
+    { njs_str("String(Symbol())"),
+      njs_str("Symbol()") },
+
+    { njs_str("String(Symbol('desc'))"),
+      njs_str("Symbol(desc)") },
+
+    { njs_str("new String(Symbol())"),
+      njs_str("TypeError: Cannot convert a Symbol value to a string") },
+
+    { njs_str("String(Object(Symbol()))"),
+      njs_str("TypeError: Cannot convert a Symbol value to a string") },
+
     { njs_str("Object('123').length"),
       njs_str("3") },
 
@@ -10340,6 +10611,12 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("Object.prototype.toString.call(new Object(1))"),
       njs_str("[object Number]") },
 
+    { njs_str("Object.prototype.toString.call(new Object(Symbol()))"),
+      njs_str("[object Symbol]") },
+
+    { njs_str("Object.prototype.toString.call(new Object(Symbol('desc')))"),
+      njs_str("[object Symbol]") },
+
     { njs_str("Object.prototype.toString.call(new Object(''))"),
       njs_str("[object String]") },
 
@@ -11064,7 +11341,7 @@ static njs_unit_test_t  njs_test[] =
                  "Object.getPrototypeOf(o) === Object.prototype"),
       njs_str("true") },
 
-    { njs_str("[true, 42, '' /*, Symbol()*/]"
+    { njs_str("[true, 42, '', Symbol()]"
               ".every((x) => Object.getPrototypeOf(x) == Object.getPrototypeOf(Object(x)))"),
       njs_str("true") },
 
@@ -11582,6 +11859,7 @@ static njs_unit_test_t  njs_test[] =
         "        'prototype',"
         "        'caller',"
         "        'arguments',"
+        "        'description',"
         "    ];"
         "    return Object.getOwnPropertyNames(o)"
         "                 .filter(v => !except.includes(v)"
@@ -11593,6 +11871,7 @@ static njs_unit_test_t  njs_test[] =
         "["
         "    Boolean, Boolean.prototype,"
         "    Number, Number.prototype,"
+        "    Symbol, Symbol.prototype,"
         "    String, String.prototype,"
         "    Object, Object.prototype,"
         "    Array, Array.prototype,"
@@ -11613,6 +11892,7 @@ static njs_unit_test_t  njs_test[] =
         "        'prototype',"
         "        'caller',"
         "        'arguments',"
+        "        'description',"
         "    ];"
         "    return Object.getOwnPropertyNames(o)"
         "                 .filter(v => !except.includes(v)"
@@ -11624,6 +11904,7 @@ static njs_unit_test_t  njs_test[] =
         "["
         "    Boolean, Boolean.prototype,"
         "    Number, Number.prototype,"
+        "    Symbol, Symbol.prototype,"
         "    String, String.prototype,"
         "    Object, Object.prototype,"
         "    Array, Array.prototype,"
@@ -14207,6 +14488,24 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("njs.dump([0, -0])"),
       njs_str("[0,-0]") },
 
+    { njs_str("njs.dump(Symbol())"),
+      njs_str("Symbol()") },
+
+    { njs_str("njs.dump(Object(Symbol()))"),
+      njs_str("[Symbol: Symbol()]") },
+
+    { njs_str("njs.dump(Symbol('desc'))"),
+      njs_str("Symbol(desc)") },
+
+    { njs_str("njs.dump(Object(Symbol('desc')))"),
+      njs_str("[Symbol: Symbol(desc)]") },
+
+    { njs_str("njs.dump(Symbol.iterator)"),
+      njs_str("Symbol(Symbol.iterator)") },
+
+    { njs_str("njs.dump(Object(Symbol.iterator))"),
+      njs_str("[Symbol: Symbol(Symbol.iterator)]") },
+
     /* Built-in methods name. */
 
     { njs_str(
@@ -14217,6 +14516,7 @@ static njs_unit_test_t  njs_test[] =
         "        'constructor',"
         "        'caller',"
         "        'arguments',"
+        "        'description',"
         "    ];"
         "    return Object.getOwnPropertyNames(o)"
         "                 .filter(v => !except.includes(v)"
@@ -14227,6 +14527,7 @@ static njs_unit_test_t  njs_test[] =
         "["
         "    Boolean, Boolean.prototype,"
         "    Number, Number.prototype,"
+        "    Symbol, Symbol.prototype,"
         "    String, String.prototype,"
         "    Object, Object.prototype,"
         "    Array, Array.prototype,"