summaryrefslogtreecommitdiff
path: root/src/vm/siginfo.hpp
blob: 19afe5c25da923bdb0fb6a687d6c86e7b96f5560 (plain)
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// siginfo.hpp
//


#ifndef _H_SIGINFO
#define _H_SIGINFO


#include "util.hpp"
#include "vars.hpp"
#include "clsload.hpp"
#include "sigparser.h"
#include "zapsig.h"
#include "threads.h"

#include "eecontract.h"
#include "typectxt.h"

//---------------------------------------------------------------------------------------
// These macros define how arguments are mapped to the stack in the managed calling convention.
// We assume to be walking a method's signature left-to-right, in the virtual calling convention.
// See MethodDesc::Call for details on this virtual calling convention.
// These macros tell us whether the arguments we see as we proceed with the signature walk are mapped
//   to increasing or decreasing stack addresses. This is valid only for arguments that go on the stack.
//---------------------------------------------------------------------------------------
#if defined(_TARGET_X86_)
#define STACK_GROWS_DOWN_ON_ARGS_WALK
#else
#define STACK_GROWS_UP_ON_ARGS_WALK
#endif

BOOL IsTypeRefOrDef(LPCSTR szClassName, Module *pModule, mdToken token);

struct ElementTypeInfo {
#ifdef _DEBUG
    int            m_elementType;
#endif
    int            m_cbSize;
    CorInfoGCType  m_gc         : 3;
    int            m_enregister : 1;
};
extern const ElementTypeInfo gElementTypeInfo[];

unsigned GetSizeForCorElementType(CorElementType etyp);
const ElementTypeInfo* GetElementTypeInfo(CorElementType etyp);

class SigBuilder;
class ArgDestination;

typedef const struct HardCodedMetaSig *LPHARDCODEDMETASIG;

//@GENERICS: flags returned from IsPolyType indicating the presence or absence of class and
// method type parameters in a type whose instantiation cannot be determined at JIT-compile time
enum VarKind
{
  hasNoVars = 0x0000,
  hasClassVar = 0x0001,
  hasMethodVar = 0x0002,
  hasSharableClassVar = 0x0004,
  hasSharableMethodVar = 0x0008,
  hasAnyVarsMask = 0x0003,
  hasSharableVarsMask = 0x000c
};

//---------------------------------------------------------------------------------------

struct ScanContext;
typedef void promote_func(PTR_PTR_Object, ScanContext*, uint32_t);
typedef void promote_carefully_func(promote_func*, PTR_PTR_Object, ScanContext*, uint32_t);

void PromoteCarefully(promote_func   fn,
                      PTR_PTR_Object obj, 
                      ScanContext*   sc, 
                      uint32_t       flags = GC_CALL_INTERIOR);

class LoaderAllocator;
void GcReportLoaderAllocator(promote_func* fn, ScanContext* sc, LoaderAllocator *pLoaderAllocator);

//---------------------------------------------------------------------------------------
// 
// Encapsulates how compressed integers and typeref tokens are encoded into
// a bytestream.
//
// As you use this class please understand the implicit normalizations 
// on the CorElementType's returned by the various methods, especially
// for variable types (e.g. !0 in generic signatures), string types
// (i.e. E_T_STRING), object types (E_T_OBJECT), constructed types 
// (e.g. List<int>) and enums.
// 
class SigPointer : public SigParser
{
    friend class MetaSig;

public:
    // Constructor.
    SigPointer() { LIMITED_METHOD_DAC_CONTRACT; }
    
    // Copy constructor.
    SigPointer(const SigPointer & sig) : SigParser(sig)
    {
        WRAPPER_NO_CONTRACT;
    }

    SigPointer(const SigParser & sig) : SigParser(sig)
    {
        WRAPPER_NO_CONTRACT;
    }

    // Signature from a pointer. INSECURE!!!
    // WARNING: Should not be used as it is insecure, because we do not have size of the signature and 
    // therefore we can read behind the end of buffer/file.
    FORCEINLINE 
    SigPointer(PCCOR_SIGNATURE ptr) : SigParser(ptr)
    {
        WRAPPER_NO_CONTRACT;
    }
    
    // Signature from a pointer and size.
    FORCEINLINE 
    SigPointer(PCCOR_SIGNATURE ptr, DWORD len) : SigParser(ptr, len)
    {
        WRAPPER_NO_CONTRACT;
    }


    //=========================================================================
    // The RAW interface for reading signatures.  You see exactly the signature,
    // apart from custom modifiers which for historical reasons tend to get eaten.
    //
    // DO NOT USE THESE METHODS UNLESS YOU'RE TOTALLY SURE YOU WANT
    // THE RAW signature.  You nearly always want GetElemTypeClosed() or 
    // PeekElemTypeClosed() or one of the MetaSig functions.  See the notes above.
    // These functions will return E_T_INTERNAL, E_T_VAR, E_T_MVAR and such
    // so the caller must be able to deal with those
    //=========================================================================


