Skip to content

Commit 283dae5

Browse files
feat(dotnet): distinguish error types (#3764)
Adds a new class `JsiiError` that subclasses `JsiiException` to help distinguish jsii kernel errors that are likely unrecoverable from `RuntimeErrors` which may be errors expected and handled within the JS process and may be caught and handled in the host language runtime. See #3747 for more information --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0
1 parent 0cf5008 commit 283dae5

File tree

12 files changed

+64
-24
lines changed

12 files changed

+64
-24
lines changed

packages/@jsii/dotnet-runtime-test/test/Amazon.JSII.Runtime.IntegrationTests/ComplianceTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ public void Exceptions()
378378
calc.Add(3);
379379
Assert.Equal(23d, calc.Value);
380380

381-
Assert.Throws<JsiiException>(() => calc.Add(10));
381+
Assert.Throws<JsiiError>(() => calc.Add(10));
382382

383383
calc.MaxValue = 40;
384384
calc.Add(10);
@@ -491,7 +491,7 @@ public void AsyncOverrides_OverrideThrows()
491491
{
492492
AsyncVirtualMethodsChild obj = new AsyncVirtualMethodsChild();
493493

494-
JsiiException exception = Assert.Throws<JsiiException>(() => obj.CallMe());
494+
JsiiError exception = Assert.Throws<JsiiError>(() => obj.CallMe());
495495
Assert.Contains("Thrown by native code", exception.Message);
496496
}
497497

@@ -558,7 +558,7 @@ public void PropertyOverrides_Get_Throws()
558558
{
559559
SyncVirtualMethodsChild_Throws so = new SyncVirtualMethodsChild_Throws();
560560

561-
JsiiException exception = Assert.Throws<JsiiException>(() => so.RetrieveValueOfTheProperty());
561+
JsiiError exception = Assert.Throws<JsiiError>(() => so.RetrieveValueOfTheProperty());
562562
Assert.Contains("Oh no, this is bad", exception.Message);
563563
}
564564

@@ -576,7 +576,7 @@ public void PropertyOverrides_Set_Throws()
576576
{
577577
SyncVirtualMethodsChild_Throws so = new SyncVirtualMethodsChild_Throws();
578578

579-
JsiiException exception = Assert.Throws<JsiiException>(() => so.ModifyValueOfTheProperty("Hii"));
579+
JsiiError exception = Assert.Throws<JsiiError>(() => so.ModifyValueOfTheProperty("Hii"));
580580
Assert.Contains("Exception from overloaded setter", exception.Message);
581581
}
582582

@@ -624,7 +624,7 @@ public void SyncOverrides_CallsDoubleAsyncMethodFails()
624624
SyncOverrides obj = new SyncOverrides();
625625
obj.CallAsync = true;
626626

627-
Assert.Throws<JsiiException>(() => obj.CallerIsMethod());
627+
Assert.Throws<JsiiError>(() => obj.CallerIsMethod());
628628
}
629629

630630
[Fact(DisplayName = Prefix + nameof(SyncOverrides_CallsDoubleAsyncPropertyGetterFails))]
@@ -633,7 +633,7 @@ public void SyncOverrides_CallsDoubleAsyncPropertyGetterFails()
633633
SyncOverrides obj = new SyncOverrides();
634634
obj.CallAsync = true;
635635

636-
Assert.Throws<JsiiException>(() => obj.CallerIsProperty);
636+
Assert.Throws<JsiiError>(() => obj.CallerIsProperty);
637637
}
638638

639639
[Fact(DisplayName = Prefix + nameof(SyncOverrides_CallsDoubleAsyncPropertySetterFails))]
@@ -642,7 +642,7 @@ public void SyncOverrides_CallsDoubleAsyncPropertySetterFails()
642642
SyncOverrides obj = new SyncOverrides();
643643
obj.CallAsync = true;
644644

645-
Assert.Throws<JsiiException>(() => obj.CallerIsProperty = 12);
645+
Assert.Throws<JsiiError>(() => obj.CallerIsProperty = 12);
646646
}
647647

