clo*_*ven 8 c# reflection parsing rpc code-analysis
确定给定方法是读取还是写入成员变量或属性的最简单方法是什么?我正在编写一个工具来协助RPC系统,其中对远程对象的访问是昂贵的.能够检测方法中是否未使用给定对象可以允许我们避免序列化其状态.在源代码上执行它是完全合理的(但是能够在编译的代码上执行它将是惊人的)
我想我可以编写自己的简单解析器,我可以尝试使用现有的C#解析器之一并使用AST.我不确定是否可以使用Reflection使用Assemblies执行此操作.还有其他方法吗?什么是最简单的?
编辑:感谢您的所有快速回复.让我提供一些更多信息,使问题更清楚.我当然更喜欢正确,但绝对不应该非常复杂.我的意思是我们不能过分检查极端或不可能的事情(如提到的传入的代表,这是一个很好的观点).只需要检测这些情况并假设一切都可以使用而不是在那里进行优化就足够了.我认为这些案件相对不常见.我们的想法是将这个工具交给我们团队以外的开发人员,不应该关注这个优化.该工具获取其代码并为我们自己的RPC协议生成代理.(我们只使用protobuf-net进行序列化,但没有使用wcf和.net远程处理).出于这个原因,我们使用的任何东西都必须是免费的,否则我们就不会
你可以有简单的或者你可以有正确的 - 你更喜欢哪一个?
最简单的方法是解析类和方法体.然后标识作为类的属性和字段名称的标记集.出现在方法体中的那些标记的子集是您关心的属性和字段名称.
这种琐碎的分析当然是不正确的.如果你有
class C
{
int Length;
void M() { int x = "".Length; }
}
Run Code Online (Sandbox Code Playgroud)
然后你会错误地断定M引用C.Length.那是误报.
在正确的做到这一点的方法是写一个完整的C#编译器,并使用其语义分析的输出来回答你的问题.这就是IDE如何实现"转到定义"等功能.
Eric 说得对:要做好这件事,您需要相当于编译器前端的东西。他没有充分强调的是对强大的流量分析能力的需求(或者愿意接受可能通过用户注释缓解的非常保守的答案)。也许他的意思是在“语义分析”一词中,尽管他的“转到定义”示例只需要符号表,而不是流分析。
普通的 C# 解析器只能用于获得非常保守的答案(例如,如果类 C 中的方法 A 包含标识符X,则假设它读取类成员X;如果 A 不包含任何调用,则您知道它无法读取成员 X)。
除此之外的第一步是拥有编译器的符号表和类型信息(如果方法 A 直接引用类成员X,则假设它读取成员 X;如果 A 不包含任何调用并且仅在访问上下文中提及标识符X对于不属于此类类型的对象,那么您就知道它无法读取成员 X)。您还必须担心合格的参考资料;如果 Q 与 C 兼容,QX 可以读取成员 X。
关键点是调用,它可以隐藏任意操作。仅基于解析和符号表的分析可以确定,如果存在调用,则参数仅引用常量或不属于 A 可能表示的类(可能是继承的)的对象。
如果您发现一个具有 C 兼容类类型的参数,现在您必须确定该参数是否可以绑定到this,需要控制和数据流分析:
method A( ) { Object q=this;
...
...q=that;...
...
foo(q);
}
Run Code Online (Sandbox Code Playgroud)
foo 可能隐藏对 X 的访问。因此您需要两件事:流分析以确定对 q 的初始分配是否可以到达调用 foo (可能不会;q=that 可能主导对 foo 的所有调用),并调用图分析确定 foo 可能实际调用哪些方法,以便您可以分析这些方法以访问成员 X。
只要您没有足够的信息来证明相反的情况,您就可以决定要走多远,只需做出保守的假设“A 读 X”。这会给你一个“安全”的答案(如果不是“正确”或者我更愿意称之为“精确”)。
在可能有用的框架中,您可能会考虑 Mono,它肯定会解析和构建符号表。我不知道它为流程分析或调用图提取提供了哪些支持;我不希望 Mono-to-IL 前端编译器做很多这样的事情,因为人们通常将该机制隐藏在基于 JIT 的系统的 JIT 部分中。缺点是 Mono 可能落后于“现代 C#”曲线;上次我听说它只处理 C# 2.0,但我的信息可能已经过时了。
另一种选择是我们的DMS 软件重组工具包及其C# 前端。(不是开源产品)。
DMS 提供通用源代码解析、树构建/检查/分析、通用符号表支持和内置机制,用于实现控制流分析、数据流分析、指向分析(“对象 O 指向什么?”所需)。 ),并构建调用图。该机器已经通过 DMS 的 Java 和 C 前端的严格测试,并且符号表支持已用于实现完整的 C++ 名称和类型解析,因此非常有效。(您不想低估建造所有这些机器所需的工作量;我们自 1995 年以来一直致力于 DMS)。
C# 前端提供完整的 C# 4.0 解析和完整的树构建。它目前不为 C# 构建符号表(我们正在努力解决这个问题),与 Mono 相比这是一个缺点。然而,有了这样的符号表,您就可以访问所有流分析机制(已经使用 DMS 的 Java 和 C 前端进行了测试),如果 Mono 不提供这些功能,那么这可能是 Mono 的一大进步。
如果你想做好这件事,你面前有大量的工作要做。如果您想坚持“简单”,则只需解析树即可,并且可以非常保守。
您没有说太多关于了解方法是否写入成员的信息。如果您要按照您所描述的方式最小化流量,您需要区分“读”、“写”和“更新”情况,并在两个方向上优化消息。对于各种情况的分析显然非常相似。
最后,您可能会考虑直接处理 MSIL 以获取您需要的信息;您仍然会遇到流量分析和保守分析问题。您可能会发现以下技术论文很有趣;它描述了一个完全分布式的 Java 对象系统,它必须执行与您想要执行的相同的基本分析,IIRC 通过分析类文件和进行大量字节代码重写来实现。 Java管弦乐队系统