How do I invoke a C# delegate from Lua where the delegate can throw an exception

448 Views Asked by At

Running the following code in a Linux based docker image causes the CLR to crash.

static void Main(string[] args)
    {
        System.Console.WriteLine("Starting program");
        using (NLua.Lua luaState = new NLua.Lua())
        {
            System.Action invoke = () => throw new System.Exception("message");
            luaState["invoke"] = invoke;
            try
            {
                System.Console.WriteLine("Invoking delegate");
                luaState.DoString(@"invoke()");
            }
            catch (System.Exception ex)
            {
                // We never get here
                System.Console.WriteLine("Exception caught");
                throw;
            }
        }

    }

Am I doing something wrong?

Dockerfile is the default suggestion from Visual Studio, and process is started from Visual Studio.

Running the program outside Docker, ie. on the Windows host, works as expected. (Hits the catch block and does not cause the fatal condition on the CLR)

Wrapping invokation in a pcall does not resolve the issue.

It is possible to get avoid the issue by registering functions rather than sending delegates. If the delegate does not throw an exception it gets invoked as expected.

Console log from the image;

Starting program
Invoking delegate
Unhandled exception. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.Exception: message
at LuaTest.Program.<>c.<Main>b__0_0() in C:\Source\Tests\LuaTest\Program.cs:line 10
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at NLua.MetaFunctions.CallDelegateInternal(Lua luaState)
at NLua.MetaFunctions.CallDelegate(IntPtr state)
at KeraLua.NativeMethods.lua_pcallk(IntPtr luaState, Int32 nargs, Int32 nresults, Int32 errorfunc, IntPtr ctx, IntPtr k)
at KeraLua.Lua.PCall(Int32 arguments, Int32 results, Int32 errorFunctionIndex)
at NLua.Lua.DoString(String chunk, String chunkName)
at LuaTest.Program.Main(String[] args) in C:\Source\Tests\LuaTest\Program.cs:line 15
Fatal error. Internal CLR error. (0x80131506)
at KeraLua.NativeMethods.lua_pcallk(IntPtr, Int32, Int32, Int32, IntPtr, IntPtr)
at KeraLua.NativeMethods.lua_pcallk(IntPtr, Int32, Int32, Int32, IntPtr, IntPtr)
at KeraLua.Lua.PCall(Int32, Int32, Int32)
at NLua.Lua.DoString(System.String, System.String)
at LuaTest.Program.Main(System.String[])
1

There are 1 best solutions below

0
On

Self answer;

This seems to be a bug in NLua's MetaFunctions.CallDelegate. The invocation of the delegate is not in a try-catch block. Contrast with LuaMethodWrapper.Call.

The fatal is likely caused by attempting to propagate the exception through unmanaged code that invokes longjmp. See dotnet core note on exceptions interoperability

Most direct workaround is to register the delegate with register function.

Action action=()=>throw new Exception()
luastate.RegisterFunction("invoke",action.Target,action.Method);