lldb 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -3
  3. data/README.md +86 -2
  4. data/Rakefile +36 -0
  5. data/bindings/constants.yml +137 -0
  6. data/bindings/surface.yml +3072 -0
  7. data/ext/lldb/discovery.rb +101 -0
  8. data/ext/lldb/extconf.rb +206 -88
  9. data/ext/lldb/lldb_wrapper.cpp +7977 -856
  10. data/ext/lldb/lldb_wrapper.h +599 -324
  11. data/lib/lldb/address.rb +81 -0
  12. data/lib/lldb/api_support.rb +91 -0
  13. data/lib/lldb/attach_info.rb +122 -0
  14. data/lib/lldb/block.rb +121 -0
  15. data/lib/lldb/breakpoint.rb +11 -6
  16. data/lib/lldb/breakpoint_location.rb +20 -5
  17. data/lib/lldb/broadcaster.rb +79 -0
  18. data/lib/lldb/command_interpreter.rb +9 -4
  19. data/lib/lldb/command_return_object.rb +24 -5
  20. data/lib/lldb/compile_unit.rb +64 -0
  21. data/lib/lldb/context.rb +81 -0
  22. data/lib/lldb/debugger.rb +55 -12
  23. data/lib/lldb/error.rb +51 -3
  24. data/lib/lldb/event.rb +109 -0
  25. data/lib/lldb/expression_options.rb +124 -0
  26. data/lib/lldb/ffi_bindings.rb +326 -31
  27. data/lib/lldb/file_spec.rb +90 -0
  28. data/lib/lldb/file_spec_list.rb +78 -0
  29. data/lib/lldb/frame.rb +113 -18
  30. data/lib/lldb/function.rb +121 -0
  31. data/lib/lldb/instruction.rb +83 -0
  32. data/lib/lldb/instruction_list.rb +68 -0
  33. data/lib/lldb/launch_info.rb +145 -19
  34. data/lib/lldb/line_entry.rb +75 -0
  35. data/lib/lldb/listener.rb +96 -0
  36. data/lib/lldb/memory_region_info.rb +109 -0
  37. data/lib/lldb/module.rb +46 -5
  38. data/lib/lldb/native.rb +36 -0
  39. data/lib/lldb/native_buffer.rb +32 -0
  40. data/lib/lldb/native_handle.rb +60 -0
  41. data/lib/lldb/native_lifecycle.rb +64 -0
  42. data/lib/lldb/native_string_array.rb +34 -0
  43. data/lib/lldb/process.rb +121 -38
  44. data/lib/lldb/symbol.rb +117 -0
  45. data/lib/lldb/symbol_context.rb +9 -4
  46. data/lib/lldb/target.rb +73 -67
  47. data/lib/lldb/thread.rb +56 -19
  48. data/lib/lldb/type.rb +40 -3
  49. data/lib/lldb/type_member.rb +67 -0
  50. data/lib/lldb/types.rb +69 -4
  51. data/lib/lldb/value.rb +27 -17
  52. data/lib/lldb/value_list.rb +9 -5
  53. data/lib/lldb/version.rb +1 -1
  54. data/lib/lldb/watchpoint.rb +26 -9
  55. data/lib/lldb.rb +80 -10
  56. data/lldb.gemspec +4 -1
  57. data/rbs_collection.yaml +1 -1
  58. data/script/check_bindings +259 -0
  59. data/script/guard_c_exports +139 -0
  60. data/sig/lldb/ffi_bindings.rbs +258 -21
  61. data/sig/lldb/weak_ref.rbs +7 -0
  62. metadata +32 -6
  63. data/ext/lldb/Makefile +0 -24
  64. data/ext/lldb/liblldb_wrapper.dylib +0 -0
  65. data/ext/lldb/lldb_wrapper.o +0 -0
  66. data/ext/lldb/mkmf.log +0 -24
data/lib/lldb/target.rb CHANGED
@@ -4,19 +4,24 @@
4
4
 
5
5
  module LLDB
6
6
  class Target
7
+ prepend NativeLifecycle
8
+
7
9
  # @rbs return: Debugger
8
10
  attr_reader :debugger
9
11
 
10
12
  # @rbs ptr: FFI::Pointer
11
13
  # @rbs debugger: Debugger
12
14
  # @rbs return: void
