Bug 58361 - EnumBuilder can no longer be used to generate IL code
Summary: EnumBuilder can no longer be used to generate IL code
Status: RESOLVED FIXED
Alias: None
Product: Runtime
Classification: Mono
Component: Reflection (show other bugs)
Version: master
Hardware: PC Mac OS
: --- major
Target Milestone: ---
Assignee: Aleksey Kliger
URL:
Depends on:
Blocks:
 
Reported: 2017-07-25 12:19 UTC by Marek Safar
Modified: 2017-07-31 14:47 UTC (History)
2 users (show)

See Also:
Tags:
Is this bug a regression?: Yes
Last known good build:


Attachments

Description Marek Safar 2017-07-25 12:19:48 UTC
using System;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;


class TestILGenerator
{

	public static Type GenType ()
	{
		AppDomain myDomain = Thread.GetDomain ();
		AssemblyName myAsmName = new AssemblyName ();
		myAsmName.Name = "IntVectorAsm";

		AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly (
					   myAsmName,
					   AssemblyBuilderAccess.RunAndSave);

		ModuleBuilder IntVectorModule = myAsmBuilder.DefineDynamicModule ("IntVectorModule",
									  "test.dll");

		var enumTypeBld = IntVectorModule.DefineEnum ("TestEnum", TypeAttributes.Public, typeof (int));
		enumTypeBld.DefineLiteral ("Lit1", 1);

		TypeBuilder ivTypeBld = IntVectorModule.DefineType ("IntVector",
									   TypeAttributes.Public);

		Type objType = Type.GetType ("System.Object");
		ConstructorInfo objCtor = objType.GetConstructor (new Type [0]);

		ConstructorBuilder ivCtor = ivTypeBld.DefineConstructor (
					   MethodAttributes.Public,
					   CallingConventions.Standard,
			Type.EmptyTypes);

		ILGenerator ctorIL = ivCtor.GetILGenerator ();

		ctorIL.Emit (OpCodes.Ldtoken, enumTypeBld);
		ctorIL.Emit (OpCodes.Pop);
		ctorIL.Emit (OpCodes.Ret);

		var ivType = ivTypeBld.CreateType ();
		enumTypeBld.CreateType ();

		return ivType;

	}

	public static void Main ()
	{
		var IVType = GenType ();

		ConstructorInfo myDTctor = IVType.GetConstructor (Type.EmptyTypes);
		myDTctor.Invoke (null);
	}

}


>>>>

EnumBuilder
* Assertion: should not be reached at sre.c:4196

Stacktrace:

  at <unknown> <0xffffffff>
  at (wrapper managed-to-native) System.Reflection.MonoCMethod.InternalInvoke (System.Reflection.MonoCMethod,object,object[],System.Exception&) [0x00016] in <86c197a875074720b43a3307073d85bf>:0
  at System.Reflection.MonoCMethod.InternalInvoke (object,object[]) [0x00002] in /Users/marek/git/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:661
  at System.Reflection.MonoCMethod.DoInvoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) [0x0007a] in /Users/marek/git/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:652
  at System.Reflection.MonoCMethod.Invoke (System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) [0x00000] in /Users/marek/git/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:680
  at System.Reflection.ConstructorInfo.Invoke (object[]) [0x00000] in /Users/marek/git/mono/mcs/class/corlib/System.Reflection/ConstructorInfo.cs:62
  at TestILGenerator.Main () [0x00013] in /Users/marek/Projects/console61/console61/Program.cs:55
  at (wrapper runtime-invoke) object.runtime_invoke_void (object,intptr,intptr,intptr) [0x0004d] in <86c197a875074720b43a3307073d85bf>:0

