65.9K
CodeProject 正在变化。 阅读更多。
Home

DotNetPeLib: 一个用 C++11 读写和生成 .NET 程序集的库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (49投票s)

2017年2月12日

CPOL

3分钟阅读

viewsIcon

31954

downloadIcon

427

DotNetPELib 是一个抽象托管信息的库,例如命名空间、类、字段、方法和指令。然后,这些信息可用于生成汇编语言源文件,或者 PE 可执行文件或 DLL。

引言

当我向 C/C++ 编译器 OrangeC 的创建者和开发者 David 提出从 C 代码生成 .NET 代码的想法时,我同时提出了创建库以协助生成 .NET 代码和 .NET 程序集的想法,网址为 https://github.com/LADSoft/OrangeC

看到互联网上没有类似的东西,除了用 C#/VB.NET 编写的用于生成 .NET 代码的库之外,这个想法非常受欢迎,因此他开始用 C++11 开发一个库,用于生成要使用的 .NET 程序集。

基本上,这个库是为编译器开发者设计的,他们想要或打算将他们的编译器移植为也能生成 .NET 代码。

这篇文章的主要目的是展示如何使用这个库,而不是展示它的内部工作原理。

在哪里可以获取库代码?

您可以在 这里 获取库代码。

使用此库的项目

Orange C 编译器 (https://codeproject.org.cn/Articles/1128868/Compiling-your-C-code-to-NET)

让我们构建一些测试和示例

对于这些示例,我在 VS 2015 中创建了一个项目,并在项目中添加了所有库代码。

最简单的程序

在第一个例子中,让我们构建一个最简单的程序,一个带有 main 方法并返回值的简单类。

C# 代码的表示

namespace ConsoleApp
{
    class Program
    {
        static int Main()
        {
            return 0;
        }
    }
}

为此,有必要知道这段代码在 MSIL 代码中的样子,为此,我们可以使用 C# 编译器构建这个程序,并反编译代码以查看 MSIL 代码。

MSIL 代码

.class private auto ansi beforefieldinit ConsoleApp.Program
    extends [mscorlib]System.Object
{
    .method public hidebysig specialname rtspecialname instance void .ctor () cil managed
    {
        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: nop
        IL_0007: ret
    }

    .method private hidebysig static int32 Main () cil managed
    {
        .entrypoint
        .locals init (
            [0] int32 V_0
        )

        IL_0000: ldc.i4.0
        IL_0001: stloc.0
        IL_0002: ldloc.0
        IL_0003: ret
    }
}

附注:对于这个例子,我只是做了字面翻译

#include <iostream>
#include "DotNetPELib.h"

using namespace DotNetPELib;

void main()
{
 // Create a instance of PELib with assembly name and the CorFlags
 PELib libEntry("test1", PELib::ilonly | PELib::bits32);

 // Get the context of current assemblie working
 DataContainer* working = libEntry.WorkingAssembly();

 // Create the namespace
 Namespace* nameSpace = libEntry.AllocateNamespace("ConsoleApp");
 working->Add(nameSpace); // Add the new namespace at the assemblie

 // Create the class
 Class* cls = libEntry.AllocateClass("Program", Qualifiers::Ansi, -1, -1);
 nameSpace->Add(cls); // Add the new class to the namespace

 // Create the signature of main method
 MethodSignature* sig = libEntry.AllocateMethodSignature("Main", MethodSignature::Managed, cls);
 sig->ReturnType(libEntry.AllocateType(Type::i32, 0)); // Set the return type of method

 // Create the main method
 Method* main = libEntry.AllocateMethod(sig, Qualifiers::Private | 
                Qualifiers::HideBySig | Qualifiers::Static, true);
 
 // Add the instructions to the method main
 main->AddLocal(libEntry.AllocateLocal("V_0", libEntry.AllocateType(Type::i32, 0)));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ldc_i4_0));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_stloc_0));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ldloc_0));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ret));

 try
 {
  // Try to optimize the MSIL code
  main->Optimize(libEntry);
 }
 catch (PELibError exc)
 {
  std::cout << "Optimizer error: " << exc.what() << std::endl;
 }
 
 cls->Add(main); // Add the method to the class

 libEntry.DumpOutputFile("test1.il", PELib::ilasm, false);  // Generate the MSIL code
 libEntry.DumpOutputFile("test1.exe", PELib::peexe, false); // Generate the .Net Executable
}

