[gjs/refactor-globals: 3/4] global: Refactor to support multiple global types.



commit f8c0cf19635f4e233e9fc88d7cdd8badd378186f
Author: Evan Welsh <noreply evanwelsh com>
Date:   Sat Jul 18 01:31:33 2020 -0500

    global: Refactor to support multiple global types.

 gi/repo.cpp                           |   9 +-
 gjs/context.cpp                       |  13 +-
 gjs/coverage.cpp                      |   5 +-
 gjs/debugger.cpp                      |   6 +-
 gjs/global.cpp                        | 332 ++++++++++++++++++++++++++--------
 gjs/global.h                          |  80 +++++---
 gjs/jsapi-class.h                     |  19 +-
 modules/script/_bootstrap/debugger.js |   4 +-
 8 files changed, 342 insertions(+), 126 deletions(-)
---
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 08df95e1..25b4369d 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -601,7 +601,9 @@ lookup_override_function(JSContext             *cx,
 {
     JS::AutoSaveExceptionState saved_exc(cx);
 
-    JS::RootedValue importer(cx, gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS));
+    JS::RootedObject global(cx, gjs_get_import_global(cx));
+    JS::RootedValue importer(
+        cx, gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS));
     g_assert(importer.isObject());
 
     JS::RootedObject overridespkg(cx), module(cx);
@@ -644,8 +646,9 @@ JSObject*
 gjs_lookup_namespace_object_by_name(JSContext      *context,
                                     JS::HandleId    ns_name)
 {
-    JS::RootedValue importer(context,
-        gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS));
+    JS::RootedObject global(context, gjs_get_import_global(context));
+    JS::RootedValue importer(
+        context, gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS));
     g_assert(importer.isObject());
 
     JS::RootedObject repo(context), importer_obj(context, &importer.toObject());
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 04f28f73..892dfb0f 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -494,7 +494,9 @@ GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
 
     m_atoms = new GjsAtoms();
 
-    JS::RootedObject global(m_cx, gjs_create_global_object(m_cx));
+    JS::RootedObject global(
+        m_cx, gjs_create_global_object(cx, GjsGlobalType::DEFAULT));
+
     if (!global) {
         gjs_log_exception(m_cx);
         g_error("Failed to initialize global object");
@@ -519,14 +521,15 @@ GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
         g_error("Failed to create root importer");
     }
 
