Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,33 @@ protected StatementSyntax GenerateByValueOutUnmarshalStatement(TypePositionInfo
StubCodeContext.Stage.Unmarshal));
}

protected StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context);
StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)),
IdentifierName("Length")),
StubCodeContext.Stage.Cleanup);

if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement))
{
return EmptyStatement();
}

return Block(
LocalDeclarationStatement(VariableDeclaration(
GenericName(
Identifier(TypeNames.System_Span),
TypeArgumentList(SingletonSeparatedList(_unmanagedElementType))),
SingletonSeparatedList(
VariableDeclarator(
Identifier(nativeSpanIdentifier))
.WithInitializer(EqualsValueClause(
GetUnmanagedValuesDestination(info, context)))))),
contentsCleanupStatements);
}

protected StatementSyntax GenerateContentsMarshallingStatement(
TypePositionInfo info,
StubCodeContext context,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,5 +300,37 @@ public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInf
}
}
}

public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubCodeContext context)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))))));
}
else
{
// Assign out params to default
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword))));
}
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -447,7 +447,27 @@ public StatefulLinearCollectionNonBlittableElementsMarshalling(
}

public TypeSyntax AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}

if (!_shape.HasFlag(MarshallerShape.Free))
yield break;

Comment thread
jkoritzinsky marked this conversation as resolved.
string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context);
// <marshaller>.Free();
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(marshaller),
IdentifierName(ShapeMemberNames.Free)),
ArgumentList()));
}
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);

public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,10 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller,

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context))
{
yield return statement;
}
// <marshallerType>.Free(<nativeIdentifier>);
yield return ExpressionStatement(
InvocationExpression(
Expand DownExpand Up@@ -372,11 +376,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i
public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand DownExpand Up@@ -512,7 +524,15 @@ public StatelessLinearCollectionNonBlittableElementsMarshalling(

public TypeSyntax AsNativeType(TypePositionInfo info) => _nativeTypeSyntax;

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}
}

public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
{
Expand DownExpand Up@@ -588,11 +608,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i

public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,34 +29,7 @@ public static VariableDeclarations GenerateDeclarationsForManagedToNative(BoundG

if (info.RefKind == RefKind.Out)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
initializations.Add(ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))));
}
else
{
// Assign out params to default
initializations.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword)))));
}
initializations.Add(MarshallerHelpers.SkipInitOrDefaultInit(info, context));
}

// Declare variables for parameters
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,9 @@ public partial class Stateless
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumWithBuffer([MarshalUsing(typeof(ListMarshallerWithBuffer<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshaller<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial int DoubleValues([MarshalUsing(typeof(ListMarshallerWithPinning<,>))] List<BlittableIntWrapper> values, int length);

Expand DownExpand Up@@ -99,6 +102,9 @@ public partial class Stateful
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshallerStateful<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshallerStateful<,>))] in List<int> values, int numValues);

Expand DownExpand Up@@ -369,6 +375,30 @@ public void NonBlittableElementCollection_GuaranteedUnmarshal()
Assert.True(NativeExportsNE.Collections.Stateful.ListGuaranteedUnmarshal<BoolStruct, BoolStructMarshaller.BoolStructNative>.Marshaller.ToManagedFinallyCalled);
}

[Fact]
public void ElementsFreed()
{
List<IntWrapper> list = new List<IntWrapper>
{
new IntWrapper { i = 1 },
new IntWrapper { i = 10 },
new IntWrapper { i = 24 },
new IntWrapper { i = 30 },
};

int startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateless.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);

startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateful.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);
}

private static List<BoolStruct> GetBoolStructsToAnd(bool result) => new List<BoolStruct>
{
new BoolStruct
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,31 @@ public static void Free(int* unmanaged)
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(IntWrapperMarshallerWithFreeCounts))]
public static unsafe class IntWrapperMarshallerWithFreeCounts
{
[ThreadStatic]
public static int NumCallsToFree = 0;

public static int* ConvertToUnmanaged(IntWrapper managed)
{
int* ret = (int*)Marshal.AllocCoTaskMem(sizeof(int));
*ret = managed.i;
return ret;
}

public static IntWrapper ConvertToManaged(int* unmanaged)
{
return new IntWrapper { i = *unmanaged };
}

public static void Free(int* unmanaged)
{
NumCallsToFree++;
Marshal.FreeCoTaskMem((IntPtr)unmanaged);
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
Expand DownExpand Up@@ -477,14 +502,14 @@ public void FromManaged(List<T> managed, Span<TUnmanagedElement> buffer)

_list = managed;
// Always allocate at least one byte when the list is zero-length.
int spaceToAllocate = Math.Max(managed.Count * sizeof(TUnmanagedElement), 1);
if (spaceToAllocate <= buffer.Length)
int countToAllocate = Math.Max(managed.Count, 1);
if (countToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
_span = buffer[0..countToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_allocatedMemory = Marshal.AllocCoTaskMem(countToAllocate * sizeof(TUnmanagedElement));
_span = new Span<TUnmanagedElement>((void*)_allocatedMemory, managed.Count);
}
}
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,33 @@ protected StatementSyntax GenerateByValueOutUnmarshalStatement(TypePositionInfo
StubCodeContext.Stage.Unmarshal));
}

protected StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context);
StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)),
IdentifierName("Length")),
StubCodeContext.Stage.Cleanup);

if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement))
{
return EmptyStatement();
}

return Block(
LocalDeclarationStatement(VariableDeclaration(
GenericName(
Identifier(TypeNames.System_Span),
TypeArgumentList(SingletonSeparatedList(_unmanagedElementType))),
SingletonSeparatedList(
VariableDeclarator(
Identifier(nativeSpanIdentifier))
.WithInitializer(EqualsValueClause(
GetUnmanagedValuesDestination(info, context)))))),
contentsCleanupStatements);
}

