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 EmitLoadSubject (Expression expression
)
303 if (expression
.Type
.IsValueType
) {
304 EmitLoadAddress (expression
);
311 public void EmitLoadSubject (LocalBuilder local
)
313 if (local
.LocalType
.IsValueType
) {
314 EmitLoadAddress (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
);
338 public void EmitCall (LocalBuilder local
, MethodInfo method
)
340 EmitLoadSubject (local
);
344 public void EmitCall (Expression expression
, MethodInfo method
)
346 if (!method
.IsStatic
)
347 EmitLoadSubject (expression
);
352 public void EmitCall (Expression expression
, IList
<Expression
> arguments
, MethodInfo method
)
354 if (!method
.IsStatic
)
355 EmitLoadSubject (expression
);
357 EmitArguments (method
, arguments
);
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
));
374 Emit (arguments
[i
]);
378 public void EmitCall (MethodInfo method
)
381 method
.IsVirtual
? OpCodes
.Callvirt
: OpCodes
.Call
,
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 ()
461 ig
.Emit (OpCodes
.Ldfld
, typeof (ExecutionScope
).GetField ("Globals"));
464 public void EmitReadGlobal (object global, Type type
)
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
)
491 ig
.Emit (OpCodes
.Ldc_I4
, AddChildContext (lambda
));
492 if (hoisted_store
!= null)
493 ig
.Emit (OpCodes
.Ldloc
, hoisted_store
);
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 ()
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
)
556 return hoisted
.IndexOf (parameter
);
559 public bool IsHoistedLocal (ParameterExpression parameter
, ref int level
, ref int position
)
564 if (parent
.hoisted
!= null) {
565 position
= parent
.hoisted
.IndexOf (parameter
);
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);