648648
[Fact(DisplayName = Prefix + nameof(TestInterfaces))]

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime.UnitTests/Client/RuntimeTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ public RuntimeTests()
2424
_sut = new Services.Runtime(_nodeProcessMock);
2525
}
2626

27-
[Fact(DisplayName = Prefix + nameof(ThrowsJsiiExceptionWhenResponseNotReceived))]
28-
public void ThrowsJsiiExceptionWhenResponseNotReceived()
27+
[Fact(DisplayName = Prefix + nameof(ThrowsJsiiErrorWhenResponseNotReceived))]
28+
public void ThrowsJsiiErrorWhenResponseNotReceived()
2929
{
3030
_nodeProcessMock.StandardOutput.ReadLine().ReturnsNull();
3131

32-
var ex = Assert.Throws<JsiiException>(() => _sut.ReadResponse());
32+
var ex = Assert.Throws<JsiiError>(() => _sut.ReadResponse());
3333
Assert.Equal("Child process exited unexpectedly!", ex.Message);
3434
}
3535
}

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/CallbackExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ out object? result
9494
if (!converter.TryConvert(attribute.Parameters[paramIndex], requiredType, referenceMap,
9595
request.Arguments![n], out var value))
9696
{
97-
throw new JsiiException($"Unable to convert {request.Arguments![n]} to {requiredType.Name}");
97+
throw new JsiiError($"Unable to convert {request.Arguments![n]} to {requiredType.Name}");
9898
}
9999

100100
if (attribute.Parameters[paramIndex].IsVariadic)

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/Deputy/DeputyBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ private bool MakeProxy(Type interfaceType, bool force, [NotNullWhen(true)] out o
576576
);
577577
if (constructorInfo == null)
578578
{
579-
throw new JsiiException($"Could not find constructor to instantiate {proxyType.FullName}");
579+
throw new JsiiError($"Could not find constructor to instantiate {proxyType.FullName}");
580580
}
581581

582582
result = constructorInfo.Invoke(new object[]{ Reference.ForProxy() });

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/Deputy/JsiiTypeAttributeBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ internal static void Load(Assembly assembly)
4848
.FirstOrDefault(name => name.EndsWith(".tgz", StringComparison.InvariantCultureIgnoreCase));
4949
if (tarballResourceName == null)
5050
{
51-
throw new JsiiException("Cannot find embedded tarball resource in assembly " + assembly.GetName(), null);
51+
throw new JsiiError("Cannot find embedded tarball resource in assembly " + assembly.GetName(), null);
5252
}
5353

5454
IServiceProvider serviceProvider = ServiceContainer.ServiceProvider;

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/JsiiException.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
namespace Amazon.JSII.Runtime
55
{
6-
public sealed class JsiiException : Exception
6+
public abstract class JsiiException : Exception
77
{
88
public ErrorResponse? ErrorResponse { get; }
9+
public Type? ExceptionType { get; }
910

1011
internal JsiiException() : base()
1112
{
@@ -26,4 +27,26 @@ internal JsiiException(ErrorResponse response, Exception? innerException)
2627
ErrorResponse = response;
2728
}
2829
}
30+
31+
public sealed class JsiiError : JsiiException
32+
{
33+
internal JsiiError() : base()
34+
{
35+
}
36+
37+
internal JsiiError(string message) : base(message)
38+
{
39+
}
40+
41+
internal JsiiError(string message, Exception? innerException)
42+
: base(message, innerException)
43+
{
44+
}
45+
46+
internal JsiiError(ErrorResponse response, Exception? innerException)
47+
: base(response, innerException)
48+
{
49+
}
50+
51+
}
2952
}

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/JsonModel/Api/Response/ErrorResponse.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
using Newtonsoft.Json;
22
using System;
3+
using System.Runtime.Serialization;
34