protected StatementSyntax GenerateContentsMarshallingStatement(
TypePositionInfo info,
StubCodeContext context,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,5 +300,37 @@ public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInf
}
}
}

public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubCodeContext context)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))))));
}
else
{
// Assign out params to default
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword))));
}
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -447,7 +447,27 @@ public StatefulLinearCollectionNonBlittableElementsMarshalling(
}

public TypeSyntax AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}

if (!_shape.HasFlag(MarshallerShape.Free))
yield break;

Comment thread
jkoritzinsky marked this conversation as resolved.
string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context);
// <marshaller>.Free();
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(marshaller),
IdentifierName(ShapeMemberNames.Free)),
ArgumentList()));
}
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);

public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,10 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller,

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context))
{
yield return statement;
}
// <marshallerType>.Free(<nativeIdentifier>);
yield return ExpressionStatement(
InvocationExpression(
Expand DownExpand Up@@ -372,11 +376,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i
public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand DownExpand Up@@ -512,7 +524,15 @@ public StatelessLinearCollectionNonBlittableElementsMarshalling(

public TypeSyntax AsNativeType(TypePositionInfo info) => _nativeTypeSyntax;

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}
}

public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
{
Expand DownExpand Up@@ -588,11 +608,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i

public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,34 +29,7 @@ public static VariableDeclarations GenerateDeclarationsForManagedToNative(BoundG

if (info.RefKind == RefKind.Out)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
initializations.Add(ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))));
}
else
{
// Assign out params to default
initializations.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword)))));
}
initializations.Add(MarshallerHelpers.SkipInitOrDefaultInit(info, context));
}

// Declare variables for parameters
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,9 @@ public partial class Stateless
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumWithBuffer([MarshalUsing(typeof(ListMarshallerWithBuffer<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshaller<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial int DoubleValues([MarshalUsing(typeof(ListMarshallerWithPinning<,>))] List<BlittableIntWrapper> values, int length);

Expand DownExpand Up@@ -99,6 +102,9 @@ public partial class Stateful
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshallerStateful<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshallerStateful<,>))] in List<int> values, int numValues);

Expand DownExpand Up@@ -369,6 +375,30 @@ public void NonBlittableElementCollection_GuaranteedUnmarshal()
Assert.True(NativeExportsNE.Collections.Stateful.ListGuaranteedUnmarshal<BoolStruct, BoolStructMarshaller.BoolStructNative>.Marshaller.ToManagedFinallyCalled);
}

[Fact]
public void ElementsFreed()
{
List<IntWrapper> list = new List<IntWrapper>
{
new IntWrapper { i = 1 },
new IntWrapper { i = 10 },
new IntWrapper { i = 24 },
new IntWrapper { i = 30 },
};

int startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateless.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);

startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateful.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);
}

private static List<BoolStruct> GetBoolStructsToAnd(bool result) => new List<BoolStruct>
{
new BoolStruct
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,31 @@ public static void Free(int* unmanaged)
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(IntWrapperMarshallerWithFreeCounts))]
public static unsafe class IntWrapperMarshallerWithFreeCounts
{
[ThreadStatic]
public static int NumCallsToFree = 0;

public static int* ConvertToUnmanaged(IntWrapper managed)
{
int* ret = (int*)Marshal.AllocCoTaskMem(sizeof(int));
*ret = managed.i;
return ret;
}

public static IntWrapper ConvertToManaged(int* unmanaged)
{
return new IntWrapper { i = *unmanaged };
}

public static void Free(int* unmanaged)
{
NumCallsToFree++;
Marshal.FreeCoTaskMem((IntPtr)unmanaged);
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
Expand DownExpand Up@@ -477,14 +502,14 @@ public void FromManaged(List<T> managed, Span<TUnmanagedElement> buffer)

_list = managed;
// Always allocate at least one byte when the list is zero-length.
int spaceToAllocate = Math.Max(managed.Count * sizeof(TUnmanagedElement), 1);
if (spaceToAllocate <= buffer.Length)
int countToAllocate = Math.Max(managed.Count, 1);
if (countToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
_span = buffer[0..countToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_allocatedMemory = Marshal.AllocCoTaskMem(countToAllocate * sizeof(TUnmanagedElement));
_span = new Span<TUnmanagedElement>((void*)_allocatedMemory, managed.Count);
}
}
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,33 @@ protected StatementSyntax GenerateByValueOutUnmarshalStatement(TypePositionInfo
StubCodeContext.Stage.Unmarshal));
}

protected StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context);
StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)),
IdentifierName("Length")),
StubCodeContext.Stage.Cleanup);

if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement))
{
return EmptyStatement();
}

return Block(
LocalDeclarationStatement(VariableDeclaration(
GenericName(
Identifier(TypeNames.System_Span),
TypeArgumentList(SingletonSeparatedList(_unmanagedElementType))),
SingletonSeparatedList(
VariableDeclarator(
Identifier(nativeSpanIdentifier))
.WithInitializer(EqualsValueClause(
GetUnmanagedValuesDestination(info, context)))))),
contentsCleanupStatements);
}

protected StatementSyntax GenerateContentsMarshallingStatement(
TypePositionInfo info,
StubCodeContext context,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,5 +300,37 @@ public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInf
}
}
}

public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubCodeContext context)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))))));
}
else
{
// Assign out params to default
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword))));
}
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -447,7 +447,27 @@ public StatefulLinearCollectionNonBlittableElementsMarshalling(
}

public TypeSyntax AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}

if (!_shape.HasFlag(MarshallerShape.Free))
yield break;

