Add support for exceptions to Reinterop
Currently, exceptions thrown in C# cannot be caught in C++. They won't even run destructors for C++ RAII objects. This isn't a huge immediate problem, because we should generally avoid letting C# code throw exceptions. But if it does happen, it's likely to cause spectacular failures and probably be hard to debug, so it's worth doing at some point.
UnityNativeScripting supports exceptions crossing the interop boundary by catching on one side, squirreling the exception away in a variable, and checking that variable on the other side. This is probably the best approach, but care must be taken to do this in a thread-safe manner.
Thinking about this a bit more (two years later), we should use a parameter instead of a squirrel variable.
When C++ calls C#, it should pass an additional void** (IntPtr* on the C# side): the address of a void* initialized to nullptr. The C# side of the interface should have a try/catch, and in the catch it should call Reinterop.ObjectHandleUtility.CreateHandle(exception) and store that at the address pointed to by the IntPtr*. On the C++ side of the interface, we should check if the void* is now non-null. If so, create and throw a std::exception-derived class called DotNetException which holds a reference to the original C# exception.
This should have very little performance overhead and no thread safety concerns.
We may also want the ability to check the type of the C# exception and to upcast it, both of which would be nice Reinterop capabilities in general. But in their absence, we can hand-write static methods to do this as needed.