From ed9c2be858cbd696ffa2a9d06adb2894b5f62805 Mon Sep 17 00:00:00 2001
From: "SND\\EreTIk_cp" <SND\EreTIk_cp@9b283d60-5439-405e-af05-b73fd8c4d996>
Date: Fri, 20 Jul 2012 15:01:13 +0000
Subject: [PATCH] [0.1.x] ~field offset moved to parent TypeInfo (method
 fieldOffset)

git-svn-id: https://pykd.svn.codeplex.com/svn@78192 9b283d60-5439-405e-af05-b73fd8c4d996
---
 pykd/dbgclient.cpp       |   6 +-
 pykd/module.cpp          |  18 ++++--
 pykd/pykd_2008.vcproj    |   8 +++
 pykd/pymod.cpp           |   7 +-
 pykd/typedvar.cpp        |   6 +-
 pykd/typedvar.h          |   4 +-
 pykd/typeinfo.cpp        | 135 ++++++++++-----------------------------
 pykd/typeinfo.h          |  73 ++++++++++++++-------
 pykd/udtutils.cpp        |  68 ++++++++++++++++++++
 pykd/udtutils.h          |  65 +++++++++++++++++++
 test/scripts/typedvar.py |  11 ++--
 test/scripts/typeinfo.py |  41 ++++++------
 12 files changed, 275 insertions(+), 167 deletions(-)
 create mode 100644 pykd/udtutils.cpp
 create mode 100644 pykd/udtutils.h