13
- def initialize(ptr, debugger:)
14
- @ptr = ptr # : FFI::Pointer
15
+ def initialize(ptr, debugger:, context: debugger.context)
15
16
  @debugger = debugger
16
17
  @breakpoints = [] # : Array[Breakpoint]
17
18
  @watchpoints = [] # : Array[Watchpoint]
18
19
  @process = nil # : Process?
19
- ObjectSpace.define_finalizer(self, self.class.release(@ptr))
20
+ initialize_native_object(
21
+ ptr,
22
+ release: ->(released) { FFIBindings.lldb_target_destroy(released) },
23
+ context: context
24
+ )
20
25
  end
21
26
 
22
27
  # @rbs ptr: FFI::Pointer
@@ -33,38 +38,16 @@ module LLDB
33
38
  # @rbs args: Array[String]?
34
39
  # @rbs env: Hash[String, String]?
35
40
  # @rbs working_dir: String?
41
+ # @rbs launch_flags: Integer?
36
42
  # @rbs return: Process
37
- def launch(args: nil, env: nil, working_dir: nil)
43
+ def launch(args: nil, env: nil, working_dir: nil, launch_flags: nil)
38
44
  raise InvalidObjectError, 'Target is not valid' unless valid?
39
45
 
40
- # Use LaunchInfo with STOP_AT_ENTRY flag for reliable cross-platform behavior
41
- launch_info = LaunchInfo.new(args)
42
- launch_info.launch_flags = LaunchFlags::STOP_AT_ENTRY
46
+ launch_info = LaunchInfo.new(args, context: context)
47
+ launch_info.launch_flags = launch_flags unless launch_flags.nil?
43
48
  launch_info.working_directory = working_dir if working_dir
44
49
  launch_info.set_environment(env) if env
45
-
46
- error = Error.new
47
- process_ptr = FFIBindings.lldb_target_launch(@ptr, launch_info.to_ptr, error.to_ptr)
48
-
49
- error.raise_if_error!
50
- raise LaunchError, 'Failed to launch process' if process_ptr.nil? || process_ptr.null?
51
-
52
- @process = Process.new(process_ptr, target: self)
53
-
54
- # Wait for process to stop when in synchronous mode
55
- # On some platforms (Linux), the process may not be immediately stopped
56
- unless @debugger.async?
57
- wait_for_process_stop(@process)
58
-
59
- # STOP_AT_ENTRY stops at the program entry point, not at breakpoints.
60
- # If there are breakpoints set, continue to let the process run to the first breakpoint.
61
- if num_breakpoints > 0 && @process.state == State::STOPPED && @process.valid?
62
- @process.continue
63
- wait_for_process_stop(@process)
64
- end
65
- end
66
-
67
- @process
50
+ launch_with_info(launch_info)
68
51
  end
69
52
 
70
53
  # @rbs launch_info: LaunchInfo
@@ -76,10 +59,10 @@ module LLDB
76
59
  error = Error.new
77
60
  process_ptr = FFIBindings.lldb_target_launch(@ptr, launch_info.to_ptr, error.to_ptr)
78
61
 
79
- error.raise_if_error!
62
+ error.raise_if_error!('target.launch')
80
63
  raise LaunchError, 'Failed to launch process' if process_ptr.nil? || process_ptr.null?
81
64
 
82
- @process = Process.new(process_ptr, target: self)
65
+ @process = Process.new(process_ptr, target: self, context: context)
83
66
  end
84
67
 
85
68
  # @rbs pid: Integer
@@ -93,7 +76,22 @@ module LLDB
93
76
  error.raise_if_error!
94
77
  raise AttachError, "Failed to attach to process #{pid}" if process_ptr.nil? || process_ptr.null?
95
78
 
96
- @process = Process.new(process_ptr, target: self)
79
+ @process = Process.new(process_ptr, target: self, context: context)
80
+ end
81
+
82
+ # @rbs attach_info: AttachInfo
83
+ # @rbs return: Process
84
+ def attach_with_info(attach_info)
85
+ raise InvalidObjectError, 'Target is not valid' unless valid?
86
+ raise ArgumentError, 'attach_info is required' unless attach_info.is_a?(AttachInfo)
87
+
88
+ error = Error.new
89
+ process_ptr = FFIBindings.lldb_target_attach_with_info(@ptr, attach_info.to_ptr, error.to_ptr)
90
+
91
+ error.raise_if_error!('target.attach')
92
+ raise AttachError, 'Failed to attach using attach info' if process_ptr.nil? || process_ptr.null?
93
+
94
+ @process = Process.new(process_ptr, target: self, context: context)
97
95
  end