        void ConvertToInternalExactlyOne(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
        void ConvertToInternalSignature(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);


    //=========================================================================
    // The CLOSED interface for reading signatures.  With the following
    // methods you see the signature "as if" all type variables are 
    // replaced by the given instantiations.  However, no type loads happen.
    //
    // In general this is what you want to use if the signature may include
    // generic type variables.  Even if you know it doesn't you can always
    // pass in NULL for the instantiations and put a comment to that effect.
    //
    // The CLOSED api also hides E_T_INTERNAL by return E_T_CLASS or E_T_VALUETYPE
    // appropriately (as directed by the TypeHandle following E_T_INTERNAL)
    //=========================================================================

        // The CorElementTypes returned correspond
        // to those returned by TypeHandle::GetSignatureCorElementType.
        CorElementType PeekElemTypeClosed(Module *pModule, const SigTypeContext *pTypeContext) const;

        //------------------------------------------------------------------------
        // Fetch the token for a CLASS, VALUETYPE or GENRICINST, or a type
        // variable instantiatied to be one of these, taking into account
        // the given instantiations.
        //
        // SigPointer should be in a position that satisfies
        //  ptr.PeekElemTypeClosed(pTypeContext) = ELEMENT_TYPE_VALUETYPE
        //
        // A type ref or def is returned.  For an instantiated generic struct
        // this will return the token for the generic class, e.g. for a signature
        // for "struct Pair<int,int>" this will return a token for "Pair".
        //
        // The token will only make sense in the context of the module where 
        // the signature occurs.
        //
        // WARNING: This api will return a mdTokenNil for a E_T_VALUETYPE obtained
        //          from a E_T_INTERNAL, as the token is meaningless in that case
        //          Users of this api must be prepared to deal with a null token
        //------------------------------------------------------------------------
        mdTypeRef PeekValueTypeTokenClosed(Module *pModule, const SigTypeContext *pTypeContext, Module **ppModuleOfToken) const;


    //=========================================================================
    // The INTERNAL-NORMALIZED interface for reading signatures.  You see 
    // information concerning the signature, but taking into account normalizations
    // performed for layout of data, e.g. enums and one-field VCs.
    //=========================================================================

        // The CorElementTypes returned correspond
        // to those returned by TypeHandle::GetInternalCorElementType.
        CorElementType PeekElemTypeNormalized(Module* pModule, const SigTypeContext *pTypeContext, TypeHandle * pthValueType = NULL) const;

        //------------------------------------------------------------------------
        // Assumes that the SigPointer points to the start of an element type.
        // Returns size of that element in bytes. This is the minimum size that a
        // field of this type would occupy inside an object. 
        //------------------------------------------------------------------------
        UINT SizeOf(Module* pModule, const SigTypeContext *pTypeContext) const;
    
private:

        // SigPointer should be just after E_T_VAR or E_T_MVAR
        TypeHandle GetTypeVariable(CorElementType et,const SigTypeContext *pTypeContext);
        TypeHandle GetTypeVariableThrowing(Module *pModule, 
                                           CorElementType et,
                                           ClassLoader::LoadTypesFlag fLoadTypes,
                                           const SigTypeContext *pTypeContext);

        // Parse type following E_T_GENERICINST
        TypeHandle GetGenericInstType(Module *        pModule,
                                      ClassLoader::LoadTypesFlag = ClassLoader::LoadTypes,
                                      ClassLoadLevel level = CLASS_LOADED,
                                      const ZapSig::Context *pZapSigContext = NULL);

public:

        //------------------------------------------------------------------------
        // Assuming that the SigPointer points the start if an element type.
        // Use SigTypeContext to fill in any  type parameters
        //
        // Also advance the pointer to after the element type.
        //------------------------------------------------------------------------

        // OBSOLETE - Use GetTypeHandleThrowing()
        TypeHandle GetTypeHandleNT(Module* pModule,
                                   const SigTypeContext *pTypeContext) const;

        // pTypeContext indicates how to instantiate any generic type parameters we come 
        // However, first we implicitly apply the substitution pSubst to the metadata if pSubst is supplied.
        // That is, if the metadata contains a type variable "!0" then we first look up
        // !0 in pSubst to produce another item of metdata and continue processing.  
        // If pSubst is empty then we look up !0 in the pTypeContext to produce a final
        // type handle.  If any of these are out of range we throw an exception.
        //
        // The level is the level to which the result type will be loaded (see classloadlevel.h)
        // If dropGenericArgumentLevel is TRUE, and the metadata represents an instantiated generic type,
        // then generic arguments to the generic type will be loaded one level lower. (This is used by the
        // class loader to avoid looping on definitions such as class C : D<C>)
        //   
        // If dropGenericArgumentLevel is TRUE and
        // level=CLASS_LOAD_APPROXPARENTS, then the instantiated
        // generic type is "approximated" in the following way: 
        // - for generic interfaces, the generic type (uninstantiated) is returned 
        // - for other generic instantiations, System.Object is used in place of any reference types
        //   occurring in the type arguments 
        // This semantics is used by the class loader to load tricky recursive definitions in phases
        // (e.g. class C : D<C>, or struct S : I<S>)
        TypeHandle GetTypeHandleThrowing(Module* pModule,
                                         const SigTypeContext *pTypeContext, 
                                         ClassLoader::LoadTypesFlag fLoadTypes = ClassLoader::LoadTypes,
                                         ClassLoadLevel level = CLASS_LOADED,
                                         BOOL dropGenericArgumentLevel = FALSE,
                                         const Substitution *pSubst = NULL,
                                         const ZapSig::Context *pZapSigContext = NULL) const;

public:
        //------------------------------------------------------------------------
        // Does this type contain class or method type parameters whose instantiation cannot
        // be determined at JIT-compile time from the instantiations in the method context? 
        // Return a combination of hasClassVar and hasMethodVar flags.
        //
        // Example: class C<A,B> containing instance method m<T,U>
        // Suppose that the method context is C<float,string>::m<double,object>
        // Then the type Dict<!0,!!0> is considered to have *no* "polymorphic" type parameters because 
        // !0 is known to be float and !!0 is known to be double
        // But Dict<!1,!!1> has polymorphic class *and* method type parameters because both
        // !1=string and !!1=object are reference types and so code using these can be shared with
        // other reference instantiations.
        //------------------------------------------------------------------------
        VarKind IsPolyType(const SigTypeContext *pTypeContext) const;

        //------------------------------------------------------------------------
        // Tests if the element type is a System.String. Accepts
        // either ELEMENT_TYPE_STRING or ELEMENT_TYPE_CLASS encoding.
        //------------------------------------------------------------------------
        BOOL IsStringType(Module* pModule, const SigTypeContext *pTypeContext) const;
        BOOL IsStringTypeThrowing(Module* pModule, const SigTypeContext *pTypeContext) const;

private:
        BOOL IsStringTypeHelper(Module* pModule, const SigTypeContext* pTypeContext, BOOL fThrow) const;

public:


        //------------------------------------------------------------------------
        // Tests if the element class name is szClassName. 
        //------------------------------------------------------------------------
        BOOL IsClass(Module* pModule, LPCUTF8 szClassName, const SigTypeContext *pTypeContext = NULL) const;
        BOOL IsClassThrowing(Module* pModule, LPCUTF8 szClassName, const SigTypeContext *pTypeContext = NULL) const;

private:
        BOOL IsClassHelper(Module* pModule, LPCUTF8 szClassName, const SigTypeContext* pTypeContext, BOOL fThrow) const;

public:
        //------------------------------------------------------------------------
        // Tests for the existence of a custom modifier
        //------------------------------------------------------------------------
        BOOL HasCustomModifier(Module *pModule, LPCSTR szModName, CorElementType cmodtype) const;

        //------------------------------------------------------------------------
        // Tests for ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUETYPE followed by a TypeDef,
        // and returns the TypeDef
        //------------------------------------------------------------------------
        BOOL IsTypeDef(mdTypeDef* pTypeDef) const;

};  // class SigPointer

// forward declarations needed for the friends declared in Signature
struct FrameInfo;
struct VASigCookie;
#if defined(DACCESS_COMPILE)
class  DacDbiInterfaceImpl;
#endif // DACCESS_COMPILE

//---------------------------------------------------------------------------------------
//
// Currently, PCCOR_SIGNATURE is used all over the runtime to represent a signature, which is just 
// an array of bytes.  The problem with PCCOR_SIGNATURE is that it doesn't tell you the length of 
// the signature (i.e. the number of bytes in the array).  This is particularly troublesome for DAC, 
// which needs to know how much memory to grab from out of process.  This class is an encapsulation 
// over PCCOR_SIGNATURE AND the length of the signature it points to.
//
// Notes:
//    This class is meant to be read-only.  Moreover, preferrably we should never read the raw 
//    PCCOR_SIGNATURE pointer directly, but there are likely some cases where it is inevitable.
//    We should keep these to a minimum.
//
//    We should move over to Signature instead of PCCOR_SIGNATURE.  
//
//    To get a Signature, you can create one yourself by using a constructor.  However, it's recommended 
//    that you check whether the Signature should be constructed at a lower level.  For example, instead of 
//    creating a Signature in FramedMethodFrame::PromoteCallerStackWalker(), we should add a member function
//    to MethodDesc to return a Signature.
//

class Signature
{
public:
    // create an empty Signature
    Signature();

