[gjs: 1/10] js: Move all functions requiring GjsContext out of jsapi-util



commit 685b32f01220c22f2713a6973c7eef3b7f81e90f
Author: Philip Chimento <philip chimento gmail com>
Date:   Wed Aug 29 23:08:32 2018 -0400

    js: Move all functions requiring GjsContext out of jsapi-util
    
    This moves gjs_eval_with_scope() and gjs_call_function_value() to be
    methods of GjsContextPrivate. Since they call into JS, they schedule an
    idle garbage collection afterwards, and so they depend on GjsContext to
    do the scheduling.
    
    Also merges gjs_schedule_gc_if_needed() into
    GjsContextPrivate::schedule_gc_if_needed().

 gi/boxed.cpp          |   9 ++--
 gi/closure.cpp        |   3 +-
 gi/foreign.cpp        |   6 ++-
 gi/function.cpp       |   2 +-
 gi/object.cpp         |   6 +--
 gi/repo.cpp           |   3 +-
 gjs/context-private.h |  10 ++++-
 gjs/context.cpp       | 112 +++++++++++++++++++++++++++++++++++++++++++++++++-
 gjs/importer.cpp      |   5 ++-
 gjs/jsapi-util.cpp    |  91 ----------------------------------------
 gjs/jsapi-util.h      |  15 -------
 gjs/module.cpp        |   4 +-
 modules/console.cpp   |   3 +-
 13 files changed, 143 insertions(+), 126 deletions(-)
---
diff --git a/gi/boxed.cpp b/gi/boxed.cpp
index e430efb3..eaae200b 100644
--- a/gi/boxed.cpp
+++ b/gi/boxed.cpp
@@ -317,11 +317,11 @@ boxed_invoke_constructor(JSContext             *context,
                          JS::HandleId           constructor_name,
                          JS::CallArgs&          args)
 {
+    GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
     JS::RootedObject js_constructor(context);
 
-    const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
-    if (!gjs_object_require_property(context, obj, NULL, atoms.constructor(),
-                                     &js_constructor))
+    if (!gjs_object_require_property(
+            context, obj, nullptr, gjs->atoms().constructor(), &js_constructor))
         return false;
 
     JS::RootedValue js_constructor_func(context);
@@ -329,8 +329,7 @@ boxed_invoke_constructor(JSContext             *context,
                                      constructor_name, &js_constructor_func))
         return false;
 
-    return gjs_call_function_value(context, nullptr, js_constructor_func,
-                                   args, args.rval());
+    return gjs->call_function(nullptr, js_constructor_func, args, args.rval());
 }
 
 GJS_JSAPI_RETURN_CONVENTION
diff --git a/gi/closure.cpp b/gi/closure.cpp
index 5bf082d3..afc3d3e3 100644
--- a/gi/closure.cpp
+++ b/gi/closure.cpp
@@ -240,7 +240,8 @@ gjs_closure_invoke(GClosure                   *closure,
                           " - closure %p", closure);
     }
 
-    gjs_schedule_gc_if_needed(context);
+    GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
+    gjs->schedule_gc_if_needed();
     return true;
 }
 
diff --git a/gi/foreign.cpp b/gi/foreign.cpp
index ac4ef624..bb65da49 100644
--- a/gi/foreign.cpp
+++ b/gi/foreign.cpp
@@ -28,6 +28,7 @@
 
 #include "arg.h"
 #include "foreign.h"
+#include "gjs/context-private.h"
 #include "gjs/jsapi-wrapper.h"
 
 static struct {
@@ -75,8 +76,9 @@ gjs_foreign_load_foreign_module(JSContext *context,
         //        and only execute this statement if isn't
         script = g_strdup_printf("imports.%s;", gi_namespace);
         JS::RootedValue retval(context);
-        if (!gjs_eval_with_scope(context, nullptr, script, strlen(script),
-                                 "<internal>", &retval)) {
+        GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
+        if (!gjs->eval_with_scope(nullptr, script, strlen(script), "<internal>",
+                                  &retval)) {
             g_critical("ERROR importing foreign module %s\n", gi_namespace);
             g_free(script);
             return false;
diff --git a/gi/function.cpp b/gi/function.cpp
index eb8ace4a..8973b677 100644
--- a/gi/function.cpp
+++ b/gi/function.cpp
@@ -472,7 +472,7 @@ out:
     }
 
     gjs_callback_trampoline_unref(trampoline);
-    gjs_schedule_gc_if_needed(context);
+    gjs->schedule_gc_if_needed();
 
     JS_EndRequest(context);
 }
diff --git a/gi/object.cpp b/gi/object.cpp
index e6687caa..63ea5e7b 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -1678,13 +1678,13 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
      * might be traced and we will end up dereferencing a null pointer */
     JS_SetPrivate(object, ObjectInstance::new_for_js_object(context, object));
 
-    const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
+    GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
     if (!gjs_object_require_property(context, object, "GObject instance",
-                                     atoms.init(), &initer))
+                                     gjs->atoms().init(), &initer))
         return false;
 
     argv.rval().setUndefined();
