Skip to content

Add hot reload apply changes API: AssemblyExtensions.ApplyUpdate #48366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,46 @@ public static unsafe bool TryGetRawMetadata(this Assembly assembly, out byte* bl

return InternalTryGetRawMetadata(new QCallAssembly(ref rtAsm), ref blob, ref length);
}

[DllImport(RuntimeHelpers.QCall)]
private static extern unsafe void ApplyUpdate(QCallAssembly assembly, byte* metadataDelta, int metadataDeltaLength, byte* ilDelta, int ilDeltaLength, byte* pdbDelta, int pdbDeltaLength);

/// <summary>
/// Updates the specified assembly using the provided metadata, IL and PDB deltas.
/// </summary>
/// <remarks>
/// Currently executing methods will continue to use the existing IL. New executions of modified methods will
/// use the new IL. Different runtimes may have different limitations on what kinds of changes are supported,
/// and runtimes make no guarantees as to the state of the assembly and process if the delta includes
/// unsupported changes.
/// </remarks>
/// <param name="assembly">The assembly to update.</param>
/// <param name="metadataDelta">The metadata changes to be applied.</param>
/// <param name="ilDelta">The IL changes to be applied.</param>
/// <param name="pdbDelta">The PDB changes to be applied.</param>
/// <exception cref="ArgumentNullException">The assembly argument is null.</exception>
/// <exception cref="NotSupportedException">The update could not be applied.</exception>
public static void ApplyUpdate(Assembly assembly, ReadOnlySpan<byte> metadataDelta, ReadOnlySpan<byte> ilDelta, ReadOnlySpan<byte> pdbDelta)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}

RuntimeAssembly? runtimeAssembly = assembly as RuntimeAssembly;
if (runtimeAssembly == null)
{
throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
}

unsafe
{
RuntimeAssembly rtAsm = runtimeAssembly;
fixed (byte* metadataDeltaPtr = metadataDelta, ilDeltaPtr = ilDelta, pdbDeltaPtr = pdbDelta)
{
ApplyUpdate(new QCallAssembly(ref rtAsm), metadataDeltaPtr, metadataDelta.Length, ilDeltaPtr, ilDelta.Length, pdbDeltaPtr, pdbDelta.Length);
}
}
}
}
}
47 changes: 47 additions & 0 deletions src/coreclr/vm/assemblynative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "interoputil.h"
#include "frames.h"
#include "typeparse.h"
#include "encee.h"
#include "threadsuspend.h"

#include "appdomainnative.hpp"
#include "../binder/inc/bindertracing.h"
Expand Down Expand Up @@ -1407,3 +1409,48 @@ void QCALLTYPE AssemblyNative::TraceSatelliteSubdirectoryPathProbed(LPCWSTR file

END_QCALL;
}

// static
void QCALLTYPE AssemblyNative::ApplyUpdate(
QCall::AssemblyHandle assembly,
UINT8* metadataDelta,
INT32 metadataDeltaLength,
UINT8* ilDelta,
INT32 ilDeltaLength,
UINT8* pdbDelta,
INT32 pdbDeltaLength)
{
QCALL_CONTRACT;

BEGIN_QCALL;

_ASSERTE(assembly != nullptr);
_ASSERTE(metadataDelta != nullptr);
_ASSERTE(metadataDeltaLength > 0);
_ASSERTE(ilDelta != nullptr);
_ASSERTE(ilDeltaLength > 0);

#ifdef EnC_SUPPORTED
GCX_COOP();
{
if (CORDebuggerAttached())
{
COMPlusThrow(kNotSupportedException);
}
Module* pModule = assembly->GetDomainAssembly()->GetModule();
if (!pModule->IsEditAndContinueEnabled())
{
COMPlusThrow(kInvalidOperationException, W("InvalidOperation_AssemblyNotEditable"));
}
HRESULT hr = ((EditAndContinueModule*)pModule)->ApplyEditAndContinue(metadataDeltaLength, metadataDelta, ilDeltaLength, ilDelta);
if (FAILED(hr))
{
COMPlusThrow(kInvalidOperationException, W("InvalidOperation_EditFailed"));
}
}
#else
COMPlusThrow(kNotImplementedException);
#endif

END_QCALL;
}
2 changes: 2 additions & 0 deletions src/coreclr/vm/assemblynative.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ class AssemblyNative
static void QCALLTYPE TraceAssemblyResolveHandlerInvoked(LPCWSTR assemblyName, LPCWSTR handlerName, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath);
static void QCALLTYPE TraceAssemblyLoadFromResolveHandlerInvoked(LPCWSTR assemblyName, bool isTrackedAssembly, LPCWSTR requestingAssemblyPath, LPCWSTR requestedAssemblyPath);
static void QCALLTYPE TraceSatelliteSubdirectoryPathProbed(LPCWSTR filePath, HRESULT hr);

static void QCALLTYPE ApplyUpdate(QCall::AssemblyHandle assembly, UINT8* metadataDelta, INT32 metadataDeltaLength, UINT8* ilDelta, INT32 ilDeltaLength, UINT8* pdbDelta, INT32 pdbDeltaLength);
};

#endif
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ FCFuncEnd()

FCFuncStart(gAssemblyExtensionsFuncs)
QCFuncElement("InternalTryGetRawMetadata", AssemblyNative::InternalTryGetRawMetadata)
QCFuncElement("ApplyUpdate", AssemblyNative::ApplyUpdate)
FCFuncEnd()

