@@ -129,95 +129,71 @@ FCIMPL1(void, ArrayNative::Initialize, ArrayBase* array)
129
129
FCIMPLEND
130
130
131
131
132
-
133
-
134
-
135
-
136
- // Returns an enum saying whether you can copy an array of srcType into destType.
137
- ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayTypeNoGC (const BASEARRAYREF pSrc, const BASEARRAYREF pDest)
132
+ // Returns whether you can directly copy an array of srcType into destType.
133
+ FCIMPL2 (FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst)
138
134
{
139
- CONTRACTL
140
- {
141
- NOTHROW;
142
- GC_NOTRIGGER;
143
- MODE_COOPERATIVE;
144
- PRECONDITION (pSrc != NULL );
145
- PRECONDITION (pDest != NULL );
146
- }
147
- CONTRACTL_END;
135
+ FCALL_CONTRACT;
148
136
149
- // The next 50 lines are a little tricky. Change them with great care.
150
- //
137
+ _ASSERTE (pSrc != NULL );
138
+ _ASSERTE (pDst != NULL );
151
139
152
- // This first bit is a minor optimization: e.g. when copying byte[] to byte[]
153
- // we do not need to call GetArrayElementTypeHandle().
154
- MethodTable *pSrcMT = pSrc->GetMethodTable ();
155
- MethodTable *pDestMT = pDest->GetMethodTable ();
156
- if (pSrcMT == pDestMT)
157
- return AssignWillWork;
140
+ // This case is expected to be handled by the fast path
141
+ _ASSERTE (pSrc->GetMethodTable () != pDst->GetMethodTable ());
158
142
159
- TypeHandle srcTH = pSrcMT ->GetApproxArrayElementTypeHandle ();
160
- TypeHandle destTH = pDestMT ->GetApproxArrayElementTypeHandle ();
143
+ TypeHandle srcTH = pSrc-> GetMethodTable () ->GetApproxArrayElementTypeHandle ();
144
+ TypeHandle destTH = pDst-> GetMethodTable () ->GetApproxArrayElementTypeHandle ();
161
145
if (srcTH == destTH) // This check kicks for different array kind or dimensions
162
- return AssignWillWork ;
146
+ FC_RETURN_BOOL ( true ) ;
163
147
164
- // Value class boxing
165
- if (srcTH.IsValueType () && !destTH.IsValueType ())
148
+ if (srcTH.IsValueType ())
166
149
{
167
- switch (srcTH.CanCastToCached (destTH))
150
+ // Value class boxing
151
+ if (!destTH.IsValueType ())
152
+ FC_RETURN_BOOL (false );
153
+
154
+ const CorElementType srcElType = srcTH.GetVerifierCorElementType ();
155
+ const CorElementType destElType = destTH.GetVerifierCorElementType ();
156
+ _ASSERTE (srcElType < ELEMENT_TYPE_MAX);
157
+ _ASSERTE (destElType < ELEMENT_TYPE_MAX);
158
+
159
+ // Copying primitives from one type to another
160
+ if (CorTypeInfo::IsPrimitiveType_NoThrow (srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow (destElType))
168
161
{
169
- case TypeHandle::CanCast : return AssignBoxValueClassOrPrimitive;
170
- case TypeHandle::CannotCast : return AssignWrongType;
171
- default : return AssignDontKnow;
162
+ if (GetNormalizedIntegralArrayElementType (srcElType) == GetNormalizedIntegralArrayElementType (destElType))
163
+ FC_RETURN_BOOL (true );
172
164
}
173
165
}
174
-
175
- // Value class unboxing.
176
- if (!srcTH.IsValueType () && destTH.IsValueType ())
166
+ else
177
167
{
178
- if (srcTH.CanCastToCached (destTH) == TypeHandle::CanCast)
179
- return AssignUnboxValueClass;
180
- else if (destTH.CanCastToCached (srcTH) == TypeHandle::CanCast) // V extends IV. Copying from IV to V, or Object to V.
181
- return AssignUnboxValueClass;
182
- else
183
- return AssignDontKnow;
168
+ // Value class unboxing
169
+ if (destTH.IsValueType ())
170
+ FC_RETURN_BOOL (false );
184
171
}
185
172
186
- const CorElementType srcElType = srcTH.GetVerifierCorElementType ();
187
- const CorElementType destElType = destTH.GetVerifierCorElementType ();
188
- _ASSERTE (srcElType < ELEMENT_TYPE_MAX);
189
- _ASSERTE (destElType < ELEMENT_TYPE_MAX);
190
-
191
- // Copying primitives from one type to another
192
- if (CorTypeInfo::IsPrimitiveType_NoThrow (srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow (destElType))
173
+ TypeHandle::CastResult r = srcTH.CanCastToCached (destTH);
174
+ if (r != TypeHandle::MaybeCast)
193
175
{
194
- if (GetNormalizedIntegralArrayElementType (srcElType) == GetNormalizedIntegralArrayElementType (destElType))
195
- return AssignWillWork;
196
-
197
- if (InvokeUtil::CanPrimitiveWiden (destElType, srcElType))
198
- return AssignPrimitiveWiden;
199
- else
200
- return AssignWrongType;
176
+ FC_RETURN_BOOL (r);
201
177
}
202
178
203
- // dest Object extends src
204
- if (srcTH.CanCastToCached (destTH) == TypeHandle::CanCast)
205
- return AssignWillWork;
179
+ struct
180
+ {
181
+ OBJECTREF src;
182
+ OBJECTREF dst;
183
+ } gc;
206
184
207
- // src Object extends dest
208
- if (destTH.CanCastToCached (srcTH) == TypeHandle::CanCast)
209
- return AssignMustCast;
185
+ gc.src = ObjectToOBJECTREF (pSrc);
186
+ gc.dst = ObjectToOBJECTREF (pDst);
210
187
211
- // class X extends/implements src and implements dest.
212
- if (destTH.IsInterface () && srcElType != ELEMENT_TYPE_VALUETYPE)
213
- return AssignMustCast;
188
+ BOOL iRetVal = FALSE ;
214
189
215
- // class X implements src and extends/implements dest
216
- if ( srcTH.IsInterface () && destElType != ELEMENT_TYPE_VALUETYPE)
217
- return AssignMustCast ;
190
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT (gc);
191
+ iRetVal = srcTH.CanCastTo (destTH);
192
+ HELPER_METHOD_FRAME_END () ;
218
193
219
- return AssignDontKnow ;
194
+ FC_RETURN_BOOL (iRetVal) ;
220
195
}
196
+ FCIMPLEND
221
197
222
198
223
199
// Returns an enum saying whether you can copy an array of srcType into destType.
@@ -233,21 +209,16 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF
233
209
}
234
210
CONTRACTL_END;
235
211
236
- // The next 50 lines are a little tricky. Change them with great care.
237
- //
238
-
239
212
// This first bit is a minor optimization: e.g. when copying byte[] to byte[]
240
213
// we do not need to call GetArrayElementTypeHandle().
241
214
MethodTable *pSrcMT = pSrc->GetMethodTable ();
242
215
MethodTable *pDestMT = pDest->GetMethodTable ();
243
- if (pSrcMT == pDestMT)
244
- return AssignWillWork;
216
+ _ASSERTE (pSrcMT != pDestMT); // Handled by fast path
245
217
246
218
TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle ();
247
219
TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle ();
248
- if (srcTH == destTH) // This check kicks for different array kind or dimensions
249
- return AssignWillWork;
250
-
220
+ _ASSERTE (srcTH != destTH); // Handled by fast path
221
+
251
222
// Value class boxing
252
223
if (srcTH.IsValueType () && !destTH.IsValueType ())
253
224
{
@@ -276,17 +247,15 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF
276
247
// Copying primitives from one type to another
277
248
if (CorTypeInfo::IsPrimitiveType_NoThrow (srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow (destElType))
278
249
{
279
- if (srcElType == destElType)
280
- return AssignWillWork;
250
+ _ASSERTE (srcElType != destElType); // Handled by fast path
281
251
if (InvokeUtil::CanPrimitiveWiden (destElType, srcElType))
282
252
return AssignPrimitiveWiden;
283
253
else
284
254
return AssignWrongType;
285
255
}
286
256
287
257
// dest Object extends src
288
- if (srcTH.CanCastTo (destTH))
289
- return AssignWillWork;
258
+ _ASSERTE (!srcTH.CanCastTo (destTH)); // Handled by fast path
290
259
291
260
// src Object extends dest
292
261
if (destTH.CanCastTo (srcTH))
@@ -766,38 +735,7 @@ void memmoveGCRefs(void *dest, const void *src, size_t len)
766
735
}
767
736
}
768
737
769
- void ArrayNative::ArrayCopyNoTypeCheck (BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length)
770
- {
771
- CONTRACTL
772
- {
773
- NOTHROW;
774
- GC_NOTRIGGER;
775
- MODE_COOPERATIVE;
776
- PRECONDITION (pSrc != NULL );
777
- PRECONDITION (srcIndex >= 0 );
778
- PRECONDITION (pDest != NULL );
779
- PRECONDITION (length > 0 );
780
- }
781
- CONTRACTL_END;
782
-
783
- BYTE *src = (BYTE*)pSrc->GetDataPtr ();
784
- BYTE *dst = (BYTE*)pDest->GetDataPtr ();
785
- SIZE_T size = pSrc->GetComponentSize ();
786
-
787
- src += srcIndex * size;
788
- dst += destIndex * size;
789
-
790
- if (pDest->GetMethodTable ()->ContainsPointers ())
791
- {
792
- memmoveGCRefs (dst, src, length * size);
793
- }
794
- else
795
- {
796
- memmove (dst, src, length * size);
797
- }
798
- }
799
-
800
- FCIMPL6 (void , ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable)
738
+ FCIMPL5 (void , ArrayNative::CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength)
801
739
{
802
740
FCALL_CONTRACT;
803
741
@@ -807,115 +745,50 @@ FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, Arra
807
745
BASEARRAYREF pDst;
808
746
} gc;
809
747
810
- gc.pSrc = (BASEARRAYREF)m_pSrc;
811
- gc.pDst = (BASEARRAYREF)m_pDst;
812
-
813
- //
814
- // creating a HelperMethodFrame is quite expensive,
815
- // so we want to delay this for the most common case which doesn't trigger a GC.
816
- // FCThrow is needed to throw an exception without a HelperMethodFrame
817
- //
748
+ gc.pSrc = (BASEARRAYREF)pSrc;
749
+ gc.pDst = (BASEARRAYREF)pDst;
818
750
819
751
// cannot pass null for source or destination
820
- if (gc.pSrc == NULL || gc.pDst == NULL ) {
821
- FCThrowArgumentNullVoid (gc.pSrc ==NULL ? W (" sourceArray" ) : W (" destinationArray" ));
822
- }
752
+ _ASSERTE (gc.pSrc != NULL && gc.pDst != NULL );
823
753
824
754
// source and destination must be arrays
825
755
_ASSERTE (gc.pSrc ->GetMethodTable ()->IsArray ());
826
756
_ASSERTE (gc.pDst ->GetMethodTable ()->IsArray ());
827
757
828
- // Equal method tables should imply equal rank
829
- _ASSERTE (!(gc.pSrc ->GetMethodTable () == gc.pDst ->GetMethodTable () && gc.pSrc ->GetRank () != gc.pDst ->GetRank ()));
758
+ _ASSERTE (gc.pSrc ->GetRank () == gc.pDst ->GetRank ());
830
759
831
- // Which enables us to avoid touching the EEClass in simple cases
832
- if (gc.pSrc ->GetMethodTable () != gc.pDst ->GetMethodTable () && gc.pSrc ->GetRank () != gc.pDst ->GetRank ()) {
833
- FCThrowResVoid (kRankException , W (" Rank_MustMatch" ));
834
- }
835
-
836
- g_IBCLogger.LogMethodTableAccess (gc.pSrc ->GetMethodTable ());
837
- g_IBCLogger.LogMethodTableAccess (gc.pDst ->GetMethodTable ());
838
-
839
- int srcLB = gc.pSrc ->GetLowerBoundsPtr ()[0 ];
840
- int destLB = gc.pDst ->GetLowerBoundsPtr ()[0 ];
841
760
// array bounds checking
842
- const unsigned int srcLen = gc.pSrc ->GetNumComponents ();
843
- const unsigned int destLen = gc.pDst ->GetNumComponents ();
844
- if (m_iLength < 0 )
845
- FCThrowArgumentOutOfRangeVoid (W (" length" ), W (" ArgumentOutOfRange_NeedNonNegNum" ));
846
-
847
- if (m_iSrcIndex < srcLB || (m_iSrcIndex - srcLB < 0 ))
848
- FCThrowArgumentOutOfRangeVoid (W (" sourceIndex" ), W (" ArgumentOutOfRange_ArrayLB" ));
849
-
850
- if (m_iDstIndex < destLB || (m_iDstIndex - destLB < 0 ))
851
- FCThrowArgumentOutOfRangeVoid (W (" destinationIndex" ), W (" ArgumentOutOfRange_ArrayLB" ));
852
-
853
- if ((DWORD)(m_iSrcIndex - srcLB + m_iLength) > srcLen)
854
- FCThrowArgumentVoid (W (" sourceArray" ), W (" Arg_LongerThanSrcArray" ));
855
-
856
- if ((DWORD)(m_iDstIndex - destLB + m_iLength) > destLen)
857
- FCThrowArgumentVoid (W (" destinationArray" ), W (" Arg_LongerThanDestArray" ));
858
-
859
- int r = 0 ;
860
-
861
- // Small perf optimization - we copy from one portion of an array back to
862
- // itself a lot when resizing collections, etc. The cost of doing the type
863
- // checking is significant for copying small numbers of bytes (~half of the time
864
- // for copying 1 byte within one array from element 0 to element 1).
865
- if (gc.pSrc == gc.pDst )
866
- r = AssignWillWork;
867
- else
868
- r = CanAssignArrayTypeNoGC (gc.pSrc , gc.pDst );
869
-
870
- if (r == AssignWrongType) {
871
- FCThrowResVoid (kArrayTypeMismatchException , W (" ArrayTypeMismatch_CantAssignType" ));
872
- }
873
-
874
- if (r == AssignWillWork) {
875
- if (m_iLength > 0 )
876
- ArrayCopyNoTypeCheck (gc.pSrc , m_iSrcIndex - srcLB, gc.pDst , m_iDstIndex - destLB, m_iLength);
877
-
878
- FC_GC_POLL ();
879
- return ;
880
- }
761
+ _ASSERTE (iLength >= 0 );
762
+ _ASSERTE (iSrcIndex >= 0 );
763
+ _ASSERTE (iDstIndex >= 0 );
764
+ _ASSERTE ((DWORD)(iSrcIndex + iLength) <= gc.pSrc ->GetNumComponents ());
765
+ _ASSERTE ((DWORD)(iDstIndex + iLength) <= gc.pDst ->GetNumComponents ());
881
766
882
767
HELPER_METHOD_FRAME_BEGIN_PROTECT (gc);
883
- if (r == AssignDontKnow)
884
- {
885
- r = CanAssignArrayType (gc.pSrc , gc.pDst );
886
- }
887
- CONSISTENCY_CHECK (r != AssignDontKnow);
888
768
889
- // If we were called from Array.ConstrainedCopy, ensure that the array copy
890
- // is guaranteed to succeed.
891
- if (reliable && r != AssignWillWork)
892
- COMPlusThrow (kArrayTypeMismatchException , W (" ArrayTypeMismatch_ConstrainedCopy" ));
769
+ int r = CanAssignArrayType (gc.pSrc , gc.pDst );
893
770
894
771
if (r == AssignWrongType)
895
772
COMPlusThrow (kArrayTypeMismatchException , W (" ArrayTypeMismatch_CantAssignType" ));
896
773
897
- if (m_iLength > 0 )
774
+ if (iLength > 0 )
898
775
{
899
776
switch (r)
900
777
{
901
- case AssignWillWork:
902
- ArrayCopyNoTypeCheck (gc.pSrc , m_iSrcIndex - srcLB, gc.pDst , m_iDstIndex - destLB, m_iLength);
903
- break ;
904
-
905
778
case AssignUnboxValueClass:
906
- UnBoxEachElement (gc.pSrc , m_iSrcIndex - srcLB , gc.pDst , m_iDstIndex - destLB, m_iLength );
779
+ UnBoxEachElement (gc.pSrc , iSrcIndex , gc.pDst , iDstIndex, iLength );
907
780
break ;
908
781
909
782
case AssignBoxValueClassOrPrimitive:
910
- BoxEachElement (gc.pSrc , m_iSrcIndex - srcLB , gc.pDst , m_iDstIndex - destLB, m_iLength );
783
+ BoxEachElement (gc.pSrc , iSrcIndex , gc.pDst , iDstIndex, iLength );
911
784
break ;
912
785
913
786
case AssignMustCast:
914
- CastCheckEachElement (gc.pSrc , m_iSrcIndex - srcLB , gc.pDst , m_iDstIndex - destLB, m_iLength );
787
+ CastCheckEachElement (gc.pSrc , iSrcIndex , gc.pDst , iDstIndex, iLength );
915
788
break ;
916
789
917
790
case AssignPrimitiveWiden:
918
- PrimitiveWiden (gc.pSrc , m_iSrcIndex - srcLB , gc.pDst , m_iDstIndex - destLB, m_iLength );
791
+ PrimitiveWiden (gc.pSrc , iSrcIndex , gc.pDst , iDstIndex, iLength );
919
792
break ;
920
793
921
794
default :
0 commit comments