-    JS::Value v_importer = gjs_get_global_slot(m_cx, GJS_GLOBAL_SLOT_IMPORTS);
-    g_assert(((void) "Someone else already created root importer",
+    JS::Value v_importer = gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS);
+    g_assert(((void)"Someone else already created root importer",
               v_importer.isUndefined()));
 
-    gjs_set_global_slot(m_cx, GJS_GLOBAL_SLOT_IMPORTS,
+    gjs_set_global_slot(global, GjsGlobalSlot::IMPORTS,
                         JS::ObjectValue(*importer));
 
-    if (!gjs_define_global_properties(m_cx, global, "GJS", "default")) {
+    if (!gjs_define_global_properties(m_cx, global, GjsGlobalType::DEFAULT,
+                                      "GJS", "default")) {
         gjs_log_exception(m_cx);
         g_error("Failed to define properties on global object");
     }
diff --git a/gjs/coverage.cpp b/gjs/coverage.cpp
index cbeeafcb..12395078 100644
--- a/gjs/coverage.cpp
+++ b/gjs/coverage.cpp
@@ -350,8 +350,8 @@ bootstrap_coverage(GjsCoverage *coverage)
     JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context);
 
     JSObject *debuggee = gjs_get_import_global(context);
-    JS::RootedObject debugger_global(context,
-                                     gjs_create_global_object(context));
+    JS::RootedObject debugger_global(
+        context, gjs_create_global_object(context, GjsGlobalType::DEBUGGER));
     {
         JSAutoRealm ar(context, debugger_global);
         JS::RootedObject debuggeeWrapper(context, debuggee);
@@ -363,6 +363,7 @@ bootstrap_coverage(GjsCoverage *coverage)
         if (!JS_SetPropertyById(context, debugger_global, atoms.debuggee(),
                                 debuggeeWrapperValue) ||
             !gjs_define_global_properties(context, debugger_global,
+                                          GjsGlobalType::DEBUGGER,
                                           "GJS coverage", "coverage"))
             return false;
 
diff --git a/gjs/debugger.cpp b/gjs/debugger.cpp
index 682ce73d..30b6f88b 100644
--- a/gjs/debugger.cpp
+++ b/gjs/debugger.cpp
@@ -135,7 +135,8 @@ void gjs_context_setup_debugger_console(GjsContext* gjs) {
     auto cx = static_cast<JSContext*>(gjs_context_get_native_context(gjs));
 
     JS::RootedObject debuggee(cx, gjs_get_import_global(cx));
-    JS::RootedObject debugger_global(cx, gjs_create_global_object(cx));
+    JS::RootedObject debugger_global(
+        cx, gjs_create_global_object(cx, GjsGlobalType::DEBUGGER));
 
     // Enter realm of the debugger and initialize it with the debuggee
     JSAutoRealm ar(cx, debugger_global);
@@ -149,7 +150,8 @@ void gjs_context_setup_debugger_console(GjsContext* gjs) {
     JS::RootedValue v_wrapper(cx, JS::ObjectValue(*debuggee_wrapper));
     if (!JS_SetPropertyById(cx, debugger_global, atoms.debuggee(), v_wrapper) ||
         !JS_DefineFunctions(cx, debugger_global, debugger_funcs) ||
-        !gjs_define_global_properties(cx, debugger_global, "GJS debugger",
+        !gjs_define_global_properties(cx, debugger_global,
+                                      GjsGlobalType::DEBUGGER, "GJS debugger",
                                       "debugger"))
         gjs_log_exception(cx);
 }
diff --git a/gjs/global.cpp b/gjs/global.cpp
index 846c65f9..edff5e5c 100644
--- a/gjs/global.cpp
+++ b/gjs/global.cpp
@@ -3,6 +3,7 @@
  * Copyright (c) 2008  litl, LLC
  * Copyright (c) 2009 Red Hat, Inc.
  * Copyright (c) 2017  Philip Chimento <philip chimento gmail com>
+ * Copyright (c) 2020  Evan Welsh <contact evanwelsh com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -29,6 +30,8 @@
 
 #include <glib.h>
 
+#include <js/CallArgs.h>           // for CallArgs, CallArgsFromVp
+#include <js/CharacterEncoding.h>  // for JS_EncodeStringToUTF8
 #include <js/Class.h>
 #include <js/CompilationAndEvaluation.h>
 #include <js/CompileOptions.h>
@@ -39,6 +42,7 @@
 #include <js/RootingAPI.h>
 #include <js/SourceText.h>
 #include <js/TypeDecls.h>
+#include <js/Utility.h>  // for UniqueChars
 #include <jsapi.h>       // for AutoSaveExceptionState, ...
 
 #include "gjs/atoms.h"
@@ -46,50 +50,123 @@
 #include "gjs/engine.h"
 #include "gjs/global.h"
 #include "gjs/jsapi-util.h"
+#include "gjs/native.h"
 
 namespace mozilla {
 union Utf8Unit;
 }
 
-GJS_JSAPI_RETURN_CONVENTION
-static bool
-run_bootstrap(JSContext       *cx,
-              const char      *bootstrap_script,
-              JS::HandleObject global)
-{
-    GjsAutoChar uri = g_strdup_printf(
-        "resource:///org/gnome/gjs/modules/script/_bootstrap/%s.js",
-        bootstrap_script);
-
-    JSAutoRealm ar(cx, global);
-
-    JS::CompileOptions options(cx);
-    options.setFileAndLine(uri, 1).setSourceIsLazy(true);
-
-    char* script;
-    size_t script_len;
-    if (!gjs_load_internal_source(cx, uri, &script, &script_len))
-        return false;
-
-    JS::SourceText<mozilla::Utf8Unit> source;
-    if (!source.init(cx, script, script_len,
-                     JS::SourceOwnership::TakeOwnership))
-        return false;
-
-    JS::RootedScript compiled_script(cx, JS::Compile(cx, options, source));
-    if (!compiled_script)
-        return false;
-
-    JS::RootedValue ignored(cx);
-    return JS::CloneAndExecuteScript(cx, compiled_script, &ignored);
-}
+class GjsBaseGlobal {
+    static JSObject* base(JSContext* cx, const JSClass* clasp,
+                          JS::RealmCreationOptions options) {
+        options.setBigIntEnabled(true);
+        options.setFieldsEnabled(true);
+
+        JS::RealmBehaviors behaviors;
+        JS::RealmOptions compartment_options(options, behaviors);
+
+        JS::RootedObject global(
+            cx, JS_NewGlobalObject(cx, clasp, nullptr, JS::FireOnNewGlobalHook,
+                                   compartment_options));
+
+        if (!global)
+            return nullptr;
+
+        JSAutoRealm ac(cx, global);
+
+        if (!JS_InitReflectParse(cx, global) ||
+            !JS_DefineDebuggerObject(cx, global))
+            return nullptr;
+
+        return global;
+    }
+
+ protected:
+    GJS_USE
+    static JSObject* create(JSContext* cx, const JSClass* clasp) {
+        JS::RealmCreationOptions creation;
+        creation.setNewCompartmentAndZone();
+
+        return base(cx, clasp, creation);
+    }
+
+    GJS_USE
+    static JSObject* create_with_compartment(JSContext* cx,
+                                             JS::HandleObject existing,
+                                             const JSClass* clasp) {
+        JS::RealmCreationOptions creation;
+        creation.setExistingCompartment(existing);
+
+        return base(cx, clasp, creation);
+    }
+
+    GJS_JSAPI_RETURN_CONVENTION
+    static bool run_bootstrap(JSContext* cx, const char* bootstrap_script,
+                              JS::HandleObject global) {
+        GjsAutoChar uri = g_strdup_printf(
+            "resource:///org/gnome/gjs/modules/script/_bootstrap/%s.js",
+            bootstrap_script);
+
+        JSAutoRealm ar(cx, global);
+
+        JS::CompileOptions options(cx);
+        options.setFileAndLine(uri, 1).setSourceIsLazy(true);
+
+        char* script;
+        size_t script_len;
+        if (!gjs_load_internal_source(cx, uri, &script, &script_len))
+            return false;
+
+        JS::SourceText<mozilla::Utf8Unit> source;
+        if (!source.init(cx, script, script_len,
+                         JS::SourceOwnership::TakeOwnership))
+            return false;
+
+        JS::RootedScript compiled_script(cx, JS::Compile(cx, options, source));
+        if (!compiled_script)
+            return false;
+
+        JS::RootedValue ignored(cx);
+        return JS::CloneAndExecuteScript(cx, compiled_script, &ignored);
+    }
+
+    GJS_JSAPI_RETURN_CONVENTION
+    static bool load_native_module(JSContext* m_cx, unsigned argc,
+                                   JS::Value* vp) {
+        JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
+
+        // This function should never be directly exposed to user code, so we
+        // can be strict.
+        g_assert(argc == 1);
+        g_assert(argv[0].isString());
+
+        JS::RootedString str(m_cx, argv[0].toString());
+        JS::UniqueChars id(JS_EncodeStringToUTF8(m_cx, str));
+
+        if (!id)
+            return false;
+
+        JS::RootedObject native_obj(m_cx);
+
+        if (!gjs_load_native_module(m_cx, id.get(), &native_obj)) {
+            gjs_throw(m_cx, "Failed to load native module: %s", id.get());
+            return false;
+        }
+
+        argv.rval().setObject(*native_obj);
+        return true;
+    }
+};
 
 const JSClassOps defaultclassops = JS::DefaultGlobalClassOps;
 
-class GjsGlobal {
+class GjsGlobal : GjsBaseGlobal {
     static constexpr JSClass klass = {
+        // Jasmine depends on the class name "GjsGlobal" to detect GJS' global
+        // object.
         "GjsGlobal",
-        JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST),
+        JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
+            static_cast<uint32_t>(GjsGlobalSlot::LAST)),
         &defaultclassops,
     };
 
@@ -98,30 +175,14 @@ class GjsGlobal {
 
  public:
     GJS_USE
-    static JSObject *
-    create(JSContext *cx)
-    {
-        JS::RealmBehaviors behaviors;
-
-        JS::RealmCreationOptions creation;
-        creation.setFieldsEnabled(true);
-        creation.setBigIntEnabled(true);
-
-        JS::RealmOptions options(creation, behaviors);
-
-        JS::RootedObject global(
-            cx, JS_NewGlobalObject(cx, &GjsGlobal::klass, nullptr,
-                                   JS::FireOnNewGlobalHook, options));
-        if (!global)
-            return nullptr;
-
-        JSAutoRealm ar(cx, global);
-
-        if (!JS_InitReflectParse(cx, global) ||
-            !JS_DefineDebuggerObject(cx, global))
-            return nullptr;
+    static JSObject* create(JSContext* cx) {
+        return GjsBaseGlobal::create(cx, &klass);
+    }
 
-        return global;
+    GJS_USE
+    static JSObject* create_with_compartment(JSContext* cx,
+                                             JS::HandleObject cmp_global) {
+        return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
     }
 
     GJS_JSAPI_RETURN_CONVENTION
@@ -139,9 +200,10 @@ class GjsGlobal {
         // const_cast is allowed here if we never free the realm data
         JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
 
-        JS::Value v_importer = gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS);
-        g_assert(((void) "importer should be defined before passing null "
-                  "importer to GjsGlobal::define_properties",
+        JS::Value v_importer =
+            gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS);
+        g_assert(((void)"importer should be defined before passing null "
+                        "importer to GjsGlobal::define_properties",
                   v_importer.isObject()));
         JS::RootedObject root_importer(cx, &v_importer.toObject());
 
@@ -160,6 +222,52 @@ class GjsGlobal {
     }
 };
 
+class GjsDebuggerGlobal : GjsBaseGlobal {
+    static constexpr JSClass klass = {
+        "GjsDebuggerGlobal",
+        JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
+            static_cast<uint32_t>(GjsDebuggerGlobalSlot::LAST)),
+        &defaultclassops,
+    };
+
+    static constexpr JSFunctionSpec static_funcs[] = {
+        JS_FN("loadNative", &load_native_module, 1, 0), JS_FS_END};
+
+ public:
+    GJS_USE
+    static JSObject* create(JSContext* cx) {
+        return GjsBaseGlobal::create(cx, &klass);
+    }
+
+    GJS_USE
+    static JSObject* create_with_compartment(JSContext* cx,
+                                             JS::HandleObject cmp_global) {
+        return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
+    }
+
+    static bool define_properties(JSContext* cx, JS::HandleObject global,
+                                  const char* realm_name,
+                                  const char* bootstrap_script) {
+        const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
+        if (!JS_DefinePropertyById(cx, global, atoms.window(), global,
+                                   JSPROP_READONLY | JSPROP_PERMANENT) ||
+            !JS_DefineFunctions(cx, global, GjsDebuggerGlobal::static_funcs))
+            return false;
+
+        JS::Realm* realm = JS::GetObjectRealmOrNull(global);
+        g_assert(realm && "Global object must be associated with a realm");
+        // const_cast is allowed here if we never free the realm data
+        JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
+
+        if (bootstrap_script) {
+            if (!run_bootstrap(cx, bootstrap_script, global))
+                return false;
+        }
+
+        return true;
+    }
+};
+
 /**
  * gjs_create_global_object:
  * @cx: a #JSContext
@@ -169,10 +277,51 @@ class GjsGlobal {
  * Returns: the created global object on success, nullptr otherwise, in which
  * case an exception is pending on @cx
  */
-JSObject *
-gjs_create_global_object(JSContext *cx)
-{
-    return GjsGlobal::create(cx);
+JSObject* gjs_create_global_object(JSContext* cx, GjsGlobalType global_type,
+                                   JS::HandleObject current_global) {
+    if (current_global) {
+        switch (global_type) {
+            case GjsGlobalType::DEFAULT:
+                return GjsGlobal::create_with_compartment(cx, current_global);
+            case GjsGlobalType::DEBUGGER:
+                return GjsDebuggerGlobal::create_with_compartment(
+                    cx, current_global);
+            default:
+                return nullptr;
+        }
+    }
+
+    switch (global_type) {
+        case GjsGlobalType::DEFAULT:
+            return GjsGlobal::create(cx);
+        case GjsGlobalType::DEBUGGER:
+            return GjsDebuggerGlobal::create(cx);
+        default:
+            return nullptr;
+    }
+}
+
+GjsGlobalType gjs_global_get_type(JSContext* cx) {
+    auto global = JS::CurrentGlobalOrNull(cx);
+
+    g_assert(global &&
+             "gjs_global_get_type called before a realm was entered.");
+
+    auto global_type =
+        gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
+
+    g_assert(global_type.isInt32());
+
+    return static_cast<GjsGlobalType>(global_type.toInt32());
+}
+
+GjsGlobalType gjs_global_get_type(JSObject* global) {
+    auto global_type =
+        gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
+
+    g_assert(global_type.isInt32());
+
+    return static_cast<GjsGlobalType>(global_type.toInt32());
 }
 
 /**
@@ -202,28 +351,51 @@ gjs_create_global_object(JSContext *cx)
  * pending on @cx
  */
 bool gjs_define_global_properties(JSContext* cx, JS::HandleObject global,
+                                  GjsGlobalType global_type,
                                   const char* realm_name,
                                   const char* bootstrap_script) {
-    return GjsGlobal::define_properties(cx, global, realm_name,
-                                        bootstrap_script);
+    gjs_set_global_slot(global.get(), GjsBaseGlobalSlot::GLOBAL_TYPE,
+                        JS::Int32Value(static_cast<uint32_t>(global_type)));
+
+    switch (global_type) {
+        case GjsGlobalType::DEFAULT:
+            return GjsGlobal::define_properties(cx, global, realm_name,
+                                                bootstrap_script);
+        case GjsGlobalType::DEBUGGER:
+            return GjsDebuggerGlobal::define_properties(cx, global, realm_name,
+                                                        bootstrap_script);
+        default:
+            return true;
+    }
 }
 
-void
-gjs_set_global_slot(JSContext    *cx,
-                    GjsGlobalSlot slot,
-                    JS::Value     value)
-{
-    JSObject *global = gjs_get_import_global(cx);
-    JS_SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value);
+template <typename GlobalSlot>
+inline void gjs_set_global_slot(JSObject* global, GlobalSlot slot,
+                                JS::Value value) {
+    JS_SetReservedSlot(
+        global, JSCLASS_GLOBAL_SLOT_COUNT + static_cast<uint32_t>(slot), value);
 }
-
-JS::Value
-gjs_get_global_slot(JSContext    *cx,
-                    GjsGlobalSlot slot)
-{
-    JSObject *global = gjs_get_import_global(cx);
-    return JS_GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot);
+template void gjs_set_global_slot(JSObject* global, GjsBaseGlobalSlot slot,
+                                  JS::Value value);
+template void gjs_set_global_slot(JSObject* global, GjsGlobalSlot slot,
+                                  JS::Value value);
+template void gjs_set_global_slot(JSObject* global, GjsDebuggerGlobalSlot slot,
+                                  JS::Value value);
+
+template <typename GlobalSlot>
+inline JS::Value gjs_get_global_slot(JSObject* global, GlobalSlot slot) {
+    return JS_GetReservedSlot(
+        global, JSCLASS_GLOBAL_SLOT_COUNT + static_cast<uint32_t>(slot));
 }
+template JS::Value gjs_get_global_slot(JSObject* global,
+                                       GjsBaseGlobalSlot slot);
+template JS::Value gjs_get_global_slot(JSObject* global, GjsGlobalSlot slot);
+template JS::Value gjs_get_global_slot(JSObject* global,
+                                       GjsDebuggerGlobalSlot slot);
 
 decltype(GjsGlobal::klass) constexpr GjsGlobal::klass;
 decltype(GjsGlobal::static_funcs) constexpr GjsGlobal::static_funcs;
+
+decltype(GjsDebuggerGlobal::klass) constexpr GjsDebuggerGlobal::klass;
+decltype(
+    GjsDebuggerGlobal::static_funcs) constexpr GjsDebuggerGlobal::static_funcs;
diff --git a/gjs/global.h b/gjs/global.h
index 649ee3dc..1bf75556 100644
--- a/gjs/global.h
+++ b/gjs/global.h
@@ -1,6 +1,7 @@
 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 /*
  * Copyright (c) 2017  Philip Chimento <philip chimento gmail com>
+ * Copyright (c) 2020  Evan Welsh <contact evanwelsh com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -26,47 +27,70 @@
 
 #include <config.h>
 
+#include <js/RootingAPI.h>  // for Handle
 #include <js/TypeDecls.h>
 #include <js/Value.h>
 
+#include <stdint.h>
+
 #include "gjs/macros.h"
 
-typedef enum {
-    GJS_GLOBAL_SLOT_IMPORTS,
-    GJS_GLOBAL_SLOT_PROTOTYPE_gtype,
-    GJS_GLOBAL_SLOT_PROTOTYPE_function,
-    GJS_GLOBAL_SLOT_PROTOTYPE_ns,
-    GJS_GLOBAL_SLOT_PROTOTYPE_repo,
-    GJS_GLOBAL_SLOT_PROTOTYPE_importer,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_context,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_gradient,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_image_surface,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_linear_gradient,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_path,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_pattern,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_pdf_surface,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_ps_surface,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_radial_gradient,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_region,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_solid_pattern,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_surface,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_surface_pattern,
-    GJS_GLOBAL_SLOT_PROTOTYPE_cairo_svg_surface,
-    GJS_GLOBAL_SLOT_LAST,
-} GjsGlobalSlot;
+enum class GjsGlobalType {
+    DEFAULT,
+    DEBUGGER,
+};
+
+enum class GjsBaseGlobalSlot : uint32_t {
+    GLOBAL_TYPE = 0,
+    LAST,
+};
+
+enum class GjsDebuggerGlobalSlot : uint32_t {
+    LAST = static_cast<uint32_t>(GjsBaseGlobalSlot::LAST),
+};
+
+enum class GjsGlobalSlot : uint32_t {
+    IMPORTS = static_cast<uint32_t>(GjsBaseGlobalSlot::LAST),
+    PROTOTYPE_gtype,
+    PROTOTYPE_importer,
+    PROTOTYPE_function,
+    PROTOTYPE_ns,
+    PROTOTYPE_repo,
+    PROTOTYPE_byte_array,
+    PROTOTYPE_cairo_context,
+    PROTOTYPE_cairo_gradient,
+    PROTOTYPE_cairo_image_surface,
+    PROTOTYPE_cairo_linear_gradient,
+    PROTOTYPE_cairo_path,
+    PROTOTYPE_cairo_pattern,
+    PROTOTYPE_cairo_pdf_surface,
+    PROTOTYPE_cairo_ps_surface,
+    PROTOTYPE_cairo_radial_gradient,
+    PROTOTYPE_cairo_region,
+    PROTOTYPE_cairo_solid_pattern,
+    PROTOTYPE_cairo_surface,
+    PROTOTYPE_cairo_surface_pattern,
+    PROTOTYPE_cairo_svg_surface,
+    LAST,
+};
+
+GjsGlobalType gjs_global_get_type(JSContext* cx);
+GjsGlobalType gjs_global_get_type(JSObject* global);
 
 GJS_JSAPI_RETURN_CONVENTION
-JSObject *gjs_create_global_object(JSContext *cx);
+JSObject* gjs_create_global_object(JSContext* cx, GjsGlobalType global_type,
+                                   JS::HandleObject existing_global = nullptr);
 
 GJS_JSAPI_RETURN_CONVENTION
 bool gjs_define_global_properties(JSContext* cx, JS::HandleObject global,
+                                  GjsGlobalType global_type,
                                   const char* realm_name,
                                   const char* bootstrap_script);
 
-void gjs_set_global_slot(JSContext    *context,
-                         GjsGlobalSlot slot,
-                         JS::Value     value);
+template <typename GlobalSlot>
+void gjs_set_global_slot(JSObject* global, GlobalSlot slot, JS::Value value);
 
-JS::Value gjs_get_global_slot(JSContext* cx, GjsGlobalSlot slot);
+template <typename GlobalSlot>
+JS::Value gjs_get_global_slot(JSObject* global, GlobalSlot slot);
 
 #endif  // GJS_GLOBAL_H_
diff --git a/gjs/jsapi-class.h b/gjs/jsapi-class.h
index 17ab8fbb..1957ed68 100644
--- a/gjs/jsapi-class.h
+++ b/gjs/jsapi-class.h
@@ -198,8 +198,13 @@ GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(cname, no_parent)
 
 #define _GJS_DEFINE_GET_PROTO(cname)                                           \
     GJS_USE JSObject* gjs_##cname##_get_proto(JSContext* cx) {                 \
+        JSObject* global = JS::CurrentGlobalOrNull(cx);                        \
+        g_assert(global);                                                      \
+                                                                               \
+        JSAutoRealm ar(cx, global);                                            \
         JS::RootedValue v_proto(                                               \
-            cx, gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_PROTOTYPE_##cname));   \
+            cx,                                                                \
+            gjs_get_global_slot(global, GjsGlobalSlot::PROTOTYPE_##cname));    \
         g_assert(((void)"gjs_" #cname "_define_proto() must be called before " \
                         "gjs_" #cname "_get_proto()",                          \
                   !v_proto.isUndefined()));                                    \
@@ -213,8 +218,14 @@ GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(cname, no_parent)
     bool gjs_##cname##_define_proto(JSContext* cx, JS::HandleObject module,    \
                                     JS::MutableHandleObject proto) {           \
         /* If we've been here more than once, we already have the proto */     \
+        JSObject* global = JS::CurrentGlobalOrNull(cx);                        \
+        g_assert(global);                                                      \
+                                                                               \
+        JSAutoRealm ar(cx, global);                                            \
         JS::RootedValue v_proto(                                               \
-            cx, gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_PROTOTYPE_##cname));   \
+            cx,                                                                \
+            gjs_get_global_slot(global, GjsGlobalSlot::PROTOTYPE_##cname));    \
+                                                                               \
         if (!v_proto.isUndefined()) {                                          \
             g_assert(                                                          \
                 ((void)"Someone stored some weird value in a global slot",     \
@@ -226,7 +237,7 @@ GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(cname, no_parent)
         /* If module is not given, we are defining a global class */           \
         JS::RootedObject in_obj(cx, module);                                   \
         if (!in_obj)                                                           \
-            in_obj = gjs_get_import_global(cx);                                \
+            in_obj = global;                                                   \
                                                                                \
         /* Create the class, prototype, and constructor */                     \
         JS::RootedObject parent_proto(cx, gjs_##parent_cname##_get_proto(cx)); \
@@ -236,7 +247,7 @@ GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(cname, no_parent)
                                gjs_##cname##_static_funcs));                   \
         if (!proto)                                                            \
             return false;                                                      \
-        gjs_set_global_slot(cx, GJS_GLOBAL_SLOT_PROTOTYPE_##cname,             \
+        gjs_set_global_slot(global, GjsGlobalSlot::PROTOTYPE_##cname,          \
                             JS::ObjectValue(*proto));                          \
                                                                                \
         /* Look up the constructor */                                          \
diff --git a/modules/script/_bootstrap/debugger.js b/modules/script/_bootstrap/debugger.js
index 39a5ff60..8013055e 100644
--- a/modules/script/_bootstrap/debugger.js
+++ b/modules/script/_bootstrap/debugger.js
@@ -1,4 +1,4 @@
-/* global debuggee, quit, readline, uneval */
+/* global debuggee, quit, loadNative, readline, uneval */
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
@@ -16,7 +16,7 @@
  * the first frame is executed.
  */
 
-const {print, logError} = imports._print;
+const {print, logError} = loadNative('_print');
 
 // Debugger state.
 var focusedFrame = null;


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