Merge ~3v1n0/ubuntu/+source/gjs:ubuntu/bionic into ~ubuntu-desktop/ubuntu/+source/gjs:ubuntu/bionic

Proposed by Marco Trevisan (Treviño) on 2018-12-19
Status: Merged
Approved by: Jeremy Bicha on 2018-12-20
Approved revision: b7075ca403da21571a7caad617fb22f584ea0b29
Merged at revision: b7075ca403da21571a7caad617fb22f584ea0b29
Proposed branch: ~3v1n0/ubuntu/+source/gjs:ubuntu/bionic
Merge into: ~ubuntu-desktop/ubuntu/+source/gjs:ubuntu/bionic
Diff against target: 1653 lines (+481/-189)
22 files modified
Makefile.in (+2/-3)
NEWS (+73/-0)
configure (+43/-13)
configure.ac (+13/-2)
debian/changelog (+10/-0)
debian/control (+4/-2)
debian/gbp.conf (+3/-2)
dev/null (+0/-6)
gi/gtype.cpp (+12/-3)
gi/object.cpp (+227/-86)
gi/object.h (+2/-0)
gjs/context-private.h (+2/-0)
gjs/context.cpp (+33/-6)
gjs/engine.cpp (+8/-21)
gjs/importer.cpp (+1/-1)
gjs/jsapi-util-root.h (+7/-4)
gjs/jsapi-util.cpp (+5/-3)
installed-tests/extra/gjs.supp (+23/-29)
installed-tests/extra/lsan.supp (+0/-3)
modules/overrides/GObject.js (+8/-0)
modules/tweener/tweener.js (+1/-1)
win32/config.h.win32 (+4/-4)
Reviewer Review Type Date Requested Status
Ubuntu Desktop 2018-12-19 Pending
Review via email: mp+361167@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/Makefile.in b/Makefile.in
2index fea2a6b..ed5566b 100644
3--- a/Makefile.in
4+++ b/Makefile.in
5@@ -756,9 +756,8 @@ am__DIST_COMMON = $(srcdir)/Makefile-examples.am \
6 $(srcdir)/config.h.in $(srcdir)/gjs-1.0.pc.in \
7 $(srcdir)/gjs-modules-srcs.mk $(srcdir)/gjs-srcs.mk \
8 $(top_srcdir)/win32/config.h.win32.in AUTHORS COPYING \
9- ChangeLog INSTALL NEWS README compile config.guess \
10- config.rpath config.sub depcomp install-sh ltmain.sh missing \
11- tap-driver.sh
12+ ChangeLog INSTALL NEWS README compile config.guess config.sub \
13+ depcomp install-sh ltmain.sh missing tap-driver.sh
14 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
15 distdir = $(PACKAGE)-$(VERSION)
16 top_distdir = $(distdir)
17diff --git a/NEWS b/NEWS
18index 43feb1f..1e33aa5 100644
19--- a/NEWS
20+++ b/NEWS
21@@ -1,3 +1,76 @@
22+Version 1.52.5
23+--------------
24+
25+- This release includes the "Big Hammer" patch from GNOME 3.30 to reduce memory
26+ usage. For more information, read the blog post at
27+ https://feaneron.com/2018/04/20/the-infamous-gnome-shell-memory-leak/
28+ It was not originally intended to be backported to GNOME 3.28, but in practice
29+ several Linux distributions already backported it, and it has been working
30+ well to reduce memory usage, and the bugs have been ironed out of it.
31+
32+ It does decrease performance somewhat, so if you don't want that then don't
33+ install this update.
34+
35+- Closed bugs and merge requests:
36+
37+ * Ensure not to miss the force_gc flag [#150, !132, Carlos Garnacho]
38+ * Make GC much more aggressive [#62, !50, Giovanni Campagna, Georges Basile
39+ Stavracas Neto, Philip Chimento]
40+ * Queue GC when a GObject reference is toggled down [#140, !114, !127, Georges
41+ Basile Stavracas Neto]
42+ * Reduce memory overhead of g_object_weak_ref() [#144, !122, Carlos Garnacho,
43+ Philip Chimento]
44+ * context: Defer and therefore batch forced GC runs [performance] [!236,
45+ Daniel van Vugt]
46+ * context: use timeout with seconds to schedule a gc trigger [!239, Marco
47+ Trevisan]
48+ * Use compacting GC on RSS size growth [!133, #151, Carlos Garnacho]
49+ * GType memleak fixes [!244, Marco Trevisan]
50+
51+Version 1.52.4
52+--------------
53+
54+- Closed bugs and merge requests:
55+
56+ * `ARGV` encoding issues [#22, !108, Evan Welsh]
57+ * Segfault on enumeration of GjSFileImporter properties when a searchpath
58+ entry contains a symlink [#154, !144, Ole Jørgen Brønner]
59+ * Possible refcounting bug around GtkListbox signal handlers [#24, !154,
60+ Philip Chimento]
61+ * Fix up GJS_DISABLE_JIT flag now the JIT is enabled by default in
62+ SpiderMonkey [!159, Christopher Wheeldon]
63+ * Expose GObject static property symbols. [!197, Evan Welsh]
64+ * Do not run linters on tagged commits [!181, Claudio André]
65+ * gjs-1.52.0 fails to compile against x86_64 musl systems [#132, !214, Philip
66+ Chimento]
67+ * gjs no longer builds after recent autoconf-archive updates [#149, !217,
68+ Philip Chimento]
69+
70+Version 1.52.3
71+--------------
72+
73+- Closed bugs and merge requests:
74+
75+ * Include calc.js example from Seed [!130, William Barath, Philip Chimento]
76+ * CI: Un-pin the Fedora Docker image [#141, !131, Claudio André]
77+ * Reduce overhead of wrapped objects [#142, !121, Carlos Garnacho, Philip
78+ Chimento]
79+ * Various CI changes [!134, !136, Claudio André]
80+
81+Version 1.52.2
82+--------------
83+
84+- This is an unscheuled release in order to revert a commit that causes a crash
85+ on exit, with some Cairo versions.
86+
87+- Closed bugs and merge requests:
88+
89+ * CI: pinned Fedora to old tag [!119, Claudio André]
90+ * heapgraph.py: adjust terminal output style [!120, Andy Holmes]
91+ * CI: small tweaks [!123, Claudio André]
92+ * Warn about compilation warnings [!125, Claudio André]
93+ * Miscellaneous commits [Philip Chimento, Jason Hicks]
94+
95 Version 1.52.1
96 --------------
97
98diff --git a/config.rpath b/config.rpath
99deleted file mode 100644
100index e69de29..0000000
101--- a/config.rpath
102+++ /dev/null
103diff --git a/configure b/configure
104index 8410fb2..b79e278 100755
105--- a/configure
106+++ b/configure
107@@ -1,6 +1,6 @@
108 #! /bin/sh
109 # Guess values for system-dependent variables and create Makefiles.
110-# Generated by GNU Autoconf 2.69 for gjs 1.52.1.
111+# Generated by GNU Autoconf 2.69 for gjs 1.52.5.
112 #
113 # Report bugs to <http://bugzilla.gnome.org/enter_bug.cgi?product=gjs>.
114 #
115@@ -591,8 +591,8 @@ MAKEFLAGS=
116 # Identity of this package.
117 PACKAGE_NAME='gjs'
118 PACKAGE_TARNAME='gjs'
119-PACKAGE_VERSION='1.52.1'
120-PACKAGE_STRING='gjs 1.52.1'
121+PACKAGE_VERSION='1.52.5'
122+PACKAGE_STRING='gjs 1.52.5'
123 PACKAGE_BUGREPORT='http://bugzilla.gnome.org/enter_bug.cgi?product=gjs'
124 PACKAGE_URL='https://wiki.gnome.org/Projects/Gjs'
125
126@@ -1451,7 +1451,7 @@ if test "$ac_init_help" = "long"; then
127 # Omit some internal or obsolete options to make the list less imposing.
128 # This message is too long to be a string in the A/UX 3.1 sh.
129 cat <<_ACEOF
130-\`configure' configures gjs 1.52.1 to adapt to many kinds of systems.
131+\`configure' configures gjs 1.52.5 to adapt to many kinds of systems.
132
133 Usage: $0 [OPTION]... [VAR=VALUE]...
134
135@@ -1521,7 +1521,7 @@ fi
136
137 if test -n "$ac_init_help"; then
138 case $ac_init_help in
139- short | recursive ) echo "Configuration of gjs 1.52.1:";;
140+ short | recursive ) echo "Configuration of gjs 1.52.5:";;
141 esac
142 cat <<\_ACEOF
143
144@@ -1700,7 +1700,7 @@ fi
145 test -n "$ac_init_help" && exit $ac_status
146 if $ac_init_version; then
147 cat <<\_ACEOF
148-gjs configure 1.52.1
149+gjs configure 1.52.5
150 generated by GNU Autoconf 2.69
151
152 Copyright (C) 2012 Free Software Foundation, Inc.
153@@ -2251,7 +2251,7 @@ cat >config.log <<_ACEOF
154 This file contains any messages produced by compilers while
155 running configure, to aid debugging if configure makes a mistake.
156
157-It was created by gjs $as_me 1.52.1, which was
158+It was created by gjs $as_me 1.52.5, which was
159 generated by GNU Autoconf 2.69. Invocation command line was
160
161 $ $0 $@
162@@ -3114,7 +3114,7 @@ fi
163
164 # Define the identity of the package.
165 PACKAGE='gjs'
166- VERSION='1.52.1'
167+ VERSION='1.52.5'
168
169
170 cat >>confdefs.h <<_ACEOF
171@@ -3341,10 +3341,10 @@ ac_config_headers="$ac_config_headers config.h"
172
173
174
175-GJS_VERSION=15201
176+GJS_VERSION=15205
177
178
179-$as_echo "#define GJS_VERSION (1 * 100 + 52) * 100 + 1" >>confdefs.h
180+$as_echo "#define GJS_VERSION (1 * 100 + 52) * 100 + 5" >>confdefs.h
181
182
183 GETTEXT_PACKAGE=gjs
184@@ -21806,6 +21806,7 @@ fi
185
186 if test x$enable_profiler != xno; then :
187
188+ # Requires timer_settime() - only on Linux
189
190
191
192@@ -21886,8 +21887,37 @@ done
193
194 LIBS=$gl_saved_libs
195
196- if test x$ac_cv_func_timer_settime = xno; then :
197+ # Requires SIGEV_THREAD_ID - not in some stdlibs
198+ have_sigev_thread_id=no
199+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux SIGEV_THREAD_ID" >&5
200+$as_echo_n "checking for Linux SIGEV_THREAD_ID... " >&6; }
201+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
202+/* end confdefs.h. */
203+
204+ #include <signal.h>
205+int
206+main ()
207+{
208+return SIGEV_THREAD_ID;
209+ ;
210+ return 0;
211+}
212+
213+_ACEOF
214+if ac_fn_cxx_try_compile "$LINENO"; then :
215+
216+ have_sigev_thread_id=yes
217+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
218+$as_echo "yes" >&6; }
219+
220+else
221+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
222+$as_echo "no" >&6; }
223+fi
224+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
225+ if test x$ac_cv_func_timer_settime = xno -o x$have_sigev_thread_id = xno; then :
226 as_fn_error $? "The profiler is currently only supported on Linux.
227+The standard library must support timer_settime() and SIGEV_THREAD_ID.
228 Configure with --disable-profiler to skip it on other platforms." "$LINENO" 5
229 fi
230
231@@ -23346,7 +23376,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
232 # report actual input values of CONFIG_FILES etc. instead of their
233 # values after options handling.
234 ac_log="
235-This file was extended by gjs $as_me 1.52.1, which was
236+This file was extended by gjs $as_me 1.52.5, which was
237 generated by GNU Autoconf 2.69. Invocation command line was
238
239 CONFIG_FILES = $CONFIG_FILES
240@@ -23417,7 +23447,7 @@ _ACEOF
241 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
242 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
243 ac_cs_version="\\
244-gjs config.status 1.52.1
245+gjs config.status 1.52.5
246 configured by $0, generated by GNU Autoconf 2.69,
247 with options \\"\$ac_cs_config\\"
248
249diff --git a/configure.ac b/configure.ac
250index c7b7dcb..1c62d93 100644
251--- a/configure.ac
252+++ b/configure.ac
253@@ -3,7 +3,7 @@
254
255 m4_define(pkg_major_version, 1)
256 m4_define(pkg_minor_version, 52)
257-m4_define(pkg_micro_version, 1)
258+m4_define(pkg_micro_version, 5)
259 m4_define(pkg_version, pkg_major_version.pkg_minor_version.pkg_micro_version)
260 m4_define(pkg_int_version, (pkg_major_version * 100 + pkg_minor_version) * 100 + pkg_micro_version)
261
262@@ -152,9 +152,20 @@ AS_IF([test x$have_gtk = xyes], [
263 AC_ARG_ENABLE([profiler],
264 [AS_HELP_STRING([--disable-profiler], [Don't build profiler])])
265 AS_IF([test x$enable_profiler != xno], [
266+ # Requires timer_settime() - only on Linux
267 gl_TIMER_TIME
268- AS_IF([test x$ac_cv_func_timer_settime = xno],
269+ # Requires SIGEV_THREAD_ID - not in some stdlibs
270+ have_sigev_thread_id=no
271+ AC_MSG_CHECKING([for Linux SIGEV_THREAD_ID])
272+ AC_COMPILE_IFELSE([
273+ AC_LANG_PROGRAM([[#include <signal.h>]], [return SIGEV_THREAD_ID;])
274+ ], [
275+ have_sigev_thread_id=yes
276+ AC_MSG_RESULT([yes])
277+ ], [AC_MSG_RESULT([no])])
278+ AS_IF([test x$ac_cv_func_timer_settime = xno -o x$have_sigev_thread_id = xno],
279 [AC_MSG_ERROR([The profiler is currently only supported on Linux.
280+The standard library must support timer_settime() and SIGEV_THREAD_ID.
281 Configure with --disable-profiler to skip it on other platforms.])])
282 AC_DEFINE([ENABLE_PROFILER], [1], [Define if the profiler should be built.])
283 ])
284diff --git a/debian/changelog b/debian/changelog
285index feae994..c276370 100644
286--- a/debian/changelog
287+++ b/debian/changelog
288@@ -1,3 +1,13 @@
289+gjs (1.52.5-0~ubuntu18.04.1) UNRELEASED; urgency=medium
290+
291+ * New upstream release (LP: #1809181, LP: #1803271)
292+ * d/p/fix-crashes-lp1763878-revert-575f1e2e077.patch,
293+ d/p/fix-leaks-lp1672297-1-context-Add-API-to-force-GC-schedule.patch,
294+ d/p/fix-leaks-lp1672297-2-object-Queue-a-forced-GC-when-toggling-down.patch:
295+ - Drop patches included in new release
296+ * debian/gbp.conf:
297+ - Point branches and tag rules to ubuntu
298+
299 gjs (1.52.1-1ubuntu1) bionic; urgency=medium
300
301 * Add fix-crashes-lp1763878-revert-575f1e2e077.patch to fix shutdown
302diff --git a/debian/control b/debian/control
303index bcbd2dc..d6cb30a 100644
304--- a/debian/control
305+++ b/debian/control
306@@ -25,8 +25,10 @@ Build-Depends: debhelper (>= 11),
307 xvfb <!nocheck>
308 Rules-Requires-Root: no
309 Standards-Version: 4.1.3
310-Vcs-Git: https://salsa.debian.org/gnome-team/gjs.git
311-Vcs-Browser: https://salsa.debian.org/gnome-team/gjs
312+XS-Debian-Vcs-Browser: https://salsa.debian.org/gnome-team/gjs
313+XS-Debian-Vcs-Git: https://salsa.debian.org/gnome-team/gjs.git -b debian/1.52.x
314+Vcs-Browser: https://git.launchpad.net/~ubuntu-desktop/ubuntu/+source/gjs
315+Vcs-Git: https://git.launchpad.net/~ubuntu-desktop/ubuntu/+source/gjs -b ubuntu/bionic
316 Homepage: https://wiki.gnome.org/Projects/Gjs
317
318 Package: gjs
319diff --git a/debian/gbp.conf b/debian/gbp.conf
320index e0196c4..0592ca0 100644
321--- a/debian/gbp.conf
322+++ b/debian/gbp.conf
323@@ -1,5 +1,6 @@
324 [DEFAULT]
325 pristine-tar = True
326-debian-branch = debian/master
327-upstream-branch = upstream/latest
328+debian-branch = debian/bionic
329+debian-tag = ubuntu/%(version)s
330+upstream-branch = upstream/1.52.x
331 upstream-vcs-tag = %(version)s
332diff --git a/debian/patches/fix-crashes-lp1763878-revert-575f1e2e077.patch b/debian/patches/fix-crashes-lp1763878-revert-575f1e2e077.patch
333deleted file mode 100644
334index 4da9220..0000000
335--- a/debian/patches/fix-crashes-lp1763878-revert-575f1e2e077.patch
336+++ /dev/null
337@@ -1,69 +0,0 @@
338-From 8510bede1dd1f8a5fb95a2f594b4d3a68289e5ea Mon Sep 17 00:00:00 2001
339-From: Philip Chimento <philip.chimento@gmail.com>
340-Date: Sat, 14 Apr 2018 16:25:58 -0700
341-Subject: [PATCH] Revert "engine: Free Cairo static data on shutdown"
342-
343-This reverts commit 575f1e2e077af04a112b9e5eaabaf008b086568e.
344-Per https://bugs.freedesktop.org/show_bug.cgi?id=105466, calling
345-cairo_debug_reset_static_data() was supposed to be safe in production,
346-but actually it fails assertions on program exit, with certain Cairo
347-versions, including 1.14.12.
348-
349-Unreviewed.
350----
351- gjs/engine.cpp | 18 ++----------------
352- 1 file changed, 2 insertions(+), 16 deletions(-)
353-
354-diff --git a/gjs/engine.cpp b/gjs/engine.cpp
355-index 90fa57c..67911ee 100644
356---- a/gjs/engine.cpp
357-+++ b/gjs/engine.cpp
358-@@ -37,10 +37,6 @@
359- #include <windows.h>
360- #endif
361-
362--#ifdef ENABLE_CAIRO
363--# include <cairo.h>
364--#endif
365--
366- /* Implementations of locale-specific operations; these are used
367- * in the implementation of String.localeCompare(), Date.toLocaleDateString(),
368- * and so forth. We take the straight-forward approach of converting
369-@@ -218,16 +214,6 @@ on_promise_unhandled_rejection(JSContext *cx,
370- std::move(stack));
371- }
372-
373--static void
374--shutdown(void)
375--{
376-- JS_ShutDown();
377--
378--#ifdef ENABLE_CAIRO
379-- cairo_debug_reset_static_data(); /* for valgrind reports */
380--#endif
381--}
382--
383- #ifdef G_OS_WIN32
384- HMODULE gjs_dll;
385- static bool gjs_is_inited = false;
386-@@ -245,7 +231,7 @@ LPVOID lpvReserved)
387- break;
388-
389- case DLL_THREAD_DETACH:
390-- shutdown();
391-+ JS_ShutDown ();
392- break;
393-
394- default:
395-@@ -265,7 +251,7 @@ public:
396- }
397-
398- ~GjsInit() {
399-- shutdown();
400-+ JS_ShutDown();
401- }
402-
403- operator bool() {
404---
405-2.17.0
406-
407diff --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
408deleted file mode 100644
409index 84944e6..0000000
410--- a/debian/patches/fix-leaks-lp1672297-1-context-Add-API-to-force-GC-schedule.patch
411+++ /dev/null
412@@ -1,90 +0,0 @@
413-From 33cbbeb11b61a0ffc8ff50e261e5dd33806590f9 Mon Sep 17 00:00:00 2001
414-From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
415-Date: Fri, 30 Mar 2018 21:37:37 -0300
416-Subject: [PATCH 1/2] context: Add API to force GC schedule
417-
418-There are situations where we cannot run the
419-GC right away, but we also cannot ignore the
420-need of running it.
421-
422-For those cases, add a new private function
423-that forces GC to happen on idle.
424----
425- gjs/context-private.h | 2 ++
426- gjs/context.cpp | 29 +++++++++++++++++++++++++----
427- 2 files changed, 27 insertions(+), 4 deletions(-)
428-
429-diff --git a/gjs/context-private.h b/gjs/context-private.h
430-index 6dbe669..c45c8d0 100644
431---- a/gjs/context-private.h
432-+++ b/gjs/context-private.h
433-@@ -36,6 +36,8 @@ bool _gjs_context_destroying (GjsContext *js_context);
434-
435- void _gjs_context_schedule_gc_if_needed (GjsContext *js_context);
436-
437-+void _gjs_context_schedule_gc (GjsContext *js_context);
438-+
439- void _gjs_context_exit(GjsContext *js_context,
440- uint8_t exit_code);
441-
442-diff --git a/gjs/context.cpp b/gjs/context.cpp
443-index c509943..77d7eaa 100644
444---- a/gjs/context.cpp
445-+++ b/gjs/context.cpp
446-@@ -90,6 +90,7 @@ struct _GjsContext {
447- uint8_t exit_code;
448-
449- guint auto_gc_id;
450-+ bool force_gc;
451-
452- std::array<JS::PersistentRootedId*, GJS_STRING_LAST> const_strings;
453-
454-@@ -592,21 +593,41 @@ trigger_gc_if_needed (gpointer user_data)
455- {
456- GjsContext *js_context = GJS_CONTEXT(user_data);
457- js_context->auto_gc_id = 0;
458-- gjs_gc_if_needed(js_context->context);
459-+
460-+ if (js_context->force_gc)
461-+ JS_GC(js_context->context);
462-+ else
463-+ gjs_gc_if_needed(js_context->context);
464-+
465- return G_SOURCE_REMOVE;
466- }
467-
468--void
469--_gjs_context_schedule_gc_if_needed (GjsContext *js_context)
470-+
471-+static void
472-+_gjs_context_schedule_gc_internal (GjsContext *js_context,
473-+ bool force_gc)
474- {
475- if (js_context->auto_gc_id > 0)
476-- return;
477-+ g_source_remove(js_context->auto_gc_id);
478-
479-+ js_context->force_gc = force_gc;
480- js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW,
481- trigger_gc_if_needed,
482- js_context, NULL);
483- }
484-
485-+void
486-+_gjs_context_schedule_gc (GjsContext *js_context)
487-+{
488-+ _gjs_context_schedule_gc_internal(js_context, true);
489-+}
490-+
491-+void
492-+_gjs_context_schedule_gc_if_needed (GjsContext *js_context)
493-+{
494-+ _gjs_context_schedule_gc_internal(js_context, false);
495-+}
496-+
497- void
498- _gjs_context_exit(GjsContext *js_context,
499- uint8_t exit_code)
500---
501-2.17.0
502-
503diff --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
504deleted file mode 100644
505index 34f3d3b..0000000
506--- a/debian/patches/fix-leaks-lp1672297-2-object-Queue-a-forced-GC-when-toggling-down.patch
507+++ /dev/null
508@@ -1,123 +0,0 @@
509-From 3f94b11a943c0e4d29c96930ced238580dc18fc7 Mon Sep 17 00:00:00 2001
510-From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
511-Date: Wed, 28 Mar 2018 19:21:52 -0300
512-Subject: [PATCH 2/2] object: Queue a forced GC when toggling down
513-
514-During a GC, the collector asks each object which other
515-objects that it wants to hold on to so if there's an entire
516-section of the heap graph that's not connected to anything
517-else, and not reachable from the root set, then it can be
518-trashed all at once.
519-
520-GObjects, however, don't work like that, there's only a
521-reference count but no notion of who owns the reference so,
522-a JS object that's proxying a GObject is unconditionally held
523-alive as long as the GObject has >1 references.
524-
525-Since we cannot know how many more wrapped GObjects are going
526-be marked for garbage collection after the owner is destroyed,
527-always queue a garbage collection when a toggle reference goes
528-down.
529-
530-Issue: #140
531----
532- gi/object.cpp | 22 ++++++++++++++++++++++
533- gjs/context-private.h | 2 +-
534- gjs/context.cpp | 14 ++++++++------
535- 3 files changed, 31 insertions(+), 7 deletions(-)
536-
537-diff --git a/gi/object.cpp b/gi/object.cpp
538-index b20d8b9..f9cf3cc 100644
539---- a/gi/object.cpp
540-+++ b/gi/object.cpp
541-@@ -987,8 +987,30 @@ handle_toggle_down(GObject *gobj)
542- * collected by the GC
543- */
544- if (priv->keep_alive.rooted()) {
545-+ GjsContext *context;
546-+
547- gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object");
548- priv->keep_alive.switch_to_unrooted();
549-+
550-+ /* During a GC, the collector asks each object which other
551-+ * objects that it wants to hold on to so if there's an entire
552-+ * section of the heap graph that's not connected to anything
553-+ * else, and not reachable from the root set, then it can be
554-+ * trashed all at once.
555-+ *
556-+ * GObjects, however, don't work like that, there's only a
557-+ * reference count but no notion of who owns the reference so,
558-+ * a JS object that's proxying a GObject is unconditionally held
559-+ * alive as long as the GObject has >1 references.
560-+ *
561-+ * Since we cannot know how many more wrapped GObjects are going
562-+ * be marked for garbage collection after the owner is destroyed,
563-+ * always queue a garbage collection when a toggle reference goes
564-+ * down.
565-+ */
566-+ context = gjs_context_get_current();
567-+ if (!_gjs_context_destroying(context))
568-+ _gjs_context_schedule_gc(context);
569- }
570- }
571-
572-diff --git a/gjs/context-private.h b/gjs/context-private.h
573-index c45c8d0..49c0cf9 100644
574---- a/gjs/context-private.h
575-+++ b/gjs/context-private.h
576-@@ -36,7 +36,7 @@ bool _gjs_context_destroying (GjsContext *js_context);
577-
578- void _gjs_context_schedule_gc_if_needed (GjsContext *js_context);
579-
580--void _gjs_context_schedule_gc (GjsContext *js_context);
581-+void _gjs_context_schedule_gc(GjsContext *js_context);
582-
583- void _gjs_context_exit(GjsContext *js_context,
584- uint8_t exit_code);
585-diff --git a/gjs/context.cpp b/gjs/context.cpp
586-index 77d7eaa..a2ce34a 100644
587---- a/gjs/context.cpp
588-+++ b/gjs/context.cpp
589-@@ -599,31 +599,33 @@ trigger_gc_if_needed (gpointer user_data)
590- else
591- gjs_gc_if_needed(js_context->context);
592-
593-+ js_context->force_gc = false;
594-+
595- return G_SOURCE_REMOVE;
596- }
597-
598-
599- static void
600--_gjs_context_schedule_gc_internal (GjsContext *js_context,
601-- bool force_gc)
602-+_gjs_context_schedule_gc_internal(GjsContext *js_context,
603-+ bool force_gc)
604- {
605- if (js_context->auto_gc_id > 0)
606-- g_source_remove(js_context->auto_gc_id);
607-+ return;
608-
609-- js_context->force_gc = force_gc;
610-+ js_context->force_gc |= force_gc;
611- js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW,
612- trigger_gc_if_needed,
613- js_context, NULL);
614- }
615-
616- void
617--_gjs_context_schedule_gc (GjsContext *js_context)
618-+_gjs_context_schedule_gc(GjsContext *js_context)
619- {
620- _gjs_context_schedule_gc_internal(js_context, true);
621- }
622-
623- void
624--_gjs_context_schedule_gc_if_needed (GjsContext *js_context)
625-+_gjs_context_schedule_gc_if_needed(GjsContext *js_context)
626- {
627- _gjs_context_schedule_gc_internal(js_context, false);
628- }
629---
630-2.17.0
631-
632diff --git a/debian/patches/series b/debian/patches/series
633deleted file mode 100644
634index 1de36f6..0000000
635--- a/debian/patches/series
636+++ /dev/null
637@@ -1,6 +0,0 @@
638-# Cherry picked from gjs master:
639-fix-crashes-lp1763878-revert-575f1e2e077.patch
640-
641-# Critical leak fixes (not yet landed in gjs master):
642-fix-leaks-lp1672297-1-context-Add-API-to-force-GC-schedule.patch
643-fix-leaks-lp1672297-2-object-Queue-a-forced-GC-when-toggling-down.patch
644diff --git a/gi/gtype.cpp b/gi/gtype.cpp
645index 314f4b2..03920eb 100644
646--- a/gi/gtype.cpp
647+++ b/gi/gtype.cpp
648@@ -63,13 +63,18 @@ update_gtype_weak_pointers(JSContext *cx,
649 void *data)
650 {
651 for (auto iter = weak_pointer_list.begin(); iter != weak_pointer_list.end(); ) {
652- auto heap_wrapper = static_cast<JS::Heap<JSObject *> *>(g_type_get_qdata(*iter, gjs_get_gtype_wrapper_quark()));
653+ GType gtype = *iter;
654+ auto heap_wrapper = static_cast<JS::Heap<JSObject *> *>(
655+ g_type_get_qdata(gtype, gjs_get_gtype_wrapper_quark()));
656 JS_UpdateWeakPointerAfterGC(heap_wrapper);
657
658 /* No read barriers are needed if the only thing we are doing with the
659 * pointer is comparing it to nullptr. */
660- if (heap_wrapper->unbarrieredGet() == nullptr)
661+ if (heap_wrapper->unbarrieredGet() == nullptr) {
662+ g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), nullptr);
663 iter = weak_pointer_list.erase(iter);
664+ delete heap_wrapper;
665+ }
666 else
667 iter++;
668 }
669@@ -95,8 +100,12 @@ gjs_gtype_finalize(JSFreeOp *fop,
670 if (G_UNLIKELY(gtype == 0))
671 return;
672
673- weak_pointer_list.erase(gtype);
674+ auto heap_wrapper = static_cast<JS::Heap<JSObject*>*>(
675+ g_type_get_qdata(gtype, gjs_get_gtype_wrapper_quark()));
676+
677 g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), NULL);
678+ weak_pointer_list.erase(gtype);
679+ delete heap_wrapper;
680 }
681
682 static bool
683diff --git a/gi/object.cpp b/gi/object.cpp
684index b20d8b9..fe381ec 100644
685--- a/gi/object.cpp
686+++ b/gi/object.cpp
687@@ -54,6 +54,65 @@
688 #include <util/log.h>
689 #include <girepository.h>
690
691+typedef class GjsListLink GjsListLink;
692+typedef struct ObjectInstance ObjectInstance;
693+
694+static GjsListLink* object_instance_get_link(ObjectInstance *priv);
695+
696+class GjsListLink {
697+ private:
698+ ObjectInstance *m_prev;
699+ ObjectInstance *m_next;
700+
701+ public:
702+ ObjectInstance* prev() {
703+ return m_prev;
704+ }
705+
706+ ObjectInstance* next() {
707+ return m_next;
708+ }
709+
710+ void prepend(ObjectInstance *this_instance,
711+ ObjectInstance *head) {
712+ GjsListLink *elem = object_instance_get_link(head);
713+
714+ g_assert(object_instance_get_link(this_instance) == this);
715+
716+ if (elem->m_prev) {
717+ GjsListLink *prev = object_instance_get_link(elem->m_prev);
718+ prev->m_next = this_instance;
719+ this->m_prev = elem->m_prev;
720+ }
721+
722+ elem->m_prev = this_instance;
723+ this->m_next = head;
724+ }
725+
726+ void unlink() {
727+ if (m_prev)
728+ object_instance_get_link(m_prev)->m_next = m_next;
729+ if (m_next)
730+ object_instance_get_link(m_next)->m_prev = m_prev;
731+
732+ m_prev = m_next = NULL;
733+ }
734+
735+ int size() {
736+ GjsListLink *elem = this;
737+ int count = 0;
738+
739+ do {
740+ count++;
741+ if (!elem->m_next)
742+ break;
743+ elem = object_instance_get_link(elem->m_next);
744+ } while (elem);
745+
746+ return count;
747+ }
748+};
749+
750 struct ObjectInstance {
751 GIObjectInfo *info;
752 GObject *gobj; /* NULL if we are the prototype and not an instance */
753@@ -68,8 +127,15 @@ struct ObjectInstance {
754 prototypes) */
755 GTypeClass *klass;
756
757+ GjsListLink instance_link;
758+
759 unsigned js_object_finalized : 1;
760 unsigned g_object_finalized : 1;
761+
762+ /* True if this object has visible JS state, and thus its lifecycle is
763+ * managed using toggle references. False if this object just keeps a
764+ * hard ref on the underlying GObject, and may be finalized at will. */
765+ bool uses_toggle_ref : 1;
766 };
767
768 static std::stack<JS::PersistentRootedObject> object_init_list;
769@@ -78,13 +144,15 @@ using ParamRef = std::unique_ptr<GParamSpec, decltype(&g_param_spec_unref)>;
770 using ParamRefArray = std::vector<ParamRef>;
771 static std::unordered_map<GType, ParamRefArray> class_init_properties;
772
773+static bool context_weak_pointer_callback = false;
774 static bool weak_pointer_callback = false;
775-static std::set<ObjectInstance *> wrapped_gobject_list;
776+ObjectInstance *wrapped_gobject_list;
777
778 extern struct JSClass gjs_object_instance_class;
779 GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class)
780
781 static void disassociate_js_gobject (GObject *gobj);
782+static void ensure_uses_toggle_ref(JSContext *cx, ObjectInstance *priv);
783
784 typedef enum {
785 SOME_ERROR_OCCURRED = false,
786@@ -152,7 +220,7 @@ get_object_qdata(GObject *gobj)
787 auto priv = static_cast<ObjectInstance *>(g_object_get_qdata(gobj,
788 gjs_object_priv_quark()));
789
790- if (priv && G_UNLIKELY(priv->js_object_finalized)) {
791+ if (priv && priv->uses_toggle_ref && G_UNLIKELY(priv->js_object_finalized)) {
792 g_critical("Object %p (a %s) resurfaced after the JS wrapper was finalized. "
793 "This is some library doing dubious memory management inside dispose()",
794 gobj, g_type_name(G_TYPE_FROM_INSTANCE(gobj)));
795@@ -430,6 +498,9 @@ set_g_param_from_prop(JSContext *context,
796 case SOME_ERROR_OCCURRED:
797 return false;
798 case NO_SUCH_G_PROPERTY:
799+ /* We need to keep the wrapper alive in order not to lose custom
800+ * "expando" properties */
801+ ensure_uses_toggle_ref(context, priv);
802 return result.succeed();
803 case VALUE_WAS_SET:
804 default:
805@@ -496,14 +567,7 @@ object_instance_set_prop(JSContext *context,
806 bool ret = true;
807 bool g_param_was_set = false;
808
809- if (!gjs_get_string_id(context, id, &name))
810- return result.succeed(); /* not resolved, but no error */
811-
812 priv = priv_from_js(context, obj);
813- gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
814- "Set prop '%s' hook obj %p priv %p",
815- name.get(), obj.get(), priv);
816-
817 if (priv == nullptr)
818 /* see the comment in object_instance_get_prop() on this */
819 return result.succeed();
820@@ -521,6 +585,18 @@ object_instance_set_prop(JSContext *context,
821 return result.succeed();
822 }
823
824+ if (!gjs_get_string_id(context, id, &name)) {
825+ /* We need to keep the wrapper alive in order not to lose custom
826+ * "expando" properties. In this case if gjs_get_string_id() is false
827+ * then a number or symbol property was probably set. */
828+ ensure_uses_toggle_ref(context, priv);
829+ return result.succeed(); /* not resolved, but no error */
830+ }
831+
832+ gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
833+ "Set prop '%s' hook obj %p priv %p",
834+ name.get(), obj.get(), priv);
835+
836 ret = set_g_param_from_prop(context, priv, name, g_param_was_set, value_p, result);
837 if (g_param_was_set || !ret)
838 return ret;
839@@ -755,18 +831,6 @@ object_instance_resolve(JSContext *context,
840 return true;
841 }
842
843- if (priv->g_object_finalized) {
844- g_critical("Object %s.%s (%p), has been already finalized. "
845- "Impossible to resolve it.",
846- priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
847- priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
848- priv->gobj);
849- gjs_dumpstack();
850-
851- *resolved = false;
852- return true;
853- }
854-
855 /* If we have no GIRepository information (we're a JS GObject subclass),
856 * we need to look at exposing interfaces. Look up our interfaces through
857 * GType data, and then hope that *those* are introspectable. */
858@@ -945,6 +1009,28 @@ object_instance_props_to_g_parameters(JSContext *context,
859 return true;
860 }
861
862+static GjsListLink *
863+object_instance_get_link(ObjectInstance *priv)
864+{
865+ return &priv->instance_link;
866+}
867+
868+static void
869+object_instance_unlink(ObjectInstance *priv)
870+{
871+ if (wrapped_gobject_list == priv)
872+ wrapped_gobject_list = priv->instance_link.next();
873+ priv->instance_link.unlink();
874+}
875+
876+static void
877+object_instance_link(ObjectInstance *priv)
878+{
879+ if (wrapped_gobject_list)
880+ priv->instance_link.prepend(priv, wrapped_gobject_list);
881+ wrapped_gobject_list = priv;
882+}
883+
884 static void
885 wrapped_gobj_dispose_notify(gpointer data,
886 GObject *where_the_object_was)
887@@ -952,26 +1038,30 @@ wrapped_gobj_dispose_notify(gpointer data,
888 auto *priv = static_cast<ObjectInstance *>(data);
889
890 priv->g_object_finalized = true;
891- wrapped_gobject_list.erase(priv);
892+ object_instance_unlink(priv);
893 gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Wrapped GObject %p disposed",
894 where_the_object_was);
895 }
896
897-static void
898-gobj_no_longer_kept_alive_func(JS::HandleObject obj,
899- void *data)
900+void
901+gjs_object_context_dispose_notify(void *data,
902+ GObject *where_the_object_was)
903 {
904- ObjectInstance *priv;
905+ ObjectInstance *priv = wrapped_gobject_list;
906+ while (priv) {
907+ ObjectInstance *next = priv->instance_link.next();
908
909- priv = (ObjectInstance *) data;
910-
911- gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "GObject wrapper %p for GObject "
912- "%p (%s) was rooted but is now unrooted due to "
913- "GjsContext dispose", obj.get(), priv->gobj,
914- G_OBJECT_TYPE_NAME(priv->gobj));
915+ if (priv->keep_alive.rooted()) {
916+ gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "GObject wrapper %p for GObject "
917+ "%p (%s) was rooted but is now unrooted due to "
918+ "GjsContext dispose", priv->keep_alive.get(),
919+ priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj));
920+ priv->keep_alive.reset();
921+ object_instance_unlink(priv);
922+ }
923
924- priv->keep_alive.reset();
925- wrapped_gobject_list.erase(priv);
926+ priv = next;
927+ }
928 }
929
930 static void
931@@ -987,8 +1077,30 @@ handle_toggle_down(GObject *gobj)
932 * collected by the GC
933 */
934 if (priv->keep_alive.rooted()) {
935+ GjsContext *context;
936+
937 gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object");
938 priv->keep_alive.switch_to_unrooted();
939+
940+ /* During a GC, the collector asks each object which other
941+ * objects that it wants to hold on to so if there's an entire
942+ * section of the heap graph that's not connected to anything
943+ * else, and not reachable from the root set, then it can be
944+ * trashed all at once.
945+ *
946+ * GObjects, however, don't work like that, there's only a
947+ * reference count but no notion of who owns the reference so,
948+ * a JS object that's proxying a GObject is unconditionally held
949+ * alive as long as the GObject has >1 references.
950+ *
951+ * Since we cannot know how many more wrapped GObjects are going
952+ * be marked for garbage collection after the owner is destroyed,
953+ * always queue a garbage collection when a toggle reference goes
954+ * down.
955+ */
956+ context = gjs_context_get_current();
957+ if (!_gjs_context_destroying(context))
958+ _gjs_context_schedule_gc(context);
959 }
960 }
961
962@@ -1017,7 +1129,7 @@ handle_toggle_up(GObject *gobj)
963 GjsContext *context = gjs_context_get_current();
964 gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Rooting object");
965 auto cx = static_cast<JSContext *>(gjs_context_get_native_context(context));
966- priv->keep_alive.switch_to_rooted(cx, gobj_no_longer_kept_alive_func, priv);
967+ priv->keep_alive.switch_to_rooted(cx);
968 }
969 }
970
971@@ -1126,7 +1238,10 @@ static void
972 release_native_object (ObjectInstance *priv)
973 {
974 priv->keep_alive.reset();
975- g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, NULL);
976+ if (priv->uses_toggle_ref)
977+ g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, nullptr);
978+ else
979+ g_object_unref(priv->gobj);
980 priv->gobj = NULL;
981 }
982
983@@ -1156,14 +1271,15 @@ gjs_object_prepare_shutdown(void)
984 * toggle ref removal -> gobj dispose -> toggle ref notify
985 * by emptying the toggle queue earlier in the shutdown sequence. */
986 std::vector<ObjectInstance *> to_be_released;
987- for (auto iter = wrapped_gobject_list.begin(); iter != wrapped_gobject_list.end(); ) {
988- ObjectInstance *priv = *iter;
989- if (priv->keep_alive.rooted()) {
990- to_be_released.push_back(priv);
991- iter = wrapped_gobject_list.erase(iter);
992- } else {
993- iter++;
994+ ObjectInstance *link = wrapped_gobject_list;
995+ while (link) {
996+ ObjectInstance *next = link->instance_link.next();
997+ if (link->keep_alive.rooted()) {
998+ to_be_released.push_back(link);
999+ object_instance_unlink(link);
1000 }
1001+
1002+ link = next;
1003 }
1004 for (ObjectInstance *priv : to_be_released)
1005 release_native_object(priv);
1006@@ -1209,16 +1325,18 @@ update_heap_wrapper_weak_pointers(JSContext *cx,
1007 {
1008 gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Weak pointer update callback, "
1009 "%zu wrapped GObject(s) to examine",
1010- wrapped_gobject_list.size());
1011+ wrapped_gobject_list ?
1012+ wrapped_gobject_list->instance_link.size() : 0);
1013
1014 std::vector<GObject *> to_be_disassociated;
1015+ ObjectInstance *priv = wrapped_gobject_list;
1016
1017- for (auto iter = wrapped_gobject_list.begin(); iter != wrapped_gobject_list.end(); ) {
1018- ObjectInstance *priv = *iter;
1019- if (priv->keep_alive.rooted() || priv->keep_alive == nullptr ||
1020- !priv->keep_alive.update_after_gc()) {
1021- iter++;
1022- } else {
1023+ while (priv) {
1024+ ObjectInstance *next = priv->instance_link.next();
1025+
1026+ if (!priv->keep_alive.rooted() &&
1027+ priv->keep_alive != nullptr &&
1028+ priv->keep_alive.update_after_gc()) {
1029 /* Ouch, the JS object is dead already. Disassociate the
1030 * GObject and hope the GObject dies too. (Remove it from
1031 * the weak pointer list first, since the disassociation
1032@@ -1229,8 +1347,10 @@ update_heap_wrapper_weak_pointers(JSContext *cx,
1033 "%p (%s)", priv->keep_alive.get(), priv->gobj,
1034 G_OBJECT_TYPE_NAME(priv->gobj));
1035 to_be_disassociated.push_back(priv->gobj);
1036- iter = wrapped_gobject_list.erase(iter);
1037+ object_instance_unlink(priv);
1038 }
1039+
1040+ priv = next;
1041 }
1042
1043 for (GObject *gobj : to_be_disassociated)
1044@@ -1256,16 +1376,28 @@ associate_js_gobject (JSContext *context,
1045 ObjectInstance *priv;
1046
1047 priv = priv_from_js(context, object);
1048+ priv->uses_toggle_ref = false;
1049 priv->gobj = gobj;
1050
1051 g_assert(!priv->keep_alive.rooted());
1052
1053 set_object_qdata(gobj, priv);
1054
1055+ priv->keep_alive = object;
1056 ensure_weak_pointer_callback(context);
1057- wrapped_gobject_list.insert(priv);
1058+ object_instance_link(priv);
1059
1060 g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, priv);
1061+}
1062+
1063+static void
1064+ensure_uses_toggle_ref(JSContext *cx,
1065+ ObjectInstance *priv)
1066+{
1067+ if (priv->uses_toggle_ref)
1068+ return;
1069+
1070+ g_assert(!priv->keep_alive.rooted());
1071
1072 /* OK, here is where things get complicated. We want the
1073 * wrapped gobj to keep the JSObject* wrapper alive, because
1074@@ -1278,8 +1410,14 @@ associate_js_gobject (JSContext *context,
1075 * the wrapper to be garbage collected (and thus unref the
1076 * wrappee).
1077 */
1078- priv->keep_alive.root(context, object, gobj_no_longer_kept_alive_func, priv);
1079- g_object_add_toggle_ref(gobj, wrapped_gobj_toggle_notify, NULL);
1080+ priv->uses_toggle_ref = true;
1081+ priv->keep_alive.switch_to_rooted(cx);
1082+ g_object_add_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, nullptr);
1083+
1084+ /* We now have both a ref and a toggle ref, we only want the toggle ref.
1085+ * This may immediately remove the GC root we just added, since refcount
1086+ * may drop to 1. */
1087+ g_object_unref(priv->gobj);
1088 }
1089
1090 static void
1091@@ -1303,7 +1441,8 @@ disassociate_js_gobject(GObject *gobj)
1092 ObjectInstance *priv = get_object_qdata(gobj);
1093 bool had_toggle_down, had_toggle_up;
1094
1095- g_object_weak_unref(priv->gobj, wrapped_gobj_dispose_notify, priv);
1096+ if (!priv->g_object_finalized)
1097+ g_object_weak_unref(gobj, wrapped_gobj_dispose_notify, priv);
1098
1099 /* FIXME: this check fails when JS code runs after the main loop ends,
1100 * because the idle functions are not dispatched without a main loop.
1101@@ -1322,11 +1461,16 @@ disassociate_js_gobject(GObject *gobj)
1102 gobj, G_OBJECT_TYPE_NAME(gobj));
1103 }
1104
1105+ /* Fist, remove the wrapper pointer from the wrapped GObject */
1106+ set_object_qdata(gobj, nullptr);
1107+
1108+ /* Now release all the resources the current wrapper has */
1109 invalidate_all_closures(priv);
1110 release_native_object(priv);
1111
1112 /* Mark that a JS object once existed, but it doesn't any more */
1113 priv->js_object_finalized = true;
1114+ priv->keep_alive = nullptr;
1115 }
1116
1117 static void
1118@@ -1383,6 +1527,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS
1119 * we're not actually using it, so just let it get collected. Avoiding
1120 * this would require a non-trivial amount of work.
1121 * */
1122+ ensure_uses_toggle_ref(context, other_priv);
1123 object.set(other_priv->keep_alive);
1124 g_object_unref(gobj); /* We already own a reference */
1125 gobj = NULL;
1126@@ -1410,11 +1555,6 @@ G_GNUC_END_IGNORE_DEPRECATIONS
1127
1128 if (priv->gobj == NULL)
1129 associate_js_gobject(context, object, gobj);
1130- /* We now have both a ref and a toggle ref, we only want the
1131- * toggle ref. This may immediately remove the GC root
1132- * we just added, since refcount may drop to 1.
1133- */
1134- g_object_unref(gobj);
1135
1136 gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "JSObject created with GObject %p (%s)",
1137 priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj));
1138@@ -1463,15 +1603,6 @@ object_instance_trace(JSTracer *tracer,
1139 if (priv == NULL)
1140 return;
1141
1142- if (priv->g_object_finalized) {
1143- g_debug("Object %s.%s (%p), has been already finalized. "
1144- "Impossible to trace it.",
1145- priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
1146- priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
1147- priv->gobj);
1148- return;
1149- }
1150-
1151 for (GClosure *closure : priv->closures)
1152 gjs_closure_trace(closure, tracer);
1153 }
1154@@ -1541,7 +1672,7 @@ object_instance_finalize(JSFreeOp *fop,
1155
1156 priv->keep_alive.reset();
1157 }
1158- wrapped_gobject_list.erase(priv);
1159+ object_instance_unlink(priv);
1160
1161 if (priv->info) {
1162 g_base_info_unref( (GIBaseInfo*) priv->info);
1163@@ -1556,6 +1687,9 @@ object_instance_finalize(JSFreeOp *fop,
1164 GJS_DEC_COUNTER(object);
1165 priv->~ObjectInstance();
1166 g_slice_free(ObjectInstance, priv);
1167+
1168+ /* Remove the ObjectInstance pointer from the JSObject */
1169+ JS_SetPrivate(obj, nullptr);
1170 }
1171
1172 static JSObject *
1173@@ -1683,6 +1817,8 @@ real_connect_func(JSContext *context,
1174 return true;
1175 }
1176
1177+ ensure_uses_toggle_ref(context, priv);
1178+
1179 if (argc != 2 || !argv[0].isString() || !JS::IsCallable(&argv[1].toObject())) {
1180 gjs_throw(context, "connect() takes two args, the signal name and the callback");
1181 return false;
1182@@ -2123,9 +2259,6 @@ gjs_object_from_g_object(JSContext *context,
1183 g_object_ref_sink(gobj);
1184 associate_js_gobject(context, obj, gobj);
1185
1186- /* see the comment in init_object_instance() for this */
1187- g_object_unref(gobj);
1188-
1189 g_assert(priv->keep_alive == obj.get());
1190 }
1191
1192@@ -2142,6 +2275,19 @@ gjs_g_object_from_object(JSContext *context,
1193 return NULL;
1194
1195 priv = priv_from_js(context, obj);
1196+
1197+ if (priv->g_object_finalized) {
1198+ g_critical("Object %s.%s (%p), has been already deallocated - "
1199+ "impossible to access it. This might be caused by the "
1200+ "object having been destroyed from C code using something "
1201+ "such as destroy(), dispose(), or remove() vfuncs",
1202+ priv->info ? g_base_info_get_namespace(priv->info) : "",
1203+ priv->info ? g_base_info_get_name(priv->info) : g_type_name(priv->gtype),
1204+ priv->gobj);
1205+ gjs_dumpstack();
1206+ return nullptr;
1207+ }
1208+
1209 return priv->gobj;
1210 }
1211
1212@@ -2188,19 +2334,7 @@ gjs_typecheck_object(JSContext *context,
1213 return false;
1214 }
1215
1216- if (priv->g_object_finalized) {
1217- g_critical("Object %s.%s (%p), has been already deallocated - impossible to access to it. "
1218- "This might be caused by the fact that the object has been destroyed from C "
1219- "code using something such as destroy(), dispose(), or remove() vfuncs",
1220- priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
1221- priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
1222- priv->gobj);
1223- gjs_dumpstack();
1224-
1225- return true;
1226- }
1227-
1228- g_assert(priv->gtype == G_OBJECT_TYPE(priv->gobj));
1229+ g_assert(priv->g_object_finalized || priv->gtype == G_OBJECT_TYPE(priv->gobj));
1230
1231 if (expected_type != G_TYPE_NONE)
1232 result = g_type_is_a (priv->gtype, expected_type);
1233@@ -2657,6 +2791,10 @@ gjs_object_custom_init(GTypeInstance *instance,
1234
1235 associate_js_gobject(context, object, G_OBJECT (instance));
1236
1237+ /* Custom JS objects will most likely have visible state, so
1238+ * just do this from the start */
1239+ ensure_uses_toggle_ref(context, priv);
1240+
1241 JS::RootedValue v(context);
1242 if (!gjs_object_get_property(context, object,
1243 GJS_STRING_INSTANCE_INIT, &v)) {
1244@@ -3109,6 +3247,9 @@ gjs_object_associate_closure(JSContext *cx,
1245 if (!priv)
1246 return false;
1247
1248+ if (priv->gobj)
1249+ ensure_uses_toggle_ref(cx, priv);
1250+
1251 do_associate_closure(priv, closure);
1252 return true;
1253 }
1254diff --git a/gi/object.h b/gi/object.h
1255index 63aeb37..1f1dce8 100644
1256--- a/gi/object.h
1257+++ b/gi/object.h
1258@@ -61,6 +61,8 @@ bool gjs_typecheck_is_object(JSContext *context,
1259 void gjs_object_prepare_shutdown(void);
1260 void gjs_object_clear_toggles(void);
1261 void gjs_object_shutdown_toggle_queue(void);
1262+void gjs_object_context_dispose_notify(void *data,
1263+ GObject *where_the_object_was);
1264
1265 void gjs_object_define_static_methods(JSContext *context,
1266 JS::HandleObject constructor,
1267diff --git a/gjs/context-private.h b/gjs/context-private.h
1268index 6dbe669..49c0cf9 100644
1269--- a/gjs/context-private.h
1270+++ b/gjs/context-private.h
1271@@ -36,6 +36,8 @@ bool _gjs_context_destroying (GjsContext *js_context);
1272
1273 void _gjs_context_schedule_gc_if_needed (GjsContext *js_context);
1274
1275+void _gjs_context_schedule_gc(GjsContext *js_context);
1276+
1277 void _gjs_context_exit(GjsContext *js_context,
1278 uint8_t exit_code);
1279
1280diff --git a/gjs/context.cpp b/gjs/context.cpp
1281index c509943..fc88741 100644
1282--- a/gjs/context.cpp
1283+++ b/gjs/context.cpp
1284@@ -90,6 +90,7 @@ struct _GjsContext {
1285 uint8_t exit_code;
1286
1287 guint auto_gc_id;
1288+ bool force_gc;
1289
1290 std::array<JS::PersistentRootedId*, GJS_STRING_LAST> const_strings;
1291
1292@@ -515,6 +516,8 @@ gjs_context_constructed(GObject *object)
1293 g_mutex_unlock (&contexts_lock);
1294
1295 setup_dump_heap();
1296+
1297+ g_object_weak_ref(object, gjs_object_context_dispose_notify, nullptr);
1298 }
1299
1300 static void
1301@@ -592,19 +595,43 @@ trigger_gc_if_needed (gpointer user_data)
1302 {
1303 GjsContext *js_context = GJS_CONTEXT(user_data);
1304 js_context->auto_gc_id = 0;
1305- gjs_gc_if_needed(js_context->context);
1306+
1307+ if (js_context->force_gc)
1308+ JS_GC(js_context->context);
1309+ else
1310+ gjs_gc_if_needed(js_context->context);
1311+
1312+ js_context->force_gc = false;
1313+
1314 return G_SOURCE_REMOVE;
1315 }
1316
1317-void
1318-_gjs_context_schedule_gc_if_needed (GjsContext *js_context)
1319+
1320+static void
1321+_gjs_context_schedule_gc_internal(GjsContext *js_context,
1322+ bool force_gc)
1323 {
1324+ js_context->force_gc |= force_gc;
1325+
1326 if (js_context->auto_gc_id > 0)
1327 return;
1328
1329- js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW,
1330- trigger_gc_if_needed,
1331- js_context, NULL);
1332+ js_context->force_gc |= force_gc;
1333+ js_context->auto_gc_id = g_timeout_add_seconds_full(G_PRIORITY_LOW, 10,
1334+ trigger_gc_if_needed,
1335+ js_context, NULL);
1336+}
1337+
1338+void
1339+_gjs_context_schedule_gc(GjsContext *js_context)
1340+{
1341+ _gjs_context_schedule_gc_internal(js_context, true);
1342+}
1343+
1344+void
1345+_gjs_context_schedule_gc_if_needed(GjsContext *js_context)
1346+{
1347+ _gjs_context_schedule_gc_internal(js_context, false);
1348 }
1349
1350 void
1351diff --git a/gjs/engine.cpp b/gjs/engine.cpp
1352index 90fa57c..720267d 100644
1353--- a/gjs/engine.cpp
1354+++ b/gjs/engine.cpp
1355@@ -37,10 +37,6 @@
1356 #include <windows.h>
1357 #endif
1358
1359-#ifdef ENABLE_CAIRO
1360-# include <cairo.h>
1361-#endif
1362-
1363 /* Implementations of locale-specific operations; these are used
1364 * in the implementation of String.localeCompare(), Date.toLocaleDateString(),
1365 * and so forth. We take the straight-forward approach of converting
1366@@ -218,16 +214,6 @@ on_promise_unhandled_rejection(JSContext *cx,
1367 std::move(stack));
1368 }
1369
1370-static void
1371-shutdown(void)
1372-{
1373- JS_ShutDown();
1374-
1375-#ifdef ENABLE_CAIRO
1376- cairo_debug_reset_static_data(); /* for valgrind reports */
1377-#endif
1378-}
1379-
1380 #ifdef G_OS_WIN32
1381 HMODULE gjs_dll;
1382 static bool gjs_is_inited = false;
1383@@ -245,7 +231,7 @@ LPVOID lpvReserved)
1384 break;
1385
1386 case DLL_THREAD_DETACH:
1387- shutdown();
1388+ JS_ShutDown ();
1389 break;
1390
1391 default:
1392@@ -265,7 +251,7 @@ public:
1393 }
1394
1395 ~GjsInit() {
1396- shutdown();
1397+ JS_ShutDown();
1398 }
1399
1400 operator bool() {
1401@@ -322,13 +308,14 @@ gjs_create_js_context(GjsContext *js_context)
1402 JS::ContextOptionsRef(cx).setExtraWarnings(true);
1403 }
1404
1405- if (!g_getenv("GJS_DISABLE_JIT")) {
1406+ bool enable_jit = !(g_getenv("GJS_DISABLE_JIT"));
1407+ if (enable_jit) {
1408 gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT");
1409- JS::ContextOptionsRef(cx)
1410- .setIon(true)
1411- .setBaseline(true)
1412- .setAsmJS(true);
1413 }
1414+ JS::ContextOptionsRef(cx)
1415+ .setIon(enable_jit)
1416+ .setBaseline(enable_jit)
1417+ .setAsmJS(enable_jit);
1418
1419 return cx;
1420 }
1421diff --git a/gjs/importer.cpp b/gjs/importer.cpp
1422index b4ea3c8..4c42a84 100644
1423--- a/gjs/importer.cpp
1424+++ b/gjs/importer.cpp
1425@@ -706,7 +706,7 @@ importer_enumerate(JSContext *context,
1426 /* new_for_commandline_arg handles resource:/// paths */
1427 GjsAutoUnref<GFile> dir = g_file_new_for_commandline_arg(dirname);
1428 GjsAutoUnref<GFileEnumerator> direnum =
1429- g_file_enumerate_children(dir, G_FILE_ATTRIBUTE_STANDARD_TYPE,
1430+ g_file_enumerate_children(dir, "standard::name,standard::type",
1431 G_FILE_QUERY_INFO_NONE, NULL, NULL);
1432
1433 while (true) {
1434diff --git a/gjs/jsapi-util-root.h b/gjs/jsapi-util-root.h
1435index 5baed48..d64eccb 100644
1436--- a/gjs/jsapi-util-root.h
1437+++ b/gjs/jsapi-util-root.h
1438@@ -219,6 +219,7 @@ public:
1439 return m_root->get() == nullptr;
1440 return m_heap.unbarrieredGet() == nullptr;
1441 }
1442+ inline bool operator!=(std::nullptr_t) const { return !(*this == nullptr); }
1443
1444 /* You can get a Handle<T> if the thing is rooted, so that you can use this
1445 * wrapper with stack rooting. However, you must not do this if the
1446@@ -247,10 +248,12 @@ public:
1447 m_data = data;
1448 m_root = new JS::PersistentRooted<T>(m_cx, thing);
1449
1450- auto gjs_cx = static_cast<GjsContext *>(JS_GetContextPrivate(m_cx));
1451- g_assert(GJS_IS_CONTEXT(gjs_cx));
1452- g_object_weak_ref(G_OBJECT(gjs_cx), on_context_destroy, this);
1453- m_has_weakref = true;
1454+ if (notify) {
1455+ auto gjs_cx = static_cast<GjsContext *>(JS_GetContextPrivate(m_cx));
1456+ g_assert(GJS_IS_CONTEXT(gjs_cx));
1457+ g_object_weak_ref(G_OBJECT(gjs_cx), on_context_destroy, this);
1458+ m_has_weakref = true;
1459+ }
1460 }
1461
1462 /* You can only assign directly to the GjsMaybeOwned wrapper in the
1463diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
1464index 322a41b..ed3e649 100644
1465--- a/gjs/jsapi-util.cpp
1466+++ b/gjs/jsapi-util.cpp
1467@@ -26,6 +26,8 @@
1468
1469 #include <codecvt>
1470 #include <locale>
1471+#include "jsapi-wrapper.h"
1472+#include <js/GCAPI.h>
1473
1474 #include <util/log.h>
1475 #include <util/glib.h>
1476@@ -34,7 +36,6 @@
1477
1478 #include "jsapi-class.h"
1479 #include "jsapi-util.h"
1480-#include "jsapi-wrapper.h"
1481 #include "context-private.h"
1482 #include <gi/boxed.h>
1483
1484@@ -313,8 +314,9 @@ gjs_build_string_array(JSContext *context,
1485 g_error("Unable to reserve memory for vector");
1486
1487 for (i = 0; i < array_length; ++i) {
1488+ JS::ConstUTF8CharsZ chars(array_values[i], strlen(array_values[i]));
1489 JS::RootedValue element(context,
1490- JS::StringValue(JS_NewStringCopyZ(context, array_values[i])));
1491+ JS::StringValue(JS_NewStringCopyUTF8Z(context, chars)));
1492 if (!elems.append(element))
1493 g_error("Unable to append to vector");
1494 }
1495@@ -731,7 +733,7 @@ gjs_gc_if_needed (JSContext *context)
1496 */
1497 if (rss_size > linux_rss_trigger) {
1498 linux_rss_trigger = (gulong) MIN(G_MAXULONG, rss_size * 1.25);
1499- JS_GC(context);
1500+ JS::GCForReason(context, GC_SHRINK, JS::gcreason::Reason::API);
1501 } else if (rss_size < (0.75 * linux_rss_trigger)) {
1502 /* If we've shrunk by 75%, lower the trigger */
1503 linux_rss_trigger = (rss_size * 1.25);
1504diff --git a/installed-tests/extra/gjs.supp b/installed-tests/extra/gjs.supp
1505index a768e27..b99eb25 100644
1506--- a/installed-tests/extra/gjs.supp
1507+++ b/installed-tests/extra/gjs.supp
1508@@ -1,35 +1,6 @@
1509 # Valgrind suppressions file for GJS
1510 # This is intended to be used in addition to GLib's glib.supp file.
1511
1512-# We leak a small wrapper in GJS for each registered GType.
1513-
1514-{
1515- gtype-wrapper-new
1516- Memcheck:Leak
1517- match-leak-kinds: definite
1518- fun:_Znwm
1519- fun:gjs_gtype_create_gtype_wrapper
1520-}
1521-
1522-{
1523- gtype-wrapper-qdata
1524- Memcheck:Leak
1525- match-leak-kinds: possible
1526- ...
1527- fun:type_set_qdata_W
1528- fun:g_type_set_qdata
1529- fun:gjs_gtype_create_gtype_wrapper
1530-}
1531-
1532-{
1533- g_type_register_fundamental never freed
1534- Memcheck:Leak
1535- fun:calloc
1536- ...
1537- fun:g_type_register_fundamental
1538- ...
1539-}
1540-
1541 # SpiderMonkey leaks
1542
1543 {
1544@@ -143,6 +114,29 @@
1545 fun:cairo_show_text
1546 }
1547
1548+# Data that Cairo keeps around for the process lifetime
1549+# This could be freed by calling cairo_debug_reset_static_data(), but it's
1550+# not a good idea to call that function in production, because certain versions
1551+# of Cairo have bugs that cause it to fail assertions and crash.
1552+{
1553+ cairo-static-data
1554+ Memcheck:Leak
1555+ match-leak-kinds: definite
1556+ fun:malloc
1557+ ...
1558+ fun:FcPatternDuplicate
1559+ fun:_cairo_ft_font_face_create_for_pattern
1560+ fun:_cairo_ft_font_face_create_for_toy
1561+ fun:_cairo_toy_font_face_create_impl_face
1562+ fun:_cairo_toy_font_face_init
1563+ fun:cairo_toy_font_face_create
1564+ fun:_cairo_gstate_ensure_font_face
1565+ fun:_cairo_gstate_ensure_scaled_font
1566+ fun:_cairo_gstate_get_scaled_font
1567+ fun:_cairo_default_context_get_scaled_font
1568+ fun:cairo_show_text
1569+}
1570+
1571 # SpiderMonkey data races
1572
1573 # These are in SpiderMonkey's atomics / thread barrier stuff so presumably
1574diff --git a/installed-tests/extra/lsan.supp b/installed-tests/extra/lsan.supp
1575index 179eb9c..3c69851 100644
1576--- a/installed-tests/extra/lsan.supp
1577+++ b/installed-tests/extra/lsan.supp
1578@@ -1,8 +1,5 @@
1579 # SpiderMonkey leaks a mutex for each GC helper thread.
1580 leak:js::HelperThread::threadLoop
1581
1582-# We leak a small wrapper in GJS for each registered GType.
1583-leak:gjs_gtype_create_gtype_wrapper
1584-
1585 # https://bugs.freedesktop.org/show_bug.cgi?id=105466
1586 leak:libfontconfig.so.1
1587diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
1588index 741a9f3..bf1ae97 100644
1589--- a/modules/overrides/GObject.js
1590+++ b/modules/overrides/GObject.js
1591@@ -441,6 +441,14 @@ function _init() {
1592 GObject._children = _children;
1593 GObject._internalChildren = _internalChildren;
1594
1595+ // Expose GObject static properties for ES6 classes
1596+
1597+ GObject.GTypeName = GTypeName;
1598+ GObject.requires = requires;
1599+ GObject.interfaces = interfaces;
1600+ GObject.properties = properties;
1601+ GObject.signals = signals;
1602+
1603 // fake enum for signal accumulators, keep in sync with gi/object.c
1604 this.AccumulatorType = {
1605 NONE: 0,
1606diff --git a/modules/tweener/tweener.js b/modules/tweener/tweener.js
1607index 98bde02..61c6654 100644
1608--- a/modules/tweener/tweener.js
1609+++ b/modules/tweener/tweener.js
1610@@ -368,7 +368,7 @@ function _onEnterFrame() {
1611 return true;
1612 }
1613
1614-const restrictedWords = {
1615+var restrictedWords = {
1616 time: true,
1617 delay: true,
1618 userFrames: true,
1619diff --git a/win32/config.h.win32 b/win32/config.h.win32
1620index 7495f0a..1e78fa6 100644
1621--- a/win32/config.h.win32
1622+++ b/win32/config.h.win32
1623@@ -13,7 +13,7 @@
1624 #define GETTEXT_PACKAGE "gjs"
1625
1626 /* The gjs version as an integer */
1627-#define GJS_VERSION 15201
1628+#define GJS_VERSION 15205
1629
1630 /* define if the compiler supports basic C++11 syntax */
1631 #define HAVE_CXX11 1
1632@@ -74,7 +74,7 @@
1633 #define PACKAGE_NAME "gjs"
1634
1635 /* Define to the full name and version of this package. */
1636-#define PACKAGE_STRING "gjs 1.52.1"
1637+#define PACKAGE_STRING "gjs 1.52.5"
1638
1639 /* Define to the one symbol short name of this package. */
1640 #define PACKAGE_TARNAME "gjs"
1641@@ -83,10 +83,10 @@
1642 #define PACKAGE_URL ""
1643
1644 /* Define to the version of this package. */
1645-#define PACKAGE_VERSION "1.52.1"
1646+#define PACKAGE_VERSION "1.52.5"
1647
1648 /* Define to 1 if you have the ANSI C header files. */
1649 #define STDC_HEADERS 1
1650
1651 /* Version number of package */
1652-#define VERSION "1.52.1"
1653+#define VERSION "1.52.5"

Subscribers

People subscribed via source and target branches