Delphi中类的运行期TypeInfo信息结构说明(转载)
Delphi中类的运行期TypeInfo信息结构说明
作者:刘啸
CnPack开发组 http://www.cnpack.org
关键字:RTTI, TypeInfo, TypeData, PropInfo
(转载请注明出处并保持完整)
一、引子
Delphi运行期间,一个对象变量实际上是一个四字节指针,指向内存中此对象具体占据的一片区域,而区域的首个四字节又是一个指针指向该类的VMT,所有该类的实例对象的区域的首四字节指针都指向同一个VMT,故此一个VMT基本上就可以代表类本身。而每个类的VMT前面(VMT指针所指处的负偏移处)保存了该类的一些运行期信息,包括-44(vmtClassName)处的指向ClassName的字符串指针,-40(vmtInstanceSize)处的对象实例大小InstanceSize等。而本文专门讲述其-60(vmtTypeInfo)处的TypeInfo/ClassInfo指针所指的、本类的属性的RTTI信息。
二、TTypeInfo及其结构
TypInfo单元中声明的TTypeInfo结构描述了所有带RTTI的基本类型信息,而不光是针对类的。一个类的VMT首部偏移-60(vmtTypeInfo)处的四字节是一个TypeInfo/ClassInfo指针,指向一个TTypeInfo结构。
TTypeInfo在TypInfo中的定义与加的注释如下:
TTypeInfo = record
Kind: TTypeKind; // 该类型信息所描述的类型,是类则为tkClass
Name: ShortString; // 该类型信息所描述的类型名,是类时则为类名。
{TypeData: TTypeData}
end;
虽然看起来它定义得挺简单,只有两个成员,但它在运行期却是个巨大的复杂结构,因为它后面实际上紧接着一个TTypeData结构。TTypeData 结构是个大的共用体,对于类来说,它的定义和注释节选一段如下:
TTypeData = packed record
...
case TTypeKind of
tkClass: (
ClassType: TClass;
ParentInfo: PPTypeInfo; // 指向父类的 TypeInfo 结构
PropCount: SmallInt; // 本类的总属性数目,包括父类的属性数
UnitName: ShortStringBase; // 本类所在的单元名
{PropData: TPropData});
...
end;
这个结构除了这四个成员外,后面在运行期间跟着一个TPropData结构,这个结构则存储了所有属性的类型信息。TPropData的结构定义和注释如下:
TPropData = packed record
PropCount: Word; // 本类的属性数目,不包括父类
PropList: record end;
{PropList: array[1..PropCount] of TPropInfo}
end;
它其中就一个PropCount,后面是个不定长的PropList的数组,每个元素是一个属性描述结构TPropInfo。
TPropInfo定义又如下:
PPropInfo = ^TPropInfo;
TPropInfo = packed record
PropType: PPTypeInfo;
GetProc: Pointer;
SetProc: Pointer;
StoredProc: Pointer;
Index: Integer;
Default: Longint;
NameIndex: SmallInt;
// NameIndex 是本属性在本类所有属性中的排名。
// 一个类的所有直属属性的排名可能不是从0开始的,因为父类可能有属性。
Name: ShortString;
end;
这样,以上几个结构便嵌套而组成了一个类的巨大的属性信息,所有内容全是顺序排列,连ShortString都是。
需要说明的是,这儿所写的ShortString在实际场合并不是固定的长255,而是个可变长的字符串,第0个字节是长度,从字符串第一位开始跳过长度所指明的距离便到了下一个成员。这样的字符串紧凑结构有利于节省内存。
三、图示
以上介绍难免不够直观,这里用文本画一个图以指明它们的关系:
|---------|
|ClassInfo|---|
|---------| |
Object Ref |---------| |
|-------| | ... | |
| Ref | Object |---------| |
|-------|----->|-------|0 |---------| |
|VMT Ptr|----->|---------|0 |
|Field1 | | VM 1 | |
|Field2 | | VM 2 | |
|-------| |---------| |
|
|
|-------------------------------------------
|
|
|--->|TTypeInfo--------------------------|0
|Kind: TTypeKind; |
|Name: ShortString; // 不定长 |
| |TTypeData------------------------|
| |ClassType: TClass; |
| |ParentInfo: PPTypeInfo; |// 指向父类的ClassInfo
| |PropCount: SmallInt; |
| |UnitName: ShortStringBase; |// 不定长
| | |TPropData----------------------|
| | |PropCount: Word; |
| | | |PropList(TPropInfo array)----|
| | | | |1PropType: PPTypeInfo; |
| | | | |1GetProc: Pointer; |
| | | | |1SetProc: Pointer; |
| | | | |1StoredProc: Pointer; |
| | | | |1Index: Integer; |
| | | | |1Default: Longint; |
| | | | |1NameIndex: SmallInt; |
| | | | |1Name: ShortString; |// 不定长
| | | | |2PropType: PPTypeInfo; |
| | | | |2GetProc: Pointer; |
| | | | |2SetProc: Pointer; |
| | | | |2StoredProc: Pointer; |
| | | | |2Index: Integer; |
| | | | |2Default: Longint; |
| | | | |2NameIndex: SmallInt; |
| | | | |2Name: ShortString; |
| | | | |... |
| | | | |... |
四、获取属性信息的系统函数分析
这里分析几个运行期获得类属性的RTTI信息的函数,以加深对本文的理解。
1.GetTypeData 从一个类的 TypeInfo/ClassInfo 指针得到一个类的 TypeData 指针。
function GetTypeData(TypeInfo: PTypeInfo): PTypeData; assembler;
asm
{ -> EAX Pointer to type info }
{ <- EAX Pointer to type data }
{ it's really just to skip the kind and the name }
XOR EDX,EDX
MOV DL,[EAX].TTypeInfo.Name.Byte[0]
LEA EAX,[EAX].TTypeInfo.Name[EDX+1]
end;
这个函数比较简单,就是从TTypeInfo中跳过Kind和Name,直接到TypeData的指针。代码中的注释也说明了这一点。
2. GetPropInfos
本函数将一个类的所有属性信息的地址转存到一个预先分配好的列表中,其内在机制稍微复杂一点,简而言之是遍历本类以及父类的属性数组并把遍历到的每一处的属性地址写入列表中。详见注释:
procedure GetPropInfos(TypeInfo: PTypeInfo; PropList: PPropList); assembler;
asm
{ -> EAX Pointer to type info }
{ EDX Pointer to prop list }
{ <- nothing }
PUSH EBX
PUSH ESI
PUSH EDI
XOR ECX,ECX
MOV ESI,EAX // ESI 指向 TypeInfo
MOV CL,[EAX].TTypeInfo.Name.Byte[0]
MOV EDI,EDX
XOR EAX,EAX
MOVZX ECX,[ESI].TTypeInfo.Name[ECX+1].TTypeData.PropCount
// 跳过类型名,得到后面的TypeData
REP STOSD
// 根据本类的总属性数目(已经包括了父类了),将目的数组初始化填0
@outerLoop:
MOV CL,[ESI].TTypeInfo.Name.Byte[0]
// 跳过 Name 字符串长度
LEA ESI,[ESI].TTypeInfo.Name[ECX+1]
// ESI 得到一个类的TypeData,循环开始时是本类的TypeData,
// 下一个循环时可能是父类的TypeData
MOV CL,[ESI].TTypeData.UnitName.Byte[0]
// 跳过UnitName字符串的长度
MOVZX EAX,[ESI].TTypeData.UnitName[ECX+1].TPropData.PropCount
// 得到本类的属性数目,不包括父类
TEST EAX,EAX
JE @parent // 如果本类无属性则跳到寻找父类处
LEA EDI,[ESI].TTypeData.UnitName[ECX+1].TPropData.PropList
// 准备写入PropList
@innerLoop: // 第一次进入时,EDI 指向 PropList中的第一个元素,此后 EDI 递增。
MOVZX EBX,[EDI].TPropInfo.NameIndex
// EBX 获得 EDI 指向的属性的 Index
MOV CL,[EDI].TPropInfo.Name.Byte[0]
CMP dword ptr [EDX+EBX*4],0
// 查该PropList的Index位置上是否已经存了指针了。
JNE @alreadySet
MOV [EDX+EBX*4],EDI // 没存过,则存
@alreadySet:
LEA EDI,[EDI].TPropInfo.Name[ECX+1]
// 跳过一个Name的ShortString,EDI便指向PropList中的下一个元素了。
DEC EAX
JNE @innerLoop
@parent:
MOV ESI,[ESI].TTypeData.ParentInfo
// 寻找父类的,如果有父类的信息,则 ESI 指向父类的 TypeInfo
XOR ECX,ECX
TEST ESI,ESI
JE @exit
MOV ESI,[ESI]
JMP @outerLoop
@exit:
POP EDI
POP ESI
POP EBX
end;
五、总结
本文是作者在写代码过程中的一些研究总结的结果,主要以D5/D7为准。其他版本IDE的VCL源码的相关部分和本文中的应该也没多大本质区别,欢迎一起讨论。
Delphi中类的运行期TypeInfo信息结构说明(转载)的更多相关文章
- Delphi中类的运行期TypeInfo信息结构说明
Delphi中类的运行期TypeInfo信息结构说明 CnPack 开源软件项目 2007-09-19 21:55:58 Delphi中类的运行期TypeInfo信息结构说明作者:刘啸CnPack开发 ...
- 深入理解JAVA虚拟机 晚期(运行期)优化(转载)
这一章节的内容实用性不强 所以不再手打笔记 转载了一篇 原文地址是http://blog.csdn.net/qq_27350929/article/details/54837595 在部分的商用虚拟机 ...
- Access Violation分成两大类:运行期和设计期(很全的解释)
用Delphi开发程序时,我们可以把遇到的Access Violation分成两大类:运行期和设计期. 一.设计期的Access Violation 1.硬件原因 在启动或关闭Delphi IDE以 ...
- C++学习笔记28:运行期型式信息
RTTI 运行期标识对象的型式信息 优势:允许使用指向基类的指针或引用自如地操作派生类的对象 typeid:获取表达式的型式:type_info:型式信息类 头文件:typeinfo 对象转型模板 d ...
- C++编译期多态与运行期多态
前言 今日的C++不再是个单纯的"带类的C"语言,它已经发展成为一个多种次语言所组成的语言集合,其中泛型编程与基于它的STL是C++发展中最为出彩的那部分.在面向对象C++编程中, ...
- Effective Objective-C 2.0 — 第二章 对象、消息、运行期 - 第六条:理解“属性”这一概念
开发者通过对象来 存储并传递数据. 在对象之间传递数据并执行任务的过程就叫做“消息传递”. 这两条特性的工作原理? Objective-C运行期环境(Objective-C runtime) ,提供了 ...
- effective OC2.0 52阅读笔记(二 对象、消息、运行期)
第二章:对象.消息.运行期 6 理解属性这一概念 总结:OC解决硬编码偏移量问题的做法,一种方案是把实例变量当做一种存储偏移量所用的特殊变量,交由类对象保管,偏移量会在运行期查找,叫做稳固的“应用程序 ...
- Spring事务管理只对出现运行期异常进行回滚
原文:http://blog.csdn.net/abc19900828/article/details/39497631 使用spring难免要用到spring的事务管理,要用事务管理又会很自然的选择 ...
- JVM-程序编译与代码晚期(运行期)优化
晚期(运行期)优化 1.为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time,JI ...
随机推荐
- 1.介绍(introduction)
这里主要记录一本书的学习过程: 条件独立: 意思是X和Y在given Z的情况下是独立的. 满足P(X,Y|Z) = P(X|Z)*P(Y|Z)以及P(X|Y,Z) = P(X|Z) 条件独立的一些性 ...
- Windows SDK DDK WDK (Windows Driver Kit) 区别
首先,先从基础的东西说起,开发WINDOWS下的驱动程序,需要一个专门的开发包,如:开发JAVA程序,我们可能需要一个JDK,开发WINDOWS应用程序,我们需要WINDOWS的SDK,现在开发WIN ...
- Mybatis常考面试题汇总(附答案)
1.#{}和${}的区别是什么? #{}和${}的区别是什么? 在Mybatis中,有两种占位符 #{}解析传递进来的参数数据 ${}对传递进来的参数原样拼接在SQL中 #{}是预编译处理,${}是字 ...
- 写文件的工具类,输出有格式的文件(txt、json/csv)
import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io. ...
- 10.Oracle Golden Date(ogg)的搭建和管理
一. GoldenGate 概述 GoldenGate现在是业内成熟的数据容灾与复制产品:GoldenGate是一种基于日志的结构化数据复制方式,它通过解析源数据库在线日志或归档日志获得数据的增删改变 ...
- 三维计算机视觉 —— 中层次视觉 —— RCNN Family
RCNN是从图像中检测物体位置的方法,严格来讲不属于三维计算机视觉.但是这种方法却又非常非常重要,对三维物体的检测非常有启发,所以在这里做个总结. 1.RCNN - the original idea ...
- Bootstrap模态框原理分析及问题解决
最近自学了bootstrap觉得里面模板样式挺好的,就想自己实现实现,不多说了,开始进入正题了 今天就来实现bootstrap里面的模态框弹出效果 首先很简单 实现一个类似于panel的modal 1 ...
- PHP实现今天是星期几
echo "今天是星期" . mb_substr( "日一二三四五六",date("w"),1,"utf-8" );
- 算法提高 金属采集_树形dp
算法提高 金属采集 时间限制:1.0s 内存限制:256.0MB 问题描述 人类在火星上发现了一种新的金属!这些金属分布在一些奇怪的地方,不妨叫它节点好了.一些节点之间有道路相连 ...
- weblogic反序列化漏洞CVE-2018-2628-批量检测脚本
#coding=utf-8 import socket import time import re,os,sys,codecs type = 'utf-8' reload(sys) sys.setde ...