98
96
 
99
97
  # @rbs symbol_name: String
@@ -105,7 +103,7 @@ module LLDB
105
103
  bp_ptr = FFIBindings.lldb_target_breakpoint_create_by_name(@ptr, symbol_name, module_name)
106
104
  raise BreakpointError, "Failed to create breakpoint for '#{symbol_name}'" if bp_ptr.nil? || bp_ptr.null?
107
105
 
108
- bp = Breakpoint.new(bp_ptr, target: self)
106
+ bp = Breakpoint.new(bp_ptr, target: self, context: context)
109
107
  @breakpoints << bp
110
108
  bp
111
109
  end
@@ -119,7 +117,7 @@ module LLDB
119
117
  bp_ptr = FFIBindings.lldb_target_breakpoint_create_by_location(@ptr, file, line)
120
118
  raise BreakpointError, "Failed to create breakpoint at #{file}:#{line}" if bp_ptr.nil? || bp_ptr.null?
121
119
 
122
- bp = Breakpoint.new(bp_ptr, target: self)
120
+ bp = Breakpoint.new(bp_ptr, target: self, context: context)
123
121
  @breakpoints << bp
124
122
  bp
125
123
  end
@@ -128,6 +126,8 @@ module LLDB
128
126
  # @rbs return: Breakpoint
129
127
  def breakpoint_create_by_address(address)
130
128
  raise InvalidObjectError, 'Target is not valid' unless valid?
129
+ APISupport.require_method!(:lldb_target_breakpoint_create_by_address,
130
+ 'breakpoint_create_by_address')
131
131
 
132
132
  bp_ptr = FFIBindings.lldb_target_breakpoint_create_by_address(@ptr, address)
133
133
  if bp_ptr.nil? || bp_ptr.null?
@@ -135,7 +135,7 @@ module LLDB
135
135
  "Failed to create breakpoint at address 0x#{address.to_s(16)}"
136
136
  end
137
137
 
138
- bp = Breakpoint.new(bp_ptr, target: self)
138
+ bp = Breakpoint.new(bp_ptr, target: self, context: context)
139
139
  @breakpoints << bp
140
140
  bp
141
141
  end
@@ -158,7 +158,7 @@ module LLDB
158
158
  bp_ptr = FFIBindings.lldb_target_find_breakpoint_by_id(@ptr, id)
159
159
  return nil if bp_ptr.nil? || bp_ptr.null?
160
160
 
161
- Breakpoint.new(bp_ptr, target: self)
161
+ Breakpoint.new(bp_ptr, target: self, context: context)
162
162
  end
163
163
 
164
164
  # @rbs return: Integer
@@ -176,7 +176,7 @@ module LLDB
176
176
  bp_ptr = FFIBindings.lldb_target_get_breakpoint_at_index(@ptr, index)
177
177
  return nil if bp_ptr.nil? || bp_ptr.null?
178
178
 
179
- Breakpoint.new(bp_ptr, target: self)
179
+ Breakpoint.new(bp_ptr, target: self, context: context)
180
180
  end
181
181
 
182
182
  # @rbs return: Array[Breakpoint]
@@ -191,14 +191,24 @@ module LLDB
191
191
  process_ptr = FFIBindings.lldb_target_get_process(@ptr)
192
192
  return nil if process_ptr.nil? || process_ptr.null?
193
193
 
194
- Process.new(process_ptr, target: self)
194
+ Process.new(process_ptr, target: self, context: context)
195
195
  end
196
196
 
197
197
  # @rbs return: String?
198
198
  def executable_path
199
199
  raise InvalidObjectError, 'Target is not valid' unless valid?
200
200
 
201
- FFIBindings.lldb_target_get_executable_path(@ptr)
201
+ executable_file&.path
202
+ end
203
+
204
+ # @rbs return: FileSpec?
205
+ def executable_file
206
+ raise InvalidObjectError, 'Target is not valid' unless valid?
207
+
208
+ file_ptr = FFIBindings.lldb_target_get_executable_file(@ptr)
209
+ return nil if file_ptr.nil? || file_ptr.null?
210
+
211
+ FileSpec.from_ptr(file_ptr, context: context)
202
212
  end
