mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 22:02:49 -06:00
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar especially type casts. To support host APIs, char16 is introduced (but there is no 16-bit String varian). Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API. - Support of drawing non-BMP characters in GUI - Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used) - Last instances of Win32 ANSI calls (those ending with A) are removed - UTF handling routines are refactored and their's naming is unified - RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText) Other minor changes: - fixed TryRealloc issue - improved MemoryCheck - Removed MemoryAlloc48/MemoryFree48 - In theide Background parsing should less often cause delays in the main thread
563 lines
15 KiB
C++
563 lines
15 KiB
C++
#include "Debuggers.h"
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
|
|
#define LLOG(x) // DLOG(x)
|
|
|
|
#ifdef _DEBUG
|
|
|
|
String SymTagAsString(int n) {
|
|
static VectorMap<int, String> tagmap = {
|
|
{ SymTagNull, "SymTagNull" },
|
|
{ SymTagExe, "SymTagExe" },
|
|
{ SymTagCompiland, "SymTagCompiland" },
|
|
{ SymTagCompilandDetails, "SymTagCompilandDetails" },
|
|
{ SymTagCompilandEnv, "SymTagCompilandEnv" },
|
|
{ SymTagFunction, "SymTagFunction" },
|
|
{ SymTagBlock, "SymTagBlock" },
|
|
{ SymTagData, "SymTagData" },
|
|
{ SymTagAnnotation, "SymTagAnnotation" },
|
|
{ SymTagLabel, "SymTagLabel" },
|
|
{ SymTagPublicSymbol, "SymTagPublicSymbol" },
|
|
{ SymTagUDT, "SymTagUDT" },
|
|
{ SymTagEnum, "SymTagEnum" },
|
|
{ SymTagFunctionType, "SymTagFunctionType" },
|
|
{ SymTagPointerType, "SymTagPointerType" },
|
|
{ SymTagArrayType, "SymTagArrayType" },
|
|
{ SymTagBaseType, "SymTagBaseType" },
|
|
{ SymTagTypedef, "SymTagTypedef" },
|
|
{ SymTagBaseClass, "SymTagBaseClass" },
|
|
{ SymTagFriend, "SymTagFriend" },
|
|
{ SymTagFunctionArgType, "SymTagFunctionArgType" },
|
|
{ SymTagFuncDebugStart, "SymTagFuncDebugStart" },
|
|
{ SymTagFuncDebugEnd, "SymTagFuncDebugEnd" },
|
|
{ SymTagUsingNamespace, "SymTagUsingNamespace" },
|
|
{ SymTagVTableShape, "SymTagVTableShape" },
|
|
{ SymTagVTable, "SymTagVTable" },
|
|
{ SymTagCustom, "SymTagCustom" },
|
|
{ SymTagThunk, "SymTagThunk" },
|
|
{ SymTagCustomType, "SymTagCustomType" },
|
|
{ SymTagManagedType, "SymTagManagedType" },
|
|
{ SymTagDimension, "SymTagDimension" },
|
|
};
|
|
return tagmap.Get(n, "");
|
|
}
|
|
|
|
const char * BaseTypeAsString( DWORD baseType )
|
|
{
|
|
switch ( baseType )
|
|
{
|
|
case btNoType: return "btNoType";
|
|
case btVoid: return "btVoid";
|
|
case btChar: return "btChar";
|
|
case btWChar: return "btWChar";
|
|
case btInt: return "btInt";
|
|
case btUInt: return "btUInt";
|
|
case btFloat: return "btFloat";
|
|
case btBCD: return "btBCD";
|
|
case btBool: return "btBool";
|
|
case btLong: return "btLong";
|
|
case btULong: return "btULong";
|
|
case btCurrency: return "btCurrency";
|
|
case btDate: return "btDate";
|
|
case btVariant: return "btVariant";
|
|
case btComplex: return "btComplex";
|
|
case btBit: return "btBit";
|
|
case btBSTR: return "btBSTR";
|
|
case btHresult: return "btHresult";
|
|
default: return "???";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
adr_t Pdb::GetAddress(FilePos p)
|
|
{
|
|
LONG dummy;
|
|
IMAGEHLP_LINE ln;
|
|
ln.SizeOfStruct = sizeof(ln);
|
|
char h[MAX_PATH];
|
|
strcpy(h, p.path);
|
|
if(SymGetLineFromName(hProcess, NULL, h, p.line + 1, &dummy, &ln)) {
|
|
LLOG("GetAddress " << p.path << "(" << p.line << "): " << Hex(ln.Address));
|
|
return ln.Address;
|
|
}
|
|
LLOG("GetAddress " << p.path << "(" << p.line << "): ??");
|
|
return 0;
|
|
}
|
|
|
|
Pdb::FilePos Pdb::GetFilePos(adr_t address)
|
|
{
|
|
FilePos fp;
|
|
DWORD dummy;
|
|
IMAGEHLP_LINE ln;
|
|
ln.SizeOfStruct = sizeof(ln);
|
|
fp.address = address;
|
|
if(SymGetLineFromAddr(hProcess, (uintptr_t)address, &dummy, &ln) && FileExists(ln.FileName)) {
|
|
fp.line = ln.LineNumber - 1;
|
|
fp.path = ln.FileName;
|
|
fp.address = ln.Address;
|
|
}
|
|
LLOG("GetFilePos(" << Hex(address) << "): " << fp.path << ": " << fp.line);
|
|
return fp;
|
|
}
|
|
|
|
#define MAX_SYMB_NAME 1024
|
|
|
|
Pdb::FnInfo Pdb::GetFnInfo0(adr_t address)
|
|
{
|
|
DWORD64 h;
|
|
|
|
ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYMB_NAME + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
|
|
SYMBOL_INFO *f = (SYMBOL_INFO*)buffer;
|
|
|
|
f->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
f->MaxNameLen = MAX_SYMB_NAME;
|
|
|
|
LLOG("GetFnInfo " << Format64Hex(address));
|
|
FnInfo fn;
|
|
if(SymFromAddr(hProcess, address, &h, f)) {
|
|
LLOG("GetFnInfo " << f->Name
|
|
<< ", type index: " << f->TypeIndex
|
|
<< ", Flags: " << FormatIntHex(f->Flags)
|
|
<< ", Address: " << Hex((dword)f->Address)
|
|
<< ", Size: " << FormatIntHex((dword)f->Size)
|
|
<< ", Tag: " << SymTagAsString(f->Tag));
|
|
fn.name = f->Name;
|
|
fn.address = (adr_t)f->Address;
|
|
fn.size = f->Size;
|
|
fn.pdbtype = f->TypeIndex;
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
Pdb::FnInfo Pdb::GetFnInfo(adr_t address)
|
|
{
|
|
int q = fninfo_cache.Find(address);
|
|
if(q >= 0)
|
|
return fninfo_cache[q];
|
|
if(fninfo_cache.GetCount() > 100)
|
|
fninfo_cache.Clear();
|
|
FnInfo f = GetFnInfo0(address);
|
|
fninfo_cache.Add(address, f);
|
|
return f;
|
|
}
|
|
|
|
void Pdb::TypeVal(Pdb::Val& v, int typeId, adr_t modbase)
|
|
{
|
|
adr_t tag;
|
|
|
|
BOOL reference;
|
|
dword dw = 0;
|
|
#ifdef COMPILER_MINGW
|
|
SymGetTypeInfo(hProcess, modbase, typeId, (IMAGEHLP_SYMBOL_TYPE_INFO)31, &reference);
|
|
#else
|
|
SymGetTypeInfo(hProcess, modbase, typeId, TI_GET_IS_REFERENCE, &reference);
|
|
#endif
|
|
v.reference = reference;
|
|
|
|
for(;;) {
|
|
tag = GetSymInfo(modbase, typeId, TI_GET_SYMTAG);
|
|
if(tag == SymTagPointerType)
|
|
v.ref++;
|
|
else
|
|
if(tag == SymTagArrayType)
|
|
v.array = true;
|
|
else {
|
|
if(tag == SymTagUDT)
|
|
v.udt = true;
|
|
break;
|
|
}
|
|
typeId = GetSymInfo(modbase, typeId, TI_GET_TYPE); // follow pointer(s) to base type
|
|
}
|
|
v.type = UNKNOWN;
|
|
if(tag == SymTagUDT)
|
|
v.type = GetTypeIndex(modbase, typeId);
|
|
else {
|
|
ULONG64 sz = 0;
|
|
SymGetTypeInfo(hProcess, modbase, typeId, TI_GET_LENGTH, &sz);
|
|
dword size = (dword)sz;
|
|
if(tag == SymTagEnum)
|
|
v.type = size == 8 ? SINT8 : size == 4 ? SINT4 : size == 2 ? SINT2 : SINT1;
|
|
else {
|
|
switch(GetSymInfo(modbase, typeId, TI_GET_BASETYPE)) {
|
|
case btBool:
|
|
v.type = BOOL1;
|
|
break;
|
|
case btChar:
|
|
case btWChar:
|
|
case btInt:
|
|
case btLong:
|
|
v.type = size == 8 ? SINT8 : size == 4 ? SINT4 : size == 2 ? SINT2 : SINT1;
|
|
break;
|
|
case btUInt:
|
|
case btULong:
|
|
v.type = size == 8 ? UINT8 : size == 4 ? UINT4 : size == 2 ? UINT2 : UINT1;
|
|
break;
|
|
case btFloat:
|
|
v.type = size == 8 ? DBL : FLT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Pdb::LocalsCtx {
|
|
adr_t frame;
|
|
VectorMap<String, Pdb::Val> param;
|
|
VectorMap<String, Pdb::Val> local;
|
|
Pdb *pdb;
|
|
Context *context;
|
|
};
|
|
|
|
BOOL CALLBACK Pdb::EnumLocals(PSYMBOL_INFO pSym, ULONG SymbolSize, PVOID UserContext)
|
|
{
|
|
LocalsCtx& c = *(LocalsCtx *)UserContext;
|
|
|
|
if(pSym->Tag == SymTagFunction)
|
|
return TRUE;
|
|
|
|
bool param = pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER;
|
|
Val& v = (param ? c.param : c.local).Add(pSym->Name);
|
|
v.address = (adr_t)pSym->Address;
|
|
if(pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
|
|
v.address = pSym->Register;
|
|
else
|
|
if(pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE) {
|
|
if(pSym->Register == CV_ALLREG_VFRAME) {
|
|
#ifdef CPU_64
|
|
if(c.pdb->win64)
|
|
v.address += c.pdb->GetCpuRegister(*c.context, CV_AMD64_RBP);
|
|
else
|
|
#endif
|
|
{
|
|
adr_t ebp = (adr_t)c.pdb->GetCpuRegister(*c.context, CV_REG_EBP);
|
|
if(c.pdb->clang)
|
|
ebp &= ~(adr_t)7; // Workaround for supposed clang/win32 issue
|
|
v.address += ebp;
|
|
}
|
|
}
|
|
else
|
|
v.address += (adr_t)c.pdb->GetCpuRegister(*c.context, pSym->Register);
|
|
}
|
|
else
|
|
if(pSym->Flags & IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE)
|
|
v.address += c.frame;
|
|
|
|
c.pdb->TypeVal(v, pSym->TypeIndex, (adr_t)pSym->ModBase);
|
|
if(param && v.udt && v.ref == 0 && c.pdb->win64) { // dbghelp.dll incorrectly does not report pointer for (copied) value struct params
|
|
v.ref++;
|
|
v.reference = true;
|
|
}
|
|
v.reported_size = pSym->Size;
|
|
v.context = c.context;
|
|
#if 0
|
|
DLOG("------");
|
|
DDUMP(pSym->Name);
|
|
DLOG("TYPE: " << (v.type >= 0 ? c.pdb->GetType(v.type).name : "primitive"));
|
|
DDUMPHEX(pSym->Flags);
|
|
DDUMP(pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER);
|
|
DDUMP(pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER);
|
|
DDUMP(pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE);
|
|
DDUMP(pSym->Flags & IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE);
|
|
DDUMP(pSym->Register == CV_ALLREG_VFRAME);
|
|
DDUMP(pSym->Register);
|
|
DDUMP(pSym->Scope);
|
|
DDUMP(pSym->Value);
|
|
DDUMP(pSym->ModBase);
|
|
DDUMPHEX((adr_t)pSym->Address);
|
|
#endif
|
|
|
|
LLOG("LOCAL " << c.pdb->GetType(v.type).name << " " << pSym->Name << ": " << Format64Hex(v.address));
|
|
return TRUE;
|
|
}
|
|
|
|
void Pdb::GetLocals(Frame& frame, Context& context, VectorMap<String, Pdb::Val>& param,
|
|
VectorMap<String, Pdb::Val>& local)
|
|
{
|
|
LLOG("GetLocals *****************");
|
|
static IMAGEHLP_STACK_FRAME f;
|
|
f.InstructionOffset = frame.pc;
|
|
SymSetContext(hProcess, &f, 0);
|
|
LocalsCtx c;
|
|
c.frame = frame.frame;
|
|
c.pdb = this;
|
|
c.context = &context;
|
|
SymEnumSymbols(hProcess, 0, 0, &EnumLocals, &c);
|
|
param = pick(c.param);
|
|
local = pick(c.local);
|
|
LLOG("===========================");
|
|
}
|
|
|
|
BOOL CALLBACK Pdb::EnumGlobals(PSYMBOL_INFO pSym, ULONG SymbolSize, PVOID UserContext)
|
|
{
|
|
LocalsCtx& c = *(LocalsCtx *)UserContext;
|
|
|
|
if(pSym->Tag != SymTagData)
|
|
return TRUE;
|
|
|
|
LLOG("GLOBAL: " << pSym->Name << " " << Format64Hex(pSym->Address));
|
|
|
|
Val& v = c.pdb->global.GetAdd(pSym->Name);
|
|
v.address = (adr_t)pSym->Address;
|
|
v.reported_size = pSym->Size;
|
|
c.pdb->TypeVal(v, pSym->TypeIndex, (adr_t)pSym->ModBase);
|
|
return TRUE;
|
|
}
|
|
|
|
void Pdb::LoadGlobals(DWORD64 base)
|
|
{
|
|
LocalsCtx c;
|
|
c.pdb = this;
|
|
c.context = &context;
|
|
SymEnumSymbols(hProcess, base, NULL, &EnumGlobals, &c);
|
|
}
|
|
|
|
Pdb::Val Pdb::GetGlobal(const String& name)
|
|
{
|
|
return global.Get(name, Val());
|
|
}
|
|
|
|
String Pdb::GetSymName(adr_t modbase, dword typeindex)
|
|
{
|
|
WCHAR *pwszTypeName;
|
|
if(SymGetTypeInfo(hProcess, modbase, typeindex, TI_GET_SYMNAME, &pwszTypeName)) {
|
|
WString w = pwszTypeName;
|
|
LocalFree(pwszTypeName);
|
|
return w.ToString();
|
|
}
|
|
return Null;
|
|
}
|
|
|
|
dword Pdb::GetSymInfo(adr_t modbase, dword typeindex, IMAGEHLP_SYMBOL_TYPE_INFO info)
|
|
{
|
|
dword dw = 0;
|
|
SymGetTypeInfo(hProcess, modbase, typeindex, info, &dw);
|
|
return dw;
|
|
}
|
|
|
|
int Pdb::GetTypeIndex(adr_t modbase, dword typeindex)
|
|
{
|
|
int q = type.Find(typeindex);
|
|
if(q < 0) {
|
|
q = type.GetCount();
|
|
type.Add(typeindex).modbase = modbase;
|
|
}
|
|
return q;
|
|
}
|
|
|
|
const Pdb::Type& Pdb::GetType(int ti)
|
|
{
|
|
if(ti < 0 || ti >= type.GetCount())
|
|
ThrowError("Invalid type");
|
|
Type& t = type[ti];
|
|
int typeindex = type.GetKey(ti);
|
|
if(t.size < 0) {
|
|
t.name = GetSymName(t.modbase, typeindex);
|
|
type_name.GetAdd(t.name) = ti;
|
|
ULONG64 sz = 0;
|
|
SymGetTypeInfo(hProcess, t.modbase, typeindex, TI_GET_LENGTH, &sz);
|
|
t.size = (dword)sz;
|
|
dword count = GetSymInfo(t.modbase, typeindex, TI_GET_CHILDRENCOUNT);
|
|
if(count) {
|
|
Buffer<byte> b(sizeof(TI_FINDCHILDREN_PARAMS) + sizeof(ULONG) * count);
|
|
TI_FINDCHILDREN_PARAMS *children = (TI_FINDCHILDREN_PARAMS *) ~b;
|
|
children->Count = count;
|
|
children->Start = 0;
|
|
if(SymGetTypeInfo(hProcess, t.modbase, typeindex, TI_FINDCHILDREN, children)) {
|
|
for(dword i = 0; i < count; i++) {
|
|
dword ch = children->ChildId[i];
|
|
dword tag = GetSymInfo(t.modbase, ch, TI_GET_SYMTAG);
|
|
dword kind = GetSymInfo(t.modbase, ch, TI_GET_DATAKIND);
|
|
if(tag == SymTagData) {
|
|
String name = GetSymName(t.modbase, ch);
|
|
if(kind == DataIsMember) {
|
|
Val& v = t.member.Add(name);
|
|
TypeVal(v, GetSymInfo(t.modbase, ch, TI_GET_TYPEID), t.modbase);
|
|
v.address = GetSymInfo(t.modbase, ch, TI_GET_OFFSET);
|
|
ULONG64 bitcnt = 0;
|
|
SymGetTypeInfo(hProcess, t.modbase, ch, TI_GET_LENGTH, &bitcnt);
|
|
if(bitcnt) {
|
|
v.bitcnt = (byte)bitcnt;
|
|
v.bitpos = (byte)GetSymInfo(t.modbase, ch, TI_GET_BITPOSITION);
|
|
}
|
|
}
|
|
if(kind == DataIsStaticMember || kind == DataIsGlobal) {
|
|
Val& v = t.static_member.Add(name);
|
|
TypeVal(v, GetSymInfo(t.modbase, ch, TI_GET_TYPEID), t.modbase);
|
|
ULONG64 adr = 0;
|
|
SymGetTypeInfo(hProcess, t.modbase, ch, TI_GET_ADDRESS, &adr);
|
|
v.address = (adr_t)adr;
|
|
}
|
|
}
|
|
else
|
|
if(tag == SymTagBaseClass) {
|
|
Val& v = t.base.Add();
|
|
TypeVal(v, GetSymInfo(t.modbase, ch, TI_GET_TYPEID), t.modbase);
|
|
v.address = GetSymInfo(t.modbase, ch, TI_GET_OFFSET);
|
|
}
|
|
else
|
|
if(tag == SymTagVTable) {
|
|
t.vtbl_offset = GetSymInfo(t.modbase, ch, TI_GET_OFFSET);
|
|
dword typeId = GetSymInfo(t.modbase, ch, TI_GET_TYPEID);
|
|
while(GetSymInfo(t.modbase, typeId, TI_GET_SYMTAG) == SymTagPointerType)
|
|
typeId = GetSymInfo(t.modbase, typeId, TI_GET_TYPE);
|
|
if((t.vtbl_typeindex = type.Find(typeId)) < 0) {
|
|
t.vtbl_typeindex = type.GetCount();
|
|
Type& vt = type.Add(typeId);
|
|
vt.modbase = t.modbase;
|
|
vt.size = 0;
|
|
vt.vtbl_typeindex = -2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
static int CALLBACK sSymEnum(PSYMBOL_INFO pSym, ULONG SymbolSize, PVOID UserContext)
|
|
{
|
|
auto type_index = (VectorMap<String, int> *)UserContext;
|
|
type_index->GetAdd(pSym->Name) = pSym->TypeIndex;
|
|
return TRUE;
|
|
}
|
|
|
|
int Pdb::FindType(adr_t modbase, const String& name)
|
|
{
|
|
static VectorMap<String, int> primitive = {
|
|
{ "bool", BOOL1 },
|
|
{ "char", SINT1 },
|
|
{ "unsigned char", UINT1 },
|
|
{ "short", SINT2 },
|
|
{ "unsigned short", UINT2 },
|
|
{ "wchar_t", UINT2 },
|
|
{ "int", SINT4 },
|
|
{ "unsigned int", UINT4 },
|
|
{ "float", FLT },
|
|
{ "double", DBL },
|
|
{ "int64", SINT8 },
|
|
{ "uint64", UINT8 },
|
|
{ "__int64", SINT8 },
|
|
{ "unsigned __int64", UINT8 },
|
|
};
|
|
|
|
int q = primitive.Get(name, Null);
|
|
if(!IsNull(q))
|
|
return q;
|
|
q = type_name.Get(name, Null);
|
|
if(!IsNull(q))
|
|
return q;
|
|
if(type_bases.Find(modbase) < 0) {
|
|
type_bases.Add(modbase);
|
|
SymEnumTypes(hProcess, current_modbase, sSymEnum, &type_index);
|
|
// DDUMPM(type_index);
|
|
}
|
|
int ndx = type_index.Get(name, Null);
|
|
if(IsNull(ndx))
|
|
return Null;
|
|
return GetTypeIndex(modbase, ndx);
|
|
}
|
|
|
|
String Pdb::TypeInfoAsString(TypeInfo tf)
|
|
{
|
|
static VectorMap<int, String> primitive = { // todo: UINT8
|
|
{ BOOL1, "bool" },
|
|
{ SINT1, "char" },
|
|
{ UINT1, "unsigned char" },
|
|
{ SINT2, "short" },
|
|
{ UINT2, "unsigned short" },
|
|
{ SINT4, "int" },
|
|
{ UINT4, "unsigned int" },
|
|
{ SINT8, "int64" },
|
|
{ UINT8, "uint64" },
|
|
{ FLT, "float" },
|
|
{ DBL, "double" },
|
|
};
|
|
|
|
String r = primitive.Get(tf.type, Null);
|
|
if(IsNull(r))
|
|
r = GetType(tf.type).name;
|
|
|
|
while(tf.ref > 0) {
|
|
r << "*";
|
|
tf.ref--;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
Pdb::TypeInfo Pdb::GetTypeInfo(adr_t modbase, const String& name)
|
|
{
|
|
int q = typeinfo_cache.Find(name);
|
|
if(q >= 0)
|
|
return typeinfo_cache[q];
|
|
|
|
TypeInfo r;
|
|
String tp = name;
|
|
bool spc = false;
|
|
for(;;)
|
|
if(tp.TrimEnd("*") || tp.TrimEnd("&"))
|
|
r.ref++;
|
|
else
|
|
if(!tp.TrimEnd(" ") && !tp.TrimEnd("const"))
|
|
break;
|
|
r.type = FindType(modbase, TrimBoth(tp));
|
|
typeinfo_cache.Add(name, r);
|
|
return r;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
|
|
String Pdb::TypeAsString(int ti, bool deep)
|
|
{
|
|
String r;
|
|
#define sTYPE(x) case x: return #x;
|
|
switch(ti) {
|
|
sTYPE(BOOL1)
|
|
sTYPE(UINT1)
|
|
sTYPE(SINT1)
|
|
sTYPE(UINT2)
|
|
sTYPE(SINT2)
|
|
sTYPE(UINT4)
|
|
sTYPE(SINT4)
|
|
sTYPE(UINT8)
|
|
sTYPE(SINT8)
|
|
sTYPE(FLT)
|
|
sTYPE(DBL)
|
|
sTYPE(UNKNOWN)
|
|
}
|
|
if(ti < 0)
|
|
return r;
|
|
const Type& t = GetType(ti);
|
|
r << t.name << "(sizeof = " << t.size << ") ";
|
|
if(!deep)
|
|
return r;
|
|
if(t.member.GetCount()) {
|
|
r << "{ ";
|
|
for(int i = 0; i < t.member.GetCount(); i++) {
|
|
if(i)
|
|
r << ", ";
|
|
r << t.member.GetKey(i) << " +" << t.member[i].address;
|
|
if(t.member[i].ref)
|
|
r << TypeAsString(t.member[i].type, false) << String('*', t.member[i].ref);
|
|
else
|
|
r << ": " << TypeAsString(t.member[i].type);
|
|
}
|
|
r << " }";
|
|
}
|
|
if(t.base.GetCount()) {
|
|
r << " BASE: ";
|
|
for(int i = 0; i < t.base.GetCount(); i++) {
|
|
if(i)
|
|
r << ", ";
|
|
r << " +" << t.base[i].address;
|
|
r << TypeAsString(t.base[i].type);
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|