    // this is the primary constructor
    Signature(PCCOR_SIGNATURE pSig,
              DWORD           cbSig);

    // check whether the signature is empty, i.e. have a NULL PCCOR_SIGNATURE
    BOOL IsEmpty() const;

    // create a SigParser from the signature
    SigParser CreateSigParser() const;

    // create a SigPointer from the signature
    SigPointer CreateSigPointer() const;

    // pretty print the signature
    void PrettyPrint(const CHAR *        pszMethodName,
                     CQuickBytes *       pqbOut,
                     IMDInternalImport * pIMDI) const;

    // retrieve the raw PCCOR_SIGNATURE pointer
    PCCOR_SIGNATURE GetRawSig() const;

    // retrieve the length of the signature
    DWORD           GetRawSigLen() const;

private:
    PCCOR_SIGNATURE m_pSig;
    DWORD           m_cbSig;
};  // class Signature


#ifdef _DEBUG
#define MAX_CACHED_SIG_SIZE     3       // To excercize non-cached code path
#else
#define MAX_CACHED_SIG_SIZE     15
#endif


//---------------------------------------------------------------------------------------
// 
// A substitution represents the composition of several formal type instantiations
// It is used when matching formal signatures across the inheritance hierarchy.
//
// It has the form of a linked list:
//   [mod_1, <inst_1>] ->
//   [mod_2, <inst_2>] ->
//   ...
//   [mod_n, <inst_n>]
//
// Here the types in <inst_1> must be resolved in the scope of module mod_1 but
// may contain type variables instantiated by <inst_2>
// ...
// and the types in <inst_(n-1)> must be resolved in the scope of mould mod_(n-1) but
// may contain type variables instantiated by <inst_n>
//
// Any type variables in <inst_n> are treated as "free".
// 
class Substitution
{
private:
    Module *             m_pModule; // Module in which instantiation lives (needed to resolve typerefs)
    SigPointer           m_sigInst;
    const Substitution * m_pNext;

public:
    Substitution()
    { 
        LIMITED_METHOD_CONTRACT;
        m_pModule = NULL; 
        m_pNext = NULL;
    }
    
    Substitution(
        Module *             pModuleArg, 
        const SigPointer &   sigInst, 
        const Substitution * pNextSubstitution)
    { 
        LIMITED_METHOD_CONTRACT;
        m_pModule = pModuleArg; 
        m_sigInst = sigInst;
        m_pNext = pNextSubstitution;
    }

    Substitution(
        mdToken              parentTypeDefOrRefOrSpec, 
        Module *             pModuleArg, 
        const Substitution * nextArg);
    
    Substitution(const Substitution & subst)
    { 
        LIMITED_METHOD_CONTRACT;
        m_pModule = subst.m_pModule; 
        m_sigInst = subst.m_sigInst;
        m_pNext = subst.m_pNext;
    }
    void DeleteChain();

