//
using System;
+using System.Collections.ObjectModel;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
+using System.Runtime.CompilerServices;
namespace System.Linq.Expressions {
- abstract class EmitContext {
+ class CompilationContext {
- protected LambdaExpression owner;
- protected Type [] param_types;
+ List<object> globals = new List<object> ();
+ List<EmitContext> units = new List<EmitContext> ();
- public ILGenerator ig;
+ public int AddGlobal (object global)
+ {
+ return AddItemToList (global, globals);
+ }
+
+ public object [] GetGlobals ()
+ {
+ return globals.ToArray ();
+ }
- static object mlock = new object ();
- static int method_count;
+ static int AddItemToList<T> (T item, IList<T> list)
+ {
+ list.Add (item);
+ return list.Count - 1;
+ }
- protected EmitContext (LambdaExpression lambda)
+ public int AddCompilationUnit (LambdaExpression lambda)
{
+ var context = new EmitContext (this, lambda);
+ var unit = AddItemToList (context, units);
+ context.Emit ();
+ return unit;
+ }
+
+ public Delegate CreateDelegate ()
+ {
+ return CreateDelegate (0, new ExecutionScope (this));
+ }
+
+ public Delegate CreateDelegate (int unit, ExecutionScope scope)
+ {
+ return units [unit].CreateDelegate (scope);
+ }
+ }
+
+ class EmitContext {
+
+ LambdaExpression owner;
+ CompilationContext context;
+ DynamicMethod method;
+
+ public ILGenerator ig;
+
+ public EmitContext (CompilationContext context, LambdaExpression lambda)
+ {
+ this.context = context;
this.owner = lambda;
- param_types = owner.Parameters.Select (p => p.Type).ToArray ();
+ method = new DynamicMethod ("lambda_method", owner.GetReturnType (),
+ CreateParameterTypes (owner.Parameters), typeof (ExecutionScope), true);
+
+ ig = method.GetILGenerator ();
}
- public static EmitContext Create (LambdaExpression lambda)
+ public void Emit ()
{
- //if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null)
- return new DebugEmitContext (lambda);
+ owner.EmitBody (this);
+ }
+
+ static Type [] CreateParameterTypes (ReadOnlyCollection<ParameterExpression> parameters)
+ {
+ var types = new Type [parameters.Count + 1];
+ types [0] = typeof (ExecutionScope);
+
+ for (int i = 0; i < parameters.Count; i++)
+ types [i + 1] = parameters [i].Type;
- //return new DynamicEmitContext (lambda);
+ return types;
}
public int GetParameterPosition (ParameterExpression p)
if (position == -1)
throw new InvalidOperationException ("Parameter not in scope");
- return position;
+ return position + 1; // + 1 because 0 is the ExecutionScope
}
- public abstract Delegate CreateDelegate ();
+ public Delegate CreateDelegate (ExecutionScope scope)
+ {
+ return method.CreateDelegate (owner.Type, scope);
+ }
+
+ public void Emit (Expression expression)
+ {
+ expression.Emit (this);
+ }
+
+ public LocalBuilder EmitStored (Expression expression)
+ {
+ var local = ig.DeclareLocal (expression.Type);
+ expression.Emit (this);
+ ig.Emit (OpCodes.Stloc, local);
- public abstract void Emit ();
+ return local;
+ }
+
+ public void EmitLoadAddress (Expression expression)
+ {
+ ig.Emit (OpCodes.Ldloca, EmitStored (expression));
+ }
- protected static string GenerateName ()
+ public void EmitLoadSubject (Expression expression)
{
- lock (mlock) {
- return "lambda_method-" + (method_count++);
+ if (expression.Type.IsValueType) {
+ EmitLoadAddress (expression);
+ return;
}
+
+ Emit (expression);
}
- }
- class DynamicEmitContext : EmitContext {
+ public void EmitLoadSubject (LocalBuilder local)
+ {
+ if (local.LocalType.IsValueType) {
+ EmitLoadAddress (local);
+ return;
+ }
+
+ EmitLoad (local);
+ }
- DynamicMethod method;
+ public void EmitLoadAddress (LocalBuilder local)
+ {
+ ig.Emit (OpCodes.Ldloca, local);
+ }
- public DynamicEmitContext (LambdaExpression lambda)
- : base (lambda)
- {
- //
- // We probably want to use the 3.5 new API calls to associate
- // the method with the "sandboxed" Assembly, instead am currently
- // dumping these types in this class
- //
- Type owner_of_code = typeof (EmitContext);
-
- //
- // FIXME: Need to force this to be verifiable, see:
- // https://bugzilla.novell.com/show_bug.cgi?id=355005
- //
- method = new DynamicMethod (GenerateName (), lambda.Body.Type, param_types, owner_of_code);
- ig = method.GetILGenerator ();
+ public void EmitLoad (LocalBuilder local)
+ {
+ ig.Emit (OpCodes.Ldloc, local);
}
- public override Delegate CreateDelegate ()
+ public void EmitCall (LocalBuilder local, ReadOnlyCollection<Expression> arguments, MethodInfo method)
{
- return method.CreateDelegate (owner.Type);
+ EmitLoadSubject (local);
+ EmitArguments (method, arguments);
+ EmitCall (method);
}
- public override void Emit ()
+ public void EmitCall (LocalBuilder local, MethodInfo method)
{
- owner.Emit (this);
+ EmitLoadSubject (local);
+ EmitCall (method);
}
- }
- class DebugEmitContext : EmitContext {
+ public void EmitCall (Expression expression, MethodInfo method)
+ {
+ if (!method.IsStatic)
+ EmitLoadSubject (expression);
- AssemblyBuilder assembly;
- string file_name;
- TypeBuilder type;
- MethodBuilder method;
+ EmitCall (method);
+ }
- public DebugEmitContext (LambdaExpression lambda)
- : base (lambda)
+ public void EmitCall (Expression expression, ReadOnlyCollection<Expression> arguments, MethodInfo method)
{
- var name = GenerateName ();
- file_name = name + ".dll";
+ if (!method.IsStatic)
+ EmitLoadSubject (expression);
- assembly = AppDomain.CurrentDomain.DefineDynamicAssembly (
- new AssemblyName (name), AssemblyBuilderAccess.RunAndSave, Path.GetTempPath ());
+ EmitArguments (method, arguments);
+ EmitCall (method);
+ }
- type = assembly.DefineDynamicModule (file_name, file_name).DefineType ("Linq", TypeAttributes.Public);
+ void EmitArguments (MethodInfo method, ReadOnlyCollection<Expression> arguments)
+ {
+ var parameters = method.GetParameters ();
- method = type.DefineMethod (name, MethodAttributes.Public | MethodAttributes.Static, owner.Body.Type, param_types);
- ig = method.GetILGenerator ();
+ 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.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
+ 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 override Delegate CreateDelegate ()
+ public void EmitNullableGetValueOrDefault (LocalBuilder local)
{
- return Delegate.CreateDelegate (owner.Type, GetMethod ());
+ EmitCall (local, "GetValueOrDefault");
}
- MethodInfo GetMethod ()
+ void EmitCall (LocalBuilder local, string method_name)
{
- return type.GetMethod (method.Name, BindingFlags.Static | BindingFlags.Public);
+ EmitCall (local, local.LocalType.GetMethod (method_name, Type.EmptyTypes));
}
- public override void Emit ()
+ public void EmitNullableNew (Type of)
{
- owner.Emit (this);
+ 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);
+ }
+
+ public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
+ {
+ foreach (var initializer in initializers)
+ initializer.Emit (this, local);
+ }
+
+ public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
+ {
+ foreach (var binding in bindings)
+ binding.Emit (this, local);
+ }
+
+ public void EmitIsInst (Expression expression, Type candidate)
+ {
+ expression.Emit (this);
+
+ var type = expression.Type;
+
+ if (type.IsValueType)
+ ig.Emit (OpCodes.Box, type);
- type.CreateType ();
- assembly.Save (file_name);
+ ig.Emit (OpCodes.Isinst, candidate);
+ }
+
+ public void EmitScope ()
+ {
+ ig.Emit (OpCodes.Ldarg_0);
+ }
+
+ public void EmitReadGlobal (object global)
+ {
+ EmitReadGlobal (global, global.GetType ());
+ }
+
+ public void EmitReadGlobal (object global, Type type)
+ {
+ EmitScope ();
+
+ ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
+
+ ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
+ ig.Emit (OpCodes.Ldelem, typeof (object));
+
+ var strongbox = type.MakeStrongBoxType ();
+
+ ig.Emit (OpCodes.Isinst, strongbox);
+ ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
+ }
+
+ int AddGlobal (object value, Type type)
+ {
+ return context.AddGlobal (CreateStrongBox (value, type));
+ }
+
+ public void EmitCreateDelegate (LambdaExpression lambda)
+ {
+ EmitScope ();
+
+ ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
+ ig.Emit (OpCodes.Ldnull);
+
+ ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
+
+ ig.Emit (OpCodes.Castclass, lambda.Type);
+ }
+
+ int AddChildContext (LambdaExpression lambda)
+ {
+ return context.AddCompilationUnit (lambda);
+ }
+
+ static object CreateStrongBox (object value, Type type)
+ {
+ return Activator.CreateInstance (
+ type.MakeStrongBoxType (), value);
}
}
}