203
213
 
204
214
  # @rbs return: Integer
@@ -216,7 +226,7 @@ module LLDB
216
226
  mod_ptr = FFIBindings.lldb_target_get_module_at_index(@ptr, index)
217
227
  return nil if mod_ptr.nil? || mod_ptr.null?
218
228
 
219
- Module.new(mod_ptr, target: self)
229
+ Module.new(mod_ptr, target: self, context: context)
220
230
  end
221
231
 
222
232
  # @rbs return: Array[Module]
@@ -236,7 +246,7 @@ module LLDB
236
246
  error.raise_if_error!
237
247
  raise AttachError, "Failed to attach to process '#{name}'" if process_ptr.nil? || process_ptr.null?
238
248
 
239
- @process = Process.new(process_ptr, target: self)
249
+ @process = Process.new(process_ptr, target: self, context: context)
240
250
  end
241
251
 
242
252
  # @rbs regex: String
@@ -244,11 +254,13 @@ module LLDB
244
254
  # @rbs return: Breakpoint
245
255
  def breakpoint_create_by_regex(regex, module_name: nil)
246
256
  raise InvalidObjectError, 'Target is not valid' unless valid?
257
+ APISupport.require_method!(:lldb_target_breakpoint_create_by_regex,
258
+ 'breakpoint_create_by_regex')
247
259
 
248
260
  bp_ptr = FFIBindings.lldb_target_breakpoint_create_by_regex(@ptr, regex, module_name)
249
261
  raise BreakpointError, "Failed to create breakpoint for regex '#{regex}'" if bp_ptr.nil? || bp_ptr.null?
250
262
 
251
- bp = Breakpoint.new(bp_ptr, target: self)
263
+ bp = Breakpoint.new(bp_ptr, target: self, context: context)
252
264
  @breakpoints << bp
253
265
  bp
254
266
  end
@@ -262,7 +274,7 @@ module LLDB
262
274
  bp_ptr = FFIBindings.lldb_target_breakpoint_create_by_source_regex(@ptr, regex, source_file)
263
275
  raise BreakpointError, "Failed to create breakpoint for source regex '#{regex}'" if bp_ptr.nil? || bp_ptr.null?
264
276
 
265
- bp = Breakpoint.new(bp_ptr, target: self)
277
+ bp = Breakpoint.new(bp_ptr, target: self, context: context)
266
278
  @breakpoints << bp
267
279
  bp
268
280
  end
@@ -291,14 +303,25 @@ module LLDB
291
303
  end
292
304
 
293
305
  # @rbs expression: String
306
+ # @rbs options: ExpressionOptions?
294
307
  # @rbs return: Value?
295
- def evaluate_expression(expression)
308
+ def evaluate_expression(expression, options: nil)
296
309
  raise InvalidObjectError, 'Target is not valid' unless valid?
297
310
 
298
- value_ptr = FFIBindings.lldb_target_evaluate_expression(@ptr, expression)
311
+ value_ptr = if options
312
+ unless options.is_a?(ExpressionOptions)
313
+ raise ArgumentError, 'options must be an ExpressionOptions'
314
+ end
315
+
316
+ FFIBindings.lldb_target_evaluate_expression_with_options(
317
+ @ptr, expression, options.to_ptr
318
+ )
319
+ else
320
+ FFIBindings.lldb_target_evaluate_expression(@ptr, expression)
321
+ end
299
322
  return nil if value_ptr.nil? || value_ptr.null?
300
323
 
301
- Value.new(value_ptr, parent: self)
324
+ Value.new(value_ptr, parent: self, context: context)
302
325
  end
303
326
 
304
327
  # @rbs address: Integer
@@ -343,7 +366,7 @@ module LLDB
343
366
  error.raise_if_error!
344
367
  raise LLDBError, "Failed to create watchpoint at address 0x#{address.to_s(16)}" if wp_ptr.nil? || wp_ptr.null?
345
368
 
346
- wp = Watchpoint.new(wp_ptr, target: self)
369
+ wp = Watchpoint.new(wp_ptr, target: self, context: context)
347
370
  @watchpoints << wp
