Skip to content

Commit e31a45b

Browse files
committed
Flesh out exception handling and tests.
1 parent b5da340 commit e31a45b

10 files changed

+105
-15
lines changed

Reinterop~/CSharpReinteropException.cs

+16
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ public static void Generate(GeneratorExecutionContext context)
1414
context.AddSource("ReinteropException", Source);
1515
}
1616

17+
public static CppType GetCppWrapperType(CppGenerationContext context)
18+
{
19+
List<string> ns = new List<string>();
20+
if (context.BaseNamespace.Length > 0)
21+
ns.Add(context.BaseNamespace);
22+
ns.Add("Reinterop");
23+
24+
// If the first two namespaces are identical, remove the duplication.
25+
// This is to avoid `Reinterop::Reinterop`.
26+
if (ns.Count >= 2 && ns[0] == ns[1])
27+
ns.RemoveAt(0);
28+
29+
return new CppType(InteropTypeKind.ClassWrapper, ns, "ReinteropException", null, 0);
30+
}
31+
1732
public const string Source =
1833
"""
1934
namespace Reinterop
@@ -26,6 +41,7 @@ public ReinteropException(string message) : base(message) {}
2641
internal static void ExposeToCPP()
2742
{
2843
ReinteropException e = new ReinteropException("message");
44+
string s = e.Message;
2945
}
3046
}
3147
}

Reinterop~/CppReinteropException.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public static CppType GetCppType(CppGenerationContext context)
1414
if (ns.Count >= 2 && ns[0] == ns[1])
1515
ns.RemoveAt(0);
1616

17-
return new CppType(InteropTypeKind.ClassWrapper, ns, "ReinteropException", null, 0);
17+
return new CppType(InteropTypeKind.ClassWrapper, ns, "ReinteropNativeException", null, 0);
1818
}
1919

2020
public static void Generate(CppGenerationContext context, IDictionary<string, CppSourceFile> sourceFiles)
@@ -35,9 +35,9 @@ public static void Generate(CppGenerationContext context, IDictionary<string, Cp
3535
var headerNamespace = headerFile.GetNamespace(type.GetFullyQualifiedNamespace(false));
3636
headerNamespace.Members.Add(
3737
$$"""
38-
class ReinteropException : public std::runtime_error {
38+
class ReinteropNativeException : public std::runtime_error {
3939
public:
40-
ReinteropException(const DotNet::System::Exception& exception);
40+
ReinteropNativeException(const DotNet::System::Exception& exception);
4141
const ::DotNet::System::Exception& GetDotNetException() const;
4242
4343
private:
@@ -69,11 +69,11 @@ class ReinteropException : public std::runtime_error {
6969
var sourceNamespace = sourceFile.GetNamespace(type.GetFullyQualifiedNamespace(false));
7070
sourceNamespace.Members.Add(
7171
$$"""
72-
ReinteropException::ReinteropException(const DotNet::System::Exception& exception)
72+
ReinteropNativeException::ReinteropNativeException(const DotNet::System::Exception& exception)
7373
: std::runtime_error(exception.Message().ToStlString()),
7474
_exception(exception) {}
7575
76-
const ::DotNet::System::Exception& ReinteropException::GetDotNetException() const {
76+
const ::DotNet::System::Exception& ReinteropNativeException::GetDotNetException() const {
7777
return this->_exception;
7878
}
7979
""");

Reinterop~/Methods.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ public static void GenerateSingleMethod(CppGenerationContext context, TypeToGene
236236
void* reinteropException = nullptr;
237237
{{interopName}}({{string.Join(", ", parameterPassStrings)}});
238238
if (reinteropException != nullptr)
239-
throw Reinterop::ReinteropException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
239+
throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
240240
}
241241
""",
242242
TypeDefinitionsReferenced: new[]
@@ -280,7 +280,7 @@ public static void GenerateSingleMethod(CppGenerationContext context, TypeToGene
280280
void* reinteropException = nullptr;
281281
{{GenerationUtility.JoinAndIndent(invocation, " ")}}
282282
if (reinteropException != nullptr)
283-
throw Reinterop::ReinteropException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
283+
throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
284284
{{returnStatement}}
285285
}
286286
""",

Reinterop~/MethodsImplementedInCpp.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ private static void GenerateMethod(CppGenerationContext context, TypeToGenerate
339339
try {
340340
{{GenerationUtility.JoinAndIndent(new[] { getCallTarget }, " ")}}
341341
{{new[] { implementation }.JoinAndIndent(" ")}}
342-
} catch (::DotNet::Reinterop::ReinteropException& e) {
342+
} catch (::DotNet::Reinterop::ReinteropNativeException& e) {
343343
*reinteropException = ::DotNet::Reinterop::ObjectHandle(e.GetDotNetException().GetHandle()).Release();
344344
{{returnDefault}}
345345
} catch (std::exception& e) {
@@ -357,7 +357,9 @@ private static void GenerateMethod(CppGenerationContext context, TypeToGenerate
357357
implType,
358358
returnType,
359359
objectHandleType,
360-
CppReinteropException.GetCppType(context)
360+
CppReinteropException.GetCppType(context),
361+
CSharpReinteropException.GetCppWrapperType(context),
362+
CppType.FromCSharp(context, context.Compilation.GetSpecialType(SpecialType.System_String))
361363
}.Concat(parameters.Select(parameter => parameter.Type))
362364
.Concat(parameters.Select(parameter => parameter.InteropType)),
363365
AdditionalIncludes: hasStructRewrite ? new[] { "<utility>" } : null // for std::move

Reinterop~/Properties.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ private static void GenerateSingleMethod(CppGenerationContext context, TypeToGen
100100

101101
// Method definition
102102
var parameterPassStrings = interopParameters.Select(parameter => parameter.Type.GetConversionToInteropType(context, parameter.CallSiteName));
103-
parameterPassStrings = parameterPassStrings.Concat(new[] {"&reinteropException"}).Where(s => !string.IsNullOrEmpty(s));
103+
parameterPassStrings = parameterPassStrings.Concat(new[] { "&reinteropException" }).Where(s => !string.IsNullOrEmpty(s));
104104
if (returnType.Name == "void" && !returnType.Flags.HasFlag(CppTypeFlags.Pointer))
105105
{
106106
definition.Elements.Add(new(
@@ -110,7 +110,7 @@ private static void GenerateSingleMethod(CppGenerationContext context, TypeToGen
110110
void* reinteropException = nullptr;
111111
Property_{{method.Name}}({{string.Join(", ", parameterPassStrings)}});
112112
if (reinteropException != nullptr)
113-
throw Reinterop::ReinteropException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
113+
throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
114114
}
115115
""",
116116
TypeDefinitionsReferenced: new[]
@@ -140,7 +140,7 @@ private static void GenerateSingleMethod(CppGenerationContext context, TypeToGen
140140
void* reinteropException = nullptr;
141141
{{GenerationUtility.JoinAndIndent(invocation, " ")}}
142142
if (reinteropException != nullptr)
143-
throw Reinterop::ReinteropException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
143+
throw Reinterop::ReinteropNativeException(::DotNet::System::Exception(::DotNet::Reinterop::ObjectHandle(reinteropException)));
144144
return {{returnType.GetConversionFromInteropType(context, "result")}};
145145
}
146146
""",

Reinterop~/RoslynSourceGenerator.cs

+25-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ public void Execute(GeneratorExecutionContext context)
3636
// Create a new Compilation with the CSharpObjectHandleUtility created above.
3737
// Newer versions of Roslyn make this easy, but not the one in Unity.
3838
CSharpParseOptions options = (CSharpParseOptions)((CSharpCompilation)context.Compilation).SyntaxTrees[0].Options;
39-
Compilation compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(CSharpObjectHandleUtility.Source), options));
39+
Compilation compilation = context.Compilation.AddSyntaxTrees(
40+
CSharpSyntaxTree.ParseText(SourceText.From(CSharpObjectHandleUtility.Source), options),
41+
CSharpSyntaxTree.ParseText(SourceText.From(CSharpReinteropException.Source), options)
42+
);
4043

4144
// Add ObjectHandleUtility's ExposeToCPP to the receiver.
4245
INamedTypeSymbol? objectHandleUtilityType = compilation.GetTypeByMetadataName("Reinterop.ObjectHandleUtility");
@@ -59,6 +62,27 @@ public void Execute(GeneratorExecutionContext context)
5962
}
6063
}
6164

65+
// Add ReinteropExceptions's ExposeToCPP to the receiver.
66+
INamedTypeSymbol? reinteropExceptionType = compilation.GetTypeByMetadataName("Reinterop.ReinteropException");
67+
if (reinteropExceptionType != null)
68+
{
69+
var exposeToCpp = CSharpTypeUtility.FindMembers(reinteropExceptionType, "ExposeToCPP");
70+
foreach (ISymbol symbol in exposeToCpp)
71+
{
72+
IMethodSymbol? method = symbol as IMethodSymbol;
73+
if (method == null)
74+
continue;
75+
76+
foreach (var reference in method.DeclaringSyntaxReferences)
77+
{
78+
if (reference.GetSyntax() is MethodDeclarationSyntax methodDeclaration)
79+
{
80+
receiver.ExposeToCppMethods.Add(methodDeclaration);
81+
}
82+
}
83+
}
84+
}
85+
6286
Dictionary<string, object> properties = new Dictionary<string, object>();
6387
foreach (var property in receiver.Properties)
6488
{

Runtime/TestReinterop.cs

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ internal partial class TestReinterop
1010
{
1111
public partial bool CallThrowAnExceptionFromCppAndCatchIt();
1212
public partial bool CallThrowAnExceptionFromCppAndDontCatchIt();
13+
public partial bool ThrowCppStdException();
14+
public partial bool ThrowOtherCppExceptionType();
1315

1416
public static void ThrowAnException()
1517
{

Tests/TestReinterop.cs

+32-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,41 @@ public void TestADotNetExceptionCanBeCaughtInCpp()
1313
public void TestADotNetExceptionCanPropagateThroughCpp()
1414
{
1515
CesiumForUnity.TestReinterop o = new CesiumForUnity.TestReinterop();
16-
try {
16+
try
17+
{
1718
o.CallThrowAnExceptionFromCppAndDontCatchIt();
18-
} catch (System.Exception e)
19+
}
20+
catch (System.Exception e)
1921
{
2022
Assert.AreEqual("Test Exception!", e.Message);
2123
}
2224
}
25+
26+
[Test]
27+
public void TestACppStdExceptionCanBeCaughtInCSharp()
28+
{
29+
CesiumForUnity.TestReinterop o = new CesiumForUnity.TestReinterop();
30+
try
31+
{
32+
o.ThrowCppStdException();
33+
}
34+
catch (System.Exception e)
35+
{
36+
Assert.AreEqual("An exceptional hello from C++!", e.Message);
37+
}
38+
}
39+
40+
[Test]
41+
public void TestAGeneralCppExceptionCanBeCaughtInCSharp()
42+
{
43+
CesiumForUnity.TestReinterop o = new CesiumForUnity.TestReinterop();
44+
try
45+
{
46+
o.ThrowOtherCppExceptionType();
47+
}
48+
catch (System.Exception e)
49+
{
50+
Assert.AreEqual("An unknown native exception occurred.", e.Message);
51+
}
52+
}
2353
}

native~/Runtime/src/TestReinteropImpl.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include <DotNet/CesiumForUnity/TestReinterop.h>
44

5+
#include <stdexcept>
6+
57
namespace CesiumForUnityNative {
68

79
bool TestReinteropImpl::CallThrowAnExceptionFromCppAndCatchIt(
@@ -21,4 +23,14 @@ bool TestReinteropImpl::CallThrowAnExceptionFromCppAndDontCatchIt(
2123
return false;
2224
}
2325

26+
bool TestReinteropImpl::ThrowCppStdException(
27+
const DotNet::CesiumForUnity::TestReinterop& instance) {
28+
throw std::exception("An exceptional hello from C++!");
29+
}
30+
31+
bool TestReinteropImpl::ThrowOtherCppExceptionType(
32+
const DotNet::CesiumForUnity::TestReinterop& instance) {
33+
throw "This is a dodgy exception.";
34+
}
35+
2436
} // namespace CesiumForUnityNative

native~/Runtime/src/TestReinteropImpl.h

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class TestReinteropImpl {
1414
const DotNet::CesiumForUnity::TestReinterop& instance);
1515
static bool CallThrowAnExceptionFromCppAndDontCatchIt(
1616
const DotNet::CesiumForUnity::TestReinterop& instance);
17+
static bool
18+
ThrowCppStdException(const DotNet::CesiumForUnity::TestReinterop& instance);
19+
static bool ThrowOtherCppExceptionType(
20+
const DotNet::CesiumForUnity::TestReinterop& instance);
1721
};
1822

1923
} // namespace CesiumForUnityNative

0 commit comments

Comments
 (0)