5 // Miguel de Icaza (miguel@novell.com)
6 // Jb Evain (jbevain@novell.com)
8 // (C) 2008 Novell, Inc. (http://www.novell.com)
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.
31 using System.Collections.ObjectModel;
32 using System.Collections.Generic;
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;
49 public ParameterReplacer (CompilationContext context, ExecutionScope scope, object [] locals)
51 this.context = context;
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);
64 return ReadHoistedLocalFromArray (locals, position);
66 locals = scope.Locals;
73 Expression ReadHoistedLocalFromArray (object [] locals, int position)
75 return Expression.Field (
77 Expression.ArrayIndex (
78 Expression.Constant (locals),
79 Expression.Constant (position)),
80 locals [position].GetType ()),
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)
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))
121 void Hoist (ParameterExpression parameter)
123 LambdaExpression lambda;
124 if (!parameter_to_lambda.TryGetValue (parameter, out lambda))
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)
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);
174 void DetectHoistedVariables (LambdaExpression lambda)
176 hoisted_map = new HoistedVariableDetector ().Process (lambda);
179 public List<ParameterExpression> GetHoistedLocals (LambdaExpression lambda)
181 if (hoisted_map == null)
184 List<ParameterExpression> hoisted;
185 hoisted_map.TryGetValue (lambda, out 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);
213 CompilationContext context;
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 (
239 lambda.GetReturnType (),
240 CreateParameterTypes (lambda.Parameters),
241 typeof (ExecutionScope),
244 ig = method.GetILGenerator ();
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;
266 public bool IsLocalParameter (ParameterExpression parameter, ref int position)
268 position = lambda.Parameters.IndexOf (parameter);
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);
296 public void EmitLoadAddress (Expression expression)
298 ig.Emit (OpCodes.Ldloca, EmitStored (expression));
301 public void EmitLoadEnum (Expression expression)
303 expression.Emit (this);
304 ig.Emit (OpCodes.Box, expression.Type);
307 public void EmitLoadEnum (LocalBuilder local)
309 ig.Emit (OpCodes.Ldloc, local);
310 ig.Emit (OpCodes.Box, local.LocalType);
313 public void EmitLoadSubject (Expression expression)
315 if (expression.Type.IsEnum) {
316 EmitLoadEnum (expression);
320 if (expression.Type.IsValueType) {
321 EmitLoadAddress (expression);
328 public void EmitLoadSubject (LocalBuilder local)
330 if (local.LocalType.IsEnum) {
331 EmitLoadEnum (local);
335 if (local.LocalType.IsValueType) {
336 EmitLoadAddress (local);
343 public void EmitLoadAddress (LocalBuilder local)
345 ig.Emit (OpCodes.Ldloca, local);
348 public void EmitLoad (LocalBuilder local)
350 ig.Emit (OpCodes.Ldloc, local);
353 public void EmitCall (LocalBuilder local, IList<Expression> arguments, MethodInfo method)
355 EmitLoadSubject (local);
356 EmitArguments (method, arguments);
360 public void EmitCall (LocalBuilder local, MethodInfo method)
362 EmitLoadSubject (local);
366 public void EmitCall (Expression expression, MethodInfo method)
368 if (!method.IsStatic)
369 EmitLoadSubject (expression);
374 public void EmitCall (Expression expression, IList<Expression> arguments, MethodInfo method)
376 if (!method.IsStatic)
377 EmitLoadSubject (expression);
379 EmitArguments (method, arguments);
383 void EmitArguments (MethodInfo method, IList<Expression> arguments)
385 var parameters = method.GetParameters ();
387 for (int i = 0; i < parameters.Length; i++) {
388 var parameter = parameters [i];
389 var argument = arguments [i];
391 if (parameter.ParameterType.IsByRef) {
392 ig.Emit (OpCodes.Ldloca, EmitStored (argument));
396 Emit (arguments [i]);
400 public void EmitCall (MethodInfo method)
403 method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
407 public void EmitNullableHasValue (LocalBuilder local)
409 EmitCall (local, "get_HasValue");
412 public void EmitNullableInitialize (LocalBuilder local)
414 ig.Emit (OpCodes.Ldloca, local);
415 ig.Emit (OpCodes.Initobj, local.LocalType);
416 ig.Emit (OpCodes.Ldloc, local);
419 public void EmitNullableGetValue (LocalBuilder local)
421 EmitCall (local, "get_Value");
424 public void EmitNullableGetValueOrDefault (LocalBuilder local)
426 EmitCall (local, "GetValueOrDefault");
429 void EmitCall (LocalBuilder local, string method_name)
431 EmitCall (local, local.LocalType.GetMethod (method_name, Type.EmptyTypes));
434 public void EmitNullableNew (Type of)
436 ig.Emit (OpCodes.Newobj, of.GetConstructor (new [] { of.GetFirstGenericArgument () }));
439 public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
441 foreach (var expression in collection)
442 expression.Emit (this);
445 public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
447 foreach (var initializer in initializers)
448 initializer.Emit (this, local);
451 public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
453 foreach (var binding in bindings)
454 binding.Emit (this, local);
457 public void EmitIsInst (Expression expression, Type candidate)
459 expression.Emit (this);
461 var type = expression.Type;
463 if (type.IsValueType)
464 ig.Emit (OpCodes.Box, type);
466 ig.Emit (OpCodes.Isinst, candidate);
469 public void EmitScope ()
471 ig.Emit (OpCodes.Ldarg_0);
474 public void EmitReadGlobal (object global)
476 EmitReadGlobal (global, global.GetType ());
479 public void EmitLoadGlobals ()
483 ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
486 public void EmitReadGlobal (object global, Type type)
490 ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
491 ig.Emit (OpCodes.Ldelem, typeof (object));
493 EmitLoadStrongBoxValue (type);
496 public void EmitLoadStrongBoxValue (Type type)
498 var strongbox = type.MakeStrongBoxType ();
500 ig.Emit (OpCodes.Isinst, strongbox);
501 ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
504 int AddGlobal (object value, Type type)
506 return context.AddGlobal (CreateStrongBox (value, type));
509 public void EmitCreateDelegate (LambdaExpression lambda)
513 ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
514 if (hoisted_store != null)
515 ig.Emit (OpCodes.Ldloc, hoisted_store);
517 ig.Emit (OpCodes.Ldnull);
519 ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
521 ig.Emit (OpCodes.Castclass, lambda.Type);
524 void EmitStoreHoistedLocals ()
526 EmitHoistedLocalsStore ();
527 for (int i = 0; i < hoisted.Count; i++)
528 EmitStoreHoistedLocal (i, hoisted [i]);
531 void EmitStoreHoistedLocal (int position, ParameterExpression parameter)
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));
540 public void EmitLoadHoistedLocalsStore ()
542 ig.Emit (OpCodes.Ldloc, hoisted_store);
545 void EmitCreateStrongBox (Type type)
547 ig.Emit (OpCodes.Newobj, type.MakeStrongBoxType ().GetConstructor (new [] { type }));
550 void EmitHoistedLocalsStore ()
553 hoisted_store = ig.DeclareLocal (typeof (object []));
554 ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateHoistedLocals"));
555 ig.Emit (OpCodes.Stloc, hoisted_store);
558 public void EmitLoadLocals ()
560 ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Locals"));
563 public void EmitParentScope ()
565 ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Parent"));
568 public void EmitIsolateExpression ()
570 ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("IsolateExpression"));
573 public int IndexOfHoistedLocal (ParameterExpression parameter)
575 if (!HasHoistedLocals)
578 return hoisted.IndexOf (parameter);
581 public bool IsHoistedLocal (ParameterExpression parameter, ref int level, ref int position)
586 if (parent.hoisted != null) {
587 position = parent.hoisted.IndexOf (parameter);
594 return parent.IsHoistedLocal (parameter, ref level, ref position);
597 int AddChildContext (LambdaExpression lambda)
599 return context.AddCompilationUnit (this, lambda);
602 static object CreateStrongBox (object value, Type type)
604 return Activator.CreateInstance (
605 type.MakeStrongBoxType (), value);