2010-05-25 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Core / System.Linq.Expressions / EmitContext.cs
blobb58ae9d51390feb1a342b6557caf8f3dbc08071c
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
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;
39 namespace System.Linq.Expressions {
41 class CompilationContext {
43 class ParameterReplacer : ExpressionTransformer {
45 CompilationContext context;
46 ExecutionScope scope;
47 object [] locals;
49 public ParameterReplacer (CompilationContext context, ExecutionScope scope, object [] locals)
51 this.context = context;
52 this.scope = scope;
53 this.locals = locals;
56 protected override Expression VisitParameter (ParameterExpression parameter)
58 var scope = this.scope;
59 var locals = this.locals;
61 while (scope != null) {
62 int position = IndexOfHoistedLocal (scope, parameter);
63 if (position != -1)
64 return ReadHoistedLocalFromArray (locals, position);
66 locals = scope.Locals;
67 scope = scope.Parent;
70 return parameter;
73 Expression ReadHoistedLocalFromArray (object [] locals, int position)
75 return Expression.Field (
76 Expression.Convert (
77 Expression.ArrayIndex (
78 Expression.Constant (locals),
79 Expression.Constant (position)),
80 locals [position].GetType ()),
81 "Value");
84 int IndexOfHoistedLocal (ExecutionScope scope, ParameterExpression parameter)
86 return context.units [scope.compilation_unit].IndexOfHoistedLocal (parameter);
90 class HoistedVariableDetector : ExpressionVisitor {
92 Dictionary<ParameterExpression, LambdaExpression> parameter_to_lambda =
93 new Dictionary<ParameterExpression, LambdaExpression> ();
95 Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
97 LambdaExpression lambda;
99 public Dictionary<LambdaExpression, List<ParameterExpression>> Process (LambdaExpression lambda)
101 Visit (lambda);
102 return hoisted_map;
105 protected override void VisitLambda (LambdaExpression lambda)
107 this.lambda = lambda;
108 foreach (var parameter in lambda.Parameters)
109 parameter_to_lambda [parameter] = lambda;
110 base.VisitLambda (lambda);
113 protected override void VisitParameter (ParameterExpression parameter)
115 if (lambda.Parameters.Contains (parameter))
116 return;
118 Hoist (parameter);
121 void Hoist (ParameterExpression parameter)
123 LambdaExpression lambda;
124 if (!parameter_to_lambda.TryGetValue (parameter, out lambda))
125 return;
127 if (hoisted_map == null)
128 hoisted_map = new Dictionary<LambdaExpression, List<ParameterExpression>> ();
130 List<ParameterExpression> hoisted;
131 if (!hoisted_map.TryGetValue (lambda, out hoisted)) {
132 hoisted = new List<ParameterExpression> ();
133 hoisted_map [lambda] = hoisted;
136 hoisted.Add (parameter);
140 List<object> globals = new List<object> ();
141 List<EmitContext> units = new List<EmitContext> ();
142 Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
144 public int AddGlobal (object global)
146 return AddItemToList (global, globals);
149 public object [] GetGlobals ()
151 return globals.ToArray ();
154 static int AddItemToList<T> (T item, IList<T> list)
156 list.Add (item);
157 return list.Count - 1;
160 public int AddCompilationUnit (LambdaExpression lambda)
162 DetectHoistedVariables (lambda);
163 return AddCompilationUnit (null, lambda);
166 public int AddCompilationUnit (EmitContext parent, LambdaExpression lambda)
168 var context = new EmitContext (this, parent, lambda);
169 var unit = AddItemToList (context, units);
170 context.Emit ();
171 return unit;
174 void DetectHoistedVariables (LambdaExpression lambda)
176 hoisted_map = new HoistedVariableDetector ().Process (lambda);
179 public List<ParameterExpression> GetHoistedLocals (LambdaExpression lambda)
181 if (hoisted_map == null)
182 return null;
184 List<ParameterExpression> hoisted;
185 hoisted_map.TryGetValue (lambda, out hoisted);
186 return hoisted;
189 public object [] CreateHoistedLocals (int unit)
191 var hoisted = GetHoistedLocals (units [unit].Lambda);
192 return new object [hoisted == null ? 0 : hoisted.Count];
195 public Expression IsolateExpression (ExecutionScope scope, object [] locals, Expression expression)
197 return new ParameterReplacer (this, scope, locals).Transform (expression);
200 public Delegate CreateDelegate ()
202 return CreateDelegate (0, new ExecutionScope (this));
205 public Delegate CreateDelegate (int unit, ExecutionScope scope)
207 return units [unit].CreateDelegate (scope);
211 class EmitContext {
213 CompilationContext context;
214 EmitContext parent;
215 LambdaExpression lambda;
216 DynamicMethod method;
217 LocalBuilder hoisted_store;
218 List<ParameterExpression> hoisted;
220 public readonly ILGenerator ig;
222 public bool HasHoistedLocals {
223 get { return hoisted != null && hoisted.Count > 0; }
226 public LambdaExpression Lambda {
227 get { return lambda; }
230 public EmitContext (CompilationContext context, EmitContext parent, LambdaExpression lambda)
232 this.context = context;
233 this.parent = parent;
234 this.lambda = lambda;
235 this.hoisted = context.GetHoistedLocals (lambda);
237 method = new DynamicMethod (
238 "lambda_method",
239 lambda.GetReturnType (),
240 CreateParameterTypes (lambda.Parameters),
241 typeof (ExecutionScope),
242 true);
244 ig = method.GetILGenerator ();
247 public void Emit ()
249 if (HasHoistedLocals)
250 EmitStoreHoistedLocals ();
252 lambda.EmitBody (this);
255 static Type [] CreateParameterTypes (IList<ParameterExpression> parameters)
257 var types = new Type [parameters.Count + 1];
258 types [0] = typeof (ExecutionScope);
260 for (int i = 0; i < parameters.Count; i++)
261 types [i + 1] = parameters [i].Type;
263 return types;
266 public bool IsLocalParameter (ParameterExpression parameter, ref int position)
268 position = lambda.Parameters.IndexOf (parameter);
269 if (position > -1) {
270 position++;
271 return true;
274 return false;
277 public Delegate CreateDelegate (ExecutionScope scope)
279 return method.CreateDelegate (lambda.Type, scope);
282 public void Emit (Expression expression)
284 expression.Emit (this);
287 public LocalBuilder EmitStored (Expression expression)
289 var local = ig.DeclareLocal (expression.Type);
290 expression.Emit (this);
291 ig.Emit (OpCodes.Stloc, local);
293 return local;
296 public void EmitLoadAddress (Expression expression)
298 ig.Emit (OpCodes.Ldloca, EmitStored (expression));
301 public void EmitLoadSubject (Expression expression)
303 if (expression.Type.IsValueType) {
304 EmitLoadAddress (expression);
305 return;
308 Emit (expression);
311 public void EmitLoadSubject (LocalBuilder local)
313 if (local.LocalType.IsValueType) {
314 EmitLoadAddress (local);
315 return;
318 EmitLoad (local);
321 public void EmitLoadAddress (LocalBuilder local)
323 ig.Emit (OpCodes.Ldloca, local);
326 public void EmitLoad (LocalBuilder local)
328 ig.Emit (OpCodes.Ldloc, local);
331 public void EmitCall (LocalBuilder local, IList<Expression> arguments, MethodInfo method)
333 EmitLoadSubject (local);
334 EmitArguments (method, arguments);
335 EmitCall (method);
338 public void EmitCall (LocalBuilder local, MethodInfo method)
340 EmitLoadSubject (local);
341 EmitCall (method);
344 public void EmitCall (Expression expression, MethodInfo method)
346 if (!method.IsStatic)
347 EmitLoadSubject (expression);
349 EmitCall (method);
352 public void EmitCall (Expression expression, IList<Expression> arguments, MethodInfo method)
354 if (!method.IsStatic)
355 EmitLoadSubject (expression);
357 EmitArguments (method, arguments);
358 EmitCall (method);
361 void EmitArguments (MethodInfo method, IList<Expression> arguments)
363 var parameters = method.GetParameters ();
365 for (int i = 0; i < parameters.Length; i++) {
366 var parameter = parameters [i];
367 var argument = arguments [i];
369 if (parameter.ParameterType.IsByRef) {
370 ig.Emit (OpCodes.Ldloca, EmitStored (argument));
371 continue;
374 Emit (arguments [i]);
378 public void EmitCall (MethodInfo method)
380 ig.Emit (
381 method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
382 method);
385 public void EmitNullableHasValue (LocalBuilder local)
387 EmitCall (local, "get_HasValue");
390 public void EmitNullableInitialize (LocalBuilder local)
392 ig.Emit (OpCodes.Ldloca, local);
393 ig.Emit (OpCodes.Initobj, local.LocalType);
394 ig.Emit (OpCodes.Ldloc, local);
397 public void EmitNullableGetValue (LocalBuilder local)
399 EmitCall (local, "get_Value");
402 public void EmitNullableGetValueOrDefault (LocalBuilder local)
404 EmitCall (local, "GetValueOrDefault");
407 void EmitCall (LocalBuilder local, string method_name)
409 EmitCall (local, local.LocalType.GetMethod (method_name, Type.EmptyTypes));
412 public void EmitNullableNew (Type of)
414 ig.Emit (OpCodes.Newobj, of.GetConstructor (new [] { of.GetFirstGenericArgument () }));
417 public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
419 foreach (var expression in collection)
420 expression.Emit (this);
423 public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
425 foreach (var initializer in initializers)
426 initializer.Emit (this, local);
429 public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
431 foreach (var binding in bindings)
432 binding.Emit (this, local);
435 public void EmitIsInst (Expression expression, Type candidate)
437 expression.Emit (this);
439 var type = expression.Type;
441 if (type.IsValueType)
442 ig.Emit (OpCodes.Box, type);
444 ig.Emit (OpCodes.Isinst, candidate);
447 public void EmitScope ()
449 ig.Emit (OpCodes.Ldarg_0);
452 public void EmitReadGlobal (object global)
454 EmitReadGlobal (global, global.GetType ());
457 public void EmitLoadGlobals ()
459 EmitScope ();
461 ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
464 public void EmitReadGlobal (object global, Type type)
466 EmitLoadGlobals ();
468 ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
469 ig.Emit (OpCodes.Ldelem, typeof (object));
471 EmitLoadStrongBoxValue (type);
474 public void EmitLoadStrongBoxValue (Type type)
476 var strongbox = type.MakeStrongBoxType ();
478 ig.Emit (OpCodes.Isinst, strongbox);
479 ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
482 int AddGlobal (object value, Type type)
484 return context.AddGlobal (CreateStrongBox (value, type));
487 public void EmitCreateDelegate (LambdaExpression lambda)
489 EmitScope ();
491 ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
492 if (hoisted_store != null)
493 ig.Emit (OpCodes.Ldloc, hoisted_store);
494 else
495 ig.Emit (OpCodes.Ldnull);
497 ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
499 ig.Emit (OpCodes.Castclass, lambda.Type);
502 void EmitStoreHoistedLocals ()
504 EmitHoistedLocalsStore ();
505 for (int i = 0; i < hoisted.Count; i++)
506 EmitStoreHoistedLocal (i, hoisted [i]);
509 void EmitStoreHoistedLocal (int position, ParameterExpression parameter)
511 ig.Emit (OpCodes.Ldloc, hoisted_store);
512 ig.Emit (OpCodes.Ldc_I4, position);
513 parameter.Emit (this);
514 EmitCreateStrongBox (parameter.Type);
515 ig.Emit (OpCodes.Stelem, typeof (object));
518 public void EmitLoadHoistedLocalsStore ()
520 ig.Emit (OpCodes.Ldloc, hoisted_store);
523 void EmitCreateStrongBox (Type type)
525 ig.Emit (OpCodes.Newobj, type.MakeStrongBoxType ().GetConstructor (new [] { type }));
528 void EmitHoistedLocalsStore ()
530 EmitScope ();
531 hoisted_store = ig.DeclareLocal (typeof (object []));
532 ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateHoistedLocals"));
533 ig.Emit (OpCodes.Stloc, hoisted_store);
536 public void EmitLoadLocals ()
538 ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Locals"));
541 public void EmitParentScope ()
543 ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Parent"));
546 public void EmitIsolateExpression ()
548 ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("IsolateExpression"));
551 public int IndexOfHoistedLocal (ParameterExpression parameter)
553 if (!HasHoistedLocals)
554 return -1;
556 return hoisted.IndexOf (parameter);
559 public bool IsHoistedLocal (ParameterExpression parameter, ref int level, ref int position)
561 if (parent == null)
562 return false;
564 if (parent.hoisted != null) {
565 position = parent.hoisted.IndexOf (parameter);
566 if (position > -1)
567 return true;
570 level++;
572 return parent.IsHoistedLocal (parameter, ref level, ref position);
575 int AddChildContext (LambdaExpression lambda)
577 return context.AddCompilationUnit (this, lambda);
580 static object CreateStrongBox (object value, Type type)
582 return Activator.CreateInstance (
583 type.MakeStrongBoxType (), value);