Marco Trevisan (Treviño) has proposed merging ~3v1n0/ubuntu/+source/gjs:ubuntu/bionic into ~ubuntu-desktop/ubuntu/+source/gjs:ubuntu/bionic.
Requested reviews: Ubuntu Desktop (ubuntu-desktop) Related bugs: Bug #1803271 in gjs (Ubuntu): "[regression] Much higher CPU during some gnome-shell operations" https://bugs.launchpad.net/ubuntu/+source/gjs/+bug/1803271 For more details, see: https://code.launchpad.net/~3v1n0/ubuntu/+source/gjs/+git/gjs/+merge/361167 -- Your team Ubuntu Desktop is requested to review the proposed merge of ~3v1n0/ubuntu/+source/gjs:ubuntu/bionic into ~ubuntu-desktop/ubuntu/+source/gjs:ubuntu/bionic.
diff --git a/Makefile.in b/Makefile.in index fea2a6b..ed5566b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -756,9 +756,8 @@ am__DIST_COMMON = $(srcdir)/Makefile-examples.am \ $(srcdir)/config.h.in $(srcdir)/gjs-1.0.pc.in \ $(srcdir)/gjs-modules-srcs.mk $(srcdir)/gjs-srcs.mk \ $(top_srcdir)/win32/config.h.win32.in AUTHORS COPYING \ - ChangeLog INSTALL NEWS README compile config.guess \ - config.rpath config.sub depcomp install-sh ltmain.sh missing \ - tap-driver.sh + ChangeLog INSTALL NEWS README compile config.guess config.sub \ + depcomp install-sh ltmain.sh missing tap-driver.sh DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) diff --git a/NEWS b/NEWS index 43feb1f..1e33aa5 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,76 @@ +Version 1.52.5 +-------------- + +- This release includes the "Big Hammer" patch from GNOME 3.30 to reduce memory + usage. For more information, read the blog post at + https://feaneron.com/2018/04/20/the-infamous-gnome-shell-memory-leak/ + It was not originally intended to be backported to GNOME 3.28, but in practice + several Linux distributions already backported it, and it has been working + well to reduce memory usage, and the bugs have been ironed out of it. + + It does decrease performance somewhat, so if you don't want that then don't + install this update. + +- Closed bugs and merge requests: + + * Ensure not to miss the force_gc flag [#150, !132, Carlos Garnacho] + * Make GC much more aggressive [#62, !50, Giovanni Campagna, Georges Basile + Stavracas Neto, Philip Chimento] + * Queue GC when a GObject reference is toggled down [#140, !114, !127, Georges + Basile Stavracas Neto] + * Reduce memory overhead of g_object_weak_ref() [#144, !122, Carlos Garnacho, + Philip Chimento] + * context: Defer and therefore batch forced GC runs [performance] [!236, + Daniel van Vugt] + * context: use timeout with seconds to schedule a gc trigger [!239, Marco + Trevisan] + * Use compacting GC on RSS size growth [!133, #151, Carlos Garnacho] + * GType memleak fixes [!244, Marco Trevisan] + +Version 1.52.4 +-------------- + +- Closed bugs and merge requests: + + * `ARGV` encoding issues [#22, !108, Evan Welsh] + * Segfault on enumeration of GjSFileImporter properties when a searchpath + entry contains a symlink [#154, !144, Ole Jørgen Brønner] + * Possible refcounting bug around GtkListbox signal handlers [#24, !154, + Philip Chimento] + * Fix up GJS_DISABLE_JIT flag now the JIT is enabled by default in + SpiderMonkey [!159, Christopher Wheeldon] + * Expose GObject static property symbols. [!197, Evan Welsh] + * Do not run linters on tagged commits [!181, Claudio André] + * gjs-1.52.0 fails to compile against x86_64 musl systems [#132, !214, Philip + Chimento] + * gjs no longer builds after recent autoconf-archive updates [#149, !217, + Philip Chimento] + +Version 1.52.3 +-------------- + +- Closed bugs and merge requests: + + * Include calc.js example from Seed [!130, William Barath, Philip Chimento] + * CI: Un-pin the Fedora Docker image [#141, !131, Claudio André] + * Reduce overhead of wrapped objects [#142, !121, Carlos Garnacho, Philip + Chimento] + * Various CI changes [!134, !136, Claudio André] + +Version 1.52.2 +-------------- + +- This is an unscheuled release in order to revert a commit that causes a crash + on exit, with some Cairo versions. + +- Closed bugs and merge requests: + + * CI: pinned Fedora to old tag [!119, Claudio André] + * heapgraph.py: adjust terminal output style [!120, Andy Holmes] + * CI: small tweaks [!123, Claudio André] + * Warn about compilation warnings [!125, Claudio André] + * Miscellaneous commits [Philip Chimento, Jason Hicks] + Version 1.52.1 -------------- diff --git a/config.rpath b/config.rpath deleted file mode 100644 index e69de29..0000000 --- a/config.rpath +++ /dev/null diff --git a/configure b/configure index 8410fb2..b79e278 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for gjs 1.52.1. +# Generated by GNU Autoconf 2.69 for gjs 1.52.5. # # Report bugs to <http://bugzilla.gnome.org/enter_bug.cgi?product=gjs>. # @@ -591,8 +591,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='gjs' PACKAGE_TARNAME='gjs' -PACKAGE_VERSION='1.52.1' -PACKAGE_STRING='gjs 1.52.1' +PACKAGE_VERSION='1.52.5' +PACKAGE_STRING='gjs 1.52.5' PACKAGE_BUGREPORT='http://bugzilla.gnome.org/enter_bug.cgi?product=gjs' PACKAGE_URL='https://wiki.gnome.org/Projects/Gjs' @@ -1451,7 +1451,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures gjs 1.52.1 to adapt to many kinds of systems. +\`configure' configures gjs 1.52.5 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1521,7 +1521,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of gjs 1.52.1:";; + short | recursive ) echo "Configuration of gjs 1.52.5:";; esac cat <<\_ACEOF @@ -1700,7 +1700,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -gjs configure 1.52.1 +gjs configure 1.52.5 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2251,7 +2251,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by gjs $as_me 1.52.1, which was +It was created by gjs $as_me 1.52.5, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3114,7 +3114,7 @@ fi # Define the identity of the package. PACKAGE='gjs' - VERSION='1.52.1' + VERSION='1.52.5' cat >>confdefs.h <<_ACEOF @@ -3341,10 +3341,10 @@ ac_config_headers="$ac_config_headers config.h" -GJS_VERSION=15201 +GJS_VERSION=15205 -$as_echo "#define GJS_VERSION (1 * 100 + 52) * 100 + 1" >>confdefs.h +$as_echo "#define GJS_VERSION (1 * 100 + 52) * 100 + 5" >>confdefs.h GETTEXT_PACKAGE=gjs @@ -21806,6 +21806,7 @@ fi if test x$enable_profiler != xno; then : + # Requires timer_settime() - only on Linux @@ -21886,8 +21887,37 @@ done LIBS=$gl_saved_libs - if test x$ac_cv_func_timer_settime = xno; then : + # Requires SIGEV_THREAD_ID - not in some stdlibs + have_sigev_thread_id=no + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux SIGEV_THREAD_ID" >&5 +$as_echo_n "checking for Linux SIGEV_THREAD_ID... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <signal.h> +int +main () +{ +return SIGEV_THREAD_ID; + ; + return 0; +} + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + have_sigev_thread_id=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test x$ac_cv_func_timer_settime = xno -o x$have_sigev_thread_id = xno; then : as_fn_error $? "The profiler is currently only supported on Linux. +The standard library must support timer_settime() and SIGEV_THREAD_ID. Configure with --disable-profiler to skip it on other platforms." "$LINENO" 5 fi @@ -23346,7 +23376,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by gjs $as_me 1.52.1, which was +This file was extended by gjs $as_me 1.52.5, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23417,7 +23447,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -gjs config.status 1.52.1 +gjs config.status 1.52.5 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index c7b7dcb..1c62d93 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ m4_define(pkg_major_version, 1) m4_define(pkg_minor_version, 52) -m4_define(pkg_micro_version, 1) +m4_define(pkg_micro_version, 5) m4_define(pkg_version, pkg_major_version.pkg_minor_version.pkg_micro_version) m4_define(pkg_int_version, (pkg_major_version * 100 + pkg_minor_version) * 100 + pkg_micro_version) @@ -152,9 +152,20 @@ AS_IF([test x$have_gtk = xyes], [ AC_ARG_ENABLE([profiler], [AS_HELP_STRING([--disable-profiler], [Don't build profiler])]) AS_IF([test x$enable_profiler != xno], [ + # Requires timer_settime() - only on Linux gl_TIMER_TIME - AS_IF([test x$ac_cv_func_timer_settime = xno], + # Requires SIGEV_THREAD_ID - not in some stdlibs + have_sigev_thread_id=no + AC_MSG_CHECKING([for Linux SIGEV_THREAD_ID]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[#include <signal.h>]], [return SIGEV_THREAD_ID;]) + ], [ + have_sigev_thread_id=yes + AC_MSG_RESULT([yes]) + ], [AC_MSG_RESULT([no])]) + AS_IF([test x$ac_cv_func_timer_settime = xno -o x$have_sigev_thread_id = xno], [AC_MSG_ERROR([The profiler is currently only supported on Linux. +The standard library must support timer_settime() and SIGEV_THREAD_ID. Configure with --disable-profiler to skip it on other platforms.])]) AC_DEFINE([ENABLE_PROFILER], [1], [Define if the profiler should be built.]) ]) diff --git a/debian/changelog b/debian/changelog index feae994..803f2a2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +gjs (1.52.5-0~ubuntu18.04.1) UNRELEASED; urgency=medium + + * New upstream release (LP: #1803271) + * d/p/context-Add-API-to-force-GC-schedule.patch, + d/p/context-Ensure-force_gc-flag-is-not-lost-if-the-idle-is-s.patch, + d/p/object-Queue-a-forced-GC-when-toggling-down.patch: + - Drop patches included in new release + gjs (1.52.1-1ubuntu1) bionic; urgency=medium * Add fix-crashes-lp1763878-revert-575f1e2e077.patch to fix shutdown diff --git a/debian/control b/debian/control index bcbd2dc..d6cb30a 100644 --- a/debian/control +++ b/debian/control @@ -25,8 +25,10 @@ Build-Depends: debhelper (>= 11), xvfb <!nocheck> Rules-Requires-Root: no Standards-Version: 4.1.3 -Vcs-Git: https://salsa.debian.org/gnome-team/gjs.git -Vcs-Browser: https://salsa.debian.org/gnome-team/gjs +XS-Debian-Vcs-Browser: https://salsa.debian.org/gnome-team/gjs +XS-Debian-Vcs-Git: https://salsa.debian.org/gnome-team/gjs.git -b debian/1.52.x +Vcs-Browser: https://git.launchpad.net/~ubuntu-desktop/ubuntu/+source/gjs +Vcs-Git: https://git.launchpad.net/~ubuntu-desktop/ubuntu/+source/gjs -b ubuntu/bionic Homepage: https://wiki.gnome.org/Projects/Gjs Package: gjs diff --git a/debian/gbp.conf b/debian/gbp.conf index e0196c4..7573460 100644 --- a/debian/gbp.conf +++ b/debian/gbp.conf @@ -1,5 +1,18 @@ [DEFAULT] pristine-tar = True -debian-branch = debian/master -upstream-branch = upstream/latest +debian-branch = debian/bionic +debian-tag = ubuntu/%(version)s +upstream-branch = upstream/1.52.x upstream-vcs-tag = %(version)s + +[buildpackage] +sign-tags = True + +[dch] +multimaint-merge = True + +[import-orig] +postimport = dch -v%(version)s New upstream release; git add debian/changelog; debcommit + +[pq] +patch-numbers = False diff --git a/debian/patches/fix-crashes-lp1763878-revert-575f1e2e077.patch b/debian/patches/fix-crashes-lp1763878-revert-575f1e2e077.patch deleted file mode 100644 index 4da9220..0000000 --- a/debian/patches/fix-crashes-lp1763878-revert-575f1e2e077.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 8510bede1dd1f8a5fb95a2f594b4d3a68289e5ea Mon Sep 17 00:00:00 2001 -From: Philip Chimento <philip.chime...@gmail.com> -Date: Sat, 14 Apr 2018 16:25:58 -0700 -Subject: [PATCH] Revert "engine: Free Cairo static data on shutdown" - -This reverts commit 575f1e2e077af04a112b9e5eaabaf008b086568e. -Per https://bugs.freedesktop.org/show_bug.cgi?id=105466, calling -cairo_debug_reset_static_data() was supposed to be safe in production, -but actually it fails assertions on program exit, with certain Cairo -versions, including 1.14.12. - -Unreviewed. ---- - gjs/engine.cpp | 18 ++---------------- - 1 file changed, 2 insertions(+), 16 deletions(-) - -diff --git a/gjs/engine.cpp b/gjs/engine.cpp -index 90fa57c..67911ee 100644 ---- a/gjs/engine.cpp -+++ b/gjs/engine.cpp -@@ -37,10 +37,6 @@ - #include <windows.h> - #endif - --#ifdef ENABLE_CAIRO --# include <cairo.h> --#endif -- - /* Implementations of locale-specific operations; these are used - * in the implementation of String.localeCompare(), Date.toLocaleDateString(), - * and so forth. We take the straight-forward approach of converting -@@ -218,16 +214,6 @@ on_promise_unhandled_rejection(JSContext *cx, - std::move(stack)); - } - --static void --shutdown(void) --{ -- JS_ShutDown(); -- --#ifdef ENABLE_CAIRO -- cairo_debug_reset_static_data(); /* for valgrind reports */ --#endif --} -- - #ifdef G_OS_WIN32 - HMODULE gjs_dll; - static bool gjs_is_inited = false; -@@ -245,7 +231,7 @@ LPVOID lpvReserved) - break; - - case DLL_THREAD_DETACH: -- shutdown(); -+ JS_ShutDown (); - break; - - default: -@@ -265,7 +251,7 @@ public: - } - - ~GjsInit() { -- shutdown(); -+ JS_ShutDown(); - } - - operator bool() { --- -2.17.0 - diff --git a/debian/patches/fix-leaks-lp1672297-1-context-Add-API-to-force-GC-schedule.patch b/debian/patches/fix-leaks-lp1672297-1-context-Add-API-to-force-GC-schedule.patch deleted file mode 100644 index 84944e6..0000000 --- a/debian/patches/fix-leaks-lp1672297-1-context-Add-API-to-force-GC-schedule.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 33cbbeb11b61a0ffc8ff50e261e5dd33806590f9 Mon Sep 17 00:00:00 2001 -From: Georges Basile Stavracas Neto <georges.stavra...@gmail.com> -Date: Fri, 30 Mar 2018 21:37:37 -0300 -Subject: [PATCH 1/2] context: Add API to force GC schedule - -There are situations where we cannot run the -GC right away, but we also cannot ignore the -need of running it. - -For those cases, add a new private function -that forces GC to happen on idle. ---- - gjs/context-private.h | 2 ++ - gjs/context.cpp | 29 +++++++++++++++++++++++++---- - 2 files changed, 27 insertions(+), 4 deletions(-) - -diff --git a/gjs/context-private.h b/gjs/context-private.h -index 6dbe669..c45c8d0 100644 ---- a/gjs/context-private.h -+++ b/gjs/context-private.h -@@ -36,6 +36,8 @@ bool _gjs_context_destroying (GjsContext *js_context); - - void _gjs_context_schedule_gc_if_needed (GjsContext *js_context); - -+void _gjs_context_schedule_gc (GjsContext *js_context); -+ - void _gjs_context_exit(GjsContext *js_context, - uint8_t exit_code); - -diff --git a/gjs/context.cpp b/gjs/context.cpp -index c509943..77d7eaa 100644 ---- a/gjs/context.cpp -+++ b/gjs/context.cpp -@@ -90,6 +90,7 @@ struct _GjsContext { - uint8_t exit_code; - - guint auto_gc_id; -+ bool force_gc; - - std::array<JS::PersistentRootedId*, GJS_STRING_LAST> const_strings; - -@@ -592,21 +593,41 @@ trigger_gc_if_needed (gpointer user_data) - { - GjsContext *js_context = GJS_CONTEXT(user_data); - js_context->auto_gc_id = 0; -- gjs_gc_if_needed(js_context->context); -+ -+ if (js_context->force_gc) -+ JS_GC(js_context->context); -+ else -+ gjs_gc_if_needed(js_context->context); -+ - return G_SOURCE_REMOVE; - } - --void --_gjs_context_schedule_gc_if_needed (GjsContext *js_context) -+ -+static void -+_gjs_context_schedule_gc_internal (GjsContext *js_context, -+ bool force_gc) - { - if (js_context->auto_gc_id > 0) -- return; -+ g_source_remove(js_context->auto_gc_id); - -+ js_context->force_gc = force_gc; - js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW, - trigger_gc_if_needed, - js_context, NULL); - } - -+void -+_gjs_context_schedule_gc (GjsContext *js_context) -+{ -+ _gjs_context_schedule_gc_internal(js_context, true); -+} -+ -+void -+_gjs_context_schedule_gc_if_needed (GjsContext *js_context) -+{ -+ _gjs_context_schedule_gc_internal(js_context, false); -+} -+ - void - _gjs_context_exit(GjsContext *js_context, - uint8_t exit_code) --- -2.17.0 - diff --git a/debian/patches/fix-leaks-lp1672297-2-object-Queue-a-forced-GC-when-toggling-down.patch b/debian/patches/fix-leaks-lp1672297-2-object-Queue-a-forced-GC-when-toggling-down.patch deleted file mode 100644 index 34f3d3b..0000000 --- a/debian/patches/fix-leaks-lp1672297-2-object-Queue-a-forced-GC-when-toggling-down.patch +++ /dev/null @@ -1,123 +0,0 @@ -From 3f94b11a943c0e4d29c96930ced238580dc18fc7 Mon Sep 17 00:00:00 2001 -From: Georges Basile Stavracas Neto <georges.stavra...@gmail.com> -Date: Wed, 28 Mar 2018 19:21:52 -0300 -Subject: [PATCH 2/2] object: Queue a forced GC when toggling down - -During a GC, the collector asks each object which other -objects that it wants to hold on to so if there's an entire -section of the heap graph that's not connected to anything -else, and not reachable from the root set, then it can be -trashed all at once. - -GObjects, however, don't work like that, there's only a -reference count but no notion of who owns the reference so, -a JS object that's proxying a GObject is unconditionally held -alive as long as the GObject has >1 references. - -Since we cannot know how many more wrapped GObjects are going -be marked for garbage collection after the owner is destroyed, -always queue a garbage collection when a toggle reference goes -down. - -Issue: #140 ---- - gi/object.cpp | 22 ++++++++++++++++++++++ - gjs/context-private.h | 2 +- - gjs/context.cpp | 14 ++++++++------ - 3 files changed, 31 insertions(+), 7 deletions(-) - -diff --git a/gi/object.cpp b/gi/object.cpp -index b20d8b9..f9cf3cc 100644 ---- a/gi/object.cpp -+++ b/gi/object.cpp -@@ -987,8 +987,30 @@ handle_toggle_down(GObject *gobj) - * collected by the GC - */ - if (priv->keep_alive.rooted()) { -+ GjsContext *context; -+ - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object"); - priv->keep_alive.switch_to_unrooted(); -+ -+ /* During a GC, the collector asks each object which other -+ * objects that it wants to hold on to so if there's an entire -+ * section of the heap graph that's not connected to anything -+ * else, and not reachable from the root set, then it can be -+ * trashed all at once. -+ * -+ * GObjects, however, don't work like that, there's only a -+ * reference count but no notion of who owns the reference so, -+ * a JS object that's proxying a GObject is unconditionally held -+ * alive as long as the GObject has >1 references. -+ * -+ * Since we cannot know how many more wrapped GObjects are going -+ * be marked for garbage collection after the owner is destroyed, -+ * always queue a garbage collection when a toggle reference goes -+ * down. -+ */ -+ context = gjs_context_get_current(); -+ if (!_gjs_context_destroying(context)) -+ _gjs_context_schedule_gc(context); - } - } - -diff --git a/gjs/context-private.h b/gjs/context-private.h -index c45c8d0..49c0cf9 100644 ---- a/gjs/context-private.h -+++ b/gjs/context-private.h -@@ -36,7 +36,7 @@ bool _gjs_context_destroying (GjsContext *js_context); - - void _gjs_context_schedule_gc_if_needed (GjsContext *js_context); - --void _gjs_context_schedule_gc (GjsContext *js_context); -+void _gjs_context_schedule_gc(GjsContext *js_context); - - void _gjs_context_exit(GjsContext *js_context, - uint8_t exit_code); -diff --git a/gjs/context.cpp b/gjs/context.cpp -index 77d7eaa..a2ce34a 100644 ---- a/gjs/context.cpp -+++ b/gjs/context.cpp -@@ -599,31 +599,33 @@ trigger_gc_if_needed (gpointer user_data) - else - gjs_gc_if_needed(js_context->context); - -+ js_context->force_gc = false; -+ - return G_SOURCE_REMOVE; - } - - - static void --_gjs_context_schedule_gc_internal (GjsContext *js_context, -- bool force_gc) -+_gjs_context_schedule_gc_internal(GjsContext *js_context, -+ bool force_gc) - { - if (js_context->auto_gc_id > 0) -- g_source_remove(js_context->auto_gc_id); -+ return; - -- js_context->force_gc = force_gc; -+ js_context->force_gc |= force_gc; - js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW, - trigger_gc_if_needed, - js_context, NULL); - } - - void --_gjs_context_schedule_gc (GjsContext *js_context) -+_gjs_context_schedule_gc(GjsContext *js_context) - { - _gjs_context_schedule_gc_internal(js_context, true); - } - - void --_gjs_context_schedule_gc_if_needed (GjsContext *js_context) -+_gjs_context_schedule_gc_if_needed(GjsContext *js_context) - { - _gjs_context_schedule_gc_internal(js_context, false); - } --- -2.17.0 - diff --git a/debian/patches/series b/debian/patches/series deleted file mode 100644 index 1de36f6..0000000 --- a/debian/patches/series +++ /dev/null @@ -1,6 +0,0 @@ -# Cherry picked from gjs master: -fix-crashes-lp1763878-revert-575f1e2e077.patch - -# Critical leak fixes (not yet landed in gjs master): -fix-leaks-lp1672297-1-context-Add-API-to-force-GC-schedule.patch -fix-leaks-lp1672297-2-object-Queue-a-forced-GC-when-toggling-down.patch diff --git a/gi/gtype.cpp b/gi/gtype.cpp index 314f4b2..03920eb 100644 --- a/gi/gtype.cpp +++ b/gi/gtype.cpp @@ -63,13 +63,18 @@ update_gtype_weak_pointers(JSContext *cx, void *data) { for (auto iter = weak_pointer_list.begin(); iter != weak_pointer_list.end(); ) { - auto heap_wrapper = static_cast<JS::Heap<JSObject *> *>(g_type_get_qdata(*iter, gjs_get_gtype_wrapper_quark())); + GType gtype = *iter; + auto heap_wrapper = static_cast<JS::Heap<JSObject *> *>( + g_type_get_qdata(gtype, gjs_get_gtype_wrapper_quark())); JS_UpdateWeakPointerAfterGC(heap_wrapper); /* No read barriers are needed if the only thing we are doing with the * pointer is comparing it to nullptr. */ - if (heap_wrapper->unbarrieredGet() == nullptr) + if (heap_wrapper->unbarrieredGet() == nullptr) { + g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), nullptr); iter = weak_pointer_list.erase(iter); + delete heap_wrapper; + } else iter++; } @@ -95,8 +100,12 @@ gjs_gtype_finalize(JSFreeOp *fop, if (G_UNLIKELY(gtype == 0)) return; - weak_pointer_list.erase(gtype); + auto heap_wrapper = static_cast<JS::Heap<JSObject*>*>( + g_type_get_qdata(gtype, gjs_get_gtype_wrapper_quark())); + g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), NULL); + weak_pointer_list.erase(gtype); + delete heap_wrapper; } static bool diff --git a/gi/object.cpp b/gi/object.cpp index b20d8b9..fe381ec 100644 --- a/gi/object.cpp +++ b/gi/object.cpp @@ -54,6 +54,65 @@ #include <util/log.h> #include <girepository.h> +typedef class GjsListLink GjsListLink; +typedef struct ObjectInstance ObjectInstance; + +static GjsListLink* object_instance_get_link(ObjectInstance *priv); + +class GjsListLink { + private: + ObjectInstance *m_prev; + ObjectInstance *m_next; + + public: + ObjectInstance* prev() { + return m_prev; + } + + ObjectInstance* next() { + return m_next; + } + + void prepend(ObjectInstance *this_instance, + ObjectInstance *head) { + GjsListLink *elem = object_instance_get_link(head); + + g_assert(object_instance_get_link(this_instance) == this); + + if (elem->m_prev) { + GjsListLink *prev = object_instance_get_link(elem->m_prev); + prev->m_next = this_instance; + this->m_prev = elem->m_prev; + } + + elem->m_prev = this_instance; + this->m_next = head; + } + + void unlink() { + if (m_prev) + object_instance_get_link(m_prev)->m_next = m_next; + if (m_next) + object_instance_get_link(m_next)->m_prev = m_prev; + + m_prev = m_next = NULL; + } + + int size() { + GjsListLink *elem = this; + int count = 0; + + do { + count++; + if (!elem->m_next) + break; + elem = object_instance_get_link(elem->m_next); + } while (elem); + + return count; + } +}; + struct ObjectInstance { GIObjectInfo *info; GObject *gobj; /* NULL if we are the prototype and not an instance */ @@ -68,8 +127,15 @@ struct ObjectInstance { prototypes) */ GTypeClass *klass; + GjsListLink instance_link; + unsigned js_object_finalized : 1; unsigned g_object_finalized : 1; + + /* True if this object has visible JS state, and thus its lifecycle is + * managed using toggle references. False if this object just keeps a + * hard ref on the underlying GObject, and may be finalized at will. */ + bool uses_toggle_ref : 1; }; static std::stack<JS::PersistentRootedObject> object_init_list; @@ -78,13 +144,15 @@ using ParamRef = std::unique_ptr<GParamSpec, decltype(&g_param_spec_unref)>; using ParamRefArray = std::vector<ParamRef>; static std::unordered_map<GType, ParamRefArray> class_init_properties; +static bool context_weak_pointer_callback = false; static bool weak_pointer_callback = false; -static std::set<ObjectInstance *> wrapped_gobject_list; +ObjectInstance *wrapped_gobject_list; extern struct JSClass gjs_object_instance_class; GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class) static void disassociate_js_gobject (GObject *gobj); +static void ensure_uses_toggle_ref(JSContext *cx, ObjectInstance *priv); typedef enum { SOME_ERROR_OCCURRED = false, @@ -152,7 +220,7 @@ get_object_qdata(GObject *gobj) auto priv = static_cast<ObjectInstance *>(g_object_get_qdata(gobj, gjs_object_priv_quark())); - if (priv && G_UNLIKELY(priv->js_object_finalized)) { + if (priv && priv->uses_toggle_ref && G_UNLIKELY(priv->js_object_finalized)) { g_critical("Object %p (a %s) resurfaced after the JS wrapper was finalized. " "This is some library doing dubious memory management inside dispose()", gobj, g_type_name(G_TYPE_FROM_INSTANCE(gobj))); @@ -430,6 +498,9 @@ set_g_param_from_prop(JSContext *context, case SOME_ERROR_OCCURRED: return false; case NO_SUCH_G_PROPERTY: + /* We need to keep the wrapper alive in order not to lose custom + * "expando" properties */ + ensure_uses_toggle_ref(context, priv); return result.succeed(); case VALUE_WAS_SET: default: @@ -496,14 +567,7 @@ object_instance_set_prop(JSContext *context, bool ret = true; bool g_param_was_set = false; - if (!gjs_get_string_id(context, id, &name)) - return result.succeed(); /* not resolved, but no error */ - priv = priv_from_js(context, obj); - gjs_debug_jsprop(GJS_DEBUG_GOBJECT, - "Set prop '%s' hook obj %p priv %p", - name.get(), obj.get(), priv); - if (priv == nullptr) /* see the comment in object_instance_get_prop() on this */ return result.succeed(); @@ -521,6 +585,18 @@ object_instance_set_prop(JSContext *context, return result.succeed(); } + if (!gjs_get_string_id(context, id, &name)) { + /* We need to keep the wrapper alive in order not to lose custom + * "expando" properties. In this case if gjs_get_string_id() is false + * then a number or symbol property was probably set. */ + ensure_uses_toggle_ref(context, priv); + return result.succeed(); /* not resolved, but no error */ + } + + gjs_debug_jsprop(GJS_DEBUG_GOBJECT, + "Set prop '%s' hook obj %p priv %p", + name.get(), obj.get(), priv); + ret = set_g_param_from_prop(context, priv, name, g_param_was_set, value_p, result); if (g_param_was_set || !ret) return ret; @@ -755,18 +831,6 @@ object_instance_resolve(JSContext *context, return true; } - if (priv->g_object_finalized) { - g_critical("Object %s.%s (%p), has been already finalized. " - "Impossible to resolve it.", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - gjs_dumpstack(); - - *resolved = false; - return true; - } - /* If we have no GIRepository information (we're a JS GObject subclass), * we need to look at exposing interfaces. Look up our interfaces through * GType data, and then hope that *those* are introspectable. */ @@ -945,6 +1009,28 @@ object_instance_props_to_g_parameters(JSContext *context, return true; } +static GjsListLink * +object_instance_get_link(ObjectInstance *priv) +{ + return &priv->instance_link; +} + +static void +object_instance_unlink(ObjectInstance *priv) +{ + if (wrapped_gobject_list == priv) + wrapped_gobject_list = priv->instance_link.next(); + priv->instance_link.unlink(); +} + +static void +object_instance_link(ObjectInstance *priv) +{ + if (wrapped_gobject_list) + priv->instance_link.prepend(priv, wrapped_gobject_list); + wrapped_gobject_list = priv; +} + static void wrapped_gobj_dispose_notify(gpointer data, GObject *where_the_object_was) @@ -952,26 +1038,30 @@ wrapped_gobj_dispose_notify(gpointer data, auto *priv = static_cast<ObjectInstance *>(data); priv->g_object_finalized = true; - wrapped_gobject_list.erase(priv); + object_instance_unlink(priv); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Wrapped GObject %p disposed", where_the_object_was); } -static void -gobj_no_longer_kept_alive_func(JS::HandleObject obj, - void *data) +void +gjs_object_context_dispose_notify(void *data, + GObject *where_the_object_was) { - ObjectInstance *priv; + ObjectInstance *priv = wrapped_gobject_list; + while (priv) { + ObjectInstance *next = priv->instance_link.next(); - priv = (ObjectInstance *) data; - - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "GObject wrapper %p for GObject " - "%p (%s) was rooted but is now unrooted due to " - "GjsContext dispose", obj.get(), priv->gobj, - G_OBJECT_TYPE_NAME(priv->gobj)); + if (priv->keep_alive.rooted()) { + gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "GObject wrapper %p for GObject " + "%p (%s) was rooted but is now unrooted due to " + "GjsContext dispose", priv->keep_alive.get(), + priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); + priv->keep_alive.reset(); + object_instance_unlink(priv); + } - priv->keep_alive.reset(); - wrapped_gobject_list.erase(priv); + priv = next; + } } static void @@ -987,8 +1077,30 @@ handle_toggle_down(GObject *gobj) * collected by the GC */ if (priv->keep_alive.rooted()) { + GjsContext *context; + gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object"); priv->keep_alive.switch_to_unrooted(); + + /* During a GC, the collector asks each object which other + * objects that it wants to hold on to so if there's an entire + * section of the heap graph that's not connected to anything + * else, and not reachable from the root set, then it can be + * trashed all at once. + * + * GObjects, however, don't work like that, there's only a + * reference count but no notion of who owns the reference so, + * a JS object that's proxying a GObject is unconditionally held + * alive as long as the GObject has >1 references. + * + * Since we cannot know how many more wrapped GObjects are going + * be marked for garbage collection after the owner is destroyed, + * always queue a garbage collection when a toggle reference goes + * down. + */ + context = gjs_context_get_current(); + if (!_gjs_context_destroying(context)) + _gjs_context_schedule_gc(context); } } @@ -1017,7 +1129,7 @@ handle_toggle_up(GObject *gobj) GjsContext *context = gjs_context_get_current(); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Rooting object"); auto cx = static_cast<JSContext *>(gjs_context_get_native_context(context)); - priv->keep_alive.switch_to_rooted(cx, gobj_no_longer_kept_alive_func, priv); + priv->keep_alive.switch_to_rooted(cx); } } @@ -1126,7 +1238,10 @@ static void release_native_object (ObjectInstance *priv) { priv->keep_alive.reset(); - g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, NULL); + if (priv->uses_toggle_ref) + g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, nullptr); + else + g_object_unref(priv->gobj); priv->gobj = NULL; } @@ -1156,14 +1271,15 @@ gjs_object_prepare_shutdown(void) * toggle ref removal -> gobj dispose -> toggle ref notify * by emptying the toggle queue earlier in the shutdown sequence. */ std::vector<ObjectInstance *> to_be_released; - for (auto iter = wrapped_gobject_list.begin(); iter != wrapped_gobject_list.end(); ) { - ObjectInstance *priv = *iter; - if (priv->keep_alive.rooted()) { - to_be_released.push_back(priv); - iter = wrapped_gobject_list.erase(iter); - } else { - iter++; + ObjectInstance *link = wrapped_gobject_list; + while (link) { + ObjectInstance *next = link->instance_link.next(); + if (link->keep_alive.rooted()) { + to_be_released.push_back(link); + object_instance_unlink(link); } + + link = next; } for (ObjectInstance *priv : to_be_released) release_native_object(priv); @@ -1209,16 +1325,18 @@ update_heap_wrapper_weak_pointers(JSContext *cx, { gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Weak pointer update callback, " "%zu wrapped GObject(s) to examine", - wrapped_gobject_list.size()); + wrapped_gobject_list ? + wrapped_gobject_list->instance_link.size() : 0); std::vector<GObject *> to_be_disassociated; + ObjectInstance *priv = wrapped_gobject_list; - for (auto iter = wrapped_gobject_list.begin(); iter != wrapped_gobject_list.end(); ) { - ObjectInstance *priv = *iter; - if (priv->keep_alive.rooted() || priv->keep_alive == nullptr || - !priv->keep_alive.update_after_gc()) { - iter++; - } else { + while (priv) { + ObjectInstance *next = priv->instance_link.next(); + + if (!priv->keep_alive.rooted() && + priv->keep_alive != nullptr && + priv->keep_alive.update_after_gc()) { /* Ouch, the JS object is dead already. Disassociate the * GObject and hope the GObject dies too. (Remove it from * the weak pointer list first, since the disassociation @@ -1229,8 +1347,10 @@ update_heap_wrapper_weak_pointers(JSContext *cx, "%p (%s)", priv->keep_alive.get(), priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); to_be_disassociated.push_back(priv->gobj); - iter = wrapped_gobject_list.erase(iter); + object_instance_unlink(priv); } + + priv = next; } for (GObject *gobj : to_be_disassociated) @@ -1256,16 +1376,28 @@ associate_js_gobject (JSContext *context, ObjectInstance *priv; priv = priv_from_js(context, object); + priv->uses_toggle_ref = false; priv->gobj = gobj; g_assert(!priv->keep_alive.rooted()); set_object_qdata(gobj, priv); + priv->keep_alive = object; ensure_weak_pointer_callback(context); - wrapped_gobject_list.insert(priv); + object_instance_link(priv); g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, priv); +} + +static void +ensure_uses_toggle_ref(JSContext *cx, + ObjectInstance *priv) +{ + if (priv->uses_toggle_ref) + return; + + g_assert(!priv->keep_alive.rooted()); /* OK, here is where things get complicated. We want the * wrapped gobj to keep the JSObject* wrapper alive, because @@ -1278,8 +1410,14 @@ associate_js_gobject (JSContext *context, * the wrapper to be garbage collected (and thus unref the * wrappee). */ - priv->keep_alive.root(context, object, gobj_no_longer_kept_alive_func, priv); - g_object_add_toggle_ref(gobj, wrapped_gobj_toggle_notify, NULL); + priv->uses_toggle_ref = true; + priv->keep_alive.switch_to_rooted(cx); + g_object_add_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, nullptr); + + /* We now have both a ref and a toggle ref, we only want the toggle ref. + * This may immediately remove the GC root we just added, since refcount + * may drop to 1. */ + g_object_unref(priv->gobj); } static void @@ -1303,7 +1441,8 @@ disassociate_js_gobject(GObject *gobj) ObjectInstance *priv = get_object_qdata(gobj); bool had_toggle_down, had_toggle_up; - g_object_weak_unref(priv->gobj, wrapped_gobj_dispose_notify, priv); + if (!priv->g_object_finalized) + g_object_weak_unref(gobj, wrapped_gobj_dispose_notify, priv); /* FIXME: this check fails when JS code runs after the main loop ends, * because the idle functions are not dispatched without a main loop. @@ -1322,11 +1461,16 @@ disassociate_js_gobject(GObject *gobj) gobj, G_OBJECT_TYPE_NAME(gobj)); } + /* Fist, remove the wrapper pointer from the wrapped GObject */ + set_object_qdata(gobj, nullptr); + + /* Now release all the resources the current wrapper has */ invalidate_all_closures(priv); release_native_object(priv); /* Mark that a JS object once existed, but it doesn't any more */ priv->js_object_finalized = true; + priv->keep_alive = nullptr; } static void @@ -1383,6 +1527,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS * we're not actually using it, so just let it get collected. Avoiding * this would require a non-trivial amount of work. * */ + ensure_uses_toggle_ref(context, other_priv); object.set(other_priv->keep_alive); g_object_unref(gobj); /* We already own a reference */ gobj = NULL; @@ -1410,11 +1555,6 @@ G_GNUC_END_IGNORE_DEPRECATIONS if (priv->gobj == NULL) associate_js_gobject(context, object, gobj); - /* We now have both a ref and a toggle ref, we only want the - * toggle ref. This may immediately remove the GC root - * we just added, since refcount may drop to 1. - */ - g_object_unref(gobj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "JSObject created with GObject %p (%s)", priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); @@ -1463,15 +1603,6 @@ object_instance_trace(JSTracer *tracer, if (priv == NULL) return; - if (priv->g_object_finalized) { - g_debug("Object %s.%s (%p), has been already finalized. " - "Impossible to trace it.", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - return; - } - for (GClosure *closure : priv->closures) gjs_closure_trace(closure, tracer); } @@ -1541,7 +1672,7 @@ object_instance_finalize(JSFreeOp *fop, priv->keep_alive.reset(); } - wrapped_gobject_list.erase(priv); + object_instance_unlink(priv); if (priv->info) { g_base_info_unref( (GIBaseInfo*) priv->info); @@ -1556,6 +1687,9 @@ object_instance_finalize(JSFreeOp *fop, GJS_DEC_COUNTER(object); priv->~ObjectInstance(); g_slice_free(ObjectInstance, priv); + + /* Remove the ObjectInstance pointer from the JSObject */ + JS_SetPrivate(obj, nullptr); } static JSObject * @@ -1683,6 +1817,8 @@ real_connect_func(JSContext *context, return true; } + ensure_uses_toggle_ref(context, priv); + if (argc != 2 || !argv[0].isString() || !JS::IsCallable(&argv[1].toObject())) { gjs_throw(context, "connect() takes two args, the signal name and the callback"); return false; @@ -2123,9 +2259,6 @@ gjs_object_from_g_object(JSContext *context, g_object_ref_sink(gobj); associate_js_gobject(context, obj, gobj); - /* see the comment in init_object_instance() for this */ - g_object_unref(gobj); - g_assert(priv->keep_alive == obj.get()); } @@ -2142,6 +2275,19 @@ gjs_g_object_from_object(JSContext *context, return NULL; priv = priv_from_js(context, obj); + + if (priv->g_object_finalized) { + g_critical("Object %s.%s (%p), has been already deallocated - " + "impossible to access it. This might be caused by the " + "object having been destroyed from C code using something " + "such as destroy(), dispose(), or remove() vfuncs", + priv->info ? g_base_info_get_namespace(priv->info) : "", + priv->info ? g_base_info_get_name(priv->info) : g_type_name(priv->gtype), + priv->gobj); + gjs_dumpstack(); + return nullptr; + } + return priv->gobj; } @@ -2188,19 +2334,7 @@ gjs_typecheck_object(JSContext *context, return false; } - if (priv->g_object_finalized) { - g_critical("Object %s.%s (%p), has been already deallocated - impossible to access to it. " - "This might be caused by the fact that the object has been destroyed from C " - "code using something such as destroy(), dispose(), or remove() vfuncs", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - gjs_dumpstack(); - - return true; - } - - g_assert(priv->gtype == G_OBJECT_TYPE(priv->gobj)); + g_assert(priv->g_object_finalized || priv->gtype == G_OBJECT_TYPE(priv->gobj)); if (expected_type != G_TYPE_NONE) result = g_type_is_a (priv->gtype, expected_type); @@ -2657,6 +2791,10 @@ gjs_object_custom_init(GTypeInstance *instance, associate_js_gobject(context, object, G_OBJECT (instance)); + /* Custom JS objects will most likely have visible state, so + * just do this from the start */ + ensure_uses_toggle_ref(context, priv); + JS::RootedValue v(context); if (!gjs_object_get_property(context, object, GJS_STRING_INSTANCE_INIT, &v)) { @@ -3109,6 +3247,9 @@ gjs_object_associate_closure(JSContext *cx, if (!priv) return false; + if (priv->gobj) + ensure_uses_toggle_ref(cx, priv); + do_associate_closure(priv, closure); return true; } diff --git a/gi/object.h b/gi/object.h index 63aeb37..1f1dce8 100644 --- a/gi/object.h +++ b/gi/object.h @@ -61,6 +61,8 @@ bool gjs_typecheck_is_object(JSContext *context, void gjs_object_prepare_shutdown(void); void gjs_object_clear_toggles(void); void gjs_object_shutdown_toggle_queue(void); +void gjs_object_context_dispose_notify(void *data, + GObject *where_the_object_was); void gjs_object_define_static_methods(JSContext *context, JS::HandleObject constructor, diff --git a/gjs/context-private.h b/gjs/context-private.h index 6dbe669..49c0cf9 100644 --- a/gjs/context-private.h +++ b/gjs/context-private.h @@ -36,6 +36,8 @@ bool _gjs_context_destroying (GjsContext *js_context); void _gjs_context_schedule_gc_if_needed (GjsContext *js_context); +void _gjs_context_schedule_gc(GjsContext *js_context); + void _gjs_context_exit(GjsContext *js_context, uint8_t exit_code); diff --git a/gjs/context.cpp b/gjs/context.cpp index c509943..fc88741 100644 --- a/gjs/context.cpp +++ b/gjs/context.cpp @@ -90,6 +90,7 @@ struct _GjsContext { uint8_t exit_code; guint auto_gc_id; + bool force_gc; std::array<JS::PersistentRootedId*, GJS_STRING_LAST> const_strings; @@ -515,6 +516,8 @@ gjs_context_constructed(GObject *object) g_mutex_unlock (&contexts_lock); setup_dump_heap(); + + g_object_weak_ref(object, gjs_object_context_dispose_notify, nullptr); } static void @@ -592,19 +595,43 @@ trigger_gc_if_needed (gpointer user_data) { GjsContext *js_context = GJS_CONTEXT(user_data); js_context->auto_gc_id = 0; - gjs_gc_if_needed(js_context->context); + + if (js_context->force_gc) + JS_GC(js_context->context); + else + gjs_gc_if_needed(js_context->context); + + js_context->force_gc = false; + return G_SOURCE_REMOVE; } -void -_gjs_context_schedule_gc_if_needed (GjsContext *js_context) + +static void +_gjs_context_schedule_gc_internal(GjsContext *js_context, + bool force_gc) { + js_context->force_gc |= force_gc; + if (js_context->auto_gc_id > 0) return; - js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW, - trigger_gc_if_needed, - js_context, NULL); + js_context->force_gc |= force_gc; + js_context->auto_gc_id = g_timeout_add_seconds_full(G_PRIORITY_LOW, 10, + trigger_gc_if_needed, + js_context, NULL); +} + +void +_gjs_context_schedule_gc(GjsContext *js_context) +{ + _gjs_context_schedule_gc_internal(js_context, true); +} + +void +_gjs_context_schedule_gc_if_needed(GjsContext *js_context) +{ + _gjs_context_schedule_gc_internal(js_context, false); } void diff --git a/gjs/engine.cpp b/gjs/engine.cpp index 90fa57c..720267d 100644 --- a/gjs/engine.cpp +++ b/gjs/engine.cpp @@ -37,10 +37,6 @@ #include <windows.h> #endif -#ifdef ENABLE_CAIRO -# include <cairo.h> -#endif - /* Implementations of locale-specific operations; these are used * in the implementation of String.localeCompare(), Date.toLocaleDateString(), * and so forth. We take the straight-forward approach of converting @@ -218,16 +214,6 @@ on_promise_unhandled_rejection(JSContext *cx, std::move(stack)); } -static void -shutdown(void) -{ - JS_ShutDown(); - -#ifdef ENABLE_CAIRO - cairo_debug_reset_static_data(); /* for valgrind reports */ -#endif -} - #ifdef G_OS_WIN32 HMODULE gjs_dll; static bool gjs_is_inited = false; @@ -245,7 +231,7 @@ LPVOID lpvReserved) break; case DLL_THREAD_DETACH: - shutdown(); + JS_ShutDown (); break; default: @@ -265,7 +251,7 @@ public: } ~GjsInit() { - shutdown(); + JS_ShutDown(); } operator bool() { @@ -322,13 +308,14 @@ gjs_create_js_context(GjsContext *js_context) JS::ContextOptionsRef(cx).setExtraWarnings(true); } - if (!g_getenv("GJS_DISABLE_JIT")) { + bool enable_jit = !(g_getenv("GJS_DISABLE_JIT")); + if (enable_jit) { gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT"); - JS::ContextOptionsRef(cx) - .setIon(true) - .setBaseline(true) - .setAsmJS(true); } + JS::ContextOptionsRef(cx) + .setIon(enable_jit) + .setBaseline(enable_jit) + .setAsmJS(enable_jit); return cx; } diff --git a/gjs/importer.cpp b/gjs/importer.cpp index b4ea3c8..4c42a84 100644 --- a/gjs/importer.cpp +++ b/gjs/importer.cpp @@ -706,7 +706,7 @@ importer_enumerate(JSContext *context, /* new_for_commandline_arg handles resource:/// paths */ GjsAutoUnref<GFile> dir = g_file_new_for_commandline_arg(dirname); GjsAutoUnref<GFileEnumerator> direnum = - g_file_enumerate_children(dir, G_FILE_ATTRIBUTE_STANDARD_TYPE, + g_file_enumerate_children(dir, "standard::name,standard::type", G_FILE_QUERY_INFO_NONE, NULL, NULL); while (true) { diff --git a/gjs/jsapi-util-root.h b/gjs/jsapi-util-root.h index 5baed48..d64eccb 100644 --- a/gjs/jsapi-util-root.h +++ b/gjs/jsapi-util-root.h @@ -219,6 +219,7 @@ public: return m_root->get() == nullptr; return m_heap.unbarrieredGet() == nullptr; } + inline 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 @@ -247,10 +248,12 @@ public: m_data = data; m_root = new JS::PersistentRooted<T>(m_cx, thing); - auto gjs_cx = static_cast<GjsContext *>(JS_GetContextPrivate(m_cx)); - g_assert(GJS_IS_CONTEXT(gjs_cx)); - g_object_weak_ref(G_OBJECT(gjs_cx), on_context_destroy, this); - m_has_weakref = true; + if (notify) { + auto gjs_cx = static_cast<GjsContext *>(JS_GetContextPrivate(m_cx)); + g_assert(GJS_IS_CONTEXT(gjs_cx)); + g_object_weak_ref(G_OBJECT(gjs_cx), on_context_destroy, this); + m_has_weakref = true; + } } /* You can only assign directly to the GjsMaybeOwned wrapper in the diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp index 322a41b..ed3e649 100644 --- a/gjs/jsapi-util.cpp +++ b/gjs/jsapi-util.cpp @@ -26,6 +26,8 @@ #include <codecvt> #include <locale> +#include "jsapi-wrapper.h" +#include <js/GCAPI.h> #include <util/log.h> #include <util/glib.h> @@ -34,7 +36,6 @@ #include "jsapi-class.h" #include "jsapi-util.h" -#include "jsapi-wrapper.h" #include "context-private.h" #include <gi/boxed.h> @@ -313,8 +314,9 @@ gjs_build_string_array(JSContext *context, g_error("Unable to reserve memory for vector"); for (i = 0; i < array_length; ++i) { + JS::ConstUTF8CharsZ chars(array_values[i], strlen(array_values[i])); JS::RootedValue element(context, - JS::StringValue(JS_NewStringCopyZ(context, array_values[i]))); + JS::StringValue(JS_NewStringCopyUTF8Z(context, chars))); if (!elems.append(element)) g_error("Unable to append to vector"); } @@ -731,7 +733,7 @@ gjs_gc_if_needed (JSContext *context) */ if (rss_size > linux_rss_trigger) { linux_rss_trigger = (gulong) MIN(G_MAXULONG, rss_size * 1.25); - JS_GC(context); + JS::GCForReason(context, GC_SHRINK, JS::gcreason::Reason::API); } else if (rss_size < (0.75 * linux_rss_trigger)) { /* If we've shrunk by 75%, lower the trigger */ linux_rss_trigger = (rss_size * 1.25); diff --git a/installed-tests/extra/gjs.supp b/installed-tests/extra/gjs.supp index a768e27..b99eb25 100644 --- a/installed-tests/extra/gjs.supp +++ b/installed-tests/extra/gjs.supp @@ -1,35 +1,6 @@ # Valgrind suppressions file for GJS # This is intended to be used in addition to GLib's glib.supp file. -# We leak a small wrapper in GJS for each registered GType. - -{ - gtype-wrapper-new - Memcheck:Leak - match-leak-kinds: definite - fun:_Znwm - fun:gjs_gtype_create_gtype_wrapper -} - -{ - gtype-wrapper-qdata - Memcheck:Leak - match-leak-kinds: possible - ... - fun:type_set_qdata_W - fun:g_type_set_qdata - fun:gjs_gtype_create_gtype_wrapper -} - -{ - g_type_register_fundamental never freed - Memcheck:Leak - fun:calloc - ... - fun:g_type_register_fundamental - ... -} - # SpiderMonkey leaks { @@ -143,6 +114,29 @@ fun:cairo_show_text } +# Data that Cairo keeps around for the process lifetime +# This could be freed by calling cairo_debug_reset_static_data(), but it's +# not a good idea to call that function in production, because certain versions +# of Cairo have bugs that cause it to fail assertions and crash. +{ + cairo-static-data + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + ... + fun:FcPatternDuplicate + fun:_cairo_ft_font_face_create_for_pattern + fun:_cairo_ft_font_face_create_for_toy + fun:_cairo_toy_font_face_create_impl_face + fun:_cairo_toy_font_face_init + fun:cairo_toy_font_face_create + fun:_cairo_gstate_ensure_font_face + fun:_cairo_gstate_ensure_scaled_font + fun:_cairo_gstate_get_scaled_font + fun:_cairo_default_context_get_scaled_font + fun:cairo_show_text +} + # SpiderMonkey data races # These are in SpiderMonkey's atomics / thread barrier stuff so presumably diff --git a/installed-tests/extra/lsan.supp b/installed-tests/extra/lsan.supp index 179eb9c..3c69851 100644 --- a/installed-tests/extra/lsan.supp +++ b/installed-tests/extra/lsan.supp @@ -1,8 +1,5 @@ # SpiderMonkey leaks a mutex for each GC helper thread. leak:js::HelperThread::threadLoop -# We leak a small wrapper in GJS for each registered GType. -leak:gjs_gtype_create_gtype_wrapper - # https://bugs.freedesktop.org/show_bug.cgi?id=105466 leak:libfontconfig.so.1 diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js index 741a9f3..bf1ae97 100644 --- a/modules/overrides/GObject.js +++ b/modules/overrides/GObject.js @@ -441,6 +441,14 @@ function _init() { GObject._children = _children; GObject._internalChildren = _internalChildren; + // Expose GObject static properties for ES6 classes + + GObject.GTypeName = GTypeName; + GObject.requires = requires; + GObject.interfaces = interfaces; + GObject.properties = properties; + GObject.signals = signals; + // fake enum for signal accumulators, keep in sync with gi/object.c this.AccumulatorType = { NONE: 0, diff --git a/modules/tweener/tweener.js b/modules/tweener/tweener.js index 98bde02..61c6654 100644 --- a/modules/tweener/tweener.js +++ b/modules/tweener/tweener.js @@ -368,7 +368,7 @@ function _onEnterFrame() { return true; } -const restrictedWords = { +var restrictedWords = { time: true, delay: true, userFrames: true, diff --git a/win32/config.h.win32 b/win32/config.h.win32 index 7495f0a..1e78fa6 100644 --- a/win32/config.h.win32 +++ b/win32/config.h.win32 @@ -13,7 +13,7 @@ #define GETTEXT_PACKAGE "gjs" /* The gjs version as an integer */ -#define GJS_VERSION 15201 +#define GJS_VERSION 15205 /* define if the compiler supports basic C++11 syntax */ #define HAVE_CXX11 1 @@ -74,7 +74,7 @@ #define PACKAGE_NAME "gjs" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "gjs 1.52.1" +#define PACKAGE_STRING "gjs 1.52.5" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "gjs" @@ -83,10 +83,10 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.52.1" +#define PACKAGE_VERSION "1.52.5" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Version number of package */ -#define VERSION "1.52.1" +#define VERSION "1.52.5"
-- ubuntu-desktop mailing list ubuntu-desktop@lists.ubuntu.com https://lists.ubuntu.com/mailman/listinfo/ubuntu-desktop