1 | //===-- Variable.cpp ------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Symbol/Variable.h" |
10 | |
11 | #include "lldb/Core/Module.h" |
12 | #include "lldb/Core/ValueObject.h" |
13 | #include "lldb/Core/ValueObjectVariable.h" |
14 | #include "lldb/Symbol/Block.h" |
15 | #include "lldb/Symbol/CompileUnit.h" |
16 | #include "lldb/Symbol/CompilerDecl.h" |
17 | #include "lldb/Symbol/CompilerDeclContext.h" |
18 | #include "lldb/Symbol/Function.h" |
19 | #include "lldb/Symbol/SymbolContext.h" |
20 | #include "lldb/Symbol/SymbolFile.h" |
21 | #include "lldb/Symbol/Type.h" |
22 | #include "lldb/Symbol/TypeSystem.h" |
23 | #include "lldb/Symbol/VariableList.h" |
24 | #include "lldb/Target/ABI.h" |
25 | #include "lldb/Target/Process.h" |
26 | #include "lldb/Target/RegisterContext.h" |
27 | #include "lldb/Target/StackFrame.h" |
28 | #include "lldb/Target/Target.h" |
29 | #include "lldb/Target/Thread.h" |
30 | #include "lldb/Utility/RegularExpression.h" |
31 | #include "lldb/Utility/Stream.h" |
32 | |
33 | #include "llvm/ADT/Twine.h" |
34 | |
35 | using namespace lldb; |
36 | using namespace lldb_private; |
37 | |
38 | Variable::Variable(lldb::user_id_t uid, const char *name, const char *mangled, |
39 | const lldb::SymbolFileTypeSP &symfile_type_sp, |
40 | ValueType scope, SymbolContextScope *context, |
41 | const RangeList &scope_range, Declaration *decl_ptr, |
42 | const DWARFExpressionList &location_list, bool external, |
43 | bool artificial, bool location_is_constant_data, |
44 | bool static_member) |
45 | : UserID(uid), m_name(name), m_mangled(ConstString(mangled)), |
46 | m_symfile_type_sp(symfile_type_sp), m_scope(scope), |
47 | m_owner_scope(context), m_scope_range(scope_range), |
48 | m_declaration(decl_ptr), m_location_list(location_list), m_external(external), |
49 | m_artificial(artificial), m_loc_is_const_data(location_is_constant_data), |
50 | m_static_member(static_member) {} |
51 | |
52 | Variable::~Variable() = default; |
53 | |
54 | lldb::LanguageType Variable::GetLanguage() const { |
55 | lldb::LanguageType lang = m_mangled.GuessLanguage(); |
56 | if (lang != lldb::eLanguageTypeUnknown) |
57 | return lang; |
58 | |
59 | if (auto *func = m_owner_scope->CalculateSymbolContextFunction()) { |
60 | if ((lang = func->GetLanguage()) != lldb::eLanguageTypeUnknown) |
61 | return lang; |
62 | } else if (auto *comp_unit = |
63 | m_owner_scope->CalculateSymbolContextCompileUnit()) { |
64 | if ((lang = comp_unit->GetLanguage()) != lldb::eLanguageTypeUnknown) |
65 | return lang; |
66 | } |
67 | |
68 | return lldb::eLanguageTypeUnknown; |
69 | } |
70 | |
71 | ConstString Variable::GetName() const { |
72 | ConstString name = m_mangled.GetName(); |
73 | if (name) |
74 | return name; |
75 | return m_name; |
76 | } |
77 | |
78 | ConstString Variable::GetUnqualifiedName() const { return m_name; } |
79 | |
80 | bool Variable::NameMatches(ConstString name) const { |
81 | if (m_name == name) |
82 | return true; |
83 | SymbolContext variable_sc; |
84 | m_owner_scope->CalculateSymbolContext(sc: &variable_sc); |
85 | |
86 | return m_mangled.NameMatches(name); |
87 | } |
88 | bool Variable::NameMatches(const RegularExpression ®ex) const { |
89 | if (regex.Execute(string: m_name.AsCString())) |
90 | return true; |
91 | if (m_mangled) |
92 | return m_mangled.NameMatches(regex); |
93 | return false; |
94 | } |
95 | |
96 | Type *Variable::GetType() { |
97 | if (m_symfile_type_sp) |
98 | return m_symfile_type_sp->GetType(); |
99 | return nullptr; |
100 | } |
101 | |
102 | void Variable::Dump(Stream *s, bool show_context) const { |
103 | s->Printf(format: "%p: " , static_cast<const void *>(this)); |
104 | s->Indent(); |
105 | *s << "Variable" << (const UserID &)*this; |
106 | |
107 | if (m_name) |
108 | *s << ", name = \"" << m_name << "\"" ; |
109 | |
110 | if (m_symfile_type_sp) { |
111 | Type *type = m_symfile_type_sp->GetType(); |
112 | if (type) { |
113 | s->Format(format: ", type = {{{0:x-16}} {1} (" , args: type->GetID(), args&: type); |
114 | type->DumpTypeName(s); |
115 | s->PutChar(ch: ')'); |
116 | } |
117 | } |
118 | |
119 | if (m_scope != eValueTypeInvalid) { |
120 | s->PutCString(cstr: ", scope = " ); |
121 | switch (m_scope) { |
122 | case eValueTypeVariableGlobal: |
123 | s->PutCString(cstr: m_external ? "global" : "static" ); |
124 | break; |
125 | case eValueTypeVariableArgument: |
126 | s->PutCString(cstr: "parameter" ); |
127 | break; |
128 | case eValueTypeVariableLocal: |
129 | s->PutCString(cstr: "local" ); |
130 | break; |
131 | case eValueTypeVariableThreadLocal: |
132 | s->PutCString(cstr: "thread local" ); |
133 | break; |
134 | default: |
135 | s->AsRawOstream() << "??? (" << m_scope << ')'; |
136 | } |
137 | } |
138 | |
139 | if (show_context && m_owner_scope != nullptr) { |
140 | s->PutCString(cstr: ", context = ( " ); |
141 | m_owner_scope->DumpSymbolContext(s); |
142 | s->PutCString(cstr: " )" ); |
143 | } |
144 | |
145 | bool show_fullpaths = false; |
146 | m_declaration.Dump(s, show_fullpaths); |
147 | |
148 | if (m_location_list.IsValid()) { |
149 | s->PutCString(cstr: ", location = " ); |
150 | ABISP abi; |
151 | if (m_owner_scope) { |
152 | ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule()); |
153 | if (module_sp) |
154 | abi = ABI::FindPlugin(process_sp: ProcessSP(), arch: module_sp->GetArchitecture()); |
155 | } |
156 | m_location_list.GetDescription(s, level: lldb::eDescriptionLevelBrief, abi: abi.get()); |
157 | } |
158 | |
159 | if (m_external) |
160 | s->PutCString(cstr: ", external" ); |
161 | |
162 | if (m_artificial) |
163 | s->PutCString(cstr: ", artificial" ); |
164 | |
165 | s->EOL(); |
166 | } |
167 | |
168 | bool Variable::DumpDeclaration(Stream *s, bool show_fullpaths, |
169 | bool show_module) { |
170 | bool dumped_declaration_info = false; |
171 | if (m_owner_scope) { |
172 | SymbolContext sc; |
173 | m_owner_scope->CalculateSymbolContext(sc: &sc); |
174 | sc.block = nullptr; |
175 | sc.line_entry.Clear(); |
176 | bool show_inlined_frames = false; |
177 | const bool show_function_arguments = true; |
178 | const bool show_function_name = true; |
179 | |
180 | dumped_declaration_info = sc.DumpStopContext( |
181 | s, exe_scope: nullptr, so_addr: Address(), show_fullpaths, show_module, show_inlined_frames, |
182 | show_function_arguments, show_function_name); |
183 | |
184 | if (sc.function) |
185 | s->PutChar(ch: ':'); |
186 | } |
187 | if (m_declaration.DumpStopContext(s, show_fullpaths: false)) |
188 | dumped_declaration_info = true; |
189 | return dumped_declaration_info; |
190 | } |
191 | |
192 | size_t Variable::MemorySize() const { return sizeof(Variable); } |
193 | |
194 | CompilerDeclContext Variable::GetDeclContext() { |
195 | Type *type = GetType(); |
196 | if (type) |
197 | return type->GetSymbolFile()->GetDeclContextContainingUID(uid: GetID()); |
198 | return CompilerDeclContext(); |
199 | } |
200 | |
201 | CompilerDecl Variable::GetDecl() { |
202 | Type *type = GetType(); |
203 | return type ? type->GetSymbolFile()->GetDeclForUID(uid: GetID()) : CompilerDecl(); |
204 | } |
205 | |
206 | void Variable::CalculateSymbolContext(SymbolContext *sc) { |
207 | if (m_owner_scope) { |
208 | m_owner_scope->CalculateSymbolContext(sc); |
209 | sc->variable = this; |
210 | } else |
211 | sc->Clear(clear_target: false); |
212 | } |
213 | |
214 | bool Variable::LocationIsValidForFrame(StackFrame *frame) { |
215 | if (frame) { |
216 | Function *function = |
217 | frame->GetSymbolContext(resolve_scope: eSymbolContextFunction).function; |
218 | if (function) { |
219 | TargetSP target_sp(frame->CalculateTarget()); |
220 | |
221 | addr_t loclist_base_load_addr = |
222 | function->GetAddressRange().GetBaseAddress().GetLoadAddress( |
223 | target: target_sp.get()); |
224 | if (loclist_base_load_addr == LLDB_INVALID_ADDRESS) |
225 | return false; |
226 | // It is a location list. We just need to tell if the location list |
227 | // contains the current address when converted to a load address |
228 | return m_location_list.ContainsAddress( |
229 | func_load_addr: loclist_base_load_addr, |
230 | addr: frame->GetFrameCodeAddressForSymbolication().GetLoadAddress( |
231 | target: target_sp.get())); |
232 | } |
233 | } |
234 | return false; |
235 | } |
236 | |
237 | bool Variable::LocationIsValidForAddress(const Address &address) { |
238 | // Be sure to resolve the address to section offset prior to calling this |
239 | // function. |
240 | if (address.IsSectionOffset()) { |
241 | // We need to check if the address is valid for both scope range and value |
242 | // range. |
243 | // Empty scope range means block range. |
244 | bool valid_in_scope_range = |
245 | GetScopeRange().IsEmpty() || GetScopeRange().FindEntryThatContains( |
246 | addr: address.GetFileAddress()) != nullptr; |
247 | if (!valid_in_scope_range) |
248 | return false; |
249 | SymbolContext sc; |
250 | CalculateSymbolContext(sc: &sc); |
251 | if (sc.module_sp == address.GetModule()) { |
252 | // Is the variable is described by a single location? |
253 | if (m_location_list.IsAlwaysValidSingleExpr()) { |
254 | // Yes it is, the location is valid. |
255 | return true; |
256 | } |
257 | |
258 | if (sc.function) { |
259 | addr_t loclist_base_file_addr = |
260 | sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); |
261 | if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) |
262 | return false; |
263 | // It is a location list. We just need to tell if the location list |
264 | // contains the current address when converted to a load address |
265 | return m_location_list.ContainsAddress(func_load_addr: loclist_base_file_addr, |
266 | addr: address.GetFileAddress()); |
267 | } |
268 | } |
269 | } |
270 | return false; |
271 | } |
272 | |
273 | bool Variable::IsInScope(StackFrame *frame) { |
274 | switch (m_scope) { |
275 | case eValueTypeRegister: |
276 | case eValueTypeRegisterSet: |
277 | return frame != nullptr; |
278 | |
279 | case eValueTypeConstResult: |
280 | case eValueTypeVariableGlobal: |
281 | case eValueTypeVariableStatic: |
282 | case eValueTypeVariableThreadLocal: |
283 | return true; |
284 | |
285 | case eValueTypeVariableArgument: |
286 | case eValueTypeVariableLocal: |
287 | if (frame) { |
288 | // We don't have a location list, we just need to see if the block that |
289 | // this variable was defined in is currently |
290 | Block *deepest_frame_block = |
291 | frame->GetSymbolContext(resolve_scope: eSymbolContextBlock).block; |
292 | if (deepest_frame_block) { |
293 | SymbolContext variable_sc; |
294 | CalculateSymbolContext(sc: &variable_sc); |
295 | |
296 | // Check for static or global variable defined at the compile unit |
297 | // level that wasn't defined in a block |
298 | if (variable_sc.block == nullptr) |
299 | return true; |
300 | |
301 | // Check if the variable is valid in the current block |
302 | if (variable_sc.block != deepest_frame_block && |
303 | !variable_sc.block->Contains(block: deepest_frame_block)) |
304 | return false; |
305 | |
306 | // If no scope range is specified then it means that the scope is the |
307 | // same as the scope of the enclosing lexical block. |
308 | if (m_scope_range.IsEmpty()) |
309 | return true; |
310 | |
311 | addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress(); |
312 | return m_scope_range.FindEntryThatContains(addr: file_address) != nullptr; |
313 | } |
314 | } |
315 | break; |
316 | |
317 | default: |
318 | break; |
319 | } |
320 | return false; |
321 | } |
322 | |
323 | Status Variable::GetValuesForVariableExpressionPath( |
324 | llvm::StringRef variable_expr_path, ExecutionContextScope *scope, |
325 | GetVariableCallback callback, void *baton, VariableList &variable_list, |
326 | ValueObjectList &valobj_list) { |
327 | Status error; |
328 | if (!callback || variable_expr_path.empty()) { |
329 | error.SetErrorString("unknown error" ); |
330 | return error; |
331 | } |
332 | |
333 | switch (variable_expr_path.front()) { |
334 | case '*': |
335 | error = Variable::GetValuesForVariableExpressionPath( |
336 | variable_expr_path: variable_expr_path.drop_front(), scope, callback, baton, variable_list, |
337 | valobj_list); |
338 | if (error.Fail()) { |
339 | error.SetErrorString("unknown error" ); |
340 | return error; |
341 | } |
342 | for (uint32_t i = 0; i < valobj_list.GetSize();) { |
343 | Status tmp_error; |
344 | ValueObjectSP valobj_sp( |
345 | valobj_list.GetValueObjectAtIndex(idx: i)->Dereference(error&: tmp_error)); |
346 | if (tmp_error.Fail()) { |
347 | variable_list.RemoveVariableAtIndex(idx: i); |
348 | valobj_list.RemoveValueObjectAtIndex(idx: i); |
349 | } else { |
350 | valobj_list.SetValueObjectAtIndex(idx: i, valobj_sp); |
351 | ++i; |
352 | } |
353 | } |
354 | return error; |
355 | case '&': { |
356 | error = Variable::GetValuesForVariableExpressionPath( |
357 | variable_expr_path: variable_expr_path.drop_front(), scope, callback, baton, variable_list, |
358 | valobj_list); |
359 | if (error.Success()) { |
360 | for (uint32_t i = 0; i < valobj_list.GetSize();) { |
361 | Status tmp_error; |
362 | ValueObjectSP valobj_sp( |
363 | valobj_list.GetValueObjectAtIndex(idx: i)->AddressOf(error&: tmp_error)); |
364 | if (tmp_error.Fail()) { |
365 | variable_list.RemoveVariableAtIndex(idx: i); |
366 | valobj_list.RemoveValueObjectAtIndex(idx: i); |
367 | } else { |
368 | valobj_list.SetValueObjectAtIndex(idx: i, valobj_sp); |
369 | ++i; |
370 | } |
371 | } |
372 | } else { |
373 | error.SetErrorString("unknown error" ); |
374 | } |
375 | return error; |
376 | } break; |
377 | |
378 | default: { |
379 | static RegularExpression g_regex( |
380 | llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)" )); |
381 | llvm::SmallVector<llvm::StringRef, 2> matches; |
382 | variable_list.Clear(); |
383 | if (!g_regex.Execute(string: variable_expr_path, matches: &matches)) { |
384 | error.SetErrorStringWithFormatv( |
385 | format: "unable to extract a variable name from '{0}'" , args&: variable_expr_path); |
386 | return error; |
387 | } |
388 | std::string variable_name = matches[1].str(); |
389 | if (!callback(baton, variable_name.c_str(), variable_list)) { |
390 | error.SetErrorString("unknown error" ); |
391 | return error; |
392 | } |
393 | uint32_t i = 0; |
394 | while (i < variable_list.GetSize()) { |
395 | VariableSP var_sp(variable_list.GetVariableAtIndex(idx: i)); |
396 | ValueObjectSP valobj_sp; |
397 | if (!var_sp) { |
398 | variable_list.RemoveVariableAtIndex(idx: i); |
399 | continue; |
400 | } |
401 | ValueObjectSP variable_valobj_sp( |
402 | ValueObjectVariable::Create(exe_scope: scope, var_sp)); |
403 | if (!variable_valobj_sp) { |
404 | variable_list.RemoveVariableAtIndex(idx: i); |
405 | continue; |
406 | } |
407 | |
408 | llvm::StringRef variable_sub_expr_path = |
409 | variable_expr_path.drop_front(N: variable_name.size()); |
410 | if (!variable_sub_expr_path.empty()) { |
411 | valobj_sp = variable_valobj_sp->GetValueForExpressionPath( |
412 | expression: variable_sub_expr_path); |
413 | if (!valobj_sp) { |
414 | error.SetErrorStringWithFormatv( |
415 | format: "invalid expression path '{0}' for variable '{1}'" , |
416 | args&: variable_sub_expr_path, args: var_sp->GetName().GetCString()); |
417 | variable_list.RemoveVariableAtIndex(idx: i); |
418 | continue; |
419 | } |
420 | } else { |
421 | // Just the name of a variable with no extras |
422 | valobj_sp = variable_valobj_sp; |
423 | } |
424 | |
425 | valobj_list.Append(val_obj_sp: valobj_sp); |
426 | ++i; |
427 | } |
428 | |
429 | if (variable_list.GetSize() > 0) { |
430 | error.Clear(); |
431 | return error; |
432 | } |
433 | } break; |
434 | } |
435 | error.SetErrorString("unknown error" ); |
436 | return error; |
437 | } |
438 | |
439 | bool Variable::DumpLocations(Stream *s, const Address &address) { |
440 | SymbolContext sc; |
441 | CalculateSymbolContext(sc: &sc); |
442 | ABISP abi; |
443 | if (m_owner_scope) { |
444 | ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule()); |
445 | if (module_sp) |
446 | abi = ABI::FindPlugin(process_sp: ProcessSP(), arch: module_sp->GetArchitecture()); |
447 | } |
448 | |
449 | const addr_t file_addr = address.GetFileAddress(); |
450 | if (sc.function) { |
451 | addr_t loclist_base_file_addr = |
452 | sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); |
453 | if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) |
454 | return false; |
455 | return m_location_list.DumpLocations(s, level: eDescriptionLevelBrief, |
456 | func_load_addr: loclist_base_file_addr, file_addr, |
457 | abi: abi.get()); |
458 | } |
459 | return false; |
460 | } |
461 | |
462 | static void PrivateAutoComplete( |
463 | StackFrame *frame, llvm::StringRef partial_path, |
464 | const llvm::Twine |
465 | &prefix_path, // Anything that has been resolved already will be in here |
466 | const CompilerType &compiler_type, CompletionRequest &request); |
467 | |
468 | static void PrivateAutoCompleteMembers( |
469 | StackFrame *frame, const std::string &partial_member_name, |
470 | llvm::StringRef partial_path, |
471 | const llvm::Twine |
472 | &prefix_path, // Anything that has been resolved already will be in here |
473 | const CompilerType &compiler_type, CompletionRequest &request) { |
474 | |
475 | // We are in a type parsing child members |
476 | const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses(); |
477 | |
478 | if (num_bases > 0) { |
479 | for (uint32_t i = 0; i < num_bases; ++i) { |
480 | CompilerType base_class_type = |
481 | compiler_type.GetDirectBaseClassAtIndex(idx: i, bit_offset_ptr: nullptr); |
482 | |
483 | PrivateAutoCompleteMembers(frame, partial_member_name, partial_path, |
484 | prefix_path, |
485 | compiler_type: base_class_type.GetCanonicalType(), request); |
486 | } |
487 | } |
488 | |
489 | const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses(); |
490 | |
491 | if (num_vbases > 0) { |
492 | for (uint32_t i = 0; i < num_vbases; ++i) { |
493 | CompilerType vbase_class_type = |
494 | compiler_type.GetVirtualBaseClassAtIndex(idx: i, bit_offset_ptr: nullptr); |
495 | |
496 | PrivateAutoCompleteMembers(frame, partial_member_name, partial_path, |
497 | prefix_path, |
498 | compiler_type: vbase_class_type.GetCanonicalType(), request); |
499 | } |
500 | } |
501 | |
502 | // We are in a type parsing child members |
503 | const uint32_t num_fields = compiler_type.GetNumFields(); |
504 | |
505 | if (num_fields > 0) { |
506 | for (uint32_t i = 0; i < num_fields; ++i) { |
507 | std::string member_name; |
508 | |
509 | CompilerType member_compiler_type = compiler_type.GetFieldAtIndex( |
510 | idx: i, name&: member_name, bit_offset_ptr: nullptr, bitfield_bit_size_ptr: nullptr, is_bitfield_ptr: nullptr); |
511 | |
512 | if (partial_member_name.empty() || |
513 | llvm::StringRef(member_name).starts_with(Prefix: partial_member_name)) { |
514 | if (member_name == partial_member_name) { |
515 | PrivateAutoComplete( |
516 | frame, partial_path, |
517 | prefix_path: prefix_path + member_name, // Anything that has been resolved |
518 | // already will be in here |
519 | compiler_type: member_compiler_type.GetCanonicalType(), request); |
520 | } else { |
521 | request.AddCompletion(completion: (prefix_path + member_name).str()); |
522 | } |
523 | } |
524 | } |
525 | } |
526 | } |
527 | |
528 | static void PrivateAutoComplete( |
529 | StackFrame *frame, llvm::StringRef partial_path, |
530 | const llvm::Twine |
531 | &prefix_path, // Anything that has been resolved already will be in here |
532 | const CompilerType &compiler_type, CompletionRequest &request) { |
533 | // printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path = |
534 | // '%s'\n", prefix_path.c_str(), partial_path.c_str()); |
535 | std::string remaining_partial_path; |
536 | |
537 | const lldb::TypeClass type_class = compiler_type.GetTypeClass(); |
538 | if (partial_path.empty()) { |
539 | if (compiler_type.IsValid()) { |
540 | switch (type_class) { |
541 | default: |
542 | case eTypeClassArray: |
543 | case eTypeClassBlockPointer: |
544 | case eTypeClassBuiltin: |
545 | case eTypeClassComplexFloat: |
546 | case eTypeClassComplexInteger: |
547 | case eTypeClassEnumeration: |
548 | case eTypeClassFunction: |
549 | case eTypeClassMemberPointer: |
550 | case eTypeClassReference: |
551 | case eTypeClassTypedef: |
552 | case eTypeClassVector: { |
553 | request.AddCompletion(completion: prefix_path.str()); |
554 | } break; |
555 | |
556 | case eTypeClassClass: |
557 | case eTypeClassStruct: |
558 | case eTypeClassUnion: |
559 | if (prefix_path.str().back() != '.') |
560 | request.AddCompletion(completion: (prefix_path + "." ).str()); |
561 | break; |
562 | |
563 | case eTypeClassObjCObject: |
564 | case eTypeClassObjCInterface: |
565 | break; |
566 | case eTypeClassObjCObjectPointer: |
567 | case eTypeClassPointer: { |
568 | bool omit_empty_base_classes = true; |
569 | if (compiler_type.GetNumChildren(omit_empty_base_classes, exe_ctx: nullptr) > 0) |
570 | request.AddCompletion(completion: (prefix_path + "->" ).str()); |
571 | else { |
572 | request.AddCompletion(completion: prefix_path.str()); |
573 | } |
574 | } break; |
575 | } |
576 | } else { |
577 | if (frame) { |
578 | const bool get_file_globals = true; |
579 | |
580 | VariableList *variable_list = frame->GetVariableList(get_file_globals, |
581 | error_ptr: nullptr); |
582 | |
583 | if (variable_list) { |
584 | for (const VariableSP &var_sp : *variable_list) |
585 | request.AddCompletion(completion: var_sp->GetName().AsCString()); |
586 | } |
587 | } |
588 | } |
589 | } else { |
590 | const char ch = partial_path[0]; |
591 | switch (ch) { |
592 | case '*': |
593 | if (prefix_path.str().empty()) { |
594 | PrivateAutoComplete(frame, partial_path: partial_path.substr(Start: 1), prefix_path: "*" , compiler_type, |
595 | request); |
596 | } |
597 | break; |
598 | |
599 | case '&': |
600 | if (prefix_path.isTriviallyEmpty()) { |
601 | PrivateAutoComplete(frame, partial_path: partial_path.substr(Start: 1), prefix_path: std::string("&" ), |
602 | compiler_type, request); |
603 | } |
604 | break; |
605 | |
606 | case '-': |
607 | if (partial_path.size() > 1 && partial_path[1] == '>' && |
608 | !prefix_path.str().empty()) { |
609 | switch (type_class) { |
610 | case lldb::eTypeClassPointer: { |
611 | CompilerType pointee_type(compiler_type.GetPointeeType()); |
612 | if (partial_path.size() > 2 && partial_path[2]) { |
613 | // If there is more after the "->", then search deeper |
614 | PrivateAutoComplete(frame, partial_path: partial_path.substr(Start: 2), |
615 | prefix_path: prefix_path + "->" , |
616 | compiler_type: pointee_type.GetCanonicalType(), request); |
617 | } else { |
618 | // Nothing after the "->", so list all members |
619 | PrivateAutoCompleteMembers( |
620 | frame, partial_member_name: std::string(), partial_path: std::string(), prefix_path: prefix_path + "->" , |
621 | compiler_type: pointee_type.GetCanonicalType(), request); |
622 | } |
623 | } break; |
624 | default: |
625 | break; |
626 | } |
627 | } |
628 | break; |
629 | |
630 | case '.': |
631 | if (compiler_type.IsValid()) { |
632 | switch (type_class) { |
633 | case lldb::eTypeClassUnion: |
634 | case lldb::eTypeClassStruct: |
635 | case lldb::eTypeClassClass: |
636 | if (partial_path.size() > 1 && partial_path[1]) { |
637 | // If there is more after the ".", then search deeper |
638 | PrivateAutoComplete(frame, partial_path: partial_path.substr(Start: 1), |
639 | prefix_path: prefix_path + "." , compiler_type, request); |
640 | |
641 | } else { |
642 | // Nothing after the ".", so list all members |
643 | PrivateAutoCompleteMembers(frame, partial_member_name: std::string(), partial_path, |
644 | prefix_path: prefix_path + "." , compiler_type, |
645 | request); |
646 | } |
647 | break; |
648 | default: |
649 | break; |
650 | } |
651 | } |
652 | break; |
653 | default: |
654 | if (isalpha(ch) || ch == '_' || ch == '$') { |
655 | const size_t partial_path_len = partial_path.size(); |
656 | size_t pos = 1; |
657 | while (pos < partial_path_len) { |
658 | const char curr_ch = partial_path[pos]; |
659 | if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') { |
660 | ++pos; |
661 | continue; |
662 | } |
663 | break; |
664 | } |
665 | |
666 | std::string token(std::string(partial_path), 0, pos); |
667 | remaining_partial_path = std::string(partial_path.substr(Start: pos)); |
668 | |
669 | if (compiler_type.IsValid()) { |
670 | PrivateAutoCompleteMembers(frame, partial_member_name: token, partial_path: remaining_partial_path, |
671 | prefix_path, compiler_type, request); |
672 | } else if (frame) { |
673 | // We haven't found our variable yet |
674 | const bool get_file_globals = true; |
675 | |
676 | VariableList *variable_list = |
677 | frame->GetVariableList(get_file_globals, error_ptr: nullptr); |
678 | |
679 | if (!variable_list) |
680 | break; |
681 | |
682 | for (VariableSP var_sp : *variable_list) { |
683 | |
684 | if (!var_sp) |
685 | continue; |
686 | |
687 | llvm::StringRef variable_name = var_sp->GetName().GetStringRef(); |
688 | if (variable_name.starts_with(Prefix: token)) { |
689 | if (variable_name == token) { |
690 | Type *variable_type = var_sp->GetType(); |
691 | if (variable_type) { |
692 | CompilerType variable_compiler_type( |
693 | variable_type->GetForwardCompilerType()); |
694 | PrivateAutoComplete( |
695 | frame, partial_path: remaining_partial_path, |
696 | prefix_path: prefix_path + token, // Anything that has been resolved |
697 | // already will be in here |
698 | compiler_type: variable_compiler_type.GetCanonicalType(), request); |
699 | } else { |
700 | request.AddCompletion(completion: (prefix_path + variable_name).str()); |
701 | } |
702 | } else if (remaining_partial_path.empty()) { |
703 | request.AddCompletion(completion: (prefix_path + variable_name).str()); |
704 | } |
705 | } |
706 | } |
707 | } |
708 | } |
709 | break; |
710 | } |
711 | } |
712 | } |
713 | |
714 | void Variable::AutoComplete(const ExecutionContext &exe_ctx, |
715 | CompletionRequest &request) { |
716 | CompilerType compiler_type; |
717 | |
718 | PrivateAutoComplete(frame: exe_ctx.GetFramePtr(), partial_path: request.GetCursorArgumentPrefix(), |
719 | prefix_path: "" , compiler_type, request); |
720 | } |
721 | |