让我们看看输出结果

.corflags 3

.assembly test1{
}

.namespace 'ConsoleApp' {
.class ansi 'Program' {.method private static hidebysig int32 'Main'(){
 .locals (
  [0] int32 'V_0/0'
 )
 .entrypoint
 .maxstack 1

 ldc.i4.0
 stloc.0
 ldloc.0
 ret
}
}
}

创建方法签名

为了能够使用来自 .NET 的外部方法,有必要创建方法的签名,为此,我们将创建另一个源文件,所有这些签名都将在所有源代码示例中使用

#include "Signatures.h"

MethodSignature* CreateSignatureForToStringMethod(PELib& libEntry)
{
 // Create the signature of "ToString()" method
 MethodSignature *sig = libEntry.AllocateMethodSignature
                        ("ToString", MethodSignature::Instance, nullptr);
 sig->ReturnType(libEntry.AllocateType(Type::string, 0));
 sig->FullName("[mscorlib]System.Int32::ToString");
 return sig;
}

MethodSignature* CreateSignatureForToStringFormatMethod(PELib& libEntry)
{
 // Create the signature of "Format()" method
 MethodSignature *sig = libEntry.AllocateMethodSignature("ToString", 0, nullptr);
 sig->ReturnType(libEntry.AllocateType(Type::string, 0));
 sig->AddParam(libEntry.AllocateParam("a_0str", libEntry.AllocateType(Type::string, 0)));
 sig->AddParam(libEntry.AllocateParam("a_0obj", libEntry.AllocateType(Type::object, 0)));
 sig->FullName("[mscorlib]System.String::Format");
 return sig;
}

MethodSignature* CreateSignatureWriteMethod(PELib& libEntry)
{
 // Create the signature of "System.Console::WriteLine" method
 MethodSignature *sig = libEntry.AllocateMethodSignature("Write", 0, nullptr);
 sig->ReturnType(libEntry.AllocateType(Type::Void, 0));
 sig->AddParam(libEntry.AllocateParam("strng", libEntry.AllocateType(Type::string, 0)));
 sig->FullName("[mscorlib]System.Console::Write");
 return sig;
}

MethodSignature* CreateSignatureWriteLineMethod(PELib& libEntry)
{
 // Create the signature of "System.Console::WriteLine" method
 MethodSignature *sig = 
    libEntry.AllocateMethodSignature("WriteLine", 0, nullptr);
 sig->ReturnType(libEntry.AllocateType(Type::Void, 0));
 sig->AddParam(libEntry.AllocateParam
    ("strng", libEntry.AllocateType(Type::string, 0)));
 sig->FullName("[mscorlib]System.Console::WriteLine");
 return sig;
}

MethodSignature* CreateSignatureStringLength(PELib& libEntry)
{
 // callvirt instance int32 [mscorlib]System.String::get_Length()
 MethodSignature *sig = libEntry.AllocateMethodSignature
          ("get_Length", MethodSignature::Instance, nullptr);
 sig->ReturnType(libEntry.AllocateType(Type::i32, 0));
 sig->FullName("[mscorlib]System.String::get_Length");
 return sig;
}

MethodSignature* CreateSignatureSubString(PELib& libEntry)
{
 // callvirt instance string[mscorlib]System.String::Substring(int32, int32)
 MethodSignature *sig = libEntry.AllocateMethodSignature
        ("Substring", MethodSignature::Instance, nullptr);
 sig->ReturnType(libEntry.AllocateType(Type::string, 0));
 sig->AddParam(libEntry.AllocateParam
 ("a_iStart", libEntry.AllocateType(Type::i32, 0)));
 sig->AddParam(libEntry.AllocateParam
 ("a_iEnd", libEntry.AllocateType(Type::i32, 0)));
 sig->FullName("[mscorlib]System.String::Substring");
 return sig;
}

