summaryrefslogtreecommitdiff
path: root/src/vm/clsload.cpp
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/vm/clsload.cpp
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/vm/clsload.cpp')
-rw-r--r--src/vm/clsload.cpp6637
1 files changed, 6637 insertions, 0 deletions
diff --git a/src/vm/clsload.cpp b/src/vm/clsload.cpp
new file mode 100644
index 0000000000..1aa6f23e8c
--- /dev/null
+++ b/src/vm/clsload.cpp
@@ -0,0 +1,6637 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+// File: clsload.cpp
+//
+
+
+
+//
+
+//
+// ============================================================================
+
+#include "common.h"
+#include "winwrap.h"
+#include "ceeload.h"
+#include "siginfo.hpp"
+#include "vars.hpp"
+#include "clsload.hpp"
+#include "classhash.inl"
+#include "class.h"
+#include "method.hpp"
+#include "ecall.h"
+#include "stublink.h"
+#include "object.h"
+#include "excep.h"
+#include "threads.h"
+#include "comsynchronizable.h"
+#include "threads.h"
+#include "dllimport.h"
+#include "security.h"
+#include "dbginterface.h"
+#include "log.h"
+#include "eeconfig.h"
+#include "fieldmarshaler.h"
+#include "jitinterface.h"
+#include "vars.hpp"
+#include "assembly.hpp"
+#include "perfcounters.h"
+#include "eeprofinterfaces.h"
+#include "eehash.h"
+#include "typehash.h"
+#include "comdelegate.h"
+#include "array.h"
+#include "stackprobe.h"
+#include "posterror.h"
+#include "wrappers.h"
+#include "generics.h"
+#include "typestring.h"
+#include "typedesc.h"
+#include "cgencpu.h"
+#include "eventtrace.h"
+#include "typekey.h"
+#include "pendingload.h"
+#include "proftoeeinterfaceimpl.h"
+#include "mdaassistants.h"
+#include "virtualcallstub.h"
+#include "stringarraylist.h"
+
+#if defined(FEATURE_FUSION) && !defined(DACCESS_COMPILE)
+#include "policy.h" // For Fusion::Util::IsAnyFrameworkAssembly
+#endif
+
+// This method determines the "loader module" for an instantiated type
+// or method. The rule must ensure that any types involved in the
+// instantiated type or method do not outlive the loader module itself
+// with respect to app-domain unloading (e.g. MyList<MyType> can't be
+// put in the module of MyList if MyList's assembly is
+// app-domain-neutral but MyType's assembly is app-domain-specific).
+// The rule we use is:
+//
+// * Pick the first type in the class instantiation, followed by
+// method instantiation, whose loader module is non-shared (app-domain-bound)
+// * If no type is app-domain-bound, return the module containing the generic type itself
+//
+// Some useful effects of this rule (for ngen purposes) are:
+//
+// * G<object,...,object> lives in the module defining G
+// * non-mscorlib instantiations of mscorlib-defined generic types live in the module
+// of the instantiation (when only one module is invloved in the instantiation)
+//
+
+/* static */
+PTR_Module ClassLoader::ComputeLoaderModuleWorker(
+ Module * pDefinitionModule, // the module that declares the generic type or method
+ mdToken token, // method or class token for this item
+ Instantiation classInst, // the type arguments to the type (if any)
+ Instantiation methodInst) // the type arguments to the method (if any)
+{
+ CONTRACT(Module*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pDefinitionModule, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL));
+ SO_INTOLERANT;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END
+
+ if (classInst.IsEmpty() && methodInst.IsEmpty())
+ RETURN PTR_Module(pDefinitionModule);
+
+#ifndef DACCESS_COMPILE
+#ifdef FEATURE_NATIVE_IMAGE_GENERATION
+ // Check we're NGEN'ing
+ if (IsCompilationProcess())
+ {
+ RETURN(ComputeLoaderModuleForCompilation(pDefinitionModule, token, classInst, methodInst));
+ }
+#endif // FEATURE_PREJIT
+#endif // #ifndef DACCESS_COMPILE
+
+ Module *pFirstNonSharedLoaderModule = NULL;
+ Module *pFirstNonSystemSharedModule = NULL;
+ Module *pLoaderModule = NULL;
+
+ if (pDefinitionModule)
+ {
+ if (pDefinitionModule->IsCollectible())
+ goto ComputeCollectibleLoaderModule;
+ if (!pDefinitionModule->GetAssembly()->IsDomainNeutral())
+ {
+ pFirstNonSharedLoaderModule = pDefinitionModule;
+ }
+ else
+ if (!pDefinitionModule->IsSystem())
+ {
+ pFirstNonSystemSharedModule = pDefinitionModule;
+ }
+ }
+
+ for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
+ {
+ TypeHandle classArg = classInst[i];
+ _ASSERTE(!classArg.IsEncodedFixup());
+ Module* pModule = classArg.GetLoaderModule();
+ if (pModule->IsCollectible())
+ goto ComputeCollectibleLoaderModule;
+ if (!pModule->GetAssembly()->IsDomainNeutral())
+ {
+ if (pFirstNonSharedLoaderModule == NULL)
+ pFirstNonSharedLoaderModule = pModule;
+ }
+ else
+ if (!pModule->IsSystem())
+ {
+ if (pFirstNonSystemSharedModule == NULL)
+ pFirstNonSystemSharedModule = pModule;
+ }
+ }
+
+ for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
+ {
+ TypeHandle methodArg = methodInst[i];
+ _ASSERTE(!methodArg.IsEncodedFixup());
+ Module *pModule = methodArg.GetLoaderModule();
+ if (pModule->IsCollectible())
+ goto ComputeCollectibleLoaderModule;
+ if (!pModule->GetAssembly()->IsDomainNeutral())
+ {
+ if (pFirstNonSharedLoaderModule == NULL)
+ pFirstNonSharedLoaderModule = pModule;
+ }
+ else
+ if (!pModule->IsSystem())
+ {
+ if (pFirstNonSystemSharedModule == NULL)
+ pFirstNonSystemSharedModule = pModule;
+ }
+ }
+
+ // RULE: Prefer modules in non-shared assemblies.
+ // This ensures safety of app-domain unloading.
+ if (pFirstNonSharedLoaderModule != NULL)
+ {
+ pLoaderModule = pFirstNonSharedLoaderModule;
+ }
+ else if (pFirstNonSystemSharedModule != NULL)
+ {
+#ifdef FEATURE_FULL_NGEN
+ // pFirstNonSystemSharedModule may be module of speculative generic instantiation.
+ // If we are domain neutral, we have to use constituent of the instantiation to store
+ // statics. We need to ensure that we can create DomainModule in all domains
+ // that this instantiations may get activated in. PZM is good approximation of such constituent.
+ pLoaderModule = Module::ComputePreferredZapModule(pDefinitionModule, classInst, methodInst);
+#else
+ // Use pFirstNonSystemSharedModule just so C<object> ends up in module C - it
+ // shouldn't actually matter at all though.
+ pLoaderModule = pFirstNonSystemSharedModule;
+#endif
+ }
+ else
+ {
+ CONSISTENCY_CHECK(MscorlibBinder::GetModule() && MscorlibBinder::GetModule()->IsSystem());
+
+ pLoaderModule = MscorlibBinder::GetModule();
+ }
+
+ if (FALSE)
+ {
+ComputeCollectibleLoaderModule:
+ LoaderAllocator *pLoaderAllocatorOfDefiningType = NULL;
+ LoaderAllocator *pOldestLoaderAllocator = NULL;
+ Module *pOldestLoaderModule = NULL;
+ UINT64 oldestFoundAge = 0;
+ DWORD classArgsCount = classInst.GetNumArgs();
+ DWORD totalArgsCount = classArgsCount + methodInst.GetNumArgs();
+
+ if (pDefinitionModule != NULL) pLoaderAllocatorOfDefiningType = pDefinitionModule->GetLoaderAllocator();
+
+ for (DWORD i = 0; i < totalArgsCount; i++) {
+
+ TypeHandle arg;
+
+ if (i < classArgsCount)
+ arg = classInst[i];
+ else
+ arg = methodInst[i - classArgsCount];
+
+ Module *pModuleCheck = arg.GetLoaderModule();
+ LoaderAllocator *pLoaderAllocatorCheck = pModuleCheck->GetLoaderAllocator();
+
+ if (pLoaderAllocatorCheck != pLoaderAllocatorOfDefiningType &&
+ pLoaderAllocatorCheck->IsCollectible() &&
+ pLoaderAllocatorCheck->GetCreationNumber() > oldestFoundAge)
+ {
+ pOldestLoaderModule = pModuleCheck;
+ pOldestLoaderAllocator = pLoaderAllocatorCheck;
+ oldestFoundAge = pLoaderAllocatorCheck->GetCreationNumber();
+ }
+ }
+
+ // Only if we didn't find a different loader allocator than the defining loader allocator do we
+ // use the defining loader allocator
+ if (pOldestLoaderModule != NULL)
+ pLoaderModule = pOldestLoaderModule;
+ else
+ pLoaderModule = pDefinitionModule;
+ }
+ RETURN PTR_Module(pLoaderModule);
+}
+
+#ifndef DACCESS_COMPILE
+#ifdef FEATURE_NATIVE_IMAGE_GENERATION
+/* static */
+PTR_Module ClassLoader::ComputeLoaderModuleForCompilation(
+ Module * pDefinitionModule, // the module that declares the generic type or method
+ mdToken token, // method or class token for this item
+ Instantiation classInst, // the type arguments to the type (if any)
+ Instantiation methodInst) // the type arguments to the method (if any)
+{
+ CONTRACT(Module*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pDefinitionModule, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL));
+ SO_INTOLERANT;
+ }
+ CONTRACT_END
+
+ // The NGEN rule for compiling constructed types and instantiated methods
+ // into modules other than their "natural" LoaderModule. This is at the heart of
+ // "full generics NGEN".
+ //
+ // If this instantiation doesn't have a unique home then use the ngen module
+
+ // OK, we're certainly NGEN'ing. And if we're NGEN'ing then we're not on the debugger thread.
+ CONSISTENCY_CHECK(((GetThread() && GetAppDomain()) || IsGCThread()) &&
+ "unexpected: running a load on debug thread but IsCompilationProcess() returned TRUE");
+
+ // Save it into its PreferredZapModule if it's always going to be saved there.
+ // This is a stable choice - no need to record it in the table (as we do for others below)
+ if (Module::IsAlwaysSavedInPreferredZapModule(classInst, methodInst))
+ {
+ RETURN (Module::ComputePreferredZapModule(pDefinitionModule, classInst, methodInst));
+ }
+
+ // Check if this compilation process has already decided on an adjustment. Once we decide
+ // on the LoaderModule for an item it must be stable for the duration of a
+ // compilation process, no matter how many modules get NGEN'd.
+
+ ZapperLoaderModuleTableKey key(pDefinitionModule,
+ token,
+ classInst,
+ methodInst);
+
+ Module * pZapperLoaderModule = g_pCEECompileInfo->LookupZapperLoaderModule(&key);
+ if (pZapperLoaderModule != NULL)
+ {
+ RETURN (pZapperLoaderModule);
+ }
+
+ // OK, we need to compute a non-standard zapping module.
+
+ Module * pPreferredZapModule = Module::ComputePreferredZapModule(pDefinitionModule, classInst, methodInst);
+
+ // Check if we're NGEN'ing but where perhaps the compilation domain
+ // isn't set up yet. This can happen in following situations:
+ // - Managed code running during startup before compilation domain is setup.
+ // - Exceptions (e.g. invalid program exceptions) thrown from compilation domain and caught in default domain
+
+ // We're a little stuck - we can't force the item into an NGEN image at this point. So just bail out
+ // and use the loader module we've computed without recording the choice. The loader module should always
+ // be mscorlib in this case.
+ AppDomain * pAppDomain = GetAppDomain();
+ if (!pAppDomain->IsCompilationDomain() ||
+ !pAppDomain->ToCompilationDomain()->GetTargetModule())
+ {
+ _ASSERTE(pPreferredZapModule->IsSystem() || IsNgenPDBCompilationProcess());
+ RETURN (pPreferredZapModule);
+ }
+
+ Module * pTargetModule = pAppDomain->ToCompilationDomain()->GetTargetModule();
+
+ // If it is multi-module assembly and we have not saved PZM yet, do not create
+ // speculative instantiation - just save it in PZM.
+ if (pTargetModule->GetAssembly() == pPreferredZapModule->GetAssembly() &&
+ !pPreferredZapModule->IsModuleSaved())
+ {
+ pZapperLoaderModule = pPreferredZapModule;
+ }
+ else
+ {
+ // Everything else can be saved into the current module.
+ pZapperLoaderModule = pTargetModule;
+ }
+
+ // If generating WinMD resilient code and we so far choose to use the target module,
+ // we need to check if the definition module or any of the instantiation type can
+ // cause version resilient problems.
+ if (g_fNGenWinMDResilient && pZapperLoaderModule == pTargetModule)
+ {
+ if (pDefinitionModule != NULL && !pDefinitionModule->IsInCurrentVersionBubble())
+ {
+ pZapperLoaderModule = pDefinitionModule;
+ goto ModuleAdjustedForVersionResiliency;
+ }
+
+ for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
+ {
+ Module * pModule = classInst[i].GetLoaderModule();
+ if (!pModule->IsInCurrentVersionBubble())
+ {
+ pZapperLoaderModule = pModule;
+ goto ModuleAdjustedForVersionResiliency;
+ }
+ }
+
+ for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
+ {
+ Module * pModule = methodInst[i].GetLoaderModule();
+ if (!pModule->IsInCurrentVersionBubble())
+ {
+ pZapperLoaderModule = pModule;
+ goto ModuleAdjustedForVersionResiliency;
+ }
+ }
+ModuleAdjustedForVersionResiliency: ;
+ }
+
+ // Record this choice just in case we're NGEN'ing multiple modules
+ // to make sure we always do the same thing if we're asked to compute
+ // the loader module again.
+
+ // Note this whole code path only happens while NGEN'ing, so this violation
+ // is not so bad. It is needed since we allocate stuff on the heap.
+ CONTRACT_VIOLATION(ThrowsViolation|FaultViolation);
+
+ // Copy the instantiation arrays so they can escape the scope of this method.
+ // Since this is a permanent entry in a table for this compilation process
+ // we do not need to collect these. If we did have to we would do it when we deleteed the
+ // ZapperLoaderModuleTable.
+ NewArrayHolder<TypeHandle> pClassArgs = NULL;
+ if (!classInst.IsEmpty())
+ {
+ pClassArgs = new TypeHandle[classInst.GetNumArgs()];
+ for (unsigned int i = 0; i < classInst.GetNumArgs(); i++)
+ pClassArgs[i] = classInst[i];
+ }
+
+ NewArrayHolder<TypeHandle> pMethodArgs = NULL;
+ if (!methodInst.IsEmpty())
+ {
+ pMethodArgs = new TypeHandle[methodInst.GetNumArgs()];
+ for (unsigned int i = 0; i < methodInst.GetNumArgs(); i++)
+ pMethodArgs[i] = methodInst[i];
+ }
+
+ ZapperLoaderModuleTableKey key2(pDefinitionModule,
+ token,
+ Instantiation(pClassArgs, classInst.GetNumArgs()),
+ Instantiation(pMethodArgs, methodInst.GetNumArgs()));
+ g_pCEECompileInfo->RecordZapperLoaderModule(&key2, pZapperLoaderModule);
+
+ pClassArgs.SuppressRelease();
+ pMethodArgs.SuppressRelease();
+
+ RETURN (pZapperLoaderModule);
+}
+#endif // FEATURE_NATIVE_IMAGE_GENERATION
+#endif // #ifndef DACCESS_COMPILE
+
+/*static*/
+Module * ClassLoader::ComputeLoaderModule(MethodTable * pMT,
+ mdToken token,
+ Instantiation methodInst)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ return ComputeLoaderModuleWorker(pMT->GetModule(),
+ token,
+ pMT->GetInstantiation(),
+ methodInst);
+}
+/*static*/
+Module *ClassLoader::ComputeLoaderModule(TypeKey *typeKey)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+
+ if (typeKey->GetKind() == ELEMENT_TYPE_CLASS)
+ return ComputeLoaderModuleWorker(typeKey->GetModule(),
+ typeKey->GetTypeToken(),
+ typeKey->GetInstantiation(),
+ Instantiation());
+ else if (typeKey->GetKind() == ELEMENT_TYPE_FNPTR)
+ return ComputeLoaderModuleForFunctionPointer(typeKey->GetRetAndArgTypes(), typeKey->GetNumArgs() + 1);
+ else
+ return ComputeLoaderModuleForParamType(typeKey->GetElementType());
+}
+
+/*static*/
+BOOL ClassLoader::IsTypicalInstantiation(Module *pModule, mdToken token, Instantiation inst)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(TypeFromToken(token) == mdtTypeDef || TypeFromToken(token) == mdtMethodDef);
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ for (DWORD i = 0; i < inst.GetNumArgs(); i++)
+ {
+ TypeHandle thArg = inst[i];
+
+ if (thArg.IsGenericVariable())
+ {
+ TypeVarTypeDesc* tyvar = thArg.AsGenericVariable();
+
+ PREFIX_ASSUME(tyvar!=NULL);
+ if ((tyvar->GetTypeOrMethodDef() != token) ||
+ (tyvar->GetModule() != dac_cast<PTR_Module>(pModule)) ||
+ (tyvar->GetIndex() != i))
+ return FALSE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// External class loader entry point: load a type by name
+/*static*/
+TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly,
+ LPCUTF8 nameSpace,
+ LPCUTF8 name,
+ NotFoundAction fNotFound,
+ ClassLoader::LoadTypesFlag fLoadTypes,
+ ClassLoadLevel level)
+{
+ CONTRACT(TypeHandle)
+ {
+ 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;
+
+ if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+
+ PRECONDITION(CheckPointer(pAssembly));
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ POSTCONDITION(CheckPointer(RETVAL,
+ (fNotFound == ThrowIfNotFound && fLoadTypes == LoadTypes )? NULL_NOT_OK : NULL_OK));
+ POSTCONDITION(RETVAL.IsNull() || RETVAL.CheckLoadLevel(level));
+ SUPPORTS_DAC;
+#ifdef DACCESS_COMPILE
+ PRECONDITION((fNotFound == ClassLoader::ReturnNullIfNotFound) && (fLoadTypes == DontLoadTypes));
+#endif
+ }
+ CONTRACT_END
+
+ NameHandle nameHandle(nameSpace, name);
+ if (fLoadTypes == DontLoadTypes)
+ nameHandle.SetTokenNotToLoad(tdAllTypes);
+ if (fNotFound == ThrowIfNotFound)
+ RETURN pAssembly->GetLoader()->LoadTypeHandleThrowIfFailed(&nameHandle, level);
+ else
+ RETURN pAssembly->GetLoader()->LoadTypeHandleThrowing(&nameHandle, level);
+}
+
+#ifndef DACCESS_COMPILE
+
+#define DAC_LOADS_TYPE(level, expression) \
+ if (FORBIDGC_LOADER_USE_ENABLED() || (expression)) \
+ { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+#else
+
+#define DAC_LOADS_TYPE(level, expression) { LOADS_TYPE(CLASS_LOAD_BEGIN); }
+#endif // #ifndef DACCESS_COMPILE
+
+//
+// Find a class given name, using the classloader's global list of known classes.
+// If the type is found, it will be restored unless pName->GetTokenNotToLoad() prohibits that
+// Returns NULL if class not found AND pName->OKToLoad returns false
+TypeHandle ClassLoader::LoadTypeHandleThrowIfFailed(NameHandle* pName, ClassLoadLevel level,
+ Module* pLookInThisModuleOnly/*=NULL*/)
+{
+ CONTRACT(TypeHandle)
+ {
+ INSTANCE_CHECK;
+ 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()); }
+ DAC_LOADS_TYPE(level, !pName->OKToLoad());
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pName));
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ POSTCONDITION(CheckPointer(RETVAL, pName->OKToLoad() ? NULL_NOT_OK : NULL_OK));
+ POSTCONDITION(RETVAL.IsNull() || RETVAL.CheckLoadLevel(level));
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ // Lookup in the classes that this class loader knows about
+ TypeHandle typeHnd = LoadTypeHandleThrowing(pName, level, pLookInThisModuleOnly);
+
+ if(typeHnd.IsNull()) {
+
+ if ( pName->OKToLoad() ) {
+#ifdef _DEBUG_IMPL
+ {
+ LPCUTF8 szName = pName->GetName();
+ if (szName == NULL)
+ szName = "<UNKNOWN>";
+
+ StackSString codeBase;
+ GetAssembly()->GetCodeBase(codeBase);
+
+ LOG((LF_CLASSLOADER, LL_INFO10, "Failed to find class \"%s\" in the manifest for assembly \"%ws\"\n", szName, (LPCWSTR)codeBase));
+ }
+#endif
+
+#ifndef DACCESS_COMPILE
+ COUNTER_ONLY(GetPerfCounters().m_Loading.cLoadFailures++);
+
+ m_pAssembly->ThrowTypeLoadException(pName, IDS_CLASSLOAD_GENERAL);
+#else
+ DacNotImpl();
+#endif
+ }
+ }
+
+ RETURN(typeHnd);
+}
+
+#ifndef DACCESS_COMPILE
+
+//<TODO>@TODO: Need to allow exceptions to be thrown when classloader is cleaned up</TODO>
+EEClassHashEntry_t* ClassLoader::InsertValue(EEClassHashTable *pClassHash, EEClassHashTable *pClassCaseInsHash, LPCUTF8 pszNamespace, LPCUTF8 pszClassName, HashDatum Data, EEClassHashEntry_t *pEncloser, AllocMemTracker *pamTracker)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ LPUTF8 pszLowerCaseNS = NULL;
+ LPUTF8 pszLowerCaseName = NULL;
+ EEClassHashEntry_t *pCaseInsEntry = NULL;
+
+ EEClassHashEntry_t *pEntry = pClassHash->AllocNewEntry(pamTracker);
+
+ if (pClassCaseInsHash) {
+ CreateCanonicallyCasedKey(pszNamespace, pszClassName, &pszLowerCaseNS, &pszLowerCaseName);
+ pCaseInsEntry = pClassCaseInsHash->AllocNewEntry(pamTracker);
+ }
+
+
+ {
+ // ! We cannot fail after this point.
+ CANNOTTHROWCOMPLUSEXCEPTION();
+ FAULT_FORBID();
+
+
+ pClassHash->InsertValueUsingPreallocatedEntry(pEntry, pszNamespace, pszClassName, Data, pEncloser);
+
+ //If we're keeping a table for case-insensitive lookup, keep that up to date
+ if (pClassCaseInsHash)
+ pClassCaseInsHash->InsertValueUsingPreallocatedEntry(pCaseInsEntry, pszLowerCaseNS, pszLowerCaseName, pEntry, pEncloser);
+
+ return pEntry;
+ }
+
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+BOOL ClassLoader::CompareNestedEntryWithExportedType(IMDInternalImport * pImport,
+ mdExportedType mdCurrent,
+ EEClassHashTable * pClassHash,
+ PTR_EEClassHashEntry pEntry)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ LPCUTF8 Key[2];
+
+ do
+ {
+ if (FAILED(pImport->GetExportedTypeProps(
+ mdCurrent,
+ &Key[0],
+ &Key[1],
+ &mdCurrent,
+ NULL, //binding (type def)
+ NULL))) //flags
+ {
+ return FALSE;
+ }
+
+ if (pClassHash->CompareKeys(pEntry, Key))
+ {
+ // Reached top level class for mdCurrent - return whether
+ // or not pEntry is a top level class
+ // (pEntry is a top level class if its pEncloser is NULL)
+ if ((TypeFromToken(mdCurrent) != mdtExportedType) ||
+ (mdCurrent == mdExportedTypeNil))
+ {
+ return pEntry->GetEncloser() == NULL;
+ }
+ }
+ else // Keys don't match - wrong entry
+ {
+ return FALSE;
+ }
+ }
+ while ((pEntry = pEntry->GetEncloser()) != NULL);
+
+ // Reached the top level class for pEntry, but mdCurrent is nested
+ return FALSE;
+}
+
+
+BOOL ClassLoader::CompareNestedEntryWithTypeDef(IMDInternalImport * pImport,
+ mdTypeDef mdCurrent,
+ EEClassHashTable * pClassHash,
+ PTR_EEClassHashEntry pEntry)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ LPCUTF8 Key[2];
+
+ do {
+ if (FAILED(pImport->GetNameOfTypeDef(mdCurrent, &Key[1], &Key[0])))
+ {
+ return FALSE;
+ }
+
+ if (pClassHash->CompareKeys(pEntry, Key)) {
+ // Reached top level class for mdCurrent - return whether
+ // or not pEntry is a top level class
+ // (pEntry is a top level class if its pEncloser is NULL)
+ if (FAILED(pImport->GetNestedClassProps(mdCurrent, &mdCurrent)))
+ return pEntry->GetEncloser() == NULL;
+ }
+ else // Keys don't match - wrong entry
+ return FALSE;
+ }
+ while ((pEntry = pEntry->GetEncloser()) != NULL);
+
+ // Reached the top level class for pEntry, but mdCurrent is nested
+ return FALSE;
+}
+
+
+BOOL ClassLoader::CompareNestedEntryWithTypeRef(IMDInternalImport * pImport,
+ mdTypeRef mdCurrent,
+ EEClassHashTable * pClassHash,
+ PTR_EEClassHashEntry pEntry)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ LPCUTF8 Key[2];
+
+ do {
+ if (FAILED(pImport->GetNameOfTypeRef(mdCurrent, &Key[0], &Key[1])))
+ {
+ return FALSE;
+ }
+
+ if (pClassHash->CompareKeys(pEntry, Key))
+ {
+ if (FAILED(pImport->GetResolutionScopeOfTypeRef(mdCurrent, &mdCurrent)))
+ {
+ return FALSE;
+ }
+ // Reached top level class for mdCurrent - return whether
+ // or not pEntry is a top level class
+ // (pEntry is a top level class if its pEncloser is NULL)
+ if ((TypeFromToken(mdCurrent) != mdtTypeRef) ||
+ (mdCurrent == mdTypeRefNil))
+ return pEntry->GetEncloser() == NULL;
+ }
+ else // Keys don't match - wrong entry
+ return FALSE;
+ }
+ while ((pEntry = pEntry->GetEncloser())!=NULL);
+
+ // Reached the top level class for pEntry, but mdCurrent is nested
+ return FALSE;
+}
+
+
+/*static*/
+BOOL ClassLoader::IsNested(Module *pModule, mdToken token, mdToken *mdEncloser)
+{
+ 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;
+
+ switch(TypeFromToken(token)) {
+ case mdtTypeDef:
+ return (SUCCEEDED(pModule->GetMDImport()->GetNestedClassProps(token, mdEncloser)));
+
+ case mdtTypeRef:
+ IfFailThrow(pModule->GetMDImport()->GetResolutionScopeOfTypeRef(token, mdEncloser));
+ return ((TypeFromToken(*mdEncloser) == mdtTypeRef) &&
+ (*mdEncloser != mdTypeRefNil));
+
+ case mdtExportedType:
+ IfFailThrow(pModule->GetAssembly()->GetManifestImport()->GetExportedTypeProps(
+ token,
+ NULL, // namespace
+ NULL, // name
+ mdEncloser,
+ NULL, //binding (type def)
+ NULL)); //flags
+ return ((TypeFromToken(*mdEncloser) == mdtExportedType) &&
+ (*mdEncloser != mdExportedTypeNil));
+
+ default:
+ ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE);
+ }
+}
+
+BOOL ClassLoader::IsNested(NameHandle* pName, mdToken *mdEncloser)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ 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;
+
+ if (pName->GetTypeModule()) {
+ if (TypeFromToken(pName->GetTypeToken()) == mdtBaseType)
+ {
+ if (pName->GetBucket())
+ return TRUE;
+ return FALSE;
+ }
+ else
+ return IsNested(pName->GetTypeModule(), pName->GetTypeToken(), mdEncloser);
+ }
+ else
+ return FALSE;
+}
+
+EEClassHashEntry_t *ClassLoader::GetClassValue(NameHandleTable nhTable,
+ NameHandle *pName,
+ HashDatum *pData,
+ EEClassHashTable **ppTable,
+ Module* pLookInThisModuleOnly)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ MODE_ANY;
+ 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()); }
+ PRECONDITION(CheckPointer(pName));
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+
+ mdToken mdEncloser;
+ EEClassHashEntry_t *pBucket = NULL;
+
+#if _DEBUG
+ if (pName->GetName()) {
+ if (pName->GetNameSpace() == NULL)
+ LOG((LF_CLASSLOADER, LL_INFO1000, "Looking up %s by name.\n",
+ pName->GetName()));
+ else
+ LOG((LF_CLASSLOADER, LL_INFO1000, "Looking up %s.%s by name.\n",
+ pName->GetNameSpace(), pName->GetName()));
+ }
+#endif
+
+ if (IsNested(pName, &mdEncloser))
+ {
+ Module *pModule = pName->GetTypeModule();
+ PREFIX_ASSUME(pModule != NULL);
+ PTR_Assembly assembly=GetAssembly();
+ PREFIX_ASSUME(assembly!=NULL);
+ ModuleIterator i = assembly->IterateModules();
+ Module *pClsModule = NULL;
+
+ while (i.Next()) {
+ pClsModule = i.GetModule();
+ if (pClsModule->IsResource())
+ continue;
+ if (pLookInThisModuleOnly && (pClsModule != pLookInThisModuleOnly))
+ continue;
+
+ EEClassHashTable* pTable = NULL;
+ if (nhTable == nhCaseSensitive)
+ {
+ *ppTable = pTable = pClsModule->GetAvailableClassHash();
+
+ }
+ else {
+ // currently we expect only these two kinds--for DAC builds, nhTable will be nhCaseSensitive
+ _ASSERTE(nhTable == nhCaseInsensitive);
+ *ppTable = pTable = pClsModule->GetAvailableClassCaseInsHash();
+
+ if (pTable == NULL) {
+ // We have not built the table yet - the caller will handle
+ return NULL;
+ }
+ }
+
+ _ASSERTE(pTable);
+
+ EEClassHashTable::LookupContext sContext;
+ if ((pBucket = pTable->GetValue(pName, pData, TRUE, &sContext)) != NULL) {
+ switch (TypeFromToken(pName->GetTypeToken())) {
+ case mdtTypeDef:
+ while ((!CompareNestedEntryWithTypeDef(pModule->GetMDImport(),
+ mdEncloser,
+ pClsModule->GetAvailableClassHash(),
+ pBucket->GetEncloser())) &&
+ (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL);
+ break;
+ case mdtTypeRef:
+ while ((!CompareNestedEntryWithTypeRef(pModule->GetMDImport(),
+ mdEncloser,
+ pClsModule->GetAvailableClassHash(),
+ pBucket->GetEncloser())) &&
+ (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL);
+ break;
+ case mdtExportedType:
+ while ((!CompareNestedEntryWithExportedType(pModule->GetAssembly()->GetManifestImport(),
+ mdEncloser,
+ pClsModule->GetAvailableClassHash(),
+ pBucket->GetEncloser())) &&
+ (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL);
+ break;
+ default:
+ while ((pBucket->GetEncloser() != pName->GetBucket()) &&
+ (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL);
+ }
+ }
+ if (pBucket) // break on the first success
+ break;
+ }
+ }
+ else {
+ // Check if this non-nested class is in the table of available classes.
+ ModuleIterator i = GetAssembly()->IterateModules();
+ Module *pModule = NULL;
+
+ while (i.Next()) {
+ pModule = i.GetModule();
+ // i.Next will not return TRUE unless i.GetModule will return non-NULL.
+ PREFIX_ASSUME(pModule != NULL);
+ if (pModule->IsResource())
+ continue;
+ if (pLookInThisModuleOnly && (pModule != pLookInThisModuleOnly))
+ continue;
+
+ PREFIX_ASSUME(pModule!=NULL);
+ EEClassHashTable* pTable = NULL;
+ if (nhTable == nhCaseSensitive)
+ *ppTable = pTable = pModule->GetAvailableClassHash();
+ else {
+ // currently we support only these two types
+ _ASSERTE(nhTable == nhCaseInsensitive);
+ *ppTable = pTable = pModule->GetAvailableClassCaseInsHash();
+
+ // We have not built the table yet - the caller will handle
+ if (pTable == NULL)
+ return NULL;
+ }
+
+ _ASSERTE(pTable);
+ pBucket = pTable->GetValue(pName, pData, FALSE, NULL);
+ if (pBucket) // break on the first success
+ break;
+ }
+ }
+
+ return pBucket;
+}
+
+#ifndef DACCESS_COMPILE
+
+VOID ClassLoader::PopulateAvailableClassHashTable(Module* pModule,
+ AllocMemTracker *pamTracker)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ mdTypeDef td;
+ HENUMInternal hTypeDefEnum;
+ IMDInternalImport * pImport = pModule->GetMDImport();
+
+ LPCSTR szWinRtNamespacePrefix = NULL;
+ DWORD cchWinRtNamespacePrefix = 0;
+
+#ifdef FEATURE_COMINTEROP
+ SString ssFileName;
+ StackScratchBuffer ssFileNameBuffer;
+
+ if (pModule->GetAssembly()->IsWinMD() &&
+ !pModule->IsIntrospectionOnly())
+ { // WinMD file in execution context (not ReflectionOnly context) - use its file name as WinRT namespace prefix
+ // (Windows requirement)
+ // Note: Reflection can work on 'unfinished' WinMD files where the types are in 'wrong' WinMD file (i.e.
+ // type namespace does not start with the file name)
+
+ _ASSERTE(pModule->GetFile()->IsAssembly()); // No multi-module WinMD file support
+ _ASSERTE(!pModule->GetFile()->GetPath().IsEmpty());
+
+ SplitPath(
+ pModule->GetFile()->GetPath(),
+ NULL, // Drive
+ NULL, // Directory
+ &ssFileName,
+ NULL); // Extension
+
+ szWinRtNamespacePrefix = ssFileName.GetUTF8(ssFileNameBuffer);
+ cchWinRtNamespacePrefix = (DWORD)strlen(szWinRtNamespacePrefix);
+ }
+#endif //FEATURE_COMINTEROP
+
+ IfFailThrow(pImport->EnumTypeDefInit(&hTypeDefEnum));
+
+ // Now loop through all the classdefs adding the CVID and scope to the hash
+ while(pImport->EnumTypeDefNext(&hTypeDefEnum, &td)) {
+
+ AddAvailableClassHaveLock(pModule,
+ td,
+ pamTracker,
+ szWinRtNamespacePrefix,
+ cchWinRtNamespacePrefix);
+ }
+ pImport->EnumTypeDefClose(&hTypeDefEnum);
+}
+
+
+void ClassLoader::LazyPopulateCaseInsensitiveHashTables()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END;
+
+
+ // Add any unhashed modules into our hash tables, and try again.
+
+ ModuleIterator i = GetAssembly()->IterateModules();
+
+ while (i.Next()) {
+ Module *pModule = i.GetModule();
+ PREFIX_ASSUME(pModule!=NULL);
+ if (pModule->IsResource())
+ continue;
+
+ if (pModule->GetAvailableClassCaseInsHash() == NULL) {
+ AllocMemTracker amTracker;
+ EEClassHashTable *pNewClassCaseInsHash = pModule->GetAvailableClassHash()->MakeCaseInsensitiveTable(pModule, &amTracker);
+
+ LOG((LF_CLASSLOADER, LL_INFO10, "%s's classes being added to case insensitive hash table\n",
+ pModule->GetSimpleName()));
+
+ {
+ CANNOTTHROWCOMPLUSEXCEPTION();
+ FAULT_FORBID();
+
+ amTracker.SuppressRelease();
+ pModule->SetAvailableClassCaseInsHash(pNewClassCaseInsHash);
+ FastInterlockDecrement((LONG*)&m_cUnhashedModules);
+ }
+ }
+ }
+}
+
+/*static*/
+void DECLSPEC_NORETURN ClassLoader::ThrowTypeLoadException(TypeKey *pKey,
+ UINT resIDWhy)
+{
+ STATIC_CONTRACT_THROWS;
+
+ StackSString fullName;
+ StackSString assemblyName;
+ TypeString::AppendTypeKey(fullName, pKey);
+ pKey->GetModule()->GetAssembly()->GetDisplayName(assemblyName);
+ ::ThrowTypeLoadException(fullName, assemblyName, NULL, resIDWhy);
+}
+
+#endif
+
+
+TypeHandle ClassLoader::LoadConstructedTypeThrowing(TypeKey *pKey,
+ LoadTypesFlag fLoadTypes /*= LoadTypes*/,
+ ClassLoadLevel level /*=CLASS_LOADED*/,
+ const InstantiationContext *pInstContext /*=NULL*/)
+{
+ CONTRACT(TypeHandle)
+ {
+ 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()); }
+ if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+ if (fLoadTypes == DontLoadTypes) SO_TOLERANT; else SO_INTOLERANT;
+ PRECONDITION(CheckPointer(pKey));
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ PRECONDITION(CheckPointer(pInstContext, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL, fLoadTypes==DontLoadTypes ? NULL_OK : NULL_NOT_OK));
+ POSTCONDITION(RETVAL.IsNull() || RETVAL.GetLoadLevel() >= level);
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END
+
+ TypeHandle typeHnd;
+ ClassLoadLevel existingLoadLevel = CLASS_LOAD_BEGIN;
+
+ // Lookup in the classes that this class loader knows about
+
+ if (pKey->HasInstantiation() && ClassLoader::IsTypicalSharedInstantiation(pKey->GetInstantiation()))
+ {
+ _ASSERTE(pKey->GetModule() == ComputeLoaderModule(pKey));
+ typeHnd = pKey->GetModule()->LookupFullyCanonicalInstantiation(pKey->GetTypeToken(), &existingLoadLevel);
+ }
+
+ if (typeHnd.IsNull())
+ {
+ typeHnd = LookupTypeHandleForTypeKey(pKey);
+ if (!typeHnd.IsNull())
+ {
+ existingLoadLevel = typeHnd.GetLoadLevel();
+ if (existingLoadLevel >= level)
+ g_IBCLogger.LogTypeHashTableAccess(&typeHnd);
+ }
+ }
+
+ // If something has been published in the tables, and it's at the right level, just return it
+ if (!typeHnd.IsNull() && existingLoadLevel >= level)
+ {
+ RETURN typeHnd;
+ }
+
+#ifndef DACCESS_COMPILE
+ if (typeHnd.IsNull() && pKey->HasInstantiation())
+ {
+ if (!Generics::CheckInstantiation(pKey->GetInstantiation()))
+ pKey->GetModule()->GetAssembly()->ThrowTypeLoadException(pKey->GetModule()->GetMDImport(), pKey->GetTypeToken(), IDS_CLASSLOAD_INVALIDINSTANTIATION);
+ }
+#endif
+
+ // If we're not loading any types at all, then we're not creating
+ // instantiations either because we're in FORBIDGC_LOADER_USE mode, so
+ // we should bail out here.
+ if (fLoadTypes == DontLoadTypes)
+ RETURN TypeHandle();
+
+#ifndef DACCESS_COMPILE
+ // If we got here, we now have to allocate a new parameterized type.
+ // By definition, forbidgc-users aren't allowed to reach this point.
+ CONSISTENCY_CHECK(!FORBIDGC_LOADER_USE_ENABLED());
+
+ Module *pLoaderModule = ComputeLoaderModule(pKey);
+ RETURN(pLoaderModule->GetClassLoader()->LoadTypeHandleForTypeKey(pKey, typeHnd, level, pInstContext));
+#else
+ DacNotImpl();
+ RETURN(typeHnd);
+#endif
+}
+
+
+/*static*/
+void ClassLoader::EnsureLoaded(TypeHandle typeHnd, ClassLoadLevel level)
+{
+ CONTRACTL
+ {
+ PRECONDITION(CheckPointer(typeHnd));
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ 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()); }
+ if (FORBIDGC_LOADER_USE_ENABLED()) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+ SUPPORTS_DAC;
+
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+#ifndef DACCESS_COMPILE // Nothing to do for the DAC case
+
+ if (typeHnd.GetLoadLevel() < level)
+ {
+ INTERIOR_STACK_PROBE_CHECK_THREAD;
+
+#ifdef FEATURE_PREJIT
+ if (typeHnd.GetLoadLevel() == CLASS_LOAD_UNRESTOREDTYPEKEY)
+ {
+ typeHnd.DoRestoreTypeKey();
+ }
+#endif
+ if (level > CLASS_LOAD_UNRESTORED)
+ {
+ TypeKey typeKey = typeHnd.GetTypeKey();
+
+ Module *pLoaderModule = ComputeLoaderModule(&typeKey);
+ pLoaderModule->GetClassLoader()->LoadTypeHandleForTypeKey(&typeKey, typeHnd, level);
+ }
+
+ END_INTERIOR_STACK_PROBE;
+ }
+
+#endif // DACCESS_COMPILE
+}
+
+/*static*/
+void ClassLoader::TryEnsureLoaded(TypeHandle typeHnd, ClassLoadLevel level)
+{
+ WRAPPER_NO_CONTRACT;
+
+#ifndef DACCESS_COMPILE // Nothing to do for the DAC case
+
+ EX_TRY
+ {
+ ClassLoader::EnsureLoaded(typeHnd, level);
+ }
+ EX_CATCH
+ {
+ // Some type may not load successfully. For eg. generic instantiations
+ // that do not satisfy the constraints of the type arguments.
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+
+#endif // DACCESS_COMPILE
+}
+
+// This is separated out to avoid the overhead of C++ exception handling in the non-locking case.
+/* static */
+TypeHandle ClassLoader::LookupTypeKeyUnderLock(TypeKey *pKey,
+ EETypeHashTable *pTable,
+ CrstBase *pLock)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ CrstHolder ch(pLock);
+ return pTable->GetValue(pKey);
+}
+
+/* static */
+TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey,
+ EETypeHashTable *pTable,
+ CrstBase *pLock,
+ BOOL fCheckUnderLock)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ PRECONDITION(CheckPointer(pKey));
+ PRECONDITION(pKey->IsConstructed());
+ PRECONDITION(CheckPointer(pTable));
+ PRECONDITION(!fCheckUnderLock || CheckPointer(pLock));
+ MODE_ANY;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ TypeHandle th;
+
+ // If this is the GC thread, and we're hosted, we're in a sticky situation with
+ // SQL where we may have suspended another thread while doing Thread::SuspendRuntime.
+ // In this case, we have the issue that a thread holding this lock could be
+ // suspended, perhaps implicitly because the active thread on the SQL scheduler
+ // has been suspended by the GC thread. In such a case, we need to skip taking
+ // the lock. We can be sure that there will be no races in such a condition because
+ // we will only be looking for types that are already loaded, or for a type that
+ // is not loaded, but we will never cause the type to get loaded, and so the result
+ // of the lookup will not change.
+#ifndef DACCESS_COMPILE
+ if (fCheckUnderLock && !(IsGCThread() && CLRTaskHosted()))
+#else
+ if (fCheckUnderLock)
+#endif // DACCESS_COMPILE
+ {
+ th = LookupTypeKeyUnderLock(pKey, pTable, pLock);
+ }
+ else
+ {
+ th = pTable->GetValue(pKey);
+ }
+ return th;
+}
+
+
+#ifdef FEATURE_PREJIT
+/* static */
+TypeHandle ClassLoader::LookupInPreferredZapModule(TypeKey *pKey, BOOL fCheckUnderLock)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ PRECONDITION(CheckPointer(pKey));
+ PRECONDITION(pKey->IsConstructed());
+ MODE_ANY;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ // First look for an NGEN'd type in the preferred ngen module
+ TypeHandle th;
+ PTR_Module pPreferredZapModule = Module::ComputePreferredZapModule(pKey);
+
+ if (pPreferredZapModule != NULL && pPreferredZapModule->HasNativeImage())
+ {
+ th = LookupTypeKey(pKey,
+ pPreferredZapModule->GetAvailableParamTypes(),
+ &pPreferredZapModule->GetClassLoader()->m_AvailableTypesLock,
+ fCheckUnderLock);
+ }
+
+ return th;
+}
+#endif // FEATURE_PREJIT
+
+
+/* static */
+TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey, BOOL fCheckUnderLock)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ PRECONDITION(CheckPointer(pKey));
+ PRECONDITION(pKey->IsConstructed());
+ MODE_ANY;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ Module *pLoaderModule = ComputeLoaderModule(pKey);
+ PREFIX_ASSUME(pLoaderModule!=NULL);
+
+ return LookupTypeKey(pKey,
+ pLoaderModule->GetAvailableParamTypes(),
+ &pLoaderModule->GetClassLoader()->m_AvailableTypesLock,
+ fCheckUnderLock);
+}
+
+
+/* static */
+TypeHandle ClassLoader::LookupTypeHandleForTypeKey(TypeKey *pKey)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ // Make an initial lookup without taking any locks.
+ TypeHandle th = LookupTypeHandleForTypeKeyInner(pKey, FALSE);
+
+ // A non-null TypeHandle for the above lookup indicates success
+ // A null TypeHandle only indicates "well, it might have been there,
+ // try again with a lock". This kind of negative result will
+ // only happen while accessing the underlying EETypeHashTable
+ // during a resize, i.e. very rarely. In such a case, we just
+ // perform the lookup again, but indicate that appropriate locks
+ // should be taken.
+
+ if (th.IsNull())
+ {
+ th = LookupTypeHandleForTypeKeyInner(pKey, TRUE);
+ }
+
+ return th;
+}
+/* static */
+TypeHandle ClassLoader::LookupTypeHandleForTypeKeyInner(TypeKey *pKey, BOOL fCheckUnderLock)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ PRECONDITION(CheckPointer(pKey));
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ // Check if it's the typical instantiation. In this case it's not stored in the same
+ // way as other constructed types.
+ if (!pKey->IsConstructed() ||
+ pKey->GetKind() == ELEMENT_TYPE_CLASS && ClassLoader::IsTypicalInstantiation(pKey->GetModule(),
+ pKey->GetTypeToken(),
+ pKey->GetInstantiation()))
+ {
+ return TypeHandle(pKey->GetModule()->LookupTypeDef(pKey->GetTypeToken()));
+ }
+
+#ifdef FEATURE_PREJIT
+ // The following ways of finding a constructed type should be mutually exclusive!
+ // 1. Look for a zapped item in the PreferredZapModule
+ // 2. Look for a unzapped (JIT-loaded) item in the LoaderModule
+
+ TypeHandle thPZM = LookupInPreferredZapModule(pKey, fCheckUnderLock);
+ if (!thPZM.IsNull())
+ {
+ return thPZM;
+ }
+#endif // FEATURE_PREJIT
+
+ // Next look in the loader module. This is where the item is guaranteed to live if
+ // it is not latched from an NGEN image, i.e. if it is JIT loaded.
+ // If the thing is not NGEN'd then this may
+ // be different to pPreferredZapModule. If they are the same then
+ // we can reuse the results of the lookup above.
+ TypeHandle thLM = LookupInLoaderModule(pKey, fCheckUnderLock);
+ if (!thLM.IsNull())
+ {
+ return thLM;
+ }
+
+ return TypeHandle();
+}
+
+
+//---------------------------------------------------------------------------
+// ClassLoader::TryFindDynLinkZapType
+//
+// This is a major routine in the process of finding and using
+// zapped generic instantiations (excluding those which were zapped into
+// their PreferredZapModule).
+//
+// DynLinkZapItems are generic instantiations that may have been NGEN'd
+// into more than one NGEN image (e.g. the code and TypeHandle for
+// List<int> may in principle be zapped into several client images - it is theoretically
+// an NGEN policy decision about how often this done, though for now we
+// have hard-baked a strategy).
+//
+// There are lots of potential problems with this kind of duplication
+// and the way we get around nearly all of these is to make sure that
+// we only use one at most one "unique" copy of each item
+// at runtime. Thus we keep tables in the SharedDomain and the AppDomain indicating
+// which unique items have been chosen. If an item is "loaded" by this technique
+// then it will not be loaded by any other technique.
+//
+// Note generic instantiations may have the good fortune to be zapped
+// into the "PreferredZapModule". If so we can eager bind to them and
+// they will not be considered to be DynLinkZapItems. We always
+// look in the PreferredZapModule first, and we do not add an entry to the
+// DynLinkZapItems table for this case.
+//
+// Zap references to DynLinkZapItems are always via encoded fixups, except
+// for a few intra-module references when one DynLinkZapItem is "TightlyBound"
+// to another, e.g. an canonical DynLinkZap MethodTable may directly refer to
+// its EEClass - this is because we know that if one is used at runtime then the
+// other will also be. These items should be thought of as together constituting
+// one DynLinkedZapItem.
+//
+// This function section searches for a copy of the instantiation in various NGEN images.
+// This is effectively like doing a load since we are choosing which copy of the instantiation
+// to use from among a number of potential candidates. We have to have the loading lock
+// for this item before we can do this to make sure no other threads choose a
+// different copy of the instantiation, and that no other threads are JIT-loading
+// the instantiation.
+
+
+
+#ifndef DACCESS_COMPILE
+#ifdef FEATURE_FULL_NGEN
+/* static */
+TypeHandle ClassLoader::TryFindDynLinkZapType(TypeKey *pKey)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pKey));
+ PRECONDITION(pKey->IsConstructed());
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // For the introspection-only case, we can skip this step as introspection assemblies
+ // do not use NGEN images.
+ if (pKey->IsIntrospectionOnly())
+ return TypeHandle();
+
+ // Never use dyn link zap items during ngen time. We will independently decide later
+ // whether we want to store the item into ngen image or not.
+ // Note that it is not good idea to make decisions based on the list of depencies here
+ // since their list may not be fully populated yet.
+ if (IsCompilationProcess())
+ return TypeHandle();
+
+ TypeHandle th = TypeHandle();
+
+#ifndef CROSSGEN_COMPILE
+ // We need to know which domain the item must live in (DomainNeutral or AppDomain)
+ // Note we can't use the domain from GetLoaderModule()->GetDomain() because at NGEN
+ // time this may not be accurate (we may be deliberately duplicating a domain-neutral
+ // instantiation into a domain-specific image, in the sense that the LoaderModule
+ // returned by ComputeLoaderModule may be the current module being
+ // NGEN'd)....
+
+ BaseDomain * pRequiredDomain = BaseDomain::ComputeBaseDomain(pKey);
+
+ // Next look in each ngen'ed image in turn
+
+ // Searching the shared domain and the app domain are slightly different.
+ if (pRequiredDomain->IsSharedDomain())
+ {
+ // This switch to cooperative mode makes the iteration below thread safe. It ensures that the underlying
+ // async HashMap storage is not going to disapper while we are iterating it. Other uses of SharedAssemblyIterator
+ // have same problem, but I have fixed just this one as targeted ask mode fix.
+ GCX_COOP();
+
+ // Searching for SharedDomain instantiation involves searching all shared assemblies....
+ // Note we may choose to use an instantiation from an assembly that is from an NGEN
+ // image that is not logically speaking part of the currently running AppDomain. This
+ // tkaes advantage of the fact that at the moment SharedDomain NGEN images are never unloaded.
+ // Thus SharedDomain NGEN images effectively contribute all their instantiations to all
+ // AppDomains.
+ //
+ // <NOTE> This will have to change if we ever start unloading NGEN images from the SharedDomain </NOTE>
+ SharedDomain::SharedAssemblyIterator assem;
+ while (th.IsNull() && assem.Next())
+ {
+ ModuleIterator i = assem.GetAssembly()->IterateModules();
+
+ while (i.Next())
+ {
+ Module *pModule = i.GetModule();
+ if (!pModule->HasNativeImage())
+ continue;
+
+ // If the module hasn't reached FILE_LOADED in some domain, it cannot provide candidate instantiations
+ if (!pModule->IsReadyForTypeLoad())
+ continue;
+
+ TypeHandle thFromZapModule = pModule->GetAvailableParamTypes()->GetValue(pKey);
+
+ // Check that the item really is a zapped item, i.e. that it has not been JIT-loaded to the module
+ if (thFromZapModule.IsNull() || !thFromZapModule.IsZapped())
+ continue;
+
+ th = thFromZapModule;
+ }
+ }
+ }
+ else
+ {
+ // Searching for domain specific instantiation involves searching all
+ // domain-specific assemblies in the relevant AppDomain....
+
+ AppDomain * pDomain = pRequiredDomain->AsAppDomain();
+
+ _ASSERTE(!(pKey->IsIntrospectionOnly()));
+ AppDomain::AssemblyIterator assemblyIterator = pDomain->IterateAssembliesEx(
+ (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (th.IsNull() && assemblyIterator.Next(pDomainAssembly.This()))
+ {
+ CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
+ // Make sure the domain of the NGEN'd images associated with the assembly matches...
+ if (pAssembly->GetDomain() == pRequiredDomain)
+ {
+ DomainAssembly::ModuleIterator i = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
+ while (th.IsNull() && i.Next())
+ {
+ Module * pModule = i.GetLoadedModule();
+ if (!pModule->HasNativeImage())
+ continue;
+
+ // If the module hasn't reached FILE_LOADED in some domain, it cannot provide candidate instantiations
+ if (!pModule->IsReadyForTypeLoad())
+ continue;
+
+ TypeHandle thFromZapModule = pModule->GetAvailableParamTypes()->GetValue(pKey);
+
+ // Check that the item really is a zapped item
+ if (thFromZapModule.IsNull() || !thFromZapModule.IsZapped())
+ continue;
+
+ th = thFromZapModule;
+ }
+ }
+ }
+ }
+#endif // CROSSGEN_COMPILE
+
+ return th;
+}
+#endif // FEATURE_FULL_NGEN
+#endif // !DACCESS_COMPILE
+
+// FindClassModuleThrowing discovers which module the type you're looking for is in and loads the Module if necessary.
+// Basically, it iterates through all of the assembly's modules until a name match is found in a module's
+// AvailableClassHashTable.
+//
+// The possible outcomes are:
+//
+// - Function returns TRUE - class exists and we successfully found/created the containing Module. See below
+// for how to deconstruct the results.
+// - Function returns FALSE - class affirmatively NOT found (that means it doesn't exist as a regular type although
+// it could also be a parameterized type)
+// - Function throws - OOM or some other reason we couldn't do the job (if it's a case-sensitive search
+// and you're looking for already loaded type or you've set the TokenNotToLoad.
+// we are guaranteed not to find a reason to throw.)
+//
+//
+// If it succeeds (returns TRUE), one of the following will occur. Check (*pType)->IsNull() to discriminate.
+//
+// 1. *pType: set to the null TypeHandle()
+// *ppModule: set to the owning Module
+// *pmdClassToken: set to the typedef
+// *pmdFoundExportedType: if this name bound to an ExportedType, this contains the mdtExportedType token (otherwise,
+// it's set to mdTokenNil.) You need this because in this case, *pmdClassToken is just
+// a best guess and you need to verify it. (The division of labor between this
+// and LoadTypeHandle could definitely be better!)
+//
+// 2. *pType: set to non-null TypeHandle()
+// This means someone else had already done this same lookup before you and caused the actual
+// TypeHandle to be cached. Since we know that's what you *really* wanted, we'll just forget the
+// Module/typedef stuff and give you the actual TypeHandle.
+//
+//
+BOOL
+ClassLoader::FindClassModuleThrowing(
+ const NameHandle * pOriginalName,
+ TypeHandle * pType,
+ mdToken * pmdClassToken,
+ Module ** ppModule,
+ mdToken * pmdFoundExportedType,
+ EEClassHashEntry_t ** ppEntry,
+ Module * pLookInThisModuleOnly,
+ Loader::LoadFlag loadFlag)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ 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()); }
+ PRECONDITION(CheckPointer(pOriginalName));
+ PRECONDITION(CheckPointer(ppModule));
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ NameHandleTable nhTable = nhCaseSensitive; // just to initialize this ...
+
+ // Make a copy of the original name which we can modify (to lowercase)
+ NameHandle localName = *pOriginalName;
+ NameHandle * pName = &localName;
+
+ switch (pName->GetTable())
+ {
+ case nhCaseInsensitive:
+ {
+#ifndef DACCESS_COMPILE
+ // GC-type users should only be loading types through tokens.
+#ifdef _DEBUG_IMPL
+ _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
+#endif
+
+ // Use the case insensitive table
+ nhTable = nhCaseInsensitive;
+
+ // Create a low case version of the namespace and name
+ LPUTF8 pszLowerNameSpace = NULL;
+ LPUTF8 pszLowerClassName = NULL;
+ int allocLen;
+
+ if (pName->GetNameSpace())
+ {
+ allocLen = InternalCasingHelper::InvariantToLower(
+ NULL,
+ 0,
+ pName->GetNameSpace());
+ if (allocLen == 0)
+ {
+ return FALSE;
+ }
+
+ pszLowerNameSpace = (LPUTF8)_alloca(allocLen);
+ if (allocLen == 1)
+ {
+ *pszLowerNameSpace = '\0';
+ }
+ else if (!InternalCasingHelper::InvariantToLower(
+ pszLowerNameSpace,
+ allocLen,
+ pName->GetNameSpace()))
+ {
+ return FALSE;
+ }
+ }
+
+ _ASSERTE(pName->GetName() != NULL);
+ allocLen = InternalCasingHelper::InvariantToLower(NULL, 0, pName->GetName());
+ if (allocLen == 0)
+ {
+ return FALSE;
+ }
+
+ pszLowerClassName = (LPUTF8)_alloca(allocLen);
+ if (!InternalCasingHelper::InvariantToLower(
+ pszLowerClassName,
+ allocLen,
+ pName->GetName()))
+ {
+ return FALSE;
+ }
+
+ // Substitute the lower case version of the name.
+ // The field are will be released when we leave this scope
+ pName->SetName(pszLowerNameSpace, pszLowerClassName);
+ break;
+#else
+ DacNotImpl();
+ break;
+#endif // #ifndef DACCESS_COMPILE
+ }
+ case nhCaseSensitive:
+ nhTable = nhCaseSensitive;
+ break;
+ }
+
+ // Remember if there are any unhashed modules. We must do this before
+ // the actual look to avoid a race condition with other threads doing lookups.
+#ifdef LOGGING
+ BOOL incomplete = (m_cUnhashedModules > 0);
+#endif
+
+ HashDatum Data;
+ EEClassHashTable * pTable = NULL;
+ EEClassHashEntry_t * pBucket = GetClassValue(
+ nhTable,
+ pName,
+ &Data,
+ &pTable,
+ pLookInThisModuleOnly);
+
+ if (pBucket == NULL)
+ {
+ if (nhTable == nhCaseInsensitive)
+ {
+ AvailableClasses_LockHolder lh(this);
+
+ // Try again with the lock. This will protect against another thread reallocating
+ // the hash table underneath us
+ pBucket = GetClassValue(
+ nhTable,
+ pName,
+ &Data,
+ &pTable,
+ pLookInThisModuleOnly);
+
+#ifndef DACCESS_COMPILE
+ if ((pBucket == NULL) && (m_cUnhashedModules > 0))
+ {
+ LazyPopulateCaseInsensitiveHashTables();
+
+ // Try yet again with the new classes added
+ pBucket = GetClassValue(
+ nhTable,
+ pName,
+ &Data,
+ &pTable,
+ pLookInThisModuleOnly);
+ }
+#endif
+ }
+ }
+
+ if (pBucket == NULL)
+ {
+#if defined(_DEBUG_IMPL) && !defined(DACCESS_COMPILE)
+ LPCUTF8 szName = pName->GetName();
+ if (szName == NULL)
+ szName = "<UNKNOWN>";
+ LOG((LF_CLASSLOADER, LL_INFO10, "Failed to find type \"%s\", assembly \"%ws\" in hash table. Incomplete = %d\n",
+ szName, GetAssembly()->GetDebugName(), incomplete));
+#endif
+ return FALSE;
+ }
+
+ if (pName->GetTable() == nhCaseInsensitive)
+ {
+ _ASSERTE(Data);
+ pBucket = PTR_EEClassHashEntry(Data);
+ Data = pBucket->GetData();
+ }
+
+ // Lower bit is a discriminator. If the lower bit is NOT SET, it means we have
+ // a TypeHandle. Otherwise, we have a Module/CL.
+ if ((dac_cast<TADDR>(Data) & EECLASSHASH_TYPEHANDLE_DISCR) == 0)
+ {
+ TypeHandle t = TypeHandle::FromPtr(Data);
+ _ASSERTE(!t.IsNull());
+
+ *pType = t;
+ if (ppEntry != NULL)
+ {
+ *ppEntry = pBucket;
+ }
+ return TRUE;
+ }
+
+ // We have a Module/CL
+ if (!pTable->UncompressModuleAndClassDef(Data,
+ loadFlag,
+ ppModule,
+ pmdClassToken,
+ pmdFoundExportedType))
+ {
+ _ASSERTE(loadFlag != Loader::Load);
+ return FALSE;
+ }
+
+ *pType = TypeHandle();
+ if (ppEntry != NULL)
+ {
+ *ppEntry = pBucket;
+ }
+ return TRUE;
+} // ClassLoader::FindClassModuleThrowing
+
+#ifndef DACCESS_COMPILE
+// Returns true if the full name (namespace+name) of pName matches that
+// of typeHnd; otherwise false. Because this is nothrow, it will default
+// to false for all exceptions (such as OOM).
+bool CompareNameHandleWithTypeHandleNoThrow(
+ const NameHandle * pName,
+ TypeHandle typeHnd)
+{
+ bool fRet = false;
+
+ EX_TRY
+ {
+ // This block is specifically designed to handle transient faults such
+ // as OOM exceptions.
+ CONTRACT_VIOLATION(FaultViolation | ThrowsViolation);
+ StackSString ssBuiltName;
+ ns::MakePath(ssBuiltName,
+ StackSString(SString::Utf8, pName->GetNameSpace()),
+ StackSString(SString::Utf8, pName->GetName()));
+ StackSString ssName;
+ typeHnd.GetName(ssName);
+ fRet = ssName.Equals(ssBuiltName) == TRUE;
+ }
+ EX_CATCH
+ {
+ // Technically, the above operations should never result in a non-OOM
+ // exception, but we'll put the rethrow line in there just in case.
+ CONSISTENCY_CHECK(!GET_EXCEPTION()->IsTerminal());
+ RethrowTerminalExceptions;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return fRet;
+}
+#endif // #ifndef DACCESS_COMPILE
+
+// 1024 seems like a good bet at detecting a loop in the type forwarding.
+static const UINT32 const_cMaxTypeForwardingChainSize = 1024;
+
+// Does not throw an exception if the type was not found. Use LoadTypeHandleThrowIfFailed()
+// instead if you need that.
+//
+// Returns:
+// pName->m_pBucket
+// Will be set to the 'final' TypeDef bucket if pName->GetTokenType() is mdtBaseType.
+//
+TypeHandle
+ClassLoader::LoadTypeHandleThrowing(
+ NameHandle * pName,
+ ClassLoadLevel level,
+ Module * pLookInThisModuleOnly /*=NULL*/)
+{
+ CONTRACT(TypeHandle) {
+ INSTANCE_CHECK;
+ 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()); }
+ DAC_LOADS_TYPE(level, !pName->OKToLoad());
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ PRECONDITION(CheckPointer(pName));
+ POSTCONDITION(RETVAL.IsNull() || RETVAL.GetLoadLevel() >= level);
+ MODE_ANY;
+ SUPPORTS_DAC;
+ } CONTRACT_END
+
+ TypeHandle typeHnd;
+ INTERIOR_STACK_PROBE_NOTHROW_CHECK_THREAD(RETURN_FROM_INTERIOR_PROBE(TypeHandle()));
+
+ Module * pFoundModule = NULL;
+ mdToken FoundCl;
+ EEClassHashEntry_t * pEntry = NULL;
+ mdExportedType FoundExportedType = mdTokenNil;
+
+ UINT32 cLoopIterations = 0;
+
+ ClassLoader * pClsLdr = this;
+
+ while (true)
+ {
+ if (cLoopIterations++ >= const_cMaxTypeForwardingChainSize)
+ { // If we've looped too many times due to type forwarding, return null TypeHandle
+ // Would prefer to return a format exception, but the original behaviour
+ // was to detect a stack overflow possibility and return a null, and
+ // so we need to maintain this.
+ typeHnd = TypeHandle();
+ break;
+ }
+
+ // Look outside the lock (though we're actually still a long way from the
+ // lock at this point...). This may discover that the type is actually
+ // defined in another module...
+
+ if (!pClsLdr->FindClassModuleThrowing(
+ pName,
+ &typeHnd,
+ &FoundCl,
+ &pFoundModule,
+ &FoundExportedType,
+ &pEntry,
+ pLookInThisModuleOnly,
+ pName->OKToLoad() ? Loader::Load
+ : Loader::DontLoad))
+ { // Didn't find anything, no point looping indefinitely
+ break;
+ }
+ _ASSERTE(pEntry != NULL);
+
+ if (pName->GetTypeToken() == mdtBaseType)
+ { // We should return the found bucket in the pName
+ pName->SetBucket(pEntry);
+ }
+
+ if (!typeHnd.IsNull())
+ { // Found the cached value, or a constructedtype
+ if (typeHnd.GetLoadLevel() < level)
+ {
+ typeHnd = pClsLdr->LoadTypeDefThrowing(
+ typeHnd.GetModule(),
+ typeHnd.GetCl(),
+ ClassLoader::ReturnNullIfNotFound,
+ ClassLoader::PermitUninstDefOrRef, // When loading by name we always permit naked type defs/refs
+ pName->GetTokenNotToLoad(),
+ level);
+ }
+ break;
+ }
+
+ // Found a cl, pModule pair
+
+ // If the found module's class loader is not the same as the current class loader,
+ // then this is a forwarded type and we want to do something else (see
+ // code:#LoadTypeHandle_TypeForwarded).
+ if (pFoundModule->GetClassLoader() == pClsLdr)
+ {
+ BOOL fTrustTD = TRUE;
+#ifndef DACCESS_COMPILE
+ CONTRACT_VIOLATION(ThrowsViolation);
+ BOOL fVerifyTD = (FoundExportedType != mdTokenNil) &&
+ !pClsLdr->GetAssembly()->GetSecurityDescriptor()->IsFullyTrusted();
+
+ // If this is an exported type with a mdTokenNil class token, then then
+ // exported type did not give a typedefID hint. We won't be able to trust the typedef
+ // here.
+ if ((FoundExportedType != mdTokenNil) && (FoundCl == mdTokenNil))
+ {
+ fVerifyTD = TRUE;
+ fTrustTD = FALSE;
+ }
+ // verify that FoundCl is a valid token for pFoundModule, because
+ // it may be just the hint saved in an ExportedType in another scope
+ else if (fVerifyTD)
+ {
+ fTrustTD = pFoundModule->GetMDImport()->IsValidToken(FoundCl);
+ }
+#endif // #ifndef DACCESS_COMPILE
+
+ if (fTrustTD)
+ {
+ typeHnd = pClsLdr->LoadTypeDefThrowing(
+ pFoundModule,
+ FoundCl,
+ ClassLoader::ReturnNullIfNotFound,
+ ClassLoader::PermitUninstDefOrRef, // when loading by name we always permit naked type defs/refs
+ pName->GetTokenNotToLoad(),
+ level);
+ }
+#ifndef DACCESS_COMPILE
+ // If we used a TypeDef saved in a ExportedType, if we didn't verify
+ // the hash for this internal module, don't trust the TD value.
+ if (fVerifyTD)
+ {
+ if (typeHnd.IsNull() || !CompareNameHandleWithTypeHandleNoThrow(pName, typeHnd))
+ {
+ if (SUCCEEDED(pClsLdr->FindTypeDefByExportedType(
+ pClsLdr->GetAssembly()->GetManifestImport(),
+ FoundExportedType,
+ pFoundModule->GetMDImport(),
+ &FoundCl)))
+ {
+ typeHnd = pClsLdr->LoadTypeDefThrowing(
+ pFoundModule,
+ FoundCl,
+ ClassLoader::ReturnNullIfNotFound,
+ ClassLoader::PermitUninstDefOrRef,
+ pName->GetTokenNotToLoad(),
+ level);
+ }
+ else
+ {
+ typeHnd = TypeHandle();
+ }
+ }
+ }
+#endif // #ifndef DACCESS_COMPILE
+ break;
+ }
+ else
+ { //#LoadTypeHandle_TypeForwarded
+ // pName is a host instance so it's okay to set fields in it in a DAC build
+ EEClassHashEntry_t * pBucket = pName->GetBucket();
+
+ if (pBucket != NULL)
+ { // Reset pName's bucket entry
+
+ // We will be searching for the type name again, so set the nesting/context type to the
+ // encloser of just found type
+ pName->SetBucket(pBucket->GetEncloser());
+ }
+
+ // Update the class loader for the new module/token pair.
+ pClsLdr = pFoundModule->GetClassLoader();
+ pLookInThisModuleOnly = NULL;
+ }
+
+#ifndef DACCESS_COMPILE
+ // Replace AvailableClasses Module entry with found TypeHandle
+ if (!typeHnd.IsNull() &&
+ typeHnd.IsRestored() &&
+ (pEntry != NULL) &&
+ (pEntry->GetData() != typeHnd.AsPtr()))
+ {
+ pEntry->SetData(typeHnd.AsPtr());
+ }
+#endif // !DACCESS_COMPILE
+ }
+
+ END_INTERIOR_STACK_PROBE;
+ RETURN typeHnd;
+} // ClassLoader::LoadTypeHandleThrowing
+
+/* static */
+TypeHandle ClassLoader::LoadPointerOrByrefTypeThrowing(CorElementType typ,
+ TypeHandle baseType,
+ LoadTypesFlag fLoadTypes/*=LoadTypes*/,
+ ClassLoadLevel level/*=CLASS_LOADED*/)
+{
+ CONTRACT(TypeHandle)
+ {
+ 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()); }
+ if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+ MODE_ANY;
+ PRECONDITION(CheckPointer(baseType));
+ PRECONDITION(typ == ELEMENT_TYPE_BYREF || typ == ELEMENT_TYPE_PTR);
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END
+
+ TypeKey key(typ, baseType);
+ RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level));
+}
+
+/* static */
+TypeHandle ClassLoader::LoadNativeValueTypeThrowing(TypeHandle baseType,
+ LoadTypesFlag fLoadTypes/*=LoadTypes*/,
+ ClassLoadLevel level/*=CLASS_LOADED*/)
+{
+ CONTRACT(TypeHandle)
+ {
+ 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;
+ PRECONDITION(CheckPointer(baseType));
+ PRECONDITION(baseType.AsMethodTable()->IsValueType());
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
+ }
+ CONTRACT_END
+
+ TypeKey key(ELEMENT_TYPE_VALUETYPE, baseType);
+ RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level));
+}
+
+/* static */
+TypeHandle ClassLoader::LoadFnptrTypeThrowing(BYTE callConv,
+ DWORD ntypars,
+ TypeHandle* inst,
+ LoadTypesFlag fLoadTypes/*=LoadTypes*/,
+ ClassLoadLevel level/*=CLASS_LOADED*/)
+{
+ CONTRACT(TypeHandle)
+ {
+ 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()); }
+ if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END
+
+ TypeKey key(callConv, ntypars, inst);
+ RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level));
+}
+
+// Find an instantiation of a generic type if it has already been created.
+// If typeDef is not a generic type or is already instantiated then throw an exception.
+// If its arity does not match ntypars then throw an exception.
+// Value will be non-null if we're loading types.
+/* static */
+TypeHandle ClassLoader::LoadGenericInstantiationThrowing(Module *pModule,
+ mdTypeDef typeDef,
+ Instantiation inst,
+ LoadTypesFlag fLoadTypes/*=LoadTypes*/,
+ ClassLoadLevel level/*=CLASS_LOADED*/,
+ const InstantiationContext *pInstContext/*=NULL*/,
+ BOOL fFromNativeImage /*=FALSE*/)
+{
+ // This can be called in FORBIDGC_LOADER_USE mode by the debugger to find
+ // a particular generic type instance that is already loaded.
+ CONTRACT(TypeHandle)
+ {
+ if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
+ if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
+ if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+ PRECONDITION(CheckPointer(pModule));
+ MODE_ANY;
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ PRECONDITION(CheckPointer(pInstContext, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END
+
+ // Essentially all checks to determine if a generic instantiation of a type
+ // is well-formed go in this method, i.e. this is the
+ // "choke" point through which all attempts
+ // to create an instantiation flow. There is a similar choke point for generic
+ // methods in genmeth.cpp.
+
+ if (inst.IsEmpty() || ClassLoader::IsTypicalInstantiation(pModule, typeDef, inst))
+ {
+ TypeHandle th = LoadTypeDefThrowing(pModule, typeDef,
+ ThrowIfNotFound,
+ PermitUninstDefOrRef,
+ fLoadTypes == DontLoadTypes ? tdAllTypes : tdNoTypes,
+ level,
+ fFromNativeImage ? NULL : &inst);
+ _ASSERTE(th.GetNumGenericArgs() == inst.GetNumArgs());
+ RETURN th;
+ }
+
+ if (!fFromNativeImage)
+ {
+ TypeHandle th = ClassLoader::LoadTypeDefThrowing(pModule, typeDef,
+ ThrowIfNotFound,
+ PermitUninstDefOrRef,
+ fLoadTypes == DontLoadTypes ? tdAllTypes : tdNoTypes,
+ level,
+ fFromNativeImage ? NULL : &inst);
+ _ASSERTE(th.GetNumGenericArgs() == inst.GetNumArgs());
+ }
+
+ TypeKey key(pModule, typeDef, inst);
+
+#ifndef DACCESS_COMPILE
+ // To avoid loading useless shared instantiations, normalize shared instantiations to the canonical form
+ // (e.g. Dictionary<String,_Canon> -> Dictionary<_Canon,_Canon>)
+ // The denormalized shared instantiations should be needed only during JITing, so it is fine to skip this
+ // for DACCESS_COMPILE.
+ if (TypeHandle::IsCanonicalSubtypeInstantiation(inst) && !IsCanonicalGenericInstantiation(inst))
+ {
+ RETURN(ClassLoader::LoadCanonicalGenericInstantiation(&key, fLoadTypes, level));
+ }
+#endif
+
+ RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level, pInstContext));
+}
+
+// For non-nested classes, gets the ExportedType name and finds the corresponding
+// TypeDef.
+// For nested classes, gets the name of the ExportedType and its encloser.
+// Recursively gets and keeps the name for each encloser until we have the top
+// level one. Gets the TypeDef token for that. Then, returns from the
+// recursion, using the last found TypeDef token in order to find the
+// next nested level down TypeDef token. Finally, returns the TypeDef
+// token for the type we care about.
+/*static*/
+HRESULT ClassLoader::FindTypeDefByExportedType(IMDInternalImport *pCTImport, mdExportedType mdCurrent,
+ IMDInternalImport *pTDImport, mdTypeDef *mtd)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ mdToken mdImpl;
+ LPCSTR szcNameSpace;
+ LPCSTR szcName;
+ HRESULT hr;
+
+ IfFailRet(pCTImport->GetExportedTypeProps(
+ mdCurrent,
+ &szcNameSpace,
+ &szcName,
+ &mdImpl,
+ NULL, //binding
+ NULL)); //flags
+
+ if ((TypeFromToken(mdImpl) == mdtExportedType) &&
+ (mdImpl != mdExportedTypeNil)) {
+ // mdCurrent is a nested ExportedType
+ IfFailRet(FindTypeDefByExportedType(pCTImport, mdImpl, pTDImport, mtd));
+
+ // Get TypeDef token for this nested type
+ return pTDImport->FindTypeDef(szcNameSpace, szcName, *mtd, mtd);
+ }
+
+ // Get TypeDef token for this top-level type
+ return pTDImport->FindTypeDef(szcNameSpace, szcName, mdTokenNil, mtd);
+}
+
+#ifndef DACCESS_COMPILE
+
+VOID ClassLoader::CreateCanonicallyCasedKey(LPCUTF8 pszNameSpace, LPCUTF8 pszName, __out LPUTF8 *ppszOutNameSpace, __out LPUTF8 *ppszOutName)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(COMPlusThrowOM(););
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ // We can use the NoThrow versions here because we only call this routine if we're maintaining
+ // a case-insensitive hash table, and the creation of that table initialized the
+ // CasingHelper system.
+ INT32 iNSLength = InternalCasingHelper::InvariantToLowerNoThrow(NULL, 0, pszNameSpace);
+ if (!iNSLength)
+ {
+ COMPlusThrowOM();
+ }
+
+ INT32 iNameLength = InternalCasingHelper::InvariantToLowerNoThrow(NULL, 0, pszName);
+ if (!iNameLength)
+ {
+ COMPlusThrowOM();
+ }
+
+ {
+ //Calc & allocate path length
+ //Includes terminating null
+ S_SIZE_T allocSize = S_SIZE_T(iNSLength) + S_SIZE_T(iNameLength);
+ if (allocSize.IsOverflow())
+ {
+ ThrowHR(COR_E_OVERFLOW);
+ }
+
+ AllocMemHolder<char> pszOutNameSpace (GetAssembly()->GetHighFrequencyHeap()->AllocMem(allocSize));
+ *ppszOutNameSpace = pszOutNameSpace;
+
+ if (iNSLength == 1)
+ {
+ **ppszOutNameSpace = '\0';
+ }
+ else
+ {
+ if (!InternalCasingHelper::InvariantToLowerNoThrow(*ppszOutNameSpace, iNSLength, pszNameSpace))
+ {
+ COMPlusThrowOM();
+ }
+ }
+
+ *ppszOutName = *ppszOutNameSpace + iNSLength;
+
+ if (!InternalCasingHelper::InvariantToLowerNoThrow(*ppszOutName, iNameLength, pszName))
+ {
+ COMPlusThrowOM();
+ }
+
+ pszOutNameSpace.SuppressRelease();
+ }
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+
+//
+// Return a class that is already loaded
+// Only for type refs and type defs (not type specs)
+//
+/*static*/
+TypeHandle ClassLoader::LookupTypeDefOrRefInModule(Module *pModule, mdToken cl, ClassLoadLevel *pLoadLevel)
+{
+ CONTRACT(TypeHandle)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pModule));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END
+
+ BAD_FORMAT_NOTHROW_ASSERT((TypeFromToken(cl) == mdtTypeRef ||
+ TypeFromToken(cl) == mdtTypeDef ||
+ TypeFromToken(cl) == mdtTypeSpec));
+
+ TypeHandle typeHandle;
+
+ if (TypeFromToken(cl) == mdtTypeDef)
+ typeHandle = pModule->LookupTypeDef(cl, pLoadLevel);
+ else if (TypeFromToken(cl) == mdtTypeRef)
+ {
+ typeHandle = pModule->LookupTypeRef(cl);
+
+ if (pLoadLevel && !typeHandle.IsNull())
+ {
+ *pLoadLevel = typeHandle.GetLoadLevel();
+ }
+ }
+
+ RETURN(typeHandle);
+}
+
+DomainAssembly *ClassLoader::GetDomainAssembly(AppDomain *pDomain/*=NULL*/)
+{
+ WRAPPER_NO_CONTRACT;
+ return GetAssembly()->GetDomainAssembly(pDomain);
+}
+
+#ifndef DACCESS_COMPILE
+
+//
+// Free all modules associated with this loader
+//
+void ClassLoader::FreeModules()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ DISABLED(FORBID_FAULT); //Lots of crud to clean up to make this work
+ }
+ CONTRACTL_END;
+
+ Module *pManifest = NULL;
+ if (GetAssembly() && (NULL != (pManifest = GetAssembly()->GetManifestModule()))) {
+ // Unload the manifest last, since it contains the module list in its rid map
+ ModuleIterator i = GetAssembly()->IterateModules();
+ while (i.Next()) {
+ // Have the module free its various tables and some of the EEClass links
+ if (i.GetModule() != pManifest)
+ i.GetModule()->Destruct();
+ }
+
+ // Now do the manifest module.
+ pManifest->Destruct();
+ }
+
+}
+
+ClassLoader::~ClassLoader()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ DESTRUCTOR_CHECK;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ DISABLED(FORBID_FAULT); //Lots of crud to clean up to make this work
+ }
+ CONTRACTL_END
+
+#ifdef _DEBUG
+ // Do not walk m_pUnresolvedClassHash at destruct time as it is loaderheap allocated memory
+ // and may already have been deallocated via an AllocMemTracker.
+ m_pUnresolvedClassHash = (PendingTypeLoadTable*)(UINT_PTR)0xcccccccc;
+#endif
+
+#ifdef _DEBUG
+// LOG((
+// LF_CLASSLOADER,
+// INFO3,
+// "Deleting classloader %x\n"
+// " >EEClass data: %10d bytes\n"
+// " >Classname hash: %10d bytes\n"
+// " >FieldDesc data: %10d bytes\n"
+// " >MethodDesc data: %10d bytes\n"
+// " >GCInfo: %10d bytes\n"
+// " >Interface maps: %10d bytes\n"
+// " >MethodTables: %10d bytes\n"
+// " >Vtables: %10d bytes\n"
+// " >Static fields: %10d bytes\n"
+// "# methods: %10d\n"
+// "# field descs: %10d\n"
+// "# classes: %10d\n"
+// "# dup intf slots: %10d\n"
+// "# array classrefs: %10d\n"
+// "Array class overhead:%10d bytes\n",
+// this,
+// m_dwEEClassData,
+// m_pAvailableClasses->m_dwDebugMemory,
+// m_dwFieldDescData,
+// m_dwMethodDescData,
+// m_dwGCSize,
+// m_dwInterfaceMapSize,
+// m_dwMethodTableSize,
+// m_dwVtableData,
+// m_dwStaticFieldData,
+// m_dwDebugMethods,
+// m_dwDebugFieldDescs,
+// m_dwDebugClasses,
+// m_dwDebugDuplicateInterfaceSlots,
+// ));
+#endif
+
+ FreeModules();
+
+ m_UnresolvedClassLock.Destroy();
+ m_AvailableClassLock.Destroy();
+ m_AvailableTypesLock.Destroy();
+}
+
+
+//----------------------------------------------------------------------------
+// The constructor should only initialize enough to ensure that the destructor doesn't
+// crash. It cannot allocate or do anything that might fail as that would leave
+// the ClassLoader undestructable. Any such tasks should be done in ClassLoader::Init().
+//----------------------------------------------------------------------------
+ClassLoader::ClassLoader(Assembly *pAssembly)
+{
+ CONTRACTL
+ {
+ CONSTRUCTOR_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ m_pAssembly = pAssembly;
+
+ m_pUnresolvedClassHash = NULL;
+ m_cUnhashedModules = 0;
+
+#ifdef _DEBUG
+ m_dwDebugMethods = 0;
+ m_dwDebugFieldDescs = 0;
+ m_dwDebugClasses = 0;
+ m_dwDebugDuplicateInterfaceSlots = 0;
+ m_dwGCSize = 0;
+ m_dwInterfaceMapSize = 0;
+ m_dwMethodTableSize = 0;
+ m_dwVtableData = 0;
+ m_dwStaticFieldData = 0;
+ m_dwFieldDescData = 0;
+ m_dwMethodDescData = 0;
+ m_dwEEClassData = 0;
+#endif
+}
+
+
+//----------------------------------------------------------------------------
+// This function completes the initialization of the ClassLoader. It can
+// assume the constructor is run and that the function is entered with
+// ClassLoader in a safely destructable state. This function can throw
+// but whether it throws or succeeds, it must leave the ClassLoader in a safely
+// destructable state.
+//----------------------------------------------------------------------------
+VOID ClassLoader::Init(AllocMemTracker *pamTracker)
+{
+ STANDARD_VM_CONTRACT;
+
+ m_pUnresolvedClassHash = PendingTypeLoadTable::Create(GetAssembly()->GetLowFrequencyHeap(),
+ UNRESOLVED_CLASS_HASH_BUCKETS,
+ pamTracker);
+
+ m_UnresolvedClassLock.Init(CrstUnresolvedClassLock);
+
+ // This lock is taken within the classloader whenever we have to enter a
+ // type in one of the modules governed by the loader.
+ // The process of creating these types may be reentrant. The ordering has
+ // not yet been sorted out, and when we sort it out we should also modify the
+ // ordering for m_AvailableTypesLock in BaseDomain.
+ m_AvailableClassLock.Init(
+ CrstAvailableClass,
+ CRST_REENTRANCY);
+
+ // This lock is taken within the classloader whenever we have to insert a new param. type into the table
+ // This lock also needs to be taken for a read operation in a GC_NOTRIGGER scope, thus the ANYMODE flag.
+ m_AvailableTypesLock.Init(
+ CrstAvailableParamTypes,
+ (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD));
+
+#ifdef _DEBUG
+ CorTypeInfo::CheckConsistency();
+#endif
+
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+/*static*/
+TypeHandle ClassLoader::LoadTypeDefOrRefOrSpecThrowing(Module *pModule,
+ mdToken typeDefOrRefOrSpec,
+ const SigTypeContext *pTypeContext,
+ NotFoundAction fNotFoundAction /* = ThrowIfNotFound */ ,
+ PermitUninstantiatedFlag fUninstantiated /* = FailIfUninstDefOrRef */,
+ LoadTypesFlag fLoadTypes/*=LoadTypes*/ ,
+ ClassLoadLevel level /* = CLASS_LOADED */,
+ BOOL dropGenericArgumentLevel /* = FALSE */,
+ const Substitution *pSubst)
+{
+ CONTRACT(TypeHandle)
+ {
+ if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
+ if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
+ MODE_ANY;
+ if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
+ if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ PRECONDITION(FORBIDGC_LOADER_USE_ENABLED() || GetAppDomain()->CheckCanLoadTypes(pModule->GetAssembly()));
+ POSTCONDITION(CheckPointer(RETVAL, (fNotFoundAction == ThrowIfNotFound)? NULL_NOT_OK : NULL_OK));
+ }
+ CONTRACT_END
+
+ if (TypeFromToken(typeDefOrRefOrSpec) == mdtTypeSpec)
+ {
+ ULONG cSig;
+ PCCOR_SIGNATURE pSig;
+
+ IMDInternalImport *pInternalImport = pModule->GetMDImport();
+ if (FAILED(pInternalImport->GetTypeSpecFromToken(typeDefOrRefOrSpec, &pSig, &cSig)))
+ {
+#ifndef DACCESS_COMPILE
+ if (fNotFoundAction == ThrowIfNotFound)
+ {
+ pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDefOrRefOrSpec, IDS_CLASSLOAD_BADFORMAT);
+ }
+#endif //!DACCESS_COMPILE
+ RETURN (TypeHandle());
+ }
+ SigPointer sigptr(pSig, cSig);
+ TypeHandle typeHnd = sigptr.GetTypeHandleThrowing(pModule, pTypeContext, fLoadTypes,
+ level, dropGenericArgumentLevel, pSubst);
+#ifndef DACCESS_COMPILE
+ if ((fNotFoundAction == ThrowIfNotFound) && typeHnd.IsNull())
+ pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDefOrRefOrSpec,
+ IDS_CLASSLOAD_GENERAL);
+#endif
+ RETURN (typeHnd);
+ }
+ else
+ {
+ RETURN (LoadTypeDefOrRefThrowing(pModule, typeDefOrRefOrSpec,
+ fNotFoundAction,
+ fUninstantiated,
+ ((fLoadTypes == LoadTypes) ? tdNoTypes : tdAllTypes),
+ level));
+ }
+} // ClassLoader::LoadTypeDefOrRefOrSpecThrowing
+
+// Given a token specifying a typeDef, and a module in which to
+// interpret that token, find or load the corresponding type handle.
+//
+//
+/*static*/
+TypeHandle ClassLoader::LoadTypeDefThrowing(Module *pModule,
+ mdToken typeDef,
+ NotFoundAction fNotFoundAction /* = ThrowIfNotFound */ ,
+ PermitUninstantiatedFlag fUninstantiated /* = FailIfUninstDefOrRef */,
+ mdToken tokenNotToLoad,
+ ClassLoadLevel level,
+ Instantiation * pTargetInstantiation)
+{
+
+ CONTRACT(TypeHandle)
+ {
+ if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
+ if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
+ MODE_ANY;
+ if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
+ DAC_LOADS_TYPE(level, !NameHandle::OKToLoad(typeDef, tokenNotToLoad));
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ PRECONDITION(FORBIDGC_LOADER_USE_ENABLED()
+ || GetAppDomain()->CheckCanLoadTypes(pModule->GetAssembly()));
+
+ POSTCONDITION(CheckPointer(RETVAL, NameHandle::OKToLoad(typeDef, tokenNotToLoad) && (fNotFoundAction == ThrowIfNotFound) ? NULL_NOT_OK : NULL_OK));
+ POSTCONDITION(RETVAL.IsNull() || RETVAL.GetCl() == typeDef);
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ TypeHandle typeHnd;
+
+ // First, attempt to find the class if it is already loaded
+ ClassLoadLevel existingLoadLevel = CLASS_LOAD_BEGIN;
+ typeHnd = pModule->LookupTypeDef(typeDef, &existingLoadLevel);
+ if (!typeHnd.IsNull())
+ {
+#ifndef DACCESS_COMPILE
+ // If the type is loaded, we can do cheap arity verification
+ if (pTargetInstantiation != NULL && pTargetInstantiation->GetNumArgs() != typeHnd.AsMethodTable()->GetNumGenericArgs())
+ pModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(), typeDef, IDS_CLASSLOAD_TYPEWRONGNUMGENERICARGS);
+#endif
+
+ if (existingLoadLevel >= level)
+ RETURN(typeHnd);
+ }
+
+ // We don't want to probe on any threads except for those with a managed thread. This function
+ // can be called from the GC thread etc. so need to control how we probe.
+ INTERIOR_STACK_PROBE_NOTHROW_CHECK_THREAD(goto Exit;);
+
+ IMDInternalImport *pInternalImport = pModule->GetMDImport();
+
+#ifndef DACCESS_COMPILE
+ if (typeHnd.IsNull() && pTargetInstantiation != NULL)
+ {
+ // If the type is not loaded yet, we have to do heavy weight arity verification based on metadata
+ HENUMInternal hEnumGenericPars;
+ HRESULT hr = pInternalImport->EnumInit(mdtGenericParam, typeDef, &hEnumGenericPars);
+ if (FAILED(hr))
+ pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDef, IDS_CLASSLOAD_BADFORMAT);
+ DWORD nGenericClassParams = pInternalImport->EnumGetCount(&hEnumGenericPars);
+ pInternalImport->EnumClose(&hEnumGenericPars);
+
+ if (pTargetInstantiation->GetNumArgs() != nGenericClassParams)
+ pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDef, IDS_CLASSLOAD_TYPEWRONGNUMGENERICARGS);
+ }
+#endif
+
+ if (IsNilToken(typeDef) || TypeFromToken(typeDef) != mdtTypeDef || !pInternalImport->IsValidToken(typeDef) )
+ {
+ LOG((LF_CLASSLOADER, LL_INFO10, "Bogus class token to load: 0x%08x\n", typeDef));
+ typeHnd = TypeHandle();
+ }
+ else
+ {
+ // *****************************************************************************
+ //
+ // Important invariant:
+ //
+ // The rule here is that we never go to LoadTypeHandleForTypeKey if a Find should succeed.
+ // This is vital, because otherwise a stack crawl will open up opportunities for
+ // GC. Since operations like setting up a GCFrame will trigger a crawl in stress
+ // mode, a GC at that point would be disastrous. We can't assert this, because
+ // of race conditions. (In other words, the type could suddently be find-able
+ // because another thread loaded it while we were in this method.
+
+ // Not found - try to load it unless we are told not to
+
+#ifndef DACCESS_COMPILE
+ if ( !NameHandle::OKToLoad(typeDef, tokenNotToLoad) )
+ {
+ typeHnd = TypeHandle();
+ }
+ else
+ {
+ // Anybody who puts himself in a FORBIDGC_LOADER state has promised
+ // to use us only for resolving, not loading. We are now transitioning into
+ // loading.
+#ifdef _DEBUG_IMPL
+ _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
+#endif
+ TRIGGERSGC();
+
+ if (pModule->IsReflection())
+ {
+ //if (!(pModule->IsIntrospectionOnly()))
+ {
+ // Don't try to load types that are not in available table, when this
+ // is an in-memory module. Raise the type-resolve event instead.
+ typeHnd = TypeHandle();
+
+ // Avoid infinite recursion
+ if (tokenNotToLoad != tdAllAssemblies)
+ {
+ AppDomain* pDomain = SystemDomain::GetCurrentDomain();
+
+ LPUTF8 pszFullName;
+ LPCUTF8 className;
+ LPCUTF8 nameSpace;
+ if (FAILED(pInternalImport->GetNameOfTypeDef(typeDef, &className, &nameSpace)))
+ {
+ LOG((LF_CLASSLOADER, LL_INFO10, "Bogus TypeDef record while loading: 0x%08x\n", typeDef));
+ typeHnd = TypeHandle();
+ }
+ else
+ {
+ MAKE_FULL_PATH_ON_STACK_UTF8(pszFullName,
+ nameSpace,
+ className);
+ GCX_COOP();
+ ASSEMBLYREF asmRef = NULL;
+ DomainAssembly *pDomainAssembly = NULL;
+ GCPROTECT_BEGIN(asmRef);
+
+ pDomainAssembly = pDomain->RaiseTypeResolveEventThrowing(
+ pModule->GetAssembly()->GetDomainAssembly(),
+ pszFullName, &asmRef);
+
+ if (asmRef != NULL)
+ {
+ _ASSERTE(pDomainAssembly != NULL);
+ if (pDomainAssembly->GetAssembly()->GetLoaderAllocator()->IsCollectible())
+ {
+ if (!pModule->GetLoaderAllocator()->IsCollectible())
+ {
+ LOG((LF_CLASSLOADER, LL_INFO10, "Bad result from TypeResolveEvent while loader TypeDef record: 0x%08x\n", typeDef));
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible"));
+ }
+
+ pModule->GetLoaderAllocator()->EnsureReference(pDomainAssembly->GetAssembly()->GetLoaderAllocator());
+ }
+ }
+ GCPROTECT_END();
+ if (pDomainAssembly != NULL)
+ {
+ Assembly *pAssembly = pDomainAssembly->GetAssembly();
+
+ NameHandle name(nameSpace, className);
+ name.SetTypeToken(pModule, typeDef);
+ name.SetTokenNotToLoad(tdAllAssemblies);
+ typeHnd = pAssembly->GetLoader()->LoadTypeHandleThrowing(&name, level);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ TypeKey typeKey(pModule, typeDef);
+ typeHnd = pModule->GetClassLoader()->LoadTypeHandleForTypeKey(&typeKey,
+ typeHnd,
+ level);
+ }
+ }
+#endif // !DACCESS_COMPILE
+ }
+
+// If stack guards are disabled, then this label is unreferenced and produces a compile error.
+#if defined(FEATURE_STACK_PROBE) && !defined(DACCESS_COMPILE)
+Exit:
+#endif
+
+#ifndef DACCESS_COMPILE
+ if ((fUninstantiated == FailIfUninstDefOrRef) && !typeHnd.IsNull() && typeHnd.IsGenericTypeDefinition())
+ {
+ typeHnd = TypeHandle();
+ }
+
+ if ((fNotFoundAction == ThrowIfNotFound) && typeHnd.IsNull() && (tokenNotToLoad != tdAllTypes))
+ {
+ pModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(),
+ typeDef,
+ IDS_CLASSLOAD_GENERAL);
+ }
+#endif
+ ;
+ END_INTERIOR_STACK_PROBE;
+
+ RETURN(typeHnd);
+}
+
+// Given a token specifying a typeDef or typeRef, and a module in
+// which to interpret that token, find or load the corresponding type
+// handle.
+//
+/*static*/
+TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule,
+ mdToken typeDefOrRef,
+ NotFoundAction fNotFoundAction /* = ThrowIfNotFound */ ,
+ PermitUninstantiatedFlag fUninstantiated /* = FailIfUninstDefOrRef */,
+ mdToken tokenNotToLoad,
+ ClassLoadLevel level)
+{
+
+ CONTRACT(TypeHandle)
+ {
+ if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
+ if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
+ MODE_ANY;
+ if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
+ PRECONDITION(FORBIDGC_LOADER_USE_ENABLED()
+ || GetAppDomain()->CheckCanLoadTypes(pModule->GetAssembly()));
+
+ POSTCONDITION(CheckPointer(RETVAL, NameHandle::OKToLoad(typeDefOrRef, tokenNotToLoad) && (fNotFoundAction == ThrowIfNotFound) ? NULL_NOT_OK : NULL_OK));
+ POSTCONDITION(level <= CLASS_LOAD_UNRESTORED || RETVAL.IsNull() || RETVAL.IsRestored());
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ // NotFoundAction could be the bizarre 'ThrowButNullV11McppWorkaround',
+ // which means ThrowIfNotFound EXCEPT if this might be the Everett MCPP
+ // Nil-token ResolutionScope for value type. In that case, it means
+ // ReturnNullIfNotFound.
+ // If we have ThrowButNullV11McppWorkaround, remember that NULL *might*
+ // be OK if there is no resolution scope, but change the value to
+ // ThrowIfNotFound.
+ BOOLEAN bReturnNullOkWhenNoResolutionScope = false;
+ if (fNotFoundAction == ThrowButNullV11McppWorkaround)
+ {
+ bReturnNullOkWhenNoResolutionScope = true;
+ fNotFoundAction = ThrowIfNotFound;
+ }
+
+ // First, attempt to find the class if it is already loaded
+ ClassLoadLevel existingLoadLevel = CLASS_LOAD_BEGIN;
+ TypeHandle typeHnd = LookupTypeDefOrRefInModule(pModule, typeDefOrRef, &existingLoadLevel);
+ if (!typeHnd.IsNull())
+ {
+ if (existingLoadLevel < level)
+ {
+ pModule = typeHnd.GetModule();
+ typeDefOrRef = typeHnd.GetCl();
+ }
+ }
+
+ if (!typeHnd.IsNull() && existingLoadLevel >= level)
+ {
+ // perform the check that it's not an uninstantiated TypeDef/TypeRef
+ // being used inappropriately.
+ if (!((fUninstantiated == FailIfUninstDefOrRef) && !typeHnd.IsNull() && typeHnd.IsGenericTypeDefinition()))
+ {
+ RETURN(typeHnd);
+ }
+ }
+ else
+ {
+ // otherwise try to resolve the TypeRef and/or load the corresponding TypeDef
+ IMDInternalImport *pInternalImport = pModule->GetMDImport();
+ mdToken tokType = TypeFromToken(typeDefOrRef);
+
+ if (IsNilToken(typeDefOrRef) || ((tokType != mdtTypeDef)&&(tokType != mdtTypeRef))
+ || !pInternalImport->IsValidToken(typeDefOrRef) )
+ {
+#ifdef _DEBUG
+ LOG((LF_CLASSLOADER, LL_INFO10, "Bogus class token to load: 0x%08x\n", typeDefOrRef));
+#endif
+
+ typeHnd = TypeHandle();
+ }
+
+ else if (tokType == mdtTypeRef)
+ {
+ BOOL fNoResolutionScope;
+ Module *pFoundModule = Assembly::FindModuleByTypeRef(pModule, typeDefOrRef,
+ tokenNotToLoad==tdAllTypes ?
+ Loader::DontLoad :
+ Loader::Load,
+ &fNoResolutionScope);
+
+ if (pFoundModule != NULL)
+ {
+
+ // Not in my module, have to look it up by name. This is the primary path
+ // taken by the TypeRef case, i.e. we've resolve a TypeRef to a TypeDef/Module
+ // pair.
+ LPCUTF8 pszNameSpace;
+ LPCUTF8 pszClassName;
+ if (FAILED(pInternalImport->GetNameOfTypeRef(
+ typeDefOrRef,
+ &pszNameSpace,
+ &pszClassName)))
+ {
+ typeHnd = TypeHandle();
+ }
+ else
+ {
+ if (fNoResolutionScope)
+ {
+ // Everett C++ compiler can generate a TypeRef with RS=0
+ // without respective TypeDef for unmanaged valuetypes,
+ // referenced only by pointers to them,
+ // so we can fail to load legally w/ no exception
+ typeHnd = ClassLoader::LoadTypeByNameThrowing(pFoundModule->GetAssembly(),
+ pszNameSpace,
+ pszClassName,
+ ClassLoader::ReturnNullIfNotFound,
+ tokenNotToLoad==tdAllTypes ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes,
+ level);
+
+ if(typeHnd.IsNull() && bReturnNullOkWhenNoResolutionScope)
+ {
+ fNotFoundAction = ReturnNullIfNotFound;
+ RETURN(typeHnd);
+ }
+ }
+ else
+ {
+ NameHandle nameHandle(pModule, typeDefOrRef);
+ nameHandle.SetName(pszNameSpace, pszClassName);
+ nameHandle.SetTokenNotToLoad(tokenNotToLoad);
+ typeHnd = pFoundModule->GetClassLoader()->
+ LoadTypeHandleThrowIfFailed(&nameHandle, level,
+ pFoundModule->IsReflection() ? NULL : pFoundModule);
+ }
+ }
+
+#ifndef DACCESS_COMPILE
+ if (!(typeHnd.IsNull()))
+ pModule->StoreTypeRef(typeDefOrRef, typeHnd);
+#endif
+ }
+ }
+ else
+ {
+ // This is the mdtTypeDef case...
+ typeHnd = LoadTypeDefThrowing(pModule, typeDefOrRef,
+ fNotFoundAction,
+ fUninstantiated,
+ tokenNotToLoad,
+ level);
+ }
+ }
+ TypeHandle thRes = typeHnd;
+
+ // reject the load if it's an uninstantiated TypeDef/TypeRef
+ // being used inappropriately.
+ if ((fUninstantiated == FailIfUninstDefOrRef) && !typeHnd.IsNull() && typeHnd.IsGenericTypeDefinition())
+ thRes = TypeHandle();
+
+ // perform the check to throw when the thing is not found
+ if ((fNotFoundAction == ThrowIfNotFound) && thRes.IsNull() && (tokenNotToLoad != tdAllTypes))
+ {
+#ifndef DACCESS_COMPILE
+ pModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(),
+ typeDefOrRef,
+ IDS_CLASSLOAD_GENERAL);
+#else
+ DacNotImpl();
+#endif
+ }
+
+ RETURN(thRes);
+}
+
+/*static*/
+BOOL
+ClassLoader::ResolveTokenToTypeDefThrowing(
+ Module * pTypeRefModule,
+ mdTypeRef typeRefToken,
+ Module ** ppTypeDefModule,
+ mdTypeDef * pTypeDefToken,
+ Loader::LoadFlag loadFlag,
+ BOOL * pfUsesTypeForwarder) // The semantic of this parameter: TRUE if a type forwarder is found. It is never set to FALSE.
+{
+ CONTRACT(BOOL)
+ {
+ if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
+ if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
+ MODE_ANY;
+ if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
+ PRECONDITION(CheckPointer(pTypeRefModule));
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ // It's a TypeDef already
+ if (TypeFromToken(typeRefToken) == mdtTypeDef)
+ {
+ if (ppTypeDefModule != NULL)
+ *ppTypeDefModule = pTypeRefModule;
+ if (pTypeDefToken != NULL)
+ *pTypeDefToken = typeRefToken;
+ RETURN TRUE;
+ }
+
+ TypeHandle typeHnd = pTypeRefModule->LookupTypeRef(typeRefToken);
+
+ // Type is already (partially) loaded and cached in the module's TypeRef table
+ // Do not return here if we are checking for type forwarders
+ if (!typeHnd.IsNull() && (pfUsesTypeForwarder == NULL))
+ {
+ if (ppTypeDefModule != NULL)
+ *ppTypeDefModule = typeHnd.GetModule();
+ if (pTypeDefToken != NULL)
+ *pTypeDefToken = typeHnd.GetCl();
+ RETURN TRUE;
+ }
+
+ BOOL fNoResolutionScope; //not used
+ Module * pFoundRefModule = Assembly::FindModuleByTypeRef(
+ pTypeRefModule,
+ typeRefToken,
+ loadFlag,
+ &fNoResolutionScope);
+
+ if (pFoundRefModule == NULL)
+ { // We didn't find the TypeRef anywhere
+ RETURN FALSE;
+ }
+
+ // If checking for type forwarders, then we can see if a type forwarder was used based on the output of
+ // pFoundRefModule and typeHnd (if typeHnd is set)
+ if (!typeHnd.IsNull() && (pfUsesTypeForwarder != NULL))
+ {
+ if (typeHnd.GetModule() != pFoundRefModule)
+ {
+ *pfUsesTypeForwarder = TRUE;
+ }
+
+ if (ppTypeDefModule != NULL)
+ *ppTypeDefModule = typeHnd.GetModule();
+ if (pTypeDefToken != NULL)
+ *pTypeDefToken = typeHnd.GetCl();
+ RETURN TRUE;
+ }
+
+ // Not in my module, have to look it up by name
+ LPCUTF8 pszNameSpace;
+ LPCUTF8 pszClassName;
+ if (FAILED(pTypeRefModule->GetMDImport()->GetNameOfTypeRef(typeRefToken, &pszNameSpace, &pszClassName)))
+ {
+ RETURN FALSE;
+ }
+ NameHandle nameHandle(pTypeRefModule, typeRefToken);
+ nameHandle.SetName(pszNameSpace, pszClassName);
+ if (loadFlag != Loader::Load)
+ {
+ nameHandle.SetTokenNotToLoad(tdAllTypes);
+ }
+
+ mdToken foundTypeDef;
+ Module * pFoundModule;
+ mdExportedType foundExportedType;
+ Module * pSourceModule = pFoundRefModule;
+
+ for (UINT32 nTypeForwardingChainSize = 0; nTypeForwardingChainSize < const_cMaxTypeForwardingChainSize; nTypeForwardingChainSize++)
+ {
+ foundTypeDef = mdTokenNil;
+ pFoundModule = NULL;
+ foundExportedType = mdTokenNil;
+ if (!pSourceModule->GetClassLoader()->FindClassModuleThrowing(
+ &nameHandle,
+ &typeHnd,
+ &foundTypeDef,
+ &pFoundModule,
+ &foundExportedType,
+ NULL,
+ pSourceModule->IsReflection() ? NULL : pSourceModule,
+ loadFlag))
+ {
+ RETURN FALSE;
+ }
+
+ // Type is already loaded and cached in the loader's by-name table
+ if (!typeHnd.IsNull())
+ {
+ if ((typeHnd.GetModule() != pFoundRefModule) && (pfUsesTypeForwarder != NULL))
+ { // We followed at least one type forwarder to resolve the type
+ *pfUsesTypeForwarder = TRUE;
+ }
+ if (ppTypeDefModule != NULL)
+ *ppTypeDefModule = typeHnd.GetModule();
+ if (pTypeDefToken != NULL)
+ *pTypeDefToken = typeHnd.GetCl();
+ RETURN TRUE;
+ }
+
+ if (pFoundModule == NULL)
+ { // Module was probably not loaded
+ RETURN FALSE;
+ }
+
+ if (TypeFromToken(foundExportedType) != mdtExportedType)
+ { // It's not exported type
+ _ASSERTE(foundExportedType == mdTokenNil);
+
+ if ((pFoundModule != pFoundRefModule) && (pfUsesTypeForwarder != NULL))
+ { // We followed at least one type forwarder to resolve the type
+ *pfUsesTypeForwarder = TRUE;
+ }
+ if (pTypeDefToken != NULL)
+ *pTypeDefToken = foundTypeDef;
+ if (ppTypeDefModule != NULL)
+ *ppTypeDefModule = pFoundModule;
+ RETURN TRUE;
+ }
+ // It's exported type
+
+ // Repeat the search for the type in the newly found module
+ pSourceModule = pFoundModule;
+ }
+ // Type forwarding chain is too long
+ RETURN FALSE;
+} // ClassLoader::ResolveTokenToTypeDefThrowing
+
+#ifndef DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------
+//
+//static
+VOID
+ClassLoader::GetEnclosingClassThrowing(
+ IMDInternalImport * pInternalImport,
+ Module * pModule,
+ mdTypeDef cl,
+ mdTypeDef * tdEnclosing)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(tdEnclosing);
+ *tdEnclosing = mdTypeDefNil;
+
+ HRESULT hr = pInternalImport->GetNestedClassProps(cl, tdEnclosing);
+
+ if (FAILED(hr))
+ {
+ if (hr != CLDB_E_RECORD_NOTFOUND)
+ COMPlusThrowHR(hr);
+ return;
+ }
+
+ if (TypeFromToken(*tdEnclosing) != mdtTypeDef)
+ pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_ENCLOSING);
+} // ClassLoader::GetEnclosingClassThrowing
+
+
+//---------------------------------------------------------------------------------------
+//
+// Load a parent type or implemented interface type.
+//
+// If this is an instantiated type represented by a type spec, then instead of attempting to load the
+// exact type, load an approximate instantiation in which all reference types are replaced by Object.
+// The exact instantiated types will be loaded later by LoadInstantiatedInfo.
+// We do this to avoid cycles early in class loading caused by definitions such as
+// struct M : ICloneable<M> // load ICloneable<object>
+// class C<T> : D<C<T>,int> for any T // load D<object,int>
+//
+//static
+TypeHandle
+ClassLoader::LoadApproxTypeThrowing(
+ Module * pModule,
+ mdToken tok,
+ SigPointer * pSigInst,
+ const SigTypeContext * pClassTypeContext)
+{
+ CONTRACT(TypeHandle)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pSigInst, NULL_OK));
+ PRECONDITION(CheckPointer(pModule));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ IMDInternalImport * pInternalImport = pModule->GetMDImport();
+
+ if (TypeFromToken(tok) == mdtTypeSpec)
+ {
+ ULONG cSig;
+ PCCOR_SIGNATURE pSig;
+ IfFailThrowBF(pInternalImport->GetTypeSpecFromToken(tok, &pSig, &cSig), BFA_METADATA_CORRUPT, pModule);
+
+ SigPointer sigptr = SigPointer(pSig, cSig);
+ CorElementType type = ELEMENT_TYPE_END;
+ IfFailThrowBF(sigptr.GetElemType(&type), BFA_BAD_SIGNATURE, pModule);
+
+ // The only kind of type specs that we recognise are instantiated types
+ if (type != ELEMENT_TYPE_GENERICINST)
+ pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, tok, IDS_CLASSLOAD_GENERAL);
+
+ // Of these, we outlaw instantiated value classes (they can't be interfaces and can't be subclassed)
+ IfFailThrowBF(sigptr.GetElemType(&type), BFA_BAD_SIGNATURE, pModule);
+
+ if (type != ELEMENT_TYPE_CLASS)
+ pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, tok, IDS_CLASSLOAD_GENERAL);
+
+ mdToken genericTok = 0;
+ IfFailThrowBF(sigptr.GetToken(&genericTok), BFA_BAD_SIGNATURE, pModule);
+ IfFailThrowBF(sigptr.GetData(NULL), BFA_BAD_SIGNATURE, pModule);
+
+ if (pSigInst != NULL)
+ *pSigInst = sigptr;
+
+ // Try to load the generic type itself
+ THROW_BAD_FORMAT_MAYBE(
+ ((TypeFromToken(genericTok) == mdtTypeRef) || (TypeFromToken(genericTok) == mdtTypeDef)),
+ BFA_UNEXPECTED_GENERIC_TOKENTYPE,
+ pModule);
+ TypeHandle genericTypeTH = LoadTypeDefOrRefThrowing(
+ pModule,
+ genericTok,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::PermitUninstDefOrRef,
+ tdNoTypes,
+ CLASS_LOAD_APPROXPARENTS);
+
+ // We load interfaces at very approximate types - the generic
+ // interface itself. We fix this up in LoadInstantiatedInfo.
+ // This allows us to load recursive interfaces on structs such
+ // as "struct VC : I<VC>". The details of the interface
+ // are not currently needed during the first phase
+ // of setting up the method table.
+ if (genericTypeTH.IsInterface())
+ {
+ RETURN genericTypeTH;
+ }
+ else
+ {
+ // approxTypes, i.e. approximate reference types by Object, i.e. load the canonical type
+ RETURN SigPointer(pSig, cSig).GetTypeHandleThrowing(
+ pModule,
+ pClassTypeContext,
+ ClassLoader::LoadTypes,
+ CLASS_LOAD_APPROXPARENTS,
+ TRUE /*dropGenericArgumentLevel*/);
+ }
+ }
+ else
+ {
+ if (pSigInst != NULL)
+ *pSigInst = SigPointer();
+ RETURN LoadTypeDefOrRefThrowing(
+ pModule,
+ tok,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::FailIfUninstDefOrRef,
+ tdNoTypes,
+ CLASS_LOAD_APPROXPARENTS);
+ }
+} // ClassLoader::LoadApproxTypeThrowing
+
+
+//---------------------------------------------------------------------------------------
+//
+//static
+MethodTable *
+ClassLoader::LoadApproxParentThrowing(
+ Module * pModule,
+ mdToken cl,
+ SigPointer * pParentInst,
+ const SigTypeContext * pClassTypeContext)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ mdTypeRef crExtends;
+ MethodTable * pParentMethodTable = NULL;
+ TypeHandle parentType;
+ DWORD dwAttrClass;
+ Assembly * pAssembly = pModule->GetAssembly();
+ IMDInternalImport * pInternalImport = pModule->GetMDImport();
+
+ // Initialize the return value;
+ *pParentInst = SigPointer();
+
+ // Now load all dependencies of this class
+ if (FAILED(pInternalImport->GetTypeDefProps(
+ cl,
+ &dwAttrClass, // AttrClass
+ &crExtends)))
+ {
+ pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_BADFORMAT);
+ }
+
+ if (RidFromToken(crExtends) != mdTokenNil)
+ {
+ // Do an "approximate" load of the parent, replacing reference types in the instantiation by Object
+ // This is to avoid cycles in the loader e.g. on class C : D<C> or class C<T> : D<C<T>>
+ // We fix up the exact parent later in LoadInstantiatedInfo
+ parentType = LoadApproxTypeThrowing(pModule, crExtends, pParentInst, pClassTypeContext);
+
+ pParentMethodTable = parentType.GetMethodTable();
+
+ if (pParentMethodTable == NULL)
+ pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_PARENTNULL);
+
+ // cannot inherit from an interface
+ if (pParentMethodTable->IsInterface())
+ pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_PARENTINTERFACE);
+
+ if (IsTdInterface(dwAttrClass))
+ {
+ // Interfaces must extend from Object
+ if (! pParentMethodTable->IsObjectClass())
+ pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_INTERFACEOBJECT);
+ }
+ }
+
+ return pParentMethodTable;
+} // ClassLoader::LoadApproxParentThrowing
+
+// Perform a single phase of class loading
+// It is the caller's responsibility to lock
+/*static*/
+TypeHandle ClassLoader::DoIncrementalLoad(TypeKey *pTypeKey, TypeHandle typeHnd, ClassLoadLevel currentLevel)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pTypeKey));
+ PRECONDITION(currentLevel >= CLASS_LOAD_BEGIN && currentLevel < CLASS_LOADED);
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ if (LoggingOn(LF_CLASSLOADER, LL_INFO10000))
+ {
+ SString name;
+ TypeString::AppendTypeKeyDebug(name, pTypeKey);
+ LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: About to do incremental load of type %S (%p) from level %s\n", name.GetUnicode(), typeHnd.AsPtr(), classLoadLevelName[currentLevel]));
+ }
+#endif
+
+ // Level is BEGIN if and only if type handle is null
+ CONSISTENCY_CHECK((currentLevel == CLASS_LOAD_BEGIN) == typeHnd.IsNull());
+
+ switch (currentLevel)
+ {
+ // Attain at least level CLASS_LOAD_UNRESTORED (if just locating type in ngen image)
+ // or at least level CLASS_LOAD_APPROXPARENTS (if creating type for the first time)
+ case CLASS_LOAD_BEGIN :
+ {
+ IBCLoggerAwareAllocMemTracker amTracker;
+ typeHnd = CreateTypeHandleForTypeKey(pTypeKey, &amTracker);
+ CONSISTENCY_CHECK(!typeHnd.IsNull());
+ TypeHandle published = PublishType(pTypeKey, typeHnd);
+ if (published == typeHnd)
+ amTracker.SuppressRelease();
+ typeHnd = published;
+ }
+ break;
+
+ case CLASS_LOAD_UNRESTOREDTYPEKEY :
+#ifdef FEATURE_PREJIT
+ typeHnd.DoRestoreTypeKey();
+#endif
+ break;
+
+ // Attain level CLASS_LOAD_APPROXPARENTS, starting with unrestored class
+ case CLASS_LOAD_UNRESTORED :
+#ifdef FEATURE_PREJIT
+ {
+ CONSISTENCY_CHECK(!typeHnd.IsRestored_NoLogging());
+ if (typeHnd.IsTypeDesc())
+ typeHnd.AsTypeDesc()->Restore();
+ else
+ typeHnd.AsMethodTable()->Restore();
+ }
+#endif
+ break;
+
+ // Attain level CLASS_LOAD_EXACTPARENTS
+ case CLASS_LOAD_APPROXPARENTS :
+ if (!typeHnd.IsTypeDesc())
+ {
+ LoadExactParents(typeHnd.AsMethodTable());
+ }
+ break;
+
+ case CLASS_LOAD_EXACTPARENTS :
+ case CLASS_DEPENDENCIES_LOADED :
+ case CLASS_LOADED :
+ break;
+
+ }
+
+ if (typeHnd.GetLoadLevel() >= CLASS_LOAD_EXACTPARENTS)
+ {
+ Notify(typeHnd);
+ }
+
+ return typeHnd;
+}
+
+/*static*/
+// For non-canonical instantiations of generic types, create a fresh type by replicating the canonical instantiation
+// For canonical instantiations of generic types, create a brand new method table
+// For other constructed types, create a type desc and template method table if necessary
+// For all other types, create a method table
+TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracker* pamTracker)
+{
+ CONTRACT(TypeHandle)
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pKey));
+
+ POSTCONDITION(RETVAL.CheckMatchesKey(pKey));
+ MODE_ANY;
+ }
+ CONTRACT_END
+
+ TypeHandle typeHnd = TypeHandle();
+
+ if (!pKey->IsConstructed())
+ {
+ typeHnd = CreateTypeHandleForTypeDefThrowing(pKey->GetModule(),
+ pKey->GetTypeToken(),
+ pKey->GetInstantiation(),
+ pamTracker);
+ }
+ else if (pKey->HasInstantiation())
+ {
+#ifdef FEATURE_FULL_NGEN
+ // Try to find the type in an NGEN'd image.
+ typeHnd = TryFindDynLinkZapType(pKey);
+
+ if (!typeHnd.IsNull())
+ {
+#ifdef _DEBUG
+ if (LoggingOn(LF_CLASSLOADER, LL_INFO10000))
+ {
+ SString name;
+ TypeString::AppendTypeKeyDebug(name, pKey);
+ LOG((LF_CLASSLOADER, LL_INFO10000, "GENERICS:CreateTypeHandleForTypeKey: found dyn-link ngen type %S with pointer %p in module %S\n", name.GetUnicode(), typeHnd.AsPtr(),
+ typeHnd.GetLoaderModule()->GetDebugName()));
+ }
+#endif
+ if (typeHnd.GetLoadLevel() == CLASS_LOAD_UNRESTOREDTYPEKEY)
+ {
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+
+ typeHnd.DoRestoreTypeKey();
+ }
+ }
+ else
+#endif // FEATURE_FULL_NGEN
+ {
+ if (IsCanonicalGenericInstantiation(pKey->GetInstantiation()))
+ {
+ typeHnd = CreateTypeHandleForTypeDefThrowing(pKey->GetModule(),
+ pKey->GetTypeToken(),
+ pKey->GetInstantiation(),
+ pamTracker);
+ }
+ else
+ {
+ typeHnd = CreateTypeHandleForNonCanonicalGenericInstantiation(pKey,
+ pamTracker);
+ }
+#if defined(_DEBUG) && !defined(CROSSGEN_COMPILE)
+ if (Nullable::IsNullableType(typeHnd))
+ Nullable::CheckFieldOffsets(typeHnd);
+#endif
+ }
+ }
+ else if (pKey->GetKind() == ELEMENT_TYPE_FNPTR)
+ {
+ Module *pLoaderModule = ComputeLoaderModule(pKey);
+ pLoaderModule->GetLoaderAllocator()->EnsureInstantiation(NULL, Instantiation(pKey->GetRetAndArgTypes(), pKey->GetNumArgs() + 1));
+
+ PREFIX_ASSUME(pLoaderModule!=NULL);
+ DWORD numArgs = pKey->GetNumArgs();
+ BYTE* mem = (BYTE*) pamTracker->Track(pLoaderModule->GetAssembly()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(FnPtrTypeDesc)) + S_SIZE_T(sizeof(TypeHandle)) * S_SIZE_T(numArgs)));
+
+ typeHnd = TypeHandle(new(mem) FnPtrTypeDesc(pKey->GetCallConv(), numArgs, pKey->GetRetAndArgTypes()));
+ }
+ else
+ {
+ Module *pLoaderModule = ComputeLoaderModule(pKey);
+ PREFIX_ASSUME(pLoaderModule!=NULL);
+
+ CorElementType kind = pKey->GetKind();
+ TypeHandle paramType = pKey->GetElementType();
+ MethodTable *templateMT;
+
+ // Create a new type descriptor and insert into constructed type table
+ if (CorTypeInfo::IsArray(kind))
+ {
+ DWORD rank = pKey->GetRank();
+ THROW_BAD_FORMAT_MAYBE((kind != ELEMENT_TYPE_ARRAY) || rank > 0, BFA_MDARRAY_BADRANK, pLoaderModule);
+ THROW_BAD_FORMAT_MAYBE((kind != ELEMENT_TYPE_SZARRAY) || rank == 1, BFA_SDARRAY_BADRANK, pLoaderModule);
+
+ // Arrays of BYREFS not allowed
+ if (paramType.GetInternalCorElementType() == ELEMENT_TYPE_BYREF ||
+ paramType.GetInternalCorElementType() == ELEMENT_TYPE_TYPEDBYREF)
+ {
+ ThrowTypeLoadException(pKey, IDS_CLASSLOAD_CANTCREATEARRAYCLASS);
+ }
+
+ // We really don't need this check anymore.
+ if (rank > MAX_RANK)
+ {
+ ThrowTypeLoadException(pKey, IDS_CLASSLOAD_RANK_TOOLARGE);
+ }
+
+ templateMT = pLoaderModule->CreateArrayMethodTable(paramType, kind, rank, pamTracker);
+
+ BYTE* mem = (BYTE*) pamTracker->Track(pLoaderModule->GetAssembly()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(ArrayTypeDesc))));
+ typeHnd = TypeHandle(new(mem) ArrayTypeDesc(templateMT, paramType));
+ }
+ else
+ {
+ // no parameterized type allowed on a reference
+ if (paramType.GetInternalCorElementType() == ELEMENT_TYPE_BYREF ||
+ paramType.GetInternalCorElementType() == ELEMENT_TYPE_TYPEDBYREF)
+ {
+ ThrowTypeLoadException(pKey, IDS_CLASSLOAD_GENERAL);
+ }
+
+ // let <Type>* type have a method table
+ // System.UIntPtr's method table is used for types like int*, void *, string * etc.
+ if (kind == ELEMENT_TYPE_PTR)
+ templateMT = MscorlibBinder::GetElementType(ELEMENT_TYPE_U);
+ else
+ templateMT = NULL;
+
+ BYTE* mem = (BYTE*) pamTracker->Track(pLoaderModule->GetAssembly()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(ParamTypeDesc))));
+ typeHnd = TypeHandle(new(mem) ParamTypeDesc(kind, templateMT, paramType));
+ }
+ }
+
+ RETURN typeHnd;
+}
+
+// Publish a type (and possibly member information) in the loader's
+// tables Types are published before they are fully loaded. In
+// particular, exact parent info (base class and interfaces) is loaded
+// in a later phase
+/*static*/
+TypeHandle ClassLoader::PublishType(TypeKey *pTypeKey, TypeHandle typeHnd)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(typeHnd));
+ PRECONDITION(CheckPointer(pTypeKey));
+
+ // Key must match that of the handle
+ PRECONDITION(typeHnd.CheckMatchesKey(pTypeKey));
+
+ // Don't publish array template method tables; these are accessed only through type descs
+ PRECONDITION(typeHnd.IsTypeDesc() || !typeHnd.AsMethodTable()->IsArray());
+ }
+ CONTRACTL_END;
+
+
+ if (pTypeKey->IsConstructed())
+ {
+ Module *pLoaderModule = ComputeLoaderModule(pTypeKey);
+ EETypeHashTable *pTable = pLoaderModule->GetAvailableParamTypes();
+
+ CrstHolder ch(&pLoaderModule->GetClassLoader()->m_AvailableTypesLock);
+
+ // The type could have been loaded by a different thread as side-effect of avoiding deadlocks caused by LoadsTypeViolation
+ TypeHandle existing = pTable->GetValue(pTypeKey);
+ if (!existing.IsNull())
+ return existing;
+
+ pTable->InsertValue(typeHnd);
+
+#ifdef _DEBUG
+ // Checks to help ensure that the mscorlib in the ngen process does not get contaminated with pointers to the compilation domains.
+ if (pLoaderModule->IsSystem() && IsCompilationProcess() && pLoaderModule->HasNativeImage())
+ {
+ CorElementType kind = pTypeKey->GetKind();
+ MethodTable *typeHandleMethodTable = typeHnd.GetMethodTable();
+ if ((typeHandleMethodTable != NULL) && (typeHandleMethodTable->GetLoaderAllocator() != pLoaderModule->GetLoaderAllocator()))
+ {
+ _ASSERTE(!"MethodTable of type loaded into mscorlib during NGen is not from mscorlib!");
+ }
+ if ((kind != ELEMENT_TYPE_FNPTR) && (kind != ELEMENT_TYPE_VAR) && (kind != ELEMENT_TYPE_MVAR))
+ {
+ if ((kind == ELEMENT_TYPE_SZARRAY) || (kind == ELEMENT_TYPE_ARRAY) || (kind == ELEMENT_TYPE_BYREF) || (kind == ELEMENT_TYPE_PTR) || (kind == ELEMENT_TYPE_VALUETYPE))
+ {
+ // Check to ensure param value is also part of mscorlib.
+ if (pTypeKey->GetElementType().GetLoaderAllocator() != pLoaderModule->GetLoaderAllocator())
+ {
+ _ASSERTE(!"Param value of type key used to load type during NGEN not located within mscorlib yet type is placed into mscorlib");
+ }
+ }
+ else if (kind == ELEMENT_TYPE_FNPTR)
+ {
+ // Check to ensure the parameter types of fnptr are in mscorlib
+ for (DWORD i = 0; i <= pTypeKey->GetNumArgs(); i++)
+ {
+ if (pTypeKey->GetRetAndArgTypes()[i].GetLoaderAllocator() != pLoaderModule->GetLoaderAllocator())
+ {
+ _ASSERTE(!"Ret or Arg type of function pointer type key used to load type during NGEN not located within mscorlib yet type is placed into mscorlib");
+ }
+ }
+ }
+ else if (kind == ELEMENT_TYPE_CLASS)
+ {
+ // Check to ensure that the generic parameters are all within mscorlib
+ for (DWORD i = 0; i < pTypeKey->GetNumGenericArgs(); i++)
+ {
+ if (pTypeKey->GetInstantiation()[i].GetLoaderAllocator() != pLoaderModule->GetLoaderAllocator())
+ {
+ _ASSERTE(!"Instantiation parameter of generic class type key used to load type during NGEN not located within mscorlib yet type is placed into mscorlib");
+ }
+ }
+ }
+ else
+ {
+ // Should not be able to get here
+ _ASSERTE(!"Unknown type key type");
+ }
+ }
+ }
+#endif // DEBUG
+ }
+ else
+ {
+ Module *pModule = pTypeKey->GetModule();
+ mdTypeDef typeDef = pTypeKey->GetTypeToken();
+
+ CrstHolder ch(&pModule->GetClassLoader()->m_AvailableTypesLock);
+
+ // ! We cannot fail after this point.
+ CANNOTTHROWCOMPLUSEXCEPTION();
+ FAULT_FORBID();
+
+ // The type could have been loaded by a different thread as side-effect of avoiding deadlocks caused by LoadsTypeViolation
+ TypeHandle existing = pModule->LookupTypeDef(typeDef);
+ if (!existing.IsNull())
+ return existing;
+
+ MethodTable *pMT = typeHnd.AsMethodTable();
+
+ MethodTable::IntroducedMethodIterator it(pMT);
+ for (; it.IsValid(); it.Next())
+ {
+ MethodDesc * pMD = it.GetMethodDesc();
+ CONSISTENCY_CHECK(pMD != NULL && pMD->GetMethodTable() == pMT);
+ if (!pMD->IsUnboxingStub())
+ {
+ pModule->EnsuredStoreMethodDef(pMD->GetMemberDef(), pMD);
+ }
+ }
+
+ ApproxFieldDescIterator fdIterator(pMT, ApproxFieldDescIterator::ALL_FIELDS);
+ FieldDesc* pFD;
+
+ while ((pFD = fdIterator.Next()) != NULL)
+ {
+ if (pFD->GetEnclosingMethodTable() == pMT)
+ {
+ pModule->EnsuredStoreFieldDef(pFD->GetMemberDef(), pFD);
+ }
+ }
+
+ // Publish the type last - to ensure that nobody can see it until all the method and field RID maps are filled in
+ pModule->EnsuredStoreTypeDef(typeDef, typeHnd);
+ }
+
+ return typeHnd;
+}
+
+// Notify profiler and debugger that a type load has completed
+// Also adjust perf counters
+/*static*/
+void ClassLoader::Notify(TypeHandle typeHnd)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(typeHnd));
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CLASSLOADER, LL_INFO1000, "Notify: %p %s\n", typeHnd.AsPtr(), typeHnd.IsTypeDesc() ? "typedesc" : typeHnd.AsMethodTable()->GetDebugClassName()));
+
+ if (typeHnd.IsTypeDesc())
+ return;
+
+ MethodTable * pMT = typeHnd.AsMethodTable();
+
+#ifdef PROFILING_SUPPORTED
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackClasses());
+ // We don't tell profilers about typedescs, as per IF above. Also, we don't
+ // tell profilers about:
+ if (
+ // ...generics with unbound variables
+ (!pMT->ContainsGenericVariables()) &&
+ // ...or array method tables
+ // (This check is mainly for NGEN restore, as JITted code won't hit
+ // this code path for array method tables anyway)
+ (!pMT->IsArray()))
+ {
+ LOG((LF_CLASSLOADER, LL_INFO1000, "Notifying profiler of Started1 %p %s\n", pMT, pMT->GetDebugClassName()));
+ // Record successful load of the class for the profiler
+ g_profControlBlock.pProfInterface->ClassLoadStarted(TypeHandleToClassID(typeHnd));
+
+ //
+ // Profiler can turn off TrackClasses during the Started() callback. Need to
+ // retest the flag here.
+ //
+ if (CORProfilerTrackClasses())
+ {
+ LOG((LF_CLASSLOADER, LL_INFO1000, "Notifying profiler of Finished1 %p %s\n", pMT, pMT->GetDebugClassName()));
+ g_profControlBlock.pProfInterface->ClassLoadFinished(TypeHandleToClassID(typeHnd),
+ S_OK);
+ }
+ }
+ END_PIN_PROFILER();
+ }
+#endif //PROFILING_SUPPORTED
+
+ g_IBCLogger.LogMethodTableAccess(pMT);
+
+ if (pMT->IsTypicalTypeDefinition())
+ {
+ LOG((LF_CLASSLOADER, LL_INFO100, "Successfully loaded class %s\n", pMT->GetDebugClassName()));
+
+#ifdef DEBUGGING_SUPPORTED
+ {
+ Module * pModule = pMT->GetModule();
+ // Update metadata for dynamic module.
+ pModule->UpdateDynamicMetadataIfNeeded();
+ }
+
+ if (CORDebuggerAttached())
+ {
+ LOG((LF_CORDB, LL_EVERYTHING, "NotifyDebuggerLoad clsload 2239 class %s\n", pMT->GetDebugClassName()));
+ typeHnd.NotifyDebuggerLoad(NULL, FALSE);
+ }
+#endif // DEBUGGING_SUPPORTED
+
+#if defined(ENABLE_PERF_COUNTERS)
+ GetPerfCounters().m_Loading.cClassesLoaded ++;
+#endif
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Common helper for LoadTypeHandleForTypeKey and LoadTypeHandleForTypeKeyNoLock.
+// Makes the root level call to kick off the transitive closure walk for
+// the final level pushes.
+//-----------------------------------------------------------------------------
+static void PushFinalLevels(TypeHandle typeHnd, ClassLoadLevel targetLevel, const InstantiationContext *pInstContext)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ LOADS_TYPE(targetLevel);
+ }
+ CONTRACTL_END
+
+
+ // This phase brings the type and all its transitive dependencies to their
+ // final state, sans the IsFullyLoaded bit.
+ if (targetLevel >= CLASS_DEPENDENCIES_LOADED)
+ {
+ BOOL fBailed = FALSE;
+ typeHnd.DoFullyLoad(NULL, CLASS_DEPENDENCIES_LOADED, NULL, &fBailed, pInstContext);
+ }
+
+ // This phase does access/constraint and other type-safety checks on the type
+ // and on its transitive dependencies.
+ if (targetLevel == CLASS_LOADED)
+ {
+ DFLPendingList pendingList;
+ BOOL fBailed = FALSE;
+
+ typeHnd.DoFullyLoad(NULL, CLASS_LOADED, &pendingList, &fBailed, pInstContext);
+
+
+ // In the case of a circular dependency, one or more types will have
+ // had their promotions deferred.
+ //
+ // If we got to this point, all checks have successfully passed on
+ // the transitive closure (otherwise, DoFullyLoad would have thrown.)
+ //
+ // So we can go ahead and mark everyone as fully loaded.
+ //
+ UINT numTH = pendingList.Count();
+ TypeHandle *pTHPending = pendingList.Table();
+ for (UINT i = 0; i < numTH; i++)
+ {
+ // NOTE: It is possible for duplicates to appear in this list so
+ // don't do any operation that isn't idempodent.
+
+ pTHPending[i].SetIsFullyLoaded();
+ }
+ }
+}
+
+
+//
+TypeHandle ClassLoader::LoadTypeHandleForTypeKey(TypeKey *pTypeKey,
+ TypeHandle typeHnd,
+ ClassLoadLevel targetLevel/*=CLASS_LOADED*/,
+ const InstantiationContext *pInstContext/*=NULL*/)
+{
+
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ LOADS_TYPE(targetLevel);
+ }
+ CONTRACTL_END
+
+ GCX_PREEMP();
+
+ // Type loading can be recursive. Probe for sufficient stack.
+ //
+ // Execution of the FINALLY in LoadTypeHandleForTypeKey_Body can eat
+ // a lot of stack because LoadTypeHandleForTypeKey_Inner can rethrow
+ // any non-SO exceptions that it takes, ensure that we have plenty
+ // of stack before getting into it (>24 pages on AMD64, remember
+ // that num pages probed is 2*N on AMD64).
+ INTERIOR_STACK_PROBE_FOR(GetThread(),20);
+
+#ifdef _DEBUG
+ if (LoggingOn(LF_CLASSLOADER, LL_INFO1000))
+ {
+ SString name;
+ TypeString::AppendTypeKeyDebug(name, pTypeKey);
+ LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: LoadTypeHandleForTypeKey for type %S to level %s\n", name.GetUnicode(), classLoadLevelName[targetLevel]));
+ CrstHolder unresolvedClassLockHolder(&m_UnresolvedClassLock);
+ m_pUnresolvedClassHash->Dump();
+ }
+#endif
+
+ // When using domain neutral assemblies (and not eagerly propagating dependency loads),
+ // it's possible to get here without having injected the module into the current app domain.
+ // GetDomainFile will accomplish that.
+
+ if (!pTypeKey->IsConstructed())
+ {
+ pTypeKey->GetModule()->GetDomainFile();
+ }
+
+ ClassLoadLevel currentLevel = typeHnd.IsNull() ? CLASS_LOAD_BEGIN : typeHnd.GetLoadLevel();
+ ClassLoadLevel targetLevelUnderLock = targetLevel < CLASS_DEPENDENCIES_LOADED ? targetLevel : (ClassLoadLevel) (CLASS_DEPENDENCIES_LOADED-1);
+ if (currentLevel < targetLevelUnderLock)
+ {
+ typeHnd = LoadTypeHandleForTypeKey_Body(pTypeKey,
+ typeHnd,
+ targetLevelUnderLock);
+ _ASSERTE(!typeHnd.IsNull());
+ }
+ _ASSERTE(typeHnd.GetLoadLevel() >= targetLevelUnderLock);
+
+ PushFinalLevels(typeHnd, targetLevel, pInstContext);
+
+ END_INTERIOR_STACK_PROBE;
+
+ return typeHnd;
+}
+
+//
+TypeHandle ClassLoader::LoadTypeHandleForTypeKeyNoLock(TypeKey *pTypeKey,
+ ClassLoadLevel targetLevel/*=CLASS_LOADED*/,
+ const InstantiationContext *pInstContext/*=NULL*/)
+{
+
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ LOADS_TYPE(targetLevel);
+ PRECONDITION(CheckPointer(pTypeKey));
+ PRECONDITION(targetLevel >= 0 && targetLevel <= CLASS_LOADED);
+ }
+ CONTRACTL_END
+
+ GCX_PREEMP();
+
+ TypeHandle typeHnd = TypeHandle();
+
+ // Type loading can be recursive. Probe for sufficient stack.
+ INTERIOR_STACK_PROBE_FOR(GetThread(),8);
+
+ ClassLoadLevel currentLevel = CLASS_LOAD_BEGIN;
+ ClassLoadLevel targetLevelUnderLock = targetLevel < CLASS_DEPENDENCIES_LOADED ? targetLevel : (ClassLoadLevel) (CLASS_DEPENDENCIES_LOADED-1);
+ while (currentLevel < targetLevelUnderLock)
+ {
+ typeHnd = DoIncrementalLoad(pTypeKey, typeHnd, currentLevel);
+ CONSISTENCY_CHECK(typeHnd.GetLoadLevel() > currentLevel);
+ currentLevel = typeHnd.GetLoadLevel();
+ }
+
+ PushFinalLevels(typeHnd, targetLevel, pInstContext);
+
+ END_INTERIOR_STACK_PROBE;
+
+ return typeHnd;
+}
+
+//---------------------------------------------------------------------------------------
+//
+class PendingTypeLoadHolder
+{
+ Thread * m_pThread;
+ PendingTypeLoadEntry * m_pEntry;
+ PendingTypeLoadHolder * m_pPrevious;
+
+public:
+ PendingTypeLoadHolder(PendingTypeLoadEntry * pEntry)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_pThread = GetThread();
+ m_pEntry = pEntry;
+
+ m_pPrevious = m_pThread->GetPendingTypeLoad();
+ m_pThread->SetPendingTypeLoad(this);
+ }
+
+ ~PendingTypeLoadHolder()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(m_pThread->GetPendingTypeLoad() == this);
+ m_pThread->SetPendingTypeLoad(m_pPrevious);
+ }
+
+ static bool CheckForDeadLockOnCurrentThread(PendingTypeLoadEntry * pEntry)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ PendingTypeLoadHolder * pCurrent = GetThread()->GetPendingTypeLoad();
+
+ while (pCurrent != NULL)
+ {
+ if (pCurrent->m_pEntry == pEntry)
+ return true;
+
+ pCurrent = pCurrent->m_pPrevious;
+ }
+
+ return false;
+ }
+};
+
+//---------------------------------------------------------------------------------------
+//
+TypeHandle
+ClassLoader::LoadTypeHandleForTypeKey_Body(
+ TypeKey * pTypeKey,
+ TypeHandle typeHnd,
+ ClassLoadLevel targetLevel)
+{
+ CONTRACT(TypeHandle)
+ {
+ STANDARD_VM_CHECK;
+ POSTCONDITION(!typeHnd.IsNull() && typeHnd.GetLoadLevel() >= targetLevel);
+ }
+ CONTRACT_END
+
+ if (!pTypeKey->IsConstructed())
+ {
+ Module *pModule = pTypeKey->GetModule();
+ mdTypeDef cl = pTypeKey->GetTypeToken();
+
+ STRESS_LOG2(LF_CLASSLOADER, LL_INFO100000, "LoadTypeHandle: Loading Class from Module %p token %x\n", pModule, cl);
+
+#ifdef _DEBUG
+ IMDInternalImport* pInternalImport = pModule->GetMDImport();
+ LPCUTF8 className;
+ LPCUTF8 nameSpace;
+ if (FAILED(pInternalImport->GetNameOfTypeDef(cl, &className, &nameSpace)))
+ {
+ className = nameSpace = "Invalid TypeDef record";
+ }
+ if (g_pConfig->ShouldBreakOnClassLoad(className))
+ CONSISTENCY_CHECK_MSGF(false, ("BreakOnClassLoad: typename '%s' ", className));
+#endif
+ }
+
+retry:
+ ReleaseHolder<PendingTypeLoadEntry> pLoadingEntry;
+
+ CrstHolderWithState unresolvedClassLockHolder(&m_UnresolvedClassLock);
+
+ // Is it in the hash of classes currently being loaded?
+ pLoadingEntry = m_pUnresolvedClassHash->GetValue(pTypeKey);
+ if (pLoadingEntry)
+ {
+ pLoadingEntry->AddRef();
+
+ // It is in the hash, which means that another thread is waiting for it (or that we are
+ // already loading this class on this thread, which should never happen, since that implies
+ // a recursive dependency).
+ unresolvedClassLockHolder.Release();
+
+ //
+ // Check one last time before waiting that the type handle is not sufficiently loaded to
+ // prevent deadlocks
+ //
+ {
+ if (typeHnd.IsNull())
+ {
+ typeHnd = LookupTypeHandleForTypeKey(pTypeKey);
+ }
+
+ if (!typeHnd.IsNull())
+ {
+ if (typeHnd.GetLoadLevel() >= targetLevel)
+ RETURN typeHnd;
+ }
+ }
+
+ if (PendingTypeLoadHolder::CheckForDeadLockOnCurrentThread(pLoadingEntry))
+ {
+ // Attempting recursive load
+ ClassLoader::ThrowTypeLoadException(pTypeKey, IDS_CLASSLOAD_GENERAL);
+ }
+
+ //
+ // Violation of type loadlevel ordering rules depends on type load failing in case of cyclic dependency that would
+ // otherwise lead to deadlock. We will speculatively proceed with the type load to make it fail in the right spot,
+ // in backward compatible way. In case the type load succeeds, we will only let one type win in PublishType.
+ //
+ if (typeHnd.IsNull() && GetThread()->HasThreadStateNC(Thread::TSNC_LoadsTypeViolation))
+ {
+ PendingTypeLoadHolder ptlh(pLoadingEntry);
+ typeHnd = DoIncrementalLoad(pTypeKey, TypeHandle(), CLASS_LOAD_BEGIN);
+ goto retry;
+ }
+
+ {
+ // Wait for class to be loaded by another thread. This is where we start tracking the
+ // entry, so there is an implicit Acquire in our use of Assign here.
+ CrstHolder loadingEntryLockHolder(&pLoadingEntry->m_Crst);
+ _ASSERTE(pLoadingEntry->HasLock());
+ }
+
+ // Result of other thread loading the class
+ HRESULT hr = pLoadingEntry->m_hrResult;
+
+ if (FAILED(hr)) {
+
+ //
+ // Redo the lookup one more time and return a valid type if possible. The other thread could
+ // have hit error while loading the type to higher level than we need.
+ //
+ {
+ if (typeHnd.IsNull())
+ {
+ typeHnd = LookupTypeHandleForTypeKey(pTypeKey);
+ }
+
+ if (!typeHnd.IsNull())
+ {
+ if (typeHnd.GetLoadLevel() >= targetLevel)
+ RETURN typeHnd;
+ }
+ }
+
+ if (hr == E_ABORT) {
+ LOG((LF_CLASSLOADER, LL_INFO10, "need to retry LoadTypeHandle: %x\n", hr));
+ goto retry;
+ }
+
+ LOG((LF_CLASSLOADER, LL_INFO10, "Failed to load in other entry: %x\n", hr));
+
+ if (hr == E_OUTOFMEMORY) {
+ COMPlusThrowOM();
+ }
+
+ pLoadingEntry->ThrowException();
+ }
+
+ // Get a pointer to the EEClass being loaded
+ typeHnd = pLoadingEntry->m_typeHandle;
+
+ if (!typeHnd.IsNull())
+ {
+ // If the type load on the other thread loaded the type to the needed level, return it here.
+ if (typeHnd.GetLoadLevel() >= targetLevel)
+ RETURN typeHnd;
+ }
+
+ // The type load on the other thread did not load the type "enough". Begin the type load
+ // process again to cause us to load to the needed level.
+ goto retry;
+ }
+
+ if (typeHnd.IsNull())
+ {
+ // The class was not being loaded. However, it may have already been loaded after our
+ // first LoadTypeHandleThrowIfFailed() and before taking the lock.
+ typeHnd = LookupTypeHandleForTypeKey(pTypeKey);
+ }
+
+ ClassLoadLevel currentLevel = CLASS_LOAD_BEGIN;
+ if (!typeHnd.IsNull())
+ {
+ currentLevel = typeHnd.GetLoadLevel();
+ if (currentLevel >= targetLevel)
+ RETURN typeHnd;
+ }
+
+ // It was not loaded, and it is not being loaded, so we must load it. Create a new LoadingEntry
+ // and acquire it immediately so that other threads will block.
+ pLoadingEntry = new PendingTypeLoadEntry(*pTypeKey, typeHnd); // this atomically creates a crst and acquires it
+
+ if (!(m_pUnresolvedClassHash->InsertValue(pLoadingEntry)))
+ {
+ COMPlusThrowOM();
+ }
+
+ // Leave the global lock, so that other threads may now start waiting on our class's lock
+ unresolvedClassLockHolder.Release();
+
+ EX_TRY
+ {
+ PendingTypeLoadHolder ptlh(pLoadingEntry);
+
+ TRIGGERS_TYPELOAD();
+
+ while (currentLevel < targetLevel)
+ {
+ typeHnd = DoIncrementalLoad(pTypeKey, typeHnd, currentLevel);
+ CONSISTENCY_CHECK(typeHnd.GetLoadLevel() > currentLevel);
+ currentLevel = typeHnd.GetLoadLevel();
+
+ // If other threads are waiting for this load, unblock them as soon as possible to prevent deadlocks.
+ if (pLoadingEntry->HasWaiters())
+ break;
+ }
+
+ _ASSERTE(!typeHnd.IsNull());
+ pLoadingEntry->SetResult(typeHnd);
+ }
+ EX_HOOK
+ {
+ LOG((LF_CLASSLOADER, LL_INFO10, "Caught an exception loading: %x, %0x (Module)\n", pTypeKey->GetTypeToken(), pTypeKey->GetModule()));
+
+ if (!GetThread()->HasThreadStateNC(Thread::TSNC_LoadsTypeViolation))
+ {
+ // Fix up the loading entry.
+ Exception *pException = GET_EXCEPTION();
+ pLoadingEntry->SetException(pException);
+ }
+
+ // Unlink this class from the unresolved class list.
+ unresolvedClassLockHolder.Acquire();
+ m_pUnresolvedClassHash->DeleteValue(pTypeKey);
+
+ // Release the lock before proceeding. The unhandled exception filters take number of locks that
+ // have ordering violations with this lock.
+ unresolvedClassLockHolder.Release();
+ }
+ EX_END_HOOK;
+
+ // Unlink this class from the unresolved class list.
+ unresolvedClassLockHolder.Acquire();
+ m_pUnresolvedClassHash->DeleteValue(pTypeKey);
+
+ if (currentLevel < targetLevel)
+ goto retry;
+
+ RETURN typeHnd;
+} // ClassLoader::LoadTypeHandleForTypeKey_Body
+
+#endif //!DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------
+//
+//static
+TypeHandle
+ClassLoader::LoadArrayTypeThrowing(
+ TypeHandle elemType,
+ CorElementType arrayKind,
+ unsigned rank, //=0
+ LoadTypesFlag fLoadTypes, //=LoadTypes
+ ClassLoadLevel level)
+{
+ CONTRACT(TypeHandle)
+ {
+ 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()); }
+ if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
+ if (fLoadTypes == DontLoadTypes) SO_TOLERANT; else SO_INTOLERANT;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
+ }
+ CONTRACT_END
+
+ CorElementType predefinedElementType = ELEMENT_TYPE_END;
+
+ // Try finding it in our cache of primitive SD arrays
+ if (arrayKind == ELEMENT_TYPE_SZARRAY) {
+ predefinedElementType = elemType.GetSignatureCorElementType();
+ if (predefinedElementType <= ELEMENT_TYPE_R8) {
+ ArrayTypeDesc* typeDesc = g_pPredefinedArrayTypes[predefinedElementType];
+ if (typeDesc != 0)
+ RETURN(TypeHandle(typeDesc));
+ }
+ // This call to AsPtr is somewhat bogus and only used
+ // as an optimization. If the TypeHandle is really a TypeDesc
+ // then the equality checks for the optimizations below will
+ // fail. Thus ArrayMT should not be used elsewhere in this function
+ else if (elemType.AsPtr() == PTR_VOID(g_pObjectClass)) {
+ // Code duplicated because Object[]'s SigCorElementType is E_T_CLASS, not OBJECT
+ ArrayTypeDesc* typeDesc = g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT];
+ if (typeDesc != 0)
+ RETURN(TypeHandle(typeDesc));
+ predefinedElementType = ELEMENT_TYPE_OBJECT;
+ }
+ else if (elemType.AsPtr() == PTR_VOID(g_pStringClass)) {
+ // Code duplicated because String[]'s SigCorElementType is E_T_CLASS, not STRING
+ ArrayTypeDesc* typeDesc = g_pPredefinedArrayTypes[ELEMENT_TYPE_STRING];
+ if (typeDesc != 0)
+ RETURN(TypeHandle(typeDesc));
+ predefinedElementType = ELEMENT_TYPE_STRING;
+ }
+ else {
+ predefinedElementType = ELEMENT_TYPE_END;
+ }
+ rank = 1;
+ }
+
+#ifndef DACCESS_COMPILE
+ // To avoid loading useless shared instantiations, normalize shared instantiations to the canonical form
+ // (e.g. List<_Canon>[] -> _Canon[])
+ // The denormalized shared instantiations should be needed only during JITing, so it is fine to skip this
+ // for DACCESS_COMPILE.
+ if (elemType.IsCanonicalSubtype())
+ {
+ elemType = ClassLoader::CanonicalizeGenericArg(elemType);
+ }
+#endif
+
+ TypeKey key(arrayKind, elemType, FALSE, rank);
+ TypeHandle th = LoadConstructedTypeThrowing(&key, fLoadTypes, level);
+
+ if (predefinedElementType != ELEMENT_TYPE_END && !th.IsNull() && th.IsFullyLoaded())
+ {
+ g_pPredefinedArrayTypes[predefinedElementType] = th.AsArray();
+ }
+
+ RETURN(th);
+} // ClassLoader::LoadArrayTypeThrowing
+
+#ifndef DACCESS_COMPILE
+
+VOID ClassLoader::AddAvailableClassDontHaveLock(Module *pModule,
+ mdTypeDef classdef,
+ AllocMemTracker *pamTracker)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+#ifdef FEATURE_COMINTEROP
+ _ASSERTE(!pModule->GetAssembly()->IsWinMD()); // WinMD files should never get into this path, otherwise provide szWinRtNamespacePrefix
+#endif
+
+ CrstHolder ch(&m_AvailableClassLock);
+ AddAvailableClassHaveLock(
+ pModule,
+ classdef,
+ pamTracker,
+ NULL, // szWinRtNamespacePrefix
+ 0); // cchWinRtNamespacePrefix
+}
+
+// This routine must be single threaded! The reason is that there are situations which allow
+// the same class name to have two different mdTypeDef tokens (for example, we load two different DLLs
+// simultaneously, and they have some common class files, or we convert the same class file
+// simultaneously on two threads). The problem is that we do not want to overwrite the old
+// <classname> -> pModule mapping with the new one, because this may cause identity problems.
+//
+// This routine assumes you already have the lock. Use AddAvailableClassDontHaveLock() if you
+// don't have it.
+//
+// Also validates that TypeDef namespace begins with szWinRTNamespacePrefix (if it is not NULL).
+// The prefix should be NULL for normal non-WinRT .NET assemblies.
+//
+VOID ClassLoader::AddAvailableClassHaveLock(
+ Module * pModule,
+ mdTypeDef classdef,
+ AllocMemTracker * pamTracker,
+ LPCSTR szWinRtNamespacePrefix,
+ DWORD cchWinRtNamespacePrefix) // Optimization for faster prefix comparison implementation
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ EEClassHashTable *pClassHash = pModule->GetAvailableClassHash();
+ EEClassHashTable *pClassCaseInsHash = pModule->GetAvailableClassCaseInsHash();
+
+ LPCUTF8 pszName;
+ LPCUTF8 pszNameSpace;
+ HashDatum ThrowawayData;
+ IMDInternalImport *pMDImport = pModule->GetMDImport();
+ if (FAILED(pMDImport->GetNameOfTypeDef(classdef, &pszName, &pszNameSpace)))
+ {
+ pszName = pszNameSpace = "Invalid TypeDef token";
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
+ }
+
+ EEClassHashEntry_t *pBucket;
+ mdTypeDef enclosing;
+ if (SUCCEEDED(pMDImport->GetNestedClassProps(classdef, &enclosing))) {
+ // nested type
+
+ LPCUTF8 pszEnclosingName;
+ LPCUTF8 pszEnclosingNameSpace;
+ mdTypeDef enclEnclosing;
+
+ // Find this type's encloser's entry in the available table.
+ // We'll save a pointer to it in the new hash entry for this type.
+ BOOL fNestedEncl = SUCCEEDED(pMDImport->GetNestedClassProps(enclosing, &enclEnclosing));
+
+ EEClassHashTable::LookupContext sContext;
+ if (FAILED(pMDImport->GetNameOfTypeDef(enclosing, &pszEnclosingName, &pszEnclosingNameSpace)))
+ {
+ pszName = pszNameSpace = "Invalid TypeDef token";
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
+ }
+ if ((pBucket = pClassHash->GetValue(pszEnclosingNameSpace,
+ pszEnclosingName,
+ &ThrowawayData,
+ fNestedEncl,
+ &sContext)) != NULL) {
+ if (fNestedEncl) {
+ // Find entry for enclosing class - NOTE, this assumes that the
+ // enclosing class's TypeDef or ExportedType was inserted previously,
+ // which assumes that, when enuming TD's, we get the enclosing class first
+ while ((!CompareNestedEntryWithTypeDef(pMDImport,
+ enclEnclosing,
+ pClassHash,
+ pBucket->GetEncloser())) &&
+ (pBucket = pClassHash->FindNextNestedClass(pszEnclosingNameSpace,
+ pszEnclosingName,
+ &ThrowawayData,
+ &sContext)) != NULL);
+ }
+
+ if (!pBucket) // Enclosing type not found in hash table
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_ENCLOSING_TYPE_NOT_FOUND);
+
+ // In this hash table, if the lower bit is set, it means a Module, otherwise it means EEClass*
+ ThrowawayData = EEClassHashTable::CompressClassDef(classdef);
+ InsertValue(pClassHash, pClassCaseInsHash, pszNameSpace, pszName, ThrowawayData, pBucket, pamTracker);
+ }
+ }
+ else {
+ // Don't add duplicate top-level classes. Top-level classes are
+ // added to the beginning of the bucket, while nested classes are
+ // added to the end. So, a duplicate top-level class could hide
+ // the previous type's EEClass* entry in the hash table.
+ EEClassHashEntry_t *pCaseInsEntry = NULL;
+ LPUTF8 pszLowerCaseNS = NULL;
+ LPUTF8 pszLowerCaseName = NULL;
+
+ if (pClassCaseInsHash) {
+ CreateCanonicallyCasedKey(pszNameSpace, pszName, &pszLowerCaseNS, &pszLowerCaseName);
+ pCaseInsEntry = pClassCaseInsHash->AllocNewEntry(pamTracker);
+ }
+
+ EEClassHashEntry_t *pEntry = pClassHash->FindItem(pszNameSpace, pszName, FALSE, NULL);
+ if (pEntry) {
+ HashDatum Data = pEntry->GetData();
+
+ if (((size_t)Data & EECLASSHASH_TYPEHANDLE_DISCR) &&
+ ((size_t)Data & EECLASSHASH_MDEXPORT_DISCR)) {
+
+ // it's an ExportedType - check the 'already seen' bit and if on, report a class loading exception
+ // otherwise, set it
+ if ((size_t)Data & EECLASSHASH_ALREADYSEEN)
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_MULT_TYPE_SAME_NAME);
+ else {
+ Data = (HashDatum)((size_t)Data | EECLASSHASH_ALREADYSEEN);
+ pEntry->SetData(Data);
+ }
+ }
+ else {
+ // We want to throw an exception for a duplicate typedef.
+ // However, this used to be allowed in 1.0/1.1, and some third-party DLLs have
+ // been obfuscated so that they have duplicate private typedefs.
+ // We must allow this for old assemblies for app compat reasons
+#ifdef FEATURE_CORECLR
+#ifdef FEATURE_LEGACYNETCF
+ if (!RuntimeIsLegacyNetCF(0))
+#endif
+ {
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_MULT_TYPE_SAME_NAME);
+ }
+#else
+ LPCSTR pszVersion = NULL;
+ if (FAILED(pModule->GetMDImport()->GetVersionString(&pszVersion)))
+ {
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_MULT_TYPE_SAME_NAME);
+ }
+
+ SString ssVersion(SString::Utf8, pszVersion);
+ SString ssV1(SString::Literal, "v1.");
+
+ AdjustImageRuntimeVersion(&ssVersion);
+
+ // If not "v1.*", throw an exception
+ if (!ssVersion.BeginsWith(ssV1))
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_MULT_TYPE_SAME_NAME);
+#endif
+ }
+ }
+ else {
+ pEntry = pClassHash->AllocNewEntry(pamTracker);
+
+ CANNOTTHROWCOMPLUSEXCEPTION();
+ FAULT_FORBID();
+
+ pClassHash->InsertValueUsingPreallocatedEntry(pEntry, pszNameSpace, pszName, EEClassHashTable::CompressClassDef(classdef), NULL);
+
+ if (pClassCaseInsHash)
+ pClassCaseInsHash->InsertValueUsingPreallocatedEntry(pCaseInsEntry, pszLowerCaseNS, pszLowerCaseName, pEntry, pEntry->GetEncloser());
+ }
+
+#ifdef FEATURE_COMINTEROP
+ // Check WinRT namespace prefix if required
+ if (szWinRtNamespacePrefix != NULL)
+ {
+ DWORD dwAttr;
+ if (FAILED(pMDImport->GetTypeDefProps(classdef, &dwAttr, NULL)))
+ {
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
+ }
+
+ // Check only public WinRT types that are not nested (i.e. only types available for binding, excluding NoPIA)
+ if (IsTdPublic(dwAttr) && IsTdWindowsRuntime(dwAttr))
+ {
+ // Guaranteed by the caller - code:ClassLoader::PopulateAvailableClassHashTable
+ _ASSERTE(cchWinRtNamespacePrefix == strlen(szWinRtNamespacePrefix));
+
+ // Now make sure namespace is, or begins with the namespace-prefix (note: 'MyN' should not match namespace 'MyName')
+ // Note: Case insensitive comparison function has to be in sync with Win8 implementation
+ // (ExtractExactCaseNamespaceSegmentFromMetadataFile in com\WinRT\WinTypes\TypeResolution\NamespaceResolution.cpp)
+ BOOL fIsNamespaceSubstring = (pszNameSpace != NULL) &&
+ ((strncmp(pszNameSpace, szWinRtNamespacePrefix, cchWinRtNamespacePrefix) == 0) ||
+ (_strnicmp(pszNameSpace, szWinRtNamespacePrefix, cchWinRtNamespacePrefix) == 0));
+ BOOL fIsSubNamespace = fIsNamespaceSubstring &&
+ ((pszNameSpace[cchWinRtNamespacePrefix] == '\0') ||
+ (pszNameSpace[cchWinRtNamespacePrefix] == '.'));
+ if (!fIsSubNamespace)
+ {
+ pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_WINRT_INVALID_NAMESPACE_FOR_TYPE);
+ }
+ }
+ }
+#endif // FEATURE_COMINTEROP
+ }
+}
+
+VOID ClassLoader::AddExportedTypeDontHaveLock(Module *pManifestModule,
+ mdExportedType cl,
+ AllocMemTracker *pamTracker)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ CrstHolder ch(&m_AvailableClassLock);
+ AddExportedTypeHaveLock(
+ pManifestModule,
+ cl,
+ pamTracker);
+}
+
+VOID ClassLoader::AddExportedTypeHaveLock(Module *pManifestModule,
+ mdExportedType cl,
+ AllocMemTracker *pamTracker)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+
+ mdToken mdImpl;
+ LPCSTR pszName;
+ LPCSTR pszNameSpace;
+ IMDInternalImport* pAsmImport = pManifestModule->GetMDImport();
+ if (FAILED(pAsmImport->GetExportedTypeProps(
+ cl,
+ &pszNameSpace,
+ &pszName,
+ &mdImpl,
+ NULL, // type def
+ NULL))) // flags
+ {
+ pManifestModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
+ }
+
+ HashDatum ThrowawayData;
+
+ if (TypeFromToken(mdImpl) == mdtExportedType)
+ {
+ // nested class
+ LPCUTF8 pszEnclosingNameSpace;
+ LPCUTF8 pszEnclosingName;
+ mdToken nextImpl;
+ if (FAILED(pAsmImport->GetExportedTypeProps(
+ mdImpl,
+ &pszEnclosingNameSpace,
+ &pszEnclosingName,
+ &nextImpl,
+ NULL, // type def
+ NULL))) // flags
+ {
+ pManifestModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
+ }
+
+ // Find entry for enclosing class - NOTE, this assumes that the
+ // enclosing class's ExportedType was inserted previously, which assumes that,
+ // when enuming ExportedTypes, we get the enclosing class first
+ EEClassHashEntry_t *pBucket;
+ EEClassHashTable::LookupContext sContext;
+ if ((pBucket = pManifestModule->GetAvailableClassHash()->GetValue(pszEnclosingNameSpace,
+ pszEnclosingName,
+ &ThrowawayData,
+ TypeFromToken(nextImpl) == mdtExportedType,
+ &sContext)) != NULL) {
+ do {
+ // check to see if this is the correct class
+ if (EEClassHashTable::UncompressModuleAndClassDef(ThrowawayData) == mdImpl) {
+ ThrowawayData = EEClassHashTable::CompressClassDef(cl);
+
+ // we explicitly don't check for the case insensitive hash table because we know it can't have been created yet
+ pManifestModule->GetAvailableClassHash()->InsertValue(pszNameSpace, pszName, ThrowawayData, pBucket, pamTracker);
+ }
+ pBucket = pManifestModule->GetAvailableClassHash()->FindNextNestedClass(pszEnclosingNameSpace, pszEnclosingName, &ThrowawayData, &sContext);
+ } while (pBucket);
+ }
+
+ // If the encloser is not in the hash table, this nested class
+ // was defined in the manifest module, so it doesn't need to be added
+ return;
+ }
+ else {
+ // Defined in the manifest module - add to the hash table by TypeDef instead
+ if (mdImpl == mdFileNil)
+ return;
+
+ // Don't add duplicate top-level classes
+ // In this hash table, if the lower bit is set, it means a Module, otherwise it means EEClass*
+ ThrowawayData = EEClassHashTable::CompressClassDef(cl);
+ // ThrowawayData is an IN OUT param. Going in its the pointer to the new value if the entry needs
+ // to be inserted. The OUT param points to the value stored in the hash table.
+ BOOL bFound;
+ pManifestModule->GetAvailableClassHash()->InsertValueIfNotFound(pszNameSpace, pszName, &ThrowawayData, NULL, FALSE, &bFound, pamTracker);
+ if (bFound) {
+
+ // Check for duplicate ExportedTypes
+ // Let it slide if it's pointing to the same type
+ mdToken foundTypeImpl;
+ if ((size_t)ThrowawayData & EECLASSHASH_MDEXPORT_DISCR)
+ {
+ mdExportedType foundExportedType = EEClassHashTable::UncompressModuleAndClassDef(ThrowawayData);
+ if (FAILED(pAsmImport->GetExportedTypeProps(
+ foundExportedType,
+ NULL, // namespace
+ NULL, // name
+ &foundTypeImpl,
+ NULL, // TypeDef
+ NULL))) // flags
+ {
+ pManifestModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
+ }
+ }
+ else
+ {
+ foundTypeImpl = mdFileNil;
+ }
+
+ if (mdImpl != foundTypeImpl)
+ {
+ pManifestModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_MULT_TYPE_SAME_NAME);
+ }
+ }
+ }
+}
+
+static MethodTable* GetEnclosingMethodTable(MethodTable *pMT)
+{
+ CONTRACT(MethodTable*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pMT));
+ POSTCONDITION(RETVAL == NULL || RETVAL->IsTypicalTypeDefinition());
+ }
+ CONTRACT_END;
+
+ MethodTable *pmtEnclosing = NULL;
+
+ // In the common case, the method table will be either shared or in the AppDomain we're currently
+ // running in. If this is true, we can just access its enclosing method table directly.
+ //
+ // However, if the current method table is actually in another AppDomain (for instance, we're reflecting
+ // across AppDomains), then we cannot get its enclsoing type in our AppDomain since doing that may involve
+ // loading the enclosing type. Instead, we need to transition back to the original domain (which we
+ // should already be running in higher up on the stack) and get the method table we're looking for.
+
+ if (pMT->GetDomain()->IsSharedDomain() || pMT->GetDomain()->AsAppDomain() == GetAppDomain())
+ {
+ pmtEnclosing = pMT->LoadEnclosingMethodTable();
+ }
+ else
+ {
+ GCX_COOP();
+ ENTER_DOMAIN_PTR(pMT->GetDomain()->AsAppDomain(), ADV_RUNNINGIN);
+ pmtEnclosing = pMT->LoadEnclosingMethodTable();
+ END_DOMAIN_TRANSITION;
+ }
+
+ RETURN pmtEnclosing;
+}
+
+StaticAccessCheckContext::StaticAccessCheckContext(MethodDesc* pCallerMethod)
+{
+ CONTRACTL
+ {
+ LIMITED_METHOD_CONTRACT;
+ PRECONDITION(CheckPointer(pCallerMethod));
+ }
+ CONTRACTL_END;
+
+ m_pCallerMethod = pCallerMethod;
+ m_pCallerMT = m_pCallerMethod->GetMethodTable();
+ m_pCallerAssembly = m_pCallerMT->GetAssembly();
+}
+
+StaticAccessCheckContext::StaticAccessCheckContext(MethodDesc* pCallerMethod, MethodTable* pCallerType)
+{
+ CONTRACTL
+ {
+ LIMITED_METHOD_CONTRACT;
+ PRECONDITION(CheckPointer(pCallerMethod, NULL_OK));
+ PRECONDITION(CheckPointer(pCallerType));
+ }
+ CONTRACTL_END;
+
+ m_pCallerMethod = pCallerMethod;
+ m_pCallerMT = pCallerType;
+ m_pCallerAssembly = pCallerType->GetAssembly();
+}
+
+// Critical callers do not need the extra access checks
+bool StaticAccessCheckContext::IsCallerCritical()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pCallerMethod == NULL || !Security::IsMethodTransparent(m_pCallerMethod))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+#ifndef FEATURE_CORECLR
+
+//******************************************************************************
+// This function determines whether a Type is accessible from
+// outside of the assembly it lives in.
+
+static BOOL IsTypeVisibleOutsideAssembly(MethodTable* pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ DWORD dwProtection;
+ // check all types in nesting chain, while inner types are public
+ while (IsTdPublic(dwProtection = pMT->GetClass()->GetProtection()) ||
+ IsTdNestedPublic(dwProtection))
+ {
+ // if type is nested, check outer type, too
+ if (IsTdNested(dwProtection))
+ {
+ pMT = GetEnclosingMethodTable(pMT);
+ }
+ // otherwise, type is visible outside of the assembly
+ else
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+} // static BOOL IsTypeVisibleOutsideAssembly(MethodTable* pMT)
+
+#endif //!FEATURE_CORECLR
+
+//******************************************************************************
+
+// static
+AccessCheckOptions* AccessCheckOptions::s_pNormalAccessChecks;
+
+//******************************************************************************
+
+void AccessCheckOptions::Startup()
+{
+ STANDARD_VM_CONTRACT;
+
+ s_pNormalAccessChecks = new AccessCheckOptions(
+ AccessCheckOptions::kNormalAccessibilityChecks,
+ NULL,
+ FALSE,
+ (MethodTable *)NULL);
+}
+
+//******************************************************************************
+AccessCheckOptions::AccessCheckOptions(
+ const AccessCheckOptions & templateOptions,
+ BOOL throwIfTargetIsInaccessible,
+ BOOL skipCheckForCriticalCode /*=FALSE*/) :
+ m_pAccessContext(templateOptions.m_pAccessContext)
+{
+ WRAPPER_NO_CONTRACT;
+
+ Initialize(
+ templateOptions.m_accessCheckType,
+ throwIfTargetIsInaccessible,
+ templateOptions.m_pTargetMT,
+ templateOptions.m_pTargetMethod,
+ templateOptions.m_pTargetField,
+ skipCheckForCriticalCode);
+}
+
+//******************************************************************************
+// This function should only be called when normal accessibility is not possible.
+// It returns TRUE if the target can be accessed.
+// Otherwise, it either returns FALSE or throws an exception, depending on the value of throwIfTargetIsInaccessible.
+
+BOOL AccessCheckOptions::DemandMemberAccess(AccessCheckContext *pContext, MethodTable * pTargetMT, BOOL visibilityCheck) const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(m_accessCheckType != kNormalAccessibilityChecks);
+ PRECONDITION(CheckPointer(pContext));
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(m_accessCheckType != kNormalAccessibilityChecks);
+
+ if (NingenEnabled())
+ {
+ // NinGen should always perform normal accessibility checks
+ _ASSERTE(false);
+
+ if (m_fThrowIfTargetIsInaccessible)
+ {
+ ThrowAccessException(pContext, pTargetMT, NULL, FALSE);
+ }
+
+ return FALSE;
+ }
+
+ if (pTargetMT && pTargetMT->GetAssembly()->IsDisabledPrivateReflection())
+ {
+ if (m_fThrowIfTargetIsInaccessible)
+ {
+ ThrowAccessException(pContext, pTargetMT, NULL, FALSE);
+ }
+
+ return FALSE;
+ }
+
+ BOOL canAccessTarget = FALSE;
+
+#ifndef CROSSGEN_COMPILE
+#ifdef FEATURE_CORECLR
+
+ BOOL fAccessingFrameworkCode = FALSE;
+
+ // In CoreCLR kRestrictedMemberAccess means that one can access private/internal
+ // classes/members in app code.
+ if (m_accessCheckType != kMemberAccess && pTargetMT)
+ {
+ // m_accessCheckType must be kRestrictedMemberAccess if we are running in PT.
+ _ASSERTE(GetAppDomain()->GetSecurityDescriptor()->IsFullyTrusted() ||
+ m_accessCheckType == kRestrictedMemberAccess);
+
+ if (visibilityCheck)
+ {
+ // In CoreCLR RMA means visibility checks always succeed if the target is user code.
+ if ((m_accessCheckType == kRestrictedMemberAccess || m_accessCheckType == kRestrictedMemberAccessNoTransparency) &&
+ !Security::IsMicrosoftPlatform(pTargetMT->GetAssembly()->GetSecurityDescriptor()))
+ return TRUE;
+
+ // Accessing private types/members in platform code.
+ fAccessingFrameworkCode = TRUE;
+ }
+ else
+ {
+ // We allow all transparency checks to succeed in LCG methods and reflection invocation.
+ if (m_accessCheckType == kNormalAccessNoTransparency || m_accessCheckType == kRestrictedMemberAccessNoTransparency)
+ return TRUE;
+ }
+ }
+
+ // Always allow interop (NULL) callers full access.
+ if (pContext->IsCalledFromInterop())
+ return TRUE;
+
+ MethodDesc* pCallerMD = pContext->GetCallerMethod();
+
+ // Platform critical code is exempted from all accessibility rules, regardless of the AccessCheckType.
+ if (pCallerMD != NULL &&
+ !Security::IsMethodTransparent(pCallerMD)
+ && Security::IsMicrosoftPlatform(pCallerMD->GetAssembly()->GetSecurityDescriptor()))
+ {
+ return TRUE;
+ }
+
+ // No Access
+ if (m_fThrowIfTargetIsInaccessible)
+ {
+ ThrowAccessException(pContext, pTargetMT, NULL, fAccessingFrameworkCode);
+ }
+
+#else // FEATURE_CORECLR
+
+ GCX_COOP();
+
+ // Overriding the rules of visibility checks in Win8 immersive: no access is allowed to internal
+ // code in the framework even in full trust, unless the caller is also framework code.
+ if ( (m_accessCheckType == kUserCodeOnlyRestrictedMemberAccess ||
+ m_accessCheckType == kUserCodeOnlyRestrictedMemberAccessNoTransparency) &&
+ visibilityCheck )
+ {
+ IAssemblyName *pIAssemblyName = pTargetMT->GetAssembly()->GetFusionAssemblyName();
+
+ HRESULT hr = Fusion::Util::IsAnyFrameworkAssembly(pIAssemblyName);
+
+ // S_OK: pIAssemblyName is a framework assembly.
+ // S_FALSE: pIAssemblyName is not a framework assembly.
+ // Other values: pIAssemblyName is an invalid name.
+ if (hr == S_OK)
+ {
+ if (pContext->IsCalledFromInterop())
+ return TRUE;
+
+ // If the caller method is NULL and we are not called from interop
+ // this is not a normal method access check (e.g. a CA accessibility check)
+ // The access check should fail in this case.
+ hr = S_FALSE;
+
+ MethodDesc* pCallerMD = pContext->GetCallerMethod();
+ if (pCallerMD != NULL)
+ {
+ pIAssemblyName = pCallerMD->GetAssembly()->GetFusionAssemblyName();
+ hr = Fusion::Util::IsAnyFrameworkAssembly(pIAssemblyName);
+ }
+
+ // The caller is not framework code.
+ if (hr != S_OK)
+ {
+ if (m_fThrowIfTargetIsInaccessible)
+ ThrowAccessException(pContext, pTargetMT, NULL, TRUE);
+ else
+ return FALSE;
+ }
+ }
+ }
+
+ EX_TRY
+ {
+ if (m_accessCheckType == kMemberAccess)
+ {
+ Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, REFLECTION_MEMBER_ACCESS);
+ }
+ else
+ {
+ _ASSERTE(m_accessCheckType == kRestrictedMemberAccess ||
+ m_accessCheckType == kUserCodeOnlyRestrictedMemberAccess ||
+ (m_accessCheckType == kUserCodeOnlyRestrictedMemberAccessNoTransparency && visibilityCheck));
+
+ // JIT guarantees that pTargetMT has been fully loaded and ready to execute by this point, but reflection doesn't.
+ // So GetSecurityDescriptor could AV because the DomainAssembly cannot be found.
+ // For now we avoid this by calling EnsureActive aggressively. We might want to move this to the reflection code in the future:
+ // ReflectionInvocation::PerformVisibilityCheck, PerformSecurityCheckHelper, COMDelegate::BindToMethodName/Info, etc.
+ // We don't need to call EnsureInstanceActive because we will be doing access check on all the generic arguments any way so
+ // EnsureActive will be called on everyone of them if needed.
+ pTargetMT->EnsureActive();
+
+ IAssemblySecurityDescriptor * pTargetSecurityDescriptor = pTargetMT->GetModule()->GetSecurityDescriptor();
+ _ASSERTE(pTargetSecurityDescriptor != NULL);
+
+ if (m_pAccessContext != NULL)
+ {
+ // If we have a context, use it to do the demand
+ Security::ReflectionTargetDemand(REFLECTION_MEMBER_ACCESS,
+ pTargetSecurityDescriptor,
+ m_pAccessContext);
+ }
+ else
+ {
+ // Just do a normal Demand
+ Security::ReflectionTargetDemand(REFLECTION_MEMBER_ACCESS, pTargetSecurityDescriptor);
+ }
+ }
+
+ canAccessTarget = TRUE;
+ }
+ EX_CATCH
+ {
+ canAccessTarget = FALSE;
+
+ if (m_fThrowIfTargetIsInaccessible)
+ {
+ ThrowAccessException(pContext, pTargetMT, GET_EXCEPTION());
+ }
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+
+#endif // FEATURE_CORECLR
+#endif // CROSSGEN_COMPILE
+
+ return canAccessTarget;
+}
+
+//******************************************************************************
+// pFailureMT - the MethodTable that we were trying to access. It can be null
+// if the failure is not because of a specific type. This will be a
+// a component of the instantiation of m_pTargetMT/m_pTargetMethod/m_pTargetField.
+
+void AccessCheckOptions::ThrowAccessException(
+ AccessCheckContext* pContext,
+ MethodTable* pFailureMT, /* = NULL */
+ Exception* pInnerException, /* = NULL */
+ BOOL fAccessingFrameworkCode /* = FALSE */) const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ PRECONDITION(CheckPointer(pInnerException, NULL_OK));
+ PRECONDITION(m_fThrowIfTargetIsInaccessible);
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+
+ MethodDesc* pCallerMD = pContext->GetCallerMethod();
+
+ if (m_pTargetMT != NULL)
+ {
+ // If we know the specific type that caused the failure, display it.
+ // Else display the whole type that we are trying to access.
+ MethodTable * pMT = (pFailureMT != NULL) ? pFailureMT : m_pTargetMT;
+ ThrowTypeAccessException(pContext, pMT, 0, pInnerException, fAccessingFrameworkCode);
+ }
+ else if (m_pTargetMethod != NULL)
+ {
+ // If the caller and target method are non-null and the same, then this means that we're checking to see
+ // if the method has access to itself in order to validate that it has access to its parameter types,
+ // containing type, and return type. In this case, throw a more informative TypeAccessException to
+ // describe the error that occured (for instance, "this method doesn't have access to one of its
+ // parameter types", rather than "this method doesn't have access to itself").
+ // We only want to do this if we know the exact type that caused the problem, otherwise fall back to
+ // throwing the standard MethodAccessException.
+ if (pCallerMD != NULL && m_pTargetMethod == pCallerMD && pFailureMT != NULL)
+ {
+ ThrowTypeAccessException(pContext, pFailureMT, 0, pInnerException, fAccessingFrameworkCode);
+ }
+ else
+ {
+ ThrowMethodAccessException(pContext, m_pTargetMethod, 0, pInnerException, fAccessingFrameworkCode);
+ }
+ }
+ else
+ {
+ _ASSERTE(m_pTargetField != NULL);
+ ThrowFieldAccessException(pContext, m_pTargetField, 0, pInnerException, fAccessingFrameworkCode);
+ }
+}
+
+//******************************************************************************
+// This will do a security demand if appropriate.
+// If access is not possible, this will either throw an exception or return FALSE
+BOOL AccessCheckOptions::DemandMemberAccessOrFail(AccessCheckContext *pContext, MethodTable * pTargetMT, BOOL visibilityCheck) const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // m_fSkipCheckForCriticalCode is only ever set to true for CanAccessMemberForExtraChecks.
+ // For legacy compat we allow the access check to succeed for all AccessCheckType if the caller is critical.
+ if (m_fSkipCheckForCriticalCode)
+ {
+ if (pContext->IsCalledFromInterop() ||
+ !Security::IsMethodTransparent(pContext->GetCallerMethod()))
+ return TRUE;
+ }
+
+ if (DoNormalAccessibilityChecks())
+ {
+ if (pContext->GetCallerAssembly()->IgnoresAccessChecksTo(pTargetMT->GetAssembly()))
+ {
+ return TRUE;
+ }
+
+#if defined(FEATURE_CORECLR) && defined(CROSSGEN_COMPILE)
+ CONSISTENCY_CHECK_MSGF(!pContext->GetCallerAssembly()->GetManifestFile()->IsProfileAssembly(),
+ ("Accessibility check failed while compiling platform assembly. Are you missing FriendAccessAllowed attribute? Caller: %s %s %s Target: %s",
+ pContext->GetCallerAssembly() ? pContext->GetCallerAssembly()->GetSimpleName() : "",
+ pContext->GetCallerMT() ? pContext->GetCallerMT()->GetDebugClassName() : "",
+ pContext->GetCallerMethod() ? pContext->GetCallerMethod()->GetName() : "",
+ pTargetMT ? pTargetMT->GetDebugClassName() : ""));
+#endif
+
+ if (m_fThrowIfTargetIsInaccessible)
+ {
+ ThrowAccessException(pContext, pTargetMT);
+ }
+
+ return FALSE;
+ }
+
+ return DemandMemberAccess(pContext, pTargetMT, visibilityCheck);
+}
+
+//******************************************************************************
+// This should be called if access to the target is not possible.
+// This will either throw an exception or return FALSE.
+BOOL AccessCheckOptions::FailOrThrow(AccessCheckContext *pContext) const
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ }
+ CONTRACTL_END;
+
+ // m_fSkipCheckForCriticalCode is only ever set to true for CanAccessMemberForExtraChecks.
+ // For legacy compat we allow the access check to succeed for all AccessCheckType if the caller is critical.
+ if (m_fSkipCheckForCriticalCode)
+ {
+ if (pContext->IsCalledFromInterop() ||
+ !Security::IsMethodTransparent(pContext->GetCallerMethod()))
+ return TRUE;
+ }
+
+ if (m_fThrowIfTargetIsInaccessible)
+ {
+ ThrowAccessException(pContext);
+ }
+
+ return FALSE;
+}
+
+// Generate access exception context strings that are due to potential security misconfiguration
+void GetAccessExceptionAdditionalContextForSecurity(Assembly *pAccessingAssembly,
+ Assembly *pTargetAssembly,
+ BOOL isTransparencyError,
+ BOOL fAccessingFrameworkCode,
+ StringArrayList *pContextInformation)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pAccessingAssembly));
+ PRECONDITION(CheckPointer(pTargetAssembly));
+ PRECONDITION(CheckPointer(pContextInformation));
+ }
+ CONTRACTL_END;
+
+ if (fAccessingFrameworkCode)
+ {
+ SString accessingFrameworkCodeError;
+ EEException::GetResourceMessage(IDS_E_ACCESSING_PRIVATE_FRAMEWORK_CODE, accessingFrameworkCodeError);
+
+ pContextInformation->Append(accessingFrameworkCodeError);
+ }
+
+#ifndef FEATURE_CORECLR
+ if (isTransparencyError)
+ {
+ ModuleSecurityDescriptor *pMSD = ModuleSecurityDescriptor::GetModuleSecurityDescriptor(pAccessingAssembly);
+
+ // If the accessing assembly is APTCA and using level 2 transparency, then transparency errors may be
+ // because APTCA newly opts assemblies into being all transparent.
+ if (pMSD->IsMixedTransparency() && !pAccessingAssembly->GetSecurityTransparencyBehavior()->DoesUnsignedImplyAPTCA())
+ {
+ SString callerDisplayName;
+ pAccessingAssembly->GetDisplayName(callerDisplayName);
+
+ SString level2AptcaTransparencyError;
+ EEException::GetResourceMessage(IDS_ACCESS_EXCEPTION_CONTEXT_LEVEL2_APTCA, level2AptcaTransparencyError, callerDisplayName);
+
+ pContextInformation->Append(level2AptcaTransparencyError);
+ }
+
+ // If the assessing assembly is fully transparent and it is partially trusted, then transparency
+ // errors may be because the CLR forced the assembly to be transparent due to its trust level.
+ if (pMSD->IsAllTransparentDueToPartialTrust())
+ {
+ _ASSERTE(pMSD->IsAllTransparent());
+ SString callerDisplayName;
+ pAccessingAssembly->GetDisplayName(callerDisplayName);
+
+ SString partialTrustTransparencyError;
+ EEException::GetResourceMessage(IDS_ACCESS_EXCEPTION_CONTEXT_PT_TRANSPARENT, partialTrustTransparencyError, callerDisplayName);
+
+ pContextInformation->Append(partialTrustTransparencyError);
+ }
+ }
+#endif // FEATURE_CORECLR
+
+#if defined(FEATURE_APTCA) && !defined(CROSSGEN_COMPILE)
+ // If the target assembly is conditionally APTCA, then it may needed to have been enabled in the domain
+ SString conditionalAptcaContext = Security::GetConditionalAptcaAccessExceptionContext(pTargetAssembly);
+ if (!conditionalAptcaContext.IsEmpty())
+ {
+ pContextInformation->Append(conditionalAptcaContext);
+ }
+
+ // If the target assembly is APTCA killbitted, then indicate that as well
+ SString aptcaKillBitContext = Security::GetAptcaKillBitAccessExceptionContext(pTargetAssembly);
+ if (!aptcaKillBitContext.IsEmpty())
+ {
+ pContextInformation->Append(aptcaKillBitContext);
+ }
+#endif // FEATURE_APTCA && !CROSSGEN_COMPILE
+}
+
+// Generate additional context about the root cause of an access exception which may help in debugging it (for
+// instance v4 APTCA implying transparnecy, or conditional APTCA not being enabled). If no additional
+// context is available, then this returns SString.Empty.
+SString GetAdditionalAccessExceptionContext(Assembly *pAccessingAssembly,
+ Assembly *pTargetAssembly,
+ BOOL isTransparencyError,
+ BOOL fAccessingFrameworkCode)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pAccessingAssembly));
+ PRECONDITION(CheckPointer(pTargetAssembly));
+ }
+ CONTRACTL_END;
+
+ StringArrayList contextComponents;
+
+ // See if the exception may have been caused by security
+ GetAccessExceptionAdditionalContextForSecurity(pAccessingAssembly,
+ pTargetAssembly,
+ isTransparencyError,
+ fAccessingFrameworkCode,
+ &contextComponents);
+
+ // Append each component of additional context we found into the additional context string in its own
+ // paragraph.
+ SString additionalContext;
+ for (DWORD i = 0; i < contextComponents.GetCount(); ++i)
+ {
+ SString contextComponent = contextComponents.Get(i);
+ if (!contextComponent.IsEmpty())
+ {
+ additionalContext.Append(W("\n\n"));
+ additionalContext.Append(contextComponent);
+ }
+ }
+
+ return additionalContext;
+}
+
+void DECLSPEC_NORETURN ThrowFieldAccessException(AccessCheckContext* pContext,
+ FieldDesc *pFD,
+ UINT messageID /* = 0 */,
+ Exception *pInnerException /* = NULL */,
+ BOOL fAccessingFrameworkCode /* = FALSE */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ PRECONDITION(CheckPointer(pFD));
+ }
+ CONTRACTL_END;
+
+ BOOL isTransparencyError = FALSE;
+
+ MethodDesc* pCallerMD = pContext->GetCallerMethod();
+ if (pCallerMD != NULL)
+ isTransparencyError = !Security::CheckCriticalAccess(pContext, NULL, pFD, NULL);
+
+ ThrowFieldAccessException(pCallerMD,
+ pFD,
+ isTransparencyError,
+ messageID,
+ pInnerException,
+ fAccessingFrameworkCode);
+}
+
+void DECLSPEC_NORETURN ThrowFieldAccessException(MethodDesc* pCallerMD,
+ FieldDesc *pFD,
+ BOOL isTransparencyError,
+ UINT messageID /* = 0 */,
+ Exception *pInnerException /* = NULL */,
+ BOOL fAccessingFrameworkCode /* = FALSE */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pCallerMD, NULL_OK));
+ PRECONDITION(CheckPointer(pFD));
+ }
+ CONTRACTL_END;
+
+ if (pCallerMD != NULL)
+ {
+ if (messageID == 0)
+ {
+ // Figure out if we can give a specific reason why this field access was rejected - for instance, if
+ // we see that the caller is transparent and accessing a critical field, then we can put that
+ // information into the exception message.
+ if (isTransparencyError)
+ {
+ messageID = IDS_E_CRITICAL_FIELD_ACCESS_DENIED;
+ }
+ else
+ {
+ messageID = IDS_E_FIELDACCESS;
+ }
+ }
+
+ SString strAdditionalContext = GetAdditionalAccessExceptionContext(pCallerMD->GetAssembly(),
+ pFD->GetApproxEnclosingMethodTable()->GetAssembly(),
+ isTransparencyError,
+ fAccessingFrameworkCode);
+
+ EX_THROW_WITH_INNER(EEFieldException, (pFD, pCallerMD, strAdditionalContext, messageID), pInnerException);
+ }
+ else
+ {
+ EX_THROW_WITH_INNER(EEFieldException, (pFD), pInnerException);
+ }
+}
+
+void DECLSPEC_NORETURN ThrowMethodAccessException(AccessCheckContext* pContext,
+ MethodDesc *pCalleeMD,
+ UINT messageID /* = 0 */,
+ Exception *pInnerException /* = NULL */,
+ BOOL fAccessingFrameworkCode /* = FALSE */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ PRECONDITION(CheckPointer(pCalleeMD));
+ }
+ CONTRACTL_END;
+
+ BOOL isTransparencyError = FALSE;
+
+ MethodDesc* pCallerMD = pContext->GetCallerMethod();
+ if (pCallerMD != NULL)
+ isTransparencyError = !Security::CheckCriticalAccess(pContext, pCalleeMD, NULL, NULL);
+
+ ThrowMethodAccessException(pCallerMD,
+ pCalleeMD,
+ isTransparencyError,
+ messageID,
+ pInnerException,
+ fAccessingFrameworkCode);
+}
+
+void DECLSPEC_NORETURN ThrowMethodAccessException(MethodDesc* pCallerMD,
+ MethodDesc *pCalleeMD,
+ BOOL isTransparencyError,
+ UINT messageID /* = 0 */,
+ Exception *pInnerException /* = NULL */,
+ BOOL fAccessingFrameworkCode /* = FALSE */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pCallerMD, NULL_OK));
+ PRECONDITION(CheckPointer(pCalleeMD));
+ }
+ CONTRACTL_END;
+
+ if (pCallerMD != NULL)
+ {
+ if (messageID == 0)
+ {
+ // Figure out if we can give a specific reason why this method access was rejected - for instance, if
+ // we see that the caller is transparent and the callee is critical, then we can put that
+ // information into the exception message.
+ if (isTransparencyError)
+ {
+ messageID = IDS_E_CRITICAL_METHOD_ACCESS_DENIED;
+ }
+ else
+ {
+ messageID = IDS_E_METHODACCESS;
+ }
+ }
+
+ SString strAdditionalContext = GetAdditionalAccessExceptionContext(pCallerMD->GetAssembly(),
+ pCalleeMD->GetAssembly(),
+ isTransparencyError,
+ fAccessingFrameworkCode);
+
+ EX_THROW_WITH_INNER(EEMethodException, (pCalleeMD, pCallerMD, strAdditionalContext, messageID), pInnerException);
+ }
+ else
+ {
+ EX_THROW_WITH_INNER(EEMethodException, (pCalleeMD), pInnerException);
+ }
+}
+
+void DECLSPEC_NORETURN ThrowTypeAccessException(AccessCheckContext* pContext,
+ MethodTable *pMT,
+ UINT messageID /* = 0 */,
+ Exception *pInnerException /* = NULL */,
+ BOOL fAccessingFrameworkCode /* = FALSE */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ BOOL isTransparencyError = FALSE;
+
+ MethodDesc* pCallerMD = pContext->GetCallerMethod();
+ if (pCallerMD != NULL)
+ isTransparencyError = !Security::CheckCriticalAccess(pContext, NULL, NULL, pMT);
+
+ ThrowTypeAccessException(pCallerMD,
+ pMT,
+ isTransparencyError,
+ messageID,
+ pInnerException,
+ fAccessingFrameworkCode);
+}
+
+void DECLSPEC_NORETURN ThrowTypeAccessException(MethodDesc* pCallerMD,
+ MethodTable *pMT,
+ BOOL isTransparencyError,
+ UINT messageID /* = 0 */,
+ Exception *pInnerException /* = NULL */,
+ BOOL fAccessingFrameworkCode /* = FALSE */)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pCallerMD, NULL_OK));
+ PRECONDITION(CheckPointer(pMT));
+ }
+ CONTRACTL_END;
+
+ if (pCallerMD != NULL)
+ {
+ if (messageID == 0)
+ {
+ // Figure out if we can give a specific reason why this type access was rejected - for instance, if
+ // we see that the caller is transparent and is accessing a critical type, then we can put that
+ // information into the exception message.
+ if (isTransparencyError)
+ {
+ messageID = IDS_E_CRITICAL_TYPE_ACCESS_DENIED;
+ }
+ else
+ {
+ messageID = IDS_E_TYPEACCESS;
+ }
+ }
+
+ SString strAdditionalContext = GetAdditionalAccessExceptionContext(pCallerMD->GetAssembly(),
+ pMT->GetAssembly(),
+ isTransparencyError,
+ fAccessingFrameworkCode);
+
+ EX_THROW_WITH_INNER(EETypeAccessException, (pMT, pCallerMD, strAdditionalContext, messageID), pInnerException);
+ }
+ else
+ {
+ EX_THROW_WITH_INNER(EETypeAccessException, (pMT), pInnerException);
+ }
+}
+
+//******************************************************************************
+// This function determines whether a method [if transparent]
+// can access a specified target (e.g. Type, Method, Field)
+static BOOL CheckTransparentAccessToCriticalCode(
+ AccessCheckContext* pContext,
+ DWORD dwMemberAccess,
+ MethodTable* pTargetMT,
+ MethodDesc* pOptionalTargetMethod,
+ FieldDesc* pOptionalTargetField,
+ MethodTable* pOptionalTargetType,
+ const AccessCheckOptions & accessCheckOptions)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ PRECONDITION(accessCheckOptions.TransparencyCheckNeeded());
+ }
+ CONTRACTL_END;
+
+ // At most one of these should be non-NULL
+ _ASSERTE(1 >= ((pOptionalTargetMethod ? 1 : 0) +
+ (pOptionalTargetField ? 1 : 0) +
+ (pOptionalTargetType ? 1 : 0)));
+
+#ifndef FEATURE_CORECLR
+ if (pTargetMT->GetAssembly()->GetSecurityTransparencyBehavior()->DoesPublicImplyTreatAsSafe())
+ {
+ // @ telesto: public => TAS in non-coreclr only. The intent is to remove this ifdef and remove
+ // public => TAS in all flavors/branches.
+ // check if the Target member accessible outside the assembly
+ if (IsMdPublic(dwMemberAccess) && IsTypeVisibleOutsideAssembly(pTargetMT))
+ {
+ return TRUE;
+ }
+ }
+#endif // !FEATURE_CORECLR
+
+ // if the caller [Method] is transparent, do special security checks
+ // check if security disallows access to target member
+ if (!Security::CheckCriticalAccess(
+ pContext,
+ pOptionalTargetMethod,
+ pOptionalTargetField,
+ pOptionalTargetType))
+ {
+#ifdef _DEBUG
+ if (g_pConfig->LogTransparencyErrors())
+ {
+ SecurityTransparent::LogTransparencyError(pContext->GetCallerMethod(), "Transparent code accessing a critical type, method, or field", pOptionalTargetMethod);
+ }
+ if (!g_pConfig->DisableTransparencyEnforcement())
+#endif // _DEBUG
+ {
+ return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, FALSE /*visibilityCheck*/);
+ }
+ }
+
+ return TRUE;
+} // static BOOL CheckTransparentAccessToCriticalCode
+
+//---------------------------------------------------------------------------------------
+//
+// Checks to see if access to a member with assembly visiblity is allowed.
+//
+// Arguments:
+// pAccessingAssembly - The assembly requesting access to the internal member
+// pTargetAssembly - The assembly which contains the target member
+// pOptionalTargetField - Internal field being accessed OR
+// pOptionalTargetMethod - Internal type being accessed OR
+// pOptionalTargetType - Internal type being accessed
+//
+// Return Value:
+// TRUE if pTargetAssembly is pAccessingAssembly, or if pTargetAssembly allows
+// pAccessingAssembly friend access to the target. FALSE otherwise.
+//
+
+static BOOL AssemblyOrFriendAccessAllowed(Assembly *pAccessingAssembly,
+ Assembly *pTargetAssembly,
+ FieldDesc *pOptionalTargetField,
+ MethodDesc *pOptionalTargetMethod,
+ MethodTable *pOptionalTargetType)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(pAccessingAssembly));
+ PRECONDITION(CheckPointer(pTargetAssembly));
+ PRECONDITION(pOptionalTargetField != NULL || pOptionalTargetMethod != NULL || pOptionalTargetType != NULL);
+ PRECONDITION(pOptionalTargetField == NULL || pOptionalTargetMethod == NULL);
+ }
+ CONTRACTL_END;
+
+ if (pAccessingAssembly == pTargetAssembly)
+ {
+ return TRUE;
+ }
+
+ if (pAccessingAssembly->IgnoresAccessChecksTo(pTargetAssembly))
+ {
+ return TRUE;
+ }
+
+#if defined(FEATURE_REMOTING) && !defined(CROSSGEN_COMPILE)
+ else if (pAccessingAssembly->GetDomain() != pTargetAssembly->GetDomain() &&
+ pAccessingAssembly->GetFusionAssemblyName()->IsEqual(pTargetAssembly->GetFusionAssemblyName(), ASM_CMPF_NAME | ASM_CMPF_PUBLIC_KEY_TOKEN) == S_OK)
+ {
+ // If we're accessing an internal type across AppDomains, we'll end up saying that an assembly is
+ // not allowed to access internal types in itself, since the Assembly *'s will not compare equal.
+ // This ends up being confusing for users who don't have a deep understanding of the loader and type
+ // system, and also creates different behavior if your assembly is shared vs unshared (if you are
+ // shared, your Assembly *'s will match since they're in the shared domain).
+ //
+ // In order to ease the confusion, we'll consider assemblies to be friends of themselves in this
+ // scenario -- if a name and public key match succeeds, we'll grant internal access across domains.
+ return TRUE;
+ }
+#endif // FEATURE_REMOTING && !CROSSGEN_COMPILE
+ else if (pOptionalTargetField != NULL)
+ {
+ return pTargetAssembly->GrantsFriendAccessTo(pAccessingAssembly, pOptionalTargetField);
+ }
+ else if (pOptionalTargetMethod != NULL)
+ {
+ return pTargetAssembly->GrantsFriendAccessTo(pAccessingAssembly, pOptionalTargetMethod);
+ }
+ else
+ {
+ return pTargetAssembly->GrantsFriendAccessTo(pAccessingAssembly, pOptionalTargetType);
+ }
+}
+
+//******************************************************************************
+// This function determines whether a target class is accessible from
+// some given class.
+/* static */
+BOOL ClassLoader::CanAccessMethodInstantiation( // True if access is legal, false otherwise.
+ AccessCheckContext* pContext,
+ MethodDesc* pOptionalTargetMethod, // The desired method; if NULL, return TRUE (or)
+ const AccessCheckOptions & accessCheckOptions)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ }
+ CONTRACTL_END
+
+ // If there is no target method just allow access.
+ // NB: the caller may just be checking access to a field or class, so we allow for NULL.
+ if (!pOptionalTargetMethod)
+ return TRUE;
+
+ // Is the desired target an instantiated generic method?
+ if (pOptionalTargetMethod->HasMethodInstantiation())
+ { // check that the current class has access
+ // to all of the instantiating classes.
+ Instantiation inst = pOptionalTargetMethod->GetMethodInstantiation();
+ for (DWORD i = 0; i < inst.GetNumArgs(); i++)
+ {
+ TypeHandle th = inst[i];
+
+ MethodTable* pMT = th.GetMethodTableOfElementType();
+
+ // Either a TypeVarTypeDesc or a FnPtrTypeDesc. No access check needed.
+ if (pMT == NULL)
+ continue;
+
+ if (!CanAccessClass(
+ pContext,
+ pMT,
+ th.GetAssembly(),
+ accessCheckOptions))
+ {
+ return FALSE;
+ }
+ }
+ // If we are here, the current class has access to all of the target's instantiating args,
+ }
+ return TRUE;
+}
+
+//******************************************************************************
+// This function determines whether a target class is accessible from
+// some given class.
+// CanAccessClass does the following checks:
+// 1. Transparency check on the target class
+// 2. Recursively calls CanAccessClass on the generic arguments of the target class if it is generic.
+// 3. Visibility check on the target class, if the target class is nested, this will be translated
+// to a member access check on the enclosing type (calling CanAccess with appropriate dwProtection.
+//
+/* static */
+BOOL ClassLoader::CanAccessClass( // True if access is legal, false otherwise.
+ AccessCheckContext* pContext, // The caller context
+ MethodTable* pTargetClass, // The desired target class.
+ Assembly* pTargetAssembly, // Assembly containing the target class.
+ const AccessCheckOptions & accessCheckOptions,
+ BOOL checkTargetTypeTransparency)// = TRUE
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ PRECONDITION(CheckPointer(pTargetClass));
+ }
+ CONTRACTL_END
+
+ // If there is no target class, allow access.
+ // @todo: what does that mean?
+ //if (!pTargetClass)
+ // return TRUE;
+
+ // check transparent/critical on type
+ // Note that dwMemberAccess is of no use here since we don't have a target method yet. It really should be made an optional arg.
+ // For now, we pass in mdPublic.
+ if (checkTargetTypeTransparency && accessCheckOptions.TransparencyCheckNeeded())
+ {
+ if (!CheckTransparentAccessToCriticalCode(
+ pContext,
+ mdPublic,
+ pTargetClass,
+ NULL,
+ NULL,
+ pTargetClass,
+ accessCheckOptions))
+ {
+ // no need to call accessCheckOptions.DemandMemberAccessOrFail here because
+ // CheckTransparentAccessToCriticalCode does that already
+ return FALSE;
+ }
+ }
+
+ // Step 2: Recursively call CanAccessClass on the generic type arguments
+ // Is the desired target a generic instantiation?
+ if (pTargetClass->HasInstantiation())
+ { // Yes, so before going any further, check that the current class has access
+ // to all of the instantiating classes.
+ Instantiation inst = pTargetClass->GetInstantiation();
+ for (DWORD i = 0; i < inst.GetNumArgs(); i++)
+ {
+ TypeHandle th = inst[i];
+
+ MethodTable* pMT = th.GetMethodTableOfElementType();
+
+ // Either a TypeVarTypeDesc or a FnPtrTypeDesc. No access check needed.
+ if (pMT == NULL)
+ continue;
+
+ if (!CanAccessClass(
+ pContext,
+ pMT,
+ th.GetAssembly(),
+ accessCheckOptions,
+ checkTargetTypeTransparency))
+ {
+ // no need to call accessCheckOptions.DemandMemberAccessOrFail here because the base case in
+ // CanAccessClass does that already
+ return FALSE;
+ }
+ }
+ // If we are here, the current class has access to all of the desired target's instantiating args.
+ // Now, check whether the current class has access to the desired target itself.
+ }
+
+ // Step 3: Visibility Check
+ if (!pTargetClass->GetClass()->IsNested())
+ { // a non-nested class can be either all public or accessible only from its own assembly (and friends).
+ if (IsTdPublic(pTargetClass->GetClass()->GetProtection()))
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Always allow interop callers full access.
+ if (pContext->IsCalledFromInterop())
+ return TRUE;
+
+ Assembly* pCurrentAssembly = pContext->GetCallerAssembly();
+ _ASSERTE(pCurrentAssembly != NULL);
+
+ if (AssemblyOrFriendAccessAllowed(pCurrentAssembly,
+ pTargetAssembly,
+ NULL,
+ NULL,
+ pTargetClass))
+ {
+ return TRUE;
+ }
+ else
+ {
+ return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetClass, TRUE /*visibilityCheck*/);
+ }
+ }
+ }
+
+ // If we are here, the desired target class is nested. Translate the type flags
+ // to corresponding method access flags. We need to make a note if friend access was allowed to the
+ // type being checked since we're not passing it directly to the recurisve call to CanAccess, and
+ // instead are just passing in the dwProtectionFlags.
+ DWORD dwProtection = pTargetClass->GetClass()->GetProtection();
+
+ switch(dwProtection) {
+ case tdNestedPublic:
+ dwProtection = mdPublic;
+ break;
+ case tdNestedFamily:
+ dwProtection = mdFamily;
+ break;
+ case tdNestedPrivate:
+ dwProtection = mdPrivate;
+ break;
+ case tdNestedFamORAssem:
+ // If we can access the class because we have assembly or friend access, we have satisfied the
+ // FamORAssem accessibility, so we we can simplify it down to public. Otherwise we require that
+ // family access be allowed to grant access.
+ case tdNestedFamANDAssem:
+ // If we don't grant assembly or friend access to the target class, then there is no way we
+ // could satisfy the FamANDAssem requirement. Otherwise, since we have satsified the Assm
+ // portion, we only need to check for the Fam portion.
+ case tdNestedAssembly:
+ // If we don't grant assembly or friend access to the target class, and that class has assembly
+ // protection, we can fail the request now. Otherwise we can check to make sure a public member
+ // of the outer class is allowed, since we have satisfied the target's accessibility rules.
+
+ // Always allow interop callers full access.
+ if (pContext->IsCalledFromInterop())
+ return TRUE;
+
+ if (AssemblyOrFriendAccessAllowed(pContext->GetCallerAssembly(), pTargetAssembly, NULL, NULL, pTargetClass))
+ dwProtection = (dwProtection == tdNestedFamANDAssem) ? mdFamily : mdPublic;
+ else if (dwProtection == tdNestedFamORAssem)
+ dwProtection = mdFamily;
+ else
+ return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetClass, TRUE /*visibilityCheck*/);
+
+ break;
+
+ default:
+ THROW_BAD_FORMAT_MAYBE(!"Unexpected class visibility flag value", BFA_BAD_VISIBILITY, pTargetClass);
+ }
+
+ // The desired target class is nested, so translate the class access request into
+ // a member access request. That is, if the current class is trying to access A::B,
+ // check if it can access things in A with the visibility of B.
+ // So, pass A as the desired target class and visibility of B within A as the member access
+ // We've already done transparency check above. No need to do it again.
+ return ClassLoader::CanAccess(
+ pContext,
+ GetEnclosingMethodTable(pTargetClass),
+ pTargetAssembly,
+ dwProtection,
+ NULL,
+ NULL,
+ accessCheckOptions,
+ FALSE,
+ FALSE);
+} // BOOL ClassLoader::CanAccessClass()
+
+//******************************************************************************
+// This is a front-end to CheckAccessMember that handles the nested class scope. If can't access
+// from the current point and are a nested class, then try from the enclosing class.
+// It does two things in addition to CanAccessMember:
+// 1. If the caller class doesn't have access to the caller, see if the enclosing class does.
+// 2. CanAccessMemberForExtraChecks which checks whether the caller class has access to
+// the signature of the target method or field.
+//
+// checkTargetMethodTransparency is set to FALSE only when the check is for JIT-compilation
+// because the JIT has a mechanism to insert a callout for the case where
+// we need to perform the currentMD <-> TargetMD check at runtime.
+
+/* static */
+BOOL ClassLoader::CanAccess( // TRUE if access is allowed, FALSE otherwise.
+ AccessCheckContext* pContext, // The caller context
+ MethodTable* pTargetMT, // The class containing the desired target member.
+ Assembly* pTargetAssembly, // Assembly containing that class.
+ DWORD dwMemberAccess, // Member access flags of the desired target member (as method bits).
+ MethodDesc* pOptionalTargetMethod, // The target method; NULL if the target is a not a method or
+ // there is no need to check the method's instantiation.
+ FieldDesc* pOptionalTargetField, // or The desired field; if NULL, return TRUE
+ const AccessCheckOptions & accessCheckOptions, // = s_NormalAccessChecks
+ BOOL checkTargetMethodTransparency, // = TRUE
+ BOOL checkTargetTypeTransparency) // = TRUE
+{
+ CONTRACT(BOOL)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ PRECONDITION(CheckPointer(pContext));
+ MODE_ANY;
+ }
+ CONTRACT_END;
+
+ // Recursive: CanAccess->CheckAccessMember->CanAccessClass->CanAccess
+ INTERIOR_STACK_PROBE(GetThread());
+
+ AccessCheckOptions accessCheckOptionsNoThrow(accessCheckOptions, FALSE);
+
+ if (!CheckAccessMember(pContext,
+ pTargetMT,
+ pTargetAssembly,
+ dwMemberAccess,
+ pOptionalTargetMethod,
+ pOptionalTargetField,
+ // Suppress exceptions for nested classes since this is not a hard-failure,
+ // and we can do additional checks
+ accessCheckOptionsNoThrow,
+ checkTargetMethodTransparency,
+ checkTargetTypeTransparency))
+ {
+ // If we're here, CheckAccessMember didn't allow access.
+ BOOL canAccess = FALSE;
+
+ // If the current class is nested, there may be an enclosing class that might have access
+ // to the target. And if the pCurrentMT == NULL, the current class is global, and so there
+ // is no enclosing class.
+ MethodTable* pCurrentMT = pContext->GetCallerMT();
+
+ // if this is called from interop, the CheckAccessMember call above should have already succeeded.
+ _ASSERTE(!pContext->IsCalledFromInterop());
+
+ BOOL isNestedClass = (pCurrentMT && pCurrentMT->GetClass()->IsNested());
+
+ if (isNestedClass)
+ {
+ // A nested class also has access to anything that the enclosing class does, so
+ // recursively check whether the enclosing class can access the desired target member.
+ MethodTable * pEnclosingMT = GetEnclosingMethodTable(pCurrentMT);
+
+ StaticAccessCheckContext accessContext(pContext->GetCallerMethod(),
+ pEnclosingMT,
+ pContext->GetCallerAssembly());
+
+ // On failure, do not throw from inside this call since that will cause the exception message
+ // to refer to the enclosing type.
+ canAccess = ClassLoader::CanAccess(
+ &accessContext,
+ pTargetMT,
+ pTargetAssembly,
+ dwMemberAccess,
+ pOptionalTargetMethod,
+ pOptionalTargetField,
+ accessCheckOptionsNoThrow,
+ checkTargetMethodTransparency,
+ checkTargetTypeTransparency);
+ }
+
+ if (!canAccess)
+ {
+ BOOL fail = accessCheckOptions.FailOrThrow(pContext);
+ RETURN_FROM_INTERIOR_PROBE(fail);
+ }
+ }
+
+ // For member access, we do additional checks to ensure that the specific member can
+ // be accessed
+
+ if (!CanAccessMemberForExtraChecks(
+ pContext,
+ pTargetMT,
+ pOptionalTargetMethod,
+ pOptionalTargetField,
+ accessCheckOptions,
+ checkTargetMethodTransparency))
+ {
+ RETURN_FROM_INTERIOR_PROBE(FALSE);
+ }
+
+ RETURN_FROM_INTERIOR_PROBE(TRUE);
+
+ END_INTERIOR_STACK_PROBE;
+} // BOOL ClassLoader::CanAccess()
+
+//******************************************************************************
+// Performs additional checks for member access
+
+BOOL ClassLoader::CanAccessMemberForExtraChecks(
+ AccessCheckContext* pContext,
+ MethodTable* pTargetExactMT,
+ MethodDesc* pOptionalTargetMethod,
+ FieldDesc* pOptionalTargetField,
+ const AccessCheckOptions & accessCheckOptions,
+ BOOL checkTargetMethodTransparency)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ }
+ CONTRACTL_END;
+
+ // Critical callers do not need the extra checks
+ // This early-out saves the cost of all the subsequent work
+ if (pContext->IsCallerCritical())
+ {
+ return TRUE;
+ }
+
+ if (pOptionalTargetMethod == NULL && pOptionalTargetField == NULL)
+ return TRUE;
+
+ _ASSERTE((pOptionalTargetMethod == NULL) != (pOptionalTargetField == NULL));
+
+ // We should always do checks on member signatures. But for backward compatibility we skip this check
+ // for critical callers. And since we don't want to look for the caller here which might incur a stack walk,
+ // we delay the check to DemandMemberAccessOrFail time.
+ AccessCheckOptions legacyAccessCheckOptions(accessCheckOptions, accessCheckOptions.Throws(), TRUE);
+
+ if (pOptionalTargetMethod)
+ {
+ // A method is accessible only if all the types in the signature
+ // are also accessible.
+ if (!CanAccessSigForExtraChecks(pContext,
+ pOptionalTargetMethod,
+ pTargetExactMT,
+ legacyAccessCheckOptions,
+ checkTargetMethodTransparency))
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ _ASSERTE(pOptionalTargetField != NULL);
+
+ // A field is accessible only if the field type is also accessible
+
+ TypeHandle fieldType = pOptionalTargetField->GetExactFieldType(TypeHandle(pTargetExactMT));
+ CorElementType fieldCorType = fieldType.GetSignatureCorElementType();
+
+ MethodTable * pFieldTypeMT = fieldType.GetMethodTableOfElementType();
+
+ // No access check needed on a generic variable or a function pointer
+ if (pFieldTypeMT != NULL)
+ {
+ if (!CanAccessClassForExtraChecks(pContext,
+ pFieldTypeMT,
+ pFieldTypeMT->GetAssembly(),
+ legacyAccessCheckOptions,
+ TRUE))
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//******************************************************************************
+// Can all the types in the signature of the pTargetMethodSig be accessed?
+//
+// "ForExtraChecks" means that we only do extra checks (security and transparency)
+// instead of the usual loader visibility checks. Post V2, we can enable all checks.
+
+BOOL ClassLoader::CanAccessSigForExtraChecks( // TRUE if access is allowed, FALSE otherwise.
+ AccessCheckContext* pContext,
+ MethodDesc* pTargetMethodSig, // The target method. If this is a shared method, pTargetExactMT gives
+ // additional information about the exact type
+ MethodTable* pTargetExactMT, // or The desired field; if NULL, return TRUE
+ const AccessCheckOptions & accessCheckOptions,
+ BOOL checkTargetTransparency)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ }
+ CONTRACTL_END;
+
+ MetaSig sig(pTargetMethodSig, TypeHandle(pTargetExactMT));
+
+ // First, check the return type
+
+ TypeHandle retType = sig.GetRetTypeHandleThrowing();
+ MethodTable * pRetMT = retType.GetMethodTableOfElementType();
+
+ // No access check needed on a generic variable or a function pointer
+ if (pRetMT != NULL)
+ {
+ if (!CanAccessClassForExtraChecks(pContext,
+ pRetMT,
+ retType.GetAssembly(),
+ accessCheckOptions,
+ checkTargetTransparency))
+ {
+ return FALSE;
+ }
+ }
+
+ //
+ // Now walk all the arguments in the signature
+ //
+
+ for (CorElementType argType = sig.NextArg(); argType != ELEMENT_TYPE_END; argType = sig.NextArg())
+ {
+ TypeHandle thArg = sig.GetLastTypeHandleThrowing();
+
+ MethodTable * pArgMT = thArg.GetMethodTableOfElementType();
+
+ // Either a TypeVarTypeDesc or a FnPtrTypeDesc. No access check needed.
+ if (pArgMT == NULL)
+ continue;
+
+ BOOL canAcesssElement = CanAccessClassForExtraChecks(
+ pContext,
+ pArgMT,
+ thArg.GetAssembly(),
+ accessCheckOptions,
+ checkTargetTransparency);
+ if (!canAcesssElement)
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//******************************************************************************
+// Can the type be accessed?
+//
+// "ForExtraChecks" means that we only do extra checks (security and transparency)
+// instead of the usual loader visibility checks. Post V2, we can enable all checks.
+
+BOOL ClassLoader::CanAccessClassForExtraChecks( // True if access is legal, false otherwise.
+ AccessCheckContext* pContext,
+ MethodTable* pTargetClass, // The desired target class.
+ Assembly* pTargetAssembly, // Assembly containing that class.
+ const AccessCheckOptions & accessCheckOptions,
+ BOOL checkTargetTypeTransparency)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pContext));
+ }
+ CONTRACTL_END;
+
+ // ------------- Old comments begins ------------
+ // Critical callers do not need the extra checks
+ // TODO: can we enable full access checks now?
+ // ------------- Old comments ends ------------
+
+ // We shouldn't bypass accessibility check on member signature for FT/Critical callers
+
+ return CanAccessClass(pContext,
+ pTargetClass,
+ pTargetAssembly,
+ accessCheckOptions,
+ checkTargetTypeTransparency);
+}
+
+//******************************************************************************
+// This is the helper function for the corresponding CanAccess()
+// It does the following checks:
+// 1. CanAccessClass on pTargetMT
+// 2. CanAccessMethodInstantiation if the pOptionalTargetMethod is provided and is generic.
+// 3. Transparency check on pTargetMT, pOptionalTargetMethod and pOptionalTargetField.
+// 4. Visibility check on dwMemberAccess (on pTargetMT)
+
+/* static */
+BOOL ClassLoader::CheckAccessMember( // TRUE if access is allowed, false otherwise.
+ AccessCheckContext* pContext,
+ MethodTable* pTargetMT, // The class containing the desired target member.
+ Assembly* pTargetAssembly, // Assembly containing that class.
+ DWORD dwMemberAccess, // Member access flags of the desired target member (as method bits).
+ MethodDesc* pOptionalTargetMethod, // The target method; NULL if the target is a not a method or
+ // there is no need to check the method's instantiation.
+ FieldDesc* pOptionalTargetField, // target field, NULL if there is no Target field
+ const AccessCheckOptions & accessCheckOptions,
+ BOOL checkTargetMethodTransparency,
+ BOOL checkTargetTypeTransparency
+ )
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ PRECONDITION(CheckPointer(pContext));
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ // we're trying to access a member that is contained in the class pTargetClass, so need to
+ // check if have access to pTargetClass itself from the current point before worry about
+ // having access to the member within the class
+ if (!CanAccessClass(pContext,
+ pTargetMT,
+ pTargetAssembly,
+ accessCheckOptions,
+ checkTargetTypeTransparency))
+ {
+ return FALSE;
+ }
+
+ // If we are trying to access a generic method, we have to ensure its instantiation is accessible.
+ // Note that we need to perform transparency checks on the instantiation even if we have
+ // checkTargetMethodTransparency set to false, since generic type parameters by design do not effect
+ // the transparency of the generic method that is closing over them. This means standard transparency
+ // checks between caller and closed callee may succeed even if the callee's closure includes a critical type.
+ if (!CanAccessMethodInstantiation(
+ pContext,
+ pOptionalTargetMethod,
+ accessCheckOptions))
+ {
+ return FALSE;
+ }
+
+ // pOptionalTargetMethod and pOptionalTargetField can never be NULL at the same time.
+ _ASSERTE(pOptionalTargetMethod == NULL || pOptionalTargetField == NULL);
+
+ // Perform transparency checks
+ // We don't need to do transparency check against pTargetMT here because
+ // it was already done in CanAccessClass above.
+
+ if (accessCheckOptions.TransparencyCheckNeeded() &&
+ (checkTargetMethodTransparency && pOptionalTargetMethod ||
+ pOptionalTargetField))
+ {
+ if (!CheckTransparentAccessToCriticalCode(
+ pContext,
+ dwMemberAccess,
+ pTargetMT,
+ pOptionalTargetMethod,
+ pOptionalTargetField,
+ NULL,
+ accessCheckOptions))
+ {
+ return FALSE;
+ }
+ }
+
+ if (IsMdPublic(dwMemberAccess))
+ {
+ return TRUE;
+ }
+
+ // Always allow interop callers full access.
+ if (pContext->IsCalledFromInterop())
+ return TRUE;
+
+ MethodTable* pCurrentMT = pContext->GetCallerMT();
+
+ if (IsMdPrivateScope(dwMemberAccess))
+ {
+ if (pCurrentMT != NULL && pCurrentMT->GetModule() == pTargetMT->GetModule())
+ {
+ return TRUE;
+ }
+ else
+ {
+ return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, TRUE /*visibilityCheck*/);
+ }
+ }
+
+
+#ifdef _DEBUG
+ if (pTargetMT == NULL &&
+ (IsMdFamORAssem(dwMemberAccess) ||
+ IsMdFamANDAssem(dwMemberAccess) ||
+ IsMdFamily(dwMemberAccess))) {
+ THROW_BAD_FORMAT_MAYBE(!"Family flag is not allowed on global functions", BFA_FAMILY_ON_GLOBAL, pTargetMT);
+ }
+#endif
+
+ if (pTargetMT == NULL ||
+ IsMdAssem(dwMemberAccess) ||
+ IsMdFamORAssem(dwMemberAccess) ||
+ IsMdFamANDAssem(dwMemberAccess))
+ {
+ // If the member has Assembly accessibility, grant access if the current
+ // class is in the same assembly as the desired target member, or if the
+ // desired target member's assembly grants friend access to the current
+ // assembly.
+ // @todo: What does it mean for the target class to be NULL?
+
+ Assembly* pCurrentAssembly = pContext->GetCallerAssembly();
+
+ // pCurrentAssembly should never be NULL, unless we are called from interop,
+ // in which case we should have already returned TRUE.
+ _ASSERTE(pCurrentAssembly != NULL);
+
+ const BOOL fAssemblyOrFriendAccessAllowed = AssemblyOrFriendAccessAllowed(pCurrentAssembly,
+ pTargetAssembly,
+ pOptionalTargetField,
+ pOptionalTargetMethod,
+ pTargetMT);
+
+ if ((pTargetMT == NULL || IsMdAssem(dwMemberAccess) || IsMdFamORAssem(dwMemberAccess)) &&
+ fAssemblyOrFriendAccessAllowed)
+ {
+ return TRUE;
+ }
+ else if (IsMdFamANDAssem(dwMemberAccess) &&
+ !fAssemblyOrFriendAccessAllowed)
+ {
+ return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, TRUE /*visibilityCheck*/);
+ }
+ }
+
+ // Nested classes can access all members of the parent class.
+ while(pCurrentMT != NULL)
+ {
+ //@GENERICSVER:
+ if (pTargetMT->HasSameTypeDefAs(pCurrentMT))
+ return TRUE;
+
+ if (IsMdPrivate(dwMemberAccess))
+ {
+ if (!pCurrentMT->GetClass()->IsNested())
+ {
+ return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, TRUE /*visibilityCheck*/);
+ }
+ }
+ else if (IsMdFamORAssem(dwMemberAccess) || IsMdFamily(dwMemberAccess) || IsMdFamANDAssem(dwMemberAccess))
+ {
+ if (CanAccessFamily(pCurrentMT, pTargetMT))
+ {
+ return TRUE;
+ }
+ }
+
+ pCurrentMT = GetEnclosingMethodTable(pCurrentMT);
+ }
+
+ return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, TRUE /*visibilityCheck*/);
+}
+
+// The family check is actually in two parts (Partition I, 8.5.3.2). The first part:
+//
+// ...accessible to referents that support the same type
+// (i.e., an exact type and all of the types that inherit
+// from it).
+//
+// Translation: pCurrentClass must be the same type as pTargetClass or a derived class. (i.e. Derived
+// can access Base.protected but Unrelated cannot access Base.protected).
+//
+// The second part:
+//
+// For verifiable code (see §8.8), there is an additional
+// requirement that can require a runtime check: the reference
+// shall be made through an item whose exact type supports
+// the exact type of the referent. That is, the item whose
+// member is being accessed shall inherit from the type
+// performing the access.
+//
+// Translation: The C++ protected rule. For those unfamiliar, it means that:
+// if you have:
+// GrandChild : Child
+// and
+// Child : Parent
+// and
+// Parent {
+// protected:
+// int protectedField;
+// }
+//
+// Child::function(GrandChild * o) {
+// o->protectedField; //This access is legal.
+// }
+//
+// GrandChild:function2(Child * o) {
+// o->protectedField; //This access is illegal.
+// }
+//
+// The reason for this rule is that if you had:
+// Sibling : Parent
+//
+// Child::function3( Sibling * o ) {
+// o->protectedField; //This access is illegal
+// }
+//
+// This is intuitively correct. However, you need to prevent:
+// Child::function4( Sibling * o ) {
+// ((Parent*)o)->protectedField;
+// }
+//
+// Which means that you must access protected fields through a type that is yourself or one of your
+// derived types.
+
+//This checks the first part of the rule above.
+/* static */
+BOOL ClassLoader::CanAccessFamily(
+ MethodTable *pCurrentClass,
+ MethodTable *pTargetClass)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pTargetClass));
+ }
+ CONTRACTL_END
+
+ _ASSERTE(pCurrentClass);
+ _ASSERTE(pTargetClass);
+
+ //Look to see if Current is a child of the Target.
+ while (pCurrentClass) {
+ MethodTable *pCurInstance = pCurrentClass;
+
+ while (pCurInstance) {
+ //This is correct. csc is incredibly lax about generics. Essentially if you are a subclass of
+ //any type of generic it lets you access it. Since the standard is totally unclear, mirror that
+ //behavior here.
+ if (pCurInstance->HasSameTypeDefAs(pTargetClass)) {
+ return TRUE;
+ }
+
+ pCurInstance = pCurInstance->GetParentMethodTable();
+ }
+
+ ///Looking at 8.5.3, it looks like a protected member of a nested class in a parent type is also
+ //accessible.
+ pCurrentClass = GetEnclosingMethodTable(pCurrentClass);
+ }
+
+ return FALSE;
+}
+
+//If instance is an inner class, this also succeeds if the outer class conforms to 8.5.3.2. A nested class
+//is enclosed inside of the enclosing class' open type. So we need to ignore generic variables. That also
+//helps us with:
+/*
+class Base {
+ protected int m_family;
+}
+class Derived<T> : Base {
+ class Inner {
+ public int function(Derived<T> d) {
+ return d.m_family;
+ }
+ }
+}
+*/
+
+//Since the inner T is not the same T as the enclosing T (since accessing generic variables is a CLS rule,
+//not a CLI rule), we see that as a comparison between Derived<T> and Derived<T'>. CanCastTo rejects that.
+//Instead we just check against the typedef of the two types. This ignores all generic parameters (formal
+//or not).
+
+BOOL CanAccessFamilyVerificationEnclosingHelper(MethodTable * pMTCurrentEnclosingClass,
+ TypeHandle thInstanceClass)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ _ASSERTE(pMTCurrentEnclosingClass);
+
+ if (thInstanceClass.IsGenericVariable())
+ {
+ //In this case it is a TypeVarTypeDesc (i.e. T). If this access would be legal due to a
+ //constraint:
+ //
+ /*
+ public class My<T>
+ {
+ public class Inner<U> where U : My<T>
+ {
+ public int foo(U u)
+ {
+ return u.field;
+ }
+ }
+ protected int field;
+ }
+ */
+ //We need to find the generic class constraint. (The above is legal because U must be a My<T> which makes this
+ //legal by 8.5.3.2)
+ // There may only be 1 class constraint on a generic parameter
+
+ // Get the constraints on this generic variable
+ // At most 1 of them is a class constraint.
+ // That class constraint methodtable can go through the normal search for matching typedef logic below
+ TypeVarTypeDesc *tyvar = thInstanceClass.AsGenericVariable();
+ DWORD numConstraints;
+ TypeHandle *constraints = tyvar->GetConstraints(&numConstraints, CLASS_DEPENDENCIES_LOADED);
+ if (constraints == NULL)
+ {
+ // If we did not find a class constraint, we cannot generate a methodtable to search for
+ return FALSE;
+ }
+ else
+ {
+ for (DWORD i = 0; i < numConstraints; i++)
+ {
+ if (!constraints[i].IsInterface())
+ {
+ // We have found the class constraint on this TypeVarTypeDesc
+ // Recurse on the found class constraint. It is possible that this constraint may also be a TypeVarTypeDesc
+//class Outer4<T>
+//{
+// protected int field;
+//
+// public class Inner<U,V> where V:U where U : Outer4<T>
+// {
+// public int Method(V param) { return (++param.field); }
+// }
+//}
+ return CanAccessFamilyVerificationEnclosingHelper(pMTCurrentEnclosingClass, constraints[i]);
+ }
+ }
+ // If we did not find a class constraint, we cannot generate a methodtable to search for
+ return FALSE;
+ }
+ }
+ do
+ {
+ MethodTable * pAccessor = pMTCurrentEnclosingClass;
+ //If thInstanceClass is a MethodTable, we should only be doing the TypeDef comparison (see
+ //above).
+ if (!thInstanceClass.IsTypeDesc())
+ {
+ MethodTable *pInstanceMT = thInstanceClass.AsMethodTable();
+
+ // This is a CanCastTo implementation for classes, assuming we should ignore generic instantiation parameters.
+ do
+ {
+ if (pAccessor->HasSameTypeDefAs(pInstanceMT))
+ return TRUE;
+ pInstanceMT = pInstanceMT->GetParentMethodTable();
+ }while(pInstanceMT);
+ }
+ else
+ {
+ // Leave this logic in place for now, as I'm not fully confident it can't happen, and we are very close to RTM
+ // This logic was originally written to handle TypeVarTypeDescs, but those are now handled above.
+ _ASSERTE(FALSE);
+ if (thInstanceClass.CanCastTo(TypeHandle(pAccessor)))
+ return TRUE;
+ }
+
+ pMTCurrentEnclosingClass = GetEnclosingMethodTable(pMTCurrentEnclosingClass);
+ }while(pMTCurrentEnclosingClass);
+ return FALSE;
+}
+
+
+//This checks the verification only part of the rule above.
+//From the example above:
+// GrandChild::function2(Child * o) {
+// o->protectedField; //This access is illegal.
+// }
+// pCurrentClass is GrandChild and pTargetClass is Child. This check is completely unnecessary for statics,
+// but by legacy convention you can use GrandChild for pTargetClass in that case.
+
+BOOL ClassLoader::CanAccessFamilyVerification(TypeHandle thCurrentClass,
+ TypeHandle thInstanceClass)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ MODE_ANY;
+ PRECONDITION(!thCurrentClass.IsNull());
+ PRECONDITION(!thCurrentClass.IsTypeDesc());
+ }
+ CONTRACTL_END
+
+ //Check to see if Instance is equal to or derived from pCurrentClass.
+ //
+ //In some cases the type we have for the instance type is actually a TypeVarTypeDesc. In those cases we
+ //need to check against the constraints (You're accessing a member through a 'T' with a type constraint
+ //that makes this legal). For those cases, CanCastTo does what I want.
+ MethodTable * pAccessor = thCurrentClass.GetMethodTable();
+ if (thInstanceClass.CanCastTo(TypeHandle(pAccessor)))
+ return TRUE;
+
+ //ArrayTypeDescs are the only typedescs that have methods, and their methods don't have IL. All other
+ //TypeDescs don't need to be here. So only run this on MethodTables.
+ if (!thInstanceClass.IsNull())
+ {
+ return CanAccessFamilyVerificationEnclosingHelper(pAccessor, thInstanceClass);
+ }
+ return FALSE;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+#ifdef DACCESS_COMPILE
+
+void
+ClassLoader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+ DAC_ENUM_DTHIS();
+
+ EMEM_OUT(("MEM: %p ClassLoader\n", dac_cast<TADDR>(this)));
+
+ if (m_pAssembly.IsValid())
+ {
+ ModuleIterator modIter = GetAssembly()->IterateModules();
+
+ while (modIter.Next())
+ {
+ modIter.GetModule()->EnumMemoryRegions(flags, true);
+ }
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE