[njs] Added Symbol.for() and Symbol.keyfor().

Dmitry Volyntsev xeioex at nginx.com
Wed Jun 22 05:14:59 UTC 2022


details:   https://hg.nginx.org/njs/rev/f7fec5be38a0
branches:  
changeset: 1895:f7fec5be38a0
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Jun 21 21:54:14 2022 -0700
description:
Added Symbol.for() and Symbol.keyfor().

diffstat:

 src/njs_extern.c         |    2 +-
 src/njs_symbol.c         |  146 +++++++++++++++++++++++++++++++++++-----------
 src/njs_symbol.h         |   10 +++
 src/njs_value.h          |    3 +-
 src/njs_vm.c             |    2 +
 src/njs_vm.h             |    1 +
 src/test/njs_unit_test.c |   37 +++++++++++
 7 files changed, 162 insertions(+), 39 deletions(-)

diffs (336 lines):

diff -r 8ff4c3126a89 -r f7fec5be38a0 src/njs_extern.c
--- a/src/njs_extern.c	Tue Jun 21 15:29:00 2022 -0700
+++ b/src/njs_extern.c	Tue Jun 21 21:54:14 2022 -0700
@@ -49,7 +49,7 @@ njs_external_add(njs_vm_t *vm, njs_arr_t
         prop->enumerable = external->enumerable;
 
         if (external->flags & 4) {
-            njs_set_symbol(&prop->name, external->name.symbol);
+            njs_set_symbol(&prop->name, external->name.symbol, NULL);
 
             lhq.key_hash = external->name.symbol;
 
diff -r 8ff4c3126a89 -r f7fec5be38a0 src/njs_symbol.c
--- a/src/njs_symbol.c	Tue Jun 21 15:29:00 2022 -0700
+++ b/src/njs_symbol.c	Tue Jun 21 21:54:14 2022 -0700
@@ -57,20 +57,16 @@ static const njs_value_t  *njs_symbol_na
 const njs_value_t *
 njs_symbol_description(const njs_value_t *value)
 {
-    const njs_value_t  *name;
-
-    if (njs_symbol_key(value) < NJS_SYMBOL_KNOWN_MAX) {
-        name = njs_symbol_names[njs_symbol_key(value)];
+    uint32_t  key;
 
-    } else {
-        name = value->data.u.value;
+    key = njs_symbol_key(value);
 
-        if (name == NULL) {
-            return  &njs_value_undefined;
-        }
+    if (key < NJS_SYMBOL_KNOWN_MAX) {
+        return njs_symbol_names[key];
     }
 
-    return name;
+    return value->data.u.value != NULL ? value->data.u.value
+                                       : &njs_value_undefined;
 }
 
 
@@ -109,9 +105,9 @@ static njs_int_t
 njs_symbol_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
+    uint64_t     key;
     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");
@@ -120,25 +116,13 @@ njs_symbol_constructor(njs_vm_t *vm, njs
 
     value = njs_arg(args, nargs, 1);
 
-    if (njs_is_undefined(value)) {
-        name = NULL;
-
-    } else {
+    if (njs_is_defined(value)) {
         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;
@@ -148,32 +132,100 @@ njs_symbol_constructor(njs_vm_t *vm, njs
         return NJS_ERROR;
     }
 
-    vm->retval.type = NJS_SYMBOL;
-    vm->retval.data.truth = 1;
-    vm->retval.data.magic32 = key;
-    vm->retval.data.u.value = name;
+    name = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+    if (njs_slow_path(name == NULL)) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    njs_value_assign(name, value);
+    njs_set_symbol(&vm->retval, key, 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_symbol_for(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
 {
-    njs_internal_error(vm, "not implemented");
+    uint64_t              key;
+    njs_int_t             ret;
+    njs_value_t           *value;
+    njs_rbtree_node_t     *rb_node;
+    njs_rb_symbol_node_t  *node;
+
+    value = njs_arg(args, nargs, 1);
+
+    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;
+        }
+    }
+
+    rb_node = njs_rbtree_min(&vm->global_symbols);
+
+    while (njs_rbtree_is_there_successor(&vm->global_symbols, rb_node)) {
+
+        node = (njs_rb_symbol_node_t *) rb_node;
 
-    return NJS_ERROR;
+        if (njs_is_string(&node->name)
+            && njs_string_cmp(value, &node->name) == 0)
+        {
+            njs_set_symbol(&vm->retval, node->key, &node->name);
+            return NJS_OK;
+        }
+
+        rb_node = njs_rbtree_node_successor(&vm->global_symbols, rb_node);
+    }
+
+    key = ++vm->symbol_generator;
+
+    if (njs_slow_path(key >= UINT32_MAX)) {
+        njs_internal_error(vm, "Symbol generator overflow");
+        return NJS_ERROR;
+    }
+
+    node = njs_mp_alloc(vm->mem_pool, sizeof(njs_rb_symbol_node_t));
+    if (njs_slow_path(node == NULL)) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    node->key = key;
+    njs_value_assign(&node->name, value);
+
+    njs_rbtree_insert(&vm->global_symbols, &node->node);
+
+    njs_set_symbol(&vm->retval, key, &node->name);
+
+    return NJS_OK;
 }
 
 
 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_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");
+    njs_value_t           *value;
+    njs_rb_symbol_node_t  query, *node;
+
+    value = njs_arg(args, nargs, 1);
 
-    return NJS_ERROR;
+    if (njs_slow_path(!njs_is_symbol(value))) {
+        njs_type_error(vm, "is not a symbol");
+        return NJS_ERROR;
+    }
+
+    query.key = njs_symbol_key(value);
+    node = (njs_rb_symbol_node_t *) njs_rbtree_find(&vm->global_symbols,
+                                                    &query.node);
+
+    njs_value_assign(&vm->retval,
+                     node != NULL ? &node->name : &njs_value_undefined);
+
+    return NJS_OK;
 }
 
 
@@ -355,7 +407,7 @@ njs_symbol_prototype_description(njs_vm_
         return ret;
     }
 
-    vm->retval = *njs_symbol_description(&vm->retval);
+    njs_value_assign(&vm->retval, njs_symbol_description(&vm->retval));
 
     return NJS_OK;
 }
@@ -426,3 +478,23 @@ const njs_object_type_init_t  njs_symbol
    .prototype_props = &njs_symbol_prototype_init,
    .prototype_value = { .object = { .type = NJS_OBJECT } },
 };
+
+
+intptr_t
+njs_symbol_rbtree_cmp(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2)
+{
+    njs_rb_symbol_node_t  *item1, *item2;
+
+    item1 = (njs_rb_symbol_node_t *) node1;
+    item2 = (njs_rb_symbol_node_t *) node2;
+
+    if (item1->key < item2->key) {
+        return -1;
+    }
+
+    if (item1->key == item2->key) {
+        return 0;
+    }
+
+    return 1;
+}
diff -r 8ff4c3126a89 -r f7fec5be38a0 src/njs_symbol.h
--- a/src/njs_symbol.h	Tue Jun 21 15:29:00 2022 -0700
+++ b/src/njs_symbol.h	Tue Jun 21 21:54:14 2022 -0700
@@ -7,9 +7,19 @@
 #ifndef _NJS_SYMBOL_H_INCLUDED_
 #define _NJS_SYMBOL_H_INCLUDED_
 
+
+typedef struct {
+    NJS_RBTREE_NODE  (node);
+    uint32_t         key;
+    njs_value_t      name;
+} njs_rb_symbol_node_t;
+
+
 const njs_value_t *njs_symbol_description(const njs_value_t *value);
 njs_int_t njs_symbol_descriptive_string(njs_vm_t *vm, njs_value_t *dst,
     const njs_value_t *value);
+intptr_t njs_symbol_rbtree_cmp(njs_rbtree_node_t *node1,
+    njs_rbtree_node_t *node2);
 
 
 extern const njs_object_type_init_t  njs_symbol_type_init;
diff -r 8ff4c3126a89 -r f7fec5be38a0 src/njs_value.h
--- a/src/njs_value.h	Tue Jun 21 15:29:00 2022 -0700
+++ b/src/njs_value.h	Tue Jun 21 21:54:14 2022 -0700
@@ -875,11 +875,12 @@ njs_set_uint32(njs_value_t *value, uint3
 
 
 njs_inline void
-njs_set_symbol(njs_value_t *value, uint32_t symbol)
+njs_set_symbol(njs_value_t *value, uint32_t symbol, njs_value_t *name)
 {
     value->data.magic32 = symbol;
     value->type = NJS_SYMBOL;
     value->data.truth = 1;
+    value->data.u.value = name;
 }
 
 
diff -r 8ff4c3126a89 -r f7fec5be38a0 src/njs_vm.c
--- a/src/njs_vm.c	Tue Jun 21 15:29:00 2022 -0700
+++ b/src/njs_vm.c	Tue Jun 21 21:54:14 2022 -0700
@@ -396,6 +396,8 @@ njs_vm_init(njs_vm_t *vm)
     njs_lvlhsh_init(&vm->modules_hash);
     njs_lvlhsh_init(&vm->events_hash);
 
+    njs_rbtree_init(&vm->global_symbols, njs_symbol_rbtree_cmp);
+
     njs_queue_init(&vm->posted_events);
     njs_queue_init(&vm->promise_events);
 
diff -r 8ff4c3126a89 -r f7fec5be38a0 src/njs_vm.h
--- a/src/njs_vm.h	Tue Jun 21 15:29:00 2022 -0700
+++ b/src/njs_vm.h	Tue Jun 21 21:54:14 2022 -0700
@@ -197,6 +197,7 @@ struct njs_vm_s {
     njs_trace_t              trace;
     njs_random_t             random;
 
+    njs_rbtree_t             global_symbols;
     uint64_t                 symbol_generator;
 };
 
diff -r 8ff4c3126a89 -r f7fec5be38a0 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Tue Jun 21 15:29:00 2022 -0700
+++ b/src/test/njs_unit_test.c	Tue Jun 21 21:54:14 2022 -0700
@@ -12866,6 +12866,43 @@ static njs_unit_test_t  njs_test[] =
               ".every((x) => 'Symbol(Symbol.' + x.k + ')' == String(x.v))"),
       njs_str("true") },
 
+    { njs_str("Symbol.for('desc').toString()"),
+      njs_str("Symbol(desc)") },
+
+    { njs_str("Symbol.for({toString: () => 'desc'}).description"),
+      njs_str("desc") },
+
+    { njs_str("Symbol.for('desc') === Symbol.for('desc')"),
+      njs_str("true") },
+
+    { njs_str("Symbol.keyFor(Symbol())"),
+      njs_str("undefined") },
+
+    { njs_str("Symbol.keyFor(Symbol('desc'))"),
+      njs_str("undefined") },
+
+    { njs_str("Symbol.keyFor(1)"),
+      njs_str("TypeError: is not a symbol") },
+
+    { njs_str("Symbol.keyFor(Symbol.for('desc'))"),
+      njs_str("desc") },
+
+    { njs_str("[ 'asyncIterator',"
+              "  'hasInstance',"
+              "  'isConcatSpreadable',"
+              "  'iterator',"
+              "  'match',"
+              "  'matchAll',"
+              "  'replace',"
+              "  'search',"
+              "  'species',"
+              "  'split',"
+              "  'toPrimitive',"
+              "  'toStringTag',"
+              "  'unscopables' ]"
+              ".every(v => Symbol.keyFor(Symbol[v]) === undefined)"),
+      njs_str("true") },
+
     { njs_str("typeof Symbol.prototype.description"),
       njs_str("TypeError: unexpected value type:object") },
 



More information about the nginx-devel mailing list