MethodSignature* CreateSignatureReadLineMethod(PELib& libEntry)
{
 // Create the signature of "System.Console::ReadLine" method
 MethodSignature *sig = libEntry.AllocateMethodSignature("ReadLine", 0, nullptr);
 sig->ReturnType(libEntry.AllocateType(Type::string, 0));
 sig->FullName("[mscorlib]System.Console::ReadLine");
 return sig;
}

MethodSignature* CreateSignatureConverToIn32Method(PELib& libEntry)
{
 // Create the signature of "System.Console::ReadLine" method
 MethodSignature *sig = libEntry.AllocateMethodSignature("ToInt32", 0, nullptr);
 sig->ReturnType(libEntry.AllocateType(Type::i32, 0));
 sig->AddParam(libEntry.AllocateParam
 ("c_nToConvert", libEntry.AllocateType(Type::string, 0)));
 sig->FullName("[mscorlib]System.Convert::ToInt32");
 return sig;
}

目前,虽然无法读取 .NET 程序集来优化方法调用过程,但有一种简单的方法可以调用方法,开发者必须给出方法的全名,正如您在源代码中看到的那样,考虑到它所属的库的名称、命名空间和类。

调用 .NET 方法

让我们构建一个简单的例子(让我们将 C# 程序作为基本示例)

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            var hello = "Hello World!";
            Console.WriteLine(hello);
            Console.WriteLine(hello.Length.ToString());
            Console.WriteLine(hello.Substring(0, 4));
        }
    }
}

所以,有一种方法可以使用 DotNetPeLib 生成这种代码,让我们构建代码...

#include <iostream>
#include "Signatures.h"

//.class private auto ansi beforefieldinit ConsoleApp.Program
//    extends [mscorlib]System.Object
//{
// .method private hidebysig static void Main () cil managed
// {
//     .entrypoint
//     .locals init (
//         [0] int32 V_0
//     )

//     IL_0000: ldstr "Hello World!"
//     IL_0005: dup
//     IL_0006: call void [mscorlib]System.Console::WriteLine(string)
//     IL_000b: dup
//     IL_000c: callvirt instance int32 [mscorlib]System.String::get_Length()
//     IL_0011: stloc.0
//     IL_0012: ldloca.s V_0
//     IL_0014: call instance string [mscorlib]System.Int32::ToString()
//     IL_0019: call void [mscorlib]System.Console::WriteLine(string)
//     IL_001e: ldc.i4.0
//     IL_001f: ldc.i4.4
//     IL_0020: callvirt instance string [mscorlib]System.String::Substring(int32,  int32)
//     IL_0025: call void [mscorlib]System.Console::WriteLine(string)
//     IL_002a: ret
// }
//}
void main()
{
 // Create a instance of PELib with assembly name and the CorFlags
 PELib libEntry("test2", PELib::ilonly | PELib::bits32);

 // Get the context of current assemblie working
 DataContainer* working = libEntry.WorkingAssembly();

 // add a reference to the assembly
 libEntry.AddExternalAssembly("mscorlib");

 // Create the namespace
 Namespace* nameSpace = libEntry.AllocateNamespace("ConsoleApp");
 working->Add(nameSpace); // Add the new namespace at the assemblie

 // Create the class
 Class* cls = libEntry.AllocateClass("Program", Qualifiers::Ansi, -1, -1);
 nameSpace->Add(cls); // Add the new class to the namespace

 // Create the signature of main method
 MethodSignature* sig = 
   libEntry.AllocateMethodSignature("Main", MethodSignature::Managed, cls);
 sig->ReturnType(libEntry.AllocateType(Type::Void, 0)); // Set the return type of method

 // Create the main method
 Method* main = libEntry.AllocateMethod(sig, Qualifiers::Private | 
 Qualifiers::HideBySig | Qualifiers::Static | Qualifiers::CIL | Qualifiers::Managed, true);

 // Create a local variable to store the lenght of string
 Local* strSize = libEntry.AllocateLocal("i", libEntry.AllocateType(Type::i32, 0));
 main->AddLocal(strSize); // Add the variable to the method
 Operand* strSizeOperand = 
 libEntry.AllocateOperand(strSize); // Create the operand of string size variable

 // Create the assignatures
 MethodSignature* writeLineMethod = CreateSignatureWriteLineMethod(libEntry);
 MethodSignature* toStringSiganature = CreateSignatureForToStringMethod(libEntry);
 MethodSignature* stringLengthSignature = CreateSignatureStringLength(libEntry);
 MethodSignature* substringSignature = CreateSignatureSubString(libEntry);

 // Alocate the string
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldstr, libEntry.AllocateOperand("Hello World!", true)));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_dup)); // copy the value to stack

 // Call the WriteLineMethod
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_call, 
 libEntry.AllocateOperand(libEntry.AllocateMethodName(writeLineMethod))));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_dup)); // copy the value to stack

 // Call the get_length() method from instancied string
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_callvirt, 
 libEntry.AllocateOperand(libEntry.AllocateMethodName(stringLengthSignature))));

 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_stloc_0)); // Put result of get_length() at local variable
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldloca_s, strSizeOperand)); // load the value of local variable

 // Call the toString method
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
                       (libEntry.AllocateMethodName(toStringSiganature))));
 
 // Call the WriteLine method
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_call, 
 libEntry.AllocateOperand(libEntry.AllocateMethodName(writeLineMethod))));
 
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldc_i4_0));   // put on stack the start index of substring
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldc_i4_5)); // put on stack the end index of substring
 
 // Call the substring() method from instancied string
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_callvirt, 
 libEntry.AllocateOperand(libEntry.AllocateMethodName(substringSignature))));
 
 // Call the WriteLine method
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_call, 
 libEntry.AllocateOperand(libEntry.AllocateMethodName(writeLineMethod))));
 
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ret)); // Return

 try
 {
  main->Optimize(libEntry);
 }
 catch (PELibError exc)
 {
  std::cout << "Optimizer error: " << exc.what() << std::endl;
 }
 cls->Add(main);

 libEntry.DumpOutputFile("test2.il", PELib::ilasm, false);  // Generate the MSIL code
 libEntry.DumpOutputFile("test2.exe", PELib::peexe, false); // Generate the .Net Executable
}

