[Mono-devel-list] Re: ILGenerator::BeginExceptFilterBlock()

Jb Evain mono at evain.net
Sun May 15 20:29:54 EDT 2005


Hello Miguel, 

> 	* What does this code do?

It implements the method BeginExceptionFilterBlock in SRE.ILGenerator, and 
add the support for this in the runtime. This way, compilers for languages 
such as VB.NET, that uses exception filters, could use it. 

> 	* Do you have a test case of the use of this?
> 	* We should add regression test cases for this.
> 	* ChangeLog is missing

Everything is attached.
Waiting for the comments from the owners of this part :) 

Jb 


-------------- next part --------------
Index: System.Reflection.Emit/ChangeLog
===================================================================
--- System.Reflection.Emit/ChangeLog	(revision 44546)
+++ System.Reflection.Emit/ChangeLog	(working copy)
@@ -1,3 +1,7 @@
+2005-05-15  Jb Evain  <jbevain at gmail.com>
+
+	* ILGenerator.cs: Implement filter blocks support.
+
 2005-05-13  Zoltan Varga  <vargaz at freemail.hu>
 
 	* MethodBuilder.cs (fixup): Take into account CreateMethodBody as
Index: System.Reflection.Emit/ILGenerator.cs
===================================================================
--- System.Reflection.Emit/ILGenerator.cs	(revision 44546)
+++ System.Reflection.Emit/ILGenerator.cs	(working copy)
@@ -104,6 +104,17 @@
 			handlers [i].extype = null;
 		}
 
+		internal void AddFilter (int offset)
+		{
+			int i;
+			End (offset);
+			add_block (offset);
+			i = handlers.Length - 1;
+			handlers [i].type = ILExceptionBlock.FILTER;
+			handlers [i].extype = null;
+			handlers [i].filter_offset = offset;
+		}
+
 		internal void End (int offset)
 		{
 			if (handlers == null)
@@ -121,6 +132,12 @@
 				return ILExceptionBlock.CATCH;
 		}
 