Comment thread
jkoritzinsky marked this conversation as resolved.
string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context);
// <marshaller>.Free();
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(marshaller),
IdentifierName(ShapeMemberNames.Free)),
ArgumentList()));
}
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);

public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,10 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller,

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context))
{
yield return statement;
}
// <marshallerType>.Free(<nativeIdentifier>);
yield return ExpressionStatement(
InvocationExpression(
Expand DownExpand Up@@ -372,11 +376,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i
public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand DownExpand Up@@ -512,7 +524,15 @@ public StatelessLinearCollectionNonBlittableElementsMarshalling(

public TypeSyntax AsNativeType(TypePositionInfo info) => _nativeTypeSyntax;

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}
}

public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
{
Expand DownExpand Up@@ -588,11 +608,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i

public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,34 +29,7 @@ public static VariableDeclarations GenerateDeclarationsForManagedToNative(BoundG

if (info.RefKind == RefKind.Out)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
initializations.Add(ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))));
}
else
{
// Assign out params to default
initializations.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword)))));
}
initializations.Add(MarshallerHelpers.SkipInitOrDefaultInit(info, context));
}

// Declare variables for parameters
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,9 @@ public partial class Stateless
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumWithBuffer([MarshalUsing(typeof(ListMarshallerWithBuffer<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshaller<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial int DoubleValues([MarshalUsing(typeof(ListMarshallerWithPinning<,>))] List<BlittableIntWrapper> values, int length);

Expand DownExpand Up@@ -99,6 +102,9 @@ public partial class Stateful
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshallerStateful<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshallerStateful<,>))] in List<int> values, int numValues);

Expand DownExpand Up@@ -369,6 +375,30 @@ public void NonBlittableElementCollection_GuaranteedUnmarshal()
Assert.True(NativeExportsNE.Collections.Stateful.ListGuaranteedUnmarshal<BoolStruct, BoolStructMarshaller.BoolStructNative>.Marshaller.ToManagedFinallyCalled);
}

[Fact]
public void ElementsFreed()
{
List<IntWrapper> list = new List<IntWrapper>
{
new IntWrapper { i = 1 },
new IntWrapper { i = 10 },
new IntWrapper { i = 24 },
new IntWrapper { i = 30 },
};

int startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateless.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);

startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateful.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);
}

private static List<BoolStruct> GetBoolStructsToAnd(bool result) => new List<BoolStruct>
{
new BoolStruct
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,31 @@ public static void Free(int* unmanaged)
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(IntWrapperMarshallerWithFreeCounts))]
public static unsafe class IntWrapperMarshallerWithFreeCounts
{
[ThreadStatic]
public static int NumCallsToFree = 0;

public static int* ConvertToUnmanaged(IntWrapper managed)
{
int* ret = (int*)Marshal.AllocCoTaskMem(sizeof(int));
*ret = managed.i;
return ret;
}

public static IntWrapper ConvertToManaged(int* unmanaged)
{
return new IntWrapper { i = *unmanaged };
}

public static void Free(int* unmanaged)
{
NumCallsToFree++;
Marshal.FreeCoTaskMem((IntPtr)unmanaged);
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
Expand DownExpand Up@@ -477,14 +502,14 @@ public void FromManaged(List<T> managed, Span<TUnmanagedElement> buffer)

_list = managed;
// Always allocate at least one byte when the list is zero-length.
int spaceToAllocate = Math.Max(managed.Count * sizeof(TUnmanagedElement), 1);
if (spaceToAllocate <= buffer.Length)
int countToAllocate = Math.Max(managed.Count, 1);
if (countToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
_span = buffer[0..countToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_allocatedMemory = Marshal.AllocCoTaskMem(countToAllocate * sizeof(TUnmanagedElement));
_span = new Span<TUnmanagedElement>((void*)_allocatedMemory, managed.Count);
}
}
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,33 @@ protected StatementSyntax GenerateByValueOutUnmarshalStatement(TypePositionInfo
StubCodeContext.Stage.Unmarshal));
}

protected StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context);
StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)),
IdentifierName("Length")),
StubCodeContext.Stage.Cleanup);

if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement))
{
return EmptyStatement();
}

return Block(
LocalDeclarationStatement(VariableDeclaration(
GenericName(
Identifier(TypeNames.System_Span),
TypeArgumentList(SingletonSeparatedList(_unmanagedElementType))),
SingletonSeparatedList(
VariableDeclarator(
Identifier(nativeSpanIdentifier))
.WithInitializer(EqualsValueClause(
GetUnmanagedValuesDestination(info, context)))))),
contentsCleanupStatements);
}

protected StatementSyntax GenerateContentsMarshallingStatement(
TypePositionInfo info,
StubCodeContext context,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,5 +300,37 @@ public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInf
}
}
}

public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubCodeContext context)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))))));
}
else
{
// Assign out params to default
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword))));
}
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -447,7 +447,27 @@ public StatefulLinearCollectionNonBlittableElementsMarshalling(
}

public TypeSyntax AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}

if (!_shape.HasFlag(MarshallerShape.Free))
yield break;

Comment thread
jkoritzinsky marked this conversation as resolved.
string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context);
// <marshaller>.Free();
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(marshaller),
IdentifierName(ShapeMemberNames.Free)),
ArgumentList()));
}
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);