创建和调用方法

在这个例子中,我们将创建一个简单的方法,该方法接收一个整数,并且该方法将使用该数字格式化一个 string,所以,基本的程序是

    class Program
    {
        static string FormatString(int age)
        {
            string nAge = age.ToString();
            return string.Format("Your age is {0}", age);
        }

        static void Main()
        {
            Console.WriteLine("Enter with your age:");
            int age = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine(FormatString(age));
        }
    }

生成一个做同样事情的程序的代码是

#include <iostream>
#include "Signatures.h"

//.method private hidebysig static string FormatString (
//        int32 age
//    ) cil managed
//{
//    .locals init (
//        [0] string iString
//    )
//    IL_0000: ldarga.s age
//    IL_0002: call instance string [mscorlib]System.Int32::ToString()
//    IL_0007: stloc.0
//    IL_0008: ldstr "Your age is {0}"
//    IL_000d: ldloc.0
//    IL_000e: call string [mscorlib]System.String::Format(string,  object)
//    IL_0013: ret
//}
MethodSignature* CreateFormatStringMethod(PELib& libEntry, Class* cls)
{
 // Create the signature of main method
 MethodSignature* sig = libEntry.AllocateMethodSignature
 ("FormatString", MethodSignature::Managed, cls);
 sig->ReturnType(libEntry.AllocateType
 (Type::string, 0)); // Set the return type of method

                // Create a parameter
 Param* parames1 = libEntry.AllocateParam
 ("age", libEntry.AllocateType(Type::i32, 0));
 sig->AddParam(parames1);

 // Create the FormatString method
 Method* formatString = libEntry.AllocateMethod(sig, Qualifiers::Private | 
 Qualifiers::HideBySig | Qualifiers::Static | Qualifiers::CIL | Qualifiers::Managed, false);

 // Create the local variable "iString"
 Local* iStringLocal = libEntry.AllocateLocal
 ("iString", libEntry.AllocateType(Type::string, 0));
 formatString->AddLocal(iStringLocal); // Add the variable to the method

 // Create the operand of local variable
 Operand* iStringOperand = libEntry.AllocateOperand(iStringLocal);

 // Load the argument "i"
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldarga_s, libEntry.AllocateOperand(parames1)));

 // Call the toString method
 MethodSignature* toStringSiganature = CreateSignatureForToStringMethod(libEntry);
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
 (libEntry.AllocateMethodName(toStringSiganature))));

 // Put return value onto stack
 formatString->AddInstruction
 (libEntry.AllocateInstruction(Instruction::i_stloc, iStringOperand));

 // Put the string in stack
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldstr, libEntry.AllocateOperand("Your age is {0}", true)));

 // Load the top element of stack
 formatString->AddInstruction(libEntry.AllocateInstruction
                             (Instruction::i_ldloc, iStringOperand));

 // Call the format string method
 MethodSignature* formatStringMethod = CreateSignatureForToStringFormatMethod(libEntry);
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
                       (libEntry.AllocateMethodName(formatStringMethod))));

 // Return
 formatString->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ret));

 try
 {
  formatString->Optimize(libEntry);
 }
 catch (PELibError exc)
 {
  std::cout << "Optimizer error: " << exc.what() << std::endl;
 }
 cls->Add(formatString);
 return sig;
}

