Опубликован: 28.06.2006 | Уровень: специалист | Доступ: платный | ВУЗ: Московский государственный технический университет им. Н.Э. Баумана
Дополнительный материал 2:

Приложение Б

Исходный код программы CilCodec

Исходный код программы CilCodec, выполняющей кодирование/декодирование потока инструкций языка CIL, располагается в файле CilCodec.cs:

using System;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;

public class Instruction
{
 public Instruction(OpCode code, int offset)
 {
   this.code = code;
   this.offset = offset;
   this.operand = null;
 }

 public OpCode Code { get { return code; } }

 public int Offset { get { return offset; } }

 public object Operand
 {
  get { return operand; }
  set { operand = value; }
 }

 public override string ToString()
 {
  return String.Format("  IL_{0:X8}: ",Offset) +
    Code.ToString().PadRight(10) + " " + formatOperand();
 }

 #region Private members

 private OpCode code;
 private object operand;
 private int offset;

 private string formatOperand()
 {
    string s;

    switch (code.OperandType)
    {
      case OperandType.InlineNone: /* None */
         return "";

      case OperandType.ShortInlineBrTarget: /* int8 */
      case OperandType.InlineBrTarget: /* int32 */
         return String.Format("IL_{0:X8}",operand);

      case OperandType.InlineI: /* int32 */
         return String.Format("0x{0:X8}",operand);

      case OperandType.InlineI8: /* int64 */
        return String.Format("0x{0:X16}",operand);

      case OperandType.ShortInlineI: /* int8 */
        return String.Format("0x{0:X2}",operand);

      case OperandType.ShortInlineVar: /* unsigned int8 */
      case OperandType.InlineVar: /* unsigned int16 */
        return String.Format("{0:d}",operand);

      case OperandType.ShortInlineR: /* float32 */
      case OperandType.InlineR: /* float64 */
        return String.Format("{0:e}",operand);

      case OperandType.InlineSwitch: /* switch */
        s = "(";
        foreach (int target in (int[])operand)
          s += String.Format("IL_{0:X8}",target) + " ";
        return s + ")";

      default: /* token */
         s = String.Format("{0:X8}",operand);
         return "("+s.Substring(0,2)+")"+s.Substring(2);
       }
    }

  #endregion
}