45
namespace Amazon.JSII.JsonModel.Api.Response
56
{
7+
public enum ErrorResponseName
8+
{
9+
[EnumMember(Value = "@jsii/kernel.Fault")]
10+
JsiiError,
11+
[EnumMember(Value = "@jsii/kernel.RuntimeError")]
12+
RuntimeException,
13+
}
14+
615
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
716
public sealed class ErrorResponse
817
{
@@ -17,5 +26,8 @@ public ErrorResponse(string error, string? stack = null)
1726

1827
[JsonProperty("stack", NullValueHandling = NullValueHandling.Ignore)]
1928
public string? Stack { get; }
29+
30+
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
31+
public ErrorResponseName Name { get ; }
2032
}
2133
}

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/Services/Client.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ void SendRequest<TRequest>(TRequest requestObject)
7878
}
7979
catch (IOException exception)
8080
{
81-
throw new JsiiException("Unexpected error communicating with jsii-runtime", exception);
81+
throw new JsiiError("Unexpected error communicating with jsii-runtime", exception);
8282
}
8383
}
8484

@@ -94,7 +94,7 @@ TResponse ReceiveResponse<TResponse>()
9494
}
9595
catch (IOException exception)
9696
{
97-
throw new JsiiException("Unexpected error communicating with jsii-runtime", exception);
97+
throw new JsiiError("Unexpected error communicating with jsii-runtime", exception);
9898
}
9999
}
100100

@@ -109,7 +109,12 @@ TResponse TryDeserialize<TResponse>(string responseJson) where TResponse : class
109109
{
110110
ErrorResponse errorResponse = responseObject.ToObject<ErrorResponse>()!;
111111

112-
throw new JsiiException(errorResponse, null);
112+
if (errorResponse.Name.Equals(ErrorResponseName.JsiiError))
113+
{
114+
throw new JsiiError(errorResponse, null);
115+
}
116+
117+
throw new Exception(errorResponse.Error);
113118
}
114119

115120
if (typeof(TResponse).IsAssignableFrom(typeof(HelloResponse)))

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/Services/ITypeCache.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@ internal interface ITypeCache
1515
System.Type GetClassType(string fullyQualifiedName)
1616
{
1717
return TryGetClassType(fullyQualifiedName)
18-
?? throw new JsiiException($"Unable to find class for jsii FQN \"{fullyQualifiedName}\"");
18+
?? throw new JsiiError($"Unable to find class for jsii FQN \"{fullyQualifiedName}\"");
1919
}
2020

2121
System.Type GetEnumType(string fullyQualifiedName)
2222
{
2323
return TryGetEnumType(fullyQualifiedName)
24-
?? throw new JsiiException($"Unable to find enum for jsii FQN \"{fullyQualifiedName}\"");
24+
?? throw new JsiiError($"Unable to find enum for jsii FQN \"{fullyQualifiedName}\"");
2525
}
2626

2727
System.Type GetInterfaceType(string fullyQualifiedName)
2828
{
2929
return TryGetInterfaceType(fullyQualifiedName)
30-
?? throw new JsiiException($"Unable to find interface for jsii FQN \"{fullyQualifiedName}\"");
30+
?? throw new JsiiError($"Unable to find interface for jsii FQN \"{fullyQualifiedName}\"");
3131
}
3232

3333
System.Type GetProxyType(string fullyQualifiedName)
3434
{
3535
return TryGetProxyType(fullyQualifiedName)
36-
?? throw new JsiiException($"Unable to find proxy type for jsii FQN \"{fullyQualifiedName}\"");
36+
?? throw new JsiiError($"Unable to find proxy type for jsii FQN \"{fullyQualifiedName}\"");
3737
}
3838

3939
System.Type GetFrameworkType(TypeReference reference, bool isOptional);

packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/Services/ReferenceMap.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ ConstructorInfo GetByRefConstructor()
6767
ConstructorInfo? constructorInfo = type.GetConstructor(constructorFlags, null, new[] {typeof(ByRefValue)}, null);
6868
if (constructorInfo == null)
6969
{
70-
throw new JsiiException($"Could not find constructor to initialize {type.FullName} with a {typeof(ByRefValue).FullName}");
70+
throw new JsiiError($"Could not find constructor to initialize {type.FullName} with a {typeof(ByRefValue).FullName}");
7171
}
7272
return constructorInfo;
7373
}

0 commit comments

Comments
 (0)