[gjs/ewlsh/refactor-argv: 6/6] Refactor ARGV handling and add system.programArgs.




commit 9436394b5db672500334fd92816340e572a3116c
Author: Evan Welsh <contact evanwelsh com>
Date:   Sat Jan 30 14:02:40 2021 -0800

    Refactor ARGV handling and add system.programArgs.

 gjs/atoms.h                          |  1 +
 gjs/console.cpp                      | 11 ++---------
 gjs/context-private.h                |  6 ++++++
 gjs/context.cpp                      | 23 +++++++++++++++++++++++
 gjs/context.h                        |  5 +++++
 installed-tests/js/testSystem.js     | 17 +++++++++++++++++
 modules/esm/system.js                |  2 ++
 modules/script/_bootstrap/default.js |  8 ++++++++
 modules/system.cpp                   | 35 ++++++++++++++++++++++++++++++++++-
 9 files changed, 98 insertions(+), 10 deletions(-)
---
diff --git a/gjs/atoms.h b/gjs/atoms.h
index 424f6ea2..25c9904d 100644
--- a/gjs/atoms.h
+++ b/gjs/atoms.h
@@ -52,6 +52,7 @@ class JSTracer;
     macro(overrides, "overrides") \
     macro(param_spec, "ParamSpec") \
     macro(parent_module, "__parentModule__") \
+    macro(program_args, "programArgs") \
     macro(program_invocation_name, "programInvocationName") \
     macro(program_path, "programPath") \
     macro(prototype, "prototype") \
