Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert HELPER_METHOD_FRAME to QCalls (4/N) #95670

Merged
merged 11 commits into from
Dec 8, 2023
136 changes: 78 additions & 58 deletions src/coreclr/System.Private.CoreLib/src/System/ArgIterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,128 +6,148 @@

namespace System
{
// This class will not be marked serializable
// Note: This type must have the same layout as the CLR's VARARGS type in CLRVarArgs.h.
// It also contains an inline SigPointer data structure - must keep those fields in sync.
[StructLayout(LayoutKind.Sequential)]
public ref struct ArgIterator
public unsafe ref partial struct ArgIterator
{
private IntPtr ArgCookie; // Cookie from the EE.
private IntPtr _argCookie; // Cookie from the EE.

// The SigPointer structure consists of the following members. (Note: this is an inline native SigPointer data type)
private IntPtr sigPtr; // Pointer to remaining signature.
private IntPtr sigPtrLen; // Remaining length of the pointer
// Note: this is an inline native SigPointer data type.
private IntPtr _sigPtr; // Pointer to remaining signature.

// Note, sigPtrLen is actually a DWORD, but on 64bit systems this structure becomes
// Note, this is actually a UInt32, but on 64bit systems SigPointer structure becomes
jkotas marked this conversation as resolved.
Show resolved Hide resolved
// 8-byte aligned, which requires us to pad it.
private IntPtr _sigPtrLenAndPadding_DoNotUse; // Remaining length of the pointer

private IntPtr ArgPtr; // Pointer to remaining args.
private int RemainingArgs; // # of remaining args.
private IntPtr _argPtr; // Pointer to remaining args.
private int _remainingArgs; // # of remaining args.

#if (TARGET_WINDOWS && !TARGET_ARM) // Native Varargs are not supported on Unix (all architectures) and Windows ARM
[MethodImpl(MethodImplOptions.InternalCall)]
private extern ArgIterator(IntPtr arglist);
#if TARGET_WINDOWS // Native Varargs are not supported on Unix
// ArgIterator is a ref struct. It does not require pinning.
// This method null checks the this pointer as a side-effect.
private ArgIterator* ThisPtr => (ArgIterator*)Unsafe.AsPointer(ref _argCookie);

// create an arg iterator that points at the first argument that
// is not statically declared (that is the first ... arg)
// 'arglist' is the value returned by the ARGLIST instruction
public ArgIterator(RuntimeArgumentHandle arglist) : this(arglist.Value)
public ArgIterator(RuntimeArgumentHandle arglist)
{
IntPtr cookie = arglist.Value;
if (cookie == 0)
throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized);
Init(ThisPtr, cookie);
}

[MethodImpl(MethodImplOptions.InternalCall)]
private extern unsafe ArgIterator(IntPtr arglist, void* ptr);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_Init")]
private static partial void Init(ArgIterator* thisPtr, IntPtr cookie);

// create an arg iterator that points just past 'firstArg'.
// 'arglist' is the value returned by the ARGLIST instruction
// This is much like the C va_start macro

[CLSCompliant(false)]
public unsafe ArgIterator(RuntimeArgumentHandle arglist, void* ptr) : this(arglist.Value, ptr)
public ArgIterator(RuntimeArgumentHandle arglist, void* ptr)
{
IntPtr cookie = arglist.Value;
if (cookie == 0)
throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized);
Init(ThisPtr, cookie, ptr);
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_Init2")]
private static partial void Init(ArgIterator* thisPtr, IntPtr cookie, void* ptr);

#pragma warning disable CS8500 // Takes a pointer to a managed type
// Fetch an argument as a typed referece, advance the iterator.
// Throws an exception if past end of argument list
[CLSCompliant(false)]
public TypedReference GetNextArg()
{
TypedReference result = default;
// reference to TypedReference is banned, so have to pass result as pointer
unsafe
if (_argCookie == 0)
{
#pragma warning disable CS8500 // Takes a pointer to a managed type
FCallGetNextArg(&result);
#pragma warning restore CS8500
// This ArgIterator was created by marshaling from an unmanaged va_list -
// can't do this operation
ThrowHelper.ThrowNotSupportedException();
}

// Make sure there are remaining args.
if (_remainingArgs == 0)
{
throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
}

TypedReference result = default;
GetNextArg(ThisPtr, &result);
return result;
}

[MethodImpl(MethodImplOptions.InternalCall)]
// reference to TypedReference is banned, so have to pass result as void pointer
private extern unsafe void FCallGetNextArg(void* result);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_GetNextArg")]
private static partial void GetNextArg(ArgIterator* thisPtr, TypedReference* pResult);

// Alternate version of GetNextArg() intended primarily for IJW code
// generated by VC's "va_arg()" construct.
[CLSCompliant(false)]
public TypedReference GetNextArg(RuntimeTypeHandle rth)
{
if (sigPtr != IntPtr.Zero)
if (_sigPtr != IntPtr.Zero)
{
// This is an ordinary ArgIterator capable of determining
// types from a signature. Just do a regular GetNextArg.
return GetNextArg();
}
else

// Prevent abuse of this API with a default ArgIterator (it
// doesn't require permission to create a zero-inited value
// type). Check that _argPtr isn't zero or this API will allow a
// malicious caller to increment the pointer to an arbitrary
// location in memory and read the contents.
if (_argPtr == IntPtr.Zero)
{
// Prevent abuse of this API with a default ArgIterator (it
// doesn't require permission to create a zero-inited value
// type). Check that ArgPtr isn't zero or this API will allow a
// malicious caller to increment the pointer to an arbitrary
// location in memory and read the contents.
if (ArgPtr == IntPtr.Zero)
#pragma warning disable CA2208 // Instantiate argument exceptions correctly, the argument not applicable
throw new ArgumentNullException();
#pragma warning restore CA2208

TypedReference result = default;
// reference to TypedReference is banned, so have to pass result as pointer
unsafe
{
#pragma warning disable CS8500 // Takes a pointer to a managed type
InternalGetNextArg(&result, rth.GetRuntimeType());
#pragma warning restore CS8500
}
return result;
throw new ArgumentNullException(null);
}
}

if (rth.IsNullHandle())
{
throw new ArgumentNullException(nameof(rth), SR.Arg_InvalidHandle);
}

[MethodImpl(MethodImplOptions.InternalCall)]
// reference to TypedReference is banned, so have to pass result as void pointer
private extern unsafe void InternalGetNextArg(void* result, RuntimeType rt);
TypedReference result = default;
GetNextArg(ThisPtr, new QCallTypeHandle(ref rth), &result);
return result;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_GetNextArg2")]
private static partial void GetNextArg(ArgIterator* thisPtr, QCallTypeHandle rth, TypedReference* pResult);

// This method should invalidate the iterator (va_end). It is not supported yet.
public void End()
{
}

// How many arguments are left in the list
[MethodImpl(MethodImplOptions.InternalCall)]
public extern int GetRemainingCount();
public int GetRemainingCount()
{
if (_argCookie == 0)
{
// This ArgIterator was created by marshaling from an unmanaged va_list -
// can't do this operation
ThrowHelper.ThrowNotSupportedException();
}
return _remainingArgs;
}

// Gets the type of the current arg, does NOT advance the iterator
[MethodImpl(MethodImplOptions.InternalCall)]
private extern unsafe void* _GetNextArgType();

public unsafe RuntimeTypeHandle GetNextArgType()
{
return new RuntimeTypeHandle(Type.GetTypeFromHandleUnsafe((IntPtr)_GetNextArgType()));
return RuntimeTypeHandle.FromIntPtr(GetNextArgType(ThisPtr));
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_GetNextArgType")]
private static partial IntPtr GetNextArgType(ArgIterator* thisPtr);

public override int GetHashCode()
{
return HashCode.Combine(ArgCookie);
return HashCode.Combine(_argCookie);
}

// Inherited from object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDel
if (pMT->HasInstantiation)
throw new ArgumentException(SR.Argument_NeedNonGenericObject, nameof(structure));

delegate*<ref byte, byte*, int, ref CleanupWorkListElement, void> structMarshalStub;
delegate*<ref byte, byte*, int, ref CleanupWorkListElement?, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub((IntPtr)pMT, &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structure));
Expand All @@ -257,10 +257,10 @@ public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDel
{
if (fDeleteOld)
{
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement>());
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement?>());
}

structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Marshal, ref Unsafe.NullRef<CleanupWorkListElement>());
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Marshal, ref Unsafe.NullRef<CleanupWorkListElement?>());
}
else
{
Expand All @@ -278,14 +278,14 @@ private static unsafe void PtrToStructureHelper(IntPtr ptr, object structure, bo
if (!allowValueClasses && pMT->IsValueType)
throw new ArgumentException(SR.Argument_StructMustNotBeValueClass, nameof(structure));

delegate*<ref byte, byte*, int, ref CleanupWorkListElement, void> structMarshalStub;
delegate*<ref byte, byte*, int, ref CleanupWorkListElement?, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub((IntPtr)pMT, &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structure));

if (structMarshalStub != null)
{
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Unmarshal, ref Unsafe.NullRef<CleanupWorkListElement>());
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Unmarshal, ref Unsafe.NullRef<CleanupWorkListElement?>());
}
else
{
Expand All @@ -310,7 +310,7 @@ public static unsafe void DestroyStructure(IntPtr ptr, Type structuretype)
if (rt.IsGenericType)
throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(structuretype));

delegate*<ref byte, byte*, int, ref CleanupWorkListElement, void> structMarshalStub;
delegate*<ref byte, byte*, int, ref CleanupWorkListElement?, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub(rt.GetUnderlyingNativeHandle(), &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structuretype));
Expand All @@ -319,13 +319,13 @@ public static unsafe void DestroyStructure(IntPtr ptr, Type structuretype)