    Module * GetModule() const { LIMITED_METHOD_DAC_CONTRACT; return m_pModule; }
    const Substitution * GetNext() const { LIMITED_METHOD_DAC_CONTRACT; return m_pNext; }
    const SigPointer & GetInst() const { LIMITED_METHOD_DAC_CONTRACT; return m_sigInst; }
    DWORD GetLength() const;
    
    void CopyToArray(Substitution * pTarget /* must have type Substitution[GetLength()] */ ) const;

};  // class Substitution

//---------------------------------------------------------------------------------------
// 
// Linked list that records what tokens are currently being compared for equivalence. This prevents
// infinite recursion when types refer to each other in a cycle, e.g. a delegate that takes itself as
// a parameter or a struct that declares a field of itself (illegal but we don't know at this point).
// 
class TokenPairList
{
public:
    // Chain using this constructor when comparing two typedefs for equivalence.
    TokenPairList(mdToken token1, Module *pModule1, mdToken token2, Module *pModule2, TokenPairList *pNext)
        : m_token1(token1), m_token2(token2),
          m_pModule1(pModule1), m_pModule2(pModule2),
          m_bInTypeEquivalenceForbiddenScope(pNext == NULL ? FALSE : pNext->m_bInTypeEquivalenceForbiddenScope),
          m_pNext(pNext)
    { LIMITED_METHOD_CONTRACT; }

    static BOOL Exists(TokenPairList *pList, mdToken token1, Module *pModule1, mdToken token2, Module *pModule2)
    {
        LIMITED_METHOD_CONTRACT;
        while (pList != NULL)
        {
            if (pList->m_token1 == token1 && pList->m_pModule1 == pModule1 &&
                pList->m_token2 == token2 && pList->m_pModule2 == pModule2)
                return TRUE;

            if (pList->m_token1 == token2 && pList->m_pModule1 == pModule2 &&
                pList->m_token2 == token1 && pList->m_pModule2 == pModule1)
                return TRUE;

            pList = pList->m_pNext;
        }
        return FALSE;
    }

    static BOOL InTypeEquivalenceForbiddenScope(TokenPairList *pList)
    {
        return (pList == NULL ? FALSE : pList->m_bInTypeEquivalenceForbiddenScope);
    }

    // Chain using this method when comparing type specs.
    static TokenPairList AdjustForTypeSpec(TokenPairList *pTemplate, Module *pTypeSpecModule, PCCOR_SIGNATURE pTypeSpecSig, DWORD cbTypeSpecSig);
    static TokenPairList AdjustForTypeEquivalenceForbiddenScope(TokenPairList *pTemplate);

private:
    TokenPairList(TokenPairList *pTemplate)
        : m_token1(pTemplate ? pTemplate->m_token1 : mdTokenNil),
          m_token2(pTemplate ? pTemplate->m_token2 : mdTokenNil),
          m_pModule1(pTemplate ? pTemplate->m_pModule1 : NULL),
          m_pModule2(pTemplate ? pTemplate->m_pModule2 : NULL),
          m_bInTypeEquivalenceForbiddenScope(pTemplate ? pTemplate->m_bInTypeEquivalenceForbiddenScope : FALSE),
          m_pNext(pTemplate ? pTemplate->m_pNext : NULL)
    { LIMITED_METHOD_CONTRACT; }

    mdToken m_token1, m_token2;
    Module *m_pModule1, *m_pModule2;
    BOOL m_bInTypeEquivalenceForbiddenScope;
    TokenPairList *m_pNext;
};  // class TokenPairList

//---------------------------------------------------------------------------------------
// 
class MetaSig
{
    public:
        enum MetaSigKind { 
            sigMember, 
            sigLocalVars,
            sigField,
            };

        //------------------------------------------------------------------
        // Common init used by other constructors
        //------------------------------------------------------------------
        void Init(PCCOR_SIGNATURE szMetaSig,
                DWORD cbMetaSig,
                Module* pModule, 
                const SigTypeContext *pTypeContext, 
                MetaSigKind kind = sigMember);

        //------------------------------------------------------------------
        // Constructor. Warning: Does NOT make a copy of szMetaSig.
        //
        // The instantiations are used to fill in type variables on calls
        // to PeekArg, GetReturnType, GetNextArg, GetTypeHandle, GetRetTypeHandle and
        // so on.  
        //
        // Please make sure you know what you're doing by leaving classInst and methodInst to default NULL
        // Are you sure the signature cannot contain type parameters (E_T_VAR, E_T_MVAR)?
        //------------------------------------------------------------------
        MetaSig(PCCOR_SIGNATURE szMetaSig, 
                DWORD cbMetaSig,
                Module* pModule, 
                const SigTypeContext *pTypeContext, 
                MetaSigKind kind = sigMember)
        {
            WRAPPER_NO_CONTRACT;
            Init(szMetaSig, cbMetaSig, pModule, pTypeContext, kind);
        }

        // this is just a variation of the previous constructor to ease the transition to Signature
        MetaSig(const Signature &      signature,
                Module               * pModule, 
                const SigTypeContext * pTypeContext, 
                MetaSigKind            kind = sigMember)
        {
            WRAPPER_NO_CONTRACT;
            Init(signature.GetRawSig(), signature.GetRawSigLen(), pModule, pTypeContext, kind);
        }

        // The following create MetaSigs for parsing the signature of the given method.  
        // They are identical except that they give slightly different 
        // type contexts.  (Note the type context will only be relevant if we 
        // are parsing a method on an array type or on a generic type.)
        // See TypeCtxt.h for more details.
        // If declaringType is omitted then a *representative* instantiation may be obtained from pMD or pFD
        MetaSig(MethodDesc *pMD, TypeHandle declaringType = TypeHandle());
        MetaSig(MethodDesc *pMD, Instantiation classInst, Instantiation methodInst);

