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






4.95/5 (49投票s)
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)
- 官方网站: http://ladsoft.tripod.com/index.html
- github 上的 OrangeC/C++ 编译器和工具链: https://github.com/LADSoft/OrangeC
让我们构建一些测试和示例
对于这些示例,我在 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
}
尚未实现的内容
- 析构函数的代码生成
- 重写方法的代码生成
项目在 Zip 中的组织
所有测试都在 VS 解决方案的“test”文件夹中。
有任何问题吗?
欢迎提问! :)
历史
- 2017年2月11日:文章创建