Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.cs
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / EmitContext.cs
1 //
2 // EmitContext.cs
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@novell.com)
6 //   Jb Evain (jbevain@novell.com)
7 //
8 // (C) 2008 Novell, Inc. (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Collections.ObjectModel;
32 using System.Collections.Generic;
33 using System.IO;
34 using System.Linq;
35 using System.Reflection;
36 using System.Reflection.Emit;
37 using System.Runtime.CompilerServices;
38
39 namespace System.Linq.Expressions {
40
41         class CompilationContext {
42
43                 class ParameterReplacer : ExpressionTransformer {
44
45                         CompilationContext context;
46                         ExecutionScope scope;
47                         object [] locals;
48
49                         public ParameterReplacer (CompilationContext context, ExecutionScope scope, object [] locals)
50                         {
51                                 this.context = context;
52                                 this.scope = scope;
53                                 this.locals = locals;
54                         }
55
56                         protected override Expression VisitParameter (ParameterExpression parameter)
57                         {
58                                 var scope = this.scope;
59                                 var locals = this.locals;
60
61                                 while (scope != null) {
62                                         int position = IndexOfHoistedLocal (scope, parameter);
63                                         if (position != -1)
64                                                 return ReadHoistedLocalFromArray (locals, position);
65
66                                         locals = scope.Locals;
67                                         scope = scope.Parent;
68                                 }
69
70                                 return parameter;
71                         }
72
73                         Expression ReadHoistedLocalFromArray (object [] locals, int position)
74                         {
75                                 return Expression.Field (
76                                         Expression.Convert (
77                                                 Expression.ArrayIndex (
78                                                         Expression.Constant (locals),
79                                                         Expression.Constant (position)),
80                                                 locals [position].GetType ()),
81                                         "Value");
82                         }
83
84                         int IndexOfHoistedLocal (ExecutionScope scope, ParameterExpression parameter)
85                         {
86                                 return context.units [scope.compilation_unit].IndexOfHoistedLocal (parameter);
87                         }
88                 }
89
90                 class HoistedVariableDetector : ExpressionVisitor {
91
92                         Dictionary<ParameterExpression, LambdaExpression> parameter_to_lambda =
93                                 new Dictionary<ParameterExpression, LambdaExpression> ();
94
95                         Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
96
97                         LambdaExpression lambda;
98
99                         public Dictionary<LambdaExpression, List<ParameterExpression>> Process (LambdaExpression lambda)
100                         {
101                                 Visit (lambda);
102                                 return hoisted_map;
103                         }
104
105                         protected override void VisitLambda (LambdaExpression lambda)
106                         {
107                                 this.lambda = lambda;
108                                 foreach (var parameter in lambda.Parameters)
109                                         parameter_to_lambda [parameter] = lambda;
110                                 base.VisitLambda (lambda);
111                         }
112
113                         protected override void VisitParameter (ParameterExpression parameter)
114                         {
115                                 if (lambda.Parameters.Contains (parameter))
116                                         return;
117
118                                 Hoist (parameter);
119                         }
120
121                         void Hoist (ParameterExpression parameter)
122                         {
123                                 LambdaExpression lambda;
124                                 if (!parameter_to_lambda.TryGetValue (parameter, out lambda))
125                                         return;
126
127                                 if (hoisted_map == null)
128                                         hoisted_map = new Dictionary<LambdaExpression, List<ParameterExpression>> ();
129
130                                 List<ParameterExpression> hoisted;
131                                 if (!hoisted_map.TryGetValue (lambda, out hoisted)) {
132                                         hoisted = new List<ParameterExpression> ();
133                                         hoisted_map [lambda] = hoisted;
134                                 }
135
136                                 hoisted.Add (parameter);
137                         }
138                 }
139
140                 List<object> globals = new List<object> ();
141                 List<EmitContext> units = new List<EmitContext> ();
142                 Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
143
144                 public int AddGlobal (object global)
145                 {
146                         return AddItemToList (global, globals);
147                 }
148
149                 public object [] GetGlobals ()
150                 {
151                         return globals.ToArray ();
152                 }
153
154                 static int AddItemToList<T> (T item, IList<T> list)
155                 {
156                         list.Add (item);
157                         return list.Count - 1;
158                 }
159
160                 public int AddCompilationUnit (LambdaExpression lambda)
161                 {
162                         DetectHoistedVariables (lambda);
163                         return AddCompilationUnit (null, lambda);
164                 }
165
166                 public int AddCompilationUnit (EmitContext parent, LambdaExpression lambda)
167                 {
168                         var context = new EmitContext (this, parent, lambda);
169                         var unit = AddItemToList (context, units);
170                         context.Emit ();
171                         return unit;
172                 }
173
174                 void DetectHoistedVariables (LambdaExpression lambda)
175                 {
176                         hoisted_map = new HoistedVariableDetector ().Process (lambda);
177                 }
178
179                 public List<ParameterExpression> GetHoistedLocals (LambdaExpression lambda)
180                 {
181                         if (hoisted_map == null)
182                                 return null;
183
184                         List<ParameterExpression> hoisted;
185                         hoisted_map.TryGetValue (lambda, out hoisted);
186                         return hoisted;
187                 }
188
189                 public object [] CreateHoistedLocals (int unit)
190                 {
191                         var hoisted = GetHoistedLocals (units [unit].Lambda);
192                         return new object [hoisted == null ? 0 : hoisted.Count];
193                 }
194
195                 public Expression IsolateExpression (ExecutionScope scope, object [] locals, Expression expression)
196                 {
197                         return new ParameterReplacer (this, scope, locals).Transform (expression);
198                 }
199
200                 public Delegate CreateDelegate ()
201                 {
202                         return CreateDelegate (0, new ExecutionScope (this));
203                 }
204
205                 public Delegate CreateDelegate (int unit, ExecutionScope scope)
206                 {
207                         return units [unit].CreateDelegate (scope);
208                 }
209         }
210
211         class EmitContext {
212
213                 CompilationContext context;
214                 EmitContext parent;
215                 LambdaExpression lambda;
216                 DynamicMethod method;
217                 LocalBuilder hoisted_store;
218                 List<ParameterExpression> hoisted;
219
220                 public readonly ILGenerator ig;
221
222                 public bool HasHoistedLocals {
223                         get { return hoisted != null && hoisted.Count > 0; }
224                 }
225
226                 public LambdaExpression Lambda {
227                         get { return lambda; }
228                 }
229
230                 public EmitContext (CompilationContext context, EmitContext parent, LambdaExpression lambda)
231                 {
232                         this.context = context;
233                         this.parent = parent;
234                         this.lambda = lambda;
235                         this.hoisted = context.GetHoistedLocals (lambda);
236
237                         method = new DynamicMethod (
238                                 "lambda_method",
239                                 lambda.GetReturnType (),
240                                 CreateParameterTypes (lambda.Parameters),
241                                 typeof (ExecutionScope),
242                                 true);
243
244                         ig = method.GetILGenerator ();
245                 }
246
247                 public void Emit ()
248                 {
249                         if (HasHoistedLocals)
250                                 EmitStoreHoistedLocals ();
251
252                         lambda.EmitBody (this);
253                 }
254
255                 static Type [] CreateParameterTypes (IList<ParameterExpression> parameters)
256                 {
257                         var types = new Type [parameters.Count + 1];
258                         types [0] = typeof (ExecutionScope);
259
260                         for (int i = 0; i < parameters.Count; i++)
261                                 types [i + 1] = parameters [i].Type;
262
263                         return types;
264                 }
265
266                 public bool IsLocalParameter (ParameterExpression parameter, ref int position)
267                 {
268                         position = lambda.Parameters.IndexOf (parameter);
269                         if (position > -1) {
270                                 position++;
271                                 return true;
272                         }
273
274                         return false;
275                 }
276
277                 public Delegate CreateDelegate (ExecutionScope scope)
278                 {
279                         return method.CreateDelegate (lambda.Type, scope);
280                 }
281
282                 public void Emit (Expression expression)
283                 {
284                         expression.Emit (this);
285                 }
286
287                 public LocalBuilder EmitStored (Expression expression)
288                 {
289                         var local = ig.DeclareLocal (expression.Type);
290                         expression.Emit (this);
291                         ig.Emit (OpCodes.Stloc, local);
292
293                         return local;
294                 }
295
296                 public void EmitLoadAddress (Expression expression)
297                 {
298                         ig.Emit (OpCodes.Ldloca, EmitStored (expression));
299                 }
300
301                 public void EmitLoadEnum (Expression expression)
302                 {
303                         expression.Emit (this);
304                         ig.Emit (OpCodes.Box, expression.Type);
305                 }
306
307                 public void EmitLoadEnum (LocalBuilder local)
308                 {
309                         ig.Emit (OpCodes.Ldloc, local);
310                         ig.Emit (OpCodes.Box, local.LocalType);
311                 }
312
313                 public void EmitLoadSubject (Expression expression)
314                 {
315                         if (expression.Type.IsEnum) {
316                                 EmitLoadEnum (expression);
317                                 return;
318                         }
319
320                         if (expression.Type.IsValueType) {
321                                 EmitLoadAddress (expression);
322                                 return;
323                         }
324
325                         Emit (expression);
326                 }
327
328                 public void EmitLoadSubject (LocalBuilder local)
329                 {
330                         if (local.LocalType.IsEnum) {
331                                 EmitLoadEnum (local);
332                                 return;
333                         }
334
335                         if (local.LocalType.IsValueType) {
336                                 EmitLoadAddress (local);
337                                 return;
338                         }
339
340                         EmitLoad (local);
341                 }
342
343                 public void EmitLoadAddress (LocalBuilder local)
344                 {
345                         ig.Emit (OpCodes.Ldloca, local);
346                 }
347
348                 public void EmitLoad (LocalBuilder local)
349                 {
350                         ig.Emit (OpCodes.Ldloc, local);
351                 }
352
353                 public void EmitCall (LocalBuilder local, IList<Expression> arguments, MethodInfo method)
354                 {
355                         EmitLoadSubject (local);
356                         EmitArguments (method, arguments);
357                         EmitCall (method);
358                 }
359
360                 public void EmitCall (LocalBuilder local, MethodInfo method)
361                 {
362                         EmitLoadSubject (local);
363                         EmitCall (method);
364                 }
365
366                 public void EmitCall (Expression expression, MethodInfo method)
367                 {
368                         if (!method.IsStatic)
369                                 EmitLoadSubject (expression);
370
371                         EmitCall (method);
372                 }
373
374                 public void EmitCall (Expression expression, IList<Expression> arguments, MethodInfo method)
375                 {
376                         if (!method.IsStatic)
377                                 EmitLoadSubject (expression);
378
379                         EmitArguments (method, arguments);
380                         EmitCall (method);
381                 }
382
383                 void EmitArguments (MethodInfo method, IList<Expression> arguments)
384                 {
385                         var parameters = method.GetParameters ();
386
387                         for (int i = 0; i < parameters.Length; i++) {
388                                 var parameter = parameters [i];
389                                 var argument = arguments [i];
390
391                                 if (parameter.ParameterType.IsByRef) {
392                                         ig.Emit (OpCodes.Ldloca, EmitStored (argument));
393                                         continue;
394                                 }
395
396                                 Emit (arguments [i]);
397                         }
398                 }
399
400                 public void EmitCall (MethodInfo method)
401                 {
402                         ig.Emit (
403                                 method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
404                                 method);
405                 }
406
407                 public void EmitNullableHasValue (LocalBuilder local)
408                 {
409                         EmitCall (local, "get_HasValue");
410                 }
411
412                 public void EmitNullableInitialize (LocalBuilder local)
413                 {
414                         ig.Emit (OpCodes.Ldloca, local);
415                         ig.Emit (OpCodes.Initobj, local.LocalType);
416                         ig.Emit (OpCodes.Ldloc, local);
417                 }
418
419                 public void EmitNullableGetValue (LocalBuilder local)
420                 {
421                         EmitCall (local, "get_Value");
422                 }
423
424                 public void EmitNullableGetValueOrDefault (LocalBuilder local)
425                 {
426                         EmitCall (local, "GetValueOrDefault");
427                 }
428
429                 void EmitCall (LocalBuilder local, string method_name)
430                 {
431                         EmitCall (local, local.LocalType.GetMethod (method_name, Type.EmptyTypes));
432                 }
433
434                 public void EmitNullableNew (Type of)
435                 {
436                         ig.Emit (OpCodes.Newobj, of.GetConstructor (new [] { of.GetFirstGenericArgument () }));
437                 }
438
439                 public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
440                 {
441                         foreach (var expression in collection)
442                                 expression.Emit (this);
443                 }
444
445                 public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
446                 {
447                         foreach (var initializer in initializers)
448                                 initializer.Emit (this, local);
449                 }
450
451                 public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
452                 {
453                         foreach (var binding in bindings)
454                                 binding.Emit (this, local);
455                 }
456
457                 public void EmitIsInst (Expression expression, Type candidate)
458                 {
459                         expression.Emit (this);
460
461                         var type = expression.Type;
462
463                         if (type.IsValueType)
464                                 ig.Emit (OpCodes.Box, type);
465
466                         ig.Emit (OpCodes.Isinst, candidate);
467                 }
468
469                 public void EmitScope ()
470                 {
471                         ig.Emit (OpCodes.Ldarg_0);
472                 }
473
474                 public void EmitReadGlobal (object global)
475                 {
476                         EmitReadGlobal (global, global.GetType ());
477                 }
478
479                 public void EmitLoadGlobals ()
480                 {
481                         EmitScope ();
482
483                         ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
484                 }
485
486                 public void EmitReadGlobal (object global, Type type)
487                 {
488                         EmitLoadGlobals ();
489
490                         ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
491                         ig.Emit (OpCodes.Ldelem, typeof (object));
492
493                         EmitLoadStrongBoxValue (type);
494                 }
495
496                 public void EmitLoadStrongBoxValue (Type type)
497                 {
498                         var strongbox = type.MakeStrongBoxType ();
499
500                         ig.Emit (OpCodes.Isinst, strongbox);
501                         ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
502                 }
503
504                 int AddGlobal (object value, Type type)
505                 {
506                         return context.AddGlobal (CreateStrongBox (value, type));
507                 }
508
509                 public void EmitCreateDelegate (LambdaExpression lambda)
510                 {
511                         EmitScope ();
512
513                         ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
514                         if (hoisted_store != null)
515                                 ig.Emit (OpCodes.Ldloc, hoisted_store);
516                         else
517                                 ig.Emit (OpCodes.Ldnull);
518
519                         ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
520
521                         ig.Emit (OpCodes.Castclass, lambda.Type);
522                 }
523
524                 void EmitStoreHoistedLocals ()
525                 {
526                         EmitHoistedLocalsStore ();
527                         for (int i = 0; i < hoisted.Count; i++)
528                                 EmitStoreHoistedLocal (i, hoisted [i]);
529                 }
530
531                 void EmitStoreHoistedLocal (int position, ParameterExpression parameter)
532                 {
533                         ig.Emit (OpCodes.Ldloc, hoisted_store);
534                         ig.Emit (OpCodes.Ldc_I4, position);
535                         parameter.Emit (this);
536                         EmitCreateStrongBox (parameter.Type);
537                         ig.Emit (OpCodes.Stelem, typeof (object));
538                 }
539
540                 public void EmitLoadHoistedLocalsStore ()
541                 {
542                         ig.Emit (OpCodes.Ldloc, hoisted_store);
543                 }
544
545                 void EmitCreateStrongBox (Type type)
546                 {
547                         ig.Emit (OpCodes.Newobj, type.MakeStrongBoxType ().GetConstructor (new [] { type }));
548                 }
549
550                 void EmitHoistedLocalsStore ()
551                 {
552                         EmitScope ();
553                         hoisted_store = ig.DeclareLocal (typeof (object []));
554                         ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateHoistedLocals"));
555                         ig.Emit (OpCodes.Stloc, hoisted_store);
556                 }
557
558                 public void EmitLoadLocals ()
559                 {
560                         ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Locals"));
561                 }
562
563                 public void EmitParentScope ()
564                 {
565                         ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Parent"));
566                 }
567
568                 public void EmitIsolateExpression ()
569                 {
570                         ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("IsolateExpression"));
571                 }
572
573                 public int IndexOfHoistedLocal (ParameterExpression parameter)
574                 {
575                         if (!HasHoistedLocals)
576                                 return -1;
577
578                         return hoisted.IndexOf (parameter);
579                 }
580
581                 public bool IsHoistedLocal (ParameterExpression parameter, ref int level, ref int position)
582                 {
583                         if (parent == null)
584                                 return false;
585
586                         if (parent.hoisted != null) {
587                                 position = parent.hoisted.IndexOf (parameter);
588                                 if (position > -1)
589                                         return true;
590                         }
591
592                         level++;
593
594                         return parent.IsHoistedLocal (parameter, ref level, ref position);
595                 }
596
597                 int AddChildContext (LambdaExpression lambda)
598                 {
599                         return context.AddCompilationUnit (this, lambda);
600                 }
601
602                 static object CreateStrongBox (object value, Type type)
603                 {
604                         return Activator.CreateInstance (
605                                 type.MakeStrongBoxType (), value);
606                 }
607         }
608 }