|
6 | 6 |
|
7 | 7 | namespace System
|
8 | 8 | {
|
9 |
| - // This class will not be marked serializable |
10 | 9 | // 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. |
12 | 10 | [StructLayout(LayoutKind.Sequential)]
|
13 |
| - public ref struct ArgIterator |
| 11 | + public unsafe ref partial struct ArgIterator |
14 | 12 | {
|
15 |
| - private IntPtr ArgCookie; // Cookie from the EE. |
| 13 | + private IntPtr _argCookie; // Cookie from the EE. |
16 | 14 |
|
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. |
23 | 22 |
|
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. |
26 | 25 |
|
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); |
30 | 30 |
|
31 | 31 | // create an arg iterator that points at the first argument that
|
32 | 32 | // is not statically declared (that is the first ... arg)
|
33 | 33 | // 'arglist' is the value returned by the ARGLIST instruction
|
34 |
| - public ArgIterator(RuntimeArgumentHandle arglist) : this(arglist.Value) |
| 34 | + public ArgIterator(RuntimeArgumentHandle arglist) |
35 | 35 | {
|
| 36 | + IntPtr cookie = arglist.Value; |
| 37 | + if (cookie == 0) |
| 38 | + throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized); |
| 39 | + Init(ThisPtr, cookie); |
36 | 40 | }
|
37 | 41 |
|
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); |
40 | 44 |
|
41 | 45 | // create an arg iterator that points just past 'firstArg'.
|
42 | 46 | // 'arglist' is the value returned by the ARGLIST instruction
|
43 | 47 | // This is much like the C va_start macro
|
44 | 48 |
|
45 | 49 | [CLSCompliant(false)]
|
46 |
| - public unsafe ArgIterator(RuntimeArgumentHandle arglist, void* ptr) : this(arglist.Value, ptr) |
| 50 | + public ArgIterator(RuntimeArgumentHandle arglist, void* ptr) |
47 | 51 | {
|
| 52 | + IntPtr cookie = arglist.Value; |
| 53 | + if (cookie == 0) |
| 54 | + throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized); |
| 55 | + Init(ThisPtr, cookie, ptr); |
48 | 56 | }
|
49 | 57 |
|
| 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 |
50 | 62 | // Fetch an argument as a typed referece, advance the iterator.
|
51 | 63 | // Throws an exception if past end of argument list
|
52 | 64 | [CLSCompliant(false)]
|
53 | 65 | public TypedReference GetNextArg()
|
54 | 66 | {
|
55 |
| - TypedReference result = default; |
56 |
| - // reference to TypedReference is banned, so have to pass result as pointer |
57 |
| - unsafe |
| 67 | + if (_argCookie == 0) |
58 | 68 | {
|
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(); |
62 | 72 | }
|
| 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); |
63 | 82 | return result;
|
64 | 83 | }
|
65 | 84 |
|
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); |
69 | 87 |
|
70 | 88 | // Alternate version of GetNextArg() intended primarily for IJW code
|
71 | 89 | // generated by VC's "va_arg()" construct.
|
72 | 90 | [CLSCompliant(false)]
|
73 | 91 | public TypedReference GetNextArg(RuntimeTypeHandle rth)
|
74 | 92 | {
|
75 |
| - if (sigPtr != IntPtr.Zero) |
| 93 | + if (_sigPtr._ptr != IntPtr.Zero) |
76 | 94 | {
|
77 | 95 | // This is an ordinary ArgIterator capable of determining
|
78 | 96 | // types from a signature. Just do a regular GetNextArg.
|
79 | 97 | return GetNextArg();
|
80 | 98 | }
|
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) |
82 | 106 | {
|
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); |
102 | 113 | }
|
103 |
| - } |
104 | 114 |
|
| 115 | + TypedReference result = default; |
| 116 | + GetNextArg(ThisPtr, new QCallTypeHandle(ref rth), &result); |
| 117 | + return result; |
| 118 | + } |
105 | 119 |
|
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); |
109 | 122 |
|
110 | 123 | // This method should invalidate the iterator (va_end). It is not supported yet.
|
111 | 124 | public void End()
|
112 | 125 | {
|
113 | 126 | }
|
114 | 127 |
|
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 | + } |
118 | 138 |
|
119 | 139 | // Gets the type of the current arg, does NOT advance the iterator
|
120 |
| - [MethodImpl(MethodImplOptions.InternalCall)] |
121 |
| - private extern unsafe void* _GetNextArgType(); |
122 |
| - |
123 | 140 | public unsafe RuntimeTypeHandle GetNextArgType()
|
124 | 141 | {
|
125 |
| - return new RuntimeTypeHandle(Type.GetTypeFromHandleUnsafe((IntPtr)_GetNextArgType())); |
| 142 | + return RuntimeTypeHandle.FromIntPtr(GetNextArgType(ThisPtr)); |
126 | 143 | }
|
127 | 144 |
|
| 145 | + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ArgIterator_GetNextArgType")] |
| 146 | + private static partial IntPtr GetNextArgType(ArgIterator* thisPtr); |
| 147 | + |
128 | 148 | public override int GetHashCode()
|
129 | 149 | {
|
130 |
| - return HashCode.Combine(ArgCookie); |
| 150 | + return HashCode.Combine(_argCookie); |
131 | 151 | }
|
132 | 152 |
|
133 | 153 | // Inherited from object
|
|
0 commit comments