-
Notifications
You must be signed in to change notification settings - Fork 5k
/
Copy pathobject.h
2590 lines (2138 loc) · 75.4 KB
/
object.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//
// OBJECT.H
//
// Definitions of a CLR Object
//
// See code:EEStartup#TableOfContents for overview
#ifndef _OBJECT_H_
#define _OBJECT_H_
#include "util.hpp"
#include "syncblk.h"
#include "gcdesc.h"
#include "specialstatics.h"
#include "sstring.h"
#include "daccess.h"
#include "fcall.h"
extern "C" void __fastcall ZeroMemoryInGCHeap(void*, size_t);
void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref);
/*
#ObjectModel
* CLR Internal Object Model
*
*
* Object - This is the common base part to all CLR objects
* | it contains the MethodTable pointer and the
* | sync block index, which is at a negative offset
* |
* +-- code:StringObject - String objects are specialized objects for string
* | storage/retrieval for higher performance (UCS-2 / UTF-16 data)
* |
* +-- code:Utf8StringObject - String objects are specialized objects for string
* | storage/retrieval for higher performance (UTF-8 data)
* |
* +-- BaseObjectWithCachedData - Object Plus one object field for caching.
* | |
* | +- ReflectClassBaseObject - The base object for the RuntimeType class
* | +- ReflectMethodObject - The base object for the RuntimeMethodInfo class
* | +- ReflectFieldObject - The base object for the RtFieldInfo class
* |
* +-- code:ArrayBase - Base portion of all arrays
* | |
* | +- I1Array - Base type arrays
* | | I2Array
* | | ...
* | |
* | +- PtrArray - Array of OBJECTREFs, different than base arrays because of pObjectClass
* |
* +-- code:AssemblyBaseObject - The base object for the class Assembly
*
*
* PLEASE NOTE THE FOLLOWING WHEN ADDING A NEW OBJECT TYPE:
*
* The size of the object in the heap must be able to be computed
* very, very quickly for GC purposes. Restrictions on the layout
* of the object guarantee this is possible.
*
* Any object that inherits from Object must be able to
* compute its complete size by using the first 4 bytes of
* the object following the Object part and constants
* reachable from the MethodTable...
*
* The formula used for this calculation is:
* MT->GetBaseSize() + ((OBJECTTYPEREF->GetSizeField() * MT->GetComponentSize())
*
* So for Object, since this is of fixed size, the ComponentSize is 0, which makes the right side
* of the equation above equal to 0 no matter what the value of GetSizeField(), so the size is just the base size.
*
*/
class MethodTable;
class Thread;
class Assembly;
class DomainAssembly;
class AssemblyNative;
class WaitHandleNative;
class ArgDestination;
struct RCW;
#ifdef TARGET_64BIT
#define OBJHEADER_SIZE (sizeof(DWORD) /* m_alignpad */ + sizeof(DWORD) /* m_SyncBlockValue */)
#else
#define OBJHEADER_SIZE sizeof(DWORD) /* m_SyncBlockValue */
#endif
#define OBJECT_SIZE TARGET_POINTER_SIZE /* m_pMethTab */
#define OBJECT_BASESIZE (OBJHEADER_SIZE + OBJECT_SIZE)
#ifdef TARGET_64BIT
#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */ + sizeof(DWORD) /* pad */)
#else
#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */)
#endif
#define ARRAYBASE_BASESIZE (OBJHEADER_SIZE + ARRAYBASE_SIZE)
//
// The generational GC requires that every object be at least 12 bytes
// in size.
#define MIN_OBJECT_SIZE (2*TARGET_POINTER_SIZE + OBJHEADER_SIZE)
#define PTRALIGNCONST (DATA_ALIGNMENT-1)
#ifndef PtrAlign
#define PtrAlign(size) \
(((size) + PTRALIGNCONST) & (~PTRALIGNCONST))
#endif //!PtrAlign
// code:Object is the representation of an managed object on the GC heap.
//
// See code:#ObjectModel for some important subclasses of code:Object
//
// The only fields mandated by all objects are
//
// * a pointer to the code:MethodTable at offset 0
// * a pointer to a code:ObjHeader at a negative offset. This is often zero. It holds information that
// any addition information that we might need to attach to arbitrary objects.
//
class Object
{
protected:
PTR_MethodTable m_pMethTab;
protected:
Object() { LIMITED_METHOD_CONTRACT; };
~Object() { LIMITED_METHOD_CONTRACT; };
public:
MethodTable *RawGetMethodTable() const
{
return m_pMethTab;
}
#ifndef DACCESS_COMPILE
void RawSetMethodTable(MethodTable *pMT)
{
LIMITED_METHOD_CONTRACT;
m_pMethTab = pMT;
}
VOID SetMethodTable(MethodTable *pMT)
{
WRAPPER_NO_CONTRACT;
RawSetMethodTable(pMT);
}
VOID SetMethodTableForUOHObject(MethodTable *pMT)
{
WRAPPER_NO_CONTRACT;
// This function must be used if the allocation occurs on a UOH heap, and the method table might be a collectible type
ErectWriteBarrierForMT(&m_pMethTab, pMT);
}
#endif //!DACCESS_COMPILE
#define MARKED_BIT 0x1
PTR_MethodTable GetMethodTable() const
{
LIMITED_METHOD_DAC_CONTRACT;
#ifndef DACCESS_COMPILE
// We should always use GetGCSafeMethodTable() if we're running during a GC.
// If the mark bit is set then we're running during a GC
_ASSERTE((dac_cast<TADDR>(m_pMethTab) & MARKED_BIT) == 0);
return m_pMethTab;
#else //DACCESS_COMPILE
//@dbgtodo dharvey Make this a type which supports bitwise and operations
//when available
return PTR_MethodTable((dac_cast<TADDR>(m_pMethTab)) & (~MARKED_BIT));
#endif //DACCESS_COMPILE
}
DPTR(PTR_MethodTable) GetMethodTablePtr() const
{
LIMITED_METHOD_CONTRACT;
return dac_cast<DPTR(PTR_MethodTable)>(PTR_HOST_MEMBER_TADDR(Object, this, m_pMethTab));
}
TypeHandle GetTypeHandle();
// Methods used to determine if an object supports a given interface.
static BOOL SupportsInterface(OBJECTREF pObj, MethodTable *pInterfaceMT);
inline DWORD GetNumComponents();
inline SIZE_T GetSize();
CGCDesc* GetSlotMap()
{
WRAPPER_NO_CONTRACT;
return( CGCDesc::GetCGCDescFromMT(GetMethodTable()));
}
// Sync Block & Synchronization services
// Access the ObjHeader which is at a negative offset on the object (because of
// cache lines)
PTR_ObjHeader GetHeader()
{
LIMITED_METHOD_DAC_CONTRACT;
return dac_cast<PTR_ObjHeader>(this) - 1;
}
// Get the current address of the object (works for debug refs, too.)
PTR_BYTE GetAddress()
{
LIMITED_METHOD_DAC_CONTRACT;
return dac_cast<PTR_BYTE>(this);
}
#ifdef _DEBUG
// TRUE if the header has a real SyncBlockIndex (i.e. it has an entry in the
// SyncTable, though it doesn't necessarily have an entry in the SyncBlockCache)
BOOL HasEmptySyncBlockInfo()
{
WRAPPER_NO_CONTRACT;
return GetHeader()->HasEmptySyncBlockInfo();
}
#endif
// retrieve or allocate a sync block for this object
SyncBlock *GetSyncBlock()
{
WRAPPER_NO_CONTRACT;
return GetHeader()->GetSyncBlock();
}
DWORD GetSyncBlockIndex()
{
WRAPPER_NO_CONTRACT;
return GetHeader()->GetSyncBlockIndex();
}
// DO NOT ADD ANY ASSERTS TO THIS METHOD.
// DO NOT USE THIS METHOD.
// Yes folks, for better or worse the debugger pokes supposed object addresses
// to try to see if objects are valid, possibly firing an AccessViolation or worse,
// and then catches the AV and reports a failure to the debug client. This makes
// the debugger slightly more robust should any corrupted object references appear
// in a session. Thus it is "correct" behaviour for this to AV when used with
// an invalid object pointer, and incorrect behaviour for it to
// assert.
BOOL ValidateObjectWithPossibleAV();
// Validate an object ref out of the VerifyHeap routine in the GC
void ValidateHeap(BOOL bDeep=TRUE);
PTR_SyncBlock PassiveGetSyncBlock()
{
LIMITED_METHOD_DAC_CONTRACT;
return GetHeader()->PassiveGetSyncBlock();
}
static DWORD ComputeHashCode();
static DWORD GetGlobalNewHashCode();
#ifndef DACCESS_COMPILE
INT32 GetHashCodeEx();
#endif // #ifndef DACCESS_COMPILE
// Synchronization
#ifndef DACCESS_COMPILE
void EnterObjMonitor()
{
WRAPPER_NO_CONTRACT;
GetHeader()->EnterObjMonitor();
}
BOOL TryEnterObjMonitor(INT32 timeOut = 0)
{
WRAPPER_NO_CONTRACT;
return GetHeader()->TryEnterObjMonitor(timeOut);
}
bool TryEnterObjMonitorSpinHelper();
FORCEINLINE AwareLock::EnterHelperResult EnterObjMonitorHelper(Thread* pCurThread)
{
WRAPPER_NO_CONTRACT;
return GetHeader()->EnterObjMonitorHelper(pCurThread);
}
FORCEINLINE AwareLock::EnterHelperResult EnterObjMonitorHelperSpin(Thread* pCurThread)
{
WRAPPER_NO_CONTRACT;
return GetHeader()->EnterObjMonitorHelperSpin(pCurThread);
}
BOOL LeaveObjMonitor()
{
WRAPPER_NO_CONTRACT;
return GetHeader()->LeaveObjMonitor();
}
// should be called only from unwind code; used in the
// case where EnterObjMonitor failed to allocate the
// sync-object.
BOOL LeaveObjMonitorAtException()
{
WRAPPER_NO_CONTRACT;
return GetHeader()->LeaveObjMonitorAtException();
}
FORCEINLINE AwareLock::LeaveHelperAction LeaveObjMonitorHelper(Thread* pCurThread)
{
WRAPPER_NO_CONTRACT;
return GetHeader()->LeaveObjMonitorHelper(pCurThread);
}
// Returns TRUE if the lock is owned and FALSE otherwise
// threadId is set to the ID (Thread::GetThreadId()) of the thread which owns the lock
// acquisitionCount is set to the number of times the lock needs to be released before
// it is unowned
BOOL GetThreadOwningMonitorLock(DWORD *pThreadId, DWORD *pAcquisitionCount)
{
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
return GetHeader()->GetThreadOwningMonitorLock(pThreadId, pAcquisitionCount);
}
#endif // #ifndef DACCESS_COMPILE
BOOL Wait(INT32 timeOut)
{
WRAPPER_NO_CONTRACT;
return GetHeader()->Wait(timeOut);
}
void Pulse()
{
WRAPPER_NO_CONTRACT;
GetHeader()->Pulse();
}
void PulseAll()
{
WRAPPER_NO_CONTRACT;
GetHeader()->PulseAll();
}
PTR_VOID UnBox(); // if it is a value class, get the pointer to the first field
PTR_BYTE GetData(void)
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
return dac_cast<PTR_BYTE>(this) + sizeof(Object);
}
static UINT GetOffsetOfFirstField()
{
LIMITED_METHOD_CONTRACT;
return sizeof(Object);
}
DWORD GetOffset32(DWORD dwOffset)
{
WRAPPER_NO_CONTRACT;
return * PTR_DWORD(GetData() + dwOffset);
}
USHORT GetOffset16(DWORD dwOffset)
{
WRAPPER_NO_CONTRACT;
return * PTR_USHORT(GetData() + dwOffset);
}
BYTE GetOffset8(DWORD dwOffset)
{
WRAPPER_NO_CONTRACT;
return * PTR_BYTE(GetData() + dwOffset);
}
int64_t GetOffset64(DWORD dwOffset)
{
WRAPPER_NO_CONTRACT;
return (int64_t) * PTR_ULONG64(GetData() + dwOffset);
}
void *GetPtrOffset(DWORD dwOffset)
{
WRAPPER_NO_CONTRACT;
return (void *)(TADDR)*PTR_TADDR(GetData() + dwOffset);
}
#ifndef DACCESS_COMPILE
void SetOffsetObjectRef(DWORD dwOffset, size_t dwValue);
void SetOffsetPtr(DWORD dwOffset, LPVOID value)
{
WRAPPER_NO_CONTRACT;
*(LPVOID *) &GetData()[dwOffset] = value;
}
void SetOffset32(DWORD dwOffset, DWORD dwValue)
{
WRAPPER_NO_CONTRACT;
*(DWORD *) &GetData()[dwOffset] = dwValue;
}
void SetOffset16(DWORD dwOffset, DWORD dwValue)
{
WRAPPER_NO_CONTRACT;
*(USHORT *) &GetData()[dwOffset] = (USHORT) dwValue;
}
void SetOffset8(DWORD dwOffset, DWORD dwValue)
{
WRAPPER_NO_CONTRACT;
*(BYTE *) &GetData()[dwOffset] = (BYTE) dwValue;
}
void SetOffset64(DWORD dwOffset, int64_t qwValue)
{
WRAPPER_NO_CONTRACT;
*(int64_t *) &GetData()[dwOffset] = qwValue;
}
#endif // #ifndef DACCESS_COMPILE
VOID Validate(BOOL bDeep = TRUE, BOOL bVerifyNextHeader = TRUE, BOOL bVerifySyncBlock = TRUE);
PTR_MethodTable GetGCSafeMethodTable() const
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
// lose GC marking bit and the reserved bit
// A method table pointer should always be aligned. During GC we set the least
// significant bit for marked objects, and the second to least significant
// bit is reserved. So if we want the actual MT pointer during a GC
// we must zero out the lowest 2 bits on 32-bit and 3 bits on 64-bit.
#ifdef TARGET_64BIT
return dac_cast<PTR_MethodTable>((dac_cast<TADDR>(m_pMethTab)) & ~((UINT_PTR)7));
#else
return dac_cast<PTR_MethodTable>((dac_cast<TADDR>(m_pMethTab)) & ~((UINT_PTR)3));
#endif //TARGET_64BIT
}
// There are some cases where it is unsafe to get the type handle during a GC.
// This occurs when the type has already been unloaded as part of an in-progress appdomain shutdown.
TypeHandle GetGCSafeTypeHandleIfPossible() const;
inline TypeHandle GetGCSafeTypeHandle() const;
#ifdef DACCESS_COMPILE
void EnumMemoryRegions(void);
#endif
private:
VOID ValidateInner(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock);
friend struct ::cdac_data<Object>;
};
template<>
struct cdac_data<Object>
{
static constexpr size_t m_pMethTab = offsetof(Object, m_pMethTab);
};
/*
* Object ref setting routines. You must use these to do
* proper write barrier support.
*/
// SetObjectReference sets an OBJECTREF field
void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref);
#ifdef _DEBUG
void EnableStressHeapHelper();
#endif
//Used to clear the object reference
inline void ClearObjectReference(OBJECTREF* dst)
{
LIMITED_METHOD_CONTRACT;
*(void**)(dst) = NULL;
}
// CopyValueClass sets a value class field
void STDCALL CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT);
void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset);
inline void InitValueClass(void *dest, MethodTable *pMT)
{
WRAPPER_NO_CONTRACT;
ZeroMemoryInGCHeap(dest, pMT->GetNumInstanceFieldBytes());
}
// Initialize value class argument
void InitValueClassArg(ArgDestination *argDest, MethodTable *pMT);
#define SetObjectReference(_d,_r) SetObjectReferenceUnchecked(_d, _r)
#define CopyValueClass(_d,_s,_m) CopyValueClassUnchecked(_d,_s,_m)
#define CopyValueClassArg(_d,_s,_m,_o) CopyValueClassArgUnchecked(_d,_s,_m,_o)
#include <pshpack4.h>
// There are two basic kinds of array layouts in CLR
// ELEMENT_TYPE_ARRAY - a multidimensional array with lower bounds on the dims
// ELMENNT_TYPE_SZARRAY - A zero based single dimensional array
//
// In addition the layout of an array in memory is also affected by
// whether the method table is shared (eg in the case of arrays of object refs)
// or not. In the shared case, the array has to hold the type handle of
// the element type.
//
// ArrayBase encapuslates all of these details. In theory you should never
// have to peek inside this abstraction
//
class ArrayBase : public Object
{
friend class GCHeap;
friend class CObjectHeader;
friend class Object;
friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags);
friend OBJECTREF TryAllocateFrozenSzArray(MethodTable* pArrayMT, INT32 length);
friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags);
friend FCDECL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
friend FCDECL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
friend class JIT_TrialAlloc;
friend class CheckAsmOffsets;
friend struct _DacGlobals;
private:
// This MUST be the first field, so that it directly follows Object. This is because
// Object::GetSize() looks at m_NumComponents even though it may not be an array (the
// values is shifted out if not an array, so it's ok).
DWORD m_NumComponents;
#ifdef TARGET_64BIT
DWORD pad;
#endif // TARGET_64BIT
SVAL_DECL(INT32, s_arrayBoundsZero); // = 0
// What comes after this conceputally is:
// INT32 bounds[rank]; The bounds are only present for Multidimensional arrays
// INT32 lowerBounds[rank]; Valid indexes are lowerBounds[i] <= index[i] < lowerBounds[i] + bounds[i]
public:
// Get the element type for the array, this works whether the element
// type is stored in the array or not
inline TypeHandle GetArrayElementTypeHandle() const;
// Get the CorElementType for the elements in the array. Avoids creating a TypeHandle
inline CorElementType GetArrayElementType() const;
inline unsigned GetRank() const;
// Total element count for the array
inline DWORD GetNumComponents() const;
// Get pointer to elements, handles any number of dimensions
PTR_BYTE GetDataPtr(BOOL inGC = FALSE) const {
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
#ifdef _DEBUG
#ifndef DACCESS_COMPILE
EnableStressHeapHelper();
#endif
#endif
return dac_cast<PTR_BYTE>(this) +
GetDataPtrOffset(inGC ? GetGCSafeMethodTable() : GetMethodTable());
}
// The component size is actually 16-bit WORD, but this method is returning SIZE_T to ensure
// that SIZE_T is used everywhere for object size computation. It is necessary to support
// objects bigger than 2GB.
SIZE_T GetComponentSize() const {
WRAPPER_NO_CONTRACT;
MethodTable * pMT;
pMT = GetMethodTable();
_ASSERTE(pMT->HasComponentSize());
return pMT->RawGetComponentSize();
}
// Note that this can be a multidimensional array of rank 1
// (for example if we had a 1-D array with lower bounds
BOOL IsMultiDimArray() const {
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
return(GetMethodTable()->IsMultiDimArray());
}
// Get pointer to the beginning of the bounds (counts for each dim)
// Works for any array type
PTR_INT32 GetBoundsPtr() const {
WRAPPER_NO_CONTRACT;
MethodTable * pMT = GetMethodTable();
if (pMT->IsMultiDimArray())
{
return dac_cast<PTR_INT32>(
dac_cast<TADDR>(this) + sizeof(*this));
}
else
{
return dac_cast<PTR_INT32>(PTR_HOST_MEMBER_TADDR(ArrayBase, this,
m_NumComponents));
}
}
// Works for any array type
PTR_INT32 GetLowerBoundsPtr() const {
WRAPPER_NO_CONTRACT;
if (IsMultiDimArray())
{
// Lower bounds info is after total bounds info
// and total bounds info has rank elements
return GetBoundsPtr() + GetRank();
}
else
return dac_cast<PTR_INT32>(GVAL_ADDR(s_arrayBoundsZero));
}
static unsigned GetOffsetOfNumComponents() {
LIMITED_METHOD_CONTRACT;
return offsetof(ArrayBase, m_NumComponents);
}
inline static unsigned GetDataPtrOffset(MethodTable* pMT);
inline static unsigned GetBoundsOffset(MethodTable* pMT);
inline static unsigned GetLowerBoundsOffset(MethodTable* pMT);
friend struct ::cdac_data<ArrayBase>;
};
#ifndef DACCESS_COMPILE
template<>
struct cdac_data<ArrayBase>
{
static constexpr size_t m_NumComponents = offsetof(ArrayBase, m_NumComponents);
static constexpr INT32* ArrayBoundsZero = &ArrayBase::s_arrayBoundsZero;
};
#endif
//
// Template used to build all the non-object
// arrays of a single dimension
//
template < class KIND >
class Array : public ArrayBase
{
public:
typedef DPTR(KIND) PTR_KIND;
typedef DPTR(const KIND) PTR_CKIND;
KIND m_Array[1];
PTR_KIND GetDirectPointerToNonObjectElements()
{
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
// return m_Array;
return PTR_KIND(GetDataPtr()); // This also handles arrays of dim 1 with lower bounds present
}
PTR_CKIND GetDirectConstPointerToNonObjectElements() const
{
WRAPPER_NO_CONTRACT;
// return m_Array;
return PTR_CKIND(GetDataPtr()); // This also handles arrays of dim 1 with lower bounds present
}
};
// Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
class PtrArray : public ArrayBase
{
friend class GCHeap;
friend class ClrDataAccess;
friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, DWORD flags);
friend class JIT_TrialAlloc;
friend class CheckAsmOffsets;
public:
TypeHandle GetArrayElementTypeHandle()
{
LIMITED_METHOD_CONTRACT;
return GetMethodTable()->GetArrayElementTypeHandle();
}
PTR_OBJECTREF GetDataPtr()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
return dac_cast<PTR_OBJECTREF>(dac_cast<PTR_BYTE>(this) + GetDataOffset());
}
static SIZE_T GetDataOffset()
{
LIMITED_METHOD_CONTRACT;
return offsetof(PtrArray, m_Array);
}
void SetAt(SIZE_T i, OBJECTREF ref)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
}
CONTRACTL_END;
_ASSERTE(i < GetNumComponents());
SetObjectReference(m_Array + i, ref);
}
void ClearAt(SIZE_T i)
{
WRAPPER_NO_CONTRACT;
_ASSERTE(i < GetNumComponents());
ClearObjectReference(m_Array + i);
}
OBJECTREF GetAt(SIZE_T i)
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
_ASSERTE(i < GetNumComponents());
// DAC doesn't know the true size of this array
// the compiler thinks it is size 1, but really it is size N hanging off the structure
#ifndef DACCESS_COMPILE
return m_Array[i];
#else
TADDR arrayTargetAddress = dac_cast<TADDR>(this) + offsetof(PtrArray, m_Array);
__ArrayDPtr<OBJECTREF> targetArray = dac_cast< __ArrayDPtr<OBJECTREF> >(arrayTargetAddress);
return targetArray[i];
#endif
}
friend class StubLinkerCPU;
friend class ArrayOpLinker;
public:
OBJECTREF m_Array[1];
};
#define OFFSETOF__PtrArray__m_Array_ ARRAYBASE_SIZE
/* a TypedByRef is a structure that is used to implement VB's BYREF variants.
it is basically a tuple of an address of some data along with a TypeHandle
that indicates the type of the address */
class TypedByRef
{
public:
PTR_VOID data;
TypeHandle type;
};
typedef DPTR(TypedByRef) PTR_TypedByRef;
typedef Array<CLR_I1> I1Array;
typedef Array<CLR_I2> I2Array;
typedef Array<CLR_I4> I4Array;
typedef Array<CLR_I8> I8Array;
typedef Array<CLR_R4> R4Array;
typedef Array<CLR_R8> R8Array;
typedef Array<CLR_U1> U1Array;
typedef Array<CLR_U1> BOOLArray;
typedef Array<CLR_U2> U2Array;
typedef Array<CLR_CHAR> CHARArray;
typedef Array<CLR_U4> U4Array;
typedef Array<CLR_U8> U8Array;
typedef Array<UPTR> UPTRArray;
typedef PtrArray PTRArray;
typedef DPTR(I1Array) PTR_I1Array;
typedef DPTR(I2Array) PTR_I2Array;
typedef DPTR(I4Array) PTR_I4Array;
typedef DPTR(I8Array) PTR_I8Array;
typedef DPTR(R4Array) PTR_R4Array;
typedef DPTR(R8Array) PTR_R8Array;
typedef DPTR(U1Array) PTR_U1Array;
typedef DPTR(BOOLArray) PTR_BOOLArray;
typedef DPTR(U2Array) PTR_U2Array;
typedef DPTR(CHARArray) PTR_CHARArray;
typedef DPTR(U4Array) PTR_U4Array;
typedef DPTR(U8Array) PTR_U8Array;
typedef DPTR(UPTRArray) PTR_UPTRArray;
typedef DPTR(PTRArray) PTR_PTRArray;
class StringObject;
#ifdef USE_CHECKED_OBJECTREFS
typedef REF<ArrayBase> BASEARRAYREF;
typedef REF<I1Array> I1ARRAYREF;
typedef REF<I2Array> I2ARRAYREF;
typedef REF<I4Array> I4ARRAYREF;
typedef REF<I8Array> I8ARRAYREF;
typedef REF<R4Array> R4ARRAYREF;
typedef REF<R8Array> R8ARRAYREF;
typedef REF<U1Array> U1ARRAYREF;
typedef REF<BOOLArray> BOOLARRAYREF;
typedef REF<U2Array> U2ARRAYREF;
typedef REF<U4Array> U4ARRAYREF;
typedef REF<U8Array> U8ARRAYREF;
typedef REF<UPTRArray> UPTRARRAYREF;
typedef REF<CHARArray> CHARARRAYREF;
typedef REF<PTRArray> PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
typedef REF<StringObject> STRINGREF;
#else // USE_CHECKED_OBJECTREFS
typedef PTR_ArrayBase BASEARRAYREF;
typedef PTR_I1Array I1ARRAYREF;
typedef PTR_I2Array I2ARRAYREF;
typedef PTR_I4Array I4ARRAYREF;
typedef PTR_I8Array I8ARRAYREF;
typedef PTR_R4Array R4ARRAYREF;
typedef PTR_R8Array R8ARRAYREF;
typedef PTR_U1Array U1ARRAYREF;
typedef PTR_BOOLArray BOOLARRAYREF;
typedef PTR_U2Array U2ARRAYREF;
typedef PTR_U4Array U4ARRAYREF;
typedef PTR_U8Array U8ARRAYREF;
typedef PTR_UPTRArray UPTRARRAYREF;
typedef PTR_CHARArray CHARARRAYREF;
typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
typedef PTR_StringObject STRINGREF;
#endif // USE_CHECKED_OBJECTREFS
#include <poppack.h>
/*
* StringObject
*
* Special String implementation for performance.
*
* m_StringLength - Length of string in number of WCHARs
* m_FirstChar - The string buffer
*
*/
class StringObject : public Object
{
#ifdef DACCESS_COMPILE
friend class ClrDataAccess;
#endif
friend class GCHeap;
friend class JIT_TrialAlloc;
friend class CheckAsmOffsets;
friend class COMString;
private:
DWORD m_StringLength;
WCHAR m_FirstChar;
public:
VOID SetStringLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; }
protected:
StringObject() {LIMITED_METHOD_CONTRACT; }
~StringObject() {LIMITED_METHOD_CONTRACT; }
public:
static DWORD GetBaseSize();
static SIZE_T GetSize(DWORD stringLength);
DWORD GetStringLength() { LIMITED_METHOD_DAC_CONTRACT; return( m_StringLength );}
WCHAR* GetBuffer() { LIMITED_METHOD_CONTRACT; return (WCHAR*)( dac_cast<TADDR>(this) + offsetof(StringObject, m_FirstChar) ); }
static UINT GetBufferOffset()
{
LIMITED_METHOD_DAC_CONTRACT;
return (UINT)(offsetof(StringObject, m_FirstChar));
}
static UINT GetStringLengthOffset()
{
LIMITED_METHOD_CONTRACT;
return (UINT)(offsetof(StringObject, m_StringLength));
}
VOID GetSString(SString &result)
{
WRAPPER_NO_CONTRACT;
result.Set(GetBuffer(), GetStringLength());
}
//========================================================================
// Creates a System.String object. All the functions that take a length
// or a count of bytes will add the null terminator after length
// characters. So this means that if you have a string that has 5
// characters and the null terminator you should pass in 5 and NOT 6.
//========================================================================
static STRINGREF NewString(int length);
static STRINGREF NewString(int length, BOOL bHasTrailByte);
static STRINGREF NewString(const WCHAR *pwsz);
static STRINGREF NewString(const WCHAR *pwsz, int length);
static STRINGREF NewString(LPCUTF8 psz);
static STRINGREF NewString(LPCUTF8 psz, int cBytes);
static STRINGREF GetEmptyString();
static STRINGREF* GetEmptyStringRefPtr(void** pinnedString);
static STRINGREF* InitEmptyStringRefPtr();
BOOL HasTrailByte();
BOOL GetTrailByte(BYTE *bTrailByte);
BOOL SetTrailByte(BYTE bTrailByte);
/*=================RefInterpretGetStringValuesDangerousForGC======================
**N.B.: This performs no range checking and relies on the caller to have done this.
**Args: (IN)ref -- the String to be interpretted.
** (OUT)chars -- a pointer to the characters in the buffer.
** (OUT)length -- a pointer to the length of the buffer.
**Returns: void.
**Exceptions: None.
==============================================================================*/
// !!!! If you use this function, you have to be careful because chars is a pointer
// !!!! to the data buffer of ref. If GC happens after this call, you need to make
// !!!! sure that you have a pin handle on ref, or use GCPROTECT_BEGINPINNING on ref.
void RefInterpretGetStringValuesDangerousForGC(_Outptr_result_buffer_(*length + 1) WCHAR **chars, int *length) {
WRAPPER_NO_CONTRACT;
_ASSERTE(GetGCSafeMethodTable() == g_pStringClass);
*length = GetStringLength();
*chars = GetBuffer();
#ifdef _DEBUG
EnableStressHeapHelper();
#endif
}
private:
static STRINGREF* EmptyStringRefPtr;
static bool EmptyStringIsFrozen;
friend struct ::cdac_data<StringObject>;
};
template<>
struct cdac_data<StringObject>
{
static constexpr size_t m_FirstChar = offsetof(StringObject, m_FirstChar);
static constexpr size_t m_StringLength = offsetof(StringObject, m_StringLength);
};
/*================================GetEmptyString================================
**Get a reference to the empty string. If we haven't already gotten one, we
**query the String class for a pointer to the empty string that we know was
**created at startup.
**
**Args: None
**Returns: A STRINGREF to the EmptyString
**Exceptions: None
==============================================================================*/
inline STRINGREF StringObject::GetEmptyString() {
CONTRACTL {
THROWS;
MODE_COOPERATIVE;
GC_TRIGGERS;
} CONTRACTL_END;
STRINGREF* refptr = EmptyStringRefPtr;
//If we've never gotten a reference to the EmptyString, we need to go get one.
if (refptr==NULL) {
refptr = InitEmptyStringRefPtr();
}
//We've already have a reference to the EmptyString, so we can just return it.
return *refptr;
}
inline STRINGREF* StringObject::GetEmptyStringRefPtr(void** pinnedString) {
CONTRACTL {
THROWS;
MODE_ANY;
GC_TRIGGERS;
} CONTRACTL_END;
STRINGREF* refptr = EmptyStringRefPtr;
//If we've never gotten a reference to the EmptyString, we need to go get one.
if (refptr == nullptr)
{