Too much recursion error accessing overrided gobject interface property from a subclass
System information
I have been able to reproduce this issue in the org.gnome.Platform Flatpak runtime branches 3.30 to 3.34; gjs >= 1.54.3.
Bug information
Steps to reproduce
The test case I have arrived on here is slightly complex and there may be a simpler underlying problem.
The base class is a GObject class which implements an interface, such as Gtk.Orientable. It overrides a property from that interface, using GObject.ParamSpec.override
. (This problem does not occur with new properties).
The subclass simply extends the base class with no additional code. We assume that getters and setters from the base class will be available in the subclass. It is possible to interact with the property correctly with an instance of the base class, but with an instance of the subclass, the property exists but an error occurs accessing the getters and setters.
imports.gi.versions.Gtk = '3.0';
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const BaseContainer = GObject.registerClass({
Name: 'BaseContainer',
Implements: [Gtk.Orientable],
Properties: {
'orientation': GObject.ParamSpec.override(
'orientation',
Gtk.Orientable
),
'strprop': GObject.ParamSpec.string(
'strprop',
'Short description',
'Longer description',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
''
),
}
}, class BaseContainer extends Gtk.Container {
_init (props) {
this._strprop = '';
this._orientation = Gtk.Orientation.HORIZONTAL;
super._init(props);
}
get strprop() {
print("get strprop");
return this._strprop;
}
set strprop(value) {
if (this._strprop === value)
return;
this._strprop = value;
this.notify('strprop');
print("set strprop", value);
}
get orientation() {
print("get orientation");
return this._orientation;
}
set orientation(value) {
if (this._orientation === value)
return;
this._orientation = value;
this.notify('orientation');
print("set orientation", value);
}
});
const ContainerSubclass = GObject.registerClass({
Name: 'ContainerSubclass',
}, class ContainerSubclass extends BaseContainer {
});
Gtk.init(null);
const baseContainer = new BaseContainer();
const containerSubclass = new ContainerSubclass();
baseContainer.strprop = "hello";
containerSubclass.strprop = "hello";
baseContainer.orientation = Gtk.Orientation.VERTICAL;
baseContainer.get_orientation() == Gtk.Orientation.VERTICAL || print("FAIL");
containerSubclass.orientation = Gtk.Orientation.VERTICAL;
containerSubclass.get_orientation() == Gtk.Orientation.VERTICAL || print("FAIL");
Current behaviour
Running this code with gjs 1.58.5, I see the following output:
$ gjs propblem.js
set strprop hello
set strprop hello
set orientation 1
get orientation
(gjs:222160): Gjs-WARNING **: 15:21:03.584: JS ERROR: (null)
@propblem.js:89:1
(gjs:222160): Gjs-WARNING **: 15:21:03.584: JS ERROR: (null)
FAIL
(gjs:222160): Gjs-WARNING **: 15:21:03.586: JS::Evaluate() returned true but exception was pending; did somebody call gjs_throw() without returning false?
(gjs:222160): Gjs-WARNING **: 15:21:03.586: JS ERROR: InternalError: too much recursion
@propblem.js:90:1
Script propblem.js threw an exception
Running the same program in gdb with G_DEBUG=fatal-warnings provides an interesting backtrace:
$ env G_DEBUG=fatal-warnings gdb gjs
(gdb) run propblem.js
Starting program: /usr/bin/gjs propblem.js
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7ffff5741700 (LWP 222249)]
[Thread 0x7ffff5741700 (LWP 222249) exited]
[New Thread 0x7ffff5741700 (LWP 222250)]
[New Thread 0x7ffff4f40700 (LWP 222251)]
[New Thread 0x7ffff4d3f700 (LWP 222252)]
[New Thread 0x7ffff4b3e700 (LWP 222253)]
[New Thread 0x7ffff493d700 (LWP 222254)]
[New Thread 0x7ffff473c700 (LWP 222255)]
[New Thread 0x7ffff453b700 (LWP 222256)]
[New Thread 0x7ffff433a700 (LWP 222257)]
[New Thread 0x7fffe2e53700 (LWP 222258)]
[New Thread 0x7fffe2652700 (LWP 222259)]
[New Thread 0x7fffe0df7700 (LWP 222260)]
[New Thread 0x7fffbbfff700 (LWP 222261)]
set strprop hello
set strprop hello
set orientation 1
get orientation
(gjs:222248): Gjs-WARNING **: 15:22:06.170: JS ERROR: (null)
Thread 1 "gjs" received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff7b7f765 in _g_log_abort () from /lib64/libglib-2.0.so.0
(gdb) bt
#0 0x00007ffff7b7f765 in _g_log_abort () at /lib64/libglib-2.0.so.0
#1 0x00007ffff7b80a36 in g_logv () at /lib64/libglib-2.0.so.0
#2 0x00007ffff7b80c03 in g_log () at /lib64/libglib-2.0.so.0
#3 0x00007ffff7f1446d in gjs_log_exception_full(JSContext*, JS::Handle<JS::Value>, JS::Handle<JSString*>) () at /lib64/libgjs.so.0
#4 0x00007ffff7f14812 in gjs_log_exception(JSContext*) () at /lib64/libgjs.so.0
#5 0x00007ffff7ee47d9 in () at /lib64/libgjs.so.0
#6 0x00007ffff7c6b8fe in g_object_setv () at /lib64/libgobject-2.0.so.0
#7 0x00007ffff7c6c86f in g_object_set_property () at /lib64/libgobject-2.0.so.0
#8 0x00007ffff7eed9d6 in ObjectInstance::prop_setter_impl(JSContext*, JS::Handle<JSString*>, JS::Handle<JS::Value>) () at /lib64/libgjs.so.0
#9 0x00007ffff7eedb99 in ObjectBase::prop_setter(JSContext*, unsigned int, JS::Value*) () at /lib64/libgjs.so.0
#10 0x00007ffff6b106eb in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct) () at /lib64/libmozjs-60.so.0
#11 0x00007ffff6b10bed in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>) ()
at /lib64/libmozjs-60.so.0
#12 0x00007ffff6b11217 in js::CallSetter(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::Handle<JS::Value>) () at /lib64/libmozjs-60.so.0
#13 0x00007ffff669818a in SetExistingProperty(JSContext*, JS::Handle<js::NativeObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::Handle<js::NativeObject*>, JS::Handle<JS::PropertyResult>, JS::ObjectOpResult&) ()
at /lib64/libmozjs-60.so.0
--Type <RET> for more, q to quit, c to continue without paging--
JS::Handle<js::NativeObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::ObjectOpResult&) () at /lib64/libmozjs-60.so.0
#15 0x00007ffff67cccad in JS_SetPropertyById(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>) () at /lib64/libmozjs-60.so.0
#16 0x00007ffff67cced8 in JS_SetProperty(JSContext*, JS::Handle<JSObject*>, char const*, JS::Handle<JS::Value>) () at /lib64/libmozjs-60.so.0
#17 0x00007ffff7ee3ddd in () at /lib64/libgjs.so.0
#18 0x00007ffff7ee47cd in () at /lib64/libgjs.so.0
#19 0x00007ffff7c6b8fe in g_object_setv () at /lib64/libgobject-2.0.so.0
#20 0x00007ffff7c6c86f in g_object_set_property () at /lib64/libgobject-2.0.so.0
[…]
#7546 0x00007ffff7c6b8fe in g_object_setv () at /lib64/libgobject-2.0.so.0
#7547 0x00007ffff7c6c86f in g_object_set_property () at /lib64/libgobject-2.0.so.0
#7548 0x00007ffff7eed9d6 in ObjectInstance::prop_setter_impl(JSContext*, JS::Handle<JSString*>, JS::Handle<JS::Value>) () at /lib64/libgjs.so.0
#7549 0x00007ffff7eedb99 in ObjectBase::prop_setter(JSContext*, unsigned int, JS::Value*) () at /lib64/libgjs.so.0
#7550 0x00007ffff6b106eb in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct) () at /lib64/libmozjs-60.so.0
#7551 0x00007ffff6b10bed in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>) () at /lib64/libmozjs-60.so.0
#7552 0x00007ffff6b11217 in js::CallSetter(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::Handle<JS::Value>) () at /lib64/libmozjs-60.so.0
#7553 0x00007ffff669818a in SetExistingProperty(JSContext*, JS::Handle<js::NativeObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::Handle<js::NativeObject*>, JS::Handle<JS::PropertyResult>, JS::ObjectOpResult&) () at /lib64/libmozjs-60.so.0
#7554 0x00007ffff669c4a3 in bool js::NativeSetProperty<(js::QualifiedBool)1>(JSContext*, JS::Handle<js::NativeObject*>, JS::Handle<jsid>, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::ObjectOpResult&) () at /lib64/libmozjs-60.so.0
#7555 0x00007ffff6b1529c in Interpret(JSContext*, js::RunState&) [clone .lto_priv.0] () at /lib64/libmozjs-60.so.0
#7556 0x00007ffff6b36ad8 in js::RunScript(JSContext*, js::RunState&) () at /lib64/libmozjs-60.so.0
#7557 0x00007ffff6b11966 in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JSObject&, JS::Value const&, js::AbstractFramePtr, JS::Value*) () at /lib64/libmozjs-60.so.0
#7558 0x00007ffff6b11a94 in js::Execute(JSContext*, JS::Handle<JSScript*>, JSObject&, JS::Value*) () at /lib64/libmozjs-60.so.0
#7559 0x00007ffff67c990f in Evaluate(JSContext*, js::ScopeKind, JS::Handle<JSObject*>, JS::ReadOnlyCompileOptions const&, JS::SourceBufferHolder&, JS::MutableHandle<JS::Value>) ()
at /lib64/libmozjs-60.so.0
#7560 0x00007ffff67c9bbb in Evaluate(JSContext*, JS::AutoVector<JSObject*>&, JS::ReadOnlyCompileOptions const&, JS::SourceBufferHolder&, JS::MutableHandle<JS::Value>) ()
at /lib64/libmozjs-60.so.0
#7561 0x00007ffff7f07541 in GjsContextPrivate::eval_with_scope(JS::Handle<JSObject*>, char const*, long, char const*, JS::MutableHandle<JS::Value>) () at /lib64/libgjs.so.0
#7562 0x00007ffff7f078c7 in GjsContextPrivate::eval(char const*, long, char const*, int*, _GError**) () at /lib64/libgjs.so.0
#7563 0x00007ffff7f07bdd in gjs_context_eval () at /lib64/libgjs.so.0
#7564 0x0000555555556da9 in main ()
As noted in the example program, if the subclass (ContainerSubclass) includes its own getter and setter for the overrided property, the error does not occur.
Expected behaviour
Ideally, the property in the subclass should be set as usual without any errors. This same program runs as I would expect with gjs 1.52.2 in the org.gnome.Platform//3.28 Flatpak runtime, with the following output:
$ gjs propblem.js
set strprop hello
set strprop hello
set orientation 1
get orientation
set orientation 1
get orientation