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.

New Topic/Question
Reply



MultiQuote



|