[gjs/refactor-globals: 1/2] global: Refactor to support multiple global types.
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/refactor-globals: 1/2] global: Refactor to support multiple global types.
- Date: Tue, 23 Jun 2020 02:03:38 +0000 (UTC)
commit 0bdcd21eb509c7bed25826341dc603db3fe13705
Author: Evan Welsh <noreply evanwelsh com>
Date: Sat Jun 6 12:11:56 2020 -0500
global: Refactor to support multiple global types.
gi/repo.cpp | 63 ++++++++++----
gjs/context.cpp | 16 ++--
gjs/coverage.cpp | 5 +-
gjs/debugger.cpp | 6 +-
gjs/global.cpp | 245 ++++++++++++++++++++++++++++++++++++++++++++----------
gjs/global.h | 72 +++++++++-------
gjs/importer.cpp | 19 ++++-
gjs/jsapi-class.h | 15 +++-
gjs/module.cpp | 13 ++-
gjs/module.h | 46 ++++++++++
10 files changed, 393 insertions(+), 107 deletions(-)
---
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 08df95e1..73ec1839 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -31,6 +31,7 @@
#include <glib.h>
#include <js/Class.h>
+#include <js/GCHashTable.h> // for GCHashMap
#include <js/Id.h> // for JSID_IS_STRING, JSID_VOID
#include <js/PropertyDescriptor.h> // for JSPROP_PERMANENT, JSPROP_RESOLVING
#include <js/RootingAPI.h>
@@ -40,6 +41,8 @@
#include <js/Warnings.h>
#include <jsapi.h> // for JS_DefinePropertyById, JS_GetProp...
+#include <mozilla/HashTable.h> // for HashTable<>::Ptr, HashMapEntry
+
#include "gi/arg.h"
#include "gi/boxed.h"
#include "gi/enumeration.h"
@@ -58,6 +61,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 +154,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() &&
@@ -601,7 +610,9 @@ lookup_override_function(JSContext *cx,
{
JS::AutoSaveExceptionState saved_exc(cx);
- JS::RootedValue importer(cx, gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS));
+ auto global = 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);
@@ -635,36 +646,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)
-{
- JS::RootedValue importer(context,
- gjs_get_global_slot(context, GJS_GLOBAL_SLOT_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/context.cpp b/gjs/context.cpp
index 04f28f73..97f40cc2 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");
@@ -509,24 +511,24 @@ GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
gjs_log_exception(m_cx);
g_error("Failed to initialize global strings");
}
-
std::vector<std::string> paths;
if (m_search_path)
paths = {m_search_path, m_search_path + g_strv_length(m_search_path)};
JS::RootedObject importer(m_cx, gjs_create_root_importer(m_cx, paths));
if (!importer) {
- gjs_log_exception(cx);
+ gjs_log_exception(m_cx);
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 dd2ad2a9..c8593626 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
@@ -50,11 +51,52 @@
#include "gjs/engine.h"
#include "gjs/global.h"
#include "gjs/jsapi-util.h"
+#include "gjs/module.h"
namespace mozilla {
union Utf8Unit;
}
+inline JSObject* global_create(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;
+}
+
+inline JSObject* global_create_new(JSContext* cx, const JSClass* clasp) {
+ JS::RealmCreationOptions creation;
+ creation.setNewCompartmentAndZone();
+
+ return global_create(cx, clasp, creation);
+}
+
+inline JSObject* global_create_with_existing(JSContext* cx, JSObject* existing,
+ const JSClass* clasp) {
+ JS::RealmCreationOptions creation;
+ JS::RootedObject comp(cx, existing);
+ creation.setExistingCompartment(comp);
+
+ return global_create(cx, clasp, creation);
+}
+
GJS_JSAPI_RETURN_CONVENTION
static bool
run_bootstrap(JSContext *cx,
@@ -230,10 +272,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_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_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[] = {
@@ -245,28 +309,16 @@ 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;
+ static JSObject* create(JSContext* cx) {
+ auto global = global_create_new(cx, &klass);
- JSAutoRealm ar(cx, global);
+ return global;
+ }
- if (!JS_InitReflectParse(cx, global) ||
- !JS_DefineDebuggerObject(cx, global))
- return nullptr;
+ GJS_USE
+ static JSObject* create_with_compartment(JSContext* cx,
+ JSObject* cmp_global) {
+ auto global = global_create_with_existing(cx, cmp_global, &klass);
return global;
}
@@ -286,9 +338,13 @@ 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",
+ 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 "
+ "importer to GjsGlobal::define_properties",
v_importer.isObject()));
JS::RootedObject root_importer(cx, &v_importer.toObject());
@@ -307,6 +363,53 @@ class GjsGlobal {
}
};
+class GjsDebuggerGlobal {
+ static constexpr JSClass klass = {
+ "GjsDebuggerGlobal",
+ JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(
+ static_cast<uint32_t>(GjsGlobalSlot::LAST)),
+ &defaultclassops,
+ };
+
+ static constexpr JSFunctionSpec static_funcs[] = {
+ JS_FN("logError", gjs_log_error, 2, GJS_MODULE_PROP_FLAGS),
+ JS_FN("print", gjs_print, 0, GJS_MODULE_PROP_FLAGS), JS_FS_END};
+
+ public:
+ GJS_USE
+ static JSObject* create(JSContext* cx) {
+ return global_create_new(cx, &klass);
+ }
+
+ GJS_USE
+ static JSObject* create_with_compartment(JSContext* cx,
+ JSObject* cmp_global) {
+ return global_create_with_existing(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
@@ -316,10 +419,49 @@ 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,
+ JSObject* 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(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);
+
+ g_assert(global_type.isInt32() &&
+ "Invalid type for GLOBAL_TYPE slot. Expected int32.");
+
+ return static_cast<GjsGlobalType>(global_type.toInt32());
+}
+
+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 type == static_cast<GjsGlobalType>(global_type.toInt32());
}
/**
@@ -349,28 +491,43 @@ 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(), GjsGlobalSlot::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);
+ }
+
+ return false;
}
-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>
+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);
}
+template void gjs_set_global_slot(JSObject* global, GjsGlobalSlot slot,
+ JS::Value 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 <typename GlobalSlot>
+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, 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;
+decltype(
+ GjsDebuggerGlobal::static_funcs) constexpr GjsDebuggerGlobal::static_funcs;
diff --git a/gjs/global.h b/gjs/global.h
index 649ee3dc..b4ae394d 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,62 @@
#include <config.h>
+#include <stdint.h>
+
#include <js/TypeDecls.h>
#include <js/Value.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 GjsGlobalSlot : uint32_t {
+ GLOBAL_TYPE = 0,
+ IMPORTS,
+ NATIVE_REGISTRY,
+ 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,
+};
+
+bool gjs_global_is_type(JSContext* cx, GjsGlobalType type);
+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,
+ JSObject* 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/importer.cpp b/gjs/importer.cpp
index 628d3cad..5809b6c3 100644
--- a/gjs/importer.cpp
+++ b/gjs/importer.cpp
@@ -31,6 +31,7 @@
#endif
#include <string>
+#include <utility> // for move
#include <vector> // for vector
#include <gio/gio.h>
@@ -39,6 +40,7 @@
#include <js/CallArgs.h>
#include <js/CharacterEncoding.h>
#include <js/Class.h>
+#include <js/GCHashTable.h> // for GCHashMap
#include <js/Id.h> // for PropertyKey, JSID_IS_STRING
#include <js/PropertyDescriptor.h>
#include <js/PropertySpec.h>
@@ -49,6 +51,7 @@
#include <js/Value.h>
#include <jsapi.h> // for JS_DefinePropertyById, JS_DefineP...
#include <jspubtd.h> // for JSProto_Error
+#include <mozilla/HashTable.h> // for HashTable<>::AddPtr, HashMapEntry
#include <mozilla/UniquePtr.h>
#include <mozilla/Vector.h>
@@ -297,10 +300,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/jsapi-class.h b/gjs/jsapi-class.h
index 17ab8fbb..3cdad3f1 100644
--- a/gjs/jsapi-class.h
+++ b/gjs/jsapi-class.h
@@ -198,8 +198,11 @@ 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); \
+ 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 +216,12 @@ 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); \
+ 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 +233,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 +243,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/gjs/module.cpp b/gjs/module.cpp
index 6942ee46..f028a25b 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
@@ -39,9 +40,11 @@
#include <js/RootingAPI.h>
#include <js/SourceText.h>
#include <js/TypeDecls.h>
+#include <js/Value.h>
#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"
@@ -50,7 +53,7 @@
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 +271,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..07be1148 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,52 @@
#include <gio/gio.h>
+#include <js/GCHashTable.h>
#include <js/TypeDecls.h>
+#include <string>
+
+#include <string>
+
#include "gjs/macros.h"
+namespace JS {
+template <typename T>
+struct GCPolicy;
+template <typename T>
+class Heap;
+} // namespace JS
+
+namespace js {
+class SystemAllocPolicy;
+}
+
+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 +83,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]