11
11
#include " Plugins/TypeSystem/Clang/TypeSystemClang.h"
12
12
#include " lldb/Symbol/Function.h"
13
13
#include " lldb/Symbol/VariableList.h"
14
- #include " lldb/Utility/LLDBLog.h"
15
- #include " lldb/Utility/Log.h"
16
14
17
15
using namespace lldb ;
18
16
using namespace lldb_private ;
19
17
using namespace lldb_private ::formatters;
20
18
21
- static lldb::addr_t GetCoroFramePtrFromHandle (ValueObjectSP valobj_sp ) {
22
- if (!valobj_sp )
19
+ static lldb::addr_t GetCoroFramePtrFromHandle (ValueObjectSP corohandle_sp ) {
20
+ if (!corohandle_sp )
23
21
return LLDB_INVALID_ADDRESS;
24
22
25
23
// We expect a single pointer in the `coroutine_handle` class.
26
24
// We don't care about its name.
27
- if (valobj_sp ->GetNumChildrenIgnoringErrors () != 1 )
25
+ if (corohandle_sp ->GetNumChildrenIgnoringErrors () != 1 )
28
26
return LLDB_INVALID_ADDRESS;
29
- ValueObjectSP ptr_sp (valobj_sp ->GetChildAtIndex (0 ));
27
+ ValueObjectSP ptr_sp (corohandle_sp ->GetChildAtIndex (0 ));
30
28
if (!ptr_sp)
31
29
return LLDB_INVALID_ADDRESS;
32
30
if (!ptr_sp->GetCompilerType ().IsPointerType ())
@@ -62,19 +60,20 @@ static Function *ExtractDestroyFunction(lldb::TargetSP target_sp,
62
60
return destroy_func_address.CalculateSymbolContextFunction ();
63
61
}
64
62
65
- static CompilerType InferPromiseType (Function &destroy_func) {
63
+ // clang generates aritifical `__promise` and `__coro_frame` variables inside
64
+ // the destroy function. Look for those variables and extract their type.
65
+ static CompilerType InferArtificialCoroType (Function &destroy_func,
66
+ ConstString var_name) {
66
67
Block &block = destroy_func.GetBlock (true );
67
68
auto variable_list = block.GetBlockVariableList (true );
68
69
69
- // clang generates an artificial `__promise` variable inside the
70
- // `destroy` function. Look for it.
71
- auto promise_var = variable_list->FindVariable (ConstString (" __promise" ));
72
- if (!promise_var)
70
+ auto var = variable_list->FindVariable (var_name);
71
+ if (!var)
73
72
return {};
74
- if (!promise_var ->IsArtificial ())
73
+ if (!var ->IsArtificial ())
75
74
return {};
76
75
77
- Type *promise_type = promise_var ->GetType ();
76
+ Type *promise_type = var ->GetType ();
78
77
if (!promise_type)
79
78
return {};
80
79
return promise_type->GetForwardCompilerType ();
@@ -108,30 +107,17 @@ lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
108
107
109
108
llvm::Expected<uint32_t > lldb_private::formatters::
110
109
StdlibCoroutineHandleSyntheticFrontEnd::CalculateNumChildren () {
111
- if (!m_resume_ptr_sp || !m_destroy_ptr_sp)
112
- return 0 ;
113
-
114
- return m_promise_ptr_sp ? 3 : 2 ;
110
+ return m_children.size ();
115
111
}
116
112
117
113
lldb::ValueObjectSP lldb_private::formatters::
118
114
StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex (uint32_t idx) {
119
- switch (idx) {
120
- case 0 :
121
- return m_resume_ptr_sp;
122
- case 1 :
123
- return m_destroy_ptr_sp;
124
- case 2 :
125
- return m_promise_ptr_sp;
126
- }
127
- return lldb::ValueObjectSP ();
115
+ return idx < m_children.size () ? m_children[idx] : lldb::ValueObjectSP ();
128
116
}
129
117
130
118
lldb::ChildCacheState
131
119
lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::Update () {
132
- m_resume_ptr_sp.reset ();
133
- m_destroy_ptr_sp.reset ();
134
- m_promise_ptr_sp.reset ();
120
+ m_children.clear ();
135
121
136
122
ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue ();
137
123
if (!valobj_sp)
@@ -141,77 +127,77 @@ lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::Update() {
141
127
if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)
142
128
return lldb::ChildCacheState::eRefetch;
143
129
144
- auto ts = valobj_sp->GetCompilerType ().GetTypeSystem ();
145
- auto ast_ctx = ts.dyn_cast_or_null <TypeSystemClang>();
146
- if (!ast_ctx)
147
- return lldb::ChildCacheState::eRefetch;
148
-
149
- // Create the `resume` and `destroy` children.
150
130
lldb::TargetSP target_sp = m_backend.GetTargetSP ();
151
131
auto &exe_ctx = m_backend.GetExecutionContextRef ();
152
132
lldb::ProcessSP process_sp = target_sp->GetProcessSP ();
153
133
auto ptr_size = process_sp->GetAddressByteSize ();
154
- CompilerType void_type = ast_ctx->GetBasicType (lldb::eBasicTypeVoid);
155
- CompilerType coro_func_type = ast_ctx->CreateFunctionType (
156
- /* result_type=*/ void_type, /* args=*/ &void_type, /* num_args=*/ 1 ,
157
- /* is_variadic=*/ false , /* qualifiers=*/ 0 );
158
- CompilerType coro_func_ptr_type = coro_func_type.GetPointerType ();
159
- m_resume_ptr_sp = CreateValueObjectFromAddress (
160
- " resume" , frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);
161
- lldbassert (m_resume_ptr_sp);
162
- m_destroy_ptr_sp = CreateValueObjectFromAddress (
163
- " destroy" , frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);
164
- lldbassert (m_destroy_ptr_sp);
165
-
166
- // Get the `promise_type` from the template argument
167
- CompilerType promise_type (
168
- valobj_sp->GetCompilerType ().GetTypeTemplateArgument (0 ));
169
- if (!promise_type)
134
+ auto ts = valobj_sp->GetCompilerType ().GetTypeSystem ();
135
+ auto ast_ctx = ts.dyn_cast_or_null <TypeSystemClang>();
136
+ if (!ast_ctx)
170
137
return lldb::ChildCacheState::eRefetch;
171
138
172
- // Try to infer the promise_type if it was type-erased
139
+ // Determine the coroutine frame type and the promise type. Fall back
140
+ // to `void`, since even the pointer itself might be useful, even if the
141
+ // type inference failed.
142
+ Function *destroy_func = ExtractDestroyFunction (target_sp, frame_ptr_addr);
143
+ CompilerType void_type = ast_ctx->GetBasicType (lldb::eBasicTypeVoid);
144
+ CompilerType promise_type;
145
+ if (CompilerType template_argt =
146
+ valobj_sp->GetCompilerType ().GetTypeTemplateArgument (0 ))
147
+ promise_type = std::move (template_argt);
173
148
if (promise_type.IsVoidType ()) {
174
- if (Function *destroy_func =
175
- ExtractDestroyFunction (target_sp, frame_ptr_addr)) {
176
- if (CompilerType inferred_type = InferPromiseType (*destroy_func)) {
149
+ // Try to infer the promise_type if it was type-erased
150
+ if (destroy_func) {
151
+ if (CompilerType inferred_type = InferArtificialCoroType (
152
+ *destroy_func, ConstString (" __promise" ))) {
177
153
promise_type = inferred_type;
178
154
}
179
155
}
180
156
}
157
+ CompilerType coro_frame_type =
158
+ InferArtificialCoroType (*destroy_func, ConstString (" __coro_frame" ));
159
+ if (!coro_frame_type)
160
+ coro_frame_type = void_type;
181
161
182
- // If we don't know the promise type, we don't display the `promise` member.
183
- // `CreateValueObjectFromAddress` below would fail for `void` types.
184
- if (promise_type.IsVoidType ()) {
185
- return lldb::ChildCacheState::eRefetch;
186
- }
187
-
188
- // Add the `promise` member. We intentionally add `promise` as a pointer type
189
- // instead of a value type, and don't automatically dereference this pointer.
190
- // We do so to avoid potential very deep recursion in case there is a cycle
191
- // formed between `std::coroutine_handle`s and their promises.
192
- lldb::ValueObjectSP promise = CreateValueObjectFromAddress (
193
- " promise" , frame_ptr_addr + 2 * ptr_size, exe_ctx, promise_type);
194
- Status error;
195
- lldb::ValueObjectSP promisePtr = promise->AddressOf (error);
196
- if (error.Success ())
197
- m_promise_ptr_sp = promisePtr->Clone (ConstString (" promise" ));
162
+ // Create the `resume` and `destroy` children.
163
+ CompilerType coro_func_type = ast_ctx->CreateFunctionType (
164
+ /* result_type=*/ void_type, /* args=*/ &coro_frame_type, /* num_args=*/ 1 ,
165
+ /* is_variadic=*/ false , /* qualifiers=*/ 0 );
166
+ CompilerType coro_func_ptr_type = coro_func_type.GetPointerType ();
167
+ ValueObjectSP resume_ptr_sp = CreateValueObjectFromAddress (
168
+ " resume" , frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);
169
+ lldbassert (resume_ptr_sp);
170
+ m_children.push_back (std::move (resume_ptr_sp));
171
+ ValueObjectSP destroy_ptr_sp = CreateValueObjectFromAddress (
172
+ " destroy" , frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);
173
+ lldbassert (destroy_ptr_sp);
174
+ m_children.push_back (std::move (destroy_ptr_sp));
175
+
176
+ // Add promise and coro_frame
177
+ // Add the `promise` and `coro_frame` member. We intentionally add them as
178
+ // pointer types instead of a value type, and don't automatically dereference
179
+ // those pointers. We do so to avoid potential very deep recursion in case
180
+ // there is a cycle formed between `std::coroutine_handle`s and their
181
+ // promises.
182
+ ValueObjectSP promise_ptr_sp = CreateValueObjectFromAddress (
183
+ " promise" , frame_ptr_addr + 2 * ptr_size, exe_ctx,
184
+ promise_type.GetPointerType (), /* do_deref=*/ false );
185
+ m_children.push_back (std::move (promise_ptr_sp));
186
+ ValueObjectSP coroframe_ptr_sp = CreateValueObjectFromAddress (
187
+ " coro_frame" , frame_ptr_addr, exe_ctx, coro_frame_type);
188
+ m_children.push_back (std::move (coroframe_ptr_sp));
198
189
199
190
return lldb::ChildCacheState::eRefetch;
200
191
}
201
192
202
193
llvm::Expected<size_t >
203
194
StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName (
204
195
ConstString name) {
205
- if (!m_resume_ptr_sp || !m_destroy_ptr_sp)
206
- return llvm::createStringError (" Type has no child named '%s'" ,
207
- name.AsCString ());
208
-
209
- if (name == ConstString (" resume" ))
210
- return 0 ;
211
- if (name == ConstString (" destroy" ))
212
- return 1 ;
213
- if (name == ConstString (" promise_ptr" ) && m_promise_ptr_sp)
214
- return 2 ;
196
+ for (size_t i = 0 , limit = m_children.size (); i < limit; ++i) {
197
+ if (m_children[i]->GetName () == name) {
198
+ return i;
199
+ }
200
+ }
215
201
216
202
return llvm::createStringError (" Type has no child named '%s'" ,
217
203
name.AsCString ());
0 commit comments