Added Symbol.for() and Symbol.keyfor().
diff --git a/src/njs_extern.c b/src/njs_extern.c
index 51121df..fdccf66 100644
--- a/src/njs_extern.c
+++ b/src/njs_extern.c
@@ -49,7 +49,7 @@
         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 --git a/src/njs_symbol.c b/src/njs_symbol.c
index fc352b8..fe2f363 100644
--- a/src/njs_symbol.c
+++ b/src/njs_symbol.c
@@ -57,20 +57,16 @@
 const njs_value_t *
 njs_symbol_description(const njs_value_t *value)
 {
-    const njs_value_t  *name;
+    uint32_t  key;
 
-    if (njs_symbol_key(value) < NJS_SYMBOL_KNOWN_MAX) {
-        name = njs_symbol_names[njs_symbol_key(value)];
+    key = njs_symbol_key(value);
 
-    } else {
-        name = value->data.u.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 @@
 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 @@
 
     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 @@
         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;
 
-    return NJS_ERROR;
+    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;
+
+        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;
 
-    return NJS_ERROR;
+    value = njs_arg(args, nargs, 1);
+
+    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 @@
         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 @@
    .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 --git a/src/njs_symbol.h b/src/njs_symbol.h
index d722d2f..10e9785 100644
--- a/src/njs_symbol.h
+++ b/src/njs_symbol.h
@@ -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 --git a/src/njs_value.h b/src/njs_value.h
index 2ebbd0c..6507c09 100644
--- a/src/njs_value.h
+++ b/src/njs_value.h
@@ -875,11 +875,12 @@
 
 
 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 --git a/src/njs_vm.c b/src/njs_vm.c
index c1e422b..85aecc8 100644
--- a/src/njs_vm.c
+++ b/src/njs_vm.c
@@ -396,6 +396,8 @@
     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 --git a/src/njs_vm.h b/src/njs_vm.h
index 64cc74f..1b247b7 100644
--- a/src/njs_vm.h
+++ b/src/njs_vm.h
@@ -197,6 +197,7 @@
     njs_trace_t              trace;
     njs_random_t             random;
 
+    njs_rbtree_t             global_symbols;
     uint64_t                 symbol_generator;
 };
 
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index c24a0e8..21587b6 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -12866,6 +12866,43 @@
               ".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") },