From 5be29ad86e6df689a04bc3f5b1710d990193ed88 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Wed, 7 Jun 2017 14:12:23 +0300 Subject: [PATCH] Object.keys() method. --- njs/njs_object.c | 101 +++++++++++++++++++++++++++++++++++++++ njs/njs_vm.h | 13 +++++ njs/test/njs_unit_test.c | 19 ++++++++ 3 files changed, 133 insertions(+) diff --git a/njs/njs_object.c b/njs/njs_object.c index 988928cc..57882be4 100644 --- a/njs/njs_object.c +++ b/njs/njs_object.c @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include #include @@ -318,6 +320,97 @@ njs_object_create(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } +static njs_ret_t +njs_object_keys(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + size_t size; + uint32_t i, n, keys_length, array_length; + njs_value_t *value; + njs_array_t *keys, *array; + nxt_lvlhsh_t *hash; + njs_object_prop_t *prop; + nxt_lvlhsh_each_t lhe; + + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + keys_length = 0; + array_length = 0; + + if (njs_is_array(&args[1])) { + array = args[1].data.u.array; + array_length = array->length; + + for (i = 0; i < array_length; i++) { + if (njs_is_valid(&array->start[i])) { + keys_length++; + } + } + } + + memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); + lhe.proto = &njs_object_hash_proto; + + hash = &args[1].data.u.object->hash; + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + if (prop->enumerable) { + keys_length++; + } + } + + keys = njs_array_alloc(vm, keys_length, NJS_ARRAY_SPARE); + if (nxt_slow_path(keys == NULL)) { + return NXT_ERROR; + } + + n = 0; + + for (i = 0; i < array_length; i++) { + if (njs_is_valid(&array->start[i])) { + value = &keys->start[n++]; + /* + * The maximum array index is 4294967294, so + * it can be stored as a short string inside value. + */ + size = snprintf((char *) njs_string_short_start(value), + NJS_STRING_SHORT, "%u", i); + njs_string_short_set(value, size, size); + } + } + + memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); + lhe.proto = &njs_object_hash_proto; + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + if (prop->enumerable) { + njs_string_copy(&keys->start[n++], &prop->name); + } + } + + vm->retval.data.u.array = keys; + vm->retval.type = NJS_ARRAY; + vm->retval.data.truth = 1; + + return NXT_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -456,6 +549,14 @@ static const njs_object_prop_t njs_object_constructor_properties[] = .name = njs_string("create"), .value = njs_native_function(njs_object_create, 0, 0), }, + + /* Object.keys(). */ + { + .type = NJS_METHOD, + .name = njs_string("keys"), + .value = njs_native_function(njs_object_keys, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, }; diff --git a/njs/njs_vm.h b/njs/njs_vm.h index a50285d3..61d06189 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -408,6 +408,19 @@ typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1, #define njs_string_truth(value, size) +#define njs_string_short_start(value) \ + (value)->short_string.start + + +#define njs_string_short_set(value, _size, _length) \ + do { \ + (value)->type = NJS_STRING; \ + njs_string_truth(value, _size); \ + (value)->short_string.size = _size; \ + (value)->short_string.length = _length; \ + } while (0) + + #define njs_is_primitive(value) \ ((value)->type <= NJS_STRING) diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 167cff7c..ff2e963b 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -5843,6 +5843,25 @@ static njs_unit_test_t njs_test[] = { nxt_string("var o = Object.create(null); '__proto__' in o"), nxt_string("false") }, + { nxt_string("var o = {a:1, b:2, c:3};" + "Object.keys(o)"), + nxt_string("a,b,c") }, + + { nxt_string("var a = []; a.one = 7; Object.keys(a)"), + nxt_string("one") }, + + { nxt_string("var a = [,,]; a.one = 7; Object.keys(a)"), + nxt_string("one") }, + + { nxt_string("var a = [,6,,3]; a.one = 7; Object.keys(a)"), + nxt_string("1,3,one") }, + + { nxt_string("Object.keys('a')"), + nxt_string("TypeError") }, + + { nxt_string("Object.keys(1)"), + nxt_string("TypeError") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, -- 2.47.3