1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "pytype.h"
27 
28 #include "extensioncontext.h"
29 #include "pycdbextmodule.h"
30 #include "pyvalue.h"
31 #include "stringutils.h"
32 #include "symbolgroupvalue.h"
33 
34 #include <Windows.h>
35 #ifndef _NO_CVCONST_H
36 #define _NO_CVCONST_H
37 #include <dbghelp.h>
38 #undef _NO_CVCONST_H
39 #else
40 #include <dbghelp.h>
41 #endif
42 
43 #include <regex>
44 
45 constexpr bool debugPyType = false;
debuggingTypeEnabled()46 constexpr bool debuggingTypeEnabled() { return debugPyType || debugPyCdbextModule; }
47 
48 enum TypeCodes {
49     TypeCodeTypedef,
50     TypeCodeStruct,
51     TypeCodeVoid,
52     TypeCodeIntegral,
53     TypeCodeFloat,
54     TypeCodeEnum,
55     TypeCodePointer,
56     TypeCodeArray,
57     TypeCodeComplex,
58     TypeCodeReference,
59     TypeCodeFunction,
60     TypeCodeMemberPointer,
61     TypeCodeFortranString,
62     TypeCodeUnresolvable,
63     TypeCodeBitField,
64     TypeCodeRValueReference,
65 };
66 
isType(const std::string & typeName,const std::vector<std::string> & types)67 static bool isType(const std::string &typeName, const std::vector<std::string> &types)
68 {
69     return std::find(types.begin(), types.end(), typeName) != types.end();
70 }
71 
isIntegralType(const std::string & typeName)72 static bool isIntegralType(const std::string &typeName)
73 {
74     static const std::vector<std::string> integralTypes({"bool",
75             "char", "unsigned char", "char16_t", "char32_t", "wchar_t",
76             "short", "unsigned short", "int", "unsigned int",
77             "long", "unsigned long", "int64", "unsigned int64", "__int64", "unsigned __int64"});
78     return isType(typeName, integralTypes);
79 }
80 
isFloatType(const std::string & typeName)81 static bool isFloatType(const std::string &typeName)
82 {
83     static const std::vector<std::string> floatTypes({"float", "double"});
84     return isType(typeName, floatTypes);
85 }
86 
isArrayPointerAtPosition(const std::string & typeName,size_t position)87 static bool isArrayPointerAtPosition(const std::string &typeName, size_t position)
88 {
89     if (typeName.length() < position + 3)
90         return false;
91     return typeName.at(position) == '('
92             && typeName.at(position + 1) == '*'
93             && typeName.at(position + 2) == ')';
94 }
95 
isArrayType(const std::string & typeName)96 static bool isArrayType(const std::string &typeName)
97 {
98     if (typeName.empty() || typeName.back() != ']' || (typeName.find('[') == std::string::npos))
99         return false;
100     // check for types like "int (*)[3]" which is a pointer to an integer array with 3 elements
101     const auto arrayPosition = typeName.find_first_of('[');
102     const bool isArrayPointer = arrayPosition != std::string::npos
103             && arrayPosition >= 3 && isArrayPointerAtPosition(typeName, arrayPosition - 3);
104     return !isArrayPointer && (typeName.compare(0, 8, "__fptr()") != 0);
105 }
106 
extractArraySize(const std::string & typeName,size_t openArrayPos=0)107 static ULONG extractArraySize(const std::string &typeName, size_t openArrayPos = 0)
108 {
109     if (openArrayPos == 0)
110         openArrayPos = typeName.find_first_of('[');
111     if (openArrayPos == std::string::npos)
112         return 0;
113     const auto closeArrayPos = typeName.find_first_of(']', openArrayPos);
114     if (closeArrayPos == std::string::npos)
115         return 0;
116     const std::string arraySizeString = typeName.substr(openArrayPos + 1,
117                                                         closeArrayPos - openArrayPos - 1);
118     try {
119         return std::stoul(arraySizeString);
120     }
121     catch (const std::invalid_argument &) {} // fall through
122     catch (const std::out_of_range &) {} // fall through
123     return 0;
124 }
125 
isPointerType(const std::string & typeName)126 static bool isPointerType(const std::string &typeName)
127 {
128     if (typeName.empty())
129         return false;
130     if (typeName.back() == '*')
131         return true;
132 
133     // check for types like "int (*)[3]" which is a pointer to an integer array with 3 elements
134     const auto arrayPosition = typeName.find_first_of('[');
135     return arrayPosition != std::string::npos
136             && arrayPosition >= 3 && isArrayPointerAtPosition(typeName, arrayPosition - 3);
137 }
138 
stripPointerType(const std::string & typeNameIn)139 static std::string stripPointerType(const std::string &typeNameIn)
140 {
141     if (typeNameIn.empty())
142         return typeNameIn;
143     std::string typeName = typeNameIn;
144     if (typeName.back() == '*') {
145         typeName.pop_back();
146     } else {
147         const auto arrayPosition = typeName.find_first_of('[');
148         if (arrayPosition != std::string::npos
149                 && arrayPosition >= 3 && isArrayPointerAtPosition(typeName, arrayPosition - 3)) {
150             typeName.erase(arrayPosition - 3, 3);
151         }
152     }
153     trimBack(typeName);
154     return typeName;
155 }
156 
innerTypesOf(const std::string & t)157 static std::vector<std::string> innerTypesOf(const std::string &t)
158 {
159     std::vector<std::string> rc;
160 
161     std::string::size_type pos = t.find('<');
162     if (pos == std::string::npos)
163         return rc;
164 
165     // exclude special values like "<Value unavailable error>"
166     if (pos == 0)
167         return rc;
168     // exclude untyped member in the form of class::<untyped>
169     if (pos >= 2 && t.at(pos - 1) == ':' && t.at(pos - 2) == ':')
170         return rc;
171 
172     rc.reserve(5);
173     const std::string::size_type size = t.size();
174     // Record all elements of level 1 to work correctly for
175     // 'std::map<std::basic_string< .. > >'
176     unsigned level = 0;
177     std::string::size_type start = 0;
178     for ( ; pos < size ; pos++) {
179         const char c = t.at(pos);
180         switch (c) {
181         case '<':
182             if (++level == 1)
183                 start = pos + 1;
184             break;
185         case '>':
186             if (--level == 0) { // last element
187                 std::string innerType = t.substr(start, pos - start);
188                 trimFront(innerType);
189                 trimBack(innerType);
190                 rc.push_back(innerType);
191                 return rc;
192             }
193             break;
194         case ',':
195             if (level == 1) { // std::map<a, b>: start anew at ','.
196                 std::string innerType = t.substr(start, pos - start);
197                 trimFront(innerType);
198                 trimBack(innerType);
199                 rc.push_back(innerType);
200                 start = pos + 1;
201             }
202             break;
203         }
204     }
205     return rc;
206 }
207 
getModuleName(ULONG64 module)208 static std::string getModuleName(ULONG64 module)
209 {
210     CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
211     ULONG size;
212     symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, DEBUG_ANY_ID, module, NULL, 0, &size);
213     std::string name(size - 1, '\0');
214     if (SUCCEEDED(symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, DEBUG_ANY_ID,
215                                                module, &name[0], size, NULL))) {
216         return name;
217     }
218     return std::string();
219 }
220 
PyType(ULONG64 module,unsigned long typeId,const std::string & name,int tag)221 PyType::PyType(ULONG64 module, unsigned long typeId, const std::string &name, int tag)
222     : m_module(module)
223     , m_typeId(typeId)
224     , m_resolved(true)
225     , m_tag(tag)
226 {
227     m_name = SymbolGroupValue::stripClassPrefixes(name);
228     if (m_name.compare(0, 6, "union ") == 0)
229            m_name.erase(0, 6);
230     if (m_name == "<function> *")
231            m_name.erase(10);
232 }
233 
name(bool withModule) const234 std::string PyType::name(bool withModule) const
235 {
236     if (m_name.empty()) {
237         auto symbols = ExtensionCommandContext::instance()->symbols();
238         ULONG size = 0;
239         symbols->GetTypeName(m_module, m_typeId, NULL, 0, &size);
240         if (size == 0)
241             return std::string();
242 
243         std::string typeName(size - 1, '\0');
244         if (FAILED(symbols->GetTypeName(m_module, m_typeId, &typeName[0], size, &size)))
245             return std::string();
246 
247         m_name = typeName;
248     }
249 
250     if (withModule && !isIntegralType(m_name) && !isFloatType(m_name)) {
251         std::string completeName = module();
252         if (!completeName.empty())
253             completeName.append("!");
254         completeName.append(m_name);
255         return completeName;
256     }
257 
258     return m_name;
259 }
260 
bitsize() const261 ULONG64 PyType::bitsize() const
262 {
263     if (!m_resolved)
264         return 0;
265 
266     ULONG size = 0;
267     auto symbols = ExtensionCommandContext::instance()->symbols();
268     if (SUCCEEDED(symbols->GetTypeSize(m_module, m_typeId, &size)))
269         return size * 8;
270     return 0;
271 }
272 
code() const273 int PyType::code() const
274 {
275     if (!m_resolved)
276         return TypeCodeUnresolvable;
277 
278     if (m_tag < 0) {
279         // try to parse typeName
280         const std::string &typeName = name();
281         if (typeName.empty())
282             return TypeCodeUnresolvable;
283         if (isPointerType(typeName))
284             return TypeCodePointer;
285         if (isArrayType(typeName))
286             return TypeCodeArray;
287         if (typeName.find("<function>") == 0)
288             return TypeCodeFunction;
289         if (isIntegralType(typeName))
290             return TypeCodeIntegral;
291         if (isFloatType(typeName))
292             return TypeCodeFloat;
293 
294         IDebugSymbolGroup2 *sg = 0;
295         if (FAILED(ExtensionCommandContext::instance()->symbols()->CreateSymbolGroup2(&sg)))
296             return TypeCodeStruct;
297 
298         if (knownType(name(), 0) != KT_Unknown)
299             return TypeCodeStruct;
300 
301         const std::string helperValueName = SymbolGroupValue::pointedToSymbolName(0, name(true));
302         ULONG index = DEBUG_ANY_ID;
303         if (SUCCEEDED(sg->AddSymbol(helperValueName.c_str(), &index)))
304             m_tag = PyValue(index, sg).tag();
305         sg->Release();
306     }
307     switch (m_tag) {
308     case SymTagUDT: return TypeCodeStruct;
309     case SymTagEnum: return TypeCodeEnum;
310     case SymTagTypedef: return TypeCodeTypedef;
311     case SymTagFunctionType: return TypeCodeFunction;
312     case SymTagPointerType: return TypeCodePointer;
313     case SymTagArrayType: return TypeCodeArray;
314     case SymTagBaseType: return isIntegralType(name()) ? TypeCodeIntegral : TypeCodeFloat;
315     default: break;
316     }
317 
318     return TypeCodeStruct;
319 }
320 
target() const321 PyType PyType::target() const
322 {
323     const std::string &typeName = name();
324     if (isPointerType(typeName) || isArrayType(typeName))
325         return lookupType(targetName(), m_module);
326     return PyType();
327 }
328 
targetName() const329 std::string PyType::targetName() const
330 {
331     const std::string &typeName = name();
332     if (isPointerType(typeName))
333         return stripPointerType(typeName);
334     if (isArrayType(typeName)) {
335         const auto openArrayPos = typeName.find_first_of('[');
336         if (openArrayPos == std::string::npos)
337             return typeName;
338         const auto closeArrayPos = typeName.find_first_of(']', openArrayPos);
339         if (closeArrayPos == std::string::npos)
340             return typeName;
341         return typeName.substr(0, openArrayPos) + typeName.substr(closeArrayPos + 1);
342     }
343     return typeName;
344 }
345 
fields() const346 PyFields PyType::fields() const
347 {
348     CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
349     PyFields fields;
350     if (isArrayType(name()) || isPointerType(name()))
351         return fields;
352     for (ULONG fieldIndex = 0;; ++fieldIndex) {
353         ULONG size = 0;
354         symbols->GetFieldName(m_module, m_typeId, fieldIndex, NULL, 0, &size);
355         if (size == 0)
356             break;
357         std::string name(size - 1, '\0');
358         if (FAILED(symbols->GetFieldName(m_module, m_typeId, fieldIndex, &name[0], size, NULL)))
359             break;
360 
361         fields.push_back(PyField(name, *this));
362     }
363     return fields;
364 }
365 
module() const366 std::string PyType::module() const
367 {
368     CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
369     ULONG size;
370     symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, DEBUG_ANY_ID, m_module, NULL, 0, &size);
371     std::string name(size - 1, '\0');
372     if (SUCCEEDED(symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, DEBUG_ANY_ID,
373                                                m_module, &name[0], size, NULL))) {
374         return name;
375     }
376     return std::string();
377 }
378 
moduleId() const379 ULONG64 PyType::moduleId() const
380 {
381     return m_module;
382 }
383 
arrayElements() const384 int PyType::arrayElements() const
385 {
386     return extractArraySize(name());
387 }
388 
templateArguments()389 PyType::TemplateArguments PyType::templateArguments()
390 {
391     std::vector<std::string> innerTypes = innerTypesOf(name());
392     if (debuggingTypeEnabled())
393         DebugPrint() << "template arguments of: " << name();
394     TemplateArguments templateArguments;
395     for (const std::string &innerType : innerTypes) {
396         if (debuggingTypeEnabled())
397             DebugPrint() << "    template argument: " << innerType;
398         TemplateArgument arg;
399         try {
400             int numericValue = std::stoi(innerType);
401             arg.numericValue = numericValue;
402         }
403         catch (std::invalid_argument) {
404             arg.numeric = false;
405             arg.typeName = innerType;
406         }
407         templateArguments.push_back(arg);
408     }
409     return templateArguments;
410 }
411 
lookupType(const std::string & typeNameIn,ULONG64 module)412 PyType PyType::lookupType(const std::string &typeNameIn, ULONG64 module)
413 {
414     if (debuggingTypeEnabled())
415         DebugPrint() << "lookup type '" << typeNameIn << "'";
416     std::string typeName = typeNameIn;
417     trimBack(typeName);
418     trimFront(typeName);
419 
420     if (isPointerType(typeName) || isArrayType(typeName))
421         return PyType(0, 0, typeName);
422 
423     if (typeName.find("enum ") == 0)
424         typeName.erase(0, 5);
425     if (endsWith(typeName, " const"))
426         typeName.erase(typeName.length() - 6);
427     if (typeName == "__int64" || typeName == "unsigned __int64")
428         typeName.erase(typeName.find("__"), 2);
429     if (typeName == "signed char")
430         typeName.erase(0, 7);
431 
432     const static std::regex typeNameRE("^[a-zA-Z_][a-zA-Z0-9_]*!?[a-zA-Z0-9_<>:, \\*\\&\\[\\]]*$");
433     if (!std::regex_match(typeName, typeNameRE))
434         return PyType();
435 
436     CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
437     ULONG typeId;
438     HRESULT result = S_FALSE;
439     if (module != 0 && !isIntegralType(typeName) && !isFloatType(typeName))
440         result = symbols->GetTypeId(module, typeName.c_str(), &typeId);
441     if (FAILED(result) || result == S_FALSE)
442         result = symbols->GetSymbolTypeId(typeName.c_str(), &typeId, &module);
443     if (FAILED(result))
444         return createUnresolvedType(typeName);
445     return PyType(module, typeId, typeName);
446 
447 }
448 
createUnresolvedType(const std::string & typeName)449 PyType PyType::createUnresolvedType(const std::string &typeName)
450 {
451     PyType unresolvedType;
452     unresolvedType.m_name = typeName;
453     return unresolvedType;
454 }
455 
getTypeId() const456 unsigned long PyType::getTypeId() const
457 {
458     return m_typeId;
459 }
460 
isValid() const461 bool PyType::isValid() const
462 {
463     return m_resolved;
464 }
465 
466 // Python interface implementation
467 
468 namespace PyTypeInterface {
469 #define PY_OBJ_NAME TypePythonObject
470 PY_NEW_FUNC(PY_OBJ_NAME)
PY_DEALLOC_FUNC(PY_OBJ_NAME)471 PY_DEALLOC_FUNC(PY_OBJ_NAME)
472 PY_FUNC_RET_STD_STRING(name, PY_OBJ_NAME)
473 PY_FUNC(bitsize, PY_OBJ_NAME, "k")
474 PY_FUNC(code, PY_OBJ_NAME, "k")
475 PY_FUNC_RET_SELF(unqualified, PY_OBJ_NAME)
476 PY_FUNC_RET_OBJECT(target, PY_OBJ_NAME)
477 PY_FUNC_RET_STD_STRING(targetName, PY_OBJ_NAME)
478 PY_FUNC_RET_SELF(stripTypedef, PY_OBJ_NAME)
479 PY_FUNC_RET_OBJECT_LIST(fields, PY_OBJ_NAME)
480 PY_FUNC_RET_STD_STRING(module, PY_OBJ_NAME)
481 PY_FUNC(moduleId, PY_OBJ_NAME, "K")
482 PY_FUNC(arrayElements, PY_OBJ_NAME, "k")
483 PY_FUNC_DECL(templateArguments, PY_OBJ_NAME)
484 {
485     PY_IMPL_GUARD;
486     auto list = PyList_New(0);
487     for (PyType::TemplateArgument arg : self->impl->templateArguments())
488         PyList_Append(list, arg.numeric ? Py_BuildValue("i", arg.numericValue)
489                                         : Py_BuildValue("s", arg.typeName.c_str()));
490     return list;
491 }
492 
493 static PyMethodDef typeMethods[] = {
494     {"name",                PyCFunction(name),                  METH_NOARGS,
495      "Return the type name"},
496     {"bitsize",             PyCFunction(bitsize),               METH_NOARGS,
497      "Return the size of the type in bits"},
498     {"code",                PyCFunction(code),                  METH_NOARGS,
499      "Return type code"},
500     {"unqualified",         PyCFunction(unqualified),           METH_NOARGS,
501      "Type without const/volatile"},
502     {"target",              PyCFunction(target),                METH_NOARGS,
503      "Type dereferenced if it is a pointer type, element if array etc"},
504     {"targetName",          PyCFunction(targetName),            METH_NOARGS,
505      "Typename dereferenced if it is a pointer type, element if array etc"},
506     {"stripTypedef",        PyCFunction(stripTypedef),          METH_NOARGS,
507      "Type with typedefs removed"},
508     {"fields",              PyCFunction(fields),                METH_NOARGS,
509      "List of fields (member and base classes) of this type"},
510     {"module",              PyCFunction(module),                METH_NOARGS,
511      "Returns name for the module containing this type"},
512     {"moduleId",            PyCFunction(moduleId),              METH_NOARGS,
513      "Returns id for the module containing this type"},
514     {"arrayElements",       PyCFunction(arrayElements),         METH_NOARGS,
515      "Returns the number of elements in an array or 0 for non array types"},
516     {"templateArguments",   PyCFunction(templateArguments),     METH_NOARGS,
517      "Returns all template arguments."},
518 
519     {NULL}  /* Sentinel */
520 };
521 
522 } // namespace PyTypeInterface
523 
type_pytype()524 PyTypeObject *type_pytype()
525 {
526     static PyTypeObject cdbext_PyType = {
527         PyVarObject_HEAD_INIT(NULL, 0)
528         "cdbext.Type",                              /* tp_name */
529         sizeof(TypePythonObject),                   /* tp_basicsize */
530         0,
531         (destructor)PyTypeInterface::dealloc,       /* tp_dealloc */
532         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
533         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
534         "Type objects",                             /* tp_doc */
535         0, 0, 0, 0, 0, 0,
536         PyTypeInterface::typeMethods,               /* tp_methods */
537         0, 0, 0, 0, 0, 0, 0, 0, 0,
538         PyTypeInterface::newObject,                 /* tp_new */
539     };
540 
541     return &cdbext_PyType;
542 }
543 
createPythonObject(PyType implClass)544 PyObject *createPythonObject(PyType implClass)
545 {
546     if (!implClass.isValid())
547         Py_RETURN_NONE;
548     TypePythonObject *newPyType = PyObject_New(TypePythonObject, type_pytype());
549     newPyType->impl = new PyType(implClass);
550     return reinterpret_cast<PyObject *>(newPyType);
551 }
552