New tests, update.
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / EmitContext.cs
index ef5edcae54a3cd7032ec928e56c1512d4214722a..221571dff419032f071ec8951a19c3fd0fd46f8b 100644 (file)
 //
 
 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)
@@ -66,91 +119,230 @@ namespace System.Linq.Expressions {
                        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 ("lambda_method", 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;
-               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 ();
-                       var file_name = name + ".dll";
+                       if (!method.IsStatic)
+                               EmitLoadSubject (expression);
 
-                       assembly = AppDomain.CurrentDomain.DefineDynamicAssembly (
-                               new AssemblyName (file_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 (assembly.GetName ().FullName);
+                       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);
                }
        }
 }