public class CilCodec
{
  public static Instruction[] DecodeCil(byte[] cilStream)
  {
     int offset = 0;
     ArrayList instructions = new ArrayList();

     while (offset < cilStream.Length)
      {
         OpCode code;
         short s2 = cilStream[offset];
         if (s2 == 0xFE)
          {
             byte s1 = cilStream[offset+1];
             code = (OpCode)(codes[((s2 << 8) | s1)]);
          }
          else
           code = (OpCode)(codes[s2]);

          Instruction ins = new Instruction(code,offset);
          offset += code.Size;

          switch (code.OperandType)
          {
             case OperandType.InlineNone: /* None */
                 break;

            case OperandType.ShortInlineBrTarget: /* int8 */
                ins.Operand = offset + 1 + (sbyte)cilStream[offset];
                offset++;
                break;

           case OperandType.InlineI: /* int32 */
               ins.Operand = BitConverter.ToInt32(cilStream,offset);
               offset += 4;
              break;

          case OperandType.InlineBrTarget: /* int32 */
              ins.Operand = offset + 4 + 
                 BitConverter.ToInt32(cilStream,offset);
              offset += 4;
              break;

          case OperandType.InlineI8: /* int64 */
              ins.Operand = BitConverter.ToInt64(cilStream,offset);
              offset += 8;
              break;

         case OperandType.ShortInlineI: /* int8 */
              ins.Operand = (sbyte)cilStream[offset++];
              break;

         case OperandType.ShortInlineVar: /* unsigned int8 */
             ins.Operand = cilStream[offset++];
             break;

          case OperandType.InlineVar: /* unsigned int16 */
              ins.Operand = BitConverter.ToUInt16(cilStream,offset);
              offset += 2;
              break;

          case OperandType.ShortInlineR: /* float32 */
              ins.Operand = BitConverter.ToSingle(cilStream,offset);
              offset += 4;
             break;

          case OperandType.InlineR: /* float64 */
              ins.Operand = BitConverter.ToDouble(cilStream,offset);
              offset += 8;
             break;

          case OperandType.InlineSwitch: /* switch */
              uint num = BitConverter.ToUInt32(cilStream,offset);
              offset += 4;
              int[] targets = new int[num];
              for (int i = 0; i < num; i++)
              {
                targets[i] = BitConverter.ToInt32(cilStream,offset);
                offset += 4;
               }

               for (int i = 0; i < num; i++)
                  targets[i] += offset;

              ins.Operand = targets;
              break;

          default: /* token */
             byte b1 = cilStream[offset++],
                 b2 = cilStream[offset++],
                 b3 = cilStream[offset++],
                 b4 = cilStream[offset++];
          
                ins.Operand = ((int)b1 << 24) | ((int)b2 << 16) | ((int)b3 << 8) | (int)b4;
                break;
      }

       instructions.Add(ins);
 }

     Instruction[] instrArray = new Instruction [instructions.Count];
     instructions.CopyTo(instrArray);
     return instrArray;
 }

public static byte[] EncodeCil(Instruction[] instructions)
{
  ArrayList Result = new ArrayList();

  foreach(Instruction ins in instructions)
  {
    short codeValue = ins.Code.Value;
    Result.Add((byte)codeValue);

    object operand = ins.Operand;
    int dataOffset = ins.Offset + ins.Code.Size;
    switch (ins.Code.OperandType)
    {
      case OperandType.InlineNone: /* None */
        break;

      case OperandType.ShortInlineBrTarget: /* int8 */
        Result.Add((byte)((int)ins.Operand - dataOffset - 1));        
        break;

      case OperandType.InlineI: /* int32 */        
        byte[] operands = BitConverter.GetBytes((Int32)operand);
        foreach( byte op in operands )
        {
        Result.Add(op);
        }
        break;

      case OperandType.InlineBrTarget: /* int32 */
        operands = BitConverter.GetBytes((Int32)operand);
        foreach( byte op in operands )
        {
        Result.Add(op);
        }
        break;

      case OperandType.InlineI8: /* int64 */
        operands = BitConverter.GetBytes((Int64)operand);
        foreach( byte op in operands )
        {
        Result.Add(op);
        }
        break;

      case OperandType.ShortInlineI: /* int8 */
        operands = BitConverter.GetBytes((sbyte)operand);
        foreach( byte op in operands )
        {
        Result.Add(op);
        }
        break;

      case OperandType.ShortInlineVar: /* unsigned int8 */        
        Result.Add((byte)operand);        
        break;

      case OperandType.InlineVar: /* unsigned int16 */        
        operands = BitConverter.GetBytes((UInt16)operand);
        foreach( byte op in operands )
        {
        Result.Add(op);
        }
        break;

      case OperandType.ShortInlineR: /* float32 */
        operands = BitConverter.GetBytes((Single)operand);
        foreach( byte op in operands )
        {
        Result.Add(op);
        }
        break;
      case OperandType.InlineR: /* float64 */
        operands = BitConverter.GetBytes((double)operand);
        foreach( byte op in operands )
        {
        Result.Add(op);
        }
        break;

      case OperandType.InlineSwitch: /* switch */
        int[] targets = (int[])ins.Operand;
        //Write length of switching table
        operands = BitConverter.GetBytes(targets.Length);
        int nextOpOffset = dataOffset;
        foreach( byte op in operands )
        {
        Result.Add(op);    
        nextOpOffset++;
        }
        nextOpOffset += targets.Length*4;
        
        foreach(int target in targets)
        {
        operands = 
          BitConverter.GetBytes(target-nextOpOffset);
        foreach( byte op in operands )
            Result.Add(op);              
        }
        break;

      default: /* token */        
        Result.Add((byte) ((int)ins.Operand >> 24));
        Result.Add((byte) ((int)ins.Operand >> 16));
        Result.Add((byte) ((int)ins.Operand >> 8));
        Result.Add((byte) ((int)ins.Operand));
        break;
    }      
    }

    return Result.ToArray(typeof(byte)) as byte[];
	}

   static CilCodec()
   {
      codes = new Hashtable();

      Type opCodesType = 
            Type.GetType("System.Reflection.Emit.OpCodes");
      FieldInfo[] fields = opCodesType.GetFields(
         (BindingFlags.Static | BindingFlags.Public));

      foreach (FieldInfo field in fields)
      {
         OpCode opCode = (OpCode)(field.GetValue(null));
         codes.Add(opCode.Value,opCode);
      }
   }

   private static Hashtable codes;
}

class Demo
{
   static void Main()
   {
      testCodec(test1);
      testCodec(test2);
   }

   static void testCodec(byte[] cilStream)
   {
      Console.WriteLine("Decoded CIL stream:");

      Instruction[] instrArray = CilCodec.DecodeCil(cilStream);
      foreach (Instruction ins in instrArray)
         Console.WriteLine(ins);

      byte[] cilStream2 = CilCodec.EncodeCil(instrArray);
      bool areEqual = cilStream.Length == cilStream2.Length;
      for (int i = 0; areEqual && i < cilStream.Length; i++)
         areEqual = cilStream[i] == cilStream2[i];

      Console.WriteLine(areEqual ? 
      "Codec is correct" : "Codec is incorrect");
      Console.WriteLine("---------------------------------------");
   }

   #region Sample instruction streams

