13c68c9d17d082f03ab58408c65c3df5e0e402f1
[sdk/tools/netcoredbg.git] / src / debug / netcoredbg / variables.cpp
1 // Copyright (c) 2017 Samsung Electronics Co., LTD
2 // Distributed under the MIT License.
3 // See the LICENSE file in the project root for more information.
4
5 #include "manageddebugger.h"
6
7 #include <unordered_set>
8 #include <vector>
9
10 #include "typeprinter.h"
11 #include "valueprint.h"
12 #include "frames.h"
13
14
15 HRESULT Variables::GetNumChild(
16     ICorDebugValue *pValue,
17     unsigned int &numchild,
18     bool static_members)
19 {
20     HRESULT Status = S_OK;
21     numchild = 0;
22
23     ULONG numstatic = 0;
24     ULONG numinstance = 0;
25
26     if (pValue == nullptr)
27         return 0;
28
29     IfFailRet(m_evaluator.WalkMembers(pValue, nullptr, nullptr, [&numstatic, &numinstance](
30         mdMethodDef,
31         ICorDebugModule *,
32         ICorDebugType *,
33         ICorDebugValue *,
34         bool is_static,
35         const std::string &)
36     {
37         if (is_static)
38             numstatic++;
39         else
40             numinstance++;
41         return S_OK;
42     }));
43
44     if (static_members)
45     {
46         numchild = numstatic;
47     }
48     else
49     {
50         numchild = (numstatic > 0) ? numinstance + 1 : numinstance;
51     }
52     return S_OK;
53 }
54
55 struct Variables::Member
56 {
57     std::string name;
58     std::string ownerType;
59     ToRelease<ICorDebugValue> value;
60     Member(const std::string &name, const std::string ownerType, ToRelease<ICorDebugValue> value) :
61         name(name),
62         ownerType(ownerType),
63         value(std::move(value))
64     {}
65     Member(Member &&that) = default;
66     Member(const Member &that) = delete;
67 };
68
69 HRESULT Variables::FetchFieldsAndProperties(
70     ICorDebugValue *pInputValue,
71     ICorDebugThread *pThread,
72     ICorDebugILFrame *pILFrame,
73     std::vector<Member> &members,
74     bool fetchOnlyStatic,
75     bool &hasStaticMembers,
76     int childStart,
77     int childEnd)
78 {
79     hasStaticMembers = false;
80     HRESULT Status;
81
82     DWORD threadId = 0;
83     IfFailRet(pThread->GetID(&threadId));
84
85     int currentIndex = -1;
86
87     IfFailRet(m_evaluator.WalkMembers(pInputValue, pThread, pILFrame, [&](
88         mdMethodDef mdGetter,
89         ICorDebugModule *pModule,
90         ICorDebugType *pType,
91         ICorDebugValue *pValue,
92         bool is_static,
93         const std::string &name)
94     {
95         if (is_static)
96             hasStaticMembers = true;
97
98         bool addMember = fetchOnlyStatic ? is_static : !is_static;
99         if (!addMember)
100             return S_OK;
101
102         ++currentIndex;
103         if (currentIndex < childStart)
104             return S_OK;
105         if (currentIndex >= childEnd)
106             return S_OK;
107
108         std::string className;
109         if (pType)
110             TypePrinter::GetTypeOfValue(pType, className);
111
112         ToRelease<ICorDebugValue> pResultValue;
113
114         if (mdGetter != mdMethodDefNil)
115         {
116             ToRelease<ICorDebugFunction> pFunc;
117             if (SUCCEEDED(pModule->GetFunctionFromToken(mdGetter, &pFunc)))
118                 m_evaluator.EvalFunction(pThread, pFunc, pType, is_static ? nullptr : pInputValue, &pResultValue);
119         }
120         else
121         {
122             if (pValue)
123                 pValue->AddRef();
124             pResultValue = pValue;
125         }
126
127         members.emplace_back(name, className, std::move(pResultValue));
128         return S_OK;
129     }));
130
131     return S_OK;
132 }
133
134 int ManagedDebugger::GetNamedVariables(uint32_t variablesReference)
135 {
136     return m_variables.GetNamedVariables(variablesReference);
137 }
138
139 int Variables::GetNamedVariables(uint32_t variablesReference)
140 {
141     auto it = m_variables.find(variablesReference);
142     if (it == m_variables.end())
143         return 0;
144     return it->second.namedVariables;
145 }
146
147 HRESULT ManagedDebugger::GetVariables(
148     uint32_t variablesReference,
149     VariablesFilter filter,
150     int start,
151     int count,
152     std::vector<Variable> &variables)
153 {
154     return m_variables.GetVariables(m_pProcess, variablesReference, filter, start, count, variables);
155 }
156
157 HRESULT Variables::GetVariables(
158     ICorDebugProcess *pProcess,
159     uint32_t variablesReference,
160     VariablesFilter filter,
161     int start,
162     int count,
163     std::vector<Variable> &variables)
164 {
165     if (pProcess == nullptr)
166         return E_FAIL;
167
168     auto it = m_variables.find(variablesReference);
169     if (it == m_variables.end())
170         return E_FAIL;
171
172     VariableReference &ref = it->second;
173
174     HRESULT Status;
175
176     StackFrame stackFrame(ref.frameId);
177     ToRelease<ICorDebugThread> pThread;
178     IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
179     ToRelease<ICorDebugFrame> pFrame;
180     IfFailRet(GetFrameAt(pThread, stackFrame.GetLevel(), &pFrame));
181
182     // Named and Indexed variables are in the same index (internally), Named variables go first
183     if (filter == VariablesNamed && (start + count > ref.namedVariables || count == 0))
184         count = ref.namedVariables - start;
185     if (filter == VariablesIndexed)
186         start += ref.namedVariables;
187
188     if (ref.IsScope())
189     {
190         IfFailRet(GetStackVariables(ref.frameId, pThread, pFrame, start, count, variables));
191     } else {
192         IfFailRet(GetChildren(ref, pThread, pFrame, start, count, variables));
193     }
194     return S_OK;
195 }
196
197 void Variables::AddVariableReference(Variable &variable, uint64_t frameId, ICorDebugValue *value, ValueKind valueKind)
198 {
199     HRESULT Status;
200     unsigned int numChild = 0;
201     GetNumChild(value, numChild, valueKind == ValueIsClass);
202     if (numChild == 0)
203         return;
204
205     variable.namedVariables = numChild;
206     variable.variablesReference = m_nextVariableReference++;
207     value->AddRef();
208     VariableReference variableReference(variable, frameId, value, valueKind);
209     m_variables.emplace(std::make_pair(variable.variablesReference, std::move(variableReference)));
210 }
211
212 HRESULT Variables::GetStackVariables(
213     uint64_t frameId,
214     ICorDebugThread *pThread,
215     ICorDebugFrame *pFrame,
216     int start,
217     int count,
218     std::vector<Variable> &variables)
219 {
220     HRESULT Status;
221
222     int currentIndex = -1;
223
224     ToRelease<ICorDebugValue> pExceptionValue;
225     if (SUCCEEDED(pThread->GetCurrentException(&pExceptionValue)) && pExceptionValue != nullptr)
226     {
227         ++currentIndex;
228         bool outOfRange = currentIndex < start || (count != 0 && currentIndex >= start + count);
229         if (!outOfRange)
230         {
231             Variable var;
232             var.name = "$exception";
233             var.evaluateName = var.name;
234             bool escape = true;
235             PrintValue(pExceptionValue, var.value, escape);
236             TypePrinter::GetTypeOfValue(pExceptionValue, var.type);
237             AddVariableReference(var, frameId, pExceptionValue, ValueIsVariable);
238             variables.push_back(var);
239         }
240     }
241
242     IfFailRet(m_evaluator.WalkStackVars(pFrame, [&](
243         ICorDebugILFrame *pILFrame,
244         ICorDebugValue *pValue,
245         const std::string &name) -> HRESULT
246     {
247         ++currentIndex;
248         if (currentIndex < start || (count != 0 && currentIndex >= start + count))
249             return S_OK;
250         Variable var;
251         var.name = name;
252         var.evaluateName = var.name;
253         bool escape = true;
254         PrintValue(pValue, var.value, escape);
255         TypePrinter::GetTypeOfValue(pValue, var.type);
256         AddVariableReference(var, frameId, pValue, ValueIsVariable);
257         variables.push_back(var);
258         return S_OK;
259     }));
260
261     return S_OK;
262 }
263
264 HRESULT ManagedDebugger::GetScopes(uint64_t frameId, std::vector<Scope> &scopes)
265 {
266     return m_variables.GetScopes(m_pProcess, frameId, scopes);
267 }
268
269 HRESULT Variables::GetScopes(ICorDebugProcess *pProcess, uint64_t frameId, std::vector<Scope> &scopes)
270 {
271     if (pProcess == nullptr)
272         return E_FAIL;
273
274     HRESULT Status;
275
276     StackFrame stackFrame(frameId);
277     ToRelease<ICorDebugThread> pThread;
278     IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
279     ToRelease<ICorDebugFrame> pFrame;
280     IfFailRet(GetFrameAt(pThread, stackFrame.GetLevel(), &pFrame));
281
282     int namedVariables = 0;
283     uint32_t variablesReference = 0;
284
285     ToRelease<ICorDebugValue> pExceptionValue;
286     if (SUCCEEDED(pThread->GetCurrentException(&pExceptionValue)) && pExceptionValue != nullptr)
287         namedVariables++;
288
289     IfFailRet(m_evaluator.WalkStackVars(pFrame, [&](
290         ICorDebugILFrame *pILFrame,
291         ICorDebugValue *pValue,
292         const std::string &name) -> HRESULT
293     {
294         namedVariables++;
295         return S_OK;
296     }));
297
298     if (namedVariables > 0)
299     {
300         variablesReference = m_nextVariableReference++;
301         VariableReference scopeReference(variablesReference, frameId, namedVariables);
302         m_variables.emplace(std::make_pair(variablesReference, std::move(scopeReference)));
303     }
304
305     scopes.emplace_back(variablesReference, "Locals", namedVariables);
306
307     return S_OK;
308 }
309
310 void Variables::FixupInheritedFieldNames(std::vector<Member> &members)
311 {
312     std::unordered_set<std::string> names;
313     for (auto &it : members)
314     {
315         auto r = names.insert(it.name);
316         if (!r.second)
317         {
318             it.name += " (" + it.ownerType + ")";
319         }
320     }
321 }
322
323 HRESULT Variables::GetChildren(
324     VariableReference &ref,
325     ICorDebugThread *pThread,
326     ICorDebugFrame *pFrame,
327     int start,
328     int count,
329     std::vector<Variable> &variables)
330 {
331     if (ref.IsScope())
332         return E_INVALIDARG;
333
334     HRESULT Status;
335
336     ToRelease<ICorDebugILFrame> pILFrame;
337     if (pFrame)
338         IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
339
340     std::vector<Member> members;
341
342     bool hasStaticMembers = false;
343
344     if (!ref.value)
345         return S_OK;
346
347     IfFailRet(FetchFieldsAndProperties(ref.value,
348                                        pThread,
349                                        pILFrame,
350                                        members,
351                                        ref.valueKind == ValueIsClass,
352                                        hasStaticMembers,
353                                        start,
354                                        count == 0 ? INT_MAX : start + count));
355
356     FixupInheritedFieldNames(members);
357
358     for (auto &it : members)
359     {
360         Variable var;
361         var.name = it.name;
362         bool isIndex = !it.name.empty() && it.name.at(0) == '[';
363         if (var.name.find('(') == std::string::npos) // expression evaluator does not support typecasts
364             var.evaluateName = ref.evaluateName + (isIndex ? "" : ".") + var.name;
365         bool escape = true;
366         if (it.value == nullptr)
367         {
368             var.value = "<error>";
369         }
370         else
371         {
372             PrintValue(it.value, var.value, escape);
373             TypePrinter::GetTypeOfValue(it.value, var.type);
374         }
375         AddVariableReference(var, ref.frameId, it.value, ValueIsVariable);
376         variables.push_back(var);
377     }
378
379     if (ref.valueKind == ValueIsVariable && hasStaticMembers)
380     {
381         bool staticsInRange = start < ref.namedVariables && (count == 0 || start + count >= ref.namedVariables);
382         if (staticsInRange)
383         {
384             m_evaluator.RunClassConstructor(pThread, ref.value);
385
386             Variable var;
387             var.name = "Static members";
388             TypePrinter::GetTypeOfValue(ref.value, var.evaluateName); // do not expose type for this fake variable
389             AddVariableReference(var, ref.frameId, ref.value, ValueIsClass);
390             variables.push_back(var);
391         }
392     }
393
394     return S_OK;
395 }
396
397 HRESULT ManagedDebugger::Evaluate(uint64_t frameId, const std::string &expression, Variable &variable)
398 {
399     return m_variables.Evaluate(m_pProcess, frameId, expression, variable);
400 }
401
402 HRESULT Variables::Evaluate(
403     ICorDebugProcess *pProcess,
404     uint64_t frameId,
405     const std::string &expression,
406     Variable &variable)
407 {
408     if (pProcess == nullptr)
409         return E_FAIL;
410
411     HRESULT Status;
412
413     StackFrame stackFrame(frameId);
414     ToRelease<ICorDebugThread> pThread;
415     IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
416     ToRelease<ICorDebugFrame> pFrame;
417     IfFailRet(GetFrameAt(pThread, stackFrame.GetLevel(), &pFrame));
418
419     ToRelease<ICorDebugValue> pResultValue;
420     IfFailRet(m_evaluator.EvalExpr(pThread, pFrame, expression, &pResultValue));
421
422     variable.evaluateName = expression;
423
424     bool escape = true;
425     PrintValue(pResultValue, variable.value, escape);
426     TypePrinter::GetTypeOfValue(pResultValue, variable.type);
427     AddVariableReference(variable, frameId, pResultValue, ValueIsVariable);
428
429     return S_OK;
430 }