[gjs: 1/3] objectbox: Support native JSObject GType for signal parameters and properties




commit ac2e87c743207b8098b6a823f7bcf5dd18944ecf
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Wed Jun 5 22:08:10 2019 -0500

    objectbox: Support native JSObject GType for signal parameters and properties
    
    Define a new boxed 'JSObject' GType and use it to wrap native JSObjects.
    
    To do this, create an ObjectBox class that automatically roots and unroots
    a JSObject persistently.
    This class uses a private constructor and a pimpl idiom to force the
    allocation of the wrapper in the stack, that is doable using a 'boxed'
    static method that returns a unique_ptr.
    Internally the boxed wrapper uses a ref-counting system that allows us to
    avoid copies and re-rooting.
    
    Define GObject.TYPE_JSOBJECT as 'JSObject' GType and add an alias for the
    JS Objects using the $gtype property.
    
    Add a ParamSpec jsobject alias.

 gi/value.cpp                           |  15 +-
 gjs/context.cpp                        |   4 +
 gjs/objectbox.cpp                      |  89 ++++++++++
 gjs/objectbox.h                        |  31 ++++
 installed-tests/js/testGObjectClass.js | 316 +++++++++++++++++++++++++++++++++
 installed-tests/js/testParamSpec.js    |   1 +
 meson.build                            |   1 +
 modules/core/overrides/GObject.js      |   8 +
 tools/run_iwyu.sh                      |   8 +-
 9 files changed, 468 insertions(+), 5 deletions(-)
---
diff --git a/gi/value.cpp b/gi/value.cpp
index 7aabaa5a..c9bb7f25 100644
--- a/gi/value.cpp
+++ b/gi/value.cpp
@@ -40,6 +40,7 @@
 #include "gjs/context-private.h"
 #include "gjs/context.h"
 #include "gjs/jsapi-util.h"
+#include "gjs/objectbox.h"
 #include "util/log.h"
 
 GJS_JSAPI_RETURN_CONVENTION
