Skip to content

Commit 6dc1087

Browse files
authored
Convert HELPER_METHOD_FRAME to QCalls (4/N) (#95670)
* StubHelpers.FmtClassUpdateNativeInternal, FmtClassUpdateCLRInternal, LayoutDestroyNativeInternal * Delegate.GetDelegateForFunctionPointerInternal, GetFunctionPointerForDelegateInternal * String.Intern, IsInterned * StubHelpers.ThrowInteropParamException * SetStringTrailByte * Delete dead code * ArgIterator.Init, Init2, GetNextArgType, GetNextArg, GetNextArg2, GetRemainingCount * FB
1 parent b79f27c commit 6dc1087

File tree

27 files changed

+416
-624
lines changed

27 files changed

+416
-624
lines changed

src/coreclr/System.Private.CoreLib/src/System/ArgIterator.cs

+81-61
Original file line numberDiff line numberDiff line change
@@ -6,128 +6,148 @@
66

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

17-
// The SigPointer structure consists of the following members. (Note: this is an inline native SigPointer data type)
18-
private IntPtr sigPtr; // Pointer to remaining signature.
19-
private IntPtr sigPtrLen; // Remaining length of the pointer
20-
21-
// Note, sigPtrLen is actually a DWORD, but on 64bit systems this structure becomes
22-
// 8-byte aligned, which requires us to pad it.
15+
[StructLayout(LayoutKind.Sequential)]
16+
private struct SigPointer
17+
{
18+
internal IntPtr _ptr;
19+
internal uint _len;
20+
}
21+
private SigPointer _sigPtr; // Pointer to remaining signature.
2322

24-
private IntPtr ArgPtr; // Pointer to remaining args.
25-
private int RemainingArgs; // # of remaining args.
23+
private IntPtr _argPtr; // Pointer to remaining args.
24+
private int _remainingArgs; // # of remaining args.
2625

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

3131
// create an arg iterator that points at the first argument that
3232
// is not statically declared (that is the first ... arg)
3333
// 'arglist' is the value returned by the ARGLIST instruction
34-
public ArgIterator(RuntimeArgumentHandle arglist) : this(arglist.Value)
34+
public ArgIterator(RuntimeArgumentHandle arglist)
3535
{
36+
IntPtr cookie = arglist.Value;
37+
if (cookie == 0)
38+
throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized);
39+
Init(ThisPtr, cookie);
3640
}
3741

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

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

4549
[CLSCompliant(false)]
46-
public unsafe ArgIterator(RuntimeArgumentHandle arglist, void* ptr) : this(arglist.Value, ptr)
50+
public ArgIterator(RuntimeArgumentHandle arglist, void* ptr)
4751
{
52+
IntPtr cookie = arglist.Value;
53+
if (cookie == 0)
54+
throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized);
55+
Init(ThisPtr, cookie, ptr);
4856
}
4957

58+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_Init2")]
59+
private static partial void Init(ArgIterator* thisPtr, IntPtr cookie, void* ptr);
60+
61+
#pragma warning disable CS8500 // Takes a pointer to a managed type
5062
// Fetch an argument as a typed referece, advance the iterator.
5163
// Throws an exception if past end of argument list
5264
[CLSCompliant(false)]
5365
public TypedReference GetNextArg()
5466
{
55-
TypedReference result = default;
56-
// reference to TypedReference is banned, so have to pass result as pointer
57-
unsafe
67+
if (_argCookie == 0)
5868
{
59-
#pragma warning disable CS8500 // Takes a pointer to a managed type
60-
FCallGetNextArg(&result);
61-
#pragma warning restore CS8500
69+
// This ArgIterator was created by marshaling from an unmanaged va_list -
70+
// can't do this operation
71+
ThrowHelper.ThrowNotSupportedException();
6272
}
73+
74+
// Make sure there are remaining args.
75+
if (_remainingArgs == 0)
76+
{
77+
throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
78+
}
79+
80+
TypedReference result = default;
81+
GetNextArg(ThisPtr, &result);
6382
return result;
6483
}
6584

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