if (structMarshalStub != null)
{
structMarshalStub(ref Unsafe.NullRef<byte>(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement>());
structMarshalStub(ref Unsafe.NullRef<byte>(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement?>());
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_TryGetStructMarshalStub")]
[return: MarshalAs(UnmanagedType.Bool)]
private static unsafe partial bool TryGetStructMarshalStub(IntPtr th, delegate*<ref byte, byte*, int, ref CleanupWorkListElement, void>* structMarshalStub, nuint* size);
internal static unsafe partial bool TryGetStructMarshalStub(IntPtr th, delegate*<ref byte, byte*, int, ref CleanupWorkListElement?, void>* structMarshalStub, nuint* size);

// Note: Callers are required to keep obj alive
internal static unsafe bool IsPinnable(object? obj)
Expand Down Expand Up @@ -959,11 +959,23 @@ public static void ChangeWrapperHandleStrength(object otp, bool fIsWeak)
private static partial void ChangeWrapperHandleStrength(ObjectHandleOnStack otp, [MarshalAs(UnmanagedType.Bool)] bool fIsWeak);
#endif // FEATURE_COMINTEROP

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Delegate GetDelegateForFunctionPointerInternal(IntPtr ptr, Type t);
internal static Delegate GetDelegateForFunctionPointerInternal(IntPtr ptr, RuntimeType t)
{
Delegate? retDelegate = null;
GetDelegateForFunctionPointerInternal(ptr, new QCallTypeHandle(ref t), ObjectHandleOnStack.Create(ref retDelegate));
return retDelegate!;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr GetFunctionPointerForDelegateInternal(Delegate d);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetDelegateForFunctionPointerInternal")]
private static partial void GetDelegateForFunctionPointerInternal(IntPtr ptr, QCallTypeHandle t, ObjectHandleOnStack retDelegate);

internal static IntPtr GetFunctionPointerForDelegateInternal(Delegate d)
{
return GetFunctionPointerForDelegateInternal(ObjectHandleOnStack.Create(ref d));
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetFunctionPointerForDelegateInternal")]
private static partial IntPtr GetFunctionPointerForDelegateInternal(ObjectHandleOnStack d);

#if DEBUG // Used for testing in Checked or Debug
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetIsInCooperativeGCModeFunctionPointer")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ internal RuntimeTypeHandle(RuntimeType type)
m_type = type;
}

internal bool IsNullHandle()
{
return m_type == null;
}

internal static bool IsTypeDefinition(RuntimeType type)
{
CorElementType corElemType = GetCorElementType(type);
Expand Down
26 changes: 11 additions & 15 deletions src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Text;

Expand All @@ -11,30 +12,25 @@ public partial class String
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string FastAllocateString(int length);

// Set extra byte for odd-sized strings that came from interop as BSTR.
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern void SetTrailByte(byte data);
// Try to retrieve the extra byte - returns false if not present.
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern bool TryGetTrailByte(out byte data);

[MethodImpl(MethodImplOptions.InternalCall)]
private extern string Intern();
[MethodImpl(MethodImplOptions.InternalCall)]
private extern string? IsInterned();
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "String_Intern")]
private static partial void Intern(StringHandleOnStack src);

public static string Intern(string str)
{
ArgumentNullException.ThrowIfNull(str);

return str.Intern();
Intern(new StringHandleOnStack(ref str!));
jkotas marked this conversation as resolved.
Show resolved Hide resolved
return str!;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "String_IsInterned")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial void IsInterned(StringHandleOnStack src);

public static string? IsInterned(string str)
{
ArgumentNullException.ThrowIfNull(str);

return str.IsInterned();
Intern(new StringHandleOnStack(ref str!));
return str;
}

// Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes.
Expand Down
Loading
Loading