//.method private hidebysig static void Main () cil managed
//{
//    .entrypoint
//    IL_0000: ldstr "Enter with your age:"
//    IL_0005: call void [mscorlib]System.Console::WriteLine(string)
//    IL_000a: call string [mscorlib]System.Console::ReadLine()
//    IL_000f: call int32 [mscorlib]System.Convert::ToInt32(string)
//    IL_0014: call string ConsoleApp1.Program::FormatString(int32)
//    IL_0019: call void [mscorlib]System.Console::WriteLine(string)
//    IL_001e: ret
//}
void CreateMainMethod(PELib& libEntry, Class* cls, MethodSignature* myFormatStringMethod)
{
 // Create the signature of main method
 MethodSignature* sig = libEntry.AllocateMethodSignature
                        ("Main", MethodSignature::Managed, cls);
 sig->ReturnType(libEntry.AllocateType(Type::Void, 0)); // Set the return type of method

 // Create the main method
 Method* main = libEntry.AllocateMethod(sig, Qualifiers::Private | 
 Qualifiers::HideBySig | Qualifiers::Static | Qualifiers::CIL | Qualifiers::Managed, true);

 // Create the signatures
 MethodSignature* writeMethodSignature = CreateSignatureWriteMethod(libEntry);
 MethodSignature* writeLineMethodSignature = CreateSignatureWriteLineMethod(libEntry);
 MethodSignature* toStringSiganature = CreateSignatureForToStringMethod(libEntry);
 MethodSignature* stringLengthSignature = CreateSignatureStringLength(libEntry);
 MethodSignature* substringSignature = CreateSignatureSubString(libEntry);
 MethodSignature* convertToIn32Signature = CreateSignatureConverToIn32Method(libEntry);
 MethodSignature* readLineSignature = CreateSignatureReadLineMethod(libEntry);

 // Instructions
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldstr, libEntry.AllocateOperand("Enter with your age:", true)));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
 (libEntry.AllocateMethodName(writeMethodSignature))));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
 (libEntry.AllocateMethodName(readLineSignature))));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
 (libEntry.AllocateMethodName(convertToIn32Signature))));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
 (libEntry.AllocateMethodName(myFormatStringMethod))));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
 (libEntry.AllocateMethodName(writeLineMethodSignature))));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ret));

 try
 {
  main->Optimize(libEntry);
 }
 catch (PELibError exc)
 {
  std::cout << "Optimizer error: " << exc.what() << std::endl;
 }
 cls->Add(main);
}

void main()
{
 // Create a instance of PELib with assembly name and the CorFlags
 PELib libEntry("test3", PELib::ilonly | PELib::bits32);

 // Get the context of current assemblie working
 DataContainer* working = libEntry.WorkingAssembly();

 // add a reference to the assembly
 libEntry.AddExternalAssembly("mscorlib");

 // Create the namespace
 Namespace* nameSpace = libEntry.AllocateNamespace("ConsoleApp");
 working->Add(nameSpace); // Add the new namespace at the assemblie

 // Create the class
 Class* cls = libEntry.AllocateClass("Program", Qualifiers::Ansi, -1, -1);
 nameSpace->Add(cls);     // Add the new class to the namespace

 MethodSignature* myFormatString = CreateFormatStringMethod(libEntry, cls);
 CreateMainMethod(libEntry, cls, myFormatString);

 libEntry.DumpOutputFile("test3.il", PELib::ilasm, false);  // Generate the MSIL code
 libEntry.DumpOutputFile("test3.exe", PELib::peexe, false); // Generate the .NET Executable
}

