Skip to content

Commit f4e46ac

Browse files
committed
looking-glass: better gi type inspection
looks up object keys through the GIRepository by GType.
1 parent f4b45ec commit f4e46ac

File tree

2 files changed

+104
-25
lines changed

2 files changed

+104
-25
lines changed

files/usr/share/cinnamon/cinnamon-looking-glass/page_inspect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def pushInspectionElement(self):
129129
self.insert.set_sensitive(True)
130130

131131
def updateInspector(self, path, objType, name, value, pushToStack=False):
132-
if objType in ("array", "object"):
132+
if objType in ("array", "object", "gobject", "gboxed"):
133133
if pushToStack:
134134
self.pushInspectionElement()
135135

js/ui/lookingGlass.js

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const Cinnamon = imports.gi.Cinnamon;
44
const Clutter = imports.gi.Clutter;
55
const Cogl = imports.gi.Cogl;
66
const Gio = imports.gi.Gio;
7+
const Gir = imports.gi.GIRepository;
78
const GLib = imports.gi.GLib;
89
const GObject = imports.gi.GObject;
910
const Lang = imports.lang;
@@ -39,14 +40,24 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
3940
const HISTORY_KEY = 'looking-glass-history';
4041

4142
/* fake types for special cases:
42-
* -"array": objects that pass Array.isArray() and should only show enumerable properties
43-
* -"boxedproto": boxed prototypes throw an error on property access
44-
* -"importer": objects that load modules on property access
43+
* -"array": Array() - should only show enumerable properties
44+
* -"gobject": gobject instance proxy - inspected via GIRepository
45+
* -"gboxed": gboxed instance proxy - inspected via GIRepository
46+
* -"prototype": prototypes for gobjects and gboxed objects - not inspectable
47+
* -"importer": file importers - load modules on property access, not inspectable
4548
*/
4649

50+
51+
// matches gi tostring patterns
52+
const GI_RE = /^\[(boxed|object) (instance|prototype) (?:proxy|of) GIName:([\w.]+) [^\r\n]+\]$/;
53+
54+
// matches known importers
55+
const IMPORT_RE = /^\[(?:GjsFileImporter \w+|object GjsModule gi)\]$/;
56+
4757
// returns [typeString, valueString] for any object
4858
function getObjInfo(o) {
4959
let type, value;
60+
5061
if (o === null)
5162
type = "null";
5263
else if (o === undefined)
@@ -55,28 +66,34 @@ function getObjInfo(o) {
5566
if (type) {
5667
value = "[" + type + "]";
5768
} else {
58-
// try to detect importers via their string representation
59-
try {
60-
value = o.toString();
61-
} catch (e) {
62-
if (e.message.includes("not an object instance - cannot convert to GObject*")) {
63-
// work around Clutter.Actor.prototype.toString override not handling being
64-
// called with the prototype itself as 'this'
65-
value = GObject.Object.prototype.toString.call(o);
66-
} else {
69+
// try to detect detailed type via their string representation
70+
if (o instanceof GObject.Object) {
71+
// this works around Clutter.Actor.prototype.toString overrides
72+
value = GObject.Object.prototype.toString.call(o);
73+
} else {
74+
try {
75+
value = o.toString();
76+
} catch (e) {
6777
value = "[error getting value]";
6878
}
6979
}
7080

7181
type = typeof(o);
72-
if (type == "object") {
73-
if (value.startsWith("[GjsFileImporter")
74-
|| value.startsWith("[object GjsModule gi")) {
82+
if (type === "object") {
83+
let matches = IMPORT_RE.test(value);
84+
if (matches) {
7585
type = "importer";
76-
} else if (value.startsWith("[boxed prototype")) {
77-
type = "boxedproto";
78-
} else if (Array.isArray(o)) {
79-
type = "array";
86+
} else {
87+
matches = GI_RE.exec(value);
88+
if (matches) {
89+
if (matches[2] === "prototype") {
90+
type = "prototype";
91+
} else {
92+
type = `g${matches[1]}`;
93+
}
94+
} else if (Array.isArray(o)) {
95+
type = "array";
96+
}
8097
}
8198
}
8299

@@ -90,10 +107,23 @@ function getObjInfo(o) {
90107

91108
// returns an array of dictionaries conforming to the Inspect dbus schema
92109
function getObjKeysInfo(obj) {
110+
let keys;
93111
let [type, ] = getObjInfo(obj);
94-
if (!["array", "object"].includes(type))
112+
if (["gobject", "gboxed"].includes(type))
113+
keys = _giObjectGetKeys(obj, type);
114+
else if (["array", "object"].includes(type))
115+
keys = _jsObjectGetKeys(obj, type);
116+
else
95117
return [];
96118

119+
return keys.map((k) => {
120+
let [t, v] = getObjInfo(obj[k]);
121+
return { name: k.toString(), type: t, value: v, shortValue: "" };
122+
});
123+
}
124+
125+
// get list of keys for js objects
126+
function _jsObjectGetKeys(obj, type) {
97127
let keys = new Set();
98128
let curProto = obj;
99129

@@ -112,11 +142,60 @@ function getObjKeysInfo(obj) {
112142
curProto = Object.getPrototypeOf(curProto);
113143
}
114144

145+
return Array.from(keys);
146+
}
115147

116-
return Array.from(keys).map((k) => {
117-
let [t, v] = getObjInfo(obj[k]);
118-
return { name: k.toString(), type: t, value: v, shortValue: "" };
119-
});
148+
// get list of keys for gboxed and gobject
149+
function _giObjectGetKeys(obj, type) {
150+
let baseInfo = Gir.Repository.get_default().find_by_gtype(obj.constructor.$gtype);
151+
if (type === "gboxed")
152+
return _giStructInfoGetKeys(baseInfo);
153+
else if (type === "gobject")
154+
return _giObjectInfoGetKeys(baseInfo);
155+
else
156+
return [];
157+
}
158+
159+
// get a list of fields and methods for a gi struct info
160+
function _giStructInfoGetKeys(info) {
161+
let keys = [];
162+
while (info) {
163+
let n = Gir.struct_info_get_n_fields(info);
164+
for (let i = 0; i < n; i++)
165+
keys.push(Gir.struct_info_get_field(info, i).get_name());
166+
167+
n = Gir.struct_info_get_n_methods(info);
168+
for (let i = 0; i < n; i++)
169+
keys.push(Gir.struct_info_get_method(info, i).get_name());
170+
171+
info = Gir.object_info_get_parent(info);
172+
}
173+
return keys;
174+
}
175+
176+
// get a list of fields and methods for a gi object info
177+
function _giObjectInfoGetKeys(info) {
178+
let keys = [];
179+
while(info) {
180+
let n = Gir.object_info_get_n_constants(info);
181+
for (let i = 0; i < n; i++)
182+
keys.push(Gir.object_info_get_constant(info, i).get_name());
183+
184+
n = Gir.object_info_get_n_properties(info);
185+
for (let i = 0; i < n; i++) {
186+
let propInfo = Gir.object_info_get_property(info, i);
187+
let flags = Gir.property_info_get_flags(propInfo);
188+
if ((flags & GObject.ParamFlags.READABLE) === GObject.ParamFlags.READABLE)
189+
keys.push(propInfo.get_name());
190+
}
191+
192+
n = Gir.object_info_get_n_methods(info);
193+
for (let i = 0; i < n; i++)
194+
keys.push(Gir.object_info_get_method(info, i).get_name());
195+
196+
info = Gir.object_info_get_parent(info);
197+
}
198+
return keys;
120199
}
121200

122201
// always returns an object we can give back to melange.

0 commit comments

Comments
 (0)