348
371
  wp
349
372
  end
@@ -375,7 +398,7 @@ module LLDB
375
398
  wp_ptr = FFIBindings.lldb_target_find_watchpoint_by_id(@ptr, id)
376
399
  return nil if wp_ptr.nil? || wp_ptr.null?
377
400
 
378
- Watchpoint.new(wp_ptr, target: self)
401
+ Watchpoint.new(wp_ptr, target: self, context: context)
379
402
  end
380
403
 
381
404
  # @rbs return: Integer
@@ -393,7 +416,7 @@ module LLDB
393
416
  wp_ptr = FFIBindings.lldb_target_get_watchpoint_at_index(@ptr, index)
394
417
  return nil if wp_ptr.nil? || wp_ptr.null?
395
418
 
396
- Watchpoint.new(wp_ptr, target: self)
419
+ Watchpoint.new(wp_ptr, target: self, context: context)
397
420
  end
398
421
 
399
422
  # @rbs return: Array[Watchpoint]
@@ -406,22 +429,5 @@ module LLDB
406
429
  @ptr
407
430
  end
408
431
 
409
- private
410
-
411
- # @rbs process: Process
412
- # @rbs timeout: Float
413
- # @rbs return: void
414
- def wait_for_process_stop(process, timeout: 10.0)
415
- require 'timeout'
416
-
417
- Timeout.timeout(timeout, LaunchError, "Process failed to stop within #{timeout} seconds") do
418
- loop do
419
- state = process.state
420
- break if state == State::STOPPED || state == State::EXITED || state == State::CRASHED
421
-
422
- sleep(0.01)
423
- end
424
- end
425
- end
426
432
  end
427
433
  end
data/lib/lldb/thread.rb CHANGED
@@ -4,16 +4,21 @@
4
4
 
5
5
  module LLDB
6
6
  class Thread
7
+ prepend NativeLifecycle
8
+
7
9
  # @rbs return: Process
8
10
  attr_reader :process
9
11
 
10
12
  # @rbs ptr: FFI::Pointer
11
13
  # @rbs process: Process
12
14
  # @rbs return: void
13
- def initialize(ptr, process:)
14
- @ptr = ptr # : FFI::Pointer
15
+ def initialize(ptr, process:, context: process.context)
15
16
  @process = process
16
- ObjectSpace.define_finalizer(self, self.class.release(@ptr))
17
+ initialize_native_object(
18
+ ptr,
19
+ release: ->(released) { FFIBindings.lldb_thread_destroy(released) },
20
+ context: context
21
+ )
17
22
  end
18
23
 
19
24
  # @rbs ptr: FFI::Pointer
@@ -27,33 +32,59 @@ module LLDB
27
32
  !@ptr.null? && FFIBindings.lldb_thread_is_valid(@ptr) != 0
28
33
  end
29
34
 
30
- # @rbs return: bool
31
- def step_over
35
+ # @rbs run_mode: Integer
36
+ # @rbs return: true
37
+ def step_over(run_mode: RunMode::ONLY_DURING_STEPPING)
32
38
  raise InvalidObjectError, 'Thread is not valid' unless valid?
39
+ APISupport.require_method!(:lldb_thread_step_over, 'step_over')
33
40
 
34
- FFIBindings.lldb_thread_step_over(@ptr) != 0
41
+ error = Error.new
42
+ status = FFIBindings.lldb_thread_step_over(@ptr, run_mode, error.to_ptr)
43
+ Native.check_status!(status, 'thread.step_over', error)
44
+ true
35
45
  end
36
46
 
37
- # @rbs return: bool
38
- def step_into
47
+ # @rbs target_name: String?
48
+ # @rbs end_line: Integer
49
+ # @rbs run_mode: Integer
50
+ # @rbs return: true
51
+ def step_into(target_name: nil, end_line: INVALID_LINE_NUMBER, run_mode: RunMode::ONLY_DURING_STEPPING)
39
52
  raise InvalidObjectError, 'Thread is not valid' unless valid?