public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,10 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller,

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context))
{
yield return statement;
}
// <marshallerType>.Free(<nativeIdentifier>);
yield return ExpressionStatement(
InvocationExpression(
Expand DownExpand Up@@ -372,11 +376,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i
public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand DownExpand Up@@ -512,7 +524,15 @@ public StatelessLinearCollectionNonBlittableElementsMarshalling(

public TypeSyntax AsNativeType(TypePositionInfo info) => _nativeTypeSyntax;

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}
}

public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
{
Expand DownExpand Up@@ -588,11 +608,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i

public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,34 +29,7 @@ public static VariableDeclarations GenerateDeclarationsForManagedToNative(BoundG

if (info.RefKind == RefKind.Out)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
initializations.Add(ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))));
}
else
{
// Assign out params to default
initializations.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword)))));
}
initializations.Add(MarshallerHelpers.SkipInitOrDefaultInit(info, context));
}

// Declare variables for parameters
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,9 @@ public partial class Stateless
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumWithBuffer([MarshalUsing(typeof(ListMarshallerWithBuffer<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshaller<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial int DoubleValues([MarshalUsing(typeof(ListMarshallerWithPinning<,>))] List<BlittableIntWrapper> values, int length);

Expand DownExpand Up@@ -99,6 +102,9 @@ public partial class Stateful
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshallerStateful<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshallerStateful<,>))] in List<int> values, int numValues);

Expand DownExpand Up@@ -369,6 +375,30 @@ public void NonBlittableElementCollection_GuaranteedUnmarshal()
Assert.True(NativeExportsNE.Collections.Stateful.ListGuaranteedUnmarshal<BoolStruct, BoolStructMarshaller.BoolStructNative>.Marshaller.ToManagedFinallyCalled);
}

[Fact]
public void ElementsFreed()
{
List<IntWrapper> list = new List<IntWrapper>
{
new IntWrapper { i = 1 },
new IntWrapper { i = 10 },
new IntWrapper { i = 24 },
new IntWrapper { i = 30 },
};

int startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateless.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);

startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateful.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);
}

private static List<BoolStruct> GetBoolStructsToAnd(bool result) => new List<BoolStruct>
{
new BoolStruct
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,31 @@ public static void Free(int* unmanaged)
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(IntWrapperMarshallerWithFreeCounts))]
public static unsafe class IntWrapperMarshallerWithFreeCounts
{
[ThreadStatic]
public static int NumCallsToFree = 0;

public static int* ConvertToUnmanaged(IntWrapper managed)
{
int* ret = (int*)Marshal.AllocCoTaskMem(sizeof(int));
*ret = managed.i;
return ret;
}

public static IntWrapper ConvertToManaged(int* unmanaged)
{
return new IntWrapper { i = *unmanaged };
}

public static void Free(int* unmanaged)
{
NumCallsToFree++;
Marshal.FreeCoTaskMem((IntPtr)unmanaged);
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
Expand DownExpand Up@@ -477,14 +502,14 @@ public void FromManaged(List<T> managed, Span<TUnmanagedElement> buffer)

_list = managed;
// Always allocate at least one byte when the list is zero-length.
int spaceToAllocate = Math.Max(managed.Count * sizeof(TUnmanagedElement), 1);
if (spaceToAllocate <= buffer.Length)
int countToAllocate = Math.Max(managed.Count, 1);
if (countToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
_span = buffer[0..countToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_allocatedMemory = Marshal.AllocCoTaskMem(countToAllocate * sizeof(TUnmanagedElement));
_span = new Span<TUnmanagedElement>((void*)_allocatedMemory, managed.Count);
}
}
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,33 @@ protected StatementSyntax GenerateByValueOutUnmarshalStatement(TypePositionInfo
StubCodeContext.Stage.Unmarshal));
}

protected StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context);
StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)),
IdentifierName("Length")),
StubCodeContext.Stage.Cleanup);

if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement))
{
return EmptyStatement();
}

return Block(
LocalDeclarationStatement(VariableDeclaration(
GenericName(
Identifier(TypeNames.System_Span),
TypeArgumentList(SingletonSeparatedList(_unmanagedElementType))),
SingletonSeparatedList(
VariableDeclarator(
Identifier(nativeSpanIdentifier))
.WithInitializer(EqualsValueClause(
GetUnmanagedValuesDestination(info, context)))))),
contentsCleanupStatements);
}

protected StatementSyntax GenerateContentsMarshallingStatement(
TypePositionInfo info,
StubCodeContext context,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,5 +300,37 @@ public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInf
}
}
}

public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubCodeContext context)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))))));
}
else
{
// Assign out params to default
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword))));
}
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -447,7 +447,27 @@ public StatefulLinearCollectionNonBlittableElementsMarshalling(
}

public TypeSyntax AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}

if (!_shape.HasFlag(MarshallerShape.Free))
yield break;

Comment thread
jkoritzinsky marked this conversation as resolved.
string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context);
// <marshaller>.Free();
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(marshaller),
IdentifierName(ShapeMemberNames.Free)),
ArgumentList()));
}
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);

public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,10 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller,

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context))
{
yield return statement;
}
// <marshallerType>.Free(<nativeIdentifier>);
yield return ExpressionStatement(
InvocationExpression(
Expand DownExpand Up@@ -372,11 +376,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i
public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand DownExpand Up@@ -512,7 +524,15 @@ public StatelessLinearCollectionNonBlittableElementsMarshalling(

public TypeSyntax AsNativeType(TypePositionInfo info) => _nativeTypeSyntax;

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}
}

public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
{
Expand DownExpand Up@@ -588,11 +608,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i

public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,34 +29,7 @@ public static VariableDeclarations GenerateDeclarationsForManagedToNative(BoundG

if (info.RefKind == RefKind.Out)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
initializations.Add(ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))));
}
else
{
// Assign out params to default
initializations.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword)))));
}
initializations.Add(MarshallerHelpers.SkipInitOrDefaultInit(info, context));
}