@@ -536,7 +537,10 @@ gjs_value_to_g_value_internal(JSContext      *context,
         if (value.isObject()) {
             JS::RootedObject obj(context, &value.toObject());
 
-            if (g_type_is_a(gtype, G_TYPE_ERROR)) {
+            if (g_type_is_a(gtype, ObjectBox::gtype())) {
+                g_value_set_boxed(gvalue, ObjectBox::boxed(context, obj).get());
+                return true;
+            } else if (g_type_is_a(gtype, G_TYPE_ERROR)) {
                 /* special case GError */
                 gboxed = ErrorBase::to_c_ptr(context, obj);
                 if (!gboxed)
@@ -852,6 +856,15 @@ gjs_value_from_g_value_internal(JSContext             *context,
             return true;
         }
 
+        if (g_type_is_a(gtype, ObjectBox::gtype())) {
+            obj = ObjectBox::object_for_c_ptr(context,
+                                              static_cast<ObjectBox*>(gboxed));
+            if (!obj)
+                return false;
+            value_p.setObject(*obj);
+            return true;
+        }
+
         /* special case GError */
         if (g_type_is_a(gtype, G_TYPE_ERROR)) {
             obj = ErrorInstance::object_for_c_ptr(context,
diff --git a/gjs/context.cpp b/gjs/context.cpp
index c3810d97..4d8e7144 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -72,6 +72,7 @@
 #include "gjs/mem.h"
 #include "gjs/module.h"
 #include "gjs/native.h"
+#include "gjs/objectbox.h"
 #include "gjs/profiler-private.h"
 #include "gjs/profiler.h"
 #include "modules/modules.h"
@@ -485,6 +486,9 @@ GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
 
     m_atoms = new GjsAtoms();
 
+    if (ObjectBox::gtype() == 0)
+        g_error("Failed to initialize JSObject GType");
+
     JS::RootedObject internal_global(
         m_cx, gjs_create_global_object(cx, GjsGlobalType::INTERNAL));
 
diff --git a/gjs/objectbox.cpp b/gjs/objectbox.cpp
new file mode 100644
index 00000000..3bbef096
--- /dev/null
+++ b/gjs/objectbox.cpp
@@ -0,0 +1,89 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2019 Marco Trevisan <marco trevisan canonical com>
+
+#include <config.h>
+
+#include <stddef.h>  // for size_t
+
+#include <glib.h>
+
+#include <js/ComparisonOperators.h>
+#include <js/RootingAPI.h>
+#include <js/TypeDecls.h>
+
+#include "gjs/jsapi-util.h"
+#include "gjs/objectbox.h"
+#include "util/log.h"
+
+/* gjs/objectbox.cpp - GObject boxed type used to "box" a JS object so that it
+ * can be passed to or returned from a GObject signal, or used as the type of a
+ * GObject property.
+ */
+
+struct ObjectBox::impl {
+    impl(ObjectBox* parent, JSContext* cx, JSObject* obj)
+        : m_parent(parent), m_root(cx, obj) {
+        g_atomic_ref_count_init(&m_refcount);
+    }
+
+    void ref() {
+        debug("incref");
+        g_atomic_ref_count_inc(&m_refcount);
+    }
+
+    void unref() {
+        debug("decref");
+        if (g_atomic_ref_count_dec(&m_refcount))
+            delete m_parent;
+    }
+
+    void debug(const char* what GJS_USED_VERBOSE_LIFECYCLE) {
+        gjs_debug_lifecycle(GJS_DEBUG_GBOXED,
+                            "%s: ObjectBox %p, JSObject %s", what, m_parent,
+                            gjs_debug_object(m_root).c_str());
+    }
+
+    ObjectBox* m_parent;
+    JS::PersistentRooted<JSObject*> m_root;
+    gatomicrefcount m_refcount;
+};
+
+ObjectBox::ObjectBox(JSContext* cx, JSObject* obj)
+    : m_impl(std::make_unique<ObjectBox::impl>(this, cx, obj)) {}
+
+ObjectBox::Ptr ObjectBox::boxed(JSContext* cx, JSObject* obj) {
+    return ObjectBox::Ptr(new ObjectBox(cx, obj),
+                          [](ObjectBox* box) { box->m_impl->unref(); });
+}
+
+JSObject* ObjectBox::object_for_c_ptr(JSContext* cx, ObjectBox* box) {
+    if (!box) {
+        gjs_throw(cx, "Cannot get JSObject for null ObjectBox pointer");
+        return nullptr;
+    }
+
+    box->m_impl->debug("retrieved JSObject");
+    return box->m_impl->m_root.get();
+}
+
+GType ObjectBox::gtype() {
+    static volatile size_t type_id = 0;
+
+    if (g_once_init_enter(&type_id)) {
+        auto objectbox_copy = [](void* boxed) -> void* {
+            auto* box = static_cast<ObjectBox*>(boxed);
+            box->m_impl->ref();
+            return box;
+        };
+        auto objectbox_free = [](void* boxed) {
+            auto* box = static_cast<ObjectBox*>(boxed);
+            box->m_impl->unref();
+        };
+        GType type = g_boxed_type_register_static("JSObject", objectbox_copy,
+                                                  objectbox_free);
+        g_once_init_leave(&type_id, type);
+    }
+
+    return type_id;
+}
diff --git a/gjs/objectbox.h b/gjs/objectbox.h
new file mode 100644
index 00000000..62179fc2
--- /dev/null
+++ b/gjs/objectbox.h
@@ -0,0 +1,31 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2019 Marco Trevisan <marco trevisan canonical com>
+
+#pragma once
+
+#include <config.h>
+
+#include <memory>
+
+#include <glib-object.h>
+
+#include <js/TypeDecls.h>
+
+#include "gjs/macros.h"
+
+struct ObjectBox {
+    using Ptr = std::unique_ptr<ObjectBox, void (*)(ObjectBox*)>;
+
+    [[nodiscard]] static GType gtype();
+    [[nodiscard]] static ObjectBox::Ptr boxed(JSContext*, JSObject*);
+
+    GJS_JSAPI_RETURN_CONVENTION
+    static JSObject* object_for_c_ptr(JSContext*, ObjectBox*);
+
+ private:
+    ObjectBox(JSContext*, JSObject*);
+
+    struct impl;
+    std::unique_ptr<impl> m_impl;
+};
diff --git a/installed-tests/js/testGObjectClass.js b/installed-tests/js/testGObjectClass.js
index efd9bcfb..dd0f2dca 100644
--- a/installed-tests/js/testGObjectClass.js
+++ b/installed-tests/js/testGObjectClass.js
@@ -2,6 +2,8 @@
 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
 // SPDX-FileCopyrightText: 2011 Giovanni Campagna <gcampagna src gnome org>
 
+const System = imports.system;
+
 imports.gi.versions.Gtk = '3.0';
 
 const Gio = imports.gi.Gio;
@@ -888,3 +890,317 @@ describe('Auto accessor generation', function () {
         expect(() => (a.missingSetter = 1)).toThrowError(/setter/);
     });
 });
+
+const MyObjectWithJSObjectProperty = GObject.registerClass({
+    Properties: {
+        'jsobj-prop': GObject.ParamSpec.jsobject('jsobj-prop', 'jsobj-prop', 'jsobj-prop',
+            GObject.ParamFlags.CONSTRUCT | GObject.ParamFlags.READWRITE, ''),
+    },
+}, class MyObjectWithJSObjectProperty extends GObject.Object {
+});
+
+describe('GObject class with JSObject property', function () {
+    it('assigns a valid JSObject on construct', function () {
+        let date = new Date();
+        let obj = new MyObjectWithJSObjectProperty({jsobj_prop: date});
+        expect(obj.jsobj_prop).toEqual(date);
+        expect(obj.jsobj_prop).not.toEqual(new Date(0));
+        expect(() => obj.jsobj_prop.setFullYear(1985)).not.toThrow();
+        expect(obj.jsobj_prop.getFullYear()).toEqual(1985);
+    });
+
+    it('Set null with an empty JSObject on construct', function () {
+        expect(new MyObjectWithJSObjectProperty().jsobj_prop).toBeNull();
+        expect(new MyObjectWithJSObjectProperty({}).jsobj_prop).toBeNull();
+    });
+
+    it('assigns a null JSObject on construct', function () {
+        expect(new MyObjectWithJSObjectProperty({jsobj_prop: null}).jsobj_prop)
+            .toBeNull();
+    });
+
+    it('assigns a JSObject Array on construct', function () {
+        expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: [1, 2, 3]}))
+            .not.toThrow();
+    });
+
+    it('assigns a Function on construct', function () {
+        expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: () => {
+            return true;
+        }})).not.toThrow();
+    });
+
+    it('throws an error when using a boolean value on construct', function () {
+        expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: true}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('throws an error when using an int value on construct', function () {
+        expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: 1}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('throws an error when using a numeric value on construct', function () {
+        expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: Math.PI}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('throws an error when using a string value on construct', function () {
+        expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: 'string'}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('throws an error when using an undefined value on construct', function () {
+        expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: undefined})).toThrow();
+    });
+
+    it('property value survives when GObject wrapper is collected', function () {
+        const MyConverter = GObject.registerClass({
+            Properties: {
+                testprop: GObject.ParamSpec.jsobject('testprop', 'testprop', 'Test property',
+                    GObject.ParamFlags.CONSTRUCT | GObject.ParamFlags.READWRITE),
+            },
+            Implements: [Gio.Converter],
+        }, class MyConverter extends GObject.Object {});
+
+        function stashObject() {
+            const base = new Gio.MemoryInputStream();
+            const converter = new MyConverter({testprop: [1, 2, 3]});
+            return Gio.ConverterInputStream.new(base, converter);
+        }
+
+        const stream = stashObject();
+        System.gc();
+        expect(stream.get_converter().testprop).toEqual([1, 2, 3]);
+    });
+});
+
+const MyObjectWithJSObjectSignals = GObject.registerClass({
+    Signals: {
+        'send-object': {param_types: [GObject.TYPE_JSOBJECT]},
+        'send-many-objects': {
+            param_types: [GObject.TYPE_JSOBJECT,
+                GObject.TYPE_JSOBJECT,
+                GObject.TYPE_JSOBJECT],
+        },
+        'get-object': {
+            flags: GObject.SignalFlags.RUN_LAST,
+            accumulator: GObject.AccumulatorType.FIRST_WINS,
+            return_type: GObject.TYPE_JSOBJECT,
+            param_types: [GObject.TYPE_JSOBJECT],
+        },
+    },
+}, class MyObjectWithJSObjectSignals extends GObject.Object {
+    emitObject(obj) {
+        this.emit('send-object', obj);
+    }
+});
+
+describe('GObject class with JSObject signals', function () {
+    let myInstance;
+    beforeEach(function () {
+        myInstance = new MyObjectWithJSObjectSignals();
+    });
+
+    it('emits signal with null JSObject parameter', function () {
+        let customSpy = jasmine.createSpy('sendObjectSpy');
+        myInstance.connect('send-object', customSpy);
+        myInstance.emitObject(null);
+        expect(customSpy).toHaveBeenCalledWith(myInstance, null);
+    });
+
+    it('emits signal with JSObject parameter', function () {
+        let customSpy = jasmine.createSpy('sendObjectSpy');
+        myInstance.connect('send-object', customSpy);
+
+        let obj = {
+            foo: [1, 2, 3],
+            sub: {a: {}, 'b': this},
+            desc: 'test',
+            date: new Date(),
+        };
+        myInstance.emitObject(obj);
+        expect(customSpy).toHaveBeenCalledWith(myInstance, obj);
+    });
+
+    it('emits signal with multiple JSObject parameters', function () {
+        let customSpy = jasmine.createSpy('sendManyObjectsSpy');
+        myInstance.connect('send-many-objects', customSpy);
+
+        let obj = {
+            foo: [9, 8, 7, 'a', 'b', 'c'],
+            sub: {a: {}, 'b': this},
+            desc: 'test',
+            date: new RegExp('\\w+'),
+        };
+        myInstance.emit('send-many-objects', obj, obj.foo, obj.sub);
+        expect(customSpy).toHaveBeenCalledWith(myInstance, obj, obj.foo, obj.sub);
+    });
+
+    it('re-emits signal with same JSObject parameter', function () {
+        let obj = {
+            foo: [9, 8, 7, 'a', 'b', 'c'],
+            sub: {a: {}, 'b': this},
+            func: arg => {
+                return {ret: [arg]};
+            },
+        };
+
+        myInstance.connect('send-many-objects', (instance, func, args, foo) => {
+            expect(instance).toEqual(myInstance);
+            expect(System.addressOf(instance)).toEqual(System.addressOf(myInstance));
+            expect(foo).toEqual(obj.foo);
+            expect(System.addressOf(foo)).toEqual(System.addressOf(obj.foo));
+            expect(func(args).ret[0]).toEqual(args);
+        });
+        myInstance.connect('send-object', (instance, param) => {
+            expect(instance).toEqual(myInstance);
+            expect(System.addressOf(instance)).toEqual(System.addressOf(myInstance));
+            expect(param).toEqual(obj);
+            expect(System.addressOf(param)).toEqual(System.addressOf(obj));
+            expect(() => instance.emit('send-many-objects', param.func, param, param.foo))
+                .not.toThrow();
+        });
+
+        myInstance.emit('send-object', obj);
+    });
+
+    it('throws an error when using a boolean value as parameter', function () {
+        expect(() => myInstance.emit('send-object', true))
+            .toThrowError(/JSObject expected/);
+        expect(() => myInstance.emit('send-many-objects', ['a'], true, {}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('throws an error when using an int value as parameter', function () {
+        expect(() => myInstance.emit('send-object', 1))
+            .toThrowError(/JSObject expected/);
+        expect(() => myInstance.emit('send-many-objects', ['a'], 1, {}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('throws an error when using a numeric value as parameter', function () {
+        expect(() => myInstance.emit('send-object', Math.PI))
+            .toThrowError(/JSObject expected/);
+        expect(() => myInstance.emit('send-many-objects', ['a'], Math.PI, {}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('throws an error when using a string value as parameter', function () {
+        expect(() => myInstance.emit('send-object', 'string'))
+            .toThrowError(/JSObject expected/);
+        expect(() => myInstance.emit('send-many-objects', ['a'], 'string', {}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('throws an error when using an undefined value as parameter', function () {
+        expect(() => myInstance.emit('send-object', undefined))
+            .toThrowError(/JSObject expected/);
+        expect(() => myInstance.emit('send-many-objects', ['a'], undefined, {}))
+            .toThrowError(/JSObject expected/);
+    });
+
+    it('returns a JSObject', function () {
+        let data = {
+            foo: [9, 8, 7, 'a', 'b', 'c'],
+            sub: {a: {}, 'b': this},
+            func: arg => {
+                return {ret: [arg]};
+            },
+        };
+        let id = myInstance.connect('get-object', () => {
+            return data;
+        });
+        expect(myInstance.emit('get-object', {})).toBe(data);
+        myInstance.disconnect(id);
+
+        myInstance.connect('get-object', (instance, input) => {
+            if (input) {
+                if (typeof input === 'function')
+                    input();
+                return input;
+            }
+
+            class SubObject {
+                constructor() {
+                    this.pi = Math.PI;
+                }
+
+                method() {}
+
+                gobject() {
+                    return GObject.Object;
+                }
+
+                get data() {
+                    return data;
+                }
+            }
+
+            return new SubObject();
+        });
+
+        expect(myInstance.emit('get-object', null).constructor.name).toBe('SubObject');
+        expect(myInstance.emit('get-object', null).data).toBe(data);
+        expect(myInstance.emit('get-object', null).pi).toBe(Math.PI);
+        expect(() => myInstance.emit('get-object', null).method()).not.toThrow();
+        expect(myInstance.emit('get-object', null).gobject()).toBe(GObject.Object);
+        expect(new (myInstance.emit('get-object', null).gobject())() instanceof GObject.Object)
+            .toBeTruthy();
+        expect(myInstance.emit('get-object', data)).toBe(data);
+        expect(myInstance.emit('get-object', jasmine.createSpy('callMeSpy')))
+            .toHaveBeenCalled();
+    });
+
+    it('returns null when returning undefined', function () {
+        myInstance.connect('get-object', () => {
+            return undefined;
+        });
+        expect(myInstance.emit('get-object', {})).toBeNull();
+    });
+
+    it('returns null when not returning', function () {
+        myInstance.connect('get-object', () => { });
+        expect(myInstance.emit('get-object', {})).toBeNull();
+    });
+
+    // These tests are intended to throw an error, but currently errors cannot
+    // be caught from signal handlers, so we check for logged messages instead
+
+    it('throws an error when returning a boolean value', function () {
+        GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_WARNING,
+            '*JSObject expected*');
+        myInstance.connect('get-object', () => true);
+        myInstance.emit('get-object', {});
+        GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectClass.js', 0,
+            'throws an error when returning a boolean value');
+    });
+
+    it('throws an error when returning an int value', function () {
+        GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_WARNING,
+            '*JSObject expected*');
+        myInstance.connect('get-object', () => 1);
+        myInstance.emit('get-object', {});
+        GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectClass.js', 0,
+            'throws an error when returning a boolean value');
+    });
+
+    it('throws an error when returning a numeric value', function () {
+        GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_WARNING,
+            '*JSObject expected*');
+        myInstance.connect('get-object', () => Math.PI);
+        myInstance.emit('get-object', {});
+        GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectClass.js', 0,
+            'throws an error when returning a boolean value');
+    });
+
+    it('throws an error when returning a string value', function () {
+        GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_WARNING,
+            '*JSObject expected*');
+        myInstance.connect('get-object', () => 'string');
+        myInstance.emit('get-object', {});
+        GLib.test_assert_expected_messages_internal('Gjs', 'testGObjectClass.js', 0,
+            'throws an error when returning a boolean value');
+    });
+});
+
diff --git a/installed-tests/js/testParamSpec.js b/installed-tests/js/testParamSpec.js
index a3c45518..be64a34a 100644
--- a/installed-tests/js/testParamSpec.js
+++ b/installed-tests/js/testParamSpec.js
@@ -42,6 +42,7 @@ testParamSpec('enum', [Regress.TestEnum, Regress.TestEnum.VALUE2],
 testParamSpec('flags', [Regress.TestFlags, Regress.TestFlags.FLAG2],
     Regress.TestFlags.FLAG2);
 testParamSpec('object', [GObject.Object], null);
+testParamSpec('jsobject', [], null);
 
 describe('GObject.ParamSpec object', function () {
     it("doesn't crash when resolving a non-string property", function () {
diff --git a/meson.build b/meson.build
index 27a6cb9b..23e6ae76 100644
--- a/meson.build
+++ b/meson.build
@@ -406,6 +406,7 @@ libgjs_sources = [
     'gjs/mem.cpp', 'gjs/mem-private.h',
     'gjs/module.cpp', 'gjs/module.h',
     'gjs/native.cpp', 'gjs/native.h',
+    'gjs/objectbox.cpp', 'gjs/objectbox.h',
     'gjs/profiler.cpp', 'gjs/profiler-private.h',
     'gjs/stack.cpp',
     'modules/console.cpp', 'modules/console.h',
diff --git a/modules/core/overrides/GObject.js b/modules/core/overrides/GObject.js
index 5210a9e3..640a4fc6 100644
--- a/modules/core/overrides/GObject.js
+++ b/modules/core/overrides/GObject.js
@@ -261,6 +261,10 @@ function _init() {
     GObject.String = String;
     String.$gtype = GObject.TYPE_STRING;
 
+    GObject.TYPE_JSOBJECT = GObject.type_from_name('JSObject');
+    GObject.JSObject = Object;
+    Object.$gtype = GObject.TYPE_JSOBJECT;
+
     GObject.TYPE_POINTER = GObject.type_from_name('gpointer');
     GObject.TYPE_BOXED = GObject.type_from_name('GBoxed');
     GObject.TYPE_PARAM = GObject.type_from_name('GParam');
@@ -334,6 +338,10 @@ function _init() {
         return GObject.param_spec_object(name, nick, blurb, objectType, flags);
     };
 
+    GObject.ParamSpec.jsobject = function (name, nick, blurb, flags) {
+        return GObject.param_spec_boxed(name, nick, blurb, Object.$gtype, flags);
+    };
+
     GObject.ParamSpec.param = function (name, nick, blurb, flags, paramType) {
         return GObject.param_spec_param(name, nick, blurb, paramType, flags);
     };
diff --git a/tools/run_iwyu.sh b/tools/run_iwyu.sh
index 1efc5806..cae6fbeb 100755
--- a/tools/run_iwyu.sh
+++ b/tools/run_iwyu.sh
@@ -71,10 +71,10 @@ for FILE in $SRCDIR/gi/*.cpp $SRCDIR/gjs/atoms.cpp $SRCDIR/gjs/byteArray.cpp \
     $SRCDIR/gjs/deprecation.cpp $SRCDIR/gjs/error-types.cpp \
     $SRCDIR/gjs/engine.cpp $SRCDIR/gjs/global.cpp $SRCDIR/gjs/importer.cpp \
     $SRCDIR/gjs/jsapi-util*.cpp $SRCDIR/gjs/module.cpp $SRCDIR/gjs/native.cpp \
-    $SRCDIR/gjs/stack.cpp $SRCDIR/modules/cairo-*.cpp \
-    $SRCDIR/modules/console.cpp $SRCDIR/modules/print.cpp \
-    $SRCDIR/modules/system.cpp $SRCDIR/test/*.cpp $SRCDIR/util/*.cpp \
-    $SRCDIR/libgjs-private/*.c
+    $SRCDIR/gjs/objectbox.cpp $SRCDIR/gjs/stack.cpp \
+    $SRCDIR/modules/cairo-*.cpp $SRCDIR/modules/console.cpp \
+    $SRCDIR/modules/print.cpp $SRCDIR/modules/system.cpp $SRCDIR/test/*.cpp \
+    $SRCDIR/util/*.cpp $SRCDIR/libgjs-private/*.c
 do
     if should_analyze $FILE; then
         if ! $IWYU $FILE -- $PRIVATE_MAPPING | $POSTPROCESS; then


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]