在 Roslyn 的哪个位置,作为 IL 发出的 C# 'class' 语句?
Where in Roslyn is a C# 'class' statement emitted as IL?
我正在尝试修改 Microsoft Roslyn 编译器以执行一些超出所提供 API 范围的奇怪操作。但我对 Roslyn 完全陌生,而且 Roslyn 非常庞大(460 万行代码),我发现很难找到适合自己的方法。
具体来说,我想找到发出“.class”IL 语句的位置,通常是执行 C# class 的最终编译,即外部class 的结构。 (我发现了方法和表达式等内部东西的发出。)
编辑:
@nejcs 是正确的,发出的代码中没有“.class”IL 语句。我误以为您使用 .Net Reflector 或 dotPeek 时会看到什么。
我会尝试更详细地解释我正在尝试做什么,以及我正在寻找什么,希望这能让我做我想做的事情。
考虑这样一个简单的 C# class:
public static class Yacks00020001
{
public static readonly string s;
static Yacks00020001()
{
s = YacksCore.M0002("Hello world!", 42);
}
}
我想要做的是 "create" 并在发射器处理期间即时发射大量这些静态对象(显然具有不同的名称和不同的字符串)。我希望通过为 "fool" 发出 class 声明、方法和语句的发射器方法创建足够的模拟数据,并使用此模拟输入调用它们来做到这一点。
我想我在编译期间找到了描述 C# class 声明的对象,它在这里:
它是在这里创建的:
但我对此不是 100% 确定。
尽管@nejcs 在他的回答中提供了很好的信息,但我仍然找不到发射器处理 class 声明的位置。
没有 .class
IL 语句。您指的是用于 class 声明的 IL 汇编程序 (ilasm) 指令。 class 在程序集中的实际声明在特定部分中,未由任何关键字标识。有关 CLR 部分的详细说明,我建议阅读 part1 and part2.
因为 classes 只是声明,所以也没有 classes 的编译。只有当你想发送到 PE 流时,你必须检索所有声明的类型、方法和其他对象,以将它们写入流中的正确位置。所以编译是这样的:
- 让我们从CompileAndEmit方法开始。此方法执行编译并发出最终程序集。
- 如果没有解析错误,将调用 module builder is created. For C# overriden CreateModuleBuilder,这将
PEAssemblyBuilder
存储对编译的源程序集符号的引用,该符号包含所有其他符号,包括 classes。
CompileAndEmit
调用方法体的编译并将结果存储到模块构建器。
- 如果一切成功,将调用
CompileAndEmit
serialises module builder to PE stream. After some setup SerializePeToStream 并创建 EmitContext
并传递给元数据编写器的模块构建器引用。 Writer 使用模块生成器提取有关 classes 和其他对象的信息,并为最终存储创建适当的索引。
简而言之 Compilation
class(CSharpCompilation
对于 C# 代码)向程序集生成器提供源程序集符号,然后将其传递并可以查询各种对象。我不知道你想要实现什么,但你可能想要修改存储在 Compilation
本身中的内容,因为管道中没有太多逻辑。不过,我可能漏掉了什么。
编辑
根据问题编辑,我将更详细地描述元数据编写器如何提取有关 classes 的信息。为简化起见,我将专注于编写完整元数据并忽略差异元数据。
- 如果我们从
SerializePeToStreamMethod
继续,将创建完整的元数据编写器并调用 BuildMetadataAndIL。
- 此方法将调用 CreateIndices which is responsible for creation of internal structures which will be serialised to PE. We are interested in CreateIndicesForModule 方法。
CreateIndicesForModule
在 CommonPEModuleBuilder
上检索 top level types, by eventually calling GetTopLevelType。您可以看到有多种类型被检索,但我们对 GetTopLevelTypesCore
方法感兴趣。
- GetTopLevelTypesCore returns 顶级类型并遍历所有命名空间符号和 return 子类型。此时,当元数据编写器设置其内部结构以进行序列化时,您会看到直接从编译符号中检索类型。
至于你的具体问题,我仍然认为生成有效的编译对象(具有正确的符号)并保持发射阶段不变会更好。否则,您必须非常小心数据处于一致状态,否则您将遇到异常或无效 PE。
我正在尝试修改 Microsoft Roslyn 编译器以执行一些超出所提供 API 范围的奇怪操作。但我对 Roslyn 完全陌生,而且 Roslyn 非常庞大(460 万行代码),我发现很难找到适合自己的方法。
具体来说,我想找到发出“.class”IL 语句的位置,通常是执行 C# class 的最终编译,即外部class 的结构。 (我发现了方法和表达式等内部东西的发出。)
编辑:
@nejcs 是正确的,发出的代码中没有“.class”IL 语句。我误以为您使用 .Net Reflector 或 dotPeek 时会看到什么。
我会尝试更详细地解释我正在尝试做什么,以及我正在寻找什么,希望这能让我做我想做的事情。
考虑这样一个简单的 C# class:
public static class Yacks00020001
{
public static readonly string s;
static Yacks00020001()
{
s = YacksCore.M0002("Hello world!", 42);
}
}
我想要做的是 "create" 并在发射器处理期间即时发射大量这些静态对象(显然具有不同的名称和不同的字符串)。我希望通过为 "fool" 发出 class 声明、方法和语句的发射器方法创建足够的模拟数据,并使用此模拟输入调用它们来做到这一点。
我想我在编译期间找到了描述 C# class 声明的对象,它在这里:
它是在这里创建的:
但我对此不是 100% 确定。
尽管@nejcs 在他的回答中提供了很好的信息,但我仍然找不到发射器处理 class 声明的位置。
没有 .class
IL 语句。您指的是用于 class 声明的 IL 汇编程序 (ilasm) 指令。 class 在程序集中的实际声明在特定部分中,未由任何关键字标识。有关 CLR 部分的详细说明,我建议阅读 part1 and part2.
因为 classes 只是声明,所以也没有 classes 的编译。只有当你想发送到 PE 流时,你必须检索所有声明的类型、方法和其他对象,以将它们写入流中的正确位置。所以编译是这样的:
- 让我们从CompileAndEmit方法开始。此方法执行编译并发出最终程序集。
- 如果没有解析错误,将调用 module builder is created. For C# overriden CreateModuleBuilder,这将
PEAssemblyBuilder
存储对编译的源程序集符号的引用,该符号包含所有其他符号,包括 classes。 CompileAndEmit
调用方法体的编译并将结果存储到模块构建器。- 如果一切成功,将调用
CompileAndEmit
serialises module builder to PE stream. After some setup SerializePeToStream 并创建EmitContext
并传递给元数据编写器的模块构建器引用。 Writer 使用模块生成器提取有关 classes 和其他对象的信息,并为最终存储创建适当的索引。
简而言之 Compilation
class(CSharpCompilation
对于 C# 代码)向程序集生成器提供源程序集符号,然后将其传递并可以查询各种对象。我不知道你想要实现什么,但你可能想要修改存储在 Compilation
本身中的内容,因为管道中没有太多逻辑。不过,我可能漏掉了什么。
编辑
根据问题编辑,我将更详细地描述元数据编写器如何提取有关 classes 的信息。为简化起见,我将专注于编写完整元数据并忽略差异元数据。
- 如果我们从
SerializePeToStreamMethod
继续,将创建完整的元数据编写器并调用 BuildMetadataAndIL。 - 此方法将调用 CreateIndices which is responsible for creation of internal structures which will be serialised to PE. We are interested in CreateIndicesForModule 方法。
CreateIndicesForModule
在CommonPEModuleBuilder
上检索 top level types, by eventually calling GetTopLevelType。您可以看到有多种类型被检索,但我们对GetTopLevelTypesCore
方法感兴趣。- GetTopLevelTypesCore returns 顶级类型并遍历所有命名空间符号和 return 子类型。此时,当元数据编写器设置其内部结构以进行序列化时,您会看到直接从编译符号中检索类型。
至于你的具体问题,我仍然认为生成有效的编译对象(具有正确的符号)并保持发射阶段不变会更好。否则,您必须非常小心数据处于一致状态,否则您将遇到异常或无效 PE。