[gjs/esm/static-imports: 4/10] Add native registry for GI modules.




commit 1f892bda4911feb25399326dd5fde35ac1026a0d
Author: Evan Welsh <noreply evanwelsh com>
Date:   Fri Oct 16 18:25:00 2020 -0500

    Add native registry for GI modules.

 gi/repo.cpp      |  56 +++++++++++++++++++--------
 gjs/global.cpp   | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 gjs/global.h     |  11 +++++-
 gjs/importer.cpp |  30 ++++++++++++++-
 gjs/module.cpp   |  26 +++++++++++++
 gjs/module.h     |   4 ++
 6 files changed, 216 insertions(+), 26 deletions(-)
---
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 1f76d847..5153ea95 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -40,6 +40,8 @@
 #include "gjs/context-private.h"
 #include "gjs/global.h"
 #include "gjs/jsapi-util.h"
+#include "gjs/mem-private.h"
+#include "gjs/module.h"
 #include "util/log.h"
 
 GJS_JSAPI_RETURN_CONVENTION
@@ -557,32 +559,52 @@ lookup_override_function(JSContext             *cx,
     return false;
 }
 
-JSObject*
-gjs_lookup_namespace_object_by_name(JSContext      *context,
-                                    JS::HandleId    ns_name)
-{
-    JS::RootedObject global(context, gjs_get_import_global(context));
-    JS::RootedValue importer(
-        context, gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS));
-    g_assert(importer.isObject());
+GJS_JSAPI_RETURN_CONVENTION
+static JSObject* lookup_namespace(JSContext* cx, JSObject* global,
+                                  JS::HandleId ns_name) {
+    JS::RootedObject native_registry(cx, gjs_get_native_registry(cx, global));
+    auto priv = GjsContextPrivate::from_cx(cx);
+    auto atoms = priv->atoms();
+    JS::RootedObject gi(cx);
+
+    if (!gjs_global_registry_get(cx, native_registry, atoms.gi(), &gi)) {
+        gjs_throw(cx, "No gi property in importer native registry.");
+        return nullptr;
+    }
 
-    JS::RootedObject repo(context), importer_obj(context, &importer.toObject());
-    const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
-    if (!gjs_object_require_property(context, importer_obj, "importer",
-                                     atoms.gi(), &repo)) {
-        gjs_log_exception(context);
-        gjs_throw(context, "No gi property in importer");
+    if (!gi) {
+        gjs_throw(cx, "No gi property in native registry");
         return NULL;
     }
 
-    JS::RootedObject retval(context);
-    if (!gjs_object_require_property(context, repo, "GI repository object",
-                                     ns_name, &retval))
+    JS::RootedObject retval(cx);
+    if (!gjs_object_require_property(cx, gi, "GI repository object", ns_name,
+                                     &retval))
         return NULL;
 
     return retval;
 }
 
+JSObject* gjs_lookup_namespace_object_by_name(JSContext* cx,
+                                              JS::HandleId ns_name) {
+    JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+    JSObject* ns = nullptr;
+
+    switch (gjs_global_get_type(global)) {
+        case GjsGlobalType::DEFAULT:
+            ns = lookup_namespace(cx, global, ns_name);
+            break;
+        case GjsGlobalType::DEBUGGER:
+            ns = nullptr;
+            // It is not possible to load namespace objects in the debugger
+            // global.
+            g_assert_not_reached();
+            break;
+    }
+
+    return ns;
+}
+
 const char*
 gjs_info_type_name(GIInfoType type)
 {
diff --git a/gjs/global.cpp b/gjs/global.cpp
index 4779e4c4..70fcf317 100644
--- a/gjs/global.cpp
+++ b/gjs/global.cpp
@@ -16,6 +16,7 @@
 #include <js/Class.h>
 #include <js/CompilationAndEvaluation.h>
 #include <js/CompileOptions.h>
+#include <js/Modules.h>
 #include <js/PropertyDescriptor.h>  // for JSPROP_PERMANENT, JSPROP_RE...
 #include <js/PropertySpec.h>
 #include <js/Realm.h>  // for GetObjectRealmOrNull, SetRealmPrivate
@@ -26,11 +27,13 @@
 #include <js/Utility.h>  // for UniqueChars
 #include <jsapi.h>       // for AutoSaveExceptionState, ...
 
+#include "gi/ns.h"
 #include "gjs/atoms.h"
 #include "gjs/context-private.h"
 #include "gjs/engine.h"
 #include "gjs/global.h"
 #include "gjs/jsapi-util.h"
+#include "gjs/module.h"
 #include "gjs/native.h"
 
 namespace mozilla {
@@ -132,8 +135,6 @@ class GjsBaseGlobal {
     }
 };
 
-const JSClassOps defaultclassops = JS::DefaultGlobalClassOps;
-
 class GjsGlobal : GjsBaseGlobal {
     static constexpr JSClass klass = {
         // Jasmine depends on the class name "GjsGlobal" to detect GJS' global
@@ -141,7 +142,7 @@ class GjsGlobal : GjsBaseGlobal {
         "GjsGlobal",
         JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
             static_cast<uint32_t>(GjsGlobalSlot::LAST)),
-        &defaultclassops,
+        &JS::DefaultGlobalClassOps,
     };
 
     // clang-format off
@@ -179,6 +180,15 @@ class GjsGlobal : GjsBaseGlobal {
         // const_cast is allowed here if we never free the realm data
         JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
 
+        JS::RootedObject native_registry(cx, JS::NewMapObject(cx));
+
+        if (!native_registry) {
+            return false;
+        }
+
+        gjs_set_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY,
+                            JS::ObjectValue(*native_registry));
+
         JS::Value v_importer =
             gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS);
         g_assert(((void) "importer should be defined before passing null "
@@ -206,7 +216,7 @@ class GjsDebuggerGlobal : GjsBaseGlobal {
         "GjsDebuggerGlobal",
         JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
             static_cast<uint32_t>(GjsDebuggerGlobalSlot::LAST)),
-        &defaultclassops,
+        &JS::DefaultGlobalClassOps,
     };
 
     static constexpr JSFunctionSpec static_funcs[] = {
@@ -280,8 +290,29 @@ JSObject* gjs_create_global_object(JSContext* cx, GjsGlobalType global_type,
     }
 }
 
+/**
+ * gjs_global_is_type:
+ *
+ * @param cx the current #JSContext
+ * @param type the global type to test for
+ *
+ * @returns whether the current global is the same type as #type
+ */
+bool gjs_global_is_type(JSContext* cx, GjsGlobalType type) {
+    JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+
+    g_assert(global && "gjs_global_is_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()) == type;
+}
+
 GjsGlobalType gjs_global_get_type(JSContext* cx) {
-    auto global = JS::CurrentGlobalOrNull(cx);
+    JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
 
     g_assert(global &&
              "gjs_global_get_type called before a realm was entered.");
@@ -303,6 +334,69 @@ GjsGlobalType gjs_global_get_type(JSObject* global) {
     return static_cast<GjsGlobalType>(global_type.toInt32());
 }
 
+/**
+ * gjs_global_registry_set:
+ *
+ * @param cx the current #JSContext
+ * @param registry
+ * @param key
+ * @param value
+ *
+ * @returns whether the current global is the same type as #type
+ */
+bool gjs_global_registry_set(JSContext* cx, JS::HandleObject registry,
+                             JS::PropertyKey key, JS::HandleObject value) {
+    JS::RootedValue keyv(cx);
+
+    if (!JS_IdToValue(cx, key, &keyv)) {
+        return false;
+    }
+
+    bool has_key;
+
+    if (!JS::MapHas(cx, registry, keyv, &has_key)) {
+        return false;
+    }
+
+    if (has_key) {
+        JS::RootedString str(cx, keyv.toString());
+        gjs_throw(cx, "Duplicate map set attempted: %s.",
+                  JS_EncodeStringToUTF8(cx, str).get());
+        return false;
+    }
+
+    JS::RootedValue valuev(cx, JS::ObjectValue(*value));
+
+    return JS::MapSet(cx, registry, keyv, valuev);
+}
+
+bool gjs_global_registry_get(JSContext* cx, JS::HandleObject registry,
+                             JS::PropertyKey key,
+                             JS::MutableHandleObject value) {
+    JS::RootedValue keyv(cx);
+
+    if (!JS_IdToValue(cx, key, &keyv)) {
+        return false;
+    }
+    JS::RootedValue valuev(cx);
+
+    if (keyv.isString()) {
+        JS::RootedString str(cx, keyv.toString());
+    }
+
+    if (!JS::MapGet(cx, registry, keyv, &valuev)) {
+        return false;
+    }
+
+    if (valuev.isUndefined()) {
+        return true;
+    }
+
+    value.set(&valuev.toObject());
+
+    return true;
+}
+
 /**
  * gjs_define_global_properties:
  * @cx: a #JSContext
@@ -343,18 +437,25 @@ bool gjs_define_global_properties(JSContext* cx, JS::HandleObject global,
         case GjsGlobalType::DEBUGGER:
             return GjsDebuggerGlobal::define_properties(cx, global, realm_name,
                                                         bootstrap_script);
-        default:
-            return true;
     }
+
+    return false;
 }
 
 void detail::set_global_slot(JSObject* global, uint32_t slot, JS::Value value) {
     JS_SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value);
 }
+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);
 
 JS::Value detail::get_global_slot(JSObject* global, uint32_t slot) {
     return JS_GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot);
 }
+template JS::Value gjs_get_global_slot(JSObject* global,
+                                       GjsBaseGlobalSlot slot);
+template JS::Value gjs_get_global_slot(JSObject* global, GjsGlobalSlot slot);
 
 decltype(GjsGlobal::klass) constexpr GjsGlobal::klass;
 decltype(GjsGlobal::static_funcs) constexpr GjsGlobal::static_funcs;
diff --git a/gjs/global.h b/gjs/global.h
index bdbc18e2..d087f851 100644
--- a/gjs/global.h
+++ b/gjs/global.h
@@ -32,6 +32,7 @@ enum class GjsDebuggerGlobalSlot : uint32_t {
 
 enum class GjsGlobalSlot : uint32_t {
     IMPORTS = static_cast<uint32_t>(GjsBaseGlobalSlot::LAST),
+    NATIVE_REGISTRY,
     PROTOTYPE_gtype,
     PROTOTYPE_importer,
     PROTOTYPE_function,
@@ -53,9 +54,17 @@ enum class GjsGlobalSlot : uint32_t {
     LAST,
 };
 
-GjsGlobalType gjs_global_get_type(JSContext* cx);
+bool gjs_global_is_type(JSContext* cx, GjsGlobalType type);
 GjsGlobalType gjs_global_get_type(JSObject* global);
 
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_global_registry_set(JSContext* cx, JS::HandleObject registry,
+                             JS::PropertyKey key, JS::HandleObject value);
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_global_registry_get(JSContext* cx, JS::HandleObject registry,
+                             JS::PropertyKey key,
+                             JS::MutableHandleObject value);
+
 GJS_JSAPI_RETURN_CONVENTION
 JSObject* gjs_create_global_object(JSContext* cx, GjsGlobalType global_type,
                                    JS::HandleObject existing_global = nullptr);
diff --git a/gjs/importer.cpp b/gjs/importer.cpp
index 6cdfd11d..1c4267c6 100644
--- a/gjs/importer.cpp
+++ b/gjs/importer.cpp
@@ -12,6 +12,7 @@
 #endif
 
 #include <string>
+#include <utility>  // for move
 #include <vector>   // for vector
 
 #include <gio/gio.h>
@@ -33,6 +34,7 @@
 #include <js/Value.h>
 #include <jsapi.h>    // for JS_DefinePropertyById, JS_DefineP...
 #include <jspubtd.h>  // for JSProto_Error
+#include <mozilla/HashTable.h>
 #include <mozilla/UniquePtr.h>
 
 #include "gjs/atoms.h"
@@ -281,10 +283,36 @@ gjs_import_native_module(JSContext       *cx,
 {
     gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", parse_name);
 
+    JS::RootedObject native_registry(
+        cx, gjs_get_native_registry(cx, gjs_get_import_global(cx)));
+    JS::RootedValue nv(cx);
+    if (!gjs_string_from_utf8(cx, parse_name, &nv)) {
+        return false;
+    }
+    JS::RootedId id(cx);
+
+    if (!JS_ValueToId(cx, nv, &id)) {
+        return false;
+    }
+
     JS::RootedObject module(cx);
+
+    if (!gjs_global_registry_get(cx, native_registry, id, &module)) {
+        return false;
+    }
+
+    if (module) {
+        return define_meta_properties(cx, module, nullptr, parse_name,
+                                      importer) &&
+               JS_DefineProperty(cx, importer, parse_name, module,
+                                 GJS_MODULE_PROP_FLAGS);
+    }
+
     return gjs_load_native_module(cx, parse_name, &module) &&
+           gjs_global_registry_set(cx, native_registry, id, module) &&
            define_meta_properties(cx, module, nullptr, parse_name, importer) &&
-           JS_DefineProperty(cx, importer, parse_name, module, GJS_MODULE_PROP_FLAGS);
+           JS_DefineProperty(cx, importer, parse_name, module,
+                             GJS_MODULE_PROP_FLAGS);
 }
 
 GJS_JSAPI_RETURN_CONVENTION
diff --git a/gjs/module.cpp b/gjs/module.cpp
index 4e4a8b6a..6de166e8 100644
--- a/gjs/module.cpp
+++ b/gjs/module.cpp
@@ -1,6 +1,7 @@
 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
 // SPDX-FileCopyrightText: 2017 Philip Chimento <philip chimento gmail com>
+// SPDX-FileCopyrightText: 2020 Evan Welsh <contact evanwelsh com>
 
 #include <config.h>
 
@@ -16,13 +17,16 @@
 #include <js/CompilationAndEvaluation.h>
 #include <js/CompileOptions.h>
 #include <js/GCVector.h>  // for RootedVector
+#include <js/Modules.h>
 #include <js/PropertyDescriptor.h>
 #include <js/RootingAPI.h>
 #include <js/SourceText.h>
 #include <js/TypeDecls.h>
+#include <js/Value.h>  // for Value
 #include <jsapi.h>  // for JS_DefinePropertyById, ...
 
 #include "gjs/context-private.h"
+#include "gjs/global.h"
 #include "gjs/jsapi-util.h"
 #include "gjs/mem-private.h"
 #include "gjs/module.h"
@@ -247,3 +251,25 @@ gjs_module_import(JSContext       *cx,
 
 decltype(GjsScriptModule::klass) constexpr GjsScriptModule::klass;
 decltype(GjsScriptModule::class_ops) constexpr GjsScriptModule::class_ops;
+
+/**
+ * gjs_get_module_registry:
+ *
+ * Retrieves a global's native registry from the NATIVE_REGISTRY slot.
+ * Registries are actually JS Maps.
+ *
+ * @param cx the current #JSContext
+ * @param global a global #JSObject
+ *
+ * @returns the registry map as a #JSObject
+ */
+JSObject* gjs_get_native_registry(JSContext* cx, JSObject* global) {
+    JS::Value native_registry =
+        gjs_get_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY);
+
+    g_assert(native_registry.isObject());
+
+    JS::RootedObject root_registry(cx, &native_registry.toObject());
+
+    return root_registry;
+}
diff --git a/gjs/module.h b/gjs/module.h
index e366eaf8..eb81fe50 100644
--- a/gjs/module.h
+++ b/gjs/module.h
@@ -1,6 +1,7 @@
 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
 // SPDX-FileCopyrightText: 2017 Philip Chimento <philip chimento gmail com>
+// SPDX-FileCopyrightText: 2020 Evan Welsh <contact evanwelsh com>
 
 #ifndef GJS_MODULE_H_
 #define GJS_MODULE_H_
@@ -21,4 +22,7 @@ gjs_module_import(JSContext       *cx,
                   const char      *name,
                   GFile           *file);
 
+GJS_JSAPI_RETURN_CONVENTION
+JSObject* gjs_get_native_registry(JSContext* cx, JSObject* global);
+
 #endif  // GJS_MODULE_H_


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