[njs] Added initial version of Symbol primitive type.
Dmitry Volyntsev
xeioex at nginx.com
Thu Nov 21 17:57:34 UTC 2019
details: https://hg.nginx.org/njs/rev/263c75a40999
branches:
changeset: 1247:263c75a40999
user: Artem S. Povalyukhin <artem.povaluhin at gmail.com>
date: Tue Nov 19 20:45:22 2019 +0300
description:
Added initial version of Symbol primitive type.
This closes #249 issue on Github.
diffstat:
auto/sources | 1 +
src/njs_builtin.c | 10 +
src/njs_json.c | 27 ++
src/njs_main.h | 1 +
src/njs_object.c | 14 +-
src/njs_object_hash.h | 10 +
src/njs_string.c | 8 +
src/njs_symbol.c | 441 +++++++++++++++++++++++++++++++++++++++++++++++
src/njs_symbol.h | 36 +++
src/njs_value.c | 17 +
src/njs_value.h | 76 ++++++-
src/njs_vm.c | 2 +
src/njs_vm.h | 3 +
src/njs_vmcode.c | 33 ++-
src/test/njs_unit_test.c | 303 ++++++++++++++++++++++++++++++++-
15 files changed, 955 insertions(+), 27 deletions(-)
diffs (truncated from 1392 to 1000 lines):
diff -r 884d3a86beab -r 263c75a40999 auto/sources
--- a/auto/sources Tue Nov 19 19:32:26 2019 +0300
+++ b/auto/sources Tue Nov 19 20:45:22 2019 +0300
@@ -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 \
diff -r 884d3a86beab -r 263c75a40999 src/njs_builtin.c
--- a/src/njs_builtin.c Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_builtin.c Tue Nov 19 20:45:22 2019 +0300
@@ -60,6 +60,7 @@ static const njs_object_type_init_t *con
&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,
@@ -1169,6 +1170,15 @@ static const njs_object_prop_t njs_glob
{
.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"),
.value = njs_prop_handler2(njs_top_level_constructor,
NJS_OBJ_TYPE_STRING, NJS_STRING_HASH),
diff -r 884d3a86beab -r 263c75a40999 src/njs_json.c
--- a/src/njs_json.c Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_json.c Tue Nov 19 20:45:22 2019 +0300
@@ -1933,6 +1933,33 @@ njs_dump_value(njs_json_stringify_t *str
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);
diff -r 884d3a86beab -r 263c75a40999 src/njs_main.h
--- a/src/njs_main.h Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_main.h Tue Nov 19 20:45:22 2019 +0300
@@ -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>
diff -r 884d3a86beab -r 263c75a40999 src/njs_object.c
--- a/src/njs_object.c Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_object.c Tue Nov 19 20:45:22 2019 +0300
@@ -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
if (!njs_is_null_or_undefined(value)) {
index = njs_primitive_prototype_index(value->type);
-
- njs_set_type_object(&vm->retval, &vm->prototypes[index].object,
- njs_object_value_type(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, type);
return NJS_OK;
}
@@ -2189,6 +2190,8 @@ static const njs_value_t njs_object_boo
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
&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
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,
diff -r 884d3a86beab -r 263c75a40999 src/njs_object_hash.h
--- a/src/njs_object_hash.h Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_object_hash.h Tue Nov 19 20:45:22 2019 +0300
@@ -421,6 +421,16 @@
'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( \
diff -r 884d3a86beab -r 263c75a40999 src/njs_string.c
--- a/src/njs_string.c Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_string.c Tue Nov 19 20:45:22 2019 +0300
@@ -552,6 +552,10 @@ njs_string_constructor(njs_vm_t *vm, njs
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 *
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 -r 884d3a86beab -r 263c75a40999 src/njs_symbol.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_symbol.c Tue Nov 19 20:45:22 2019 +0300
@@ -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 -r 884d3a86beab -r 263c75a40999 src/njs_symbol.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_symbol.h Tue Nov 19 20:45:22 2019 +0300
@@ -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_ */
diff -r 884d3a86beab -r 263c75a40999 src/njs_value.c
--- a/src/njs_value.c Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_value.c Tue Nov 19 20:45:22 2019 +0300
@@ -47,6 +47,7 @@ const njs_value_t njs_string_minus_infi
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_pro
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_pro
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_va
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");
+}
diff -r 884d3a86beab -r 263c75a40999 src/njs_value.h
--- a/src/njs_value.h Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_value.h Tue Nov 19 20:45:22 2019 +0300
@@ -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_min
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 *
#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_va
}
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_va
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);
+
+ } else {
+ ret = njs_value_to_primitive(vm, &primitive, value, 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ value = &primitive;
}
-
- value = &primitive;
}
return njs_primitive_value_to_string(vm, dst, value);
@@ -1047,6 +1089,10 @@ njs_values_strict_equal(const njs_value_
return njs_string_eq(val1, val2);
}
+ if (njs_is_symbol(val1)) {
+ return njs_symbol_eq(val1, val2);
+ }
+
return (njs_object(val1) == njs_object(val2));
}
diff -r 884d3a86beab -r 263c75a40999 src/njs_vm.c
--- a/src/njs_vm.c Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_vm.c Tue Nov 19 20:45:22 2019 +0300
@@ -92,6 +92,8 @@ njs_vm_create(njs_vm_opt_t *options)
}
}
+ vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX;
+
return vm;
}
diff -r 884d3a86beab -r 263c75a40999 src/njs_vm.h
--- a/src/njs_vm.h Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_vm.h Tue Nov 19 20:45:22 2019 +0300
@@ -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;
};
diff -r 884d3a86beab -r 263c75a40999 src/njs_vmcode.c
--- a/src/njs_vmcode.c Tue Nov 19 19:32:26 2019 +0300
+++ b/src/njs_vmcode.c Tue Nov 19 20:45:22 2019 +0300
@@ -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_valu
&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_valu
&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 (njs_is_string(hv)) {
- return (njs_number(lv) == njs_string_to_number(hv, 0));
+ /* If "hv" is a symbol then "lv" can only be a numeric. */
+ if (njs_is_symbol(hv)) {
+ return 0;
}
- /* "hv" is an object and "lv" is either a string or a numeric. */
+ /* If "hv" is a string then "lv" can be a numeric or symbol. */
More information about the nginx-devel
mailing list