创建一个循环

在这个例子中,我们将重用最后一个例子,我们只需添加一个代码来循环 N 次并格式化一个 string 来显示索引位置。

这个例子的基本程序是

namespace ConsoleApp
{
    class Program
    {
        static string FormatString(int i)
        {
            string iString = i.ToString();
            return string.Format("The value of index is: {0}", iString);
        }

        static void Main()
        {
            for(int i=0;i<100;i++)
                Console.WriteLine(FormatString(i));
        }
    }
}
#include <iostream>
#include "Signatures.h"

//.method private hidebysig static string FormatString (
//        int32 i
//    ) cil managed
//{
//    .locals init (
//        [0] string iString
//    )
//    IL_0000: ldarga.s i
//    IL_0002: call instance string [mscorlib]System.Int32::ToString()
//    IL_0007: stloc.0
//    IL_0008: ldstr "The parameter I is equals: {0}"
//    IL_000d: ldloc.0
//    IL_000e: call string [mscorlib]System.String::Format(string,  object)
//    IL_0013: ret
//}
MethodSignature* CreateMethod_FormatString(PELib& libEntry, Class* cls)
{
 // Create the signature of main method
 MethodSignature* sig = libEntry.AllocateMethodSignature
 ("FormatString", MethodSignature::Managed, cls);
 sig->ReturnType(libEntry.AllocateType(Type::string, 0)); // Set the return type of method

 // Create a parameter
 Param* parames1 = libEntry.AllocateParam("i", libEntry.AllocateType(Type::i32, 0));
 sig->AddParam(parames1);

 // Create the FormatString method
 Method* formatString = libEntry.AllocateMethod(sig, Qualifiers::Private | 
 Qualifiers::HideBySig | Qualifiers::Static | Qualifiers::CIL | Qualifiers::Managed, false);

 // Create the local variable "iString"
 Local* iStringLocal = libEntry.AllocateLocal
 ("iString", libEntry.AllocateType(Type::string, 0));
 formatString->AddLocal(iStringLocal); // Add the variable to the method

 // Create the operand of local variable
 Operand* iStringOperand = libEntry.AllocateOperand(iStringLocal);

 // Load the argument "i"
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldarga_s, libEntry.AllocateOperand(parames1)));

 // Call the toString method
 MethodSignature* toStringSiganature = CreateSignatureForToStringMethod(libEntry);
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
                       (libEntry.AllocateMethodName(toStringSiganature))));

 // Put return value onto stack
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_stloc, iStringOperand));

 // Put the string in stack
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldstr, libEntry.AllocateOperand
 ("The parameter I is equals: {0}", true))); // Put the string on stack

// Load the top element of stack
 formatString->AddInstruction(libEntry.AllocateInstruction
                             (Instruction::i_ldloc, iStringOperand));

 // Call the format string method
 MethodSignature* formatStringMethod = CreateSignatureForToStringFormatMethod(libEntry);
 formatString->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_call, libEntry.AllocateOperand
                       (libEntry.AllocateMethodName(formatStringMethod))));

 // Return
 formatString->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ret));

 try
 {
  formatString->Optimize(libEntry);
 }
 catch (PELibError exc)
 {
  std::cout << "Optimizer error: " << exc.what() << std::endl;
 }
 cls->Add(formatString);
 return sig;
}