Native stacktrace:

	0   mono                                0x0000000108ffa791 mono_handle_native_crash + 257
	1   libsystem_platform.dylib            0x00007fffc4f64b3a _sigtramp + 26
	2   ???                                 0x0000000000000004 0x0 + 4
	3   libsystem_c.dylib                   0x00007fffc4de9420 abort + 129
	4   mono                                0x00000001091cfcdf mono_log_write_logfile + 351
	5   mono                                0x00000001091ea1fd monoeg_g_logv + 109
	6   mono                                0x00000001091ea5e4 monoeg_assertion_message + 356
	7   mono                                0x0000000109160692 mono_reflection_resolve_object + 2914
	8   mono                                0x0000000109158f2e mono_reflection_lookup_dynamic_token + 142
	9   mono                                0x0000000109091a44 mono_ldtoken_checked + 260
	10  mono                                0x0000000108f741b4 mono_method_to_ir + 97812
	11  mono                                0x0000000108f4967d mini_method_compile + 2989
	12  mono                                0x0000000108f4ca16 mono_jit_compile_method_inner + 790
	13  mono                                0x0000000108f50023 mono_jit_compile_method_with_opt + 1363
	14  mono                                0x0000000108f5397d mono_jit_runtime_invoke + 445
	15  mono                                0x000000010914f8c4 do_runtime_invoke + 84
	16  mono                                0x00000001091539ba mono_runtime_try_invoke_array + 2154
	17  mono                                0x00000001090ae4e7 ves_icall_InternalInvoke + 647
	18  ???                                 0x0000000109a260e1 0x0 + 4456603873
	19  mscorlib.dll.dylib                  0x000000010b57b684 System_Reflection_MonoCMethod_DoInvoke_object_System_Reflection_BindingFlags_System_Reflection_Binder_object___System_Globalization_CultureInfo + 212

Debug info from gdb:

lldb: error: unable to find utility "lldb", not a developer tool or in PATH

=================================================================
Got a SIGABRT while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
=================================================================

Abort trap: 6
Comment 1 Aleksey Kliger 2017-07-25 14:47:38 UTC
Works on mono 4.8.0, broken as early as 5.0.0.36 (2017-02)
Comment 2 Aleksey Kliger 2017-07-25 19:25:37 UTC
Looks like I broke it with 6e91d455db69e266393028e07a53cd09d669ee22

Here's what (eventually) happens when you call ILGenerator.Emit(OpCode, Type):
1. The way EnumBuilder is implemented, it delegates to a TypeBuilder for most operations.
2. Emit(OpCode,Type) calls ModuleBuilder:getToken and passes it the EnumBuilder
3. getToken calls mono_image_create_token 
4. mono_image_create_token calls mono_reflection_type_handle_mono_type on the EnumBuilder
5. that ends up delegating to the TypeBuilder by call mono_reflection_type_handle_mono_type on the TypeBuilder
6. that calls reflection_setup_internal_class which ends up registering the TypeBuilder
   using mono_dynamic_image_register_token
7. eventually we return back to mono_image_create_token at which point we again call mono_dynamic_image_register_token for the same token but this time with the EnumBuilder.
8. eventually when we jit the emitted method we find an EnumBuilder where we expected to find the TypeBuilder.

Step 7 is where things went wrong. Prior to 6e91d455db69e266393028e07a53cd09d669ee22 we actually had two functions:
  * mono_dynamic_image_register_token
  * mono_image_register_token

They both added a mapping from a token to an object, but they differed in what they would do if the token was already mapped to something: _dynamic_image_ would overwrite but _image_ would just keep the existing mapping.

In 6e91d455db69e266393028e07a53cd09d669ee22 I got rid of mono_image_register_token.  So now we always overwrite.  Which turned out to be the wrong thing in this case.

I guess the safe fix is to put back all the uses of `mono_image_register_token` with its "keep the existing mapping" behavior.  That feels really unsatisfying because all of this is incredibly error-prone either way.
Comment 3 Aleksey Kliger 2017-07-27 22:00:21 UTC
Fixed on mono master https://github.com/mono/mono/commit/c18b403fa58d7d7d6f401b5ad0c2c2f926c06f47
Comment 4 Aleksey Kliger 2017-07-31 14:47:17 UTC
Fixed on mono 2017-06 https://github.com/mono/mono/commit/50f2938561661b58919fc2deee9a1e0bb73e6190

Note You need to log in before you can comment on or make changes to this bug.