using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; namespace Flee.InternalTypes { internal class FleeILGenerator { private ILGenerator _myIlGenerator; private int _myLength; private int _myLabelCount; private readonly Dictionary _localBuilderTemp; private int _myPass; private int _brContext; private BranchManager _bm; public FleeILGenerator(ILGenerator ilg) { _myIlGenerator = ilg; _localBuilderTemp = new Dictionary(); _myLength = 0; _myPass = 1; _bm = new BranchManager(); } public int GetTempLocalIndex(Type localType) { LocalBuilder local = null; if (_localBuilderTemp.TryGetValue(localType, out local) == false) { local = _myIlGenerator.DeclareLocal(localType); _localBuilderTemp.Add(localType, local); } return local.LocalIndex; } /// /// after first pass, check for long branches. /// If any, we need to generate again. /// /// public bool NeedsSecondPass() { return _bm.HasLongBranches(); } /// /// need a new ILGenerator for 2nd pass. This can also /// get called for a 3rd pass when emitting to assembly. /// /// public void PrepareSecondPass(ILGenerator ilg) { _bm.ComputeBranches(); _localBuilderTemp.Clear(); _myIlGenerator = ilg; _myLength = 0; _myPass++; } public void Emit(OpCode op) { this.RecordOpcode(op); _myIlGenerator.Emit(op); } public void Emit(OpCode op, Type arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, ConstructorInfo arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, MethodInfo arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, FieldInfo arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, byte arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, sbyte arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, short arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, int arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, long arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, float arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, double arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, string arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void Emit(OpCode op, Label arg) { this.RecordOpcode(op); _myIlGenerator.Emit(op, arg); } public void EmitBranch(Label arg) { if (_myPass == 1) { _bm.AddBranch(this, arg); Emit(OpCodes.Br_S, arg); } else if (_bm.IsLongBranch(this) == false) { Emit(OpCodes.Br_S, arg); } else { Emit(OpCodes.Br, arg); } } public void EmitBranchFalse(Label arg) { if (_myPass == 1) { _bm.AddBranch(this, arg); Emit(OpCodes.Brfalse_S, arg); } else if (_bm.IsLongBranch(this) == false) { Emit(OpCodes.Brfalse_S, arg); } else { Emit(OpCodes.Brfalse, arg); } } public void EmitBranchTrue(Label arg) { if (_myPass == 1) { _bm.AddBranch(this, arg); Emit(OpCodes.Brtrue_S, arg); } else if (_bm.IsLongBranch(this) == false) { Emit(OpCodes.Brtrue_S, arg); } else { Emit(OpCodes.Brtrue, arg); } } public void MarkLabel(Label lbl) { _myIlGenerator.MarkLabel(lbl); _bm.MarkLabel(this, lbl); } public Label DefineLabel() { _myLabelCount += 1; var label = _myIlGenerator.DefineLabel(); return label; } public LocalBuilder DeclareLocal(Type localType) { return _myIlGenerator.DeclareLocal(localType); } private void RecordOpcode(OpCode op) { //Trace.WriteLine(String.Format("{0:x}: {1}", MyLength, op.Name)) int operandLength = GetOpcodeOperandSize(op.OperandType); _myLength += op.Size + operandLength; } private static int GetOpcodeOperandSize(OperandType operand) { switch (operand) { case OperandType.InlineNone: return 0; case OperandType.ShortInlineBrTarget: case OperandType.ShortInlineI: case OperandType.ShortInlineVar: return 1; case OperandType.InlineVar: return 2; case OperandType.InlineBrTarget: case OperandType.InlineField: case OperandType.InlineI: case OperandType.InlineMethod: case OperandType.InlineSig: case OperandType.InlineString: case OperandType.InlineTok: case OperandType.InlineType: case OperandType.ShortInlineR: return 4; case OperandType.InlineI8: case OperandType.InlineR: return 8; default: Debug.Fail("Unknown operand type"); break; } return 0; } [Conditional("DEBUG")] public void ValidateLength() { Debug.Assert(this.Length == this.ILGeneratorLength, "ILGenerator length mismatch"); } public int Length => _myLength; public int LabelCount => _myLabelCount; private int ILGeneratorLength => Utility.GetILGeneratorLength(_myIlGenerator); } }