-    ret = gjs_call_function_value(context, object, initer, argv, argv.rval());
+    ret = gjs->call_function(object, initer, argv, argv.rval());
 
     if (argv.rval().isUndefined())
         argv.rval().setObject(*object);
diff --git a/gi/repo.cpp b/gi/repo.cpp
index e5284000..e4e0ca3f 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -149,7 +149,8 @@ static bool resolve_namespace_object(JSContext* context,
               "Defined namespace '%s' %p in GIRepository %p", ns_name.get(),
               gi_namespace.get(), repo_obj.get());
 
-    gjs_schedule_gc_if_needed(context);
+    GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
+    gjs->schedule_gc_if_needed();
     return true;
 }
 
diff --git a/gjs/context-private.h b/gjs/context-private.h
index a87c6811..3b499c90 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -128,9 +128,17 @@ class GjsContextPrivate {
     GJS_JSAPI_RETURN_CONVENTION
     bool eval(const char* script, ssize_t script_len, const char* filename,
               int* exit_status_p, GError** error);
+    GJS_JSAPI_RETURN_CONVENTION
+    bool eval_with_scope(JS::HandleObject scope_object, const char* script,
+                         ssize_t script_len, const char* filename,
+                         JS::MutableHandleValue retval);
+    GJS_JSAPI_RETURN_CONVENTION
+    bool call_function(JS::HandleObject this_obj, JS::HandleValue func_val,
+                       const JS::HandleValueArray& args,
+                       JS::MutableHandleValue rval);
 
     void schedule_gc(void) { schedule_gc_internal(true); }
-    void schedule_gc_if_needed(void) { schedule_gc_internal(false); }
+    void schedule_gc_if_needed(void);
 
     void exit(uint8_t exit_code);
     GJS_USE bool should_exit(uint8_t* exit_code_p) const;
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 3f70d04f..6fc48c78 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -28,6 +28,8 @@
 #include <unistd.h>
 
 #include <array>
+#include <codecvt>
+#include <locale>
 #include <unordered_map>
 
 #include <gio/gio.h>
@@ -574,6 +576,20 @@ void GjsContextPrivate::schedule_gc_internal(bool force_gc) {
                                               nullptr);
 }
 
