📄 emitcodegen.cs
字号:
string stName = stParent + "+" + args.Name;
//string stName = args.Name;
#if false
// Debugging help
Type [] ts = m_bldModule.GetTypes();
foreach(Type t2 in ts)
{
Console.WriteLine("{0},{1}", t2.FullName, t2.GetType());
}
#endif
// ...
// The ResolveCreateType() event is fundamentally broken for Emit in V1 of .NET.
// The args just give us the short name of a class (ex 'B'). So we don't know
// if the class is a nested (really 'A+B') or a value type field.
// Since we get an ambiguous name, we have to guess what it really is
// But we may guess wrong. It could be either a nested class or a valuetype field.
// Assume the nested class (more common) and assert elsewise.
// Until this dumb bug is fixed, we can't do any better.
//
// What this means is that we can't build nested-types with the same flexibility as
// CSC.exe. (JScript, which uses Emit, also ahas the same problem).
Type t = ModuleBuilder_GetType(m_bldModule, stName);
Debug.Assert(t != null, "@todo - Can't find class '" + stName + "'. This may be due to a bug in emit regarding nested classes.");
// If the type isn't already baked, then bake it.
System.Reflection.Emit.TypeBuilder tb = t as System.Reflection.Emit.TypeBuilder;
if (tb != null)
{
CreateType(tb);
} else {
Log.WriteLine(Log.LF.CodeGen, "'{0}' already created.", stName);
}
// We're suppose to return the assembly that the type was found in.
// That's just the assembly we're creating.
return this.m_bldModule.Assembly;
}
//-------------------------------------------------------------------------
// Given an array of the assemblies that we reference (not including mscorlib)
// return a CLRTypeProvider for symbol resolution to use.
//-------------------------------------------------------------------------
public virtual ICLRtypeProvider GetProvider(Assembly [] assemblyRefs)
{
SetImportedAssemblies(assemblyRefs);
return this;
}
/***************************************************************************\
*
* EmitCodeGen.BeginOutput
*
* BeginOutput() initializes CodeGen for output to a specific file.
*
\***************************************************************************/
public virtual void
BeginOutput(
string [] stFilenames)
{
// We know that all of the options have been processed by this point.
// If no output filename was supplied via /out, then use the first
// name in the stFilenames list with the appropriate extension
if (m_stOutputName == null)
{
string stDefault = stFilenames[0];
string stExt = "";
switch(m_TargetType)
{
case TargetType.Dll:
stExt = "dll"; break;
case TargetType.Console:
case TargetType.Windows:
stExt = "exe"; break;
}
m_stOutputName = System.IO.Path.ChangeExtension(stDefault, stExt);
}
string stShortName = System.IO.Path.GetFileNameWithoutExtension(m_stOutputName);
m_alClasses = new ArrayList();
m_domain = Thread.GetDomain();
if (m_domain == null)
{
throw new AppDomainUnloadedException("Unable to retrieve AppDomain");
}
//
// Setup the AssemblyName
//
AssemblyName asmName = new AssemblyName();
// Note that we must exclude the extension or fusion gets confused.
// Specifiy the full filename when we save
asmName.Name = stShortName;
//
// Create a ModuleBuilder for our output executable file
//
//m_bldAssembly = m_domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
m_bldAssembly = m_domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Save);
Debug.Assert(m_bldAssembly != null);
m_bldModule = m_bldAssembly.DefineDynamicModule(m_stOutputName, m_stOutputName, m_fDebugInfo);
Debug.Assert(m_bldModule != null);
// Hook up the event listening.
m_resolveHandler = new ResolveEventHandler(this.ResolveCreateType);
m_domain.TypeResolve += m_resolveHandler;
// Create all the PDB documents
if (m_fDebugInfo)
{
// @todo - can we totally replace m_symWriter w/ emit?
m_symWriter = m_bldModule.GetSymWriter();
Guid g = Guid.Empty;
m_stFilenames = stFilenames;
m_symDocs = new ISymbolDocumentWriter[stFilenames.Length];
for(int i = 0; i < stFilenames.Length; i++)
{
m_symDocs[i] =
m_symWriter.DefineDocument(stFilenames[i],
g,
g,
g);
Debug.Assert(m_symDocs[i] != null);
}
}
// Get some standard hooks
// Codegen for typeof(t) must call 'System.Type::GetTypeFromHandle()'
// So get the methodinfo for that ahead of time.
System.Type t = typeof(Type);
m_infoGetTypeFromHandle = t.GetMethod("GetTypeFromHandle");
Debug.Assert(m_infoGetTypeFromHandle != null);
}
// Lookup a string filename to get the PDB document info
void SetCurrentDebugDocument(FileRange location)
{
// Nothing to do if we're not setting debug information.
if (!m_fDebugInfo)
return;
string stFilename = location.Filename;
// We know m_symDocs & m_stFilenames are parallel arrays.
// So search the string array and return from the doc array.
for(int i = 0; i < m_stFilenames.Length; i++)
{
if (m_stFilenames[i] == stFilename)
{
m_symCurrentDoc = m_symDocs[i];
return;
}
}
Debug.Assert(false, "Unknown document:" + stFilename);
}
//-------------------------------------------------------------------------
// Actually do the codegen, given the root of the tree
//-------------------------------------------------------------------------
public virtual void DoCodeGen(AST.ProgramDecl root)
{
this.Generate(root);
}
/***************************************************************************\
*
* EmitCodeGen.EndOutput
*
* EndOutput() commits any outstanding data and generates the module and assembly.
*
\***************************************************************************/
public virtual void
EndOutput()
{
//
// Setup an entry point
//
PEFileKinds kind;
switch (m_TargetType)
{
case TargetType.Console:
kind = PEFileKinds.ConsoleApplication;
break;
case TargetType.Windows:
kind = PEFileKinds.WindowApplication;
break;
case TargetType.Dll:
kind = PEFileKinds.Dll;
break;
default:
throw new ArgumentOutOfRangeException("Unknown Target type");
}
//
// Find Main() entry point for an .exe
//
if (m_TargetType == TargetType.Console || m_TargetType == TargetType.Windows)
{
MethodInfo miMain = null;
if (m_stMainClass != null)
{
// A class was specified containing the main method
Type tEntry = this.m_bldModule.GetType(m_stMainClass);
if (tEntry == null)
{
PrintError_EntryClassNotFound(m_stMainClass);
}
miMain = tEntry.GetMethod("Main");
if (miMain != null)
{
// Ensure that the main method is valid.
if (!miMain.IsStatic)
miMain = null;
}
} else {
// No explicit class specified, search for main method
// It's an error if there are multiple valid mains.
foreach (Type typeClass in m_alClasses)
{
MethodInfo miTemp = typeClass.GetMethod("Main",
BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.NonPublic);
if (miTemp != null)
{
if (miMain == null)
{
// Main has not already been found, so store this first copy.
miMain = miTemp;
}
else
{
// Main has already been found, so we may be using the wrong one.
this.PrintError_DuplicateMain();
}
}
}
} // end search
// By now, we'd better have a main method;
if (miMain == null)
{
this.PrintError_NoMain();
}
else
{
m_bldAssembly.SetEntryPoint(miMain, kind);
}
} // set Main() entry point
//
// Save to a file
// This will also do a whole bunch of verfication / Checks. If we have errors
// elsewhere, this may throw an exception
try
{
Log.WriteLine(Log.LF.CodeGen, "About to save Assembly:" + m_stOutputName);
m_bldAssembly.Save(m_stOutputName);
Log.WriteLine(Log.LF.CodeGen, "Save completed successfully");
}
// IO exceptions are ok. For example, we can't write to the file
// But anything else means that we made a mistake somewhere in codegen / resolution.
catch(System.IO.IOException e)
{
PrintError_IOException(e.Message);
}
// Unhook listeners
m_domain.TypeResolve -= m_resolveHandler;
m_resolveHandler = null;
}
#endregion Driver
#region Methods to do Generation
#region Generate Helpers
//-----------------------------------------------------------------------------
// These are emit helpers that let us abstract various emit decisions.
//-----------------------------------------------------------------------------
// Emit a this pointer onto the stack
void EmitThisRef()
{
m_ilGenerator.Emit(OpCodes.Ldarg_0); // load a 'this' pointer
}
void EmitNonPolymorphicCall(SymbolEngine.MethodExpEntry sym)
{
Debug.Assert(sym != null);
MethodInfo mdInfo = sym.Info as MethodInfo;
Debug.Assert(mdInfo != null);
m_ilGenerator.Emit(OpCodes.Call, mdInfo);
}
// Emit a call/callvirt to the specified symbol
void EmitCall(SymbolEngine.MethodExpEntry sym)
{
Debug.Assert(sym != null);
MethodInfo mdInfo = sym.Info as MethodInfo;
Debug.Assert(mdInfo != null);
if (mdInfo.IsVirtual && !sym.SymbolClass.CLRType.IsValueType)
{
m_ilGenerator.Emit(OpCodes.Callvirt, mdInfo);
}
else
{
m_ilGenerator.Emit(OpCodes.Call, mdInfo);
}
}
// Do a return by jumping to a standard label
void EmitReturn()
{
if (m_cTryDepth == 0)
m_ilGenerator.Emit(OpCodes.Br, m_lblReturn);
else
m_ilGenerator.Emit(OpCodes.Leave, m_lblReturn);
}
// Since Enter/Exit must be paired up, return an int from Enter
// that we pass to Exit to ensure a match
int EnterProtectedBlock()
{
m_cTryDepth++;
return m_cTryDepth;
}
void ExitProtectedBlock(int iOldDepth)
{
Debug.Assert(m_cTryDepth > 0);
Debug.Assert(m_cTryDepth == iOldDepth);
m_cTryDepth--;
}
// Emit the proper form of ldarg
void Emit_Ldarg(int iSlot)
{
OpCode code = OpCodes.Ldarg;
switch(iSlot)
{
case 0: code = OpCodes.Ldarg_0; break;
case 1: code = OpCodes.Ldarg_1; break;
case 2: code = OpCodes.Ldarg_2; break;
case 3: code = OpCodes.Ldarg_3; break;
}
if (4 <= iSlot && iSlot <= 255)
code = OpCodes.Ldarg_S;
if (iSlot < 4)
m_ilGenerator.Emit(code);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -