// 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. // // File: generics.cpp // // // Helper functions for generics prototype // // // ============================================================================ #ifndef _GENERICS_H #define _GENERICS_H #include "typehandle.h" #include "arraylist.h" class CrawlFrame; class DictionaryEntryLayout; // Generics helper functions namespace Generics { // Part of the recursive inheritance graph as defined by ECMA part.II Section 9.2. // // code:MethodTable.DoFullyLoad and code:TypeDesc.DoFullyLoad declare local variable of // this type and initialize it with: // - pointer to the previous (in terms of callstack) RecursionGraph instance, // - the type handle representing the type that is being fully-loaded. // // By walking the RecursionGraph chain, it is possible to tell whether the same type is // not being fully-loaded already. So far this could as well be a description of the // code:TypeHandleList. But aside from the "owner type", RecursionGraph can also hold // part of a directed graph in which nodes are generic variables and edges represent the // is-substituted-by relation. In particular, one RecursionGraph instance maintains nodes // corresponding to generic variables declared by the owner type, and all edges going // out of these nodes. // // As an example consider: // class B { } // class A : B { } // // B's RecursionGraph has one node (U) and no edges. A's RecursionGraph has one node (T) // and one edge from T to U. // // This is how it looks like on the stack: // // A's DoFullyLoad activation - RecursionGraph(NULL, A<>) -> [T] // ^-------- | // | v // B's DoFullyLoad activation - RecursionGraph( | , B<>) -> [U] // // The edges are obviously not real pointers because the destination may not yet be // present on the stack when the edge is being added. Instead the edge end points are // identified by TypeVarTypeDesc pointers. Edges come in two flavors - non-expanding // and expanding, please see ECMA for detailed explanation. Finding an expanding cycle // (i.e. cycle with at least one expanding edge) in the graph means that the types // currently on stack are defined recursively and should be refused by the loader. // Reliable detection of this condition is the ultimate purpose of this class. // // We do not always see all dependencies of a type on the stack. If the dependencies // have been loaded earlier, loading stops there and part of the graph may be missing. // However, this is of no concern because we are only interested in types with cyclic // dependencies, and loading any type from such a closure will cause loading the rest // of it. If part of the rest had been loaded earlier, it would have triggered loading // the current type so there's really no way how expanding cycles can go undetected. // // Observation: if there is a cycle in type dependencies, there will be a moment when // we'll have all the types participating in the cycle on the stack. // // Note that having a cycle in type dependencies is OK as long as it is not an expanding // cycle. The simplest example of a cycle that is not expanding is A : B>. That // is a perfectly valid type. // // The most interesting methods in this class are: // * code:RecursionGraph.AddDependency - adds edges according to a type's instantiation. // * code:RecursionGraph.HasExpandingCycle - looks for expanding cycles in the graph. // class RecursionGraph { public: // Just records the owner and links to the previous graph. To actually construct the // graph, call CheckForIllegalRecursion. Without CheckForIllegalRecursion, the // functionality is limited to that of code:TypeHandleList. RecursionGraph(RecursionGraph *pPrev, TypeHandle thOwner); ~RecursionGraph(); // Adds edges generated by the parent and implemented interfaces; returns TRUE iff // an expanding cycle was found after adding the edges. BOOL CheckForIllegalRecursion(); // Returns TRUE iff the given type is already on the stack (in fact an analogue of // code:TypeHandleList.Exists). This is to prevent recursively loading exactly the // same type. static BOOL HasSeenType(RecursionGraph *pDepGraph, TypeHandle thType); #ifndef DACCESS_COMPILE protected: // Adds the specified MT as a dependency (parent or interface) of the owner. // pExpansionVars used internally. void AddDependency(MethodTable *pMT, TypeHandleList *pExpansionVars = NULL); // Adds an edge from pFromVar to pToVar, non-expanding or expanding. void AddEdge(TypeVarTypeDesc *pFromVar, TypeVarTypeDesc *pToVar, BOOL fExpanding); // Represents a node (a generic variable). class Node { friend class RecursionGraph; union { TypeVarTypeDesc *m_pFromVar; // The generic variable represented by this node. ULONG_PTR m_pFromVarAsPtr; // The lowest bit determines the is-visited state. }; ArrayList m_edges; // The outgoing edges (pointers to TypeVarTypeDesc). enum { NODE_VISITED_FLAG = 0x1, // ORed with m_pFromVar if this node is currently being visited. EDGE_EXPANDING_FLAG = 0x1 // ORed with an m_edges element if the edge is expanding. }; public: Node() : m_pFromVar(NULL) { LIMITED_METHOD_CONTRACT; } inline ArrayList *GetEdges() { LIMITED_METHOD_CONTRACT; return &m_edges; } inline TypeVarTypeDesc *GetSourceVar() { LIMITED_METHOD_CONTRACT; return ((TypeVarTypeDesc *)(m_pFromVarAsPtr & ~NODE_VISITED_FLAG)); } inline void SetSourceVar(TypeVarTypeDesc *pVar) { LIMITED_METHOD_CONTRACT; _ASSERTE(!m_pFromVar); m_pFromVar = pVar; } inline BOOL IsVisited() { LIMITED_METHOD_CONTRACT; return (m_pFromVarAsPtr & NODE_VISITED_FLAG); } inline void SetVisited() { LIMITED_METHOD_CONTRACT; m_pFromVarAsPtr |= NODE_VISITED_FLAG; } inline void ClearVisited() { LIMITED_METHOD_CONTRACT; m_pFromVarAsPtr &= ~NODE_VISITED_FLAG; } }; // Recursive worker that checks whether a node is part of an expanding cycle. BOOL HasExpandingCycle(Node *pCurrentNode, Node *pStartNode, BOOL fExpanded = FALSE); protected: // Array of nodes, each representing a generic variable owned by m_thOwner. The // number of nodes is m_thOwner.GetNumGenericArgs() and the order corresponds // to m_thOwner's instantiation. Node *m_pNodes; #endif // !DACCESS_COMPILE protected: RecursionGraph *m_pPrev; TypeHandle m_thOwner; }; // Check for legal instantiations. Returns true if the instantiation is legal. BOOL CheckInstantiation(Instantiation inst); BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation( /* in */ MethodDesc *pRepMethod, /* in */ OBJECTREF pThis, /* in */ PTR_VOID pParamTypeArg, /* out*/ TypeHandle *pSpecificClass, /* out*/ MethodDesc** pSpecificMethod); BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation( /* in */ MethodDesc *pRepMethod, /* in */ PTR_VOID pExactGenericArgsToken, /* out*/ TypeHandle *pSpecificClass, /* out*/ MethodDesc** pSpecificMethod); inline void DetermineCCWTemplateAndGUIDPresenceOnNonCanonicalMethodTable( // Input MethodTable *pOldMT, BOOL fNewMTContainsGenericVariables, // Output BOOL *pfHasGuidInfo, BOOL *pfHasCCWTemplate); }; #endif