        MetaSig(FieldDesc *pFD, TypeHandle declaringType = TypeHandle());

        // Used to avoid touching metadata for mscorlib methods.  Nb. only use for non-generic methods.
        MetaSig(BinderMethodID id);

        MetaSig(LPHARDCODEDMETASIG pwzMetaSig);

        //------------------------------------------------------------------
        // Returns type of current argument index. Returns ELEMENT_TYPE_END
        // if already past end of arguments.
        //------------------------------------------------------------------
        CorElementType PeekArg() const;

        //------------------------------------------------------------------
        // Returns type of current argument index. Returns ELEMENT_TYPE_END
        // if already past end of arguments.
        //------------------------------------------------------------------
        CorElementType PeekArgNormalized(TypeHandle * pthValueType = NULL) const;

        //------------------------------------------------------------------
        // Returns type of current argument, then advances the argument
        // index. Returns ELEMENT_TYPE_END if already past end of arguments.
        // This method updates m_pLastType
        //------------------------------------------------------------------
        CorElementType NextArg();

        //------------------------------------------------------------------
        // Advance the argument index. Can be used with GetArgProps() to
        // to iterate when you do not have a valid type context.
        // This method updates m_pLastType
        //------------------------------------------------------------------
        void SkipArg();

        //------------------------------------------------------------------
        // Returns a read-only SigPointer for the m_pLastType set by one
        // of NextArg() or SkipArg()
        // This allows extracting more information for complex types.
        //------------------------------------------------------------------
        const SigPointer & GetArgProps() const
        {
            LIMITED_METHOD_CONTRACT;
            return m_pLastType;
        }

        //------------------------------------------------------------------
        // Returns a read-only SigPointer for the return type.
        // This allows extracting more information for complex types.
        //------------------------------------------------------------------
        const SigPointer & GetReturnProps() const
        {
            LIMITED_METHOD_CONTRACT;
            return m_pRetType;
        }


        //------------------------------------------------------------------------
        // Returns # of arguments. Does not count the return value.
        // Does not count the "this" argument (which is not reflected om the
        // sig.) 64-bit arguments are counted as one argument.
        //------------------------------------------------------------------------
        UINT NumFixedArgs()
        {
            LIMITED_METHOD_DAC_CONTRACT;
            return m_nArgs;
        }
        
        //----------------------------------------------------------
        // Returns the calling convention (see IMAGE_CEE_CS_CALLCONV_*
        // defines in cor.h) - throws.
        //----------------------------------------------------------
        static BYTE GetCallingConvention(
            Module          *pModule, 
            const Signature &signature)
        {
            CONTRACTL
            {
                THROWS;
                GC_NOTRIGGER;
                MODE_ANY;
                SUPPORTS_DAC;
            }
            CONTRACTL_END
            
            PCCOR_SIGNATURE pSig = signature.GetRawSig();
            
            if (signature.GetRawSigLen() < 1)
            {
                ThrowHR(COR_E_BADIMAGEFORMAT);
            }
            return (BYTE)(IMAGE_CEE_CS_CALLCONV_MASK & CorSigUncompressCallingConv(/*modifies*/pSig));
        }
        
        //----------------------------------------------------------
        // Returns the calling convention (see IMAGE_CEE_CS_CALLCONV_*
        // defines in cor.h) - doesn't throw.
        //----------------------------------------------------------
        __checkReturn 
        static HRESULT GetCallingConvention_NoThrow(
            Module          *pModule, 
            const Signature &signature, 
            BYTE            *pbCallingConvention)
        {
            CONTRACTL
            {
                NOTHROW;
                GC_NOTRIGGER;
                MODE_ANY;
                SUPPORTS_DAC;
            }
            CONTRACTL_END
            
            PCCOR_SIGNATURE pSig = signature.GetRawSig();
            
            if (signature.GetRawSigLen() < 1)
            {
                *pbCallingConvention = 0;
                return COR_E_BADIMAGEFORMAT;
            }
            *pbCallingConvention = (BYTE)(IMAGE_CEE_CS_CALLCONV_MASK & CorSigUncompressCallingConv(/*modifies*/pSig));
            return S_OK;
        }
        
        //----------------------------------------------------------
        // Returns the calling convention (see IMAGE_CEE_CS_CALLCONV_*
        // defines in cor.h)
        //----------------------------------------------------------
        BYTE GetCallingConvention()
        {
            LIMITED_METHOD_CONTRACT;
            SUPPORTS_DAC;
            return m_CallConv & IMAGE_CEE_CS_CALLCONV_MASK; 
        }

        //----------------------------------------------------------
        // Returns the calling convention & flags (see IMAGE_CEE_CS_CALLCONV_*
        // defines in cor.h)
        //----------------------------------------------------------
        BYTE GetCallingConventionInfo()
        {
            LIMITED_METHOD_DAC_CONTRACT;

            return m_CallConv;
        }

        //----------------------------------------------------------
        // Has a 'this' pointer?
        //----------------------------------------------------------
        BOOL HasThis()
        {
            LIMITED_METHOD_CONTRACT;

            return m_CallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS;
        }  

        //----------------------------------------------------------
        // Has a explicit 'this' pointer?
        //----------------------------------------------------------
        BOOL HasExplicitThis()
        {
            LIMITED_METHOD_CONTRACT;

            return m_CallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS;
        }  
                
        //----------------------------------------------------------
        // Is a generic method with explicit arity?
        //----------------------------------------------------------
        BOOL IsGenericMethod()
        {
            LIMITED_METHOD_CONTRACT;
            return m_CallConv & IMAGE_CEE_CS_CALLCONV_GENERIC;
        }  
        
