details: https://hg.nginx.org/njs/rev/85313a068147 branches: changeset: 2302:85313a068147 user: Dmitry Volyntsev <xei...@nginx.com> date: Mon Mar 18 22:24:57 2024 -0700 description: Shell: added completions for QuickJS engine.
diffstat: external/njs_shell.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++- test/shell_test.exp | 147 ++++++++++++++++++++++++++++++++++ test/shell_test_njs.exp | 148 ----------------------------------- 3 files changed, 345 insertions(+), 152 deletions(-) diffs (541 lines): diff -r fe94552843d7 -r 85313a068147 external/njs_shell.c --- a/external/njs_shell.c Mon Mar 18 22:15:48 2024 -0700 +++ b/external/njs_shell.c Mon Mar 18 22:24:57 2024 -0700 @@ -3106,6 +3106,203 @@ njs_engine_qjs_output(njs_engine_t *engi return NJS_OK; } + +static njs_arr_t * +njs_qjs_object_completions(njs_engine_t *engine, JSContext *ctx, + JSValueConst object, njs_str_t *expression) +{ + u_char *prefix; + size_t len, prefix_len; + JSValue prototype; + uint32_t k, n, length; + njs_int_t ret; + njs_arr_t *array; + njs_str_t *completion, key; + JSPropertyEnum *ptab; + + prefix = expression->start + expression->length; + + while (prefix > expression->start && *prefix != '.') { + prefix--; + } + + if (prefix != expression->start) { + prefix++; + } + + ptab = NULL; + key.start = NULL; + prefix_len = prefix - expression->start; + len = expression->length - prefix_len; + + array = njs_arr_create(engine->pool, 8, sizeof(njs_str_t)); + if (njs_slow_path(array == NULL)) { + goto fail; + } + + while (!JS_IsNull(object)) { + ret = JS_GetOwnPropertyNames(ctx, &ptab, &length, object, + JS_GPN_STRING_MASK); + if (ret < 0) { + goto fail; + } + + for (n = 0; n < length; n++) { + key.start = (u_char *) JS_AtomToCString(ctx, ptab[n].atom); + JS_FreeAtom(ctx, ptab[n].atom); + if (njs_slow_path(key.start == NULL)) { + goto fail; + } + + key.length = njs_strlen(key.start); + + if (len > key.length || njs_strncmp(key.start, prefix, len) != 0) { + goto next; + } + + for (k = 0; k < array->items; k++) { + completion = njs_arr_item(array, k); + + if ((completion->length - prefix_len - 1) == key.length + && njs_strncmp(&completion->start[prefix_len], + key.start, key.length) + == 0) + { + goto next; + } + } + + completion = njs_arr_add(array); + if (njs_slow_path(completion == NULL)) { + goto fail; + } + + completion->length = prefix_len + key.length + 1; + completion->start = njs_mp_alloc(engine->pool, completion->length); + if (njs_slow_path(completion->start == NULL)) { + goto fail; + } + + njs_sprintf(completion->start, + completion->start + completion->length, + "%*s%V%Z", prefix_len, expression->start, &key); + +next: + + JS_FreeCString(ctx, (const char *) key.start); + } + + js_free_rt(JS_GetRuntime(ctx), ptab); + + prototype = JS_GetPrototype(ctx, object); + if (JS_IsException(prototype)) { + goto fail; + } + + JS_FreeValue(ctx, object); + object = prototype; + } + + return array; + +fail: + + if (array != NULL) { + njs_arr_destroy(array); + } + + if (key.start != NULL) { + JS_FreeCString(ctx, (const char *) key.start); + } + + if (ptab != NULL) { + js_free_rt(JS_GetRuntime(ctx), ptab); + } + + JS_FreeValue(ctx, object); + + return NULL; +} + + +static njs_arr_t * +njs_engine_qjs_complete(njs_engine_t *engine, njs_str_t *expression) +{ + u_char *p, *start, *end; + JSAtom key; + JSValue value, retval; + njs_arr_t *arr; + JSContext *ctx; + njs_bool_t global; + + ctx = engine->u.qjs.ctx; + + p = expression->start; + end = p + expression->length; + + global = 1; + value = JS_GetGlobalObject(ctx); + + while (p < end && *p != '.') { p++; } + + if (p == end) { + goto done; + } + + p = expression->start; + + for ( ;; ) { + + start = (*p == '.' && p < end) ? ++p: p; + + if (p == end) { + break; + } + + while (p < end && *p != '.') { p++; } + + key = JS_NewAtomLen(ctx, (char *) start, p - start); + if (key == JS_ATOM_NULL) { + goto fail; + } + + retval = JS_GetProperty(ctx, value, key); + + JS_FreeAtom(ctx, key); + + if (JS_IsUndefined(retval)) { + if (global) { + goto fail; + } + + goto done; + } + + if (JS_IsException(retval)) { + goto fail; + } + + JS_FreeValue(ctx, value); + value = retval; + global = 0; + } + +done: + + arr = njs_qjs_object_completions(engine, ctx, JS_DupValue(ctx, value), + expression); + + JS_FreeValue(ctx, value); + + return arr; + +fail: + + JS_FreeValue(ctx, value); + + return NULL; +} + #endif @@ -3163,6 +3360,7 @@ njs_create_engine(njs_opts_t *opts) engine->process_events = njs_engine_qjs_process_events; engine->destroy = njs_engine_qjs_destroy; engine->output = njs_engine_qjs_output; + engine->complete = njs_engine_qjs_complete; break; #endif @@ -3583,10 +3781,6 @@ njs_completion_generator(const char *tex njs_completion_t *cmpl; engine = njs_console.engine; - if (engine->complete == NULL) { - return NULL; - } - cmpl = &engine->completion; if (state == 0) { diff -r fe94552843d7 -r 85313a068147 test/shell_test.exp --- a/test/shell_test.exp Mon Mar 18 22:15:48 2024 -0700 +++ b/test/shell_test.exp Mon Mar 18 22:24:57 2024 -0700 @@ -58,6 +58,153 @@ njs_test { "2\r\n>> "} } +# Global completions + +njs_test { + {"\t\t" + "$262*"} +} + +# Global completions, single partial match + +njs_test { + {"O\t" + "O\a*bject"} +} + +njs_test { + {"Mat\t" + "Mat\a*h"} +} + + njs_test { + {"conso\t" + "conso\a*le"} + } + +# Global completions, multiple partial match +njs_test { + {"cons\t\t" + "console*const"} +} + +njs_test { + {"Type\t" + "Type\a*Error"} + {".\t\t" + "TypeError.__proto__"} +} + +njs_test { + {"TypeError.\t\t" + "TypeError.__proto__*TypeError.prototype"} +} + +njs_test { + {"Object.ge\t" + "Object.ge\a*t"} + {"\t\t" + "Object.getOwnPropertyDescriptor*Object.getPrototypeOf"} +} + +njs_test { + {"JS\t" + "JS\a*ON"} + {".\t\t" + "JSON.__*JSON.stringify"} +} + +njs_test { + {"decodeURI\t\t" + "decodeURI*decodeURIComponent"} + {"C\t" + "decodeURIC\a*omponent"} +} + +# Global completions, no matches +njs_test { + {"1.\t\t" + "1."} +} + +njs_test { + {"1..\t\t" + "1.."} +} + +njs_test { + {"'abc'.\t\t" + "'abc'."} +} + +# Global completions, global vars +njs_test { + {"var AA = 1; var AAA = 2\r\n" + "var AA = 1; var AAA = 2\r\nundefined\r\n>> "} + {"AA\t\t" + "AA*AAA*"} +} + +njs_test { + {"var zz = 1\r\n" + "var zz = 1\r\nundefined\r\n>> "} + {"1 + z\t\r\n" + "1 + z*z*\r\n2"} +} + +njs_test { + {"unknown_var\t\t" + "unknown_var"} +} + +njs_test { + {"unknown_var.\t\t" + "unknown_var."} +} + +# An object's level completions +njs_test { + {"var o = {zz:1, zb:2}\r\n" + "var o = {zz:1, zb:2}\r\nundefined\r\n>> "} + {"o.z\t\t" + "o.zb*o.zz"} +} + +njs_test { + {"var d = new Date()\r\n" + "var d = new Date()\r\nundefined\r\n>> "} + {"d.to\t\t" + "d.toDateString*d.toLocaleDateString*d.toString"} +} + +njs_test { + {"var o = {a:new Date()}\r\n" + "var o = {a:new Date()}\r\nundefined\r\n>> "} + {"o.a.to\t\t" + "o.a.toDateString*o.a.toLocaleDateString*o.a.toString"} +} + +njs_test { + {"var o = {a:1,b:2,333:'t'}\r\n" + "var o = {a:1,b:2,333:'t'}\r\nundefined\r\n>> "} + {"o.3\t\t" + "o.3"} +} + +njs_test { + {"var a = Array(5000000); a.aab = 1; a.aac = 2\r\n" + "var a = Array(5000000); a.aab = 1; a.aac = 2\r\n2\r\n>> "} + {"a.\t\t" + "a.aab*"} +} + +njs_test { + {"var a = new Uint8Array([5,6,7,8,8]); a.aab = 1; a.aac = 2\r\n" + "var a = new Uint8Array(\\\[5,6,7,8,8]); a.aab = 1; a.aac = 2\r\n2\r\n>> "} + {"a.\t\t" + "a.aab*"} +} + # console object njs_test { {"console[Symbol.toStringTag]\r\n" diff -r fe94552843d7 -r 85313a068147 test/shell_test_njs.exp --- a/test/shell_test_njs.exp Mon Mar 18 22:15:48 2024 -0700 +++ b/test/shell_test_njs.exp Mon Mar 18 22:24:57 2024 -0700 @@ -38,154 +38,6 @@ njs_test { "njs.version\r\n\*\.\*\.\*"} } -# simple multi line interaction -njs_test { - {"var a = 1\r\n" - "var a = 1\r\nundefined\r\n>> "} - {"a *= 2\r\n" - "a *= 2\r\n2\r\n>> "} -} - -# Global completions - -njs_test { - {"\t\t" - "$262*"} -} - -# Global completions, single partial match - -njs_test { - {"O\t" - "O\a*bject"} -} - -njs_test { - {"Ma\t" - "Ma\a*th"} -} - - njs_test { - {"conso\t" - "conso\a*le"} - } - -# Global completions, multiple partial match -njs_test { - {"cons\t\t" - "console*const"} -} - -njs_test { - {"Type\t" - "Type\a*Error"} - {".\t\t" - "TypeError.__proto__"} -} - -njs_test { - {"TypeError.\t\t" - "TypeError.__proto__*TypeError.prototype"} -} - -njs_test { - {"Object.g\t" - "Object.g\a*et"} - {"\t\t" - "Object.getOwnPropertyDescriptor*Object.getPrototypeOf"} -} - -njs_test { - {"JS\t" - "JS\a*ON"} - {".\t\t" - "JSON.__proto__*JSON.stringify"} -} - -# Global completions, no matches -njs_test { - {"1.\t\t" - "1."} -} - -njs_test { - {"1..\t\t" - "1.."} -} - -njs_test { - {"'abc'.\t\t" - "'abc'."} -} - -# Global completions, global vars -njs_test { - {"var AA = 1; var AAA = 2\r\n" - "var AA = 1; var AAA = 2\r\nundefined\r\n>> "} - {"AA\t\t" - "AA*AAA*"} -} - -njs_test { - {"var zz = 1\r\n" - "var zz = 1\r\nundefined\r\n>> "} - {"1 + z\t\r\n" - "1 + z*z*\r\n2"} -} - -njs_test { - {"unknown_var\t\t" - "unknown_var"} -} - -njs_test { - {"unknown_var.\t\t" - "unknown_var."} -} - -# An object's level completions -njs_test { - {"var o = {zz:1, zb:2}\r\n" - "var o = {zz:1, zb:2}\r\nundefined\r\n>> "} - {"o.z\t\t" - "o.zb*o.zz"} -} - -njs_test { - {"var d = new Date()\r\n" - "var d = new Date()\r\nundefined\r\n>> "} - {"d.to\t\t" - "d.toDateString*d.toLocaleDateString*d.toString"} -} - -njs_test { - {"var o = {a:new Date()}\r\n" - "var o = {a:new Date()}\r\nundefined\r\n>> "} - {"o.a.to\t\t" - "o.a.toDateString*o.a.toLocaleDateString*o.a.toString"} -} - -njs_test { - {"var o = {a:1,b:2,333:'t'}\r\n" - "var o = {a:1,b:2,333:'t'}\r\nundefined\r\n>> "} - {"o.3\t\t" - "o.3"} -} - -njs_test { - {"var a = Array(5000000); a.aab = 1; a.aac = 2\r\n" - "var a = Array(5000000); a.aab = 1; a.aac = 2\r\n2\r\n>> "} - {"a.\t\t" - "a.aab*"} -} - -njs_test { - {"var a = new Uint8Array([5,6,7,8,8]); a.aab = 1; a.aac = 2\r\n" - "var a = new Uint8Array(\\\[5,6,7,8,8]); a.aab = 1; a.aac = 2\r\n2\r\n>> "} - {"a.\t\t" - "a.aab*"} -} - # console dump njs_test { {"console.dump()\r\n" _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org https://mailman.nginx.org/mailman/listinfo/nginx-devel