40
-
41
- FFIBindings.lldb_thread_step_into(@ptr) != 0
53
+ APISupport.require_method!(:lldb_thread_step_into, 'step_into')
54
+
55
+ error = Error.new
56
+ status = FFIBindings.lldb_thread_step_into(
57
+ @ptr,
58
+ target_name,
59
+ end_line,
60
+ run_mode,
61
+ error.to_ptr
62
+ )
63
+ Native.check_status!(status, 'thread.step_into', error)
64
+ true
42
65
  end
43
66
 
44
- # @rbs return: bool
67
+ # @rbs return: true
45
68
  def step_out
46
69
  raise InvalidObjectError, 'Thread is not valid' unless valid?
70
+ APISupport.require_method!(:lldb_thread_step_out, 'step_out')
47
71
 
48
- FFIBindings.lldb_thread_step_out(@ptr) != 0
72
+ error = Error.new
73
+ status = FFIBindings.lldb_thread_step_out(@ptr, error.to_ptr)
74
+ Native.check_status!(status, 'thread.step_out', error)
75
+ true
49
76
  end
50
77
 
51
78
  # @rbs step_over: bool
52
- # @rbs return: bool
79
+ # @rbs return: true
53
80
  def step_instruction(step_over: false)
54
81
  raise InvalidObjectError, 'Thread is not valid' unless valid?
82
+ APISupport.require_method!(:lldb_thread_step_instruction, 'step_instruction')
55
83
 
56
- FFIBindings.lldb_thread_step_instruction(@ptr, step_over ? 1 : 0) != 0
84
+ error = Error.new
85
+ status = FFIBindings.lldb_thread_step_instruction(@ptr, step_over ? 1 : 0, error.to_ptr)
86
+ Native.check_status!(status, 'thread.step_instruction', error)
87
+ true
57
88
  end
58
89
 
59
90
  # @rbs return: Integer
@@ -71,7 +102,7 @@ module LLDB
71
102
  frame_ptr = FFIBindings.lldb_thread_get_frame_at_index(@ptr, index)
72
103
  return nil if frame_ptr.nil? || frame_ptr.null?
73
104
 
74
- Frame.new(frame_ptr, thread: self)
105
+ Frame.new(frame_ptr, thread: self, context: context)
75
106
  end
76
107
 
77
108
  # @rbs return: Frame?
@@ -81,7 +112,7 @@ module LLDB
81
112
  frame_ptr = FFIBindings.lldb_thread_get_selected_frame(@ptr)
82
113
  return nil if frame_ptr.nil? || frame_ptr.null?
83
114
 
84
- Frame.new(frame_ptr, thread: self)
115
+ Frame.new(frame_ptr, thread: self, context: context)
85
116
  end
86
117
 
87
118
  # @rbs index: Integer
@@ -155,7 +186,10 @@ module LLDB
155
186
  def run_to_address(address)
156
187
  raise InvalidObjectError, 'Thread is not valid' unless valid?
157
188
 
158
- FFIBindings.lldb_thread_run_to_address(@ptr, address) != 0
189
+ error = Error.new
190
+ status = FFIBindings.lldb_thread_run_to_address(@ptr, address, error.to_ptr)
191
+ Native.check_status!(status, 'thread.run_to_address', error)
192
+ true
159
193
  end
160
194
 
161
195
  # @rbs return: Integer
@@ -176,8 +210,11 @@ module LLDB
176
210
  # @rbs return: String?
177
211
  def stop_description(max_size = 256)
178
212
  return nil unless valid?
213
+ return '' if max_size.zero?
179
214
 
180
- FFIBindings.lldb_thread_get_stop_description(@ptr, max_size)
215
+ NativeBuffer.read_c_string(max_size: max_size) do |buffer, length|
216
+ FFIBindings.lldb_thread_get_stop_description(@ptr, buffer, length)
217
+ end
181
218
  end
182
219
 
183
220
  # @rbs return: bool
@@ -215,7 +252,7 @@ module LLDB
215
252
  process_ptr = FFIBindings.lldb_thread_get_process(@ptr)
216
253
  return nil if process_ptr.nil? || process_ptr.null?
217
254
 
218
- Process.new(process_ptr, target: @process.target)
255
+ Process.new(process_ptr, target: @process.target, context: context)
219
256
  end
220
257
 
221
258
  # @rbs return: FFI::Pointer
data/lib/lldb/type.rb CHANGED
@@ -4,11 +4,16 @@
4
4
 
5
5
  module LLDB
6
6
  class Type
