From: Dmitry Volyntsev Date: Thu, 31 Oct 2019 15:17:31 +0000 (+0300) Subject: Refactored completions and backtrace matches. X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=d32d8314acfe36b31e459cddc8827db64db1267c;p=njs.git Refactored completions and backtrace matches. --- diff --git a/src/njs_builtin.c b/src/njs_builtin.c index e479cc9f..7396787f 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -14,6 +14,19 @@ typedef struct { } njs_function_init_t; +typedef struct { + enum { + NJS_BUILTIN_TRAVERSE_KEYS, + NJS_BUILTIN_TRAVERSE_MATCH, + } type; + + njs_function_native_t native; + + njs_lvlhsh_t keys; + njs_str_t match; +} njs_builtin_traverse_t; + + static njs_int_t njs_prototype_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); static njs_arr_t *njs_vm_expression_completions(njs_vm_t *vm, @@ -503,88 +516,130 @@ njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global) } -static size_t -njs_builtin_completions_size(njs_vm_t *vm) +static njs_int_t +njs_builtin_traverse(njs_vm_t *vm, njs_traverse_t *traverse, void *data) { - njs_uint_t n; - njs_keyword_t *keyword; - njs_lvlhsh_each_t lhe, lhe_prop; - njs_extern_value_t *ev; - const njs_extern_t *ext_proto, *ext_prop; - const njs_object_init_t **p; + size_t len; + u_char *p, *start, *end; + njs_int_t ret, n; + njs_str_t name; + njs_object_prop_t *prop; + njs_lvlhsh_query_t lhq; + njs_builtin_traverse_t *ctx; + njs_traverse_t *path[NJS_TRAVERSE_MAX_DEPTH]; + u_char buf[256]; + + ctx = data; + + if (ctx->type == NJS_BUILTIN_TRAVERSE_MATCH) { + prop = traverse->prop; + + if (!(njs_is_function(&prop->value) + && njs_function(&prop->value)->native + && njs_function(&prop->value)->u.native == ctx->native)) + { + return NJS_OK; + } + } n = 0; - njs_lvlhsh_each_init(&lhe, &njs_keyword_hash_proto); + while (traverse != NULL) { + path[n++] = traverse; + traverse = traverse->parent; + } - for ( ;; ) { - keyword = njs_lvlhsh_each(&vm->shared->keywords_hash, &lhe); + n--; - if (keyword == NULL) { - break; - } + p = buf; + end = buf + sizeof(buf); - n++; - } + do { + njs_string_get(&path[n]->prop->name, &name); - for (p = njs_object_init; *p != NULL; p++) { - n += (*p)->items; - } + if (njs_slow_path((p + name.length + 1) > end)) { + njs_type_error(vm, "njs_builtin_traverse() key is too long"); + return NJS_ERROR; + } - for (p = njs_prototype_init; *p != NULL; p++) { - n += (*p)->items; - } + p = njs_cpymem(p, name.start, name.length); - for (p = njs_constructor_init; *p != NULL; p++) { - n += (*p)->items; - } + if (n != 0) { + *p++ = '.'; + } - njs_lvlhsh_each_init(&lhe, &njs_extern_value_hash_proto); + } while (n-- > 0); - for ( ;; ) { - ev = njs_lvlhsh_each(&vm->externals_hash, &lhe); + if (ctx->type == NJS_BUILTIN_TRAVERSE_MATCH) { + len = ctx->match.length; + start = njs_mp_alloc(vm->mem_pool, len + (p - buf) + (len != 0)); + if (njs_slow_path(start == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } - if (ev == NULL) { - break; + if (len != 0) { + memcpy(start, ctx->match.start, len); + start[len++] = '.'; } - ext_proto = ev->value.external.proto; + memcpy(start + len, buf, p - buf); + ctx->match.length = len + p - buf; + ctx->match.start = start; - njs_lvlhsh_each_init(&lhe_prop, &njs_extern_hash_proto); + return NJS_DONE; + } - n++; + /* NJS_BUILTIN_TRAVERSE_KEYS. */ - for ( ;; ) { - ext_prop = njs_lvlhsh_each(&ext_proto->hash, &lhe_prop); + prop = njs_object_prop_alloc(vm, &njs_value_undefined, &njs_value_null, 0); + if (njs_slow_path(prop == NULL)) { + return NJS_ERROR; + } - if (ext_prop == NULL) { - break; - } + ret = njs_string_new(vm, &prop->name, buf, p - buf, 0); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - n++; - } + lhq.value = prop; + njs_string_get(&prop->name, &lhq.key); + lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + lhq.replace = 1; + lhq.pool = vm->mem_pool; + lhq.proto = &njs_object_hash_proto; + + ret = njs_lvlhsh_insert(&ctx->keys, &lhq); + if (njs_slow_path(ret != NJS_OK)) { + njs_internal_error(vm, "lvlhsh insert/replace failed"); + return NJS_ERROR; } - return n; + return NJS_OK; } static njs_arr_t * -njs_builtin_completions(njs_vm_t *vm, njs_arr_t *array) +njs_builtin_completions(njs_vm_t *vm) { u_char *compl; - size_t n, len; - njs_str_t string, *completions; - njs_uint_t i, k; + size_t len; + njs_arr_t *array; + njs_str_t *completion; + njs_int_t ret; njs_keyword_t *keyword; njs_lvlhsh_each_t lhe, lhe_prop; njs_extern_value_t *ev; const njs_extern_t *ext_proto, *ext_prop; + njs_builtin_traverse_t ctx; const njs_object_prop_t *prop; - const njs_object_init_t *obj, **p; - n = 0; - completions = array->start; + array = njs_arr_create(vm->mem_pool, 64, sizeof(njs_str_t)); + if (njs_slow_path(array == NULL)) { + return NULL; + } + + /* Keywords completions. */ njs_lvlhsh_each_init(&lhe, &njs_keyword_hash_proto); @@ -595,79 +650,44 @@ njs_builtin_completions(njs_vm_t *vm, njs_arr_t *array) break; } - completions[n++] = keyword->name; - } - - for (p = njs_object_init; *p != NULL; p++) { - obj = *p; - - for (i = 0; i < obj->items; i++) { - prop = &obj->properties[i]; - njs_string_get(&prop->name, &string); - len = obj->name.length + string.length + 2; - - compl = njs_mp_zalloc(vm->mem_pool, len); - if (compl == NULL) { - return NULL; - } - - njs_sprintf(compl, compl + len, "%s.%s%Z", obj->name.start, - string.start); - - completions[n].length = len; - completions[n++].start = (u_char *) compl; + completion = njs_arr_add(array); + if (njs_slow_path(completion == NULL)) { + return NULL; } - } - - for (p = njs_prototype_init; *p != NULL; p++) { - obj = *p; - for (i = 0; i < obj->items; i++) { - prop = &obj->properties[i]; - njs_string_get(&prop->name, &string); - len = string.length + 2; + *completion = keyword->name; + } - compl = njs_mp_zalloc(vm->mem_pool, len); - if (compl == NULL) { - return NULL; - } + /* Global object completions. */ - njs_sprintf(compl, compl + len, ".%s%Z", string.start); + ctx.type = NJS_BUILTIN_TRAVERSE_KEYS; + njs_lvlhsh_init(&ctx.keys); - for (k = 0; k < n; k++) { - if (njs_strncmp(completions[k].start, compl, len) == 0) { - break; - } - } - - if (k == n) { - completions[n].length = len; - completions[n++].start = (u_char *) compl; - } - } + ret = njs_object_traverse(vm, &vm->global_object, &ctx, + njs_builtin_traverse); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; } - for (p = njs_constructor_init; *p != NULL; p++) { - obj = *p; + njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); - for (i = 0; i < obj->items; i++) { - prop = &obj->properties[i]; - njs_string_get(&prop->name, &string); - len = obj->name.length + string.length + 2; - - compl = njs_mp_zalloc(vm->mem_pool, len); - if (compl == NULL) { - return NULL; - } + for ( ;; ) { + prop = njs_lvlhsh_each(&ctx.keys, &lhe); - njs_sprintf(compl, compl + len, "%s.%s%Z", obj->name.start, - string.start); + if (prop == NULL) { + break; + } - completions[n].length = len; - completions[n++].start = (u_char *) compl; + completion = njs_arr_add(array); + if (njs_slow_path(completion == NULL)) { + return NULL; } + + njs_string_get(&prop->name, completion); } + /* Externals completions. */ + njs_lvlhsh_each_init(&lhe, &njs_extern_value_hash_proto); for ( ;; ) { @@ -689,8 +709,13 @@ njs_builtin_completions(njs_vm_t *vm, njs_arr_t *array) njs_sprintf(compl, compl + len, "%V%Z", &ev->name); - completions[n].length = len; - completions[n++].start = (u_char *) compl; + completion = njs_arr_add(array); + if (njs_slow_path(completion == NULL)) { + return NULL; + } + + completion->length = len; + completion->start = (u_char *) compl; for ( ;; ) { ext_prop = njs_lvlhsh_each(&ext_proto->hash, &lhe_prop); @@ -708,13 +733,16 @@ njs_builtin_completions(njs_vm_t *vm, njs_arr_t *array) njs_sprintf(compl, compl + len, "%V.%V%Z", &ev->name, &ext_prop->name); - completions[n].length = len; - completions[n++].start = (u_char *) compl; + completion = njs_arr_add(array); + if (njs_slow_path(completion == NULL)) { + return NULL; + } + + completion->length = len; + completion->start = (u_char *) compl; } } - array->items = n; - return array; } @@ -722,18 +750,8 @@ njs_builtin_completions(njs_vm_t *vm, njs_arr_t *array) njs_arr_t * njs_vm_completions(njs_vm_t *vm, njs_str_t *expression) { - size_t size; - njs_arr_t *completions; - if (expression == NULL) { - size = njs_builtin_completions_size(vm); - - completions = njs_arr_create(vm->mem_pool, size, sizeof(njs_str_t)); - if (njs_slow_path(completions == NULL)) { - return NULL; - } - - return njs_builtin_completions(vm, completions); + return njs_builtin_completions(vm); } return njs_vm_expression_completions(vm, expression); @@ -913,101 +931,76 @@ njs_object_completions(njs_vm_t *vm, njs_object_t *object) } -static njs_int_t -njs_builtin_match(const njs_object_init_t **objects, njs_function_t *function, - const njs_object_prop_t **prop, const njs_object_init_t **object) +njs_int_t +njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function, + njs_str_t *name) { - njs_uint_t i; - njs_function_t *fun; - const njs_object_init_t *o, **p; - const njs_object_prop_t *pr; - - for (p = objects; *p != NULL; p++) { - o = *p; + njs_int_t ret; + njs_uint_t i; + njs_value_t value; + njs_module_t *module; + njs_lvlhsh_each_t lhe; + njs_builtin_traverse_t ctx; - for (i = 0; i < o->items; i++) { - pr = &o->properties[i]; + ctx.type = NJS_BUILTIN_TRAVERSE_MATCH; + ctx.native = function->u.native; - if (pr->type != NJS_PROPERTY || !njs_is_function(&pr->value)) { - continue; - } + /* Global object. */ - fun = njs_function(&pr->value); + ctx.match = njs_str_value(""); - if (function->u.native != fun->u.native) { - continue; - } + ret = njs_object_traverse(vm, &vm->global_object, &ctx, + njs_builtin_traverse); - *prop = pr; - *object = o; - - return NJS_OK; - } + if (ret == NJS_DONE) { + *name = ctx.match; + return NJS_OK; } - return NJS_DECLINED; -} - - -njs_int_t -njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function, - njs_str_t *name) -{ - size_t len; - njs_str_t string, middle; - njs_int_t ret; - const njs_object_init_t *obj; - const njs_object_prop_t *prop; + /* Constructor from built-in modules (not-mapped to global object). */ - middle = njs_str_value("."); + for (i = NJS_OBJ_TYPE_CRYPTO_HASH; i < NJS_OBJ_TYPE_ERROR; i++) { + njs_set_object(&value, &vm->constructors[i].object); - ret = njs_builtin_match(njs_object_init, function, &prop, &obj); + ret = njs_value_property(vm, &value, njs_value_arg(&njs_string_name), + &value); - if (ret == NJS_OK) { - if (!obj->name.length) { - middle = njs_str_value(""); + if (ret == NJS_OK && njs_is_string(&value)) { + njs_string_get(&value, &ctx.match); } - goto found; - } - - ret = njs_builtin_match(njs_prototype_init, function, &prop, &obj); - - if (ret == NJS_OK) { - middle = njs_str_value(".prototype."); - goto found; - } - - ret = njs_builtin_match(njs_constructor_init, function, &prop, &obj); + ret = njs_object_traverse(vm, &vm->constructors[i].object, &ctx, + njs_builtin_traverse); - if (ret == NJS_OK) { - goto found; + if (ret == NJS_DONE) { + *name = ctx.match; + return NJS_OK; + } } - ret = njs_builtin_match(njs_module_init, function, &prop, &obj); + /* Modules. */ - if (ret == NJS_OK) { - goto found; - } + njs_lvlhsh_each_init(&lhe, &njs_modules_hash_proto); - return NJS_DECLINED; + for ( ;; ) { + module = njs_lvlhsh_each(&vm->modules_hash, &lhe); -found: + if (module == NULL) { + break; + } - njs_string_get(&prop->name, &string); + ctx.match = module->name; - len = obj->name.length + middle.length + string.length; + ret = njs_object_traverse(vm, &module->object, &ctx, + njs_builtin_traverse); - name->length = len; - name->start = njs_mp_zalloc(vm->mem_pool, len); - if (name->start == NULL) { - return NJS_ERROR; + if (ret == NJS_DONE) { + *name = ctx.match; + return NJS_OK; + } } - njs_sprintf(name->start, name->start + len, - "%V%V%V", &obj->name, &middle, &string); - - return NJS_OK; + return NJS_DECLINED; } @@ -1515,11 +1508,6 @@ njs_process_object_argv(njs_vm_t *vm, njs_object_prop_t *pr, static const njs_value_t argv_string = njs_string("argv"); - if (njs_slow_path(vm->options.argv == NULL)) { - njs_internal_error(vm, "argv was not provided by host environment"); - return NJS_ERROR; - } - argv = njs_array_alloc(vm, vm->options.argc, 0); if (njs_slow_path(argv == NULL)) { return NJS_ERROR; diff --git a/src/njs_crypto.c b/src/njs_crypto.c index c02796a7..81c90e1d 100644 --- a/src/njs_crypto.c +++ b/src/njs_crypto.c @@ -361,10 +361,34 @@ njs_hash_constructor(njs_vm_t *vm, njs_value_t *args, } +static const njs_object_prop_t njs_hash_constructor_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("name"), + .value = njs_string("Hash"), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("length"), + .value = njs_value(NJS_NUMBER, 1, 2.0), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("prototype"), + .value = njs_prop_handler(njs_object_prototype_create), + }, +}; + + const njs_object_init_t njs_hash_constructor_init = { njs_str("Hash"), - NULL, - 0, + njs_hash_constructor_properties, + njs_nitems(njs_hash_constructor_properties), }; @@ -621,10 +645,34 @@ njs_hmac_constructor(njs_vm_t *vm, njs_value_t *args, } +static const njs_object_prop_t njs_hmac_constructor_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("name"), + .value = njs_string("Hmac"), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("length"), + .value = njs_value(NJS_NUMBER, 1, 3.0), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("prototype"), + .value = njs_prop_handler(njs_object_prototype_create), + }, +}; + + const njs_object_init_t njs_hmac_constructor_init = { njs_str("Hmac"), - NULL, - 0, + njs_hmac_constructor_properties, + njs_nitems(njs_hmac_constructor_properties), }; diff --git a/src/njs_object.c b/src/njs_object.c index 87b7ab99..2c41c8a9 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -1084,6 +1084,87 @@ njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, } +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 depth, ret; + njs_str_t name; + njs_value_t value, obj; + njs_object_prop_t *prop; + njs_traverse_t state[NJS_TRAVERSE_MAX_DEPTH]; + + static const njs_str_t constructor_key = njs_str("constructor"); + + depth = 0; + + state[depth].prop = NULL; + state[depth].parent = NULL; + state[depth].object = object; + state[depth].hash = &object->shared_hash; + njs_lvlhsh_each_init(&state[depth].lhe, &njs_object_hash_proto); + + for ( ;; ) { + prop = njs_lvlhsh_each(state[depth].hash, &state[depth].lhe); + + if (prop == NULL) { + if (state[depth].hash == &state[depth].object->shared_hash) { + state[depth].hash = &state[depth].object->hash; + njs_lvlhsh_each_init(&state[depth].lhe, &njs_object_hash_proto); + continue; + } + + if (depth == 0) { + return NJS_OK; + } + + depth--; + continue; + } + + state[depth].prop = prop; + + ret = cb(vm, &state[depth], ctx); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + value = prop->value; + + if (prop->type == NJS_PROPERTY_HANDLER) { + njs_set_object(&obj, state[depth].object); + ret = prop->value.data.u.prop_handler(vm, prop, &obj, NULL, &value); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + + } + } + + njs_string_get(&prop->name, &name); + + /* + * "constructor" properties make loops in object hierarchies. + * Object.prototype.constructor -> Object. + */ + + if (njs_is_object(&value) && !njs_strstr_eq(&name, &constructor_key)) { + if (++depth > (NJS_TRAVERSE_MAX_DEPTH - 1)) { + njs_type_error(vm, "njs_object_traverse() recursion limit:%d", + depth); + return NJS_ERROR; + } + + state[depth].prop = NULL; + state[depth].parent = &state[depth - 1]; + state[depth].object = njs_object(&value); + state[depth].hash = &njs_object(&value)->shared_hash; + njs_lvlhsh_each_init(&state[depth].lhe, &njs_object_hash_proto); + } + + } +} + + static njs_int_t njs_object_define_property(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) diff --git a/src/njs_object.h b/src/njs_object.h index 1f21f6f8..23823be3 100644 --- a/src/njs_object.h +++ b/src/njs_object.h @@ -43,6 +43,24 @@ struct njs_object_init_s { }; +typedef struct njs_traverse_s njs_traverse_t; + +struct njs_traverse_s { + struct njs_traverse_s *parent; + njs_object_prop_t *prop; + + njs_object_t *object; + njs_lvlhsh_t *hash; + njs_lvlhsh_each_t lhe; + +#define NJS_TRAVERSE_MAX_DEPTH 32 +}; + + +typedef njs_int_t (*njs_object_traverse_cb_t)(njs_vm_t *vm, + njs_traverse_t *traverse, void *ctx); + + njs_object_t *njs_object_alloc(njs_vm_t *vm); 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, @@ -51,6 +69,8 @@ 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_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_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, const njs_object_prop_t *prop, njs_uint_t n); njs_int_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args, diff --git a/src/njs_value.c b/src/njs_value.c index f86a4ed9..a716f2db 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -48,6 +48,7 @@ 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_string = njs_string("string"); +const njs_value_t njs_string_name = njs_string("name"); const njs_value_t njs_string_data = njs_string("data"); const njs_value_t njs_string_external = njs_string("external"); const njs_value_t njs_string_invalid = njs_string("invalid"); diff --git a/src/njs_value.h b/src/njs_value.h index edf4c91e..60f87175 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -665,6 +665,7 @@ extern const njs_value_t njs_string_plus_infinity; extern const njs_value_t njs_string_nan; extern const njs_value_t njs_string_string; extern const njs_value_t njs_string_data; +extern const njs_value_t njs_string_name; extern const njs_value_t njs_string_external; extern const njs_value_t njs_string_invalid; extern const njs_value_t njs_string_object; diff --git a/test/njs_expect_test.exp b/test/njs_expect_test.exp index 991f5ead..77431015 100644 --- a/test/njs_expect_test.exp +++ b/test/njs_expect_test.exp @@ -101,7 +101,7 @@ njs_test { njs_test { {"O\t" - "O\a*bject."} + "O\a*bject"} {"\t\t" "Object.create*Object.isSealed"} } @@ -120,7 +120,7 @@ njs_test { njs_test { {"Ma\t" - "Ma\a*th."} + "Ma\a*th"} {"\t\t" "Math.abs*Math.atan2"} }