在Roslyn中哪个是以IL发出的C#'类'语句?

时间:2018-02-10 08:08:45

标签: c# roslyn

我正在尝试修改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#类声明的对象,它就在这里:

https://github.com/dotnet/roslyn/blob/45f6e9bc6dd457a5279f0f1b380a70ca8ac0a59d/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs

它在这里创建:

https://github.com/dotnet/roslyn/blob/45f6e9bc6dd457a5279f0f1b380a70ca8ac0a59d/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs#L343

但我对此并不是百分之百确定。

尽管@nejcs在他的回答中提供了很好的信息,但我仍然找不到发射器处理中发出类声明的位置。

1 个答案:

答案 0 :(得分:5)

没有.class IL声明。你所指的是用于类声明的IL汇编程序(ilasm)指令。程序集中类的实际声明在特定部分中,并且不由任何关键字标识。有关CLR部分的详细说明,我建议您阅读part1part2

由于类只是声明,因此也没有类的编译。只有当您想要发送到PE流时,您必须检索所有声明的类型,方法和其他对象,以将它们写入流内的正确位置。所以汇编就是这样的:

  1. 让我们从CompileAndEmit方法开始。此方法执行编译并发出最终程序集。
  2. 如果没有解析错误,module builder is created。对于C#overriden CreateModuleBuilder将被调用,PEAssemblyBuilder将存储对编译的源程序集符号的引用,该符号包含所有其他符号,包括类。
  3. CompileAndEmit调用方法体的编译并将结果存储到模块构建器。
  4. 如果一切顺利,CompileAndEmit serialises module builder to PE stream。在调用一些设置SerializePeToStream之后,创建EmitContext并将模块构建器引用传递给元数据编写器。 Writer使用模块构建器来提取有关类和其他对象的信息,并为最终存储创建适当的索引。
  5. 简而言之Compilation类(C#代码的CSharpCompilation)为程序集构建器提供源程序集符号,然后传递它并可以查询各种对象。我不知道你想要实现什么,但你可能想要修改Compilation本身内存储的内容,因为管道中没有太多逻辑。不过,我可能错过了一些东西。

    修改

    基于问题编辑,我将更详细地描述元数据编写器如何提取有关类的信息。为了简化,我将专注于编写完整的元数据并忽略差异元数据。

    1. 如果我们从SerializePeToStreamMethod继续,则会创建完整的元数据编写器并调用BuildMetadataAndIL
    2. 此方法将调用CreateIndices,负责创建将序列化为PE的内部结构。我们对CreateIndicesForModule方法感兴趣。
    3. CreateIndicesForModule最终在CommonPEModuleBuilder上调用top level types来检索GetTopLevelType。您可以看到有多种类型被检索,但我们对GetTopLevelTypesCore方法感兴趣。
    4. GetTopLevelTypesCore返回顶级类型并遍历所有名称空间符号并返回子类型。此时,您会看到当元数据编写器设置序列化的内部结构时,直接从编译符号中检索类型。
    5. 至于你的具体问题,我仍然认为最好生成有效的编译对象(使用正确的符号)并保持发射阶段不变。否则,您必须非常小心数据处于一致状态,否则您将获得异常或PE无效。