text: // Licensed to the .NET Foundation under one or more agreements.
  // The .NET Foundation licenses this file to you under the MIT license.
  // See the LICENSE file in the project root for more information.
  
  /*============================================================
  **
  ** Header: Assembly.cpp
  **
  **
  ** Purpose: Implements assembly (loader domain) architecture
  **
  **
  ===========================================================*/
  
  #include "common.h"
  
  #include <stdlib.h>
  
  #include "assembly.hpp"
  #include "appdomain.hpp"
  #include "perfcounters.h"
  #include "assemblyname.hpp"
  
  
  
  #include "eeprofinterfaces.h"
  #include "reflectclasswriter.h"
  #include "comdynamic.h"
  
  #include <wincrypt.h>
  #include "urlmon.h"
  #include "sha1.h"
  
  #include "eeconfig.h"
  #include "strongname.h"
  
  #include "ceefilegenwriter.h"
  #include "assemblynative.hpp"
  #include "threadsuspend.h"
  
  #ifdef FEATURE_PREJIT
  #include "corcompile.h"
  #endif
  
  #include "appdomainnative.hpp"
  #include "customattribute.h"
  #include "winnls.h"
  
  #include "caparser.h"
  #include "../md/compiler/custattr.h"
  #include "mdaassistants.h"
  
  #include "peimagelayout.inl"
  
  
  // Define these macro's to do strict validation for jit lock and class init entry leaks.
  // This defines determine if the asserts that verify for these leaks are defined or not.
  // These asserts can sometimes go off even if no entries have been leaked so this defines
  // should be used with caution.
  //
  // If we are inside a .cctor when the application shut's down then the class init lock's
  // head will be set and this will cause the assert to go off.,
  //
  // If we are jitting a method when the application shut's down then the jit lock's head
  // will be set causing the assert to go off.
  
  //#define STRICT_JITLOCK_ENTRY_LEAK_DETECTION
  //#define STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
  
  
  #ifndef DACCESS_COMPILE
  
  // This value is to make it easier to diagnose Assembly Loader "grant set" crashes.
  // See Dev11 bug 358184 for more details.
  
  // This value is not thread safe and is not intended to be. It is just a best
  // effort to collect more data on the problem. Is is possible, though unlikely,
  // that thread A would record a reason for an upcoming crash,
  // thread B would then record a different reason, and we would then
  // crash on thread A, thus ending up with the recorded reason not matching
  // the thread we crash in. Be aware of this when using this value
  // to help your debugging.
  DWORD g_dwLoaderReasonForNotSharing = 0; // See code:DomainFile::m_dwReasonForRejectingNativeImage for a similar variable.
  
  // These will sometimes result in a crash with error code 0x80131401 SECURITY_E_INCOMPATIBLE_SHARE
  // "Loading this assembly would produce a different grant set from other instances."
  enum ReasonForNotSharing
  {
      ReasonForNotSharing_NoInfoRecorded = 0x1,
      ReasonForNotSharing_NullDomainassembly = 0x2,
      ReasonForNotSharing_DebuggerFlagMismatch = 0x3,
      ReasonForNotSharing_NullPeassembly = 0x4,
      ReasonForNotSharing_MissingAssemblyClosure1 = 0x5,
      ReasonForNotSharing_MissingAssemblyClosure2 = 0x6,
      ReasonForNotSharing_MissingDependenciesResolved = 0x7,
      ReasonForNotSharing_ClosureComparisonFailed = 0x8,
  };
  
  #define NO_FRIEND_ASSEMBLIES_MARKER ((FriendAssemblyDescriptor *)S_FALSE)
  
  //----------------------------------------------------------------------------------------------
  // The ctor's job is to initialize the Assembly enough so that the dtor can safely run.
  // It cannot do any allocations or operations that might fail. Those operations should be done
  // in Assembly::Init()
  //----------------------------------------------------------------------------------------------
  Assembly::Assembly(BaseDomain *pDomain, PEAssembly* pFile, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible) :
      m_pDomain(pDomain),
      m_pClassLoader(NULL),
      m_pEntryPoint(NULL),
      m_pManifest(NULL),
      m_pManifestFile(clr::SafeAddRef(pFile)),
      m_pFriendAssemblyDescriptor(NULL),
      m_isDynamic(false),
  #ifdef FEATURE_COLLECTIBLE_TYPES
      m_isCollectible(fIsCollectible),
  #endif
      m_nextAvailableModuleIndex(1),
      m_pLoaderAllocator(NULL),
      m_isDisabledPrivateReflection(0),
  #ifdef FEATURE_COMINTEROP
      m_winMDStatus(WinMDStatus_Unknown),
      m_pManifestWinMDImport(NULL),
  #endif // FEATURE_COMINTEROP
      m_debuggerFlags(debuggerFlags),
      m_fTerminated(FALSE)
  #ifdef FEATURE_COMINTEROP
      , m_InteropAttributeStatus(INTEROP_ATTRIBUTE_UNSET)
  #endif
  #ifdef FEATURE_PREJIT
      , m_isInstrumentedStatus(IS_INSTRUMENTED_UNSET)
  #endif
  {
      STANDARD_VM_CONTRACT;
  }
  
  // This name needs to stay in sync with AssemblyBuilder.ManifestModuleName
  // which is used in AssemblyBuilder.InitManifestModule
  #define REFEMIT_MANIFEST_MODULE_NAME W("RefEmit_InMemoryManifestModule")
  
  
  //----------------------------------------------------------------------------------------------
  // Does most Assembly initialization tasks. It can assume the ctor has already run
  // and the assembly is safely destructable. Whether this function throws or succeeds,
  // it must leave the Assembly in a safely destructable state.
  //----------------------------------------------------------------------------------------------
  void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator)
  {
      STANDARD_VM_CONTRACT;
  
      if (IsSystem())
      {
          _ASSERTE(pLoaderAllocator == NULL); // pLoaderAllocator may only be non-null for collectible types
          m_pLoaderAllocator = SystemDomain::GetGlobalLoaderAllocator();
      }
      else
      {
          if (!IsDomainNeutral())
          {
              if (!IsCollectible())
              {
                  // pLoaderAllocator will only be non-null for reflection emit assemblies
                  _ASSERTE((pLoaderAllocator == NULL) || (pLoaderAllocator == GetDomain()->AsAppDomain()->GetLoaderAllocator()));
                  m_pLoaderAllocator = GetDomain()->AsAppDomain()->GetLoaderAllocator();
              }
              else
              {
                  _ASSERTE(pLoaderAllocator != NULL); // ppLoaderAllocator must be non-null for collectible assemblies
  
                  m_pLoaderAllocator = pLoaderAllocator;
              }
          }
          else
          {
              _ASSERTE(pLoaderAllocator == NULL); // pLoaderAllocator may only be non-null for collectible types
              // use global loader heaps
              m_pLoaderAllocator = SystemDomain::GetGlobalLoaderAllocator();
          }
      }
      _ASSERTE(m_pLoaderAllocator != NULL);
  
      m_pClassLoader = new ClassLoader(this);
      m_pClassLoader->Init(pamTracker);
  
      COUNTER_ONLY(GetPerfCounters().m_Loading.cAssemblies++);
  
  #ifndef CROSSGEN_COMPILE
      if (GetManifestFile()->IsDynamic())
          // manifest modules of dynamic assemblies are always transient
          m_pManifest = ReflectionModule::Create(this, GetManifestFile(), pamTracker, REFEMIT_MANIFEST_MODULE_NAME, TRUE);
      else
  #endif
          m_pManifest = Module::Create(this, mdFileNil, GetManifestFile(), pamTracker);
  
      PrepareModuleForAssembly(m_pManifest, pamTracker);
  
      CacheManifestFiles();
  
      if (!m_pManifest->IsReadyToRun())
          CacheManifestExportedTypes(pamTracker);
  
  
      // Check for the assemblies that contain SIMD Vector types.
      // If we encounter a non-trusted assembly with these names, we will simply not recognize any of its
      // methods as intrinsics.
      LPCUTF8 assemblyName = GetSimpleName();
      const int length = sizeof("System.Numerics") - 1;
      if ((strncmp(assemblyName, "System.Numerics", length) == 0) &&
          ((assemblyName[length] == '\0') || (strcmp(assemblyName+length, ".Vectors") == 0)))
      {
          m_fIsSIMDVectorAssembly = true;
      }
      else
      {
          m_fIsSIMDVectorAssembly = false;
      }
  
      // We'll load the friend assembly information lazily.  For the ngen case we should avoid
      //  loading it entirely.
      //CacheFriendAssemblyInfo();
  
      {
          CANNOTTHROWCOMPLUSEXCEPTION();
          FAULT_FORBID();
          //Cannot fail after this point.
  
          PublishModuleIntoAssembly(m_pManifest);
  
          return;  // Explicit return to let you know you are NOT welcome to add code after the CANNOTTHROW/FAULT_FORBID expires
      }
  }
  
  BOOL Assembly::IsDisabledPrivateReflection()
  {
      CONTRACTL
      {
          THROWS;
      }
      CONTRACTL_END;
  
      enum { UNINITIALIZED, ENABLED, DISABLED};
  
      if (m_isDisabledPrivateReflection == UNINITIALIZED)
      {
          IMDInternalImport *pImport = GetManifestImport();
          HRESULT hr = pImport->GetCustomAttributeByName(GetManifestToken(), DISABLED_PRIVATE_REFLECTION_TYPE, NULL, 0);
          IfFailThrow(hr);
  
          if (hr == S_OK)
          {
              m_isDisabledPrivateReflection = DISABLED;
          }
          else
          {
              m_isDisabledPrivateReflection = ENABLED;
          }
      }
  
      return m_isDisabledPrivateReflection == DISABLED;
  }
  
  #ifndef CROSSGEN_COMPILE
  Assembly::~Assembly()
  {
      CONTRACTL
      {
          NOTHROW;
          GC_TRIGGERS;
          DISABLED(FORBID_FAULT); //Must clean up some profiler stuff
      }
      CONTRACTL_END
  
      Terminate();
  
      if (m_pFriendAssemblyDescriptor != NULL && m_pFriendAssemblyDescriptor != NO_FRIEND_ASSEMBLIES_MARKER)
          delete m_pFriendAssemblyDescriptor;
  
      if (m_pManifestFile)
      {
          m_pManifestFile->Release();
      }
  
  #ifdef FEATURE_COMINTEROP
      if (m_pManifestWinMDImport)
      {
          m_pManifestWinMDImport->Release();
      }
  #endif // FEATURE_COMINTEROP
  }
  
  #ifdef  FEATURE_PREJIT
  void Assembly::DeleteNativeCodeRanges()
  {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          MODE_PREEMPTIVE;
          FORBID_FAULT;
      }
      CONTRACTL_END
  
      ModuleIterator i = IterateModules();
      while (i.Next()) 
              i.GetModule()->DeleteNativeCodeRanges();
  }
  #endif
  
  #ifdef PROFILING_SUPPORTED
  void ProfilerCallAssemblyUnloadStarted(Assembly* assemblyUnloaded)
  {
      WRAPPER_NO_CONTRACT;
      {
          BEGIN_PIN_PROFILER(CORProfilerPresent());
          GCX_PREEMP();
          g_profControlBlock.pProfInterface->AssemblyUnloadStarted((AssemblyID)assemblyUnloaded);
          END_PIN_PROFILER();
      }
  }
  
  void ProfilerCallAssemblyUnloadFinished(Assembly* assemblyUnloaded)
  {
      WRAPPER_NO_CONTRACT;
      {
          BEGIN_PIN_PROFILER(CORProfilerPresent());
          GCX_PREEMP();
          g_profControlBlock.pProfInterface->AssemblyUnloadFinished((AssemblyID) assemblyUnloaded, S_OK);
          END_PIN_PROFILER();
      }
  }
  #endif
  
  void Assembly::StartUnload()
  {
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_GC_TRIGGERS;
      STATIC_CONTRACT_FORBID_FAULT;
  #ifdef PROFILING_SUPPORTED
      if (CORProfilerTrackAssemblyLoads())
      {
          ProfilerCallAssemblyUnloadStarted(this);
      }
  #endif
  }
  
  void Assembly::Terminate( BOOL signalProfiler )
  {
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_GC_TRIGGERS;
  
      STRESS_LOG1(LF_LOADER, LL_INFO100, "Assembly::Terminate (this = 0x%p)\n", reinterpret_cast<void *>(this));
  
      if (this->m_fTerminated)
          return;
  
      if (m_pClassLoader != NULL)
      {
          GCX_PREEMP();
          delete m_pClassLoader;
          m_pClassLoader = NULL;
      }
  
      COUNTER_ONLY(GetPerfCounters().m_Loading.cAssemblies--);
  
  #ifdef PROFILING_SUPPORTED
      if (CORProfilerTrackAssemblyLoads())
      {
          ProfilerCallAssemblyUnloadFinished(this);
      }    
  #endif // PROFILING_SUPPORTED
  
      this->m_fTerminated = TRUE;
  }
  #endif // CROSSGEN_COMPILE
  
  Assembly * Assembly::Create(
      BaseDomain *                 pDomain, 
      PEAssembly *                 pFile, 
      DebuggerAssemblyControlFlags debuggerFlags, 
      BOOL                         fIsCollectible, 
      AllocMemTracker *            pamTracker, 
      LoaderAllocator *            pLoaderAllocator)
  {
      STANDARD_VM_CONTRACT;
  
      NewHolder<Assembly> pAssembly (new Assembly(pDomain, pFile, debuggerFlags, fIsCollectible));
  
      // If there are problems that arise from this call stack, we'll chew up a lot of stack
      // with the various EX_TRY/EX_HOOKs that we will encounter.
      INTERIOR_STACK_PROBE_FOR(GetThread(), DEFAULT_ENTRY_PROBE_SIZE); 
  #ifdef PROFILING_SUPPORTED
      {
          BEGIN_PIN_PROFILER(CORProfilerTrackAssemblyLoads());
          GCX_COOP();
          g_profControlBlock.pProfInterface->AssemblyLoadStarted((AssemblyID)(Assembly *) pAssembly);
          END_PIN_PROFILER();
      }
  
      // Need TRY/HOOK instead of holder so we can get HR of exception thrown for profiler callback
      EX_TRY
  #endif    
      {
          pAssembly->Init(pamTracker, pLoaderAllocator);
      }
  #ifdef PROFILING_SUPPORTED
      EX_HOOK
      {
          {
              BEGIN_PIN_PROFILER(CORProfilerTrackAssemblyLoads());
              GCX_COOP();
              g_profControlBlock.pProfInterface->AssemblyLoadFinished((AssemblyID)(Assembly *) pAssembly,
                                                                      GET_EXCEPTION()->GetHR());
              END_PIN_PROFILER();
          }
      }
      EX_END_HOOK;
  #endif
      pAssembly.SuppressRelease();
      END_INTERIOR_STACK_PROBE;
      
      return pAssembly;
  } // Assembly::Create
  
  
  #ifndef CROSSGEN_COMPILE
  Assembly *Assembly::CreateDynamic(AppDomain *pDomain, CreateDynamicAssemblyArgs *args)
  {
      // WARNING: not backout clean
      CONTRACT(Assembly *)
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
          MODE_COOPERATIVE;
          PRECONDITION(CheckPointer(args));
      }
      CONTRACT_END;
      
      // This must be before creation of the AllocMemTracker so that the destructor for the AllocMemTracker happens before the destructor for pLoaderAllocator.
      // That is necessary as the allocation of Assembly objects and other related details is done on top of heaps located in
      // the loader allocator objects.
      NewHolder<LoaderAllocator> pLoaderAllocator;
  
      AllocMemTracker amTracker;
      AllocMemTracker *pamTracker = &amTracker;
      
      Assembly *pRetVal = NULL;
      
      AppDomain  *pCallersDomain;
      MethodDesc *pmdEmitter = SystemDomain::GetCallersMethod(args->stackMark, &pCallersDomain);
  
      // Called either from interop or async delegate invocation. Rejecting because we don't
      // know how to set the correct permission on the new dynamic assembly.
      if (!pmdEmitter)
          COMPlusThrow(kInvalidOperationException);
  
      Assembly   *pCallerAssembly = pmdEmitter->GetAssembly();
      
      // First, we set up a pseudo-manifest file for the assembly.
      
      // Set up the assembly name
      
      STRINGREF strRefName = (STRINGREF) args->assemblyName->GetSimpleName();
      
      if (strRefName == NULL)
          COMPlusThrow(kArgumentException, W("ArgumentNull_AssemblyNameName"));
      
      StackSString name;
      strRefName->GetSString(name);
      
      if (name.GetCount() == 0)
          COMPlusThrow(kArgumentException, W("ArgumentNull_AssemblyNameName"));
      
      SString::Iterator i = name.Begin();
      if (COMCharacter::nativeIsWhiteSpace(*i)
          || name.Find(i, '\\')
          || name.Find(i, ':')
          || name.Find(i, '/'))
      {
          COMPlusThrow(kArgumentException, W("Argument_InvalidAssemblyName"));
      }
      
      // Set up the assembly manifest metadata
      // When we create dynamic assembly, we always use a working copy of IMetaDataAssemblyEmit
      // to store temporary runtime assembly information. This is to preserve the invariant that
      // an assembly must have a PEFile with proper metadata.
      // This working copy of IMetaDataAssemblyEmit will store every AssemblyRef as a simple name
      // reference as we must have an instance of Assembly(can be dynamic assembly) before we can
      // add such a reference. Also because the referenced assembly if dynamic strong name, it may
      // not be ready to be hashed!
      
      SafeComHolder<IMetaDataAssemblyEmit> pAssemblyEmit;
      PEFile::DefineEmitScope(
          IID_IMetaDataAssemblyEmit, 
          &pAssemblyEmit);
      
      // remember the hash algorithm
      ULONG ulHashAlgId = args->assemblyName->GetAssemblyHashAlgorithm();
      if (ulHashAlgId == 0)
          ulHashAlgId = CALG_SHA1;
      
      ASSEMBLYMETADATA assemData;
      memset(&assemData, 0, sizeof(assemData));
      
      // get the version info (default to 0.0.0.0 if none)
      VERSIONREF versionRef = (VERSIONREF) args->assemblyName->GetVersion();
      if (versionRef != NULL)
      {
          assemData.usMajorVersion = (USHORT)versionRef->GetMajor();
          assemData.usMinorVersion = (USHORT)versionRef->GetMinor();
          assemData.usBuildNumber = (USHORT)versionRef->GetBuild();
          assemData.usRevisionNumber = (USHORT)versionRef->GetRevision();
      }
      
      struct _gc
      {
          OBJECTREF cultureinfo;
          STRINGREF pString;
          OBJECTREF orArrayOrContainer;
          OBJECTREF throwable;
          OBJECTREF strongNameKeyPair;
      } gc;
      ZeroMemory(&gc, sizeof(gc));
      
      GCPROTECT_BEGIN(gc);
      
      StackSString culture;
      
      gc.cultureinfo = args->assemblyName->GetCultureInfo();
      if (gc.cultureinfo != NULL)
      {
          MethodDescCallSite getName(METHOD__CULTURE_INFO__GET_NAME, &gc.cultureinfo);
          
          ARG_SLOT args2[] = 
          {
              ObjToArgSlot(gc.cultureinfo)
          };
          
          // convert culture info into a managed string form
          gc.pString = getName.Call_RetSTRINGREF(args2);
          gc.pString->GetSString(culture);
          
          assemData.szLocale = (LPWSTR) (LPCWSTR) culture;
      }
      
      SBuffer publicKey;
      if (args->assemblyName->GetPublicKey() != NULL)
      {
          publicKey.Set(args->assemblyName->GetPublicKey()->GetDataPtr(),
                        args->assemblyName->GetPublicKey()->GetNumComponents());
      }
      
  
      // get flags
      DWORD dwFlags = args->assemblyName->GetFlags();
      
      // Now create a dynamic PE file out of the name & metadata
      PEAssemblyHolder pFile;
  
      {
          GCX_PREEMP();
  
          mdAssembly ma;
          IfFailThrow(pAssemblyEmit->DefineAssembly(publicKey, publicKey.GetSize(), ulHashAlgId,
                                                     name, &assemData, dwFlags,
                                                     &ma));
          pFile = PEAssembly::Create(pCallerAssembly->GetManifestFile(), pAssemblyEmit);
  
          // Dynamically created modules (aka RefEmit assemblies) do not have a LoadContext associated with them since they are not bound
          // using an actual binder. As a result, we will assume the same binding/loadcontext information for the dynamic assembly as its
          // caller/creator to ensure that any assembly loads triggered by the dynamic assembly are resolved using the intended load context.
          //
          // If the creator assembly has a HostAssembly associated with it, then use it for binding. Otherwise, the creator is dynamic
          // and will have a fallback load context binder associated with it.
          ICLRPrivBinder *pFallbackLoadContextBinder = nullptr;
          
          // There is always a manifest file - wehther working with static or dynamic assemblies.
          PEFile *pCallerAssemblyManifestFile = pCallerAssembly->GetManifestFile();
          _ASSERTE(pCallerAssemblyManifestFile != NULL);
  
          if (!pCallerAssemblyManifestFile->IsDynamic())
          {
              // Static assemblies with do not have fallback load context
              _ASSERTE(pCallerAssemblyManifestFile->GetFallbackLoadContextBinder() == nullptr);
  
              if (pCallerAssemblyManifestFile->IsSystem())
              {
                  // CoreLibrary is always bound to TPA binder
                  pFallbackLoadContextBinder = pDomain->GetTPABinderContext();
              }
              else
              {
                  // Fetch the binder from the host assembly
                  PTR_ICLRPrivAssembly pCallerAssemblyHostAssembly = pCallerAssemblyManifestFile->GetHostAssembly();
                  _ASSERTE(pCallerAssemblyHostAssembly != nullptr);
  
                  UINT_PTR assemblyBinderID = 0;
                  IfFailThrow(pCallerAssemblyHostAssembly->GetBinderID(&assemblyBinderID));
                  pFallbackLoadContextBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
              }
          }
          else
          {
              // Creator assembly is dynamic too, so use its fallback load context for the one
              // we are creating.
              pFallbackLoadContextBinder = pCallerAssemblyManifestFile->GetFallbackLoadContextBinder(); 
          }
  
          // At this point, we should have a fallback load context binder to work with
          _ASSERTE(pFallbackLoadContextBinder != nullptr);
  
          // Set it as the fallback load context binder for the dynamic assembly being created
          pFile->SetFallbackLoadContextBinder(pFallbackLoadContextBinder);
      }
  
      NewHolder<DomainAssembly> pDomainAssembly;
  
      {
          GCX_PREEMP();
  
          // Create a new LoaderAllocator if appropriate
          if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0)
          {
              AssemblyLoaderAllocator *pAssemblyLoaderAllocator = new AssemblyLoaderAllocator();
              pAssemblyLoaderAllocator->SetCollectible();
              pLoaderAllocator = pAssemblyLoaderAllocator;
  
              // Some of the initialization functions are not virtual. Call through the derived class
              // to prevent calling the base class version.
              pAssemblyLoaderAllocator->Init(pDomain);
  
              // Setup the managed proxy now, but do not actually transfer ownership to it.
              // Once everything is setup and nothing can fail anymore, the ownership will be
              // atomically transfered by call to LoaderAllocator::ActivateManagedTracking().
              pAssemblyLoaderAllocator->SetupManagedTracking(&args->loaderAllocator);
          }
          else
          {
              pLoaderAllocator = pDomain->GetLoaderAllocator();
              pLoaderAllocator.SuppressRelease();
          }
  
          // Create a domain assembly
          pDomainAssembly = new DomainAssembly(pDomain, pFile, pLoaderAllocator);
          if (pDomainAssembly->IsCollectible())
          {
              // We add the assembly to the LoaderAllocator only when we are sure that it can be added
              // and won't be deleted in case of a concurrent load from the same ALC
              ((AssemblyLoaderAllocator *)(LoaderAllocator *)pLoaderAllocator)->AddDomainAssembly(pDomainAssembly);
          }
      }
  
      // Start loading process
      {
          // Create a concrete assembly
          // (!Do not remove scoping brace: order is important here: the Assembly holder must destruct before the AllocMemTracker!)
          NewHolder<Assembly> pAssem;
          
          {
              GCX_PREEMP();
              // Assembly::Create will call SuppressRelease on the NewHolder that holds the LoaderAllocator when it transfers ownership
              pAssem = Assembly::Create(pDomain, pFile, pDomainAssembly->GetDebuggerInfoBits(), args->access & ASSEMBLY_ACCESS_COLLECT ? TRUE : FALSE, pamTracker, pLoaderAllocator);
              
              ReflectionModule* pModule = (ReflectionModule*) pAssem->GetManifestModule();
              pModule->SetCreatingAssembly( pCallerAssembly );
  
  
              if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0)
              {
                  // Initializing the virtual call stub manager is delayed to remove the need for the LoaderAllocator destructor to properly handle
                  // uninitializing the VSD system. (There is a need to suspend the runtime, and that's tricky)
                  pLoaderAllocator->InitVirtualCallStubManager(pDomain);
              }
          }
  
          pAssem->m_isDynamic = true;
  
          if (publicKey.GetSize() > 0)
          {
              // Since we have no way to validate the public key of a dynamic assembly we don't allow 
              // partial trust code to emit a dynamic assembly with an arbitrary public key.
              // Ideally we shouldn't allow anyone to emit a dynamic assembly with only a public key,
              // but we allow a couple of exceptions to reduce the compat risk: full trust, caller's own key.
              // As usual we treat anonymously hosted dynamic methods as partial trust code.
              DomainAssembly* pCallerDomainAssembly = pCallerAssembly->GetDomainAssembly(pCallersDomain);
              if (pCallerDomainAssembly == pCallersDomain->GetAnonymouslyHostedDynamicMethodsAssembly())
              {
                  DWORD cbKey = 0;
                  const void* pKey = pCallerAssembly->GetPublicKey(&cbKey);
  
                  if (!publicKey.Equals((const BYTE *)pKey, cbKey))
                      COMPlusThrow(kInvalidOperationException, W("InvalidOperation_StrongNameKeyPairRequired"));
              }
          }
  
          //we need to suppress release for pAssem to avoid double release
          pAssem.SuppressRelease ();
  
          {
              GCX_PREEMP();
  
              // Finish loading process
              // <TODO> would be REALLY nice to unify this with main loading loop </TODO>
              pDomainAssembly->Begin();
              pDomainAssembly->SetAssembly(pAssem);
              pDomainAssembly->m_level = FILE_LOAD_ALLOCATE;
              pDomainAssembly->DeliverSyncEvents();
              pDomainAssembly->DeliverAsyncEvents();
              pDomainAssembly->FinishLoad();
              pDomainAssembly->ClearLoading();
              pDomainAssembly->m_level = FILE_ACTIVE;
          }
  
          {
              CANNOTTHROWCOMPLUSEXCEPTION();
              FAULT_FORBID();
  
              //Cannot fail after this point
  
              pDomainAssembly.SuppressRelease(); // This also effectively suppresses the release of the pAssem 
              pamTracker->SuppressRelease();
  
              // Once we reach this point, the loader allocator lifetime is controlled by the Assembly object.
              if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0)
              {
                  // Atomically transfer ownership to the managed heap
                  pLoaderAllocator->ActivateManagedTracking();
                  pLoaderAllocator.SuppressRelease();
              }
  
              pAssem->SetIsTenured();
              pRetVal = pAssem;
          }
      }
      GCPROTECT_END();
  
      RETURN pRetVal;
  } // Assembly::CreateDynamic
  
  
  #endif // CROSSGEN_COMPILE
  
  void Assembly::SetDomainAssembly(DomainAssembly *pDomainAssembly)
  {
      CONTRACTL
      {
          PRECONDITION(CheckPointer(pDomainAssembly));
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
      }
      CONTRACTL_END;
  
      GetManifestModule()->SetDomainFile(pDomainAssembly);
  
  } // Assembly::SetDomainAssembly
  
  #endif // #ifndef DACCESS_COMPILE
  
  DomainAssembly *Assembly::GetDomainAssembly(AppDomain *pDomain)
  {
      CONTRACT(DomainAssembly *)
      {
          PRECONDITION(CheckPointer(pDomain, NULL_NOT_OK));
          POSTCONDITION(CheckPointer(RETVAL));
          THROWS;
          GC_TRIGGERS;
      }
      CONTRACT_END;
  
      RETURN GetManifestModule()->GetDomainAssembly(pDomain);
  }
  
  DomainAssembly *Assembly::FindDomainAssembly(AppDomain *pDomain)
  {
      CONTRACT(DomainAssembly *)
      {
          PRECONDITION(CheckPointer(pDomain));
          POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
          NOTHROW;
          GC_NOTRIGGER;
          FORBID_FAULT;
          SO_TOLERANT;
          SUPPORTS_DAC;
      }
      CONTRACT_END;
  
      PREFIX_ASSUME (GetManifestModule() !=NULL); 
      RETURN GetManifestModule()->FindDomainAssembly(pDomain);
  }
  
  PTR_LoaderHeap Assembly::GetLowFrequencyHeap()
  {
      WRAPPER_NO_CONTRACT;
  
      return GetLoaderAllocator()->GetLowFrequencyHeap();
  }
  
  PTR_LoaderHeap Assembly::GetHighFrequencyHeap()
  {
      WRAPPER_NO_CONTRACT;
  
      return GetLoaderAllocator()->GetHighFrequencyHeap();
  }
  
  
  PTR_LoaderHeap Assembly::GetStubHeap()
  {
      WRAPPER_NO_CONTRACT;
  
      return GetLoaderAllocator()->GetStubHeap();
  }
  
  
  PTR_BaseDomain Assembly::GetDomain()
  {
      LIMITED_METHOD_CONTRACT;
      SUPPORTS_DAC;
  
      _ASSERTE(m_pDomain);
      return (m_pDomain);
  }
  
  #ifndef DACCESS_COMPILE
  
  void Assembly::SetParent(BaseDomain* pParent)
  {
      LIMITED_METHOD_CONTRACT;
  
      m_pDomain = pParent;
  }
  
  #endif // !DACCCESS_COMPILE
  
  mdFile Assembly::GetManifestFileToken(LPCSTR name)
  {
  
      return mdFileNil;
  }
  
  mdFile Assembly::GetManifestFileToken(IMDInternalImport *pImport, mdFile kFile)
  {
      WRAPPER_NO_CONTRACT;
      SUPPORTS_DAC;
  
      LPCSTR name;
      if ((TypeFromToken(kFile) != mdtFile) || 
          !pImport->IsValidToken(kFile))
      {
          BAD_FORMAT_NOTHROW_ASSERT(!"Invalid File token");
          return mdTokenNil;
      }
      
      if (FAILED(pImport->GetFileProps(kFile, &name, NULL, NULL, NULL)))
      {
          BAD_FORMAT_NOTHROW_ASSERT(!"Invalid File token");
          return mdTokenNil;
      }
      
      return GetManifestFileToken(name);
  }
  
  Module *Assembly::FindModuleByExportedType(mdExportedType mdType,
                                             Loader::LoadFlag loadFlag,
                                             mdTypeDef mdNested,
                                             mdTypeDef* pCL)
  {
      CONTRACT(Module *)
      {
          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;
          POSTCONDITION(CheckPointer(RETVAL, loadFlag==Loader::Load ? NULL_NOT_OK : NULL_OK));
          SUPPORTS_DAC;
      }
      CONTRACT_END
      
      mdToken mdLinkRef;
      mdToken mdBinding;
      
      IMDInternalImport *pManifestImport = GetManifestImport();
      
      IfFailThrow(pManifestImport->GetExportedTypeProps(
          mdType, 
          NULL, 
          NULL, 
          &mdLinkRef,     // Impl
          &mdBinding,     // Hint
          NULL));         // dwflags
      
      // Don't trust the returned tokens.
      if (!pManifestImport->IsValidToken(mdLinkRef))
      {
          if (loadFlag != Loader::Load)
          {
              RETURN NULL;
          }
          else
          {
              ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN);
          }
      }
      
      switch(TypeFromToken(mdLinkRef)) {
      case mdtAssemblyRef:
          {
              *pCL = mdTypeDefNil;  // We don't trust the mdBinding token
  
              Assembly *pAssembly = NULL;
              switch(loadFlag) 
              {
                  case Loader::Load:
                  {
  #ifndef DACCESS_COMPILE
                      // LoadAssembly never returns NULL
                      DomainAssembly * pDomainAssembly =
                          GetManifestModule()->LoadAssembly(::GetAppDomain(), mdLinkRef);
                      PREFIX_ASSUME(pDomainAssembly != NULL);
  
                      RETURN pDomainAssembly->GetCurrentModule();
  #else
                      _ASSERTE(!"DAC shouldn't attempt to trigger loading");
                      return NULL;
  #endif // !DACCESS_COMPILE
                  };
                  case Loader::DontLoad: 
                      pAssembly = GetManifestModule()->GetAssemblyIfLoaded(mdLinkRef);
                      break;
                  case Loader::SafeLookup:
                      pAssembly = GetManifestModule()->LookupAssemblyRef(mdLinkRef);
                      break;
                  default:
                      _ASSERTE(FALSE);
              }  
              
              if (pAssembly)
                  RETURN pAssembly->GetManifestModule();
              else
                  RETURN NULL;
  
          }
  
      case mdtFile:
          {
              // We may not want to trust this TypeDef token, since it
              // was saved in a scope other than the one it was defined in
              if (mdNested == mdTypeDefNil)
                  *pCL = mdBinding;
              else
                  *pCL = mdNested;
  
              // Note that we don't want to attempt a LoadModule if a GetModuleIfLoaded will
              // succeed, because it has a stronger contract.
              Module *pModule = GetManifestModule()->GetModuleIfLoaded(mdLinkRef, TRUE, FALSE);
  #ifdef DACCESS_COMPILE
              return pModule;
  #else
              if (pModule != NULL)
                  RETURN pModule;
  
              if(loadFlag==Loader::SafeLookup)
                  return NULL;
  
              // We should never get here in the GC case - the above should have succeeded.
              CONSISTENCY_CHECK(!FORBIDGC_LOADER_USE_ENABLED());
  
              DomainFile * pDomainModule = GetManifestModule()->LoadModule(::GetAppDomain(), mdLinkRef, FALSE, loadFlag!=Loader::Load);
  
              if (pDomainModule == NULL)
                  RETURN NULL;
              else
              {
                  pModule = pDomainModule->GetCurrentModule();
                  if (pModule == NULL)
                  {
                      _ASSERTE(loadFlag!=Loader::Load);
                  }
  
                  RETURN pModule;
              }
  #endif // DACCESS_COMPILE
          }
  
      case mdtExportedType:
          // Only override the nested type token if it hasn't been set yet.
          if (mdNested != mdTypeDefNil)
              mdBinding = mdNested;
  
          RETURN FindModuleByExportedType(mdLinkRef, loadFlag, mdBinding, pCL);
  
      default:
          ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE);
      }
  } // Assembly::FindModuleByExportedType
  
  
  // The returned Module is non-NULL unless you prevented the load by setting loadFlag=Loader::DontLoad.
  /* static */
  Module * Assembly::FindModuleByTypeRef(
      Module *         pModule, 
      mdTypeRef        tkType,
      Loader::LoadFlag loadFlag, 
      BOOL *           pfNoResolutionScope)
  {
      CONTRACT(Module *)
      {
          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(pModule));
          PRECONDITION(TypeFromToken(tkType) == mdtTypeRef);
          PRECONDITION(CheckPointer(pfNoResolutionScope));
          POSTCONDITION( CheckPointer(RETVAL, loadFlag==Loader::Load ? NULL_NOT_OK : NULL_OK) );
          SUPPORTS_DAC;
      }
      CONTRACT_END
  
      // WARNING! Correctness of the type forwarder detection algorithm in code:ClassLoader::ResolveTokenToTypeDefThrowing
      // relies on this function not performing any form of type forwarding itself.
  
      IMDInternalImport * pImport;
      mdTypeRef           tkTopLevelEncloserTypeRef;
  
      pImport = pModule->GetMDImport();
      if (TypeFromToken(tkType) != mdtTypeRef)
      {
          ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE);
      }
      
      {
          // Find the top level encloser
          GCX_NOTRIGGER();
          
          // If nested, get top level encloser's impl
          int iter = 0;
          int maxIter = 1000;
          do
          {
              _ASSERTE(TypeFromToken(tkType) == mdtTypeRef);
              tkTopLevelEncloserTypeRef = tkType;
              
              if (!pImport->IsValidToken(tkType) || iter >= maxIter)
              {
                  break;
              }
              
              IfFailThrow(pImport->GetResolutionScopeOfTypeRef(tkType, &tkType));
              
              // nil-scope TR okay if there's an ExportedType
              // Return manifest file
              if (IsNilToken(tkType))
              {
                  *pfNoResolutionScope = TRUE;
                  RETURN(pModule);
              }
              iter++;
          }
          while (TypeFromToken(tkType) == mdtTypeRef);
      }
      
      *pfNoResolutionScope = FALSE;
      
  #ifndef DACCESS_COMPILE
      if (!pImport->IsValidToken(tkType)) // redundant check only when invalid token already found.
      {
          THROW_BAD_FORMAT(BFA_BAD_TYPEREF_TOKEN, pModule);
      }
  #endif //!DACCESS_COMPILE
  
      switch (TypeFromToken(tkType))
      {
          case mdtModule:
          {
              // Type is in the referencing module.
              GCX_NOTRIGGER();
              CANNOTTHROWCOMPLUSEXCEPTION();
              RETURN( pModule );
          }
  
          case mdtModuleRef:
          {
              if ((loadFlag != Loader::Load) || IsGCThread() || IsStackWalkerThread())
              {
                  // Either we're not supposed to load, or we're doing a GC or stackwalk
                  // in which case we shouldn't need to load.  So just look up the module
                  // and return what we find.
                  RETURN(pModule->LookupModule(tkType,FALSE));
              }
              
  #ifndef DACCESS_COMPILE
              DomainFile * pActualDomainFile = pModule->LoadModule(::GetAppDomain(), tkType, FALSE, loadFlag!=Loader::Load);
              if (pActualDomainFile == NULL)
              {
                  RETURN NULL;
              }
              else
              {
                  RETURN(pActualDomainFile->GetModule());
              }
  
  #else //DACCESS_COMPILE
              _ASSERTE(loadFlag!=Loader::Load);
              DacNotImpl();
              RETURN NULL;
  #endif //DACCESS_COMPILE
          }
          break;
  
          case mdtAssemblyRef:
          {
              // Do this first because it has a strong contract
              Assembly * pAssembly = NULL;
              
  #if defined(FEATURE_COMINTEROP) || !defined(DACCESS_COMPILE)
              LPCUTF8 szNamespace = NULL;
              LPCUTF8 szClassName = NULL;
  #endif
              
  #ifdef FEATURE_COMINTEROP
              if (pModule->HasBindableIdentity(tkType))
  #endif// FEATURE_COMINTEROP
              {
                  if (loadFlag == Loader::SafeLookup)
                  {
                      pAssembly = pModule->LookupAssemblyRef(tkType);
                  }
                  else
                  {
                      pAssembly = pModule->GetAssemblyIfLoaded(tkType);
                  }
              }
  #ifdef FEATURE_COMINTEROP
              else
              {
                  _ASSERTE(IsAfContentType_WindowsRuntime(pModule->GetAssemblyRefFlags(tkType)));
                  
                  if (FAILED(pImport->GetNameOfTypeRef(
                      tkTopLevelEncloserTypeRef, 
                      &szNamespace, 
                      &szClassName)))
                  {
                      THROW_BAD_FORMAT(BFA_BAD_TYPEREF_TOKEN, pModule);
                  }
                  
                  pAssembly = pModule->GetAssemblyIfLoaded(
                          tkType, 
                          szNamespace, 
                          szClassName, 
                          NULL);  // pMDImportOverride                
              }
  #endif // FEATURE_COMINTEROP
              
              if (pAssembly != NULL)
              {
                  RETURN pAssembly->m_pManifest;
              }
  
  #ifdef DACCESS_COMPILE
              RETURN NULL;
  #else
              if (loadFlag != Loader::Load)
              {
                  RETURN NULL;
              }
  
              
              DomainAssembly * pDomainAssembly = pModule->LoadAssembly(
                      ::GetAppDomain(), 
                      tkType, 
                      szNamespace, 
                      szClassName);
  
  
              if (pDomainAssembly == NULL)
                  RETURN NULL;
              
              pAssembly = pDomainAssembly->GetCurrentAssembly();
              if (pAssembly == NULL)
              {
                  RETURN NULL;
              }
              else
              {
                  RETURN pAssembly->m_pManifest;
              }
  #endif //!DACCESS_COMPILE
          }
  
      default:
          ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE);
      }
  } // Assembly::FindModuleByTypeRef
  
  #ifndef DACCESS_COMPILE
  
  Module *Assembly::FindModuleByName(LPCSTR pszModuleName)
  {
      CONTRACT(Module *)
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
          MODE_ANY;
          POSTCONDITION(CheckPointer(RETVAL));
      }
      CONTRACT_END;
  
      CQuickBytes qbLC;
  
      // Need to perform case insensitive hashing.
      UTF8_TO_LOWER_CASE(pszModuleName, qbLC);
      pszModuleName = (LPUTF8) qbLC.Ptr();
  
      mdFile kFile = GetManifestFileToken(pszModuleName);
      if (kFile == mdTokenNil)
          ThrowHR(COR_E_UNAUTHORIZEDACCESS);
  
      if (this == SystemDomain::SystemAssembly())
          RETURN m_pManifest->GetModuleIfLoaded(kFile, TRUE, TRUE);
      else
          RETURN m_pManifest->LoadModule(::GetAppDomain(), kFile)->GetModule();
  }
  
  void Assembly::CacheManifestExportedTypes(AllocMemTracker *pamTracker)
  {
      CONTRACT_VOID
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
      }
      CONTRACT_END;
  
      // Prejitted assemblies are expected to have their table prebuilt.
      // If not, we do it here at load time (as if we would jit the assembly).
  
      if (m_pManifest->IsPersistedObject(m_pManifest->m_pAvailableClasses))
          RETURN;
  
      mdToken mdExportedType;
  
      HENUMInternalHolder phEnum(GetManifestImport());
      phEnum.EnumInit(mdtExportedType,
                      mdTokenNil);
  
      ClassLoader::AvailableClasses_LockHolder lh(m_pClassLoader);
  
      for(int i = 0; GetManifestImport()->EnumNext(&phEnum, &mdExportedType); i++)
          m_pClassLoader->AddExportedTypeHaveLock(GetManifestModule(),
                                                  mdExportedType,
                                                  pamTracker);
  
      RETURN;
  }
  void Assembly::CacheManifestFiles()
  {
  }
  
  
  //<TODO>@TODO: if module is not signed it needs to acquire the
  //permissions from the assembly.</TODO>
  void Assembly::PrepareModuleForAssembly(Module* module, AllocMemTracker *pamTracker)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
          PRECONDITION(CheckPointer(module));
      }
      CONTRACTL_END;
      
      if (module->m_pAvailableClasses != NULL && !module->IsPersistedObject(module->m_pAvailableClasses)) 
      {
          // ! We intentionally do not take the AvailableClass lock here. It creates problems at
          // startup and we haven't yet published the module yet so nobody should be searching it.
          m_pClassLoader->PopulateAvailableClassHashTable(module, pamTracker);
      }
  
  
  #ifdef DEBUGGING_SUPPORTED
      // Modules take the DebuggerAssemblyControlFlags down from its
      // parent Assembly initially.
      module->SetDebuggerInfoBits(GetDebuggerInfoBits());
  
      LOG((LF_CORDB, LL_INFO10, "Module %s: bits=0x%x\n",
           module->GetFile()->GetSimpleName(),
           module->GetDebuggerInfoBits()));
  #endif // DEBUGGING_SUPPORTED
  
      m_pManifest->EnsureFileCanBeStored(module->GetModuleRef());
  }
  
  // This is the final step of publishing a Module into an Assembly. This step cannot fail.
  void Assembly::PublishModuleIntoAssembly(Module *module)
  {
      CONTRACTL
      {
          NOTHROW;
          GC_TRIGGERS;
          FORBID_FAULT;
      }
      CONTRACTL_END
  
      GetManifestModule()->EnsuredStoreFile(module->GetModuleRef(), module);
      FastInterlockIncrement((LONG*)&m_pClassLoader->m_cUnhashedModules);
  }
  
  
  
  
  
  //*****************************************************************************
  // Set up the list of names of any friend assemblies
  void Assembly::CacheFriendAssemblyInfo()
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
      }
      CONTRACTL_END
  
      if (m_pFriendAssemblyDescriptor == NULL)
      {
          FriendAssemblyDescriptor *pFriendAssemblies = FriendAssemblyDescriptor::CreateFriendAssemblyDescriptor(this->GetManifestFile());
          if (pFriendAssemblies == NULL)
          {
              pFriendAssemblies = NO_FRIEND_ASSEMBLIES_MARKER;
          }
  
          void *pvPreviousDescriptor = InterlockedCompareExchangeT(&m_pFriendAssemblyDescriptor,
                                                                   pFriendAssemblies,
                                                                   NULL);
  
          if (pvPreviousDescriptor != NULL && pFriendAssemblies != NO_FRIEND_ASSEMBLIES_MARKER)
          {
              if (pFriendAssemblies != NO_FRIEND_ASSEMBLIES_MARKER)
              {
                  delete pFriendAssemblies;
              }
          }
      }
  } // void Assembly::CacheFriendAssemblyInfo()
  
  //*****************************************************************************
  // Is the given assembly a friend of this assembly?
  bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, FieldDesc *pFD)
  {
      WRAPPER_NO_CONTRACT;
  
      CacheFriendAssemblyInfo();
  
      if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER)
      {
          return false;
      }
  
      return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pFD);
  }
  
  bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, MethodDesc *pMD)
  {
      WRAPPER_NO_CONTRACT;
  
      CacheFriendAssemblyInfo();
  
      if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER)
      {
          return false;
      }
  
      return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pMD);
  }
  
  bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, MethodTable *pMT)
  {
      WRAPPER_NO_CONTRACT;
  
      CacheFriendAssemblyInfo();
  
      if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER)
      {
          return false;
      }
  
      return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pMT);
  }
  
  bool Assembly::IgnoresAccessChecksTo(Assembly *pAccessedAssembly)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          PRECONDITION(CheckPointer(pAccessedAssembly));
      }
      CONTRACTL_END;
  
      CacheFriendAssemblyInfo();
  
      if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER)
      {
          return false;
      }
  
      if (pAccessedAssembly->IsDisabledPrivateReflection())
      {
          return false;
      }
  
      return m_pFriendAssemblyDescriptor->IgnoresAccessChecksTo(pAccessedAssembly);
  }
  
  
  #ifndef CROSSGEN_COMPILE
  
  enum CorEntryPointType
  {
      EntryManagedMain,                   // void main(String[])
      EntryCrtMain                        // unsigned main(void)
  };
  
  void DECLSPEC_NORETURN ThrowMainMethodException(MethodDesc* pMD, UINT resID)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          MODE_ANY;
          INJECT_FAULT(COMPlusThrowOM());
      }
      CONTRACTL_END;
  
      DefineFullyQualifiedNameForClassW();
      LPCWSTR szClassName = GetFullyQualifiedNameForClassW(pMD->GetMethodTable());
      LPCUTF8 szUTFMethodName;
      if (FAILED(pMD->GetMDImport()->GetNameOfMethodDef(pMD->GetMemberDef(), &szUTFMethodName)))
      {
          szUTFMethodName = "Invalid MethodDef record";
      }
      PREFIX_ASSUME(szUTFMethodName!=NULL);
      MAKE_WIDEPTR_FROMUTF8(szMethodName, szUTFMethodName);
      COMPlusThrowHR(COR_E_METHODACCESS, resID, szClassName, szMethodName);
  }
  
  // Returns true if this is a valid main method?
  void ValidateMainMethod(MethodDesc * pFD, CorEntryPointType *pType)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          MODE_ANY;
          INJECT_FAULT(COMPlusThrowOM());
  
          PRECONDITION(CheckPointer(pType));
      }
      CONTRACTL_END;
  
      // Must be static, but we don't care about accessibility
      if ((pFD->GetAttrs() & mdStatic) == 0)
          ThrowMainMethodException(pFD, IDS_EE_MAIN_METHOD_MUST_BE_STATIC);
  
      if (pFD->GetNumGenericClassArgs() != 0 || pFD->GetNumGenericMethodArgs() != 0)
          ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG);
  
      // Check for types
      SigPointer sig(pFD->GetSigPointer());
  
      ULONG nCallConv;
      if (FAILED(sig.GetData(&nCallConv)))
          ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
      
      if (nCallConv != IMAGE_CEE_CS_CALLCONV_DEFAULT)
          ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG);
  
      ULONG nParamCount;
      if (FAILED(sig.GetData(&nParamCount)))
          ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
      
  
      CorElementType nReturnType;
      if (FAILED(sig.GetElemType(&nReturnType)))
          ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
      
      if ((nReturnType != ELEMENT_TYPE_VOID) && (nReturnType != ELEMENT_TYPE_I4) && (nReturnType != ELEMENT_TYPE_U4))
           ThrowMainMethodException(pFD, IDS_EE_MAIN_METHOD_HAS_INVALID_RTN);
  
      if (nParamCount == 0)
          *pType = EntryCrtMain;
      else {
          *pType = EntryManagedMain;
  
          if (nParamCount != 1)
              ThrowMainMethodException(pFD, IDS_EE_TO_MANY_ARGUMENTS_IN_MAIN);
  
          CorElementType argType;
          CorElementType argType2 = ELEMENT_TYPE_END;
  
          if (FAILED(sig.GetElemType(&argType)))
              ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
  
          if (argType == ELEMENT_TYPE_SZARRAY)
              if (FAILED(sig.GetElemType(&argType2)))
                  ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
              
          if (argType != ELEMENT_TYPE_SZARRAY || argType2 != ELEMENT_TYPE_STRING)
              ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG);
      }
  }
  
  /* static */
  HRESULT RunMain(MethodDesc *pFD ,
                  short numSkipArgs,
                  INT32 *piRetVal,
                  PTRARRAYREF *stringArgs /*=NULL*/)
  {
      STATIC_CONTRACT_THROWS;
      _ASSERTE(piRetVal);
  
      DWORD       cCommandArgs = 0;  // count of args on command line
      LPWSTR      *wzArgs = NULL; // command line args
      HRESULT     hr = S_OK;
  
      *piRetVal = -1;
  
      // The exit code for the process is communicated in one of two ways.  If the
      // entrypoint returns an 'int' we take that.  Otherwise we take a latched
      // process exit code.  This can be modified by the app via setting
      // Environment's ExitCode property.
      //
      // When we're executing the default exe main in the default domain, set the latched exit code to 
      // zero as a default.  If it gets set to something else by user code then that value will be returned. 
      //
      // StringArgs appears to be non-null only when the main method is explicitly invoked via the hosting api
      // or through creating a subsequent domain and running an exe within it.  In those cases we don't
      // want to reset the (global) latched exit code.
      if (stringArgs == NULL)
          SetLatchedExitCode(0);
  
      if (!pFD) {
          _ASSERTE(!"Must have a function to call!");
          return E_FAIL;
      }
  
      CorEntryPointType EntryType = EntryManagedMain;
      ValidateMainMethod(pFD, &EntryType);
  
      if ((EntryType == EntryManagedMain) &&
          (stringArgs == NULL)) {
          return E_INVALIDARG;
      }
  
      ETWFireEvent(Main_V1);
  
      struct Param
      {
          MethodDesc *pFD;
          short numSkipArgs;
          INT32 *piRetVal;
          PTRARRAYREF *stringArgs;
          CorEntryPointType EntryType;
          DWORD cCommandArgs;
          LPWSTR *wzArgs;
      } param;
      param.pFD = pFD;
      param.numSkipArgs = numSkipArgs;
      param.piRetVal = piRetVal;
      param.stringArgs = stringArgs;
      param.EntryType = EntryType;
      param.cCommandArgs = cCommandArgs;
      param.wzArgs = wzArgs;
  
      EX_TRY_NOCATCH(Param *, pParam, &param)
      {
          MethodDescCallSite  threadStart(pParam->pFD);
          
          PTRARRAYREF StrArgArray = NULL;
          GCPROTECT_BEGIN(StrArgArray);
  
          // Build the parameter array and invoke the method.
          if (pParam->EntryType == EntryManagedMain) {
              if (pParam->stringArgs == NULL) {
                  // Allocate a COM Array object with enough slots for cCommandArgs - 1
                  StrArgArray = (PTRARRAYREF) AllocateObjectArray((pParam->cCommandArgs - pParam->numSkipArgs), g_pStringClass);
  
                  // Create Stringrefs for each of the args
                  for (DWORD arg = pParam->numSkipArgs; arg < pParam->cCommandArgs; arg++) {
                      STRINGREF sref = StringObject::NewString(pParam->wzArgs[arg]);
                      StrArgArray->SetAt(arg - pParam->numSkipArgs, (OBJECTREF) sref);
                  }
              }
              else
                  StrArgArray = *pParam->stringArgs;
          }
  
          ARG_SLOT stackVar = ObjToArgSlot(StrArgArray);
  
          if (pParam->pFD->IsVoid()) 
          {
              // Set the return value to 0 instead of returning random junk
              *pParam->piRetVal = 0;
              threadStart.Call(&stackVar);
          }
          else 
          {
              *pParam->piRetVal = (INT32)threadStart.Call_RetArgSlot(&stackVar);
              SetLatchedExitCode(*pParam->piRetVal);
          }
  
          GCPROTECT_END();
  
          //<TODO>
          // When we get mainCRTStartup from the C++ then this should be able to go away.</TODO>
          fflush(stdout);
          fflush(stderr);
      }
      EX_END_NOCATCH
  
      ETWFireEvent(MainEnd_V1);
  
      return hr;
  }
  
  static void RunMainPre()
  {
      LIMITED_METHOD_CONTRACT;
  
      _ASSERTE(GetThread() != 0);
      g_fWeControlLifetime = TRUE;
  }
  
  static void RunMainPost()
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          MODE_ANY;
          INJECT_FAULT(COMPlusThrowOM(););
          PRECONDITION(CheckPointer(GetThread()));
      }
      CONTRACTL_END
  
      GCX_PREEMP();
      ThreadStore::s_pThreadStore->WaitForOtherThreads();
  
      DWORD dwSecondsToSleep = g_pConfig->GetSleepOnExit();
  
      // if dwSeconds is non-zero then we will sleep for that many seconds
      // before we exit this allows the vaDumpCmd to detect that our process
      // has gone idle and this allows us to get a vadump of our process at
      // this point in it's execution
      //
      if (dwSecondsToSleep != 0)
      {
          ClrSleepEx(dwSecondsToSleep * 1000, FALSE);   
      }
  }
  
  static void RunStartupHooks()
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          MODE_COOPERATIVE;
          INJECT_FAULT(COMPlusThrowOM(););
      }
      CONTRACTL_END;
  
      MethodDescCallSite processStartupHooks(METHOD__STARTUP_HOOK_PROVIDER__PROCESS_STARTUP_HOOKS);
      processStartupHooks.Call(NULL);
  }
  
  INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs, BOOL waitForOtherThreads)
  {
      CONTRACTL
      {
          INSTANCE_CHECK;
          THROWS;
          GC_TRIGGERS;
          MODE_ANY;
          ENTRY_POINT;
          INJECT_FAULT(COMPlusThrowOM());
      }
      CONTRACTL_END;
  
      // reset the error code for std C
      errno=0; 
  
      HRESULT hr = S_OK;
      INT32   iRetVal = 0;
  
      BEGIN_ENTRYPOINT_THROWS;
  
      Thread *pThread = GetThread();
      MethodDesc *pMeth;
      {
          // This thread looks like it wandered in -- but actually we rely on it to keep the process alive.
          pThread->SetBackground(FALSE);
      
          GCX_COOP();
  
          pMeth = GetEntryPoint();
  
          if (pMeth) {
              {
  #ifdef FEATURE_COMINTEROP
                  GCX_PREEMP();
  
                  Thread::ApartmentState state = Thread::AS_Unknown;
                  state = SystemDomain::GetEntryPointThreadAptState(pMeth->GetMDImport(), pMeth->GetMemberDef());
                  SystemDomain::SetThreadAptState(state);
  #endif // FEATURE_COMINTEROP
              }
  
              RunMainPre();
  
              // Set the root assembly as the assembly that is containing the main method
              // The root assembly is used in the GetEntryAssembly method that on CoreCLR is used
              // to get the TargetFrameworkMoniker for the app
              AppDomain * pDomain = pThread->GetDomain();
              pDomain->SetRootAssembly(pMeth->GetAssembly());
  
              RunStartupHooks();
  
              hr = RunMain(pMeth, 1, &iRetVal, stringArgs);
          }
      }
  
      //RunMainPost is supposed to be called on the main thread of an EXE,
      //after that thread has finished doing useful work.  It contains logic
      //to decide when the process should get torn down.  So, don't call it from
      // AppDomain.ExecuteAssembly()
      if (pMeth) {
          if (waitForOtherThreads)
              RunMainPost();
      }
      else {
          StackSString displayName;
          GetDisplayName(displayName);
          COMPlusThrowHR(COR_E_MISSINGMETHOD, IDS_EE_FAILED_TO_FIND_MAIN, displayName);
      }
      
      IfFailThrow(hr);
      
      END_ENTRYPOINT_THROWS;
      return iRetVal;
  }
  #endif // CROSSGEN_COMPILE
  
  MethodDesc* Assembly::GetEntryPoint()
  {
      CONTRACT(MethodDesc*)
      {
          THROWS;
          INJECT_FAULT(COMPlusThrowOM(););
          MODE_ANY;
  
          // Can return NULL if no entry point.
          POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
      }
      CONTRACT_END;
  
      if (m_pEntryPoint)
          RETURN m_pEntryPoint;
  
      mdToken mdEntry = m_pManifestFile->GetEntryPointToken();
      if (IsNilToken(mdEntry))
          RETURN NULL;
  
      Module *pModule = NULL;
      switch(TypeFromToken(mdEntry)) {
      case mdtFile:
          pModule = m_pManifest->LoadModule(::GetAppDomain(), mdEntry, FALSE)->GetModule();
          
          mdEntry = pModule->GetEntryPointToken();
          if ( (TypeFromToken(mdEntry) != mdtMethodDef) ||
               (!pModule->GetMDImport()->IsValidToken(mdEntry)) )
              pModule = NULL;
          break;
          
      case mdtMethodDef:
          if (m_pManifestFile->GetPersistentMDImport()->IsValidToken(mdEntry))
              pModule = m_pManifest;
          break;
      }
  
      // May be unmanaged entrypoint
      if (!pModule)
          RETURN NULL;
  
      // We need to get its properties and the class token for this MethodDef token.
      mdToken mdParent;
      if (FAILED(pModule->GetMDImport()->GetParentToken(mdEntry, &mdParent))) {
          StackSString displayName;
          GetDisplayName(displayName);
          COMPlusThrowHR(COR_E_BADIMAGEFORMAT, IDS_EE_ILLEGAL_TOKEN_FOR_MAIN, displayName);
      }
  
      // For the entrypoint, also validate if the paramList is valid or not. We do this check
      // by asking for the return-value  (sequence 0) parameter to MDInternalRO::FindParamOfMethod. 
      // Incase the parameter list is invalid, CLDB_E_FILE_CORRUPT will be returned 
      // byMDInternalRO::FindParamOfMethod and we will bail out. 
      // 
      // If it does not exist (return value CLDB_E_RECORD_NOTFOUND) or if it is found (S_OK),
      // we do not bother as the values would have come upon ensuring a valid parameter record
      // list.
      mdParamDef pdParam;
      HRESULT hrValidParamList = pModule->GetMDImport()->FindParamOfMethod(mdEntry, 0, &pdParam);
      if (hrValidParamList == CLDB_E_FILE_CORRUPT)
      {
          // Throw an exception for bad_image_format (because of corrupt metadata)
          StackSString displayName;
          GetDisplayName(displayName);
          COMPlusThrowHR(COR_E_BADIMAGEFORMAT, IDS_EE_ILLEGAL_TOKEN_FOR_MAIN, displayName);
      }
  
      if (mdParent != COR_GLOBAL_PARENT_TOKEN) {
          GCX_COOP();
          // This code needs a class init frame, because without it, the
          // debugger will assume any code that results from searching for a
          // type handle (ie, loading an assembly) is the first line of a program.
          FrameWithCookie<DebuggerClassInitMarkFrame> __dcimf;
              
          MethodTable * pInitialMT = ClassLoader::LoadTypeDefOrRefThrowing(pModule, mdParent, 
                                                                         ClassLoader::ThrowIfNotFound,
                                                                         ClassLoader::FailIfUninstDefOrRef).GetMethodTable();
  
          m_pEntryPoint = MemberLoader::FindMethod(pInitialMT, mdEntry);
  
          __dcimf.Pop();
      }
      else
      {
          m_pEntryPoint = pModule->FindMethod(mdEntry);
      }
      
      RETURN m_pEntryPoint;
  }
  
  #ifndef CROSSGEN_COMPILE
  OBJECTREF Assembly::GetExposedObject()
  {
      CONTRACT(OBJECTREF)
      {
          GC_TRIGGERS;
          THROWS;
          INJECT_FAULT(COMPlusThrowOM(););
          MODE_COOPERATIVE;
      }
      CONTRACT_END;
  
      RETURN GetDomainAssembly()->GetExposedAssemblyObject();
  }
  #endif // CROSSGEN_COMPILE
  
  /* static */
  BOOL Assembly::FileNotFound(HRESULT hr)
  {
      LIMITED_METHOD_CONTRACT;
      return IsHRESULTForExceptionKind(hr, kFileNotFoundException) || 
  #ifdef FEATURE_COMINTEROP
             (hr == RO_E_METADATA_NAME_NOT_FOUND) || 
  #endif //FEATURE_COMINTEROP
             (hr == CLR_E_BIND_TYPE_NOT_FOUND);
  }
  
  
  BOOL Assembly::GetResource(LPCSTR szName, DWORD *cbResource,
                                PBYTE *pbInMemoryResource, Assembly** pAssemblyRef,
                                LPCSTR *szFileName, DWORD *dwLocation,
                                StackCrawlMark *pStackMark, BOOL fSkipSecurityCheck,
                                BOOL fSkipRaiseResolveEvent)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
      }
      CONTRACTL_END;
      
      DomainAssembly *pAssembly = NULL;
      BOOL result = GetDomainAssembly()->GetResource(szName, cbResource,
                                                     pbInMemoryResource, &pAssembly,
                                                     szFileName, dwLocation, pStackMark, fSkipSecurityCheck,
                                                     fSkipRaiseResolveEvent);
      if (result && pAssemblyRef != NULL && pAssembly!=NULL)
          *pAssemblyRef = pAssembly->GetAssembly();
  
      return result;
  }
  
  #ifdef FEATURE_PREJIT
  BOOL Assembly::IsInstrumented()
  {
      STATIC_CONTRACT_THROWS;
      STATIC_CONTRACT_GC_TRIGGERS;
      STATIC_CONTRACT_FAULT;
  
      // This will set the value of m_isInstrumentedStatus by calling IsInstrumentedHelper()
      // that method performs string pattern matching using the Config value of ZapBBInstr
      // We cache the value returned from that method in m_isInstrumentedStatus
      //
      if (m_isInstrumentedStatus == IS_INSTRUMENTED_UNSET)
      {
          EX_TRY
          {
              FAULT_NOT_FATAL();
  
              if (IsInstrumentedHelper())
              {
                  m_isInstrumentedStatus = IS_INSTRUMENTED_TRUE;
              }
              else
              {
                  m_isInstrumentedStatus = IS_INSTRUMENTED_FALSE;
              }
          }
  
          EX_CATCH
          {
              m_isInstrumentedStatus = IS_INSTRUMENTED_FALSE;
          }
          EX_END_CATCH(RethrowTerminalExceptions);
      }
  
      // At this point m_isInstrumentedStatus can't have the value of IS_INSTRUMENTED_UNSET
      _ASSERTE(m_isInstrumentedStatus != IS_INSTRUMENTED_UNSET);
  
      return (m_isInstrumentedStatus == IS_INSTRUMENTED_TRUE);
  }
  
  BOOL Assembly::IsInstrumentedHelper()
  {
      STATIC_CONTRACT_THROWS;
      STATIC_CONTRACT_GC_TRIGGERS;
      STATIC_CONTRACT_FAULT;
  
      // Dynamic Assemblies cannot be instrumented
      if (IsDynamic())
          return false;
  
      // We must have a native image in order to perform IBC instrumentation
      if (!GetManifestFile()->HasNativeOrReadyToRunImage())
          return false;
      
      // @Consider using the full name instead of the short form
      // (see GetFusionAssemblyName()->IsEqual).
  
      LPCUTF8 szZapBBInstr = g_pConfig->GetZapBBInstr();
      LPCUTF8 szAssemblyName = GetSimpleName();
  
      if (!szZapBBInstr || !szAssemblyName ||
          (*szZapBBInstr == '\0') || (*szAssemblyName == '\0'))
          return false;
  
      // Convert to unicode so that we can do a case insensitive comparison
  
      SString instrumentedAssemblyNamesList(SString::Utf8, szZapBBInstr);
      SString assemblyName(SString::Utf8, szAssemblyName);
  
      const WCHAR *wszInstrumentedAssemblyNamesList = instrumentedAssemblyNamesList.GetUnicode();
      const WCHAR *wszAssemblyName                  = assemblyName.GetUnicode();
  
      // wszInstrumentedAssemblyNamesList is a space separated list of assembly names. 
      // We need to determine if wszAssemblyName is in this list.
      // If there is a "*" in the list, then all assemblies match.
  
      const WCHAR * pCur = wszInstrumentedAssemblyNamesList;
  
      do
      {
          _ASSERTE(pCur[0] != W('\0'));
          const WCHAR * pNextSpace = wcschr(pCur, W(' '));
          _ASSERTE(pNextSpace == NULL || pNextSpace[0] == W(' '));
          
          if (pCur != pNextSpace)
          {
              // pCur is not pointing to a space
              _ASSERTE(pCur[0] != W(' '));
              
              if (pCur[0] == W('*') && (pCur[1] == W(' ') || pCur[1] == W('\0')))
                  return true;
  
              if (pNextSpace == NULL)
              {
                  // We have reached the last name in the list. There are no more spaces.
                  return (SString::_wcsicmp(wszAssemblyName, pCur) == 0);
              }
              else
              {
                  if (SString::_wcsnicmp(wszAssemblyName, pCur, static_cast<COUNT_T>(pNextSpace - pCur)) == 0)
                      return true;
              }
          }
  
          pCur = pNextSpace + 1;
      }
      while (pCur[0] != W('\0'));
  
      return false;    
  }
  #endif // FEATURE_PREJIT
  
  //***********************************************************
  // Add an assembly to the assemblyref list. pAssemEmitter specifies where
  // the AssemblyRef is emitted to.
  //***********************************************************
  mdAssemblyRef Assembly::AddAssemblyRef(Assembly *refedAssembly, IMetaDataAssemblyEmit *pAssemEmitter, BOOL fUsePublicKeyToken)
  {
      CONTRACT(mdAssemblyRef)
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
          PRECONDITION(CheckPointer(refedAssembly));
          PRECONDITION(CheckPointer(pAssemEmitter, NULL_NOT_OK));
          POSTCONDITION(!IsNilToken(RETVAL));
          POSTCONDITION(TypeFromToken(RETVAL) == mdtAssemblyRef);
      }
      CONTRACT_END;
  
      SafeComHolder<IMetaDataAssemblyEmit> emitHolder;
  
      AssemblySpec spec;
      spec.InitializeSpec(refedAssembly->GetManifestFile());
  
      if (refedAssembly->IsCollectible())
      {
          if (this->IsCollectible())
              this->GetLoaderAllocator()->EnsureReference(refedAssembly->GetLoaderAllocator());
          else
              COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible"));
      }
  
      mdAssemblyRef ar;
      IfFailThrow(spec.EmitToken(pAssemEmitter, &ar, fUsePublicKeyToken));
  
      RETURN ar;
  }   // Assembly::AddAssemblyRef
  
  //***********************************************************
  // Add a typedef to the runtime TypeDef table of this assembly
  //***********************************************************
  void Assembly::AddType(
      Module          *pModule,
      mdTypeDef       cl)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
      }
      CONTRACTL_END
  
      AllocMemTracker amTracker;
  
      if (pModule->GetAssembly() != this)
      {
          // you cannot add a typedef outside of the assembly to the typedef table
          _ASSERTE(!"Bad usage!");
      }
      m_pClassLoader->AddAvailableClassDontHaveLock(pModule,
                                                    cl,
                                                    &amTracker);
      amTracker.SuppressRelease();
  }
  
  //***********************************************************
  // Add an ExportedType to the runtime TypeDef table of this assembly
  //***********************************************************
  void Assembly::AddExportedType(mdExportedType cl)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
      }
      CONTRACTL_END
  
      AllocMemTracker amTracker;
      m_pClassLoader->AddExportedTypeDontHaveLock(GetManifestModule(),
          cl,
          &amTracker);
      amTracker.SuppressRelease();
  }
  
  
  
  
  HRESULT STDMETHODCALLTYPE
  GetAssembliesByName(LPCWSTR  szAppBase,
                      LPCWSTR  szPrivateBin,
                      LPCWSTR  szAssemblyName,
                      IUnknown *ppIUnk[],
                      ULONG    cMax,
                      ULONG    *pcAssemblies)
  {
      CONTRACTL
      {
          NOTHROW;
          MODE_PREEMPTIVE;
          GC_TRIGGERS;
          INJECT_FAULT(return E_OUTOFMEMORY;);
      }
      CONTRACTL_END
  
      HRESULT hr = S_OK;
  
      if (g_fEEInit) {
          // Cannot call this during EE startup
          return MSEE_E_ASSEMBLYLOADINPROGRESS;
      }
  
      if (!(szAssemblyName && ppIUnk && pcAssemblies))
          return E_POINTER;
  
      hr = COR_E_NOTSUPPORTED;
  
      return hr;
  }// Used by the IMetadata API's to access an assemblies metadata.
  
  void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszFullName, UINT resIDWhy)
  {
      WRAPPER_NO_CONTRACT;
      ThrowTypeLoadException(NULL, pszFullName, NULL,
                             resIDWhy);
  }
  
  void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszNameSpace, LPCUTF8 pszTypeName,
                                                          UINT resIDWhy)
  {
      WRAPPER_NO_CONTRACT;
      ThrowTypeLoadException(pszNameSpace, pszTypeName, NULL,
                             resIDWhy);
  
  }
  
  void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(NameHandle *pName, UINT resIDWhy)
  {
      STATIC_CONTRACT_THROWS;
  
      if (pName->GetName()) {
          ThrowTypeLoadException(pName->GetNameSpace(),
                                 pName->GetName(), 
                                 NULL, 
                                 resIDWhy);
      }
      else
          ThrowTypeLoadException(pName->GetTypeModule()->GetMDImport(),
                                 pName->GetTypeToken(),
                                 resIDWhy);
  
  }
  
  void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(IMDInternalImport *pInternalImport,
                                                          mdToken token,
                                                          UINT resIDWhy)
  {
      WRAPPER_NO_CONTRACT;
      ThrowTypeLoadException(pInternalImport, token, NULL, resIDWhy);
  }
  
  void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(IMDInternalImport *pInternalImport,
                                                          mdToken token,
                                                          LPCUTF8 pszFieldOrMethodName,
                                                          UINT resIDWhy)
  {
      STATIC_CONTRACT_THROWS;
      char pszBuff[32];
      LPCUTF8 pszClassName = (LPCUTF8)pszBuff;
      LPCUTF8 pszNameSpace = "Invalid_Token";
  
      if(pInternalImport->IsValidToken(token))
      {
          switch (TypeFromToken(token)) {
          case mdtTypeRef:
              if (FAILED(pInternalImport->GetNameOfTypeRef(token, &pszNameSpace, &pszClassName)))
              {
                  pszNameSpace = pszClassName = "Invalid TypeRef record";
              }
              break;
          case mdtTypeDef:
              if (FAILED(pInternalImport->GetNameOfTypeDef(token, &pszClassName, &pszNameSpace)))
              {
                  pszNameSpace = pszClassName = "Invalid TypeDef record";
              }
              break;
          case mdtTypeSpec:
  
              // If you see this assert, you need to make sure the message for
              // this resID is appropriate for TypeSpecs
              _ASSERTE((resIDWhy == IDS_CLASSLOAD_GENERAL) || 
                       (resIDWhy == IDS_CLASSLOAD_BADFORMAT) || 
                       (resIDWhy == IDS_CLASSLOAD_TYPESPEC));
  
              resIDWhy = IDS_CLASSLOAD_TYPESPEC;
          }
      }
      else
          sprintf_s(pszBuff, sizeof(pszBuff), "0x%8.8X", token);
  
      ThrowTypeLoadException(pszNameSpace, pszClassName,
                             pszFieldOrMethodName, resIDWhy);
  }
  
  
  
  void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszNameSpace,
                                                          LPCUTF8 pszTypeName,
                                                          LPCUTF8 pszMethodName,
                                                          UINT resIDWhy)
  {
      STATIC_CONTRACT_THROWS;
  
      StackSString displayName;
      GetDisplayName(displayName);
  
      ::ThrowTypeLoadException(pszNameSpace, pszTypeName, displayName,
                               pszMethodName, resIDWhy);
  }
  
  void DECLSPEC_NORETURN Assembly::ThrowBadImageException(LPCUTF8 pszNameSpace,
                                                          LPCUTF8 pszTypeName,
                                                          UINT resIDWhy)
  {
      STATIC_CONTRACT_THROWS;
  
      StackSString displayName;
      GetDisplayName(displayName);
  
      StackSString fullName;
      SString sNameSpace(SString::Utf8, pszNameSpace);
      SString sTypeName(SString::Utf8, pszTypeName);
      fullName.MakeFullNamespacePath(sNameSpace, sTypeName);
  
      COMPlusThrowHR(COR_E_BADIMAGEFORMAT, resIDWhy, fullName, displayName);
  }
  
  
  #ifdef FEATURE_COMINTEROP
  Assembly::WinMDStatus Assembly::GetWinMDStatus()
  {
      LIMITED_METHOD_CONTRACT;
  
      if (m_winMDStatus == WinMDStatus_Unknown)
      {
          IWinMDImport *pWinMDImport = GetManifestWinMDImport();
          if (pWinMDImport != NULL)
          {
              BOOL bIsWinMDExp;
              VERIFY(SUCCEEDED(pWinMDImport->IsScenarioWinMDExp(&bIsWinMDExp)));
  
              if (bIsWinMDExp)
              {
                  // this is a managed backed WinMD
                  m_winMDStatus = WinMDStatus_IsManagedWinMD;
              }
              else
              {
                  // this is a pure WinMD
                  m_winMDStatus = WinMDStatus_IsPureWinMD;
              }
          }
          else
          {
              // this is not a WinMD at all
              m_winMDStatus = WinMDStatus_IsNotWinMD;
          }
      }
  
      return m_winMDStatus;
  }
  
  bool Assembly::IsWinMD()
  {
      LIMITED_METHOD_CONTRACT;
      return GetWinMDStatus() != WinMDStatus_IsNotWinMD;
  }
  
  bool Assembly::IsManagedWinMD()
  {
      LIMITED_METHOD_CONTRACT;
      return GetWinMDStatus() == WinMDStatus_IsManagedWinMD;
  }
  
  IWinMDImport *Assembly::GetManifestWinMDImport()
  {
      LIMITED_METHOD_CONTRACT;
  
      if (m_pManifestWinMDImport == NULL)
      {
          ReleaseHolder<IWinMDImport> pWinMDImport;
          if (SUCCEEDED(m_pManifest->GetMDImport()->QueryInterface(IID_IWinMDImport, (void **)&pWinMDImport)))
          {
              if (InterlockedCompareExchangeT<IWinMDImport *>(&m_pManifestWinMDImport, pWinMDImport, NULL) == NULL)
              {
                  pWinMDImport.SuppressRelease();
              }
          }
      }
  
      return m_pManifestWinMDImport;
  }
  
  #endif // FEATURE_COMINTEROP
  
  
  #endif // #ifndef DACCESS_COMPILE
  
  #ifndef DACCESS_COMPILE
  void Assembly::EnsureActive()
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          INJECT_FAULT(COMPlusThrowOM(););
      }
      CONTRACTL_END;
  
      GetDomainAssembly()->EnsureActive();
  }
  #endif //!DACCESS_COMPILE
  
  CHECK Assembly::CheckActivated()
  {
  #ifndef DACCESS_COMPILE
      WRAPPER_NO_CONTRACT;
  
      CHECK(GetDomainAssembly()->CheckActivated());
  #endif
      CHECK_OK;
  }
  
  
  
  #ifdef DACCESS_COMPILE
  
  void
  Assembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
  {
      WRAPPER_NO_CONTRACT;
      SUPPORTS_DAC;
  
       // We don't need Assembly info in triage dumps.
      if (flags == CLRDATA_ENUM_MEM_TRIAGE)
      {
          return;
      }
  
      DAC_ENUM_DTHIS();
      EMEM_OUT(("MEM: %p Assembly\n", dac_cast<TADDR>(this)));
  
      if (m_pDomain.IsValid())
      {
          m_pDomain->EnumMemoryRegions(flags, true);
      }
      if (m_pClassLoader.IsValid())
      {
          m_pClassLoader->EnumMemoryRegions(flags);
      }
      if (m_pManifest.IsValid())
      {
          m_pManifest->EnumMemoryRegions(flags, true);
      }
      if (m_pManifestFile.IsValid())
      {
          m_pManifestFile->EnumMemoryRegions(flags);
      }
  }
  
  #endif
  
  #ifndef DACCESS_COMPILE
  
  FriendAssemblyDescriptor::FriendAssemblyDescriptor()
  {
  }
  
  FriendAssemblyDescriptor::~FriendAssemblyDescriptor()
  {
      CONTRACTL
      {
          DESTRUCTOR_CHECK;
      }
      CONTRACTL_END;
  
      ArrayList::Iterator itFullAccessAssemblies = m_alFullAccessFriendAssemblies.Iterate();
      while (itFullAccessAssemblies.Next())
      {
          FriendAssemblyName_t *pFriendAssemblyName = static_cast<FriendAssemblyName_t *>(itFullAccessAssemblies.GetElement());
          delete pFriendAssemblyName;
      }
  }
  
  
  //---------------------------------------------------------------------------------------
  //
  // Builds a FriendAssemblyDescriptor for a given assembly
  //
  // Arguments:
  //    pAssembly - assembly to get friend assembly information for
  //
  // Return Value:
  //    A friend assembly descriptor if the assembly declares any friend assemblies, otherwise NULL
  //
  
  // static
  FriendAssemblyDescriptor *FriendAssemblyDescriptor::CreateFriendAssemblyDescriptor(PEAssembly *pAssembly)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          PRECONDITION(CheckPointer(pAssembly));
      }
      CONTRACTL_END
  
      NewHolder<FriendAssemblyDescriptor> pFriendAssemblies = new FriendAssemblyDescriptor;
  
      // We're going to do this twice, once for InternalsVisibleTo and once for IgnoresAccessChecks
      ReleaseHolder<IMDInternalImport> pImport(pAssembly->GetMDImportWithRef());
      for(int count = 0 ; count < 2 ; ++count)
      {
          _ASSERTE(pImport != NULL);
          MDEnumHolder hEnum(pImport);
          HRESULT hr = S_OK;
  
          if (count == 0)
          {
              hr = pImport->EnumCustomAttributeByNameInit(TokenFromRid(1, mdtAssembly), FRIEND_ASSEMBLY_TYPE, &hEnum);
          }
          else
          {
              hr = pImport->EnumCustomAttributeByNameInit(TokenFromRid(1, mdtAssembly), SUBJECT_ASSEMBLY_TYPE, &hEnum);
          }
  
          IfFailThrow(hr);
  
          // Nothing to do if there are no attributes
          if (hr == S_FALSE)
          {
              continue;
          }
  
          // Enumerate over the declared friends
          mdCustomAttribute tkAttribute;
          while (pImport->EnumNext(&hEnum, &tkAttribute))
          {
              // Get raw custom attribute.
              const BYTE  *pbAttr = NULL;         // Custom attribute data as a BYTE*.
              ULONG cbAttr = 0;                   // Size of custom attribute data.
              if (FAILED(pImport->GetCustomAttributeAsBlob(tkAttribute, reinterpret_cast<const void **>(&pbAttr), &cbAttr)))
              {
                  THROW_BAD_FORMAT(BFA_INVALID_TOKEN, pAssembly);
              }
  
              CustomAttributeParser cap(pbAttr, cbAttr);
              if (FAILED(cap.ValidateProlog()))
              {
                  THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, pAssembly);
              }
  
              // Get the name of the friend assembly.
              LPCUTF8 szString;
              ULONG   cbString;
              if (FAILED(cap.GetNonNullString(&szString, &cbString)))
              {
                  THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, pAssembly);
              }
  
              // Convert the string to Unicode.
              StackSString displayName(SString::Utf8, szString, cbString);
  
              // Create an AssemblyNameObject from the string.
              FriendAssemblyNameHolder pFriendAssemblyName;
              StackScratchBuffer buffer;
              pFriendAssemblyName = new FriendAssemblyName_t;
              hr = pFriendAssemblyName->Init(displayName.GetUTF8(buffer));
  
              if (SUCCEEDED(hr))
              {
                  hr = pFriendAssemblyName->CheckFriendAssemblyName();
              }
  
              if (FAILED(hr))
              {
                  THROW_HR_ERROR_WITH_INFO(hr, pAssembly);
              }
  
              if (count == 1)
              {
                  pFriendAssemblies->AddSubjectAssembly(pFriendAssemblyName);
                  pFriendAssemblyName.SuppressRelease();
                  // Below checks are unnecessary for IgnoresAccessChecks
                  continue;
              }
  
              // CoreCLR does not have a valid scenario for strong-named assemblies requiring their dependencies
              // to be strong-named as well.
  
              pFriendAssemblies->AddFriendAssembly(pFriendAssemblyName);
  
              pFriendAssemblyName.SuppressRelease();
          }
      }
  
      pFriendAssemblies.SuppressRelease();
      return pFriendAssemblies.Extract();
  }
  
  //---------------------------------------------------------------------------------------
  //
  // Adds an assembly to the list of friend assemblies for this descriptor
  //
  // Arguments:
  //    pFriendAssembly      - friend assembly to add to the list
  //    fAllInternalsVisible - true if all internals are visible to the friend, false if only specifically
  //                           marked internals are visible
  //
  // Notes:
  //    This method takes ownership of the friend assembly name. It is not thread safe and does not check to
  //    see if an assembly has already been added to the friend assembly list.
  //
  
  void FriendAssemblyDescriptor::AddFriendAssembly(FriendAssemblyName_t *pFriendAssembly)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          PRECONDITION(CheckPointer(pFriendAssembly));
      }
      CONTRACTL_END
  
      m_alFullAccessFriendAssemblies.Append(pFriendAssembly);
  }
  
  void FriendAssemblyDescriptor::AddSubjectAssembly(FriendAssemblyName_t *pFriendAssembly)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          PRECONDITION(CheckPointer(pFriendAssembly));
      }
      CONTRACTL_END
  
      m_subjectAssemblies.Append(pFriendAssembly);
  }
  
  // static
  bool FriendAssemblyDescriptor::IsAssemblyOnList(PEAssembly *pAssembly, const ArrayList &alAssemblyNames)
  {
      CONTRACTL
      {
          THROWS;
          GC_TRIGGERS;
          PRECONDITION(CheckPointer(pAssembly));
      }
      CONTRACTL_END;
  
      AssemblySpec asmDef;
      asmDef.InitializeSpec(pAssembly);
  
      ArrayList::ConstIterator itAssemblyNames = alAssemblyNames.Iterate();
      while (itAssemblyNames.Next())
      {
          const FriendAssemblyName_t *pFriendAssemblyName = static_cast<const FriendAssemblyName_t *>(itAssemblyNames.GetElement());
          HRESULT hr = AssemblySpec::RefMatchesDef(pFriendAssemblyName, &asmDef) ? S_OK : S_FALSE;
  
          if (hr == S_OK)
          {
              return true;
          }
      }
  
      return false;
  }
  
  #endif // !DACCESS_COMPILE
  
  
  