7088
// Alternate version of GetNextArg() intended primarily for IJW code
7189
// generated by VC's "va_arg()" construct.
7290
[CLSCompliant(false)]
7391
public TypedReference GetNextArg(RuntimeTypeHandle rth)
7492
{
75-
if (sigPtr != IntPtr.Zero)
93+
if (_sigPtr._ptr != IntPtr.Zero)
7694
{
7795
// This is an ordinary ArgIterator capable of determining
7896
// types from a signature. Just do a regular GetNextArg.
7997
return GetNextArg();
8098
}
81-
else
99+
100+
// Prevent abuse of this API with a default ArgIterator (it
101+
// doesn't require permission to create a zero-inited value
102+
// type). Check that _argPtr isn't zero or this API will allow a
103+
// malicious caller to increment the pointer to an arbitrary
104+
// location in memory and read the contents.
105+
if (_argPtr == IntPtr.Zero)
82106
{
83-
// Prevent abuse of this API with a default ArgIterator (it
84-
// doesn't require permission to create a zero-inited value
85-
// type). Check that ArgPtr isn't zero or this API will allow a
86-
// malicious caller to increment the pointer to an arbitrary
87-
// location in memory and read the contents.
88-
if (ArgPtr == IntPtr.Zero)
89-
#pragma warning disable CA2208 // Instantiate argument exceptions correctly, the argument not applicable
90-
throw new ArgumentNullException();
91-
#pragma warning restore CA2208
92-
93-
TypedReference result = default;
94-
// reference to TypedReference is banned, so have to pass result as pointer
95-
unsafe
96-
{
97-
#pragma warning disable CS8500 // Takes a pointer to a managed type
98-
InternalGetNextArg(&result, rth.GetRuntimeType());
99-
#pragma warning restore CS8500
100-
}
101-
return result;
107+
throw new ArgumentNullException(null);
108+
}
109+
110+
if (rth.IsNullHandle())
111+
{
112+
throw new ArgumentNullException(nameof(rth), SR.Arg_InvalidHandle);
102113
}
103-
}
104114

115+
TypedReference result = default;
116+
GetNextArg(ThisPtr, new QCallTypeHandle(ref rth), &result);
117+
return result;
118+
}
105119

106-
[MethodImpl(MethodImplOptions.InternalCall)]
107-
// reference to TypedReference is banned, so have to pass result as void pointer
108-
private extern unsafe void InternalGetNextArg(void* result, RuntimeType rt);
120+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_GetNextArg2")]
121+
private static partial void GetNextArg(ArgIterator* thisPtr, QCallTypeHandle rth, TypedReference* pResult);
109122

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

115-
// How many arguments are left in the list
116-
[MethodImpl(MethodImplOptions.InternalCall)]
117-
public extern int GetRemainingCount();
128+
public int GetRemainingCount()
129+
{
130+
if (_argCookie == 0)
131+
{
132+
// This ArgIterator was created by marshaling from an unmanaged va_list -
133+
// can't do this operation
134+
ThrowHelper.ThrowNotSupportedException();
135+
}
136+
return _remainingArgs;
137+
}
118138

119139
// Gets the type of the current arg, does NOT advance the iterator
120-
[MethodImpl(MethodImplOptions.InternalCall)]
121-
private extern unsafe void* _GetNextArgType();
122-
123140
public unsafe RuntimeTypeHandle GetNextArgType()
124141
{
125-
return new RuntimeTypeHandle(Type.GetTypeFromHandleUnsafe((IntPtr)_GetNextArgType()));
142+
return RuntimeTypeHandle.FromIntPtr(GetNextArgType(ThisPtr));
126143
}
127144

145+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_GetNextArgType")]
146+
private static partial IntPtr GetNextArgType(ArgIterator* thisPtr);
147+
128148
public override int GetHashCode()
129149
{
130-
return HashCode.Combine(ArgCookie);
150+
return HashCode.Combine(_argCookie);
131151
}
132152

