[gjs/native-registry: 3/4] Add native registry for GI modules.
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/native-registry: 3/4] Add native registry for GI modules.
- Date: Wed, 2 Dec 2020 23:31:52 +0000 (UTC)
commit d5c486874e739e25533de1dffd819729e8d03b10
Author: Evan Welsh <contact evanwelsh com>
Date: Fri Oct 16 18:25:00 2020 -0500
Add native registry for GI modules.
gi/repo.cpp | 43 ++++++++++++---------
gjs/global.cpp | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
gjs/global.h | 11 +++++-
gjs/importer.cpp | 24 +++++++++++-
gjs/module.cpp | 24 ++++++++++++
gjs/module.h | 3 ++
6 files changed, 192 insertions(+), 26 deletions(-)
---
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 446addc1..8c705c1d 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -39,6 +39,7 @@
#include "gjs/context-private.h"
#include "gjs/global.h"
#include "gjs/jsapi-util.h"
+#include "gjs/module.h"
#include "util/log.h"
GJS_JSAPI_RETURN_CONVENTION
@@ -556,32 +557,38 @@ 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(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))
+ 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");
- return NULL;
+ if (!gi) {
+ gjs_throw(cx, "No gi property in native registry");
+ return nullptr;
}
- 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));
+
+ g_assert(gjs_global_get_type(global) == GjsGlobalType::DEFAULT);
+ return lookup_namespace(cx, global, ns_name);
+}
+
const char*
gjs_info_type_name(GIInfoType type)
{
diff --git a/gjs/global.cpp b/gjs/global.cpp
index 4779e4c4..8097ebbc 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,17 +27,21 @@
#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 {
union Utf8Unit;
}
+const JSClassOps defaultclassops = JS::DefaultGlobalClassOps;
+
class GjsBaseGlobal {
static JSObject* base(JSContext* cx, const JSClass* clasp,
JS::RealmCreationOptions options) {
@@ -132,8 +137,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
@@ -179,6 +182,14 @@ 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 "
@@ -280,13 +291,34 @@ 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) {
+ auto global = JS::CurrentGlobalOrNull(cx);
+
+ g_assert(global && "gjs_global_is_type called before a realm was entered.");
+
+ JS::Value 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);
g_assert(global &&
"gjs_global_get_type called before a realm was entered.");
- auto global_type =
+ JS::Value global_type =
gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
g_assert(global_type.isInt32());
@@ -295,7 +327,7 @@ GjsGlobalType gjs_global_get_type(JSContext* cx) {
}
GjsGlobalType gjs_global_get_type(JSObject* global) {
- auto global_type =
+ JS::Value global_type =
gjs_get_global_slot(global, GjsBaseGlobalSlot::GLOBAL_TYPE);
g_assert(global_type.isInt32());
@@ -303,6 +335,73 @@ GjsGlobalType gjs_global_get_type(JSObject* global) {
return static_cast<GjsGlobalType>(global_type.toInt32());
}
+/**
+ * gjs_global_registry_set:
+ *
+ * @brief This function inserts a module object into a global registry.
+ * Global registries are JS Map objects for easy reuse and access
+ * within internal JS. This function will assert if a module has
+ * already been inserted at the given key.
+
+ * @param cx the current #JSContext
+ * @param registry a JS Map object
+ * @param key a module identifier, typically a string or symbol
+ * @param module a module object
+ */
+bool gjs_global_registry_set(JSContext* cx, JS::HandleObject registry,
+ JS::PropertyKey key, JS::HandleObject module) {
+ JS::RootedValue v_key(cx);
+
+ if (!JS_IdToValue(cx, key, &v_key))
+ return false;
+
+ bool has_key;
+
+ if (!JS::MapHas(cx, registry, v_key, &has_key))
+ return false;
+
+ if (has_key)
+ g_assert("Module key already exists in the registry.");
+
+ JS::RootedValue v_value(cx, JS::ObjectValue(*module));
+
+ return JS::MapSet(cx, registry, v_key, v_value);
+}
+
+/**
+ * gjs_global_registry_get:
+ *
+ * @brief This function inserts a module object into a global registry.
+ * Global registries are JS Map objects for easy reuse and access
+ * within internal JS. This function will assert if a module has
+ * already been inserted at the given key.
+
+ * @param cx the current #JSContext
+ * @param registry a JS Map object
+ * @param key a module identifier, typically a string or symbol
+ * @param module a module object
+ */
+bool gjs_global_registry_get(JSContext* cx, JS::HandleObject registry,
+ JS::PropertyKey key,
+ JS::MutableHandleObject module_out) {
+ JS::RootedValue v_key(cx), v_value(cx);
+
+ if (!JS_IdToValue(cx, key, &v_key) ||
+ !JS::MapGet(cx, registry, v_key, &v_value))
+ return false;
+
+ g_assert((v_value.isUndefined() || v_value.isObject()) &&
+ "Invalid value in module registry");
+
+ if (v_value.isObject()) {
+ module_out.set(&v_value.toObject());
+ return true;
+ }
+
+ module_out.set(nullptr);
+ return true;
+}
+
/**
* gjs_define_global_properties:
* @cx: a #JSContext
@@ -343,9 +442,11 @@ 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;
}
+
+ // Global type does not handle define_properties
+ g_assert_not_reached();
+ return false;
}
void detail::set_global_slot(JSObject* global, uint32_t slot, JS::Value value) {
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 2d99ebcf..50dbcd56 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>
@@ -281,10 +282,31 @@ gjs_import_native_module(JSContext *cx,
{
gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", parse_name);
+ JS::RootedObject native_registry(
+ cx, gjs_get_native_registry(gjs_get_import_global(cx)));
+
+ JS::RootedId id(cx, gjs_intern_string_to_id(cx, parse_name));
+
+ if (id == JSID_VOID)
+ 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..21bb4fea 100644
--- a/gjs/module.cpp
+++ b/gjs/module.cpp
@@ -16,13 +16,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 +250,24 @@ gjs_module_import(JSContext *cx,
decltype(GjsScriptModule::klass) constexpr GjsScriptModule::klass;
decltype(GjsScriptModule::class_ops) constexpr GjsScriptModule::class_ops;
+
+/**
+ * gjs_get_native_registry:
+ *
+ * @brief Retrieves a global's native registry from the NATIVE_REGISTRY slot.
+ * Registries are JS Map objects created with JS::NewMapObject instead
+ * of GCHashMaps (used elsewhere in GJS) because the objects need to be
+ * exposed to internal JS code and accessed from native C++ code.
+ *
+ * @param global a global #JSObject
+ *
+ * @returns the registry map as a #JSObject
+ */
+JSObject* gjs_get_native_registry(JSObject* global) {
+ JS::Value native_registry =
+ gjs_get_global_slot(global, GjsGlobalSlot::NATIVE_REGISTRY);
+
+ g_assert(native_registry.isObject());
+
+ return &native_registry.toObject();
+}
diff --git a/gjs/module.h b/gjs/module.h
index e366eaf8..0d35af26 100644
--- a/gjs/module.h
+++ b/gjs/module.h
@@ -21,4 +21,7 @@ gjs_module_import(JSContext *cx,
const char *name,
GFile *file);
+GJS_JSAPI_RETURN_CONVENTION
+JSObject* gjs_get_native_registry(JSObject* global);
+
#endif // GJS_MODULE_H_
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]