我正在尝试修改Microsoft Roslyn编译器,以执行超出所提供API范围的一些奇怪的事情。但我对Roslyn来说是全新的,Roslyn是庞大的(460万行代码),而且我发现很难找到它的方法。
具体来说,我想找到发出“.class”IL语句的位置,并且通常是执行C#类的最终编译,即类的外部结构。 (我发现了内部的东西,比如方法和表达式。)
编辑:
@nejcs是正确的,在发出的代码中没有“.class”IL语句这样的东西。当你使用.Net Reflector或dotPeek时,我错误地想到了你所看到的。
我会尝试更详细地解释我正在尝试做什么,以及我正在寻找什么,希望能让我做我想做的事。
考虑一个简单的C#类:
public static class Yacks00020001
{
public static readonly string s;
static Yacks00020001()
{
s = YacksCore.M0002("Hello world!", 42);
}
}
我想做的是在发射器处理过程中“创建”并在运行中发出大量这些静态对象(显然有不同的名称和不同的字符串)。我希望通过创建足够的模拟数据来“欺骗”发出类声明,方法和语句的发射器方法,并使用此模拟输入调用它们。
我想我在编译过程中找到了描述C#类声明的对象,它就在这里:
它在这里创建:
但我对此并不是百分之百确定。
尽管@nejcs在他的回答中提供了很好的信息,但我仍然找不到发射器处理中发出类声明的位置。
答案 0 :(得分:5)
没有.class
IL声明。你所指的是用于类声明的IL汇编程序(ilasm)指令。程序集中类的实际声明在特定部分中,并且不由任何关键字标识。有关CLR部分的详细说明,我建议您阅读part1和part2。
由于类只是声明,因此也没有类的编译。只有当您想要发送到PE流时,您必须检索所有声明的类型,方法和其他对象,以将它们写入流内的正确位置。所以汇编就是这样的:
PEAssemblyBuilder
将存储对编译的源程序集符号的引用,该符号包含所有其他符号,包括类。CompileAndEmit
调用方法体的编译并将结果存储到模块构建器。CompileAndEmit
serialises module builder to PE stream。在调用一些设置SerializePeToStream之后,创建EmitContext
并将模块构建器引用传递给元数据编写器。 Writer使用模块构建器来提取有关类和其他对象的信息,并为最终存储创建适当的索引。简而言之Compilation
类(C#代码的CSharpCompilation
)为程序集构建器提供源程序集符号,然后传递它并可以查询各种对象。我不知道你想要实现什么,但你可能想要修改Compilation
本身内存储的内容,因为管道中没有太多逻辑。不过,我可能错过了一些东西。
修改
基于问题编辑,我将更详细地描述元数据编写器如何提取有关类的信息。为了简化,我将专注于编写完整的元数据并忽略差异元数据。
SerializePeToStreamMethod
继续,则会创建完整的元数据编写器并调用BuildMetadataAndIL。CreateIndicesForModule
最终在CommonPEModuleBuilder
上调用top level types来检索GetTopLevelType。您可以看到有多种类型被检索,但我们对GetTopLevelTypesCore
方法感兴趣。至于你的具体问题,我仍然认为最好生成有效的编译对象(使用正确的符号)并保持发射阶段不变。否则,您必须非常小心数据处于一致状态,否则您将获得异常或PE无效。