2008-11-19 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / EmitContext.cs
index fcc155a05d518556fab6cb2c42b0766a2f852844..221571dff419032f071ec8951a19c3fd0fd46f8b 100644 (file)
@@ -38,22 +38,68 @@ using System.Runtime.CompilerServices;
 
 namespace System.Linq.Expressions {
 
-       abstract class EmitContext {
+       class CompilationContext {
 
-               protected LambdaExpression owner;
-               protected Type [] param_types;
-               protected Type return_type;
+               List<object> globals = new List<object> ();
+               List<EmitContext> units = new List<EmitContext> ();
 
-               protected List<object> globals = new List<object> ();
+               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)
+               {
+                       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;
 
-               protected EmitContext (LambdaExpression lambda)
+               public EmitContext (CompilationContext context, LambdaExpression lambda)
                {
+                       this.context = context;
                        this.owner = lambda;
 
-                       param_types = CreateParameterTypes (owner.Parameters);
-                       return_type = owner.GetReturnType ();
+                       method = new DynamicMethod ("lambda_method", owner.GetReturnType (),
+                               CreateParameterTypes (owner.Parameters), typeof (ExecutionScope), true);
+
+                       ig = method.GetILGenerator ();
+               }
+
+               public void Emit ()
+               {
+                       owner.EmitBody (this);
                }
 
                static Type [] CreateParameterTypes (ReadOnlyCollection<ParameterExpression> parameters)
@@ -67,15 +113,6 @@ namespace System.Linq.Expressions {
                        return types;
                }
 
-               public static EmitContext Create (LambdaExpression lambda)
-               {
-#if !NET_2_1
-                       if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null)
-                               return new DebugEmitContext (lambda);
-#endif
-                       return new DynamicEmitContext (lambda);
-               }
-
                public int GetParameterPosition (ParameterExpression p)
                {
                        int position = owner.Parameters.IndexOf (p);
@@ -85,7 +122,10 @@ namespace System.Linq.Expressions {
                        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)
                {
@@ -101,13 +141,34 @@ namespace System.Linq.Expressions {
                        return local;
                }
 
-               public void EmitLoad (Expression expression)
+               public void EmitLoadAddress (Expression expression)
+               {
+                       ig.Emit (OpCodes.Ldloca, EmitStored (expression));
+               }
+
+               public void EmitLoadSubject (Expression expression)
                {
                        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.IsValueType) {
+                               EmitLoadAddress (local);
+                               return;
+                       }
+
+                       EmitLoad (local);
+               }
+
+               public void EmitLoadAddress (LocalBuilder local)
+               {
+                       ig.Emit (OpCodes.Ldloca, local);
                }
 
                public void EmitLoad (LocalBuilder local)
@@ -115,22 +176,53 @@ namespace System.Linq.Expressions {
                        ig.Emit (OpCodes.Ldloc, local);
                }
 
-               public void EmitCall (LocalBuilder local, IEnumerable<Expression> arguments, MethodInfo method)
+               public void EmitCall (LocalBuilder local, ReadOnlyCollection<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, MethodInfo method)
+               {
+                       if (!method.IsStatic)
+                               EmitLoadSubject (expression);
+
                        EmitCall (method);
                }
 
-               public void EmitCall (Expression expression, IEnumerable<Expression> arguments, MethodInfo method)
+               public void EmitCall (Expression expression, ReadOnlyCollection<Expression> arguments, MethodInfo method)
                {
                        if (!method.IsStatic)
-                               EmitLoad (expression);
+                               EmitLoadSubject (expression);
 
-                       EmitCollection (arguments);
+                       EmitArguments (method, arguments);
                        EmitCall (method);
                }
 
+               void EmitArguments (MethodInfo method, ReadOnlyCollection<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 (
@@ -138,6 +230,38 @@ namespace System.Linq.Expressions {
                                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)
@@ -160,8 +284,10 @@ namespace System.Linq.Expressions {
                {
                        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);
                }
@@ -193,74 +319,30 @@ namespace System.Linq.Expressions {
 
                int AddGlobal (object value, Type type)
                {
-                       globals.Add (CreateStrongBox (value, type));
-                       return globals.Count - 1;
+                       return context.AddGlobal (CreateStrongBox (value, type));
                }
 
-               static object CreateStrongBox (object value, Type type)
+               public void EmitCreateDelegate (LambdaExpression lambda)
                {
-                       return Activator.CreateInstance (
-                               type.MakeStrongBoxType (), value);
-               }
-       }
-
-       class DynamicEmitContext : EmitContext {
-
-               DynamicMethod method;
-
-               static object mlock = new object ();
-               static int method_count;
-
-               public DynamicMethod Method {
-                       get { return method; }
-               }
+                       EmitScope ();
 
-               public DynamicEmitContext (LambdaExpression lambda)
-                       : base (lambda)
-               {
-                       // 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.Ldc_I4, AddChildContext (lambda));
+                       ig.Emit (OpCodes.Ldnull);
 
-                       owner.Emit (this);
-               }
+                       ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
 
-               public override Delegate CreateDelegate ()
-               {
-                       return method.CreateDelegate (owner.Type, new ExecutionScope (globals.ToArray ()));
+                       ig.Emit (OpCodes.Castclass, lambda.Type);
                }
 
-               static string GenerateName ()
+               int AddChildContext (LambdaExpression lambda)
                {
-                       lock (mlock) {
-                               return "lambda_method-" + (method_count++);
-                       }
+                       return context.AddCompilationUnit (lambda);
                }
-       }
 
-#if !NET_2_1
-       class DebugEmitContext : DynamicEmitContext {
-
-               public DebugEmitContext (LambdaExpression lambda)
-                       : base (lambda)
+               static object CreateStrongBox (object value, Type type)
                {
-                       var name = Method.Name;
-                       var file_name = name + ".dll";
-
-                       var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly (
-                               new AssemblyName (name), AssemblyBuilderAccess.RunAndSave, Path.GetTempPath ());
-
-                       var type = assembly.DefineDynamicModule (file_name, file_name).DefineType ("Linq", TypeAttributes.Public);
-
-                       var method = type.DefineMethod (name, MethodAttributes.Public | MethodAttributes.Static, return_type, param_types);
-                       ig = method.GetILGenerator ();
-
-                       owner.Emit (this);
-
-                       type.CreateType ();
-                       assembly.Save (file_name);
+                       return Activator.CreateInstance (
+                               type.MakeStrongBoxType (), value);
                }
        }
-#endif
 }