mirror of https://github.com/dotnet/runtime
Merge c36b3ebbc8
into 123627ba0f
This commit is contained in:
commit
e1fe2f5c96
|
@ -58,7 +58,12 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// <param name="numElements">The unmanaged element count.</param>
|
||||
/// <returns>The <see cref="Span{TUnmanagedElement}"/> of unmanaged elements.</returns>
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||
=> new Span<TUnmanagedElement>(unmanaged, numElements);
|
||||
{
|
||||
if (unmanaged is null)
|
||||
return [];
|
||||
|
||||
return new Span<TUnmanagedElement>(unmanaged, numElements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates memory for the managed representation of the array.
|
||||
|
@ -89,7 +94,12 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// <param name="numElements">The unmanaged element count.</param>
|
||||
/// <returns>The <see cref="ReadOnlySpan{TUnmanagedElement}"/> containing the unmanaged elements to marshal.</returns>
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
|
||||
=> new ReadOnlySpan<TUnmanagedElement>(unmanagedValue, numElements);
|
||||
{
|
||||
if (unmanagedValue is null)
|
||||
return [];
|
||||
|
||||
return new ReadOnlySpan<TUnmanagedElement>(unmanagedValue, numElements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees memory for the unmanaged array.
|
||||
|
|
|
@ -59,7 +59,11 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// <param name="numElements">The unmanaged element count.</param>
|
||||
/// <returns>The <see cref="Span{TUnmanagedElement}"/> of unmanaged elements.</returns>
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||
=> new Span<TUnmanagedElement>(unmanaged, numElements);
|
||||
{
|
||||
if (unmanaged is null)
|
||||
return [];
|
||||
return new Span<TUnmanagedElement>(unmanaged, numElements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates memory for the managed representation of the array.
|
||||
|
@ -90,7 +94,12 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// <param name="numElements">The unmanaged element count.</param>
|
||||
/// <returns>The <see cref="ReadOnlySpan{TUnmanagedElement}"/> containing the unmanaged elements to marshal.</returns>
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
|
||||
=> new ReadOnlySpan<TUnmanagedElement>(unmanagedValue, numElements);
|
||||
{
|
||||
if (unmanagedValue is null)
|
||||
return [];
|
||||
|
||||
return new ReadOnlySpan<TUnmanagedElement>(unmanagedValue, numElements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees memory for the unmanaged array.
|
||||
|
|
|
@ -69,7 +69,12 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// <param name="numElements">The number of elements that will be copied into the memory block.</param>
|
||||
/// <returns>A span over the unmanaged memory that can contain the specified number of elements.</returns>
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||
=> new Span<TUnmanagedElement>(unmanaged, numElements);
|
||||
{
|
||||
if (unmanaged == null)
|
||||
return [];
|
||||
|
||||
return new Span<TUnmanagedElement>(unmanaged, numElements);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -201,6 +206,8 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// <returns>A span over unmanaged values of the array.</returns>
|
||||
public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements)
|
||||
{
|
||||
if (_unmanagedArray is null)
|
||||
return [];
|
||||
return new ReadOnlySpan<TUnmanagedElement>(_unmanagedArray, numElements);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,11 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// <param name="numElements">The number of elements that will be copied into the memory block.</param>
|
||||
/// <returns>A span over the unmanaged memory that can contain the specified number of elements.</returns>
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||
=> new Span<TUnmanagedElement>(unmanaged, numElements);
|
||||
{
|
||||
if (unmanaged == null)
|
||||
return [];
|
||||
return new Span<TUnmanagedElement>(unmanaged, numElements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates space to store the managed elements.
|
||||
|
@ -94,7 +98,11 @@ namespace System.Runtime.InteropServices.Marshalling
|
|||
/// <param name="numElements">The number of elements in the unmanaged collection.</param>
|
||||
/// <returns>A span over the native collection elements.</returns>
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
||||
=> new ReadOnlySpan<TUnmanagedElement>(unmanaged, numElements);
|
||||
{
|
||||
if (unmanaged == null)
|
||||
return [];
|
||||
return new ReadOnlySpan<TUnmanagedElement>(unmanaged, numElements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees the allocated unmanaged memory.
|
||||
|
|
|
@ -39,7 +39,6 @@ namespace Microsoft.Interop
|
|||
/// </code>
|
||||
/// </summary>
|
||||
public StatementSyntax GenerateClearUnmanagedDestination(StubIdentifierContext context)
|
||||
|
||||
{
|
||||
// <GetUnmanagedValuesDestination>.Clear();
|
||||
return MethodInvocationStatement(
|
||||
|
@ -284,7 +283,7 @@ namespace Microsoft.Interop
|
|||
statements.Add(GenerateContentsMarshallingStatement(
|
||||
context,
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(MarshallerHelpers.GetManagedSpanIdentifier(CollectionSource.TypeInfo, context)),
|
||||
IdentifierName(managedSpanIdentifier),
|
||||
IdentifierName("Length")),
|
||||
elementMarshaller,
|
||||
StubIdentifierContext.Stage.Marshal));
|
||||
|
@ -295,7 +294,6 @@ namespace Microsoft.Interop
|
|||
{
|
||||
string managedSpanIdentifier = MarshallerHelpers.GetManagedSpanIdentifier(CollectionSource.TypeInfo, context);
|
||||
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(CollectionSource.TypeInfo, context);
|
||||
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(CollectionSource.TypeInfo, context);
|
||||
|
||||
// ReadOnlySpan<TUnmanagedElement> <nativeSpan> = <GetUnmanagedValuesSource>
|
||||
// Span<T> <managedSpan> = <GetManagedValuesDestination>
|
||||
|
@ -311,7 +309,9 @@ namespace Microsoft.Interop
|
|||
CollectionSource.GetManagedValuesDestination(context)),
|
||||
GenerateContentsMarshallingStatement(
|
||||
context,
|
||||
IdentifierName(numElementsIdentifier),
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nativeSpanIdentifier),
|
||||
IdentifierName("Length")),
|
||||
elementMarshaller,
|
||||
StubIdentifierContext.Stage.UnmarshalCapture, StubIdentifierContext.Stage.Unmarshal));
|
||||
}
|
||||
|
@ -356,7 +356,9 @@ namespace Microsoft.Interop
|
|||
unmanagedValuesDeclaration,
|
||||
GenerateContentsMarshallingStatement(
|
||||
context,
|
||||
IdentifierName(numElementsIdentifier),
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(managedSpanIdentifier),
|
||||
IdentifierName("Length")),
|
||||
elementMarshaller,
|
||||
StubIdentifierContext.Stage.UnmarshalCapture, StubIdentifierContext.Stage.Unmarshal));
|
||||
}
|
||||
|
@ -460,7 +462,9 @@ namespace Microsoft.Interop
|
|||
managedValuesDestination,
|
||||
GenerateContentsMarshallingStatement(
|
||||
context,
|
||||
IdentifierName(numElementsIdentifier),
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(nativeSpanIdentifier),
|
||||
IdentifierName("Length")),
|
||||
new FreeAlwaysOwnedOriginalValueGenerator(elementMarshaller),
|
||||
stagesToGenerate));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
using SharedTypes.ComInterfaces;
|
||||
using SharedTypes;
|
||||
using Xunit;
|
||||
|
||||
namespace ComInterfaceGenerator.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for edge cases involving null arrays when their length parameters are non-zero.
|
||||
/// This addresses https://github.com/dotnet/runtime/issues/118135
|
||||
/// </summary>
|
||||
public unsafe partial class INullArrayTests
|
||||
{
|
||||
private static INullArrayCases CreateTestInterface()
|
||||
{
|
||||
INullArrayCases originalObject = new INullArrayCasesImpl();
|
||||
ComWrappers cw = new StrategyBasedComWrappers();
|
||||
nint ptr = cw.GetOrCreateComInterfaceForObject(originalObject, CreateComInterfaceFlags.None);
|
||||
object obj = cw.GetOrCreateObjectForComInstance(ptr, CreateObjectFlags.None);
|
||||
return (INullArrayCases)obj;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleNullArray_WithNonZeroLength_DoesNotCrash()
|
||||
{
|
||||
// Arrange
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
// Act & Assert - Should not throw or crash
|
||||
testInterface.SingleNullArrayWithLength(10, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleNullArray_WithZeroLength_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
// Should not throw or crash
|
||||
testInterface.SingleNullArrayWithLength(0, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleNullArray_WithValidArray_WorksNormally()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
int[] array = new int[5];
|
||||
|
||||
testInterface.SingleNullArrayWithLength(5, array);
|
||||
|
||||
Assert.Equal(new int[] { 0, 2, 4, 6, 8 }, array);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleArrays_SomeNull_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
int[] array1 = new int[3];
|
||||
int[] array3 = new int[3];
|
||||
|
||||
testInterface.MultipleArraysSharedLength(3, array1, null, array3);
|
||||
|
||||
Assert.Equal(new int[] { 0, 1, 2 }, array1);
|
||||
Assert.Equal(new int[] { 0, 100, 200 }, array3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleArrays_AllNull_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
testInterface.MultipleArraysSharedLength(5, null, null, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NonBlittableArray_Null_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
testInterface.NonBlittableNullArray(10, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NonBlittableArray_ValidArray_WorksNormally()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
var array = new IntStructWrapper[3];
|
||||
|
||||
testInterface.NonBlittableNullArray(3, array);
|
||||
|
||||
Assert.Equal(0, array[0].Value);
|
||||
Assert.Equal(3, array[1].Value);
|
||||
Assert.Equal(6, array[2].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZeroLength_NullArray_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
testInterface.ZeroLengthArray(0, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LargeLength_NullArray_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
testInterface.LargeLengthNullArray(int.MaxValue, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpanNull_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
var (__this, __vtable) = ((IUnmanagedVirtualMethodTableProvider)testInterface).GetVirtualMethodTableInfoForKey(typeof(INullArrayCases));
|
||||
int length = 10;
|
||||
var __target = (delegate* unmanaged[MemberFunction]<void*, int, int**, int>)__vtable[11];
|
||||
int* __span_native = null;
|
||||
int __invokeRetVal = __target(__this, length, &__span_native);
|
||||
Marshal.ThrowExceptionForHR(__invokeRetVal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpanNull_WithZeroLength_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
var (__this, __vtable) = ((IUnmanagedVirtualMethodTableProvider)testInterface).GetVirtualMethodTableInfoForKey(typeof(INullArrayCases));
|
||||
int length = 0;
|
||||
var __target = (delegate* unmanaged[MemberFunction]<void*, int, int**, int>)__vtable[11];
|
||||
int* __span_native = null;
|
||||
int __invokeRetVal = __target(__this, length, &__span_native);
|
||||
Marshal.ThrowExceptionForHR(__invokeRetVal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpanNull_WithLargeLength_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
var (__this, __vtable) = ((IUnmanagedVirtualMethodTableProvider)testInterface).GetVirtualMethodTableInfoForKey(typeof(INullArrayCases));
|
||||
int length = int.MaxValue;
|
||||
var __target = (delegate* unmanaged[MemberFunction]<void*, int, int**, int>)__vtable[11];
|
||||
int* __span_native = null;
|
||||
int __invokeRetVal = __target(__this, length, &__span_native);
|
||||
Marshal.ThrowExceptionForHR(__invokeRetVal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpanNonBlittable_Null_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
var (__this, __vtable) = ((IUnmanagedVirtualMethodTableProvider)testInterface).GetVirtualMethodTableInfoForKey(typeof(INullArrayCases));
|
||||
int length = 10;
|
||||
var __target = (delegate* unmanaged[MemberFunction]<void*, int, IntStructWrapper**, int>)__vtable[12];
|
||||
IntStructWrapper* __span_native = null;
|
||||
int __invokeRetVal = __target(__this, length, &__span_native);
|
||||
Marshal.ThrowExceptionForHR(__invokeRetVal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpanValid_WorksNormally()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
var span = new Span<int>(new int[5]);
|
||||
testInterface.SpanNullCase(5, ref span);
|
||||
|
||||
Assert.Equal(0, span[0]);
|
||||
Assert.Equal(5, span[1]);
|
||||
Assert.Equal(10, span[2]);
|
||||
Assert.Equal(15, span[3]);
|
||||
Assert.Equal(20, span[4]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpanNonBlittableValid_WorksNormally()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
var span = new Span<IntStructWrapper>(new IntStructWrapper[3]);
|
||||
testInterface.SpanNonBlittableNullCase(3, ref span);
|
||||
|
||||
Assert.Equal(0, span[0].Value);
|
||||
Assert.Equal(7, span[1].Value);
|
||||
Assert.Equal(14, span[2].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InputOnlyArray_Null_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
testInterface.InOnlyNullArray(10, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OutputOnlyArray_Null_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
testInterface.OutOnlyNullArray(10, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReferenceArray_Null_DoesNotCrash()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
testInterface.ReferenceArrayNullCase(10, null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReferenceArray_ValidArray_WorksNormally()
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
string[] array = new string[3];
|
||||
|
||||
testInterface.ReferenceArrayNullCase(3, array);
|
||||
|
||||
Assert.Equal(new string[] { "Item 0", "Item 1", "Item 2" }, array);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(100)]
|
||||
public void NullArray_VariousLengths_DoesNotCrash(int length)
|
||||
{
|
||||
var testInterface = CreateTestInterface();
|
||||
|
||||
testInterface.SingleNullArrayWithLength(length, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
using SharedTypes.ComInterfaces;
|
||||
using Xunit;
|
||||
|
||||
namespace ComInterfaceGenerator.Tests
|
||||
{
|
||||
public partial class INullableOutArrayTests
|
||||
{
|
||||
[Fact]
|
||||
// Regression test for https://github.com/dotnet/runtime/issues/118135
|
||||
public unsafe void NullableOutArray_Marshalling_Works()
|
||||
{
|
||||
// Arrange
|
||||
INullableOutArray originalObject = new INullableOutArrayImpl();
|
||||
ComWrappers cw = new StrategyBasedComWrappers();
|
||||
nint ptr = cw.GetOrCreateComInterfaceForObject(originalObject, CreateComInterfaceFlags.None);
|
||||
object obj = cw.GetOrCreateObjectForComInstance(ptr, CreateObjectFlags.None);
|
||||
|
||||
INullableOutArray throughInterface = (INullableOutArray)obj;
|
||||
|
||||
var (__this, __vtable) = ((IUnmanagedVirtualMethodTableProvider)throughInterface).GetVirtualMethodTableInfoForKey(typeof(INullableOutArray));
|
||||
var __target = (delegate* unmanaged[MemberFunction]<void*, int, int*, int*, int>)__vtable[4];
|
||||
|
||||
int[] outputArray = new int[5];
|
||||
|
||||
fixed (int* __outputArray_native = &ArrayMarshaller<int, int>.ManagedToUnmanagedIn.GetPinnableReference(outputArray))
|
||||
{
|
||||
int hr = __target(__this, 5, __outputArray_native, null);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -112,6 +112,33 @@ namespace LibraryImportGenerator.IntegrationTests
|
|||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "return_duplicate_int_ptr_array")]
|
||||
[return: MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
|
||||
public static unsafe partial int*[] ReturnDuplicate(int*[] values, int numValues);
|
||||
|
||||
// Null array edge case methods
|
||||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "null_array_blittable_byval")]
|
||||
public static partial int NullArrayBlittableByVal(int[] array, int length);
|
||||
|
||||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "null_array_blittable_in")]
|
||||
public static partial int NullArrayBlittableIn(in int[] array, int length);
|
||||
|
||||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "null_array_blittable_ref")]
|
||||
public static partial int NullArrayBlittableRef([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref int[] array, int length);
|
||||
|
||||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "null_array_blittable_out")]
|
||||
public static partial int NullArrayBlittableOut(int length, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out int[] array);
|
||||
|
||||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "null_array_nonblittable_byval")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static partial bool NullArrayNonBlittableByVal(BoolStruct[] array, int length);
|
||||
|
||||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "null_array_nonblittable_in")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static partial bool NullArrayNonBlittableIn(in BoolStruct[] array, int length);
|
||||
|
||||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "null_array_nonblittable_ref")]
|
||||
public static partial void NullArrayNonBlittableRef([MarshalUsing(CountElementName = "length")] ref BoolStruct[] array, int length);
|
||||
|
||||
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "null_array_nonblittable_out")]
|
||||
public static partial void NullArrayNonBlittableOut(int length, [MarshalUsing(CountElementName = "length")] out BoolStruct[] array);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -836,5 +863,198 @@ namespace LibraryImportGenerator.IntegrationTests
|
|||
Array.Reverse(chars);
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayBlittable_ByVal_WithNonZeroLength()
|
||||
{
|
||||
// Test null array with non-zero length parameter - should not crash
|
||||
int[] nullArray = null;
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableByVal(nullArray, 5);
|
||||
Assert.Equal(-1, result); // Native method should return -1 for null array
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayBlittable_ByVal_WithZeroLength()
|
||||
{
|
||||
// Test null array with zero length parameter - should work normally
|
||||
int[] nullArray = null;
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableByVal(nullArray, 0);
|
||||
Assert.Equal(-1, result); // Native method should return -1 for null array
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayBlittable_In_WithNonZeroLength()
|
||||
{
|
||||
// Test null array with 'in' parameter and non-zero length
|
||||
int[] nullArray = null;
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableIn(nullArray, 3);
|
||||
Assert.Equal(-1, result); // Native method should return -1 for null array
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayBlittable_In_WithZeroLength()
|
||||
{
|
||||
// Test null array with 'in' parameter and zero length
|
||||
int[] nullArray = null;
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableIn(nullArray, 0);
|
||||
Assert.Equal(-1, result); // Native method should return -1 for null array
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayBlittable_Ref_WithNonZeroLength()
|
||||
{
|
||||
// Test null array with 'ref' parameter and non-zero length
|
||||
int[] nullArray = null;
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableRef(ref nullArray, 4);
|
||||
Assert.Equal(-1, result); // Native method should return -1 for null array
|
||||
Assert.Null(nullArray); // Array should remain null
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayBlittable_Ref_WithZeroLength()
|
||||
{
|
||||
// Test null array with 'ref' parameter and zero length
|
||||
int[] nullArray = null;
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableRef(ref nullArray, 0);
|
||||
Assert.Equal(-1, result); // Native method should return -1 for null array
|
||||
Assert.Null(nullArray); // Array should remain null
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayBlittable_Out_WithNonZeroLength()
|
||||
{
|
||||
// Test 'out' parameter that should set array to null with non-zero length
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableOut(3, out int[] array);
|
||||
Assert.Equal(0, result); // Native method should return 0 when setting null
|
||||
Assert.Null(array); // Array should be null
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayBlittable_Out_WithZeroLength()
|
||||
{
|
||||
// Test 'out' parameter that should set array to null with zero length
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableOut(0, out int[] array);
|
||||
Assert.Equal(0, result); // Native method should return 0 when setting null
|
||||
Assert.Null(array); // Array should be null
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayNonBlittable_ByVal_WithNonZeroLength()
|
||||
{
|
||||
// Test null array with non-blittable elements and non-zero length
|
||||
BoolStruct[] nullArray = null;
|
||||
bool result = NativeExportsNE.Arrays.NullArrayNonBlittableByVal(nullArray, 2);
|
||||
Assert.False(result); // Native method should return false for null array
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayNonBlittable_ByVal_WithZeroLength()
|
||||
{
|
||||
// Test null array with non-blittable elements and zero length
|
||||
BoolStruct[] nullArray = null;
|
||||
bool result = NativeExportsNE.Arrays.NullArrayNonBlittableByVal(nullArray, 0);
|
||||
Assert.False(result); // Native method should return false for null array
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayNonBlittable_In_WithNonZeroLength()
|
||||
{
|
||||
// Test null array with 'in' parameter, non-blittable elements, and non-zero length
|
||||
BoolStruct[] nullArray = null;
|
||||
bool result = NativeExportsNE.Arrays.NullArrayNonBlittableIn(nullArray, 1);
|
||||
Assert.False(result); // Native method should return false for null array
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayNonBlittable_In_WithZeroLength()
|
||||
{
|
||||
// Test null array with 'in' parameter, non-blittable elements, and zero length
|
||||
BoolStruct[] nullArray = null;
|
||||
bool result = NativeExportsNE.Arrays.NullArrayNonBlittableIn(nullArray, 0);
|
||||
Assert.False(result); // Native method should return false for null array
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayNonBlittable_Ref_WithNonZeroLength()
|
||||
{
|
||||
// Test null array with 'ref' parameter, non-blittable elements, and non-zero length
|
||||
BoolStruct[] nullArray = null;
|
||||
NativeExportsNE.Arrays.NullArrayNonBlittableRef(ref nullArray, 2);
|
||||
Assert.Null(nullArray); // Array should remain null
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayNonBlittable_Ref_WithZeroLength()
|
||||
{
|
||||
// Test null array with 'ref' parameter, non-blittable elements, and zero length
|
||||
BoolStruct[] nullArray = null;
|
||||
NativeExportsNE.Arrays.NullArrayNonBlittableRef(ref nullArray, 0);
|
||||
Assert.Null(nullArray); // Array should remain null
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayNonBlittable_Out_WithNonZeroLength()
|
||||
{
|
||||
// Test 'out' parameter that should set array to null with non-zero length
|
||||
NativeExportsNE.Arrays.NullArrayNonBlittableOut(1, out BoolStruct[] array);
|
||||
Assert.Null(array); // Array should be null
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullArrayNonBlittable_Out_WithZeroLength()
|
||||
{
|
||||
// Test 'out' parameter that should set array to null with zero length
|
||||
NativeExportsNE.Arrays.NullArrayNonBlittableOut(0, out BoolStruct[] array);
|
||||
Assert.Null(array); // Array should be null
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidArrayBlittable_WithNullArrayMethods()
|
||||
{
|
||||
// Test that valid arrays work correctly with our null array methods
|
||||
int[] validArray = new[] { 1, 2, 3, 4, 5 };
|
||||
|
||||
// Test ByVal with valid array
|
||||
int result = NativeExportsNE.Arrays.NullArrayBlittableByVal(validArray, validArray.Length);
|
||||
Assert.Equal(15, result); // Sum should be 1+2+3+4+5 = 15
|
||||
|
||||
// Test In with valid array
|
||||
result = NativeExportsNE.Arrays.NullArrayBlittableIn(validArray, validArray.Length);
|
||||
Assert.Equal(15, result); // Sum should be 1+2+3+4+5 = 15
|
||||
|
||||
// Test Ref with valid array
|
||||
result = NativeExportsNE.Arrays.NullArrayBlittableRef(ref validArray, validArray.Length);
|
||||
Assert.Equal(15, result); // Sum should be 1+2+3+4+5 = 15
|
||||
Assert.NotNull(validArray); // Array should still be valid
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidArrayNonBlittable_WithNullArrayMethods()
|
||||
{
|
||||
// Test that valid arrays work correctly with our null array non-blittable methods
|
||||
BoolStruct[] validArray = new[]
|
||||
{
|
||||
new BoolStruct { b1 = true, b2 = true, b3 = true },
|
||||
new BoolStruct { b1 = true, b2 = true, b3 = true }
|
||||
};
|
||||
|
||||
// Test ByVal with valid array - should return true since all fields are true
|
||||
bool result = NativeExportsNE.Arrays.NullArrayNonBlittableByVal(validArray, validArray.Length);
|
||||
Assert.True(result);
|
||||
|
||||
// Test In with valid array - should return true since all fields are true
|
||||
result = NativeExportsNE.Arrays.NullArrayNonBlittableIn(validArray, validArray.Length);
|
||||
Assert.True(result);
|
||||
|
||||
// Test with array containing false values
|
||||
BoolStruct[] mixedArray = new[]
|
||||
{
|
||||
new BoolStruct { b1 = true, b2 = true, b3 = true },
|
||||
new BoolStruct { b1 = true, b2 = false, b3 = true } // b2 is false
|
||||
};
|
||||
|
||||
result = NativeExportsNE.Arrays.NullArrayNonBlittableByVal(mixedArray, mixedArray.Length);
|
||||
Assert.False(result); // Should return false due to b2 being false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -440,5 +440,128 @@ namespace NativeExports
|
|||
*numValues = newStrings.Count;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Null array edge case methods for LibraryImportGenerator tests
|
||||
[UnmanagedCallersOnly(EntryPoint = "null_array_blittable_byval")]
|
||||
public static int NullArrayBlittableByVal(int* array, int length)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If not null, sum the array elements
|
||||
int sum = 0;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
sum += array[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "null_array_blittable_in")]
|
||||
public static int NullArrayBlittableIn(int** array, int length)
|
||||
{
|
||||
if (*array == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If not null, sum the array elements
|
||||
int sum = 0;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
sum += (*array)[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "null_array_blittable_ref")]
|
||||
public static int NullArrayBlittableRef(int** array, int length)
|
||||
{
|
||||
if (*array == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If not null, sum the array elements
|
||||
int sum = 0;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
sum += (*array)[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "null_array_blittable_out")]
|
||||
public static int NullArrayBlittableOut(int length, int** array)
|
||||
{
|
||||
// Always set the output array to null for testing purposes
|
||||
*array = null;
|
||||
return 0;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "null_array_nonblittable_byval")]
|
||||
[DNNE.C99DeclCode("struct bool_struct;")]
|
||||
public static byte NullArrayNonBlittableByVal([DNNE.C99Type("struct bool_struct*")] BoolStructMarshaller.BoolStructNative* array, int length)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
return 0; // false
|
||||
}
|
||||
|
||||
// If not null, check if all members are true
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
BoolStruct managed = BoolStructMarshaller.ConvertToManaged(array[i]);
|
||||
if (!managed.b1 || !managed.b2 || !managed.b3)
|
||||
{
|
||||
return 0; // false
|
||||
}
|
||||
}
|
||||
return 1; // true
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "null_array_nonblittable_in")]
|
||||
[DNNE.C99DeclCode("struct bool_struct;")]
|
||||
public static byte NullArrayNonBlittableIn([DNNE.C99Type("struct bool_struct**")] BoolStructMarshaller.BoolStructNative** array, int length)
|
||||
{
|
||||
if (*array == null)
|
||||
{
|
||||
return 0; // false
|
||||
}
|
||||
|
||||
// If not null, check if all members are true
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
BoolStruct managed = BoolStructMarshaller.ConvertToManaged((*array)[i]);
|
||||
if (!managed.b1 || !managed.b2 || !managed.b3)
|
||||
{
|
||||
return 0; // false
|
||||
}
|
||||
}
|
||||
return 1; // true
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "null_array_nonblittable_ref")]
|
||||
[DNNE.C99DeclCode("struct bool_struct;")]
|
||||
public static void NullArrayNonBlittableRef([DNNE.C99Type("struct bool_struct**")] BoolStructMarshaller.BoolStructNative** array, int length)
|
||||
{
|
||||
// For testing purposes, just check if array is null and leave it as is
|
||||
if (*array == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If not null, we could do some operation, but for null testing we'll just return
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "null_array_nonblittable_out")]
|
||||
[DNNE.C99DeclCode("struct bool_struct;")]
|
||||
public static void NullArrayNonBlittableOut(int length, [DNNE.C99Type("struct bool_struct**")] BoolStructMarshaller.BoolStructNative** array)
|
||||
{
|
||||
// Always set the output array to null for testing purposes
|
||||
*array = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
|
||||
namespace SharedTypes.ComInterfaces
|
||||
{
|
||||
[GeneratedComInterface]
|
||||
[Guid("F8A2C5D1-9B7E-4A3C-8F5D-2E1B9C4A7F6E")]
|
||||
internal partial interface INullArrayCases
|
||||
{
|
||||
// Basic case: single null array with non-zero length
|
||||
void SingleNullArrayWithLength(
|
||||
int length,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] int[]? array);
|
||||
|
||||
// Multiple arrays sharing same length, some null, some not
|
||||
void MultipleArraysSharedLength(
|
||||
int length,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] int[]? array1,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] int[]? array2,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] int[]? array3);
|
||||
|
||||
// Non-blittable types with null arrays
|
||||
void NonBlittableNullArray(
|
||||
int length,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] IntStructWrapper[]? array);
|
||||
|
||||
// Zero-length case (should handle gracefully)
|
||||
void ZeroLengthArray(
|
||||
int length, // Will be 0
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] int[]? array);
|
||||
|
||||
// Large length with null array (potential overflow/crash scenario)
|
||||
void LargeLengthNullArray(
|
||||
int length, // Will be int.MaxValue
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] int[]? array);
|
||||
|
||||
// Different parameter directions
|
||||
void InOnlyNullArray(
|
||||
int length,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In] int[]? array);
|
||||
|
||||
void OutOnlyNullArray(
|
||||
int length,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] int[]? array);
|
||||
|
||||
// Reference arrays with null (different from value type arrays)
|
||||
void ReferenceArrayNullCase(
|
||||
int length,
|
||||
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0), In, Out] string[]? array);
|
||||
|
||||
// Span<T> with null cases (ContiguousCollectionMarshaller)
|
||||
void SpanNullCase(
|
||||
int length,
|
||||
[MarshalUsing(CountElementName = nameof(length))] ref Span<int> span);
|
||||
|
||||
// Span<T> with non-blittable types
|
||||
void SpanNonBlittableNullCase(
|
||||
int length,
|
||||
[MarshalUsing(CountElementName = nameof(length))] ref Span<IntStructWrapper> span);
|
||||
}
|
||||
|
||||
[GeneratedComClass]
|
||||
internal partial class INullArrayCasesImpl : INullArrayCases
|
||||
{
|
||||
public void SingleNullArrayWithLength(int length, int[]? array)
|
||||
{
|
||||
// Should handle null array gracefully regardless of length
|
||||
if (array != null)
|
||||
{
|
||||
for (int i = 0; i < Math.Min(length, array.Length); i++)
|
||||
{
|
||||
array[i] = i * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MultipleArraysSharedLength(int length, int[]? array1, int[]? array2, int[]? array3)
|
||||
{
|
||||
// Should only process non-null arrays
|
||||
if (array1 != null)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
array1[i] = i;
|
||||
}
|
||||
}
|
||||
if (array2 != null)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
array2[i] = i * 10;
|
||||
}
|
||||
}
|
||||
if (array3 != null)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
array3[i] = i * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void NonBlittableNullArray(int length, IntStructWrapper[]? array)
|
||||
{
|
||||
if (array != null)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
array[i] = new IntStructWrapper { Value = i * 3 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ZeroLengthArray(int length, int[]? array)
|
||||
{
|
||||
// Should handle zero length gracefully
|
||||
if (array != null && length > 0)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
array[i] = 42;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LargeLengthNullArray(int length, int[]? array)
|
||||
{
|
||||
// Should not crash or allocate massive amounts when array is null
|
||||
if (array != null)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
array[i] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InOnlyNullArray(int length, int[]? array)
|
||||
{
|
||||
// Input-only: just read from array if not null
|
||||
if (array != null)
|
||||
{
|
||||
int sum = 0;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
sum += array[i];
|
||||
}
|
||||
// Could store sum somewhere, but for test we just ensure no crash
|
||||
}
|
||||
}
|
||||
|
||||
public void OutOnlyNullArray(int length, int[]? array)
|
||||
{
|
||||
// Output-only: write to array if not null
|
||||
if (array != null)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
array[i] = i + 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReferenceArrayNullCase(int length, string[]? array)
|
||||
{
|
||||
if (array != null)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
array[i] = $"Item {i}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SpanNullCase(int length, ref Span<int> span)
|
||||
{
|
||||
// Should handle empty/default span gracefully regardless of length
|
||||
if (!span.IsEmpty)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
span[i] = i * 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SpanNonBlittableNullCase(int length, ref Span<IntStructWrapper> span)
|
||||
{
|
||||
// Should handle empty/default span gracefully regardless of length
|
||||
if (!span.IsEmpty)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
span[i] = new IntStructWrapper { Value = i * 7 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
|
||||
namespace SharedTypes.ComInterfaces
|
||||
{
|
||||
[GeneratedComInterface]
|
||||
[Guid("5A9D3ED6-CC17-4FB9-8F82-0070489B7213")]
|
||||
internal partial interface INullableOutArray
|
||||
{
|
||||
void Method(int size, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] IntStructWrapper[]? array, [MarshalAs(UnmanagedType.Bool)] bool passedNull);
|
||||
// Definition
|
||||
void M(
|
||||
int bufferSize,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] IntStructWrapper[]? buffer1,
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), In, Out] IntStructWrapper[]? buffer2);
|
||||
}
|
||||
|
||||
[GeneratedComClass]
|
||||
internal partial class INullableOutArrayImpl : INullableOutArray
|
||||
{
|
||||
public void M(int bufferSize, IntStructWrapper[]? buffer1, IntStructWrapper[]? buffer2)
|
||||
{
|
||||
if (buffer1 is not null)
|
||||
{
|
||||
for (int i = 0; i < bufferSize; i++)
|
||||
{
|
||||
buffer1[i] = new() { Value = i };
|
||||
}
|
||||
}
|
||||
if (buffer2 is not null)
|
||||
{
|
||||
for (int i = 0; i < bufferSize; i++)
|
||||
{
|
||||
buffer2[i] = new() { Value = i };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Method(int size, IntStructWrapper[]? array, bool passedNull)
|
||||
{
|
||||
if (passedNull)
|
||||
{
|
||||
if (array is not null)
|
||||
{
|
||||
throw new ArgumentException("Expected array to be null when passedNull is true.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (size == 0 || array is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
array[i] = new() { Value = i };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue