From: Dmitry Volyntsev Date: Thu, 21 Nov 2019 17:56:06 +0000 (+0300) Subject: Added Symbol support for builtin operations. X-Git-Tag: 0.3.8~61 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=4366f456d108eed543eff624425ebce06696e2b8;p=njs.git Added Symbol support for builtin operations. 1) object property get/set, object literals. 2) Added Object.getOwnPropertySymbols(). 3) Extended to support Symbol: Object.getOwnPropertyNames(), Object.keys(), Object.defineProperty(), Object.defineProperties(), Object.getOwnPropertyDescriptor(), Object.getOwnPropertyDescriptors(). --- diff --git a/src/njs_array.c b/src/njs_array.c index 1b27903d..bf3dee56 100644 --- a/src/njs_array.c +++ b/src/njs_array.c @@ -1106,8 +1106,10 @@ njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_value_t value; njs_lvlhsh_query_t lhq; + static const njs_value_t join_string = njs_string("join"); + if (njs_is_object(&args[0])) { - njs_object_property_init(&lhq, "join", NJS_JOIN_HASH); + njs_object_property_init(&lhq, &join_string, NJS_JOIN_HASH); ret = njs_object_property(vm, &args[0], &lhq, &value); @@ -1322,7 +1324,8 @@ njs_object_indexes(njs_vm_t *vm, njs_value_t *object) uint32_t i; njs_array_t *keys; - keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, 0); + keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, NJS_ENUM_STRING, + 0); if (njs_slow_path(keys == NULL)) { return NULL; } diff --git a/src/njs_date.c b/src/njs_date.c index 05b0f197..85e5e657 100644 --- a/src/njs_date.c +++ b/src/njs_date.c @@ -1403,8 +1403,10 @@ njs_date_prototype_to_json(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_value_t value; njs_lvlhsh_query_t lhq; + static const njs_value_t to_iso_string = njs_string("toISOString"); + if (njs_is_object(&args[0])) { - njs_object_property_init(&lhq, "toISOString", NJS_TO_ISO_STRING_HASH); + njs_object_property_init(&lhq, &to_iso_string, NJS_TO_ISO_STRING_HASH); ret = njs_object_property(vm, &args[0], &lhq, &value); diff --git a/src/njs_error.c b/src/njs_error.c index e84e8f40..0adcfae0 100644 --- a/src/njs_error.c +++ b/src/njs_error.c @@ -630,7 +630,7 @@ njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error) static const njs_value_t default_name = njs_string("Error"); - njs_object_property_init(&lhq, "name", NJS_NAME_HASH); + njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH); ret = njs_object_property(vm, error, &lhq, &value1); diff --git a/src/njs_function.c b/src/njs_function.c index 13f6edd4..79b6dbc8 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -1160,7 +1160,7 @@ njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, function->u.bound_target = njs_function(&args[0]); - njs_object_property_init(&lhq, "name", NJS_NAME_HASH); + njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH); ret = njs_object_property(vm, &args[0], &lhq, &name); if (njs_slow_path(ret == NJS_ERROR)) { diff --git a/src/njs_json.c b/src/njs_json.c index 23142673..6e5f7951 100644 --- a/src/njs_json.c +++ b/src/njs_json.c @@ -68,6 +68,7 @@ typedef struct { njs_value_t replacer; njs_str_t space; u_char space_buf[16]; + njs_object_enum_type_t keys_type; } njs_json_stringify_t; @@ -227,6 +228,7 @@ njs_json_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, stringify->depth = 0; stringify->nodes = NULL; stringify->last = NULL; + stringify->keys_type = NJS_ENUM_STRING; replacer = njs_arg(args, nargs, 2); @@ -867,7 +869,8 @@ njs_json_push_parse_state(njs_vm_t *vm, njs_json_parse_t *parse, } else { state->type = NJS_JSON_OBJECT; state->prop = NULL; - state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0); + state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + NJS_ENUM_STRING, 0); if (state->keys == NULL) { return NULL; } @@ -1094,7 +1097,7 @@ njs_json_push_stringify_state(njs_vm_t *vm, njs_json_stringify_t *stringify, } else { state->keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, - 0); + stringify->keys_type, 0); } if (njs_slow_path(state->keys == NULL)) { @@ -1362,7 +1365,9 @@ njs_object_to_json_function(njs_vm_t *vm, njs_value_t *value) njs_value_t retval; njs_lvlhsh_query_t lhq; - njs_object_property_init(&lhq, "toJSON", NJS_TO_JSON_HASH); + static const njs_value_t to_json_string = njs_string("toJSON"); + + njs_object_property_init(&lhq, &to_json_string, NJS_TO_JSON_HASH); ret = njs_object_property(vm, value, &lhq, &retval); @@ -2179,6 +2184,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value, stringify->nodes = NULL; stringify->last = NULL; njs_set_undefined(&stringify->replacer); + stringify->keys_type = NJS_ENUM_STRING | NJS_ENUM_SYMBOL; if (!njs_dump_is_object(value)) { ret = njs_dump_value(stringify, value, console); @@ -2221,8 +2227,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value, } key = &state->keys->start[state->index++]; - njs_string_get(key, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + njs_object_property_key_set(&lhq, key, 0); if (njs_is_external(&state->value)) { lhq.proto = &njs_extern_hash_proto; @@ -2277,6 +2282,7 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, const njs_value_t *value, } state->written = 1; + njs_key_string_get(vm, key, &lhq.key); njs_json_stringify_append((char *) lhq.key.start, lhq.key.length); njs_json_stringify_append(":", 1); if (stringify->space.length != 0) { diff --git a/src/njs_object.c b/src/njs_object.c index 2a5a87d0..34ef9d17 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -14,19 +14,20 @@ static njs_object_prop_t *njs_object_exist_in_proto(const njs_object_t *begin, static uint32_t njs_object_enumerate_array_length(const njs_object_t *object); static uint32_t njs_object_enumerate_string_length(const njs_object_t *object); static uint32_t njs_object_enumerate_object_length(const njs_object_t *object, - njs_bool_t all); + njs_object_enum_type_t type, njs_bool_t all); static uint32_t njs_object_own_enumerate_object_length( - const njs_object_t *object, const njs_object_t *parent, njs_bool_t all); + const njs_object_t *object, const njs_object_t *parent, + njs_object_enum_type_t type, njs_bool_t all); static njs_int_t njs_object_enumerate_array(njs_vm_t *vm, const njs_array_t *array, njs_array_t *items, njs_object_enum_t kind); static njs_int_t njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value, njs_array_t *items, njs_object_enum_t kind); static njs_int_t njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object, njs_array_t *items, njs_object_enum_t kind, - njs_bool_t all); + njs_object_enum_type_t type, njs_bool_t all); static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, - njs_object_enum_t kind, njs_bool_t all); + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); njs_object_t * @@ -128,8 +129,9 @@ njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash, lhq.pool = vm->mem_pool; while (n != 0) { - njs_string_get(&prop->name, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + + njs_object_property_key_set(&lhq, &prop->name, 0); + lhq.value = (void *) prop; ret = njs_lvlhsh_insert(hash, &lhq); @@ -161,25 +163,34 @@ njs_object_hash_test(njs_lvlhsh_query_t *lhq, void *data) { size_t size; u_char *start; + njs_value_t *name; njs_object_prop_t *prop; prop = data; + name = &prop->name; + + if (njs_slow_path(njs_is_symbol(name))) { + return ((njs_symbol_key(name) == lhq->key_hash) + && lhq->key.length == 0) ? NJS_OK : NJS_DECLINED; + } + + /* string. */ - size = prop->name.short_string.size; + size = name->short_string.size; if (size != NJS_STRING_LONG) { if (lhq->key.length != size) { return NJS_DECLINED; } - start = prop->name.short_string.start; + start = name->short_string.start; } else { - if (lhq->key.length != prop->name.long_string.size) { + if (lhq->key.length != name->long_string.size) { return NJS_DECLINED; } - start = prop->name.long_string.data->start; + start = name->long_string.data->start; } if (memcmp(start, lhq->key.start, lhq->key.length) == 0) { @@ -293,7 +304,8 @@ njs_object_keys(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 0); + keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + NJS_ENUM_STRING, 0); if (keys == NULL) { return NJS_ERROR; } @@ -320,7 +332,8 @@ njs_object_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES, 0); + array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES, + NJS_ENUM_STRING, 0); if (array == NULL) { return NJS_ERROR; } @@ -347,7 +360,8 @@ njs_object_entries(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH, 0); + array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH, + NJS_ENUM_STRING, 0); if (array == NULL) { return NJS_ERROR; } @@ -396,23 +410,26 @@ next: njs_inline uint32_t -njs_object_enumerate_length(const njs_object_t *object, njs_bool_t all) +njs_object_enumerate_length(const njs_object_t *object, + njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; - length = njs_object_enumerate_object_length(object, all); + length = njs_object_enumerate_object_length(object, type, all); - switch (object->type) { - case NJS_ARRAY: - length += njs_object_enumerate_array_length(object); - break; + if (type & NJS_STRING) { + switch (object->type) { + case NJS_ARRAY: + length += njs_object_enumerate_array_length(object); + break; - case NJS_OBJECT_STRING: - length += njs_object_enumerate_string_length(object); - break; + case NJS_OBJECT_STRING: + length += njs_object_enumerate_string_length(object); + break; - default: - break; + default: + break; + } } return length; @@ -421,23 +438,25 @@ njs_object_enumerate_length(const njs_object_t *object, njs_bool_t all) njs_inline uint32_t njs_object_own_enumerate_length(const njs_object_t *object, - const njs_object_t *parent, njs_bool_t all) + const njs_object_t *parent, njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; - length = njs_object_own_enumerate_object_length(object, parent, all); + length = njs_object_own_enumerate_object_length(object, parent, type, all); - switch (object->type) { - case NJS_ARRAY: - length += njs_object_enumerate_array_length(object); - break; + if (type & NJS_ENUM_STRING) { + switch (object->type) { + case NJS_ARRAY: + length += njs_object_enumerate_array_length(object); + break; - case NJS_OBJECT_STRING: - length += njs_object_enumerate_string_length(object); - break; + case NJS_OBJECT_STRING: + length += njs_object_enumerate_string_length(object); + break; - default: - break; + default: + break; + } } return length; @@ -446,34 +465,37 @@ njs_object_own_enumerate_length(const njs_object_t *object, njs_inline njs_int_t njs_object_enumerate_value(njs_vm_t *vm, const njs_object_t *object, - njs_array_t *items, njs_object_enum_t kind, njs_bool_t all) + njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type, + njs_bool_t all) { njs_int_t ret; njs_object_value_t *obj_val; - switch (object->type) { - case NJS_ARRAY: - ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, - kind); - break; + if (type & NJS_ENUM_STRING) { + switch (object->type) { + case NJS_ARRAY: + ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, + kind); + break; - case NJS_OBJECT_STRING: - obj_val = (njs_object_value_t *) object; + case NJS_OBJECT_STRING: + obj_val = (njs_object_value_t *) object; - ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind); - break; + ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind); + break; - default: - goto object; - } + default: + goto object; + } - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } } object: - ret = njs_object_enumerate_object(vm, object, items, kind, all); + ret = njs_object_enumerate_object(vm, object, items, kind, type, all); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -485,34 +507,37 @@ object: njs_inline njs_int_t njs_object_own_enumerate_value(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind, - njs_bool_t all) + njs_object_enum_type_t type, njs_bool_t all) { njs_int_t ret; njs_object_value_t *obj_val; - switch (object->type) { - case NJS_ARRAY: - ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, - kind); - break; + if (type & NJS_ENUM_STRING) { + switch (object->type) { + case NJS_ARRAY: + ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, + kind); + break; - case NJS_OBJECT_STRING: - obj_val = (njs_object_value_t *) object; + case NJS_OBJECT_STRING: + obj_val = (njs_object_value_t *) object; - ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind); - break; + ret = njs_object_enumerate_string(vm, &obj_val->value, items, kind); + break; - default: - goto object; - } + default: + goto object; + } - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } } object: - ret = njs_object_own_enumerate_object(vm, object, parent, items, kind, all); + ret = njs_object_own_enumerate_object(vm, object, parent, items, kind, + type, all); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -523,20 +548,20 @@ object: njs_array_t * njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object, - njs_object_enum_t kind, njs_bool_t all) + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; njs_int_t ret; njs_array_t *items; - length = njs_object_enumerate_length(object, all); + length = njs_object_enumerate_length(object, type, all); items = njs_array_alloc(vm, length, NJS_ARRAY_SPARE); if (njs_slow_path(items == NULL)) { return NULL; } - ret = njs_object_enumerate_value(vm, object, items, kind, all); + ret = njs_object_enumerate_value(vm, object, items, kind, type, all); if (njs_slow_path(ret != NJS_OK)) { return NULL; } @@ -549,20 +574,21 @@ njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object, njs_array_t * njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object, - njs_object_enum_t kind, njs_bool_t all) + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; njs_int_t ret; njs_array_t *items; - length = njs_object_own_enumerate_length(object, object, all); + length = njs_object_own_enumerate_length(object, object, type, all); items = njs_array_alloc(vm, length, NJS_ARRAY_SPARE); if (njs_slow_path(items == NULL)) { return NULL; } - ret = njs_object_own_enumerate_value(vm, object, object, items, kind, all); + ret = njs_object_own_enumerate_value(vm, object, object, items, kind, type, + all); if (njs_slow_path(ret != NJS_OK)) { return NULL; } @@ -604,17 +630,18 @@ njs_object_enumerate_string_length(const njs_object_t *object) static uint32_t -njs_object_enumerate_object_length(const njs_object_t *object, njs_bool_t all) +njs_object_enumerate_object_length(const njs_object_t *object, + njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; const njs_object_t *proto; - length = njs_object_own_enumerate_object_length(object, object, all); + length = njs_object_own_enumerate_object_length(object, object, type, all); proto = object->__proto__; while (proto != NULL) { - length += njs_object_own_enumerate_length(proto, object, all); + length += njs_object_own_enumerate_length(proto, object, type, all); proto = proto->__proto__; } @@ -622,9 +649,17 @@ njs_object_enumerate_object_length(const njs_object_t *object, njs_bool_t all) } +njs_inline njs_bool_t +njs_is_enumerable(const njs_value_t *value, njs_object_enum_type_t type) +{ + return (njs_is_string(value) && (type & NJS_ENUM_STRING)) + || (njs_is_symbol(value) && (type & NJS_ENUM_SYMBOL)); +} + + static uint32_t njs_object_own_enumerate_object_length(const njs_object_t *object, - const njs_object_t *parent, njs_bool_t all) + const njs_object_t *parent, njs_object_enum_type_t type, njs_bool_t all) { uint32_t length; njs_int_t ret; @@ -645,8 +680,11 @@ njs_object_own_enumerate_object_length(const njs_object_t *object, break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); @@ -667,10 +705,12 @@ njs_object_own_enumerate_object_length(const njs_object_t *object, break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); - lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK) { @@ -866,12 +906,14 @@ njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value, static njs_int_t njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object, - njs_array_t *items, njs_object_enum_t kind, njs_bool_t all) + njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type, + njs_bool_t all) { njs_int_t ret; const njs_object_t *proto; - ret = njs_object_own_enumerate_object(vm, object, object, items, kind, all); + ret = njs_object_own_enumerate_object(vm, object, object, items, kind, + type, all); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -880,7 +922,7 @@ njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object, while (proto != NULL) { ret = njs_object_own_enumerate_value(vm, proto, object, items, kind, - all); + type, all); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -895,7 +937,7 @@ njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object, static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind, - njs_bool_t all) + njs_object_enum_type_t type, njs_bool_t all) { njs_int_t ret; njs_value_t *item; @@ -910,6 +952,8 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, item = items->start; hash = &object->hash; + lhq.proto = &njs_object_hash_proto; + switch (kind) { case NJS_ENUM_KEYS: for ( ;; ) { @@ -919,8 +963,11 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); @@ -941,10 +988,12 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); - lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK) { @@ -966,8 +1015,11 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); @@ -989,10 +1041,12 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); - lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK) { @@ -1014,8 +1068,11 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); @@ -1048,10 +1105,12 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, break; } - lhq.key_hash = lhe.key_hash; - njs_string_get(&prop->name, &lhq.key); + if (!njs_is_enumerable(&prop->name, type)) { + continue; + } + + njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); - lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK && (prop->enumerable || all)) { @@ -1194,13 +1253,6 @@ njs_object_define_property(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, name = njs_lvalue_arg(&lvalue, args, nargs, 2); - if (njs_slow_path(!njs_is_string(name))) { - ret = njs_value_to_string(vm, name, name); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - } - ret = njs_object_prop_define(vm, value, name, desc, NJS_OBJECT_PROP_DESCRIPTOR); if (njs_slow_path(ret != NJS_OK)) { @@ -1274,7 +1326,7 @@ static njs_int_t njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_value_t *value, *property; + njs_value_t lvalue, *value, *property; value = njs_arg(args, nargs, 1); @@ -1284,7 +1336,7 @@ njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args, return NJS_ERROR; } - property = njs_arg(args, nargs, 2); + property = njs_lvalue_arg(&lvalue, args, nargs, 2); return njs_object_prop_descriptor(vm, &vm->retval, value, property); } @@ -1311,7 +1363,8 @@ njs_object_get_own_property_descriptors(njs_vm_t *vm, njs_value_t *args, return NJS_ERROR; } - names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 1); + names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1); if (njs_slow_path(names == NULL)) { return NJS_ERROR; } @@ -1339,8 +1392,7 @@ njs_object_get_own_property_descriptors(njs_vm_t *vm, njs_value_t *args, return NJS_ERROR; } - njs_string_get(key, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + njs_object_property_key_set(&lhq, key, 0); lhq.value = pr; ret = njs_lvlhsh_insert(&descriptors->hash, &lhq); @@ -1357,8 +1409,8 @@ njs_object_get_own_property_descriptors(njs_vm_t *vm, njs_value_t *args, static njs_int_t -njs_object_get_own_property_names(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_index_t unused) +njs_object_get_own_property(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t type) { njs_array_t *names; njs_value_t *value; @@ -1372,7 +1424,8 @@ njs_object_get_own_property_names(njs_vm_t *vm, njs_value_t *args, return NJS_ERROR; } - names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, 1); + names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS, + type, 1); if (names == NULL) { return NJS_ERROR; } @@ -1669,7 +1722,8 @@ njs_object_assign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, for (i = 2; i < nargs; i++) { source = &args[i]; - names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS, 1); + names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS, + NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1); if (njs_slow_path(names == NULL)) { return NJS_ERROR; } @@ -1913,7 +1967,18 @@ static const njs_object_prop_t njs_object_constructor_properties[] = { .type = NJS_PROPERTY, .name = njs_long_string("getOwnPropertyNames"), - .value = njs_native_function(njs_object_get_own_property_names, 1), + .value = njs_native_function2(njs_object_get_own_property, 1, + NJS_ENUM_STRING), + .writable = 1, + .configurable = 1, + }, + + /* Object.getOwnPropertySymbols(). */ + { + .type = NJS_PROPERTY, + .name = njs_long_string("getOwnPropertySymbols"), + .value = njs_native_function2(njs_object_get_own_property, 1, + NJS_ENUM_SYMBOL), .writable = 1, .configurable = 1, }, diff --git a/src/njs_object.h b/src/njs_object.h index 4943077a..32e42936 100644 --- a/src/njs_object.h +++ b/src/njs_object.h @@ -8,27 +8,6 @@ #define _NJS_OBJECT_H_INCLUDED_ -#define njs_is_data_descriptor(prop) \ - ((prop)->writable != NJS_ATTRIBUTE_UNSET || njs_is_valid(&(prop)->value)) - - -#define njs_is_accessor_descriptor(prop) \ - (njs_is_function_or_undefined(&(prop)->getter) \ - || njs_is_function_or_undefined(&(prop)->setter)) - - -#define njs_is_generic_descriptor(prop) \ - (!njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop)) - - -#define njs_object_property_init(lhq, _key, hash) \ - do { \ - (lhq)->proto = &njs_object_hash_proto; \ - (lhq)->key_hash = hash; \ - (lhq)->key = njs_str_value(_key); \ - } while (0) - - typedef enum { NJS_OBJECT_PROP_DESCRIPTOR, NJS_OBJECT_PROP_GETTER, @@ -65,9 +44,9 @@ njs_object_t *njs_object_value_copy(njs_vm_t *vm, njs_value_t *value); njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, njs_uint_t type); njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object, - njs_object_enum_t kind, njs_bool_t all); + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); njs_array_t *njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object, - njs_object_enum_t kind, njs_bool_t all); + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); njs_int_t njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx, njs_object_traverse_cb_t cb); njs_int_t njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash, @@ -101,7 +80,147 @@ njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest, njs_value_t *value, njs_value_t *setval); const char *njs_prop_type_string(njs_object_prop_type_t type); -extern const njs_object_type_init_t njs_obj_type_init; + +njs_inline njs_bool_t +njs_is_data_descriptor(njs_object_prop_t *prop) +{ + return prop->writable != NJS_ATTRIBUTE_UNSET || njs_is_valid(&prop->value); +} + + +njs_inline njs_bool_t +njs_is_accessor_descriptor(njs_object_prop_t *prop) +{ + return njs_is_function_or_undefined(&prop->getter) + || njs_is_function_or_undefined(&prop->setter); +} + + +njs_inline njs_bool_t +njs_is_generic_descriptor(njs_object_prop_t *prop) +{ + return !njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop); +} + + +njs_inline void +njs_object_property_key_set(njs_lvlhsh_query_t *lhq, const njs_value_t *key, + uint32_t hash) +{ + if (njs_is_symbol(key)) { + + lhq->key.length = 0; + lhq->key_hash = njs_symbol_key(key); + + } else { + + /* string. */ + + njs_string_get(key, &lhq->key); + + if (hash == 0) { + lhq->key_hash = njs_djb_hash(lhq->key.start, lhq->key.length); + + } else { + lhq->key_hash = hash; + } + } +} + + +njs_inline void +njs_object_property_init(njs_lvlhsh_query_t *lhq, const njs_value_t *key, + uint32_t hash) +{ + lhq->proto = &njs_object_hash_proto; + + njs_object_property_key_set(lhq, key, hash); +} + + +njs_inline njs_int_t +njs_primitive_value_to_key(njs_vm_t *vm, njs_value_t *dst, + const njs_value_t *src) +{ + const njs_value_t *value; + + switch (src->type) { + + case NJS_NULL: + value = &njs_string_null; + break; + + case NJS_UNDEFINED: + value = &njs_string_undefined; + break; + + case NJS_BOOLEAN: + value = njs_is_true(src) ? &njs_string_true : &njs_string_false; + break; + + case NJS_NUMBER: + return njs_number_to_string(vm, dst, src); + + case NJS_SYMBOL: + case NJS_STRING: + /* GC: njs_retain(src); */ + value = src; + break; + + default: + return NJS_ERROR; + } + + *dst = *value; + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_value_to_key(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) +{ + njs_int_t ret; + njs_value_t primitive; + + if (njs_slow_path(!njs_is_primitive(value))) { + 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; + } + } + + return njs_primitive_value_to_key(vm, dst, value); +} + + +njs_inline njs_int_t +njs_key_string_get(njs_vm_t *vm, const njs_value_t *key, njs_str_t *str) +{ + njs_int_t ret; + njs_value_t dst; + + if (njs_slow_path(njs_is_symbol(key))) { + ret = njs_symbol_to_string(vm, &dst, key); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + key = &dst; + } + + njs_string_get(key, str); + + return NJS_OK; +} njs_inline njs_int_t @@ -118,4 +237,7 @@ njs_object_length_set(njs_vm_t *vm, njs_value_t *value, uint32_t length) } +extern const njs_object_type_init_t njs_obj_type_init; + + #endif /* _NJS_OBJECT_H_INCLUDED_ */ diff --git a/src/njs_object_prop.c b/src/njs_object_prop.c index 93501ccc..43112765 100644 --- a/src/njs_object_prop.c +++ b/src/njs_object_prop.c @@ -110,6 +110,13 @@ njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1); + if (njs_slow_path(!njs_is_key(name))) { + ret = njs_value_to_key(vm, name, name); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + ret = njs_property_query(vm, &pq, object, name); if (njs_slow_path(ret == NJS_ERROR)) { @@ -367,6 +374,7 @@ done: exception: + njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot redefine property: \"%V\"", &pq.lhq.key); return NJS_ERROR; @@ -448,10 +456,12 @@ njs_descriptor_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t value; njs_lvlhsh_query_t lhq; + static const njs_value_t get_string = njs_string("get"); + data = 0; accessor = 0; - njs_object_property_init(&lhq, "get", NJS_GET_HASH); + njs_object_property_init(&lhq, &get_string, NJS_GET_HASH); ret = njs_object_property(vm, desc, &lhq, &value); @@ -584,6 +594,13 @@ njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest, njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1); + if (njs_slow_path(!njs_is_key(key))) { + ret = njs_value_to_key(vm, key, key); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + ret = njs_property_query(vm, &pq, value, key); switch (ret) { diff --git a/src/njs_value.c b/src/njs_value.c index f102a33b..778e2c93 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -193,7 +193,7 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, njs_array_t * njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, - njs_object_enum_t kind, njs_bool_t all) + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all) { void *obj; njs_int_t ret; @@ -202,11 +202,14 @@ njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, const njs_extern_t *ext_proto; if (njs_is_object(value)) { - return njs_object_enumerate(vm, njs_object(value), kind, all); + return njs_object_enumerate(vm, njs_object(value), kind, type, all); } if (value->type != NJS_STRING) { - if (kind == NJS_ENUM_KEYS && njs_is_external(value)) { + if (kind == NJS_ENUM_KEYS + && (type & NJS_ENUM_STRING) + && njs_is_external(value)) + { ext_proto = value->external.proto; if (ext_proto->keys != NULL) { @@ -229,13 +232,13 @@ njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, obj_val.object = vm->string_object; obj_val.value = *value; - return njs_object_enumerate(vm, (njs_object_t *) &obj_val, kind, all); + return njs_object_enumerate(vm, (njs_object_t *) &obj_val, kind, type, all); } njs_array_t * njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, - njs_object_enum_t kind, njs_bool_t all) + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all) { void *obj; njs_int_t ret; @@ -244,11 +247,14 @@ njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, const njs_extern_t *ext_proto; if (njs_is_object(value)) { - return njs_object_own_enumerate(vm, njs_object(value), kind, all); + return njs_object_own_enumerate(vm, njs_object(value), kind, type, all); } if (value->type != NJS_STRING) { - if (kind == NJS_ENUM_KEYS && njs_is_external(value)) { + if (kind == NJS_ENUM_KEYS + && (type & NJS_ENUM_STRING) + && njs_is_external(value)) + { ext_proto = value->external.proto; if (ext_proto->keys != NULL) { @@ -271,7 +277,8 @@ njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, obj_val.object = vm->string_object; obj_val.value = *value; - return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, kind, all); + return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, kind, + type, all); } @@ -580,12 +587,18 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value, return NJS_ERROR; } - ret = njs_primitive_value_to_string(vm, &pq->key, key); + ret = njs_primitive_value_to_key(vm, &pq->key, key); if (njs_fast_path(ret == NJS_OK)) { - njs_string_get(&pq->key, &pq->lhq.key); - pq->lhq.key_hash = njs_djb_hash(pq->lhq.key.start, pq->lhq.key.length); + if (njs_is_symbol(key)) { + pq->lhq.key_hash = njs_symbol_key(key); + + } else { + njs_string_get(&pq->key, &pq->lhq.key); + pq->lhq.key_hash = njs_djb_hash(pq->lhq.key.start, + pq->lhq.key.length); + } if (obj == NULL) { pq->own = 1; @@ -1022,6 +1035,7 @@ njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, if (njs_is_data_descriptor(prop)) { if (!prop->writable) { + njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot assign to read-only property \"%V\" of %s", &pq.lhq.key, njs_type_string(value->type)); @@ -1034,6 +1048,7 @@ njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, value, setval, 1, &vm->retval); } + njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot set property \"%V\" of %s which has only a getter", &pq.lhq.key, njs_type_string(value->type)); @@ -1138,6 +1153,7 @@ njs_value_property_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, prop = pq.lhq.value; if (njs_slow_path(!prop->configurable)) { + njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot delete property \"%V\" of %s", &pq.lhq.key, njs_type_string(value->type)); return NJS_ERROR; diff --git a/src/njs_value.h b/src/njs_value.h index d464b839..7865a013 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -303,6 +303,12 @@ typedef enum { } njs_object_enum_t; +typedef enum { + NJS_ENUM_STRING = 1, + NJS_ENUM_SYMBOL = 2, +} njs_object_enum_type_t; + + typedef enum { NJS_PROPERTY = 0, NJS_PROPERTY_REF, @@ -501,6 +507,10 @@ typedef struct { ((value)->type == NJS_STRING) +#define njs_is_key(value) \ + (njs_is_string(value) || njs_is_symbol(value)) + + /* * The truth field coincides with short_string.size and short_string.length * so when string size and length are zero the string's value is false and @@ -874,9 +884,9 @@ void njs_value_release(njs_vm_t *vm, njs_value_t *value); njs_int_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, njs_uint_t hint); njs_array_t *njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value, - njs_object_enum_t kind, njs_bool_t all); + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, const njs_value_t *value, - njs_object_enum_t kind, njs_bool_t all); + njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all); njs_int_t njs_value_length(njs_vm_t *vm, njs_value_t *value, uint32_t *dest); const char *njs_type_string(njs_value_type_t type); diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 925b3307..10154544 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -637,7 +637,7 @@ next: accessor = (njs_vmcode_prop_accessor_t *) pc; function = njs_vmcode_operand(vm, accessor->value); - ret = njs_value_to_string(vm, &name, value2); + ret = njs_value_to_key(vm, &name, value2); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "failed conversion of type \"%s\" " "to string while property define", @@ -746,12 +746,12 @@ next: } if (njs_slow_path(!njs_is_function(&dst))) { - ret = njs_value_to_string(vm, value2, value2); + ret = njs_value_to_key(vm, value2, value2); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } - njs_string_get(value2, &string); + njs_key_string_get(vm, value2, &string); njs_type_error(vm, "(intermediate value)[\"%V\"] is not a function", &string); @@ -1186,13 +1186,12 @@ njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, break; case NJS_OBJECT: - ret = njs_value_to_string(vm, &name, key); + ret = njs_value_to_key(vm, &name, key); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } - njs_string_get(&name, &lhq.key); - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + njs_object_property_key_set(&lhq, &name, 0); lhq.proto = &njs_object_hash_proto; lhq.pool = vm->mem_pool; @@ -1320,7 +1319,8 @@ njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object, } next->index = 0; - next->array = njs_value_enumerate(vm, object, NJS_ENUM_KEYS, 0); + next->array = njs_value_enumerate(vm, object, NJS_ENUM_KEYS, + NJS_ENUM_STRING, 0); if (njs_slow_path(next->array == NULL)) { njs_memory_error(vm); return NJS_ERROR; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index f1b8f59e..d5826b75 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -10253,6 +10253,55 @@ static njs_unit_test_t njs_test[] = { njs_str("Symbol.iterator.description"), njs_str("Symbol.iterator") }, + { njs_str("var o = {}; o[Symbol.isConcatSpreadable] = true; " + "o[Symbol.isConcatSpreadable]"), + njs_str("true") }, + + { njs_str("var o = {[Symbol.isConcatSpreadable]:true}; " + "o[Symbol.isConcatSpreadable]"), + njs_str("true") }, + + { njs_str("var o = {[Symbol.isConcatSpreadable]:true}; " + "delete o[Symbol.isConcatSpreadable];" + "o[Symbol.isConcatSpreadable]"), + njs_str("undefined") }, + + { njs_str("var o = {get [Symbol.isConcatSpreadable](){return true}}; " + "o[Symbol.isConcatSpreadable]"), + njs_str("true") }, + + { njs_str("({[Symbol.isConcatSpreadable]:()=>true})[Symbol.isConcatSpreadable]()"), + njs_str("true") }, + + { njs_str("var o = {a:1, [Symbol.isConcatSpreadable]:true}; " + "[" + " 'getOwnPropertyNames'," + " 'keys', " + " 'getOwnPropertySymbols'," + " 'getOwnPropertyDescriptors'," + "]" + ".map(v=>Object[v](o)).map(v=>njs.dump(v))"), + njs_str("['a'],['a'],[Symbol(Symbol.isConcatSpreadable)]," + "{a:{value:1,writable:true,enumerable:true,configurable:true}," + "Symbol(Symbol.isConcatSpreadable):{value:true,writable:true,enumerable:true,configurable:true}}") }, + + { njs_str("var o = {}; o[Symbol.isConcatSpreadable] = true; " + "Object.getOwnPropertyDescriptors(o)[Symbol.isConcatSpreadable].value"), + njs_str("true") }, + + { njs_str("var o = Object.defineProperties({}, {[Symbol.isConcatSpreadable]:{value:true}}); " + "o[Symbol.isConcatSpreadable]"), + njs_str("true") }, + + { njs_str("var o = Object.defineProperty({}, Symbol.isConcatSpreadable, " + "{configurable:false, writable:false, value:true}); " + "o[Symbol.isConcatSpreadable]"), + njs_str("true") }, + + { njs_str("var o = {}; o[Symbol.isConcatSpreadable] = true; " + "Object.getOwnPropertyDescriptor(o, Symbol.isConcatSpreadable).value"), + njs_str("true") }, + /* String */ { njs_str("String()"),