| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | // |
| 5 | // File: generics.cpp |
| 6 | // |
| 7 | |
| 8 | |
| 9 | // |
| 10 | // Helper functions for generics prototype |
| 11 | // |
| 12 | |
| 13 | // |
| 14 | // ============================================================================ |
| 15 | |
| 16 | #ifndef _GENERICS_H |
| 17 | #define _GENERICS_H |
| 18 | |
| 19 | #include "typehandle.h" |
| 20 | #include "arraylist.h" |
| 21 | |
| 22 | class CrawlFrame; |
| 23 | class DictionaryEntryLayout; |
| 24 | |
| 25 | // Generics helper functions |
| 26 | namespace Generics |
| 27 | { |
| 28 | // Part of the recursive inheritance graph as defined by ECMA part.II Section 9.2. |
| 29 | // |
| 30 | // code:MethodTable.DoFullyLoad and code:TypeDesc.DoFullyLoad declare local variable of |
| 31 | // this type and initialize it with: |
| 32 | // - pointer to the previous (in terms of callstack) RecursionGraph instance, |
| 33 | // - the type handle representing the type that is being fully-loaded. |
| 34 | // |
| 35 | // By walking the RecursionGraph chain, it is possible to tell whether the same type is |
| 36 | // not being fully-loaded already. So far this could as well be a description of the |
| 37 | // code:TypeHandleList. But aside from the "owner type", RecursionGraph can also hold |
| 38 | // part of a directed graph in which nodes are generic variables and edges represent the |
| 39 | // is-substituted-by relation. In particular, one RecursionGraph instance maintains nodes |
| 40 | // corresponding to generic variables declared by the owner type, and all edges going |
| 41 | // out of these nodes. |
| 42 | // |
| 43 | // As an example consider: |
| 44 | // class B<U> { } |
| 45 | // class A<T> : B<T> { } |
| 46 | // |
| 47 | // B's RecursionGraph has one node (U) and no edges. A's RecursionGraph has one node (T) |
| 48 | // and one edge from T to U. |
| 49 | // |
| 50 | // This is how it looks like on the stack: |
| 51 | // |
| 52 | // A's DoFullyLoad activation - RecursionGraph(NULL, A<>) -> [T] |
| 53 | // ^-------- | |
| 54 | // | v |
| 55 | // B's DoFullyLoad activation - RecursionGraph( | , B<>) -> [U] |
| 56 | // |
| 57 | // The edges are obviously not real pointers because the destination may not yet be |
| 58 | // present on the stack when the edge is being added. Instead the edge end points are |
| 59 | // identified by TypeVarTypeDesc pointers. Edges come in two flavors - non-expanding |
| 60 | // and expanding, please see ECMA for detailed explanation. Finding an expanding cycle |
| 61 | // (i.e. cycle with at least one expanding edge) in the graph means that the types |
| 62 | // currently on stack are defined recursively and should be refused by the loader. |
| 63 | // Reliable detection of this condition is the ultimate purpose of this class. |
| 64 | // |
| 65 | // We do not always see all dependencies of a type on the stack. If the dependencies |
| 66 | // have been loaded earlier, loading stops there and part of the graph may be missing. |
| 67 | // However, this is of no concern because we are only interested in types with cyclic |
| 68 | // dependencies, and loading any type from such a closure will cause loading the rest |
| 69 | // of it. If part of the rest had been loaded earlier, it would have triggered loading |
| 70 | // the current type so there's really no way how expanding cycles can go undetected. |
| 71 | // |
| 72 | // Observation: if there is a cycle in type dependencies, there will be a moment when |
| 73 | // we'll have all the types participating in the cycle on the stack. |
| 74 | // |
| 75 | // Note that having a cycle in type dependencies is OK as long as it is not an expanding |
| 76 | // cycle. The simplest example of a cycle that is not expanding is A<T> : B<A<T>>. That |
| 77 | // is a perfectly valid type. |
| 78 | // |
| 79 | // The most interesting methods in this class are: |
| 80 | // * code:RecursionGraph.AddDependency - adds edges according to a type's instantiation. |
| 81 | // * code:RecursionGraph.HasExpandingCycle - looks for expanding cycles in the graph. |
| 82 | // |
| 83 | class RecursionGraph |
| 84 | { |
| 85 | public: |
| 86 | // Just records the owner and links to the previous graph. To actually construct the |
| 87 | // graph, call CheckForIllegalRecursion. Without CheckForIllegalRecursion, the |
| 88 | // functionality is limited to that of code:TypeHandleList. |
| 89 | RecursionGraph(RecursionGraph *pPrev, TypeHandle thOwner); |
| 90 | ~RecursionGraph(); |
| 91 | |
| 92 | // Adds edges generated by the parent and implemented interfaces; returns TRUE iff |
| 93 | // an expanding cycle was found after adding the edges. |
| 94 | BOOL CheckForIllegalRecursion(); |
| 95 | |
| 96 | // Returns TRUE iff the given type is already on the stack (in fact an analogue of |
| 97 | // code:TypeHandleList.Exists). This is to prevent recursively loading exactly the |
| 98 | // same type. |
| 99 | static BOOL HasSeenType(RecursionGraph *pDepGraph, TypeHandle thType); |
| 100 | |
| 101 | #ifndef DACCESS_COMPILE |
| 102 | protected: |
| 103 | // Adds the specified MT as a dependency (parent or interface) of the owner. |
| 104 | // pExpansionVars used internally. |
| 105 | void AddDependency(MethodTable *pMT, TypeHandleList *pExpansionVars = NULL); |
| 106 | |
| 107 | // Adds an edge from pFromVar to pToVar, non-expanding or expanding. |
| 108 | void AddEdge(TypeVarTypeDesc *pFromVar, TypeVarTypeDesc *pToVar, BOOL fExpanding); |
| 109 | |
| 110 | // Represents a node (a generic variable). |
| 111 | class Node |
| 112 | { |
| 113 | friend class RecursionGraph; |
| 114 | |
| 115 | union |
| 116 | { |
| 117 | TypeVarTypeDesc *m_pFromVar; // The generic variable represented by this node. |
| 118 | ULONG_PTR m_pFromVarAsPtr; // The lowest bit determines the is-visited state. |
| 119 | }; |
| 120 | ArrayList m_edges; // The outgoing edges (pointers to TypeVarTypeDesc). |
| 121 | |
| 122 | enum |
| 123 | { |
| 124 | NODE_VISITED_FLAG = 0x1, // ORed with m_pFromVar if this node is currently being visited. |
| 125 | EDGE_EXPANDING_FLAG = 0x1 // ORed with an m_edges element if the edge is expanding. |
| 126 | }; |
| 127 | |
| 128 | public: |
| 129 | Node() : m_pFromVar(NULL) |
| 130 | { LIMITED_METHOD_CONTRACT; } |
| 131 | |
| 132 | inline ArrayList *GetEdges() { LIMITED_METHOD_CONTRACT; return &m_edges; } |
| 133 | inline TypeVarTypeDesc *GetSourceVar() { LIMITED_METHOD_CONTRACT; return ((TypeVarTypeDesc *)(m_pFromVarAsPtr & ~NODE_VISITED_FLAG)); } |
| 134 | inline void SetSourceVar(TypeVarTypeDesc *pVar) { LIMITED_METHOD_CONTRACT; _ASSERTE(!m_pFromVar); m_pFromVar = pVar; } |
| 135 | |
| 136 | inline BOOL IsVisited() { LIMITED_METHOD_CONTRACT; return (m_pFromVarAsPtr & NODE_VISITED_FLAG); } |
| 137 | inline void SetVisited() { LIMITED_METHOD_CONTRACT; m_pFromVarAsPtr |= NODE_VISITED_FLAG; } |
| 138 | inline void ClearVisited() { LIMITED_METHOD_CONTRACT; m_pFromVarAsPtr &= ~NODE_VISITED_FLAG; } |
| 139 | }; |
| 140 | |
| 141 | // Recursive worker that checks whether a node is part of an expanding cycle. |
| 142 | BOOL HasExpandingCycle(Node *pCurrentNode, Node *pStartNode, BOOL fExpanded = FALSE); |
| 143 | |
| 144 | protected: |
| 145 | // Array of nodes, each representing a generic variable owned by m_thOwner. The |
| 146 | // number of nodes is m_thOwner.GetNumGenericArgs() and the order corresponds |
| 147 | // to m_thOwner's instantiation. |
| 148 | Node *m_pNodes; |
| 149 | #endif // !DACCESS_COMPILE |
| 150 | |
| 151 | protected: |
| 152 | RecursionGraph *m_pPrev; |
| 153 | TypeHandle m_thOwner; |
| 154 | }; |
| 155 | |
| 156 | // Check for legal instantiations. Returns true if the instantiation is legal. |
| 157 | BOOL CheckInstantiation(Instantiation inst); |
| 158 | |
| 159 | BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation( |
| 160 | /* in */ MethodDesc *pRepMethod, |
| 161 | /* in */ OBJECTREF pThis, |
| 162 | /* in */ PTR_VOID pParamTypeArg, |
| 163 | /* out*/ TypeHandle *pSpecificClass, |
| 164 | /* out*/ MethodDesc** pSpecificMethod); |
| 165 | |
| 166 | BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation( |
| 167 | /* in */ MethodDesc *pRepMethod, |
| 168 | /* in */ PTR_VOID pExactGenericArgsToken, |
| 169 | /* out*/ TypeHandle *pSpecificClass, |
| 170 | /* out*/ MethodDesc** pSpecificMethod); |
| 171 | |
| 172 | inline void DetermineCCWTemplateAndGUIDPresenceOnNonCanonicalMethodTable( |
| 173 | // Input |
| 174 | MethodTable *pOldMT, |
| 175 | BOOL fNewMTContainsGenericVariables, |
| 176 | // Output |
| 177 | BOOL *pfHasGuidInfo, BOOL *pfHasCCWTemplate); |
| 178 | }; |
| 179 | |
| 180 | #endif |
| 181 | |