        //----------------------------------------------------------
        // Is vararg?
        //----------------------------------------------------------
        BOOL IsVarArg()
        {
            WRAPPER_NO_CONTRACT;
            SUPPORTS_DAC;
            return GetCallingConvention() == IMAGE_CEE_CS_CALLCONV_VARARG;
        }
        
        //----------------------------------------------------------
        // Is vararg?
        //----------------------------------------------------------
        static BOOL IsVarArg(Module *pModule, const Signature &signature)
        {
            CONTRACTL
            {
                NOTHROW;
                GC_NOTRIGGER;
                MODE_ANY;
                SUPPORTS_DAC;
            }
            CONTRACTL_END
            
            HRESULT hr;
            BYTE    nCallingConvention;
            
            hr = GetCallingConvention_NoThrow(pModule, signature, &nCallingConvention);
            if (FAILED(hr))
            {   // Invalid signatures are not VarArg
                return FALSE;
            }
            return nCallingConvention == IMAGE_CEE_CS_CALLCONV_VARARG;
        }
        
        Module* GetModule() const
        {
            LIMITED_METHOD_DAC_CONTRACT;
            
            return m_pModule;
        }
        
        //----------------------------------------------------------
        // Returns the unmanaged calling convention.
        //----------------------------------------------------------
        static BOOL GetUnmanagedCallingConvention(Module *pModule, PCCOR_SIGNATURE pSig, ULONG cSig, CorPinvokeMap *pPinvokeMapOut);