+		internal void PatchLastClauseStart (int start)
+		{
+			if (handlers != null && handlers.Length > 0)
+				handlers [handlers.Length - 1].start = start;
+		}
+
 		internal void Debug (int b)
 		{
 #if NO
@@ -322,6 +339,7 @@
 		{
 			switch (ex_handlers [cur_block].LastClauseType ()) {
 			case ILExceptionBlock.CATCH:
+			case ILExceptionBlock.FILTER:
 				// how could we optimize code size here?
 				Emit (OpCodes.Leave, ex_handlers [cur_block].end);
 				break;
@@ -329,9 +347,6 @@
 			case ILExceptionBlock.FINALLY:
 				Emit (OpCodes.Endfinally);
 				break;
-			case ILExceptionBlock.FILTER:
-				Emit (OpCodes.Endfilter);
-				break;
 			}
 		}
 
@@ -339,19 +354,31 @@
 		{
 			if (open_blocks.Count <= 0)
 				throw new NotSupportedException ("Not in an exception block");
-			InternalEndClause ();
-			ex_handlers [cur_block].AddCatch (exceptionType, code_len);
+
+			if (ex_handlers [cur_block].LastClauseType () == ILExceptionBlock.FILTER) {
+				if (exceptionType != null)
+					throw new ArgumentException ("Do not supply an exception type for filter clause");
+				Emit (OpCodes.Endfilter);
+				ex_handlers [cur_block].PatchLastClauseStart (code_len);
+			} else {
+				InternalEndClause ();
+				ex_handlers [cur_block].AddCatch (exceptionType, code_len);
+			}
+			
 			cur_stack = 1; // the exception object is on the stack by default
 			if (max_stack < cur_stack)
 				max_stack = cur_stack;
+
 			//System.Console.WriteLine ("Begin catch Block: {0} {1}",exceptionType.ToString(), max_stack);
-			//throw new NotImplementedException ();
 		}
 
-		[MonoTODO]
 		public virtual void BeginExceptFilterBlock ()
 		{
-			throw new NotImplementedException ();
+			if (open_blocks.Count <= 0)
+				throw new NotSupportedException ("Not in an exception block");
+			InternalEndClause ();
+
+			ex_handlers [cur_block].AddFilter (code_len);
 		}
 
 		public virtual Label BeginExceptionBlock ()
-------------- next part --------------
Index: class/corlib/Test/System.Reflection.Emit/ILGeneratorTest.cs
===================================================================
--- class/corlib/Test/System.Reflection.Emit/ILGeneratorTest.cs	(revision 44546)
+++ class/corlib/Test/System.Reflection.Emit/ILGeneratorTest.cs	(working copy)
@@ -17,10 +17,10 @@
 	[TestFixture]
 	public class ILGeneratorTest {
 
+		TypeBuilder tb;
 		ILGenerator il_gen;
 
-		[SetUp]
-		public void SetUp ()
+		static TypeBuilder DefineDynType ()
 		{
 			AssemblyName assemblyName = new AssemblyName ();
 			assemblyName.Name = "MonoTests.System.Reflection.Emit.ILGeneratorTest";
@@ -29,19 +29,125 @@
 				assemblyName, AssemblyBuilderAccess.Run);
 
 			ModuleBuilder module = assembly.DefineDynamicModule ("module1");
-			TypeBuilder _tb = module.DefineType ("GetType", TypeAttributes.Public);
+			return module.DefineType ("T", TypeAttributes.Public);			
+		}
+		
+		void DefineBasicMethod ()
+		{
+			MethodBuilder mb = tb.DefineMethod("F",
+				MethodAttributes.Public, typeof(string), null);
+			il_gen = mb.GetILGenerator ();
+		}
 
-			MethodBuilder myMethod = _tb.DefineMethod("Function1",
-				MethodAttributes.Public, typeof(String), null);
-
-			il_gen = myMethod.GetILGenerator();
+		[SetUp]
+		public void SetUp ()
+		{			
+			tb = DefineDynType ();
 		}
 
 		[Test]
 		[ExpectedException (typeof (ArgumentNullException))]
 		public void DeclareLocal_NULL ()
 		{
+			DefineBasicMethod ();
+
 			il_gen.DeclareLocal (null);
 		}
+
+		[Test]
+		[ExpectedException (typeof (ArgumentException))]
+		public void DefineFilterBodyWithTypeNotNull ()
+		{
+			DefineBasicMethod ();
+
+			il_gen.BeginExceptionBlock ();
+			il_gen.EmitWriteLine ("in try");
+			il_gen.BeginExceptFilterBlock ();
+			il_gen.EmitWriteLine ("in filter head");
+			il_gen.BeginCatchBlock (typeof (Exception));
+			il_gen.EmitWriteLine ("in filter body");
+			il_gen.EndExceptionBlock ();
+		}
+		
+		/// <summary>
+		/// Try to emit something like that:
+		///
+		/// .method public static bool TestFilter (bool execute_handler)
+		/// {
+		/// 	.locals init(bool)
+		/// 	try {
+		/// 		newobj  instance void [mscorlib]System.Exception::.ctor()
+		/// 		throw
+		/// 	} filter {
+		/// 		pop
+		/// 		ldarg.0
+		/// 		endfilter
+		/// 	} {
+		/// 		ldc.i4.1
+		/// 		stloc.0
+		/// 		leave quit
+		/// 	}
+		/// 	ldc.i4.0
+		/// 	stloc.0
+		/// quit:
+		/// 	ldloc.0
+		/// 	ret
+		/// }
+		///
+		/// It should return true if the handler has been executed
+		/// Otherwise, the exception should not be catched
+		/// </summary>
+		void DefineTestFilterMethod ()
+		{
+			MethodBuilder mb = tb.DefineMethod("TestFilter",
+				MethodAttributes.Public | MethodAttributes.Static, typeof(bool), new Type [] { typeof (bool) });
+
+			ConstructorInfo exCtor = typeof (Exception).GetConstructor (new Type [0]);
+
+			il_gen = mb.GetILGenerator ();
+			il_gen.DeclareLocal (typeof (bool));
+			Label quit = il_gen.DefineLabel ();
+			il_gen.BeginExceptionBlock ();
+			il_gen.Emit (OpCodes.Newobj, exCtor);
+			il_gen.Emit (OpCodes.Throw);
+			il_gen.BeginExceptFilterBlock ();
+			il_gen.Emit (OpCodes.Pop);
+			il_gen.Emit (OpCodes.Ldarg_0);
+			il_gen.BeginCatchBlock (null);
+			il_gen.Emit (OpCodes.Ldc_I4_1);
+			il_gen.Emit (OpCodes.Stloc_0);
+			il_gen.Emit (OpCodes.Leave, quit);
+			il_gen.EndExceptionBlock ();
+			il_gen.Emit (OpCodes.Ldc_I4_0);
+			il_gen.Emit (OpCodes.Stloc_0);
+			il_gen.MarkLabel (quit);
+			il_gen.Emit (OpCodes.Ldloc_0);
+			il_gen.Emit (OpCodes.Ret);
+		}
+
+		[Test]
+		public void TestFilterEmittingWithHandlerExecution ()
+		{
+			DefineTestFilterMethod ();
+			Type dynt = tb.CreateType ();
+			
+			MethodInfo tf = dynt.GetMethod ("TestFilter");
+			Assert.IsTrue ((bool) tf.Invoke (null, new object [] { true }));
+		}
+
+		[Test]
+		[ExpectedException (typeof (Exception))]
+		public void TestFilterEmittingWithoutHandlerExecution ()
+		{
+			DefineTestFilterMethod ();
+			Type dynt = tb.CreateType ();
+			
+			MethodInfo tf = dynt.GetMethod ("TestFilter");
+			try {
+				tf.Invoke (null, new object [] { false });
+			} catch (TargetInvocationException tie) {
+				throw tie.InnerException;
+			}
+		}
 	}
 }
