csb/cjs/mozjs128.patch
Willy Sudiarto Raharjo b6accfbbb3
cjs: Port to mozjs 128.
Signed-off-by: Willy Sudiarto Raharjo <willysr@slackware-id.org>
2024-10-15 07:40:02 +07:00

11577 lines
438 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

diff -ruN cjs-6.2.0-orig/cjs/byteArray.cpp cjs-6.2.0/cjs/byteArray.cpp
--- cjs-6.2.0-orig/cjs/byteArray.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/byteArray.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -6,11 +6,14 @@
#include <stdint.h>
+#include <algorithm> // for copy_n
+
#include <glib-object.h>
#include <glib.h>
#include <js/ArrayBuffer.h>
#include <js/CallArgs.h>
+#include <js/GCAPI.h>
#include <js/PropertyAndElement.h>
#include <js/PropertySpec.h>
#include <js/RootingAPI.h>
@@ -28,15 +31,6 @@
#include "cjs/jsapi-util.h"
#include "cjs/macros.h"
#include "cjs/text-encoding.h"
-#include "util/misc.h" // for _gjs_memdup2
-
-// Callback to use with JS::NewExternalArrayBuffer()
-
-static void bytes_unref_arraybuffer(void* contents [[maybe_unused]],
- void* user_data) {
- auto* gbytes = static_cast<GBytes*>(user_data);
- g_bytes_unref(gbytes);
-}
GJS_JSAPI_RETURN_CONVENTION
static bool to_string_func(JSContext* cx, unsigned argc, JS::Value* vp) {
@@ -145,15 +139,18 @@
return true;
}
- JS::RootedObject array_buffer(
- context,
- JS::NewExternalArrayBuffer(
- context, len,
- const_cast<void*>(data), // the ArrayBuffer won't modify the data
- bytes_unref_arraybuffer, gbytes));
+ JS::RootedObject array_buffer{context, JS::NewArrayBuffer(context, len)};
if (!array_buffer)
return false;
- g_bytes_ref(gbytes); // now owned by both ArrayBuffer and BoxedBase
+
+ // Copy the data into the ArrayBuffer so that the copy is aligned, and
+ // because the GBytes data pointer may point into immutable memory.
+ {
+ JS::AutoCheckCannotGC nogc;
+ bool unused;
+ uint8_t* storage = JS::GetArrayBufferData(array_buffer, &unused, nogc);
+ std::copy_n(static_cast<const uint8_t*>(data), len, storage);
+ }
JS::RootedObject obj(
context, JS_NewUint8ArrayWithBuffer(context, array_buffer, 0, -1));
@@ -164,14 +161,20 @@
return true;
}
-JSObject* gjs_byte_array_from_data(JSContext* cx, size_t nbytes, void* data) {
+JSObject* gjs_byte_array_from_data_copy(JSContext* cx, size_t nbytes,
+ void* data) {
JS::RootedObject array_buffer(cx);
// a null data pointer takes precedence over whatever `nbytes` says
- if (data)
- array_buffer = JS::NewArrayBufferWithContents(
- cx, nbytes, _gjs_memdup2(data, nbytes));
- else
+ if (data) {
+ array_buffer = JS::NewArrayBuffer(cx, nbytes);
+
+ JS::AutoCheckCannotGC nogc{};
+ bool unused;
+ uint8_t* storage = JS::GetArrayBufferData(array_buffer, &unused, nogc);
+ std::copy_n(static_cast<uint8_t*>(data), nbytes, storage);
+ } else {
array_buffer = JS::NewArrayBuffer(cx, 0);
+ }
if (!array_buffer)
return nullptr;
@@ -186,7 +189,7 @@
}
JSObject* gjs_byte_array_from_byte_array(JSContext* cx, GByteArray* array) {
- return gjs_byte_array_from_data(cx, array->len, array->data);
+ return gjs_byte_array_from_data_copy(cx, array->len, array->data);
}
GBytes* gjs_byte_array_get_bytes(JSObject* obj) {
diff -ruN cjs-6.2.0-orig/cjs/byteArray.h cjs-6.2.0/cjs/byteArray.h
--- cjs-6.2.0-orig/cjs/byteArray.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/byteArray.h 2024-10-14 18:35:57.000000000 +0200
@@ -20,7 +20,8 @@
JS::MutableHandleObject module);
GJS_JSAPI_RETURN_CONVENTION
-JSObject* gjs_byte_array_from_data(JSContext* cx, size_t nbytes, void* data);
+JSObject* gjs_byte_array_from_data_copy(JSContext* cx, size_t nbytes,
+ void* data);
GJS_JSAPI_RETURN_CONVENTION
JSObject * gjs_byte_array_from_byte_array (JSContext *context,
diff -ruN cjs-6.2.0-orig/cjs/context-private.h cjs-6.2.0/cjs/context-private.h
--- cjs-6.2.0-orig/cjs/context-private.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/context-private.h 2024-10-14 18:35:57.000000000 +0200
@@ -32,12 +32,14 @@
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <js/UniquePtr.h>
+#include <js/Utility.h> // for UniqueChars, FreePolicy
#include <js/ValueArray.h>
#include <jsfriendapi.h> // for ScriptEnvironmentPreparer
#include "gi/closure.h"
#include "cjs/context.h"
#include "cjs/jsapi-util.h"
+#include "cjs/jsapi-util-root.h"
#include "cjs/macros.h"
#include "cjs/mainloop.h"
#include "cjs/profiler.h"
@@ -51,11 +53,12 @@
using ObjectInitList =
JS::GCVector<JS::Heap<JSObject*>, 0, js::SystemAllocPolicy>;
using FundamentalTable =
- JS::GCHashMap<void*, JS::Heap<JSObject*>, js::DefaultHasher<void*>,
+ JS::GCHashMap<void*, Gjs::WeakPtr<JSObject*>, js::DefaultHasher<void*>,
js::SystemAllocPolicy>;
using GTypeTable =
- JS::GCHashMap<GType, JS::Heap<JSObject*>, js::DefaultHasher<GType>,
+ JS::GCHashMap<GType, Gjs::WeakPtr<JSObject*>, js::DefaultHasher<GType>,
js::SystemAllocPolicy>;
+using FunctionVector = JS::GCVector<JSFunction*, 0, js::SystemAllocPolicy>;
class GjsContextPrivate : public JS::JobQueue {
public:
@@ -87,7 +90,8 @@
std::vector<std::pair<DestroyNotify, void*>> m_destroy_notifications;
std::vector<Gjs::Closure::Ptr> m_async_closures;
- std::unordered_map<uint64_t, GjsAutoChar> m_unhandled_rejection_stacks;
+ std::unordered_map<uint64_t, JS::UniqueChars> m_unhandled_rejection_stacks;
+ FunctionVector m_cleanup_tasks;
GjsProfiler* m_profiler;
@@ -117,7 +121,6 @@
/* flags */
std::atomic_bool m_destroying = ATOMIC_VAR_INIT(false);
- bool m_in_gc_sweep : 1;
bool m_should_exit : 1;
bool m_force_gc : 1;
bool m_draining_job_queue : 1;
@@ -126,13 +129,6 @@
bool m_unhandled_exception : 1;
bool m_should_listen_sigusr2 : 1;
- // If profiling is enabled, we record the durations and reason for GC mark
- // and sweep
- int64_t m_gc_begin_time;
- int64_t m_sweep_begin_time;
- int64_t m_group_sweep_begin_time;
- const char* m_gc_reason; // statically allocated
-
void schedule_gc_internal(bool force_gc);
static gboolean trigger_gc_if_needed(void* data);
void on_garbage_collection(JSGCStatus, JS::GCReason);
@@ -192,7 +188,6 @@
[[nodiscard]] GjsProfiler* profiler() const { return m_profiler; }
[[nodiscard]] const GjsAtoms& atoms() const { return *m_atoms; }
[[nodiscard]] bool destroying() const { return m_destroying.load(); }
- [[nodiscard]] bool sweeping() const { return m_in_gc_sweep; }
[[nodiscard]] const char* program_name() const { return m_program_name; }
void set_program_name(char* value) { m_program_name = value; }
GJS_USE const char* program_path(void) const { return m_program_path; }
@@ -256,12 +251,19 @@
JS::HandleObject incumbent_global) override;
void runJobs(JSContext* cx) override;
[[nodiscard]] bool empty() const override { return m_job_queue.empty(); }
+ [[nodiscard]] bool isDrainingStopped() const override {
+ return !m_draining_job_queue;
+ }
js::UniquePtr<JS::JobQueue::SavedJobQueue> saveJobQueue(
JSContext* cx) override;
GJS_JSAPI_RETURN_CONVENTION bool run_jobs_fallible();
- void register_unhandled_promise_rejection(uint64_t id, GjsAutoChar&& stack);
+ void register_unhandled_promise_rejection(uint64_t id,
+ JS::UniqueChars&& stack);
void unregister_unhandled_promise_rejection(uint64_t id);
+ GJS_JSAPI_RETURN_CONVENTION bool queue_finalization_registry_cleanup(
+ JSFunction* cleanup_task);
+ GJS_JSAPI_RETURN_CONVENTION bool run_finalization_registry_cleanup();
void register_notifier(DestroyNotify notify_func, void* data);
void unregister_notifier(DestroyNotify notify_func, void* data);
@@ -270,9 +272,6 @@
[[nodiscard]] bool register_module(const char* identifier,
const char* filename, GError** error);
- void set_gc_status(JSGCStatus status, JS::GCReason reason);
- void set_finalize_status(JSFinalizeStatus status);
-
static void trace(JSTracer* trc, void* data);
void free_profiler(void);
diff -ruN cjs-6.2.0-orig/cjs/context.cpp cjs-6.2.0/cjs/context.cpp
--- cjs-6.2.0-orig/cjs/context.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/context.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -12,13 +12,13 @@
#ifdef HAVE_UNISTD_H
# include <unistd.h> // for getpid
-#elif defined (_WIN32)
-# include <process.h>
#endif
-#ifdef DEBUG
-# include <algorithm> // for find
+#ifdef G_OS_WIN32
+# include <process.h>
+# include <windows.h>
#endif
+
#include <new>
#include <string> // for u16string
#include <thread> // for get_id
@@ -31,10 +31,6 @@
#include <glib-object.h>
#include <glib.h>
-#ifdef G_OS_WIN32
-#include <windows.h>
-#endif
-
#include <js/AllocPolicy.h> // for SystemAllocPolicy
#include <js/CallAndConstruct.h> // for Call, JS_CallFunctionValue
#include <js/CallArgs.h> // for UndefinedHandleValue
@@ -48,7 +44,7 @@
#include <js/GCHashTable.h> // for WeakCache
#include <js/GCVector.h> // for RootedVector
#include <js/GlobalObject.h> // for CurrentGlobalOrNull
-#include <js/HashTable.h> // for DefaultHasher via WeakCache
+#include <js/HeapAPI.h> // for ExposeObjectToActiveJS
#include <js/Id.h>
#include <js/Modules.h>
#include <js/Promise.h> // for JobQueue::SavedJobQueue
@@ -67,6 +63,7 @@
#include <js/friend/DumpFunctions.h>
#include <jsapi.h> // for JS_GetFunctionObject, JS_Ge...
#include <jsfriendapi.h> // for ScriptEnvironmentPreparer
+#include <mozilla/UniquePtr.h> // for UniquePtr::get
#include "gi/closure.h" // for Closure::Ptr, Closure
#include "gi/function.h"
@@ -93,7 +90,10 @@
#include "cjs/profiler.h"
#include "cjs/promise.h"
#include "cjs/text-encoding.h"
-#include "modules/modules.h"
+#include "modules/cairo-module.h"
+#include "modules/console.h"
+#include "modules/print.h"
+#include "modules/system.h"
#include "util/log.h"
namespace mozilla {
@@ -127,8 +127,6 @@
G_DEFINE_TYPE_WITH_PRIVATE(GjsContext, gjs_context, G_TYPE_OBJECT);
-Gjs::NativeModuleRegistry& registry = Gjs::NativeModuleRegistry::get();
-
GjsContextPrivate* GjsContextPrivate::from_object(GObject* js_context) {
g_return_val_if_fail(GJS_IS_CONTEXT(js_context), nullptr);
return static_cast<GjsContextPrivate*>(
@@ -322,7 +320,7 @@
g_object_class_install_property(object_class, PROP_EXEC_AS_MODULE, pspec);
g_param_spec_unref(pspec);
- /* For CjsPrivate */
+ /* For GjsPrivate */
if (!g_getenv("GJS_USE_UNINSTALLED_FILES")) {
#ifdef G_OS_WIN32
extern HMODULE gjs_dll;
@@ -336,13 +334,16 @@
#endif
g_irepository_prepend_search_path(priv_typelib_dir);
}
+ auto& registry = Gjs::NativeModuleDefineFuncs::get();
registry.add("_promiseNative", gjs_define_native_promise_stuff);
registry.add("_byteArrayNative", gjs_define_byte_array_stuff);
registry.add("_encodingNative", gjs_define_text_encoding_stuff);
registry.add("_gi", gjs_define_private_gi_stuff);
registry.add("gi", gjs_define_repo);
-
- gjs_register_static_modules();
+ registry.add("cairoNative", gjs_js_define_cairo_stuff);
+ registry.add("system", gjs_js_define_system_stuff);
+ registry.add("console", gjs_define_console_stuff);
+ registry.add("_print", gjs_define_print_stuff);
}
void GjsContextPrivate::trace(JSTracer* trc, void* data) {
@@ -353,12 +354,13 @@
JS::TraceEdge<JSObject*>(trc, &gjs->m_main_loop_hook, "GJS main loop hook");
gjs->m_atoms->trace(trc);
gjs->m_job_queue.trace(trc);
+ gjs->m_cleanup_tasks.trace(trc);
gjs->m_object_init_list.trace(trc);
}
void GjsContextPrivate::warn_about_unhandled_promise_rejections(void) {
for (auto& kv : m_unhandled_rejection_stacks) {
- const char *stack = kv.second;
+ const char* stack = kv.second.get();
g_warning("Unhandled promise rejection. To suppress this warning, add "
"an error handler to your promise chain with .catch() or a "
"try-catch block around your await expression. %s%s",
@@ -533,10 +535,8 @@
JS::Value* vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- JSString* id =
- JS_GetFunctionDisplayId(JS_GetObjectFunction(&args.callee()));
gjs_debug(GJS_DEBUG_IMPORTER, "Module evaluation promise rejected: %s",
- gjs_debug_string(id).c_str());
+ gjs_debug_callable(&args.callee()).c_str());
JS::HandleValue error = args.get(0);
@@ -555,10 +555,8 @@
JS::Value* vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- JSString* id =
- JS_GetFunctionDisplayId(JS_GetObjectFunction(&args.callee()));
gjs_debug(GJS_DEBUG_IMPORTER, "Module evaluation promise resolved: %s",
- gjs_debug_string(id).c_str());
+ gjs_debug_callable(&args.callee()).c_str());
args.rval().setUndefined();
@@ -619,11 +617,9 @@
[](JSContext* cx, unsigned argc, JS::Value* vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- JSString* id =
- JS_GetFunctionDisplayId(JS_GetObjectFunction(&args.callee()));
gjs_debug(GJS_DEBUG_IMPORTER,
"Module evaluation promise rejected: %s",
- gjs_debug_string(id).c_str());
+ gjs_debug_callable(&args.callee()).c_str());
JS::HandleValue error = args.get(0);
// Abort because this module is required.
@@ -886,6 +882,11 @@
m_auto_gc_id = g_timeout_add_seconds_full(G_PRIORITY_LOW, 10,
trigger_gc_if_needed, this,
nullptr);
+
+ if (force_gc)
+ g_source_set_name_by_id(m_auto_gc_id, "[gjs] Garbage Collection (Big Hammer)");
+ else
+ g_source_set_name_by_id(m_auto_gc_id, "[gjs] Garbage Collection");
}
/*
@@ -903,17 +904,14 @@
}
void GjsContextPrivate::on_garbage_collection(JSGCStatus status, JS::GCReason reason) {
- int64_t now = 0;
if (m_profiler)
- now = g_get_monotonic_time() * 1000L;
+ _gjs_profiler_set_gc_status(m_profiler, status, reason);
switch (status) {
case JSGC_BEGIN:
- m_gc_begin_time = now;
- m_gc_reason = gjs_explain_gc_reason(reason);
gjs_debug_lifecycle(GJS_DEBUG_CONTEXT,
"Begin garbage collection because of %s",
- m_gc_reason);
+ gjs_explain_gc_reason(reason));
// We finalize any pending toggle refs before doing any garbage
// collection, so that we can collect the JS wrapper objects, and in
@@ -925,14 +923,6 @@
m_async_closures.shrink_to_fit();
break;
case JSGC_END:
- if (m_profiler && m_gc_begin_time != 0) {
- _gjs_profiler_add_mark(m_profiler, m_gc_begin_time,
- now - m_gc_begin_time, "GJS",
- "Garbage collection", m_gc_reason);
- }
- m_gc_begin_time = 0;
- m_gc_reason = nullptr;
-
m_destroy_notifications.shrink_to_fit();
gjs_debug_lifecycle(GJS_DEBUG_CONTEXT, "End garbage collection");
break;
@@ -941,73 +931,6 @@
}
}
-void GjsContextPrivate::set_finalize_status(JSFinalizeStatus status) {
- // Implementation note for mozjs-24:
- //
- // Sweeping happens in two phases, in the first phase all GC things from the
- // allocation arenas are queued for sweeping, then the actual sweeping
- // happens. The first phase is marked by JSFINALIZE_GROUP_START, the second
- // one by JSFINALIZE_GROUP_END, and finally we will see
- // JSFINALIZE_COLLECTION_END at the end of all GC. (see jsgc.cpp,
- // BeginSweepPhase/BeginSweepingZoneGroup and SweepPhase, all called from
- // IncrementalCollectSlice).
- //
- // Incremental GC muddies the waters, because BeginSweepPhase is always run
- // to entirety, but SweepPhase can be run incrementally and mixed with JS
- // code runs or even native code, when MaybeGC/IncrementalGC return.
- //
- // Luckily for us, objects are treated specially, and are not really queued
- // for deferred incremental finalization (unless they are marked for
- // background sweeping). Instead, they are finalized immediately during
- // phase 1, so the following guarantees are true (and we rely on them):
- // - phase 1 of GC will begin and end in the same JSAPI call (i.e., our
- // callback will be called with GROUP_START and the triggering JSAPI call
- // will not return until we see a GROUP_END)
- // - object finalization will begin and end in the same JSAPI call
- // - therefore, if there is a finalizer frame somewhere in the stack,
- // GjsContextPrivate::sweeping() will return true.
- //
- // Comments in mozjs-24 imply that this behavior might change in the future,
- // but it hasn't changed in mozilla-central as of 2014-02-23. In addition to
- // that, the mozilla-central version has a huge comment in a different
- // portion of the file, explaining why finalization of objects can't be
- // mixed with JS code, so we can probably rely on this behavior.
-
- int64_t now = 0;
-
- if (m_profiler)
- now = g_get_monotonic_time() * 1000L;
-
- switch (status) {
- case JSFINALIZE_GROUP_PREPARE:
- m_in_gc_sweep = true;
- m_sweep_begin_time = now;
- break;
- case JSFINALIZE_GROUP_START:
- m_group_sweep_begin_time = now;
- break;
- case JSFINALIZE_GROUP_END:
- if (m_profiler && m_group_sweep_begin_time != 0) {
- _gjs_profiler_add_mark(m_profiler, m_group_sweep_begin_time,
- now - m_group_sweep_begin_time, "GJS",
- "Group sweep", nullptr);
- }
- m_group_sweep_begin_time = 0;
- break;
- case JSFINALIZE_COLLECTION_END:
- m_in_gc_sweep = false;
- if (m_profiler && m_sweep_begin_time != 0) {
- _gjs_profiler_add_mark(m_profiler, m_sweep_begin_time,
- now - m_sweep_begin_time, "GJS", "Sweep",
- nullptr);
- }
- m_sweep_begin_time = 0;
- break;
- default:
- g_assert_not_reached();
- }
-}
-
void GjsContextPrivate::exit(uint8_t exit_code) {
g_assert(!m_should_exit);
m_should_exit = true;
@@ -1096,6 +1019,14 @@
JS::HandleValueArray args(JS::HandleValueArray::empty());
JS::RootedValue rval(m_cx);
+ if (m_job_queue.length() == 0) {
+ // Check FinalizationRegistry cleanup tasks at least once if there are
+ // no microtasks queued. This may enqueue more microtasks, which will be
+ // appended to m_job_queue.
+ if (!run_finalization_registry_cleanup())
+ retval = false;
+ }
+
/* Execute jobs in a loop until we've reached the end of the queue.
* Since executing a job can trigger enqueueing of additional jobs,
* it's crucial to recheck the queue length during each iteration. */
@@ -1139,6 +1070,11 @@
}
}
gjs_debug(GJS_DEBUG_MAINLOOP, "Completed job %zu", ix);
+
+ // Run FinalizationRegistry cleanup tasks after each job. Cleanup tasks
+ // may enqueue more microtasks, which will be appended to m_job_queue.
+ if (!run_finalization_registry_cleanup())
+ retval = false;
}
m_draining_job_queue = false;
@@ -1148,6 +1084,44 @@
return retval;
}
+bool GjsContextPrivate::run_finalization_registry_cleanup() {
+ bool retval = true;
+
+ JS::Rooted<FunctionVector> tasks{m_cx};
+ std::swap(tasks.get(), m_cleanup_tasks);
+ g_assert(m_cleanup_tasks.empty());
+
+ JS::RootedFunction task{m_cx};
+ JS::RootedValue unused_rval{m_cx};
+ for (JSFunction* func : tasks) {
+ gjs_debug(GJS_DEBUG_MAINLOOP,
+ "Running FinalizationRegistry cleanup callback");
+
+ task.set(func);
+ JS::ExposeObjectToActiveJS(JS_GetFunctionObject(func));
+
+ JSAutoRealm ar{m_cx, JS_GetFunctionObject(func)};
+ if (!JS_CallFunction(m_cx, nullptr, task, JS::HandleValueArray::empty(),
+ &unused_rval)) {
+ // Same logic as above
+ if (!JS_IsExceptionPending(m_cx)) {
+ if (!should_exit(nullptr))
+ g_critical(
+ "FinalizationRegistry callback terminated with "
+ "uncatchable exception");
+ retval = false;
+ continue;
+ }
+ gjs_log_exception_uncaught(m_cx);
+ }
+
+ gjs_debug(GJS_DEBUG_MAINLOOP,
+ "Completed FinalizationRegistry cleanup callback");
+ }
+
+ return retval;
+}
+
class GjsContextPrivate::SavedQueue : public JS::JobQueue::SavedJobQueue {
private:
GjsContextPrivate* m_gjs;
@@ -1187,7 +1161,7 @@
}
void GjsContextPrivate::register_unhandled_promise_rejection(
- uint64_t id, GjsAutoChar&& stack) {
+ uint64_t id, JS::UniqueChars&& stack) {
m_unhandled_rejection_stacks[id] = std::move(stack);
}
@@ -1202,6 +1176,11 @@
}
}
+bool GjsContextPrivate::queue_finalization_registry_cleanup(
+ JSFunction* cleanup_task) {
+ return m_cleanup_tasks.append(cleanup_task);
+}
+
void GjsContextPrivate::async_closure_enqueue_for_gc(Gjs::Closure* trampoline) {
// Because we can't free the mmap'd data for a callback
// while it's in use, this list keeps track of ones that
diff -ruN cjs-6.2.0-orig/cjs/context.h cjs-6.2.0/cjs/context.h
--- cjs-6.2.0-orig/cjs/context.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/context.h 2024-10-14 18:38:17.000000000 +0200
@@ -8,7 +8,7 @@
#define GJS_CONTEXT_H_
#if !defined(INSIDE_GJS_H) && !defined(GJS_COMPILATION)
-# error "Only <cjs/gjs.h> can be included directly."
+# error "Only <gjs/gjs.h> can be included directly."
#endif
#include <stdbool.h> /* IWYU pragma: keep */
diff -ruN cjs-6.2.0-orig/cjs/coverage.h cjs-6.2.0/cjs/coverage.h
--- cjs-6.2.0-orig/cjs/coverage.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/coverage.h 2024-10-14 18:38:17.000000000 +0200
@@ -9,7 +9,7 @@
#define GJS_COVERAGE_H_
#if !defined(INSIDE_GJS_H) && !defined(GJS_COMPILATION)
-# error "Only <cjs/gjs.h> can be included directly."
+# error "Only <gjs/gjs.h> can be included directly."
#endif
#include <gio/gio.h>
diff -ruN cjs-6.2.0-orig/cjs/deprecation.cpp cjs-6.2.0/cjs/deprecation.cpp
--- cjs-6.2.0-orig/cjs/deprecation.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/deprecation.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -49,8 +49,14 @@
"to be exported from a module must be defined with 'var'. The property "
"access will work as previously for the time being, but please fix your "
"code anyway.",
+
+ // PlatformSpecificTypelib:
+ ("{} has been moved to a separate platform-specific library. Please update "
+ "your code to use {} instead."),
};
+static_assert(G_N_ELEMENTS(messages) == GjsDeprecationMessageId::LastValue);
+
struct DeprecationEntry {
GjsDeprecationMessageId id;
std::string loc;
@@ -111,9 +117,9 @@
warn_deprecated_unsafe_internal(cx, id, messages[id]);
}
-void _gjs_warn_deprecated_once_per_callsite(JSContext* cx,
- GjsDeprecationMessageId id,
- std::vector<const char*> args) {
+void _gjs_warn_deprecated_once_per_callsite(
+ JSContext* cx, GjsDeprecationMessageId id,
+ const std::vector<const char*>& args) {
// In C++20, use std::format() for this
std::string_view format_string{messages[id]};
std::stringstream message;
@@ -126,7 +132,7 @@
while ((pos = format_string.find("{}", pos)) != std::string::npos) {
if (args_ptr >= nargs_given) {
g_critical("Only %zu format args passed for message ID %u",
- nargs_given, id);
+ nargs_given, unsigned{id});
return;
}
@@ -136,11 +142,12 @@
}
if (args_ptr != nargs_given) {
g_critical("Excess %zu format args passed for message ID %u",
- nargs_given, id);
+ nargs_given, unsigned{id});
return;
}
message << format_string.substr(copied, std::string::npos);
- warn_deprecated_unsafe_internal(cx, id, message.str().c_str());
+ std::string message_formatted = message.str();
+ warn_deprecated_unsafe_internal(cx, id, message_formatted.c_str());
}
diff -ruN cjs-6.2.0-orig/cjs/deprecation.h cjs-6.2.0/cjs/deprecation.h
--- cjs-6.2.0-orig/cjs/deprecation.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/deprecation.h 2024-09-16 22:32:54.000000000 +0200
@@ -5,22 +5,26 @@
#ifndef GJS_DEPRECATION_H_
#define GJS_DEPRECATION_H_
+#include <config.h>
+
#include <vector>
struct JSContext;
-enum GjsDeprecationMessageId {
+enum GjsDeprecationMessageId : unsigned {
None,
ByteArrayInstanceToString,
DeprecatedGObjectProperty,
ModuleExportedLetOrConst,
+ PlatformSpecificTypelib,
+ LastValue, // insert new elements before this one
};
void _gjs_warn_deprecated_once_per_callsite(JSContext* cx,
GjsDeprecationMessageId message);
-void _gjs_warn_deprecated_once_per_callsite(JSContext* cx,
- GjsDeprecationMessageId id,
- std::vector<const char*> args);
+void _gjs_warn_deprecated_once_per_callsite(
+ JSContext* cx, GjsDeprecationMessageId id,
+ const std::vector<const char*>& args);
#endif // GJS_DEPRECATION_H_
diff -ruN cjs-6.2.0-orig/cjs/engine.cpp cjs-6.2.0/cjs/engine.cpp
--- cjs-6.2.0-orig/cjs/engine.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/engine.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -19,24 +19,32 @@
#include <js/ContextOptions.h>
#include <js/GCAPI.h> // for JS_SetGCParameter, JS_AddFin...
#include <js/Initialization.h> // for JS_Init, JS_ShutDown
+#include <js/Principals.h>
#include <js/Promise.h>
#include <js/RootingAPI.h>
#include <js/Stack.h> // for JS_SetNativeStackQuota
+#include <js/StructuredClone.h> // for JS_WriteUint32Pair
#include <js/TypeDecls.h>
+#include <js/Utility.h> // for UniqueChars
#include <js/Warnings.h>
#include <js/experimental/SourceHook.h>
#include <jsapi.h> // for JS_SetGlobalJitCompilerOption
+#include <mozilla/Atomics.h> // for Atomic in JSPrincipals
#include <mozilla/UniquePtr.h>
#include "cjs/context-private.h"
#include "cjs/engine.h"
#include "cjs/jsapi-util.h"
+#include "cjs/profiler-private.h"
#include "util/log.h"
+struct JSStructuredCloneWriter;
+
static void gjs_finalize_callback(JS::GCContext*, JSFinalizeStatus status,
void* data) {
auto* gjs = static_cast<GjsContextPrivate*>(data);
- gjs->set_finalize_status(status);
+ if (gjs->profiler())
+ _gjs_profiler_set_finalize_status(gjs->profiler(), status);
}
static void on_promise_unhandled_rejection(
@@ -52,10 +60,19 @@
}
JS::RootedObject allocation_site(cx, JS::GetPromiseAllocationSite(promise));
- GjsAutoChar stack = gjs_format_stack_trace(cx, allocation_site);
+ JS::UniqueChars stack = format_saved_frame(cx, allocation_site);
gjs->register_unhandled_promise_rejection(id, std::move(stack));
}
+static void on_cleanup_finalization_registry(JSFunction* cleanup_task,
+ JSObject* incumbent_global
+ [[maybe_unused]],
+ void* data) {
+ auto* gjs = static_cast<GjsContextPrivate*>(data);
+ if (!gjs->queue_finalization_registry_cleanup(cleanup_task))
+ g_critical("Out of memory queueing FinalizationRegistry cleanup task");
+}
+
bool gjs_load_internal_source(JSContext* cx, const char* filename, char** src,
size_t* length) {
GjsAutoError error;
@@ -82,31 +99,26 @@
HMODULE gjs_dll;
static bool gjs_is_inited = false;
-BOOL WINAPI
-DllMain (HINSTANCE hinstDLL,
-DWORD fdwReason,
-LPVOID lpvReserved)
-{
- switch (fdwReason)
- {
- case DLL_PROCESS_ATTACH: {
- gjs_dll = hinstDLL;
- const char* reason = JS_InitWithFailureDiagnostic();
- if (reason)
- g_error("Could not initialize JavaScript: %s", reason);
- gjs_is_inited = true;
- } break;
-
- case DLL_THREAD_DETACH:
- JS_ShutDown ();
- break;
-
- default:
- /* do nothing */
- ;
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH: {
+ gjs_dll = hinstDLL;
+ const char* reason = JS_InitWithFailureDiagnostic();
+ if (reason)
+ g_error("Could not initialize JavaScript: %s", reason);
+ gjs_is_inited = true;
+ } break;
+
+ case DLL_THREAD_DETACH:
+ JS_ShutDown();
+ break;
+
+ default:
+ /* do nothing */
+ ;
}
- return TRUE;
+ return TRUE;
}
#else
@@ -128,6 +140,49 @@
static GjsInit gjs_is_inited;
#endif
+// JSPrincipals (basically a weird name for security callbacks) which are in
+// effect in the module loader's realm (GjsInternalGlobal). This prevents module
+// loader stack frames from showing up in public stack traces.
+class ModuleLoaderPrincipals final : public JSPrincipals {
+ static constexpr uint32_t STRUCTURED_CLONE_TAG = JS_SCTAG_USER_MIN;
+
+ bool write(JSContext* cx [[maybe_unused]],
+ JSStructuredCloneWriter* writer) override {
+ g_assert_not_reached();
+ return JS_WriteUint32Pair(writer, STRUCTURED_CLONE_TAG, 1);
+ }
+
+ bool isSystemOrAddonPrincipal() override { return true; }
+
+ public:
+ static bool subsumes(JSPrincipals* first, JSPrincipals* second) {
+ if (first != &the_principals && second == &the_principals)
+ return false;
+ return true;
+ }
+
+ static void destroy(JSPrincipals* principals [[maybe_unused]]) {
+ g_assert(principals == &the_principals &&
+ "Should not create other instances of ModuleLoaderPrinciples");
+ g_assert(principals->refcount == 0 &&
+ "Mismatched JS_HoldPrincipals/JS_DropPrincipals");
+ }
+
+ // Singleton
+ static ModuleLoaderPrincipals the_principals;
+};
+
+ModuleLoaderPrincipals ModuleLoaderPrincipals::the_principals{};
+
+JSPrincipals* get_internal_principals() {
+ return &ModuleLoaderPrincipals::the_principals;
+}
+
+static const JSSecurityCallbacks security_callbacks = {
+ /* contentSecurityPolicyAllows = */ nullptr,
+ &ModuleLoaderPrincipals::subsumes,
+};
+
JSContext* gjs_create_js_context(GjsContextPrivate* uninitialized_gjs) {
g_assert(gjs_is_inited);
JSContext *cx = JS_NewContext(32 * 1024 * 1024 /* max bytes */);
@@ -149,11 +204,15 @@
/* set ourselves as the private data */
JS_SetContextPrivate(cx, uninitialized_gjs);
+ JS_SetSecurityCallbacks(cx, &security_callbacks);
+ JS_InitDestroyPrincipalsCallback(cx, &ModuleLoaderPrincipals::destroy);
JS_AddFinalizeCallback(cx, gjs_finalize_callback, uninitialized_gjs);
JS::SetWarningReporter(cx, gjs_warning_reporter);
JS::SetJobQueue(cx, dynamic_cast<JS::JobQueue*>(uninitialized_gjs));
JS::SetPromiseRejectionTrackerCallback(cx, on_promise_unhandled_rejection,
uninitialized_gjs);
+ JS::SetHostCleanupFinalizationRegistryCallback(
+ cx, on_cleanup_finalization_registry, uninitialized_gjs);
// We use this to handle "lazy sources" that SpiderMonkey doesn't need to
// keep in memory. Most sources should be kept in memory, but we can skip
diff -ruN cjs-6.2.0-orig/cjs/engine.h cjs-6.2.0/cjs/engine.h
--- cjs-6.2.0-orig/cjs/engine.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/engine.h 2024-09-16 22:32:54.000000000 +0200
@@ -5,14 +5,19 @@
#ifndef GJS_ENGINE_H_
#define GJS_ENGINE_H_
+#include <config.h>
+
#include <stddef.h> // for size_t
class GjsContextPrivate;
struct JSContext;
+struct JSPrincipals;
JSContext* gjs_create_js_context(GjsContextPrivate* uninitialized_gjs);
bool gjs_load_internal_source(JSContext* cx, const char* filename, char** src,
size_t* length);
+JSPrincipals* get_internal_principals();
+
#endif // GJS_ENGINE_H_
diff -ruN cjs-6.2.0-orig/cjs/error-types.cpp cjs-6.2.0/cjs/error-types.cpp
--- cjs-6.2.0-orig/cjs/error-types.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/error-types.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -2,6 +2,8 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2008 litl, LLC
+#include <config.h>
+
#include <glib-object.h>
#include "cjs/error-types.h"
diff -ruN cjs-6.2.0-orig/cjs/error-types.h cjs-6.2.0/cjs/error-types.h
--- cjs-6.2.0-orig/cjs/error-types.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/error-types.h 2024-10-14 18:38:17.000000000 +0200
@@ -8,7 +8,7 @@
#define GJS_ERROR_TYPES_H_
#if !defined(INSIDE_GJS_H) && !defined(GJS_COMPILATION)
-# error "Only <cjs/gjs.h> can be included directly."
+# error "Only <gjs/gjs.h> can be included directly."
#endif
#include <glib-object.h>
diff -ruN cjs-6.2.0-orig/cjs/gjs_pch.hh cjs-6.2.0/cjs/gjs_pch.hh
--- cjs-6.2.0-orig/cjs/gjs_pch.hh 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/gjs_pch.hh 2024-10-14 18:38:17.000000000 +0200
@@ -58,6 +58,7 @@
#include <js/CallArgs.h>
#include <js/CharacterEncoding.h>
#include <js/Class.h>
+#include <js/ColumnNumber.h>
#include <js/ComparisonOperators.h>
#include <js/CompilationAndEvaluation.h>
#include <js/CompileOptions.h>
@@ -80,6 +81,8 @@
#include <js/MemoryFunctions.h>
#include <js/Modules.h>
#include <js/Object.h>
+#include <js/Principals.h>
+#include <js/Printer.h>
#include <js/ProfilingCategory.h>
#include <js/ProfilingStack.h>
#include <js/Promise.h>
@@ -95,6 +98,7 @@
#include <js/SourceText.h>
#include <js/Stack.h>
#include <js/String.h>
+#include <js/StructuredClone.h>
#include <js/Symbol.h>
#include <js/TracingAPI.h>
#include <js/TypeDecls.h>
diff -ruN cjs-6.2.0-orig/cjs/global.cpp cjs-6.2.0/cjs/global.cpp
--- cjs-6.2.0-orig/cjs/global.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/global.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -46,21 +46,16 @@
}
class GjsBaseGlobal {
+ GJS_JSAPI_RETURN_CONVENTION
static JSObject* base(JSContext* cx, const JSClass* clasp,
- JS::RealmCreationOptions options) {
- // Enable WeakRef without the cleanupSome specification
- // Re-evaluate if cleanupSome is standardized
- // See: https://github.com/tc39/proposal-cleanup-some
- options.setWeakRefsEnabled(
- JS::WeakRefSpecifier::EnabledWithoutCleanupSome);
-
+ JS::RealmCreationOptions options,
+ JSPrincipals* principals = nullptr) {
JS::RealmBehaviors behaviors;
JS::RealmOptions compartment_options(options, behaviors);
- JS::RootedObject global(
- cx, JS_NewGlobalObject(cx, clasp, nullptr, JS::FireOnNewGlobalHook,
- compartment_options));
-
+ JS::RootedObject global{cx, JS_NewGlobalObject(cx, clasp, principals,
+ JS::FireOnNewGlobalHook,
+ compartment_options)};
if (!global)
return nullptr;
@@ -74,18 +69,22 @@
}
protected:
- [[nodiscard]] static JSObject* create(
+ GJS_JSAPI_RETURN_CONVENTION
+ static JSObject* create(
JSContext* cx, const JSClass* clasp,
- JS::RealmCreationOptions options = JS::RealmCreationOptions()) {
+ JS::RealmCreationOptions options = JS::RealmCreationOptions(),
+ JSPrincipals* principals = nullptr) {
options.setNewCompartmentAndZone();
- return base(cx, clasp, options);
+ return base(cx, clasp, options, principals);
}
- [[nodiscard]] static JSObject* create_with_compartment(
+ GJS_JSAPI_RETURN_CONVENTION
+ static JSObject* create_with_compartment(
JSContext* cx, JS::HandleObject existing, const JSClass* clasp,
- JS::RealmCreationOptions options = JS::RealmCreationOptions()) {
+ JS::RealmCreationOptions options = JS::RealmCreationOptions(),
+ JSPrincipals* principals = nullptr) {
options.setExistingCompartment(existing);
- return base(cx, clasp, options);
+ return base(cx, clasp, options, principals);
}
GJS_JSAPI_RETURN_CONVENTION
@@ -132,8 +131,8 @@
JS::RootedObject native_obj(m_cx);
- if (!Gjs::NativeModuleRegistry::get().load(m_cx, id.get(),
- &native_obj)) {
+ if (!Gjs::NativeModuleDefineFuncs::get().define(m_cx, id.get(),
+ &native_obj)) {
gjs_throw(m_cx, "Failed to load native module: %s", id.get());
return false;
}
@@ -165,12 +164,14 @@
JS_FS_END};
public:
- [[nodiscard]] static JSObject* create(JSContext* cx) {
+ GJS_JSAPI_RETURN_CONVENTION
+ static JSObject* create(JSContext* cx) {
return GjsBaseGlobal::create(cx, &klass);
}
- [[nodiscard]] static JSObject* create_with_compartment(
- JSContext* cx, JS::HandleObject cmp_global) {
+ GJS_JSAPI_RETURN_CONVENTION
+ static JSObject* create_with_compartment(JSContext* cx,
+ JS::HandleObject cmp_global) {
return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
}
@@ -238,17 +239,20 @@
JS_FN("loadNative", &load_native_module, 1, 0), JS_FS_END};
public:
- [[nodiscard]] static JSObject* create(JSContext* cx) {
+ GJS_JSAPI_RETURN_CONVENTION
+ static JSObject* create(JSContext* cx) {
JS::RealmCreationOptions options;
options.setToSourceEnabled(true); // debugger uses uneval()
return GjsBaseGlobal::create(cx, &klass, options);
}
- [[nodiscard]] static JSObject* create_with_compartment(
- JSContext* cx, JS::HandleObject cmp_global) {
+ GJS_JSAPI_RETURN_CONVENTION
+ static JSObject* create_with_compartment(JSContext* cx,
+ JS::HandleObject cmp_global) {
return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
}
+ GJS_JSAPI_RETURN_CONVENTION
static bool define_properties(JSContext* cx, JS::HandleObject global,
const char* realm_name,
const char* bootstrap_script) {
@@ -298,15 +302,19 @@
};
public:
- [[nodiscard]] static JSObject* create(JSContext* cx) {
- return GjsBaseGlobal::create(cx, &klass);
+ GJS_JSAPI_RETURN_CONVENTION
+ static JSObject* create(JSContext* cx) {
+ return GjsBaseGlobal::create(cx, &klass, {}, get_internal_principals());
}
- [[nodiscard]] static JSObject* create_with_compartment(
- JSContext* cx, JS::HandleObject cmp_global) {
- return GjsBaseGlobal::create_with_compartment(cx, cmp_global, &klass);
+ GJS_JSAPI_RETURN_CONVENTION
+ static JSObject* create_with_compartment(JSContext* cx,
+ JS::HandleObject cmp_global) {
+ return GjsBaseGlobal::create_with_compartment(
+ cx, cmp_global, &klass, {}, get_internal_principals());
}
+ GJS_JSAPI_RETURN_CONVENTION
static bool define_properties(JSContext* cx, JS::HandleObject global,
const char* realm_name,
const char* bootstrap_script
diff -ruN cjs-6.2.0-orig/cjs/global.h cjs-6.2.0/cjs/global.h
--- cjs-6.2.0-orig/cjs/global.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/global.h 2024-10-14 18:35:57.000000000 +0200
@@ -8,12 +8,12 @@
#include <config.h>
+#include <stdint.h>
+
#include <js/RootingAPI.h> // for Handle
#include <js/TypeDecls.h>
#include <js/Value.h>
-#include <stdint.h>
-
#include "cjs/macros.h"
namespace JS {
diff -ruN cjs-6.2.0-orig/cjs/importer.cpp cjs-6.2.0/cjs/importer.cpp
--- cjs-6.2.0-orig/cjs/importer.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/importer.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -24,9 +24,10 @@
#include <js/ComparisonOperators.h>
#include <js/ErrorReport.h> // for JS_ReportOutOfMemory, JSEXN_ERR
#include <js/Exception.h>
+#include <js/GCVector.h> // for StackGCVector
#include <js/GlobalObject.h> // for CurrentGlobalOrNull
-#include <js/Id.h> // for PropertyKey
-#include <js/Object.h> // for GetClass
+#include <js/Id.h> // for PropertyKey
+#include <js/Object.h> // for GetClass
#include <js/PropertyAndElement.h>
#include <js/PropertyDescriptor.h>
#include <js/PropertySpec.h>
@@ -265,26 +266,21 @@
* gjs_import_native_module:
* @cx: the #JSContext
* @importer: the root importer
- * @parse_name: Name under which the module was registered with
- * add(), should be in the format as returned by
- * g_file_get_parse_name()
+ * @id_str: Name under which the module was registered with add()
*
* Imports a builtin native-code module so that it is available to JS code as
- * `imports[parse_name]`.
+ * `imports[id_str]`.
*
* Returns: true on success, false if an exception was thrown.
*/
-bool
-gjs_import_native_module(JSContext *cx,
- JS::HandleObject importer,
- const char *parse_name)
-{
- gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", parse_name);
+bool gjs_import_native_module(JSContext* cx, JS::HandleObject importer,
+ const char* id_str) {
+ gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", id_str);
JS::RootedObject native_registry(
cx, gjs_get_native_registry(JS::CurrentGlobalOrNull(cx)));
- JS::RootedId id(cx, gjs_intern_string_to_id(cx, parse_name));
+ JS::RootedId id(cx, gjs_intern_string_to_id(cx, id_str));
if (id.isVoid())
return false;
@@ -293,12 +289,12 @@
return false;
if (!module &&
- (!Gjs::NativeModuleRegistry::get().load(cx, parse_name, &module) ||
+ (!Gjs::NativeModuleDefineFuncs::get().define(cx, id_str, &module) ||
!gjs_global_registry_set(cx, native_registry, id, module)))
return false;
- return define_meta_properties(cx, module, nullptr, parse_name, importer) &&
- JS_DefineProperty(cx, importer, parse_name, module,
+ return define_meta_properties(cx, module, nullptr, id_str, importer) &&
+ JS_DefineProperty(cx, importer, id_str, module,
GJS_MODULE_PROP_FLAGS);
}
@@ -496,7 +492,7 @@
/* First try importing an internal module like gi */
if (parent.isNull() &&
- Gjs::NativeModuleRegistry::get().is_registered(name.get())) {
+ Gjs::NativeModuleDefineFuncs::get().is_registered(name.get())) {
if (!gjs_import_native_module(context, obj, name.get()))
return false;
@@ -676,7 +672,8 @@
while (true) {
GFileInfo *info;
GFile *file;
- if (!g_file_enumerator_iterate(direnum, &info, &file, NULL, NULL))
+ if (!direnum ||
+ !g_file_enumerator_iterate(direnum, &info, &file, NULL, NULL))
break;
if (info == NULL || file == NULL)
break;
@@ -896,7 +893,7 @@
/* API users can replace this property from JS, is the idea */
if (!gjs_define_string_array(
context, importer, "searchPath", search_paths,
- /* settable (no READONLY) but not deleteable (PERMANENT) */
+ // settable (no READONLY) but not deletable (PERMANENT)
JSPROP_PERMANENT | JSPROP_RESOLVING))
return nullptr;
diff -ruN cjs-6.2.0-orig/cjs/internal.cpp cjs-6.2.0/cjs/internal.cpp
--- cjs-6.2.0-orig/cjs/internal.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/internal.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2020 Evan Welsh <contact@evanwelsh.com>
-#include "cjs/internal.h"
-
#include <config.h>
#include <stddef.h> // for size_t
@@ -41,6 +39,7 @@
#include "cjs/context-private.h"
#include "cjs/engine.h"
#include "cjs/global.h"
+#include "cjs/internal.h"
#include "cjs/jsapi-util-args.h"
#include "cjs/jsapi-util.h"
#include "cjs/macros.h"
@@ -346,7 +345,15 @@
if (!path)
return false;
- if (!JS_DefineProperty(cx, return_obj, "uri", string_arg,
+ GjsAutoChar no_query_str =
+ g_uri_to_string_partial(parsed, G_URI_HIDE_QUERY);
+ JS::RootedString uri_no_query{cx, JS_NewStringCopyZ(cx, no_query_str)};
+ if (!uri_no_query)
+ return false;
+
+ if (!JS_DefineProperty(cx, return_obj, "uri", uri_no_query,
+ JSPROP_ENUMERATE) ||
+ !JS_DefineProperty(cx, return_obj, "uriWithQuery", string_arg,
JSPROP_ENUMERATE) ||
!JS_DefineProperty(cx, return_obj, "scheme", scheme,
JSPROP_ENUMERATE) ||
@@ -370,12 +377,10 @@
return handle_wrong_args(cx);
GjsAutoUnref<GFile> module_file = g_file_new_for_uri(uri.get());
- GjsAutoUnref<GFile> module_parent_file = g_file_get_parent(module_file);
- if (module_parent_file) {
- GjsAutoUnref<GFile> output = g_file_resolve_relative_path(
- module_parent_file, relative_path.get());
- GjsAutoChar output_uri = g_file_get_uri(output);
+ if (module_file) {
+ GjsAutoChar output_uri = g_uri_resolve_relative(
+ uri.get(), relative_path.get(), G_URI_FLAGS_NONE, nullptr);
JS::ConstUTF8CharsZ uri_chars(output_uri, strlen(output_uri));
JS::RootedString retval(cx, JS_NewStringCopyUTF8Z(cx, uri_chars));
@@ -511,7 +516,7 @@
/* etag_out = */ nullptr, &error)) {
GjsAutoChar uri = g_file_get_uri(G_FILE(file));
gjs_throw_custom(promise->cx, JSEXN_ERR, "ImportError",
- "Unable to load file from: %s (%s)", uri.get(),
+ "Unable to load file async from: %s (%s)", uri.get(),
error->message);
promise->reject_with_pending_exception();
return;
diff -ruN cjs-6.2.0-orig/cjs/jsapi-class.h cjs-6.2.0/cjs/jsapi-class.h
--- cjs-6.2.0-orig/cjs/jsapi-class.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/jsapi-class.h 2024-10-14 18:35:57.000000000 +0200
@@ -10,9 +10,10 @@
#include <glib-object.h>
#include <glib.h>
+#include <js/CallArgs.h> // for JSNative
#include <js/TypeDecls.h>
+#include <js/ValueArray.h>
-#include "gi/wrapperutils.h"
#include "cjs/global.h"
#include "cjs/jsapi-util.h"
#include "cjs/macros.h"
@@ -35,14 +36,23 @@
const JS::HandleValueArray& args);
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_define_property_dynamic(JSContext *cx,
- JS::HandleObject proto,
- const char *prop_name,
- const char *func_namespace,
- JSNative getter,
- JSNative setter,
- JS::HandleValue private_slot,
- unsigned flags);
+bool gjs_define_property_dynamic(JSContext*, JS::HandleObject proto,
+ const char* prop_name, JS::HandleId,
+ const char* func_namespace, JSNative getter,
+ JS::HandleValue getter_slot, JSNative setter,
+ JS::HandleValue setter_slot, unsigned flags);
+
+GJS_JSAPI_RETURN_CONVENTION
+inline bool gjs_define_property_dynamic(JSContext* cx, JS::HandleObject proto,
+ const char* prop_name, JS::HandleId id,
+ const char* func_namespace,
+ JSNative getter, JSNative setter,
+ JS::HandleValue private_slot,
+ unsigned flags) {
+ return gjs_define_property_dynamic(cx, proto, prop_name, id, func_namespace,
+ getter, private_slot, setter,
+ private_slot, flags);
+}
[[nodiscard]] JS::Value gjs_dynamic_property_private_slot(
JSObject* accessor_obj);
diff -ruN cjs-6.2.0-orig/cjs/jsapi-dynamic-class.cpp cjs-6.2.0/cjs/jsapi-dynamic-class.cpp
--- cjs-6.2.0-orig/cjs/jsapi-dynamic-class.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/jsapi-dynamic-class.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -194,31 +194,32 @@
*
* Returns: %true on success, %false if an exception is pending on @cx.
*/
-bool
-gjs_define_property_dynamic(JSContext *cx,
- JS::HandleObject proto,
- const char *prop_name,
- const char *func_namespace,
- JSNative getter,
- JSNative setter,
- JS::HandleValue private_slot,
- unsigned flags)
-{
+bool gjs_define_property_dynamic(JSContext* cx, JS::HandleObject proto,
+ const char* prop_name, JS::HandleId id,
+ const char* func_namespace, JSNative getter,
+ JS::HandleValue getter_slot, JSNative setter,
+ JS::HandleValue setter_slot, unsigned flags) {
GjsAutoChar getter_name = g_strconcat(func_namespace, "_get::", prop_name, nullptr);
GjsAutoChar setter_name = g_strconcat(func_namespace, "_set::", prop_name, nullptr);
- JS::RootedObject getter_obj(cx,
- define_native_accessor_wrapper(cx, getter, 0, getter_name, private_slot));
+ JS::RootedObject getter_obj(
+ cx, define_native_accessor_wrapper(cx, getter, 0, getter_name,
+ getter_slot));
if (!getter_obj)
return false;
- JS::RootedObject setter_obj(cx,
- define_native_accessor_wrapper(cx, setter, 1, setter_name, private_slot));
+ JS::RootedObject setter_obj(
+ cx, define_native_accessor_wrapper(cx, setter, 1, setter_name,
+ setter_slot));
if (!setter_obj)
return false;
- return JS_DefineProperty(cx, proto, prop_name, getter_obj, setter_obj,
- flags);
+ if (id.isVoid()) {
+ return JS_DefineProperty(cx, proto, prop_name, getter_obj, setter_obj,
+ flags);
+ }
+
+ return JS_DefinePropertyById(cx, proto, id, getter_obj, setter_obj, flags);
}
/**
diff -ruN cjs-6.2.0-orig/cjs/jsapi-util-args.h cjs-6.2.0/cjs/jsapi-util-args.h
--- cjs-6.2.0-orig/cjs/jsapi-util-args.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/jsapi-util-args.h 2024-10-14 18:35:57.000000000 +0200
@@ -22,6 +22,7 @@
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <js/Utility.h> // for UniqueChars
+#include <js/Value.h>
#include <mozilla/Result.h> // for GenericErrorResult
#include <mozilla/ResultVariant.h> // IWYU pragma: keep
diff -ruN cjs-6.2.0-orig/cjs/jsapi-util-error.cpp cjs-6.2.0/cjs/jsapi-util-error.cpp
--- cjs-6.2.0-orig/cjs/jsapi-util-error.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/jsapi-util-error.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -12,6 +12,7 @@
#include <js/AllocPolicy.h>
#include <js/CharacterEncoding.h>
+#include <js/ColumnNumber.h>
#include <js/ErrorReport.h>
#include <js/Exception.h>
#include <js/GCHashTable.h> // for GCHashSet
@@ -113,8 +114,10 @@
&source_string);
uint32_t line_num;
JS::GetSavedFrameLine(cx, nullptr, saved_frame, &line_num);
- uint32_t column_num;
- JS::GetSavedFrameColumn(cx, nullptr, saved_frame, &column_num);
+ JS::TaggedColumnNumberOneOrigin tagged_column;
+ JS::GetSavedFrameColumn(cx, nullptr, saved_frame, &tagged_column);
+ JS::ColumnNumberOneOrigin column_num{tagged_column.toLimitedColumnNumber()};
+ // asserts that this isn't a WASM frame
JS::RootedValue v_exc{cx};
if (!JS::CreateError(cx, error_kind, saved_frame, source_string, line_num,
@@ -216,33 +219,30 @@
}
/**
- * gjs_format_stack_trace:
+ * format_saved_frame:
* @cx: the #JSContext
* @saved_frame: a SavedFrame #JSObject
+ * @indent: (optional): spaces of indentation
*
- * Formats a stack trace as a string in filename encoding, suitable for
- * printing to stderr. Ignores any errors.
+ * Formats a stack trace as a UTF-8 string. If there are errors, ignores them
+ * and returns null.
+ * If you print this to stderr, you will need to re-encode it in filename
+ * encoding with g_filename_from_utf8().
*
- * Returns: unique string in filename encoding, or nullptr if no stack trace
+ * Returns (nullable) (transfer full): unique string
*/
-GjsAutoChar
-gjs_format_stack_trace(JSContext *cx,
- JS::HandleObject saved_frame)
-{
+JS::UniqueChars format_saved_frame(JSContext* cx, JS::HandleObject saved_frame,
+ size_t indent /* = 0 */) {
JS::AutoSaveExceptionState saved_exc(cx);
JS::RootedString stack_trace(cx);
JS::UniqueChars stack_utf8;
- if (JS::BuildStackString(cx, nullptr, saved_frame, &stack_trace, 2))
+ if (JS::BuildStackString(cx, nullptr, saved_frame, &stack_trace, indent))
stack_utf8 = JS_EncodeStringToUTF8(cx, stack_trace);
saved_exc.restore();
- if (!stack_utf8)
- return nullptr;
-
- return g_filename_from_utf8(stack_utf8.get(), -1, nullptr, nullptr,
- nullptr);
+ return stack_utf8;
}
void gjs_warning_reporter(JSContext*, JSErrorReport* report) {
@@ -254,8 +254,7 @@
if (gjs_environment_variable_is_set("GJS_ABORT_ON_OOM") &&
!report->isWarning() && report->errorNumber == 137) {
/* 137, JSMSG_OUT_OF_MEMORY */
- g_error("GJS ran out of memory at %s: %i.",
- report->filename,
+ g_error("GJS ran out of memory at %s: %i.", report->filename.c_str(),
report->lineno);
}
@@ -277,6 +276,6 @@
level = G_LOG_LEVEL_WARNING;
}
- g_log(G_LOG_DOMAIN, level, "JS %s: [%s %d]: %s", warning, report->filename,
- report->lineno, report->message().c_str());
+ g_log(G_LOG_DOMAIN, level, "JS %s: [%s %d]: %s", warning,
+ report->filename.c_str(), report->lineno, report->message().c_str());
}
diff -ruN cjs-6.2.0-orig/cjs/jsapi-util-root.h cjs-6.2.0/cjs/jsapi-util-root.h
--- cjs-6.2.0-orig/cjs/jsapi-util-root.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/jsapi-util-root.h 2024-09-16 22:32:54.000000000 +0200
@@ -11,84 +11,57 @@
#include <cstddef> // for nullptr_t
#include <memory>
#include <new>
-#include <type_traits> // for enable_if_t, is_pointer
#include <glib.h>
+#include <js/ComparisonOperators.h>
#include <js/GCAPI.h>
+#include <js/HeapAPI.h> // for ExposeObjectToActiveJS, GetGCThingZone
+#include <js/RootingAPI.h> // for SafelyInitialized
#include <js/TracingAPI.h>
#include <js/TypeDecls.h>
#include "util/log.h"
+namespace JS { template <typename T> struct GCPolicy; }
+
/* jsapi-util-root.h - Utilities for dealing with the lifetime and ownership of
* JS Objects and other things that can be collected by the garbage collector
* (collectively called "GC things.")
*
- * GjsMaybeOwned<T> is a multi-purpose wrapper for a GC thing of type T. You can
+ * GjsMaybeOwned is a multi-purpose wrapper for a JSObject. You can
* wrap a thing in one of three ways:
*
- * - trace the thing (tie it to the lifetime of another GC thing),
- * - root the thing (keep it alive as long as the wrapper is in existence),
- * - maintain a weak pointer to the thing (not keep it alive at all and have it
+ * - trace the object (tie it to the lifetime of another GC thing),
+ * - root the object (keep it alive as long as the wrapper is in existence),
+ * - maintain a weak pointer to the object (not keep it alive at all and have it
* possibly be finalized out from under you).
*
- * To trace or maintain a weak pointer, simply assign a thing of type T to the
+ * To trace or maintain a weak pointer, simply assign an object to the
* GjsMaybeOwned wrapper. For tracing, you must call the trace() method when
* your other GC thing is traced.
*
* Rooting requires a JSContext so can't just assign a thing of type T. Instead
* you need to call the root() method to set up rooting.
*
- * If the thing is rooted, it will be unrooted either when the GjsMaybeOwned is
- * destroyed, or when the JSContext is destroyed. In the latter case, you can
- * get an optional notification by registering a callback in the PrivateContext.
+ * If the thing is rooted, it will be unrooted when the GjsMaybeOwned is
+ * destroyed.
*
* To switch between one of the three modes, you must first call reset(). This
- * drops all references to any GC thing and leaves the GjsMaybeOwned in the
+ * drops all references to any object and leaves the GjsMaybeOwned in the
* same state as if it had just been constructed.
*/
-/* This struct contains operations that must be implemented differently
- * depending on the type of the GC thing. Add more types as necessary. If an
- * implementation is never used, it's OK to leave it out. The compiler will
- * complain if it's used somewhere but not instantiated here.
- */
-template<typename T>
-struct GjsHeapOperation {
- [[nodiscard]] static bool update_after_gc(JS::Heap<T>* location);
- static void expose_to_js(JS::Heap<T>& thing);
-};
-
-template<>
-struct GjsHeapOperation<JSObject *> {
- [[nodiscard]] static bool update_after_gc(JSTracer* trc,
- JS::Heap<JSObject*>* location) {
- JS_UpdateWeakPointerAfterGC(trc, location);
- return (location->unbarrieredGet() == nullptr);
- }
-
- static void expose_to_js(JS::Heap<JSObject *>& thing) {
- JSObject *obj = thing.unbarrieredGet();
- /* If the object has been swept already, then the zone is nullptr */
- if (!obj || !JS::GetGCThingZone(JS::GCCellPtr(obj)))
- return;
- if (!JS::RuntimeHeapIsCollecting())
- JS::ExposeObjectToActiveJS(obj);
- }
-};
-
/* GjsMaybeOwned is intended for use as a member of classes that are allocated
* on the heap. Do not allocate GjsMaybeOwned on the stack, and do not allocate
* any instances of classes that have it as a member on the stack either. */
-template<typename T>
class GjsMaybeOwned {
private:
/* m_root value controls which of these members we can access. When switching
* from one to the other, be careful to call the constructor and destructor
* of JS::Heap, since they use post barriers. */
- JS::Heap<T> m_heap;
- std::unique_ptr<JS::PersistentRooted<T>> m_root;
+ JS::Heap<JSObject*> m_heap;
+ std::unique_ptr<JS::PersistentRootedObject> m_root;
/* No-op unless GJS_VERBOSE_ENABLE_LIFECYCLE is defined to 1. */
inline void debug(const char* what GJS_USED_VERBOSE_LIFECYCLE) {
@@ -104,7 +77,7 @@
m_root.reset();
- new (&m_heap) JS::Heap<T>();
+ new (&m_heap) JS::Heap<JSObject*>();
}
public:
@@ -116,68 +89,61 @@
debug("destroyed");
}
- /* To access the GC thing, call get(). In many cases you can just use the
- * GjsMaybeOwned wrapper in place of the GC thing itself due to the implicit
- * cast operator. But if you want to call methods on the GC thing, for
- * example if it's a JS::Value, you have to use get(). */
- [[nodiscard]] constexpr const T get() const {
+ // COMPAT: constexpr in C++23
+ [[nodiscard]] JSObject* get() const {
return m_root ? m_root->get() : m_heap.get();
}
- constexpr operator const T() const { return get(); }
- /* Use debug_addr() only for debug logging, because it is unbarriered. */
- template <typename U = T>
- [[nodiscard]] constexpr const void* debug_addr(
- std::enable_if_t<std::is_pointer_v<U>>* = nullptr) const {
+ // Use debug_addr() only for debug logging, because it is unbarriered.
+ // COMPAT: constexpr in C++23
+ [[nodiscard]] const void* debug_addr() const {
return m_root ? m_root->get() : m_heap.unbarrieredGet();
}
- constexpr bool operator==(const T& other) const {
+ // COMPAT: constexpr in C++23
+ bool operator==(JSObject* other) const {
if (m_root)
return m_root->get() == other;
return m_heap == other;
}
- constexpr bool operator!=(const T& other) const {
- return !(*this == other);
- }
+ bool operator!=(JSObject* other) const { return !(*this == other); }
- /* We can access the pointer without a read barrier if the only thing we
- * are doing with it is comparing it to nullptr. */
- constexpr bool operator==(std::nullptr_t) const {
+ // We can access the pointer without a read barrier if the only thing we are
+ // are doing with it is comparing it to nullptr.
+ // COMPAT: constexpr in C++23
+ bool operator==(std::nullptr_t) const {
if (m_root)
return m_root->get() == nullptr;
return m_heap.unbarrieredGet() == nullptr;
}
- constexpr bool operator!=(std::nullptr_t) const {
- return !(*this == nullptr);
- }
-
- /* Likewise the truth value does not require a read barrier */
- constexpr explicit operator bool() const { return *this != nullptr; }
+ bool operator!=(std::nullptr_t) const { return !(*this == nullptr); }
- /* You can get a Handle<T> if the thing is rooted, so that you can use this
- * wrapper with stack rooting. However, you must not do this if the
- * JSContext can be destroyed while the Handle is live. */
- [[nodiscard]] constexpr JS::Handle<T> handle() {
+ // Likewise the truth value does not require a read barrier
+ // COMPAT: constexpr in C++23
+ explicit operator bool() const { return *this != nullptr; }
+
+ // You can get a Handle<T> if the thing is rooted, so that you can use this
+ // wrapper with stack rooting. However, you must not do this if the
+ // JSContext can be destroyed while the Handle is live. */
+ // COMPAT: constexpr in C++23
+ [[nodiscard]] JS::HandleObject handle() {
g_assert(m_root);
return *m_root;
}
/* Roots the GC thing. You must not use this if you're already using the
* wrapper to store a non-rooted GC thing. */
- void root(JSContext* cx, const T& thing) {
+ void root(JSContext* cx, JSObject* thing) {
debug("root()");
g_assert(!m_root);
- g_assert(m_heap.get() == JS::SafelyInitialized<T>::create());
+ g_assert(!m_heap);
m_heap.~Heap();
- m_root = std::make_unique<JS::PersistentRooted<T>>(cx, thing);
+ m_root = std::make_unique<JS::PersistentRootedObject>(cx, thing);
}
/* You can only assign directly to the GjsMaybeOwned wrapper in the
* non-rooted case. */
- void
- operator=(const T& thing)
- {
+ void operator=(JSObject* thing) {
g_assert(!m_root);
m_heap = thing;
}
@@ -188,13 +154,18 @@
void prevent_collection() {
debug("prevent_collection()");
g_assert(!m_root);
- GjsHeapOperation<T>::expose_to_js(m_heap);
+ JSObject* obj = m_heap.unbarrieredGet();
+ // If the object has been swept already, then the zone is nullptr
+ if (!obj || !JS::GetGCThingZone(JS::GCCellPtr(obj)))
+ return;
+ if (!JS::RuntimeHeapIsCollecting())
+ JS::ExposeObjectToActiveJS(obj);
}
void reset() {
debug("reset()");
if (!m_root) {
- m_heap = JS::SafelyInitialized<T>::create();
+ m_heap = nullptr;
return;
}
@@ -207,7 +178,7 @@
/* Prevent the thing from being garbage collected while it is in neither
* m_heap nor m_root */
- JS::Rooted<T> thing(cx, m_heap);
+ JS::RootedObject thing{cx, m_heap};
reset();
root(cx, thing);
@@ -220,7 +191,7 @@
/* Prevent the thing from being garbage collected while it is in neither
* m_heap nor m_root */
- JS::Rooted<T> thing(cx, *m_root);
+ JS::RootedObject thing{cx, *m_root};
reset();
m_heap = thing;
@@ -235,7 +206,7 @@
{
debug("trace()");
g_assert(!m_root);
- JS::TraceEdge<T>(tracer, &m_heap, name);
+ JS::TraceEdge(tracer, &m_heap, name);
}
/* If not tracing, then you must call this method during GC in order to
@@ -244,10 +215,44 @@
bool update_after_gc(JSTracer* trc) {
debug("update_after_gc()");
g_assert(!m_root);
- return GjsHeapOperation<T>::update_after_gc(trc, &m_heap);
+ JS_UpdateWeakPointerAfterGC(trc, &m_heap);
+ return !m_heap;
+ }
+
+ // COMPAT: constexpr in C++23
+ [[nodiscard]] bool rooted() const { return m_root != nullptr; }
+};
+
+namespace Gjs {
+
+template <typename T>
+class WeakPtr : public JS::Heap<T> {
+ public:
+ using JS::Heap<T>::Heap;
+ using JS::Heap<T>::operator=;
+};
+
+} // namespace Gjs
+
+namespace JS {
+
+template <typename T>
+struct GCPolicy<Gjs::WeakPtr<T>> {
+ static void trace(JSTracer* trc, Gjs::WeakPtr<T>* thingp,
+ const char* name) {
+ return JS::TraceEdge(trc, thingp, name);
}
- [[nodiscard]] constexpr bool rooted() const { return m_root != nullptr; }
+ static bool traceWeak(JSTracer* trc, Gjs::WeakPtr<T>* thingp) {
+ return js::gc::TraceWeakEdge(trc, thingp);
+ }
+
+ static bool needsSweep(JSTracer* trc, const Gjs::WeakPtr<T>* thingp) {
+ Gjs::WeakPtr<T> thing{*thingp};
+ return !js::gc::TraceWeakEdge(trc, &thing);
+ }
};
+} // namespace JS
+
#endif // GJS_JSAPI_UTIL_ROOT_H_
diff -ruN cjs-6.2.0-orig/cjs/jsapi-util-string.cpp cjs-6.2.0/cjs/jsapi-util-string.cpp
--- cjs-6.2.0-orig/cjs/jsapi-util-string.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/jsapi-util-string.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -116,7 +116,7 @@
return false;
size_t length = JS::GetDeflatedUTF8StringLength(linear);
- char* bytes = js_pod_arena_malloc<char>(js::StringBufferArena, length + 1);
+ char* bytes = js_pod_malloc<char>(length + 1);
if (!bytes)
return false;
@@ -590,7 +590,7 @@
if (js::IsFunctionObject(obj)) {
JSFunction* fun = JS_GetObjectFunction(obj);
- JSString* display_name = JS_GetFunctionDisplayId(fun);
+ JSString* display_name = JS_GetMaybePartialFunctionDisplayId(fun);
if (display_name && JS_GetStringLength(display_name))
out << "<function " << gjs_debug_string(display_name);
else
@@ -622,6 +622,15 @@
return out.str();
}
+std::string gjs_debug_callable(JSObject* callable) {
+ if (JSFunction* fn = JS_GetObjectFunction(callable)) {
+ if (JSString* display_id = JS_GetMaybePartialFunctionDisplayId(fn))
+ return {"function " + gjs_debug_string(display_id)};
+ return {"unnamed function"};
+ }
+ return {"callable object " + gjs_debug_object(callable)};
+}
+
std::string
gjs_debug_value(JS::Value v)
{
diff -ruN cjs-6.2.0-orig/cjs/jsapi-util.cpp cjs-6.2.0/cjs/jsapi-util.cpp
--- cjs-6.2.0-orig/cjs/jsapi-util.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/jsapi-util.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -32,7 +32,6 @@
#include <js/Object.h> // for GetClass
#include <js/PropertyAndElement.h>
#include <js/RootingAPI.h>
-#include <js/Stack.h> // for BuildStackString
#include <js/String.h>
#include <js/TypeDecls.h>
#include <js/Value.h>
@@ -40,6 +39,7 @@
#include <jsapi.h> // for JS_InstanceOf
#include <jsfriendapi.h> // for ProtoKeyToClass
#include <jspubtd.h> // for JSProto_InternalError, JSProto_SyntaxError
+#include <mozilla/ScopeExit.h>
#include "cjs/atoms.h"
#include "cjs/context-private.h"
@@ -262,6 +262,44 @@
return JS::ToString(cx, exc);
}
+// Helper function: format the error's stack property.
+static std::string format_exception_stack(JSContext* cx, JS::HandleObject exc) {
+ JS::AutoSaveExceptionState saved_exc(cx);
+ auto restore =
+ mozilla::MakeScopeExit([&saved_exc]() { saved_exc.restore(); });
+
+ const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
+ std::ostringstream out;
+
+ // Check both the internal SavedFrame object and the stack property.
+ // GErrors will not have the former, and internal errors will not
+ // have the latter.
+ JS::RootedObject saved_frame{cx, JS::ExceptionStackOrNull(exc)};
+ if (saved_frame) {
+ JS::UniqueChars utf8_stack{format_saved_frame(cx, saved_frame)};
+ if (!utf8_stack)
+ return {};
+ out << '\n' << utf8_stack.get();
+ return out.str();
+ }
+
+ JS::RootedValue stack{cx};
+ if (!JS_GetPropertyById(cx, exc, atoms.stack(), &stack) || !stack.isString())
+ return {};
+
+ JS::RootedString str{cx, stack.toString()};
+ bool is_empty;
+ if (!JS_StringEqualsLiteral(cx, str, "", &is_empty) || is_empty)
+ return {};
+
+ JS::UniqueChars utf8_stack{JS_EncodeStringToUTF8(cx, str)};
+ if (!utf8_stack)
+ return {};
+
+ out << '\n' << utf8_stack.get();
+ return out.str();
+}
+
// Helper function: format the file name, line number, and column number where a
// SyntaxError occurred.
static std::string format_syntax_error_location(JSContext* cx,
@@ -311,25 +349,7 @@
std::ostringstream out;
const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
- JS::UniqueChars utf8_stack;
- // Check both the internal SavedFrame object and the stack property.
- // GErrors will not have the former, and internal errors will not
- // have the latter.
- JS::RootedObject saved_frame(cx, JS::ExceptionStackOrNull(exc_obj));
- JS::RootedString str(cx);
- if (saved_frame) {
- JS::BuildStackString(cx, nullptr, saved_frame, &str, 0);
- } else {
- JS::RootedValue stack(cx);
- if (JS_GetPropertyById(cx, exc_obj, atoms.stack(), &stack) &&
- stack.isString())
- str = stack.toString();
- }
- if (str)
- utf8_stack = JS_EncodeStringToUTF8(cx, str);
- if (utf8_stack)
- out << '\n' << utf8_stack.get();
- JS_ClearPendingException(cx);
+ out << format_exception_stack(cx, exc_obj);
JS::RootedValue v_cause(cx);
if (!JS_GetPropertyById(cx, exc_obj, atoms.cause(), &v_cause))
@@ -347,7 +367,7 @@
return out.str(); // out of memory, just stop here
}
- out << "Caused by: ";
+ out << "\nCaused by: ";
JS::RootedString exc_str(cx, exception_to_string(cx, v_cause));
if (exc_str) {
JS::UniqueChars utf8_exception = JS_EncodeStringToUTF8(cx, exc_str);
@@ -393,7 +413,8 @@
// file name, line number, and column number from the exception.
// We assume that syntax errors have no cause property, and are not the
// cause of other exceptions, so no recursion.
- out << format_syntax_error_location(cx, exc_obj);
+ out << format_syntax_error_location(cx, exc_obj)
+ << format_exception_stack(cx, exc_obj);
return out.str();
}
diff -ruN cjs-6.2.0-orig/cjs/jsapi-util.h cjs-6.2.0/cjs/jsapi-util.h
--- cjs-6.2.0-orig/cjs/jsapi-util.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/jsapi-util.h 2024-10-14 18:35:57.000000000 +0200
@@ -549,8 +549,8 @@
void gjs_gc_if_needed(JSContext *cx);
GJS_JSAPI_RETURN_CONVENTION
-GjsAutoChar gjs_format_stack_trace(JSContext *cx,
- JS::HandleObject saved_frame);
+JS::UniqueChars format_saved_frame(JSContext* cx, JS::HandleObject saved_frame,
+ size_t indent = 0);
/* Overloaded functions, must be outside G_DECLS. More types are intended to be
* added as the opportunity arises. */
@@ -600,6 +600,7 @@
[[nodiscard]] std::string gjs_debug_string(JSString* str);
[[nodiscard]] std::string gjs_debug_symbol(JS::Symbol* const sym);
[[nodiscard]] std::string gjs_debug_object(JSObject* obj);
+[[nodiscard]] std::string gjs_debug_callable(JSObject* callable);
[[nodiscard]] std::string gjs_debug_value(JS::Value v);
[[nodiscard]] std::string gjs_debug_id(jsid id);
diff -ruN cjs-6.2.0-orig/cjs/mem-private.h cjs-6.2.0/cjs/mem-private.h
--- cjs-6.2.0-orig/cjs/mem-private.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/mem-private.h 2024-09-16 22:32:54.000000000 +0200
@@ -6,6 +6,8 @@
#ifndef GJS_MEM_PRIVATE_H_
#define GJS_MEM_PRIVATE_H_
+#include <config.h>
+
#include <stddef.h> // for size_t
#include <atomic>
diff -ruN cjs-6.2.0-orig/cjs/mem.cpp cjs-6.2.0/cjs/mem.cpp
--- cjs-6.2.0-orig/cjs/mem.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/mem.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -2,6 +2,8 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2008 litl, LLC
+#include <config.h>
+
#include <stdint.h>
#include <glib.h>
diff -ruN cjs-6.2.0-orig/cjs/mem.h cjs-6.2.0/cjs/mem.h
--- cjs-6.2.0-orig/cjs/mem.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/mem.h 2024-10-14 18:35:57.000000000 +0200
@@ -8,7 +8,7 @@
#define GJS_MEM_H_
#if !defined(INSIDE_GJS_H) && !defined(GJS_COMPILATION)
-# error "Only <cjs/gjs.h> can be included directly."
+# error "Only <gjs/gjs.h> can be included directly."
#endif
#include <stdbool.h> /* IWYU pragma: keep */
diff -ruN cjs-6.2.0-orig/cjs/module.cpp cjs-6.2.0/cjs/module.cpp
--- cjs-6.2.0-orig/cjs/module.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/module.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -7,7 +7,10 @@
#include <stddef.h> // for size_t
#include <string.h>
+#include <vector> // for vector
+
#include <gio/gio.h>
+#include <glib-object.h>
#include <glib.h>
#include <js/CallAndConstruct.h>
@@ -438,7 +441,8 @@
}
JS::RootedObject native_obj(cx);
- if (!Gjs::NativeModuleRegistry::get().load(cx, id.get(), &native_obj)) {
+ if (!Gjs::NativeModuleDefineFuncs::get().define(cx, id.get(),
+ &native_obj)) {
gjs_throw(cx, "Failed to load native module: %s", id.get());
return false;
}
@@ -471,9 +475,9 @@
&private_ref.toObject());
const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
- JS::RootedValue v_uri(cx);
- if (!JS_GetPropertyById(cx, module, atoms.uri(), &v_uri) ||
- !JS_DefinePropertyById(cx, meta, atoms.url(), v_uri,
+ JS::RootedValue specifier{cx};
+ if (!JS_GetProperty(cx, module, "id", &specifier) ||
+ !JS_DefinePropertyById(cx, meta, atoms.url(), specifier,
GJS_MODULE_PROP_FLAGS))
return false;
@@ -492,6 +496,47 @@
return true;
}
+// Canonicalize specifier so that differently-spelled specifiers referring to
+// the same module don't result in duplicate entries in the registry
+static bool canonicalize_specifier(JSContext* cx,
+ JS::MutableHandleString specifier) {
+ JS::UniqueChars specifier_utf8 = JS_EncodeStringToUTF8(cx, specifier);
+ if (!specifier_utf8)
+ return false;
+
+ GjsAutoChar scheme, host, path, query;
+ if (!g_uri_split(specifier_utf8.get(), G_URI_FLAGS_NONE, scheme.out(),
+ nullptr, host.out(), nullptr, path.out(), query.out(),
+ nullptr, nullptr))
+ return false;
+
+ if (g_strcmp0(scheme, "gi")) {
+ // canonicalize without the query portion to avoid it being encoded
+ GjsAutoChar for_file_uri =
+ g_uri_join(G_URI_FLAGS_NONE, scheme.get(), nullptr, host.get(), -1,
+ path.get(), nullptr, nullptr);
+ GjsAutoUnref<GFile> file = g_file_new_for_uri(for_file_uri.get());
+ for_file_uri = g_file_get_uri(file);
+ host.reset();
+ path.reset();
+ if (!g_uri_split(for_file_uri.get(), G_URI_FLAGS_NONE, nullptr, nullptr,
+ host.out(), nullptr, path.out(), nullptr, nullptr,
+ nullptr))
+ return false;
+ }
+
+ GjsAutoChar canonical_specifier =
+ g_uri_join(G_URI_FLAGS_NONE, scheme.get(), nullptr, host.get(), -1,
+ path.get(), query.get(), nullptr);
+ JS::ConstUTF8CharsZ chars{canonical_specifier, strlen(canonical_specifier)};
+ JS::RootedString new_specifier{cx, JS_NewStringCopyUTF8Z(cx, chars)};
+ if (!new_specifier)
+ return false;
+
+ specifier.set(new_specifier);
+ return true;
+}
+
/**
* gjs_module_resolve:
*
@@ -518,6 +563,9 @@
g_assert(v_loader.isObject());
JS::RootedObject loader(cx, &v_loader.toObject());
+ if (!canonicalize_specifier(cx, &specifier))
+ return nullptr;
+
JS::RootedValueArray<2> args(cx);
args[0].set(importingModulePriv);
args[1].setString(specifier);
@@ -535,23 +583,6 @@
return &result.toObject();
}
-// Note: exception is never pending after this function finishes, even if it
-// returns null. The return value is intended to be passed to
-// JS::FinishDynamicModuleImport().
-static JSObject* reject_new_promise_with_pending_exception(JSContext* cx) {
- JS::ExceptionStack stack{cx};
- if (!JS::StealPendingExceptionStack(cx, &stack)) {
- gjs_log_exception(cx);
- return nullptr;
- }
- JS::RootedObject rejected{cx, JS::NewPromiseObject(cx, nullptr)};
- if (!rejected || !JS::RejectPromise(cx, rejected, stack.exception())) {
- gjs_log_exception(cx);
- return nullptr;
- }
- return rejected;
-}
-
// Call JS::FinishDynamicModuleImport() with the values stashed in the function.
// Can fail in JS::FinishDynamicModuleImport(), but will assert if anything
// fails in fetching the stashed values, since that would be a serious GJS bug.
@@ -593,12 +624,9 @@
// case we must not call JS::FinishDynamicModuleImport().
GJS_JSAPI_RETURN_CONVENTION
static bool fail_import(JSContext* cx, const JS::CallArgs& args) {
- if (!JS_IsExceptionPending(cx))
- return false;
-
- JS::RootedObject rejected_promise{
- cx, reject_new_promise_with_pending_exception(cx)};
- return finish_import(cx, rejected_promise, args);
+ if (JS_IsExceptionPending(cx))
+ return finish_import(cx, nullptr, args);
+ return false;
}
GJS_JSAPI_RETURN_CONVENTION
@@ -607,16 +635,12 @@
gjs_debug(GJS_DEBUG_IMPORTER, "Async import promise rejected");
- // Reject a new promise with the rejection value of the async import
- // promise, so that FinishDynamicModuleImport will reject the
- // internal_promise with it.
- JS::RootedObject rejected{cx, JS::NewPromiseObject(cx, nullptr)};
- if (!rejected || !JS::RejectPromise(cx, rejected, args.get(0))) {
- gjs_log_exception(cx);
- return finish_import(cx, nullptr, args);
- }
+ // Throw the value that the promise is rejected with, so that
+ // FinishDynamicModuleImport will reject the internal_promise with it.
+ JS_SetPendingException(cx, args.get(0),
+ JS::ExceptionStackBehavior::DoNotCapture);
- return finish_import(cx, rejected, args);
+ return finish_import(cx, nullptr, args);
}
GJS_JSAPI_RETURN_CONVENTION
@@ -660,6 +684,9 @@
JS::RootedString specifier(
cx, JS::GetModuleRequestSpecifier(cx, module_request));
+ if (!canonicalize_specifier(cx, &specifier))
+ return false;
+
JS::RootedObject callback_data(cx, JS_NewPlainObject(cx));
if (!callback_data ||
!JS_DefineProperty(cx, callback_data, "module_request", module_request,
@@ -688,16 +715,9 @@
args[1].setString(specifier);
JS::RootedValue result(cx);
- if (!JS::Call(cx, loader, "moduleResolveAsyncHook", args, &result)) {
- if (!JS_IsExceptionPending(cx))
- return false;
-
- JS::RootedObject rejected_promise{
- cx, reject_new_promise_with_pending_exception(cx)};
- return JS::FinishDynamicModuleImport(cx, rejected_promise,
- importing_module_priv,
+ if (!JS::Call(cx, loader, "moduleResolveAsyncHook", args, &result))
+ return JS::FinishDynamicModuleImport(cx, nullptr, importing_module_priv,
module_request, internal_promise);
- }
// Release in finish_import
GjsContextPrivate* priv = GjsContextPrivate::from_cx(cx);
diff -ruN cjs-6.2.0-orig/cjs/native.cpp cjs-6.2.0/cjs/native.cpp
--- cjs-6.2.0-orig/cjs/native.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/native.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -18,8 +18,8 @@
#include "cjs/native.h"
#include "util/log.h"
-void Gjs::NativeModuleRegistry::add(const char* module_id,
- GjsDefineModuleFunc func) {
+void Gjs::NativeModuleDefineFuncs::add(const char* module_id,
+ GjsDefineModuleFunc func) {
bool inserted;
std::tie(std::ignore, inserted) = m_modules.insert({module_id, func});
if (!inserted) {
@@ -41,32 +41,30 @@
* been registered. This is used to check to see if a name is a
* builtin module without starting to try and load it.
*/
-bool Gjs::NativeModuleRegistry::is_registered(const char* name) const {
+bool Gjs::NativeModuleDefineFuncs::is_registered(const char* name) const {
return m_modules.count(name) > 0;
}
/**
- * gjs_load:
+ * define:
* @context: the #JSContext
- * @parse_name: Name under which the module was registered with
- * add(), should be in the format as returned by
- * g_file_get_parse_name()
+ * @id: Name under which the module was registered with add()
* @module_out: Return location for a #JSObject
*
- * Loads a builtin native-code module called @name into @module_out.
+ * Loads a builtin native-code module called @name into @module_out by calling
+ * the function to define it.
*
* Returns: true on success, false if an exception was thrown.
*/
-bool Gjs::NativeModuleRegistry::load(JSContext* context, const char* parse_name,
- JS::MutableHandleObject module_out) {
- gjs_debug(GJS_DEBUG_NATIVE, "Defining native module '%s'", parse_name);
+bool Gjs::NativeModuleDefineFuncs::define(
+ JSContext* context, const char* id,
+ JS::MutableHandleObject module_out) const {
+ gjs_debug(GJS_DEBUG_NATIVE, "Defining native module '%s'", id);
- const auto& iter = m_modules.find(parse_name);
+ const auto& iter = m_modules.find(id);
if (iter == m_modules.end()) {
- gjs_throw(context,
- "No native module '%s' has registered itself",
- parse_name);
+ gjs_throw(context, "No native module '%s' has registered itself", id);
return false;
}
diff -ruN cjs-6.2.0-orig/cjs/native.h cjs-6.2.0/cjs/native.h
--- cjs-6.2.0-orig/cjs/native.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/native.h 2024-10-14 18:35:57.000000000 +0200
@@ -9,35 +9,34 @@
#include <string>
#include <unordered_map>
-#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include "cjs/macros.h"
namespace Gjs {
-class NativeModuleRegistry {
- NativeModuleRegistry() {}
+class NativeModuleDefineFuncs {
+ NativeModuleDefineFuncs() {}
typedef bool (*GjsDefineModuleFunc)(JSContext* context,
JS::MutableHandleObject module_out);
std::unordered_map<std::string, GjsDefineModuleFunc> m_modules;
public:
- static NativeModuleRegistry& get() {
- static NativeModuleRegistry the_singleton;
+ static NativeModuleDefineFuncs& get() {
+ static NativeModuleDefineFuncs the_singleton;
return the_singleton;
}
/* called on context init */
void add(const char* module_id, GjsDefineModuleFunc func);
- /* called by importer.c to to check for already loaded modules */
+ // called by importer.cpp to to check for already loaded modules
[[nodiscard]] bool is_registered(const char* name) const;
- /* called by importer.cpp to load a statically linked native module */
+ // called by importer.cpp to load a built-in native module
GJS_JSAPI_RETURN_CONVENTION
- bool load(JSContext* cx, const char* name,
- JS::MutableHandleObject module_out);
+ bool define(JSContext* cx, const char* name,
+ JS::MutableHandleObject module_out) const;
};
}; // namespace Gjs
diff -ruN cjs-6.2.0-orig/cjs/profiler-private.h cjs-6.2.0/cjs/profiler-private.h
--- cjs-6.2.0-orig/cjs/profiler-private.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/profiler-private.h 2024-10-14 18:35:57.000000000 +0200
@@ -5,8 +5,11 @@
#ifndef GJS_PROFILER_PRIVATE_H_
#define GJS_PROFILER_PRIVATE_H_
+#include <config.h>
+
#include <stdint.h>
+#include <js/GCAPI.h> // for JSFinalizeStatus, JSGCStatus, GCReason
#include <js/ProfilingCategory.h>
#include <js/ProfilingStack.h>
#include <js/RootingAPI.h>
@@ -55,4 +58,7 @@
void _gjs_profiler_setup_signals(GjsProfiler *self, GjsContext *context);
+void _gjs_profiler_set_finalize_status(GjsProfiler*, JSFinalizeStatus);
+void _gjs_profiler_set_gc_status(GjsProfiler*, JSGCStatus, JS::GCReason);
+
#endif // GJS_PROFILER_PRIVATE_H_
diff -ruN cjs-6.2.0-orig/cjs/profiler.cpp cjs-6.2.0/cjs/profiler.cpp
--- cjs-6.2.0-orig/cjs/profiler.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/profiler.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -7,33 +7,40 @@
# include <signal.h> // for siginfo_t, sigevent, sigaction, SIGPROF, ...
#endif
-#include <glib-object.h>
-#include <glib.h>
-
#ifdef ENABLE_PROFILER
-# include <alloca.h>
+// IWYU has a weird loop where if this is present, it asks for it to be removed,
+// and if absent, asks for it to be added
+# include <alloca.h> // IWYU pragma: keep
# include <errno.h>
# include <stdint.h>
-# include <stdio.h> // for sscanf
-# include <string.h> // for memcpy, strlen
+# include <stdio.h> // for sscanf
+# include <string.h> // for memcpy, strlen
# include <sys/syscall.h> // for __NR_gettid
+# include <sys/types.h> // for timer_t
# include <time.h> // for size_t, CLOCK_MONOTONIC, itimerspec, ...
# ifdef HAVE_UNISTD_H
# include <unistd.h> // for getpid, syscall
# endif
# include <array>
+#endif
+
+#include <glib-object.h>
+#include <glib.h>
+
+#ifdef ENABLE_PROFILER
# ifdef G_OS_UNIX
# include <glib-unix.h>
# endif
# include <sysprof-capture.h>
#endif
+#include <js/GCAPI.h> // for JSFinalizeStatus, JSGCStatus, GCReason
#include <js/ProfilingStack.h> // for EnableContextProfilingStack, ...
#include <js/TypeDecls.h>
#include <mozilla/Atomics.h> // for ProfilingStack operators
#include "cjs/context.h"
-#include "cjs/jsapi-util.h"
+#include "cjs/jsapi-util.h" // for gjs_explain_gc_reason
#include "cjs/mem-private.h"
#include "cjs/profiler-private.h"
#include "cjs/profiler.h"
@@ -49,7 +56,7 @@
* However, we do use a Linux'ism that allows us to deliver the signal
* to only a single thread. Doing this in a generic fashion would
* require thread-registration so that we can mask SIGPROF from all
- * threads execpt the JS thread. The gecko engine uses tgkill() to do
+ * threads except the JS thread. The gecko engine uses tgkill() to do
* this with a secondary thread instead of using POSIX timers. We could
* do this too, but it would still be Linux-only.
*
@@ -89,6 +96,10 @@
GSource* periodic_flush;
SysprofCaptureWriter* target_capture;
+
+ // Cache previous values of counters so that we don't overrun the output
+ // with counters that don't change very often
+ uint64_t last_counter_values[GJS_N_COUNTERS];
#endif /* ENABLE_PROFILER */
/* The filename to write to */
@@ -104,6 +115,12 @@
/* Cached copy of our pid */
GPid pid;
+ /* Timing information */
+ int64_t gc_begin_time;
+ int64_t sweep_begin_time;
+ int64_t group_sweep_begin_time;
+ const char* gc_reason; // statically allocated
+
/* GLib signal handler ID for SIGUSR2 */
unsigned sigusr2_id;
unsigned counter_base; // index of first GObject memory counter
@@ -435,15 +452,24 @@
unsigned ids[GJS_N_COUNTERS];
SysprofCaptureCounterValue values[GJS_N_COUNTERS];
+ size_t new_counts = 0;
-# define FETCH_COUNTERS(name, ix) \
- ids[ix] = self->counter_base + ix; \
- values[ix].v64 = GJS_GET_COUNTER(name);
+# define FETCH_COUNTERS(name, ix) \
+ { \
+ uint64_t count = GJS_GET_COUNTER(name); \
+ if (count != self->last_counter_values[ix]) { \
+ ids[new_counts] = self->counter_base + ix; \
+ values[new_counts].v64 = count; \
+ new_counts++; \
+ } \
+ self->last_counter_values[ix] = count; \
+ }
GJS_FOR_EACH_COUNTER(FETCH_COUNTERS);
# undef FETCH_COUNTERS
- if (!sysprof_capture_writer_set_counters(self->capture, now, -1, self->pid,
- ids, values, GJS_N_COUNTERS))
+ if (new_counts > 0 &&
+ !sysprof_capture_writer_set_counters(self->capture, now, -1, self->pid,
+ ids, values, new_counts))
gjs_profiler_stop(self);
}
@@ -848,3 +874,87 @@
(void)fd; // Unused in the no-profiler case
#endif
}
+
+void _gjs_profiler_set_finalize_status(GjsProfiler* self,
+ JSFinalizeStatus status) {
+#ifdef ENABLE_PROFILER
+ // Implementation note for mozjs-128:
+ //
+ // Sweeping happens in three phases:
+ // 1st phase (JSFINALIZE_GROUP_PREPARE): the collector prepares to sweep a
+ // group of zones. 2nd phase (JSFINALIZE_GROUP_START): weak references to
+ // unmarked things have been removed, but no GC thing has been swept. 3rd
+ // Phase (JSFINALIZE_GROUP_END): all dead GC things for a group of zones
+ // have been swept. The above repeats for each sweep group.
+ // JSFINALIZE_COLLECTION_END occurs at the end of all GC. (see jsgc.cpp,
+ // BeginSweepPhase/BeginSweepingZoneGroup and SweepPhase, all called from
+ // IncrementalCollectSlice).
+ //
+ // Incremental GC muddies the waters, because BeginSweepPhase is always run
+ // to entirety, but SweepPhase can be run incrementally and mixed with JS
+ // code runs or even native code, when MaybeGC/IncrementalGC return.
+ // After GROUP_START, the collector may yield to the mutator meaning JS code
+ // can run between the callback for GROUP_START and GROUP_END.
+
+ int64_t now = g_get_monotonic_time() * 1000L;
+
+ switch (status) {
+ case JSFINALIZE_GROUP_PREPARE:
+ self->sweep_begin_time = now;
+ break;
+ case JSFINALIZE_GROUP_START:
+ self->group_sweep_begin_time = now;
+ break;
+ case JSFINALIZE_GROUP_END:
+ if (self->group_sweep_begin_time != 0) {
+ _gjs_profiler_add_mark(self, self->group_sweep_begin_time,
+ now - self->group_sweep_begin_time,
+ "GJS", "Group sweep", nullptr);
+ }
+ self->group_sweep_begin_time = 0;
+ break;
+ case JSFINALIZE_COLLECTION_END:
+ if (self->sweep_begin_time != 0) {
+ _gjs_profiler_add_mark(self, self->sweep_begin_time,
+ now - self->sweep_begin_time, "GJS",
+ "Sweep", nullptr);
+ }
+ self->sweep_begin_time = 0;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+#else
+ (void)self;
+ (void)status;
+#endif
+}
+
+void _gjs_profiler_set_gc_status(GjsProfiler* self, JSGCStatus status,
+ JS::GCReason reason) {
+#ifdef ENABLE_PROFILER
+ int64_t now = g_get_monotonic_time() * 1000L;
+
+ switch (status) {
+ case JSGC_BEGIN:
+ self->gc_begin_time = now;
+ self->gc_reason = gjs_explain_gc_reason(reason);
+ break;
+ case JSGC_END:
+ if (self->gc_begin_time != 0) {
+ _gjs_profiler_add_mark(self, self->gc_begin_time,
+ now - self->gc_begin_time, "GJS",
+ "Garbage collection", self->gc_reason);
+ }
+ self->gc_begin_time = 0;
+ self->gc_reason = nullptr;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+#else
+ (void)self;
+ (void)status;
+ (void)reason;
+#endif
+}
diff -ruN cjs-6.2.0-orig/cjs/profiler.h cjs-6.2.0/cjs/profiler.h
--- cjs-6.2.0-orig/cjs/profiler.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/profiler.h 2024-10-14 18:38:17.000000000 +0200
@@ -7,7 +7,7 @@
#define GJS_PROFILER_H_
#if !defined(INSIDE_GJS_H) && !defined(GJS_COMPILATION)
-# error "Only <cjs/gjs.h> can be included directly."
+# error "Only <gjs/gjs.h> can be included directly."
#endif
#include <glib-object.h>
diff -ruN cjs-6.2.0-orig/cjs/promise.cpp cjs-6.2.0/cjs/promise.cpp
--- cjs-6.2.0-orig/cjs/promise.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/promise.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -16,6 +16,7 @@
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <jsapi.h> // for JS_NewPlainObject
+#include <jsfriendapi.h> // for RunJobs
#include "cjs/context-private.h"
#include "cjs/jsapi-util-args.h"
@@ -81,7 +82,7 @@
g_source_set_ready_time(this, -1);
// Drain the job queue.
- m_gjs->runJobs(m_gjs->context());
+ js::RunJobs(m_gjs->context());
return G_SOURCE_CONTINUE;
}
@@ -193,8 +194,7 @@
bool drain_microtask_queue(JSContext* cx, unsigned argc, JS::Value* vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- auto* gjs = GjsContextPrivate::from_cx(cx);
- gjs->runJobs(cx);
+ js::RunJobs(cx);
args.rval().setUndefined();
return true;
diff -ruN cjs-6.2.0-orig/cjs/stack.cpp cjs-6.2.0/cjs/stack.cpp
--- cjs-6.2.0-orig/cjs/stack.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/stack.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -4,7 +4,7 @@
#include <config.h>
-#include <stdio.h> // for stderr, open_memstream
+#include <stdio.h> // for stderr
#include <sstream>
#include <string>
@@ -12,7 +12,9 @@
#include <glib-object.h>
#include <glib.h>
+#include <js/Printer.h>
#include <js/TypeDecls.h>
+#include <js/Utility.h> // for UniqueChars
#include <js/friend/DumpFunctions.h>
#include "cjs/context-private.h"
@@ -40,48 +42,31 @@
}
}
-#ifdef HAVE_OPEN_MEMSTREAM
-static std::string
-stack_trace_string(GjsContext *context) {
- JSContext *cx = static_cast<JSContext *>(gjs_context_get_native_context(context));
- std::ostringstream out;
- FILE *stream;
- GjsAutoChar buf;
- size_t len;
-
- stream = open_memstream(buf.out(), &len);
- if (!stream) {
- out << "No stack trace for context " << context << ": "
- "open_memstream() failed\n\n";
- return out.str();
- }
- js::DumpBacktrace(cx, stream);
- fclose(stream);
- out << "== Stack trace for context " << context << " ==\n"
- << buf.get() << "\n";
- return out.str();
-}
-#endif
-
std::string
gjs_dumpstack_string() {
std::string out;
std::ostringstream all_traces;
-#ifdef HAVE_OPEN_MEMSTREAM
GjsSmartPointer<GList> contexts = gjs_context_get_all();
+ js::Sprinter printer;
GList *iter;
for (iter = contexts; iter; iter = iter->next) {
GjsAutoUnref<GjsContext> context(GJS_CONTEXT(iter->data));
- all_traces << stack_trace_string(context);
+ if (!printer.init()) {
+ all_traces << "No stack trace for context " << context.get()
+ << ": out of memory\n\n";
+ break;
+ }
+ auto* cx =
+ static_cast<JSContext*>(gjs_context_get_native_context(context));
+ js::DumpBacktrace(cx, printer);
+ JS::UniqueChars trace = printer.release();
+ all_traces << "== Stack trace for context " << context.get() << " ==\n"
+ << trace.get() << "\n";
}
out = all_traces.str();
out.resize(MAX(out.size() - 2, 0));
-#else
- out = "No stack trace: no open_memstream() support. "
- "See https://bugzilla.mozilla.org/show_bug.cgi?id=1826290";
-#endif
return out;
}
diff -ruN cjs-6.2.0-orig/cjs/text-encoding.cpp cjs-6.2.0/cjs/text-encoding.cpp
--- cjs-6.2.0-orig/cjs/text-encoding.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/cjs/text-encoding.cpp 2024-10-14 18:35:57.000000000 +0200
@@ -16,6 +16,7 @@
#include <memory> // for unique_ptr
#include <string> // for u16string
#include <tuple> // for tuple
+#include <utility> // for move
#include <gio/gio.h>
#include <glib-object.h>
@@ -40,7 +41,7 @@
#include <jspubtd.h> // for JSProto_InternalError
#include <mozilla/Maybe.h>
#include <mozilla/Span.h>
-#include <mozilla/Unused.h>
+#include <mozilla/UniquePtr.h>
#include "cjs/jsapi-util-args.h"
#include "cjs/jsapi-util.h"
@@ -93,7 +94,7 @@
// this is typical for ASCII and non-supplementary characters.
// Because we are converting from an unknown encoding
// technically a single byte could be supplementary in
- // Unicode (4 bytes) or even represen multiple Unicode characters.
+ // Unicode (4 bytes) or even represent multiple Unicode characters.
//
// std::u16string does not care about these implementation
// details, its only concern is that is consists of byte pairs.
@@ -165,7 +166,7 @@
// Append the unicode fallback character to the output
output_str.append(u"\ufffd", 1);
}
- } else if (g_error_matches(error, G_IO_ERROR,
+ } else if (g_error_matches(local_error, G_IO_ERROR,
G_IO_ERROR_NO_SPACE)) {
// If the buffer was full increase the buffer
// size and re-try the conversion.
@@ -180,15 +181,12 @@
} else {
buffer_size += bytes_len;
}
+ } else {
+ // Stop decoding if an unknown error occurs.
+ return gjs_throw_type_error_from_gerror(cx, local_error);
}
}
-
- // Stop decoding if an unknown error occurs.
- } while (input_len > 0 && !error);
-
- // An unexpected error occurred.
- if (error)
- return gjs_throw_type_error_from_gerror(cx, error);
+ } while (input_len > 0);
// Copy the accumulator's data into a JSString of Unicode (UTF-16) chars.
return JS_NewUCStringCopyN(cx, output_str.c_str(), output_str.size());
@@ -401,13 +399,8 @@
utf8_len = strlen(utf8.get());
}
- array_buffer = JS::NewArrayBufferWithContents(cx, utf8_len, utf8.get());
-
- // array_buffer only assumes ownership if the call succeeded,
- // if array_buffer assumes ownership we must release our ownership
- // without freeing the data.
- if (array_buffer)
- mozilla::Unused << utf8.release();
+ array_buffer =
+ JS::NewArrayBufferWithContents(cx, utf8_len, std::move(utf8));
} else {
GjsAutoError error;
GjsAutoChar encoded = nullptr;
@@ -456,9 +449,10 @@
if (bytes_written == 0)
return JS_NewUint8Array(cx, 0);
+ mozilla::UniquePtr<void, JS::BufferContentsDeleter> contents{
+ encoded.release(), gfree_arraybuffer_contents};
array_buffer =
- JS::NewExternalArrayBuffer(cx, bytes_written, encoded.release(),
- gfree_arraybuffer_contents, nullptr);
+ JS::NewExternalArrayBuffer(cx, bytes_written, std::move(contents));
}
if (!array_buffer)
diff -ruN cjs-6.2.0-orig/gi/arg-cache.cpp cjs-6.2.0/gi/arg-cache.cpp
--- cjs-6.2.0-orig/gi/arg-cache.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/arg-cache.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -63,9 +63,9 @@
static_assert(G_N_ELEMENTS(expected_type_names) == ExpectedType::LAST,
"Names must match the values in ExpectedType");
-static constexpr void gjs_g_argument_set_array_length(GITypeTag tag,
- GIArgument* arg,
- size_t value) {
+static constexpr void gjs_gi_argument_set_array_length(GITypeTag tag,
+ GIArgument* arg,
+ size_t value) {
switch (tag) {
case GI_TYPE_TAG_INT8:
gjs_arg_set<int8_t>(arg, value);
@@ -145,18 +145,13 @@
GITypeTag m_tag : 5;
};
-struct String {
- constexpr String() : m_filename(false) {}
- bool m_filename : 1;
-};
-
-struct TypeInfo {
+struct HasTypeInfo {
constexpr GITypeInfo* type_info() const {
// Should be const GITypeInfo*, but G-I APIs won't accept that
return const_cast<GITypeInfo*>(&m_type_info);
}
- GITypeInfo m_type_info;
+ GITypeInfo m_type_info{};
};
struct Transferable {
@@ -187,8 +182,21 @@
constexpr bool set_out_parameter(GjsFunctionCallState* state,
GIArgument* arg) {
- gjs_arg_unset<void*>(&state->out_cvalue(m_arg_pos));
- gjs_arg_set(arg, &gjs_arg_member<void*>(&state->out_cvalue(m_arg_pos)));
+ // Clear all bits of the out C value. No one member is guaranteed to
+ // span the whole union on all architectures, so use memset() instead of
+ // gjs_arg_unset<T>().
+ memset(&state->out_cvalue(m_arg_pos), 0, sizeof(GIArgument));
+ // The value passed to the function is actually the address of the out
+ // C value
+ gjs_arg_set(arg, &state->out_cvalue(m_arg_pos));
+ return true;
+ }
+
+ constexpr bool set_inout_parameter(GjsFunctionCallState* state,
+ GIArgument* arg) {
+ state->out_cvalue(m_arg_pos) = state->inout_original_cvalue(m_arg_pos) =
+ *arg;
+ gjs_arg_set(arg, &state->out_cvalue(m_arg_pos));
return true;
}
@@ -197,20 +205,24 @@
struct Array : BasicType {
uint8_t m_length_pos = 0;
+ GIDirection m_length_direction : 2;
- void set_array_length(int pos, GITypeTag tag) {
+ Array() : BasicType(), m_length_direction(GI_DIRECTION_IN) {}
+
+ void set_array_length(int pos, GITypeTag tag, GIDirection direction) {
g_assert(pos >= 0 && pos <= Argument::MAX_ARGS &&
"No more than 253 arguments allowed");
m_length_pos = pos;
+ m_length_direction = direction;
m_tag = tag;
}
};
-struct BaseInfo {
- constexpr explicit BaseInfo(GIBaseInfo* info,
- const GjsAutoTakeOwnership& add_ref)
+struct HasIntrospectionInfo {
+ constexpr explicit HasIntrospectionInfo(GIBaseInfo* info,
+ const GjsAutoTakeOwnership& add_ref)
: m_info(info, add_ref) {}
- constexpr explicit BaseInfo(GIBaseInfo* info) : m_info(info) {}
+ constexpr explicit HasIntrospectionInfo(GIBaseInfo* info) : m_info(info) {}
GjsAutoBaseInfo m_info;
};
@@ -227,7 +239,7 @@
struct RegisteredType : GTypedType {
RegisteredType(GType gtype, GIInfoType info_type)
: GTypedType(gtype), m_info_type(info_type) {}
- explicit RegisteredType(GIBaseInfo* info)
+ explicit RegisteredType(GIRegisteredTypeInfo* info)
: GTypedType(g_registered_type_info_get_g_type(info)),
m_info_type(g_base_info_get_type(info)) {
g_assert(m_gtype != G_TYPE_NONE &&
@@ -237,15 +249,15 @@
GIInfoType m_info_type : 5;
};
-struct RegisteredInterface : BaseInfo, GTypedType {
- explicit RegisteredInterface(GIBaseInfo* info)
- : BaseInfo(info, GjsAutoTakeOwnership{}),
+struct RegisteredInterface : HasIntrospectionInfo, GTypedType {
+ explicit RegisteredInterface(GIRegisteredTypeInfo* info)
+ : HasIntrospectionInfo(info, GjsAutoTakeOwnership{}),
GTypedType(g_registered_type_info_get_g_type(m_info)) {}
};
-struct Callback : Nullable, BaseInfo {
- explicit Callback(GIInterfaceInfo* info)
- : BaseInfo(info, GjsAutoTakeOwnership{}),
+struct Callback : Nullable, HasIntrospectionInfo {
+ explicit Callback(GICallbackInfo* info)
+ : HasIntrospectionInfo(info, GjsAutoTakeOwnership{}),
m_scope(GI_SCOPE_TYPE_INVALID) {}
inline void set_callback_destroy_pos(int pos) {
@@ -319,7 +331,7 @@
constexpr bool skip() { return true; }
};
-struct Generic : SkipAll, Transferable, TypeInfo {};
+struct Generic : SkipAll, Transferable, HasTypeInfo {};
struct GenericIn : Generic {
bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
@@ -347,7 +359,10 @@
bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
GIArgument*) override;
- const ReturnValue* as_return_value() const override { return this; }
+ GITypeTag return_tag() const override {
+ return g_type_info_get_tag(&const_cast<GenericOut*>(this)->m_type_info);
+ }
+ const GITypeInfo* return_type() const override { return &m_type_info; }
};
struct GenericReturn : ReturnValue {
@@ -358,6 +373,39 @@
}
};
+template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
+struct NumericOut : SkipAll, Positioned {
+ static_assert(std::is_arithmetic_v<T>, "Not arithmetic type");
+ bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
+ JS::HandleValue) override {
+ return set_out_parameter(state, arg);
+ }
+ bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
+ JS::MutableHandleValue value) override {
+ return Gjs::c_value_to_js_checked<T, TAG>(cx, gjs_arg_get<T>(arg),
+ value);
+ }
+};
+
+using BooleanOut = NumericOut<gboolean, GI_TYPE_TAG_BOOLEAN>;
+
+template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
+struct NumericReturn : SkipAll {
+ static_assert(std::is_arithmetic_v<T>, "Not arithmetic type");
+ bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
+ JS::HandleValue) override {
+ return invalid(cx, G_STRFUNC);
+ }
+ bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
+ JS::MutableHandleValue value) override {
+ return Gjs::c_value_to_js_checked<T, TAG>(cx, gjs_arg_get<T>(arg),
+ value);
+ }
+ GITypeTag return_tag() const override { return TAG; }
+};
+
+using BooleanReturn = NumericReturn<gboolean, GI_TYPE_TAG_BOOLEAN>;
+
struct SimpleOut : SkipAll, Positioned {
bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
JS::HandleValue) override {
@@ -403,6 +451,13 @@
struct ReturnArray : ExplicitArrayOut {
bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
JS::HandleValue value) override {
+ if (m_length_direction != GI_DIRECTION_OUT) {
+ gjs_throw(cx,
+ "Using different length argument direction for array %s"
+ "is not supported for out arrays",
+ m_arg_name);
+ return false;
+ }
return GenericOut::in(cx, state, arg, value);
};
};
@@ -506,14 +561,14 @@
GIArgument*) override;
};
-struct FallbackInterfaceIn : RegisteredInterfaceIn, TypeInfo {
+struct FallbackInterfaceIn : RegisteredInterfaceIn, HasTypeInfo {
using RegisteredInterfaceIn::RegisteredInterfaceIn;
bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
JS::HandleValue value) override {
- return gjs_value_to_g_argument(cx, value, &m_type_info, m_arg_name,
- GJS_ARGUMENT_ARGUMENT, m_transfer,
- flags(), arg);
+ return gjs_value_to_gi_argument(cx, value, &m_type_info, m_arg_name,
+ GJS_ARGUMENT_ARGUMENT, m_transfer,
+ flags(), arg);
}
};
@@ -536,11 +591,11 @@
}
};
-struct UnregisteredBoxedIn : BoxedIn, BaseInfo {
- explicit UnregisteredBoxedIn(GIInterfaceInfo* info)
+struct UnregisteredBoxedIn : BoxedIn, HasIntrospectionInfo {
+ explicit UnregisteredBoxedIn(GIStructInfo* info)
: BoxedIn(g_registered_type_info_get_g_type(info),
g_base_info_get_type(info)),
- BaseInfo(info, GjsAutoTakeOwnership{}) {}
+ HasIntrospectionInfo(info, GjsAutoTakeOwnership{}) {}
// This is a smart argument, no release needed
GIBaseInfo* info() const override { return m_info; }
};
@@ -625,12 +680,32 @@
JS::HandleValue) override;
};
-struct NumericIn : SkipAll, BasicType {
- explicit NumericIn(GITypeTag tag) : BasicType(tag) {}
+template <typename T>
+struct NumericIn : SkipAll {
+ static_assert(std::is_arithmetic_v<T>, "Not arithmetic type");
bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
JS::HandleValue) override;
};
+template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
+struct NumericInOut : NumericIn<T>, Positioned {
+ static_assert(std::is_arithmetic_v<T>, "Not arithmetic type");
+ bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
+ JS::HandleValue value) override {
+ if (!NumericIn<T>::in(cx, state, arg, value))
+ return false;
+
+ return set_inout_parameter(state, arg);
+ }
+ bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
+ JS::MutableHandleValue value) override {
+ return Gjs::c_value_to_js_checked<T, TAG>(cx, gjs_arg_get<T>(arg),
+ value);
+ }
+};
+
+using BooleanInOut = NumericInOut<gboolean, GI_TYPE_TAG_BOOLEAN>;
+
struct UnicharIn : SkipAll {
bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
JS::HandleValue) override;
@@ -641,26 +716,61 @@
JS::HandleValue) override;
};
-struct StringInTransferNone : NullableIn, String {
+template <GITypeTag TAG = GI_TYPE_TAG_UTF8>
+struct StringInTransferNone : NullableIn {
bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
JS::HandleValue) override;
bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
GIArgument*) override;
};
-struct StringIn : StringInTransferNone {
+struct StringIn : StringInTransferNone<GI_TYPE_TAG_UTF8> {
bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
GIArgument*) override {
return skip();
}
};
-struct FilenameInTransferNone : StringInTransferNone {
- FilenameInTransferNone() { m_filename = true; }
+template <GITransfer TRANSFER = GI_TRANSFER_NOTHING>
+struct StringOutBase : SkipAll {
+ bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
+ JS::MutableHandleValue value) override {
+ return Gjs::c_value_to_js(cx, gjs_arg_get<char*>(arg), value);
+ }
+ bool release(JSContext* cx, GjsFunctionCallState*, GIArgument*,
+ GIArgument* out_arg [[maybe_unused]]) override {
+ if constexpr (TRANSFER == GI_TRANSFER_NOTHING) {
+ return skip();
+ } else if constexpr (TRANSFER == GI_TRANSFER_EVERYTHING) {
+ g_clear_pointer(&gjs_arg_member<char*>(out_arg), g_free);
+ return true;
+ } else {
+ return invalid(cx, G_STRFUNC);
+ }
+ }
+};
+
+template <GITransfer TRANSFER = GI_TRANSFER_NOTHING>
+struct StringReturn : StringOutBase<TRANSFER> {
+ bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
+ JS::HandleValue) override {
+ return Argument::invalid(cx, G_STRFUNC);
+ }
+
+ GITypeTag return_tag() const override { return GI_TYPE_TAG_UTF8; }
+};
+
+template <GITransfer TRANSFER = GI_TRANSFER_NOTHING>
+struct StringOut : StringOutBase<TRANSFER>, Positioned {
+ bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
+ JS::HandleValue) override {
+ return set_out_parameter(state, arg);
+ }
};
+using FilenameInTransferNone = StringInTransferNone<GI_TYPE_TAG_FILENAME>;
+
struct FilenameIn : FilenameInTransferNone {
- FilenameIn() { m_filename = true; }
bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
GIArgument*) override {
return skip();
@@ -722,6 +832,78 @@
GIArgument*) override;
};
+struct ZeroTerminatedArrayInOut : GenericInOut {
+ bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument*,
+ GIArgument* out_arg) override {
+ GITransfer transfer =
+ state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
+ GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
+ if (!gjs_gi_argument_release_in_array(cx, transfer, &m_type_info,
+ original_out_arg))
+ return false;
+
+ transfer =
+ state->call_completed() ? m_transfer : GI_TRANSFER_EVERYTHING;
+ return gjs_gi_argument_release_out_array(cx, transfer, &m_type_info,
+ out_arg);
+ }
+};
+
+struct ZeroTerminatedArrayIn : GenericIn, Nullable {
+ bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
+ JS::MutableHandleValue) override {
+ return skip();
+ }
+
+ bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
+ GIArgument*) override {
+ GITransfer transfer =
+ state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
+
+ return gjs_gi_argument_release_in_array(cx, transfer, &m_type_info,
+ in_arg);
+ }
+
+ GjsArgumentFlags flags() const override {
+ return Argument::flags() | Nullable::flags();
+ }
+};
+
+struct FixedSizeArrayIn : GenericIn {
+ bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
+ JS::MutableHandleValue) override {
+ return skip();
+ }
+
+ bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
+ GIArgument*) override {
+ GITransfer transfer =
+ state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
+
+ int size = g_type_info_get_array_fixed_size(&m_type_info);
+ return gjs_gi_argument_release_in_array(cx, transfer, &m_type_info,
+ size, in_arg);
+ }
+};
+
+struct FixedSizeArrayInOut : GenericInOut {
+ bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument*,
+ GIArgument* out_arg) override {
+ GITransfer transfer =
+ state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
+ GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
+ int size = g_type_info_get_array_fixed_size(&m_type_info);
+ if (!gjs_gi_argument_release_in_array(cx, transfer, &m_type_info, size,
+ original_out_arg))
+ return false;
+
+ transfer =
+ state->call_completed() ? m_transfer : GI_TRANSFER_EVERYTHING;
+ return gjs_gi_argument_release_out_array(cx, transfer, &m_type_info,
+ size, out_arg);
+ }
+};
+
GJS_JSAPI_RETURN_CONVENTION
bool NotIntrospectable::in(JSContext* cx, GjsFunctionCallState* state,
GIArgument*, JS::HandleValue) {
@@ -769,9 +951,9 @@
GJS_JSAPI_RETURN_CONVENTION
bool GenericIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
JS::HandleValue value) {
- return gjs_value_to_g_argument(cx, value, &m_type_info, m_arg_name,
- GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
- arg);
+ return gjs_value_to_gi_argument(cx, value, &m_type_info, m_arg_name,
+ GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
+ arg);
}
GJS_JSAPI_RETURN_CONVENTION
@@ -780,25 +962,29 @@
if (!GenericIn::in(cx, state, arg, value))
return false;
- state->out_cvalue(m_arg_pos) = state->inout_original_cvalue(m_arg_pos) =
- *arg;
- gjs_arg_set(arg, &state->out_cvalue(m_arg_pos));
- return true;
+ return set_inout_parameter(state, arg);
}
GJS_JSAPI_RETURN_CONVENTION
bool ExplicitArrayIn::in(JSContext* cx, GjsFunctionCallState* state,
- GArgument* arg, JS::HandleValue value) {
+ GIArgument* arg, JS::HandleValue value) {
void* data;
size_t length;
+ if (m_length_direction != GI_DIRECTION_INOUT &&
+ m_length_direction != GI_DIRECTION_IN) {
+ gjs_throw(cx, "Using different length argument direction for array %s"
+ "is not supported for in arrays", m_arg_name);
+ return false;
+ }
+
if (!gjs_array_to_explicit_array(cx, value, &m_type_info, m_arg_name,
GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
&data, &length))
return false;
- gjs_g_argument_set_array_length(m_tag, &state->in_cvalue(m_length_pos),
- length);
+ gjs_gi_argument_set_array_length(m_tag, &state->in_cvalue(m_length_pos),
+ length);
gjs_arg_set(arg, data);
return true;
}
@@ -822,11 +1008,13 @@
gjs_arg_unset<void*>(&state->out_cvalue(ix));
gjs_arg_unset<void*>(&state->inout_original_cvalue(ix));
} else {
- state->out_cvalue(length_pos) =
- state->inout_original_cvalue(length_pos) =
- state->in_cvalue(length_pos);
- gjs_arg_set(&state->in_cvalue(length_pos),
- &state->out_cvalue(length_pos));
+ if G_LIKELY (m_length_direction == GI_DIRECTION_INOUT) {
+ state->out_cvalue(length_pos) =
+ state->inout_original_cvalue(length_pos) =
+ state->in_cvalue(length_pos);
+ gjs_arg_set(&state->in_cvalue(length_pos),
+ &state->out_cvalue(length_pos));
+ }
state->out_cvalue(ix) = state->inout_original_cvalue(ix) = *arg;
gjs_arg_set(arg, &state->out_cvalue(ix));
@@ -938,61 +1126,32 @@
}
template <typename T>
-GJS_JSAPI_RETURN_CONVENTION inline static bool gjs_arg_set_from_js_value(
- JSContext* cx, const JS::HandleValue& value, GArgument* arg,
- Argument* gjs_arg) {
+GJS_JSAPI_RETURN_CONVENTION bool NumericIn<T>::in(JSContext* cx,
+ GjsFunctionCallState*,
+ GIArgument* arg,
+ JS::HandleValue value) {
bool out_of_range = false;
if (!gjs_arg_set_from_js_value<T>(cx, value, arg, &out_of_range)) {
if (out_of_range) {
gjs_throw(cx, "Argument %s: value is out of range for %s",
- gjs_arg->arg_name(), Gjs::static_type_name<T>());
+ arg_name(), Gjs::static_type_name<T>());
}
return false;
}
- gjs_debug_marshal(
- GJS_DEBUG_GFUNCTION, "%s set to value %s (type %s)",
- GjsAutoChar(gjs_argument_display_name(gjs_arg->arg_name(),
- GJS_ARGUMENT_ARGUMENT))
- .get(),
- std::to_string(gjs_arg_get<T>(arg)).c_str(),
- Gjs::static_type_name<T>());
+ gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "%s set to value %s (type %s)",
+ GjsAutoChar{gjs_argument_display_name(
+ arg_name(), GJS_ARGUMENT_ARGUMENT)}
+ .get(),
+ std::to_string(gjs_arg_get<T>(arg)).c_str(),
+ Gjs::static_type_name<T>());
return true;
}
GJS_JSAPI_RETURN_CONVENTION
-bool NumericIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
- JS::HandleValue value) {
- switch (m_tag) {
- case GI_TYPE_TAG_INT8:
- return gjs_arg_set_from_js_value<int8_t>(cx, value, arg, this);
- case GI_TYPE_TAG_UINT8:
- return gjs_arg_set_from_js_value<uint8_t>(cx, value, arg, this);
- case GI_TYPE_TAG_INT16:
- return gjs_arg_set_from_js_value<int16_t>(cx, value, arg, this);
- case GI_TYPE_TAG_UINT16:
- return gjs_arg_set_from_js_value<uint16_t>(cx, value, arg, this);
- case GI_TYPE_TAG_INT32:
- return gjs_arg_set_from_js_value<int32_t>(cx, value, arg, this);
- case GI_TYPE_TAG_DOUBLE:
- return gjs_arg_set_from_js_value<double>(cx, value, arg, this);
- case GI_TYPE_TAG_FLOAT:
- return gjs_arg_set_from_js_value<float>(cx, value, arg, this);
- case GI_TYPE_TAG_INT64:
- return gjs_arg_set_from_js_value<int64_t>(cx, value, arg, this);
- case GI_TYPE_TAG_UINT64:
- return gjs_arg_set_from_js_value<uint64_t>(cx, value, arg, this);
- case GI_TYPE_TAG_UINT32:
- return gjs_arg_set_from_js_value<uint32_t>(cx, value, arg, this);
- default:
- g_assert_not_reached();
- }
-}
-
-GJS_JSAPI_RETURN_CONVENTION
bool UnicharIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
JS::HandleValue value) {
if (!value.isString())
@@ -1025,9 +1184,10 @@
return true;
}
-GJS_JSAPI_RETURN_CONVENTION
-bool StringInTransferNone::in(JSContext* cx, GjsFunctionCallState* state,
- GIArgument* arg, JS::HandleValue value) {
+template <GITypeTag TAG>
+GJS_JSAPI_RETURN_CONVENTION bool StringInTransferNone<TAG>::in(
+ JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
+ JS::HandleValue value) {
if (value.isNull())
return NullableIn::in(cx, state, arg, value);
@@ -1035,19 +1195,21 @@
return report_typeof_mismatch(cx, m_arg_name, value,
ExpectedType::STRING);
- if (m_filename) {
+ if constexpr (TAG == GI_TYPE_TAG_FILENAME) {
GjsAutoChar str;
if (!gjs_string_to_filename(cx, value, &str))
return false;
gjs_arg_set(arg, str.release());
return true;
+ } else if constexpr (TAG == GI_TYPE_TAG_UTF8) {
+ JS::UniqueChars str = gjs_string_to_utf8(cx, value);
+ if (!str)
+ return false;
+ gjs_arg_set(arg, g_strdup(str.get()));
+ return true;
+ } else {
+ return invalid(cx, G_STRFUNC);
}
-
- JS::UniqueChars str = gjs_string_to_utf8(cx, value);
- if (!str)
- return false;
- gjs_arg_set(arg, g_strdup(str.get()));
- return true;
}
GJS_JSAPI_RETURN_CONVENTION
@@ -1091,8 +1253,7 @@
if ((uint64_t(number) & m_mask) != uint64_t(number)) {
gjs_throw(cx,
- "0x%" G_GINT64_MODIFIER
- "x is not a valid value for flags argument %s",
+ "0x%" PRId64 " is not a valid value for flags argument %s",
number, m_arg_name);
return false;
}
@@ -1107,7 +1268,7 @@
GJS_JSAPI_RETURN_CONVENTION
bool ForeignStructInstanceIn::in(JSContext* cx, GjsFunctionCallState*,
GIArgument* arg, JS::HandleValue value) {
- return gjs_struct_foreign_convert_to_g_argument(
+ return gjs_struct_foreign_convert_to_gi_argument(
cx, value, m_info, m_arg_name, GJS_ARGUMENT_ARGUMENT, m_transfer,
flags(), arg);
}
@@ -1340,14 +1501,20 @@
GJS_JSAPI_RETURN_CONVENTION
bool GenericInOut::out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
JS::MutableHandleValue value) {
- return gjs_value_from_g_argument(cx, value, &m_type_info, arg, true);
+ return gjs_value_from_gi_argument(cx, value, &m_type_info, arg, true);
}
GJS_JSAPI_RETURN_CONVENTION
bool ExplicitArrayInOut::out(JSContext* cx, GjsFunctionCallState* state,
GIArgument* arg, JS::MutableHandleValue value) {
- GIArgument* length_arg = &(state->out_cvalue(m_length_pos));
- size_t length = gjs_g_argument_get_array_length(m_tag, length_arg);
+ GIArgument* length_arg;
+
+ if (m_length_direction != GI_DIRECTION_IN)
+ length_arg = &(state->out_cvalue(m_length_pos));
+ else
+ length_arg = &(state->in_cvalue(m_length_pos));
+
+ size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
return gjs_value_from_explicit_array(cx, value, &m_type_info, m_transfer,
arg, length);
@@ -1358,14 +1525,14 @@
GIArgument* in_arg, GIArgument*) {
GITransfer transfer =
state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
- return gjs_g_argument_release_in_arg(cx, transfer, &m_type_info, in_arg);
+ return gjs_gi_argument_release_in_arg(cx, transfer, &m_type_info, in_arg);
}
GJS_JSAPI_RETURN_CONVENTION
bool GenericOut::release(JSContext* cx, GjsFunctionCallState*,
GIArgument* in_arg [[maybe_unused]],
GIArgument* out_arg) {
- return gjs_g_argument_release(cx, m_transfer, &m_type_info, out_arg);
+ return gjs_gi_argument_release(cx, m_transfer, &m_type_info, out_arg);
}
GJS_JSAPI_RETURN_CONVENTION
@@ -1376,11 +1543,11 @@
// freeing it.
GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
- if (!gjs_g_argument_release_in_arg(cx, GI_TRANSFER_NOTHING, &m_type_info,
- original_out_arg))
+ if (!gjs_gi_argument_release_in_arg(cx, GI_TRANSFER_NOTHING, &m_type_info,
+ original_out_arg))
return false;
- return gjs_g_argument_release(cx, m_transfer, &m_type_info, out_arg);
+ return gjs_gi_argument_release(cx, m_transfer, &m_type_info, out_arg);
}
GJS_JSAPI_RETURN_CONVENTION
@@ -1388,10 +1555,10 @@
GIArgument* in_arg [[maybe_unused]],
GIArgument* out_arg) {
GIArgument* length_arg = &state->out_cvalue(m_length_pos);
- size_t length = gjs_g_argument_get_array_length(m_tag, length_arg);
+ size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
- return gjs_g_argument_release_out_array(cx, m_transfer, &m_type_info,
- length, out_arg);
+ return gjs_gi_argument_release_out_array(cx, m_transfer, &m_type_info,
+ length, out_arg);
}
GJS_JSAPI_RETURN_CONVENTION
@@ -1399,34 +1566,42 @@
GIArgument* in_arg,
GIArgument* out_arg [[maybe_unused]]) {
GIArgument* length_arg = &state->in_cvalue(m_length_pos);
- size_t length = gjs_g_argument_get_array_length(m_tag, length_arg);
+ size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
GITransfer transfer =
state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
- return gjs_g_argument_release_in_array(cx, transfer, &m_type_info, length,
- in_arg);
+ return gjs_gi_argument_release_in_array(cx, transfer, &m_type_info, length,
+ in_arg);
}
GJS_JSAPI_RETURN_CONVENTION
bool ExplicitArrayInOut::release(JSContext* cx, GjsFunctionCallState* state,
GIArgument* in_arg [[maybe_unused]],
GIArgument* out_arg) {
- GIArgument* length_arg = &state->in_cvalue(m_length_pos);
- size_t length = gjs_g_argument_get_array_length(m_tag, length_arg);
+ GIArgument* length_arg = &state->out_cvalue(m_length_pos);
+ size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
// For inout, transfer refers to what we get back from the function; for
// the temporary C value we allocated, clearly we're responsible for
// freeing it.
GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
- if (gjs_arg_get<void*>(original_out_arg) != gjs_arg_get<void*>(out_arg) &&
- !gjs_g_argument_release_in_array(cx, GI_TRANSFER_NOTHING, &m_type_info,
- length, original_out_arg))
- return false;
+ // Due to https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/192
+ // Here we've to guess what to do, but in general is "better" to leak than
+ // crash, so let's assume that in/out transfer is matching.
+ if (gjs_arg_get<void*>(original_out_arg) != gjs_arg_get<void*>(out_arg)) {
+ GITransfer transfer =
+ state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
+ if (!gjs_gi_argument_release_in_array(cx, transfer, &m_type_info,
+ length, original_out_arg))
+ return false;
+ }
- return gjs_g_argument_release_out_array(cx, m_transfer, &m_type_info,
- length, out_arg);
+ GITransfer transfer =
+ state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
+ return gjs_gi_argument_release_out_array(cx, transfer, &m_type_info, length,
+ out_arg);
}
GJS_JSAPI_RETURN_CONVENTION
@@ -1459,10 +1634,10 @@
return true;
}
-GJS_JSAPI_RETURN_CONVENTION
-bool StringInTransferNone::release(JSContext*, GjsFunctionCallState*,
- GIArgument* in_arg,
- GIArgument* out_arg [[maybe_unused]]) {
+template <GITypeTag TAG>
+GJS_JSAPI_RETURN_CONVENTION bool StringInTransferNone<TAG>::release(
+ JSContext*, GjsFunctionCallState*, GIArgument* in_arg,
+ GIArgument* out_arg [[maybe_unused]]) {
g_free(gjs_arg_get<void*>(in_arg));
return true;
}
@@ -1475,8 +1650,8 @@
state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
if (transfer == GI_TRANSFER_NOTHING)
- return gjs_struct_foreign_release_g_argument(cx, m_transfer, m_info,
- in_arg);
+ return gjs_struct_foreign_release_gi_argument(cx, m_transfer, m_info,
+ in_arg);
return true;
}
@@ -1534,7 +1709,7 @@
#ifdef GJS_DO_ARGUMENTS_SIZE_CHECK
template <typename T>
constexpr size_t argument_maximum_size() {
- if constexpr (std::is_same_v<T, Arg::NumericIn>)
+ if constexpr (std::is_same_v<T, Arg::NumericIn<int>>)
return 24;
if constexpr (std::is_same_v<T, Arg::ObjectIn> ||
std::is_same_v<T, Arg::BoxedIn>)
@@ -1582,7 +1757,7 @@
if constexpr (std::is_base_of_v<Arg::Transferable, T>)
arg->m_transfer = transfer;
- if constexpr (std::is_base_of_v<Arg::TypeInfo, T> &&
+ if constexpr (std::is_base_of_v<Arg::HasTypeInfo, T> &&
ArgKind != Arg::Kind::INSTANCE) {
arg->m_type_info = std::move(*type_info);
}
@@ -1680,12 +1855,20 @@
return instance()->as_instance()->gtype();
}
+GITypeTag ArgsCache::return_tag() const {
+ Argument* rval = return_value();
+ if (!rval)
+ return GI_TYPE_TAG_VOID;
+
+ return rval->return_tag();
+}
+
GITypeInfo* ArgsCache::return_type() const {
Argument* rval = return_value();
if (!rval)
return nullptr;
- return const_cast<GITypeInfo*>(rval->as_return_value()->type_info());
+ return const_cast<GITypeInfo*>(rval->return_type());
}
constexpr void ArgsCache::set_skip_all(uint8_t index, const char* name) {
@@ -1734,7 +1917,8 @@
static_cast<GjsArgumentFlags>(flags | GjsArgumentFlags::SKIP_ALL));
}
- array->set_array_length(length_pos, g_type_info_get_tag(&length_type));
+ array->set_array_length(length_pos, g_type_info_get_tag(&length_type),
+ g_arg_info_get_direction(&length_arg));
}
void ArgsCache::build_return(GICallableInfo* callable, bool* inc_counter_out) {
@@ -1753,14 +1937,89 @@
*inc_counter_out = true;
GjsArgumentFlags flags = GjsArgumentFlags::SKIP_IN;
- if (tag == GI_TYPE_TAG_ARRAY) {
- int length_pos = g_type_info_get_array_length(&type_info);
- if (length_pos >= 0) {
- set_array_argument<Arg::Kind::RETURN_VALUE>(
- callable, 0, &type_info, GI_DIRECTION_OUT, nullptr, flags,
- length_pos);
+ if (g_callable_info_may_return_null(callable))
+ flags |= GjsArgumentFlags::MAY_BE_NULL;
+
+ switch (tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ set_return<Arg::BooleanReturn>(&type_info, transfer, flags);
return;
+
+ case GI_TYPE_TAG_INT8:
+ set_return<Arg::NumericReturn<int8_t, GI_TYPE_TAG_INT8>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_INT16:
+ set_return<Arg::NumericReturn<int16_t, GI_TYPE_TAG_INT16>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_INT32:
+ set_return<Arg::NumericReturn<int32_t, GI_TYPE_TAG_INT32>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_UINT8:
+ set_return<Arg::NumericReturn<uint8_t, GI_TYPE_TAG_UINT8>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_UINT16:
+ set_return<Arg::NumericReturn<uint16_t, GI_TYPE_TAG_UINT16>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_UINT32:
+ set_return<Arg::NumericReturn<uint32_t, GI_TYPE_TAG_UINT32>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_INT64:
+ set_return<Arg::NumericReturn<int64_t, GI_TYPE_TAG_INT64>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_UINT64:
+ set_return<Arg::NumericReturn<uint64_t, GI_TYPE_TAG_UINT64>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_FLOAT:
+ set_return<Arg::NumericReturn<float, GI_TYPE_TAG_FLOAT>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_DOUBLE:
+ set_return<Arg::NumericReturn<double, GI_TYPE_TAG_DOUBLE>>(
+ &type_info, transfer, flags);
+ return;
+
+ case GI_TYPE_TAG_UTF8:
+ if (transfer == GI_TRANSFER_NOTHING) {
+ set_return<Arg::StringReturn<GI_TRANSFER_NOTHING>>(
+ &type_info, transfer, flags);
+ return;
+ } else {
+ set_return<Arg::StringReturn<GI_TRANSFER_EVERYTHING>>(
+ &type_info, transfer, flags);
+ return;
+ }
+
+ case GI_TYPE_TAG_ARRAY: {
+ int length_pos = g_type_info_get_array_length(&type_info);
+ if (length_pos >= 0) {
+ set_array_argument<Arg::Kind::RETURN_VALUE>(
+ callable, 0, &type_info, GI_DIRECTION_OUT, nullptr, flags,
+ length_pos);
+ return;
+ }
+
+ [[fallthrough]];
}
+
+ default:
+ break;
}
// in() is ignored for the return value, but skip_in is not (it is used
@@ -2020,17 +2279,44 @@
break;
case GI_TYPE_TAG_INT8:
+ set_argument_auto<Arg::NumericIn<int8_t>>(common_args);
+ return;
+
case GI_TYPE_TAG_INT16:
+ set_argument_auto<Arg::NumericIn<int16_t>>(common_args);
+ return;
+
case GI_TYPE_TAG_INT32:
+ set_argument_auto<Arg::NumericIn<int32_t>>(common_args);
+ return;
+
case GI_TYPE_TAG_UINT8:
+ set_argument_auto<Arg::NumericIn<uint8_t>>(common_args);
+ return;
+
case GI_TYPE_TAG_UINT16:
+ set_argument_auto<Arg::NumericIn<uint16_t>>(common_args);
+ return;
+
case GI_TYPE_TAG_UINT32:
+ set_argument_auto<Arg::NumericIn<uint32_t>>(common_args);
+ return;
+
case GI_TYPE_TAG_INT64:
+ set_argument_auto<Arg::NumericIn<int64_t>>(common_args);
+ return;
+
case GI_TYPE_TAG_UINT64:
+ set_argument_auto<Arg::NumericIn<uint64_t>>(common_args);
+ return;
+
case GI_TYPE_TAG_FLOAT:
+ set_argument_auto<Arg::NumericIn<float>>(common_args);
+ return;
+
case GI_TYPE_TAG_DOUBLE:
- set_argument_auto<Arg::NumericIn>(common_args, tag);
- break;
+ set_argument_auto<Arg::NumericIn<double>>(common_args);
+ return;
case GI_TYPE_TAG_UNICHAR:
set_argument_auto<Arg::UnicharIn>(common_args);
@@ -2049,7 +2335,8 @@
case GI_TYPE_TAG_UTF8:
if (transfer == GI_TRANSFER_NOTHING)
- set_argument_auto<Arg::StringInTransferNone>(common_args);
+ set_argument_auto<Arg::StringInTransferNone<GI_TYPE_TAG_UTF8>>(
+ common_args);
else
set_argument_auto<Arg::StringIn>(common_args);
break;
@@ -2073,6 +2360,132 @@
}
}
+void ArgsCache::build_normal_out_arg(uint8_t gi_index, GITypeInfo* type_info,
+ GIArgInfo* arg, GjsArgumentFlags flags) {
+ const char* name = g_base_info_get_name(arg);
+ GITransfer transfer = g_arg_info_get_ownership_transfer(arg);
+ auto common_args =
+ std::make_tuple(gi_index, name, type_info, transfer, flags);
+ GITypeTag tag = g_type_info_get_tag(type_info);
+
+ switch (tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ set_argument_auto<Arg::BooleanOut>(common_args);
+ break;
+
+ case GI_TYPE_TAG_INT8:
+ set_argument_auto<Arg::NumericOut<int8_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_INT16:
+ set_argument_auto<Arg::NumericOut<int16_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_INT32:
+ set_argument_auto<Arg::NumericOut<int32_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UINT8:
+ set_argument_auto<Arg::NumericOut<uint8_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UINT16:
+ set_argument_auto<Arg::NumericOut<uint16_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UINT32:
+ set_argument_auto<Arg::NumericOut<uint32_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_INT64:
+ set_argument_auto<Arg::NumericOut<int64_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UINT64:
+ set_argument_auto<Arg::NumericOut<uint64_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_FLOAT:
+ set_argument_auto<Arg::NumericOut<float>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_DOUBLE:
+ set_argument_auto<Arg::NumericOut<double>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UTF8:
+ if (transfer == GI_TRANSFER_NOTHING) {
+ set_argument_auto<Arg::StringOut<GI_TRANSFER_NOTHING>>(
+ common_args);
+ } else {
+ set_argument_auto<Arg::StringOut<GI_TRANSFER_EVERYTHING>>(
+ common_args);
+ }
+ return;
+
+ default:
+ set_argument_auto<Arg::FallbackOut>(common_args);
+ }
+}
+
+void ArgsCache::build_normal_inout_arg(uint8_t gi_index, GITypeInfo* type_info,
+ GIArgInfo* arg, GjsArgumentFlags flags) {
+ const char* name = g_base_info_get_name(arg);
+ GITransfer transfer = g_arg_info_get_ownership_transfer(arg);
+ auto common_args =
+ std::make_tuple(gi_index, name, type_info, transfer, flags);
+ GITypeTag tag = g_type_info_get_tag(type_info);
+
+ switch (tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ set_argument_auto<Arg::BooleanInOut>(common_args);
+ break;
+
+ case GI_TYPE_TAG_INT8:
+ set_argument_auto<Arg::NumericInOut<int8_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_INT16:
+ set_argument_auto<Arg::NumericInOut<int16_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_INT32:
+ set_argument_auto<Arg::NumericInOut<int32_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UINT8:
+ set_argument_auto<Arg::NumericInOut<uint8_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UINT16:
+ set_argument_auto<Arg::NumericInOut<uint16_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UINT32:
+ set_argument_auto<Arg::NumericInOut<uint32_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_INT64:
+ set_argument_auto<Arg::NumericInOut<int64_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_UINT64:
+ set_argument_auto<Arg::NumericInOut<uint64_t>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_FLOAT:
+ set_argument_auto<Arg::NumericInOut<float>>(common_args);
+ return;
+
+ case GI_TYPE_TAG_DOUBLE:
+ set_argument_auto<Arg::NumericInOut<double>>(common_args);
+ return;
+
+ default:
+ set_argument_auto<Arg::FallbackInOut>(common_args);
+ }
+}
+
void ArgsCache::build_instance(GICallableInfo* callable) {
if (!m_is_method)
return;
@@ -2107,6 +2520,11 @@
GjsArgumentFlags::NONE);
}
+static constexpr bool type_tag_is_scalar(GITypeTag tag) {
+ return GI_TYPE_TAG_IS_NUMERIC(tag) || tag == GI_TYPE_TAG_BOOLEAN ||
+ tag == GI_TYPE_TAG_GTYPE;
+}
+
void ArgsCache::build_arg(uint8_t gi_index, GIDirection direction,
GIArgInfo* arg, GICallableInfo* callable,
bool* inc_counter_out) {
@@ -2159,7 +2577,11 @@
default:
break;
}
- } else {
+ } else if (!type_tag_is_scalar(type_tag) &&
+ !g_type_info_is_pointer(&type_info)) {
+ // Scalar out parameters should not be annotated with
+ // caller-allocates, which is for structured types that need to be
+ // allocated in order for the function to fill them in.
size = gjs_type_get_element_size(type_tag, &type_info);
}
@@ -2254,15 +2676,31 @@
}
return;
+ } else if (g_type_info_is_zero_terminated(&type_info)) {
+ if (direction == GI_DIRECTION_IN) {
+ set_argument_auto<Arg::ZeroTerminatedArrayIn>(common_args);
+ return;
+ } else if (direction == GI_DIRECTION_INOUT) {
+ set_argument_auto<Arg::ZeroTerminatedArrayInOut>(common_args);
+ return;
+ }
+ } else if (g_type_info_get_array_fixed_size(&type_info) >= 0) {
+ if (direction == GI_DIRECTION_IN) {
+ set_argument_auto<Arg::FixedSizeArrayIn>(common_args);
+ return;
+ } else if (direction == GI_DIRECTION_INOUT) {
+ set_argument_auto<Arg::FixedSizeArrayInOut>(common_args);
+ return;
+ }
}
}
if (direction == GI_DIRECTION_IN)
build_normal_in_arg(gi_index, &type_info, arg, flags);
else if (direction == GI_DIRECTION_INOUT)
- set_argument_auto<Arg::FallbackInOut>(common_args);
+ build_normal_inout_arg(gi_index, &type_info, arg, flags);
else
- set_argument_auto<Arg::FallbackOut>(common_args);
+ build_normal_out_arg(gi_index, &type_info, arg, flags);
return;
}
diff -ruN cjs-6.2.0-orig/gi/arg-cache.h cjs-6.2.0/gi/arg-cache.h
--- cjs-6.2.0-orig/gi/arg-cache.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/arg-cache.h 2024-10-14 18:41:39.000000000 +0200
@@ -94,7 +94,8 @@
protected:
constexpr Argument() : m_skip_in(false), m_skip_out(false) {}
- virtual const Arg::ReturnValue* as_return_value() const { return nullptr; }
+ virtual GITypeTag return_tag() const { return GI_TYPE_TAG_VOID; }
+ virtual const GITypeInfo* return_type() const { return nullptr; }
virtual const Arg::Instance* as_instance() const { return nullptr; }
constexpr void set_instance_parameter() {
@@ -156,11 +157,16 @@
void build_instance(GICallableInfo* callable);
GType instance_type() const;
+ GITypeTag return_tag() const;
GITypeInfo* return_type() const;
private:
void build_normal_in_arg(uint8_t gi_index, GITypeInfo*, GIArgInfo*,
GjsArgumentFlags);
+ void build_normal_out_arg(uint8_t gi_index, GITypeInfo*, GIArgInfo*,
+ GjsArgumentFlags);
+ void build_normal_inout_arg(uint8_t gi_index, GITypeInfo*, GIArgInfo*,
+ GjsArgumentFlags);
template <Arg::Kind ArgKind = Arg::Kind::NORMAL>
void build_interface_in_arg(uint8_t gi_index, GITypeInfo*, GIBaseInfo*,
diff -ruN cjs-6.2.0-orig/gi/arg-inl.h cjs-6.2.0/gi/arg-inl.h
--- cjs-6.2.0-orig/gi/arg-inl.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/arg-inl.h 2024-10-14 18:41:39.000000000 +0200
@@ -4,6 +4,8 @@
#pragma once
+#include <config.h>
+
#include <stdint.h>
#include <cstddef> // for nullptr_t
@@ -114,6 +116,20 @@
}
}
+typedef enum {
+ GJS_TYPE_TAG_LONG = 0,
+} ExtraTag;
+
+template <typename T, ExtraTag TAG>
+[[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) {
+ if constexpr (TAG == GJS_TYPE_TAG_LONG &&
+ std::is_same_v<T, long>) // NOLINT(runtime/int)
+ return gjs_arg_member<&GIArgument::v_long>(arg);
+ else if constexpr (TAG == GJS_TYPE_TAG_LONG &&
+ std::is_same_v<T, unsigned long>) // NOLINT(runtime/int)
+ return gjs_arg_member<&GIArgument::v_ulong>(arg);
+}
+
template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
constexpr inline void gjs_arg_set(GIArgument* arg, T v) {
if constexpr (std::is_pointer_v<T>) {
@@ -129,6 +145,11 @@
}
}
+template <typename T, ExtraTag TAG>
+constexpr inline void gjs_arg_set(GIArgument* arg, T v) {
+ gjs_arg_member<T, TAG>(arg) = v;
+}
+
// Store function pointers as void*. It is a requirement of GLib that your
// compiler can do this
template <typename ReturnT, typename... Args>
@@ -151,6 +172,11 @@
return gjs_arg_member<T, TAG>(arg);
}
+template <typename T, ExtraTag TAG>
+[[nodiscard]] constexpr inline T gjs_arg_get(GIArgument* arg) {
+ return gjs_arg_member<T, TAG>(arg);
+}
+
template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
[[nodiscard]] constexpr inline void* gjs_arg_get_as_pointer(GIArgument* arg) {
return gjs_int_to_pointer(gjs_arg_get<T, TAG>(arg));
@@ -192,29 +218,29 @@
return static_cast<double>(val);
}
-template <typename T>
+template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
GJS_JSAPI_RETURN_CONVENTION inline bool gjs_arg_set_from_js_value(
- JSContext* cx, const JS::HandleValue& value, GArgument* arg,
+ JSContext* cx, const JS::HandleValue& value, GIArgument* arg,
bool* out_of_range) {
if constexpr (Gjs::type_has_js_getter<T>())
- return Gjs::js_value_to_c(cx, value, &gjs_arg_member<T>(arg));
+ return Gjs::js_value_to_c<TAG>(cx, value, &gjs_arg_member<T, TAG>(arg));
Gjs::JsValueHolder::Relaxed<T> val{};
- if (!Gjs::js_value_to_c_checked<T>(cx, value, &val, out_of_range))
+ if (!Gjs::js_value_to_c_checked<T, TAG>(cx, value, &val, out_of_range))
return false;
if (*out_of_range)
return false;
- gjs_arg_set<T>(arg, val);
+ gjs_arg_set<T, TAG>(arg, val);
return true;
}
// A helper function to retrieve array lengths from a GIArgument (letting the
// compiler generate good instructions in case of big endian machines)
-[[nodiscard]] constexpr size_t gjs_g_argument_get_array_length(
+[[nodiscard]] constexpr size_t gjs_gi_argument_get_array_length(
GITypeTag tag, GIArgument* arg) {
switch (tag) {
case GI_TYPE_TAG_INT8:
diff -ruN cjs-6.2.0-orig/gi/arg-types-inl.h cjs-6.2.0/gi/arg-types-inl.h
--- cjs-6.2.0-orig/gi/arg-types-inl.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/arg-types-inl.h 2024-09-16 22:32:54.000000000 +0200
@@ -102,4 +102,14 @@
return "string";
}
+template <>
+inline const char* static_type_name<const char*>() {
+ return "constant string";
+}
+
+template <>
+inline const char* static_type_name<void>() {
+ return "void";
+}
+
} // namespace Gjs
diff -ruN cjs-6.2.0-orig/gi/arg.cpp cjs-6.2.0/gi/arg.cpp
--- cjs-6.2.0-orig/gi/arg.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/arg.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -5,11 +5,11 @@
#include <config.h>
+#include <inttypes.h>
#include <stdint.h>
#include <string.h> // for strcmp, strlen, memcpy
#include <string>
-#include <type_traits>
#include <girepository.h>
#include <glib-object.h>
@@ -32,6 +32,7 @@
#include <js/ValueArray.h>
#include <js/experimental/TypedData.h>
#include <jsapi.h> // for InformalValueTypeName, IdVector
+#include <mozilla/Unused.h>
#include "gi/arg-inl.h"
#include "gi/arg-types-inl.h"
@@ -65,7 +66,7 @@
GITypeInfo* arginfo, const char* arg_name,
GjsArgumentType arg_type);
-bool _gjs_flags_value_is_valid(JSContext* context, GType gtype, int64_t value) {
+bool _gjs_flags_value_is_valid(JSContext* cx, GType gtype, int64_t value) {
/* Do proper value check for flags with GType's */
if (gtype != G_TYPE_NONE) {
GjsAutoTypeClass<GFlagsClass> gflags_class(gtype);
@@ -73,9 +74,8 @@
/* check all bits are valid bits for the flag and is a 32 bit flag*/
if ((tmpval &= gflags_class->mask) != value) { /* Not a guint32 with invalid mask values*/
- gjs_throw(context,
- "0x%" G_GINT64_MODIFIER "x is not a valid value for flags %s",
- value, g_type_name(gtype));
+ gjs_throw(cx, "0x%" PRIx64 " is not a valid value for flags %s",
+ value, g_type_name(gtype));
return false;
}
}
@@ -84,7 +84,7 @@
}
GJS_JSAPI_RETURN_CONVENTION
-static bool _gjs_enum_value_is_valid(JSContext* context, GIEnumInfo* enum_info,
+static bool _gjs_enum_value_is_valid(JSContext* cx, GIEnumInfo* enum_info,
int64_t value) {
bool found;
int n_values;
@@ -104,9 +104,8 @@
}
if (!found) {
- gjs_throw(context,
- "%" G_GINT64_MODIFIER "d is not a valid value for enumeration %s",
- value, g_base_info_get_name((GIBaseInfo *)enum_info));
+ gjs_throw(cx, "%" PRId64 " is not a valid value for enumeration %s",
+ value, g_base_info_get_name(enum_info));
}
return found;
@@ -163,7 +162,7 @@
g_type_info_get_interface(type_info);
g_assert(interface_info != nullptr);
- switch (g_base_info_get_type(interface_info)) {
+ switch (interface_info.type()) {
case GI_INFO_TYPE_STRUCT:
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
@@ -175,9 +174,6 @@
// cast is safe
gtype = g_registered_type_info_get_g_type(interface_info);
break;
- case GI_INFO_TYPE_VALUE:
- // Special case for GValues
- return true;
default:
gtype = G_TYPE_NONE;
}
@@ -218,7 +214,7 @@
GjsAutoBaseInfo interface_info =
g_type_info_get_interface(type_info);
- switch (g_base_info_get_type(interface_info)) {
+ switch (interface_info.type()) {
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
return false;
@@ -271,7 +267,7 @@
if (transfer == GI_TRANSFER_CONTAINER) {
if (type_needs_release (param_info, g_type_info_get_tag(param_info))) {
/* FIXME: to make this work, we'd have to keep a list of temporary
- * GArguments for the function call so we could free them after
+ * GIArguments for the function call so we could free them after
* the surrounding container had been freed by the callee.
*/
gjs_throw(cx, "Container transfer for in parameters not supported");
@@ -286,8 +282,6 @@
T* list = nullptr;
for (size_t i = 0; i < length; ++i) {
- GArgument elem_arg = { 0 };
-
elem = JS::UndefinedValue();
if (!JS_GetElement(cx, array, i, &elem)) {
gjs_throw(cx, "Missing array element %zu", i);
@@ -298,9 +292,10 @@
* gobject-introspection needs to tell us this.
* Always say they can't for now.
*/
- if (!gjs_value_to_g_argument(cx, elem, param_info,
- GJS_ARGUMENT_LIST_ELEMENT, transfer,
- &elem_arg)) {
+ GIArgument elem_arg;
+ if (!gjs_value_to_gi_argument(cx, elem, param_info,
+ GJS_ARGUMENT_LIST_ELEMENT, transfer,
+ &elem_arg)) {
return false;
}
@@ -327,7 +322,7 @@
GITypeTag key_type) {
/* Don't use key/value destructor functions here, because we can't
* construct correct ones in general if the value type is complex.
- * Rely on the type-aware g_argument_release functions. */
+ * Rely on the type-aware gi_argument_release functions. */
if (is_string_type(key_type))
return g_hash_table_new(g_str_hash, g_str_equal);
return g_hash_table_new(NULL, NULL);
@@ -492,14 +487,14 @@
g_assert(props && "Property bag cannot be null");
- GjsAutoBaseInfo key_param_info = g_type_info_get_param_type(type_info, 0);
- GjsAutoBaseInfo val_param_info = g_type_info_get_param_type(type_info, 1);
+ GjsAutoTypeInfo key_param_info = g_type_info_get_param_type(type_info, 0);
+ GjsAutoTypeInfo val_param_info = g_type_info_get_param_type(type_info, 1);
if (transfer == GI_TRANSFER_CONTAINER) {
if (type_needs_release (key_param_info, g_type_info_get_tag(key_param_info)) ||
type_needs_release (val_param_info, g_type_info_get_tag(val_param_info))) {
/* FIXME: to make this work, we'd have to keep a list of temporary
- * GArguments for the function call so we could free them after
+ * GIArguments for the function call so we could free them after
* the surrounding container had been freed by the callee.
*/
gjs_throw(context,
@@ -530,9 +525,9 @@
!value_to_ghashtable_key(context, key_js, key_tag, &key_ptr) ||
!JS_GetPropertyById(context, props, cur_id, &val_js) ||
// Type check and convert value to a C type
- !gjs_value_to_g_argument(context, val_js, val_param_info, nullptr,
- GJS_ARGUMENT_HASH_ELEMENT, transfer,
- GjsArgumentFlags::MAY_BE_NULL, &val_arg))
+ !gjs_value_to_gi_argument(context, val_js, val_param_info, nullptr,
+ GJS_ARGUMENT_HASH_ELEMENT, transfer,
+ GjsArgumentFlags::MAY_BE_NULL, &val_arg))
return false;
GITypeTag val_type = g_type_info_get_tag(val_param_info);
@@ -644,7 +639,7 @@
return false;
}
- JS::RootedObject obj(context, JS::NewArrayObject(context, elems));
+ JSObject* obj = JS::NewArrayObject(context, elems);
if (!obj)
return false;
@@ -731,10 +726,9 @@
return false;
}
- if (!gjs_value_to_g_argument(
- context, elem, param_info,
- GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
- &arg)) {
+ if (!gjs_value_to_gi_argument(context, elem, param_info,
+ GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
+ &arg)) {
gjs_throw(context,
"Invalid element in array");
return false;
@@ -767,9 +761,9 @@
}
GIArgument arg;
- if (!gjs_value_to_g_argument(cx, elem, param_info,
- GJS_ARGUMENT_ARRAY_ELEMENT,
- GI_TRANSFER_NOTHING, &arg))
+ if (!gjs_value_to_gi_argument(cx, elem, param_info,
+ GJS_ARGUMENT_ARRAY_ELEMENT,
+ GI_TRANSFER_NOTHING, &arg))
return false;
memcpy(&flat_array[param_size * i], gjs_arg_get<void*>(&arg),
@@ -780,11 +774,8 @@
return true;
}
-[[nodiscard]] static bool is_gvalue(GIBaseInfo* info, GIInfoType info_type) {
- switch (info_type) {
- case GI_INFO_TYPE_VALUE:
- return true;
-
+[[nodiscard]] static bool is_gvalue(GIBaseInfo* info) {
+ switch (g_base_info_get_type(info)) {
case GI_INFO_TYPE_STRUCT:
case GI_INFO_TYPE_OBJECT:
case GI_INFO_TYPE_INTERFACE:
@@ -852,9 +843,7 @@
if (!g_type_info_is_pointer(param_info)) {
GjsAutoBaseInfo interface_info =
g_type_info_get_interface(param_info);
- GIInfoType info_type = g_base_info_get_type(interface_info);
-
- if (is_gvalue(interface_info, info_type)) {
+ if (is_gvalue(interface_info)) {
// Special case for GValue "flat arrays", this could also
// using the generic case, but if we do so we're leaking atm.
return gjs_array_to_auto_array<GValue>(context, array_value,
@@ -935,7 +924,7 @@
case GI_TYPE_TAG_INTERFACE: {
GjsAutoBaseInfo interface_info = g_type_info_get_interface(type_info);
- switch (g_base_info_get_type(interface_info)) {
+ switch (interface_info.type()) {
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
return sizeof(unsigned int);
@@ -944,8 +933,6 @@
return g_struct_info_get_size(interface_info);
case GI_INFO_TYPE_UNION:
return g_union_info_get_size(interface_info);
- case GI_INFO_TYPE_VALUE:
- return sizeof(GValue);
default:
return 0;
}
@@ -960,14 +947,10 @@
if (length < 0)
return sizeof(void*);
- GjsAutoBaseInfo param_info =
+ GjsAutoTypeInfo param_info =
g_type_info_get_param_type(type_info, 0);
GITypeTag param_tag = g_type_info_get_tag(param_info);
- size_t param_size =
- gjs_type_get_element_size(param_tag, param_info);
-
- if (param_size)
- return param_size * length;
+ return gjs_type_get_element_size(param_tag, param_info);
}
return sizeof(void*);
@@ -979,22 +962,27 @@
g_return_val_if_reached(0);
}
-template <bool zero_terminated = false>
-static inline bool gjs_g_argument_release_array_internal(
+enum class ArrayReleaseType {
+ EXPLICIT_LENGTH,
+ ZERO_TERMINATED,
+};
+
+template <ArrayReleaseType release_type = ArrayReleaseType::EXPLICIT_LENGTH>
+static inline bool gjs_gi_argument_release_array_internal(
JSContext* cx, GITransfer element_transfer, GjsArgumentFlags flags,
GITypeInfo* param_type, unsigned length, GIArgument* arg) {
GjsAutoPointer<uint8_t, void, g_free> arg_array =
gjs_arg_steal<uint8_t*>(arg);
+ if (!arg_array)
+ return true;
+
if (element_transfer != GI_TRANSFER_EVERYTHING)
return true;
- if constexpr (!zero_terminated) {
+ if constexpr (release_type == ArrayReleaseType::EXPLICIT_LENGTH) {
if (length == 0)
return true;
- } else {
- if (!arg_array)
- return true;
}
GITypeTag type_tag = g_type_info_get_tag(param_type);
@@ -1015,16 +1003,21 @@
for (size_t i = 0;; i++) {
GIArgument elem;
auto* element_start = &arg_array[i * element_size];
+ auto* pointer =
+ is_pointer ? *reinterpret_cast<uint8_t**>(element_start) : nullptr;
- if constexpr (zero_terminated) {
- if (*element_start == 0 &&
- memcmp(element_start, element_start + 1, element_size - 1) == 0)
+ if constexpr (release_type == ArrayReleaseType::ZERO_TERMINATED) {
+ if (is_pointer) {
+ if (!pointer)
+ break;
+ } else if (*element_start == 0 &&
+ memcmp(element_start, element_start + 1,
+ element_size - 1) == 0) {
break;
+ }
}
- gjs_arg_set(&elem, is_pointer
- ? *reinterpret_cast<uint8_t**>(element_start)
- : element_start);
+ gjs_arg_set(&elem, is_pointer ? pointer : element_start);
JS::AutoSaveExceptionState saved_exc(cx);
if (!gjs_g_arg_release_internal(cx, element_transfer, param_type,
type_tag, GJS_ARGUMENT_ARRAY_ELEMENT,
@@ -1032,7 +1025,7 @@
return false;
}
- if constexpr (!zero_terminated) {
+ if constexpr (release_type == ArrayReleaseType::EXPLICIT_LENGTH) {
if (i == length - 1)
break;
}
@@ -1076,7 +1069,7 @@
if (tag == GI_TYPE_TAG_INTERFACE) {
GjsAutoBaseInfo interface = g_type_info_get_interface(type_info);
- return g_info_type_to_string(g_base_info_get_type(interface));
+ return g_info_type_to_string(interface.type());
} else {
return g_type_tag_to_string(tag);
}
@@ -1173,10 +1166,7 @@
}
} // namespace arg
-static void
-intern_gdk_atom(const char *name,
- GArgument *ret)
-{
+static void intern_gdk_atom(const char* name, GIArgument* ret) {
GjsAutoFunctionInfo atom_intern_fun =
g_irepository_find_by_name(nullptr, "Gdk", "atom_intern");
@@ -1187,11 +1177,8 @@
gjs_arg_set(&atom_intern_args[0], name);
gjs_arg_set(&atom_intern_args[1], false);
- g_function_info_invoke(atom_intern_fun,
- atom_intern_args, 2,
- nullptr, 0,
- ret,
- nullptr);
+ mozilla::Unused << g_function_info_invoke(atom_intern_fun, atom_intern_args,
+ 2, nullptr, 0, ret, nullptr);
}
static bool value_to_interface_gi_argument(
@@ -1215,11 +1202,6 @@
gtype = g_registered_type_info_get_g_type(interface_info);
break;
- case GI_INFO_TYPE_VALUE:
- // Special case for GValues
- gtype = G_TYPE_VALUE;
- break;
-
default:
gtype = G_TYPE_NONE;
}
@@ -1358,11 +1340,18 @@
} else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
if (g_type_is_a(gtype, G_TYPE_CLOSURE)) {
+ if (BoxedBase::typecheck(cx, obj, interface_info, gtype,
+ GjsTypecheckNoThrow())) {
+ return BoxedBase::transfer_to_gi_argument(
+ cx, obj, arg, GI_DIRECTION_IN, transfer, gtype,
+ interface_info);
+ }
+
GClosure* closure =
Gjs::Closure::create_marshaled(cx, obj, "boxed");
// GI doesn't know about floating GClosure references. We
// guess that if this is a return value going from JS::Value
- // to GArgument, it's intended to be passed to a C API that
+ // to GIArgument, it's intended to be passed to a C API that
// will consume the floating reference.
if (arg_type != GJS_ARGUMENT_RETURN_VALUE) {
g_closure_ref(closure);
@@ -1458,7 +1447,7 @@
template <typename T>
GJS_JSAPI_RETURN_CONVENTION inline static bool gjs_arg_set_from_js_value(
- JSContext* cx, const JS::HandleValue& value, GArgument* arg,
+ JSContext* cx, const JS::HandleValue& value, GIArgument* arg,
const char* arg_name, GjsArgumentType arg_type) {
bool out_of_range = false;
@@ -1498,10 +1487,10 @@
return true;
}
-bool gjs_value_to_g_argument(JSContext* context, JS::HandleValue value,
- GITypeInfo* type_info, const char* arg_name,
- GjsArgumentType arg_type, GITransfer transfer,
- GjsArgumentFlags flags, GIArgument* arg) {
+bool gjs_value_to_gi_argument(JSContext* context, JS::HandleValue value,
+ GITypeInfo* type_info, const char* arg_name,
+ GjsArgumentType arg_type, GITransfer transfer,
+ GjsArgumentFlags flags, GIArgument* arg) {
GITypeTag type_tag = g_type_info_get_tag(type_info);
gjs_debug_marshal(
@@ -1648,7 +1637,7 @@
GjsAutoBaseInfo interface_info = g_type_info_get_interface(type_info);
g_assert(interface_info);
- GIInfoType interface_type = g_base_info_get_type(interface_info);
+ GIInfoType interface_type = interface_info.type();
if (interface_type == GI_INFO_TYPE_ENUM ||
interface_type == GI_INFO_TYPE_FLAGS ||
arg::is_gdk_atom(interface_info))
@@ -1656,7 +1645,7 @@
if (interface_type == GI_INFO_TYPE_STRUCT &&
g_struct_info_is_foreign(interface_info)) {
- return gjs_struct_foreign_convert_to_g_argument(
+ return gjs_struct_foreign_convert_to_gi_argument(
context, value, interface_info, arg_name, arg_type, transfer,
flags, arg);
}
@@ -1765,7 +1754,7 @@
break;
}
default:
- g_warning("Unhandled type %s for JavaScript to GArgument conversion",
+ g_warning("Unhandled type %s for JavaScript to GIArgument conversion",
g_type_tag_to_string(type_tag));
throw_invalid_argument(context, value, type_info, arg_name, arg_type);
return false;
@@ -1779,13 +1768,11 @@
* is. It basically boils down to memset(arg, 0, sizeof(*arg)), but
* gives as a bit more future flexibility and also will work if
* libffi passes us a buffer that only has room for the appropriate
- * branch of GArgument. (Currently it appears that the return buffer
+ * branch of GIArgument. (Currently it appears that the return buffer
* has a fixed size large enough for the union of all types.)
*/
void gjs_gi_argument_init_default(GITypeInfo* type_info, GIArgument* arg) {
- GITypeTag type_tag;
-
- type_tag = g_type_info_get_tag( (GITypeInfo*) type_info);
+ GITypeTag type_tag = g_type_info_get_tag(type_info);
switch (type_tag) {
case GI_TYPE_TAG_VOID:
@@ -1839,13 +1826,11 @@
gjs_arg_unset<void*>(arg);
break;
case GI_TYPE_TAG_INTERFACE: {
- GIInfoType interface_type;
-
GjsAutoBaseInfo interface_info =
g_type_info_get_interface(type_info);
g_assert(interface_info != nullptr);
- interface_type = g_base_info_get_type(interface_info);
+ GIInfoType interface_type = interface_info.type();
if (interface_type == GI_INFO_TYPE_ENUM ||
interface_type == GI_INFO_TYPE_FLAGS)
@@ -1861,7 +1846,7 @@
gjs_arg_unset<void*>(arg);
break;
default:
- g_warning("Unhandled type %s for default GArgument initialization",
+ g_warning("Unhandled type %s for default GIArgument initialization",
g_type_tag_to_string(type_tag));
break;
}
@@ -1897,7 +1882,7 @@
if (g_arg_info_is_caller_allocates(arg_info))
flags |= GjsArgumentFlags::CALLER_ALLOCATES;
- return gjs_value_to_g_argument(
+ return gjs_value_to_gi_argument(
context, value, &type_info, g_base_info_get_name(arg_info),
(g_arg_info_is_return_value(arg_info) ? GJS_ARGUMENT_RETURN_VALUE
: GJS_ARGUMENT_ARGUMENT),
@@ -1909,12 +1894,12 @@
JSContext* cx, JS::MutableHandleValue value_p, GITypeInfo* type_info,
GITransfer transfer, T* list) {
static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
- GArgument arg;
JS::RootedValueVector elems(cx);
GjsAutoTypeInfo param_info = g_type_info_get_param_type(type_info, 0);
g_assert(param_info);
+ GIArgument arg;
for (size_t i = 0; list; list = list->next, ++i) {
g_type_info_argument_from_hash_pointer(param_info, list->data, &arg);
@@ -1923,13 +1908,13 @@
return false;
}
- if (!gjs_value_from_g_argument(cx, elems[i], param_info,
- GJS_ARGUMENT_LIST_ELEMENT, transfer,
- &arg))
+ if (!gjs_value_from_gi_argument(cx, elems[i], param_info,
+ GJS_ARGUMENT_LIST_ELEMENT, transfer,
+ &arg))
return false;
}
- JS::RootedObject obj(cx, JS::NewArrayObject(cx, elems));
+ JSObject* obj = JS::NewArrayObject(cx, elems);
if (!obj)
return false;
@@ -1974,9 +1959,9 @@
for (size_t i = 0; i < length; i++) {
gjs_arg_set<T, TAG>(arg, *(static_cast<T*>(array) + i));
- if (!gjs_value_from_g_argument(cx, elems[i], param_info,
- GJS_ARGUMENT_ARRAY_ELEMENT,
- transfer, arg))
+ if (!gjs_value_from_gi_argument(cx, elems[i], param_info,
+ GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
+ arg))
return false;
}
@@ -1987,7 +1972,6 @@
static bool gjs_array_from_carray_internal(
JSContext* context, JS::MutableHandleValue value_p, GIArrayType array_type,
GITypeInfo* param_info, GITransfer transfer, guint length, void* array) {
- GArgument arg;
GITypeTag element_type;
guint i;
@@ -1995,7 +1979,7 @@
/* Special case array(guint8) */
if (element_type == GI_TYPE_TAG_UINT8) {
- JSObject* obj = gjs_byte_array_from_data(context, length, array);
+ JSObject* obj = gjs_byte_array_from_data_copy(context, length, array);
if (!obj)
return false;
value_p.setObject(*obj);
@@ -2021,6 +2005,7 @@
return false;
}
+ GIArgument arg;
switch (element_type) {
/* Special cases handled above */
case GI_TYPE_TAG_UINT8:
@@ -2079,12 +2064,11 @@
case GI_TYPE_TAG_INTERFACE: {
GjsAutoBaseInfo interface_info =
g_type_info_get_interface(param_info);
- GIInfoType info_type = g_base_info_get_type (interface_info);
+ GIInfoType info_type = interface_info.type();
if (array_type != GI_ARRAY_TYPE_PTR_ARRAY &&
(info_type == GI_INFO_TYPE_STRUCT ||
- info_type == GI_INFO_TYPE_UNION ||
- info_type == GI_INFO_TYPE_VALUE) &&
+ info_type == GI_INFO_TYPE_UNION) &&
!g_type_info_is_pointer(param_info)) {
size_t struct_size;
@@ -2097,7 +2081,7 @@
gjs_arg_set(&arg,
static_cast<char*>(array) + (struct_size * i));
- if (!gjs_value_from_g_argument(
+ if (!gjs_value_from_gi_argument(
context, elems[i], param_info,
GJS_ARGUMENT_ARRAY_ELEMENT, transfer, &arg))
return false;
@@ -2125,7 +2109,7 @@
return false;
}
- JS::RootedObject obj(context, JS::NewArrayObject(context, elems));
+ JSObject* obj = JS::NewArrayObject(context, elems);
if (!obj)
return false;
@@ -2168,7 +2152,7 @@
JS::MutableHandleValue value_p,
GIArrayType array_type,
GITypeInfo* param_info,
- GITransfer transfer, GArgument* arg) {
+ GITransfer transfer, GIArgument* arg) {
GArray *array;
GPtrArray *ptr_array;
gpointer data = NULL;
@@ -2192,7 +2176,7 @@
data = ptr_array->pdata;
length = ptr_array->len;
break;
- case GI_ARRAY_TYPE_C: /* already checked in gjs_value_from_g_argument() */
+ case GI_ARRAY_TYPE_C: // already checked in gjs_value_from_gi_argument()
default:
g_assert_not_reached();
}
@@ -2227,6 +2211,8 @@
length = ptr_array->len;
} else {
g_assert_not_reached();
+ gjs_throw(cx, "%s is not an array type", g_type_name(value_gtype));
+ return false;
}
return gjs_array_from_carray_internal(cx, value_p, array_type, param_info,
@@ -2261,9 +2247,9 @@
return false;
}
- if (!gjs_value_from_g_argument(cx, elems[i], param_info,
- GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
- arg))
+ if (!gjs_value_from_gi_argument(cx, elems[i], param_info,
+ GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
+ arg))
return false;
}
@@ -2274,7 +2260,6 @@
static bool gjs_array_from_zero_terminated_c_array(
JSContext* context, JS::MutableHandleValue value_p, GITypeInfo* param_info,
GITransfer transfer, void* c_array) {
- GArgument arg;
GITypeTag element_type;
element_type = g_type_info_get_tag(param_info);
@@ -2282,7 +2267,7 @@
/* Special case array(guint8) */
if (element_type == GI_TYPE_TAG_UINT8) {
size_t len = strlen(static_cast<char*>(c_array));
- JSObject* obj = gjs_byte_array_from_data(context, len, c_array);
+ JSObject* obj = gjs_byte_array_from_data_copy(context, len, c_array);
if (!obj)
return false;
value_p.setObject(*obj);
@@ -2295,6 +2280,7 @@
JS::RootedValueVector elems(context);
+ GIArgument arg;
switch (element_type) {
/* Special cases handled above. */
case GI_TYPE_TAG_UINT8:
@@ -2350,14 +2336,21 @@
g_type_info_get_interface(param_info);
if (!g_type_info_is_pointer(param_info) &&
- is_gvalue(interface_info,
- g_base_info_get_type(interface_info))) {
+ is_gvalue(interface_info)) {
if (!fill_vector_from_zero_terminated_carray<GValue>(
context, elems, param_info, &arg, c_array))
return false;
break;
}
+ if (!g_type_info_is_pointer(param_info)) {
+ gjs_throw(context,
+ "Flat C array of %s.%s not supported (see "
+ "https://gitlab.gnome.org/GNOME/gjs/-/issues/603)",
+ interface_info.ns(), interface_info.name());
+ return false;
+ }
+
[[fallthrough]];
}
case GI_TYPE_TAG_GTYPE:
@@ -2383,7 +2376,7 @@
return false;
}
- JS::RootedObject obj(context, JS::NewArrayObject(context, elems));
+ JSObject* obj = JS::NewArrayObject(context, elems);
if (!obj)
return false;
@@ -2397,7 +2390,6 @@
GITypeInfo* val_param_info, GITransfer transfer,
GHashTable* hash) {
GHashTableIter iter;
- GArgument keyarg, valarg;
// a NULL hash table becomes a null JS value
if (hash==NULL) {
@@ -2417,13 +2409,14 @@
g_hash_table_iter_init(&iter, hash);
void* key_pointer;
void* val_pointer;
+ GIArgument keyarg, valarg;
while (g_hash_table_iter_next(&iter, &key_pointer, &val_pointer)) {
g_type_info_argument_from_hash_pointer(key_param_info, key_pointer,
&keyarg);
- if (!gjs_value_from_g_argument(context, &keyjs, key_param_info,
- GJS_ARGUMENT_HASH_ELEMENT,
- transfer, &keyarg))
- return false;
+ if (!gjs_value_from_gi_argument(context, &keyjs, key_param_info,
+ GJS_ARGUMENT_HASH_ELEMENT, transfer,
+ &keyarg))
+ return false;
keystr = JS::ToString(context, keyjs);
if (!keystr)
@@ -2435,9 +2428,9 @@
g_type_info_argument_from_hash_pointer(val_param_info, val_pointer,
&valarg);
- if (!gjs_value_from_g_argument(context, &valjs, val_param_info,
- GJS_ARGUMENT_HASH_ELEMENT,
- transfer, &valarg))
+ if (!gjs_value_from_gi_argument(context, &valjs, val_param_info,
+ GJS_ARGUMENT_HASH_ELEMENT, transfer,
+ &valarg))
return false;
if (!JS_DefineProperty(context, obj, keyutf8.get(), valjs,
@@ -2448,21 +2441,17 @@
return true;
}
-bool gjs_value_from_g_argument(JSContext* context,
- JS::MutableHandleValue value_p,
- GITypeInfo* type_info,
- GjsArgumentType argument_type,
- GITransfer transfer, GArgument* arg) {
- GITypeTag type_tag;
-
- type_tag = g_type_info_get_tag( (GITypeInfo*) type_info);
+bool gjs_value_from_gi_argument(JSContext* context,
+ JS::MutableHandleValue value_p,
+ GITypeInfo* type_info,
+ GjsArgumentType argument_type,
+ GITransfer transfer, GIArgument* arg) {
+ GITypeTag type_tag = g_type_info_get_tag(type_info);
gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
- "Converting GArgument %s to JS::Value",
+ "Converting GIArgument %s to JS::Value",
g_type_tag_to_string(type_tag));
- value_p.setNull();
-
switch (type_tag) {
case GI_TYPE_TAG_VOID:
// If the argument is a pointer, convert to null to match our
@@ -2510,20 +2499,22 @@
break;
case GI_TYPE_TAG_FLOAT:
- value_p.setNumber(gjs_arg_get<float>(arg));
+ value_p.setNumber(JS::CanonicalizeNaN(gjs_arg_get<float>(arg)));
break;
case GI_TYPE_TAG_DOUBLE:
- value_p.setNumber(gjs_arg_get<double>(arg));
+ value_p.setNumber(JS::CanonicalizeNaN(gjs_arg_get<double>(arg)));
break;
case GI_TYPE_TAG_GTYPE:
{
GType gtype = gjs_arg_get<GType, GI_TYPE_TAG_GTYPE>(arg);
- if (gtype == 0)
- return true; /* value_p is set to JS null */
+ if (gtype == 0) {
+ value_p.setNull();
+ return true;
+ }
- JS::RootedObject obj(context, gjs_gtype_create_gtype_wrapper(context, gtype));
+ JSObject* obj = gjs_gtype_create_gtype_wrapper(context, gtype);
if (!obj)
return false;
@@ -2553,10 +2544,10 @@
case GI_TYPE_TAG_FILENAME:
case GI_TYPE_TAG_UTF8: {
const char* str = gjs_arg_get<const char*>(arg);
- // For nullptr we'll return JS::NullValue(), which is already set
- // in *value_p
- if (!str)
+ if (!str) {
+ value_p.setNull();
return true;
+ }
if (type_tag == GI_TYPE_TAG_FILENAME)
return gjs_string_from_filename(context, str, -1, value_p);
@@ -2566,8 +2557,10 @@
case GI_TYPE_TAG_ERROR: {
GError* ptr = gjs_arg_get<GError*>(arg);
- if (!ptr)
+ if (!ptr) {
+ value_p.setNull();
return true;
+ }
JSObject* obj = ErrorInstance::object_for_c_ptr(context, ptr);
if (!obj)
@@ -2579,14 +2572,11 @@
case GI_TYPE_TAG_INTERFACE:
{
- GIInfoType interface_type;
- GType gtype;
-
GjsAutoBaseInfo interface_info =
g_type_info_get_interface(type_info);
g_assert(interface_info);
- interface_type = g_base_info_get_type(interface_info);
+ GIInfoType interface_type = interface_info.type();
if (interface_type == GI_INFO_TYPE_UNRESOLVED) {
gjs_throw(context,
@@ -2614,14 +2604,17 @@
interface_info,
gjs_arg_get<int, GI_TYPE_TAG_INTERFACE>(arg));
- gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info);
+ GType gtype = g_registered_type_info_get_g_type(
+ interface_info.as<GIRegisteredTypeInfo>());
if (gtype != G_TYPE_NONE) {
/* check make sure 32 bit flag */
- if (static_cast<uint32_t>(value_int64) != value_int64) { /* Not a guint32 */
+ if (static_cast<uint32_t>(value_int64) != value_int64) {
+ // Not a 32-bit integer
gjs_throw(context,
- "0x%" G_GINT64_MODIFIER "x is not a valid value for flags %s",
- value_int64, g_type_name(gtype));
+ "0x%" PRIx64
+ " is not a valid value for flags %s",
+ value_int64, g_type_name(gtype));
return false;
}
@@ -2635,8 +2628,8 @@
}
if (interface_type == GI_INFO_TYPE_STRUCT &&
- g_struct_info_is_foreign((GIStructInfo*)interface_info)) {
- return gjs_struct_foreign_convert_from_g_argument(
+ g_struct_info_is_foreign(interface_info.as<GIStructInfo>())) {
+ return gjs_struct_foreign_convert_from_gi_argument(
context, value_p, interface_info, arg);
}
@@ -2647,12 +2640,13 @@
}
if (interface_type == GI_INFO_TYPE_STRUCT &&
- g_struct_info_is_gtype_struct((GIStructInfo*)interface_info)) {
+ g_struct_info_is_gtype_struct(
+ interface_info.as<GIStructInfo>())) {
/* XXX: here we make the implicit assumption that GTypeClass is the same
as GTypeInterface. This is true for the GType field, which is what we
use, but not for the rest of the structure!
*/
- gtype = G_TYPE_FROM_CLASS(gjs_arg_get<GTypeClass*>(arg));
+ GType gtype = G_TYPE_FROM_CLASS(gjs_arg_get<GTypeClass*>(arg));
if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
return gjs_lookup_interface_constructor(context, gtype,
@@ -2661,7 +2655,8 @@
return gjs_lookup_object_constructor(context, gtype, value_p);
}
- gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info);
+ GType gtype = g_registered_type_info_get_g_type(
+ interface_info.as<GIRegisteredTypeInfo>());
if (G_TYPE_IS_INSTANTIATABLE(gtype) ||
G_TYPE_IS_INTERFACE(gtype))
gtype = G_TYPE_FROM_INSTANCE(gjs_arg_get<GTypeInstance*>(arg));
@@ -2691,11 +2686,14 @@
g_struct_info_find_method(interface_info, "name");
GIArgument atom_name_ret;
- g_function_info_invoke(atom_name_fun,
- arg, 1,
- nullptr, 0,
- &atom_name_ret,
- nullptr);
+ GjsAutoError error = nullptr;
+ if (!g_function_info_invoke(atom_name_fun, arg, 1, nullptr,
+ 0, &atom_name_ret,
+ error.out())) {
+ gjs_throw(context, "Failed to call gdk_atom_name(): %s",
+ error->message);
+ return false;
+ }
GjsAutoChar name = gjs_arg_get<char*>(&atom_name_ret);
if (g_strcmp0("NONE", name) == 0) {
@@ -2738,7 +2736,7 @@
if (interface_type == GI_INFO_TYPE_UNION) {
JSObject* obj = UnionInstance::new_for_c_union(
- context, static_cast<GIUnionInfo*>(interface_info),
+ context, interface_info.as<GIUnionInfo>(),
gjs_arg_get<void*>(arg));
if (!obj)
return false;
@@ -2775,7 +2773,9 @@
}
if (gtype == G_TYPE_NONE) {
- gjs_throw(context, "Unexpected unregistered type packing GArgument into JS::Value");
+ gjs_throw(context,
+ "Unexpected unregistered type packing GIArgument "
+ "into JS::Value");
return false;
}
@@ -2789,17 +2789,20 @@
}
gjs_throw(context,
- "Unhandled GType %s packing GArgument into JS::Value",
+ "Unhandled GType %s packing GIArgument into JS::Value",
g_type_name(gtype));
return false;
}
case GI_TYPE_TAG_ARRAY:
if (!gjs_arg_get<void*>(arg)) {
- /* OK, but no conversion to do */
- } else if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
+ value_p.setNull();
+ return true;
+ }
+
+ if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
if (g_type_info_is_zero_terminated(type_info)) {
- GjsAutoBaseInfo param_info =
+ GjsAutoTypeInfo param_info =
g_type_info_get_param_type(type_info, 0);
g_assert(param_info != nullptr);
@@ -2861,7 +2864,7 @@
break;
default:
- g_warning("Unhandled type %s converting GArgument to JavaScript",
+ g_warning("Unhandled type %s converting GIArgument to JavaScript",
g_type_tag_to_string(type_tag));
return false;
}
@@ -2880,14 +2883,14 @@
static gboolean
gjs_ghr_helper(gpointer key, gpointer val, gpointer user_data) {
GHR_closure *c = (GHR_closure *) user_data;
- GArgument key_arg, val_arg;
+ GIArgument key_arg, val_arg;
gjs_arg_set(&key_arg, key);
gjs_arg_set(&val_arg, val);
if (!gjs_g_arg_release_internal(c->context, c->transfer, c->key_param_info,
g_type_info_get_tag(c->key_param_info),
GJS_ARGUMENT_HASH_ELEMENT, c->flags,
&key_arg))
- c->failed = true;
+ c->failed = true;
GITypeTag val_type = g_type_info_get_tag(c->val_param_info);
@@ -2955,19 +2958,16 @@
case GI_TYPE_TAG_INTERFACE:
{
- GIInfoType interface_type;
- GType gtype;
-
GjsAutoBaseInfo interface_info =
g_type_info_get_interface(type_info);
g_assert(interface_info);
- interface_type = g_base_info_get_type(interface_info);
+ GIInfoType interface_type = interface_info.type();
if (interface_type == GI_INFO_TYPE_STRUCT &&
- g_struct_info_is_foreign((GIStructInfo*)interface_info))
- return gjs_struct_foreign_release_g_argument(context,
- transfer, interface_info, arg);
+ g_struct_info_is_foreign(interface_info.as<GIStructInfo>()))
+ return gjs_struct_foreign_release_gi_argument(
+ context, transfer, interface_info, arg);
if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS)
return true;
@@ -2976,7 +2976,8 @@
if (!gjs_arg_get<void*>(arg))
return true;
- gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info);
+ GType gtype = g_registered_type_info_get_g_type(
+ interface_info.as<GIRegisteredTypeInfo>());
if (G_TYPE_IS_INSTANTIATABLE(gtype) ||
G_TYPE_IS_INTERFACE(gtype))
gtype = G_TYPE_FROM_INSTANCE(gjs_arg_get<GTypeInstance*>(arg));
@@ -2984,11 +2985,10 @@
gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
"gtype of INTERFACE is %s", g_type_name(gtype));
- /* In gjs_value_from_g_argument we handle Struct/Union types without a
- * registered GType, but here we are specifically handling a GArgument that
- * *owns* its value, and that is non-sensical for such types, so we
- * don't have to worry about it.
- */
+ // In gjs_value_from_gi_argument we handle Struct/Union types
+ // without a registered GType, but here we are specifically handling
+ // a GIArgument that *owns* its value, and that is nonsensical for
+ // such types, so we don't have to worry about it.
if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
if (!is_transfer_in_nothing(transfer, flags))
@@ -3016,7 +3016,9 @@
g_variant_unref);
} else if (gtype == G_TYPE_NONE) {
if (!is_transfer_in_nothing(transfer, flags)) {
- gjs_throw(context, "Don't know how to release GArgument: not an object or boxed type");
+ gjs_throw(context,
+ "Don't know how to release GIArgument: not an "
+ "object or boxed type");
return false;
}
} else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
@@ -3026,7 +3028,7 @@
priv->call_unref_function(gjs_arg_steal<void*>(arg));
}
} else {
- gjs_throw(context, "Unhandled GType %s releasing GArgument",
+ gjs_throw(context, "Unhandled GType %s releasing GIArgument",
g_type_name(gtype));
return false;
}
@@ -3077,7 +3079,7 @@
if (!g_type_info_is_pointer(param_info)) {
GjsAutoBaseInfo interface_info =
g_type_info_get_interface(param_info);
- GIInfoType info_type = g_base_info_get_type(interface_info);
+ GIInfoType info_type = interface_info.type();
if (info_type == GI_INFO_TYPE_STRUCT ||
info_type == GI_INFO_TYPE_UNION) {
g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
@@ -3097,11 +3099,13 @@
element_transfer = GI_TRANSFER_NOTHING;
if (g_type_info_is_zero_terminated(type_info)) {
- return gjs_g_argument_release_array_internal<true>(
+ return gjs_gi_argument_release_array_internal<
+ ArrayReleaseType::ZERO_TERMINATED>(
context, element_transfer,
flags | GjsArgumentFlags::ARG_OUT, param_info, 0, arg);
} else {
- return gjs_g_argument_release_array_internal<false>(
+ return gjs_gi_argument_release_array_internal<
+ ArrayReleaseType::EXPLICIT_LENGTH>(
context, element_transfer,
flags | GjsArgumentFlags::ARG_OUT, param_info,
g_type_info_get_array_fixed_size(type_info), arg);
@@ -3155,7 +3159,7 @@
guint i;
for (i = 0; i < array->len; i++) {
- GArgument arg_iter;
+ GIArgument arg_iter;
gjs_arg_set(&arg_iter,
g_array_index(array, gpointer, i));
@@ -3190,11 +3194,11 @@
guint i;
for (i = 0; i < array->len; i++) {
- GArgument arg_iter;
+ GIArgument arg_iter;
gjs_arg_set(&arg_iter, g_ptr_array_index(array, i));
- if (!gjs_g_argument_release(context, transfer, param_info,
- flags, &arg_iter))
+ if (!gjs_gi_argument_release(context, transfer, param_info,
+ flags, &arg_iter))
return false;
}
}
@@ -3236,7 +3240,7 @@
break;
default:
- g_warning("Unhandled type %s releasing GArgument",
+ g_warning("Unhandled type %s releasing GIArgument",
g_type_tag_to_string(type_tag));
return false;
}
@@ -3244,30 +3248,26 @@
return true;
}
-bool gjs_g_argument_release(JSContext* cx, GITransfer transfer,
- GITypeInfo* type_info, GjsArgumentFlags flags,
- GIArgument* arg) {
- GITypeTag type_tag;
-
+bool gjs_gi_argument_release(JSContext* cx, GITransfer transfer,
+ GITypeInfo* type_info, GjsArgumentFlags flags,
+ GIArgument* arg) {
if (transfer == GI_TRANSFER_NOTHING &&
!is_transfer_in_nothing(transfer, flags))
return true;
- type_tag = g_type_info_get_tag( (GITypeInfo*) type_info);
+ GITypeTag type_tag = g_type_info_get_tag(type_info);
gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
- "Releasing GArgument %s out param or return value",
+ "Releasing GIArgument %s out param or return value",
g_type_tag_to_string(type_tag));
return gjs_g_arg_release_internal(cx, transfer, type_info, type_tag,
GJS_ARGUMENT_ARGUMENT, flags, arg);
}
-bool gjs_g_argument_release_in_arg(JSContext* cx, GITransfer transfer,
- GITypeInfo* type_info,
- GjsArgumentFlags flags, GIArgument* arg) {
- GITypeTag type_tag;
-
+bool gjs_gi_argument_release_in_arg(JSContext* cx, GITransfer transfer,
+ GITypeInfo* type_info,
+ GjsArgumentFlags flags, GIArgument* arg) {
/* GI_TRANSFER_EVERYTHING: we don't own the argument anymore.
* GI_TRANSFER_CONTAINER:
* - non-containers: treated as GI_TRANSFER_EVERYTHING
@@ -3277,10 +3277,9 @@
if (transfer != GI_TRANSFER_NOTHING)
return true;
- type_tag = g_type_info_get_tag( (GITypeInfo*) type_info);
+ GITypeTag type_tag = g_type_info_get_tag(type_info);
- gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
- "Releasing GArgument %s in param",
+ gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GIArgument %s in param",
g_type_tag_to_string(type_tag));
if (type_needs_release (type_info, type_tag))
@@ -3290,36 +3289,72 @@
return true;
}
-bool gjs_g_argument_release_in_array(JSContext* context, GITransfer transfer,
- GITypeInfo* type_info, unsigned length,
- GIArgument* arg) {
+bool gjs_gi_argument_release_in_array(JSContext* context, GITransfer transfer,
+ GITypeInfo* type_info, unsigned length,
+ GIArgument* arg) {
if (transfer != GI_TRANSFER_NOTHING)
return true;
gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
- "Releasing GArgument array in param");
+ "Releasing GIArgument array in param");
GjsAutoTypeInfo param_type = g_type_info_get_param_type(type_info, 0);
- return gjs_g_argument_release_array_internal(
- context, GI_TRANSFER_EVERYTHING, GjsArgumentFlags::ARG_IN, param_type,
- length, arg);
+ return gjs_gi_argument_release_array_internal<
+ ArrayReleaseType::EXPLICIT_LENGTH>(context, GI_TRANSFER_EVERYTHING,
+ GjsArgumentFlags::ARG_IN, param_type,
+ length, arg);
}
-bool gjs_g_argument_release_out_array(JSContext* context, GITransfer transfer,
- GITypeInfo* type_info, unsigned length,
- GIArgument* arg) {
+bool gjs_gi_argument_release_in_array(JSContext* context, GITransfer transfer,
+ GITypeInfo* type_info, GIArgument* arg) {
+ if (transfer != GI_TRANSFER_NOTHING)
+ return true;
+
+ gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
+ "Releasing GIArgument array in param");
+
+ GjsAutoTypeInfo param_type = g_type_info_get_param_type(type_info, 0);
+ return gjs_gi_argument_release_array_internal<
+ ArrayReleaseType::ZERO_TERMINATED>(context, GI_TRANSFER_EVERYTHING,
+ GjsArgumentFlags::ARG_IN, param_type,
+ 0, arg);
+}
+
+bool gjs_gi_argument_release_out_array(JSContext* context, GITransfer transfer,
+ GITypeInfo* type_info, unsigned length,
+ GIArgument* arg) {
+ if (transfer == GI_TRANSFER_NOTHING)
+ return true;
+
+ gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
+ "Releasing GIArgument array out param");
+
+ GITransfer element_transfer = transfer == GI_TRANSFER_CONTAINER
+ ? GI_TRANSFER_NOTHING
+ : GI_TRANSFER_EVERYTHING;
+
+ GjsAutoTypeInfo param_type = g_type_info_get_param_type(type_info, 0);
+ return gjs_gi_argument_release_array_internal<
+ ArrayReleaseType::EXPLICIT_LENGTH>(context, element_transfer,
+ GjsArgumentFlags::ARG_OUT,
+ param_type, length, arg);
+}
+
+bool gjs_gi_argument_release_out_array(JSContext* context, GITransfer transfer,
+ GITypeInfo* type_info, GIArgument* arg) {
if (transfer == GI_TRANSFER_NOTHING)
return true;
gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
- "Releasing GArgument array out param");
+ "Releasing GIArgument array out param");
GITransfer element_transfer = transfer == GI_TRANSFER_CONTAINER
? GI_TRANSFER_NOTHING
: GI_TRANSFER_EVERYTHING;
GjsAutoTypeInfo param_type = g_type_info_get_param_type(type_info, 0);
- return gjs_g_argument_release_array_internal(context, element_transfer,
- GjsArgumentFlags::ARG_OUT,
- param_type, length, arg);
+ return gjs_gi_argument_release_array_internal<
+ ArrayReleaseType::ZERO_TERMINATED>(context, element_transfer,
+ GjsArgumentFlags::ARG_OUT,
+ param_type, 0, arg);
}
diff -ruN cjs-6.2.0-orig/gi/arg.h cjs-6.2.0/gi/arg.h
--- cjs-6.2.0-orig/gi/arg.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/arg.h 2024-10-14 18:41:39.000000000 +0200
@@ -65,34 +65,30 @@
void gjs_gi_argument_init_default(GITypeInfo* type_info, GIArgument* arg);
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_value_to_g_argument(JSContext* cx, JS::HandleValue value,
- GITypeInfo* type_info, const char* arg_name,
- GjsArgumentType argument_type, GITransfer transfer,
- GjsArgumentFlags flags, GIArgument* arg);
+bool gjs_value_to_gi_argument(JSContext*, JS::HandleValue, GITypeInfo*,
+ const char* arg_name, GjsArgumentType, GITransfer,
+ GjsArgumentFlags, GIArgument*);
GJS_JSAPI_RETURN_CONVENTION
-bool inline gjs_value_to_g_argument(JSContext* cx, JS::HandleValue value,
- GITypeInfo* type_info,
- GjsArgumentType argument_type,
- GITransfer transfer, GIArgument* arg) {
- return gjs_value_to_g_argument(cx, value, type_info, nullptr /* arg_name */,
- argument_type, transfer,
- GjsArgumentFlags::NONE, arg);
+bool inline gjs_value_to_gi_argument(JSContext* cx, JS::HandleValue value,
+ GITypeInfo* type_info,
+ GjsArgumentType argument_type,
+ GITransfer transfer, GIArgument* arg) {
+ return gjs_value_to_gi_argument(cx, value, type_info,
+ nullptr /* arg_name */, argument_type,
+ transfer, GjsArgumentFlags::NONE, arg);
}
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_value_from_g_argument(JSContext* context,
- JS::MutableHandleValue value_p,
- GITypeInfo* type_info,
- GjsArgumentType argument_type,
- GITransfer transfer, GIArgument* arg);
+bool gjs_value_from_gi_argument(JSContext*, JS::MutableHandleValue, GITypeInfo*,
+ GjsArgumentType, GITransfer, GIArgument*);
GJS_JSAPI_RETURN_CONVENTION
-inline bool gjs_value_from_g_argument(JSContext* cx,
- JS::MutableHandleValue value_p,
- GITypeInfo* type_info, GIArgument* arg,
- bool copy_structs) {
- return gjs_value_from_g_argument(
+inline bool gjs_value_from_gi_argument(JSContext* cx,
+ JS::MutableHandleValue value_p,
+ GITypeInfo* type_info, GIArgument* arg,
+ bool copy_structs) {
+ return gjs_value_from_gi_argument(
cx, value_p, type_info, GJS_ARGUMENT_ARGUMENT,
copy_structs ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING, arg);
}
@@ -113,31 +109,35 @@
}
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_g_argument_release(JSContext*, GITransfer, GITypeInfo*,
- GjsArgumentFlags, GIArgument*);
+bool gjs_gi_argument_release(JSContext*, GITransfer, GITypeInfo*,
+ GjsArgumentFlags, GIArgument*);
GJS_JSAPI_RETURN_CONVENTION
-inline bool gjs_g_argument_release(JSContext* cx, GITransfer transfer,
- GITypeInfo* type_info, GIArgument* arg) {
- return gjs_g_argument_release(cx, transfer, type_info,
- GjsArgumentFlags::NONE, arg);
+inline bool gjs_gi_argument_release(JSContext* cx, GITransfer transfer,
+ GITypeInfo* type_info, GIArgument* arg) {
+ return gjs_gi_argument_release(cx, transfer, type_info,
+ GjsArgumentFlags::NONE, arg);
}
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_g_argument_release_out_array(JSContext* cx, GITransfer transfer,
- GITypeInfo* type_info, unsigned length,
- GIArgument* arg);
-GJS_JSAPI_RETURN_CONVENTION
-bool gjs_g_argument_release_in_array(JSContext* cx, GITransfer transfer,
- GITypeInfo* type_info, unsigned length,
- GIArgument* arg);
+bool gjs_gi_argument_release_out_array(JSContext*, GITransfer, GITypeInfo*,
+ unsigned length, GIArgument*);
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_g_argument_release_in_arg(JSContext*, GITransfer, GITypeInfo*,
- GjsArgumentFlags, GIArgument*);
+bool gjs_gi_argument_release_out_array(JSContext*, GITransfer, GITypeInfo*,
+ GIArgument*);
GJS_JSAPI_RETURN_CONVENTION
-inline bool gjs_g_argument_release_in_arg(JSContext* cx, GITransfer transfer,
- GITypeInfo* type_info,
- GIArgument* arg) {
- return gjs_g_argument_release_in_arg(cx, transfer, type_info,
- GjsArgumentFlags::ARG_IN, arg);
+bool gjs_gi_argument_release_in_array(JSContext*, GITransfer, GITypeInfo*,
+ unsigned length, GIArgument*);
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_gi_argument_release_in_array(JSContext*, GITransfer, GITypeInfo*,
+ GIArgument*);
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_gi_argument_release_in_arg(JSContext*, GITransfer, GITypeInfo*,
+ GjsArgumentFlags, GIArgument*);
+GJS_JSAPI_RETURN_CONVENTION
+inline bool gjs_gi_argument_release_in_arg(JSContext* cx, GITransfer transfer,
+ GITypeInfo* type_info,
+ GIArgument* arg) {
+ return gjs_gi_argument_release_in_arg(cx, transfer, type_info,
+ GjsArgumentFlags::ARG_IN, arg);
}
GJS_JSAPI_RETURN_CONVENTION
diff -ruN cjs-6.2.0-orig/gi/boxed.cpp cjs-6.2.0/gi/boxed.cpp
--- cjs-6.2.0-orig/gi/boxed.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/boxed.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -214,7 +214,7 @@
}
/* Initialize a newly created Boxed from an object that is a "hash" of
- * properties to set as fieds of the object. We don't require that every field
+ * properties to set as fields of the object. We don't require that every field
* of the object be set.
*/
bool BoxedInstance::init_from_props(JSContext* context, JS::Value props_value) {
@@ -480,10 +480,10 @@
*/
bool BoxedInstance::get_nested_interface_object(
JSContext* context, JSObject* parent_obj, GIFieldInfo* field_info,
- GIBaseInfo* interface_info, JS::MutableHandleValue value) const {
+ GIStructInfo* struct_info, JS::MutableHandleValue value) const {
int offset;
- if (!struct_is_simple(reinterpret_cast<GIStructInfo*>(interface_info))) {
+ if (!struct_is_simple(struct_info)) {
gjs_throw(context, "Reading field %s.%s is not supported", name(),
g_base_info_get_name(field_info));
@@ -492,8 +492,8 @@
offset = g_field_info_get_offset (field_info);
- JS::RootedObject obj(context, gjs_new_object_with_generic_prototype(
- context, interface_info));
+ JS::RootedObject obj{
+ context, gjs_new_object_with_generic_prototype(context, struct_info)};
if (!obj)
return false;
@@ -567,26 +567,26 @@
get_field_info(cx, length_field_ix);
if (!length_field_info) {
gjs_throw(cx, "Reading field %s.%s is not supported", name(),
- g_base_info_get_name(length_field_info));
+ g_base_info_get_name(field_info));
return false;
}
GIArgument length_arg;
if (!g_field_info_get_field(length_field_info, m_ptr, &length_arg)) {
gjs_throw(cx, "Reading field %s.%s is not supported", name(),
- g_base_info_get_name(length_field_info));
+ length_field_info.name());
return false;
}
GjsAutoTypeInfo length_type_info =
g_field_info_get_type(length_field_info);
- size_t length = gjs_g_argument_get_array_length(
+ size_t length = gjs_gi_argument_get_array_length(
g_type_info_get_tag(length_type_info), &length_arg);
return gjs_value_from_explicit_array(cx, rval, type_info, &arg, length);
}
- return gjs_value_from_g_argument(cx, rval, type_info, GJS_ARGUMENT_FIELD,
- GI_TRANSFER_EVERYTHING, &arg);
+ return gjs_value_from_gi_argument(cx, rval, type_info, GJS_ARGUMENT_FIELD,
+ GI_TRANSFER_EVERYTHING, &arg);
}
/*
@@ -603,19 +603,19 @@
*/
bool BoxedInstance::set_nested_interface_object(JSContext* context,
GIFieldInfo* field_info,
- GIBaseInfo* interface_info,
+ GIStructInfo* struct_info,
JS::HandleValue value) {
int offset;
- if (!struct_is_simple(reinterpret_cast<GIStructInfo*>(interface_info))) {
+ if (!struct_is_simple(struct_info)) {
gjs_throw(context, "Writing field %s.%s is not supported", name(),
g_base_info_get_name(field_info));
return false;
}
- JS::RootedObject proto(
- context, gjs_lookup_generic_prototype(context, interface_info));
+ JS::RootedObject proto{context,
+ gjs_lookup_generic_prototype(context, struct_info)};
if (!proto)
return false;
@@ -647,7 +647,6 @@
bool BoxedInstance::field_setter_impl(JSContext* context,
GIFieldInfo* field_info,
JS::HandleValue value) {
- GArgument arg;
GjsAutoTypeInfo type_info = g_field_info_get_type(field_info);
if (!g_type_info_is_pointer (type_info) &&
@@ -661,10 +660,11 @@
}
}
- if (!gjs_value_to_g_argument(context, value, type_info,
- g_base_info_get_name(field_info),
- GJS_ARGUMENT_FIELD, GI_TRANSFER_NOTHING,
- GjsArgumentFlags::MAY_BE_NULL, &arg))
+ GIArgument arg;
+ if (!gjs_value_to_gi_argument(context, value, type_info,
+ g_base_info_get_name(field_info),
+ GJS_ARGUMENT_FIELD, GI_TRANSFER_NOTHING,
+ GjsArgumentFlags::MAY_BE_NULL, &arg))
return false;
bool success = true;
@@ -675,7 +675,7 @@
}
JS::AutoSaveExceptionState saved_exc(context);
- if (!gjs_g_argument_release(context, GI_TRANSFER_NOTHING, type_info, &arg))
+ if (!gjs_gi_argument_release(context, GI_TRANSFER_NOTHING, type_info, &arg))
gjs_log_exception(context);
saved_exc.restore();
@@ -718,28 +718,41 @@
int n_fields = g_struct_info_get_n_fields(info());
int i;
- /* We define all fields as read/write so that the user gets an
- * error message. If we omitted fields or defined them read-only
- * we'd:
- *
- * - Store a new property for a non-accessible field
- * - Silently do nothing when writing a read-only field
- *
- * Which is pretty confusing if the only reason a field isn't
- * writable is language binding or memory-management restrictions.
- *
- * We just go ahead and define the fields immediately for the
- * class; doing it lazily in boxed_resolve() would be possible
- * as well if doing it ahead of time caused to much start-up
- * memory overhead.
- */
+ // We define all fields as read/write so that the user gets an error
+ // message. If we omitted fields or defined them read-only we'd:
+ //
+ // - Store a new property for a non-accessible field
+ // - Silently do nothing when writing a read-only field
+ //
+ // Which is pretty confusing if the only reason a field isn't writable is
+ // language binding or memory-management restrictions.
+ //
+ // We just go ahead and define the fields immediately for the class; doing
+ // it lazily in boxed_resolve() would be possible as well if doing it ahead
+ // of time caused too much start-up memory overhead.
+ //
+ // At this point methods have already been defined on the prototype, so we
+ // may get name conflicts which we need to check for.
for (i = 0; i < n_fields; i++) {
GjsAutoFieldInfo field = g_struct_info_get_field(info(), i);
JS::RootedValue private_id(cx, JS::PrivateUint32Value(i));
- if (!gjs_define_property_dynamic(cx, proto, field.name(), "boxed_field",
- &BoxedBase::field_getter,
- &BoxedBase::field_setter, private_id,
- GJS_MODULE_PROP_FLAGS))
+ JS::RootedId id{cx, gjs_intern_string_to_id(cx, field.name())};
+
+ bool already_defined;
+ if (!JS_HasOwnPropertyById(cx, proto, id, &already_defined))
+ return false;
+ if (already_defined) {
+ gjs_debug(GJS_DEBUG_GBOXED,
+ "Field %s.%s.%s overlaps with method of the same name; "
+ "skipping",
+ ns(), name(), field.name());
+ continue;
+ }
+
+ if (!gjs_define_property_dynamic(
+ cx, proto, field.name(), id, "boxed_field",
+ &BoxedBase::field_getter, &BoxedBase::field_setter, private_id,
+ GJS_MODULE_PROP_FLAGS))
return false;
}
@@ -768,7 +781,7 @@
&BoxedBase::trace
};
-/* We allocate 1 reserved slot; this is typically unused, but if the
+/* We allocate 1 extra reserved slot; this is typically unused, but if the
* boxed is for a nested structure inside a parent structure, the
* reserved slot is used to hold onto the parent Javascript object and
* make sure it doesn't get freed.
@@ -787,7 +800,7 @@
if (g_type_info_is_pointer(type_info)) {
if (g_type_info_get_tag(type_info) == GI_TYPE_TAG_ARRAY &&
g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
- GjsAutoBaseInfo param_info =
+ GjsAutoTypeInfo param_info =
g_type_info_get_param_type(type_info, 0);
return type_can_be_allocated_directly(param_info);
}
@@ -798,7 +811,7 @@
case GI_TYPE_TAG_INTERFACE:
{
GjsAutoBaseInfo interface = g_type_info_get_interface(type_info);
- switch (g_base_info_get_type(interface)) {
+ switch (interface.type()) {
case GI_INFO_TYPE_BOXED:
case GI_INFO_TYPE_STRUCT:
return struct_is_simple(interface.as<GIStructInfo>());
@@ -867,7 +880,7 @@
if (g_type_info_is_pointer(type_info)) {
if (g_type_info_get_tag(type_info) == GI_TYPE_TAG_ARRAY &&
g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
- GjsAutoBaseInfo param_info =
+ GjsAutoTypeInfo param_info =
g_type_info_get_param_type(type_info, 0);
return direct_allocation_has_pointers(param_info);
}
@@ -900,8 +913,8 @@
return false;
for (i = 0; i < n_fields && is_simple; i++) {
- GjsAutoBaseInfo field_info = g_struct_info_get_field(info, i);
- GjsAutoBaseInfo type_info = g_field_info_get_type(field_info);
+ GjsAutoFieldInfo field_info = g_struct_info_get_field(info, i);
+ GjsAutoTypeInfo type_info = g_field_info_get_type(field_info);
is_simple = type_can_be_allocated_directly(type_info);
}
@@ -917,8 +930,8 @@
g_assert(n_fields > 0);
for (int i = 0; i < n_fields; i++) {
- GjsAutoBaseInfo field_info = g_struct_info_get_field(info, i);
- GjsAutoBaseInfo type_info = g_field_info_get_type(field_info);
+ GjsAutoFieldInfo field_info = g_struct_info_get_field(info, i);
+ GjsAutoTypeInfo type_info = g_field_info_get_type(field_info);
if (direct_allocation_has_pointers(type_info))
return true;
}
diff -ruN cjs-6.2.0-orig/gi/boxed.h cjs-6.2.0/gi/boxed.h
--- cjs-6.2.0-orig/gi/boxed.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/boxed.h 2024-10-14 18:41:39.000000000 +0200
@@ -198,11 +198,11 @@
GJS_JSAPI_RETURN_CONVENTION
bool get_nested_interface_object(JSContext* cx, JSObject* parent_obj,
GIFieldInfo* field_info,
- GIBaseInfo* interface_info,
+ GIStructInfo* interface_info,
JS::MutableHandleValue value) const;
GJS_JSAPI_RETURN_CONVENTION
bool set_nested_interface_object(JSContext* cx, GIFieldInfo* field_info,
- GIBaseInfo* interface_info,
+ GIStructInfo* interface_info,
JS::HandleValue value);
GJS_JSAPI_RETURN_CONVENTION
diff -ruN cjs-6.2.0-orig/gi/closure.cpp cjs-6.2.0/gi/closure.cpp
--- cjs-6.2.0-orig/gi/closure.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/closure.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -171,7 +171,7 @@
return false;
}
- JSAutoRealm ar(m_cx, m_callable);
+ JSAutoRealm ar{m_cx, m_callable.get()};
if (gjs_log_exception(m_cx)) {
gjs_debug_closure(
@@ -180,7 +180,7 @@
this);
}
- JS::RootedValue v_callable(m_cx, JS::ObjectValue(*m_callable));
+ JS::RootedValue v_callable{m_cx, JS::ObjectValue(*m_callable.get())};
bool ok = JS::Call(m_cx, this_obj, v_callable, args, retval);
GjsContextPrivate* gjs = GjsContextPrivate::from_cx(m_cx);
diff -ruN cjs-6.2.0-orig/gi/closure.h cjs-6.2.0/gi/closure.h
--- cjs-6.2.0-orig/gi/closure.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/closure.h 2024-10-14 18:41:39.000000000 +0200
@@ -9,9 +9,10 @@
#include <config.h>
-#include <glib-object.h>
#include <stddef.h>
+#include <glib-object.h>
+
#include <js/TypeDecls.h>
#include "gi/utils-inl.h"
@@ -68,8 +69,9 @@
[[nodiscard]] static Closure* create_marshaled(JSContext* cx,
JSObject* callable,
- const char* description) {
- auto* self = new Closure(cx, callable, true /* root */, description);
+ const char* description,
+ bool root = true) {
+ auto* self = new Closure(cx, callable, root, description);
self->add_finalize_notifier<Closure>();
g_closure_set_marshal(self, marshal_cb);
return self;
@@ -86,7 +88,8 @@
return self;
}
- constexpr JSObject* callable() const { return m_callable; }
+ // COMPAT: constexpr in C++23
+ JSObject* callable() const { return m_callable.get(); }
[[nodiscard]] constexpr JSContext* context() const { return m_cx; }
[[nodiscard]] constexpr bool is_valid() const { return !!m_cx; }
GJS_JSAPI_RETURN_CONVENTION bool invoke(JS::HandleObject,
@@ -127,7 +130,7 @@
// The context could be attached to the default context of the runtime
// using if we wanted the closure to survive the context that created it.
JSContext* m_cx;
- GjsMaybeOwned<JSObject*> m_callable;
+ GjsMaybeOwned m_callable;
};
} // namespace Gjs
diff -ruN cjs-6.2.0-orig/gi/cwrapper.h cjs-6.2.0/gi/cwrapper.h
--- cjs-6.2.0-orig/gi/cwrapper.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/cwrapper.h 2024-10-14 18:41:39.000000000 +0200
@@ -16,6 +16,7 @@
#include <js/CallArgs.h>
#include <js/Class.h>
#include <js/ErrorReport.h> // for JSEXN_TYPEERR
+#include <js/GCVector.h> // for MutableHandleIdVector
#include <js/GlobalObject.h> // for CurrentGlobalOrNull
#include <js/Id.h>
#include <js/Object.h> // for GetClass
diff -ruN cjs-6.2.0-orig/gi/enumeration.cpp cjs-6.2.0/gi/enumeration.cpp
--- cjs-6.2.0-orig/gi/enumeration.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/enumeration.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -4,6 +4,8 @@
#include <config.h>
+#include <inttypes.h>
+
#include <girepository.h>
#include <glib-object.h>
#include <glib.h>
@@ -46,7 +48,7 @@
}
gjs_debug(GJS_DEBUG_GENUM,
- "Defining enum value %s (fixed from %s) %" G_GINT64_MODIFIER "d",
+ "Defining enum value %s (fixed from %s) %" PRId64,
fixed_name.get(), value_name, value_val);
if (!JS_DefineProperty(context, in_object,
diff -ruN cjs-6.2.0-orig/gi/foreign.cpp cjs-6.2.0/gi/foreign.cpp
--- cjs-6.2.0-orig/gi/foreign.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/foreign.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -4,112 +4,92 @@
#include <config.h>
-#include <string.h> // for strcmp
+#include <stddef.h> // for size_t
+
#include <string>
+#include <unordered_map>
+#include <utility> // for pair
#include <girepository.h>
#include <glib.h>
+#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include "gi/foreign.h"
#include "cjs/context-private.h"
#include "cjs/jsapi-util.h"
+#include "cjs/macros.h"
-static struct {
- const char* gi_namespace;
- bool loaded;
-} foreign_modules[] = {
- // clang-format off
- {"cairo", false},
- {nullptr}
- // clang-format on
-};
-
-static GHashTable* foreign_structs_table = NULL;
-
-[[nodiscard]] static GHashTable* get_foreign_structs() {
- // FIXME: look into hasing on GITypeInfo instead.
- if (!foreign_structs_table) {
- foreign_structs_table = g_hash_table_new_full(g_str_hash, g_str_equal,
- (GDestroyNotify)g_free,
- NULL);
+enum LoadedStatus { NotLoaded, Loaded };
+static std::unordered_map<std::string, LoadedStatus> foreign_modules{
+ {"cairo", NotLoaded}};
+
+using StructID = std::pair<std::string, std::string>;
+struct StructIDHash {
+ [[nodiscard]] size_t operator()(StructID val) const {
+ std::hash<std::string> hasher;
+ return hasher(val.first) ^ hasher(val.second);
}
-
- return foreign_structs_table;
-}
+};
+static std::unordered_map<StructID, GjsForeignInfo*, StructIDHash>
+ foreign_structs_table;
[[nodiscard]] static bool gjs_foreign_load_foreign_module(
- JSContext* context, const char* gi_namespace) {
- int i;
+ JSContext* cx, const char* gi_namespace) {
+ auto entry = foreign_modules.find(gi_namespace);
+ if (entry == foreign_modules.end())
+ return false;
- for (i = 0; foreign_modules[i].gi_namespace; ++i) {
- if (strcmp(gi_namespace, foreign_modules[i].gi_namespace) != 0)
- continue;
-
- if (foreign_modules[i].loaded)
- return true;
-
- // FIXME: Find a way to check if a module is imported
- // and only execute this statement if isn't
- std::string script = "imports." + std::string(gi_namespace) + ';';
- JS::RootedValue retval(context);
- GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
- if (!gjs->eval_with_scope(nullptr, script.c_str(), script.length(),
- "<internal>", &retval)) {
- g_critical("ERROR importing foreign module %s\n", gi_namespace);
- return false;
- }
- foreign_modules[i].loaded = true;
+ if (entry->second == Loaded)
return true;
- }
- return false;
+ // FIXME: Find a way to check if a module is imported and only execute this
+ // statement if it isn't
+ std::string script = "imports." + entry->first + ';';
+ JS::RootedValue retval{cx};
+ GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx);
+ if (!gjs->eval_with_scope(nullptr, script.c_str(), script.length(),
+ "<internal>", &retval)) {
+ g_critical("ERROR importing foreign module %s\n", gi_namespace);
+ return false;
+ }
+ entry->second = Loaded;
+ return true;
}
void gjs_struct_foreign_register(const char* gi_namespace,
const char* type_name, GjsForeignInfo* info) {
- char *canonical_name;
+ foreign_structs_table.insert({{gi_namespace, type_name}, info});
+}
- g_return_if_fail(info);
- g_return_if_fail(info->to_func);
- g_return_if_fail(info->from_func);
-
- canonical_name = g_strdup_printf("%s.%s", gi_namespace, type_name);
- g_hash_table_insert(get_foreign_structs(), canonical_name, info);
-}
-
-[[nodiscard]] static GjsForeignInfo* gjs_struct_foreign_lookup(
- JSContext* context, GIBaseInfo* interface_info) {
- GHashTable* hash_table;
-
- auto key = std::string(g_base_info_get_namespace(interface_info)) + '.' +
- g_base_info_get_name(interface_info);
- hash_table = get_foreign_structs();
- auto* retval = static_cast<GjsForeignInfo*>(
- g_hash_table_lookup(hash_table, key.c_str()));
- if (!retval) {
- if (gjs_foreign_load_foreign_module(context, g_base_info_get_namespace(interface_info))) {
- retval = static_cast<GjsForeignInfo*>(
- g_hash_table_lookup(hash_table, key.c_str()));
- }
+GJS_JSAPI_RETURN_CONVENTION
+static GjsForeignInfo* gjs_struct_foreign_lookup(JSContext* cx,
+ GIStructInfo* info) {
+ const char* ns = g_base_info_get_namespace(info);
+ StructID key{ns, g_base_info_get_name(info)};
+ auto entry = foreign_structs_table.find(key);
+ if (entry == foreign_structs_table.end()) {
+ if (gjs_foreign_load_foreign_module(cx, ns))
+ entry = foreign_structs_table.find(key);
}
- if (!retval) {
- gjs_throw(context, "Unable to find module implementing foreign type %s",
- key.c_str());
+ if (entry == foreign_structs_table.end()) {
+ gjs_throw(cx, "Unable to find module implementing foreign type %s.%s",
+ key.first.c_str(), key.second.c_str());
+ return nullptr;
}
- return retval;
+ return entry->second;
}
-bool gjs_struct_foreign_convert_to_g_argument(
- JSContext* context, JS::Value value, GIBaseInfo* interface_info,
+bool gjs_struct_foreign_convert_to_gi_argument(
+ JSContext* context, JS::Value value, GIStructInfo* info,
const char* arg_name, GjsArgumentType argument_type, GITransfer transfer,
- GjsArgumentFlags flags, GArgument* arg) {
+ GjsArgumentFlags flags, GIArgument* arg) {
GjsForeignInfo *foreign;
- foreign = gjs_struct_foreign_lookup(context, interface_info);
+ foreign = gjs_struct_foreign_lookup(context, info);
if (!foreign)
return false;
@@ -120,15 +100,11 @@
return true;
}
-bool
-gjs_struct_foreign_convert_from_g_argument(JSContext *context,
- JS::MutableHandleValue value_p,
- GIBaseInfo *interface_info,
- GIArgument *arg)
-{
- GjsForeignInfo *foreign;
-
- foreign = gjs_struct_foreign_lookup(context, interface_info);
+bool gjs_struct_foreign_convert_from_gi_argument(JSContext* context,
+ JS::MutableHandleValue value_p,
+ GIStructInfo* info,
+ GIArgument* arg) {
+ GjsForeignInfo* foreign = gjs_struct_foreign_lookup(context, info);
if (!foreign)
return false;
@@ -138,15 +114,11 @@
return true;
}
-bool
-gjs_struct_foreign_release_g_argument(JSContext *context,
- GITransfer transfer,
- GIBaseInfo *interface_info,
- GArgument *arg)
-{
- GjsForeignInfo *foreign;
-
- foreign = gjs_struct_foreign_lookup(context, interface_info);
+bool gjs_struct_foreign_release_gi_argument(JSContext* context,
+ GITransfer transfer,
+ GIStructInfo* info,
+ GIArgument* arg) {
+ GjsForeignInfo* foreign = gjs_struct_foreign_lookup(context, info);
if (!foreign)
return false;
@@ -158,4 +130,3 @@
return true;
}
-
diff -ruN cjs-6.2.0-orig/gi/foreign.h cjs-6.2.0/gi/foreign.h
--- cjs-6.2.0-orig/gi/foreign.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/foreign.h 2024-10-14 18:41:39.000000000 +0200
@@ -9,50 +9,46 @@
#include <girepository.h>
-#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <js/Value.h>
#include "gi/arg.h"
#include "cjs/macros.h"
-typedef bool (*GjsArgOverrideToGArgumentFunc)(
- JSContext* context, JS::Value value, const char* arg_name,
- GjsArgumentType argument_type, GITransfer transfer, GjsArgumentFlags flags,
- GArgument* arg);
-
-typedef bool (*GjsArgOverrideFromGArgumentFunc) (JSContext *context,
- JS::MutableHandleValue value_p,
- GIArgument *arg);
-
-typedef bool (*GjsArgOverrideReleaseGArgumentFunc) (JSContext *context,
- GITransfer transfer,
- GArgument *arg);
+typedef bool (*GjsArgOverrideToGIArgumentFunc)(JSContext*, JS::Value,
+ const char* arg_name,
+ GjsArgumentType, GITransfer,
+ GjsArgumentFlags, GIArgument*);
+
+typedef bool (*GjsArgOverrideFromGIArgumentFunc)(JSContext*,
+ JS::MutableHandleValue,
+ GIArgument*);
+
+typedef bool (*GjsArgOverrideReleaseGIArgumentFunc)(JSContext*, GITransfer,
+ GIArgument*);
typedef struct {
- GjsArgOverrideToGArgumentFunc to_func;
- GjsArgOverrideFromGArgumentFunc from_func;
- GjsArgOverrideReleaseGArgumentFunc release_func;
+ GjsArgOverrideToGIArgumentFunc to_func;
+ GjsArgOverrideFromGIArgumentFunc from_func;
+ GjsArgOverrideReleaseGIArgumentFunc release_func;
} GjsForeignInfo;
void gjs_struct_foreign_register(const char* gi_namespace,
const char* type_name, GjsForeignInfo* info);
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_struct_foreign_convert_to_g_argument(
- JSContext* context, JS::Value value, GIBaseInfo* interface_info,
- const char* arg_name, GjsArgumentType argument_type, GITransfer transfer,
- GjsArgumentFlags flags, GArgument* arg);
+bool gjs_struct_foreign_convert_to_gi_argument(JSContext*, JS::Value,
+ GIStructInfo*,
+ const char* arg_name,
+ GjsArgumentType, GITransfer,
+ GjsArgumentFlags, GIArgument*);
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_struct_foreign_convert_from_g_argument(JSContext *context,
- JS::MutableHandleValue value_p,
- GIBaseInfo *interface_info,
- GIArgument *arg);
+bool gjs_struct_foreign_convert_from_gi_argument(JSContext*,
+ JS::MutableHandleValue,
+ GIStructInfo*, GIArgument*);
GJS_JSAPI_RETURN_CONVENTION
-bool gjs_struct_foreign_release_g_argument (JSContext *context,
- GITransfer transfer,
- GIBaseInfo *interface_info,
- GArgument *arg);
+bool gjs_struct_foreign_release_gi_argument(JSContext*, GITransfer,
+ GIStructInfo*, GIArgument*);
#endif // GI_FOREIGN_H_
diff -ruN cjs-6.2.0-orig/gi/function.cpp cjs-6.2.0/gi/function.cpp
--- cjs-6.2.0-orig/gi/function.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/function.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -25,19 +25,18 @@
#include <js/Class.h>
#include <js/ErrorReport.h> // for JS_ReportOutOfMemory
#include <js/Exception.h>
+#include <js/HeapAPI.h> // for RuntimeHeapIsCollecting
#include <js/PropertyAndElement.h>
#include <js/PropertyDescriptor.h> // for JSPROP_PERMANENT
#include <js/PropertySpec.h>
#include <js/Realm.h> // for GetRealmFunctionPrototype
#include <js/RootingAPI.h>
-#include <js/String.h>
#include <js/TypeDecls.h>
#include <js/Value.h>
#include <js/ValueArray.h>
#include <js/Warnings.h>
-#include <jsapi.h> // for HandleValueArray
-#include <jsfriendapi.h> // for JS_GetObjectFunction
-#include <jspubtd.h> // for JSProtoKey
+#include <jsapi.h> // for HandleValueArray
+#include <jspubtd.h> // for JSProtoKey
#include "gi/arg-cache.h"
#include "gi/arg-inl.h"
@@ -132,8 +131,7 @@
static JSObject* inherit_builtin_function(JSContext* cx, JSProtoKey) {
JS::RootedObject builtin_function_proto(
cx, JS::GetRealmFunctionPrototype(cx));
- return JS_NewObjectWithGivenProto(cx, &Function::klass,
- builtin_function_proto);
+ return JS_NewObjectWithGivenProto(cx, nullptr, builtin_function_proto);
}
static const JSClassOps class_ops;
@@ -192,12 +190,10 @@
}
}
-static void
-set_return_ffi_arg_from_giargument (GITypeInfo *ret_type,
- void *result,
- GIArgument *return_value)
-{
- // Be consistent with gjs_value_to_g_argument()
+static void set_return_ffi_arg_from_gi_argument(GITypeInfo* ret_type,
+ void* result,
+ GIArgument* return_value) {
+ // Be consistent with gjs_value_to_gi_argument()
switch (g_type_info_get_tag(ret_type)) {
case GI_TYPE_TAG_VOID:
g_assert_not_reached();
@@ -230,10 +226,8 @@
break;
case GI_TYPE_TAG_INTERFACE:
{
- GIInfoType interface_type;
-
GjsAutoBaseInfo interface_info(g_type_info_get_interface(ret_type));
- interface_type = g_base_info_get_type(interface_info);
+ GIInfoType interface_type = interface_info.type();
if (interface_type == GI_INFO_TYPE_ENUM ||
interface_type == GI_INFO_TYPE_FLAGS)
@@ -303,7 +297,7 @@
if (g_type_info_get_tag(&ret_type) != GI_TYPE_TAG_VOID) {
GIArgument argument = {};
gjs_gi_argument_init_default(&ret_type, &argument);
- set_return_ffi_arg_from_giargument(&ret_type, result, &argument);
+ set_return_ffi_arg_from_gi_argument(&ret_type, result, &argument);
}
if (G_UNLIKELY(!is_valid())) {
@@ -317,7 +311,7 @@
JSContext* context = this->context();
GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
- if (G_UNLIKELY(gjs->sweeping())) {
+ if (JS::RuntimeHeapIsCollecting()) {
warn_about_illegal_js_callback(
"during garbage collection",
"destroying a Clutter actor or GTK widget with ::destroy signal "
@@ -396,12 +390,9 @@
gjs->exit_immediately(code);
// Some other uncatchable exception, e.g. out of memory
- JSFunction* fn = JS_GetObjectFunction(callable());
- std::string descr =
- fn ? "function " + gjs_debug_string(JS_GetFunctionDisplayId(fn))
- : "callable object " + gjs_debug_object(callable());
g_error("Call to %s (%s.%s) terminated with uncatchable exception",
- descr.c_str(), m_info.ns(), m_info.name());
+ gjs_debug_callable(callable()).c_str(), m_info.ns(),
+ m_info.name());
}
// If the callback has a GError** argument, then make a GError from the
@@ -480,7 +471,7 @@
g_callable_info_load_arg(m_info, array_length_pos,
&array_length_arg);
g_arg_info_load_type(&array_length_arg, &arg_type_info);
- size_t length = gjs_g_argument_get_array_length(
+ size_t length = gjs_gi_argument_get_array_length(
g_type_info_get_tag(&arg_type_info),
args[array_length_pos + c_args_offset]);
@@ -503,8 +494,8 @@
!g_arg_info_is_caller_allocates(&arg_info))
arg = *reinterpret_cast<GIArgument**>(arg);
- if (!gjs_value_from_g_argument(context, jsargs[n_jsargs++],
- &type_info, arg, false))
+ if (!gjs_value_from_gi_argument(context, jsargs[n_jsargs++],
+ &type_info, arg, false))
return false;
break;
}
@@ -530,12 +521,12 @@
transfer = g_callable_info_get_caller_owns(m_info);
/* non-void return value, no out args. Should
* be a single return value. */
- if (!gjs_value_to_g_argument(context, rval, ret_type, "callback",
- GJS_ARGUMENT_RETURN_VALUE, transfer,
- GjsArgumentFlags::MAY_BE_NULL, &argument))
+ if (!gjs_value_to_gi_argument(context, rval, ret_type, "callback",
+ GJS_ARGUMENT_RETURN_VALUE, transfer,
+ GjsArgumentFlags::MAY_BE_NULL, &argument))
return false;
- set_return_ffi_arg_from_giargument(ret_type, result, &argument);
+ set_return_ffi_arg_from_gi_argument(ret_type, result, &argument);
} else if (n_outargs == 1 && ret_type_is_void) {
/* void return value, one out args. Should
* be a single return value. */
@@ -559,14 +550,11 @@
return false;
if (!is_array) {
- JSFunction* fn = JS_GetObjectFunction(callable());
- std::string descr =
- fn ? "function " + gjs_debug_string(JS_GetFunctionDisplayId(fn))
- : "callable object " + gjs_debug_object(callable());
gjs_throw(context,
"Call to %s (%s.%s) returned unexpected value, expecting "
"an Array",
- descr.c_str(), m_info.ns(), m_info.name());
+ gjs_debug_callable(callable()).c_str(), m_info.ns(),
+ m_info.name());
return false;
}
@@ -583,10 +571,10 @@
if (!JS_GetElement(context, out_array, elem_idx, &elem))
return false;
- if (!gjs_value_to_g_argument(context, elem, ret_type, "callback",
- GJS_ARGUMENT_RETURN_VALUE, transfer,
- GjsArgumentFlags::MAY_BE_NULL,
- &argument))
+ if (!gjs_value_to_gi_argument(context, elem, ret_type, "callback",
+ GJS_ARGUMENT_RETURN_VALUE, transfer,
+ GjsArgumentFlags::MAY_BE_NULL,
+ &argument))
return false;
if ((ret_tag == GI_TYPE_TAG_FILENAME ||
@@ -604,7 +592,7 @@
}
}
- set_return_ffi_arg_from_giargument(ret_type, result, &argument);
+ set_return_ffi_arg_from_gi_argument(ret_type, result, &argument);
elem_idx++;
}
@@ -647,7 +635,7 @@
GITypeInfo type_info;
g_arg_info_load_type(&arg_info, &type_info);
- if (!gjs_g_argument_release(context, transfer, &type_info, arg))
+ if (!gjs_gi_argument_release(context, transfer, &type_info, arg))
return false;
continue;
@@ -669,8 +657,8 @@
GITypeInfo type_info;
g_arg_info_load_type(&data->arg_info, &type_info);
- if (!gjs_g_argument_release(self->context(), transfer,
- &type_info, &data->arg)) {
+ if (!gjs_gi_argument_release(self->context(), transfer,
+ &type_info, &data->arg)) {
gjs_throw(self->context(),
"Impossible to release closure argument '%s'",
g_base_info_get_name(&data->arg_info));
@@ -782,11 +770,9 @@
}
if (type_tag == GI_TYPE_TAG_INTERFACE) {
- GIInfoType interface_type;
-
GjsAutoBaseInfo interface_info =
g_type_info_get_interface(&type_info);
- interface_type = g_base_info_get_type(interface_info);
+ GIInfoType interface_type = interface_info.type();
if (interface_type == GI_INFO_TYPE_CALLBACK) {
gjs_throw(context(),
"%s %s accepts another callback as a parameter. This "
@@ -843,13 +829,13 @@
namespace Gjs {
-static void* get_return_ffi_pointer_from_giargument(
- GITypeInfo* return_type, GIFFIReturnValue* return_value) {
- // This should be the inverse of gi_type_info_extract_ffi_return_value().
- if (!return_type)
- return nullptr;
-
- switch (g_type_info_get_tag(return_type)) {
+static void* get_return_ffi_pointer_from_gi_argument(
+ GITypeTag tag, GITypeInfo* return_type, GIFFIReturnValue* return_value) {
+ if (return_type && g_type_info_is_pointer(return_type))
+ return &gjs_arg_member<void*>(return_value);
+ switch (tag) {
+ case GI_TYPE_TAG_VOID:
+ return nullptr;
case GI_TYPE_TAG_INT8:
return &gjs_arg_member<int8_t>(return_value);
case GI_TYPE_TAG_INT16:
@@ -875,9 +861,12 @@
case GI_TYPE_TAG_DOUBLE:
return &gjs_arg_member<double>(return_value);
case GI_TYPE_TAG_INTERFACE: {
+ if (!return_type)
+ return nullptr;
+
GjsAutoBaseInfo info = g_type_info_get_interface(return_type);
- switch (g_base_info_get_type(info)) {
+ switch (info.type()) {
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
return &gjs_arg_member<int, GI_TYPE_TAG_INTERFACE>(
@@ -894,7 +883,7 @@
// This function can be called in two different ways. You can either use it to
// create JavaScript objects by calling it without @r_value, or you can decide
-// to keep the return values in #GArgument format by providing a @r_value
+// to keep the return values in GIArgument format by providing a @r_value
// argument.
bool Function::invoke(JSContext* context, const JS::CallArgs& args,
JS::HandleObject this_obj /* = nullptr */,
@@ -1054,9 +1043,10 @@
g_assert_cmpuint(ffi_arg_pos, ==, ffi_argc);
g_assert_cmpuint(gi_arg_pos, ==, state.gi_argc);
+ GITypeTag return_tag = m_arguments.return_tag();
GITypeInfo* return_type = m_arguments.return_type();
- return_value_p =
- get_return_ffi_pointer_from_giargument(return_type, &return_value);
+ return_value_p = get_return_ffi_pointer_from_gi_argument(
+ return_tag, return_type, &return_value);
ffi_call(&m_invoker.cif, FFI_FN(m_invoker.native_address), return_value_p,
ffi_arg_pointers.get());
@@ -1069,6 +1059,11 @@
if (return_type) {
gi_type_info_extract_ffi_return_value(return_type, &return_value,
state.return_value());
+ } else if (return_tag != GI_TYPE_TAG_VOID) {
+ g_assert(GI_TYPE_TAG_IS_BASIC(return_tag));
+ gi_type_tag_extract_ffi_return_value(return_tag, GI_INFO_TYPE_INVALID,
+ &return_value,
+ state.return_value());
}
// Process out arguments and return values. This loop is skipped if we fail
@@ -1102,8 +1097,7 @@
"to pass to the out '%s' argument. It may be that the "
"function is unsupported, or there may be a bug in "
"its annotations.",
- g_base_info_get_namespace(m_info),
- g_base_info_get_name(m_info),
+ m_info.ns(), m_info.name(),
g_base_info_get_name(&arg_info));
state.failed = true;
break;
@@ -1234,9 +1228,7 @@
gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Call callee %p priv %p",
callee.get(), priv);
- if (priv == NULL)
- return true; // we are the prototype
-
+ g_assert(priv);
return priv->invoke(context, js_argv);
}
@@ -1246,8 +1238,7 @@
}
void Function::finalize_impl(JS::GCContext*, Function* priv) {
- if (priv == NULL)
- return; /* we are the prototype, not a real instance, so constructor never called */
+ g_assert(priv);
delete priv;
}
@@ -1262,15 +1253,6 @@
bool Function::to_string(JSContext* context, unsigned argc, JS::Value* vp) {
GJS_CHECK_WRAPPER_PRIV(context, argc, vp, rec, this_obj, Function, priv);
-
- if (priv == NULL) {
- JSString* retval = JS_NewStringCopyZ(context, "function () {\n}");
- if (!retval)
- return false;
- rec.rval().setString(retval);
- return true;
- }
-
return priv->to_string_impl(context, rec.rval());
}
@@ -1293,7 +1275,7 @@
}
GjsAutoChar descr;
- if (g_base_info_get_type(m_info) == GI_INFO_TYPE_FUNCTION) {
+ if (m_info.type() == GI_INFO_TYPE_FUNCTION) {
descr = g_strdup_printf(
"%s(%s) {\n\t/* wrapper for native symbol %s() */\n}",
format_name().c_str(), arg_names.c_str(),
diff -ruN cjs-6.2.0-orig/gi/fundamental.cpp cjs-6.2.0/gi/fundamental.cpp
--- cjs-6.2.0-orig/gi/fundamental.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/fundamental.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -8,11 +8,9 @@
#include <girepository.h>
#include <glib.h>
-#include <js/AllocPolicy.h> // for SystemAllocPolicy
#include <js/Class.h>
#include <js/ErrorReport.h> // for JS_ReportOutOfMemory
#include <js/GCHashTable.h> // for WeakCache
-#include <js/HashTable.h> // for DefaultHasher via WeakCache
#include <js/Object.h> // for GetClass
#include <js/PropertyAndElement.h>
#include <js/RootingAPI.h>
@@ -200,7 +198,7 @@
bool FundamentalInstance::constructor_impl(JSContext* cx,
JS::HandleObject object,
const JS::CallArgs& argv) {
- GArgument ret_value;
+ GIArgument ret_value;
GITypeInfo return_info;
if (!invoke_constructor(cx, object, argv, &ret_value) ||
@@ -210,7 +208,7 @@
GICallableInfo* constructor_info = get_prototype()->constructor_info();
g_callable_info_load_return_type(constructor_info, &return_info);
- return gjs_g_argument_release(
+ return gjs_gi_argument_release(
cx, g_callable_info_get_caller_owns(constructor_info), &return_info,
&ret_value);
}
diff -ruN cjs-6.2.0-orig/gi/gerror.cpp cjs-6.2.0/gi/gerror.cpp
--- cjs-6.2.0-orig/gi/gerror.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/gerror.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -12,6 +12,7 @@
#include <js/CallAndConstruct.h>
#include <js/CallArgs.h>
#include <js/Class.h>
+#include <js/ColumnNumber.h>
#include <js/Exception.h>
#include <js/PropertyAndElement.h>
#include <js/PropertyDescriptor.h> // for JSPROP_ENUMERATE
@@ -263,7 +264,7 @@
/* last attempt: load GIRepository (for invoke errors, rarely
needed) */
- g_irepository_require(nullptr, "GIRepository", "1.0",
+ g_irepository_require(nullptr, "GIRepository", "2.0",
GIRepositoryLoadFlags(0), nullptr);
info = g_irepository_find_by_error_domain(nullptr, domain);
@@ -278,7 +279,8 @@
JS::RootedObject frame(cx);
JS::RootedString stack(cx);
JS::RootedString source(cx);
- uint32_t line, column;
+ uint32_t line;
+ JS::TaggedColumnNumberOneOrigin tagged_column;
if (!JS::CaptureCurrentStack(cx, &frame) ||
!JS::BuildStackString(cx, nullptr, frame, &stack))
@@ -287,7 +289,7 @@
auto ok = JS::SavedFrameResult::Ok;
if (JS::GetSavedFrameSource(cx, nullptr, frame, &source) != ok ||
JS::GetSavedFrameLine(cx, nullptr, frame, &line) != ok ||
- JS::GetSavedFrameColumn(cx, nullptr, frame, &column) != ok) {
+ JS::GetSavedFrameColumn(cx, nullptr, frame, &tagged_column) != ok) {
gjs_throw(cx, "Error getting saved frame information");
return false;
}
@@ -299,7 +301,8 @@
JSPROP_ENUMERATE) &&
JS_DefinePropertyById(cx, obj, atoms.line_number(), line,
JSPROP_ENUMERATE) &&
- JS_DefinePropertyById(cx, obj, atoms.column_number(), column,
+ JS_DefinePropertyById(cx, obj, atoms.column_number(),
+ tagged_column.oneOriginValue(),
JSPROP_ENUMERATE);
}
diff -ruN cjs-6.2.0-orig/gi/gobject.cpp cjs-6.2.0/gi/gobject.cpp
--- cjs-6.2.0-orig/gi/gobject.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/gobject.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -163,12 +163,10 @@
*/
Gjs::AutoMainRealm ar{gjs};
- JS::RootedObject constructor(
- cx, gjs_lookup_object_constructor_from_info(cx, nullptr, type));
- if (!constructor)
+ JS::RootedValue constructor{cx};
+ if (!gjs_lookup_object_constructor(cx, type, &constructor))
return nullptr;
- JS::RootedValue v_constructor(cx, JS::ObjectValue(*constructor));
JS::RootedObject object(cx);
if (n_construct_properties) {
JS::RootedObject props_hash(cx, JS_NewPlainObject(cx));
@@ -182,9 +180,9 @@
JS::RootedValueArray<1> args(cx);
args[0].set(JS::ObjectValue(*props_hash));
- if (!JS::Construct(cx, v_constructor, args, &object))
+ if (!JS::Construct(cx, constructor, args, &object))
return nullptr;
- } else if (!JS::Construct(cx, v_constructor, JS::HandleValueArray::empty(),
+ } else if (!JS::Construct(cx, constructor, JS::HandleValueArray::empty(),
&object)) {
return nullptr;
}
diff -ruN cjs-6.2.0-orig/gi/gobject.h cjs-6.2.0/gi/gobject.h
--- cjs-6.2.0-orig/gi/gobject.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/gobject.h 2024-10-14 18:41:39.000000000 +0200
@@ -5,6 +5,8 @@
#ifndef GI_GOBJECT_H_
#define GI_GOBJECT_H_
+#include <config.h>
+
#include <vector>
#include <glib-object.h>
diff -ruN cjs-6.2.0-orig/gi/gtype.cpp cjs-6.2.0/gi/gtype.cpp
--- cjs-6.2.0-orig/gi/gtype.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/gtype.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -8,11 +8,9 @@
#include <glib-object.h>
#include <glib.h>
-#include <js/AllocPolicy.h> // for SystemAllocPolicy
#include <js/CallArgs.h>
#include <js/Class.h>
#include <js/GCHashTable.h> // for WeakCache
-#include <js/HashTable.h> // for DefaultHasher via WeakCache
#include <js/PropertyAndElement.h>
#include <js/PropertyDescriptor.h> // for JSPROP_PERMANENT
#include <js/PropertySpec.h>
diff -ruN cjs-6.2.0-orig/gi/interface.cpp cjs-6.2.0/gi/interface.cpp
--- cjs-6.2.0-orig/gi/interface.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/interface.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -9,6 +9,7 @@
#include <js/Class.h>
#include <js/ErrorReport.h> // for JS_ReportOutOfMemory
+#include <js/GCVector.h> // for MutableHandleIdVector
#include <js/Id.h> // for PropertyKey, jsid
#include <js/TypeDecls.h>
#include <js/Utility.h> // for UniqueChars
@@ -34,18 +35,20 @@
GJS_DEC_COUNTER(interface);
}
-static bool append_inferface_properties(JSContext* cx,
- JS::MutableHandleIdVector properties,
- GIInterfaceInfo* iface_info) {
- int n_methods = g_interface_info_get_n_methods(iface_info);
+bool InterfacePrototype::new_enumerate_impl(
+ JSContext* cx, JS::HandleObject, JS::MutableHandleIdVector properties,
+ bool only_enumerable [[maybe_unused]]) {
+ if (!info())
+ return true;
+
+ int n_methods = g_interface_info_get_n_methods(info());
if (!properties.reserve(properties.length() + n_methods)) {
JS_ReportOutOfMemory(cx);
return false;
}
for (int i = 0; i < n_methods; i++) {
- GjsAutoFunctionInfo meth_info =
- g_interface_info_get_method(iface_info, i);
+ GjsAutoFunctionInfo meth_info = g_interface_info_get_method(info(), i);
GIFunctionInfoFlags flags = g_function_info_get_flags(meth_info);
if (flags & GI_FUNCTION_IS_METHOD) {
@@ -60,31 +63,6 @@
return true;
}
-bool InterfacePrototype::new_enumerate_impl(
- JSContext* cx, JS::HandleObject obj [[maybe_unused]],
- JS::MutableHandleIdVector properties,
- bool only_enumerable [[maybe_unused]]) {
- unsigned n_interfaces;
- GjsAutoPointer<GType, void, &g_free> interfaces =
- g_type_interfaces(gtype(), &n_interfaces);
-
- for (unsigned k = 0; k < n_interfaces; k++) {
- GjsAutoInterfaceInfo iface_info =
- g_irepository_find_by_gtype(nullptr, interfaces[k]);
-
- if (!iface_info)
- continue;
-
- if (!append_inferface_properties(cx, properties, iface_info))
- return false;
- }
-
- if (!info())
- return true;
-
- return append_inferface_properties(cx, properties, info());
-}
-
// See GIWrapperBase::resolve().
bool InterfacePrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
JS::HandleId id, bool* resolved) {
@@ -194,25 +172,18 @@
JS::MutableHandleValue value_p)
{
JSObject *constructor;
- GIBaseInfo *interface_info;
-
- interface_info = g_irepository_find_by_gtype(nullptr, gtype);
+ GjsAutoInterfaceInfo interface_info = gjs_lookup_gtype(nullptr, gtype);
if (!interface_info) {
gjs_throw(context, "Cannot expose non introspectable interface %s",
g_type_name(gtype));
return false;
}
- g_assert(g_base_info_get_type(interface_info) ==
- GI_INFO_TYPE_INTERFACE);
-
constructor = gjs_lookup_generic_constructor(context, interface_info);
if (G_UNLIKELY(!constructor))
return false;
- g_base_info_unref(interface_info);
-
value_p.setObject(*constructor);
return true;
}
diff -ruN cjs-6.2.0-orig/gi/js-value-inl.h cjs-6.2.0/gi/js-value-inl.h
--- cjs-6.2.0-orig/gi/js-value-inl.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/js-value-inl.h 2024-10-14 18:41:39.000000000 +0200
@@ -10,6 +10,7 @@
#include <cmath> // for isnan
#include <limits>
+#include <string>
#include <girepository.h>
#include <glib-object.h>
@@ -20,6 +21,7 @@
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <js/Utility.h> // for UniqueChars
+#include <js/Value.h> // for CanonicalizeNaN
#include "gi/gtype.h"
#include "gi/value.h"
@@ -229,7 +231,7 @@
return std::numeric_limits<BigT>::lowest();
}
-template <typename WantedType, typename T>
+template <typename WantedType, GITypeTag TAG = GI_TYPE_TAG_VOID, typename T>
GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
JSContext* cx, const JS::HandleValue& value, T* out, bool* out_of_range) {
static_assert(std::numeric_limits<T>::max() >=
@@ -266,7 +268,7 @@
}
if constexpr (std::is_same_v<WantedType, T>)
- return js_value_to_c(cx, value, out);
+ return js_value_to_c<TAG>(cx, value, out);
// JS::ToIntNN() converts undefined, NaN, infinity to 0
if constexpr (std::is_integral_v<WantedType>) {
@@ -278,7 +280,7 @@
}
if constexpr (std::is_arithmetic_v<T>) {
- bool ret = js_value_to_c(cx, value, out);
+ bool ret = js_value_to_c<TAG>(cx, value, out);
if (out_of_range) {
// Infinity and NaN preserved between floating point types
if constexpr (std::is_floating_point_v<WantedType> &&
@@ -300,18 +302,20 @@
*out_of_range |= std::isnan(*out);
}
return ret;
+ // https://trac.cppcheck.net/ticket/10731
+ // cppcheck-suppress missingReturn
}
}
-template <typename WantedType>
+template <typename WantedType, GITypeTag TAG = GI_TYPE_TAG_VOID>
GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
JSContext* cx, const JS::HandleValue& value, TypeWrapper<WantedType>* out,
bool* out_of_range) {
static_assert(std::is_integral_v<WantedType>);
WantedType wanted_out;
- if (!js_value_to_c_checked<WantedType>(cx, value, &wanted_out,
- out_of_range))
+ if (!js_value_to_c_checked<WantedType, TAG>(cx, value, &wanted_out,
+ out_of_range))
return false;
*out = TypeWrapper<WantedType>{wanted_out};
@@ -319,4 +323,61 @@
return true;
}
+template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
+GJS_JSAPI_RETURN_CONVENTION inline bool c_value_to_js(
+ JSContext* cx [[maybe_unused]], T value,
+ JS::MutableHandleValue js_value_p) {
+ if constexpr (std::is_same_v<T, bool>) {
+ js_value_p.setBoolean(value);
+ return true;
+ } else if constexpr (std::is_same_v< // NOLINT(readability/braces)
+ T, gboolean> &&
+ TAG == GI_TYPE_TAG_BOOLEAN) {
+ js_value_p.setBoolean(value);
+ return true;
+ } else if constexpr (std::is_arithmetic_v<T>) {
+ if constexpr (std::is_same_v<T, int64_t> ||
+ std::is_same_v<T, uint64_t>) {
+ if (value < Gjs::min_safe_big_number<T>() ||
+ value > Gjs::max_safe_big_number<T>()) {
+ js_value_p.setDouble(value);
+ return true;
+ }
+ }
+ if constexpr (std::is_floating_point_v<T>) {
+ js_value_p.setDouble(JS::CanonicalizeNaN(double{value}));
+ return true;
+ }
+ js_value_p.setNumber(value);
+ return true;
+ } else if constexpr (std::is_same_v<T, // NOLINT(readability/braces)
+ char*> ||
+ std::is_same_v<T, const char*>) {
+ if (!value) {
+ js_value_p.setNull();
+ return true;
+ }
+ return gjs_string_from_utf8(cx, value, js_value_p);
+ } else {
+ static_assert(std::is_arithmetic_v<T>, "Unsupported type");
+ }
+}
+
+template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
+GJS_JSAPI_RETURN_CONVENTION inline bool c_value_to_js_checked(
+ JSContext* cx [[maybe_unused]], T value,
+ JS::MutableHandleValue js_value_p) {
+ if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>) {
+ if (value < Gjs::min_safe_big_number<T>() ||
+ value > Gjs::max_safe_big_number<T>()) {
+ g_warning(
+ "Value %s cannot be safely stored in a JS Number "
+ "and may be rounded",
+ std::to_string(value).c_str());
+ }
+ }
+
+ return c_value_to_js<T, TAG>(cx, value, js_value_p);
+}
+
} // namespace Gjs
diff -ruN cjs-6.2.0-orig/gi/ns.cpp cjs-6.2.0/gi/ns.cpp
--- cjs-6.2.0-orig/gi/ns.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/ns.cpp 2024-10-14 18:44:51.000000000 +0200
@@ -4,6 +4,10 @@
#include <config.h>
+#include <string.h>
+
+#include <vector>
+
#include <girepository.h>
#include <glib.h>
@@ -11,6 +15,7 @@
#include <js/Class.h>
#include <js/ComparisonOperators.h>
#include <js/ErrorReport.h> // for JS_ReportOutOfMemory
+#include <js/GCVector.h> // for MutableHandleIdVector
#include <js/Id.h>
#include <js/PropertyDescriptor.h> // for JSPROP_READONLY
#include <js/PropertySpec.h>
@@ -30,6 +35,10 @@
#include "cjs/mem-private.h"
#include "util/log.h"
+#if GLIB_CHECK_VERSION(2, 79, 2)
+# include "cjs/deprecation.h"
+#endif // GLib >= 2.79.2
+
[[nodiscard]] static bool type_is_enumerable(GIInfoType info_type) {
switch (info_type) {
case GI_INFO_TYPE_BOXED:
@@ -64,16 +73,51 @@
friend CWrapperPointerOps<Ns>;
friend CWrapper<Ns>;
+#if GLIB_CHECK_VERSION(2, 79, 2)
+ bool m_is_gio_or_glib : 1;
+#endif // GLib >= 2.79.2
+
static constexpr auto PROTOTYPE_SLOT = GjsGlobalSlot::PROTOTYPE_ns;
static constexpr GjsDebugTopic DEBUG_TOPIC = GJS_DEBUG_GNAMESPACE;
explicit Ns(const char* ns_name)
: GjsAutoChar(const_cast<char*>(ns_name), GjsAutoTakeOwnership()) {
GJS_INC_COUNTER(ns);
+#if GLIB_CHECK_VERSION(2, 79, 2)
+ m_is_gio_or_glib =
+ strcmp(ns_name, "Gio") == 0 || strcmp(ns_name, "GLib") == 0;
+#endif // GLib >= 2.79.2
}
~Ns() { GJS_DEC_COUNTER(ns); }
+#if GLIB_CHECK_VERSION(2, 79, 2)
+ // helper function
+ void platform_specific_warning(JSContext* cx, const char* prefix,
+ const char* platform,
+ const char* resolved_name,
+ const char** exceptions = nullptr) {
+ if (!g_str_has_prefix(resolved_name, prefix))
+ return;
+
+ const char* base_name = resolved_name + strlen(prefix);
+ GjsAutoChar old_name =
+ g_strdup_printf("%s.%s", this->get(), resolved_name);
+ if (exceptions) {
+ for (const char** exception = exceptions; *exception; exception++) {
+ if (strcmp(old_name, *exception) == 0)
+ return;
+ }
+ }
+
+ GjsAutoChar new_name =
+ g_strdup_printf("%s%s.%s", this->get(), platform, base_name);
+ _gjs_warn_deprecated_once_per_callsite(
+ cx, GjsDeprecationMessageId::PlatformSpecificTypelib,
+ {old_name.get(), new_name.get()});
+ }
+#endif // GLib >= 2.79.2
+
// JSClass operations
// The *resolved out parameter, on success, should be false to indicate that
@@ -112,6 +156,24 @@
"Found info type %s for '%s' in namespace '%s'",
gjs_info_type_name(info.type()), info.name(), info.ns());
+#if GLIB_CHECK_VERSION(2, 79, 2)
+ static const char* unix_types_exceptions[] = {
+ "Gio.UnixConnection",
+ "Gio.UnixCredentialsMessage",
+ "Gio.UnixFDList",
+ "Gio.UnixSocketAddress",
+ "Gio.UnixSocketAddressType",
+ nullptr};
+
+ if (m_is_gio_or_glib) {
+ platform_specific_warning(cx, "Unix", "Unix", name.get(),
+ unix_types_exceptions);
+ platform_specific_warning(cx, "unix_", "Unix", name.get());
+ platform_specific_warning(cx, "Win32", "Win32", name.get());
+ platform_specific_warning(cx, "win32_", "Win32", name.get());
+ }
+#endif // GLib >= 2.79.2
+
bool defined;
if (!gjs_define_info(cx, obj, info, &defined)) {
gjs_debug(GJS_DEBUG_GNAMESPACE, "Failed to define info '%s'",
diff -ruN cjs-6.2.0-orig/gi/ns.h cjs-6.2.0/gi/ns.h
--- cjs-6.2.0-orig/gi/ns.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/ns.h 2024-10-14 18:41:39.000000000 +0200
@@ -5,6 +5,8 @@
#ifndef GI_NS_H_
#define GI_NS_H_
+#include <config.h>
+
#include "cjs/macros.h"
class JSObject;
diff -ruN cjs-6.2.0-orig/gi/object.cpp cjs-6.2.0/gi/object.cpp
--- cjs-6.2.0-orig/gi/object.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/object.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -92,8 +92,8 @@
static const auto DISPOSED_OBJECT = std::numeric_limits<uintptr_t>::max();
GJS_JSAPI_RETURN_CONVENTION
-static JSObject* gjs_lookup_object_prototype_from_info(JSContext*,
- GIObjectInfo*, GType);
+static JSObject* gjs_lookup_object_prototype_from_info(JSContext*, GIBaseInfo*,
+ GType);
// clang-format off
G_DEFINE_QUARK(gjs::custom-type, ObjectBase::custom_type)
@@ -114,16 +114,6 @@
return !!g_type_get_qdata(gtype(), ObjectBase::custom_type_quark());
}
-// Plain g_type_query fails and leaves @query uninitialized for dynamic types.
-// See https://gitlab.gnome.org/GNOME/glib/issues/623
-void ObjectBase::type_query_dynamic_safe(GTypeQuery* query) {
- GType type = gtype();
- while (g_type_get_qdata(type, ObjectBase::custom_type_quark()))
- type = g_type_parent(type);
-
- g_type_query(type, query);
-}
-
void ObjectInstance::link() {
g_assert(std::find(s_wrapped_gobject_list.begin(),
s_wrapped_gobject_list.end(),
@@ -242,32 +232,24 @@
g_object_steal_qdata(m_ptr, priv_quark);
}
-GParamSpec* ObjectPrototype::find_param_spec_from_id(JSContext* cx,
- JS::HandleString key) {
+GParamSpec* ObjectPrototype::find_param_spec_from_id(
+ JSContext* cx, GjsAutoTypeClass<GObjectClass> const& object_class,
+ JS::HandleString key) {
/* First check for the ID in the cache */
- auto entry = m_property_cache.lookupForAdd(key);
- if (entry)
- return entry->value();
JS::UniqueChars js_prop_name(JS_EncodeStringToUTF8(cx, key));
if (!js_prop_name)
return nullptr;
GjsAutoChar gname = gjs_hyphen_from_camel(js_prop_name.get());
- GjsAutoTypeClass<GObjectClass> gobj_class(m_gtype);
- GParamSpec* pspec = g_object_class_find_property(gobj_class, gname);
- GjsAutoParam param_spec(pspec, GjsAutoTakeOwnership());
+ GParamSpec* pspec = g_object_class_find_property(object_class, gname);
- if (!param_spec) {
+ if (!pspec) {
gjs_wrapper_throw_nonexistent_field(cx, m_gtype, js_prop_name.get());
return nullptr;
}
- if (!m_property_cache.add(entry, key, std::move(param_spec))) {
- JS_ReportOutOfMemory(cx);
- return nullptr;
- }
- return pspec; /* owned by property cache */
+ return pspec;
}
/* A hook on adding a property to an object. This is called during a set
@@ -298,52 +280,41 @@
if (is_custom_js_class())
return true;
- if (!ensure_uses_toggle_ref(cx)) {
- gjs_throw(cx, "Impossible to set toggle references on %sobject %p",
- m_gobj_disposed ? "disposed " : "", m_ptr.get());
- return false;
- }
-
+ ensure_uses_toggle_ref(cx);
return true;
}
bool ObjectBase::prop_getter(JSContext* cx, unsigned argc, JS::Value* vp) {
GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, obj, ObjectBase, priv);
- JS::RootedString name(cx,
- gjs_dynamic_property_private_slot(&args.callee()).toString());
+ auto* pspec = static_cast<GParamSpec*>(
+ gjs_dynamic_property_private_slot(&args.callee()).toPrivate());
- std::string fullName{priv->format_name() + "[" + gjs_debug_string(name) +
- "]"};
+ std::string fullName{priv->format_name() + "[\"" + pspec->name + "\"]"};
AutoProfilerLabel label(cx, "property getter", fullName.c_str());
- priv->debug_jsprop("Property getter", name, obj);
+ priv->debug_jsprop("Property getter", pspec->name, obj);
if (priv->is_prototype())
return true;
/* Ignore silently; note that this is different from what we do for
* boxed types, for historical reasons */
- return priv->to_instance()->prop_getter_impl(cx, name, args.rval());
+ return priv->to_instance()->prop_getter_impl(cx, pspec, args.rval());
}
-bool ObjectInstance::prop_getter_impl(JSContext* cx, JS::HandleString name,
+bool ObjectInstance::prop_getter_impl(JSContext* cx, GParamSpec* param,
JS::MutableHandleValue rval) {
if (!check_gobject_finalized("get any property from")) {
rval.setUndefined();
return true;
}
- ObjectPrototype* proto_priv = get_prototype();
- GParamSpec *param = proto_priv->find_param_spec_from_id(cx, name);
-
- /* This is guaranteed because we resolved the property before */
- g_assert(param);
-
- /* Do not fetch JS overridden properties from GObject, to avoid
- * infinite recursion. */
- if (g_param_spec_get_qdata(param, ObjectBase::custom_property_quark()))
- return true;
+ if (param->flags & G_PARAM_DEPRECATED) {
+ const std::string& class_name = format_name();
+ _gjs_warn_deprecated_once_per_callsite(
+ cx, DeprecatedGObjectProperty, {class_name.c_str(), param->name});
+ }
if ((param->flags & G_PARAM_READABLE) == 0) {
rval.setUndefined();
@@ -384,7 +355,8 @@
JS::RootedString name(cx,
gjs_dynamic_property_private_slot(&args.callee()).toString());
- std::string fullName = priv->format_name() + "." + gjs_debug_string(name);
+ std::string fullName{priv->format_name() + "[" + gjs_debug_string(name) +
+ "]"};
AutoProfilerLabel label(cx, "field getter", fullName.c_str());
priv->debug_jsprop("Field getter", name, obj);
@@ -437,8 +409,8 @@
return false;
}
- return gjs_value_from_g_argument(cx, rval, type, GJS_ARGUMENT_FIELD,
- GI_TRANSFER_EVERYTHING, &arg);
+ return gjs_value_from_gi_argument(cx, rval, type, GJS_ARGUMENT_FIELD,
+ GI_TRANSFER_EVERYTHING, &arg);
/* transfer is irrelevant because g_field_info_get_field() doesn't
* handle boxed types */
}
@@ -448,14 +420,13 @@
bool ObjectBase::prop_setter(JSContext* cx, unsigned argc, JS::Value* vp) {
GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, obj, ObjectBase, priv);
- JS::RootedString name(cx,
- gjs_dynamic_property_private_slot(&args.callee()).toString());
+ auto* pspec = static_cast<GParamSpec*>(
+ gjs_dynamic_property_private_slot(&args.callee()).toPrivate());
- std::string fullName{priv->format_name() + "[" + gjs_debug_string(name) +
- "]"};
+ std::string fullName{priv->format_name() + "[\"" + pspec->name + "\"]"};
AutoProfilerLabel label(cx, "property setter", fullName.c_str());
- priv->debug_jsprop("Property setter", name, obj);
+ priv->debug_jsprop("Property setter", pspec->name, obj);
if (priv->is_prototype())
return true;
@@ -465,24 +436,14 @@
/* Clear the JS stored value, to avoid keeping additional references */
args.rval().setUndefined();
- return priv->to_instance()->prop_setter_impl(cx, name, args[0]);
+ return priv->to_instance()->prop_setter_impl(cx, pspec, args[0]);
}
-bool ObjectInstance::prop_setter_impl(JSContext* cx, JS::HandleString name,
+bool ObjectInstance::prop_setter_impl(JSContext* cx, GParamSpec* param_spec,
JS::HandleValue value) {
if (!check_gobject_finalized("set any property on"))
return true;
- ObjectPrototype* proto_priv = get_prototype();
- GParamSpec *param_spec = proto_priv->find_param_spec_from_id(cx, name);
- if (!param_spec)
- return false;
-
- /* Do not set JS overridden properties through GObject, to avoid
- * infinite recursion (unless constructing) */
- if (g_param_spec_get_qdata(param_spec, ObjectBase::custom_property_quark()))
- return true;
-
if (!(param_spec->flags & G_PARAM_WRITABLE))
/* prevent setting the prop even in JS */
return gjs_wrapper_throw_readonly_field(cx, gtype(), param_spec->name);
@@ -622,11 +583,9 @@
return !!prop_info;
}
-bool ObjectPrototype::lazy_define_gobject_property(JSContext* cx,
- JS::HandleObject obj,
- JS::HandleId id,
- bool* resolved,
- const char* name) {
+bool ObjectPrototype::lazy_define_gobject_property(
+ JSContext* cx, JS::HandleObject obj, JS::HandleId id, GParamSpec* pspec,
+ bool* resolved, const char* name) {
bool found = false;
if (!JS_AlreadyHasOwnPropertyById(cx, obj, id, &found))
return false;
@@ -639,10 +598,17 @@
debug_jsprop("Defining lazy GObject property", id, obj);
- JS::RootedValue private_id(cx, JS::StringValue(id.toString()));
+ // Do not fetch JS overridden properties from GObject, to avoid
+ // infinite recursion.
+ if (g_param_spec_get_qdata(pspec, ObjectBase::custom_property_quark())) {
+ *resolved = false;
+ return true;
+ }
+
+ JS::RootedValue private_value{cx, JS::PrivateValue(pspec)};
if (!gjs_define_property_dynamic(
- cx, obj, name, "gobject_prop", &ObjectBase::prop_getter,
- &ObjectBase::prop_setter, private_id,
+ cx, obj, name, id, "gobject_prop", &ObjectBase::prop_getter,
+ &ObjectBase::prop_setter, private_value,
// Make property configurable so that interface properties can be
// overridden by GObject.ParamSpec.override in the class that
// implements them
@@ -701,8 +667,11 @@
g_assert(v_prototype.isObject() && "prototype must be an object");
JS::RootedObject prototype(cx, &v_prototype.toObject());
- JS::RootedId id(cx, JS::PropertyKey::NonIntAtom(JS_GetFunctionId(
- JS_GetObjectFunction(&args.callee()))));
+ JS::RootedFunction fn_obj{cx, JS_GetObjectFunction(&args.callee())};
+ JS::RootedString fn_name{cx};
+ if (!JS_GetFunctionId(cx, fn_obj, &fn_name))
+ return false;
+ JS::RootedId id{cx, JS::PropertyKey::NonIntAtom(fn_name)};
return JS_GetPropertyById(cx, prototype, id, args.rval());
}
@@ -731,7 +700,7 @@
}
static bool resolve_on_interface_prototype(JSContext* cx,
- GIObjectInfo* iface_info,
+ GIInterfaceInfo* iface_info,
JS::HandleId identifier,
JS::HandleObject class_prototype,
bool* found) {
@@ -832,8 +801,10 @@
if (canonical_name && G_TYPE_IS_CLASSED(m_gtype) && !is_custom_js_class()) {
GjsAutoTypeClass<GObjectClass> oclass(m_gtype);
- if (g_object_class_find_property(oclass, canonical_name))
- return lazy_define_gobject_property(cx, obj, id, resolved, name);
+ if (GParamSpec* pspec =
+ g_object_class_find_property(oclass, canonical_name))
+ return lazy_define_gobject_property(cx, obj, id, pspec, resolved,
+ name);
for (i = 0; i < n_interfaces; i++) {
GType iface_gtype =
@@ -843,8 +814,10 @@
GjsAutoTypeClass<GObjectClass> iclass(iface_gtype);
- if (g_object_class_find_property(iclass, canonical_name))
- return lazy_define_gobject_property(cx, obj, id, resolved, name);
+ if (GParamSpec* pspec =
+ g_object_class_find_property(iclass, canonical_name))
+ return lazy_define_gobject_property(cx, obj, id, pspec,
+ resolved, name);
}
}
@@ -880,8 +853,8 @@
GParamSpec* pspec = g_object_class_find_property(
oclass, canonical_name); // unowned
if (pspec && pspec->owner_type == m_gtype) {
- return lazy_define_gobject_property(cx, obj, id, resolved,
- name);
+ return lazy_define_gobject_property(cx, obj, id, pspec,
+ resolved, name);
}
}
@@ -893,11 +866,11 @@
return true;
}
-[[nodiscard]] static bool is_gobject_property_name(GIObjectInfo* info,
- const char* name) {
+[[nodiscard]] static GjsAutoChar get_gobject_property_name(GIObjectInfo* info,
+ const char* name) {
// Optimization: GObject property names must start with a letter
if (!g_ascii_isalpha(name[0]))
- return false;
+ return nullptr;
int n_props = g_object_info_get_n_properties(info);
int n_ifaces = g_object_info_get_n_interfaces(info);
@@ -909,15 +882,15 @@
for (ix = 0; ix < n_props; ix++) {
GjsAutoPropertyInfo prop_info = g_object_info_get_property(info, ix);
if (strcmp(canonical_name, prop_info.name()) == 0)
- return true;
+ return canonical_name;
}
for (ix = 0; ix < n_ifaces; ix++) {
GjsAutoInterfaceInfo iface_info = g_object_info_get_interface(info, ix);
if (is_ginterface_property_name(iface_info, canonical_name))
- return true;
+ return canonical_name;
}
- return false;
+ return nullptr;
}
// Override of GIWrapperBase::id_is_never_lazy()
@@ -1005,8 +978,13 @@
* method resolution. */
}
- if (is_gobject_property_name(m_info, name))
- return lazy_define_gobject_property(context, obj, id, resolved, name);
+ if (auto const& canonical_name = get_gobject_property_name(m_info, name)) {
+ GjsAutoTypeClass<GObjectClass> gobj_class{m_gtype};
+ if (GParamSpec* pspec =
+ g_object_class_find_property(gobj_class, canonical_name))
+ return lazy_define_gobject_property(context, obj, id, pspec,
+ resolved, name);
+ }
GjsAutoFieldInfo field_info = lookup_field_info(m_info, name);
if (field_info) {
@@ -1032,8 +1010,9 @@
JS::RootedValue private_id(context, JS::StringValue(key));
if (!gjs_define_property_dynamic(
- context, obj, name, "gobject_field", &ObjectBase::field_getter,
- &ObjectBase::field_setter, private_id, flags))
+ context, obj, name, id, "gobject_field",
+ &ObjectBase::field_getter, &ObjectBase::field_setter,
+ private_id, flags))
return false;
*resolved = true;
@@ -1190,10 +1169,10 @@
/* Set properties from args to constructor (args[0] is supposed to be
* a hash) */
-bool ObjectPrototype::props_to_g_parameters(JSContext* context,
- JS::HandleObject props,
- std::vector<const char*>* names,
- AutoGValueVector* values) {
+bool ObjectPrototype::props_to_g_parameters(
+ JSContext* context, GjsAutoTypeClass<GObjectClass> const& object_class,
+ JS::HandleObject props, std::vector<const char*>* names,
+ AutoGValueVector* values) {
size_t ix, length;
JS::RootedId prop_id(context);
JS::RootedValue value(context);
@@ -1215,7 +1194,8 @@
context, m_gtype, gjs_debug_id(prop_id).c_str());
JS::RootedString js_prop_name(context, prop_id.toString());
- GParamSpec *param_spec = find_param_spec_from_id(context, js_prop_name);
+ GParamSpec* param_spec =
+ find_param_spec_from_id(context, object_class, js_prop_name);
if (!param_spec)
return false;
@@ -1241,7 +1221,7 @@
if (!gjs_value_to_g_value(context, value, &gvalue))
return false;
- names->push_back(param_spec->name); /* owned by GParamSpec in cache */
+ names->push_back(param_spec->name); // owned by GParamSpec
}
return true;
@@ -1501,6 +1481,8 @@
void
ObjectInstance::release_native_object(void)
{
+ static GType gdksurface_type = 0;
+
discard_wrapper();
if (m_gobj_finalized) {
@@ -1519,11 +1501,41 @@
if (m_gobj_disposed)
ignore_gobject_finalization();
- if (m_uses_toggle_ref && !m_gobj_disposed)
+ if (m_uses_toggle_ref && !m_gobj_disposed) {
g_object_remove_toggle_ref(m_ptr.release(), wrapped_gobj_toggle_notify,
this);
- else
- m_ptr = nullptr;
+ return;
+ }
+
+ // Unref the object. Handle any special cases for destruction here
+ if (m_ptr->ref_count == 1) {
+ // Quickest way to check for GdkSurface if Gdk has been loaded?
+ // surface_type may be 0 if Gdk not loaded. The type may be a private
+ // type and not have introspection info.
+ if (!gdksurface_type)
+ gdksurface_type = g_type_from_name("GdkSurface");
+ if (gdksurface_type && g_type_is_a(gtype(), gdksurface_type)) {
+ GObject* ptr = m_ptr.release();
+
+ // Workaround for https://gitlab.gnome.org/GNOME/gtk/-/issues/6289
+ GjsAutoObjectInfo surface_info =
+ g_irepository_find_by_gtype(nullptr, gdksurface_type);
+ g_assert(surface_info && "Could not find introspected GdkSurface info");
+ GjsAutoFunctionInfo destroy_func =
+ g_object_info_find_method(surface_info, "destroy");
+ GIArgument destroy_args;
+ gjs_arg_set(&destroy_args, ptr);
+ GIArgument unused_return;
+
+ GjsAutoError err;
+ if (!g_function_info_invoke(destroy_func, &destroy_args, 1, nullptr,
+ 0, &unused_return, err.out()))
+ g_critical("Error destroying GdkSurface %p: %s", ptr,
+ err->message);
+ }
+ }
+
+ m_ptr = nullptr;
}
/* At shutdown, we need to ensure we've cleared the context of any
@@ -1565,7 +1577,7 @@
m_gobj_finalized(false),
m_uses_toggle_ref(false) {
GTypeQuery query;
- type_query_dynamic_safe(&query);
+ g_type_query(gtype(), &query);
if (G_LIKELY(query.type))
JS::AddAssociatedMemory(object, query.instance_size,
MemoryUse::GObjectInstanceStruct);
@@ -1668,12 +1680,12 @@
g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, this);
}
-bool ObjectInstance::ensure_uses_toggle_ref(JSContext* cx) {
+void ObjectInstance::ensure_uses_toggle_ref(JSContext* cx) {
if (m_uses_toggle_ref)
- return true;
+ return;
if (!check_gobject_disposed_or_finalized("add toggle reference on"))
- return true;
+ return;
debug_lifecycle("Switching object instance to toggle ref");
@@ -1698,8 +1710,6 @@
* This may immediately remove the GC root we just added, since refcount
* may drop to 1. */
g_object_unref(m_ptr);
-
- return true;
}
static void invalidate_closure_vector(std::vector<GClosure*>* closures,
@@ -1730,8 +1740,8 @@
{
bool had_toggle_down, had_toggle_up;
- auto locked_queue = ToggleQueue::get_default();
- std::tie(had_toggle_down, had_toggle_up) = locked_queue->cancel(this);
+ std::tie(had_toggle_down, had_toggle_up) =
+ ToggleQueue::get_default()->cancel(this);
if (had_toggle_up && !had_toggle_down) {
g_error(
"JS object wrapper for GObject %p (%s) is being released while "
@@ -1766,20 +1776,30 @@
name(), args.length()))
return false;
+ GjsAutoTypeClass<GObjectClass> object_class(gtype());
std::vector<const char *> names;
AutoGValueVector values;
if (args.length() > 0 && !args[0].isUndefined()) {
if (!args[0].isObject()) {
gjs_throw(context,
- "Argument to the constructor of %s should be an object "
- "with properties to set",
+ "Argument to the constructor of %s should be a plain JS "
+ "object with properties to set",
name());
return false;
}
JS::RootedObject props(context, &args[0].toObject());
- if (!m_proto->props_to_g_parameters(context, props, &names, &values))
+ if (ObjectInstance::typecheck(context, props, nullptr, G_TYPE_NONE,
+ GjsTypecheckNoThrow{})) {
+ gjs_throw(context,
+ "Argument to the constructor of %s should be a plain JS "
+ "object with properties to set",
+ name());
+ return false;
+ }
+ if (!m_proto->props_to_g_parameters(context, object_class, props,
+ &names, &values))
return false;
}
@@ -1822,17 +1842,11 @@
* */
bool toggle_ref_added = false;
if (!m_uses_toggle_ref) {
- if (!other_priv->ensure_uses_toggle_ref(context)) {
- gjs_throw(context,
- "Impossible to set toggle references on %sobject %p",
- other_priv->m_gobj_disposed ? "disposed " : "", gobj);
- return false;
- }
-
+ other_priv->ensure_uses_toggle_ref(context);
toggle_ref_added = m_uses_toggle_ref;
}
- args.rval().setObject(*other_priv->m_wrapper);
+ args.rval().setObject(*other_priv->m_wrapper.get());
if (toggle_ref_added)
g_clear_object(&gobj); /* We already own a reference */
@@ -1897,7 +1911,6 @@
}
void ObjectPrototype::trace_impl(JSTracer* tracer) {
- m_property_cache.trace(tracer);
m_field_cache.trace(tracer);
m_unresolvable_cache.trace(tracer);
for (GClosure* closure : m_vfuncs)
@@ -1906,7 +1919,7 @@
void ObjectInstance::finalize_impl(JS::GCContext* gcx, JSObject* obj) {
GTypeQuery query;
- type_query_dynamic_safe(&query);
+ g_type_query(gtype(), &query);
if (G_LIKELY(query.type))
JS::RemoveAssociatedMemory(obj, query.instance_size,
MemoryUse::GObjectInstanceStruct);
@@ -1978,15 +1991,18 @@
GJS_DEC_COUNTER(object_prototype);
}
-JSObject* gjs_lookup_object_constructor_from_info(JSContext* context,
- GIObjectInfo* info,
- GType gtype) {
+static JSObject* gjs_lookup_object_constructor_from_info(JSContext* context,
+ GIBaseInfo* info,
+ GType gtype) {
+ g_return_val_if_fail(
+ !info || GI_IS_OBJECT_INFO(info) || GI_IS_INTERFACE_INFO(info), NULL);
+
JS::RootedObject in_object(context);
const char *constructor_name;
if (info) {
- in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info);
- constructor_name = g_base_info_get_name((GIBaseInfo*) info);
+ in_object = gjs_lookup_namespace_object(context, info);
+ constructor_name = g_base_info_get_name(info);
} else {
in_object = gjs_lookup_private_namespace(context);
constructor_name = g_type_name(gtype);
@@ -2025,11 +2041,12 @@
}
GJS_JSAPI_RETURN_CONVENTION
-static JSObject *
-gjs_lookup_object_prototype_from_info(JSContext *context,
- GIObjectInfo *info,
- GType gtype)
-{
+static JSObject* gjs_lookup_object_prototype_from_info(JSContext* context,
+ GIBaseInfo* info,
+ GType gtype) {
+ g_return_val_if_fail(
+ !info || GI_IS_OBJECT_INFO(info) || GI_IS_INTERFACE_INFO(info), NULL);
+
JS::RootedObject constructor(context,
gjs_lookup_object_constructor_from_info(context, info, gtype));
@@ -2050,7 +2067,7 @@
gjs_lookup_object_prototype(JSContext *context,
GType gtype)
{
- GjsAutoObjectInfo info = g_irepository_find_by_gtype(nullptr, gtype);
+ GjsAutoObjectInfo info = gjs_lookup_gtype(nullptr, gtype);
return gjs_lookup_object_prototype_from_info(context, info, gtype);
}
@@ -2100,13 +2117,8 @@
}
bool ObjectInstance::associate_closure(JSContext* cx, GClosure* closure) {
- if (!is_prototype()) {
- if (!to_instance()->ensure_uses_toggle_ref(cx)) {
- gjs_throw(cx, "Impossible to set toggle references on %sobject %p",
- m_gobj_disposed ? "disposed " : "", to_instance()->ptr());
- return false;
- }
- }
+ if (!is_prototype())
+ to_instance()->ensure_uses_toggle_ref(cx);
g_assert(std::find(m_closures.begin(), m_closures.end(), closure) ==
m_closures.end() &&
@@ -2148,14 +2160,22 @@
return priv->to_instance()->connect_impl(cx, args, true);
}
-bool
-ObjectInstance::connect_impl(JSContext *context,
- const JS::CallArgs& args,
- bool after)
-{
+bool ObjectBase::connect_object(JSContext* cx, unsigned argc, JS::Value* vp) {
+ GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, obj, ObjectBase, priv);
+ if (!priv->check_is_instance(cx, "connect to signals"))
+ return false;
+
+ return priv->to_instance()->connect_impl(cx, args, false, true);
+}
+
+bool ObjectInstance::connect_impl(JSContext* context, const JS::CallArgs& args,
+ bool after, bool object) {
gulong id;
guint signal_id;
GQuark signal_detail;
+ const char* func_name = object ? "connect_object"
+ : after ? "connect_after"
+ : "connect";
gjs_debug_gsignal("connect obj %p priv %p", m_wrapper.get(), this);
@@ -2166,14 +2186,29 @@
JS::UniqueChars signal_name;
JS::RootedObject callback(context);
- if (!gjs_parse_call_args(context, after ? "connect_after" : "connect", args, "so",
- "signal name", &signal_name,
- "callback", &callback))
- return false;
+ JS::RootedObject associate_obj(context);
+ GConnectFlags flags;
+ if (object) {
+ if (!gjs_parse_call_args(context, func_name, args, "sooi",
+ "signal name", &signal_name, "callback",
+ &callback, "gobject", &associate_obj,
+ "connect_flags", &flags))
+ return false;
- std::string dynamicString = format_name() + '.' +
- (after ? "connect_after" : "connect") + "('" +
- signal_name.get() + "')";
+ if (flags & G_CONNECT_SWAPPED) {
+ gjs_throw(context, "Unsupported connect flag G_CONNECT_SWAPPED");
+ return false;
+ }
+
+ after = flags & G_CONNECT_AFTER;
+ } else {
+ if (!gjs_parse_call_args(context, func_name, args, "so", "signal name",
+ &signal_name, "callback", &callback))
+ return false;
+ }
+
+ std::string dynamicString =
+ format_name() + '.' + func_name + "('" + signal_name.get() + "')";
AutoProfilerLabel label(context, "", dynamicString.c_str());
if (!JS::IsCallable(callback)) {
@@ -2192,8 +2227,17 @@
context, callback, "signal callback", signal_id);
if (closure == NULL)
return false;
- if (!associate_closure(context, closure))
+
+ if (associate_obj.get() != nullptr) {
+ ObjectInstance* obj = ObjectInstance::for_js(context, associate_obj);
+ if (!obj)
+ return false;
+
+ if (!obj->associate_closure(context, closure))
+ return false;
+ } else if (!associate_closure(context, closure)) {
return false;
+ }
id = g_signal_connect_closure_by_id(m_ptr, signal_id, signal_detail,
closure, after);
@@ -2255,6 +2299,7 @@
AutoGValueVector instance_and_args;
instance_and_args.reserve(signal_query.n_params + 1);
+ std::vector<Gjs::AutoGValue*> args_to_steal;
Gjs::AutoGValue& instance = instance_and_args.emplace_back(gtype());
g_value_set_instance(&instance, m_ptr);
@@ -2268,12 +2313,31 @@
if (!gjs_value_to_g_value(context, argv[i + 1], &value))
return false;
}
+
+ if (!ObjectBase::info())
+ continue;
+
+ GjsAutoSignalInfo signal_info = g_object_info_find_signal(
+ ObjectBase::info(), signal_query.signal_name);
+ if (!signal_info)
+ continue;
+
+ GjsAutoArgInfo arg_info = g_callable_info_get_arg(signal_info, i);
+ if (g_arg_info_get_ownership_transfer(arg_info) !=
+ GI_TRANSFER_NOTHING) {
+ // FIXME(3v1n0): As it happens in many places in gjs, we can't track
+ // (yet) containers content, so in case of transfer container we
+ // can only leak.
+ args_to_steal.push_back(&value);
+ }
}
if (signal_query.return_type == G_TYPE_NONE) {
g_signal_emitv(instance_and_args.data(), signal_id, signal_detail,
nullptr);
argv.rval().setUndefined();
+ std::for_each(args_to_steal.begin(), args_to_steal.end(),
+ [](Gjs::AutoGValue* value) { value->steal(); });
return true;
}
@@ -2281,6 +2345,9 @@
Gjs::AutoGValue rvalue(gtype);
g_signal_emitv(instance_and_args.data(), signal_id, signal_detail, &rvalue);
+ std::for_each(args_to_steal.begin(), args_to_steal.end(),
+ [](Gjs::AutoGValue* value) { value->steal(); });
+
return gjs_value_from_g_value(context, argv.rval(), &rvalue);
}
@@ -2555,6 +2622,7 @@
JS_FN("_init", &ObjectBase::init_gobject, 0, 0),
JS_FN("connect", &ObjectBase::connect, 0, 0),
JS_FN("connect_after", &ObjectBase::connect_after, 0, 0),
+ JS_FN("connect_object", &ObjectBase::connect_object, 0, 0),
JS_FN("emit", &ObjectBase::emit, 0, 0),
JS_FS_END
};
@@ -2682,7 +2750,8 @@
// Custom JS objects will most likely have visible state, so just do this
// from the start.
- if (!ensure_uses_toggle_ref(cx) || !m_uses_toggle_ref) {
+ ensure_uses_toggle_ref(cx);
+ if (!m_uses_toggle_ref) {
gjs_throw(cx, "Impossible to set toggle references on %sobject %p",
m_gobj_disposed ? "disposed " : "", gobj);
return false;
@@ -3058,7 +3127,7 @@
{
JSObject *constructor;
- GjsAutoObjectInfo object_info = g_irepository_find_by_gtype(nullptr, gtype);
+ GjsAutoObjectInfo object_info = gjs_lookup_gtype(nullptr, gtype);
constructor = gjs_lookup_object_constructor_from_info(context, object_info, gtype);
diff -ruN cjs-6.2.0-orig/gi/object.h cjs-6.2.0/gi/object.h
--- cjs-6.2.0-orig/gi/object.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/object.h 2024-10-14 18:41:39.000000000 +0200
@@ -104,8 +104,6 @@
[[nodiscard]] bool is_custom_js_class();
public:
- void type_query_dynamic_safe(GTypeQuery* query);
-
GJS_JSAPI_RETURN_CONVENTION
static bool typecheck(JSContext* cx, JS::HandleObject obj,
GIObjectInfo* expected_info, GType expected_gtype);
@@ -141,6 +139,8 @@
GJS_JSAPI_RETURN_CONVENTION
static bool connect_after(JSContext* cx, unsigned argc, JS::Value* vp);
GJS_JSAPI_RETURN_CONVENTION
+ static bool connect_object(JSContext* cx, unsigned argc, JS::Value* vp);
+ GJS_JSAPI_RETURN_CONVENTION
static bool emit(JSContext* cx, unsigned argc, JS::Value* vp);
GJS_JSAPI_RETURN_CONVENTION
static bool signal_find(JSContext* cx, unsigned argc, JS::Value* vp);
@@ -185,16 +185,12 @@
ObjectInstance>;
friend class GIWrapperBase<ObjectBase, ObjectPrototype, ObjectInstance>;
- using PropertyCache =
- JS::GCHashMap<JS::Heap<JSString*>, GjsAutoParam,
- js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
using FieldCache =
- JS::GCHashMap<JS::Heap<JSString*>, GjsAutoInfo<GI_INFO_TYPE_FIELD>,
+ JS::GCHashMap<JS::Heap<JSString*>, GjsAutoFieldInfo,
js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
using NegativeLookupCache =
JS::GCHashSet<JS::Heap<jsid>, IdHasher, js::SystemAllocPolicy>;
- PropertyCache m_property_cache;
FieldCache m_field_cache;
NegativeLookupCache m_unresolvable_cache;
// a list of vfunc GClosures installed on this prototype, used when tracing
@@ -224,8 +220,8 @@
GJS_JSAPI_RETURN_CONVENTION
bool lazy_define_gobject_property(JSContext* cx, JS::HandleObject obj,
- JS::HandleId id, bool* resolved,
- const char* name);
+ JS::HandleId id, GParamSpec*,
+ bool* resolved, const char* name);
enum ResolveWhat { ConsiderOnlyMethods, ConsiderMethodsAndProperties };
GJS_JSAPI_RETURN_CONVENTION
@@ -240,11 +236,15 @@
void set_interfaces(GType* interface_gtypes, uint32_t n_interface_gtypes);
void set_type_qdata(void);
GJS_JSAPI_RETURN_CONVENTION
- GParamSpec* find_param_spec_from_id(JSContext* cx, JS::HandleString key);
+ GParamSpec* find_param_spec_from_id(JSContext*,
+ GjsAutoTypeClass<GObjectClass> const&,
+ JS::HandleString key);
GJS_JSAPI_RETURN_CONVENTION
GIFieldInfo* lookup_cached_field_info(JSContext* cx, JS::HandleString key);
GJS_JSAPI_RETURN_CONVENTION
- bool props_to_g_parameters(JSContext* cx, JS::HandleObject props,
+ bool props_to_g_parameters(JSContext*,
+ GjsAutoTypeClass<GObjectClass> const&,
+ JS::HandleObject props,
std::vector<const char*>* names,
AutoGValueVector* values);
@@ -293,7 +293,7 @@
// GIWrapperInstance::m_ptr may be null in ObjectInstance.
- GjsMaybeOwned<JSObject*> m_wrapper;
+ GjsMaybeOwned m_wrapper;
// a list of all GClosures installed on this object (from signal connections
// and scope-notify callbacks passed to methods), used when tracing
std::vector<GClosure*> m_closures;
@@ -329,7 +329,7 @@
[[nodiscard]] bool has_wrapper() const { return !!m_wrapper; }
public:
- [[nodiscard]] JSObject* wrapper() const { return m_wrapper; }
+ [[nodiscard]] JSObject* wrapper() const { return m_wrapper.get(); }
/* Methods to manipulate the JS object wrapper */
@@ -379,7 +379,7 @@
void track_gobject_finalization();
void ignore_gobject_finalization();
void check_js_object_finalized(void);
- GJS_JSAPI_RETURN_CONVENTION bool ensure_uses_toggle_ref(JSContext* cx);
+ void ensure_uses_toggle_ref(JSContext*);
[[nodiscard]] bool check_gobject_disposed_or_finalized(
const char* for_what) const;
[[nodiscard]] bool check_gobject_finalized(const char* for_what) const;
@@ -430,14 +430,13 @@
private:
GJS_JSAPI_RETURN_CONVENTION
- bool prop_getter_impl(JSContext* cx, JS::HandleString name,
+ bool prop_getter_impl(JSContext* cx, GParamSpec*,
JS::MutableHandleValue rval);
GJS_JSAPI_RETURN_CONVENTION
bool field_getter_impl(JSContext* cx, JS::HandleString name,
JS::MutableHandleValue rval);
GJS_JSAPI_RETURN_CONVENTION
- bool prop_setter_impl(JSContext* cx, JS::HandleString name,
- JS::HandleValue value);
+ bool prop_setter_impl(JSContext* cx, GParamSpec*, JS::HandleValue value);
GJS_JSAPI_RETURN_CONVENTION
bool field_setter_not_impl(JSContext* cx, JS::HandleString name);
@@ -451,7 +450,8 @@
private:
GJS_JSAPI_RETURN_CONVENTION
- bool connect_impl(JSContext* cx, const JS::CallArgs& args, bool after);
+ bool connect_impl(JSContext* cx, const JS::CallArgs& args, bool after,
+ bool object = false);
GJS_JSAPI_RETURN_CONVENTION
bool emit_impl(JSContext* cx, const JS::CallArgs& args);
GJS_JSAPI_RETURN_CONVENTION
@@ -483,10 +483,6 @@
bool gjs_lookup_object_constructor(JSContext *context,
GType gtype,
JS::MutableHandleValue value_p);
-GJS_JSAPI_RETURN_CONVENTION
-JSObject* gjs_lookup_object_constructor_from_info(JSContext* cx,
- GIObjectInfo* info,
- GType gtype);
void gjs_object_clear_toggles(void);
void gjs_object_shutdown_toggle_queue(void);
diff -ruN cjs-6.2.0-orig/gi/param.cpp cjs-6.2.0/gi/param.cpp
--- cjs-6.2.0-orig/gi/param.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/param.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -12,8 +12,10 @@
#include <js/CallArgs.h>
#include <js/Class.h>
#include <js/ErrorReport.h> // for JSEXN_TYPEERR
-#include <js/Object.h> // for GetClass
+#include <js/Object.h> // for GetClass
#include <js/PropertyAndElement.h>
+#include <js/PropertyDescriptor.h> // for JSPROP_READONLY
+#include <js/PropertySpec.h> // for JSPropertySpec, JS_PS_END, JS_STR...
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <js/Utility.h> // for UniqueChars
@@ -148,10 +150,24 @@
nullptr, // mayResolve
param_finalize};
+static JSPropertySpec proto_props[] = {
+ JS_STRING_SYM_PS(toStringTag, "GObject_ParamSpec", JSPROP_READONLY),
+ JS_PS_END};
+
+static constexpr js::ClassSpec class_spec = {
+ nullptr, // createConstructor
+ nullptr, // createPrototype
+ nullptr, // constructorFunctions
+ nullptr, // constructorProperties
+ nullptr, // prototypeFunctions
+ proto_props, // prototypeProperties
+ nullptr // finishInit
+};
+
struct JSClass gjs_param_class = {
"GObject_ParamSpec",
JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
- &gjs_param_class_ops};
+ &gjs_param_class_ops, &class_spec};
GJS_JSAPI_RETURN_CONVENTION
static JSObject*
@@ -187,10 +203,10 @@
if (!gjs_init_class_dynamic(
context, in_object, nullptr, "GObject", "ParamSpec",
&gjs_param_class, gjs_param_constructor, 0,
- nullptr, // props of prototype
- nullptr, // funcs of prototype
- nullptr, // props of constructor, MyConstructor.myprop
- nullptr, // funcs of constructor
+ proto_props, // props of prototype
+ nullptr, // funcs of prototype
+ nullptr, // props of constructor, MyConstructor.myprop
+ nullptr, // funcs of constructor
&prototype, &constructor))
return false;
diff -ruN cjs-6.2.0-orig/gi/private.cpp cjs-6.2.0/gi/private.cpp
--- cjs-6.2.0-orig/gi/private.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/private.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -10,7 +10,8 @@
#include <glib-object.h>
#include <glib.h>
-#include <js/Array.h> // for JS::GetArrayLength,
+#include <js/Array.h> // for JS::GetArrayLength
+#include <js/CallAndConstruct.h> // for IsCallable
#include <js/CallArgs.h>
#include <js/PropertyAndElement.h>
#include <js/PropertySpec.h>
@@ -21,6 +22,7 @@
#include <js/ValueArray.h>
#include <jsapi.h> // for JS_NewPlainObject
+#include "gi/closure.h"
#include "gi/gobject.h"
#include "gi/gtype.h"
#include "gi/interface.h"
@@ -28,6 +30,7 @@
#include "gi/param.h"
#include "gi/private.h"
#include "gi/repo.h"
+#include "gi/value.h"
#include "cjs/atoms.h"
#include "cjs/context-private.h"
#include "cjs/jsapi-util-args.h"
@@ -344,10 +347,11 @@
g_assert(parent_priv);
GTypeQuery query;
- parent_priv->type_query_dynamic_safe(&query);
- if (G_UNLIKELY(query.type == 0)) {
- gjs_throw(cx,
- "Cannot inherit from a non-gjs dynamic type [bug 687184]");
+ g_type_query(parent_priv->gtype(), &query);
+
+ if (G_UNLIKELY(
+ g_type_test_flags(parent_priv->gtype(), G_TYPE_FLAG_FINAL))) {
+ gjs_throw(cx, "Cannot inherit from a final type");
return false;
}
@@ -554,6 +558,37 @@
return true;
}
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_associate_closure(JSContext* context, unsigned argc,
+ JS::Value* vp) {
+ JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
+ JS::RootedObject func_obj(context);
+ JS::RootedObject target_obj(context);
+ Gjs::Closure::Ptr closure;
+ Gjs::AutoGValue value(G_TYPE_CLOSURE);
+ ObjectInstance* obj;
+
+ if (!gjs_parse_call_args(context, "associateClosure", argv, "oo", "object",
+ &target_obj, "func", &func_obj))
+ return false;
+
+ g_assert(JS::IsCallable(func_obj) &&
+ "associateClosure's function must be callable");
+
+ obj = ObjectInstance::for_js(context, target_obj);
+ if (!obj)
+ return false;
+
+ closure =
+ Gjs::Closure::create_marshaled(context, func_obj, "wrapped", false);
+
+ if (!obj->associate_closure(context, closure))
+ return false;
+
+ g_value_set_boxed(&value, closure);
+ return gjs_value_from_g_value(context, argv.rval(), &value);
+}
+
static JSFunctionSpec private_module_funcs[] = {
JS_FN("override_property", gjs_override_property, 2, GJS_MODULE_PROP_FLAGS),
JS_FN("register_interface", gjs_register_interface, 3,
@@ -565,6 +600,7 @@
GJS_MODULE_PROP_FLAGS),
JS_FN("signal_new", gjs_signal_new, 6, GJS_MODULE_PROP_FLAGS),
JS_FN("lookupConstructor", gjs_lookup_constructor, 1, 0),
+ JS_FN("associateClosure", gjs_associate_closure, 2, GJS_MODULE_PROP_FLAGS),
JS_FS_END,
};
diff -ruN cjs-6.2.0-orig/gi/repo.cpp cjs-6.2.0/gi/repo.cpp
--- cjs-6.2.0-orig/gi/repo.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/repo.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -106,6 +106,27 @@
return false;
GjsAutoError error;
+ // If resolving Gio, load the platform-specific typelib first, so that
+ // GioUnix/GioWin32 GTypes get looked up in there with higher priority,
+ // instead of in Gio.
+#if GLIB_CHECK_VERSION(2, 79, 2) && (defined(G_OS_UNIX) || defined(G_OS_WIN32))
+ if (strcmp(ns_name.get(), "Gio") == 0) {
+# ifdef G_OS_UNIX
+ const char* platform = "Unix";
+# else // G_OS_WIN32
+ const char* platform = "Win32";
+# endif // G_OS_UNIX/G_OS_WIN32
+ GjsAutoChar platform_specific =
+ g_strconcat(ns_name.get(), platform, nullptr);
+ if (!g_irepository_require(nullptr, platform_specific, version.get(),
+ GIRepositoryLoadFlags(0), &error)) {
+ gjs_throw(context, "Failed to require %s %s: %s",
+ platform_specific.get(), version.get(), error->message);
+ return false;
+ }
+ }
+#endif // GLib >= 2.79.2
+
g_irepository_require(nullptr, ns_name.get(), version.get(),
GIRepositoryLoadFlags(0), &error);
if (error) {
@@ -122,12 +143,10 @@
gjs_create_ns(context, ns_name.get()));
JS::RootedValue override(context);
- if (!lookup_override_function(context, ns_id, &override))
- return false;
-
- /* Define the property early, to avoid reentrancy issues if
- the override module looks for namespaces that import this */
- if (!JS_DefinePropertyById(context, repo_obj, ns_id, gi_namespace,
+ if (!lookup_override_function(context, ns_id, &override) ||
+ // Define the property early, to avoid reentrancy issues if the override
+ // module looks for namespaces that import this
+ !JS_DefinePropertyById(context, repo_obj, ns_id, gi_namespace,
GJS_MODULE_PROP_FLAGS))
return false;
@@ -216,14 +235,28 @@
*/
JS::RootedString two_point_oh(context, JS_NewStringCopyZ(context, "2.0"));
if (!JS_DefinePropertyById(context, versions, atoms.glib(), two_point_oh,
+ JSPROP_PERMANENT) ||
+ !JS_DefinePropertyById(context, versions, atoms.gobject(), two_point_oh,
+ JSPROP_PERMANENT) ||
+ !JS_DefinePropertyById(context, versions, atoms.gio(), two_point_oh,
JSPROP_PERMANENT))
return nullptr;
- if (!JS_DefinePropertyById(context, versions, atoms.gobject(), two_point_oh,
- JSPROP_PERMANENT))
+
+#if GLIB_CHECK_VERSION(2, 79, 2)
+# if defined(G_OS_UNIX)
+ if (!JS_DefineProperty(context, versions, "GLibUnix", two_point_oh,
+ JSPROP_PERMANENT) ||
+ !JS_DefineProperty(context, versions, "GioUnix", two_point_oh,
+ JSPROP_PERMANENT))
return nullptr;
- if (!JS_DefinePropertyById(context, versions, atoms.gio(), two_point_oh,
- JSPROP_PERMANENT))
+# elif defined(G_OS_WIN32)
+ if (!JS_DefineProperty(context, versions, "GLibWin32", two_point_oh,
+ JSPROP_PERMANENT) ||
+ !JS_DefineProperty(context, versions, "GioWin32", two_point_oh,
+ JSPROP_PERMANENT))
return nullptr;
+# endif // G_OS_UNIX/G_OS_WIN32
+#endif // GLib >= 2.79.2
JS::RootedObject private_ns(context, JS_NewPlainObject(context));
if (!JS_DefinePropertyById(context, repo, atoms.private_ns_marker(),
@@ -249,7 +282,7 @@
GjsAutoTypeInfo type_info = g_constant_info_get_type(info);
- bool ok = gjs_value_from_g_argument(cx, value, type_info, &garg, true);
+ bool ok = gjs_value_from_gi_argument(cx, value, type_info, &garg, true);
g_constant_info_free_value(info, &garg);
return ok;
@@ -662,11 +695,9 @@
gjs_lookup_generic_constructor(JSContext *context,
GIBaseInfo *info)
{
- const char *constructor_name;
-
- JS::RootedObject in_object(context,
- gjs_lookup_namespace_object(context, (GIBaseInfo*) info));
- constructor_name = g_base_info_get_name((GIBaseInfo*) info);
+ JS::RootedObject in_object{context,
+ gjs_lookup_namespace_object(context, info)};
+ const char* constructor_name = g_base_info_get_name(info);
if (G_UNLIKELY (!in_object))
return NULL;
@@ -717,3 +748,39 @@
return JS_NewObjectWithGivenProto(cx, JS::GetClass(proto), proto);
}
+
+// Handle the case where g_irepository_find_by_gtype() returns a type in Gio
+// that should be in GioUnix or GioWin32. This may be an interface, class, or
+// boxed. This function only needs to be called if you are going to do something
+// with the GIBaseInfo that involves handing a JS object to the user. Otherwise,
+// use g_irepository_find_by_gtype() directly.
+GIBaseInfo* gjs_lookup_gtype(GIRepository* repo, GType gtype) {
+ GjsAutoBaseInfo retval = g_irepository_find_by_gtype(repo, gtype);
+ if (!retval)
+ return nullptr;
+
+#if GLIB_CHECK_VERSION(2, 79, 2) && (defined(G_OS_UNIX) || defined(G_OS_WIN32))
+# ifdef G_OS_UNIX
+ static const char* c_prefix = "GUnix";
+ static const char* new_ns = "GioUnix";
+# else // G_OS_WIN32
+ static const char* c_prefix = "GWin32";
+ static const char* new_ns = "GioWin32";
+# endif
+
+ const char* ns = g_base_info_get_namespace(retval);
+ if (strcmp(ns, "Gio") != 0)
+ return retval.release();
+
+ const char* gtype_name = g_type_name(gtype);
+ if (!g_str_has_prefix(gtype_name, c_prefix))
+ return retval.release();
+
+ const char* new_name = gtype_name + strlen(c_prefix);
+ GIBaseInfo* new_info = g_irepository_find_by_name(repo, new_ns, new_name);
+ if (new_info)
+ return new_info;
+#endif // GLib >= 2.79.2
+
+ return retval.release();
+}
diff -ruN cjs-6.2.0-orig/gi/repo.h cjs-6.2.0/gi/repo.h
--- cjs-6.2.0-orig/gi/repo.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/repo.h 2024-10-14 18:41:39.000000000 +0200
@@ -8,6 +8,7 @@
#include <config.h>
#include <girepository.h>
+#include <glib-object.h>
#include <js/TypeDecls.h>
@@ -47,6 +48,8 @@
[[nodiscard]] char* gjs_hyphen_from_camel(const char* camel_name);
+[[nodiscard]] GIBaseInfo* gjs_lookup_gtype(GIRepository*, GType);
+
#if GJS_VERBOSE_ENABLE_GI_USAGE
void _gjs_log_info_usage(GIBaseInfo *info);
#endif
diff -ruN cjs-6.2.0-orig/gi/toggle.cpp cjs-6.2.0/gi/toggle.cpp
--- cjs-6.2.0-orig/gi/toggle.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/toggle.cpp 2024-09-16 22:32:54.000000000 +0200
@@ -6,6 +6,8 @@
// SPDX-FileContributor: Philip Chimento <philip.chimento@gmail.com>
// SPDX-FileContributor: Marco Trevisan <marco.trevisan@canonical.com>
+#include <config.h>
+
#include <algorithm> // for find_if
#include <atomic>
#include <deque>
@@ -146,6 +148,8 @@
}
void ToggleQueue::enqueue(ObjectInstance* obj, ToggleQueue::Direction direction,
+ // https://trac.cppcheck.net/ticket/10733
+ // cppcheck-suppress passedByValue
ToggleQueue::Handler handler) {
g_assert(owns_lock() && "Unsafe access to queue");
diff -ruN cjs-6.2.0-orig/gi/toggle.h cjs-6.2.0/gi/toggle.h
--- cjs-6.2.0-orig/gi/toggle.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/toggle.h 2024-09-16 22:32:54.000000000 +0200
@@ -9,13 +9,15 @@
#ifndef GI_TOGGLE_H_
#define GI_TOGGLE_H_
-#include <glib.h> // for gboolean
+#include <config.h>
#include <atomic>
#include <deque>
#include <thread>
#include <utility> // for pair
+#include <glib.h> // for gboolean
+
class ObjectInstance;
namespace Gjs {
namespace Test {
diff -ruN cjs-6.2.0-orig/gi/utils-inl.h cjs-6.2.0/gi/utils-inl.h
--- cjs-6.2.0-orig/gi/utils-inl.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/utils-inl.h 2024-09-16 22:32:54.000000000 +0200
@@ -8,6 +8,7 @@
#include <stdint.h>
+#include <algorithm> // IWYU pragma: keep (for find)
#include <utility> // IWYU pragma: keep (for swap)
#include <vector>
diff -ruN cjs-6.2.0-orig/gi/value.cpp cjs-6.2.0/gi/value.cpp
--- cjs-6.2.0-orig/gi/value.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/value.cpp 2024-10-14 18:41:39.000000000 +0200
@@ -20,6 +20,7 @@
#include <js/Conversions.h>
#include <js/Exception.h>
#include <js/GCVector.h> // for RootedVector
+#include <js/HeapAPI.h> // for RuntimeHeapIsCollecting
#include <js/Realm.h>
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
@@ -28,7 +29,6 @@
#include <js/ValueArray.h>
#include <js/experimental/TypedData.h>
#include <jsapi.h> // for InformalValueTypeName, JS_Get...
-#include <jsfriendapi.h> // for JS_GetObjectFunction
#include "gi/arg-inl.h"
#include "gi/arg.h"
@@ -41,6 +41,7 @@
#include "gi/js-value-inl.h"
#include "gi/object.h"
#include "gi/param.h"
+#include "gi/repo.h"
#include "gi/union.h"
#include "gi/value.h"
#include "gi/wrapperutils.h"
@@ -54,10 +55,117 @@
GJS_JSAPI_RETURN_CONVENTION
static bool gjs_value_from_g_value_internal(JSContext*, JS::MutableHandleValue,
const GValue*, bool no_copy = false,
- GjsAutoSignalInfo const& = nullptr,
- GjsAutoArgInfo const& = nullptr,
+ bool is_introspected_signal = false,
+ GIArgInfo* = nullptr,
GITypeInfo* = nullptr);
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_arg_set_from_gvalue(JSContext* cx, GIArgument* arg,
+ const GValue* value) {
+ switch (G_VALUE_TYPE(value)) {
+ case G_TYPE_CHAR:
+ gjs_arg_set(arg, g_value_get_schar(value));
+ return true;
+ case G_TYPE_UCHAR:
+ gjs_arg_set(arg, g_value_get_uchar(value));
+ return true;
+ case G_TYPE_BOOLEAN:
+ gjs_arg_set(arg, g_value_get_boolean(value));
+ return true;
+ case G_TYPE_INT:
+ gjs_arg_set(arg, g_value_get_int(value));
+ return true;
+ case G_TYPE_UINT:
+ gjs_arg_set(arg, g_value_get_uint(value));
+ return true;
+ case G_TYPE_LONG:
+ gjs_arg_set<long, GJS_TYPE_TAG_LONG>( // NOLINT(runtime/int)
+ arg, g_value_get_long(value));
+ return true;
+ case G_TYPE_ULONG:
+ gjs_arg_set<unsigned long, // NOLINT(runtime/int)
+ GJS_TYPE_TAG_LONG>(arg, g_value_get_ulong(value));
+ return true;
+ case G_TYPE_INT64:
+ gjs_arg_set(arg, int64_t{g_value_get_int64(value)});
+ return true;
+ case G_TYPE_UINT64:
+ gjs_arg_set(arg, uint64_t{g_value_get_uint64(value)});
+ return true;
+ case G_TYPE_FLOAT:
+ gjs_arg_set(arg, g_value_get_float(value));
+ return true;
+ case G_TYPE_DOUBLE:
+ gjs_arg_set(arg, g_value_get_double(value));
+ return true;
+ case G_TYPE_STRING:
+ gjs_arg_set(arg, g_value_get_string(value));
+ return true;
+ case G_TYPE_POINTER:
+ gjs_arg_set(arg, g_value_get_pointer(value));
+ return true;
+ case G_TYPE_VARIANT:
+ gjs_arg_set(arg, g_value_get_variant(value));
+ return true;
+ default: {
+ if (g_value_fits_pointer(value)) {
+ gjs_arg_set(arg, g_value_peek_pointer(value));
+ return true;
+ }
+
+ GType gtype = G_VALUE_TYPE(value);
+
+ if (g_type_is_a(gtype, G_TYPE_FLAGS)) {
+ gjs_arg_set(arg, g_value_get_flags(value));
+ return true;
+ }
+
+ if (g_type_is_a(gtype, G_TYPE_ENUM)) {
+ gjs_arg_set(arg, g_value_get_enum(value));
+ return true;
+ }
+
+ if (g_type_is_a(gtype, G_TYPE_GTYPE)) {
+ gjs_arg_set<GType, GI_TYPE_TAG_GTYPE>(arg,
+ g_value_get_gtype(value));
+ return true;
+ }
+
+ if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ gjs_arg_set(arg, g_value_get_param(value));
+ return true;
+ }
+ }
+ }
+
+ gjs_throw(cx, "No know GArgument conversion for %s",
+ G_VALUE_TYPE_NAME(value));
+ return false;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool maybe_release_signal_value(JSContext* cx,
+ GjsAutoArgInfo const& arg_info,
+ GITypeInfo* type_info,
+ const GValue* gvalue,
+ GITransfer transfer) {
+ if (transfer == GI_TRANSFER_NOTHING)
+ return true;
+
+ GIArgument arg;
+ if (!gjs_arg_set_from_gvalue(cx, &arg, gvalue))
+ return false;
+
+ if (!gjs_gi_argument_release(cx, transfer, type_info,
+ GjsArgumentFlags::ARG_OUT, &arg)) {
+ gjs_throw(cx, "Cannot release argument %s value, we're gonna leak!",
+ arg_info.name());
+ return false;
+ }
+
+ return true;
+}
+
/*
* Gets signal introspection info about closure, or NULL if not found. Currently
* only works for signals on introspected GObjects, not signals on GJS-defined
@@ -65,8 +173,6 @@
*/
[[nodiscard]] static GjsAutoSignalInfo get_signal_info_if_available(
GSignalQuery* signal_query) {
- GIInfoType info_type;
-
if (!signal_query->itype)
return nullptr;
@@ -75,7 +181,7 @@
if (!obj)
return nullptr;
- info_type = g_base_info_get_type (obj);
+ GIInfoType info_type = obj.type();
if (info_type == GI_INFO_TYPE_OBJECT)
return g_object_info_find_signal(obj, signal_query->signal_name);
else if (info_type == GI_INFO_TYPE_INTERFACE)
@@ -90,22 +196,22 @@
*/
GJS_JSAPI_RETURN_CONVENTION
static bool gjs_value_from_array_and_length_values(
- JSContext* context, GjsAutoSignalInfo const& signal_info,
- JS::MutableHandleValue value_p, GITypeInfo* array_type_info,
- const GValue* array_value, GjsAutoArgInfo const& array_length_arg_info,
- GITypeInfo* array_length_type_info, const GValue* array_length_value,
- bool no_copy) {
+ JSContext* context, JS::MutableHandleValue value_p,
+ GITypeInfo* array_type_info, const GValue* array_value,
+ GIArgInfo* array_length_arg_info, GITypeInfo* array_length_type_info,
+ const GValue* array_length_value, bool no_copy,
+ bool is_introspected_signal) {
JS::RootedValue array_length(context);
- GArgument array_arg;
g_assert(G_VALUE_HOLDS_POINTER(array_value));
g_assert(G_VALUE_HOLDS_INT(array_length_value));
if (!gjs_value_from_g_value_internal(
- context, &array_length, array_length_value, no_copy, signal_info,
+ context, &array_length, array_length_value, no_copy, is_introspected_signal,
array_length_arg_info, array_length_type_info))
return false;
+ GIArgument array_arg;
gjs_arg_set(&array_arg, g_value_get_pointer(array_value));
return gjs_value_from_explicit_array(
@@ -131,7 +237,7 @@
context = m_cx;
GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
- if (G_UNLIKELY(gjs->sweeping())) {
+ if (JS::RuntimeHeapIsCollecting()) {
GSignalInvocationHint *hint = (GSignalInvocationHint*) invocation_hint;
std::ostringstream message;
@@ -189,6 +295,7 @@
GjsAutoArgInfo arg_info;
};
std::vector<ArgumentDetails> args_details(n_param_values);
+ bool needs_cleanup = false;
GjsAutoSignalInfo signal_info = get_signal_info_if_available(&signal_query);
if (signal_info) {
@@ -206,6 +313,11 @@
args_details[array_len_pos + 1].skip = true;
arg_details.array_len_index_for = array_len_pos + 1;
}
+
+ if (!needs_cleanup &&
+ g_arg_info_get_ownership_transfer(arg_details.arg_info) !=
+ GI_TRANSFER_NOTHING)
+ needs_cleanup = true;
}
}
@@ -214,6 +326,7 @@
if (!argv.reserve(n_param_values))
g_error("Unable to reserve space");
JS::RootedValue argv_to_append(context);
+ bool is_introspected_signal = !!signal_info;
for (i = 0; i < n_param_values; ++i) {
const GValue* gval = &param_values[i];
ArgumentDetails& arg_details = args_details[i];
@@ -235,12 +348,12 @@
ArgumentDetails& array_len_details =
args_details[arg_details.array_len_index_for];
res = gjs_value_from_array_and_length_values(
- context, signal_info, &argv_to_append, &arg_details.type_info,
- gval, array_len_details.arg_info, &array_len_details.type_info,
- array_len_gval, no_copy);
+ context, &argv_to_append, &arg_details.type_info, gval,
+ array_len_details.arg_info, &array_len_details.type_info,
+ array_len_gval, no_copy, is_introspected_signal);
} else {
res = gjs_value_from_g_value_internal(
- context, &argv_to_append, gval, no_copy, signal_info,
+ context, &argv_to_append, gval, no_copy, is_introspected_signal,
arg_details.arg_info, &arg_details.type_info);
}
@@ -268,12 +381,29 @@
gjs->exit_immediately(code);
// Some other uncatchable exception, e.g. out of memory
- JSFunction* fn = JS_GetObjectFunction(callable());
- std::string descr =
- fn ? "function " + gjs_debug_string(JS_GetFunctionDisplayId(fn))
- : "callable object " + gjs_debug_object(callable());
g_error("Call to %s terminated with uncatchable exception",
- descr.c_str());
+ gjs_debug_callable(callable()).c_str());
+ }
+ }
+
+ if (needs_cleanup) {
+ for (i = 0; i < n_param_values; ++i) {
+ ArgumentDetails& arg_details = args_details[i];
+ if (!arg_details.arg_info)
+ continue;
+
+ GITransfer transfer =
+ g_arg_info_get_ownership_transfer(arg_details.arg_info);
+
+ if (transfer == GI_TRANSFER_NOTHING)
+ continue;
+
+ if (!maybe_release_signal_value(context, arg_details.arg_info,
+ &arg_details.type_info,
+ &param_values[i], transfer)) {
+ gjs_log_exception(context);
+ return;
+ }
}
}
@@ -635,13 +765,14 @@
/* We don't necessarily have the typelib loaded when
we first see the structure... */
if (registered) {
- GIInfoType info_type = g_base_info_get_type (registered);
+ GIInfoType info_type = registered.type();
if (info_type == GI_INFO_TYPE_STRUCT &&
- g_struct_info_is_foreign ((GIStructInfo*)registered)) {
- GArgument arg;
+ g_struct_info_is_foreign(
+ registered.as<GIStructInfo>())) {
+ GIArgument arg;
- if (!gjs_struct_foreign_convert_to_g_argument(
+ if (!gjs_struct_foreign_convert_to_gi_argument(
context, value, registered, nullptr,
GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING,
GjsArgumentFlags::MAY_BE_NULL, &arg))
@@ -847,10 +978,12 @@
}
GJS_JSAPI_RETURN_CONVENTION
-static bool gjs_value_from_g_value_internal(
- JSContext* context, JS::MutableHandleValue value_p, const GValue* gvalue,
- bool no_copy, GjsAutoSignalInfo const& signal_info,
- GjsAutoArgInfo const& arg_info, GITypeInfo* type_info) {
+static bool gjs_value_from_g_value_internal(JSContext* context,
+ JS::MutableHandleValue value_p,
+ const GValue* gvalue, bool no_copy,
+ bool is_introspected_signal,
+ GIArgInfo* arg_info,
+ GITypeInfo* type_info) {
GType gtype;
gtype = G_VALUE_TYPE(gvalue);
@@ -893,11 +1026,11 @@
} else if (gtype == G_TYPE_DOUBLE) {
double d;
d = g_value_get_double(gvalue);
- value_p.setNumber(d);
+ value_p.setNumber(JS::CanonicalizeNaN(d));
} else if (gtype == G_TYPE_FLOAT) {
double d;
d = g_value_get_float(gvalue);
- value_p.setNumber(d);
+ value_p.setNumber(JS::CanonicalizeNaN(d));
} else if (gtype == G_TYPE_BOOLEAN) {
bool v;
v = g_value_get_boolean(gvalue);
@@ -928,15 +1061,15 @@
return true;
}
- if (!signal_info || !arg_info) {
+ if (!is_introspected_signal || !arg_info) {
gjs_throw(context, "Unknown signal");
return false;
}
+ GITransfer transfer = g_arg_info_get_ownership_transfer(arg_info);
GjsAutoTypeInfo element_info = g_type_info_get_param_type(type_info, 0);
- if (!gjs_array_from_g_value_array(
- context, value_p, element_info,
- g_arg_info_get_ownership_transfer(arg_info), gvalue)) {
+ if (!gjs_array_from_g_value_array(context, value_p, element_info,
+ transfer, gvalue)) {
gjs_throw(context, "Failed to convert array");
return false;
}
@@ -992,7 +1125,7 @@
/* The only way to differentiate unions and structs is from
* their g-i info as both GBoxed */
- GjsAutoBaseInfo info = g_irepository_find_by_gtype(nullptr, gtype);
+ GjsAutoBaseInfo info = gjs_lookup_gtype(nullptr, gtype);
if (!info) {
gjs_throw(context,
"No introspection information found for %s",
@@ -1004,8 +1137,8 @@
g_struct_info_is_foreign(info)) {
GIArgument arg;
gjs_arg_set(&arg, gboxed);
- return gjs_struct_foreign_convert_from_g_argument(context, value_p,
- info, &arg);
+ return gjs_struct_foreign_convert_from_gi_argument(context, value_p,
+ info, &arg);
}
GIInfoType type = info.type();
@@ -1034,9 +1167,7 @@
obj = gjs_param_from_g_param(context, gparam);
value_p.setObjectOrNull(obj);
- } else if (signal_info && g_type_is_a(gtype, G_TYPE_POINTER)) {
- GArgument arg;
-
+ } else if (is_introspected_signal && g_type_is_a(gtype, G_TYPE_POINTER)) {
if (!arg_info) {
gjs_throw(context, "Unknown signal.");
return false;
@@ -1046,10 +1177,11 @@
" calling gjs_value_from_g_value_internal()",
g_type_info_get_array_length(type_info) == -1));
+ GIArgument arg;
gjs_arg_set(&arg, g_value_get_pointer(gvalue));
- return gjs_value_from_g_argument(context, value_p, type_info, &arg,
- true);
+ return gjs_value_from_gi_argument(context, value_p, type_info, &arg,
+ true);
} else if (gtype == G_TYPE_GTYPE) {
GType gvalue_gtype = g_value_get_gtype(gvalue);
diff -ruN cjs-6.2.0-orig/gi/value.h cjs-6.2.0/gi/value.h
--- cjs-6.2.0-orig/gi/value.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/value.h 2024-10-14 18:41:39.000000000 +0200
@@ -51,11 +51,12 @@
break;
default:
// We can't safely move in complex cases, so let's just copy
- *static_cast<GValue*>(this) = G_VALUE_INIT;
+ this->steal();
*this = src;
g_value_unset(&src);
}
}
+ void steal() { *static_cast<GValue*>(this) = G_VALUE_INIT; }
~AutoGValue() { g_value_unset(this); }
};
} // namespace Gjs
diff -ruN cjs-6.2.0-orig/gi/wrapperutils.h cjs-6.2.0/gi/wrapperutils.h
--- cjs-6.2.0-orig/gi/wrapperutils.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/gi/wrapperutils.h 2024-10-14 18:41:39.000000000 +0200
@@ -20,6 +20,7 @@
#include <js/CallArgs.h>
#include <js/ComparisonOperators.h>
#include <js/ErrorReport.h> // for JSEXN_TYPEERR
+#include <js/GCVector.h> // for MutableHandleIdVector
#include <js/Id.h>
#include <js/MemoryFunctions.h>
#include <js/Object.h>
@@ -33,7 +34,7 @@
#include "gi/cwrapper.h"
#include "cjs/atoms.h"
#include "cjs/context-private.h"
-#include "cjs/jsapi-class.h" // IWYU pragma: keep
+#include "cjs/jsapi-class.h"
#include "cjs/jsapi-util.h"
#include "cjs/macros.h"
#include "cjs/profiler-private.h"
diff -ruN cjs-6.2.0-orig/libgjs-private/gjs-gdbus-wrapper.c cjs-6.2.0/libgjs-private/gjs-gdbus-wrapper.c
--- cjs-6.2.0-orig/libgjs-private/gjs-gdbus-wrapper.c 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/libgjs-private/gjs-gdbus-wrapper.c 2024-09-16 22:32:54.000000000 +0200
@@ -4,6 +4,8 @@
* SPDX-FileCopyrightText: 2011 Giovanni Campagna
*/
+#include <config.h>
+
#include <string.h> // for strcmp
#include <gio/gio.h>
diff -ruN cjs-6.2.0-orig/libgjs-private/gjs-match-info.c cjs-6.2.0/libgjs-private/gjs-match-info.c
--- cjs-6.2.0-orig/libgjs-private/gjs-match-info.c 1970-01-01 01:00:00.000000000 +0100
+++ cjs-6.2.0/libgjs-private/gjs-match-info.c 2024-09-16 22:32:54.000000000 +0200
@@ -0,0 +1,357 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+ * SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
+ */
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <stddef.h> /* for NULL */
+#include <stdint.h>
+#include <sys/types.h> /* for ssize_t */
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include "libgjs-private/gjs-match-info.h"
+
+G_DEFINE_BOXED_TYPE(GjsMatchInfo, gjs_match_info, gjs_match_info_ref,
+ gjs_match_info_unref)
+
+struct _GjsMatchInfo {
+ gatomicrefcount refcount;
+ GMatchInfo* base; /* owned */
+ char* str;
+};
+
+/* Takes ownership of string */
+static GjsMatchInfo* new_match_info(GMatchInfo* base, char* s) {
+ GjsMatchInfo* retval = g_new0(GjsMatchInfo, 1);
+ g_atomic_ref_count_init(&retval->refcount);
+ retval->base = base;
+ retval->str = s;
+ return retval;
+}
+
+/**
+ * gjs_match_info_get_regex:
+ * @self: a #GjsMatchInfo
+ *
+ * Wrapper for g_match_info_get_regex().
+ *
+ * Returns: (transfer none): #GRegex object
+ */
+GRegex* gjs_match_info_get_regex(const GjsMatchInfo* self) {
+ g_return_val_if_fail(self != NULL, NULL);
+ return g_match_info_get_regex(self->base);
+}
+
+/**
+ * gjs_match_info_get_string:
+ * @self: a #GjsMatchInfo
+ *
+ * Replacement for g_match_info_get_string(), but the string is owned by @self.
+ *
+ * Returns: (transfer none): the string searched with @match_info
+ */
+const char* gjs_match_info_get_string(const GjsMatchInfo* self) {
+ g_return_val_if_fail(self != NULL, NULL);
+ return self->str;
+}
+
+/**
+ * gjs_match_info_ref:
+ * @self: a #GjsMatchInfo
+ *
+ * Replacement for g_match_info_ref().
+ *
+ * Returns: @self
+ */
+GjsMatchInfo* gjs_match_info_ref(GjsMatchInfo* self) {
+ g_return_val_if_fail(self != NULL, NULL);
+ g_atomic_ref_count_inc(&self->refcount);
+ return self;
+}
+
+/**
+ * gjs_match_info_unref:
+ * @self: a #GjsMatchInfo
+ *
+ * Replacement for g_match_info_unref().
+ */
+void gjs_match_info_unref(GjsMatchInfo* self) {
+ g_return_if_fail(self != NULL);
+ if (g_atomic_ref_count_dec(&self->refcount)) {
+ g_match_info_unref(self->base);
+ g_free(self->str);
+ g_free(self);
+ }
+}
+
+/**
+ * gjs_match_info_free:
+ * @self: (nullable): a #GjsMatchInfo, or %NULL
+ *
+ * Replacement for g_match_info_free().
+ */
+void gjs_match_info_free(GjsMatchInfo* self) {
+ g_return_if_fail(self != NULL);
+ if (self == NULL)
+ return;
+
+ gjs_match_info_unref(self);
+}
+
+/**
+ * gjs_match_info_next:
+ * @self: a #GjsMatchInfo
+ * @error: location to store the error occurring, or %NULL to ignore errors
+ *
+ * Wrapper for g_match_info_next().
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_match_info_next(GjsMatchInfo* self, GError** error) {
+ g_return_val_if_fail(self != NULL, FALSE);
+ return g_match_info_next(self->base, error);
+}
+
+/**
+ * gjs_match_info_matches:
+ * @self: a #GjsMatchInfo
+ *
+ * Wrapper for g_match_info_matches().
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_match_info_matches(const GjsMatchInfo* self) {
+ g_return_val_if_fail(self != NULL, FALSE);
+ return g_match_info_matches(self->base);
+}
+
+/**
+ * gjs_match_info_get_match_count:
+ * @self: a #GjsMatchInfo
+ *
+ * Wrapper for g_match_info_get_match_count().
+ *
+ * Returns: Number of matched substrings, or -1 if an error occurred
+ */
+int gjs_match_info_get_match_count(const GjsMatchInfo* self) {
+ g_return_val_if_fail(self != NULL, -1);
+ return g_match_info_get_match_count(self->base);
+}
+
+/**
+ * gjs_match_info_is_partial_match:
+ * @self: a #GjsMatchInfo
+ *
+ * Wrapper for g_match_info_is_partial_match().
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_match_info_is_partial_match(const GjsMatchInfo* self) {
+ g_return_val_if_fail(self != NULL, FALSE);
+ return g_match_info_is_partial_match(self->base);
+}
+
+/**
+ * gjs_match_info_expand_references:
+ * @self: (nullable): a #GjsMatchInfo or %NULL
+ * @string_to_expand: the string to expand
+ * @error: location to store the error occurring, or %NULL to ignore errors
+ *
+ * Wrapper for g_match_info_expand_references().
+ *
+ * Returns: (nullable): the expanded string, or %NULL if an error occurred
+ */
+char* gjs_match_info_expand_references(const GjsMatchInfo* self,
+ const char* string_to_expand,
+ GError** error) {
+ return g_match_info_expand_references(self->base, string_to_expand, error);
+}
+
+/**
+ * gjs_match_info_fetch:
+ * @self: a #GjsMatchInfo
+ * @match_num: number of the sub expression
+ *
+ * Wrapper for g_match_info_fetch().
+ *
+ * Returns: (nullable): The matched substring, or %NULL if an error occurred.
+ */
+char* gjs_match_info_fetch(const GjsMatchInfo* self, int match_num) {
+ g_return_val_if_fail(self != NULL, NULL);
+ return g_match_info_fetch(self->base, match_num);
+}
+
+/**
+ * gjs_match_info_fetch_pos:
+ * @self: a #GMatchInfo
+ * @match_num: number of the sub expression
+ * @start_pos: (out) (optional): pointer to location for the start position
+ * @end_pos: (out) (optional): pointer to location for the end position
+ *
+ * Wrapper for g_match_info_fetch_pos().
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_match_info_fetch_pos(const GjsMatchInfo* self, int match_num,
+ int* start_pos, int* end_pos) {
+ g_return_val_if_fail(self != NULL, FALSE);
+ return g_match_info_fetch_pos(self->base, match_num, start_pos, end_pos);
+}
+
+/**
+ * gjs_match_info_fetch_named:
+ * @self: a #GjsMatchInfo
+ * @name: name of the subexpression
+ *
+ * Wrapper for g_match_info_fetch_named().
+ *
+ * Returns: (nullable): The matched substring, or %NULL if an error occurred.
+ */
+char* gjs_match_info_fetch_named(const GjsMatchInfo* self, const char* name) {
+ g_return_val_if_fail(self != NULL, NULL);
+ return g_match_info_fetch_named(self->base, name);
+}
+
+/**
+ * gjs_match_info_fetch_named_pos:
+ * @self: a #GMatchInfo
+ * @name: name of the subexpression
+ * @start_pos: (out) (optional): pointer to location for the start position
+ * @end_pos: (out) (optional): pointer to location for the end position
+ *
+ * Wrapper for g_match_info_fetch_named_pos().
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_match_info_fetch_named_pos(const GjsMatchInfo* self,
+ const char* name, int* start_pos,
+ int* end_pos) {
+ g_return_val_if_fail(self != NULL, FALSE);
+ return g_match_info_fetch_named_pos(self->base, name, start_pos, end_pos);
+}
+
+/**
+ * gjs_match_info_fetch_all:
+ * @self: a #GMatchInfo
+ *
+ * Wrapper for g_match_info_fetch_all().
+ *
+ * Returns: (transfer full): a %NULL-terminated array of strings. If the
+ * previous match failed %NULL is returned
+ */
+char** gjs_match_info_fetch_all(const GjsMatchInfo* self) {
+ g_return_val_if_fail(self != NULL, NULL);
+ return g_match_info_fetch_all(self->base);
+}
+
+/**
+ * gjs_regex_match:
+ * @regex: a #GRegex
+ * @s: the string to scan for matches
+ * @match_options: match options
+ * @match_info: (out) (optional): pointer to location for the #GjsMatchInfo
+ *
+ * Wrapper for g_regex_match() that doesn't require the string to be kept alive.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_regex_match(const GRegex* regex, const char* s,
+ GRegexMatchFlags match_options,
+ GjsMatchInfo** match_info) {
+ return gjs_regex_match_full(regex, (const uint8_t*)s, -1, 0, match_options,
+ match_info, NULL);
+}
+
+/**
+ * gjs_regex_match_full:
+ * @regex: a #GRegex
+ * @bytes: (array length=len): the string to scan for matches
+ * @len: the length of @bytes
+ * @start_position: starting index of the string to match, in bytes
+ * @match_options: match options
+ * @match_info: (out) (optional): pointer to location for the #GjsMatchInfo
+ * @error: location to store the error occurring, or %NULL to ignore errors
+ *
+ * Wrapper for g_regex_match_full() that doesn't require the string to be kept
+ * alive.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_regex_match_full(const GRegex* regex, const uint8_t* bytes,
+ ssize_t len, int start_position,
+ GRegexMatchFlags match_options,
+ GjsMatchInfo** match_info, GError** error) {
+ const char* s = (const char*)bytes;
+ if (match_info == NULL)
+ return g_regex_match_full(regex, s, len, start_position, match_options,
+ NULL, error);
+
+ char* string_copy = len < 0 ? g_strdup(s) : g_strndup(s, len);
+ GMatchInfo* base = NULL;
+ bool retval = g_regex_match_full(regex, string_copy, len, start_position,
+ match_options, &base, error);
+
+ if (base)
+ *match_info = new_match_info(base, string_copy);
+
+ return retval;
+}
+
+/**
+ * gjs_regex_match_all:
+ * @regex: a #GRegex
+ * @s: the string to scan for matches
+ * @match_options: match options
+ * @match_info: (out) (optional): pointer to location for the #GjsMatchInfo
+ *
+ * Wrapper for g_regex_match_all() that doesn't require the string to be kept
+ * alive.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_regex_match_all(const GRegex* regex, const char* s,
+ GRegexMatchFlags match_options,
+ GjsMatchInfo** match_info) {
+ return gjs_regex_match_all_full(regex, (const uint8_t*)s, -1, 0,
+ match_options, match_info, NULL);
+}
+
+/**
+ * gjs_regex_match_all_full:
+ * @regex: a #GRegex
+ * @bytes: (array length=len): the string to scan for matches
+ * @len: the length of @bytes
+ * @start_position: starting index of the string to match, in bytes
+ * @match_options: match options
+ * @match_info: (out) (optional): pointer to location for the #GMatchInfo
+ * @error: location to store the error occurring, or %NULL to ignore errors
+ *
+ * Wrapper for g_regex_match_all_full() that doesn't require the string to be
+ * kept alive.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean gjs_regex_match_all_full(const GRegex* regex, const uint8_t* bytes,
+ ssize_t len, int start_position,
+ GRegexMatchFlags match_options,
+ GjsMatchInfo** match_info, GError** error) {
+ const char* s = (const char*)bytes;
+ if (match_info == NULL)
+ return g_regex_match_all_full(regex, s, len, start_position,
+ match_options, NULL, error);
+
+ char* string_copy = len < 0 ? g_strdup(s) : g_strndup(s, len);
+ GMatchInfo* base = NULL;
+ bool retval = g_regex_match_all_full(
+ regex, string_copy, len, start_position, match_options, &base, error);
+
+ if (base)
+ *match_info = new_match_info(base, string_copy);
+
+ return retval;
+}
diff -ruN cjs-6.2.0-orig/libgjs-private/gjs-match-info.h cjs-6.2.0/libgjs-private/gjs-match-info.h
--- cjs-6.2.0-orig/libgjs-private/gjs-match-info.h 1970-01-01 01:00:00.000000000 +0100
+++ cjs-6.2.0/libgjs-private/gjs-match-info.h 2024-10-14 19:19:28.806368787 +0200
@@ -0,0 +1,93 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+ * SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h> /* for ssize_t */
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include "cjs/macros.h"
+
+G_BEGIN_DECLS
+
+/**
+ * GjsMatchInfo:
+ *
+ * A GjsMatchInfo is an opaque struct used to return information about
+ * matches.
+ */
+typedef struct _GjsMatchInfo GjsMatchInfo;
+
+/**
+ * GJS_TYPE_MATCH_INFO:
+ *
+ * The #GType for a boxed type holding a #GjsMatchInfo reference.
+ */
+#define GJS_TYPE_MATCH_INFO (gjs_match_info_get_type())
+
+GJS_EXPORT
+GType gjs_match_info_get_type(void) G_GNUC_CONST;
+
+GJS_EXPORT
+GRegex* gjs_match_info_get_regex(const GjsMatchInfo* self);
+GJS_EXPORT
+const char* gjs_match_info_get_string(const GjsMatchInfo* self);
+
+GJS_EXPORT
+GjsMatchInfo* gjs_match_info_ref(GjsMatchInfo* self);
+GJS_EXPORT
+void gjs_match_info_unref(GjsMatchInfo* self);
+GJS_EXPORT
+void gjs_match_info_free(GjsMatchInfo* self);
+GJS_EXPORT
+gboolean gjs_match_info_next(GjsMatchInfo* self, GError** error);
+GJS_EXPORT
+gboolean gjs_match_info_matches(const GjsMatchInfo* self);
+GJS_EXPORT
+int gjs_match_info_get_match_count(const GjsMatchInfo* self);
+GJS_EXPORT
+gboolean gjs_match_info_is_partial_match(const GjsMatchInfo* self);
+GJS_EXPORT
+char* gjs_match_info_expand_references(const GjsMatchInfo* self,
+ const char* string_to_expand,
+ GError** error);
+GJS_EXPORT
+char* gjs_match_info_fetch(const GjsMatchInfo* self, int match_num);
+GJS_EXPORT
+gboolean gjs_match_info_fetch_pos(const GjsMatchInfo* self, int match_num,
+ int* start_pos, int* end_pos);
+GJS_EXPORT
+char* gjs_match_info_fetch_named(const GjsMatchInfo* self, const char* name);
+GJS_EXPORT
+gboolean gjs_match_info_fetch_named_pos(const GjsMatchInfo* self,
+ const char* name, int* start_pos,
+ int* end_pos);
+GJS_EXPORT
+char** gjs_match_info_fetch_all(const GjsMatchInfo* self);
+
+GJS_EXPORT
+gboolean gjs_regex_match(const GRegex* regex, const char* s,
+ GRegexMatchFlags match_options,
+ GjsMatchInfo** match_info);
+GJS_EXPORT
+gboolean gjs_regex_match_full(const GRegex* regex, const uint8_t* bytes,
+ ssize_t len, int start_position,
+ GRegexMatchFlags match_options,
+ GjsMatchInfo** match_info, GError** error);
+GJS_EXPORT
+gboolean gjs_regex_match_all(const GRegex* regex, const char* s,
+ GRegexMatchFlags match_options,
+ GjsMatchInfo** match_info);
+GJS_EXPORT
+gboolean gjs_regex_match_all_full(const GRegex* regex, const uint8_t* bytes,
+ ssize_t len, int start_position,
+ GRegexMatchFlags match_options,
+ GjsMatchInfo** match_info, GError** error);
+
+G_END_DECLS
diff -ruN cjs-6.2.0-orig/libgjs-private/gjs-util.c cjs-6.2.0/libgjs-private/gjs-util.c
--- cjs-6.2.0-orig/libgjs-private/gjs-util.c 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/libgjs-private/gjs-util.c 2024-09-16 22:32:54.000000000 +0200
@@ -235,7 +235,7 @@
* @store: a #GListStore
* @item: the new item
* @compare_func: (scope call): pairwise comparison function for sorting
- * @user_data: (closure): user data for @compare_func
+ * @user_data: user data for @compare_func
*
* Inserts @item into @store at a position to be determined by the
* @compare_func.
@@ -258,7 +258,7 @@
* gjs_list_store_sort:
* @store: a #GListStore
* @compare_func: (scope call): pairwise comparison function for sorting
- * @user_data: (closure): user data for @compare_func
+ * @user_data: user data for @compare_func
*
* Sort the items in @store according to @compare_func.
*/
@@ -270,7 +270,7 @@
/**
* gjs_gtk_custom_sorter_new:
* @sort_func: (nullable) (scope call): function to sort items
- * @user_data: (closure): user data for @compare_func
+ * @user_data: user data for @sort_func
* @destroy: destroy notify for @user_data
*
* Creates a new `GtkSorter` that works by calling @sort_func to compare items.
@@ -305,7 +305,7 @@
* gjs_gtk_custom_sorter_set_sort_func:
* @sorter: a `GtkCustomSorter`
* @sort_func: (nullable) (scope call): function to sort items
- * @user_data: (closure): user data to pass to @sort_func
+ * @user_data: user data to pass to @sort_func
* @destroy: destroy notify for @user_data
*
* Sets (or unsets) the function used for sorting items.
@@ -423,7 +423,7 @@
/**
* gjs_log_set_writer_func:
* @func: (scope notified): callback with log data
- * @user_data: (closure): user data for @func
+ * @user_data: user data for @func
* @user_data_free: (destroy user_data_free): destroy for @user_data
*
* Sets a given function as the writer function for structured logging,
diff -ruN cjs-6.2.0-orig/libgjs-private/gjs-util.h cjs-6.2.0/libgjs-private/gjs-util.h
--- cjs-6.2.0-orig/libgjs-private/gjs-util.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/libgjs-private/gjs-util.h 2024-10-14 19:19:28.805368787 +0200
@@ -124,11 +124,11 @@
* @target:
* @target_property:
* @flags:
- * @to_callback: (scope notified) (nullable):
- * @to_data: (closure to_callback):
+ * @to_callback: (scope notified) (nullable) (closure to_data):
+ * @to_data:
* @to_notify: (destroy to_data):
- * @from_callback: (scope notified) (nullable):
- * @from_data: (closure from_callback):
+ * @from_callback: (scope notified) (nullable) (closure from_data):
+ * @from_data:
* @from_notify: (destroy from_data):
*
* Returns: (transfer none):
@@ -149,11 +149,11 @@
* @target:
* @target_property:
* @flags:
- * @to_callback: (scope notified) (nullable):
- * @to_data: (closure to_callback):
+ * @to_callback: (scope notified) (nullable) (closure to_data):
+ * @to_data:
* @to_notify: (destroy to_data):
- * @from_callback: (scope notified) (nullable):
- * @from_data: (closure from_callback):
+ * @from_callback: (scope notified) (nullable) (closure from_data):
+ * @from_data:
* @from_notify: (destroy from_data):
*/
GJS_EXPORT
diff -ruN cjs-6.2.0-orig/meson.build cjs-6.2.0/meson.build
--- cjs-6.2.0-orig/meson.build 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/meson.build 2024-10-14 19:04:30.000000000 +0200
@@ -44,7 +44,7 @@
add_project_arguments(cxx.get_supported_arguments([
'-utf-8', # Use UTF-8 mode
'/Zc:externConstexpr', # Required for 'extern constexpr' on MSVC
- '/Zc:preprocessor', # Required to consume the mozjs-115 headers on MSVC
+ '/Zc:preprocessor', # Required to consume the mozjs-128 headers on MSVC
# Ignore spurious compiler warnings for things that GLib and SpiderMonkey
# header files commonly do
@@ -133,22 +133,11 @@
ffi = dependency('libffi', fallback: ['libffi', 'ffi_dep'])
gi = dependency('gobject-introspection-1.0', version: '>= 1.66.0',
fallback: ['gobject-introspection', 'girepo_dep'])
-spidermonkey = dependency('mozjs-115')
-
-# We might need to look for the headers and lib's for Cairo
-# manually on MSVC/clang-cl builds...
-cairo = dependency('cairo', required: get_option('cairo').enabled() and cxx.get_argument_syntax() != 'msvc')
-cairo_gobject = dependency('cairo-gobject', required: cairo.found() and cxx.get_argument_syntax() != 'msvc')
+cairo = dependency('cairo', fallback: ['cairo', 'libcairo_dep'])
+cairo_gobject = dependency('cairo-gobject',
+ fallback: ['cairo', 'libcairogobject_dep'])
cairo_xlib = dependency('cairo-xlib', required: false)
-
-if cxx.get_argument_syntax() == 'msvc'
- if not cairo.found()
- cairo = cc.find_library('cairo', has_headers: ['cairo.h'], required: get_option('cairo').enabled())
- endif
- if not cairo_gobject.found()
- cairo_gobject = cc.find_library('cairo-gobject', has_headers: ['cairo-gobject.h'], required: cairo.found())
- endif
-endif
+spidermonkey = dependency('mozjs-128')
sysprof_capture = dependency('sysprof-capture-4',
required: get_option('profiler'), include_type: 'system',
@@ -450,7 +439,6 @@
'cjs/promise.cpp', 'cjs/promise.h',
'cjs/stack.cpp',
'modules/console.cpp', 'modules/console.h',
- 'modules/modules.cpp', 'modules/modules.h',
'modules/print.cpp', 'modules/print.h',
'modules/system.cpp', 'modules/system.h',
]
diff -ruN cjs-6.2.0-orig/modules/cairo-context.cpp cjs-6.2.0/modules/cairo-context.cpp
--- cjs-6.2.0-orig/modules/cairo-context.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/cairo-context.cpp 2024-10-14 18:50:11.000000000 +0200
@@ -90,10 +90,11 @@
JS::RootedObject array(context, JS::NewArrayObject(context, 2)); \
if (!array) \
return false; \
- JS::RootedValue r(context, JS::NumberValue(arg1)); \
+ JS::RootedValue r{context, \
+ JS::NumberValue(JS::CanonicalizeNaN(arg1))}; \
if (!JS_SetElement(context, array, 0, r)) \
return false; \
- r.setNumber(arg2); \
+ r.setNumber(JS::CanonicalizeNaN(arg2)); \
if (!JS_SetElement(context, array, 1, r)) \
return false; \
argv.rval().setObject(*array); \
@@ -109,10 +110,11 @@
JS::RootedObject array(context, JS::NewArrayObject(context, 2)); \
if (!array) \
return false; \
- JS::RootedValue r(context, JS::NumberValue(arg1)); \
+ JS::RootedValue r{context, \
+ JS::NumberValue(JS::CanonicalizeNaN(arg1))}; \
if (!JS_SetElement(context, array, 0, r)) \
return false; \
- r.setNumber(arg2); \
+ r.setNumber(JS::CanonicalizeNaN(arg2)); \
if (!JS_SetElement(context, array, 1, r)) \
return false; \
argv.rval().setObject(*array); \
@@ -128,16 +130,17 @@
JS::RootedObject array(context, JS::NewArrayObject(context, 4)); \
if (!array) \
return false; \
- JS::RootedValue r(context, JS::NumberValue(arg1)); \
+ JS::RootedValue r{context, \
+ JS::NumberValue(JS::CanonicalizeNaN(arg1))}; \
if (!JS_SetElement(context, array, 0, r)) \
return false; \
- r.setNumber(arg2); \
+ r.setNumber(JS::CanonicalizeNaN(arg2)); \
if (!JS_SetElement(context, array, 1, r)) \
return false; \
- r.setNumber(arg3); \
+ r.setNumber(JS::CanonicalizeNaN(arg3)); \
if (!JS_SetElement(context, array, 2, r)) \
return false; \
- r.setNumber(arg4); \
+ r.setNumber(JS::CanonicalizeNaN(arg4)); \
if (!JS_SetElement(context, array, 3, r)) \
return false; \
argv.rval().setObject(*array); \
@@ -149,7 +152,7 @@
double ret; \
_GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \
ret = cfunc(cr); \
- argv.rval().setNumber(ret); \
+ argv.rval().setNumber(JS::CanonicalizeNaN(ret)); \
_GJS_CAIRO_CONTEXT_DEFINE_FUNC_END
#define _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(method, cfunc, fmt, t1, n1) \
@@ -919,7 +922,7 @@
JS_FS_END};
// clang-format on
-[[nodiscard]] static bool context_to_g_argument(
+[[nodiscard]] static bool context_to_gi_argument(
JSContext* context, JS::Value value, const char* arg_name,
GjsArgumentType argument_type, GITransfer transfer, GjsArgumentFlags flags,
GIArgument* arg) {
@@ -947,11 +950,9 @@
}
GJS_JSAPI_RETURN_CONVENTION
-static bool
-context_from_g_argument(JSContext *context,
- JS::MutableHandleValue value_p,
- GIArgument *arg)
-{
+static bool context_from_gi_argument(JSContext* context,
+ JS::MutableHandleValue value_p,
+ GIArgument* arg) {
JSObject* obj = CairoContext::from_c_ptr(
context, static_cast<cairo_t*>(arg->v_pointer));
if (!obj) {
@@ -971,8 +972,8 @@
}
void gjs_cairo_context_init(void) {
- static GjsForeignInfo foreign_info = {context_to_g_argument,
- context_from_g_argument,
+ static GjsForeignInfo foreign_info = {context_to_gi_argument,
+ context_from_gi_argument,
context_release_argument};
gjs_struct_foreign_register("cairo", "Context", &foreign_info);
diff -ruN cjs-6.2.0-orig/modules/cairo-pdf-surface.cpp cjs-6.2.0/modules/cairo-pdf-surface.cpp
--- cjs-6.2.0-orig/modules/cairo-pdf-surface.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/cairo-pdf-surface.cpp 2024-10-14 19:07:48.000000000 +0200
@@ -8,19 +8,23 @@
#include <cairo-features.h> // for CAIRO_HAS_PDF_SURFACE
#include <cairo.h>
-#include <js/TypeDecls.h>
-
-#include "cjs/jsapi-util.h"
-
#if CAIRO_HAS_PDF_SURFACE
# include <cairo-pdf.h>
+#endif
+
+#include <js/TypeDecls.h>
+#if CAIRO_HAS_PDF_SURFACE
# include <js/PropertyDescriptor.h> // for JSPROP_READONLY
# include <js/PropertySpec.h>
# include <js/RootingAPI.h>
-# include <jsapi.h> // for JS_NewObjectWithGivenProto
+# include <jsapi.h> // for JS_NewObjectWithGivenProto
# include <jspubtd.h> // for JSProtoKey
+#endif
+#include "cjs/jsapi-util.h"
+
+#if CAIRO_HAS_PDF_SURFACE
# include "cjs/jsapi-util-args.h"
# include "modules/cairo-private.h"
diff -ruN cjs-6.2.0-orig/modules/cairo-ps-surface.cpp cjs-6.2.0/modules/cairo-ps-surface.cpp
--- cjs-6.2.0-orig/modules/cairo-ps-surface.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/cairo-ps-surface.cpp 2024-10-14 19:07:48.000000000 +0200
@@ -8,19 +8,23 @@
#include <cairo-features.h> // for CAIRO_HAS_PS_SURFACE
#include <cairo.h>
-#include <js/TypeDecls.h>
-
-#include "cjs/jsapi-util.h"
-
#if CAIRO_HAS_PS_SURFACE
# include <cairo-ps.h>
+#endif
+
+#include <js/TypeDecls.h>
+#if CAIRO_HAS_PS_SURFACE
# include <js/PropertyDescriptor.h> // for JSPROP_READONLY
# include <js/PropertySpec.h>
# include <js/RootingAPI.h>
-# include <jsapi.h> // for JS_NewObjectWithGivenProto
+# include <jsapi.h> // for JS_NewObjectWithGivenProto
# include <jspubtd.h> // for JSProtoKey
+#endif
+#include "cjs/jsapi-util.h"
+
+#if CAIRO_HAS_PS_SURFACE
# include "cjs/jsapi-util-args.h"
# include "modules/cairo-private.h"
diff -ruN cjs-6.2.0-orig/modules/cairo-region.cpp cjs-6.2.0/modules/cairo-region.cpp
--- cjs-6.2.0-orig/modules/cairo-region.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/cairo-region.cpp 2024-10-14 18:50:11.000000000 +0200
@@ -227,7 +227,7 @@
cairo_region_destroy(region);
}
-[[nodiscard]] static bool region_to_g_argument(
+[[nodiscard]] static bool region_to_gi_argument(
JSContext* context, JS::Value value, const char* arg_name,
GjsArgumentType argument_type, GITransfer transfer, GjsArgumentFlags flags,
GIArgument* arg) {
@@ -256,11 +256,9 @@
}
GJS_JSAPI_RETURN_CONVENTION
-static bool
-region_from_g_argument(JSContext *context,
- JS::MutableHandleValue value_p,
- GIArgument *arg)
-{
+static bool region_from_gi_argument(JSContext* context,
+ JS::MutableHandleValue value_p,
+ GIArgument* arg) {
JSObject* obj =
CairoRegion::from_c_ptr(context, gjs_arg_get<cairo_region_t*>(arg));
if (!obj)
@@ -279,8 +277,9 @@
void gjs_cairo_region_init(void) {
- static GjsForeignInfo foreign_info = {
- region_to_g_argument, region_from_g_argument, region_release_argument};
+ static GjsForeignInfo foreign_info = {region_to_gi_argument,
+ region_from_gi_argument,
+ region_release_argument};
gjs_struct_foreign_register("cairo", "Region", &foreign_info);
}
diff -ruN cjs-6.2.0-orig/modules/cairo-surface.cpp cjs-6.2.0/modules/cairo-surface.cpp
--- cjs-6.2.0-orig/modules/cairo-surface.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/cairo-surface.cpp 2024-10-14 18:50:11.000000000 +0200
@@ -173,8 +173,8 @@
// cannot error
JS::RootedValueArray<2> elements(cx);
- elements[0].setNumber(x_offset);
- elements[1].setNumber(y_offset);
+ elements[0].setNumber(JS::CanonicalizeNaN(x_offset));
+ elements[1].setNumber(JS::CanonicalizeNaN(y_offset));
JS::RootedObject retval(cx, JS::NewArrayObject(cx, elements));
if (!retval)
return false;
@@ -223,8 +223,8 @@
// cannot error
JS::RootedValueArray<2> elements(cx);
- elements[0].setNumber(x_scale);
- elements[1].setNumber(y_scale);
+ elements[0].setNumber(JS::CanonicalizeNaN(x_scale));
+ elements[1].setNumber(JS::CanonicalizeNaN(y_scale));
JS::RootedObject retval(cx, JS::NewArrayObject(cx, elements));
if (!retval)
return false;
@@ -324,7 +324,7 @@
surface_wrapper, CairoSurface::POINTER);
}
-[[nodiscard]] static bool surface_to_g_argument(
+[[nodiscard]] static bool surface_to_gi_argument(
JSContext* context, JS::Value value, const char* arg_name,
GjsArgumentType argument_type, GITransfer transfer, GjsArgumentFlags flags,
GIArgument* arg) {
@@ -359,9 +359,9 @@
}
GJS_JSAPI_RETURN_CONVENTION
-static bool surface_from_g_argument(JSContext* cx,
- JS::MutableHandleValue value_p,
- GIArgument* arg) {
+static bool surface_from_gi_argument(JSContext* cx,
+ JS::MutableHandleValue value_p,
+ GIArgument* arg) {
JSObject* obj =
CairoSurface::from_c_ptr(cx, gjs_arg_get<cairo_surface_t*>(arg));
if (!obj)
@@ -379,8 +379,8 @@
}
void gjs_cairo_surface_init(void) {
- static GjsForeignInfo foreign_info = {surface_to_g_argument,
- surface_from_g_argument,
+ static GjsForeignInfo foreign_info = {surface_to_gi_argument,
+ surface_from_gi_argument,
surface_release_argument};
gjs_struct_foreign_register("cairo", "Surface", &foreign_info);
}
diff -ruN cjs-6.2.0-orig/modules/cairo-svg-surface.cpp cjs-6.2.0/modules/cairo-svg-surface.cpp
--- cjs-6.2.0-orig/modules/cairo-svg-surface.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/cairo-svg-surface.cpp 2024-10-14 19:07:26.000000000 +0200
@@ -8,19 +8,23 @@
#include <cairo-features.h> // for CAIRO_HAS_SVG_SURFACE
#include <cairo.h>
-#include <js/TypeDecls.h>
-
-#include "cjs/jsapi-util.h"
-
#if CAIRO_HAS_SVG_SURFACE
# include <cairo-svg.h>
+#endif
+
+#include <js/TypeDecls.h>
+#if CAIRO_HAS_SVG_SURFACE
# include <js/PropertyDescriptor.h> // for JSPROP_READONLY
# include <js/PropertySpec.h>
# include <js/RootingAPI.h>
-# include <jsapi.h> // for JS_NewObjectWithGivenProto
+# include <jsapi.h> // for JS_NewObjectWithGivenProto
# include <jspubtd.h> // for JSProtoKey
+#endif
+#include "cjs/jsapi-util.h"
+
+#if CAIRO_HAS_SVG_SURFACE
# include "cjs/jsapi-util-args.h"
# include "modules/cairo-private.h"
diff -ruN cjs-6.2.0-orig/modules/cairo.cpp cjs-6.2.0/modules/cairo.cpp
--- cjs-6.2.0-orig/modules/cairo.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/cairo.cpp 2024-10-14 18:50:11.000000000 +0200
@@ -7,6 +7,13 @@
#include <cairo-features.h> // for CAIRO_HAS_PDF_SURFACE, CAIRO_HAS_PS_SURFA...
#include <cairo.h>
+#ifdef CAIRO_HAS_XLIB_SURFACE
+# include <cairo-xlib.h>
+# undef None
+// X11 defines a global None macro. Rude! This conflicts with None used as an
+// enum member in SpiderMonkey headers, e.g. JS::ExceptionStatus::None.
+#endif
+
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
#include <jsapi.h> // for JS_NewPlainObject
@@ -15,8 +22,6 @@
#include "modules/cairo-private.h"
#ifdef CAIRO_HAS_XLIB_SURFACE
-# include <cairo-xlib.h>
-
class XLibConstructor {
public:
XLibConstructor() {
diff -ruN cjs-6.2.0-orig/modules/console.cpp cjs-6.2.0/modules/console.cpp
--- cjs-6.2.0-orig/modules/console.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/console.cpp 2024-10-14 18:50:11.000000000 +0200
@@ -87,12 +87,18 @@
JS::PrintError(stderr, report, /* reportWarnings = */ false);
if (exnStack.stack()) {
- GjsAutoChar stack_str =
- gjs_format_stack_trace(m_cx, exnStack.stack());
- if (!stack_str)
+ JS::UniqueChars stack_str{
+ format_saved_frame(m_cx, exnStack.stack(), 2)};
+ if (!stack_str) {
g_printerr("(Unable to print stack trace)\n");
- else
- g_printerr("%s", stack_str.get());
+ } else {
+ GjsAutoChar encoded_stack_str{g_filename_from_utf8(
+ stack_str.get(), -1, nullptr, nullptr, nullptr)};
+ if (!encoded_stack_str)
+ g_printerr("(Unable to print stack trace)\n");
+ else
+ g_printerr("%s", stack_str.get());
+ }
}
JS_ClearPendingException(m_cx);
@@ -194,9 +200,6 @@
GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx);
gjs->schedule_gc_if_needed();
- if (result.isUndefined())
- return true;
-
JS::AutoSaveExceptionState exc_state(cx);
JS::RootedValue v_printed_string(cx);
JS::RootedValue v_pretty_print(
@@ -301,7 +304,7 @@
if (!ok) {
/* If this was an uncatchable exception, throw another uncatchable
* exception on up to the surrounding JS::Evaluate() in main(). This
- * happens when you run cjs-console and type imports.system.exit(0);
+ * happens when you run gjs-console and type imports.system.exit(0);
* at the prompt. If we don't throw another uncatchable exception
* here, then it's swallowed and main() won't exit. */
return false;
diff -ruN cjs-6.2.0-orig/modules/core/_common.js cjs-6.2.0/modules/core/_common.js
--- cjs-6.2.0-orig/modules/core/_common.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/core/_common.js 2024-09-16 22:32:54.000000000 +0200
@@ -3,7 +3,8 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2020 Philip Chimento <philip.chimento@gmail.com>
-/* exported _checkAccessors, _registerType */
+/* exported _checkAccessors, _createBuilderConnectFunc, _createClosure,
+_registerType */
// This is a helper module in which to put code that is common between the
// legacy GObject.Class system and the new GObject.registerClass system.
@@ -92,3 +93,32 @@
Object.defineProperty(proto, camelName, propdesc);
}
}
+
+function _createClosure(builder, thisArg, handlerName, swapped, connectObject) {
+ connectObject ??= thisArg;
+
+ if (swapped) {
+ throw new Error('Unsupported template signal flag "swapped"');
+ } else if (typeof thisArg[handlerName] === 'undefined') {
+ throw new Error(`A handler called ${handlerName} was not ` +
+ `defined on ${thisArg}`);
+ }
+
+ return thisArg[handlerName].bind(connectObject);
+}
+
+function _createBuilderConnectFunc(klass) {
+ const {GObject} = imports.gi;
+ return function (builder, obj, signalName, handlerName, connectObj, flags) {
+ const objects = builder.get_objects();
+ const thisObj = objects.find(o => o instanceof klass);
+ const swapped = flags & GObject.ConnectFlags.SWAPPED;
+ const closure = _createClosure(builder, thisObj, handlerName, swapped,
+ connectObj);
+
+ if (flags & GObject.ConnectFlags.AFTER)
+ obj.connect_after(signalName, closure);
+ else
+ obj.connect(signalName, closure);
+ };
+}
diff -ruN cjs-6.2.0-orig/modules/core/_format.js cjs-6.2.0/modules/core/_format.js
--- cjs-6.2.0-orig/modules/core/_format.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/core/_format.js 2024-09-16 22:32:54.000000000 +0200
@@ -5,7 +5,7 @@
/* exported vprintf */
-const CjsPrivate = imports.gi.CjsPrivate;
+const GjsPrivate = imports.gi.GjsPrivate;
function vprintf(string, args) {
let i = 0;
@@ -48,7 +48,7 @@
case 'd': {
let intV = parseInt(getArg());
if (hasAlternativeIntFlag)
- s = CjsPrivate.format_int_alternative_output(intV);
+ s = GjsPrivate.format_int_alternative_output(intV);
else
s = intV.toString();
break;
diff -ruN cjs-6.2.0-orig/modules/core/_gettext.js cjs-6.2.0/modules/core/_gettext.js
--- cjs-6.2.0-orig/modules/core/_gettext.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/core/_gettext.js 2024-09-16 22:32:54.000000000 +0200
@@ -19,19 +19,19 @@
*/
const GLib = imports.gi.GLib;
-const CjsPrivate = imports.gi.CjsPrivate;
+const GjsPrivate = imports.gi.GjsPrivate;
-var LocaleCategory = CjsPrivate.LocaleCategory;
+var LocaleCategory = GjsPrivate.LocaleCategory;
function setlocale(category, locale) {
- return CjsPrivate.setlocale(category, locale);
+ return GjsPrivate.setlocale(category, locale);
}
function textdomain(dom) {
- return CjsPrivate.textdomain(dom);
+ return GjsPrivate.textdomain(dom);
}
function bindtextdomain(dom, location) {
- return CjsPrivate.bindtextdomain(dom, location);
+ return GjsPrivate.bindtextdomain(dom, location);
}
function gettext(msgid) {
diff -ruN cjs-6.2.0-orig/modules/core/overrides/GLib.js cjs-6.2.0/modules/core/overrides/GLib.js
--- cjs-6.2.0-orig/modules/core/overrides/GLib.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/core/overrides/GLib.js 2024-09-16 22:32:54.000000000 +0200
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2011 Giovanni Campagna
+// SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
-const ByteArray = imports._byteArrayNative;
const {setMainLoopHook} = imports._promiseNative;
let GLib;
@@ -100,11 +100,8 @@
}
if (arrayType[0] === 'y') {
// special case for array of bytes
- if (typeof value === 'string') {
- value = ByteArray.fromString(value);
- if (value[value.length - 1] !== 0)
- value = Uint8Array.of(...value, 0);
- }
+ if (typeof value === 'string')
+ value = Uint8Array.of(...new TextEncoder().encode(value), 0);
const bytes = new GLib.Bytes(value);
return GLib.Variant.new_from_bytes(new GLib.VariantType('ay'),
bytes, true);
@@ -363,23 +360,23 @@
GLib.log_variant(logDomain, logLevel, new GLib.Variant('a{sv}', variantFields));
};
- // CjsPrivate depends on GLib so we cannot import it
+ // GjsPrivate depends on GLib so we cannot import it
// before GLib is fully resolved.
this.log_set_writer_func_variant = function (...args) {
- const {log_set_writer_func} = imports.gi.CjsPrivate;
+ const {log_set_writer_func} = imports.gi.GjsPrivate;
log_set_writer_func(...args);
};
this.log_set_writer_default = function (...args) {
- const {log_set_writer_default} = imports.gi.CjsPrivate;
+ const {log_set_writer_default} = imports.gi.GjsPrivate;
log_set_writer_default(...args);
};
this.log_set_writer_func = function (writer_func) {
- const {log_set_writer_func} = imports.gi.CjsPrivate;
+ const {log_set_writer_func} = imports.gi.GjsPrivate;
if (typeof writer_func !== 'function') {
log_set_writer_func(writer_func);
@@ -538,4 +535,67 @@
this.Thread.prototype.unref = function () {
throw new Error('\'GLib.Thread.unref()\' may not be called in GJS');
};
+
+ // Override GLib.MatchInfo with a type that keeps the UTF-8 encoded search
+ // string alive.
+ const oldMatchInfo = this.MatchInfo;
+ let matchInfoPatched = false;
+ function patchMatchInfo(GLibModule) {
+ if (matchInfoPatched)
+ return;
+
+ const {MatchInfo} = imports.gi.GjsPrivate;
+
+ const originalMatchInfoMethods = new Set(Object.keys(oldMatchInfo.prototype));
+ const overriddenMatchInfoMethods = new Set(Object.keys(MatchInfo.prototype));
+ const symmetricDifference = new Set(originalMatchInfoMethods);
+ for (const method of overriddenMatchInfoMethods) {
+ if (symmetricDifference.has(method))
+ symmetricDifference.delete(method);
+ else
+ symmetricDifference.add(method);
+ }
+ if (symmetricDifference.size !== 0)
+ throw new Error(`Methods of GMatchInfo and GjsMatchInfo don't match: ${[...symmetricDifference]}`);
+
+ GLibModule.MatchInfo = MatchInfo;
+ matchInfoPatched = true;
+ }
+
+ // We can't monkeypatch GLib.MatchInfo directly at override time, because
+ // importing GjsPrivate requires GLib. So this monkeypatches GLib.MatchInfo
+ // with a Proxy that overwrites itself with the real GjsPrivate.MatchInfo
+ // as soon as you try to do anything with it.
+ const allProxyOperations = ['apply', 'construct', 'defineProperty',
+ 'deleteProperty', 'get', 'getOwnPropertyDescriptor', 'getPrototypeOf',
+ 'has', 'isExtensible', 'ownKeys', 'preventExtensions', 'set',
+ 'setPrototypeOf'];
+ function delegateToMatchInfo(op) {
+ return function (target, ...params) {
+ patchMatchInfo(GLib);
+ return Reflect[op](GLib.MatchInfo, ...params);
+ };
+ }
+ this.MatchInfo = new Proxy(function () {},
+ Object.fromEntries(allProxyOperations.map(op => [op, delegateToMatchInfo(op)])));
+
+ this.Regex.prototype.match = function (...args) {
+ patchMatchInfo(GLib);
+ return imports.gi.GjsPrivate.regex_match(this, ...args);
+ };
+
+ this.Regex.prototype.match_full = function (...args) {
+ patchMatchInfo(GLib);
+ return imports.gi.GjsPrivate.regex_match_full(this, ...args);
+ };
+
+ this.Regex.prototype.match_all = function (...args) {
+ patchMatchInfo(GLib);
+ return imports.gi.GjsPrivate.regex_match_all(this, ...args);
+ };
+
+ this.Regex.prototype.match_all_full = function (...args) {
+ patchMatchInfo(GLib);
+ return imports.gi.GjsPrivate.regex_match_all_full(this, ...args);
+ };
}
diff -ruN cjs-6.2.0-orig/modules/core/overrides/GObject.js cjs-6.2.0/modules/core/overrides/GObject.js
--- cjs-6.2.0-orig/modules/core/overrides/GObject.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/core/overrides/GObject.js 2024-09-16 22:32:54.000000000 +0200
@@ -4,7 +4,7 @@
// SPDX-FileCopyrightText: 2017 Philip Chimento <philip.chimento@gmail.com>, <philip@endlessm.com>
const Gi = imports._gi;
-const {CjsPrivate, GLib} = imports.gi;
+const {GjsPrivate, GLib} = imports.gi;
const {_checkAccessors, _registerType} = imports._common;
const Legacy = imports._legacy;
@@ -179,7 +179,7 @@
function _createGTypeName(klass) {
const sanitizeGType = s => s.replace(/[^a-z0-9+_-]/gi, '_');
- if (klass.hasOwnProperty(GTypeName)) {
+ if (Object.hasOwn(klass, GTypeName)) {
let sanitized = sanitizeGType(klass[GTypeName]);
if (sanitized !== klass[GTypeName]) {
logError(new RangeError(`Provided GType name '${klass[GTypeName]}' ` +
@@ -203,7 +203,7 @@
function _propertiesAsArray(klass) {
let propertiesArray = [];
- if (klass.hasOwnProperty(properties)) {
+ if (Object.hasOwn(klass, properties)) {
for (let prop in klass[properties])
propertiesArray.push(klass[properties][prop]);
}
@@ -218,7 +218,7 @@
// Ignore properties starting with __
(typeof key !== 'string' || !key.startsWith('__')) &&
// Don't override an implementation on the target
- !targetPrototype.hasOwnProperty(key) &&
+ !Object.hasOwn(targetPrototype, key) &&
descriptor &&
// Only copy if the descriptor has a getter, is a function, or is enumerable.
(typeof descriptor.value === 'function' || descriptor.get || descriptor.enumerable))
@@ -273,8 +273,90 @@
}
}
+function _registerGObjectType(klass) {
+ const gtypename = _createGTypeName(klass);
+ const gflags = Object.hasOwn(klass, GTypeFlags) ? klass[GTypeFlags] : 0;
+ const gobjectInterfaces = Object.hasOwn(klass, interfaces) ? klass[interfaces] : [];
+ const propertiesArray = _propertiesAsArray(klass);
+ const parent = Object.getPrototypeOf(klass);
+ const gobjectSignals = Object.hasOwn(klass, signals) ? klass[signals] : [];
+
+ // Default to the GObject-specific prototype, fallback on the JS prototype
+ // for GI native classes.
+ const parentPrototype = parent.prototype[Gi.gobject_prototype_symbol] ?? parent.prototype;
+
+ const [giPrototype, registeredType] = Gi.register_type_with_class(klass,
+ parentPrototype, gtypename, gflags, gobjectInterfaces, propertiesArray);
+
+ _defineGType(klass, giPrototype, registeredType);
+ _createSignals(klass.$gtype, gobjectSignals);
+
+ // Reverse the interface array to give the last required interface
+ // precedence over the first.
+ const requiredInterfaces = [...gobjectInterfaces].reverse();
+ requiredInterfaces.forEach(iface =>
+ _copyInterfacePrototypeDescriptors(klass, iface));
+ requiredInterfaces.forEach(iface =>
+ _copyInterfacePrototypeDescriptors(klass.prototype, iface.prototype));
+
+ Object.getOwnPropertyNames(klass.prototype)
+ .filter(name => name.startsWith('vfunc_') || name.startsWith('on_'))
+ .forEach(name => {
+ let descr = Object.getOwnPropertyDescriptor(klass.prototype, name);
+ if (typeof descr.value !== 'function')
+ return;
+
+ let func = klass.prototype[name];
+
+ if (name.startsWith('vfunc_')) {
+ giPrototype[Gi.hook_up_vfunc_symbol](name.slice(6), func);
+ } else if (name.startsWith('on_')) {
+ let id = GObject.signal_lookup(name.slice(3).replace('_', '-'),
+ klass.$gtype);
+ if (id !== 0) {
+ GObject.signal_override_class_closure(id, klass.$gtype, function (...argArray) {
+ let emitter = argArray.shift();
+
+ return func.apply(emitter, argArray);
+ });
+ }
+ }
+ });
+
+ gobjectInterfaces.forEach(iface => _checkInterface(iface, klass.prototype));
+
+ // Lang.Class parent classes don't support static inheritance
+ if (!('implements' in klass))
+ klass.implements = GObject.Object.implements;
+}
+
+function _interfaceInstanceOf(instance) {
+ if (instance && typeof instance === 'object' &&
+ GObject.Interface.prototype.isPrototypeOf(this.prototype))
+ return GObject.type_is_a(instance, this);
+
+ return false;
+}
+
+function _registerInterfaceType(klass) {
+ const gtypename = _createGTypeName(klass);
+ const gobjectInterfaces = Object.hasOwn(klass, requires) ? klass[requires] : [];
+ const props = _propertiesAsArray(klass);
+ const gobjectSignals = Object.hasOwn(klass, signals) ? klass[signals] : [];
+
+ const [giPrototype, registeredType] = Gi.register_interface_with_class(
+ klass, gtypename, gobjectInterfaces, props);
+
+ _defineGType(klass, giPrototype, registeredType);
+ _createSignals(klass.$gtype, gobjectSignals);
+
+ Object.defineProperty(klass, Symbol.hasInstance, {
+ value: _interfaceInstanceOf,
+ });
+}
+
function _checkProperties(klass) {
- if (!klass.hasOwnProperty(properties))
+ if (!Object.hasOwn(klass, properties))
return;
for (let pspec of Object.values(klass[properties]))
@@ -457,21 +539,21 @@
configurable: false,
enumerable: false,
get() {
- return CjsPrivate.param_spec_get_flags(this);
+ return GjsPrivate.param_spec_get_flags(this);
},
},
'value_type': {
configurable: false,
enumerable: false,
get() {
- return CjsPrivate.param_spec_get_value_type(this);
+ return GjsPrivate.param_spec_get_value_type(this);
},
},
'owner_type': {
configurable: false,
enumerable: false,
get() {
- return CjsPrivate.param_spec_get_owner_type(this);
+ return GjsPrivate.param_spec_get_owner_type(this);
},
},
});
@@ -512,9 +594,9 @@
_checkProperties(klass);
if (_registerType in klass)
- klass[_registerType]();
+ klass[_registerType](klass);
else
- _resolveLegacyClassFunction(klass, _registerType).call(klass);
+ _resolveLegacyClassFunction(klass, _registerType)(klass);
return klass;
};
@@ -526,103 +608,15 @@
return false;
};
- function registerGObjectType() {
- let klass = this;
-
- let gtypename = _createGTypeName(klass);
- let gflags = klass.hasOwnProperty(GTypeFlags) ? klass[GTypeFlags] : 0;
- let gobjectInterfaces = klass.hasOwnProperty(interfaces) ? klass[interfaces] : [];
- let propertiesArray = _propertiesAsArray(klass);
- let parent = Object.getPrototypeOf(klass);
- let gobjectSignals = klass.hasOwnProperty(signals) ? klass[signals] : [];
-
- // Default to the GObject-specific prototype, fallback on the JS prototype for GI native classes.
- const parentPrototype = parent.prototype[Gi.gobject_prototype_symbol] ?? parent.prototype;
-
- const [giPrototype, registeredType] = Gi.register_type_with_class(
- klass, parentPrototype, gtypename, gflags,
- gobjectInterfaces, propertiesArray);
-
- _defineGType(klass, giPrototype, registeredType);
- _createSignals(klass.$gtype, gobjectSignals);
-
- // Reverse the interface array to give the last required interface precedence over the first.
- const requiredInterfaces = [...gobjectInterfaces].reverse();
- requiredInterfaces.forEach(iface =>
- _copyInterfacePrototypeDescriptors(klass, iface));
- requiredInterfaces.forEach(iface =>
- _copyInterfacePrototypeDescriptors(klass.prototype, iface.prototype));
-
- Object.getOwnPropertyNames(klass.prototype)
- .filter(name => name.startsWith('vfunc_') || name.startsWith('on_'))
- .forEach(name => {
- let descr = Object.getOwnPropertyDescriptor(klass.prototype, name);
- if (typeof descr.value !== 'function')
- return;
-
- let func = klass.prototype[name];
-
- if (name.startsWith('vfunc_')) {
- giPrototype[Gi.hook_up_vfunc_symbol](name.slice(6), func);
- } else if (name.startsWith('on_')) {
- let id = GObject.signal_lookup(name.slice(3).replace('_', '-'),
- klass.$gtype);
- if (id !== 0) {
- GObject.signal_override_class_closure(id, klass.$gtype, function (...argArray) {
- let emitter = argArray.shift();
-
- return func.apply(emitter, argArray);
- });
- }
- }
- });
-
- gobjectInterfaces.forEach(iface =>
- _checkInterface(iface, klass.prototype));
-
- // Lang.Class parent classes don't support static inheritance
- if (!('implements' in klass))
- klass.implements = GObject.Object.implements;
- }
-
Object.defineProperty(GObject.Object, _registerType, {
- value: registerGObjectType,
+ value: _registerGObjectType,
writable: false,
configurable: false,
enumerable: false,
});
- function interfaceInstanceOf(instance) {
- if (instance && typeof instance === 'object' &&
- GObject.Interface.prototype.isPrototypeOf(this.prototype))
- return GObject.type_is_a(instance, this);
-
- return false;
- }
-
- function registerInterfaceType() {
- let klass = this;
-
- let gtypename = _createGTypeName(klass);
- let gobjectInterfaces = klass.hasOwnProperty(requires) ? klass[requires] : [];
- let props = _propertiesAsArray(klass);
- let gobjectSignals = klass.hasOwnProperty(signals) ? klass[signals] : [];
-
- const [giPrototype, registeredType] = Gi.register_interface_with_class(klass, gtypename, gobjectInterfaces,
- props);
-
- _defineGType(klass, giPrototype, registeredType);
- _createSignals(klass.$gtype, gobjectSignals);
-
- Object.defineProperty(klass, Symbol.hasInstance, {
- value: interfaceInstanceOf,
- });
-
- return klass;
- }
-
Object.defineProperty(GObject.Interface, _registerType, {
- value: registerInterfaceType,
+ value: _registerInterfaceType,
writable: false,
configurable: false,
enumerable: false,
@@ -630,9 +624,9 @@
GObject.Interface._classInit = function (klass) {
if (_registerType in klass)
- klass[_registerType]();
+ klass[_registerType](klass);
else
- _resolveLegacyClassFunction(klass, _registerType).call(klass);
+ _resolveLegacyClassFunction(klass, _registerType)(klass);
Object.getOwnPropertyNames(klass.prototype)
.filter(key => key !== 'constructor')
@@ -688,12 +682,12 @@
};
GObject.Object.prototype.bind_property_full = function (...args) {
- return CjsPrivate.g_object_bind_property_full(this, ...args);
+ return GjsPrivate.g_object_bind_property_full(this, ...args);
};
if (GObject.BindingGroup !== undefined) {
GObject.BindingGroup.prototype.bind_full = function (...args) {
- return CjsPrivate.g_binding_group_bind_full(this, ...args);
+ return GjsPrivate.g_binding_group_bind_full(this, ...args);
};
}
@@ -890,4 +884,21 @@
throw new Error('GObject.signal_handlers_disconnect_by_data() is not \
introspectable. Use GObject.signal_handlers_disconnect_by_func() instead.');
};
+
+ function unsupportedDataMethod() {
+ throw new Error('Data access methods are unsupported. Use normal JS properties instead.');
+ }
+ GObject.Object.prototype.get_data = unsupportedDataMethod;
+ GObject.Object.prototype.get_qdata = unsupportedDataMethod;
+ GObject.Object.prototype.set_data = unsupportedDataMethod;
+ GObject.Object.prototype.steal_data = unsupportedDataMethod;
+ GObject.Object.prototype.steal_qdata = unsupportedDataMethod;
+
+ function unsupportedRefcountingMethod() {
+ throw new Error("Don't modify an object's reference count in JS.");
+ }
+ GObject.Object.prototype.force_floating = unsupportedRefcountingMethod;
+ GObject.Object.prototype.ref = unsupportedRefcountingMethod;
+ GObject.Object.prototype.ref_sink = unsupportedRefcountingMethod;
+ GObject.Object.prototype.unref = unsupportedRefcountingMethod;
}
diff -ruN cjs-6.2.0-orig/modules/core/overrides/Gio.js cjs-6.2.0/modules/core/overrides/Gio.js
--- cjs-6.2.0-orig/modules/core/overrides/Gio.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/core/overrides/Gio.js 2024-09-16 22:32:54.000000000 +0200
@@ -2,7 +2,7 @@
// SPDX-FileCopyrightText: 2011 Giovanni Campagna
var GLib = imports.gi.GLib;
-var CjsPrivate = imports.gi.CjsPrivate;
+var GjsPrivate = imports.gi.GjsPrivate;
var Signals = imports.signals;
var Gio;
@@ -403,7 +403,7 @@
info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo);
info.cache_build();
- var impl = new CjsPrivate.DBusImplementation({g_interface_info: info});
+ var impl = new GjsPrivate.DBusImplementation({g_interface_info: info});
impl.connect('handle-method-call', function (self, methodName, parameters, invocation) {
return _handleMethodCall.call(jsObj, info, self, methodName, parameters, invocation);
});
@@ -541,16 +541,16 @@
_wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo);
Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo;
- Gio.DBusExportedObject = CjsPrivate.DBusImplementation;
+ Gio.DBusExportedObject = GjsPrivate.DBusImplementation;
Gio.DBusExportedObject.wrapJSObject = _wrapJSObject;
// ListStore
Gio.ListStore.prototype[Symbol.iterator] = _listModelIterator;
Gio.ListStore.prototype.insert_sorted = function (item, compareFunc) {
- return CjsPrivate.list_store_insert_sorted(this, item, compareFunc);
+ return GjsPrivate.list_store_insert_sorted(this, item, compareFunc);
};
Gio.ListStore.prototype.sort = function (compareFunc) {
- return CjsPrivate.list_store_sort(this, compareFunc);
+ return GjsPrivate.list_store_sort(this, compareFunc);
};
// Promisify
diff -ruN cjs-6.2.0-orig/modules/core/overrides/Gtk.js cjs-6.2.0/modules/core/overrides/Gtk.js
--- cjs-6.2.0-orig/modules/core/overrides/Gtk.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/core/overrides/Gtk.js 2024-09-16 22:32:54.000000000 +0200
@@ -3,8 +3,9 @@
// SPDX-FileCopyrightText: 2013 Giovanni Campagna
const Legacy = imports._legacy;
-const {Gio, CjsPrivate, GObject} = imports.gi;
-const {_registerType} = imports._common;
+const {Gio, GjsPrivate, GLib, GObject} = imports.gi;
+const {_createBuilderConnectFunc, _createClosure, _registerType} = imports._common;
+const Gi = imports._gi;
let Gtk;
let BuilderScope;
@@ -22,49 +23,32 @@
if (Gtk.Container && Gtk.Container.prototype.child_set_property) {
Gtk.Container.prototype.child_set_property = function (child, property, value) {
- CjsPrivate.gtk_container_child_set_property(this, child, property, value);
+ GjsPrivate.gtk_container_child_set_property(this, child, property, value);
};
}
if (Gtk.CustomSorter) {
- Gtk.CustomSorter.new = CjsPrivate.gtk_custom_sorter_new;
+ Gtk.CustomSorter.new = GjsPrivate.gtk_custom_sorter_new;
Gtk.CustomSorter.prototype.set_sort_func = function (sortFunc) {
- CjsPrivate.gtk_custom_sorter_set_sort_func(this, sortFunc);
+ GjsPrivate.gtk_custom_sorter_set_sort_func(this, sortFunc);
};
}
Gtk.Widget.prototype._init = function (params) {
- let wrapper = this;
+ const klass = this.constructor;
+ const wrapper = GObject.Object.prototype._init.call(this, params) ?? this;
- if (wrapper.constructor[Gtk.template]) {
- if (!BuilderScope) {
- Gtk.Widget.set_connect_func.call(wrapper.constructor,
- (builder, obj, signalName, handlerName, connectObj, flags) => {
- const swapped = flags & GObject.ConnectFlags.SWAPPED;
- const closure = _createClosure(
- builder, wrapper, handlerName, swapped, connectObj);
-
- if (flags & GObject.ConnectFlags.AFTER)
- obj.connect_after(signalName, closure);
- else
- obj.connect(signalName, closure);
- });
- }
- }
-
- wrapper = GObject.Object.prototype._init.call(wrapper, params) ?? wrapper;
-
- if (wrapper.constructor[Gtk.template]) {
- let children = wrapper.constructor[Gtk.children] || [];
+ if (klass[Gtk.template]) {
+ let children = klass[Gtk.children] ?? [];
for (let child of children) {
wrapper[child.replace(/-/g, '_')] =
- wrapper.get_template_child(wrapper.constructor, child);
+ wrapper.get_template_child(klass, child);
}
- let internalChildren = wrapper.constructor[Gtk.internalChildren] || [];
+ let internalChildren = klass[Gtk.internalChildren] ?? [];
for (let child of internalChildren) {
wrapper[`_${child.replace(/-/g, '_')}`] =
- wrapper.get_template_child(wrapper.constructor, child);
+ wrapper.get_template_child(klass, child);
}
}
@@ -75,56 +59,8 @@
return GObject.Object._classInit(klass);
};
- function registerWidgetType() {
- let klass = this;
-
- let template = klass[Gtk.template];
- let cssName = klass[Gtk.cssName];
- let children = klass[Gtk.children];
- let internalChildren = klass[Gtk.internalChildren];
-
- if (template) {
- klass.prototype._instance_init = function () {
- this.init_template();
- };
- }
-
- GObject.Object[_registerType].call(klass);
-
- if (cssName)
- Gtk.Widget.set_css_name.call(klass, cssName);
-
- if (template) {
- if (typeof template === 'string') {
- if (template.startsWith('resource:///')) {
- Gtk.Widget.set_template_from_resource.call(klass,
- template.slice(11));
- } else if (template.startsWith('file:///')) {
- let file = Gio.File.new_for_uri(template);
- let [, contents] = file.load_contents(null);
- Gtk.Widget.set_template.call(klass, contents);
- }
- } else {
- Gtk.Widget.set_template.call(klass, template);
- }
-
- if (BuilderScope)
- Gtk.Widget.set_template_scope.call(klass, new BuilderScope());
- }
-
- if (children) {
- children.forEach(child =>
- Gtk.Widget.bind_template_child_full.call(klass, child, false, 0));
- }
-
- if (internalChildren) {
- internalChildren.forEach(child =>
- Gtk.Widget.bind_template_child_full.call(klass, child, true, 0));
- }
- }
-
Object.defineProperty(Gtk.Widget, _registerType, {
- value: registerWidgetType,
+ value: _registerWidgetType,
writable: false,
configurable: false,
enumerable: false,
@@ -143,23 +79,72 @@
}, class extends GObject.Object {
vfunc_create_closure(builder, handlerName, flags, connectObject) {
const swapped = flags & Gtk.BuilderClosureFlags.SWAPPED;
- return _createClosure(
- builder, builder.get_current_object(),
- handlerName, swapped, connectObject);
+ const thisArg = builder.get_current_object();
+ return Gi.associateClosure(
+ connectObject ?? thisArg,
+ _createClosure(builder, thisArg, handlerName, swapped, connectObject)
+ );
}
});
}
}
-function _createClosure(builder, thisArg, handlerName, swapped, connectObject) {
- connectObject = connectObject || thisArg;
+function _registerWidgetType(klass) {
+ const template = klass[Gtk.template];
+ const cssName = klass[Gtk.cssName];
+ const children = klass[Gtk.children];
+ const internalChildren = klass[Gtk.internalChildren];
+
+ if (template) {
+ klass.prototype._instance_init = function () {
+ this.init_template();
+ };
+ }
+
+ GObject.Object[_registerType](klass);
+
+ if (cssName)
+ Gtk.Widget.set_css_name.call(klass, cssName);
+
+ if (template) {
+ if (typeof template === 'string') {
+ try {
+ const uri = GLib.Uri.parse(template, GLib.UriFlags.NONE);
+ const scheme = uri.get_scheme();
+
+ if (scheme === 'resource') {
+ Gtk.Widget.set_template_from_resource.call(klass, uri.get_path());
+ } else if (scheme === 'file') {
+ const file = Gio.File.new_for_uri(template);
+ const [, contents] = file.load_contents(null);
+ Gtk.Widget.set_template.call(klass, contents);
+ } else {
+ throw new TypeError(`Invalid template URI: ${template}`);
+ }
+ } catch (err) {
+ if (!(err instanceof GLib.UriError))
+ throw err;
+
+ const contents = new TextEncoder().encode(template);
+ Gtk.Widget.set_template.call(klass, contents);
+ }
+ } else {
+ Gtk.Widget.set_template.call(klass, template);
+ }
+
+ if (BuilderScope)
+ Gtk.Widget.set_template_scope.call(klass, new BuilderScope());
+ else
+ Gtk.Widget.set_connect_func.call(klass, _createBuilderConnectFunc(klass));
+ }
- if (swapped) {
- throw new Error('Unsupported template signal flag "swapped"');
- } else if (typeof thisArg[handlerName] === 'undefined') {
- throw new Error(`A handler called ${handlerName} was not ` +
- `defined on ${thisArg}`);
+ if (children) {
+ children.forEach(child =>
+ Gtk.Widget.bind_template_child_full.call(klass, child, false, 0));
}
- return thisArg[handlerName].bind(connectObject);
+ if (internalChildren) {
+ internalChildren.forEach(child =>
+ Gtk.Widget.bind_template_child_full.call(klass, child, true, 0));
+ }
}
diff -ruN cjs-6.2.0-orig/modules/esm/_encoding/encoding.js cjs-6.2.0/modules/esm/_encoding/encoding.js
--- cjs-6.2.0-orig/modules/esm/_encoding/encoding.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/esm/_encoding/encoding.js 2024-09-16 22:32:54.000000000 +0200
@@ -106,6 +106,8 @@
input = new Uint8Array(buffer, byteOffset, byteLength);
} else if (bytes === undefined) {
input = new Uint8Array(0);
+ } else if (bytes instanceof import.meta.importSync('gi').GLib.Bytes) {
+ input = bytes.toArray();
} else {
throw new Error(
'Provided input cannot be converted to ArrayBufferView or ArrayBuffer'
diff -ruN cjs-6.2.0-orig/modules/esm/console.js cjs-6.2.0/modules/esm/console.js
--- cjs-6.2.0-orig/modules/esm/console.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/esm/console.js 2024-09-16 22:32:54.000000000 +0200
@@ -40,8 +40,9 @@
* @returns {string}
*/
function formatOptimally(item) {
+ const GLib = imports.gi.GLib;
// Handle optimal error formatting.
- if (item instanceof Error) {
+ if (item instanceof Error || item instanceof GLib.Error) {
return `${item.toString()}${item.stack ? '\n' : ''}${item.stack
?.split('\n')
// Pad each stacktrace line.
@@ -52,6 +53,12 @@
// TODO: Enhance 'optimal' formatting.
// There is a current work on a better object formatter for GJS in
// https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/587
+ if (typeof item === 'object' && item !== null) {
+ if (item.constructor?.name !== 'Object')
+ return `${item.constructor?.name} ${JSON.stringify(item, null, 4)}`;
+ else if (item[Symbol.toStringTag] === 'GIRepositoryNamespace')
+ return `[${item[Symbol.toStringTag]} ${item.__name__}]`;
+ }
return JSON.stringify(item, null, 4);
}
@@ -109,7 +116,7 @@
*/
clear() {
this.#groupIndentation = '';
- imports.gi.CjsPrivate.clear_terminal();
+ imports.gi.GjsPrivate.clear_terminal();
}
/**
diff -ruN cjs-6.2.0-orig/modules/internal/loader.js cjs-6.2.0/modules/internal/loader.js
--- cjs-6.2.0-orig/modules/internal/loader.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/internal/loader.js 2024-09-16 22:32:54.000000000 +0200
@@ -18,14 +18,7 @@
* Thrown when there is an error importing a module.
*/
class ImportError extends moduleGlobalThis.Error {
- /**
- * @param {string | undefined} message the import error message
- */
- constructor(message) {
- super(message);
-
- this.name = 'ImportError';
- }
+ name = 'ImportError';
}
/**
@@ -170,7 +163,7 @@
// 1) Resolve path and URI-based imports.
const uri = this.resolveSpecifier(specifier, importingModuleURI);
if (uri) {
- module = registry.get(uri.uri);
+ module = registry.get(uri.uriWithQuery);
// Check if module is already loaded (relative handling)
if (module)
@@ -182,10 +175,10 @@
const [text, internal = false] = result;
- const priv = new ModulePrivate(uri.uri, uri.uri, internal);
+ const priv = new ModulePrivate(uri.uriWithQuery, uri.uri, internal);
const compiled = this.compileModule(priv, text);
- registry.set(uri.uri, compiled);
+ registry.set(uri.uriWithQuery, compiled);
return compiled;
}
@@ -365,7 +358,7 @@
// 1) Resolve path and URI-based imports.
const uri = this.resolveSpecifier(specifier, importingModuleURI);
if (uri) {
- module = registry.get(uri.uri);
+ module = registry.get(uri.uriWithQuery);
// Check if module is already loaded (relative handling)
if (module)
@@ -376,16 +369,16 @@
return null;
// Check if module loaded while awaiting.
- module = registry.get(uri.uri);
+ module = registry.get(uri.uriWithQuery);
if (module)
return module;
const [text, internal = false] = result;
- const priv = new ModulePrivate(uri.uri, uri.uri, internal);
+ const priv = new ModulePrivate(uri.uriWithQuery, uri.uri, internal);
const compiled = this.compileModule(priv, text);
- registry.set(uri.uri, compiled);
+ registry.set(uri.uriWithQuery, compiled);
return compiled;
}
diff -ruN cjs-6.2.0-orig/modules/modules.cpp cjs-6.2.0/modules/modules.cpp
--- cjs-6.2.0-orig/modules/modules.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/modules.cpp 1970-01-01 01:00:00.000000000 +0100
@@ -1,25 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
-// SPDX-FileCopyrightText: 2013 Red Hat, Inc.
-
-#include <config.h> // for ENABLE_CAIRO
-
-#include "cjs/native.h"
-#include "modules/console.h"
-#include "modules/modules.h"
-#include "modules/print.h"
-#include "modules/system.h"
-
-#ifdef ENABLE_CAIRO
-# include "modules/cairo-module.h"
-#endif
-
-void gjs_register_static_modules(void) {
- Gjs::NativeModuleRegistry& registry = Gjs::NativeModuleRegistry::get();
-#ifdef ENABLE_CAIRO
- registry.add("cairoNative", gjs_js_define_cairo_stuff);
-#endif
- registry.add("system", gjs_js_define_system_stuff);
- registry.add("console", gjs_define_console_stuff);
- registry.add("_print", gjs_define_print_stuff);
-}
diff -ruN cjs-6.2.0-orig/modules/modules.h cjs-6.2.0/modules/modules.h
--- cjs-6.2.0-orig/modules/modules.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/modules.h 1970-01-01 01:00:00.000000000 +0100
@@ -1,10 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
-// SPDX-FileCopyrightText: 2013 Red Hat, Inc.
-
-#ifndef MODULES_MODULES_H_
-#define MODULES_MODULES_H_
-
-void gjs_register_static_modules (void);
-
-#endif // MODULES_MODULES_H_
diff -ruN cjs-6.2.0-orig/modules/script/_bootstrap/debugger.js cjs-6.2.0/modules/script/_bootstrap/debugger.js
--- cjs-6.2.0-orig/modules/script/_bootstrap/debugger.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/script/_bootstrap/debugger.js 2024-09-16 22:32:54.000000000 +0200
@@ -23,6 +23,7 @@
var lastExc = null;
var options = {pretty: true, colors: true, ignoreCaughtExceptions: true};
var breakpoints = [undefined]; // Breakpoint numbers start at 1
+var skipUnwindHandler = false;
// Cleanup functions to run when we next re-enter the repl.
var replCleanups = [];
@@ -31,9 +32,12 @@
function dvToString(v) {
if (typeof v === 'undefined')
return 'undefined'; // uneval(undefined) === '(void 0)', confusing
- if (v === null)
- return 'null'; // typeof null === 'object', so avoid that case
- return typeof v !== 'object' || v === null ? uneval(v) : `[object ${v.class}]`;
+ if (typeof v === 'object' && v !== null)
+ return `[object ${v.class}]`;
+ const s = uneval(v);
+ if (s.length > 400)
+ return `${s.substr(0, 400)}...<${s.length - 400} more bytes>...`;
+ return s;
}
function debuggeeValueToString(dv, style = {pretty: options.pretty}) {
@@ -50,21 +54,20 @@
}
const dvrepr = dvToString(dv);
- if (!style.pretty || dv === null || typeof dv !== 'object')
+ if (!style.pretty || (typeof dv !== 'object') || (dv === null))
return [dvrepr, undefined];
+ const exec = debuggeeGlobalWrapper.executeInGlobalWithBindings.bind(debuggeeGlobalWrapper);
+
if (['TypeError', 'Error', 'GIRespositoryNamespace', 'GObject_Object'].includes(dv.class)) {
- const errval = debuggeeGlobalWrapper.executeInGlobalWithBindings(
- 'v.toString()', {v: dv});
+ const errval = exec('v.toString()', {v: dv});
return [dvrepr, errval['return']];
}
if (style.brief)
return [dvrepr, dvrepr];
- const str = debuggeeGlobalWrapper.executeInGlobalWithBindings(
- 'imports._print.getPrettyPrintFunction(globalThis)(v)', {v: dv});
-
+ const str = exec('imports._print.getPrettyPrintFunction(globalThis)(v)', {v: dv});
if ('throw' in str) {
if (style.noerror)
return [dvrepr, undefined];
@@ -183,6 +186,36 @@
}
}
+// Evaluate @expr in the current frame, logging and suppressing any exceptions
+function evalInFrame(expr) {
+ if (!focusedFrame) {
+ print('No stack');
+ return;
+ }
+
+ skipUnwindHandler = true;
+ let cv;
+ try {
+ cv = saveExcursion(
+ () => focusedFrame.evalWithBindings(`(${expr})`, debuggeeValues));
+ } finally {
+ skipUnwindHandler = false;
+ }
+
+ if (cv === null) {
+ print(`Debuggee died while evaluating ${expr}`);
+ return;
+ }
+
+ const {throw: exc, return: dv} = cv;
+ if (exc) {
+ print(`Exception caught while evaluating ${expr}: ${dvToString(exc)}`);
+ return;
+ }
+
+ return {value: dv};
+}
+
// Accept debugger commands starting with '#' so that scripting the debugger
// can be annotated
function commentCommand(comment) {
@@ -346,13 +379,32 @@
expressions, or $$ for the most recently printed expression.`;
function keysCommand(rest) {
- return doPrint(`
- (o => Object.getOwnPropertyNames(o)
- .concat(Object.getOwnPropertySymbols(o)))
- (${rest})
- `);
+ if (!rest) {
+ print("Missing argument. See 'help keys'");
+ return;
+ }
+
+ const result = evalInFrame(rest);
+ if (!result)
+ return;
+
+ const dv = result.value;
+ if (!(dv instanceof Debugger.Object)) {
+ print(`${rest} is ${dvToString(dv)}, not an object`);
+ return;
+ }
+ const names = dv.getOwnPropertyNames();
+ const symbols = dv.getOwnPropertySymbols();
+ const keys = [
+ ...names.map(s => `"${s}"`),
+ ...symbols.map(s => `Symbol("${s.description}")`),
+ ];
+ if (keys.length === 0)
+ print('No own properties');
+ else
+ print(keys.join(', '));
}
-keysCommand.summary = 'Prints keys of the given object';
+keysCommand.summary = 'Prints own properties of the given object';
keysCommand.helpText = `USAGE
keys <obj>
@@ -380,25 +432,15 @@
function throwOrReturn(rest, action, defaultCompletion) {
if (focusedFrame !== topFrame) {
- print("To throw, you must select the newest frame (use 'frame 0').");
- return;
- }
- if (focusedFrame === null) {
- print('No stack.');
+ print(`To ${action}, you must select the newest frame (use 'frame 0')`);
return;
}
if (rest === '')
return [defaultCompletion];
- const cv = saveExcursion(() => focusedFrame.eval(rest));
- if (cv === null) {
- print(`Debuggee died while determining what to ${action}. Stopped.`);
- return;
- }
- if ('return' in cv)
- return [{[action]: cv['return']}];
- print(`Exception determining what to ${action}. Stopped.`);
- showDebuggeeValue(cv.throw);
+ const result = evalInFrame(rest);
+ if (result)
+ return [{[action]: result.value}];
}
function throwCommand(rest) {
@@ -838,7 +880,7 @@
return undefined;
var first = pieces[0], rest = pieces[1];
- if (!commands.hasOwnProperty(first)) {
+ if (!Object.hasOwn(commands, first)) {
print(`unrecognized command '${first}'`);
return undefined;
}
@@ -927,6 +969,9 @@
});
};
dbg.onExceptionUnwind = function (frame, value) {
+ if (skipUnwindHandler)
+ return undefined;
+
const willBeCaught = currentFrame => {
while (currentFrame) {
if (currentFrame.script.isInCatchScope(currentFrame.offset))
diff -ruN cjs-6.2.0-orig/modules/script/_bootstrap/default.js cjs-6.2.0/modules/script/_bootstrap/default.js
--- cjs-6.2.0-orig/modules/script/_bootstrap/default.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/script/_bootstrap/default.js 2024-09-16 22:32:54.000000000 +0200
@@ -23,15 +23,28 @@
return nativeLogError(e, args.map(arg => typeof arg === 'string' ? arg : prettyPrint(arg)).join(' '));
}
+ // compare against the %TypedArray% intrinsic object all typed array constructors inherit from
+ function _isTypedArray(value) {
+ return value instanceof Object.getPrototypeOf(Uint8Array);
+ }
+
+ function _hasStandardToString(value) {
+ return value.toString === Object.prototype.toString ||
+ value.toString === Array.prototype.toString ||
+ // although TypedArrays have a standard Array.prototype.toString, we currently enforce an override to warn
+ // for legacy behaviour, making the toString non-standard for
+ // "any Uint8Array instances created in situations where previously a ByteArray would have been created"
+ _isTypedArray(value) ||
+ value.toString === Date.prototype.toString;
+ }
+
function prettyPrint(value) {
switch (typeof value) {
case 'object':
if (value === null)
return 'null';
- if (value.toString === Object.prototype.toString ||
- value.toString === Array.prototype.toString ||
- value.toString === Date.prototype.toString) {
+ if (_hasStandardToString(value)) {
const printedObjects = new WeakSet();
return formatObject(value, printedObjects);
}
@@ -60,15 +73,12 @@
function formatObject(obj, printedObjects) {
printedObjects.add(obj);
- if (Array.isArray(obj))
+ if (Array.isArray(obj) || _isTypedArray(obj))
return formatArray(obj, printedObjects).toString();
if (obj instanceof Date)
return formatDate(obj);
- if (obj[Symbol.toStringTag] === 'GIRepositoryNamespace')
- return obj.toString();
-
const formattedObject = [];
const keys = Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj));
for (const propertyKey of keys) {
@@ -78,8 +88,12 @@
case 'object':
if (printedObjects.has(value))
formattedObject.push(`${key}: [Circular]`);
- else
+ else if (value === null)
+ formattedObject.push(`${key}: null`);
+ else if (_hasStandardToString(value))
formattedObject.push(`${key}: ${formatObject(value, printedObjects)}`);
+ else
+ formattedObject.push(`${key}: ${value.toString()}`);
break;
case 'function':
formattedObject.push(`${key}: ${formatFunction(value)}`);
diff -ruN cjs-6.2.0-orig/modules/script/_legacy.js cjs-6.2.0/modules/script/_legacy.js
--- cjs-6.2.0-orig/modules/script/_legacy.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/script/_legacy.js 2024-09-16 22:32:54.000000000 +0200
@@ -246,7 +246,7 @@
return req.__super__;
for (let metaclass = req.prototype.__metaclass__; metaclass;
metaclass = metaclass.__super__) {
- if (metaclass.hasOwnProperty('MetaInterface'))
+ if (Object.hasOwn(metaclass, 'MetaInterface'))
return metaclass.MetaInterface;
}
return null;
@@ -441,7 +441,7 @@
}
function _getGObjectInterfaces(interfaces) {
- return interfaces.filter(iface => iface.hasOwnProperty('$gtype'));
+ return interfaces.filter(iface => Object.hasOwn(iface, '$gtype'));
}
function _propertiesAsArray(params) {
@@ -647,6 +647,8 @@
}
function defineGtkLegacyObjects(GObject, Gtk) {
+ const {_createBuilderConnectFunc} = imports._common;
+
const GtkWidgetClass = new Class({
Name: 'GtkWidgetClass',
Extends: GObject.Class,
@@ -683,6 +685,8 @@
Gtk.Widget.set_template.call(this, template);
}
+ Gtk.Widget.set_connect_func.call(this, _createBuilderConnectFunc(this));
+
this[Gtk.template] = template;
this[Gtk.children] = children;
this[Gtk.internalChildren] = internalChildren;
diff -ruN cjs-6.2.0-orig/modules/script/lang.js cjs-6.2.0/modules/script/lang.js
--- cjs-6.2.0-orig/modules/script/lang.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/script/lang.js 2024-09-16 22:32:54.000000000 +0200
@@ -16,7 +16,7 @@
}
function getPropertyDescriptor(obj, property) {
- if (obj.hasOwnProperty(property))
+ if (Object.hasOwn(obj, property))
return Object.getOwnPropertyDescriptor(obj, property);
return getPropertyDescriptor(Object.getPrototypeOf(obj), property);
}
diff -ruN cjs-6.2.0-orig/modules/script/package.js cjs-6.2.0/modules/script/package.js
--- cjs-6.2.0-orig/modules/script/package.js 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/modules/script/package.js 2024-09-16 22:32:54.000000000 +0200
@@ -9,6 +9,7 @@
* This module provides a set of convenience APIs for building packaged
* applications.
*/
+imports.gi.versions.GIRepository = '2.0';
const GLib = imports.gi.GLib;
const GIRepository = imports.gi.GIRepository;
diff -ruN cjs-6.2.0-orig/test/check-headers.sh cjs-6.2.0/test/check-headers.sh
--- cjs-6.2.0-orig/test/check-headers.sh 1970-01-01 01:00:00.000000000 +0100
+++ cjs-6.2.0/test/check-headers.sh 2024-09-16 22:32:54.000000000 +0200
@@ -0,0 +1,181 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.0-or-later
+# SPDX-FileCopyrightText: 2024 Philip Chimento <philip.chimento@gmail.com>
+
+# Drafted with assistance from ChatGPT.
+# https://chat.openai.com/share/0cd77782-13b5-4775-80d0-c77c7749fb9d
+
+if [ -n "$SELFTEST" ]; then
+ unset SELFTEST
+ set -x
+ self="$(realpath "$0")"
+ test_paths=()
+ trap 'rm -rf -- "${test_paths[@]}"' EXIT
+
+ test_env() {
+ local code_path
+ code_path=$(mktemp -d -t "check-pch-XXXXXX")
+ test_paths+=("$code_path")
+ cd "$code_path" || exit
+ mkdir gi gjs libgjs-private modules test util
+ }
+
+ expect_success() {
+ "$self" || exit 1
+ }
+ expect_failure() {
+ "$self" && exit 1 || true
+ }
+
+ # config.h is included
+ test_env
+ echo "#include <config.h>" > gjs/program.c
+ expect_success
+
+ # config.h must be in angle brackets
+ test_env
+ echo '#include "config.h"' > gjs/program.c
+ expect_failure
+
+ # public headers are skipped
+ test_env
+ echo "#include <stdlib.h>" > gjs/macros.h
+ expect_success
+
+ # config.h must be included
+ test_env
+ echo "#include <stdlib.h>" > gjs/program.c
+ expect_failure
+
+ # config.h is included first
+ test_env
+ echo '#include <config.h>' > gjs/program.c
+ echo '#include <stdlib.h>' >> gjs/program.c
+ expect_success
+
+ # config.h must be included first
+ test_env
+ echo '#include <stdlib.h>' > gjs/program.c
+ echo '#include <config.h>' >> gjs/program.c
+ expect_failure
+
+ # other non-include things can come before the include
+ test_env
+ cat > gjs/program.h <<EOF
+/* a comment */
+#pragma once
+#include <config.h>
+EOF
+ expect_success
+
+ # spaces are taken into account
+ test_env
+ cat > gjs/program.c <<EOF
+#ifdef UNIX
+# include <unix.h>
+#endif
+#include <config.h>
+EOF
+ expect_failure
+
+ # header blocks in right order
+ test_env
+ cat > gjs/program.c <<EOF
+#include <config.h>
+#include <stdint.h>
+#include <memory>
+#include <glib.h>
+#include <js/TypeDecls.h>
+#include "program.h"
+EOF
+ expect_success
+
+ # header blocks in wrong order
+ test_env
+ cat > gjs/program.c <<EOF
+#include <config.h>
+#include <memory>
+#include <glib.h>
+#include <js/TypeDecls.h>
+#include "program.h"
+#include <stdint.h>
+EOF
+ expect_failure
+
+ exit 0
+fi
+
+failed=0
+
+function report_out_of_order {
+ file="$1"
+ shift
+ include="$1"
+ shift
+ descr="$1"
+ shift
+ headers=("$@")
+
+ if [[ ${#headers[@]} -ne 0 ]]; then
+ echo "Error: $file: include $include before $descr header ${headers[0]}"
+ failed=1
+ return 1
+ fi
+ return 0
+}
+
+function check_config_header {
+ file="$1"
+ included_files=($(sed -nE 's/^#[[:space:]]*include[[:space:]]*([<"][^>"]+[>"]).*/\1/p' "$file"))
+ if [[ "${included_files[0]}" != "<config.h>" ]]; then
+ echo "Error: $file: include <config.h> as the first #include directive"
+ failed=1
+ fi
+
+ c_headers=()
+ cpp_headers=()
+ gnome_headers=()
+ moz_headers=()
+ gjs_headers=()
+ for include in "${included_files[@]:1}"; do
+ if [[ "$include" =~ \".*\.h\" ]]; then
+ gjs_headers+=("$include")
+ continue
+ fi
+ report_out_of_order "$file" "$include" "GJS" "${gjs_headers[@]}" || continue
+ if [[ "$include" =~ \<(js|mozilla).*\.h\> ]]; then
+ moz_headers+=("$include")
+ continue
+ fi
+ report_out_of_order "$file" "$include" "Mozilla" "${moz_headers[@]}" || continue
+ if [[ "$include" =~ \<(ffi|sysprof.*|cairo.*|g.*)\.h\> ]]; then
+ gnome_headers+=("$include")
+ continue
+ fi
+ report_out_of_order "$file" "$include" "GNOME platform" "${gnome_headers[@]}" || continue
+ if [[ "$include" =~ \<.*\.h\> ]]; then
+ report_out_of_order "$file" "$include" "C++ standard library" "${cpp_headers[@]}" || continue
+ c_headers+=("$include")
+ elif [[ "$include" =~ \<.*\> ]]; then
+ cpp_headers+=("$include")
+ else
+ echo "Error: Need to fix your regex to handle $include."
+ failed=1
+ fi
+ done
+}
+
+files=$(find gi gjs libgjs-private modules test util \
+ -name '*.c' -o -name '*.cpp' -o -name '*.h')
+for file in $files; do
+ if [[ "$file" == "gjs/gjs.h" || "$file" == "gjs/macros.h" ]]; then continue; fi
+ if grep -ql "^GJS_EXPORT" "$file"; then continue; fi
+ check_config_header "$file"
+done
+
+if [[ $failed -ne 0 ]]; then
+ echo "Errors found."
+ exit 1
+else
+ echo "OK."
+fi
diff -ruN cjs-6.2.0-orig/test/check-pch.sh cjs-6.2.0/test/check-pch.sh
--- cjs-6.2.0-orig/test/check-pch.sh 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/check-pch.sh 2024-09-16 22:32:54.000000000 +0200
@@ -14,8 +14,8 @@
local code_path="$(mktemp -t -d "check-pch-XXXXXX")"
test_paths+=("$code_path")
cd "$code_path"
- mkdir cjs gi
- echo "#include <stlib.h>" >> cjs/gjs_pch.hh
+ mkdir gjs gi
+ echo "#include <stlib.h>" >> gjs/gjs_pch.hh
}
expect_success() {
@@ -53,12 +53,12 @@
expect_failure
test_env
- echo "#include <invalid.h>" >> cjs/gjs_pch.hh
+ echo "#include <invalid.h>" >> gjs/gjs_pch.hh
echo "#include <stlib.h>" >> gi/code.c
expect_failure
test_env
- echo "#include <invalid.h> // check-pch: ignore, yes" >> cjs/gjs_pch.hh
+ echo "#include <invalid.h> // check-pch: ignore, yes" >> gjs/gjs_pch.hh
echo "#include <stlib.h>" >> gi/code.c
expect_success
@@ -74,14 +74,14 @@
test_env
echo "#include <stlib.h>" >> gi/code.c
- echo '#include "local/header.h"' >> cjs/gjs_pch.hh
+ echo '#include "local/header.h"' >> gjs/gjs_pch.hh
expect_failure
test_env
echo "# include <stlib.h>" >> gi/code.c
echo "# include <other/include.h>" >> gi/code.c
echo " # include <other/include.h>" >> gi/other-file.c
- echo "# include <other/include.h>" >> cjs/gjs_pch.hh
+ echo "# include <other/include.h>" >> gjs/gjs_pch.hh
expect_success
test_env
@@ -98,7 +98,7 @@
echo "#include <stlib.h>" >> gi/code.c
echo "//#include <invalid.h>" >> gi/invalid-file.c
echo "// #include <invalid.h>" >> gi/invalid-file.c
- echo "//#include <invalid.h>" >> cjs/gjs_pch.hh
+ echo "//#include <invalid.h>" >> gjs/gjs_pch.hh
expect_success
test_env
@@ -114,10 +114,10 @@
exit 0
fi
-PCH_FILES=(cjs/gjs_pch.hh)
+PCH_FILES=(gjs/gjs_pch.hh)
IGNORE_COMMENT="check-pch: ignore"
-CODE_PATHS=(cjs gi)
+CODE_PATHS=(gjs gi)
INCLUDED_FILES=(
\*.c
\*.cpp
diff -ruN cjs-6.2.0-orig/test/extra/Dockerfile cjs-6.2.0/test/extra/Dockerfile
--- cjs-6.2.0-orig/test/extra/Dockerfile 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/extra/Dockerfile 2024-09-16 22:32:54.000000000 +0200
@@ -3,22 +3,25 @@
# === Build Spidermonkey stage ===
-FROM registry.fedoraproject.org/fedora:38 AS mozjs-build
-ARG MOZJS_BRANCH=mozjs102
+FROM registry.fedoraproject.org/fedora:40 AS mozjs-build
+ARG MOZJS_BRANCH=mozjs115
ARG MOZJS_BUILDDEPS=${MOZJS_BRANCH}
ARG BUILD_OPTS=
ENV SHELL=/bin/bash
-# mozjs102 cannot be built with python3.11 and possibly 3.10
+# mozjs115 cannot be built with python3.11
+# cbindgen should be included in builddep(mozjs128)
RUN dnf -y install 'dnf-command(builddep)' \
autoconf213 \
+ cbindgen \
clang \
git \
llvm \
llvm-devel \
make \
- python3.9 \
+ python3.10 \
+ python-packaging \
rust \
which \
xz
@@ -31,7 +34,7 @@
WORKDIR /root/mozjs/_build
-ENV PYTHON3=/usr/bin/python3.9
+ENV PYTHON3=/usr/bin/python3.10
RUN ../js/src/configure --prefix=/usr --libdir=/usr/lib64 --disable-jemalloc \
--with-system-zlib --with-intl-api ${BUILD_OPTS}
RUN make -j$(nproc)
@@ -40,7 +43,7 @@
# === Actual Docker image ===
-FROM registry.fedoraproject.org/fedora:38
+FROM registry.fedoraproject.org/fedora:40
ARG LOCALES=tr_TR
ENV SHELL=/bin/bash
diff -ruN cjs-6.2.0-orig/test/extra/Dockerfile.debug cjs-6.2.0/test/extra/Dockerfile.debug
--- cjs-6.2.0-orig/test/extra/Dockerfile.debug 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/extra/Dockerfile.debug 2024-09-16 22:32:54.000000000 +0200
@@ -3,16 +3,18 @@
# === Build stage ===
-FROM registry.fedoraproject.org/fedora:38 AS build
-ARG MOZJS_BRANCH=mozjs102
+FROM registry.fedoraproject.org/fedora:40 AS build
+ARG MOZJS_BRANCH=mozjs115
ARG MOZJS_BUILDDEPS=${MOZJS_BRANCH}
ARG BUILD_OPTS=
ENV SHELL=/bin/bash
-# mozjs102 cannot be built with python3.11 and possibly 3.10
+# mozjs115 cannot be built with python3.11
+# cbindgen should be included in builddep(mozjs128)
RUN dnf -y install 'dnf-command(builddep)' \
autoconf213 \
+ cbindgen \
clang \
clang-devel \
cmake \
@@ -21,7 +23,8 @@
llvm-devel \
make \
ninja-build \
- python3.9 \
+ python3.10 \
+ python3-packaging \
rust \
which \
xz
@@ -30,9 +33,9 @@
WORKDIR /root
RUN mkdir -p include-what-you-use/_build
-ADD https://include-what-you-use.org/downloads/include-what-you-use-0.20.src.tar.gz /root/include-what-you-use/
+ADD https://include-what-you-use.org/downloads/include-what-you-use-0.22.src.tar.gz /root/include-what-you-use/
WORKDIR /root/include-what-you-use
-RUN tar xzf include-what-you-use-0.20.src.tar.gz --strip-components=1
+RUN tar xzf include-what-you-use-0.22.src.tar.gz --strip-components=1
WORKDIR /root/include-what-you-use/_build
@@ -47,7 +50,7 @@
WORKDIR /root/mozjs/_build
-ENV PYTHON3=/usr/bin/python3.9
+ENV PYTHON3=/usr/bin/python3.10
RUN ../js/src/configure --prefix=/usr --libdir=/usr/lib64 --disable-jemalloc \
--with-system-zlib --with-intl-api --enable-debug \
${BUILD_OPTS}
@@ -55,19 +58,9 @@
RUN DESTDIR=/root/mozjs-install make install
RUN rm -f /root/mozjs-install/usr/lib64/libjs_static.ajs
-WORKDIR /root
-
-# Install gnome-introspection from main, so that we can test against it
-RUN dnf -y builddep gobject-introspection
-RUN git clone https://gitlab.gnome.org/GNOME/gobject-introspection.git
-
-WORKDIR /root/gobject-introspection
-RUN meson setup _build . --prefix=/opt/GNOME --buildtype debugoptimized
-RUN DESTDIR=/root/g-i-install ninja -C _build install
-
# === Actual Docker image ===
-FROM registry.fedoraproject.org/fedora:38
+FROM registry.fedoraproject.org/fedora:40
ARG LOCALES=tr_TR
ENV SHELL=/bin/bash
@@ -126,7 +119,6 @@
COPY --from=build /root/mozjs-install/usr /usr
COPY --from=build /root/iwyu-install/usr /usr
-COPY --from=build /root/g-i-install/opt /opt
RUN ln -s /usr/bin/iwyu_tool.py /usr/bin/iwyu_tool
# Enable sudo for wheel users
diff -ruN cjs-6.2.0-orig/test/gjs-test-common.h cjs-6.2.0/test/gjs-test-common.h
--- cjs-6.2.0-orig/test/gjs-test-common.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-test-common.h 2024-09-16 22:32:54.000000000 +0200
@@ -5,6 +5,8 @@
#ifndef TEST_GJS_TEST_COMMON_H_
#define TEST_GJS_TEST_COMMON_H_
+#include <config.h>
+
struct JSContext;
char* gjs_test_get_exception_message(JSContext* cx);
diff -ruN cjs-6.2.0-orig/test/gjs-test-coverage.cpp cjs-6.2.0/test/gjs-test-coverage.cpp
--- cjs-6.2.0-orig/test/gjs-test-coverage.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-test-coverage.cpp 2024-10-14 18:33:39.000000000 +0200
@@ -3,6 +3,8 @@
// SPDX-FileCopyrightText: 2014 Endless Mobile, Inc.
// SPDX-FileContributor: Authored By: Sam Spilsbury <sam@endlessm.com>
+#include <config.h>
+
#include <errno.h> // for errno
#include <stdio.h> // for sscanf, size_t
#include <stdlib.h> // for strtol, atoi, mkdtemp
diff -ruN cjs-6.2.0-orig/test/gjs-test-jsapi-utils.cpp cjs-6.2.0/test/gjs-test-jsapi-utils.cpp
--- cjs-6.2.0-orig/test/gjs-test-jsapi-utils.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-test-jsapi-utils.cpp 2024-10-14 18:33:39.000000000 +0200
@@ -5,11 +5,15 @@
* Copyright (c) 2020 Marco Trevisan <marco.trevisan@canonical.com>
*/
-#include <glib-object.h>
-#include <glib.h>
+#include <config.h>
+
#include <stddef.h> // for NULL
+
#include <utility> // for move, swap
+#include <glib-object.h>
+#include <glib.h>
+
#include "cjs/jsapi-util.h"
struct _GjsTestObject {
diff -ruN cjs-6.2.0-orig/test/gjs-test-no-introspection-object.cpp cjs-6.2.0/test/gjs-test-no-introspection-object.cpp
--- cjs-6.2.0-orig/test/gjs-test-no-introspection-object.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-test-no-introspection-object.cpp 2024-09-16 22:32:54.000000000 +0200
@@ -2,6 +2,8 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2020 Endless Mobile Inc.
+#include <config.h>
+
#include "test/gjs-test-no-introspection-object.h"
struct _GjsTestNoIntrospectionObject {
diff -ruN cjs-6.2.0-orig/test/gjs-test-no-introspection-object.h cjs-6.2.0/test/gjs-test-no-introspection-object.h
--- cjs-6.2.0-orig/test/gjs-test-no-introspection-object.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-test-no-introspection-object.h 2024-09-16 22:32:54.000000000 +0200
@@ -5,6 +5,8 @@
#ifndef TEST_GJS_TEST_NO_INTROSPECTION_OBJECT_H_
#define TEST_GJS_TEST_NO_INTROSPECTION_OBJECT_H_
+#include <config.h>
+
#include <glib-object.h>
#define GJSTEST_TYPE_NO_INTROSPECTION_OBJECT \
diff -ruN cjs-6.2.0-orig/test/gjs-test-rooting.cpp cjs-6.2.0/test/gjs-test-rooting.cpp
--- cjs-6.2.0-orig/test/gjs-test-rooting.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-test-rooting.cpp 2024-10-14 18:33:39.000000000 +0200
@@ -40,7 +40,7 @@
bool finalized;
bool notify_called;
- GjsMaybeOwned<JSObject *> *obj; /* only used in callback test cases */
+ GjsMaybeOwned* obj; // only used in callback test cases
};
static void test_obj_finalize(JS::GCContext*, JSObject* obj) {
@@ -111,23 +111,23 @@
static void test_maybe_owned_rooted_flag_set_when_rooted(GjsRootingFixture* fx,
const void*) {
- auto obj = new GjsMaybeOwned<JS::Value>();
- obj->root(PARENT(fx)->cx, JS::TrueValue());
+ auto* obj = new GjsMaybeOwned();
+ obj->root(PARENT(fx)->cx, JS_NewPlainObject(PARENT(fx)->cx));
g_assert_true(obj->rooted());
delete obj;
}
static void test_maybe_owned_rooted_flag_not_set_when_not_rooted(
- GjsRootingFixture*, const void*) {
- auto obj = new GjsMaybeOwned<JS::Value>();
- *obj = JS::TrueValue();
+ GjsRootingFixture* fx, const void*) {
+ auto* obj = new GjsMaybeOwned();
+ *obj = JS_NewPlainObject(PARENT(fx)->cx);
g_assert_false(obj->rooted());
delete obj;
}
static void test_maybe_owned_rooted_keeps_alive_across_gc(GjsRootingFixture* fx,
const void*) {
- auto obj = new GjsMaybeOwned<JSObject *>();
+ auto* obj = new GjsMaybeOwned();
obj->root(PARENT(fx)->cx, test_obj_new(fx));
wait_for_gc(fx);
@@ -140,7 +140,7 @@
static void test_maybe_owned_rooted_is_collected_after_reset(
GjsRootingFixture* fx, const void*) {
- auto obj = new GjsMaybeOwned<JSObject *>();
+ auto* obj = new GjsMaybeOwned();
obj->root(PARENT(fx)->cx, test_obj_new(fx));
obj->reset();
@@ -150,14 +150,14 @@
}
static void update_weak_pointer(JSTracer* trc, JS::Compartment*, void* data) {
- auto* obj = static_cast<GjsMaybeOwned<JSObject*>*>(data);
+ auto* obj = static_cast<GjsMaybeOwned*>(data);
if (*obj)
obj->update_after_gc(trc);
}
static void test_maybe_owned_weak_pointer_is_collected_by_gc(
GjsRootingFixture* fx, const void*) {
- auto obj = new GjsMaybeOwned<JSObject *>();
+ auto* obj = new GjsMaybeOwned();
*obj = test_obj_new(fx);
JS_AddWeakPointerCompartmentCallback(PARENT(fx)->cx, &update_weak_pointer,
@@ -171,7 +171,7 @@
static void test_maybe_owned_heap_rooted_keeps_alive_across_gc(
GjsRootingFixture* fx, const void*) {
- auto obj = new GjsMaybeOwned<JSObject *>();
+ auto* obj = new GjsMaybeOwned();
obj->root(PARENT(fx)->cx, test_obj_new(fx));
wait_for_gc(fx);
@@ -185,7 +185,7 @@
static void test_maybe_owned_switching_mode_keeps_same_value(
GjsRootingFixture* fx, const void*) {
JSObject *test_obj = test_obj_new(fx);
- auto obj = new GjsMaybeOwned<JSObject *>();
+ auto* obj = new GjsMaybeOwned();
*obj = test_obj;
g_assert_true(*obj == test_obj);
@@ -203,7 +203,7 @@
static void test_maybe_owned_switch_to_rooted_prevents_collection(
GjsRootingFixture* fx, const void*) {
- auto obj = new GjsMaybeOwned<JSObject *>();
+ auto* obj = new GjsMaybeOwned();
*obj = test_obj_new(fx);
obj->switch_to_rooted(PARENT(fx)->cx);
@@ -215,7 +215,7 @@
static void test_maybe_owned_switch_to_unrooted_allows_collection(
GjsRootingFixture* fx, const void*) {
- auto obj = new GjsMaybeOwned<JSObject *>();
+ auto* obj = new GjsMaybeOwned();
obj->root(PARENT(fx)->cx, test_obj_new(fx));
obj->switch_to_unrooted(PARENT(fx)->cx);
@@ -240,7 +240,7 @@
static void test_maybe_owned_notify_callback_called_on_context_destroy(
GjsRootingFixture* fx, const void*) {
auto* gjs = GjsContextPrivate::from_cx(PARENT(fx)->cx);
- fx->obj = new GjsMaybeOwned<JSObject *>();
+ fx->obj = new GjsMaybeOwned();
fx->obj->root(PARENT(fx)->cx, test_obj_new(fx));
gjs->register_notifier(context_destroyed, fx);
@@ -252,7 +252,7 @@
static void test_maybe_owned_object_destroyed_after_notify(
GjsRootingFixture* fx, const void*) {
auto* gjs = GjsContextPrivate::from_cx(PARENT(fx)->cx);
- fx->obj = new GjsMaybeOwned<JSObject *>();
+ fx->obj = new GjsMaybeOwned();
fx->obj->root(PARENT(fx)->cx, test_obj_new(fx));
gjs->register_notifier(context_destroyed, fx);
diff -ruN cjs-6.2.0-orig/test/gjs-test-toggle-queue.cpp cjs-6.2.0/test/gjs-test-toggle-queue.cpp
--- cjs-6.2.0-orig/test/gjs-test-toggle-queue.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-test-toggle-queue.cpp 2024-10-14 18:33:39.000000000 +0200
@@ -5,7 +5,6 @@
#include <config.h>
-#include <algorithm> // for copy
#include <atomic>
#include <chrono>
#include <deque>
@@ -116,8 +115,7 @@
GjsAutoUnref<GObject> gobject(
G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr)));
auto* object = ObjectInstance::new_for_gobject(fx->cx, gobject);
- g_assert_true(
- static_cast<ObjectInstance*>(object)->ensure_uses_toggle_ref(fx->cx));
+ static_cast<ObjectInstance*>(object)->ensure_uses_toggle_ref(fx->cx);
return object;
}
diff -ruN cjs-6.2.0-orig/test/gjs-test-utils.h cjs-6.2.0/test/gjs-test-utils.h
--- cjs-6.2.0-orig/test/gjs-test-utils.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-test-utils.h 2024-10-14 18:33:39.000000000 +0200
@@ -18,10 +18,10 @@
#include <glib.h> // for g_assert_...
-#include "cjs/context.h"
-
#include <js/TypeDecls.h>
+#include "cjs/context.h"
+
struct GjsUnitTestFixture {
GjsContext *gjs_context;
JSContext *cx;
diff -ruN cjs-6.2.0-orig/test/gjs-tests.cpp cjs-6.2.0/test/gjs-tests.cpp
--- cjs-6.2.0-orig/test/gjs-tests.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/gjs-tests.cpp 2024-10-14 18:33:39.000000000 +0200
@@ -744,7 +744,7 @@
const void*) {
g_test_expect_message("Gjs", G_LOG_LEVEL_WARNING,
"JS ERROR: Error: Exception 1\n"
- "Caused by: Error: Exception 2\n");
+ "Caused by: Error: Exception 2");
gjs_throw(fx->cx, "Exception 1");
gjs_throw(fx->cx, "Exception 2");
@@ -753,7 +753,7 @@
g_test_expect_message("Gjs", G_LOG_LEVEL_WARNING,
"JS ERROR: Error: Exception 1\n"
"Caused by: Error: Exception 2\n"
- "Caused by: Error: Exception 3\n");
+ "Caused by: Error: Exception 3");
gjs_throw(fx->cx, "Exception 1");
gjs_throw(fx->cx, "Exception 2");
diff -ruN cjs-6.2.0-orig/test/meson.build cjs-6.2.0/test/meson.build
--- cjs-6.2.0-orig/test/meson.build 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/meson.build 2024-09-16 22:32:54.000000000 +0200
@@ -28,7 +28,7 @@
test('API tests', gjs_tests, args: ['--tap', '--keep-going', '--verbose'],
depends: gjs_private_typelib, env: tests_environment, protocol: 'tap',
- suite: 'C', timeout: 60)
+ suite: 'C', timeout: 60, priority: 10)
gjs_tests_internal = executable('gjs-tests-internal',
sources: [
@@ -47,4 +47,4 @@
test('Internal API tests', gjs_tests_internal,
args: ['--tap', '--keep-going', '--verbose'],
env: tests_environment, protocol: 'tap',
- suite: 'C')
+ suite: 'C', priority: 10)
diff -ruN cjs-6.2.0-orig/test/test-ci.sh cjs-6.2.0/test/test-ci.sh
--- cjs-6.2.0-orig/test/test-ci.sh 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/test/test-ci.sh 2024-09-16 22:32:54.000000000 +0200
@@ -19,17 +19,6 @@
export SHELL=/bin/bash
PATH=$PATH:~/.local/bin
- if [ "$USE_UNSTABLE_GNOME_PREFIX" = "true" ]; then
- prefix=/opt/GNOME
- libdir=$prefix/lib64
- export PATH=$prefix/bin:$PATH
- export LD_LIBRARY_PATH=$libdir:$LD_LIBRARY_PATH
- export PKG_CONFIG_PATH=$libdir/pkgconfig:$PKG_CONFIG_PATH
- export GI_TYPELIB_PATH=$libdir/girepository-1.0:$GI_TYPELIB_PATH
- export XDG_DATA_DIRS=$prefix/share:$XDG_DATA_DIRS
- export ACLOCAL_PATH=$prefix/share/aclocal:$ACLOCAL_PATH
- fi
-
export DISPLAY="${DISPLAY:-:0}"
}
@@ -64,7 +53,7 @@
# Work out the newest common ancestor between the detached HEAD that this CI
# job has checked out, and the upstream target branch (which will typically
- # be `upstream/master` or `upstream/gnome-nn`).
+ # be `upstream/main` or `upstream/gnome-nn`).
newest_common_ancestor_sha=$(git merge-base ci-upstream-base-branch HEAD)
if test -z "$newest_common_ancestor_sha"; then
echo "Couldnt find common ancestor with the upstream main branch. This"
@@ -149,8 +138,8 @@
elif test "$1" = "BUILD"; then
do_Set_Env
- DEFAULT_CONFIG_OPTS="-Dcairo=enabled -Dreadline=enabled -Dprofiler=enabled \
- -Ddtrace=false -Dsystemtap=false -Dverbose_logs=false --werror"
+ DEFAULT_CONFIG_OPTS="-Dreadline=enabled -Dprofiler=enabled -Ddtrace=false \
+ -Dsystemtap=false -Dverbose_logs=false --werror"
meson setup _build $DEFAULT_CONFIG_OPTS $CONFIG_OPTS
ninja -C _build
diff -ruN cjs-6.2.0-orig/tools/run_iwyu.sh cjs-6.2.0/tools/run_iwyu.sh
--- cjs-6.2.0-orig/tools/run_iwyu.sh 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/tools/run_iwyu.sh 2024-10-14 15:45:41.000000000 +0200
@@ -47,7 +47,7 @@
IWYU_TOOL_ARGS="-I../gjs"
IWYU_ARGS="-Wno-pragma-once-outside-header"
IWYU_RAW="include-what-you-use -xc++ -std=c++17 -Xiwyu --keep=config.h $IWYU_ARGS"
-IWYU_RAW_INC="-I. -I.. $(pkg-config --cflags gobject-introspection-1.0 mozjs-115)"
+IWYU_RAW_INC="-I. -I.. $(pkg-config --cflags gobject-introspection-1.0 mozjs-128)"
PRIVATE_MAPPING="-Xiwyu --mapping_file=$SRCDIR/tools/gjs-private-iwyu.imp -Xiwyu --keep=config.h"
PUBLIC_MAPPING="-Xiwyu --mapping_file=$SRCDIR/tools/gjs-public-iwyu.imp"
POSTPROCESS="python3 $SRCDIR/tools/process_iwyu.py"
diff -ruN cjs-6.2.0-orig/util/console.h cjs-6.2.0/util/console.h
--- cjs-6.2.0-orig/util/console.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/util/console.h 2024-10-14 18:10:38.000000000 +0200
@@ -7,6 +7,8 @@
#ifndef UTIL_CONSOLE_H_
#define UTIL_CONSOLE_H_
+#include <config.h>
+
/* This file has to be valid C, because it's used in libgjs-private */
#include <stdbool.h> /* IWYU pragma: keep */
diff -ruN cjs-6.2.0-orig/util/log.cpp cjs-6.2.0/util/log.cpp
--- cjs-6.2.0-orig/util/log.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/util/log.cpp 2024-10-14 18:09:37.000000000 +0200
@@ -2,16 +2,12 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2008 litl, LLC
-#include <atomic> // for atomic_bool
-#include <memory> // for unique_ptr
-#include <string> // for string
+#include <config.h>
#include <errno.h>
-#include <fcntl.h> // for SEEK_END
#include <stdarg.h>
#include <stdio.h> // for FILE, fprintf, fflush, fopen, fputs, fseek
#include <string.h> // for strchr, strcmp
-#include "cjs/jsapi-util.h"
#ifdef _WIN32
# include <io.h>
@@ -24,9 +20,13 @@
#endif
#include <array>
+#include <atomic> // for atomic_bool
+#include <memory> // for unique_ptr
+#include <string> // for string
#include <glib.h>
+#include "cjs/jsapi-util.h"
#include "util/log.h"
#include "util/misc.h"
diff -ruN cjs-6.2.0-orig/util/log.h cjs-6.2.0/util/log.h
--- cjs-6.2.0-orig/util/log.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/util/log.h 2024-10-14 18:04:47.000000000 +0200
@@ -5,6 +5,8 @@
#ifndef UTIL_LOG_H_
#define UTIL_LOG_H_
+#include <config.h>
+
/* The idea of this is to be able to have one big log file for the entire
* environment, and grep out what you care about. So each module or app
* should have its own entry in the enum. Be sure to add new enum entries
diff -ruN cjs-6.2.0-orig/util/misc.cpp cjs-6.2.0/util/misc.cpp
--- cjs-6.2.0-orig/util/misc.cpp 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/util/misc.cpp 2024-10-14 18:05:41.000000000 +0200
@@ -2,6 +2,8 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2008 litl, LLC
+#include <config.h>
+
#include <glib.h>
#include "util/misc.h"
diff -ruN cjs-6.2.0-orig/util/misc.h cjs-6.2.0/util/misc.h
--- cjs-6.2.0-orig/util/misc.h 2024-06-11 16:27:18.000000000 +0200
+++ cjs-6.2.0/util/misc.h 2024-10-14 18:05:59.000000000 +0200
@@ -5,6 +5,8 @@
#ifndef UTIL_MISC_H_
#define UTIL_MISC_H_
+#include <config.h>
+
#include <errno.h>
#include <stdio.h> // for FILE, stdout
#include <string.h> // for memcpy