        //------------------------------------------------------------------
        // Like NextArg, but return only normalized type (enums flattned to 
        // underlying type ...
        //------------------------------------------------------------------
        CorElementType 
        NextArgNormalized(TypeHandle * pthValueType = NULL)
        {
            CONTRACTL
            {
                if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
                if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
                if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
                MODE_ANY;
                SUPPORTS_DAC;
            }
            CONTRACTL_END

            m_pLastType = m_pWalk;
            if (m_iCurArg == m_nArgs)
            {
                return ELEMENT_TYPE_END;
            }
            else
            {
                m_iCurArg++;
                CorElementType mt = m_pWalk.PeekElemTypeNormalized(m_pModule, &m_typeContext, pthValueType);
                // We should not hit ELEMENT_TYPE_END in the middle of the signature
                if (mt == ELEMENT_TYPE_END)
                {
                    THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module *)NULL);
                }
                IfFailThrowBF(m_pWalk.SkipExactlyOne(), BFA_BAD_SIGNATURE, (Module *)NULL);
                return mt;
            }
        } // NextArgNormalized

        // Tests if the return type is an object ref.  Loads types
        // if needed (though it shouldn't really need to)
        BOOL IsObjectRefReturnType();

        //------------------------------------------------------------------------
        // Compute element size from CorElementType and optional valuetype.
        //------------------------------------------------------------------------
        static UINT GetElemSize(CorElementType etype, TypeHandle thValueType);

        UINT GetReturnTypeSize() 
        {    
            WRAPPER_NO_CONTRACT;
            SUPPORTS_DAC;
            return m_pRetType.SizeOf(m_pModule, &m_typeContext);
        }

        //------------------------------------------------------------------
        // Perform type-specific GC promotion on the value (based upon the
        // last type retrieved by NextArg()).
        //------------------------------------------------------------------
        VOID GcScanRoots(ArgDestination *pValue, promote_func *fn,
                         ScanContext* sc, promote_carefully_func *fnc = NULL);

        //------------------------------------------------------------------
        // Is the return type 64 bit?
        //------------------------------------------------------------------
        BOOL Is64BitReturn() const
        {
            WRAPPER_NO_CONTRACT;
            CorElementType rt = GetReturnTypeNormalized();
            return (rt == ELEMENT_TYPE_I8 || rt == ELEMENT_TYPE_U8 || rt == ELEMENT_TYPE_R8);
        }

        //------------------------------------------------------------------
        // Is the return type floating point?
        //------------------------------------------------------------------
        BOOL HasFPReturn()
        {
            WRAPPER_NO_CONTRACT;
            CorElementType rt = GetReturnTypeNormalized();
            return (rt == ELEMENT_TYPE_R4 || rt == ELEMENT_TYPE_R8);
        }

        //------------------------------------------------------------------
        // reset: goto start pos
        //------------------------------------------------------------------
        VOID Reset();

        //------------------------------------------------------------------
        // current position of the arg iterator
        //------------------------------------------------------------------
        UINT GetArgNum()
        {
            LIMITED_METHOD_CONTRACT;
            return m_iCurArg;
        }

        //------------------------------------------------------------------
        // Returns CorElementType of return value, taking into account
        // any instantiations due to generics.  Does not load types.
        // Does not return normalized type.
        //------------------------------------------------------------------
        CorElementType GetReturnType() const;

        BOOL IsReturnTypeVoid() const;

        CorElementType GetReturnTypeNormalized(TypeHandle * pthValueType = NULL) const;

        //------------------------------------------------------------------
        // used to treat some sigs as special case vararg
        // used by calli to unmanaged target
        //------------------------------------------------------------------
        BOOL IsTreatAsVarArg()
        {
            LIMITED_METHOD_DAC_CONTRACT;
            
            return (m_flags & TREAT_AS_VARARG);
        }

        //------------------------------------------------------------------
        // Determines if the current argument is System/String.
        // Caller must determine first that the argument type is 
        // ELEMENT_TYPE_CLASS or ELEMENT_TYPE_STRING.  This may be used during
        // GC.
        //------------------------------------------------------------------
        BOOL IsStringType() const;

        //------------------------------------------------------------------
        // Determines if the current argument is a particular class.
        // Caller must determine first that the argument type 
        // is ELEMENT_TYPE_CLASS.
        //------------------------------------------------------------------
        BOOL IsClass(LPCUTF8 szClassName) const;


        //------------------------------------------------------------------
        // This method will return a TypeHandle for the last argument
        // examined.
        // If NextArg() returns ELEMENT_TYPE_BYREF, you can also call GetByRefType()
        // to get to the underlying type of the byref
        //------------------------------------------------------------------
        TypeHandle GetLastTypeHandleNT() const
        {
             WRAPPER_NO_CONTRACT;
             return m_pLastType.GetTypeHandleNT(m_pModule, &m_typeContext);
        }

        //------------------------------------------------------------------
        // This method will return a TypeHandle for the last argument
        // examined.
        // If NextArg() returns ELEMENT_TYPE_BYREF, you can also call GetByRefType()
        // to get to the underlying type of the byref
        //------------------------------------------------------------------
        TypeHandle GetLastTypeHandleThrowing(ClassLoader::LoadTypesFlag fLoadTypes = ClassLoader::LoadTypes,
                                             ClassLoadLevel level = CLASS_LOADED,
                                             BOOL dropGenericArgumentLevel = FALSE) const
        {
             WRAPPER_NO_CONTRACT;
             return m_pLastType.GetTypeHandleThrowing(m_pModule, &m_typeContext, fLoadTypes, 
                                                      level, dropGenericArgumentLevel);
        }

        //------------------------------------------------------------------
        // Returns the TypeHandle for the return type of the signature
        //------------------------------------------------------------------
        TypeHandle GetRetTypeHandleNT() const
        {
             WRAPPER_NO_CONTRACT;
             return m_pRetType.GetTypeHandleNT(m_pModule, &m_typeContext);
        }

        TypeHandle GetRetTypeHandleThrowing(ClassLoader::LoadTypesFlag fLoadTypes = ClassLoader::LoadTypes,
                                            ClassLoadLevel level = CLASS_LOADED) const
        {
             WRAPPER_NO_CONTRACT;
             return m_pRetType.GetTypeHandleThrowing(m_pModule, &m_typeContext, fLoadTypes, level);
        }

        //------------------------------------------------------------------
        // Returns the base type of the byref type of the last argument examined
        // which needs to have been ELEMENT_TYPE_BYREF.
        // For object references, the class being accessed byref is also returned in *pTy.
        // eg. for "int32 &",            return value = ELEMENT_TYPE_I4,    *pTy= ???
        //     for "System.Exception &", return value = ELEMENT_TYPE_CLASS, *pTy=System.Exception
        // Note that byref to byref is not allowed, and so the return value
        // can never be ELEMENT_TYPE_BYREF.
        //------------------------------------------------------------------
        CorElementType GetByRefType(TypeHandle* pTy) const;

        // Compare types in two signatures, first applying
        // - optional substitutions pSubst1 and pSubst2
        //   to class type parameters (E_T_VAR) in the respective signatures
        static 
        BOOL 
        CompareElementType(
            PCCOR_SIGNATURE &    pSig1, 
            PCCOR_SIGNATURE &    pSig2, 
            PCCOR_SIGNATURE      pEndSig1, 
            PCCOR_SIGNATURE      pEndSig2, 
            Module *             pModule1, 
            Module *             pModule2, 
            const Substitution * pSubst1, 
            const Substitution * pSubst2, 
            TokenPairList *      pVisited = NULL);



        // If pTypeDef1 is C<...> and pTypeDef2 is C<...> (for possibly different instantiations)
        // then check C<!0, ... !n> @ pSubst1 == C<!0, ..., !n> @ pSubst2, i.e.
        // that the head type (C) is the same and that when the head type is treated
        // as an uninstantiated type definition and we apply each of the substitutions 
        // then the same type results.  This effectively checks that the two substitutions
        // are equivalent.
        static BOOL CompareTypeDefsUnderSubstitutions(MethodTable *pTypeDef1,          MethodTable *pTypeDef2,
                                                      const Substitution*   pSubst1,   const Substitution*   pSubst2,
                                                      TokenPairList *pVisited = NULL);


        // Compare two complete method signatures, first applying optional substitutions pSubst1 and pSubst2
        // to class type parameters (E_T_VAR) in the respective signatures
        static BOOL CompareMethodSigs(
            PCCOR_SIGNATURE pSig1, 
            DWORD       cSig1, 
            Module*     pModule1, 
            const Substitution* pSubst1,
            PCCOR_SIGNATURE pSig2, 
            DWORD       cSig2, 
            Module*     pModule2,
            const Substitution* pSubst2,
            TokenPairList *pVisited = NULL
        );

        // Nonthrowing version of CompareMethodSigs
        //
        //   Return S_OK if they match
        //          S_FALSE if they don't match
        //          FAILED  if OOM or some other blocking error
        //
        static HRESULT CompareMethodSigsNT(
            PCCOR_SIGNATURE pSig1, 
            DWORD       cSig1, 
            Module*     pModule1, 
            const Substitution* pSubst1,
            PCCOR_SIGNATURE pSig2, 
            DWORD       cSig2, 
            Module*     pModule2,
            const Substitution* pSubst2,
            TokenPairList *pVisited = NULL
        );

        static BOOL CompareFieldSigs(
            PCCOR_SIGNATURE pSig1, 
            DWORD       cSig1, 
            Module*     pModule1, 
            PCCOR_SIGNATURE pSig2, 
            DWORD       cSig2, 
            Module*     pModule2,
            TokenPairList *pVisited = NULL
        );

        static BOOL CompareMethodSigs(MetaSig &msig1,
                                      MetaSig &msig2,
                                      BOOL ignoreCallconv);

        // Is each set of constraints on the implementing method's type parameters a subset
        // of the corresponding set of constraints on the declared method's type parameters,
        // given a subsitution for the latter's (class) type parameters.
        // This is used by the class loader to verify type safety of method overriding and interface implementation.
        static BOOL CompareMethodConstraints(const Substitution *pSubst1,
                                             Module *pModule1, 
                                             mdMethodDef tok1, //implementing method
                                             const Substitution *pSubst2,
                                             Module *pModule2,
                                             mdMethodDef tok2); //declared method