7
+ prepend NativeLifecycle
8
+
7
9
  # @rbs ptr: FFI::Pointer
8
10
  # @rbs return: void
9
- def initialize(ptr)
10
- @ptr = ptr # : FFI::Pointer
11
- ObjectSpace.define_finalizer(self, self.class.release(@ptr))
11
+ def initialize(ptr, context: nil)
12
+ initialize_native_object(
13
+ ptr,
14
+ release: ->(released) { FFIBindings.lldb_type_destroy(released) },
15
+ context: context
16
+ )
12
17
  end
13
18
 
14
19
  # @rbs ptr: FFI::Pointer
@@ -190,6 +195,24 @@ module LLDB
190
195
  FFIBindings.lldb_type_get_num_virtual_base_classes(@ptr)
191
196
  end
192
197
 
198
+ # @rbs index: Integer
199
+ # @rbs return: TypeMember?
200
+ def field_at_index(index)
201
+ member_at(index, :lldb_type_get_field_at_index)
202
+ end
203
+
204
+ # @rbs index: Integer
205
+ # @rbs return: TypeMember?
206
+ def direct_base_class_at_index(index)
207
+ member_at(index, :lldb_type_get_direct_base_class_at_index)
208
+ end
209
+
210
+ # @rbs index: Integer
211
+ # @rbs return: TypeMember?
212
+ def virtual_base_class_at_index(index)
213
+ member_at(index, :lldb_type_get_virtual_base_class_at_index)
214
+ end
215
+
193
216
  # @rbs return: Integer
194
217
  def basic_type
195
218
  return BasicType::INVALID unless valid?
@@ -211,5 +234,19 @@ module LLDB
211
234
  def to_ptr
212
235
  @ptr
213
236
  end
237
+
238
+ private
239
+
240
+ # @rbs index: Integer
241
+ # @rbs method_name: ::Symbol
242
+ # @rbs return: TypeMember?
243
+ def member_at(index, method_name)
244
+ raise InvalidObjectError, 'Type is not valid' unless valid?
245
+
246
+ member_ptr = FFIBindings.public_send(method_name, @ptr, index)
247
+ return nil if member_ptr.nil? || member_ptr.null?
248
+
249
+ TypeMember.new(member_ptr, context: context)
250
+ end
214
251
  end
215
252
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module LLDB
6
+ class TypeMember
7
+ prepend NativeLifecycle
8
+
9
+ # @rbs context: Context?
10
+ # @rbs return: void
11
+ def initialize(ptr, context: nil)
12
+ initialize_native_object(
13
+ ptr,
14
+ release: ->(released) { FFIBindings.lldb_type_member_destroy(released) },
15
+ context: context
16
+ )
17
+ end
18
+
19
+ # @rbs return: bool
20
+ def valid?
21
+ !@ptr.null? && FFIBindings.lldb_type_member_is_valid(@ptr) != 0
22
+ end
23
+
24
+ # @rbs return: String?
25
+ def name
26
+ return nil unless valid?
27
+
28
+ FFIBindings.lldb_type_member_get_name(@ptr)
29
+ end
30
+
31
+ # @rbs return: Type?
32
+ def type
33
+ return nil unless valid?
34
+
35
+ ptr = FFIBindings.lldb_type_member_get_type(@ptr)
36
+ return nil if ptr.nil? || ptr.null?
37
+
38
+ Type.new(ptr, context: context)
39
+ end
40
+
41
+ # @rbs return: Integer
42
+ def offset_in_bytes
43
+ return 0 unless valid?
44
+
45
+ FFIBindings.lldb_type_member_get_offset_in_bytes(@ptr)
46
+ end
47
+
48
+ # @rbs return: Integer
49
+ def offset_in_bits
50
+ return 0 unless valid?
51
+
52
+ FFIBindings.lldb_type_member_get_offset_in_bits(@ptr)
53
+ end
54
+
55
+ # @rbs return: Integer
56
+ def bitfield_size_in_bits
57
+ return 0 unless valid?
58
+
59
+ FFIBindings.lldb_type_member_get_bitfield_size_in_bits(@ptr)
60
+ end
61
+
62
+ # @rbs return: FFI::Pointer
63
+ def to_ptr
64
+ @ptr
65
+ end
66
+ end
67
+ end