// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+#if !FULL_AOT_RUNTIME
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
namespace System.Linq.Expressions {
- abstract class EmitContext {
+ class CompilationContext {
- protected LambdaExpression owner;
- protected Type [] param_types;
- protected Type return_type;
+ class ParameterReplacer : ExpressionTransformer {
- protected Dictionary<object, int> indexes;
+ CompilationContext context;
+ ExecutionScope scope;
+ object [] locals;
- public ILGenerator ig;
+ public ParameterReplacer (CompilationContext context, ExecutionScope scope, object [] locals)
+ {
+ this.context = context;
+ this.scope = scope;
+ this.locals = locals;
+ }
+
+ protected override Expression VisitParameter (ParameterExpression parameter)
+ {
+ var scope = this.scope;
+ var locals = this.locals;
+
+ while (scope != null) {
+ int position = IndexOfHoistedLocal (scope, parameter);
+ if (position != -1)
+ return ReadHoistedLocalFromArray (locals, position);
+
+ locals = scope.Locals;
+ scope = scope.Parent;
+ }
+
+ return parameter;
+ }
+
+ Expression ReadHoistedLocalFromArray (object [] locals, int position)
+ {
+ return Expression.Field (
+ Expression.Convert (
+ Expression.ArrayIndex (
+ Expression.Constant (locals),
+ Expression.Constant (position)),
+ locals [position].GetType ()),
+ "Value");
+ }
+
+ int IndexOfHoistedLocal (ExecutionScope scope, ParameterExpression parameter)
+ {
+ return context.units [scope.compilation_unit].IndexOfHoistedLocal (parameter);
+ }
+ }
+
+ class HoistedVariableDetector : ExpressionVisitor {
+
+ Dictionary<ParameterExpression, LambdaExpression> parameter_to_lambda =
+ new Dictionary<ParameterExpression, LambdaExpression> ();
+
+ Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
+
+ LambdaExpression lambda;
+
+ public Dictionary<LambdaExpression, List<ParameterExpression>> Process (LambdaExpression lambda)
+ {
+ Visit (lambda);
+ return hoisted_map;
+ }
+
+ protected override void VisitLambda (LambdaExpression lambda)
+ {
+ this.lambda = lambda;
+ foreach (var parameter in lambda.Parameters)
+ parameter_to_lambda [parameter] = lambda;
+ base.VisitLambda (lambda);
+ }
+
+ protected override void VisitParameter (ParameterExpression parameter)
+ {
+ if (lambda.Parameters.Contains (parameter))
+ return;
+
+ Hoist (parameter);
+ }
+
+ void Hoist (ParameterExpression parameter)
+ {
+ LambdaExpression lambda;
+ if (!parameter_to_lambda.TryGetValue (parameter, out lambda))
+ return;
+
+ if (hoisted_map == null)
+ hoisted_map = new Dictionary<LambdaExpression, List<ParameterExpression>> ();
+
+ List<ParameterExpression> hoisted;
+ if (!hoisted_map.TryGetValue (lambda, out hoisted)) {
+ hoisted = new List<ParameterExpression> ();
+ hoisted_map [lambda] = hoisted;
+ }
+
+ hoisted.Add (parameter);
+ }
+ }
+
+ List<object> globals = new List<object> ();
+ List<EmitContext> units = new List<EmitContext> ();
+ Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
+
+ public int AddGlobal (object global)
+ {
+ return AddItemToList (global, globals);
+ }
+
+ public object [] GetGlobals ()
+ {
+ return globals.ToArray ();
+ }
+
+ static int AddItemToList<T> (T item, IList<T> list)
+ {
+ list.Add (item);
+ return list.Count - 1;
+ }
+
+ public int AddCompilationUnit (LambdaExpression lambda)
+ {
+ DetectHoistedVariables (lambda);
+ return AddCompilationUnit (null, lambda);
+ }
+
+ public int AddCompilationUnit (EmitContext parent, LambdaExpression lambda)
+ {
+ var context = new EmitContext (this, parent, lambda);
+ var unit = AddItemToList (context, units);
+ context.Emit ();
+ return unit;
+ }
+
+ void DetectHoistedVariables (LambdaExpression lambda)
+ {
+ hoisted_map = new HoistedVariableDetector ().Process (lambda);
+ }
+
+ public List<ParameterExpression> GetHoistedLocals (LambdaExpression lambda)
+ {
+ if (hoisted_map == null)
+ return null;
- protected EmitContext (LambdaExpression lambda)
+ List<ParameterExpression> hoisted;
+ hoisted_map.TryGetValue (lambda, out hoisted);
+ return hoisted;
+ }
+
+ public object [] CreateHoistedLocals (int unit)
+ {
+ var hoisted = GetHoistedLocals (units [unit].Lambda);
+ return new object [hoisted == null ? 0 : hoisted.Count];
+ }
+
+ public Expression IsolateExpression (ExecutionScope scope, object [] locals, Expression expression)
+ {
+ return new ParameterReplacer (this, scope, locals).Transform (expression);
+ }
+
+ public Delegate CreateDelegate ()
+ {
+ return CreateDelegate (0, new ExecutionScope (this));
+ }
+
+ public Delegate CreateDelegate (int unit, ExecutionScope scope)
+ {
+ return units [unit].CreateDelegate (scope);
+ }
+ }
+
+ class EmitContext {
+
+ CompilationContext context;
+ EmitContext parent;
+ LambdaExpression lambda;
+ DynamicMethod method;
+ LocalBuilder hoisted_store;
+ List<ParameterExpression> hoisted;
+
+ public readonly ILGenerator ig;
+
+ public bool HasHoistedLocals {
+ get { return hoisted != null && hoisted.Count > 0; }
+ }
+
+ public LambdaExpression Lambda {
+ get { return lambda; }
+ }
+
+ public EmitContext (CompilationContext context, EmitContext parent, LambdaExpression lambda)
+ {
+ this.context = context;
+ this.parent = parent;
+ this.lambda = lambda;
+ this.hoisted = context.GetHoistedLocals (lambda);
+
+ method = new DynamicMethod (
+ "lambda_method",
+ lambda.GetReturnType (),
+ CreateParameterTypes (lambda.Parameters),
+ typeof (ExecutionScope),
+ true);
+
+ ig = method.GetILGenerator ();
+ }
+
+ public void Emit ()
{
- this.owner = lambda;
+ if (HasHoistedLocals)
+ EmitStoreHoistedLocals ();
- param_types = CreateParameterTypes (owner.Parameters);
- return_type = owner.GetReturnType ();
+ lambda.EmitBody (this);
}
- static Type [] CreateParameterTypes (ReadOnlyCollection<ParameterExpression> parameters)
+ static Type [] CreateParameterTypes (IList<ParameterExpression> parameters)
{
var types = new Type [parameters.Count + 1];
types [0] = typeof (ExecutionScope);
return types;
}
- public static EmitContext Create (LambdaExpression lambda)
+ public bool IsLocalParameter (ParameterExpression parameter, ref int position)
{
-#if !NET_2_1
- if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null)
- return new DebugEmitContext (lambda);
-#endif
- return new DynamicEmitContext (lambda);
+ position = lambda.Parameters.IndexOf (parameter);
+ if (position > -1) {
+ position++;
+ return true;
+ }
+
+ return false;
}
- public int GetParameterPosition (ParameterExpression p)
+ public Delegate CreateDelegate (ExecutionScope scope)
{
- int position = owner.Parameters.IndexOf (p);
- if (position == -1)
- throw new InvalidOperationException ("Parameter not in scope");
-
- return position + 1; // + 1 because 0 is the ExecutionScope
+ return method.CreateDelegate (lambda.Type, scope);
}
- public abstract Delegate CreateDelegate ();
-
public void Emit (Expression expression)
{
expression.Emit (this);
return local;
}
- public void EmitLoad (Expression expression)
+ public void EmitLoadAddress (Expression expression)
+ {
+ ig.Emit (OpCodes.Ldloca, EmitStored (expression));
+ }
+
+ public void EmitLoadEnum (Expression expression)
{
+ expression.Emit (this);
+ ig.Emit (OpCodes.Box, expression.Type);
+ }
+
+ public void EmitLoadEnum (LocalBuilder local)
+ {
+ ig.Emit (OpCodes.Ldloc, local);
+ ig.Emit (OpCodes.Box, local.LocalType);
+ }
+
+ public void EmitLoadSubject (Expression expression)
+ {
+ if (expression.Type.IsEnum) {
+ EmitLoadEnum (expression);
+ return;
+ }
+
if (expression.Type.IsValueType) {
- var local = EmitStored (expression);
- ig.Emit (OpCodes.Ldloca, local);
- } else
- expression.Emit (this);
+ EmitLoadAddress (expression);
+ return;
+ }
+
+ Emit (expression);
+ }
+
+ public void EmitLoadSubject (LocalBuilder local)
+ {
+ if (local.LocalType.IsEnum) {
+ EmitLoadEnum (local);
+ return;
+ }
+
+ if (local.LocalType.IsValueType) {
+ EmitLoadAddress (local);
+ return;
+ }
+
+ EmitLoad (local);
+ }
+
+ public void EmitLoadAddress (LocalBuilder local)
+ {
+ ig.Emit (OpCodes.Ldloca, local);
}
public void EmitLoad (LocalBuilder local)
ig.Emit (OpCodes.Ldloc, local);
}
- public void EmitCall (LocalBuilder local, IEnumerable<Expression> arguments, MethodInfo method)
+ public void EmitCall (LocalBuilder local, IList<Expression> arguments, MethodInfo method)
{
- EmitLoad (local);
- EmitCollection (arguments);
+ EmitLoadSubject (local);
+ EmitArguments (method, arguments);
+ EmitCall (method);
+ }
+
+ public void EmitCall (LocalBuilder local, MethodInfo method)
+ {
+ EmitLoadSubject (local);
EmitCall (method);
}
- public void EmitCall (Expression expression, IEnumerable<Expression> arguments, MethodInfo method)
+ public void EmitCall (Expression expression, MethodInfo method)
{
- if (expression != null)
- EmitLoad (expression);
+ if (!method.IsStatic)
+ EmitLoadSubject (expression);
- EmitCollection (arguments);
EmitCall (method);
}
+ public void EmitCall (Expression expression, IList<Expression> arguments, MethodInfo method)
+ {
+ if (!method.IsStatic)
+ EmitLoadSubject (expression);
+
+ EmitArguments (method, arguments);
+ EmitCall (method);
+ }
+
+ void EmitArguments (MethodInfo method, IList<Expression> arguments)
+ {
+ var parameters = method.GetParameters ();
+
+ for (int i = 0; i < parameters.Length; i++) {
+ var parameter = parameters [i];
+ var argument = arguments [i];
+
+ if (parameter.ParameterType.IsByRef) {
+ ig.Emit (OpCodes.Ldloca, EmitStored (argument));
+ continue;
+ }
+
+ Emit (arguments [i]);
+ }
+ }
+
public void EmitCall (MethodInfo method)
{
ig.Emit (
method);
}
+ public void EmitNullableHasValue (LocalBuilder local)
+ {
+ EmitCall (local, "get_HasValue");
+ }
+
+ public void EmitNullableInitialize (LocalBuilder local)
+ {
+ ig.Emit (OpCodes.Ldloca, local);
+ ig.Emit (OpCodes.Initobj, local.LocalType);
+ ig.Emit (OpCodes.Ldloc, local);
+ }
+
+ public void EmitNullableGetValue (LocalBuilder local)
+ {
+ EmitCall (local, "get_Value");
+ }
+
+ public void EmitNullableGetValueOrDefault (LocalBuilder local)
+ {
+ EmitCall (local, "GetValueOrDefault");
+ }
+
+ void EmitCall (LocalBuilder local, string method_name)
+ {
+ EmitCall (local, local.LocalType.GetMethod (method_name, Type.EmptyTypes));
+ }
+
+ public void EmitNullableNew (Type of)
+ {
+ ig.Emit (OpCodes.Newobj, of.GetConstructor (new [] { of.GetFirstGenericArgument () }));
+ }
+
public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
{
foreach (var expression in collection)
{
expression.Emit (this);
- if (expression.Type.IsValueType)
- ig.Emit (OpCodes.Box, expression.Type);
+ var type = expression.Type;
+
+ if (type.IsValueType)
+ ig.Emit (OpCodes.Box, type);
ig.Emit (OpCodes.Isinst, candidate);
}
}
public void EmitReadGlobal (object global)
+ {
+ EmitReadGlobal (global, global.GetType ());
+ }
+
+ public void EmitLoadGlobals ()
{
EmitScope ();
ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
+ }
- int index;
- if (!indexes.TryGetValue (global, out index))
- throw new InvalidOperationException ();
+ public void EmitReadGlobal (object global, Type type)
+ {
+ EmitLoadGlobals ();
- ig.Emit (OpCodes.Ldc_I4, index);
+ ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
ig.Emit (OpCodes.Ldelem, typeof (object));
- var type = global.GetType ().MakeStrongBoxType ();
-
- ig.Emit (OpCodes.Isinst, type);
- ig.Emit (OpCodes.Ldfld, type.GetField ("Value"));
+ EmitLoadStrongBoxValue (type);
}
- }
-
- class GlobalCollector : ExpressionVisitor {
-
- List<object> globals = new List<object> ();
- Dictionary<object, int> indexes = new Dictionary<object,int> ();
- public List<object> Globals {
- get { return globals; }
- }
+ public void EmitLoadStrongBoxValue (Type type)
+ {
+ var strongbox = type.MakeStrongBoxType ();
- public Dictionary<object, int> Indexes {
- get { return indexes; }
+ ig.Emit (OpCodes.Isinst, strongbox);
+ ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
}
- public GlobalCollector (Expression expression)
+ int AddGlobal (object value, Type type)
{
- Visit (expression);
+ return context.AddGlobal (CreateStrongBox (value, type));
}
- protected override void VisitConstant (ConstantExpression constant)
+ public void EmitCreateDelegate (LambdaExpression lambda)
{
- if (constant.Value == null)
- return;
+ EmitScope ();
- var value = constant.Value;
- var type = value.GetType ();
+ ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
+ if (hoisted_store != null)
+ ig.Emit (OpCodes.Ldloc, hoisted_store);
+ else
+ ig.Emit (OpCodes.Ldnull);
- if (Type.GetTypeCode (type) != TypeCode.Object)
- return;
+ ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
- indexes.Add (value, globals.Count);
- globals.Add (CreateStrongBox (value, type));
+ ig.Emit (OpCodes.Castclass, lambda.Type);
}
- static object CreateStrongBox (object value, Type type)
+ void EmitStoreHoistedLocals ()
{
- return Activator.CreateInstance (
- type.MakeStrongBoxType (), value);
+ EmitHoistedLocalsStore ();
+ for (int i = 0; i < hoisted.Count; i++)
+ EmitStoreHoistedLocal (i, hoisted [i]);
}
- }
-
- class DynamicEmitContext : EmitContext {
-
- DynamicMethod method;
- List<object> globals;
-
- static object mlock = new object ();
- static int method_count;
- public DynamicMethod Method {
- get { return method; }
+ void EmitStoreHoistedLocal (int position, ParameterExpression parameter)
+ {
+ ig.Emit (OpCodes.Ldloc, hoisted_store);
+ ig.Emit (OpCodes.Ldc_I4, position);
+ parameter.Emit (this);
+ EmitCreateStrongBox (parameter.Type);
+ ig.Emit (OpCodes.Stelem, typeof (object));
}
- public DynamicEmitContext (LambdaExpression lambda)
- : base (lambda)
+ public void EmitLoadHoistedLocalsStore ()
{
- // FIXME: Need to force this to be verifiable, see:
- // https://bugzilla.novell.com/show_bug.cgi?id=355005
- method = new DynamicMethod (GenerateName (), return_type, param_types, typeof (ExecutionScope), true);
- ig = method.GetILGenerator ();
+ ig.Emit (OpCodes.Ldloc, hoisted_store);
+ }
- var collector = new GlobalCollector (lambda);
+ void EmitCreateStrongBox (Type type)
+ {
+ ig.Emit (OpCodes.Newobj, type.MakeStrongBoxType ().GetConstructor (new [] { type }));
+ }
- globals = collector.Globals;
- indexes = collector.Indexes;
+ void EmitHoistedLocalsStore ()
+ {
+ EmitScope ();
+ hoisted_store = ig.DeclareLocal (typeof (object []));
+ ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateHoistedLocals"));
+ ig.Emit (OpCodes.Stloc, hoisted_store);
+ }
- owner.Emit (this);
+ public void EmitLoadLocals ()
+ {
+ ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Locals"));
}
- public override Delegate CreateDelegate ()
+ public void EmitParentScope ()
{
- return method.CreateDelegate (owner.Type, new ExecutionScope (globals.ToArray ()));
+ ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Parent"));
}
- static string GenerateName ()
+ public void EmitIsolateExpression ()
{
- lock (mlock) {
- return "lambda_method-" + (method_count++);
- }
+ ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("IsolateExpression"));
}
- }
-#if !NET_2_1
- class DebugEmitContext : DynamicEmitContext {
+ public int IndexOfHoistedLocal (ParameterExpression parameter)
+ {
+ if (!HasHoistedLocals)
+ return -1;
+
+ return hoisted.IndexOf (parameter);
+ }
- public DebugEmitContext (LambdaExpression lambda)
- : base (lambda)
+ public bool IsHoistedLocal (ParameterExpression parameter, ref int level, ref int position)
{
- var name = Method.Name;
- var file_name = name + ".dll";
+ if (parent == null)
+ return false;
- var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly (
- new AssemblyName (name), AssemblyBuilderAccess.RunAndSave, Path.GetTempPath ());
+ if (parent.hoisted != null) {
+ position = parent.hoisted.IndexOf (parameter);
+ if (position > -1)
+ return true;
+ }
- var type = assembly.DefineDynamicModule (file_name, file_name).DefineType ("Linq", TypeAttributes.Public);
+ level++;
- var method = type.DefineMethod (name, MethodAttributes.Public | MethodAttributes.Static, return_type, param_types);
- ig = method.GetILGenerator ();
+ return parent.IsHoistedLocal (parameter, ref level, ref position);
+ }
- owner.Emit (this);
+ int AddChildContext (LambdaExpression lambda)
+ {
+ return context.AddCompilationUnit (this, lambda);
+ }
- type.CreateType ();
- assembly.Save (file_name);
+ static object CreateStrongBox (object value, Type type)
+ {
+ return Activator.CreateInstance (
+ type.MakeStrongBoxType (), value);
}
}
-#endif
}
+#endif