133153
// Inherited from object

src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs

+24-12
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDel
248248
if (pMT->HasInstantiation)
249249
throw new ArgumentException(SR.Argument_NeedNonGenericObject, nameof(structure));
250250

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

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

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

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

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

320320
if (structMarshalStub != null)
321321
{
322-
structMarshalStub(ref Unsafe.NullRef<byte>(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement>());
322+
structMarshalStub(ref Unsafe.NullRef<byte>(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement?>());
323323
}
324324
}
325325

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

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

962-
[MethodImpl(MethodImplOptions.InternalCall)]
963-
internal static extern Delegate GetDelegateForFunctionPointerInternal(IntPtr ptr, Type t);
962+
internal static Delegate GetDelegateForFunctionPointerInternal(IntPtr ptr, RuntimeType t)
963+
{
964+
Delegate? retDelegate = null;
965+
GetDelegateForFunctionPointerInternal(ptr, new QCallTypeHandle(ref t), ObjectHandleOnStack.Create(ref retDelegate));
966+
return retDelegate!;
967+
}
964968

965-
[MethodImpl(MethodImplOptions.InternalCall)]
966-
internal static extern IntPtr GetFunctionPointerForDelegateInternal(Delegate d);
969+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetDelegateForFunctionPointerInternal")]
970+
private static partial void GetDelegateForFunctionPointerInternal(IntPtr ptr, QCallTypeHandle t, ObjectHandleOnStack retDelegate);
971+
972+
internal static IntPtr GetFunctionPointerForDelegateInternal(Delegate d)
973+
{
974+
return GetFunctionPointerForDelegateInternal(ObjectHandleOnStack.Create(ref d));
975+
}
976+
977+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetFunctionPointerForDelegateInternal")]
978+
private static partial IntPtr GetFunctionPointerForDelegateInternal(ObjectHandleOnStack d);
967979

968980
#if DEBUG // Used for testing in Checked or Debug
969981
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetIsInCooperativeGCModeFunctionPointer")]

src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs

+5
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ internal RuntimeTypeHandle(RuntimeType type)
8282
m_type = type;
8383
}
8484

85+
internal bool IsNullHandle()
86+
{
87+
return m_type == null;
88+
}
89+
8590
internal static bool IsTypeDefinition(RuntimeType type)
8691
{
8792
CorElementType corElemType = GetCorElementType(type);

src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.cs

+11-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Runtime.InteropServices;
45
using System.Runtime.CompilerServices;
56
using System.Text;
67

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

14-
// Set extra byte for odd-sized strings that came from interop as BSTR.
15-
[MethodImpl(MethodImplOptions.InternalCall)]
16-
internal extern void SetTrailByte(byte data);
17-
// Try to retrieve the extra byte - returns false if not present.
18-
[MethodImpl(MethodImplOptions.InternalCall)]
19-
internal extern bool TryGetTrailByte(out byte data);
20-
21-
[MethodImpl(MethodImplOptions.InternalCall)]
22-
private extern string Intern();
23-
[MethodImpl(MethodImplOptions.InternalCall)]
24-
private extern string? IsInterned();
15+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "String_Intern")]
16+
private static partial void Intern(StringHandleOnStack src);
2517

2618
public static string Intern(string str)
2719
{
2820
ArgumentNullException.ThrowIfNull(str);
29-
30-
return str.Intern();
21+
Intern(new StringHandleOnStack(ref str!));
22+
return str!;
3123
}
3224

25+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "String_IsInterned")]
26+
[return: MarshalAs(UnmanagedType.Bool)]
27+
private static partial void IsInterned(StringHandleOnStack src);
28+
3329
public static string? IsInterned(string str)
3430
{
3531
ArgumentNullException.ThrowIfNull(str);
36-
37-
return str.IsInterned();
32+
Intern(new StringHandleOnStack(ref str!));
33+
return str;
3834
}
3935

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

0 commit comments

Comments
 (0)