private:
        static BOOL CompareVariableConstraints(const Substitution *pSubst1,
                                               Module *pModule1, mdGenericParam tok1, //overriding
                                               const Substitution *pSubst2,
                                               Module *pModule2, mdGenericParam tok2); //overridden

        static BOOL CompareTypeDefOrRefOrSpec(Module *pModule1, mdToken tok1,
                                              const Substitution *pSubst1,
                                              Module *pModule2, mdToken tok2,
                                              const Substitution *pSubst2,
                                              TokenPairList *pVisited);
        static BOOL CompareTypeSpecToToken(mdTypeSpec tk1,
                                           mdToken tk2,
                                           Module *pModule1,
                                           Module *pModule2,
                                           const Substitution *pSubst1,
                                           TokenPairList *pVisited);

        static BOOL CompareElementTypeToToken(PCCOR_SIGNATURE &pSig1,
                                             PCCOR_SIGNATURE pEndSig1, // end of sig1
                                             mdToken         tk2,
                                             Module*         pModule1,
                                             Module*         pModule2,
                                             const Substitution*   pSubst1,
                                             TokenPairList *pVisited);

public:

        //------------------------------------------------------------------
        // Ensures that all the value types in the sig are loaded. This
        // should be called on sig's that have value types before they
        // are passed to Call(). This ensures that value classes will not
        // be loaded during the operation to determine the size of the
        // stack. Thus preventing the resulting GC hole.
        //------------------------------------------------------------------
        static void EnsureSigValueTypesLoaded(MethodDesc *pMD);

        // this walks the sig and checks to see if all  types in the sig can be loaded
        static void CheckSigTypesCanBeLoaded(MethodDesc *pMD);
        
        const SigTypeContext *GetSigTypeContext() const { LIMITED_METHOD_CONTRACT; return &m_typeContext; }

        // Disallow copy constructor.
        MetaSig(MetaSig *pSig);

        void SetHasParamTypeArg()
        {
            LIMITED_METHOD_CONTRACT;
            m_CallConv |= CORINFO_CALLCONV_PARAMTYPE;
        }

        void SetTreatAsVarArg()
        {
            LIMITED_METHOD_CONTRACT;
            m_flags |= TREAT_AS_VARARG;
        }


    // These are protected because Reflection subclasses Metasig
    protected:

        enum MetaSigFlags
        {
            SIG_RET_TYPE_INITTED    = 0x01,
            TREAT_AS_VARARG         = 0x02,     // used to treat some sigs as special case vararg
                                                // used by calli to unmanaged target
        };

        Module*      m_pModule;
        SigTypeContext m_typeContext;   // Instantiation for type parameters

        SigPointer   m_pStart;
        SigPointer   m_pWalk;
        SigPointer   m_pLastType;
        SigPointer   m_pRetType;
        UINT32       m_nArgs;
        UINT32       m_iCurArg;

        // The following are cached so we don't the signature
        //  multiple times

        CorElementType  m_corNormalizedRetType;
        BYTE            m_flags;
        BYTE            m_CallConv;
};  // class MetaSig


BOOL IsTypeRefOrDef(LPCSTR szClassName, Module *pModule, mdToken token);

#if defined(FEATURE_TYPEEQUIVALENCE) && !defined(DACCESS_COMPILE)

// A helper struct representing data stored in the TypeIdentifierAttribute.
struct TypeIdentifierData
{
    TypeIdentifierData() : m_cbScope(0), m_pchScope(NULL), m_cbIdentifierNamespace(0), m_pchIdentifierNamespace(NULL),
                           m_cbIdentifierName(0), m_pchIdentifierName(NULL) {}

    HRESULT Init(Module *pModule, mdToken tk);
    BOOL IsEqual(const TypeIdentifierData & data) const;
    
private:
    SIZE_T  m_cbScope;
    LPCUTF8 m_pchScope;
    SIZE_T  m_cbIdentifierNamespace;
    LPCUTF8 m_pchIdentifierNamespace;
    SIZE_T  m_cbIdentifierName;
    LPCUTF8 m_pchIdentifierName;
};

#endif // FEATURE_TYPEEQUIVALENCE && !DACCESS_COMPILE

// fResolved is TRUE when one of the tokens is a resolved TypeRef. This is used to restrict
// type equivalence checks for value types.
BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2, TokenPairList *pVisited = NULL);

// Nonthrowing version of CompareTypeTokens.
//
//   Return S_OK if they match
//          S_FALSE if they don't match
//          FAILED  if OOM or some other blocking error
//
HRESULT  CompareTypeTokensNT(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2);

// TRUE if the two TypeDefs have the same layout and field marshal information.
BOOL CompareTypeLayout(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2);
BOOL CompareTypeDefsForEquivalence(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2, TokenPairList *pVisited);
BOOL IsTypeDefEquivalent(mdToken tk, Module *pModule);
BOOL IsTypeDefExternallyVisible(mdToken tk, Module *pModule, DWORD dwAttrs);

void ReportPointersFromValueType(promote_func *fn, ScanContext *sc, PTR_MethodTable pMT, PTR_VOID pSrc);

#endif /* _H_SIGINFO */