Index: class/corlib/Test/System.Reflection.Emit/ChangeLog
===================================================================
--- class/corlib/Test/System.Reflection.Emit/ChangeLog	(revision 44546)
+++ class/corlib/Test/System.Reflection.Emit/ChangeLog	(working copy)
@@ -1,3 +1,7 @@
+2005-05-16  Jb Evain  <jbevain at gmail.com>
+
+	* ILGeneratorTest.cs: Add tests for exception filters.
+
 2005-05-12  Zoltan Varga  <vargaz at freemail.hu>
 
 	* TypeBuilderTest.cs: Add tests for bug #74906.
-------------- next part --------------
Index: mono/metadata/ChangeLog
===================================================================
--- mono/metadata/ChangeLog	(revision 44546)
+++ mono/metadata/ChangeLog	(working copy)
@@ -1,3 +1,7 @@
+2005-05-15  Jb Evain  <jbevain at gmail.com>
+
+	* reflection.c (method_encode_clauses/class): Handle filters clauses.
+
 2005-05-12  Zoltan Varga  <vargaz at freemail.hu>
 
 	* marshal.c (emit_marshal_custom): Add some error checking and call the
Index: mono/metadata/reflection.c
===================================================================
--- mono/metadata/reflection.c	(revision 44546)
+++ mono/metadata/reflection.c	(working copy)
@@ -849,8 +849,10 @@
 			if (ex_block->extype) {
 				clause->data.catch_class = mono_class_from_mono_type (ex_block->extype->type);
 			} else {
-				/* FIXME: handle filters */
-				clause->data.filter_offset = 0;
+				if (ex_block->type == MONO_EXCEPTION_CLAUSE_FILTER)
+					clause->data.filter_offset = ex_block->filter_offset;
+				else
+					clause->data.filter_offset = 0;
 			}
 			finally_start = ex_block->start + ex_block->len;
 
@@ -998,8 +1000,10 @@
 					if (ex_block->extype) {
 						val = mono_metadata_token_from_dor (mono_image_typedef_or_ref (assembly, ex_block->extype->type));
 					} else {
-						/* FIXME: handle filters */
-						val = 0;
+						if (ex_block->type == MONO_EXCEPTION_CLAUSE_FILTER)
+							val = ex_block->filter_offset;
+						else
+							val = 0;
 					}
 					val = GUINT32_TO_LE (val);
 					mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32));


More information about the Mono-devel-list mailing list