//.method private hidebysig static void Main() cil managed
//{
// .entrypoint
//  .locals init(
//   [0] int32 i
//  )
//
//  IL_0000: ldc.i4.0
//  IL_0001 : stloc.0
//  IL_0002 : br.s IL_0013
//  .loop
//  {
//   IL_0004: ldloc.0
//   IL_0005 : call string ConsoleApp1.Program::FormatString(int32)
//   IL_000a : call void[mscorlib]System.Console::WriteLine(string)
//   IL_000f : ldloc.0
//   IL_0010 : ldc.i4.1
//   IL_0011 : add
//   IL_0012 : stloc.0
//
//   IL_0013 : ldloc.0
//   IL_0014 : ldc.i4.s 100
//   IL_0016 : blt.s IL_0004
//  }
//  IL_0018: ret
//}
void CreateMethod_Main(PELib& libEntry, Class* cls, MethodSignature* formatStringMethod)
{
 // Create the signature of main method
 MethodSignature* sig = libEntry.AllocateMethodSignature
                        ("Main", MethodSignature::Managed, cls);
 sig->ReturnType(libEntry.AllocateType(Type::Void, 0)); // Set the return type of method

 // Create the main method
 Method* main = libEntry.AllocateMethod(sig, Qualifiers::Private | 
 Qualifiers::HideBySig | Qualifiers::Static | Qualifiers::CIL | Qualifiers::Managed, true);

 // Create the local variable "iString"
 Local* indexCnt = libEntry.AllocateLocal("i", libEntry.AllocateType(Type::i32, 0));
 main->AddLocal(indexCnt); // Add the variable to the method

 // Create the operand of local variable
 Operand* iStringOperand = libEntry.AllocateOperand(indexCnt);

 // Create loops
 Operand* loopLabel = libEntry.AllocateOperand("loop");
 Operand* loopLabel1 = libEntry.AllocateOperand("loop1");

 // Create the signatures
 MethodSignature* writeLineSignature = CreateSignatureWriteLineMethod(libEntry);

 // Create the instructions
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ldc_i4_0));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_stloc_0));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_br_s, loopLabel1));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_label, loopLabel));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ldloc_0));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_call, 
 libEntry.AllocateOperand(libEntry.AllocateMethodName(formatStringMethod))));
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_call, 
 libEntry.AllocateOperand(libEntry.AllocateMethodName(writeLineSignature))));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldloc_0)); // load the variable 'i'
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldc_i4_1)); // Put the value 1 on top of stack
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_add)); // Add
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_stloc_0)); // put the result on stack
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_label, loopLabel1)); // Loop
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldloc_0)); // load the variable 'i'
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_ldc_i4_s, libEntry.AllocateOperand(100, Operand::i32)));
 main->AddInstruction(libEntry.AllocateInstruction
 (Instruction::i_blt_s, loopLabel)); // loop case it's not equals
 main->AddInstruction(libEntry.AllocateInstruction(Instruction::i_ret)); // exit

 try
 {
  main->Optimize(libEntry);
 }
 catch (PELibError exc)
 {
  std::cout << "Optimizer error: " << exc.what() << std::endl;
 }
 cls->Add(main);
}

//.class private auto ansi beforefieldinit ConsoleApp.Program
//    extends [mscorlib]System.Object
//{
// ...
//}
void main()
{
 // Create a instance of PELib with assembly name and the CorFlags
 PELib libEntry("test4", PELib::ilonly | PELib::bits32);

 // Get the context of current assemblie working
 DataContainer* working = libEntry.WorkingAssembly();

 // add a reference to the assembly
 libEntry.AddExternalAssembly("mscorlib");

 // Create the namespace
 Namespace* nameSpace = libEntry.AllocateNamespace("ConsoleApp");
 working->Add(nameSpace); // Add the new namespace at the assembly

 // Create the class
 Class* cls = libEntry.AllocateClass("Program", Qualifiers::Ansi, -1, -1);
 nameSpace->Add(cls); // Add the new class to the namespace

 MethodSignature* formatString = CreateMethod_FormatString(libEntry, cls);
 CreateMethod_Main(libEntry, cls, formatString);

 libEntry.DumpOutputFile("test4.il", PELib::ilasm, false);  // Generate the MSIL code
 libEntry.DumpOutputFile("test4.exe", PELib::peexe, false); // Generate the .NET Executable
}

尚未实现的内容

  1. 析构函数的代码生成
  2. 重写方法的代码生成

项目在 Zip 中的组织

所有测试都在 VS 解决方案的“test”文件夹中。

有任何问题吗?

欢迎提问! :)

历史

  • 2017年2月11日:文章创建
© . All rights reserved.