@@ -5,6 +5,7 @@ const Clutter = imports.gi.Clutter;
55const Cogl = imports . gi . Cogl ;
66const Gio = imports . gi . Gio ;
77const GLib = imports . gi . GLib ;
8+ const GObject = imports . gi . GObject ;
89const Lang = imports . lang ;
910const Meta = imports . gi . Meta ;
1011const Signals = imports . signals ;
@@ -36,13 +37,126 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
3637 'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ' ;
3738
3839const HISTORY_KEY = 'looking-glass-history' ;
39- function objectToString ( o ) {
40- if ( typeof ( o ) == typeof ( objectToString ) ) {
41- // special case this since the default is way, way too verbose
42- return '<js function>' ;
43- } else {
44- return '' + o ;
40+
41+ /* looking glass types:
42+ *
43+ * We use a couple of custom type strings in addition to normal js type strings(from typeof):
44+ * -"array": for objects which pass Array.isArray() and should only show indexed properties
45+ * -"delegate": objects with an 'actor' property pointing to an st/clutter actor
46+ * -"importer": importer objects like "imports" which shouldn't be probed
47+ * -"gimporter": "imports.gi" object
48+ * -"boxed": structs and other non-gobject c data types
49+ * -"gobject": gobjects
50+ * -"unknown": unidentifiable c object
51+ */
52+
53+ // types with no properties or import files on get()
54+ const NON_INSPECTABLE_TYPES = [
55+ "null" ,
56+ "undefined" ,
57+ "importer" ,
58+ "gimporter"
59+ ] ;
60+
61+ // not in use
62+ const IGNORE_PROPERTIES = [
63+ "toString" ,
64+ "constructor"
65+ ] ;
66+
67+ // returns [typeString, valueString] for any object
68+ function getObjInfo ( o ) {
69+ let type , value ;
70+ if ( o === null ) {
71+ type = "null" ;
72+ value = "[" + type + "]" ;
73+ } else if ( o === undefined ) {
74+ type = "undefined" ;
75+ value = "[" + type + "]" ;
76+ }
77+
78+ if ( ! value ) {
79+ try {
80+ value = o . toString ( ) ;
81+ } catch ( e ) {
82+ if ( e . message . includes ( "not an object instance - cannot convert to GObject*" ) ) {
83+ // work around Clutter.Actor.prototype.toString override not handling being
84+ // called with the prototype as 'this'
85+ value = GObject . Object . prototype . toString . call ( o ) ;
86+ } else {
87+ value = "[error getting value]" ;
88+ }
89+ }
90+ if ( value . startsWith ( "[object Object delegate for" ) ) {
91+ type = "delegate" ;
92+ } else if ( value . includes ( "CinnamonGenericContainer" ) ) {
93+ type = "gobject" ;
94+ } else if ( value . includes ( "GIName:" ) ) {
95+ try {
96+ type = value . match ( / \[ ( .+ ) (?: i n s t a n c e p r o x y | p r o t o t y p e o f ) G I N a m e : .+ \] / ) [ 1 ] ;
97+ if ( type === "object" )
98+ type = "gobject" ;
99+ } catch ( e ) {
100+ type = "unknown" ;
101+ }
102+ } else if ( value . startsWith ( "[GjsFileImporter" ) ) {
103+ type = "importer" ;
104+ } else if ( value . includes ( "GjsModule" ) ) {
105+ if ( value === "[object GjsModule gi]" )
106+ type = "gimporter" ;
107+ else
108+ type = "module" ;
109+ } else if ( Array . isArray ( o ) ) {
110+ type = "array" ;
111+ } else {
112+ type = typeof ( o ) ;
113+ }
45114 }
115+
116+ if ( value === "" )
117+ value = "[empty]" ;
118+
119+ return [ type , value ] ;
120+ }
121+
122+ function getObjKeysInfo ( obj ) {
123+ let [ type , ] = getObjInfo ( obj ) ;
124+ if ( NON_INSPECTABLE_TYPES . includes ( type ) )
125+ return [ ] ;
126+
127+ let keys = new Set ( ) ;
128+ let curProto = obj ;
129+
130+ // we ignore the Object prototype for everything except basic objects,
131+ // to skip the common object properties unless we can't avoid it
132+ while ( curProto && ( type === "object" || curProto !== Object . prototype ) ) {
133+ let ownKeys ;
134+ if ( type === "array" ) {
135+ ownKeys = curProto . keys ( ) ; // index properties only
136+ } else {
137+ ownKeys = Reflect . ownKeys ( curProto ) ; // all own properties and symbols
138+ }
139+
140+ for ( let key of ownKeys )
141+ keys . add ( key ) ;
142+
143+ curProto = Object . getPrototypeOf ( curProto ) ;
144+ }
145+
146+ return Array . from ( keys ) . map ( ( k ) => {
147+ let [ t , v ] = getObjInfo ( obj [ k ] ) ;
148+ return { name : k . toString ( ) , type : t , value : v , shortValue : v . split ( `\n` ) [ 0 ] }
149+ } ) ;
150+ }
151+
152+ function tryEval ( js ) {
153+ let out ;
154+ try {
155+ out = eval ( js ) ;
156+ } catch ( e ) {
157+ out = e ;
158+ }
159+ return out ;
46160}
47161
48162function WindowList ( ) {
@@ -93,13 +207,13 @@ WindowList.prototype = {
93207
94208 let lgInfo = {
95209 id : metaWindow . _lgId . toString ( ) ,
96- title : objectToString ( metaWindow . title ) ,
97- wmclass : objectToString ( metaWindow . get_wm_class ( ) ) ,
210+ title : metaWindow . title + '' ,
211+ wmclass : metaWindow . get_wm_class ( ) + '' ,
98212 app : '' } ;
99213
100214 let app = tracker . get_window_app ( metaWindow ) ;
101215 if ( app != null && ! app . is_window_backed ( ) ) {
102- lgInfo . app = objectToString ( app . get_id ( ) ) ;
216+ lgInfo . app = app . get_id ( ) + '' ;
103217 } else {
104218 lgInfo . app = '<untracked>' ;
105219 }
@@ -454,43 +568,18 @@ Melange.prototype = {
454568 _pushResult : function ( command , obj , tooltip ) {
455569 let index = this . _results . length ;
456570 let result = { "o" : obj , "index" : index } ;
457- this . rawResults . push ( { command : command , type : typeof ( obj ) , object : objectToString ( obj ) , index : index . toString ( ) , tooltip : tooltip } ) ;
571+ let [ type , value ] = getObjInfo ( obj ) ;
572+ this . rawResults . push ( { command : command , type : type , object : value , index : index . toString ( ) , tooltip : tooltip } ) ;
458573 this . emitResultUpdate ( ) ;
459-
574+
460575 this . _results . push ( result ) ;
461576 this . _it = obj ;
462577 } ,
463578
464579 inspect : function ( path ) {
465580 let fullCmd = commandHeader + path ;
466-
467- let result = eval ( fullCmd ) ;
468- let resultObj = [ ] ;
469- for ( let key in result ) {
470- let type = typeof ( result [ key ] ) ;
471-
472- //fixme: move this shortvalue stuff to python lg
473- let shortValue , value ;
474- if ( type === "undefined" ) {
475- value = "" ;
476- shortValue = "" ;
477- } else if ( result [ key ] === null ) {
478- value = "[null]" ;
479- shortValue = value ;
480- } else {
481- value = result [ key ] . toString ( ) ;
482- shortValue = value ;
483- let i = value . indexOf ( '\n' ) ;
484- let j = value . indexOf ( '\r' ) ;
485- if ( j != - 1 && ( i == - 1 || i > j ) )
486- i = j ;
487- if ( i != - 1 )
488- shortValue = value . substr ( 0 , i ) + '.. <more>' ;
489- }
490- resultObj . push ( { name : key , type : type , value : value , shortValue : shortValue } ) ;
491- }
492-
493- return resultObj ;
581+ let result = tryEval ( fullCmd ) ;
582+ return getObjKeysInfo ( result ) ;
494583 } ,
495584
496585 getIt : function ( ) {
@@ -519,24 +608,14 @@ Melange.prototype = {
519608 this . _history . addItem ( command ) ;
520609
521610 let fullCmd = commandHeader + command ;
522-
523- let resultObj ;
524-
525611 let ts = GLib . get_monotonic_time ( ) ;
526612
527- try {
528- resultObj = eval ( fullCmd ) ;
529- } catch ( e ) {
530- resultObj = '<exception ' + e + '>' ;
531- }
613+ let resultObj = tryEval ( fullCmd ) ;
532614
533615 let ts2 = GLib . get_monotonic_time ( ) ;
534-
535616 let tooltip = _ ( "Execution time (ms): " ) + ( ts2 - ts ) / 1000 ;
536617
537618 this . _pushResult ( command , resultObj , tooltip ) ;
538-
539- return ;
540619 } ,
541620
542621 // DBus function
@@ -547,14 +626,7 @@ Melange.prototype = {
547626 // DBus function
548627 AddResult : function ( path ) {
549628 let fullCmd = commandHeader + path ;
550-
551- let resultObj ;
552- try {
553- resultObj = eval ( fullCmd ) ;
554- } catch ( e ) {
555- resultObj = '<exception ' + e + '>' ;
556- }
557- this . _pushResult ( path , resultObj , "" ) ;
629+ this . _pushResult ( path , tryEval ( fullCmd ) , "" ) ;
558630 } ,
559631
560632 // DBus function
0 commit comments