📄 emitcodegen.cs
字号:
return;
}
//
// Begin a new method definition.
//
SetCurrentMethod(nodeMethod);
string stMethodName = nodeMethod.Name;
SymbolEngine.MethodExpEntry symbol
= nodeMethod.Symbol;
Log.WriteLine(Log.LF.CodeGen, "Generating body for method '{0}' of class '{1}'", stMethodName, symbol.SymbolClass.FullName);
Debug.Assert(!symbol.SymbolClass.IsInterface, "Don't codegen method bodies for interfaces");
Debug.Assert(!symbol.Node.Mods.IsAbstract, "Don't codegen abstract methods");
//
// Get signature from the parameters
//
int iParam = 0;
AST.ParamVarDecl [] alParamDecls = nodeMethod.Params;
/*
Type [] alParamTypes = new Type[alParamDecls.Length];
foreach(AST.ParamVarDecl nodeParamDecl in alParamDecls)
{
SymbolEngine.ParamVarExpEntry sym = nodeParamDecl.ParamSymbol;
alParamTypes[iParam] = sym.m_type.CLRType;
iParam++;
}
*/
ConstructorBuilder bldCtor = null;
MethodBuilder bldMethod = null;
// get the generator for either a Ctor or a method
if (symbol.IsCtor)
{
bldCtor = symbol.Info as ConstructorBuilder;
Debug.Assert(bldCtor != null);
m_ilGenerator = bldCtor.GetILGenerator();
}
else
{
Type typeReturn = symbol.m_type.CLRType;
Debug.Assert(typeReturn != null, "Semantic check should catch null return type");
bldMethod = symbol.Info as MethodBuilder;
Debug.Assert(bldMethod != null);
m_ilGenerator = bldMethod.GetILGenerator();
}
// Now, doesn't matter if we're a ctor or a method. Just need an ilgenerator
Debug.Assert(m_ilGenerator != null);
Debug.Assert((bldCtor == null) ^ (bldMethod == null));
//
// Begin a new scope.
//
//
// Assign builders & slots for parameters
//
// Note that CLR reserves parameter slot 0 for 'this' pointer
// So start number the user parameters at 1
if (nodeMethod.Mods.IsStatic)
{
//Debug.Assert(!symbol.IsCtor, "Static ctors are not supported");
iParam = 0;
}
else
{
iParam = 1;
}
int i = 1;
foreach(AST.ParamVarDecl nodeParamDecl in alParamDecls)
{
SymbolEngine.ParamVarExpEntry sym = nodeParamDecl.ParamSymbol;
// Set parameter attributes. These are rather counter-intuitive, but
// this is what C# sets them too.
ParameterAttributes attrs = ParameterAttributes.None;
switch (nodeParamDecl.Flow)
{
case AST.EArgFlow.cIn: attrs = ParameterAttributes.None; break;
case AST.EArgFlow.cOut: attrs = ParameterAttributes.Out; break;
case AST.EArgFlow.cRef: attrs = ParameterAttributes.None; break;
}
ParameterBuilder p1 = (symbol.IsCtor) ?
bldCtor.DefineParameter(i, attrs, sym.Name) :
bldMethod.DefineParameter(i, attrs, sym.Name) ;
sym.Builder = p1;
sym.CodeGenSlot = iParam;
i++;
iParam++;
}
// All returns are really just a jump to the end where we have a single
// common return.
m_lblReturn = m_ilGenerator.DefineLabel();
//
// Assign builders & slots for locals. Resolve variables in nested scopes
// here by just flattening them all to be in a single scope and then
// giving them each unique slots. This is not optimal since we
// don't reuse slots for vars in disjoint scopes.
// @todo - how do we want to resolve vars in nested scopes?
// @todo - How should SymbolResolution & Codegen split the responsibilities
// for nested vars? Currently, it's all on codegen.
//
int iLocalSlot = 0;
AssignLocalSlots(nodeMethod.Body, ref iLocalSlot);
// Assign a localvar to hold the return value.
// This way we can return from inside an exception-block
bool fHasReturn = !nodeMethod.Symbol.IsCtor && (nodeMethod.Symbol.RetType.CLRType != typeof(void));
if (fHasReturn)
{
m_localReturnValue = m_ilGenerator.DeclareLocal(nodeMethod.Symbol.RetType.CLRType);
} else {
m_localReturnValue = null;
}
// When we return, we need to know whether to do a 'br' or a 'leave'.
// So maintain a counter of how deep in the try/catch/finally blocks we are.
// If the depth==0, then 'br' is ok, else we have to use 'leave'
m_cTryDepth = 0;
//
// Generate each of the statements in the method.
//
AST.Statement[] al = nodeMethod.Body.Statements;
foreach (AST.Statement nodeStatement in al)
{
nodeStatement.Generate(this);
Debug.Assert(m_cTryDepth == 0);
}
//
// End the scope.
//
// Generate end-of-method.
m_ilGenerator.MarkLabel(m_lblReturn);
if (fHasReturn)
{
this.Emit_Ldloc(m_localReturnValue);
}
// Mark the last character of the methoddecl for the implied return
FileRange f = nodeMethod.Location;
//this.MarkSequencePoint(new FileRange(f.Filename, f.RowEnd, f.ColEnd));
m_ilGenerator.Emit(OpCodes.Ret);
m_ilGenerator = null;
SetCurrentMethod(null);
}
#endregion
#region Generate Variable slot assignments
/***************************************************************************\
* EmitCodeGen.AssignLocalSlots
*
*
* Recursive function to assign slots/builders to all local variables.
* iLocalSlot is a counter for the current slot number and must be unique
* for each local variable. So start it at 0 and let it keep going up.
\***************************************************************************/
protected void
AssignLocalSlots(
AST.BlockStatement block,
ref int iLocalSlot
)
{
if (block == null)
return;
AST.LocalVarDecl [] alLocalDecls = block.Locals;
// Generate slots/builders for all locals in this block
foreach(AST.LocalVarDecl nodeLocalDecl in alLocalDecls)
{
SymbolEngine.LocalVarExpEntry sym = nodeLocalDecl.LocalSymbol;
LocalBuilder l1 = m_ilGenerator.DeclareLocal(sym.m_type.CLRType);
if (m_fDebugInfo)
{
l1.SetLocalSymInfo(sym.Name);
}
sym.Builder = l1;
//sym.CodeGenSlot = iLocalSlot;
iLocalSlot++;
}
// Call on all nested blocks
AST.Statement [] stmtList = block.Statements;
foreach(AST.Statement s in stmtList)
{
AssignLocalSlots(s, ref iLocalSlot);
}
}
protected void
AssignLocalSlots(
AST.Statement s,
ref int iLocalSlot
)
{
if (s == null)
return;
AST.BlockStatement blockNested = s as AST.BlockStatement;
if (blockNested != null)
{
AssignLocalSlots(blockNested, ref iLocalSlot);
return;
}
// @todo - this is a total hack. We special case the different AST types.
// Need to decide a good way to resolve nested locals
AST.IfStatement s2 = s as AST.IfStatement;
if (s2 != null)
{
AssignLocalSlots(s2.ThenStmt, ref iLocalSlot);
AssignLocalSlots(s2.ElseStmt, ref iLocalSlot);
return;
}
AST.LoopStatement s3 = s as AST.LoopStatement;
if (s3 != null)
{
AssignLocalSlots(s3.BodyStmt, ref iLocalSlot);
return;
}
AST.TryStatement s4 = s as AST.TryStatement;
if (s4 != null)
{
AssignLocalSlots(s4.TryStmt, ref iLocalSlot);
foreach(AST.CatchHandler c in s4.CatchHandlers)
AssignLocalSlots(c.Body, ref iLocalSlot);
AssignLocalSlots(s4.FinallyStmt, ref iLocalSlot);
}
AST.ForeachStatement s5 = s as AST.ForeachStatement;
if (s5 != null)
{
AssignLocalSlots(s5.ResolvedStmt, ref iLocalSlot);
}
AST.SwitchStatement s6 = s as AST.SwitchStatement;
if (s6 != null)
{
AssignLocalSlots(s6.ResolvedStatement, ref iLocalSlot);
}
}
#endregion
#region Generate Statements
/***************************************************************************\
*
* EmitCodeGen.Generate
*
* Generate() recursively generates the statement defined by the given node.
*
\***************************************************************************/
public void
Generate(
AST.ReturnStatement nodeStmt)
{
MarkSequencePoint(nodeStmt.Location);
AST.Exp exp = nodeStmt.Expression;
if (exp != null)
{
//exp.Generate(this);
AST.MethodDecl m = GetCurrentMethod();
Debug.Assert(m != null);
GenerateBoxable(exp, m.Symbol.RetType.CLRType);
this.Emit_Stloc(m_localReturnValue);
}
EmitReturn();
}
/***************************************************************************\
*
* EmitCodeGen.Generate
*
* Generate() recursively generates the statement defined by the given node.
*
\***************************************************************************/
public void
Generate(
AST.WhileStatement nodeStmt
)
{
#if false
// while(TestExp) BodyStmt;
lContinue:
[TestExp]
brfalse l2;
[BodyStmt]
goto l1;
lBreak:
#endif
Label lContinue = m_ilGenerator.DefineLabel();
Label lBreak = m_ilGenerator.DefineLabel();
m_ilGenerator.MarkLabel(lContinue);
MarkSequencePoint(nodeStmt.TestExp.Location);
nodeStmt.TestExp.GenerateAsRight(this);
m_ilGenerator.Emit(OpCodes.Brfalse, lBreak);
LoopFrame f = new LoopFrame(lBreak, lContinue);
PushLoopFrame(f);
nodeStmt.BodyStmt.Generate(this);
m_ilGenerator.Emit(OpCodes.Br, lContinue);
PopLoopFrame(f);
m_ilGenerator.MarkLabel(lBreak);
}
/***************************************************************************\
*
* EmitCodeGen.Generate
*
* Generate() recursively generates the statement defined by the given node.
*
\***************************************************************************/
public void
Generate(
AST.DoStatement nodeStmt
)
{
#if false
// do BodyStmt while (TestExp);
l1:
[BodyStmt]
lContinue:
[TestExp]
brtrue l1;
lBreak:
#endif
Label l1 = m_ilGenerator.DefineLabel();
Label lContinue = m_ilGenerator.DefineLabel();
Label lBreak = m_ilGenerator.DefineLabel();
LoopFrame f = new LoopFrame(lBreak, lContinue);
PushLoopFrame(f);
m_ilGenerator.MarkLabel(l1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -