Add DOTNET_MODIFIABLE_ASSEMBLIES env var for hot reload support (#48731)
authorMike McLaughlin <mikem@microsoft.com>
Thu, 25 Feb 2021 22:22:04 +0000 (14:22 -0800)
committerGitHub <noreply@github.com>
Thu, 25 Feb 2021 22:22:04 +0000 (14:22 -0800)
Add EXTERNAL_DOTNET_MODIFIABLE_ASSEMBLIES env var for hot reload support

src/coreclr/inc/clrconfigvalues.h
src/coreclr/vm/ceeload.cpp
src/coreclr/vm/ceeload.h
src/coreclr/vm/ceeload.inl
src/coreclr/vm/eeconfig.cpp
src/coreclr/vm/eeconfig.h
src/coreclr/vm/encee.h

index c6fc463..6051c2a 100644 (file)
@@ -450,6 +450,7 @@ CONFIG_DWORD_INFO_EX(INTERNAL_MD_MiniMDBreak, W("MD_MiniMDBreak"), 0, "ASSERT wh
 CONFIG_DWORD_INFO_EX(INTERNAL_MD_PreSaveBreak, W("MD_PreSaveBreak"), 0, "ASSERT when calling CMiniMdRw::PreSave", CLRConfig::EEConfig_default)
 CONFIG_DWORD_INFO_EX(INTERNAL_MD_RegMetaBreak, W("MD_RegMetaBreak"), 0, "ASSERT when creating RegMeta class", CLRConfig::EEConfig_default)
 CONFIG_DWORD_INFO_EX(INTERNAL_MD_RegMetaDump, W("MD_RegMetaDump"), 0, "Dump MD in 4 functions (?)", CLRConfig::EEConfig_default)
+RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DOTNET_MODIFIABLE_ASSEMBLIES, W("DOTNET_MODIFIABLE_ASSEMBLIES"), "Enables hot reload on debug built assemblies with the 'debug' keyword", CLRConfig::DontPrependCOMPlus_ | CLRConfig::TrimWhiteSpaceFromStringValue);
 
 // Metadata - mscordbi only - this flag is only intended to mitigate potential issues in bug fix 458597.
 RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_MD_PreserveDebuggerMetadataMemory, W("MD_PreserveDebuggerMetadataMemory"), 0, "Save all versions of metadata memory in the debugger when debuggee metadata is updated", CLRConfig::EEConfig_default)
index c17f64e..e121270 100644 (file)
@@ -686,11 +686,6 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName)
         Module::CreateAssemblyRefByNameTable(pamTracker);
     }
 
-    // If the program has the "ForceEnc" env variable set we ensure every eligible
-    // module has EnC turned on.
-    if (g_pConfig->ForceEnc() && IsEditAndContinueCapable())
-        EnableEditAndContinue();
-
 #if defined(PROFILING_SUPPORTED) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
     m_pJitInlinerTrackingMap = NULL;
     if (ReJitManager::IsReJITInlineTrackingEnabled())
@@ -1078,17 +1073,13 @@ void Module::SetDebuggerInfoBits(DebuggerAssemblyControlFlags newBits)
     m_dwTransientFlags |= (newBits << DEBUGGER_INFO_SHIFT_PRIV);
 
 #ifdef DEBUGGING_SUPPORTED
-    BOOL setEnC = ((newBits & DACF_ENC_ENABLED) != 0) && IsEditAndContinueCapable();
-
-    // The only way can change Enc is through debugger override.
-    if (setEnC)
+    if (IsEditAndContinueCapable())
     {
-        EnableEditAndContinue();
-    }
-    else
-    {
-        if (!g_pConfig->ForceEnc())
-            DisableEditAndContinue();
+        BOOL setEnC = (newBits & DACF_ENC_ENABLED) != 0 || g_pConfig->ForceEnc() || (g_pConfig->DebugAssembliesModifiable() && CORDisableJITOptimizations(GetDebuggerInfoBits()));
+        if (setEnC)
+        {
+            EnableEditAndContinue();
+        }
     }
 #endif // DEBUGGING_SUPPORTED
 
index f87e843..073b567 100644 (file)
@@ -1769,7 +1769,6 @@ protected:
         return m_moduleRef;
     }
 
-
     BOOL IsResource() const { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return GetFile()->IsResource(); }
     BOOL IsPEFile() const { WRAPPER_NO_CONTRACT; return !GetFile()->IsDynamic(); }
     BOOL IsReflection() const { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return GetFile()->IsDynamic(); }
@@ -1777,19 +1776,15 @@ protected:
     // Returns true iff the debugger can see this module.
     BOOL IsVisibleToDebugger();
 
-
     BOOL IsEditAndContinueEnabled()
     {
         LIMITED_METHOD_CONTRACT;
         SUPPORTS_DAC;
-        // We are seeing cases where this flag is set for a module that is not an EditAndContinueModule.  This should
-        // never happen unless the module is EditAndContinueCapable, in which case we would have created an EditAndContinueModule
-        // not a Module.
-        //_ASSERTE((m_dwTransientFlags & IS_EDIT_AND_CONTINUE) == 0 || IsEditAndContinueCapable());
-        return (IsEditAndContinueCapable()) && ((m_dwTransientFlags & IS_EDIT_AND_CONTINUE) != 0);
+        _ASSERTE((m_dwTransientFlags & IS_EDIT_AND_CONTINUE) == 0 || IsEditAndContinueCapable());
+        return (m_dwTransientFlags & IS_EDIT_AND_CONTINUE) != 0;
     }
 
-    BOOL IsEditAndContinueCapable();
+    virtual BOOL IsEditAndContinueCapable() const { return FALSE; }
 
     BOOL IsIStream() { LIMITED_METHOD_CONTRACT; return GetFile()->IsIStream(); }
 
@@ -1801,20 +1796,11 @@ protected:
     {
         LIMITED_METHOD_CONTRACT;
         SUPPORTS_DAC;
-        // _ASSERTE(IsEditAndContinueCapable());
+        _ASSERTE(IsEditAndContinueCapable());
         LOG((LF_ENC, LL_INFO100, "EnableEditAndContinue: this:0x%x, %s\n", this, GetDebugName()));
         m_dwTransientFlags |= IS_EDIT_AND_CONTINUE;
     }
 
-    void DisableEditAndContinue()
-    {
-        LIMITED_METHOD_CONTRACT;
-        SUPPORTS_DAC;
-        // don't _ASSERTE(IsEditAndContinueCapable());
-        LOG((LF_ENC, LL_INFO100, "DisableEditAndContinue: this:0x%x, %s\n", this, GetDebugName()));
-        m_dwTransientFlags = m_dwTransientFlags.Load() & (~IS_EDIT_AND_CONTINUE);
-    }
-
     BOOL IsTenured()
     {
         LIMITED_METHOD_CONTRACT;
index a5094b4..d6dbe8e 100644 (file)
@@ -453,20 +453,6 @@ inline mdAssemblyRef Module::FindAssemblyRef(Assembly *targetAssembly)
 
 #endif //DACCESS_COMPILE
 
-inline BOOL Module::IsEditAndContinueCapable()
-{
-    WRAPPER_NO_CONTRACT;
-    SUPPORTS_DAC;
-
-    BOOL isEnCCapable = IsEditAndContinueCapable(m_pAssembly, m_file);
-
-    // for now, Module::IsReflection is equivalent to m_file->IsDynamic,
-    // which is checked by IsEditAndContinueCapable(m_pAssembly, m_file)
-    _ASSERTE(!isEnCCapable || (!this->IsReflection()));
-
-    return isEnCCapable;
-}
-
 FORCEINLINE PTR_DomainLocalModule Module::GetDomainLocalModule()
 {
     WRAPPER_NO_CONTRACT;
index dd89bf5..6d5ea5a 100644 (file)
@@ -584,6 +584,13 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_
     fStressLog        =  GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0;
     fForceEnc         =  GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_ForceEnc, fForceEnc) != 0;
 
+    {
+        NewArrayHolder<WCHAR> wszModifiableAssemblies;
+        IfFailRet(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DOTNET_MODIFIABLE_ASSEMBLIES, &wszModifiableAssemblies));
+        if (wszModifiableAssemblies)
+            fDebugAssembliesModifiable = _wcsicmp(wszModifiableAssemblies, W("debug")) == 0;
+    }
+
     iRequireZaps        = RequireZapsType(GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_ZapRequire, iRequireZaps));
     if (IsCompilationProcess() || iRequireZaps >= REQUIRE_ZAPS_COUNT)
         iRequireZaps = REQUIRE_ZAPS_NONE;
index 5c5babf..11f1d28 100644 (file)
@@ -463,6 +463,7 @@ public:
 
     bool    StressLog()                     const { LIMITED_METHOD_CONTRACT; return fStressLog; }
     bool    ForceEnc()                      const { LIMITED_METHOD_CONTRACT; return fForceEnc; }
+    bool    DebugAssembliesModifiable()     const { LIMITED_METHOD_CONTRACT; return fDebugAssembliesModifiable; }
 
     // Optimizations to improve working set
 
@@ -691,6 +692,7 @@ private: //----------------------------------------------------------------
 
     bool fStressLog;
     bool fForceEnc;
+    bool fDebugAssembliesModifiable;
     bool fProbeForStackOverflow;
 
     // Stackwalk optimization flag
index 35e6cb9..0cd6216 100644 (file)
@@ -224,6 +224,8 @@ public:
     virtual void Destruct();
 #endif
 
+    virtual BOOL IsEditAndContinueCapable() const { return TRUE; }
+
     // Apply an EnC edit
     HRESULT ApplyEditAndContinue(DWORD cbMetadata,
                             BYTE *pMetadata,