// Declare variables for parameters
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,9 @@ public partial class Stateless
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumWithBuffer([MarshalUsing(typeof(ListMarshallerWithBuffer<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshaller<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial int DoubleValues([MarshalUsing(typeof(ListMarshallerWithPinning<,>))] List<BlittableIntWrapper> values, int length);

Expand DownExpand Up@@ -99,6 +102,9 @@ public partial class Stateful
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshallerStateful<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshallerStateful<,>))] in List<int> values, int numValues);

Expand DownExpand Up@@ -369,6 +375,30 @@ public void NonBlittableElementCollection_GuaranteedUnmarshal()
Assert.True(NativeExportsNE.Collections.Stateful.ListGuaranteedUnmarshal<BoolStruct, BoolStructMarshaller.BoolStructNative>.Marshaller.ToManagedFinallyCalled);
}

[Fact]
public void ElementsFreed()
{
List<IntWrapper> list = new List<IntWrapper>
{
new IntWrapper { i = 1 },
new IntWrapper { i = 10 },
new IntWrapper { i = 24 },
new IntWrapper { i = 30 },
};

int startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateless.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);

startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateful.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);
}

private static List<BoolStruct> GetBoolStructsToAnd(bool result) => new List<BoolStruct>
{
new BoolStruct
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,31 @@ public static void Free(int* unmanaged)
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(IntWrapperMarshallerWithFreeCounts))]
public static unsafe class IntWrapperMarshallerWithFreeCounts
{
[ThreadStatic]
public static int NumCallsToFree = 0;

public static int* ConvertToUnmanaged(IntWrapper managed)
{
int* ret = (int*)Marshal.AllocCoTaskMem(sizeof(int));
*ret = managed.i;
return ret;
}

public static IntWrapper ConvertToManaged(int* unmanaged)
{
return new IntWrapper { i = *unmanaged };
}

public static void Free(int* unmanaged)
{
NumCallsToFree++;
Marshal.FreeCoTaskMem((IntPtr)unmanaged);
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
Expand DownExpand Up@@ -477,14 +502,14 @@ public void FromManaged(List<T> managed, Span<TUnmanagedElement> buffer)

_list = managed;
// Always allocate at least one byte when the list is zero-length.
int spaceToAllocate = Math.Max(managed.Count * sizeof(TUnmanagedElement), 1);
if (spaceToAllocate <= buffer.Length)
int countToAllocate = Math.Max(managed.Count, 1);
if (countToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
_span = buffer[0..countToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_allocatedMemory = Marshal.AllocCoTaskMem(countToAllocate * sizeof(TUnmanagedElement));
_span = new Span<TUnmanagedElement>((void*)_allocatedMemory, managed.Count);
}
}
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,33 @@ protected StatementSyntax GenerateByValueOutUnmarshalStatement(TypePositionInfo
StubCodeContext.Stage.Unmarshal));
}

protected StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context);
StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)),
IdentifierName("Length")),
StubCodeContext.Stage.Cleanup);

if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement))
{
return EmptyStatement();
}

return Block(
LocalDeclarationStatement(VariableDeclaration(
GenericName(
Identifier(TypeNames.System_Span),
TypeArgumentList(SingletonSeparatedList(_unmanagedElementType))),
SingletonSeparatedList(
VariableDeclarator(
Identifier(nativeSpanIdentifier))
.WithInitializer(EqualsValueClause(
GetUnmanagedValuesDestination(info, context)))))),
contentsCleanupStatements);
}

protected StatementSyntax GenerateContentsMarshallingStatement(
TypePositionInfo info,
StubCodeContext context,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,5 +300,37 @@ public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInf
}
}
}

public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubCodeContext context)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))))));
}
else
{
// Assign out params to default
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword))));
}
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -447,7 +447,27 @@ public StatefulLinearCollectionNonBlittableElementsMarshalling(
}

public TypeSyntax AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}

if (!_shape.HasFlag(MarshallerShape.Free))
yield break;

Comment thread
jkoritzinsky marked this conversation as resolved.
string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context);
// <marshaller>.Free();
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(marshaller),
IdentifierName(ShapeMemberNames.Free)),
ArgumentList()));
}
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);

public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,10 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller,

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context))
{
yield return statement;
}
// <marshallerType>.Free(<nativeIdentifier>);
yield return ExpressionStatement(
InvocationExpression(
Expand DownExpand Up@@ -372,11 +376,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i
public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand DownExpand Up@@ -512,7 +524,15 @@ public StatelessLinearCollectionNonBlittableElementsMarshalling(

public TypeSyntax AsNativeType(TypePositionInfo info) => _nativeTypeSyntax;

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}
}

public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
{
Expand DownExpand Up@@ -588,11 +608,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i

public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,34 +29,7 @@ public static VariableDeclarations GenerateDeclarationsForManagedToNative(BoundG

if (info.RefKind == RefKind.Out)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
initializations.Add(ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))));
}
else
{
// Assign out params to default
initializations.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword)))));
}
initializations.Add(MarshallerHelpers.SkipInitOrDefaultInit(info, context));
}

// Declare variables for parameters
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,9 @@ public partial class Stateless
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumWithBuffer([MarshalUsing(typeof(ListMarshallerWithBuffer<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshaller<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial int DoubleValues([MarshalUsing(typeof(ListMarshallerWithPinning<,>))] List<BlittableIntWrapper> values, int length);

Expand DownExpand Up@@ -99,6 +102,9 @@ public partial class Stateful
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshallerStateful<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshallerStateful<,>))] in List<int> values, int numValues);

Expand DownExpand Up@@ -369,6 +375,30 @@ public void NonBlittableElementCollection_GuaranteedUnmarshal()
Assert.True(NativeExportsNE.Collections.Stateful.ListGuaranteedUnmarshal<BoolStruct, BoolStructMarshaller.BoolStructNative>.Marshaller.ToManagedFinallyCalled);
}

