[Mono.Debugger.Soft] Escape some unicode characters in literal strings
[mono.git] / mcs / class / Mono.Debugger.Soft / Mono.Debugger.Soft / MethodBodyMirror.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using Mono.Cecil.Cil;
5 using Mono.Cecil.Metadata;
6 using System.IO;
7 using System.Linq;
8 using System.Reflection;
9
10 namespace Mono.Debugger.Soft
11 {
12         public class MethodBodyMirror : Mirror
13         {
14                 MethodMirror method;
15                 MethodBodyInfo info;
16
17                 internal MethodBodyMirror (VirtualMachine vm, MethodMirror method, MethodBodyInfo info) : base (vm, 0) {
18                         this.method = method;
19                         this.info = info;
20                 }
21
22                 public MethodMirror Method {
23                         get {
24                                 return method;
25                         }
26                 }
27
28                 public List<ILExceptionHandler> ExceptionHandlers {
29                         get {
30                                 vm.CheckProtocolVersion (2, 18);
31                                 return info.clauses.Select (c =>
32                                 {
33                                         var handler = new ILExceptionHandler (c.try_offset, c.try_length, (ILExceptionHandlerType) c.flags, c.handler_offset, c.handler_length);
34                                         if (c.flags == ExceptionClauseFlags.None)
35                                                 handler.CatchType = vm.GetType (c.catch_type_id);
36                                         else if (c.flags == ExceptionClauseFlags.Filter)
37                                                 handler.FilterOffset = c.filter_offset;
38
39                                         return handler;
40                                 }).ToList ();
41                         }
42                 }
43
44                 public byte[] GetILAsByteArray () {
45                         return info.il;
46                 }
47
48                 public List<ILInstruction> Instructions {
49                         get {
50                                 return ReadCilBody (new BinaryReader (new MemoryStream (info.il)), info.il.Length);
51                         }
52                 }
53
54                 static bool opcodes_inited;
55
56                 static OpCode [] OneByteOpCode = new OpCode [0xe0 + 1];
57                 static OpCode [] TwoBytesOpCode = new OpCode [0x1e + 1];
58
59                 static string EscapeString (string text)
60                 {
61                         StringBuilder sb = new StringBuilder ();
62                         for (int i = 0; i < text.Length; i++) {
63                                 char c = text[i];
64                                 string txt;
65                                 switch (c) {
66                                 case '"': txt = "\\\""; break;
67                                 case '\0': txt = @"\0"; break;
68                                 case '\\': txt = @"\\"; break;
69                                 case '\a': txt = @"\a"; break;
70                                 case '\b': txt = @"\b"; break;
71                                 case '\f': txt = @"\f"; break;
72                                 case '\v': txt = @"\v"; break;
73                                 case '\n': txt = @"\n"; break;
74                                 case '\r': txt = @"\r"; break;
75                                 case '\t': txt = @"\t"; break;
76                                 default:
77                                         if (char.GetUnicodeCategory (c) == UnicodeCategory.OtherNotAssigned) {
78                                                 sb.AppendFormat ("\\u{0:x4}", (int) c);
79                                         } else {
80                                                 sb.Append (c);
81                                         }
82                                         continue;
83                                 }
84                                 sb.Append (txt);
85                         }
86                         return sb.ToString ();
87                 }
88
89                 // Adapted from Cecil
90                 List<ILInstruction> ReadCilBody (BinaryReader br, int code_size)
91                 {
92                         long start = br.BaseStream.Position;
93                         ILInstruction last = null;
94                         //GenericContext context = new GenericContext (body.Method);
95                         List<ILInstruction> code = new List<ILInstruction> ();
96
97                         var by_offset = new Dictionary<int, ILInstruction> ();
98
99                         if (!opcodes_inited) {
100                                 foreach (FieldInfo fi in typeof (OpCodes).GetFields (BindingFlags.Static|BindingFlags.Public)) {
101                                         var val = (OpCode)fi.GetValue (null);
102
103                                         if (val.Op1 == 0xff)
104                                                 OneByteOpCode [val.Op2] = val;
105                                         else
106                                                 TwoBytesOpCode [val.Op2] = val;
107                                 }
108                                 opcodes_inited = true;
109                         }
110
111                         while (br.BaseStream.Position < start + code_size) {
112                                 OpCode op;
113                                 long offset = br.BaseStream.Position - start;
114                                 int cursor = br.ReadByte ();
115                                 int token;
116                                 ResolvedToken t;
117
118                                 if (cursor == 0xfe)
119                                         op = TwoBytesOpCode [br.ReadByte ()];
120                                 else
121                                         op = OneByteOpCode [cursor];
122
123                                 ILInstruction instr = new ILInstruction ((int)offset, op, null);
124
125                                 by_offset [instr.Offset] = instr;
126
127                                 switch (op.OperandType) {
128                                 case OperandType.InlineNone :
129                                         break;
130                                 case OperandType.InlineSwitch :
131                                         uint length = br.ReadUInt32 ();
132                                         int [] branches = new int [length];
133                                         int [] buf = new int [length];
134                                         for (int i = 0; i < length; i++)
135                                                 buf [i] = br.ReadInt32 ();
136                                         for (int i = 0; i < length; i++)
137                                                 branches [i] = Convert.ToInt32 (br.BaseStream.Position - start + buf [i]);
138                                         instr.Operand = branches;
139                                         break;
140                                 case OperandType.ShortInlineBrTarget :
141                                         sbyte sbrtgt = br.ReadSByte ();
142                                         instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + sbrtgt);
143                                         break;
144                                 case OperandType.InlineBrTarget :
145                                         int brtgt = br.ReadInt32 ();
146                                         instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + brtgt);
147                                         break;
148                                 case OperandType.ShortInlineI :
149                                         if (op == OpCodes.Ldc_I4_S)
150                                                 instr.Operand = br.ReadSByte ();
151                                         else
152                                                 instr.Operand = br.ReadByte ();
153                                         break;
154                                 case OperandType.ShortInlineVar :
155                                         instr.Operand = br.ReadByte ();
156                                         break;
157                                 case OperandType.ShortInlineArg :
158                                         instr.Operand = br.ReadByte ();
159                                         break;
160                                 case OperandType.InlineSig :
161                                         br.ReadInt32 ();
162                                         //instr.Operand = GetCallSiteAt (br.ReadInt32 (), context);
163                                         break;
164                                 case OperandType.InlineI :
165                                         instr.Operand = br.ReadInt32 ();
166                                         break;
167                                 case OperandType.InlineVar :
168                                         instr.Operand = br.ReadInt16 ();
169                                         break;
170                                 case OperandType.InlineArg :
171                                         instr.Operand = br.ReadInt16 ();
172                                         break;
173                                 case OperandType.InlineI8 :
174                                         instr.Operand = br.ReadInt64 ();
175                                         break;
176                                 case OperandType.ShortInlineR :
177                                         instr.Operand = br.ReadSingle ();
178                                         break;
179                                 case OperandType.InlineR :
180                                         instr.Operand = br.ReadDouble ();
181                                         break;
182                                 case OperandType.InlineString :
183                                         token = br.ReadInt32 ();
184                                         t = vm.conn.Method_ResolveToken (Method.Id, token);
185                                         if (t.Type == TokenType.STRING)
186                                                 instr.Operand = EscapeString (t.Str);
187                                         break;
188                                 case OperandType.InlineField :
189                                 case OperandType.InlineMethod :
190                                 case OperandType.InlineType :
191                                 case OperandType.InlineTok :
192                                         token = br.ReadInt32 ();
193
194                                         t = vm.conn.Method_ResolveToken (Method.Id, token);
195
196                                         switch (t.Type) {
197                                         case TokenType.TYPE:
198                                                 instr.Operand = vm.GetType (t.Id);
199                                                 break;
200                                         case TokenType.FIELD:
201                                                 // FIXME: No vm.GetField ()
202                                                 //instr.Operand = vm.GetField (t.Id);
203                                                 break;
204                                         case TokenType.METHOD:
205                                                 instr.Operand = vm.GetMethod (t.Id);
206                                                 break;
207                                         case TokenType.UNKNOWN:
208                                                 break;
209                                         default:
210                                                 throw new NotImplementedException ("Unknown token type: " + t.Type);
211                                         }
212                                         break;
213                                 }
214
215                                 if (last != null) {
216                                         last.Next = instr;
217                                         instr.Previous = last;
218                                 }
219
220                                 last = instr;
221
222                                 code.Add (instr);
223                         }
224
225                         // resolve branches
226                         foreach (ILInstruction i in code) {
227                                 switch (i.OpCode.OperandType) {
228                                 case OperandType.ShortInlineBrTarget:
229                                 case OperandType.InlineBrTarget:
230                                         i.Operand = by_offset [(int)i.Operand];
231                                         break;
232                                 case OperandType.InlineSwitch:
233                                         int [] lbls = (int []) i.Operand;
234                                         ILInstruction [] instrs = new ILInstruction [lbls.Length];
235                                         for (int j = 0; j < lbls.Length; j++)
236                                                 instrs [j] = by_offset [lbls [j]];
237                                         i.Operand = instrs;
238                                         break;
239                                 }
240                         }
241
242                         return code;
243                 }
244         }
245 }