1 Replies - 6571 Views - Last Post: 03 March 2012 - 11:15 PM

#1 ModMaker  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 18
  • Joined: 12-July 10

Operation could destabalize the runtime IL

Posted 01 March 2012 - 09:45 PM

I am creating a wrapper for the Lua library for the CLR. Because the Lua library is coded in ISO C, I needed to code this in C++/CLI. I am used to C#, but luckily they are similar. The problem I am having is when registering CLR delegates to the library. When a function is registered in the Lua library, it needs to be an unmanaged function pointer that has the following signature:

int (*CFunction)(lua_State*)


Therefore, to register a function, a wrapper needs to be made. Inside this function, the parameters to the function are pop'd from the Lua stack and the function is called. Then the results are pushed to the Lua stack and the number of values pushed is returned. The best way I can figure to support all delegates is for me to create a dynamic method using IL and to pass the function pointer to the Lua library.

When I excecute my function I get the following error: VerificationException: Operation could destabilize the runtime. Unfortunately, I cannot debug generated IL. I did find out that this error happens when it tries to call the function that was passed to the function.

static System::Delegate^ Generate(System::Delegate^ d, System::String^ name)
	{
		if (_cache->ContainsKey(d))
			return _cache[d];

		System::Reflection::Emit::OpCode arg0 = d->Target == nullptr ? System::Reflection::Emit::OpCodes::Ldarg_0 : System::Reflection::Emit::OpCodes::Ldarg_1;
		array<System::Reflection::ParameterInfo^>^ params = d->Method->GetParameters();
            System::Reflection::Emit::DynamicMethod^ meth;
		if (d->Target == nullptr)
			meth = gcnew System::Reflection::Emit::DynamicMethod("DM$Wraper_" + name, int::typeid, gcnew array<System::Type^> { System::IntPtr::typeid }, false);
		else
			meth = gcnew System::Reflection::Emit::DynamicMethod("DM$Wraper_" + name, int::typeid, gcnew array<System::Type^> { d->Target->GetType(), System::IntPtr::typeid }, d->Target->GetType());
		System::Reflection::Emit::ILGenerator^ gen = meth->GetILGenerator();
		System::Reflection::Emit::Label label = gen->DefineLabel();
		int numargs = params->Length;

		//----------------------
		// start of il
		//----------------------

		/* if the supplied delegate is an instance method, the object needs to be at the top of the stack when it runs */
		if (d->Target != nullptr)
		{
			gen->Emit(System::Reflection::Emit::OpCodes::Ldarg_0);
		}

		/* foreach argument */
		for each (System::Reflection::ParameterInfo^ param in params)
		{
			label = gen->DefineLabel();

			gen->Emit(arg0);

			if (param->ParameterType == System::SByte::typeid ||
				param->ParameterType == System::Int16::typeid || 
				param->ParameterType == System::Int32::typeid || 
				param->ParameterType == System::Int64::typeid || 
				param->ParameterType == System::Byte::typeid ||
				param->ParameterType == System::UInt16::typeid || 
				param->ParameterType == System::UInt32::typeid || 
				param->ParameterType == System::UInt64::typeid || 
				param->ParameterType == System::Single::typeid || 
				param->ParameterType == System::Double::typeid || 
				param->ParameterType == System::Boolean::typeid)
			{
				/* if parameter type is a number, use lua_isnumber */
				gen->Emit(System::Reflection::Emit::OpCodes::Call,
					Reflection::typeid->GetMethod("_isnumber", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public));
			}
			else if (param->ParameterType == System::String::typeid)
			{
				/* if parameter type is a string use lua_isstring */
				gen->Emit(System::Reflection::Emit::OpCodes::Call,
					Reflection::typeid->GetMethod("_isstring", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public));
			}
			else
			{
				/* otherwise this is user data and must be checked at run-time */
				gen->Emit(System::Reflection::Emit::OpCodes::Ldtoken, param->ParameterType);
				gen->Emit(System::Reflection::Emit::OpCodes::Call,
					Reflection::typeid->GetMethod("_istype", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public));
			}
				
			gen->Emit(System::Reflection::Emit::OpCodes::Brtrue_S, label);

			/* throw new Win32Exception */
			gen->Emit(System::Reflection::Emit::OpCodes::Ldstr, "Incorrect parameter type passed to function " + name);
			gen->Emit(System::Reflection::Emit::OpCodes::Newobj, 
				System::ComponentModel::Win32Exception::typeid->GetConstructor(gcnew array<System::Type^> { System::String::typeid } ));
			gen->Emit(System::Reflection::Emit::OpCodes::Throw);

			/* END if*/
			gen->MarkLabel(label);

			/* pop the vaue, keep the value on the stack so it can be used in the call later */
			gen->Emit(arg0);
			gen->Emit(System::Reflection::Emit::OpCodes::Ldtoken, param->ParameterType);
			gen->Emit(System::Reflection::Emit::OpCodes::Call,
				System::Type::typeid->GetMethod("GetTypeFromHandle", gcnew array<System::Type^> { System::RuntimeTypeHandle::typeid }));
			gen->Emit(System::Reflection::Emit::OpCodes::Call,
				Reflection::typeid->GetMethod("_marshal", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public));
		}

		/* call the method */
		/* the exception is throw here <----------------------------------------------------- */
		if (d->Target == nullptr)
			gen->Emit(System::Reflection::Emit::OpCodes::Call, d->Method);
		else
			gen->Emit(System::Reflection::Emit::OpCodes::Callvirt, d->Method);

		if (d->Method->ReturnType != nullptr)
		{
			/* pass to helper function for handling multiple return values */
			gen->Emit(System::Reflection::Emit::OpCodes::Ldc_I4,
					d->Method->GetCustomAttributes(ModMaker::Lua::LuaMultipleReturnAttribute::typeid, false)->Length > 0 ? 1 : 0);
			gen->Emit(arg0);
			gen->Emit(System::Reflection::Emit::OpCodes::Call,
				Reflection::typeid->GetMethod("_push", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public));
		}
		else
		{
			gen->Emit(System::Reflection::Emit::OpCodes::Ldc_I4_0);
		}

		/* the return value of the above function is the number of values pushed, so return */
		gen->Emit(System::Reflection::Emit::OpCodes::Ret);

		//----------------------
		// end of il
		//----------------------

		/* add the created method to the cache */
		if (d->Target == nullptr)
			_cache->Add(d, (System::Delegate^)meth->CreateDelegate(System::Func<System::IntPtr, int>::typeid));
		else
			_cache->Add(d, (System::Delegate^)meth->CreateDelegate(System::Func<System::IntPtr, int>::typeid, d->Target));

		return _cache[d];
	}

