|
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