   static byte[] test1 = new byte[] 
	{
      0x23, 0, 0, 0, 0, 0, 0, 0xF0, 0x3F, 
                       /* IL_0000:  ldc.r8   1.        */
      0x0A,           /* IL_0009:  stloc.0             */
      0x2B, 0x1B,    /* IL_000a:  br.s    IL_0027      */
      0x03,           /* IL_000c:  ldarg.1             */
      0x18,          /* IL_000d:  ldc.i4.2             */
      0x5D,           /* IL_000e:  rem                 */
      0x17,           /* IL_000f:  ldc.i4.1            */
      0x33, 0x0B,     /* IL_0010:  bne.un.s  IL_001d   */
      0x03,           /* IL_0012:  ldarg.1             */
      0x17,           /* IL_0013:  ldc.i4.1            */
      0x59,           /* IL_0014:  sub                 */
      0x10, 0x01,     /* IL_0015:  starg.s  1          */
      0x06,           /* IL_0017:  ldloc.0             */
      0x02,           /* IL_0018:  ldarg.0             */
      0x5A,           /* IL_0019:  mul                 */
      0x0A,           /* IL_001a:  stloc.0             */
      0x2B, 0x0A,     /* IL_001b:  br.s    IL_0027     */
      0x03,           /* IL_001d:  ldarg.1             */
      0x18,           /* IL_001e:  ldc.i4.2            */
      0x5B,           /* IL_001f:  div                 */
      0x10, 0x01,     /* IL_0020:  starg.s  1          */
      0x02,           /* IL_0022:  ldarg.0             */
      0x02,           /* IL_0023:  ldarg.0             */
      0x5A,           /* IL_0024:  mul                 */
      0x10, 0x00,     /* IL_0025:  starg.s  0          */
      0x03,           /* IL_0027:  ldarg.1             */
      0x2D, 0xE2,     /* IL_0028:  brtrue.s  IL_000c   */
      0x2B, 0x0C,     /* IL_002a:  br.s    IL_0038     */
      0x06,           /* IL_002c:  ldloc.0             */
      0x23, 0, 0, 0, 0, 0, 0, 0x24, 0x40, 
                       /* IL_002d:  ldc.r8   10.       */
      0x5B,           /* IL_0036:  div                 */
      0x0A,           /* IL_0037:  stloc.0             */
      0x06,           /* IL_0038:  ldloc.0             */
      0x23, 0, 0, 0, 0, 0, 0, 0x14, 0x40,       
                       /* IL_0039:  ldc.r8   5.        */
      0x30, 0xE8,     /* IL_0042:  bgt.s   IL_002c     */
      0x06,           /* IL_0044:  ldloc.0             */
      0x0B,           /* IL_0045:  stloc.1             */
      0x2B, 0x00,     /* IL_0046:  br.s    IL_0048     */
      0x07,           /* IL_0048:  ldloc.1             */
      0x2A            /* IL_0049:  ret                 */
   };

   static byte[] test2 = new byte[]
   {
      0x02,           /* IL_0000:  ldarg.0             */
      0x0B,           /* IL_0001:  stloc.1             */
      0x07,          /* IL_0002:  ldloc.1              */
      0x45, 0x03, 0, 0, 0,        
                       /* IL_0003:  switch        (    */
      0x02, 0, 0, 0,   /*       IL_0016,               */
      0x06, 0, 0, 0,   /*       IL_001a,               */
      0x0A, 0, 0, 0,   /*       IL_001e)               */
      0x2B, 0x0C,     /* IL_0014:  br.s    IL_0022     */
      0x17,           /* IL_0016:  ldc.i4.1            */
      0x0A,           /* IL_0017:  stloc.0             */
      0x2B, 0x0C,     /* IL_0018:  br.s    IL_0026     */
      0x16,           /* IL_001a:  ldc.i4.0            */
      0x0A,           /* IL_001b:  stloc.0             */
      0x2B, 0x08,     /* IL_001c:  br.s    IL_0026     */
      0x17,           /* IL_001e:  ldc.i4.1            */
      0x0A,           /* IL_001f:  stloc.0             */
      0x2B, 0x04,     /* IL_0020:  br.s    IL_0026     */
      0x16,           /* IL_0022:  ldc.i4.0            */
      0x0A,           /* IL_0023:  stloc.0             */
      0x2B, 0x00,     /* IL_0024:  br.s    IL_0026     */
      0x06,           /* IL_0026:  ldloc.0             */
      0x2A            /* IL_0027:  ret                 */
   };

   #endregion
}
Анастасия Булинкова
Анастасия Булинкова
Рабочим названием платформы .NET было
Bogdan Drumov
Bogdan Drumov
Молдова, Республика
Azamat Nurmanbetov
Azamat Nurmanbetov
Киргизия, Bishkek