[Fact]
public void ElementsFreed()
{
List<IntWrapper> list = new List<IntWrapper>
{
new IntWrapper { i = 1 },
new IntWrapper { i = 10 },
new IntWrapper { i = 24 },
new IntWrapper { i = 30 },
};

int startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateless.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);

startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateful.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);
}

private static List<BoolStruct> GetBoolStructsToAnd(bool result) => new List<BoolStruct>
{
new BoolStruct
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,31 @@ public static void Free(int* unmanaged)
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(IntWrapperMarshallerWithFreeCounts))]
public static unsafe class IntWrapperMarshallerWithFreeCounts
{
[ThreadStatic]
public static int NumCallsToFree = 0;

public static int* ConvertToUnmanaged(IntWrapper managed)
{
int* ret = (int*)Marshal.AllocCoTaskMem(sizeof(int));
*ret = managed.i;
return ret;
}

public static IntWrapper ConvertToManaged(int* unmanaged)
{
return new IntWrapper { i = *unmanaged };
}

public static void Free(int* unmanaged)
{
NumCallsToFree++;
Marshal.FreeCoTaskMem((IntPtr)unmanaged);
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
Expand DownExpand Up@@ -477,14 +502,14 @@ public void FromManaged(List<T> managed, Span<TUnmanagedElement> buffer)

_list = managed;
// Always allocate at least one byte when the list is zero-length.
int spaceToAllocate = Math.Max(managed.Count * sizeof(TUnmanagedElement), 1);
if (spaceToAllocate <= buffer.Length)
int countToAllocate = Math.Max(managed.Count, 1);
if (countToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
_span = buffer[0..countToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_allocatedMemory = Marshal.AllocCoTaskMem(countToAllocate * sizeof(TUnmanagedElement));
_span = new Span<TUnmanagedElement>((void*)_allocatedMemory, managed.Count);
}
}
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,33 @@ protected StatementSyntax GenerateByValueOutUnmarshalStatement(TypePositionInfo
StubCodeContext.Stage.Unmarshal));
}

protected StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context);
StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)),
IdentifierName("Length")),
StubCodeContext.Stage.Cleanup);

if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement))
{
return EmptyStatement();
}

return Block(
LocalDeclarationStatement(VariableDeclaration(
GenericName(
Identifier(TypeNames.System_Span),
TypeArgumentList(SingletonSeparatedList(_unmanagedElementType))),
SingletonSeparatedList(
VariableDeclarator(
Identifier(nativeSpanIdentifier))
.WithInitializer(EqualsValueClause(
GetUnmanagedValuesDestination(info, context)))))),
contentsCleanupStatements);
}

protected StatementSyntax GenerateContentsMarshallingStatement(
TypePositionInfo info,
StubCodeContext context,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,5 +300,37 @@ public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInf
}
}
}

public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubCodeContext context)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))))));
}
else
{
// Assign out params to default
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword))));
}
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -447,7 +447,27 @@ public StatefulLinearCollectionNonBlittableElementsMarshalling(
}

public TypeSyntax AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}

if (!_shape.HasFlag(MarshallerShape.Free))
yield break;

Comment thread
jkoritzinsky marked this conversation as resolved.
string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context);
// <marshaller>.Free();
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(marshaller),
IdentifierName(ShapeMemberNames.Free)),
ArgumentList()));
}
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);

public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,10 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller,

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context))
{
yield return statement;
}
// <marshallerType>.Free(<nativeIdentifier>);
yield return ExpressionStatement(
InvocationExpression(
Expand DownExpand Up@@ -372,11 +376,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i
public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand DownExpand Up@@ -512,7 +524,15 @@ public StatelessLinearCollectionNonBlittableElementsMarshalling(

public TypeSyntax AsNativeType(TypePositionInfo info) => _nativeTypeSyntax;

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}
}

public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
{
Expand DownExpand Up@@ -588,11 +608,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i

public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,34 +29,7 @@ public static VariableDeclarations GenerateDeclarationsForManagedToNative(BoundG

if (info.RefKind == RefKind.Out)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
initializations.Add(ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))));
}
else
{
// Assign out params to default
initializations.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword)))));
}
initializations.Add(MarshallerHelpers.SkipInitOrDefaultInit(info, context));
}

// Declare variables for parameters
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,9 @@ public partial class Stateless
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumWithBuffer([MarshalUsing(typeof(ListMarshallerWithBuffer<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshaller<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial int DoubleValues([MarshalUsing(typeof(ListMarshallerWithPinning<,>))] List<BlittableIntWrapper> values, int length);

Expand DownExpand Up@@ -99,6 +102,9 @@ public partial class Stateful
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshallerStateful<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshallerStateful<,>))] in List<int> values, int numValues);

Expand DownExpand Up@@ -369,6 +375,30 @@ public void NonBlittableElementCollection_GuaranteedUnmarshal()
Assert.True(NativeExportsNE.Collections.Stateful.ListGuaranteedUnmarshal<BoolStruct, BoolStructMarshaller.BoolStructNative>.Marshaller.ToManagedFinallyCalled);
}

[Fact]
public void ElementsFreed()
{
List<IntWrapper> list = new List<IntWrapper>
{
new IntWrapper { i = 1 },
new IntWrapper { i = 10 },
new IntWrapper { i = 24 },
new IntWrapper { i = 30 },
};

int startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateless.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);

startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateful.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);
}

