2008-11-19 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / EmitContext.cs
index c5b8a52e93268c3bec49fa1b1333716dd73349e4..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)
                {
@@ -176,7 +216,7 @@ namespace System.Linq.Expressions {
 
                                if (parameter.ParameterType.IsByRef) {
                                        ig.Emit (OpCodes.Ldloca, EmitStored (argument));
-                                       return;
+                                       continue;
                                }
 
                                Emit (arguments [i]);
@@ -190,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)
@@ -212,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);
                }
@@ -245,79 +319,30 @@ namespace System.Linq.Expressions {
 
                int AddGlobal (object value, Type type)
                {
-                       globals.Add (CreateStrongBox (value, type));
-                       return globals.Count - 1;
-               }
-
-               static object CreateStrongBox (object value, Type type)
-               {
-                       return Activator.CreateInstance (
-                               type.MakeStrongBoxType (), value);
-               }
-       }
-
-       class DynamicEmitContext : EmitContext {
-
-               DynamicMethod method;
-
-               public DynamicMethod Method {
-                       get { return method; }
+                       return context.AddGlobal (CreateStrongBox (value, type));
                }
 
-               public DynamicEmitContext (LambdaExpression lambda)
-                       : base (lambda)
+               public void EmitCreateDelegate (LambdaExpression 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 ();
+                       EmitScope ();
 
-                       owner.EmitBody (this);
-               }
+                       ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
+                       ig.Emit (OpCodes.Ldnull);
 
-               public override Delegate CreateDelegate ()
-               {
-                       return method.CreateDelegate (owner.Type, new ExecutionScope (globals.ToArray ()));
-               }
+                       ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
 
-               protected virtual string GenerateName ()
-               {
-                       return "lambda_method";
+                       ig.Emit (OpCodes.Castclass, lambda.Type);
                }
-       }
-
-#if !NET_2_1
-       class DebugEmitContext : DynamicEmitContext {
 
-               static object mlock = new object ();
-               static int method_count;
-
-               public DebugEmitContext (LambdaExpression lambda)
-                       : base (lambda)
+               int AddChildContext (LambdaExpression lambda)
                {
-                       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.EmitBody (this);
-
-                       type.CreateType ();
-                       assembly.Save (file_name);
+                       return context.AddCompilationUnit (lambda);
                }
 
-               protected override string GenerateName ()
+               static object CreateStrongBox (object value, Type type)
                {
-                       lock (mlock) {
-                               return "lambda_method-" + (method_count++);
-                       }
+                       return Activator.CreateInstance (
+                               type.MakeStrongBoxType (), value);
                }
        }
-#endif
 }