[gjs/refactor-globals: 2/3] Add native registry for GI modules.



commit c882212d890f774ec20632eab907d8e5259f7fd3
Author: Evan Welsh <noreply evanwelsh com>
Date:   Tue Jun 16 16:25:28 2020 -0500

    Add native registry for GI modules.

 gi/repo.cpp      | 57 ++++++++++++++++++++++++++++++++++++++------------------
 gjs/global.cpp   | 42 +++++++++++++++++++++++++++++++----------
 gjs/global.h     |  3 ++-
 gjs/importer.cpp | 17 ++++++++++++++++-
 gjs/module.cpp   | 22 +++++++++++++++++++++-
 gjs/module.h     | 36 +++++++++++++++++++++++++++++++++++
 6 files changed, 146 insertions(+), 31 deletions(-)
---
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 93ba9447..1d0ff369 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -58,6 +58,7 @@
 #include "gjs/jsapi-class.h"
 #include "gjs/jsapi-util.h"
 #include "gjs/mem-private.h"
+#include "gjs/module.h"
 #include "util/log.h"
 
 struct JSFunctionSpec;
@@ -150,8 +151,13 @@ static bool resolve_namespace_object(JSContext* context,
         return false;
 
     JS::RootedValue override(context);
-    if (!lookup_override_function(context, ns_id, &override))
-        return false;
+
+    // We do not load overrides on the internal global.
+    if (gjs_global_is_type(context, GjsGlobalType::DEFAULT)) {
+        if (!lookup_override_function(context, ns_id, &override)) {
+            return false;
+        }
+    }
 
     JS::RootedValue result(context);
     if (!override.isUndefined() &&
@@ -637,37 +643,52 @@ lookup_override_function(JSContext             *cx,
     }
     return true;
 
- fail:
+fail:
     saved_exc.drop();
     return false;
 }
 
-JSObject*
-gjs_lookup_namespace_object_by_name(JSContext      *context,
-                                    JS::HandleId    ns_name)
-{
-    auto global = 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());
-    const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
-    if (!gjs_object_require_property(context, importer_obj, "importer",
-                                     atoms.gi(), &repo)) {
+GJS_JSAPI_RETURN_CONVENTION
+static JSObject* lookup_namespace(JSContext* context, JS::HandleId ns_name) {
+    auto native_registry = gjs_get_native_module_registry(context);
+    auto it = native_registry->lookup("gi");
+    if (!it.found()) {
         gjs_log_exception(context);
-        gjs_throw(context, "No gi property in importer");
+        gjs_throw(context, "No gi property in native registry");
         return NULL;
     }
 
+    JS::RootedObject gi(context, it->value());
     JS::RootedObject retval(context);
-    if (!gjs_object_require_property(context, repo, "GI repository object",
+    if (!gjs_object_require_property(context, gi, "GI repository object",
                                      ns_name, &retval))
         return NULL;
 
     return retval;
 }
 
+JSObject* gjs_lookup_namespace_object_by_name(JSContext* context,
+                                              JS::HandleId ns_name) {
+    JSObject* global = JS::CurrentGlobalOrNull(context);
+    JSObject* ns = nullptr;
+
+    switch (gjs_global_get_type(global)) {
+        case GjsGlobalType::DEFAULT:
+            ns = lookup_namespace(context, ns_name);
+            break;
+        case GjsGlobalType::DEBUGGER:
+            ns = nullptr;
+            break;
+    }
+
+    if (!ns) {
+        return nullptr;
+    }
+
+    JS::RootedObject retval(context, ns);
+    return retval;
+}
+
 const char*
 gjs_info_type_name(GIInfoType type)
 {
diff --git a/gjs/global.cpp b/gjs/global.cpp
index bcea9770..d2685d49 100644
--- a/gjs/global.cpp
+++ b/gjs/global.cpp
@@ -281,13 +281,32 @@ gjs_printerr(JSContext *context,
 const JSClassOps defaultclassops = JS::DefaultGlobalClassOps;
 
 class GjsGlobal {
+    static void finalize(JSFreeOp* op G_GNUC_UNUSED, JSObject* obj) {
+        delete static_cast<GjsModuleRegistry*>(
+            gjs_get_global_slot(obj, GjsGlobalSlot::NATIVE_REGISTRY)
+                .toPrivate());
+    }
+
+    static constexpr JSClassOps classops = {nullptr,  // addProperty
+                                            nullptr,  // deleteProperty
+                                            nullptr,  // enumerate
+                                            JS_NewEnumerateStandardClasses,
+                                            JS_ResolveStandardClass,
+                                            JS_MayResolveStandardClass,
+                                            GjsGlobal::finalize,
+                                            nullptr,  // call
+                                            nullptr,  // hasInstance
+                                            nullptr,  // construct
+                                            JS_GlobalObjectTraceHook};
+
     static constexpr JSClass klass = {
         // Keep this as "GjsGlobal" until Jasmine is upgraded to support
         // globalThis
         "GjsGlobal",
-        JSCLASS_HAS_PRIVATE | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
-                                  static_cast<uint32_t>(GjsGlobalSlot::LAST)),
-        &defaultclassops,
+        JSCLASS_FOREGROUND_FINALIZE | JSCLASS_HAS_PRIVATE |
+            JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
+                static_cast<uint32_t>(GjsGlobalSlot::LAST)),
+        &classops,
     };
 
     static constexpr JSFunctionSpec static_funcs[] = {
@@ -328,6 +347,9 @@ class GjsGlobal {
         // const_cast is allowed here if we never free the realm data
         JS::SetRealmPrivate(realm, const_cast<char*>(realm_name));
 
+        gjs_set_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY,
+                            JS::PrivateValue(new GjsModuleRegistry()));
+
         JS::Value v_importer =
             gjs_get_global_slot(global, GjsGlobalSlot::IMPORTS);
         g_assert(((void)"importer should be defined before passing null "
@@ -430,9 +452,7 @@ JSObject* gjs_create_global_object(JSContext* cx, GjsGlobalType global_type,
     }
 }
 
-GjsGlobalType gjs_global_get_type(JSContext* cx) {
-    auto global = JS::CurrentGlobalOrNull(cx);
-
+GjsGlobalType gjs_global_get_type(JSObject* global) {
     g_assert(global && "gjs_global_get_type called when no global is present");
 
     auto global_type = gjs_get_global_slot(global, GjsGlobalSlot::GLOBAL_TYPE);
@@ -443,13 +463,14 @@ GjsGlobalType gjs_global_get_type(JSContext* cx) {
     return static_cast<GjsGlobalType>(global_type.toInt32());
 }
 
-GjsGlobalType gjs_global_get_type(JSObject* global) {
+bool gjs_global_is_type(JSContext* context, GjsGlobalType type) {
+    auto global = JS::CurrentGlobalOrNull(context);
     auto global_type = gjs_get_global_slot(global, GjsGlobalSlot::GLOBAL_TYPE);
 
     g_assert(global_type.isInt32() &&
              "Invalid type for GLOBAL_TYPE slot. Expected int32.");
 
-    return static_cast<GjsGlobalType>(global_type.toInt32());
+    return type == static_cast<GjsGlobalType>(global_type.toInt32());
 }
 
 /**
@@ -492,9 +513,9 @@ 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;
 }
 
 template <typename GlobalSlot>
@@ -513,6 +534,7 @@ JS::Value gjs_get_global_slot(JSObject* global, GlobalSlot slot) {
 template JS::Value gjs_get_global_slot(JSObject* global, GjsGlobalSlot slot);
 
 decltype(GjsGlobal::klass) constexpr GjsGlobal::klass;
+decltype(GjsGlobal::classops) constexpr GjsGlobal::classops;
 decltype(GjsGlobal::static_funcs) constexpr GjsGlobal::static_funcs;
 
 decltype(GjsDebuggerGlobal::klass) constexpr GjsDebuggerGlobal::klass;
diff --git a/gjs/global.h b/gjs/global.h
index 919c3f48..8466b897 100644
--- a/gjs/global.h
+++ b/gjs/global.h
@@ -41,6 +41,7 @@ enum class GjsGlobalType {
 enum class GjsGlobalSlot : uint32_t {
     GLOBAL_TYPE = 0,
     IMPORTS,
+    NATIVE_REGISTRY,
     PROTOTYPE_gtype,
     PROTOTYPE_importer,
     PROTOTYPE_function,
@@ -64,7 +65,7 @@ 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
diff --git a/gjs/importer.cpp b/gjs/importer.cpp
index d294301c..6be101fa 100644
--- a/gjs/importer.cpp
+++ b/gjs/importer.cpp
@@ -50,6 +50,7 @@
 #include <js/Utility.h>  // for UniqueChars
 #include <js/Value.h>
 #include <jsapi.h>    // for JS_DefinePropertyById, JS_DefineP...
+#include <jsfriendapi.h>
 #include <jspubtd.h>  // for JSProto_Error
 #include <mozilla/UniquePtr.h>
 #include <mozilla/Vector.h>
@@ -300,10 +301,24 @@ gjs_import_native_module(JSContext       *cx,
 {
     gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", parse_name);
 
+    auto native_registry = gjs_get_native_module_registry(cx);
+    auto it = native_registry->lookupForAdd(parse_name);
+
     JS::RootedObject module(cx);
+
+    if (it.found()) {
+        module.set(it->value().get());
+        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) &&
+           native_registry->put(parse_name, 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 6942ee46..c0ccdc36 100644
--- a/gjs/module.cpp
+++ b/gjs/module.cpp
@@ -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,15 +27,22 @@
 #include <stddef.h>     // for size_t
 #include <sys/types.h>  // for ssize_t
 
+#include <codecvt>  // for codecvt_utf8_utf16
+#include <locale>   // for wstring_convert
 #include <string>  // for u16string
+#include <vector>
 
 #include <gio/gio.h>
+#include <girepository.h>
+#include <glib-object.h>
 #include <glib.h>
 
 #include <js/Class.h>
 #include <js/CompilationAndEvaluation.h>
 #include <js/CompileOptions.h>
+#include <js/Conversions.h>
 #include <js/GCVector.h>  // for RootedVector
+#include <js/Promise.h>
 #include <js/PropertyDescriptor.h>
 #include <js/RootingAPI.h>
 #include <js/SourceText.h>
@@ -42,15 +50,19 @@
 #include <jsapi.h>  // for JS_DefinePropertyById, ...
 
 #include "gjs/context-private.h"
+#include "gjs/error-types.h"
+#include "gjs/global.h"
+#include "gjs/importer.h"
 #include "gjs/jsapi-util.h"
 #include "gjs/mem-private.h"
 #include "gjs/module.h"
+#include "gjs/native.h"
 #include "util/log.h"
 
 class GjsScriptModule {
     char *m_name;
 
-    GjsScriptModule(const char* name) {
+    explicit GjsScriptModule(const char* name) {
         m_name = g_strdup(name);
         GJS_INC_COUNTER(module);
     }
@@ -268,3 +280,11 @@ gjs_module_import(JSContext       *cx,
 
 decltype(GjsScriptModule::klass) constexpr GjsScriptModule::klass;
 decltype(GjsScriptModule::class_ops) constexpr GjsScriptModule::class_ops;
+
+GjsModuleRegistry* gjs_get_native_module_registry(JSContext* js_context) {
+    auto global = gjs_get_import_global(js_context);
+    auto native_registry =
+        gjs_get_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY);
+
+    return static_cast<GjsModuleRegistry*>(native_registry.toPrivate());
+}
diff --git a/gjs/module.h b/gjs/module.h
index c63d6852..d79205fe 100644
--- a/gjs/module.h
+++ b/gjs/module.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
@@ -28,10 +29,42 @@
 
 #include <gio/gio.h>
 
+#include <js/GCHashTable.h>
+#include <js/GCVector.h>
 #include <js/TypeDecls.h>
+#include <jsapi.h>        // for JS_GetContextPrivate
+#include <jsfriendapi.h>  // for ScriptEnvironmentPreparer
+
+#include <string>
 
 #include "gjs/macros.h"
 
+class CppStringHashPolicy {
+ public:
+    typedef std::string Lookup;
+
+    static js::HashNumber hash(const Lookup& l) {
+        return std::hash<std::string>{}(std::string(l));
+    }
+
+    static bool match(const std::string& k, const Lookup& l) {
+        return k.compare(l) == 0;
+    }
+
+    static void rekey(std::string* k, const std::string& newKey) {
+        *k = newKey;
+    }
+};
+
+namespace JS {
+template <>
+struct GCPolicy<std::string> : public IgnoreGCPolicy<std::string> {};
+}  // namespace JS
+
+using GjsModuleRegistry =
+    JS::GCHashMap<std::string, JS::Heap<JSObject*>, CppStringHashPolicy,
+                  js::SystemAllocPolicy>;
+
 GJS_JSAPI_RETURN_CONVENTION
 JSObject *
 gjs_module_import(JSContext       *cx,
@@ -40,4 +73,7 @@ gjs_module_import(JSContext       *cx,
                   const char      *name,
                   GFile           *file);
 
+GJS_JSAPI_RETURN_CONVENTION
+GjsModuleRegistry* gjs_get_native_module_registry(JSContext* js_context);
+
 #endif  // GJS_MODULE_H_


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