@@ -28,7 +28,16 @@ def get_constant(self, name):
2828 """Lazy load and cache Ruby constants"""
2929 if name not in self ._constants :
3030 try :
31- self ._constants [name ] = int (gdb .parse_and_eval (name ))
31+ # Handle special case for RUBY_Qnil which might not be a constant
32+ if name == "RUBY_Qnil" :
33+ # RUBY_Qnil is typically 8 or 4 depending on Ruby version
34+ try :
35+ self ._constants [name ] = int (gdb .parse_and_eval (name ))
36+ except :
37+ # Default to 8 for modern Ruby
38+ self ._constants [name ] = 8
39+ else :
40+ self ._constants [name ] = int (gdb .parse_and_eval (name ))
3241 self .debug (f"Loaded constant { name } = { self ._constants [name ]} " )
3342 except Exception as e :
3443 self .debug (f"Failed to load constant { name } : { e } " )
@@ -138,14 +147,201 @@ def print_item_label(self, depth, index):
138147 """Print a consistently formatted array item label"""
139148 self .print_with_indent (depth , f"[{ index :>4} ] I: " , end = '' )
140149
141- def safe_rp (self , obj ):
142- """Safely execute rp command with error handling"""
150+ def print_value (self , value ):
151+ """Print a primitive Ruby value (immediate values and simple types)"""
152+ val_int = int (value )
153+
154+ # Handle special constants
155+ if val_int == 0 :
156+ print ("false" )
157+ return True
158+ elif val_int == 0x04 or val_int == 0x08 :
159+ print ("nil" )
160+ return True
161+ elif val_int == 0x14 :
162+ print ("true" )
163+ return True
164+ elif (val_int & 0x01 ) != 0 :
165+ # Fixnum - shift right to get actual value
166+ print (val_int >> 1 )
167+ return True
168+ elif (val_int & 0xFF ) == 0x0C :
169+ # Symbol - extract the symbol name
170+ return self .print_symbol (value )
171+
172+ # For other types, try to inspect the object
173+ try :
174+ basic = value .cast (self .get_type ("struct RBasic" ).pointer ())
175+ flags = int (basic .dereference ()['flags' ])
176+ type_flag = flags & self .get_constant ("RUBY_T_MASK" )
177+
178+ if type_flag == self .get_constant ("RUBY_T_STRING" ):
179+ return self .print_string_value (value , flags )
180+ elif type_flag == self .get_constant ("RUBY_T_SYMBOL" ):
181+ return self .print_symbol_object (value )
182+ else :
183+ # Unknown type - print type and address
184+ print (f"<Object:0x{ val_int :x} type=0x{ type_flag :x} >" )
185+ return True
186+ except Exception as e :
187+ print (f"<Value:0x{ val_int :x} >" )
188+ return True
189+
190+ def get_symbol_name (self , symbol_id ):
191+ """Get symbol name from ID using Ruby's global symbol table
192+ This is a direct translation of print_id from Ruby's .gdbinit"""
193+ try :
194+ # rb_id_to_serial
195+ tLAST_OP_ID = 163 # This is a constant from Ruby
196+ ID_ENTRY_UNIT = 512
197+ ID_ENTRY_SIZE = 2
198+
199+ id_val = symbol_id
200+
201+ if id_val > tLAST_OP_ID :
202+ try :
203+ RUBY_ID_SCOPE_SHIFT = int (self .get_constant ("RUBY_ID_SCOPE_SHIFT" ))
204+ except :
205+ RUBY_ID_SCOPE_SHIFT = 3 # Default value
206+ serial = id_val >> RUBY_ID_SCOPE_SHIFT
207+ else :
208+ serial = id_val
209+
210+ # Access ruby_global_symbols
211+ global_symbols = gdb .parse_and_eval ("ruby_global_symbols" )
212+ last_id = int (global_symbols ['last_id' ])
213+
214+ if not serial or serial > last_id :
215+ return None
216+
217+ idx = serial // ID_ENTRY_UNIT
218+ ids = global_symbols ['ids' ].cast (self .get_type ("struct RArray" ).pointer ())
219+ flags = int (ids .dereference ()['basic' ]['flags' ])
220+
221+ # Get idsptr and idslen based on whether array is embedded or heap
222+ if flags & self .get_constant ("RUBY_FL_USER1" ):
223+ idsptr = ids .dereference ()['as' ]['ary' ]
224+ mask = self .get_constant ("RUBY_FL_USER3" ) | self .get_constant ("RUBY_FL_USER4" )
225+ shift = self .get_constant ("RUBY_FL_USHIFT" ) + 3
226+ idslen = (flags & mask ) >> shift
227+ else :
228+ idsptr = ids .dereference ()['as' ]['heap' ]['ptr' ]
229+ idslen = int (ids .dereference ()['as' ]['heap' ]['len' ])
230+
231+ if idx >= idslen :
232+ return None
233+
234+ t = 0
235+ ary_val = idsptr [idx ]
236+
237+ # Check if RUBY_Qnil
238+ qnil = self .get_constant ("RUBY_Qnil" )
239+ if int (ary_val ) == qnil :
240+ return None
241+
242+ ary = ary_val .cast (self .get_type ("struct RArray" ).pointer ())
243+ ary_flags = int (ary .dereference ()['basic' ]['flags' ])
244+
245+ # Get aryptr and arylen based on whether array is embedded or heap
246+ if ary_flags & self .get_constant ("RUBY_FL_USER1" ):
247+ aryptr = ary .dereference ()['as' ]['ary' ]
248+ mask = self .get_constant ("RUBY_FL_USER3" ) | self .get_constant ("RUBY_FL_USER4" )
249+ shift = self .get_constant ("RUBY_FL_USHIFT" ) + 3
250+ arylen = (ary_flags & mask ) >> shift
251+ else :
252+ aryptr = ary .dereference ()['as' ]['heap' ]['ptr' ]
253+ arylen = int (ary .dereference ()['as' ]['heap' ]['len' ])
254+
255+ result = aryptr [(serial % ID_ENTRY_UNIT ) * ID_ENTRY_SIZE + t ]
256+
257+ # Check if RUBY_Qnil
258+ if int (result ) == qnil :
259+ return None
260+
261+ # Extract string from result
262+ result_flags = int (result .cast (self .get_type ("struct RBasic" ).pointer ()).dereference ()['flags' ])
263+ return self .extract_string_data (result , result_flags )
264+
265+ except Exception as e :
266+ self .debug (f"Error getting symbol name: { e } " )
267+ return None
268+
269+ def extract_string_data (self , value , flags ):
270+ """Extract raw string data from a Ruby String object"""
143271 try :
144- gdb .execute (f"rp { obj } " )
272+ rstring = value .cast (self .get_type ("struct RString" ).pointer ())
273+ length = int (rstring .dereference ()['len' ])
274+
275+ if length == 0 :
276+ return ""
277+
278+ # Check if embedded or heap-allocated
279+ if (flags & self .get_constant ("RUBY_FL_USER1" )) != 0 :
280+ # Heap string
281+ ptr = rstring .dereference ()['as' ]['heap' ]['ptr' ]
282+ else :
283+ # Embedded string
284+ ptr = rstring .dereference ()['as' ]['embed' ]['ary' ]
285+
286+ # Read the string data
287+ string_data = gdb .selected_inferior ().read_memory (ptr , length ).tobytes ()
288+ try :
289+ return string_data .decode ('utf-8' )
290+ except :
291+ return string_data .hex ()
292+ except Exception as e :
293+ self .debug (f"Error extracting string: { e } " )
294+ return None
295+
296+ def print_symbol (self , value ):
297+ """Print a symbol (immediate symbol encoding)"""
298+ val_int = int (value )
299+ try :
300+ # Symbol ID is encoded in the VALUE
301+ # Different Ruby versions encode it differently
302+ # Try to extract and look up in global symbol table
303+ symbol_id = val_int >> 8
304+
305+ # Try to get symbol name from global symbol table
306+ symbol_name = self .get_symbol_name (symbol_id )
307+
308+ if symbol_name :
309+ print (f":{ symbol_name } " )
310+ else :
311+ print (f":id_0x{ symbol_id :x} " )
145312 return True
146313 except Exception as e :
147- print (f"[Error printing object: { str (e )} ]" )
148- return False
314+ self .debug (f"Error printing symbol: { e } " )
315+ print (f":0x{ val_int :x} " )
316+ return True
317+
318+ def print_symbol_object (self , value ):
319+ """Print a T_SYMBOL object (heap-allocated symbol)"""
320+ try :
321+ rsymbol = value .cast (self .get_type ("struct RSymbol" ).pointer ())
322+ fstr = rsymbol .dereference ()['fstr' ]
323+
324+ # fstr is a String containing the symbol name
325+ print (":" , end = '' )
326+ self .print_string_value (fstr , int (fstr .cast (self .get_type ("struct RBasic" ).pointer ()).dereference ()['flags' ]))
327+ return True
328+ except Exception as e :
329+ print (f":<Symbol:0x{ int (value ):x} >" )
330+ return True
331+
332+ def print_string_value (self , value , flags ):
333+ """Print a Ruby string value"""
334+ string_data = self .extract_string_data (value , flags )
335+ if string_data is not None :
336+ print (string_data , end = '' )
337+ return True
338+ else :
339+ print (f"<String error>" )
340+ return True
341+
342+ def safe_rp (self , obj ):
343+ """Legacy method - now redirects to print_value"""
344+ return self .print_value (obj )
149345
150346 def print_object (self , value , depth , max_depth ):
151347 """Print a value, recursively if it's a hash or array"""
@@ -309,7 +505,7 @@ def print_st_table(self, object_ptr, depth, max_depth):
309505 for i in range (num_entries ):
310506 self .print_key_label (depth , i )
311507 key = st_table .dereference ()['entries' ][i ]['key' ]
312- self .safe_rp (key )
508+ self .print_object (key , depth + 1 , max_depth )
313509
314510 self .print_value_label (depth )
315511 value = st_table .dereference ()['entries' ][i ]['record' ]
@@ -338,7 +534,7 @@ def print_ar_table(self, object_ptr, flags, depth, max_depth):
338534 # Skip undefined/deleted entries:
339535 if int (key ) != self .get_constant ("RUBY_Qundef" ):
340536 self .print_key_label (depth , i )
341- self .safe_rp (key )
537+ self .print_object (key , depth + 1 , max_depth )
342538
343539 self .print_value_label (depth )
344540 value = ar_table .dereference ()['pairs' ][i ]['val' ]
0 commit comments