[gjs/wip/xulrunner-1.9.3-rebase7: 12/13] Replace call context with a concept of "current context"



commit 5aed6bb0dfc400a9194419b2fb3aa88a50c64295
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Tue Sep 28 10:46:22 2010 -0400

    Replace call context with a concept of "current context"
    
    Using multiple nested contexts on the same thread isn't normal usage of
    Spidermonkey and triggers bugs in Spidermonkey that might not be caught
    in upstream testing. (It is theoretically supported, to at least some extent.)
    
    When calling a callback, the best context to use is the current context.
    If that's not available, then using the default context from the GjsContext
    is usually sensible.
    
    Add functions:
     gjs_runtime_push_context()
     gjs_runtime_pop_context()
     gjs_runtime_get_current_context()
    
    One potential concern here is the extensive use of GDataset to get data
    from a context, which involves locking overhead and hash table lookups and
    can be slow; since we can't use GJS with multiple JS_Runtimes in the same
    process due to the limitations of toggle references, perhaps we should enforce
    that and use global variables.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=622896

 gi/closure.c           |   20 ++++--
 gi/closure.h           |    3 +-
 gi/function.c          |   30 ++++++--
 gi/value.c             |    6 +-
 gjs/context.c          |   21 +++++-
 gjs/jsapi-util.c       |  166 +++++++++++++++++++++++++++++++++---------------
 gjs/jsapi-util.h       |    9 ++-
 modules/dbus-exports.c |   36 +++++------
 modules/dbus.c         |   24 +++++--
 modules/mainloop.c     |   13 +++-
 10 files changed, 224 insertions(+), 104 deletions(-)
