@@ -62,7 +62,10 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de
62
62
if ( ( uint ) ( destinationIndex + length ) > destinationArray . NativeLength )
63
63
throw new ArgumentException ( SR . Arg_LongerThanDestArray , nameof ( destinationArray ) ) ;
64
64
65
- if ( sourceArray . GetType ( ) == destinationArray . GetType ( ) || IsSimpleCopy ( sourceArray , destinationArray ) )
65
+ ArrayAssignType assignType = ArrayAssignType . WrongType ;
66
+
67
+ if ( sourceArray . GetType ( ) == destinationArray . GetType ( )
68
+ || ( assignType = CanAssignArrayType ( sourceArray , destinationArray ) ) == ArrayAssignType . SimpleCopy )
66
69
{
67
70
MethodTable * pMT = RuntimeHelpers . GetMethodTable ( sourceArray ) ;
68
71
@@ -86,44 +89,50 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de
86
89
throw new ArrayTypeMismatchException ( SR . ArrayTypeMismatch_ConstrainedCopy ) ;
87
90
88
91
// Rare
89
- CopySlow ( sourceArray , sourceIndex , destinationArray , destinationIndex , length ) ;
92
+ CopySlow ( sourceArray , sourceIndex , destinationArray , destinationIndex , length , assignType ) ;
90
93
}
91
94
92
- [ MethodImpl ( MethodImplOptions . InternalCall ) ]
93
- private static extern bool IsSimpleCopy ( Array sourceArray , Array destinationArray ) ;
95
+ private static CorElementType GetNormalizedIntegralArrayElementType ( CorElementType elementType )
96
+ {
97
+ Debug . Assert ( elementType . IsPrimitiveType ( ) ) ;
98
+
99
+ // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable
100
+ // Enums with interchangeable underlying types are interchangeable
101
+ // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2
102
+
103
+ // U1/U2/U4/U8/U
104
+ int shift = ( 0b0010_0000_0000_0000_1010_1010_0000 >> ( int ) elementType ) & 1 ;
105
+ return ( CorElementType ) ( ( int ) elementType - shift ) ;
106
+ }
94
107
95
108
// Reliability-wise, this method will either possibly corrupt your
96
109
// instance & might fail when called from within a CER, or if the
97
110
// reliable flag is true, it will either always succeed or always
98
111
// throw an exception with no side effects.
99
- private static unsafe void CopySlow ( Array sourceArray , int sourceIndex , Array destinationArray , int destinationIndex , int length )
112
+ private static unsafe void CopySlow ( Array sourceArray , int sourceIndex , Array destinationArray , int destinationIndex , int length , ArrayAssignType assignType )
100
113
{
101
114
Debug . Assert ( sourceArray . Rank == destinationArray . Rank ) ;
102
115
103
- void * srcTH = RuntimeHelpers . GetMethodTable ( sourceArray ) ->ElementType ;
104
- void * destTH = RuntimeHelpers . GetMethodTable ( destinationArray ) ->ElementType ;
105
- AssignArrayEnum r = CanAssignArrayType ( srcTH , destTH ) ;
106
-
107
- if ( r == AssignArrayEnum . AssignWrongType )
116
+ if ( assignType == ArrayAssignType . WrongType )
108
117
throw new ArrayTypeMismatchException ( SR . ArrayTypeMismatch_CantAssignType ) ;
109
118
110
119
if ( length > 0 )
111
120
{
112
- switch ( r )
121
+ switch ( assignType )
113
122
{
114
- case AssignArrayEnum . AssignUnboxValueClass :
123
+ case ArrayAssignType . UnboxValueClass :
115
124
CopyImplUnBoxEachElement ( sourceArray , sourceIndex , destinationArray , destinationIndex , length ) ;
116
125
break ;
117
126
118
- case AssignArrayEnum . AssignBoxValueClassOrPrimitive :
127
+ case ArrayAssignType . BoxValueClassOrPrimitive :
119
128
CopyImplBoxEachElement ( sourceArray , sourceIndex , destinationArray , destinationIndex , length ) ;
120
129
break ;
121
130
122
- case AssignArrayEnum . AssignMustCast :
131
+ case ArrayAssignType . MustCast :
123
132
CopyImplCastCheckEachElement ( sourceArray , sourceIndex , destinationArray , destinationIndex , length ) ;
124
133
break ;
125
134
126
- case AssignArrayEnum . AssignPrimitiveWiden :
135
+ case ArrayAssignType . PrimitiveWiden :
127
136
CopyImplPrimitiveWiden ( sourceArray , sourceIndex , destinationArray , destinationIndex , length ) ;
128
137
break ;
129
138
@@ -134,18 +143,90 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de
134
143
}
135
144
}
136
145
137
- // Must match the definition in arraynative.cpp
138
- private enum AssignArrayEnum
146
+ private enum ArrayAssignType
139
147
{
140
- AssignWrongType ,
141
- AssignMustCast ,
142
- AssignBoxValueClassOrPrimitive ,
143
- AssignUnboxValueClass ,
144
- AssignPrimitiveWiden ,
148
+ SimpleCopy ,
149
+ WrongType ,
150
+ MustCast ,
151
+ BoxValueClassOrPrimitive ,
152
+ UnboxValueClass ,
153
+ PrimitiveWiden ,
145
154
}
146
155
147
- [ LibraryImport ( RuntimeHelpers . QCall , EntryPoint = "Array_CanAssignArrayType" ) ]
148
- private static unsafe partial AssignArrayEnum CanAssignArrayType ( void * srcTH , void * dstTH ) ;
156
+ private static unsafe ArrayAssignType CanAssignArrayType ( Array sourceArray , Array destinationArray )
157
+ {
158
+ TypeHandle srcTH = RuntimeHelpers . GetMethodTable ( sourceArray ) ->GetArrayElementTypeHandle ( ) ;
159
+ TypeHandle destTH = RuntimeHelpers . GetMethodTable ( destinationArray ) ->GetArrayElementTypeHandle ( ) ;
160
+
161
+ if ( TypeHandle . AreSameType ( srcTH , destTH ) ) // This check kicks for different array kind or dimensions
162
+ return ArrayAssignType . SimpleCopy ;
163
+
164
+ if ( ! srcTH . IsTypeDesc && ! destTH . IsTypeDesc )
165
+ {
166
+ MethodTable * pMTsrc = srcTH . AsMethodTable ( ) ;
167
+ MethodTable * pMTdest = destTH . AsMethodTable ( ) ;
168
+
169
+ // Value class boxing
170
+ if ( pMTsrc ->IsValueType && ! pMTdest ->IsValueType )
171
+ {
172
+ if ( srcTH . CanCastTo ( destTH ) )
173
+ return ArrayAssignType . BoxValueClassOrPrimitive ;
174
+ else
175
+ return ArrayAssignType . WrongType ;
176
+ }
177
+
178
+ // Value class unboxing.
179
+ if ( ! pMTsrc ->IsValueType && pMTdest ->IsValueType )
180
+ {
181
+ if ( srcTH . CanCastTo ( destTH ) )
182
+ return ArrayAssignType . UnboxValueClass ;
183
+ else if ( destTH . CanCastTo ( srcTH ) ) // V extends IV. Copying from IV to V, or Object to V.
184
+ return ArrayAssignType . UnboxValueClass ;
185
+ else
186
+ return ArrayAssignType . WrongType ;
187
+ }
188
+
189
+ // Copying primitives from one type to another
190
+ if ( pMTsrc ->IsPrimitive && pMTdest ->IsPrimitive )
191
+ {
192
+ CorElementType srcElType = pMTsrc ->GetPrimitiveCorElementType ( ) ;
193
+ CorElementType destElType = pMTdest ->GetPrimitiveCorElementType ( ) ;
194
+
195
+ if ( GetNormalizedIntegralArrayElementType ( srcElType ) == GetNormalizedIntegralArrayElementType ( destElType ) )
196
+ return ArrayAssignType . SimpleCopy ;
197
+ else if ( RuntimeHelpers . CanPrimitiveWiden ( srcElType , destElType ) )
198
+ return ArrayAssignType . PrimitiveWiden ;
199
+ else
200
+ return ArrayAssignType . WrongType ;
201
+ }
202
+
203
+ // src Object extends dest
204
+ if ( srcTH . CanCastTo ( destTH ) )
205
+ return ArrayAssignType . SimpleCopy ;
206
+
207
+ // dest Object extends src
208
+ if ( destTH . CanCastTo ( srcTH ) )
209
+ return ArrayAssignType . MustCast ;
210
+
211
+ // class X extends/implements src and implements dest.
212
+ if ( pMTdest ->IsInterface )
213
+ return ArrayAssignType . MustCast ;
214
+
215
+ // class X implements src and extends/implements dest
216
+ if ( pMTsrc ->IsInterface )
217
+ return ArrayAssignType . MustCast ;
218
+ }
219
+ else
220
+ {
221
+ // Only pointers are valid for TypeDesc in array element
222
+
223
+ // Compatible pointers
224
+ if ( srcTH . CanCastTo ( destTH ) )
225
+ return ArrayAssignType . SimpleCopy ;
226
+ }
227
+
228
+ return ArrayAssignType . WrongType ;
229
+ }
149
230
150
231
// Unboxes from an Object[] into a value class or primitive array.
151
232
private static unsafe void CopyImplUnBoxEachElement ( Array sourceArray , int sourceIndex , Array destinationArray , int destinationIndex , int length )
0 commit comments