Welcome! Log In Create A New Profile

Advanced

[njs] Shell: added completions for QuickJS engine.

Dmitry Volyntsev
March 19, 2024 02:00AM
details: https://hg.nginx.org/njs/rev/85313a068147
branches:
changeset: 2302:85313a068147
user: Dmitry Volyntsev <xeioex@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
Subject Author Views Posted

[njs] Shell: added completions for QuickJS engine.

Dmitry Volyntsev 109 March 19, 2024 02:00AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 181
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready