Merge branch 'cecil-light'
[mono.git] / mcs / class / Mono.CodeContracts / Mono.CodeContracts.Rewrite.AstVisitors / CompileVisitor.cs
1 //
2 // CompileVisitor.cs
3 //
4 // Authors:
5 //      Chris Bacon (chrisbacon76@gmail.com)
6 //
7 // Copyright (C) 2010 Chris Bacon
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections.Generic;
31 using System.Linq;
32 using System.Text;
33 using Mono.Cecil.Cil;
34 using Mono.CodeContracts.Rewrite.Ast;
35
36 namespace Mono.CodeContracts.Rewrite.AstVisitors {
37         class CompileVisitor : ExprVisitor {
38
39                 public CompileVisitor (ILProcessor il, Dictionary<Expr, Instruction> instructionLookup)
40                         : this (il, instructionLookup, il.Append)
41                 {
42                 }
43
44                 public CompileVisitor (ILProcessor il, Dictionary<Expr, Instruction> instructionLookup, Action<Instruction> fnEmit)
45                 {
46                         this.il = il;
47                         this.instructionLookup = instructionLookup;
48                         this.fnEmit = fnEmit;
49                 }
50
51                 private ILProcessor il;
52                 private Dictionary<Expr, Instruction> instructionLookup;
53                 private Action<Instruction> fnEmit;
54
55                 private void Emit (Expr originalExpr, Instruction inst)
56                 {
57                         Instruction originalInst;
58                         if (this.instructionLookup != null) {
59                                 // TODO: Doesn't handle inherited contracts - need to check what to do in this case.
60                                 if (this.instructionLookup.TryGetValue (originalExpr, out originalInst)) {
61                                         inst.SequencePoint = originalInst.SequencePoint;
62                                 }
63                         }
64                         this.fnEmit (inst);
65                 }
66
67                 private void Emit (Expr originalExpr, Func<Instruction> fnCreateInstruction)
68                 {
69                         Instruction inst = fnCreateInstruction();
70                         this.Emit (originalExpr, inst);
71                 }
72
73                 private void Emit (Expr originalExpr, Func<IEnumerable<Instruction>> fnCreateInstruction)
74                 {
75                         throw new NotImplementedException ();
76                 }
77
78                 protected override Expr VisitNop (ExprNop e)
79                 {
80                         var instNop = this.il.Create (OpCodes.Nop);
81                         this.Emit (e, instNop);
82                         return e;
83                 }
84
85                 protected override Expr VisitLoadArg (ExprLoadArg e)
86                 {
87                         this.Emit (e, () => {
88                                 int index = e.Index;
89                                 switch (index) {
90                                 case 0:
91                                         return this.il.Create (OpCodes.Ldarg_0);
92                                 case 1:
93                                         return this.il.Create (OpCodes.Ldarg_1);
94                                 case 2:
95                                         return this.il.Create (OpCodes.Ldarg_2);
96                                 case 3:
97                                         return this.il.Create (OpCodes.Ldarg_3);
98                                 default:
99                                         if (e.Index <= 255) {
100                                                 return this.il.Create (OpCodes.Ldarg_S, (byte) index);
101                                         } else {
102                                                 return this.il.Create (OpCodes.Ldarg, index);
103                                         }
104                                 }
105                                 // Required due to bug in compiler
106                                 throw new NotSupportedException();
107                         });
108                         
109                         return e;
110                 }
111
112                 protected override Expr VisitLoadConstant (ExprLoadConstant e)
113                 {
114                         this.Emit (e, () => {
115                                 object v = e.Value;
116                                 if (v == null) {
117                                         return this.il.Create (OpCodes.Ldnull);
118                                 }
119                                 Type vType = v.GetType ();
120                                 TypeCode vTypeCode = Type.GetTypeCode (vType);
121                                 switch (vTypeCode) {
122                                 case TypeCode.Int32:
123                                         int value = (int) v;
124                                         switch (value) {
125                                         case -1:
126                                                 return this.il.Create (OpCodes.Ldc_I4_M1);
127                                         case 0:
128                                                 return this.il.Create (OpCodes.Ldc_I4_0);
129                                         case 1:
130                                                 return this.il.Create (OpCodes.Ldc_I4_1);
131                                         case 2:
132                                                 return this.il.Create (OpCodes.Ldc_I4_2);
133                                         case 3:
134                                                 return this.il.Create (OpCodes.Ldc_I4_3);
135                                         case 4:
136                                                 return this.il.Create (OpCodes.Ldc_I4_4);
137                                         case 5:
138                                                 return this.il.Create (OpCodes.Ldc_I4_5);
139                                         case 6:
140                                                 return this.il.Create (OpCodes.Ldc_I4_6);
141                                         case 7:
142                                                 return this.il.Create (OpCodes.Ldc_I4_7);
143                                         case 8:
144                                                 return this.il.Create (OpCodes.Ldc_I4_8);
145                                         default:
146                                                 if (value >= -128 && value <= 127) {
147                                                         return this.il.Create (OpCodes.Ldc_I4_S, (sbyte) value);
148                                                 } else {
149                                                         return this.il.Create (OpCodes.Ldc_I4, value);
150                                                 }
151                                         }
152                                         // Required due to bug in compiler
153                                         throw new NotSupportedException();
154                                 case TypeCode.Single:
155                                         return this.il.Create (OpCodes.Ldc_R4, (float) v);
156                                 case TypeCode.Double:
157                                         return this.il.Create (OpCodes.Ldc_R8, (double) v);
158                                 case TypeCode.String:
159                                         return this.il.Create (OpCodes.Ldstr, (string) v);
160                                 default:
161                                         throw new NotSupportedException ("Cannot handle constant: " + vTypeCode);
162                                 }
163                                 // Required due to bug in compiler
164                                 throw new NotSupportedException();
165                         });
166
167                         return e;
168                 }
169
170                 private Expr VisitBinary (ExprBinaryOp e, Func<Instruction> fnCreateIl)
171                 {
172                         this.Visit (e.Left);
173                         this.Visit (e.Right);
174                         var inst = fnCreateIl ();
175                         this.Emit (e, inst);
176                         return e;
177                 }
178
179                 protected override Expr VisitCompareLessThan (ExprCompareLessThan e)
180                 {
181                         return this.VisitBinary (e, () => this.il.Create (e.IsSigned ? OpCodes.Clt : OpCodes.Clt_Un));
182                 }
183
184                 protected override Expr VisitCompareGreaterThan (ExprCompareGreaterThan e)
185                 {
186                         return this.VisitBinary (e, () => this.il.Create (e.IsSigned ? OpCodes.Cgt : OpCodes.Cgt_Un));
187                 }
188
189                 protected override Expr VisitCompareEqual (ExprCompareEqual e)
190                 {
191                         return this.VisitBinary (e, () => this.il.Create (OpCodes.Ceq));
192                 }
193
194                 protected override Expr VisitAdd (ExprAdd e)
195                 {
196                         return this.VisitBinary (e, () => {
197                                 if (!e.Overflow) {
198                                         return this.il.Create (OpCodes.Add);
199                                 } else {
200                                         return this.il.Create (e.IsSigned ? OpCodes.Add_Ovf : OpCodes.Add_Ovf_Un);
201                                 }
202                         });
203                 }
204
205                 protected override Expr VisitSub (ExprSub e)
206                 {
207                         return this.VisitBinary (e, () => {
208                                 if (!e.Overflow) {
209                                         return this.il.Create (OpCodes.Sub);
210                                 } else {
211                                         return this.il.Create (e.IsSigned ? OpCodes.Sub_Ovf : OpCodes.Sub_Ovf_Un);
212                                 }
213                         });
214                 }
215
216                 protected override Expr VisitCall (ExprCall e)
217                 {
218                         foreach (var param in e.Parameters) {
219                                 this.Visit (param);
220                         }
221                         var instCall = this.il.Create (OpCodes.Call, e.Method);
222                         this.Emit (e, instCall);
223                         return e;
224                 }
225
226                 protected override Expr VisitReturn (ExprReturn e)
227                 {
228                         var instReturn = this.il.Create (OpCodes.Ret);
229                         this.Emit (e, instReturn);
230                         return e;
231                 }
232
233                 protected override Expr VisitBox (ExprBox e)
234                 {
235                         this.Visit (e.ExprToBox);
236                         var instBox = this.il.Create (OpCodes.Box, e.ReturnType);
237                         this.Emit (e, instBox);
238                         return e;
239                 }
240
241                 protected override Expr VisitConv (ExprConv e)
242                 {
243                         this.Visit (e.ExprToConvert);
244                         Instruction instConv;
245                         switch (e.ConvToType) {
246                         case TypeCode.Int32:
247                                 instConv = this.il.Create (OpCodes.Conv_I4);
248                                 break;
249                         case TypeCode.Int64:
250                                 instConv = this.il.Create (OpCodes.Conv_I8);
251                                 break;
252                         default:
253                                 throw new NotSupportedException ("Cannot conv to: " + e.ConvToType);
254                         }
255                         this.Emit (e, instConv);
256                         return e;
257                 }
258
259         }
260 }