[gjs/esm-local-imports: 1/6] console: Add --module evaluation option.



commit 0e33eb327134e6e1bc8b1577fb051379f5b51c40
Author: Evan Welsh <noreply evanwelsh com>
Date:   Sat Apr 11 11:04:57 2020 -0500

    console: Add --module evaluation option.

 gjs/console.cpp       | 41 +++++++++++++++++++++++-----
 gjs/context-private.h |  8 ++++++
 gjs/context.cpp       | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 gjs/context.h         | 12 +++++++++
 4 files changed, 128 insertions(+), 8 deletions(-)
---
diff --git a/gjs/console.cpp b/gjs/console.cpp
index 9e335594..e3729ed7 100644
--- a/gjs/console.cpp
+++ b/gjs/console.cpp
@@ -38,6 +38,7 @@
 #include <glib.h>
 
 #include <gjs/gjs.h>
+#include <gjs/jsapi-util.h>
 
 static char **include_path = NULL;
 static char **coverage_prefixes = NULL;
@@ -47,6 +48,7 @@ static char *command = NULL;
 static gboolean print_version = false;
 static gboolean print_js_version = false;
 static gboolean debugging = false;
+static gboolean exec_as_module = false;
 static bool enable_profiler = false;
 
 static gboolean parse_profile_arg(const char *, const char *, void *, GError **);