FCFuncStart(gAssemblyLoadContextFuncs)
Expand Down
40 changes: 26 additions & 14 deletions src/coreclr/vm/encee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,9 @@ HRESULT EditAndContinueModule::ApplyEditAndContinue(
// Ensure the metadata is RW.
EX_TRY
{
// ConvertMetadataToRWForEnC should only ever be called on EnC capable files.
// ConvertMDInternalToReadWrite should only ever be called on EnC capable files.
_ASSERTE(IsEditAndContinueCapable()); // this also checks that the file is EnC capable
GetFile()->ConvertMetadataToRWForEnC();
GetFile()->ConvertMDInternalToReadWrite();
}
EX_CATCH_HRESULT(hr);

Expand Down Expand Up @@ -310,10 +310,13 @@ HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod)
CONTRACTL_END;

// Notify the debugger of the update
HRESULT hr = g_pDebugInterface->UpdateFunction(pMethod, m_applyChangesCount);
if (FAILED(hr))
if (CORDebuggerAttached())
{
return hr;
HRESULT hr = g_pDebugInterface->UpdateFunction(pMethod, m_applyChangesCount);
if (FAILED(hr))
{
return hr;
}
}

// Notify the JIT that we've got new IL for this method
Expand Down Expand Up @@ -375,7 +378,10 @@ HRESULT EditAndContinueModule::AddMethod(mdMethodDef token)
// Class isn't loaded yet, don't have to modify any existing EE data structures beyond the metadata.
// Just notify debugger and return.
LOG((LF_ENC, LL_INFO100, "EnCModule::AM class %p not loaded, our work is done\n", parentTypeDef));
hr = g_pDebugInterface->UpdateNotYetLoadedFunction(token, this, m_applyChangesCount);
if (CORDebuggerAttached())
{
hr = g_pDebugInterface->UpdateNotYetLoadedFunction(token, this, m_applyChangesCount);
}
return hr;
}

Expand All @@ -391,12 +397,15 @@ HRESULT EditAndContinueModule::AddMethod(mdMethodDef token)
return hr;
}

// Tell the debugger about the new method so it get's the version number properly
hr = g_pDebugInterface->AddFunction(pMethod, m_applyChangesCount);
if (FAILED(hr))
// Tell the debugger about the new method so it gets the version number properly
if (CORDebuggerAttached())
{
_ASSERTE(!"Failed to add function");
LOG((LF_ENC, LL_INFO100000, "**Error** EACM::AF: Failed to add method %p to debugger with hr 0x%x\n", token));
hr = g_pDebugInterface->AddFunction(pMethod, m_applyChangesCount);
if (FAILED(hr))
{
_ASSERTE(!"Failed to add function");
LOG((LF_ENC, LL_INFO100000, "**Error** EACM::AF: Failed to add method %p to debugger with hr 0x%x\n", token));
}
}

return hr;
Expand Down Expand Up @@ -463,10 +472,13 @@ HRESULT EditAndContinueModule::AddField(mdFieldDef token)
}

// Tell the debugger about the new field
hr = g_pDebugInterface->AddField(pField, m_applyChangesCount);
if (FAILED(hr))
if (CORDebuggerAttached())
{
LOG((LF_ENC, LL_INFO100000, "**Error** EACM::AF: Failed to add field %p to debugger with hr 0x%x\n", token));
hr = g_pDebugInterface->AddField(pField, m_applyChangesCount);
if (FAILED(hr))
{
LOG((LF_ENC, LL_INFO100000, "**Error** EACM::AF: Failed to add field %p to debugger with hr 0x%x\n", token));
}
}

#ifdef _DEBUG
Expand Down
24 changes: 0 additions & 24 deletions src/coreclr/vm/pefile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,30 +747,6 @@ void PEFile::ConvertMDInternalToReadWrite()
}
}

void PEFile::ConvertMetadataToRWForEnC()
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;

// This should only ever be called on EnC capable files.
// One can check this using Module::IsEditAndContinueCapable().

// This should only be called if we're debugging, stopped, and on the helper thread.
_ASSERTE(CORDebuggerAttached());
_ASSERTE((g_pDebugInterface != NULL) && g_pDebugInterface->ThisIsHelperThread());
_ASSERTE((g_pDebugInterface != NULL) && g_pDebugInterface->IsStopped());

// Convert the metadata to RW for Edit and Continue, properly replacing the metadata import interface pointer and
// properly preserving the old importer. This will be called before the EnC system tries to apply a delta to the module's
// metadata. ConvertMDInternalToReadWrite() does that quite nicely for us.
ConvertMDInternalToReadWrite();
}

void PEFile::OpenMDImport_Unsafe()
{
CONTRACTL
Expand Down
3 changes: 1 addition & 2 deletions src/coreclr/vm/pefile.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,6 @@ class PEFile
void OpenImporter();
void OpenEmitter();

void ConvertMDInternalToReadWrite();
void ReleaseMetadataInterfaces(BOOL bDestructor, BOOL bKeepNativeData=FALSE);


Expand Down Expand Up @@ -536,7 +535,7 @@ class PEFile

static PEFile* Dummy();
void MarkNativeImageInvalidIfOwned();
void ConvertMetadataToRWForEnC();
void ConvertMDInternalToReadWrite();

protected:
PTR_ICLRPrivAssembly m_pHostAssembly;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3673,4 +3673,10 @@
<data name="ResourceManager_ReflectionNotAllowed" xml:space="preserve">
<value>Use of ResourceManager for custom types is disabled. Set the MSBuild Property CustomResourceTypesSupport to true in order to enable it.</value>
</data>
<data name="InvalidOperation_AssemblyNotEditable" xml:space="preserve">
<value>The assembly can not be edited or changed.</value>
</data>
<data name="InvalidOperation_EditFailed" xml:space="preserve">
<value>The assembly update failed.</value>
</data>
</root>
Loading