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

Reply via email to