private static List<BoolStruct> GetBoolStructsToAnd(bool result) => new List<BoolStruct>
{
new BoolStruct
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,31 @@ public static void Free(int* unmanaged)
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(IntWrapperMarshallerWithFreeCounts))]
public static unsafe class IntWrapperMarshallerWithFreeCounts
{
[ThreadStatic]
public static int NumCallsToFree = 0;

public static int* ConvertToUnmanaged(IntWrapper managed)
{
int* ret = (int*)Marshal.AllocCoTaskMem(sizeof(int));
*ret = managed.i;
return ret;
}

public static IntWrapper ConvertToManaged(int* unmanaged)
{
return new IntWrapper { i = *unmanaged };
}

public static void Free(int* unmanaged)
{
NumCallsToFree++;
Marshal.FreeCoTaskMem((IntPtr)unmanaged);
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
Expand DownExpand Up@@ -477,14 +502,14 @@ public void FromManaged(List<T> managed, Span<TUnmanagedElement> buffer)

_list = managed;
// Always allocate at least one byte when the list is zero-length.
int spaceToAllocate = Math.Max(managed.Count * sizeof(TUnmanagedElement), 1);
if (spaceToAllocate <= buffer.Length)
int countToAllocate = Math.Max(managed.Count, 1);
if (countToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
_span = buffer[0..countToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_allocatedMemory = Marshal.AllocCoTaskMem(countToAllocate * sizeof(TUnmanagedElement));
_span = new Span<TUnmanagedElement>((void*)_allocatedMemory, managed.Count);
}
}
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,33 @@ protected StatementSyntax GenerateByValueOutUnmarshalStatement(TypePositionInfo
StubCodeContext.Stage.Unmarshal));
}

protected StatementSyntax GenerateElementCleanupStatement(TypePositionInfo info, StubCodeContext context)
{
string nativeSpanIdentifier = MarshallerHelpers.GetNativeSpanIdentifier(info, context);
StatementSyntax contentsCleanupStatements = GenerateContentsMarshallingStatement(info, context,
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(MarshallerHelpers.GetNativeSpanIdentifier(info, context)),
IdentifierName("Length")),
StubCodeContext.Stage.Cleanup);

if (contentsCleanupStatements.IsKind(SyntaxKind.EmptyStatement))
{
return EmptyStatement();
}

return Block(
LocalDeclarationStatement(VariableDeclaration(
GenericName(
Identifier(TypeNames.System_Span),
TypeArgumentList(SingletonSeparatedList(_unmanagedElementType))),
SingletonSeparatedList(
VariableDeclarator(
Identifier(nativeSpanIdentifier))
.WithInitializer(EqualsValueClause(
GetUnmanagedValuesDestination(info, context)))))),
contentsCleanupStatements);
}

protected StatementSyntax GenerateContentsMarshallingStatement(
TypePositionInfo info,
StubCodeContext context,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,5 +300,37 @@ public static IEnumerable<TypePositionInfo> GetDependentElementsOfMarshallingInf
}
}
}

public static StatementSyntax SkipInitOrDefaultInit(TypePositionInfo info, StubCodeContext context)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword))))));
}
else
{
// Assign out params to default
return ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword))));
}
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -447,7 +447,27 @@ public StatefulLinearCollectionNonBlittableElementsMarshalling(
}

public TypeSyntax AsNativeType(TypePositionInfo info) => _innerMarshaller.AsNativeType(info);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateCleanupStatements(info, context);
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}

if (!_shape.HasFlag(MarshallerShape.Free))
yield break;

Comment thread
jkoritzinsky marked this conversation as resolved.
string marshaller = StatefulValueMarshalling.GetMarshallerIdentifier(info, context);
// <marshaller>.Free();
yield return ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(marshaller),
IdentifierName(ShapeMemberNames.Free)),
ArgumentList()));
}
public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context) => _innerMarshaller.GenerateGuaranteedUnmarshalStatements(info, context);

public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo info, StubCodeContext context)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,6 +251,10 @@ public StatelessFreeMarshalling(ICustomTypeMarshallingStrategy innerMarshaller,

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
foreach (StatementSyntax statement in _innerMarshaller.GenerateCleanupStatements(info, context))
{
yield return statement;
}
// <marshallerType>.Free(<nativeIdentifier>);
yield return ExpressionStatement(
InvocationExpression(
Expand DownExpand Up@@ -372,11 +376,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i
public IEnumerable<StatementSyntax> GeneratePinStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand DownExpand Up@@ -512,7 +524,15 @@ public StatelessLinearCollectionNonBlittableElementsMarshalling(

public TypeSyntax AsNativeType(TypePositionInfo info) => _nativeTypeSyntax;

public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
public IEnumerable<StatementSyntax> GenerateCleanupStatements(TypePositionInfo info, StubCodeContext context)
{
StatementSyntax elementCleanup = GenerateElementCleanupStatement(info, context);

if (!elementCleanup.IsKind(SyntaxKind.EmptyStatement))
{
yield return elementCleanup;
}
}

public IEnumerable<StatementSyntax> GenerateGuaranteedUnmarshalStatements(TypePositionInfo info, StubCodeContext context)
{
Expand DownExpand Up@@ -588,11 +608,19 @@ public IEnumerable<StatementSyntax> GenerateMarshalStatements(TypePositionInfo i

public IEnumerable<StatementSyntax> GenerateSetupStatements(TypePositionInfo info, StubCodeContext context)
{
string numElementsIdentifier = MarshallerHelpers.GetNumElementsIdentifier(info, context);
yield return LocalDeclarationStatement(
VariableDeclaration(
PredefinedType(Token(SyntaxKind.IntKeyword)),
SingletonSeparatedList(
VariableDeclarator(MarshallerHelpers.GetNumElementsIdentifier(info, context)))));
VariableDeclarator(numElementsIdentifier))));
// Use the numElements local to ensure the compiler doesn't give errors for using an uninitialized variable.
// The value will never be used unless it has been initialized, so this is safe.
yield return MarshallerHelpers.SkipInitOrDefaultInit(
new TypePositionInfo(SpecialTypeInfo.Int32, NoMarshallingInfo.Instance)
{
InstanceIdentifier = numElementsIdentifier
}, context);
}

public IEnumerable<StatementSyntax> GenerateUnmarshalCaptureStatements(TypePositionInfo info, StubCodeContext context) => Array.Empty<StatementSyntax>();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,34 +29,7 @@ public static VariableDeclarations GenerateDeclarationsForManagedToNative(BoundG

if (info.RefKind == RefKind.Out)
{
(TargetFramework fmk, _) = context.GetTargetFramework();
if (info.ManagedType is not PointerTypeInfo
&& info.ManagedType is not ValueTypeInfo { IsByRefLike: true }
&& fmk is TargetFramework.Net)
{
// Use the Unsafe.SkipInit<T> API when available and
// managed type is usable as a generic parameter.
initializations.Add(ExpressionStatement(
InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
ParseName(TypeNames.System_Runtime_CompilerServices_Unsafe),
IdentifierName("SkipInit")))
.WithArgumentList(
ArgumentList(SingletonSeparatedList(
Argument(IdentifierName(info.InstanceIdentifier))
.WithRefOrOutKeyword(Token(SyntaxKind.OutKeyword)))))));
}
else
{
// Assign out params to default
initializations.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(info.InstanceIdentifier),
LiteralExpression(
SyntaxKind.DefaultLiteralExpression,
Token(SyntaxKind.DefaultKeyword)))));
}
initializations.Add(MarshallerHelpers.SkipInitOrDefaultInit(info, context));
}

