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

Reply via email to