+/*
+ * GjsContextPrivate::schedule_gc_if_needed:
+ *
+ * Does a minor GC immediately if the JS engine decides one is needed, but also
+ * schedules a full GC in the next idle time.
+ */
+void GjsContextPrivate::schedule_gc_if_needed(void) {
+    // We call JS_MaybeGC immediately, but defer a check for a full GC cycle
+    // to an idle handler.
+    JS_MaybeGC(m_cx);
+
+    schedule_gc_internal(false);
+}
+
 void GjsContextPrivate::exit(uint8_t exit_code) {
     g_assert(!m_should_exit);
     m_should_exit = true;
@@ -812,8 +828,7 @@ bool GjsContextPrivate::eval(const char* script, ssize_t script_len,
         gjs_profiler_start(m_profiler);
 
     JS::RootedValue retval(m_cx);
-    bool ok = gjs_eval_with_scope(m_cx, nullptr, script, script_len, filename,
-                                  &retval);
+    bool ok = eval_with_scope(nullptr, script, script_len, filename, &retval);
 
     /* The promise job queue should be drained even on error, to finish
      * outstanding async tasks before the context is torn down. Drain after
@@ -892,6 +907,99 @@ gjs_context_eval_file(GjsContext    *js_context,
                             exit_status_p, error);
 }
 
+/*
+ * GjsContextPrivate::eval_with_scope:
+ * @scope_object: an object to use as the global scope, or nullptr
+ * @script: JavaScript program encoded in UTF-8
+ * @script_len: length of @script
+ * @filename: filename to use as the origin of @script
+ * @retval: location for the return value of @script
+ *
+ * Executes @script with a local scope so that nothing from the script leaks out
+ * into the global scope.
+ * If @scope_object is given, then everything that @script placed in the global
+ * namespace is defined on @scope_object.
+ * Otherwise, the global definitions are just discarded.
+ */
+bool GjsContextPrivate::eval_with_scope(JS::HandleObject scope_object,
+                                        const char* script, ssize_t script_len,
+                                        const char* filename,
+                                        JS::MutableHandleValue retval) {
+    int start_line_number = 1;
+    JSAutoRequest ar(m_cx);
+    size_t real_len = script_len;
+
+    if (script_len < 0)
+        real_len = strlen(script);
+
+    script = gjs_strip_unix_shebang(script, &real_len, &start_line_number);
+
+    /* log and clear exception if it's set (should not be, normally...) */
+    if (JS_IsExceptionPending(m_cx)) {
+        g_warning("eval_in_scope called with a pending exception");
+        return false;
+    }
+
+    JS::RootedObject eval_obj(m_cx, scope_object);
+    if (!eval_obj)
+        eval_obj = JS_NewPlainObject(m_cx);
+
+    JS::CompileOptions options(m_cx);
+    options.setFileAndLine(filename, start_line_number).setSourceIsLazy(true);
+
+    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
+    std::u16string utf16_string = convert.from_bytes(script);
+    JS::SourceBufferHolder buf(utf16_string.c_str(), utf16_string.size(),
+                               JS::SourceBufferHolder::NoOwnership);
+
+    JS::AutoObjectVector scope_chain(m_cx);
+    if (!scope_chain.append(eval_obj)) {
+        JS_ReportOutOfMemory(m_cx);
+        return false;
+    }
+
+    if (!JS::Evaluate(m_cx, scope_chain, options, buf, retval))
+        return false;
+
+    schedule_gc_if_needed();
+
+    if (JS_IsExceptionPending(m_cx)) {
+        g_warning(
+            "EvaluateScript returned true but exception was pending; "
+            "did somebody call gjs_throw() without returning false?");
+        return false;
+    }
+
+    gjs_debug(GJS_DEBUG_CONTEXT, "Script evaluation succeeded");
+
+    return true;
+}
+
+/*
+ * GjsContextPrivate::call_function:
+ * @this_obj: Object to use as the 'this' for the function call
+ * @func_val: Callable to call, as a JS value
+ * @args: Arguments to pass to the callable
+ * @rval: Location for the return value
+ *
+ * Use this instead of JS_CallFunctionValue(), because it schedules a GC if
+ * one is needed. It's good practice to check if a GC should be run every time
+ * we return from JS back into C++.
+ */
+bool GjsContextPrivate::call_function(JS::HandleObject this_obj,
+                                      JS::HandleValue func_val,
+                                      const JS::HandleValueArray& args,
+                                      JS::MutableHandleValue rval) {
+    JSAutoRequest ar(m_cx);
+
+    if (!JS_CallFunctionValue(m_cx, this_obj, func_val, args, rval))
+        return false;
+
+    schedule_gc_if_needed();
+
+    return true;
+}
+
 bool
 gjs_context_define_string_array(GjsContext  *js_context,
                                 const char    *array_name,
diff --git a/gjs/importer.cpp b/gjs/importer.cpp
index 9b315821..7a9a60e9 100644
--- a/gjs/importer.cpp
+++ b/gjs/importer.cpp
@@ -308,6 +308,7 @@ import_module_init(JSContext       *context,
     gsize script_len = 0;
     GError *error = NULL;
 
+    GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
     JS::RootedValue ignored(context);
 
     if (!(g_file_load_contents(file, NULL, &script, &script_len, NULL, &error))) {
@@ -325,8 +326,8 @@ import_module_init(JSContext       *context,
 
     full_path = g_file_get_parse_name (file);
 
-    if (!gjs_eval_with_scope(context, module_obj, script, script_len,
-                             full_path, &ignored))
+    if (!gjs->eval_with_scope(module_obj, script, script_len, full_path,
+                              &ignored))
         goto out;
 
     ret = true;
diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
index 1641db85..817352be 100644
--- a/gjs/jsapi-util.cpp
+++ b/gjs/jsapi-util.cpp
@@ -524,26 +524,6 @@ gjs_log_exception(JSContext  *context)
     return retval;
 }
 
-bool
-gjs_call_function_value(JSContext                  *context,
-                        JS::HandleObject            obj,
-                        JS::HandleValue             fval,
-                        const JS::HandleValueArray& args,
-                        JS::MutableHandleValue      rval)
-{
-    bool result;
-
-    JS_BeginRequest(context);
-
-    result = JS_CallFunctionValue(context, obj, fval, args, rval);
-
-    if (result)
-        gjs_schedule_gc_if_needed(context);
-
-    JS_EndRequest(context);
-    return result;
-}
-
 #ifdef __linux__
 static void
 _linux_get_self_process_size (gulong *vm_size,
@@ -633,17 +613,6 @@ gjs_maybe_gc (JSContext *context)
     gjs_gc_if_needed(context);
 }
 
-void
-gjs_schedule_gc_if_needed (JSContext *context)
-{
-    /* We call JS_MaybeGC immediately, but defer a check for a full
-     * GC cycle to an idle handler.
-     */
-    JS_MaybeGC(context);
-
-    GjsContextPrivate::from_cx(context)->schedule_gc_if_needed();
-}
-
 /**
  * gjs_strip_unix_shebang:
  *
@@ -697,63 +666,3 @@ gjs_strip_unix_shebang(const char  *script,
 
     return script;
 }
-
-bool
-gjs_eval_with_scope(JSContext             *context,
-                    JS::HandleObject       object,
-                    const char            *script,
-                    ssize_t                script_len,
-                    const char            *filename,
-                    JS::MutableHandleValue retval)
-{
-    int start_line_number = 1;
-    JSAutoRequest ar(context);
-    size_t real_len = script_len;
-
-    if (script_len < 0)
-        real_len = strlen(script);
-
-    script = gjs_strip_unix_shebang(script,
-                                    &real_len,
-                                    &start_line_number);
-
-    /* log and clear exception if it's set (should not be, normally...) */
-    if (JS_IsExceptionPending(context)) {
-        g_warning("gjs_eval_in_scope called with a pending exception");
-        return false;
-    }
-
-    JS::RootedObject eval_obj(context, object);
-    if (!eval_obj)
-        eval_obj = JS_NewPlainObject(context);
-
-    JS::CompileOptions options(context);
-    options.setFileAndLine(filename, start_line_number);
-
-    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
-    std::u16string utf16_string = convert.from_bytes(script);
-    JS::SourceBufferHolder buf(utf16_string.c_str(), utf16_string.size(),
-                               JS::SourceBufferHolder::NoOwnership);
-
-    JS::AutoObjectVector scope_chain(context);
-    if (!scope_chain.append(eval_obj)) {
-        JS_ReportOutOfMemory(context);
-        return false;
-    }
-
-    if (!JS::Evaluate(context, scope_chain, options, buf, retval))
-        return false;
-
-    gjs_schedule_gc_if_needed(context);
-
-    if (JS_IsExceptionPending(context)) {
-        g_warning("EvaluateScript returned true but exception was pending; "
-                  "did somebody call gjs_throw() without returning false?");
-        return false;
-    }
-
-    gjs_debug(GJS_DEBUG_CONTEXT,
-              "Script evaluation succeeded");
-
-    return true;
-}
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 55cfa666..40d93309 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -250,12 +250,6 @@ GJS_USE
 char *gjs_value_debug_string(JSContext      *context,
                              JS::HandleValue value);
 
-GJS_JSAPI_RETURN_CONVENTION
-bool gjs_call_function_value(JSContext                  *context,
-                             JS::HandleObject            obj,
-                             JS::HandleValue             fval,
-                             const JS::HandleValueArray& args,
-                             JS::MutableHandleValue      rval);
 
 void gjs_warning_reporter(JSContext     *cx,
                           JSErrorReport *report);
@@ -315,17 +309,8 @@ bool        gjs_unichar_from_string          (JSContext       *context,
 /* Functions intended for more "internal" use */
 
 void gjs_maybe_gc (JSContext *context);
-void gjs_schedule_gc_if_needed(JSContext *cx);
 void gjs_gc_if_needed(JSContext *cx);
 
-GJS_JSAPI_RETURN_CONVENTION
-bool gjs_eval_with_scope(JSContext             *context,
-                         JS::HandleObject       object,
-                         const char            *script,
-                         ssize_t                script_len,
-                         const char            *filename,
-                         JS::MutableHandleValue retval);
-
 GJS_USE
 const char * gjs_strip_unix_shebang(const char *script,
                                     size_t     *script_len,
diff --git a/gjs/module.cpp b/gjs/module.cpp
index e2ab5709..ff180f37 100644
--- a/gjs/module.cpp
+++ b/gjs/module.cpp
@@ -26,6 +26,7 @@
 
 #include <gio/gio.h>
 
+#include "gjs/context-private.h"
 #include "jsapi-util.h"
 #include "jsapi-wrapper.h"
 #include "module.h"
@@ -110,7 +111,8 @@ class GjsModule {
         if (!JS::Evaluate(cx, scope_chain, options, buf, &ignored_retval))
             return false;
 
-        gjs_schedule_gc_if_needed(cx);
+        GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx);
+        gjs->schedule_gc_if_needed();
 
         gjs_debug(GJS_DEBUG_IMPORTER, "Importing module %s succeeded", m_name);
 
diff --git a/modules/console.cpp b/modules/console.cpp
index 20d2f048..05384a04 100644
--- a/modules/console.cpp
+++ b/modules/console.cpp
@@ -286,7 +286,8 @@ gjs_console_eval_and_print(JSContext  *cx,
             return false;
     }
 
-    gjs_schedule_gc_if_needed(cx);
+    GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx);
+    gjs->schedule_gc_if_needed();
 
     if (result.isUndefined())
         return true;


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