From 61f3e28a9b9fbbe07d42a69413d9b7b8bdbbe74e Mon Sep 17 00:00:00 2001 From: David Serfozo Date: Sat, 1 Feb 2025 11:49:55 +0000 Subject: [PATCH 1/2] Generated c# throws when managed array does not fit unmanaged --- crates/backend_csharp/src/writer.rs | 17 +++++++++++--- tests/tests/csharp_benchmarks/Interop.cs | 22 ++++++++++++++++--- tests/tests/csharp_overloads_dotnet.cs | 22 ++++++++++++++++--- .../tests/csharp_reference_project/Interop.cs | 22 ++++++++++++++++--- .../csharp_reference_project/Test.Array.cs | 10 ++++++++- .../Test.CharArray.cs | 9 ++++++++ tests/tests/csharp_write_types_all.cs | 22 ++++++++++++++++--- tests/tests/csharp_write_types_namespace.cs | 22 ++++++++++++++++--- ...csharp_write_types_namespace_and_global.cs | 22 ++++++++++++++++--- 9 files changed, 146 insertions(+), 22 deletions(-) diff --git a/crates/backend_csharp/src/writer.rs b/crates/backend_csharp/src/writer.rs index 1ff08d4c..83abf533 100644 --- a/crates/backend_csharp/src/writer.rs +++ b/crates/backend_csharp/src/writer.rs @@ -766,6 +766,12 @@ pub trait CSharpWriter { indented!(w, "fixed(char* s = managed.{})", field_name)?; indented!(w, "{{")?; w.indent(); + indented!(w, r"if(Encoding.UTF8.GetByteCount(managed.{0}, 0, managed.{0}.Length) + 1 > {1})", field_name, a.len())?; + indented!(w, r"{{")?; + w.indent(); + indented!(w, r#"throw new InvalidOperationException($"The managed string field '{{nameof({0}.{1})}}' cannot be encoded to fit the fixed size array of {2}.");"#, the_type.rust_name(), field_name, a.len())?; + w.unindent(); + indented!(w, r"}}")?; indented!( w, r"var written = Encoding.UTF8.GetBytes(s, managed.{0}.Length, result.{0}, {1});", @@ -776,12 +782,17 @@ pub trait CSharpWriter { w.unindent(); indented!(w, r"}}")?; } else { + indented!(w, r"if(managed.{}.Length > {})", field_name, a.len())?; + indented!(w, r"{{")?; + w.indent(); + indented!(w, r#"throw new InvalidOperationException($"The managed array field '{{nameof({0}.{1})}}' has {{managed.{1}.Length}} elements, exceeding the fixed size array of {2}.");"#, the_type.rust_name(), field_name, a.len())?; + w.unindent(); + indented!(w, r"}}")?; indented!( w, - r"var source = new ReadOnlySpan<{0}>(managed.{1}, 0, Math.Min({2}, managed.{1}.Length));", + r"var source = new ReadOnlySpan<{0}>(managed.{1}, 0, managed.{1}.Length);", type_name, - field_name, - a.len() + field_name )?; indented!(w, r"var dest = new Span<{0}>(result.{1}, {2});", type_name, field_name, a.len())?; indented!(w, r"source.CopyTo(dest);")?; diff --git a/tests/tests/csharp_benchmarks/Interop.cs b/tests/tests/csharp_benchmarks/Interop.cs index 897e8af6..d7f0bf31 100644 --- a/tests/tests/csharp_benchmarks/Interop.cs +++ b/tests/tests/csharp_benchmarks/Interop.cs @@ -989,7 +989,11 @@ public static Unmanaged ConvertToUnmanaged(Array managed) { if(managed.data != null) { - var source = new ReadOnlySpan(managed.data, 0, Math.Min(16, managed.data.Length)); + if(managed.data.Length > 16) + { + throw new InvalidOperationException($"The managed array field '{nameof(Array.data)}' has {managed.data.Length} elements, exceeding the fixed size array of 16."); + } + var source = new ReadOnlySpan(managed.data, 0, managed.data.Length); var dest = new Span(result.data, 16); source.CopyTo(dest); } @@ -1045,6 +1049,10 @@ public static Unmanaged ConvertToUnmanaged(CharArray managed) { fixed(char* s = managed.str) { + if(Encoding.UTF8.GetByteCount(managed.str, 0, managed.str.Length) + 1 > 32) + { + throw new InvalidOperationException($"The managed string field '{nameof(CharArray.str)}' cannot be encoded to fit the fixed size array of 32."); + } var written = Encoding.UTF8.GetBytes(s, managed.str.Length, result.str, 31); result.str[written] = 0; } @@ -1194,7 +1202,11 @@ public static Unmanaged ConvertToUnmanaged(NestedArray managed) { if(managed.field_array != null) { - var source = new ReadOnlySpan(managed.field_array, 0, Math.Min(5, managed.field_array.Length)); + if(managed.field_array.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(NestedArray.field_array)}' has {managed.field_array.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.field_array, 0, managed.field_array.Length); var dest = new Span(result.field_array, 5); source.CopyTo(dest); } @@ -1360,7 +1372,11 @@ public static Unmanaged ConvertToUnmanaged(Weird2u8 managed) { if(managed.a != null) { - var source = new ReadOnlySpan(managed.a, 0, Math.Min(5, managed.a.Length)); + if(managed.a.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(Weird2u8.a)}' has {managed.a.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.a, 0, managed.a.Length); var dest = new Span(result.a, 5); source.CopyTo(dest); } diff --git a/tests/tests/csharp_overloads_dotnet.cs b/tests/tests/csharp_overloads_dotnet.cs index edc8a05c..1f2fd76c 100644 --- a/tests/tests/csharp_overloads_dotnet.cs +++ b/tests/tests/csharp_overloads_dotnet.cs @@ -989,7 +989,11 @@ public static Unmanaged ConvertToUnmanaged(Array managed) { if(managed.data != null) { - var source = new ReadOnlySpan(managed.data, 0, Math.Min(16, managed.data.Length)); + if(managed.data.Length > 16) + { + throw new InvalidOperationException($"The managed array field '{nameof(Array.data)}' has {managed.data.Length} elements, exceeding the fixed size array of 16."); + } + var source = new ReadOnlySpan(managed.data, 0, managed.data.Length); var dest = new Span(result.data, 16); source.CopyTo(dest); } @@ -1045,6 +1049,10 @@ public static Unmanaged ConvertToUnmanaged(CharArray managed) { fixed(char* s = managed.str) { + if(Encoding.UTF8.GetByteCount(managed.str, 0, managed.str.Length) + 1 > 32) + { + throw new InvalidOperationException($"The managed string field '{nameof(CharArray.str)}' cannot be encoded to fit the fixed size array of 32."); + } var written = Encoding.UTF8.GetBytes(s, managed.str.Length, result.str, 31); result.str[written] = 0; } @@ -1194,7 +1202,11 @@ public static Unmanaged ConvertToUnmanaged(NestedArray managed) { if(managed.field_array != null) { - var source = new ReadOnlySpan(managed.field_array, 0, Math.Min(5, managed.field_array.Length)); + if(managed.field_array.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(NestedArray.field_array)}' has {managed.field_array.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.field_array, 0, managed.field_array.Length); var dest = new Span(result.field_array, 5); source.CopyTo(dest); } @@ -1360,7 +1372,11 @@ public static Unmanaged ConvertToUnmanaged(Weird2u8 managed) { if(managed.a != null) { - var source = new ReadOnlySpan(managed.a, 0, Math.Min(5, managed.a.Length)); + if(managed.a.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(Weird2u8.a)}' has {managed.a.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.a, 0, managed.a.Length); var dest = new Span(result.a, 5); source.CopyTo(dest); } diff --git a/tests/tests/csharp_reference_project/Interop.cs b/tests/tests/csharp_reference_project/Interop.cs index 897e8af6..d7f0bf31 100644 --- a/tests/tests/csharp_reference_project/Interop.cs +++ b/tests/tests/csharp_reference_project/Interop.cs @@ -989,7 +989,11 @@ public static Unmanaged ConvertToUnmanaged(Array managed) { if(managed.data != null) { - var source = new ReadOnlySpan(managed.data, 0, Math.Min(16, managed.data.Length)); + if(managed.data.Length > 16) + { + throw new InvalidOperationException($"The managed array field '{nameof(Array.data)}' has {managed.data.Length} elements, exceeding the fixed size array of 16."); + } + var source = new ReadOnlySpan(managed.data, 0, managed.data.Length); var dest = new Span(result.data, 16); source.CopyTo(dest); } @@ -1045,6 +1049,10 @@ public static Unmanaged ConvertToUnmanaged(CharArray managed) { fixed(char* s = managed.str) { + if(Encoding.UTF8.GetByteCount(managed.str, 0, managed.str.Length) + 1 > 32) + { + throw new InvalidOperationException($"The managed string field '{nameof(CharArray.str)}' cannot be encoded to fit the fixed size array of 32."); + } var written = Encoding.UTF8.GetBytes(s, managed.str.Length, result.str, 31); result.str[written] = 0; } @@ -1194,7 +1202,11 @@ public static Unmanaged ConvertToUnmanaged(NestedArray managed) { if(managed.field_array != null) { - var source = new ReadOnlySpan(managed.field_array, 0, Math.Min(5, managed.field_array.Length)); + if(managed.field_array.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(NestedArray.field_array)}' has {managed.field_array.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.field_array, 0, managed.field_array.Length); var dest = new Span(result.field_array, 5); source.CopyTo(dest); } @@ -1360,7 +1372,11 @@ public static Unmanaged ConvertToUnmanaged(Weird2u8 managed) { if(managed.a != null) { - var source = new ReadOnlySpan(managed.a, 0, Math.Min(5, managed.a.Length)); + if(managed.a.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(Weird2u8.a)}' has {managed.a.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.a, 0, managed.a.Length); var dest = new Span(result.a, 5); source.CopyTo(dest); } diff --git a/tests/tests/csharp_reference_project/Test.Array.cs b/tests/tests/csharp_reference_project/Test.Array.cs index aa8a0d1c..e4bcdf8b 100644 --- a/tests/tests/csharp_reference_project/Test.Array.cs +++ b/tests/tests/csharp_reference_project/Test.Array.cs @@ -8,7 +8,6 @@ public class TestArray [InlineData(new byte[] { })] [InlineData(new byte[] { 1, 2, 3 })] [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 })] - [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 })] [InlineData(null)] public void Test_array_1(byte[] array) { @@ -18,6 +17,15 @@ public void Test_array_1(byte[] array) })); } + [Fact] + public void Test_array_1_throws() + { + Assert.Throws(() => Interop.array_1(new Array + { + data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] + })); + } + [Fact] public void Test_array_2() { diff --git a/tests/tests/csharp_reference_project/Test.CharArray.cs b/tests/tests/csharp_reference_project/Test.CharArray.cs index 2543ca29..a167d281 100644 --- a/tests/tests/csharp_reference_project/Test.CharArray.cs +++ b/tests/tests/csharp_reference_project/Test.CharArray.cs @@ -20,6 +20,15 @@ public void Test_char_array_2() Assert.Equal("Hello, World!", result.str); } + [Fact] + public void Test_char_array_2_throws() + { + Assert.Throws(() => Interop.char_array_2(new CharArray + { + str = "Hello, World! Hello, World! Hello, World! Hello, World!" + })); + } + [Fact] public void Test_char_array_3() { diff --git a/tests/tests/csharp_write_types_all.cs b/tests/tests/csharp_write_types_all.cs index 73920b75..93ef116f 100644 --- a/tests/tests/csharp_write_types_all.cs +++ b/tests/tests/csharp_write_types_all.cs @@ -989,7 +989,11 @@ public static Unmanaged ConvertToUnmanaged(Array managed) { if(managed.data != null) { - var source = new ReadOnlySpan(managed.data, 0, Math.Min(16, managed.data.Length)); + if(managed.data.Length > 16) + { + throw new InvalidOperationException($"The managed array field '{nameof(Array.data)}' has {managed.data.Length} elements, exceeding the fixed size array of 16."); + } + var source = new ReadOnlySpan(managed.data, 0, managed.data.Length); var dest = new Span(result.data, 16); source.CopyTo(dest); } @@ -1045,6 +1049,10 @@ public static Unmanaged ConvertToUnmanaged(CharArray managed) { fixed(char* s = managed.str) { + if(Encoding.UTF8.GetByteCount(managed.str, 0, managed.str.Length) + 1 > 32) + { + throw new InvalidOperationException($"The managed string field '{nameof(CharArray.str)}' cannot be encoded to fit the fixed size array of 32."); + } var written = Encoding.UTF8.GetBytes(s, managed.str.Length, result.str, 31); result.str[written] = 0; } @@ -1194,7 +1202,11 @@ public static Unmanaged ConvertToUnmanaged(NestedArray managed) { if(managed.field_array != null) { - var source = new ReadOnlySpan(managed.field_array, 0, Math.Min(5, managed.field_array.Length)); + if(managed.field_array.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(NestedArray.field_array)}' has {managed.field_array.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.field_array, 0, managed.field_array.Length); var dest = new Span(result.field_array, 5); source.CopyTo(dest); } @@ -1368,7 +1380,11 @@ public static Unmanaged ConvertToUnmanaged(Weird2u8 managed) { if(managed.a != null) { - var source = new ReadOnlySpan(managed.a, 0, Math.Min(5, managed.a.Length)); + if(managed.a.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(Weird2u8.a)}' has {managed.a.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.a, 0, managed.a.Length); var dest = new Span(result.a, 5); source.CopyTo(dest); } diff --git a/tests/tests/csharp_write_types_namespace.cs b/tests/tests/csharp_write_types_namespace.cs index 09e154d3..57a5bb14 100644 --- a/tests/tests/csharp_write_types_namespace.cs +++ b/tests/tests/csharp_write_types_namespace.cs @@ -989,7 +989,11 @@ public static Unmanaged ConvertToUnmanaged(Array managed) { if(managed.data != null) { - var source = new ReadOnlySpan(managed.data, 0, Math.Min(16, managed.data.Length)); + if(managed.data.Length > 16) + { + throw new InvalidOperationException($"The managed array field '{nameof(Array.data)}' has {managed.data.Length} elements, exceeding the fixed size array of 16."); + } + var source = new ReadOnlySpan(managed.data, 0, managed.data.Length); var dest = new Span(result.data, 16); source.CopyTo(dest); } @@ -1045,6 +1049,10 @@ public static Unmanaged ConvertToUnmanaged(CharArray managed) { fixed(char* s = managed.str) { + if(Encoding.UTF8.GetByteCount(managed.str, 0, managed.str.Length) + 1 > 32) + { + throw new InvalidOperationException($"The managed string field '{nameof(CharArray.str)}' cannot be encoded to fit the fixed size array of 32."); + } var written = Encoding.UTF8.GetBytes(s, managed.str.Length, result.str, 31); result.str[written] = 0; } @@ -1194,7 +1202,11 @@ public static Unmanaged ConvertToUnmanaged(NestedArray managed) { if(managed.field_array != null) { - var source = new ReadOnlySpan(managed.field_array, 0, Math.Min(5, managed.field_array.Length)); + if(managed.field_array.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(NestedArray.field_array)}' has {managed.field_array.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.field_array, 0, managed.field_array.Length); var dest = new Span(result.field_array, 5); source.CopyTo(dest); } @@ -1360,7 +1372,11 @@ public static Unmanaged ConvertToUnmanaged(Weird2u8 managed) { if(managed.a != null) { - var source = new ReadOnlySpan(managed.a, 0, Math.Min(5, managed.a.Length)); + if(managed.a.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(Weird2u8.a)}' has {managed.a.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.a, 0, managed.a.Length); var dest = new Span(result.a, 5); source.CopyTo(dest); } diff --git a/tests/tests/csharp_write_types_namespace_and_global.cs b/tests/tests/csharp_write_types_namespace_and_global.cs index edc8a05c..1f2fd76c 100644 --- a/tests/tests/csharp_write_types_namespace_and_global.cs +++ b/tests/tests/csharp_write_types_namespace_and_global.cs @@ -989,7 +989,11 @@ public static Unmanaged ConvertToUnmanaged(Array managed) { if(managed.data != null) { - var source = new ReadOnlySpan(managed.data, 0, Math.Min(16, managed.data.Length)); + if(managed.data.Length > 16) + { + throw new InvalidOperationException($"The managed array field '{nameof(Array.data)}' has {managed.data.Length} elements, exceeding the fixed size array of 16."); + } + var source = new ReadOnlySpan(managed.data, 0, managed.data.Length); var dest = new Span(result.data, 16); source.CopyTo(dest); } @@ -1045,6 +1049,10 @@ public static Unmanaged ConvertToUnmanaged(CharArray managed) { fixed(char* s = managed.str) { + if(Encoding.UTF8.GetByteCount(managed.str, 0, managed.str.Length) + 1 > 32) + { + throw new InvalidOperationException($"The managed string field '{nameof(CharArray.str)}' cannot be encoded to fit the fixed size array of 32."); + } var written = Encoding.UTF8.GetBytes(s, managed.str.Length, result.str, 31); result.str[written] = 0; } @@ -1194,7 +1202,11 @@ public static Unmanaged ConvertToUnmanaged(NestedArray managed) { if(managed.field_array != null) { - var source = new ReadOnlySpan(managed.field_array, 0, Math.Min(5, managed.field_array.Length)); + if(managed.field_array.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(NestedArray.field_array)}' has {managed.field_array.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.field_array, 0, managed.field_array.Length); var dest = new Span(result.field_array, 5); source.CopyTo(dest); } @@ -1360,7 +1372,11 @@ public static Unmanaged ConvertToUnmanaged(Weird2u8 managed) { if(managed.a != null) { - var source = new ReadOnlySpan(managed.a, 0, Math.Min(5, managed.a.Length)); + if(managed.a.Length > 5) + { + throw new InvalidOperationException($"The managed array field '{nameof(Weird2u8.a)}' has {managed.a.Length} elements, exceeding the fixed size array of 5."); + } + var source = new ReadOnlySpan(managed.a, 0, managed.a.Length); var dest = new Span(result.a, 5); source.CopyTo(dest); } From 4f1913eb1bf3f5b28a63a94f2c25aeeacbfcc03a Mon Sep 17 00:00:00 2001 From: David Serfozo Date: Sat, 1 Feb 2025 11:52:35 +0000 Subject: [PATCH 2/2] rustfmt --- crates/backend_csharp/src/writer.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/crates/backend_csharp/src/writer.rs b/crates/backend_csharp/src/writer.rs index 83abf533..b3738bca 100644 --- a/crates/backend_csharp/src/writer.rs +++ b/crates/backend_csharp/src/writer.rs @@ -766,10 +766,21 @@ pub trait CSharpWriter { indented!(w, "fixed(char* s = managed.{})", field_name)?; indented!(w, "{{")?; w.indent(); - indented!(w, r"if(Encoding.UTF8.GetByteCount(managed.{0}, 0, managed.{0}.Length) + 1 > {1})", field_name, a.len())?; + indented!( + w, + r"if(Encoding.UTF8.GetByteCount(managed.{0}, 0, managed.{0}.Length) + 1 > {1})", + field_name, + a.len() + )?; indented!(w, r"{{")?; w.indent(); - indented!(w, r#"throw new InvalidOperationException($"The managed string field '{{nameof({0}.{1})}}' cannot be encoded to fit the fixed size array of {2}.");"#, the_type.rust_name(), field_name, a.len())?; + indented!( + w, + r#"throw new InvalidOperationException($"The managed string field '{{nameof({0}.{1})}}' cannot be encoded to fit the fixed size array of {2}.");"#, + the_type.rust_name(), + field_name, + a.len() + )?; w.unindent(); indented!(w, r"}}")?; indented!( @@ -785,15 +796,16 @@ pub trait CSharpWriter { indented!(w, r"if(managed.{}.Length > {})", field_name, a.len())?; indented!(w, r"{{")?; w.indent(); - indented!(w, r#"throw new InvalidOperationException($"The managed array field '{{nameof({0}.{1})}}' has {{managed.{1}.Length}} elements, exceeding the fixed size array of {2}.");"#, the_type.rust_name(), field_name, a.len())?; - w.unindent(); - indented!(w, r"}}")?; indented!( w, - r"var source = new ReadOnlySpan<{0}>(managed.{1}, 0, managed.{1}.Length);", - type_name, - field_name + r#"throw new InvalidOperationException($"The managed array field '{{nameof({0}.{1})}}' has {{managed.{1}.Length}} elements, exceeding the fixed size array of {2}.");"#, + the_type.rust_name(), + field_name, + a.len() )?; + w.unindent(); + indented!(w, r"}}")?; + indented!(w, r"var source = new ReadOnlySpan<{0}>(managed.{1}, 0, managed.{1}.Length);", type_name, field_name)?; indented!(w, r"var dest = new Span<{0}>(result.{1}, {2});", type_name, field_name, a.len())?; indented!(w, r"source.CopyTo(dest);")?; }