@@ -60,6 +62,8 @@ static GOptionEntry entries[] = {
     { "coverage-prefix", 'C', 0, G_OPTION_ARG_STRING_ARRAY, &coverage_prefixes, "Add the prefix PREFIX to 
the list of files to generate coverage info for", "PREFIX" },
     { "coverage-output", 0, 0, G_OPTION_ARG_STRING, &coverage_output_path, "Write coverage output to a 
directory DIR. This option is mandatory when using --coverage-path", "DIR", },
     { "include-path", 'I', 0, G_OPTION_ARG_STRING_ARRAY, &include_path, "Add the directory DIR to the list 
of directories to search for js files.", "DIR" },
+    { "module", 'm', 0, G_OPTION_ARG_NONE, &exec_as_module,
+                        "Execute the input as a js module (implies strict mode)" },
     { "profile", 0, G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME,
         G_OPTION_ARG_CALLBACK, reinterpret_cast<void *>(&parse_profile_arg),
         "Enable the profiler and write output to FILE (default: gjs-$PID.syscap)",
@@ -201,7 +205,23 @@ int define_argv_and_eval_script(GjsContext* js_context, int argc,
 
     /* evaluate the script */
     int code;
-    if (!gjs_context_eval(js_context, script, len, filename, &code, &error)) {
+    if (exec_as_module) {
+        GjsAutoUnref<GFile> output = g_file_new_for_commandline_arg(filename);
+        char* full_path = g_file_get_path(output);
+        if (!gjs_context_register_module(js_context, full_path, full_path,
+                                         script, len, &error)) {
+            g_printerr("%s\n", error->message);
+            code = 1;
+        }
+
+        uint8_t code_8 = 0;
+        if (!gjs_context_eval_module(js_context, full_path, &code_8, &error)) {
+            code = code_8;
+            if (!g_error_matches(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT))
+                g_critical("%s", error->message);
+        }
+    } else if (!gjs_context_eval(js_context, script, len, filename, &code,
+                                 &error)) {
         if (!g_error_matches(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT))
             g_critical("%s", error->message);
         g_clear_error(&error);
@@ -269,6 +289,7 @@ main(int argc, char **argv)
     print_version = false;
     print_js_version = false;
     debugging = false;
+    exec_as_module = false;
     g_option_context_set_ignore_unknown_options(context, false);
     g_option_context_set_help_enabled(context, true);
     if (!g_option_context_parse_strv(context, &gjs_argv, &error)) {
@@ -298,7 +319,14 @@ main(int argc, char **argv)
         filename = "<command line>";
         program_name = gjs_argv[0];
     } else if (gjs_argc == 1) {
-        script = g_strdup("const Console = imports.console; Console.interact();");
+        if (exec_as_module) {
+            script = g_strdup(
+                "throw new Error('Console interaction is not implemented with "
+                "the --module option. Exiting with error.')");
+        } else {
+            script = g_strdup(
+                "const Console = imports.console; Console.interact();");
+        }
         len = strlen(script);
         filename = "<stdin>";
         program_name = gjs_argv[0];
@@ -335,11 +363,10 @@ main(int argc, char **argv)
         g_unsetenv("GJS_TRACE_FD");         /* ignore env var in eval() */
     }
 
-    js_context = (GjsContext*) g_object_new(GJS_TYPE_CONTEXT,
-                                            "search-path", include_path,
-                                            "program-name", program_name,
-                                            "profiler-enabled", enable_profiler,
-                                            NULL);
+    js_context = (GjsContext*)g_object_new(
+        GJS_TYPE_CONTEXT, "search-path", include_path, "program-name",
+        program_name, "profiler-enabled", enable_profiler, "modules",
+        exec_as_module, NULL);
 
     env_coverage_output_path = g_getenv("GJS_COVERAGE_OUTPUT");
     if (env_coverage_output_path != NULL) {
diff --git a/gjs/context-private.h b/gjs/context-private.h
index 1b1e5562..e109743f 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -141,6 +141,7 @@ class GjsContextPrivate : public JS::JobQueue {
     bool m_force_gc : 1;
     bool m_draining_job_queue : 1;
     bool m_should_profile : 1;
+    bool m_exec_as_module : 1;
     bool m_should_listen_sigusr2 : 1;
 
     int64_t m_sweep_begin_time;
@@ -190,6 +191,7 @@ class GjsContextPrivate : public JS::JobQueue {
     void set_program_name(char* value) { m_program_name = value; }
     void set_search_path(char** value) { m_search_path = value; }
     void set_should_profile(bool value) { m_should_profile = value; }
+    void set_execute_as_module(bool value) { m_exec_as_module = value; }
     void set_should_listen_sigusr2(bool value) {
         m_should_listen_sigusr2 = value;
     }
@@ -218,6 +220,9 @@ class GjsContextPrivate : public JS::JobQueue {
                          ssize_t script_len, const char* filename,
                          JS::MutableHandleValue retval);
     GJS_JSAPI_RETURN_CONVENTION
+    bool eval_module(const char* identifier, uint8_t* exit_code_p,
+                     GError** error);
+    GJS_JSAPI_RETURN_CONVENTION
     bool call_function(JS::HandleObject this_obj, JS::HandleValue func_val,
                        const JS::HandleValueArray& args,
                        JS::MutableHandleValue rval);
@@ -245,6 +250,9 @@ class GjsContextPrivate : public JS::JobQueue {
     void register_unhandled_promise_rejection(uint64_t id, GjsAutoChar&& stack);
     void unregister_unhandled_promise_rejection(uint64_t id);
 
+    bool register_module(const char* identifier, const char* filename,
+                         const char* mod_text, size_t mod_len, GError** error);
+
     void set_sweeping(bool value);
 
     static void trace(JSTracer* trc, void* data);
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 04f28f73..0063056d 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -33,6 +33,7 @@
 #    include <process.h>
 #endif
 
+#include <codecvt>
 #include <new>
 #include <string>  // for u16string
 #include <unordered_map>
@@ -81,6 +82,7 @@
 #include "gjs/error-types.h"
 #include "gjs/global.h"
 #include "gjs/importer.h"
+#include "gjs/jsapi-util-args.h"
 #include "gjs/jsapi-util.h"
 #include "gjs/mem.h"
 #include "gjs/native.h"
@@ -150,6 +152,7 @@ enum {
     PROP_PROGRAM_NAME,
     PROP_PROFILER_ENABLED,
     PROP_PROFILER_SIGUSR2,
+    PROP_MODULES_ENABLED,
 };
 
 static GMutex contexts_lock;
@@ -299,6 +302,13 @@ gjs_context_class_init(GjsContextClass *klass)
     g_object_class_install_property(object_class, PROP_PROFILER_SIGUSR2, pspec);
     g_param_spec_unref(pspec);
 
+    pspec = g_param_spec_boolean(
+        "modules", "Modules enabled",
+        "Whether to profile JS code run by this context", FALSE,
+        GParamFlags(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+    g_object_class_install_property(object_class, PROP_MODULES_ENABLED, pspec);
+    g_param_spec_unref(pspec);
+
     /* For GjsPrivate */
     {
 #ifdef G_OS_WIN32
@@ -571,8 +581,11 @@ gjs_context_set_property (GObject      *object,
     case PROP_PROFILER_SIGUSR2:
         gjs->set_should_listen_sigusr2(g_value_get_boolean(value));
         break;
+    case PROP_MODULES_ENABLED:
+        gjs->set_execute_as_module(g_value_get_boolean(value));
+        break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
     }
 }
@@ -936,6 +949,28 @@ gjs_context_eval(GjsContext   *js_context,
     return gjs->eval(script, script_len, filename, exit_status_p, error);
 }
 
+bool gjs_context_eval_module(GjsContext* js_context, const char* name,
+                             uint8_t* exit_code, GError** error) {
+    g_return_val_if_fail(GJS_IS_CONTEXT(js_context), false);
+
+    GjsAutoUnref<GjsContext> js_context_ref(js_context, GjsAutoTakeOwnership());
+
+    GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
+    return gjs->eval_module(name, exit_code, error);
+}
+
+bool gjs_context_register_module(GjsContext* js_context, const char* identifier,
+                                 const char* filename, const char* module,
+                                 size_t module_len, GError** error) {
+    g_return_val_if_fail(GJS_IS_CONTEXT(js_context), false);
+
+    GjsAutoUnref<GjsContext> js_context_ref(js_context, GjsAutoTakeOwnership());
+
+    GjsContextPrivate* gjs = GjsContextPrivate::from_object(js_context);
+    return gjs->register_module(identifier, filename, module, module_len,
+                                error);
+}
+
 bool GjsContextPrivate::eval(const char* script, ssize_t script_len,
                              const char* filename, int* exit_status_p,
                              GError** error) {
@@ -1008,6 +1043,28 @@ bool GjsContextPrivate::eval(const char* script, ssize_t script_len,
     return true;
 }
 
+bool GjsContextPrivate::eval_module(const char* identifier,
+                                    uint8_t* exit_status_p, GError** error) {
+    // TODO Implement eval_module
+    g_error(
+        "GjsContextPrivate::eval_module is not implemented. Exiting with "
+        "error.");
+
+    return false;
+}
+
+bool GjsContextPrivate::register_module(const char* identifier,
+                                        const char* filename,
+                                        const char* module, size_t module_len,
+                                        GError** error) {
+    // TODO Implement register_module
+    g_warning(
+        "GjsContextPrivate::register_module is not yet implemented. Printing "
+        "module text...");
+    g_warning("%s", module);
+    return true;
+}
+
 bool
 gjs_context_eval_file(GjsContext    *js_context,
                       const char    *filename,
@@ -1027,6 +1084,22 @@ gjs_context_eval_file(GjsContext    *js_context,
                             exit_status_p, error);
 }
 
+bool gjs_context_eval_module_file(GjsContext* js_context, const char* filename,
+                                  uint8_t* exit_status_p, GError** error) {
+    char* module;
+    size_t module_len;
+    GjsAutoUnref<GFile> file = g_file_new_for_commandline_arg(filename);
+
+    if (!g_file_load_contents(file, nullptr, &module, &module_len, nullptr,
+                              error))
+        return false;
+    GjsAutoChar module_ref = module;
+
+    return gjs_context_register_module(js_context, filename, filename, module,
+                                       module_len, error) &&
+           gjs_context_eval_module(js_context, filename, exit_status_p, error);
+}
+
 /*
  * GjsContextPrivate::eval_with_scope:
  * @scope_object: an object to use as the global scope, or nullptr
diff --git a/gjs/context.h b/gjs/context.h
index 13be25b9..1b1846ab 100644
--- a/gjs/context.h
+++ b/gjs/context.h
@@ -29,6 +29,7 @@
 #endif
 
 #include <stdbool.h>    /* IWYU pragma: keep */
+#include <stdint.h>
 
 #ifndef _WIN32
 #    include <signal.h> /* for siginfo_t */
@@ -61,10 +62,21 @@ GJS_EXPORT GJS_USE bool gjs_context_eval_file(GjsContext* js_context,
                                               const char* filename,
                                               int* exit_status_p,
                                               GError** error);
+GJS_EXPORT GJS_USE bool gjs_context_eval_module_file(GjsContext* js_context,
+                                                     const char* filename,
+                                                     uint8_t* exit_status_p,
+                                                     GError** error);
 GJS_EXPORT GJS_USE bool gjs_context_eval(GjsContext* js_context,
                                          const char* script, gssize script_len,
                                          const char* filename,
                                          int* exit_status_p, GError** error);
+GJS_EXPORT GJS_USE bool gjs_context_register_module(
+    GjsContext* context, const char* name, const char* filename,
+    const char* module, size_t module_len, GError** error);
+GJS_EXPORT GJS_USE bool gjs_context_eval_module(GjsContext* context,
+                                                const char* name,
+                                                uint8_t* exit_code,
+                                                GError** error);
 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);


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