---
diff --git a/gi/closure.c b/gi/closure.c
index c6392d4..d88348b 100644
--- a/gi/closure.c
+++ b/gi/closure.c
@@ -248,7 +248,6 @@ gjs_closure_invoke(GClosure *closure,
     c = (Closure*) closure;
 
     check_context_valid(c);
-    context = c->context;
 
     if (c->obj == NULL) {
         /* We were destroyed; become a no-op */
@@ -256,12 +255,13 @@ gjs_closure_invoke(GClosure *closure,
         return;
     }
 
+    context = gjs_runtime_get_current_context(c->runtime);
     JS_BeginRequest(context);
 
     if (JS_IsExceptionPending(context)) {
         gjs_debug_closure("Exception was pending before invoking callback??? "
                           "Not expected");
-        gjs_log_exception(c->context, NULL);
+        gjs_log_exception(context, NULL);
     }
 
     if (!gjs_call_function_value(context,
@@ -287,16 +287,24 @@ gjs_closure_invoke(GClosure *closure,
     JS_EndRequest(context);
 }
 
-JSContext*
-gjs_closure_get_context(GClosure *closure)
+gboolean
+gjs_closure_is_valid(GClosure *closure)
 {
     Closure *c;
 
     c = (Closure*) closure;
 
-    check_context_valid(c);
+    return c->context != NULL;
+}
+
+JSRuntime*
+gjs_closure_get_runtime(GClosure *closure)
+{
+    Closure *c;
+
+    c = (Closure*) closure;
 
-    return c->context;
+    return c->runtime;
 }
 
 JSObject*
diff --git a/gi/closure.h b/gi/closure.h
index 1ec452c..c8a821b 100644
--- a/gi/closure.h
+++ b/gi/closure.h
@@ -37,7 +37,8 @@ void       gjs_closure_invoke        (GClosure     *closure,
                                       int           argc,
                                       jsval        *argv,
                                       jsval        *retval);
-JSContext* gjs_closure_get_context   (GClosure     *closure);
+JSRuntime* gjs_closure_get_runtime   (GClosure     *closure);
+gboolean   gjs_closure_is_valid      (GClosure     *closure);
 JSObject*  gjs_closure_get_callable  (GClosure     *closure);
 
 G_END_DECLS
diff --git a/gi/function.c b/gi/function.c
index 1132484..b56deb6 100644
--- a/gi/function.c
+++ b/gi/function.c
@@ -77,7 +77,7 @@ static struct {
 } global_destroy_trampoline;
 
 typedef struct {
-    JSContext *context;
+    JSRuntime *runtime;
     GICallableInfo *info;
     jsval js_function;
     ffi_cif cif;
@@ -128,7 +128,11 @@ function_new_resolve(JSContext *context,
 static void
 gjs_callback_trampoline_free(GjsCallbackTrampoline *trampoline)
 {
-    JS_RemoveValueRoot(trampoline->context, &trampoline->js_function);
+    JSContext *context;
+
+    context = gjs_runtime_get_current_context(trampoline->runtime);
+
+    JS_RemoveValueRoot(context, &trampoline->js_function);
     g_callable_info_free_closure(trampoline->info, trampoline->closure);
     g_base_info_unref( (GIBaseInfo*) trampoline->info);
     g_slice_free(GjsCallbackTrampoline, trampoline);
@@ -147,6 +151,7 @@ gjs_callback_closure(ffi_cif *cif,
                      void **args,
                      void *data)
 {
+    JSContext *context;
     GjsCallbackTrampoline *trampoline;
     int i, n_args, n_jsargs;
     jsval *jsargs, rval;
@@ -156,6 +161,9 @@ gjs_callback_closure(ffi_cif *cif,
     trampoline = data;
     g_assert(trampoline);
 
+    context = gjs_runtime_get_current_context(trampoline->runtime);
+    JS_BeginRequest(context);
+
     n_args = g_callable_info_get_n_args(trampoline->info);
 
     g_assert(n_args >= 0);
@@ -172,14 +180,14 @@ gjs_callback_closure(ffi_cif *cif,
         if (g_type_info_get_tag(&type_info) == GI_TYPE_TAG_VOID)
             continue;
 
-        if (!gjs_value_from_g_argument(trampoline->context,
+        if (!gjs_value_from_g_argument(context,
                                        &jsargs[n_jsargs++],
                                        &type_info,
                                        args[i]))
             goto out;
     }
 
-    if (!JS_CallFunctionValue(trampoline->context,
+    if (!JS_CallFunctionValue(context,
                               NULL,
                               trampoline->js_function,
                               n_jsargs,
@@ -190,7 +198,7 @@ gjs_callback_closure(ffi_cif *cif,
 
     g_callable_info_load_return_type(trampoline->info, &ret_type);
 
-    if (!gjs_value_to_g_argument(trampoline->context,
+    if (!gjs_value_to_g_argument(context,
                                  rval,
                                  &ret_type,
                                  "callback",
@@ -205,16 +213,18 @@ gjs_callback_closure(ffi_cif *cif,
 
 out:
     if (!success) {
-        gjs_log_exception (trampoline->context, NULL);
+        gjs_log_exception (context, NULL);
 
         /* Fill in the result with some hopefully neutral value */
         g_callable_info_load_return_type(trampoline->info, &ret_type);
-        gjs_g_argument_init_default (trampoline->context, &ret_type, result);
+        gjs_g_argument_init_default (context, &ret_type, result);
     }
 
     if (trampoline->scope == GI_SCOPE_TYPE_ASYNC) {
         completed_trampolines = g_slist_prepend(completed_trampolines, trampoline);
     }
+
+    JS_EndRequest(context);
 }
 
 /* The global entry point for any invocations of GDestroyNotify;
@@ -268,7 +278,7 @@ gjs_callback_trampoline_new(JSContext      *context,
     g_assert(JS_TypeOfValue(context, function) == JSTYPE_FUNCTION);
 
     trampoline = g_slice_new(GjsCallbackTrampoline);
-    trampoline->context = context;
+    trampoline->runtime = JS_GetRuntime(context);
     trampoline->info = callable_info;
     g_base_info_ref((GIBaseInfo*)trampoline->info);
     trampoline->js_function = function;
@@ -562,11 +572,15 @@ gjs_invoke_c_function(JSContext      *context,
          * separately */
     }
 
+    gjs_runtime_push_context(JS_GetRuntime(context), context);
+
     g_assert_cmpuint(in_args_pos, ==, (guint8)function->invoker.cif.nargs);
     g_assert_cmpuint(inout_args_pos, ==, inout_args_len);
     g_assert_cmpuint(out_args_pos, ==, out_args_len);
     ffi_call(&(function->invoker.cif), function->invoker.native_address, &return_value, in_arg_pointers);
 
+    gjs_runtime_pop_context(JS_GetRuntime(context));
+
     /* Return value and out arguments are valid only if invocation doesn't
      * return error. In arguments need to be released always.
      */
diff --git a/gi/value.c b/gi/value.c
index 39285c4..af3cf3d 100644
--- a/gi/value.c
+++ b/gi/value.c
@@ -54,6 +54,7 @@ closure_marshal(GClosure        *closure,
                 gpointer         invocation_hint,
                 gpointer         marshal_data)
 {
+    JSRuntime *runtime;
     JSContext *context;
     int argc;
     jsval *argv;
@@ -65,12 +66,13 @@ closure_marshal(GClosure        *closure,
                       "Marshal closure %p",
                       closure);
 
-    context = gjs_closure_get_context(closure);
-    if (context == NULL) {
+    if (!gjs_closure_is_valid(closure)) {
         /* We were destroyed; become a no-op */
         return;
     }
 
+    runtime = gjs_closure_get_runtime(closure);
+    context = gjs_runtime_get_current_context(runtime);
     JS_BeginRequest(context);
 
     argc = n_param_values;
diff --git a/gjs/context.c b/gjs/context.c
index ad907e6..ef866e3 100644
--- a/gjs/context.c
+++ b/gjs/context.c
@@ -354,6 +354,8 @@ gjs_context_dispose(GObject *object)
                   "Destroying JS context%s",
                   js_context->is_load_context ? " (load context)" : "");
 
+        if (js_context->we_own_runtime)
+            gjs_runtime_set_default_context(js_context->runtime, NULL);
         JS_DestroyContext(js_context->context);
         js_context->context = NULL;
     }
@@ -363,7 +365,6 @@ gjs_context_dispose(GObject *object)
             /* Avoid keeping JSContext with a dangling pointer to the
              * runtime.
              */
-            gjs_runtime_clear_call_context(js_context->runtime);
             gjs_runtime_clear_load_context(js_context->runtime);
 
             gjs_debug(GJS_DEBUG_CONTEXT,
@@ -628,6 +629,18 @@ gjs_context_constructor (GType                  type,
                            4, GJS_MODULE_PROP_FLAGS))
         gjs_fatal("Failed to define printerr function");
 
+    /* We need to know what the default context is, since it's the context whose
+     * global object is used to load imported JS modules. We currently say that
+     * it's the context of the runtime's owner, but if we needed to support
+     * externally created runtimes, we could define it in some other fashion.
+     */
+    if (js_context->we_own_runtime) {
+        gjs_runtime_set_default_context(js_context->runtime, js_context->context);
+    } else {
+        if (gjs_runtime_get_default_context(js_context->runtime) == NULL)
+            gjs_fatal("GjsContext created for a runtime not owned by GJS");
+    }
+
     /* If we created the root importer in the load context,
      * there would be infinite recursion since the load context
      * is a GjsContext
@@ -815,6 +828,7 @@ gjs_context_eval(GjsContext *js_context,
     /* JS_EvaluateScript requires a request even though it sort of seems like
      * it means we're always in a request?
      */
+    gjs_runtime_push_context(js_context->runtime, js_context->context);
     JS_BeginRequest(js_context->context);
 
     retval = JSVAL_VOID;
@@ -879,9 +893,10 @@ gjs_context_eval(GjsContext *js_context,
         }
     }
 
-    g_object_unref(G_OBJECT(js_context));
-
     JS_EndRequest(js_context->context);
+    gjs_runtime_pop_context(js_context->runtime);
+
+    g_object_unref(G_OBJECT(js_context));
 
     return success;
 }
diff --git a/gjs/jsapi-util.c b/gjs/jsapi-util.c
index 8599646..47b26c8 100644
--- a/gjs/jsapi-util.c
+++ b/gjs/jsapi-util.c
@@ -43,6 +43,13 @@ gjs_util_error_quark (void)
 
 typedef struct {
     GHashTable *dynamic_classes;
+
+    JSContext *default_context;
+
+    JSContext *default_context;
+
+    /* In a thread-safe future we'd keep this in per-thread data */
+    GSList *context_stack;
 } RuntimeData;
 
 typedef struct {
@@ -50,6 +57,9 @@ typedef struct {
     JSClass *static_class;
 } DynamicJSClass;
 
+static RuntimeData* get_data_from_runtime(JSRuntime *runtime);
+static RuntimeData* get_data_from_context(JSContext *context);
+
 void*
 gjs_runtime_get_data(JSRuntime      *runtime,
                      const char     *name)
@@ -116,60 +126,123 @@ gjs_runtime_clear_load_context(JSRuntime *runtime)
     gjs_debug(GJS_DEBUG_CONTEXT, "Load context cleared");
 }
 
-/* The call context exists because when we call a closure, the scope
- * chain on the context is set to the original scope chain of the
- * closure. We want to avoid using any existing context (especially
- * the load context) because the closure "messes up" the scope chain
- * on the context.
+/**
+ * gjs_runtime_push_context:
+ * @runtime: a #JSRuntime
+ * @context: a #JSRuntime
  *
- * Unlike the load context, which is expected to be an eternal
- * singleton, we only cache the call context for efficiency. It would
- * be just as workable to recreate it for each call.
+ * Make @context the currently active context for @runtime.
+ * A stack is maintained, although switching between different contexts
+ * in a nested fashion in the same thread ecan trigger misbehavior in
+ * Spidermonkey, so is not recommended. This does not call JS_BeginRequest();
+ * the caller needs to do it themselves.
+ *
+ * Should be called when calling from Javascript into native code that
+ * could result in callbacks back to Javascript. The context stack allows
+ * the callbacks to find the right context to use via gjs_get_current_context().
+ *
+ * When GJS is made threadsafe, this needs to maintain a per-thread stack
+ * rather than a global stack.
  */
-JSContext*
-gjs_runtime_get_call_context(JSRuntime *runtime)
+void
+gjs_runtime_push_context(JSRuntime *runtime,
+                         JSContext *context)
 {
-    GjsContext *context;
+    RuntimeData *rd;
 
-    context = gjs_runtime_get_data(runtime, "gjs-call-context");
-    if (context == NULL) {
-        gjs_debug(GJS_DEBUG_CONTEXT,
-                  "Creating call context for runtime %p",
-                  runtime);
-        context = g_object_new(GJS_TYPE_CONTEXT,
-                               "runtime", runtime,
-                               NULL);
-        gjs_runtime_set_data(runtime,
-                             "gjs-call-context",
-                             context,
-                             g_object_unref);
-    }
+    rd = get_data_from_runtime(runtime);
 
-    return (JSContext*)gjs_context_get_native_context(context);
+    rd->context_stack = g_slist_prepend(rd->context_stack, context);
 }
 
-static JSContext*
-gjs_runtime_peek_call_context(JSRuntime *runtime)
+/**
+ * gjs_runtime_pop_context:
+ * @runtime: a #JSRuntime
+ *
+ * Pops a context pushed onto the stack of active contexts by
+ * gjs_runtime_push_context().
+ */
+void
+gjs_runtime_pop_context(JSRuntime *runtime)
 {
-    GjsContext *context;
+    RuntimeData *rd;
 
-    context = gjs_runtime_get_data(runtime, "gjs-call-context");
-    if (context == NULL) {
-        return NULL;
+    rd = get_data_from_runtime(runtime);
+
+    rd->context_stack = g_slist_delete_link(rd->context_stack, rd->context_stack);
+}
+
+/**
+ * gjs_runtime_set_default_context:
+ * @runtime: a #JSRuntime
+ * @context: a #JSContext
+ *
+ * Makes @context the default context for @runtime. The default context is the
+ * context used for executing callbacks when no other context is active.
+ * This generally should only be called by GJS - GJS sets the default context
+ * when #GjsContext creates a runtime, and subsequent calls to this function
+ * will produce an error.
+ */
+void
+gjs_runtime_set_default_context(JSRuntime *runtime,
+                                JSContext *context)
+{
+    RuntimeData *rd;
+
+    rd = get_data_from_runtime(runtime);
+
+    if (context != NULL) {
+        if (rd->default_context != NULL)
+            gjs_fatal("gjs_runtime_set_default_context() called twice on the same JSRuntime");
+        rd->default_context = context;
     } else {
-        return (JSContext*)gjs_context_get_native_context(context);
+        rd->default_context = NULL;
     }
 }
 
-void
-gjs_runtime_clear_call_context(JSRuntime *runtime)
+/**
+ * gjs_runtime_get_default_context:
+ * @runtime: a #JSRuntime
+ *
+ * Gets the default context for @runtime. Generally you should use
+ * gjs_runtime_get_current_context() instead.
+ *
+ * Return value: the default context, or %NULL if GJS hasn't been initialized
+ *  for the runtime or is being shut down.
+ */
+JSContext *
+gjs_runtime_get_default_context(JSRuntime *runtime)
 {
-    gjs_debug(GJS_DEBUG_CONTEXT, "Clearing call context");
-    gjs_runtime_set_data(runtime,
-                         "gjs-call-context",
-                         NULL,
-                         NULL);
-    gjs_debug(GJS_DEBUG_CONTEXT, "Call context cleared");
+    RuntimeData *rd;
+
+    rd = get_data_from_runtime(runtime);
+
+    return rd->default_context;
+}
+
+/**
+ * gjs_runtime_get_current_context:
+ * @runtime: a #JSRuntime
+ *
+ * Gets the right context to use for code that doesn't already have a JSContext
+ * passed to it, like a callback from native code. If a context is currently
+ * active (see gjs_push_context()), uses that, otherwise uses the default
+ * context for the runtime.
+ *
+ * Return value: the current context, or %NULL if GJS hasn't been initialized
+ *  for the runtime or is being shut down.
+ */
+JSContext *
+gjs_runtime_get_current_context(JSRuntime *runtime)
+{
+    RuntimeData *rd;
+
+    rd = get_data_from_runtime(runtime);
+
+    if (rd->context_stack)
+        return rd->context_stack->data;
+    else
+        return rd->default_context;
 }
 
 static JSClass global_class = {
@@ -766,7 +839,6 @@ gjs_explain_scope(JSContext  *context,
                   const char *title)
 {
     JSContext *load_context;
-    JSContext *call_context;
     JSObject *global;
     JSObject *parent;
     GString *chain;
@@ -776,11 +848,9 @@ gjs_explain_scope(JSContext  *context,
               title);
 
     load_context = gjs_runtime_peek_load_context(JS_GetRuntime(context));
-    call_context = gjs_runtime_peek_call_context(JS_GetRuntime(context));
 
     JS_BeginRequest(context);
     JS_BeginRequest(load_context);
-    JS_BeginRequest(call_context);
 
     (void)JS_EnterLocalRootScope(context);
 
@@ -788,7 +858,6 @@ gjs_explain_scope(JSContext  *context,
               "  Context: %p %s",
               context,
               context == load_context ? "(LOAD CONTEXT)" :
-              context == call_context ? "(CALL CONTEXT)" :
               "");
 
     global = JS_GetGlobalObject(context);
@@ -816,7 +885,6 @@ gjs_explain_scope(JSContext  *context,
 
     JS_LeaveLocalRootScope(context);
 
-    JS_EndRequest(call_context);
     JS_EndRequest(load_context);
     JS_EndRequest(context);
 }
@@ -1028,18 +1096,12 @@ gjs_call_function_value(JSContext      *context,
                         jsval          *rval)
 {
     JSBool result;
-    JSContext *call_context;
 
     JS_BeginRequest(context);
 
-    call_context = gjs_runtime_get_call_context(JS_GetRuntime(context));
-    JS_BeginRequest(call_context);
-
-    result = JS_CallFunctionValue(call_context, obj, fval,
+    result = JS_CallFunctionValue(context, obj, fval,
                                   argc, argv, rval);
-    gjs_move_exception(call_context, context);
 
-    JS_EndRequest(call_context);
     JS_EndRequest(context);
     return result;
 }
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 78a0ca7..6796b53 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -202,11 +202,16 @@ void        gjs_runtime_set_data             (JSRuntime       *runtime,
                                                  const char      *name,
                                                  void            *data,
                                                  GDestroyNotify   dnotify);
+JSContext*  gjs_runtime_get_current_context  (JSRuntime       *runtime);
+void        gjs_runtime_set_default_context  (JSRuntime       *runtime,
+                                              JSContext       *context);
+JSContext*  gjs_runtime_get_default_context  (JSRuntime       *runtime);
+void        gjs_runtime_push_context         (JSRuntime       *runtime,
+                                              JSContext       *context);
+void        gjs_runtime_pop_context          (JSRuntime       *runtime);
 JSContext*  gjs_runtime_get_load_context     (JSRuntime       *runtime);
 JSContext*  gjs_runtime_peek_load_context    (JSRuntime       *runtime);
 void        gjs_runtime_clear_load_context   (JSRuntime       *runtime);
-JSContext*  gjs_runtime_get_call_context     (JSRuntime       *runtime);
-void        gjs_runtime_clear_call_context   (JSRuntime       *runtime);
 gboolean    gjs_object_has_property          (JSContext       *context,
                                               JSObject        *obj,
                                               const char      *property_name);
diff --git a/modules/dbus-exports.c b/modules/dbus-exports.c
index e0f6397..535bb9c 100644
--- a/modules/dbus-exports.c
+++ b/modules/dbus-exports.c
@@ -57,15 +57,8 @@ typedef struct {
      * around.  However, the alternatives are complicated, and
      * SpiderMonkey currently uses mark-and-sweep, so I think this
      * should be fine. We'll see I guess.
-     *
-     * Saving the context is also questionable: Objects can jump between
-     * contexts (they are only permanently bound to a runtime). However,
-     * we assume, AFAIK safely, that in our usage model the context where
-     * we created the exports object will always be the right one to
-     * invoke incoming calls. A context contains the global scope and an
-     * execution stack.
      */
-    JSContext  *context;
+    JSRuntime *runtime;
     JSObject   *object;
 
     DBusBusType which_bus;
@@ -1516,6 +1509,7 @@ on_message(DBusConnection *connection,
 {
     const char *path;
     DBusHandlerResult result;
+    JSContext *context;
     JSObject *obj, *dir_obj = NULL;
     const char *method_name;
     char *async_method_name;
@@ -1530,15 +1524,17 @@ on_message(DBusConnection *connection,
     if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-    JS_BeginRequest(priv->context);
+    context = gjs_runtime_get_current_context(priv->runtime);
+
+    JS_BeginRequest(context);
     method_value = JSVAL_VOID;
-    JS_AddValueRoot(priv->context, &method_value);
+    JS_AddValueRoot(context, &method_value);
 
     result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
     path = dbus_message_get_path(message);
 
-    obj = find_js_property_by_path(priv->context,
+    obj = find_js_property_by_path(context,
                                    priv->object,
                                    path, &dir_obj);
 
@@ -1557,7 +1553,7 @@ on_message(DBusConnection *connection,
                   path);
 
         if (dir_obj != NULL)
-            result = handle_introspect(priv->context,
+            result = handle_introspect(context,
                                        connection,
                                        dir_obj, obj,
                                        message);
@@ -1582,7 +1578,7 @@ on_message(DBusConnection *connection,
                   "Properties request %s on %s",
                   method_name,
                   iface ? iface : "MISSING INTERFACE");
-        result = handle_properties(priv->context, connection,
+        result = handle_properties(context, connection,
                                    obj, message, method_name);
         goto out;
     }
@@ -1590,7 +1586,7 @@ on_message(DBusConnection *connection,
     async_method_name = g_strdup_printf("%sAsync", method_name);
 
     /* try first if an async version exists */
-    if (find_method(priv->context,
+    if (find_method(context,
                     obj,
                     async_method_name,
                     &method_value)) {
@@ -1599,7 +1595,7 @@ on_message(DBusConnection *connection,
                   "Invoking async method %s on JS obj at dbus path %s",
                   async_method_name, path);
 
-        reply = invoke_js_async_from_dbus(priv->context,
+        reply = invoke_js_async_from_dbus(context,
                                           priv->which_bus,
                                           message,
                                           obj,
@@ -1608,7 +1604,7 @@ on_message(DBusConnection *connection,
         result = DBUS_HANDLER_RESULT_HANDLED;
 
         /* otherwise try the sync version */
-    } else if (find_method(priv->context,
+    } else if (find_method(context,
                            obj,
                            method_name,
                            &method_value)) {
@@ -1617,7 +1613,7 @@ on_message(DBusConnection *connection,
                   "Invoking method %s on JS obj at dbus path %s",
                   method_name, path);
 
-        reply = invoke_js_from_dbus(priv->context,
+        reply = invoke_js_from_dbus(context,
                                     message,
                                     obj,
                                     JSVAL_TO_OBJECT(method_value));
@@ -1639,8 +1635,8 @@ on_message(DBusConnection *connection,
  out:
     if (async_method_name)
         g_free(async_method_name);
-    JS_RemoveValueRoot(priv->context, &method_value);
-    JS_EndRequest(priv->context);
+    JS_RemoveValueRoot(context, &method_value);
+    JS_EndRequest(context);
     return result;
 }
 
@@ -1708,7 +1704,7 @@ exports_constructor(JSContext *context,
     gjs_debug_lifecycle(GJS_DEBUG_DBUS,
                         "exports constructor, obj %p priv %p", obj, priv);
 
-    priv->context = context;
+    priv->runtime = JS_GetRuntime(context);
     priv->object = obj;
 
     return JS_TRUE;
diff --git a/modules/dbus.c b/modules/dbus.c
index 561105d..eade473 100644
--- a/modules/dbus.c
+++ b/modules/dbus.c
@@ -278,6 +278,18 @@ complete_call(JSContext   *context,
     return JS_TRUE;
 }
 
+static JSContext *
+get_callback_context(GClosure *closure)
+{
+    JSRuntime *runtime;
+
+    if (!gjs_closure_is_valid(closure))
+        return NULL;
+
+    runtime = gjs_closure_get_runtime(closure);
+    return gjs_runtime_get_current_context(runtime);
+}
+
 static void
 pending_notify(DBusPendingCall *pending,
                void            *user_data)
@@ -291,7 +303,7 @@ pending_notify(DBusPendingCall *pending,
 
     closure = user_data;
 
-    context = gjs_closure_get_context(closure);
+    context = get_callback_context(closure);
 
     gjs_debug(GJS_DEBUG_DBUS,
               "Notified of reply to async call closure %p context %p",
@@ -636,7 +648,7 @@ signal_handler_callback(DBusConnection *connection,
         return;
     }
 
-    context = gjs_closure_get_context(handler->closure);
+    context = get_callback_context(handler->closure);
 
     if (!context) {
         /* The runtime is gone */
@@ -1007,7 +1019,7 @@ on_name_acquired(DBusConnection *connection,
 
     owner = data;
 
-    context = gjs_closure_get_context(owner->acquired_closure);
+    context = get_callback_context(owner->acquired_closure);
     if (context == NULL) {
         gjs_debug(GJS_DEBUG_DBUS,
                   "Closure destroyed before we could notify name acquired");
@@ -1046,7 +1058,7 @@ on_name_lost(DBusConnection *connection,
 
     owner = data;
 
-    context = gjs_closure_get_context(owner->lost_closure);
+    context = get_callback_context(owner->lost_closure);
     if (context == NULL) {
         gjs_debug(GJS_DEBUG_DBUS,
                   "Closure destroyed before we could notify name lost");
@@ -1227,7 +1239,7 @@ on_name_appeared(DBusConnection *connection,
 
     watcher = data;
 
-    context = gjs_closure_get_context(watcher->appeared_closure);
+    context = get_callback_context(watcher->appeared_closure);
     if (context == NULL) {
         gjs_debug(GJS_DEBUG_DBUS,
                   "Closure destroyed before we could notify name appeared");
@@ -1270,7 +1282,7 @@ on_name_vanished(DBusConnection *connection,
 
     watcher = data;
 
-    context = gjs_closure_get_context(watcher->vanished_closure);
+    context = get_callback_context(watcher->vanished_closure);
     if (context == NULL) {
         gjs_debug(GJS_DEBUG_DBUS,
                   "Closure destroyed before we could notify name vanished");
diff --git a/modules/mainloop.c b/modules/mainloop.c
index 0dde3d5..5a49da8 100644
--- a/modules/mainloop.c
+++ b/modules/mainloop.c
@@ -104,7 +104,10 @@ gjs_main_loop_run(JSContext *context,
               context);
     g_free(cancel_id);
 
+    gjs_runtime_push_context(JS_GetRuntime(context), context);
     g_main_loop_run(main_loop);
+    gjs_runtime_pop_context(JS_GetRuntime(context));
+
     g_main_loop_unref(main_loop);
     return JS_TRUE;
 }
@@ -115,15 +118,17 @@ closure_source_func(void *data)
     jsval retval;
     GClosure *closure;
     JSBool bool_val;
+    JSRuntime *runtime;
     JSContext *context;
 
     closure = data;
 
-    context = gjs_closure_get_context(closure);
-    if (context == NULL) {
-        /* closure is invalid now */
+    if (!gjs_closure_is_valid(closure))
         return FALSE;
-    }
+
+    runtime = gjs_closure_get_runtime(closure);
+    context = gjs_runtime_get_current_context(runtime);
+
     JS_BeginRequest(context);
 
     retval = JSVAL_VOID;



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