private int OperandToBranchDelta(CilInstruction instruction) { bool isShort = instruction.OpCode.OperandType == CilOperandType.ShortInlineBrTarget; int delta; switch (instruction.Operand) { case sbyte x: delta = x; break; case int x: delta = x; break; case ICilLabel label: int operandSize = isShort ? sizeof(sbyte) : sizeof(int); delta = label.Offset - (int)(_writer.Offset + (ulong)operandSize); break; default: return(ThrowInvalidOperandType <sbyte>(instruction, typeof(ICilLabel), typeof(sbyte))); } if (isShort && (delta < sbyte.MinValue || delta > sbyte.MaxValue)) { _errorListener.RegisterException(new OverflowException( $"{_diagnosticPrefix}Branch target at IL_{instruction.Offset:X4} is too far away for a ShortInlineBr instruction.")); } return(delta); }
private T ThrowInvalidOperandType <T>(CilInstruction instruction, Type expectedOperand) { string found = instruction.Operand?.GetType().Name ?? "null"; _errorListener.RegisterException(new ArgumentOutOfRangeException( $"{_diagnosticPrefix}Expected a {expectedOperand.Name} operand at IL_{instruction.Offset:X4}, but found {found}.")); return(default);
private double OperandToFloat64(CilInstruction instruction) { if (instruction.Operand is double x) { return(x); } return(ThrowInvalidOperandType <double>(instruction, typeof(double))); }
private float OperandToFloat32(CilInstruction instruction) { if (instruction.Operand is float x) { return(x); } return(ThrowInvalidOperandType <float>(instruction, typeof(float))); }
private long OperandToInt64(CilInstruction instruction) { if (instruction.Operand is long x) { return(x); } return(ThrowInvalidOperandType <long>(instruction, typeof(long))); }
private int OperandToInt32(CilInstruction instruction) { if (instruction.Operand is int x) { return(x); } return(ThrowInvalidOperandType <int>(instruction, typeof(int))); }
private sbyte OperandToSByte(CilInstruction instruction) { if (instruction.Operand is sbyte x) { return(x); } return(ThrowInvalidOperandType <sbyte>(instruction, typeof(sbyte))); }
private ushort OperandToArgumentIndex(CilInstruction instruction) { int variableIndex = _operandBuilder.GetArgumentIndex(instruction.Operand); if (instruction.OpCode.OperandType == CilOperandType.ShortInlineArgument && variableIndex > byte.MaxValue) { _errorListener.RegisterException(new OverflowException( $"{_diagnosticPrefix}Argument index at IL_{instruction.Offset:X4} is too large for a ShortInlineArgument instruction.")); } return(unchecked ((ushort)variableIndex)); }
private ushort OperandToArgumentIndex(CilInstruction instruction) { int variableIndex = _operandBuilder.GetArgumentIndex(instruction.Operand); if (instruction.OpCode.OperandType == CilOperandType.ShortInlineArgument && variableIndex > byte.MaxValue) { throw new OverflowException( $"Argument index at offset IL_{instruction.Offset:X4} is too large for a ShortInlineArgument instruction."); } return(unchecked ((ushort)variableIndex)); }
/// <summary> /// Reads the next instruction from the input stream. /// </summary> /// <returns>The instruction.</returns> public CilInstruction ReadInstruction() { ulong start = Reader.Offset; var code = ReadOpCode(); var operand = ReadOperand(code.OperandType); var result = new CilInstruction(_currentOffset, code, operand); _currentOffset += (int)(Reader.Offset - start); return(result); }
private static T ThrowInvalidOperandType <T>(CilInstruction instruction, params Type[] expectedOperands) { var names = expectedOperands .Select(o => o.Name) .ToArray(); string operandTypesString = expectedOperands.Length > 1 ? $"{string.Join(", ", names.Take(names.Length - 1))} or {names[names.Length - 1]}" : names[0]; string found = instruction.Operand?.GetType().Name ?? "null"; throw new ArgumentOutOfRangeException( $"Expected a {operandTypesString} operand at offset IL_{instruction.Offset:X4}, but found {found}."); }
private void WriteOperand(CilInstruction instruction) { switch (instruction.OpCode.OperandType) { case CilOperandType.InlineNone: break; case CilOperandType.ShortInlineI: _writer.WriteSByte(OperandToSByte(instruction)); break; case CilOperandType.InlineI: _writer.WriteInt32(OperandToInt32(instruction)); break; case CilOperandType.InlineI8: _writer.WriteInt64(OperandToInt64(instruction)); break; case CilOperandType.ShortInlineR: _writer.WriteSingle(OperandToFloat32(instruction)); break; case CilOperandType.InlineR: _writer.WriteDouble(OperandToFloat64(instruction)); break; case CilOperandType.ShortInlineVar: _writer.WriteByte((byte)OperandToLocalIndex(instruction)); break; case CilOperandType.InlineVar: _writer.WriteUInt16(OperandToLocalIndex(instruction)); break; case CilOperandType.ShortInlineArgument: _writer.WriteByte((byte)OperandToArgumentIndex(instruction)); break; case CilOperandType.InlineArgument: _writer.WriteUInt16(OperandToArgumentIndex(instruction)); break; case CilOperandType.ShortInlineBrTarget: _writer.WriteSByte((sbyte)OperandToBranchDelta(instruction)); break; case CilOperandType.InlineBrTarget: _writer.WriteInt32(OperandToBranchDelta(instruction)); break; case CilOperandType.InlineSwitch: var labels = (IList <ICilLabel>)instruction.Operand; _writer.WriteInt32(labels.Count); int baseOffset = (int)_writer.Offset + labels.Count * sizeof(int); for (int i = 0; i < labels.Count; i++) { _writer.WriteInt32(labels[i].Offset - baseOffset); } break; case CilOperandType.InlineString: _writer.WriteUInt32(_operandBuilder.GetStringToken(instruction.Operand)); break; case CilOperandType.InlineField: case CilOperandType.InlineMethod: case CilOperandType.InlineSig: case CilOperandType.InlineTok: case CilOperandType.InlineType: _writer.WriteUInt32(_operandBuilder.GetMemberToken(instruction.Operand).ToUInt32()); break; default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Writes a single instruction to the output stream. /// </summary> /// <param name="instruction">The instruction to write.</param> public void WriteInstruction(CilInstruction instruction) { WriteOpCode(instruction.OpCode); WriteOperand(instruction); }
/// <summary> /// Creates a new instruction label. /// </summary> /// <param name="instruction">The instruction to reference.</param> public CilInstructionLabel(CilInstruction instruction) { Instruction = instruction ?? throw new ArgumentNullException(nameof(instruction)); }
private void WriteOperand(CilInstruction instruction) { switch (instruction.OpCode.OperandType) { case CilOperandType.InlineNone: break; case CilOperandType.ShortInlineI: _writer.WriteSByte((sbyte)instruction.Operand); break; case CilOperandType.InlineI: _writer.WriteInt32((int)instruction.Operand); break; case CilOperandType.InlineI8: _writer.WriteInt64((long)instruction.Operand); break; case CilOperandType.ShortInlineR: _writer.WriteSingle((float)instruction.Operand); break; case CilOperandType.InlineR: _writer.WriteDouble((double)instruction.Operand); break; case CilOperandType.ShortInlineVar: _writer.WriteSByte((sbyte)_operandBuilder.GetVariableIndex(instruction.Operand)); break; case CilOperandType.InlineVar: _writer.WriteUInt16((ushort)_operandBuilder.GetVariableIndex(instruction.Operand)); break; case CilOperandType.ShortInlineArgument: _writer.WriteSByte((sbyte)_operandBuilder.GetArgumentIndex(instruction.Operand)); break; case CilOperandType.InlineArgument: _writer.WriteUInt16((ushort)_operandBuilder.GetArgumentIndex(instruction.Operand)); break; case CilOperandType.ShortInlineBrTarget: sbyte shortOffset = (sbyte)(((ICilLabel)instruction.Operand).Offset - (int)(_writer.Offset + sizeof(sbyte))); _writer.WriteSByte(shortOffset); break; case CilOperandType.InlineBrTarget: int longOffset = ((ICilLabel)instruction.Operand).Offset - (int)(_writer.Offset + sizeof(int)); _writer.WriteInt32(longOffset); break; case CilOperandType.InlineSwitch: var labels = (IList <ICilLabel>)instruction.Operand; _writer.WriteInt32(labels.Count); int baseOffset = (int)_writer.Offset + labels.Count * sizeof(int); for (int i = 0; i < labels.Count; i++) { _writer.WriteInt32(labels[i].Offset - baseOffset); } break; case CilOperandType.InlineString: _writer.WriteUInt32(_operandBuilder.GetStringToken(instruction.Operand)); break; case CilOperandType.InlineField: case CilOperandType.InlineMethod: case CilOperandType.InlineSig: case CilOperandType.InlineTok: case CilOperandType.InlineType: _writer.WriteUInt32(_operandBuilder.GetMemberToken(instruction.Operand).ToUInt32()); break; default: throw new ArgumentOutOfRangeException(); } }