These are the signatures of the five custom functions called here:
bool Reflection::_isnumber(IntPtr)
bool Reflection::_isstring(IntPtr)
bool Reflection::_istype(IntPtr, Type^)
Object^ Reflection::_marshal(IntPtr, Type^)
int Reflection::_push(Object^, int, IntPtr)


This should support both static delegates and instance ones. The test function I used was a static function that took an int parameter and returned null. I tested with different parameters and different return types. I checked the other code and it works. I also check both the type and value of the stack when the function is called and it has the expected values. It is also not because of accessibility because it still occurs if the passed delegate is marked as public.

Is This A Good Question/Topic? 0
  • +

Replies To: Operation could destabalize the runtime IL

#2 ModMaker  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 18
  • Joined: 12-July 10

Re: Operation could destabalize the runtime IL

Posted 03 March 2012 - 11:15 PM

The problem disappeared. I do not know what fixed the problem. I hate it when this happens. I agree with the quote "Magic has no use in computers." I made a few changes:

  • I passed d->Method->DeclaringType to the constructor of DynamicMethod
  • Removed the extra type checking on types, all type checking is done in _istype.
  • If the parameter is a value type, the value is unboxed before it is passed to the method
  • Checked for void::typeid to return 0


This is the current code:

static System::Delegate^ Generate(System::Delegate^ d, System::String^ name)
		{
			if (_cache->ContainsKey(d))
				return _cache[d];

			System::Reflection::Emit::OpCode arg0 = d->Target == nullptr ? System::Reflection::Emit::OpCodes::Ldarg_0 : System::Reflection::Emit::OpCodes::Ldarg_1;
			array<System::Reflection::ParameterInfo^>^ params = d->Method->GetParameters();
            System::Reflection::Emit::DynamicMethod^ meth;
			if (d->Target == nullptr)
				meth = gcnew System::Reflection::Emit::DynamicMethod("DM$Wraper_" + name, int::typeid, gcnew array<System::Type^> { System::IntPtr::typeid }, d->Method->DeclaringType);
			else
				meth = gcnew System::Reflection::Emit::DynamicMethod("DM$Wraper_" + name, int::typeid, gcnew array<System::Type^> { d->Target->GetType(), System::IntPtr::typeid }, d->Method->DeclaringType);
			System::Reflection::Emit::ILGenerator^ gen = meth->GetILGenerator();

			//----------------------
			// start of il
			//----------------------

			/* if the supplied delegate is an instance method, the object needs to be at the top of the stack when it runs */
			if (d->Target != nullptr)
			{
				gen->Emit(System::Reflection::Emit::OpCodes::Ldarg_0);
			}

			/* foreach argument */
			for each (System::Reflection::ParameterInfo^ param in params)
			{
				System::Reflection::Emit::Label label = gen->DefineLabel();

				/* check the type of the arg */
				gen->Emit(arg0);
				gen->Emit(System::Reflection::Emit::OpCodes::Ldtoken, param->ParameterType);
				gen->Emit(System::Reflection::Emit::OpCodes::Call,
					System::Type::typeid->GetMethod("GetTypeFromHandle", gcnew array<System::Type^> { System::RuntimeTypeHandle::typeid }));
				gen->Emit(System::Reflection::Emit::OpCodes::Call,
					Reflection::typeid->GetMethod("_istype", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public));
				
				gen->Emit(System::Reflection::Emit::OpCodes::Brtrue_S, label);

				/* throw new Win32Exception */
				gen->Emit(System::Reflection::Emit::OpCodes::Ldstr, "Incorrect parameter type passed to function " + name);
				gen->Emit(System::Reflection::Emit::OpCodes::Newobj, 
					System::ComponentModel::Win32Exception::typeid->GetConstructor(gcnew array<System::Type^> { System::String::typeid } ));
				gen->Emit(System::Reflection::Emit::OpCodes::Throw);

				/* END if */
				gen->MarkLabel(label);

				/* pop the vaue, keep the value on the stack so it can be used in the call later */
				gen->Emit(arg0);
				gen->Emit(System::Reflection::Emit::OpCodes::Ldtoken, param->ParameterType);
				gen->Emit(System::Reflection::Emit::OpCodes::Call,
					System::Type::typeid->GetMethod("GetTypeFromHandle", gcnew array<System::Type^> { System::RuntimeTypeHandle::typeid }));
				gen->Emit(System::Reflection::Emit::OpCodes::Call,
					Reflection::typeid->GetMethod("_marshal", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public));

				/* if the parameter is a value-type, it must be unboxed */
				if (param->ParameterType->IsValueType)
					gen->Emit(System::Reflection::Emit::OpCodes::Unbox, param->ParameterType);
			}

			/* call the method */
			if (d->Target == nullptr)
				gen->Emit(System::Reflection::Emit::OpCodes::Call, d->Method);
			else
				gen->Emit(System::Reflection::Emit::OpCodes::Callvirt, d->Method);

                        /* if method returns null, return 0, otherwise push the value(s) and return the numbers pushed */
			if (d->Method->ReturnType != void::typeid)
			{
				/* pass to helper function for handling multiple return values */
				gen->Emit(System::Reflection::Emit::OpCodes::Ldc_I4,
					d->Method->GetCustomAttributes(ModMaker::Lua::LuaMultipleReturnAttribute::typeid, false)->Length > 0 ? 1 : 0);
				gen->Emit(arg0);
				gen->Emit(System::Reflection::Emit::OpCodes::Call,
					Reflection::typeid->GetMethod("_push", System::Reflection::BindingFlags::Static | System::Reflection::BindingFlags::Public));
			}
			else
			{
				gen->Emit(System::Reflection::Emit::OpCodes::Ldc_I4_0);
			}

			/* the return value of the above function is the number of values pushed, so return */
			gen->Emit(System::Reflection::Emit::OpCodes::Ret);

			//----------------------
			// end of il
			//----------------------

			/* add the created method to the cache */
			if (d->Target == nullptr)
				_cache->Add(d, (System::Delegate^)meth->CreateDelegate(System::Func<System::IntPtr, int>::typeid));
			else
				_cache->Add(d, (System::Delegate^)meth->CreateDelegate(System::Func<System::IntPtr, int>::typeid, d->Target));

			return _cache[d];
		}


I have also tested it, and it works for both instance and static methods, as far as I can see.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1