// Declare variables for parameters
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,9 @@ public partial class Stateless
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int SumWithBuffer([MarshalUsing(typeof(ListMarshallerWithBuffer<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshaller<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "double_values")]
public static partial int DoubleValues([MarshalUsing(typeof(ListMarshallerWithPinning<,>))] List<BlittableIntWrapper> values, int length);

Expand DownExpand Up@@ -99,6 +102,9 @@ public partial class Stateful
[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array")]
public static partial int Sum([MarshalUsing(typeof(ListMarshallerStateful<,>))] List<int> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_ptr_array")]
public static unsafe partial int SumWithFreeTracking([MarshalUsing(typeof(ListMarshallerStateful<,>)), MarshalUsing(typeof(IntWrapperMarshallerWithFreeCounts), ElementIndirectionDepth = 1)] List<IntWrapper> values, int numValues);

[LibraryImport(NativeExportsNE_Binary, EntryPoint = "sum_int_array_ref")]
public static partial int SumInArray([MarshalUsing(typeof(ListMarshallerStateful<,>))] in List<int> values, int numValues);

Expand DownExpand Up@@ -369,6 +375,30 @@ public void NonBlittableElementCollection_GuaranteedUnmarshal()
Assert.True(NativeExportsNE.Collections.Stateful.ListGuaranteedUnmarshal<BoolStruct, BoolStructMarshaller.BoolStructNative>.Marshaller.ToManagedFinallyCalled);
}

[Fact]
public void ElementsFreed()
{
List<IntWrapper> list = new List<IntWrapper>
{
new IntWrapper { i = 1 },
new IntWrapper { i = 10 },
new IntWrapper { i = 24 },
new IntWrapper { i = 30 },
};

int startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateless.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);

startingCount = IntWrapperMarshallerWithFreeCounts.NumCallsToFree;

NativeExportsNE.Collections.Stateful.SumWithFreeTracking(list, list.Count);

Assert.Equal(startingCount + list.Count, IntWrapperMarshallerWithFreeCounts.NumCallsToFree);
}

private static List<BoolStruct> GetBoolStructsToAnd(bool result) => new List<BoolStruct>
{
new BoolStruct
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,6 +196,31 @@ public static void Free(int* unmanaged)
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(IntWrapperMarshallerWithFreeCounts))]
public static unsafe class IntWrapperMarshallerWithFreeCounts
{
[ThreadStatic]
public static int NumCallsToFree = 0;

public static int* ConvertToUnmanaged(IntWrapper managed)
{
int* ret = (int*)Marshal.AllocCoTaskMem(sizeof(int));
*ret = managed.i;
return ret;
}

public static IntWrapper ConvertToManaged(int* unmanaged)
{
return new IntWrapper { i = *unmanaged };
}

public static void Free(int* unmanaged)
{
NumCallsToFree++;
Marshal.FreeCoTaskMem((IntPtr)unmanaged);
}
}

[CustomMarshaller(typeof(IntWrapper), MarshalMode.Default, typeof(Marshaller))]
public static unsafe class IntWrapperMarshallerStateful
{
Expand DownExpand Up@@ -477,14 +502,14 @@ public void FromManaged(List<T> managed, Span<TUnmanagedElement> buffer)

_list = managed;
// Always allocate at least one byte when the list is zero-length.
int spaceToAllocate = Math.Max(managed.Count * sizeof(TUnmanagedElement), 1);
if (spaceToAllocate <= buffer.Length)
int countToAllocate = Math.Max(managed.Count, 1);
if (countToAllocate <= buffer.Length)
{
_span = buffer[0..spaceToAllocate];
_span = buffer[0..countToAllocate];
}
else
{
_allocatedMemory = Marshal.AllocCoTaskMem(spaceToAllocate);
_allocatedMemory = Marshal.AllocCoTaskMem(countToAllocate * sizeof(TUnmanagedElement));
_span = new Span<TUnmanagedElement>((void*)_allocatedMemory, managed.Count);
}
}
Expand Down