diff --git a/gjs/console.cpp b/gjs/console.cpp
index 7c39f39e..49c82299 100644
--- a/gjs/console.cpp
+++ b/gjs/console.cpp
@@ -165,16 +165,9 @@ check_script_args_for_stray_gjs_args(int           argc,
 int define_argv_and_eval_script(GjsContext* js_context, int argc,
                                 char* const* argv, const char* script,
                                 size_t len, const char* filename) {
-    GError* error = nullptr;
-
-    /* prepare command line arguments */
-    if (!gjs_context_define_string_array(
-            js_context, "ARGV", argc, const_cast<const char**>(argv), &error)) {
-        g_critical("Failed to define ARGV: %s", error->message);
-        g_clear_error(&error);
-        return 1;
-    }
+    gjs_context_set_argv(js_context, argc, const_cast<const char**>(argv));
 
+    GError* error = nullptr;
     /* evaluate the script */
     int code = 0;
     if (exec_as_module) {
diff --git a/gjs/context-private.h b/gjs/context-private.h
index 6f5cba47..b0746192 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -10,8 +10,10 @@
 #include <stdint.h>
 #include <sys/types.h>  // for ssize_t
 
+#include <string>
 #include <type_traits>  // for is_same
 #include <unordered_map>
+#include <vector>
 
 #include <glib-object.h>
 #include <glib.h>
@@ -83,6 +85,8 @@ class GjsContextPrivate : public JS::JobQueue {
 
     GjsAtoms* m_atoms;
 
+    std::vector<std::string> m_args;
+
     JobQueueStorage m_job_queue;
     unsigned m_idle_drain_handler;
 
@@ -189,6 +193,8 @@ class GjsContextPrivate : public JS::JobQueue {
     void set_should_listen_sigusr2(bool value) {
         m_should_listen_sigusr2 = value;
     }
+    void set_args(std::vector<std::string>&& args);
+    GJS_JSAPI_RETURN_CONVENTION JSObject* build_args_array();
     [[nodiscard]] bool is_owner_thread() const {
         return m_owner_thread == g_thread_self();
     }
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 59310a69..d54ab6ef 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -599,6 +599,14 @@ GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
     }
 }
 
+void GjsContextPrivate::set_args(std::vector<std::string>&& args) {
+    m_args = args;
+}
+
+JSObject* GjsContextPrivate::build_args_array() {
+    return gjs_build_string_array(m_cx, m_args);
+}
+
 static void
 gjs_context_get_property (GObject     *object,
                           guint        prop_id,
@@ -1337,6 +1345,13 @@ gjs_context_define_string_array(GjsContext  *js_context,
         strings = {array_values, array_values + array_length};
     }
 
+    // ARGV is a special case to preserve backwards compatibility.
+    if (strcmp(array_name, "ARGV") == 0) {
+        gjs->set_args(std::move(strings));
+
+        return true;
+    }
+
     JS::RootedObject global_root(gjs->context(), gjs->global());
     if (!gjs_define_string_array(gjs->context(), global_root, array_name,
                                  strings, JSPROP_READONLY | JSPROP_PERMANENT)) {
@@ -1351,6 +1366,14 @@ gjs_context_define_string_array(GjsContext  *js_context,
     return true;
 }
 
+void gjs_context_set_argv(GjsContext* js_context, ssize_t array_length,
+                          const char** array_values) {
+    g_return_if_fail(GJS_IS_CONTEXT(js_context));
+    GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
+    std::vector<std::string> args(array_values, array_values + array_length);
+    gjs->set_args(std::move(args));
+}
+
 static GjsContext *current_context;
 
 GjsContext *
diff --git a/gjs/context.h b/gjs/context.h
index 380b98b2..d2ed9eb4 100644
--- a/gjs/context.h
+++ b/gjs/context.h
@@ -13,6 +13,7 @@
 
 #include <stdbool.h>    /* IWYU pragma: keep */
 #include <stdint.h>
+#include <sys/types.h> /* for ssize_t */
 
 #ifndef _WIN32
 #    include <signal.h> /* for siginfo_t */
@@ -65,6 +66,10 @@ GJS_EXPORT GJS_USE bool gjs_context_define_string_array(
     GjsContext* js_context, const char* array_name, gssize array_length,
     const char** array_values, GError** error);
 
+GJS_EXPORT void gjs_context_set_argv(GjsContext* js_context,
+                                     ssize_t array_length,
+                                     const char** array_values);
+
 GJS_EXPORT GJS_USE GList* gjs_context_get_all(void);
 
 GJS_EXPORT GJS_USE GjsContext* gjs_context_get_current(void);
diff --git a/installed-tests/js/testSystem.js b/installed-tests/js/testSystem.js
index e805f7bb..ce4963db 100644
--- a/installed-tests/js/testSystem.js
+++ b/installed-tests/js/testSystem.js
@@ -61,3 +61,20 @@ describe('System.programPath', function () {
         expect(System.programPath).toBe(null);
     });
 });
+
+describe('System.programArgs', function () {
+    it('System.programArgs is an array', function () {
+        expect(Array.isArray(System.programArgs)).toBeTruthy();
+    });
+
+    it('modifications persist', function () {
+        System.programArgs.push('--foo');
+        expect(System.programArgs.pop()).toBe('--foo');
+    });
+
+    it('System.programArgs is equal to ARGV', function () {
+        expect(System.programArgs).toEqual(ARGV);
+        ARGV.push('--foo');
+        expect(System.programArgs.pop()).toBe('--foo');
+    });
+});
diff --git a/modules/esm/system.js b/modules/esm/system.js
index c12d8de5..7143e8a3 100644
--- a/modules/esm/system.js
+++ b/modules/esm/system.js
@@ -9,6 +9,7 @@ export let {
     clearDateCaches,
     exit,
     gc,
+    programArgs,
     programInvocationName,
     programPath,
     refcount,
@@ -21,6 +22,7 @@ export default {
     clearDateCaches,
     exit,
     gc,
+    programArgs,
     programInvocationName,
     programPath,
     refcount,
diff --git a/modules/script/_bootstrap/default.js b/modules/script/_bootstrap/default.js
index e31d80cb..952d7fe3 100644
--- a/modules/script/_bootstrap/default.js
+++ b/modules/script/_bootstrap/default.js
@@ -8,6 +8,14 @@
     const {print, printerr, log, logError} = imports._print;
 
     Object.defineProperties(exports, {
+        ARGV: {
+            configurable: false,
+            enumerable: true,
+            get() {
+                // Wait until after bootstrap or programArgs won't be set.
+                return imports.system.programArgs;
+            },
+        },
         print: {
             configurable: false,
             enumerable: true,
diff --git a/modules/system.cpp b/modules/system.cpp
index 936584c2..8c321d45 100644
--- a/modules/system.cpp
+++ b/modules/system.cpp
@@ -193,6 +193,30 @@ static JSFunctionSpec module_funcs[] = {
     JS_FN("clearDateCaches", gjs_clear_date_caches, 0, GJS_MODULE_PROP_FLAGS),
     JS_FS_END};
 
+static bool get_program_args(JSContext* cx, unsigned argc, JS::Value* vp) {
+    static const size_t SLOT_ARGV = 0;
+
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    GjsContextPrivate* priv = GjsContextPrivate::from_cx(cx);
+
+    JS::RootedValue v_argv(
+        cx, js::GetFunctionNativeReserved(&args.callee(), SLOT_ARGV));
+
+    if (v_argv.isUndefined()) {
+        // First time this property is accessed, build the array
+        JS::RootedObject argv(cx, priv->build_args_array());
+        if (!argv)
+            return false;
+        js::SetFunctionNativeReserved(&args.callee(), SLOT_ARGV,
+                                      JS::ObjectValue(*argv));
+        args.rval().setObject(*argv);
+    } else {
+        args.rval().set(v_argv);
+    }
+
+    return true;
+}
+
 bool
 gjs_js_define_system_stuff(JSContext              *context,
                            JS::MutableHandleObject module)
@@ -213,7 +237,13 @@ gjs_js_define_system_stuff(JSContext              *context,
             return false;
     }
 
-    return gjs_string_from_utf8(context, program_name,
+    JS::RootedObject program_args_getter(
+        context,
+        JS_GetFunctionObject(js::NewFunctionByIdWithReserved(
+            context, get_program_args, 0, 0, gjs->atoms().program_args())));
+
+    return program_args_getter &&
+           gjs_string_from_utf8(context, program_name,
                                 &v_program_invocation_name) &&
            /* The name is modeled after program_invocation_name, part of glibc
             */
@@ -224,6 +254,9 @@ gjs_js_define_system_stuff(JSContext              *context,
            JS_DefinePropertyById(context, module, gjs->atoms().program_path(),
                                  v_program_path,
                                  GJS_MODULE_PROP_FLAGS | JSPROP_READONLY) &&
+           JS_DefinePropertyById(context, module, gjs->atoms().program_args(),
+                                 program_args_getter, nullptr,
+                                 GJS_MODULE_PROP_FLAGS | JSPROP_GETTER) &&
            JS_DefinePropertyById(context, module, gjs->atoms().version(),
                                  GJS_VERSION,
                                  GJS_MODULE_PROP_FLAGS | JSPROP_READONLY);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]