diff --git a/pykd/dbgclient.cpp b/pykd/dbgclient.cpp
index b4665ff..9aecc37 100644
--- a/pykd/dbgclient.cpp
+++ b/pykd/dbgclient.cpp
@@ -591,9 +591,7 @@ TypedVarPtr DebugClient::containingRecordByType( ULONG64 addr, const TypeInfoPtr
 {
     addr = addr64(addr); 
 
-    TypeInfoPtr     fieldTypeInfo = typeInfo->getField( fieldName );
-
-    VarDataPtr varData = VarDataMemory::factory( m_dataSpaces, addr - fieldTypeInfo->getOffset() );
+    VarDataPtr varData = VarDataMemory::factory( m_dataSpaces, addr - typeInfo->getFieldOffsetByNameRecirsive(fieldName) );
 
     return TypedVar::getTypedVar( m_client, typeInfo, varData );
 }
@@ -617,7 +615,7 @@ python::list DebugClient::getTypedVarListByType( ULONG64 listHeadAddress, const
 
     if ( fieldTypeInfo->getName() == ( typeInfo->getName() + "*" ) )
     {
-        for( entryAddress = ptrPtr( listHeadAddress ); addr64(entryAddress) != listHeadAddress && entryAddress != NULL; entryAddress = ptrPtr( entryAddress + fieldTypeInfo->getOffset() ) )
+        for( entryAddress = ptrPtr( listHeadAddress ); addr64(entryAddress) != listHeadAddress && entryAddress != NULL; entryAddress = ptrPtr( entryAddress + typeInfo->getFieldOffsetByNameRecirsive(listEntryName) ) )
             lst.append( getTypedVarByTypeInfo( typeInfo, entryAddress ) );
     }
     else
diff --git a/pykd/module.cpp b/pykd/module.cpp
index 4eaf0b8..5c74aa9 100644
--- a/pykd/module.cpp
+++ b/pykd/module.cpp
@@ -322,9 +322,11 @@ TypedVarPtr Module::containingRecordByName( ULONG64 address, const std::string &
 
     TypeInfoPtr     typeInfo = getTypeByName( typeName );
 
-    TypeInfoPtr     fieldTypeInfo = typeInfo->getField( fieldName );
-
-    VarDataPtr varData = VarDataMemory::factory( m_dataSpaces, address - fieldTypeInfo->getOffset() );
+    VarDataPtr varData = 
+        VarDataMemory::factory( 
+            m_dataSpaces,
+            address - typeInfo->getFieldOffsetByNameRecirsive( fieldName )
+        );
 
     return TypedVar::getTypedVar( m_client, typeInfo, varData );
 }
@@ -335,9 +337,11 @@ TypedVarPtr Module::containingRecordByType( ULONG64 address, const TypeInfoPtr &
 {
     address = addr64(address); 
 
-    TypeInfoPtr     fieldTypeInfo = typeInfo->getField( fieldName );
-
-    VarDataPtr varData = VarDataMemory::factory( m_dataSpaces, address - fieldTypeInfo->getOffset() );
+    VarDataPtr varData = 
+        VarDataMemory::factory( 
+            m_dataSpaces,
+            address - typeInfo->getFieldOffsetByNameRecirsive( fieldName )
+        );
 
     return TypedVar::getTypedVar( m_client, typeInfo, varData );
 }
@@ -364,7 +368,7 @@ python::list Module::getTypedVarListByType( ULONG64 listHeadAddress, const TypeI
 
     if ( fieldTypeInfo->getName() == ( typeInfo->getName() + "*" ) )
     {
-        for( entryAddress = ptrPtr( listHeadAddress, m_dataSpaces ); addr64(entryAddress) != listHeadAddress && entryAddress != NULL; entryAddress = ptrPtr( entryAddress + fieldTypeInfo->getOffset() ) )
+        for( entryAddress = ptrPtr( listHeadAddress, m_dataSpaces ); addr64(entryAddress) != listHeadAddress && entryAddress != NULL; entryAddress = ptrPtr( entryAddress + typeInfo->getFieldOffsetByNameRecirsive(listEntryName) ) )
             lst.append( getTypedVarByType( typeInfo, entryAddress ) );
     }
     else
diff --git a/pykd/pykd_2008.vcproj b/pykd/pykd_2008.vcproj
index 418219c..a4ec4bc 100644
--- a/pykd/pykd_2008.vcproj
+++ b/pykd/pykd_2008.vcproj
@@ -493,6 +493,10 @@
 				RelativePath=".\typeinfo.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\udtutils.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\vardata.cpp"
 				>
@@ -615,6 +619,10 @@
 				RelativePath=".\typeinfo.h"
 				>
 			</File>
+			<File
+				RelativePath=".\udtutils.h"
+				>
+			</File>
 			<File
 				RelativePath=".\utils.h"
 				>
diff --git a/pykd/pymod.cpp b/pykd/pymod.cpp
index 2af674a..8c609a6 100644
--- a/pykd/pymod.cpp
+++ b/pykd/pymod.cpp
@@ -546,7 +546,8 @@ BOOST_PYTHON_MODULE( pykd )
         .def("__init__", python::make_constructor(TypeInfo::getTypeInfoByName ) )
         .def( "name", &TypeInfo::getName )
         .def( "size", &TypeInfo::getSize )
-        .def( "offset", &TypeInfo::getTypeOffset )
+        .def( "staticOffset", &TypeInfo::getStaticOffset )
+        .def( "fieldOffset", &TypeInfo::getFieldOffsetByNameRecirsive )
         .def( "bitOffset", &TypeInfo::getBitOffset )
         .def( "bitWidth", &TypeInfo::getBitWidth )
         .def( "field", &TypeInfo::getField )
@@ -566,8 +567,8 @@ BOOST_PYTHON_MODULE( pykd )
             "Return virtual address" )
         .def("sizeof", &TypedVar::getSize,
             "Return size of a variable in the target memory" )
-        .def("offset", &TypedVar::getOffset,
-            "Return offset to parent" )
+        .def("fieldOffset", &TypedVar::getFieldOffsetByNameRecirsive,
+            "Return target field offset" )
         .def("field", &TypedVar::getField,
             "Return field of structure as an object attribute" )
         .def( "dataKind", &TypedVar::getDataKind,
diff --git a/pykd/typedvar.cpp b/pykd/typedvar.cpp
index 1faef89..ffea67b 100644
--- a/pykd/typedvar.cpp
+++ b/pykd/typedvar.cpp
@@ -240,7 +240,7 @@ UdtTypedVar::getField( const std::string &fieldName )
 
     ULONG   fieldOffset = 0;
 
-    fieldOffset = fieldType->getOffset();
+    fieldOffset = m_typeInfo->getFieldOffsetByNameRecirsive(fieldName);
 
     if ( fieldType->isVirtualMember() )
     {
@@ -267,7 +267,7 @@ UdtTypedVar::getElementByIndex( ULONG  index )
             TypedVar::getTypedVar( m_client, fieldType, VarDataMemory::factory(m_dataSpaces, fieldType->getStaticOffset() ) ) );
     }
 
-    ULONG   fieldOffset = fieldType->getOffset();
+    ULONG   fieldOffset = m_typeInfo->getFieldOffsetByIndex(index);
 
     if ( fieldType->isVirtualMember() )
     {
@@ -320,7 +320,7 @@ std::string UdtTypedVar::print()
         }
         else
         {
-            ULONG   fieldOffset = fieldType->getOffset();
+            ULONG   fieldOffset = m_typeInfo->getFieldOffsetByIndex(i);
 
             if ( fieldType->isVirtualMember() )
             {
diff --git a/pykd/typedvar.h b/pykd/typedvar.h
index 11211bb..4350af6 100644
--- a/pykd/typedvar.h
+++ b/pykd/typedvar.h
@@ -35,8 +35,8 @@ public:
         return m_size;
     }
 
-    ULONG64 getOffset() {
-        return m_typeInfo->getOffset();
+    ULONG getFieldOffsetByNameRecirsive(const std::string &fieldName) {
+        return m_typeInfo->getFieldOffsetByNameRecirsive(fieldName);
     }
 
     TypeInfoPtr
diff --git a/pykd/typeinfo.cpp b/pykd/typeinfo.cpp
index 6e826f4..0cefe37 100644
--- a/pykd/typeinfo.cpp
+++ b/pykd/typeinfo.cpp
@@ -483,7 +483,8 @@ TypeInfoPtr TypeInfo::getRecurciveComplexType( TypeInfoPtr &lowestType, std::str
 
 /////////////////////////////////////////////////////////////////////////////////////
 
-ULONG64 TypeInfo::getStaticOffset() {
+ULONG64 TypeInfo::getStaticOffset()
+{
     if ( !m_staticMember )
         throw TypeException( getName(), "This is not a static member" );
 
@@ -492,83 +493,9 @@ ULONG64 TypeInfo::getStaticOffset() {
 
 /////////////////////////////////////////////////////////////////////////////////////
 
-ULONG TypeInfo::getOffset() {
-
-   if ( m_staticOffset )
-       throw TypeException( getName(), "This is a static member" );
-
-    return m_offset;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-
-ULONG64 TypeInfo::getTypeOffset()
-{
-   return m_staticMember ? m_staticOffset : m_offset;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-
-TypeInfoPtr UdtTypeInfo::getField( const std::string &fieldName )
-{
-    if ( m_fields.empty() )
-    {
-        getFields( m_dia, pyDia::SymbolPtr() );
-        getVirtualFields();
-    }
-
-    FieldList::reverse_iterator  it;
-        
-    it = std::find_if( m_fields.rbegin(), m_fields.rend(), boost::bind( &FieldType::first, _1) == fieldName );
-
-    if ( it == m_fields.rend() )
-        throw TypeException( m_dia->getName(), fieldName + ": unknown field type" );
-
-    return it->second;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-
-TypeInfoPtr UdtTypeInfo::getFieldByIndex( ULONG index )
-{
-    if ( m_fields.empty() )
-    {
-        getFields( m_dia, pyDia::SymbolPtr() );
-        getVirtualFields();
-    }
-
-    if ( index >= m_fields.size() )
-         throw PyException( PyExc_IndexError, "Index out of range");
-
-    return m_fields[ index ].second;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-
-std::string UdtTypeInfo::getFieldNameByIndex( ULONG index )
-{
-    if ( m_fields.empty() )
-    {
-        getFields( m_dia, pyDia::SymbolPtr() );
-        getVirtualFields();
-    }
-
-    if ( index >= m_fields.size() )
-         throw PyException( PyExc_IndexError, "Index out of range");
-
-    return m_fields[ index ].first;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-
 ULONG UdtTypeInfo::getFieldCount()
 {
-    if ( m_fields.empty() )
-    {
-        getFields( m_dia, pyDia::SymbolPtr() );
-        getVirtualFields();
-    }
-
+    refreshFields();
     return (ULONG)m_fields.size();
 }
 
@@ -577,12 +504,12 @@ ULONG UdtTypeInfo::getFieldCount()
 void UdtTypeInfo::getFields( 
         pyDia::SymbolPtr &rootSym, 
         pyDia::SymbolPtr &baseVirtualSym,
-        ULONG startOffset,        
+        ULONG startOffset,
         LONG virtualBasePtr,
         ULONG virtualDispIndex,
         ULONG virtualDispSize )
 {
-    ULONG   childCount = rootSym->getChildCount();  
+    ULONG   childCount = rootSym->getChildCount();
 
     for ( ULONG i = 0; i < childCount; ++i )
     {
@@ -602,6 +529,7 @@ void UdtTypeInfo::getFields(
         {
             TypeInfoPtr     ti = TypeInfo::getTypeInfo( rootSym, childSym );
 
+            ULONG fieldOffset = 0;
             switch ( childSym->getDataKind() )
             {
             case DataIsMember:
@@ -615,16 +543,15 @@ void UdtTypeInfo::getFields(
                         virtualDispSize );
                 }
 
-                ti->setOffset( startOffset + childSym->getOffset() );
-
+                fieldOffset = startOffset + childSym->getOffset();
                 break;
 
             case DataIsStaticMember:
                 ti->setStaticOffset( childSym->getVa() );
-                break;                
+                break;
             }
 
-            m_fields.push_back( std::make_pair( childSym->getName(), ti ) );
+            m_fields.push_back( UdtUtils::Field( fieldOffset, childSym->getName(), ti ) );
         }
         else
         if ( symTag == SymTagVTable )
@@ -641,9 +568,7 @@ void UdtTypeInfo::getFields(
                
             }
 
-            ti->setOffset( startOffset + childSym->getOffset() );
-
-            m_fields.push_back( std::make_pair( "__VFN_table", ti ) ); 
+            m_fields.push_back( UdtUtils::Field( childSym->getOffset(), "__VFN_table", ti ) ); 
         }
     }  
 }
@@ -673,6 +598,17 @@ void UdtTypeInfo::getVirtualFields()
 
 /////////////////////////////////////////////////////////////////////////////////////
 
+void UdtTypeInfo::refreshFields()
+{
+    if ( m_fields.empty() )
+    {
+        getFields( m_dia, pyDia::SymbolPtr() );
+        getVirtualFields();
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
 std::string UdtTypeInfo::print()
 {
     std::stringstream  sstr;
@@ -682,28 +618,29 @@ std::string UdtTypeInfo::print()
     ULONG       fieldCount = getFieldCount();
 
     for ( ULONG i = 0; i < fieldCount; ++i )
-    { 
-        TypeInfoPtr     fieldType = getFieldByIndex(i);
+    {
+        const UdtUtils::Field &udtField = lookupField(i);
+        TypeInfoPtr     fieldType = udtField.m_type;
 
         if ( fieldType->isStaticMember() )
         {   
             sstr << "   =" << std::right << std::setw(10) << std::setfill('0') << std::hex << fieldType->getStaticOffset();
-            sstr << " " << std::left << std::setw(18) << std::setfill(' ') << getFieldNameByIndex(i) << ':';
+            sstr << " " << std::left << std::setw(18) << std::setfill(' ') << udtField.m_name << ':';
         }
         else
         if ( fieldType->isVirtualMember() )
-        {           
+        {
             ULONG virtualBasePtr, virtualDispIndex, virtualDispSize;
             fieldType->getVirtualDisplacement( virtualBasePtr, virtualDispIndex, virtualDispSize );
 
             sstr << "   virtual base " <<  fieldType->getVirtualBase()->getName();
-            sstr << " +" << std::right << std::setw(4) << std::setfill('0') << std::hex << fieldType->getOffset();
-            sstr << " " << getFieldNameByIndex(i) << ':';
+            sstr << " +" << std::right << std::setw(4) << std::setfill('0') << std::hex << udtField.m_offset;
+            sstr << " " << udtField.m_name << ':';
         }
         else
         {
-            sstr << "   +" << std::right << std::setw(4) << std::setfill('0') << std::hex << fieldType->getOffset();
-            sstr << " " << std::left << std::setw(24) << std::setfill(' ') << getFieldNameByIndex(i) << ':';
+            sstr << "   +" << std::right << std::setw(4) << std::setfill('0') << std::hex << udtField.m_offset;
+            sstr << " " << std::left << std::setw(24) << std::setfill(' ') << udtField.m_name << ':';
         }
 
         sstr << " " << std::left << fieldType->getName();
@@ -715,11 +652,9 @@ std::string UdtTypeInfo::print()
 
 /////////////////////////////////////////////////////////////////////////////////////
 
-TypeInfoPtr EnumTypeInfo::getField( const std::string &fieldName ) {
-    pyDia::SymbolPtr  field = m_dia->getChildByName( fieldName );
-    TypeInfoPtr  ti = TypeInfo::getTypeInfo( m_dia, fieldName );
-    ti->setOffset( 0 );
-    return ti;
+TypeInfoPtr EnumTypeInfo::getField( const std::string &fieldName )
+{
+    return TypeInfo::getTypeInfo( m_dia, fieldName );
 }
 
 /////////////////////////////////////////////////////////////////////////////////////
@@ -734,9 +669,7 @@ TypeInfoPtr EnumTypeInfo::getFieldByIndex( ULONG index )
     if ( !field )
         throw TypeException( m_dia->getName(), ": field not found" );   
     
-    TypeInfoPtr  ti = TypeInfo::getTypeInfo( m_dia, field->getName() );
-    ti->setOffset( 0 );
-    return ti;
+    return TypeInfo::getTypeInfo( m_dia, field->getName() );
 }
 
 /////////////////////////////////////////////////////////////////////////////////////
diff --git a/pykd/typeinfo.h b/pykd/typeinfo.h
index f0f4c70..45e06a0 100644
--- a/pykd/typeinfo.h
+++ b/pykd/typeinfo.h
@@ -2,6 +2,9 @@
 
 #include <string>
 
+#include <boost\enable_shared_from_this.hpp>
+
+#include "udtutils.h"
 #include "diawrapper.h"
 #include "intbase.h"
 
@@ -14,7 +17,7 @@ typedef boost::shared_ptr<TypeInfo>  TypeInfoPtr;
 
 ///////////////////////////////////////////////////////////////////////////////////
 
-class TypeInfo : boost::noncopyable, public intBase {
+class TypeInfo : boost::noncopyable, public intBase, public boost::enable_shared_from_this<TypeInfo> {
 
 public:
 
@@ -42,7 +45,6 @@ public:
 public:
 
     TypeInfo() :
-        m_offset( 0 ),
         m_staticOffset( 0 ),
         m_constant( false ),
         m_staticMember( false ),
@@ -77,6 +79,17 @@ public:
         throw TypeException( getName(), "type is not a struct" ); 
     }
 
+    virtual ULONG getFieldOffsetByNameRecirsive( const std::string &fieldName ) {
+        throw TypeException( getName(), "type is not a struct" ); 
+    }
+    virtual ULONG getFieldOffsetByNameNotRecursively( const std::string &fieldName ) {
+        throw TypeException( getName(), "type is not a struct" ); 
+    }
+
+    virtual ULONG getFieldOffsetByIndex( ULONG index ) {
+        throw TypeException( getName(), "type is not a struct" ); 
+    }
+
     virtual ULONG getFieldCount() {
         throw TypeException( getName(), "type is not a struct" ); 
     }
@@ -139,10 +152,6 @@ public:
         throw PyException( PyExc_TypeError, "object is unsubscriptable");  
     }
 
-    void setOffset( ULONG offset ) {
-        m_offset = offset;
-    }
-
     void setConstant( const VARIANT& var )
     {
         m_constant = true;
@@ -177,10 +186,6 @@ public:
         m_virtualDispSize = virtualDispSize;
     }
 
-    ULONG64 getTypeOffset();
-
-    ULONG getOffset();
-
     ULONG64 getStaticOffset();
 
     void getVirtualDisplacement( ULONG &virtualBasePtr, ULONG &virtualDispIndex, ULONG &virtualDispSize ) {
@@ -203,8 +208,6 @@ protected:
     static
     TypeInfoPtr getRecurciveComplexType( TypeInfoPtr &lowestType, std::string &suffix, ULONG ptrSize );
 
-    ULONG       m_offset;
-
     ULONG64     m_staticOffset;
 
     bool        m_constant;
@@ -221,7 +224,7 @@ protected:
 
     ULONG       m_virtualDispSize;
 
-    TypeInfoPtr m_virtualBaseType;    
+    TypeInfoPtr m_virtualBaseType;
 };
 
 ///////////////////////////////////////////////////////////////////////////////////
@@ -295,8 +298,10 @@ class UdtTypeInfo : public TypeInfo
 public:
 
     UdtTypeInfo ( pyDia::SymbolPtr &symbol ) :
-      m_dia( symbol )
-      {}
+        m_dia( symbol ),
+        m_fields( symbol->getName() )
+    {
+    }
 
 protected:
 
@@ -308,11 +313,28 @@ protected:
         return (ULONG)m_dia->getSize();
     }
 
-    virtual TypeInfoPtr getField( const std::string &fieldName );
+    virtual TypeInfoPtr getField( const std::string &fieldName ) {
+        return lookupField(fieldName).m_type;
+    }
 
-    virtual TypeInfoPtr getFieldByIndex( ULONG index );
+    virtual TypeInfoPtr getFieldByIndex( ULONG index ) {
+        return lookupField(index).m_type;
+    }
 
-    virtual std::string getFieldNameByIndex( ULONG index );
+    virtual std::string getFieldNameByIndex( ULONG index ) {
+        return lookupField(index).m_name;
+    }
+
+    virtual ULONG getFieldOffsetByNameRecirsive( const std::string &fieldName ) {
+        return UdtUtils::getFiledOffsetRecirsive( shared_from_this(), fieldName );
+    }
+    virtual ULONG getFieldOffsetByNameNotRecursively( const std::string &fieldName ) {
+        return lookupField(fieldName).m_offset;
+    }
+
+    virtual ULONG getFieldOffsetByIndex( ULONG index ) {
+        return lookupField(index).m_offset;
+    }
 
     virtual ULONG getFieldCount();
 
@@ -332,11 +354,7 @@ protected:
 
     pyDia::SymbolPtr    m_dia;
 
-    typedef std::pair< std::string, TypeInfoPtr >   FieldType;
-
-    typedef std::vector< FieldType >   FieldList;
-    
-    FieldList           m_fields;
+    UdtUtils::FieldCollection  m_fields;
 
     void getFields( 
         pyDia::SymbolPtr &rootSym, 
@@ -348,6 +366,15 @@ protected:
 
 
     void getVirtualFields();
+
+private:
+    void refreshFields();
+
+    template <typename T>
+    const UdtUtils::Field &lookupField( T index) {
+        refreshFields();
+        return m_fields.lookup(index);
+    }
 };
 
 ///////////////////////////////////////////////////////////////////////////////////
diff --git a/pykd/udtutils.cpp b/pykd/udtutils.cpp
new file mode 100644
index 0000000..7ff0efe
--- /dev/null
+++ b/pykd/udtutils.cpp
@@ -0,0 +1,68 @@
+
+#include "stdafx.h"
+
+#include <boost\tokenizer.hpp>
+
+#include "udtutils.h"
+#include "dbgexcept.h"
+#include "typeinfo.h"
+
+///////////////////////////////////////////////////////////////////////////////////
+
+namespace pykd {
+namespace UdtUtils {
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+const Field &FieldCollection::lookup(ULONG index) const
+{
+    if (index >= Base::size())
+        throw PyException( PyExc_IndexError, m_baseTypeName + " index out of range" );
+    return at(index);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+const Field &FieldCollection::lookup(const std::string &name) const
+{
+    Base::const_reverse_iterator it = 
+        std::find(Base::rbegin(), Base::rend(), name);
+
+    if ( it == Base::rend() )
+        throw TypeException( m_baseTypeName, name + ": field not found" );
+
+    return *it;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+ULONG getFiledOffsetRecirsive(TypeInfoPtr typeInfo, const std::string &fieldName)
+{
+    // "m_field1.m_field2" -> ["m_field1", "m_field2"]
+    typedef boost::char_separator<char> CharSep;
+    boost::tokenizer< CharSep > tokenizer(fieldName, CharSep("."));
+    if (tokenizer.begin() == tokenizer.end())
+        throw TypeException(typeInfo->getName(), fieldName + ": invalid field name");
+
+    ULONG fieldOffset = 0;
+
+    boost::tokenizer< CharSep >::iterator it = tokenizer.begin();
+    for (; it != tokenizer.end(); ++it)
+    {
+        const std::string &name = *it;
+        fieldOffset += typeInfo->getFieldOffsetByNameNotRecursively(name);
+        typeInfo = typeInfo->getField(name);
+    }
+
+    return fieldOffset;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+}   // namespace UdtUtils
+
+///////////////////////////////////////////////////////////////////////////////////
+
+}   // namespace pykd
+
+///////////////////////////////////////////////////////////////////////////////////
diff --git a/pykd/udtutils.h b/pykd/udtutils.h
new file mode 100644
index 0000000..b32a818
--- /dev/null
+++ b/pykd/udtutils.h
@@ -0,0 +1,65 @@
+
+#pragma once
+
+///////////////////////////////////////////////////////////////////////////////////
+
+#include <string>
+#include <vector>
+
+///////////////////////////////////////////////////////////////////////////////////
+
+namespace pykd {
+
+///////////////////////////////////////////////////////////////////////////////////
+
+class TypeInfo;
+typedef boost::shared_ptr<TypeInfo>  TypeInfoPtr;
+
+///////////////////////////////////////////////////////////////////////////////////
+
+namespace UdtUtils {
+
+///////////////////////////////////////////////////////////////////////////////////
+
+struct Field {
+    Field( ULONG offset, const std::string &name, TypeInfoPtr type )
+        : m_offset(offset), m_name(name), m_type(type)
+    {}
+
+    bool operator ==(const std::string &name) const {
+        return m_name == name;
+    }
+
+    ULONG m_offset;
+    std::string m_name;
+    TypeInfoPtr m_type;
+};
+
+///////////////////////////////////////////////////////////////////////////////////
+
+class FieldCollection : public std::vector< Field > {
+    typedef std::vector< Field > Base;
+public:
+    FieldCollection(const std::string &baseTypeName) : m_baseTypeName(baseTypeName)
+    {}
+
+    const Field &lookup(ULONG index) const;
+    const Field &lookup(const std::string &name) const;
+
+private:
+    std::string m_baseTypeName;
+};
+
+///////////////////////////////////////////////////////////////////////////////////
+
+ULONG getFiledOffsetRecirsive(TypeInfoPtr typeInfo, const std::string &fieldName);
+
+///////////////////////////////////////////////////////////////////////////////////
+
+}   // namespace UdtUtils
+
+///////////////////////////////////////////////////////////////////////////////////
+
+}   // namespace pykd
+
+///////////////////////////////////////////////////////////////////////////////////
diff --git a/test/scripts/typedvar.py b/test/scripts/typedvar.py
index e361491..2bedc33 100644
--- a/test/scripts/typedvar.py
+++ b/test/scripts/typedvar.py
@@ -72,9 +72,9 @@ class TypedVarTest( unittest.TestCase ):
 
     def testFieldOffset(self):
         tv = target.module.typedVar( "g_structTest" )
-        self.assertEqual( 0, tv.m_field0.offset() )
-        self.assertEqual( 4, tv.m_field1.offset() )
-        self.assertEqual( 16, tv.m_field4.offset() )
+        self.assertEqual( 0, tv.fieldOffset("m_field0") )
+        self.assertEqual( 4, tv.fieldOffset("m_field1") )
+        self.assertEqual( 16, tv.fieldOffset("m_field4") )
 
     def testArrayField(self):
         tv = target.module.typedVar( "g_struct3" )
@@ -104,7 +104,7 @@ class TypedVarTest( unittest.TestCase ):
         self.assertEqual( target.module.g_structTest, target.module.typedVar( "g_structTestPtr" ) )
 
     def testContainingRecord(self):
-        off1 = target.module.type( "structTest" ).m_field2.offset()
+        off1 = target.module.type( "structTest" ).fieldOffset("m_field2")
         off2 = target.module.offset( "g_structTest" )
         tv = target.module.containingRecord( off2 + off1, "structTest", "m_field2" )
         self.assertEqual( True, tv.m_field2 )
@@ -136,11 +136,10 @@ class TypedVarTest( unittest.TestCase ):
         self.assertEqual( 3, len( tvl ) )
         self.assertEqual( [1000,2000,3000], [ tv.m_someBaseFiled2 for tv in tvl ] )
         self.assertEqual( [1001,2001,3001], [ tv.m_childFiled1 for tv in tvl ] )
-        
+
         tvl1 = target.module.typedVarList( target.module.g_listHead, "listStruct", "listEntry" )
         tvl2 = pykd.typedVarList( target.module.g_listHead, target.moduleName + "!listStruct", "listEntry" )
         self.assertEqual( tvl1, tvl2 )
-        
 
     def testTypedVarArray(self):
         tvl = target.module.typedVarArray( target.module.g_testArray, "structTest", 2 )
diff --git a/test/scripts/typeinfo.py b/test/scripts/typeinfo.py
index 7471f6c..db56f4c 100644
--- a/test/scripts/typeinfo.py
+++ b/test/scripts/typeinfo.py
@@ -93,23 +93,27 @@ class TypeInfoTest( unittest.TestCase ):
 
     def testOffset( self ):
         ti1 = target.module.type( "structTest" )
-        self.assertEqual( 0, ti1.m_field0.offset() )
-        self.assertEqual( 4, ti1.m_field1.offset() )
-        self.assertEqual( 12, ti1.m_field2.offset() )
-        self.assertEqual( 14, ti1.m_field3.offset() )
-        
+        self.assertEqual( 0, ti1.fieldOffset("m_field0") )
+        self.assertEqual( 4, ti1.fieldOffset("m_field1") )
+        self.assertEqual( 12, ti1.fieldOffset("m_field2") )
+        self.assertEqual( 14, ti1.fieldOffset("m_field3") )
+
+        ti2 = target.module.type( "struct2" )
+        self.assertTrue( ti2.fieldOffset("m_union") >= ti2.m_struct.size() )
+        self.assertEqual( ti2.fieldOffset("m_union"), ti2.fieldOffset("m_union.m_value") )
+        self.assertEqual( 0, ti2.m_union.fieldOffset("m_value") )
+
     def testSize( self ):
         ti1 = target.module.type( "structTest" )
         self.assertEqual( 16 + pykd.ptrSize(), ti1.size() )
-        self.assertEqual( pykd.ptrSize(), target.module.type("structTest**").size() )     
+        self.assertEqual( pykd.ptrSize(), target.module.type("structTest**").size() )
         self.assertEqual( pykd.sizeof("structTest"), target.module.type("structTest").size() )
         self.assertEqual( pykd.sizeof("structTest**"), target.module.type("structTest**").size() )
-        self.assertEqual( pykd.sizeof("Int1B"), target.module.type("Int1B").size() )            
-        
+        self.assertEqual( pykd.sizeof("Int1B"), target.module.type("Int1B").size() )
 
     def testBitField( self ):
         ti = target.module.type( "g_structWithBits" )
-        self.assertEqual( 0, ti.m_bit6_7.offset() )
+        self.assertEqual( 0, ti.fieldOffset("m_bit6_7") )
         self.assertEqual( 4, ti.m_bit6_7.size() )
         self.assertEqual( "ULong:2", ti.m_bit6_7.name() )
         self.assertEqual( 2, ti.m_bit6_7.bitWidth() )
@@ -119,10 +123,10 @@ class TypeInfoTest( unittest.TestCase ):
         ti = target.module.type("enumType")
         self.assertTrue( hasattr( ti, "TWO" ) )
         self.assertEqual( 4, ti.TWO.size() )
-        
+
         ti = target.module.type("classChild")
         self.assertEqual( "enumType", ti.m_enumField.name() )
-        
+
     def testPtr(self):
         self.assertEqual( "listStruct1*", target.module.type( "g_listHead1" ).name() )
         self.assertEqual( "listStruct1*[2]", target.module.type( "g_arrOfListStruct1" ).name())
@@ -133,8 +137,8 @@ class TypeInfoTest( unittest.TestCase ):
 
     def testUnion(self):
         ti = target.module.type("unionTest")
-        self.assertEqual( 0, ti.m_doubleValue.offset() )
-        self.assertEqual( 0, ti.m_bits.offset() )
+        self.assertEqual( 0, ti.fieldOffset("m_doubleValue") )
+        self.assertEqual( 0, ti.fieldOffset("m_bits") )
         self.assertEqual( ti.size(), ti.m_doubleValue.size() )
 
     def testAsMap(self):
@@ -158,7 +162,7 @@ class TypeInfoTest( unittest.TestCase ):
 
         ti = target.module.type("StructWithNested::Nested")
         self.assertTrue( hasattr( ti, "m_nestedFiled" ) )
-        
+
     def testPrint(self):
         self.assertTrue( str(target.module.type( "g_ucharValue" ) ) )
         self.assertTrue( str(target.module.type( "g_ushortValue" ) ) )
@@ -183,12 +187,13 @@ class TypeInfoTest( unittest.TestCase ):
     def testTypedef(self):
         self.assertEqual( "structTest", pykd.typeInfo( "g_structTypeDef" ).name() )
         self.assertEqual( "structTest", pykd.typeInfo( "structTestTypeDef" ).name() )
-        
+
     def testStaticField(self):
         ti = pykd.typeInfo( "g_classChild" )
-        self.assertNotEqual( 0, ti.m_staticField.offset() )
-        self.assertNotEqual( 0, ti.m_staticConst.offset() )
-        self.assertNotEqual( 0, ti.m_stdstr.offset() )
+        self.assertNotEqual( 0, ti.m_staticField.staticOffset() )
+        if not ti.m_staticConst.staticOffset():
+            self.assertFalse( "MS DIA bug: https://connect.microsoft.com/VisualStudio/feedback/details/737430" )
+        self.assertNotEqual( 0, ti.m_stdstr.staticOffset() )
         
     def testUdtSubscribe(self):
         tv = pykd.typeInfo( "g_virtChild" )