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