summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Chimento <philip.chimento@gmail.com>2023-03-05 18:26:25 +0000
committerPhilip Chimento <philip.chimento@gmail.com>2023-03-05 18:26:25 +0000
commitad36414e00d4d9c1b0160e1ec3041fd1607dcd77 (patch)
treeb9ece2e1fda25100814480aaea1c21cf0701bdd4
parent12de7257e2923c5285614d51f06d751ebc0c60a8 (diff)
parent52adc35e38b2475fd6468982bb9ed31d50304f36 (diff)
downloadgjs-ad36414e00d4d9c1b0160e1ec3041fd1607dcd77.tar.gz
Merge branch 'february-maintenance' into 'master'
February maintenance See merge request GNOME/gjs!828
-rw-r--r--gi/repo.cpp13
-rw-r--r--gjs/console.cpp2
-rw-r--r--gjs/context-private.h5
-rw-r--r--gjs/context.cpp52
-rw-r--r--gjs/mainloop.cpp3
-rw-r--r--gjs/mainloop.h8
-rw-r--r--gjs/profiler.cpp2
-rw-r--r--gjs/promise.cpp20
-rw-r--r--gjs/stack.cpp2
-rw-r--r--installed-tests/js/jsunit.gresources.xml4
-rw-r--r--installed-tests/js/meson.build1
-rw-r--r--installed-tests/js/minijasmine.js1
-rw-r--r--installed-tests/js/modules/badOverrides2/.eslintrc.yml8
-rw-r--r--installed-tests/js/modules/badOverrides2/GIMarshallingTests.js5
-rw-r--r--installed-tests/js/modules/badOverrides2/Gio.js5
-rw-r--r--installed-tests/js/modules/badOverrides2/Regress.js5
-rw-r--r--installed-tests/js/modules/badOverrides2/WarnLib.js3
-rw-r--r--installed-tests/js/testESModules.js6
-rw-r--r--installed-tests/js/testImporter.js2
-rw-r--r--installed-tests/js/testImporter2.js39
-rw-r--r--installed-tests/minijasmine.cpp1
-rw-r--r--modules/core/overrides/GLib.js13
-rw-r--r--modules/esm/_encoding/encoding.js5
-rw-r--r--modules/esm/_timers.js23
-rw-r--r--modules/esm/console.js12
-rw-r--r--modules/esm/gi.js19
-rw-r--r--test/gjs-tests.cpp6
-rw-r--r--util/log.cpp2
-rw-r--r--util/log.h1
29 files changed, 188 insertions, 80 deletions
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 27e78b5e..20f445df 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -126,16 +126,16 @@ static bool resolve_namespace_object(JSContext* context,
JS::RootedObject gi_namespace(context,
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,
GJS_MODULE_PROP_FLAGS))
return false;
- JS::RootedValue override(context);
- if (!lookup_override_function(context, ns_id, &override))
- return false;
-
JS::RootedValue result(context);
if (!override.isUndefined() &&
!JS_CallFunctionValue (context, gi_namespace, /* thisp */
@@ -556,9 +556,12 @@ lookup_override_function(JSContext *cx,
goto fail;
}
+ // If the override module is present, it must have a callable _init(). An
+ // override module without _init() is probably unintentional. (function
+ // being undefined means there was no override module.)
if (!gjs_object_require_property(cx, module, "override module",
atoms.init(), function) ||
- !function.isObjectOrNull()) {
+ !function.isObject() || !JS::IsCallable(&function.toObject())) {
gjs_throw(cx, "Unexpected value for _init in overrides module");
goto fail;
}
diff --git a/gjs/console.cpp b/gjs/console.cpp
index 3d53ade0..c2e15789 100644
--- a/gjs/console.cpp
+++ b/gjs/console.cpp
@@ -355,7 +355,7 @@ int main(int argc, char** argv) {
if (coverage_prefixes)
gjs_coverage_enable();
- GjsAutoUnref<GjsContext> js_context(static_cast<GjsContext*>(g_object_new(
+ GjsAutoUnref<GjsContext> js_context(GJS_CONTEXT(g_object_new(
GJS_TYPE_CONTEXT, "search-path", include_path.get(), "program-name",
program_name, "program-path", program_path.get(), "profiler-enabled",
enable_profiler, "exec-as-module", exec_as_module, nullptr)));
diff --git a/gjs/context-private.h b/gjs/context-private.h
index d8b7b85d..147feae4 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -17,7 +17,6 @@
#include <utility> // for pair
#include <vector>
-#include <gio/gio.h>
#include <glib-object.h>
#include <glib.h>
@@ -248,13 +247,11 @@ class GjsContextPrivate : public JS::JobQueue {
JS::HandleObject allocation_site,
JS::HandleObject incumbent_global) override;
void runJobs(JSContext* cx) override;
- void runJobs(JSContext* cx, GCancellable* cancellable);
[[nodiscard]] bool empty() const override { return m_job_queue.empty(); }
js::UniquePtr<JS::JobQueue::SavedJobQueue> saveJobQueue(
JSContext* cx) override;
- GJS_JSAPI_RETURN_CONVENTION bool run_jobs_fallible(
- GCancellable* cancellable = nullptr);
+ GJS_JSAPI_RETURN_CONVENTION bool run_jobs_fallible();
void register_unhandled_promise_rejection(uint64_t id, GjsAutoChar&& stack);
void unregister_unhandled_promise_rejection(uint64_t id);
void warn_about_unhandled_promise_rejections();
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 94a45c8b..59ac8afd 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -531,6 +531,12 @@ static bool on_context_module_rejected_log_exception(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());
+
JS::HandleValue error = args.get(0);
GjsContextPrivate* gjs_cx = GjsContextPrivate::from_cx(cx);
@@ -547,6 +553,12 @@ static bool on_context_module_rejected_log_exception(JSContext* cx,
static bool on_context_module_resolved(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 resolved: %s",
+ gjs_debug_string(id).c_str());
+
args.rval().setUndefined();
GjsContextPrivate::from_cx(cx)->main_loop_release();
@@ -565,12 +577,12 @@ static bool add_promise_reactions(JSContext* cx, JS::HandleValue promise,
JS::RootedFunction on_rejected(
cx,
- js::NewFunctionWithReserved(cx, reject, 1, 0, resolved_tag.c_str()));
+ js::NewFunctionWithReserved(cx, reject, 1, 0, rejected_tag.c_str()));
if (!on_rejected)
return false;
JS::RootedFunction on_resolved(
cx,
- js::NewFunctionWithReserved(cx, resolve, 1, 0, rejected_tag.c_str()));
+ js::NewFunctionWithReserved(cx, resolve, 1, 0, resolved_tag.c_str()));
if (!on_resolved)
return false;
@@ -606,6 +618,12 @@ static void load_context_module(JSContext* cx, const char* uri,
[](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());
+
JS::HandleValue error = args.get(0);
// Abort because this module is required.
gjs_log_exception_full(cx, error, nullptr, G_LOG_LEVEL_ERROR);
@@ -613,7 +631,7 @@ static void load_context_module(JSContext* cx, const char* uri,
GjsContextPrivate::from_cx(cx)->main_loop_release();
return false;
},
- "context");
+ debug_identifier);
if (!ok) {
gjs_log_exception(cx);
@@ -988,15 +1006,10 @@ bool GjsContextPrivate::should_exit(uint8_t* exit_code_p) const {
return m_should_exit;
}
-void GjsContextPrivate::start_draining_job_queue(void) {
- gjs_debug(GJS_DEBUG_CONTEXT, "Starting promise job dispatcher");
- m_dispatcher.start();
-}
+void GjsContextPrivate::start_draining_job_queue(void) { m_dispatcher.start(); }
void GjsContextPrivate::stop_draining_job_queue(void) {
m_draining_job_queue = false;
-
- gjs_debug(GJS_DEBUG_CONTEXT, "Stopping promise job dispatcher");
m_dispatcher.stop();
}
@@ -1015,7 +1028,7 @@ bool GjsContextPrivate::enqueuePromiseJob(JSContext* cx [[maybe_unused]],
g_assert(cx == m_cx);
g_assert(from_cx(cx) == this);
- gjs_debug(GJS_DEBUG_CONTEXT,
+ gjs_debug(GJS_DEBUG_MAINLOOP,
"Enqueue job %s, promise=%s, allocation site=%s",
gjs_debug_object(job).c_str(), gjs_debug_object(promise).c_str(),
gjs_debug_object(allocation_site).c_str());
@@ -1032,12 +1045,10 @@ bool GjsContextPrivate::enqueuePromiseJob(JSContext* cx [[maybe_unused]],
// Override of JobQueue::runJobs(). Called by js::RunJobs(), and when execution
// of the job queue was interrupted by the debugger and is resuming.
-void GjsContextPrivate::runJobs(JSContext* cx) { runJobs(cx, nullptr); }
-
-void GjsContextPrivate::runJobs(JSContext* cx, GCancellable* cancellable) {
+void GjsContextPrivate::runJobs(JSContext* cx) {
g_assert(cx == m_cx);
g_assert(from_cx(cx) == this);
- if (!run_jobs_fallible(cancellable))
+ if (!run_jobs_fallible())
gjs_log_exception(cx);
}
@@ -1053,7 +1064,7 @@ void GjsContextPrivate::runJobs(JSContext* cx, GCancellable* cancellable) {
* Returns: false if one of the jobs threw an uncatchable exception;
* otherwise true.
*/
-bool GjsContextPrivate::run_jobs_fallible(GCancellable* cancellable) {
+bool GjsContextPrivate::run_jobs_fallible() {
bool retval = true;
if (m_draining_job_queue || m_should_exit)
@@ -1070,8 +1081,11 @@ bool GjsContextPrivate::run_jobs_fallible(GCancellable* cancellable) {
* it's crucial to recheck the queue length during each iteration. */
for (size_t ix = 0; ix < m_job_queue.length(); ix++) {
/* A previous job might have set this flag. e.g., System.exit(). */
- if (m_should_exit || g_cancellable_is_cancelled(cancellable))
+ if (m_should_exit || !m_dispatcher.is_running()) {
+ gjs_debug(GJS_DEBUG_MAINLOOP, "Stopping jobs because of %s",
+ m_should_exit ? "exit" : "main loop cancel");
break;
+ }
job = m_job_queue[ix];
@@ -1085,7 +1099,7 @@ bool GjsContextPrivate::run_jobs_fallible(GCancellable* cancellable) {
m_job_queue[ix] = nullptr;
{
JSAutoRealm ar(m_cx, job);
- gjs_debug(GJS_DEBUG_CONTEXT, "handling job %s",
+ gjs_debug(GJS_DEBUG_MAINLOOP, "handling job %zu, %s", ix,
gjs_debug_object(job).c_str());
if (!JS::Call(m_cx, JS::UndefinedHandleValue, job, args, &rval)) {
/* Uncatchable exception - return false so that
@@ -1104,6 +1118,7 @@ bool GjsContextPrivate::run_jobs_fallible(GCancellable* cancellable) {
gjs_log_exception_uncaught(m_cx);
}
}
+ gjs_debug(GJS_DEBUG_MAINLOOP, "Completed job %zu", ix);
}
m_draining_job_queue = false;
@@ -1380,6 +1395,7 @@ bool GjsContextPrivate::set_main_loop_hook(JSObject* callable) {
bool GjsContextPrivate::run_main_loop_hook() {
JS::RootedObject hook(m_cx, m_main_loop_hook.get());
m_main_loop_hook = nullptr;
+ gjs_debug(GJS_DEBUG_MAINLOOP, "Running and clearing main loop hook");
JS::RootedValue ignored_rval(m_cx);
return JS::Call(m_cx, JS::NullHandleValue, hook,
JS::HandleValueArray::empty(), &ignored_rval);
@@ -1490,7 +1506,7 @@ bool GjsContextPrivate::eval_module(const char* identifier,
ok = add_promise_reactions(
m_cx, evaluation_promise, on_context_module_resolved,
- on_context_module_rejected_log_exception, "context");
+ on_context_module_rejected_log_exception, identifier);
}
bool exiting = false;
diff --git a/gjs/mainloop.cpp b/gjs/mainloop.cpp
index b69d45a8..3ad3f5c7 100644
--- a/gjs/mainloop.cpp
+++ b/gjs/mainloop.cpp
@@ -20,6 +20,7 @@ bool MainLoop::spin(GjsContextPrivate* gjs) {
if (gjs->should_exit(nullptr)) {
// Return false to indicate the loop is exiting due to an exit call,
// the queue is likely not empty
+ debug("Not spinning loop because System.exit called");
exit();
return false;
}
@@ -27,6 +28,7 @@ bool MainLoop::spin(GjsContextPrivate* gjs) {
GjsAutoPointer<GMainContext, GMainContext, g_main_context_unref>
main_context(g_main_context_ref_thread_default());
+ debug("Spinning loop until released or hook cleared");
do {
bool blocking = can_block();
@@ -36,6 +38,7 @@ bool MainLoop::spin(GjsContextPrivate* gjs) {
// If System.exit() has not been called
if (gjs->should_exit(nullptr)) {
+ debug("Stopped spinning loop because System.exit called");
exit();
return false;
}
diff --git a/gjs/mainloop.h b/gjs/mainloop.h
index f374060c..a228d761 100644
--- a/gjs/mainloop.h
+++ b/gjs/mainloop.h
@@ -8,6 +8,8 @@
#include <glib.h>
+#include "util/log.h"
+
class GjsContextPrivate;
namespace Gjs {
@@ -20,6 +22,10 @@ class MainLoop {
grefcount m_hold_count;
bool m_exiting;
+ void debug(const char* msg) {
+ gjs_debug(GJS_DEBUG_MAINLOOP, "Main loop instance %p: %s", this, msg);
+ }
+
[[nodiscard]] bool can_block() {
// Don't block if exiting
if (m_exiting)
@@ -51,6 +57,7 @@ class MainLoop {
if (m_exiting)
return;
+ debug("hold");
g_ref_count_inc(&m_hold_count);
}
@@ -59,6 +66,7 @@ class MainLoop {
if (m_exiting)
return;
+ debug("release");
bool zero [[maybe_unused]] = g_ref_count_dec(&m_hold_count);
g_assert(!zero && "main loop released too many times");
}
diff --git a/gjs/profiler.cpp b/gjs/profiler.cpp
index 39c19621..1a5560f7 100644
--- a/gjs/profiler.cpp
+++ b/gjs/profiler.cpp
@@ -653,7 +653,7 @@ gjs_profiler_stop(GjsProfiler *self)
static gboolean
gjs_profiler_sigusr2(void *data)
{
- auto context = static_cast<GjsContext *>(data);
+ GjsContext* context = GJS_CONTEXT(data);
GjsProfiler *current_profiler = gjs_context_get_profiler(context);
if (current_profiler) {
diff --git a/gjs/promise.cpp b/gjs/promise.cpp
index 39939aa9..f248b019 100644
--- a/gjs/promise.cpp
+++ b/gjs/promise.cpp
@@ -6,6 +6,8 @@
#include <stddef.h> // for size_t
+#include <string>
+
#include <gio/gio.h>
#include <glib-object.h>
@@ -22,6 +24,7 @@
#include "gjs/jsapi-util.h"
#include "gjs/macros.h"
#include "gjs/promise.h"
+#include "util/log.h"
/**
* promise.cpp - This file implements a custom GSource, PromiseJobQueueSource,
@@ -79,12 +82,8 @@ class PromiseJobDispatcher::Source : public GSource {
// next one to execute. (it will starve the other sources)
g_source_set_ready_time(this, -1);
- // A reference to the current cancellable is needed in case any
- // jobs reset PromiseJobDispatcher and thus replace the cancellable.
- GjsAutoUnref<GCancellable> cancellable(m_cancellable,
- GjsAutoTakeOwnership{});
// Drain the job queue.
- m_gjs->runJobs(m_gjs->context(), cancellable);
+ m_gjs->runJobs(m_gjs->context());
return G_SOURCE_CONTINUE;
}
@@ -135,6 +134,8 @@ class PromiseJobDispatcher::Source : public GSource {
if (!g_cancellable_is_cancelled(m_cancellable))
return;
+ gjs_debug(GJS_DEBUG_MAINLOOP, "Uncancelling promise job dispatcher");
+
if (is_running())
g_source_remove_child_source(this, m_cancellable_source);
else
@@ -179,10 +180,14 @@ void PromiseJobDispatcher::start() {
if (is_running())
return;
+ gjs_debug(GJS_DEBUG_MAINLOOP, "Starting promise job dispatcher");
g_source_attach(m_source.get(), m_main_context);
}
-void PromiseJobDispatcher::stop() { m_source->cancel(); }
+void PromiseJobDispatcher::stop() {
+ gjs_debug(GJS_DEBUG_MAINLOOP, "Stopping promise job dispatcher");
+ m_source->cancel();
+}
}; // namespace Gjs
@@ -212,6 +217,9 @@ bool set_main_loop_hook(JSContext* cx, unsigned argc, JS::Value* vp) {
return false;
}
+ gjs_debug(GJS_DEBUG_MAINLOOP, "Set main loop hook to %s",
+ gjs_debug_object(callback).c_str());
+
GjsContextPrivate* priv = GjsContextPrivate::from_cx(cx);
if (!priv->set_main_loop_hook(callback)) {
gjs_throw(
diff --git a/gjs/stack.cpp b/gjs/stack.cpp
index b3d0d9c3..3292eae4 100644
--- a/gjs/stack.cpp
+++ b/gjs/stack.cpp
@@ -31,7 +31,7 @@ gjs_dumpstack(void)
GList *iter;
for (iter = contexts; iter; iter = iter->next) {
- GjsAutoUnref<GjsContext> context(static_cast<GjsContext*>(iter->data));
+ GjsAutoUnref<GjsContext> context(GJS_CONTEXT(iter->data));
gjs_context_print_stack_stderr(context);
}
}
diff --git a/installed-tests/js/jsunit.gresources.xml b/installed-tests/js/jsunit.gresources.xml
index 209274fc..75c54c90 100644
--- a/installed-tests/js/jsunit.gresources.xml
+++ b/installed-tests/js/jsunit.gresources.xml
@@ -13,6 +13,10 @@
<file>modules/badOverrides/Gio.js</file>
<file>modules/badOverrides/Regress.js</file>
<file>modules/badOverrides/WarnLib.js</file>
+ <file>modules/badOverrides2/GIMarshallingTests.js</file>
+ <file>modules/badOverrides2/Gio.js</file>
+ <file>modules/badOverrides2/Regress.js</file>
+ <file>modules/badOverrides2/WarnLib.js</file>
<file>modules/subBadInit/__init__.js</file>
<file>modules/subErrorInit/__init__.js</file>
<file>modules/data.txt</file>
diff --git a/installed-tests/js/meson.build b/installed-tests/js/meson.build
index 5520a19d..6db887d2 100644
--- a/installed-tests/js/meson.build
+++ b/installed-tests/js/meson.build
@@ -127,6 +127,7 @@ jasmine_tests = [
'GObjectValue',
'GTypeClass',
'Importer',
+ 'Importer2',
'Introspection',
'Lang',
'LegacyByteArray',
diff --git a/installed-tests/js/minijasmine.js b/installed-tests/js/minijasmine.js
index 36f953bc..cd00f611 100644
--- a/installed-tests/js/minijasmine.js
+++ b/installed-tests/js/minijasmine.js
@@ -10,7 +10,6 @@ function _filterStack(stack) {
return stack.split('\n')
.filter(stackLine => stackLine.indexOf('resource:///org/gjs/jsunit') === -1)
- .filter(stackLine => stackLine.indexOf('<jasmine-start>') === -1)
.join('\n');
}
diff --git a/installed-tests/js/modules/badOverrides2/.eslintrc.yml b/installed-tests/js/modules/badOverrides2/.eslintrc.yml
new file mode 100644
index 00000000..b1c10f5d
--- /dev/null
+++ b/installed-tests/js/modules/badOverrides2/.eslintrc.yml
@@ -0,0 +1,8 @@
+---
+# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+# SPDX-FileCopyrightText: 2018 Philip Chimento <philip.chimento@gmail.com>
+rules:
+ no-throw-literal: 'off' # these are intended to be bad code
+ no-unused-vars:
+ - error
+ - varsIgnorePattern: ^_init$
diff --git a/installed-tests/js/modules/badOverrides2/GIMarshallingTests.js b/installed-tests/js/modules/badOverrides2/GIMarshallingTests.js
new file mode 100644
index 00000000..6cf2dfa9
--- /dev/null
+++ b/installed-tests/js/modules/badOverrides2/GIMarshallingTests.js
@@ -0,0 +1,5 @@
+// Sabotage the import of imports.gi.GIMarshallingTests!
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
+
+var _init = {thingsThatThisObjectIsNot: ['callable']};
diff --git a/installed-tests/js/modules/badOverrides2/Gio.js b/installed-tests/js/modules/badOverrides2/Gio.js
new file mode 100644
index 00000000..9758a7c0
--- /dev/null
+++ b/installed-tests/js/modules/badOverrides2/Gio.js
@@ -0,0 +1,5 @@
+// Sabotage the import of imports.gi.Gio!
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
+
+var _init = null;
diff --git a/installed-tests/js/modules/badOverrides2/Regress.js b/installed-tests/js/modules/badOverrides2/Regress.js
new file mode 100644
index 00000000..e4252f67
--- /dev/null
+++ b/installed-tests/js/modules/badOverrides2/Regress.js
@@ -0,0 +1,5 @@
+// Sabotage the import of imports.gi.Regress!
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
+
+var _init;
diff --git a/installed-tests/js/modules/badOverrides2/WarnLib.js b/installed-tests/js/modules/badOverrides2/WarnLib.js
new file mode 100644
index 00000000..b2c93b05
--- /dev/null
+++ b/installed-tests/js/modules/badOverrides2/WarnLib.js
@@ -0,0 +1,3 @@
+// Sabotage the import of imports.gi.WarnLib!
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
diff --git a/installed-tests/js/testESModules.js b/installed-tests/js/testESModules.js
index 30db17e4..e727c790 100644
--- a/installed-tests/js/testESModules.js
+++ b/installed-tests/js/testESModules.js
@@ -27,26 +27,32 @@ describe('ES module imports', function () {
it('import with version parameter', function () {
expect(gi.require('GObject', '2.0')).toBe(gi.require('GObject'));
+ expect(imports.gi.versions['GObject']).toBe('2.0');
});
it('import again with other version parameter', function () {
expect(() => gi.require('GObject', '1.75')).toThrow();
+ expect(imports.gi.versions['GObject']).toBe('2.0');
});
it('import for the first time with wrong version', function () {
expect(() => gi.require('Gtk', '1.75')).toThrow();
+ expect(imports.gi.versions['Gtk']).not.toBeDefined();
});
it('import with another version after a failed import', function () {
expect(gi.require('Gtk', '3.0').toString()).toEqual('[object GIRepositoryNamespace]');
+ expect(imports.gi.versions['Gtk']).toBe('3.0');
});
it('import nonexistent module', function () {
expect(() => gi.require('PLib')).toThrow();
+ expect(imports.gi.versions['PLib']).not.toBeDefined();
});
it('GObject introspection import via URL scheme', function () {
expect(Gio.toString()).toEqual('[object GIRepositoryNamespace]');
+ expect(imports.gi.versions['Gio']).toBe('2.0');
});
it('import.meta.url', function () {
diff --git a/installed-tests/js/testImporter.js b/installed-tests/js/testImporter.js
index 8380af20..f03bf634 100644
--- a/installed-tests/js/testImporter.js
+++ b/installed-tests/js/testImporter.js
@@ -32,7 +32,7 @@ describe('GI importer', function () {
expect(() => imports.gi.Regress).toThrow('💩');
});
- it("throws an exception when the overrides _init isn't a function", function () {
+ it('throws an exception when the overrides _init is a primitive', function () {
expect(() => imports.gi.Gio).toThrowError(/_init/);
});
});
diff --git a/installed-tests/js/testImporter2.js b/installed-tests/js/testImporter2.js
new file mode 100644
index 00000000..b4e71225
--- /dev/null
+++ b/installed-tests/js/testImporter2.js
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Philip Chimento <philip.chimento@gmail.com>
+
+// This test is in a separate file instead of testImporter.js, because it tests
+// loading overrides for g-i modules, and in the original file we have literally
+// run out of g-i modules to override -- at least, the ones that we can assume
+// will be present on any system where GJS is compiled.
+
+describe('GI importer', function () {
+ describe('on failure', function () {
+ // For these tests, we provide special overrides files to sabotage the
+ // import, at the path resource:///org/gjs/jsunit/modules/badOverrides2.
+ let oldSearchPath;
+ beforeAll(function () {
+ oldSearchPath = imports.overrides.searchPath.slice();
+ imports.overrides.searchPath = ['resource:///org/gjs/jsunit/modules/badOverrides2'];
+ });
+
+ afterAll(function () {
+ imports.overrides.searchPath = oldSearchPath;
+ });
+
+ it("throws an exception when the overrides _init isn't a function", function () {
+ expect(() => imports.gi.GIMarshallingTests).toThrowError(/_init/);
+ });
+
+ it('throws an exception when the overrides _init is null', function () {
+ expect(() => imports.gi.Gio).toThrowError(/_init/);
+ });
+
+ it('throws an exception when the overrides _init is undefined', function () {
+ expect(() => imports.gi.Regress).toThrowError(/_init/);
+ });
+
+ it('throws an exception when the overrides _init is missing', function () {
+ expect(() => imports.gi.WarnLib).toThrowError(/_init/);
+ });
+ });
+});
diff --git a/installed-tests/minijasmine.cpp b/installed-tests/minijasmine.cpp
index 9d1c65f9..3036fb87 100644
--- a/installed-tests/minijasmine.cpp
+++ b/installed-tests/minijasmine.cpp
@@ -33,7 +33,6 @@ main(int argc, char **argv)
if (argc < 2)
g_error("Need a test file");
- /* The fact that this isn't the default is kind of lame... */
g_setenv("GJS_DEBUG_OUTPUT", "stderr", false);
setlocale(LC_ALL, "");
diff --git a/modules/core/overrides/GLib.js b/modules/core/overrides/GLib.js
index 023cf795..2d51b575 100644
--- a/modules/core/overrides/GLib.js
+++ b/modules/core/overrides/GLib.js
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2011 Giovanni Campagna
-const ByteArray = imports.byteArray;
+const ByteArray = imports._byteArrayNative;
const {setMainLoopHook} = imports._promiseNative;
let GLib;
@@ -100,15 +100,12 @@ function _packVariant(signature, value) {
}
if (arrayType[0] === 'y') {
// special case for array of bytes
- let bytes;
if (typeof value === 'string') {
- let byteArray = ByteArray.fromString(value);
- if (byteArray[byteArray.length - 1] !== 0)
- byteArray = Uint8Array.of(...byteArray, 0);
- bytes = ByteArray.toGBytes(byteArray);
- } else {
- bytes = new GLib.Bytes(value);
+ value = ByteArray.fromString(value);
+ if (value[value.length - 1] !== 0)
+ value = Uint8Array.of(...value, 0);
}
+ const bytes = new GLib.Bytes(value);
return GLib.Variant.new_from_bytes(new GLib.VariantType('ay'),
bytes, true);
}
diff --git a/modules/esm/_encoding/encoding.js b/modules/esm/_encoding/encoding.js
index 41261b96..60cc0494 100644
--- a/modules/esm/_encoding/encoding.js
+++ b/modules/esm/_encoding/encoding.js
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2021 Evan Welsh <contact@evanwelsh.com>
-const Encoding = import.meta.importSync('_encodingNative');
-
import {getEncodingFromLabel} from './encodingMap.js';
class TextDecoder {
@@ -128,6 +126,7 @@ class TextDecoder {
input = new Uint8Array(buffer, byteOffset + 3, byteLength - 3);
}
+ const Encoding = import.meta.importSync('_encodingNative');
return Encoding.decode(input, this._internalEncoding, this.fatal);
}
}
@@ -142,11 +141,13 @@ class TextEncoder {
}
encode(input = '') {
+ const Encoding = import.meta.importSync('_encodingNative');
// The TextEncoder specification only allows for UTF-8 encoding.
return Encoding.encode(`${input}`, 'utf-8');
}
encodeInto(input = '', output = new Uint8Array()) {
+ const Encoding = import.meta.importSync('_encodingNative');
// The TextEncoder specification only allows for UTF-8 encoding.
return Encoding.encodeInto(`${input}`, output);
}
diff --git a/modules/esm/_timers.js b/modules/esm/_timers.js
index 94da6f8f..ca20b71d 100644
--- a/modules/esm/_timers.js
+++ b/modules/esm/_timers.js
@@ -6,11 +6,6 @@
// Note: implicit coercion with + is used to perform the ToNumber algorithm from
// the timers specification
-import GLib from 'gi://GLib';
-import GObject from 'gi://GObject';
-
-const PromiseNative = import.meta.importSync('_promiseNative');
-
/**
* @param {number} delay a number value (in milliseconds)
*/
@@ -52,9 +47,9 @@ function checkThis(thisArg) {
* @returns {GLib.Source}
*/
function createTimeoutSource(timeout, handler) {
- const source = GLib.timeout_source_new(timeout);
- source.set_priority(GLib.PRIORITY_DEFAULT);
- GObject.source_set_closure(source, handler);
+ const source = imports.gi.GLib.timeout_source_new(timeout);
+ source.set_priority(imports.gi.GLib.PRIORITY_DEFAULT);
+ imports.gi.GObject.source_set_closure(source, handler);
return source;
}
@@ -72,13 +67,13 @@ function setTimeout(callback, delay = 0, ...args) {
const boundCallback = callback.bind(globalThis, ...args);
const source = createTimeoutSource(delay, () => {
if (!timeouts.has(source))
- return GLib.SOURCE_REMOVE;
+ return imports.gi.GLib.SOURCE_REMOVE;
boundCallback();
releaseSource(source);
- PromiseNative.drainMicrotaskQueue();
+ import.meta.importSync('_promiseNative').drainMicrotaskQueue();
- return GLib.SOURCE_REMOVE;
+ return imports.gi.GLib.SOURCE_REMOVE;
});
addSource(source);
@@ -98,12 +93,12 @@ function setInterval(callback, delay = 0, ...args) {
const boundCallback = callback.bind(globalThis, ...args);
const source = createTimeoutSource(delay, () => {
if (!timeouts.has(source))
- return GLib.SOURCE_REMOVE;
+ return imports.gi.GLib.SOURCE_REMOVE;
boundCallback();
- PromiseNative.drainMicrotaskQueue();
+ import.meta.importSync('_promiseNative').drainMicrotaskQueue();
- return GLib.SOURCE_CONTINUE;
+ return imports.gi.GLib.SOURCE_CONTINUE;
});
addSource(source);
diff --git a/modules/esm/console.js b/modules/esm/console.js
index 74a34666..1532624d 100644
--- a/modules/esm/console.js
+++ b/modules/esm/console.js
@@ -1,9 +1,6 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2021 Evan Welsh <contact@evanwelsh.com>
-import GLib from 'gi://GLib';
-import GjsPrivate from 'gi://GjsPrivate';
-
const DEFAULT_LOG_DOMAIN = 'Gjs-Console';
// A line-by-line implementation of https://console.spec.whatwg.org/.
@@ -112,7 +109,7 @@ class Console {
*/
clear() {
this.#groupIndentation = '';
- GjsPrivate.clear_terminal();
+ imports.gi.GjsPrivate.clear_terminal();
}
/**
@@ -268,7 +265,7 @@ class Console {
* @returns {void}
*/
time(label) {
- this.#timeLabels[label] = GLib.get_monotonic_time();
+ this.#timeLabels[label] = imports.gi.GLib.get_monotonic_time();
}
/**
@@ -288,7 +285,7 @@ class Console {
`No time log found for label: '${label}'.`,
]);
} else {
- const durationMs = (GLib.get_monotonic_time() - startTime) / 1000;
+ const durationMs = (imports.gi.GLib.get_monotonic_time() - startTime) / 1000;
const concat = `${label}: ${durationMs.toFixed(3)} ms`;
data.unshift(concat);
@@ -314,7 +311,7 @@ class Console {
} else {
delete this.#timeLabels[label];
- const durationMs = (GLib.get_monotonic_time() - startTime) / 1000;
+ const durationMs = (imports.gi.GLib.get_monotonic_time() - startTime) / 1000;
const concat = `${label}: ${durationMs.toFixed(3)} ms`;
this.#printer('timeEnd', [concat]);
@@ -500,6 +497,7 @@ class Console {
* @returns {void}
*/
#printer(logLevel, args, options) {
+ const GLib = imports.gi.GLib;
let severity;
switch (logLevel) {
diff --git a/modules/esm/gi.js b/modules/esm/gi.js
index 126ce7e7..b49cf120 100644
--- a/modules/esm/gi.js
+++ b/modules/esm/gi.js
@@ -8,17 +8,24 @@ const Gi = {
if (namespace === 'versions')
throw new Error('Cannot import namespace "versions", use the version parameter of Gi.require to specify versions.');
+ let oldVersion = gi.versions[namespace];
if (version !== undefined)
gi.versions[namespace] = version;
- const module = gi[namespace];
+ try {
+ const module = gi[namespace];
- if (version !== undefined && version !== module.__version__) {
- throw new Error(`Version ${module.__version__} of GI module ${
- namespace} already loaded, cannot load version ${version}`);
- }
+ if (version !== undefined && version !== module.__version__) {
+ throw new Error(`Version ${module.__version__} of GI module ${
+ namespace} already loaded, cannot load version ${version}`);
+ }
- return module;
+ return module;
+ } catch (error) {
+ // Roll back change to versions object if import failed
+ gi.versions[namespace] = oldVersion;
+ throw error;
+ }
},
};
Object.freeze(Gi);
diff --git a/test/gjs-tests.cpp b/test/gjs-tests.cpp
index e48734aa..7d46e286 100644
--- a/test/gjs-tests.cpp
+++ b/test/gjs-tests.cpp
@@ -814,10 +814,8 @@ gjstest_test_func_util_misc_strv_concat_pointers(void)
static void
gjstest_test_profiler_start_stop(void)
{
- GjsAutoUnref<GjsContext> context =
- static_cast<GjsContext *>(g_object_new(GJS_TYPE_CONTEXT,
- "profiler-enabled", TRUE,
- nullptr));
+ GjsAutoUnref<GjsContext> context = GJS_CONTEXT(
+ g_object_new(GJS_TYPE_CONTEXT, "profiler-enabled", TRUE, nullptr));
GjsProfiler *profiler = gjs_context_get_profiler(context);
gjs_profiler_set_filename(profiler, "dont-conflict-with-other-test.syscap");
diff --git a/util/log.cpp b/util/log.cpp
index ce4b02dd..4a910342 100644
--- a/util/log.cpp
+++ b/util/log.cpp
@@ -53,6 +53,8 @@ static const char* topic_to_prefix(GjsDebugTopic topic) {
return "JS CAIRO";
case GJS_DEBUG_KEEP_ALIVE:
return "JS KP ALV";
+ case GJS_DEBUG_MAINLOOP:
+ return "JS MAINLOOP";
case GJS_DEBUG_GREPO:
return "JS G REPO";
case GJS_DEBUG_GNAMESPACE:
diff --git a/util/log.h b/util/log.h
index 92880e73..93ce94f5 100644
--- a/util/log.h
+++ b/util/log.h
@@ -18,6 +18,7 @@ typedef enum {
GJS_DEBUG_NATIVE,
GJS_DEBUG_CAIRO,
GJS_DEBUG_KEEP_ALIVE,
+ GJS_DEBUG_MAINLOOP,
GJS_DEBUG_GREPO,
GJS_DEBUG_GNAMESPACE,
GJS_DEBUG_GOBJECT,