diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index d51bda348b4ad..f6338d7c04cc8 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -3679,4 +3679,7 @@
The assembly update failed.
-
\ No newline at end of file
+
+ Method body replacement not supported in this runtime.
+
+
diff --git a/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs b/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs
index c254e1b67e2be..050c3266c0970 100644
--- a/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs
+++ b/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs
@@ -12,9 +12,7 @@ class NonRuntimeAssembly : Assembly
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/45689", platforms: TestPlatforms.AnyUnix, runtimes: TestRuntimes.Mono)]
- [PlatformSpecific(TestPlatforms.Windows)]
- [SkipOnMono("Not yet implemented on Mono")]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/45689", platforms: ~TestPlatforms.Windows, runtimes: TestRuntimes.CoreCLR)]
public static void ApplyUpdateInvalidParameters()
{
// Dummy delta arrays
diff --git a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml
index a80a66e4a682a..ce6945b1108ee 100644
--- a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml
+++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml
@@ -548,11 +548,6 @@
-
-
-
-
-
@@ -637,5 +632,12 @@
+
+
+
+
+
+
+
diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs
index 2f6e1e65e557c..337f1f516b1e6 100644
--- a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs
+++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Runtime.CompilerServices;
+
namespace System.Reflection.Metadata
{
public static class AssemblyExtensions
@@ -8,6 +10,59 @@ public static class AssemblyExtensions
[CLSCompliant(false)]
public static unsafe bool TryGetRawMetadata(this Assembly assembly, out byte* blob, out int length) => throw new NotImplementedException();
- public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) => throw new NotImplementedException();
+ ///
+ /// Updates the specified assembly using the provided metadata, IL and PDB deltas.
+ ///
+ ///
+ /// 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.
+ ///
+ /// The assembly to update.
+ /// The metadata changes to be applied.
+ /// The IL changes to be applied.
+ /// The PDB changes to be applied.
+ /// The assembly argument is null.
+ /// The update could not be applied.
+ public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta)
+ {
+ if (assembly is not RuntimeAssembly runtimeAssembly)
+ {
+ if (assembly is null) throw new ArgumentNullException(nameof(assembly));
+ throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
+ }
+
+ // System.Private.CoreLib is not editable
+ if (runtimeAssembly == typeof(AssemblyExtensions).Assembly)
+ throw new InvalidOperationException (SR.InvalidOperation_AssemblyNotEditable);
+
+#if !FEATURE_METADATA_UPDATE
+ throw new NotSupportedException (SR.NotSupported_MethodBodyReplacement);
+#else
+ unsafe
+ {
+ IntPtr monoAssembly = runtimeAssembly.GetUnderlyingNativeHandle ();
+ fixed (byte* metadataDeltaPtr = metadataDelta, ilDeltaPtr = ilDelta, pdbDeltaPtr = pdbDelta)
+ {
+ ApplyUpdate_internal(monoAssembly, metadataDeltaPtr, metadataDelta.Length, ilDeltaPtr, ilDelta.Length, pdbDeltaPtr, pdbDelta.Length);
+ }
+ }
+#endif
+ }
+
+ internal static void ApplyUpdateSdb(Assembly assembly, byte[] metadataDelta, byte[] ilDelta, byte[]? pdbDelta)
+ {
+ ReadOnlySpan md = metadataDelta;
+ ReadOnlySpan il = ilDelta;
+ ReadOnlySpan dpdb = pdbDelta == null ? default : pdbDelta;
+ ApplyUpdate (assembly, md, il, dpdb);
+ }
+
+#if FEATURE_METADATA_UPDATE
+ [MethodImpl (MethodImplOptions.InternalCall)]
+ private static unsafe extern void ApplyUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length, byte *dpdb_bytes, int dpdb_length);
+#endif
+
}
}
diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs
index 58c0efb6c90d0..e54e4dda91c8e 100644
--- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs
+++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs
@@ -17,24 +17,5 @@ public static bool IsDynamicCodeCompiled
[Intrinsic] // the JIT/AOT compiler will change this flag to false for FullAOT scenarios, otherwise true
get => IsDynamicCodeCompiled;
}
-
-#if !FEATURE_METADATA_UPDATE
- internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) {
- throw new NotSupportedException ("Method body replacement not supported in this runtime");
- }
-#else
- [MethodImplAttribute (MethodImplOptions.InternalCall)]
- private static unsafe extern void LoadMetadataUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length);
-
- internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) {
- unsafe {
- fixed (byte* dmeta_bytes = dmeta_data)
- fixed (byte* dil_bytes = dil_data) {
- IntPtr mono_assembly = ((RuntimeAssembly)assm).GetUnderlyingNativeHandle ();
- LoadMetadataUpdate_internal (mono_assembly, dmeta_bytes, dmeta_data.Length, dil_bytes, dil_data.Length);
- }
- }
- }
-#endif
}
}
diff --git a/src/mono/mono/metadata/icall-decl.h b/src/mono/mono/metadata/icall-decl.h
index f9ce2f0bb76f9..1d233f24600d1 100644
--- a/src/mono/mono/metadata/icall-decl.h
+++ b/src/mono/mono/metadata/icall-decl.h
@@ -239,7 +239,7 @@ ICALL_EXPORT void ves_icall_System_Runtime_Intrinsics_X86_X86Base___cpuidex (int
#endif
#ifdef ENABLE_METADATA_UPDATE
-ICALL_EXPORT void ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dil_bytes, int32_t dil_len);
+ICALL_EXPORT void ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dil_bytes, int32_t dil_len, gconstpointer dpdb_bytes, int32_t dpdb_len);
#endif
#endif // __MONO_METADATA_ICALL_DECL_H__
diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h
index ac9d50b0dde13..73c3dfca687c4 100644
--- a/src/mono/mono/metadata/icall-def-netcore.h
+++ b/src/mono/mono/metadata/icall-def-netcore.h
@@ -239,6 +239,11 @@ HANDLES(FILEDI_1, "get_marshal_info", ves_icall_System_Reflection_FieldInfo_get_
HANDLES(FILEDI_2, "internal_from_handle_type", ves_icall_System_Reflection_FieldInfo_internal_from_handle_type, MonoReflectionField, 2, (MonoClassField_ref, MonoType_ref))
+#ifdef ENABLE_METADATA_UPDATE
+ICALL_TYPE(ASSMEXT, "System.Reflection.Metadata.AssemblyExtensions", ASSMEXT_1)
+NOHANDLES(ICALL(ASSMEXT_1, "ApplyUpdate_internal", ves_icall_AssemblyExtensions_ApplyUpdate))
+#endif
+
ICALL_TYPE(MBASE, "System.Reflection.MethodBase", MBASE_1)
HANDLES(MBASE_1, "GetCurrentMethod", ves_icall_GetCurrentMethod, MonoReflectionMethod, 0, ())
@@ -324,11 +329,6 @@ HANDLES_REUSE_WRAPPER(MPROP_3, "get_metadata_token", ves_icall_reflection_get_to
HANDLES(MPROP_4, "get_property_info", ves_icall_RuntimePropertyInfo_get_property_info, void, 3, (MonoReflectionProperty, MonoPropertyInfo_ref, PInfo))
HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_RuntimePropertyInfo_internal_from_handle_type, MonoReflectionProperty, 2, (MonoProperty_ptr, MonoType_ptr))
-#ifdef ENABLE_METADATA_UPDATE
-ICALL_TYPE(RUNF, "System.Runtime.CompilerServices.RuntimeFeature", RUNF_1)
-NOHANDLES(ICALL(RUNF_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate))
-#endif
-
ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1)
HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject))
HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr))
diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c
index 7dafe39c94f11..7f2d87314cfd9 100644
--- a/src/mono/mono/metadata/icall.c
+++ b/src/mono/mono/metadata/icall.c
@@ -5842,15 +5842,17 @@ ves_icall_Mono_Runtime_DumpStateTotal (guint64 *portable_hash, guint64 *unportab
#ifdef ENABLE_METADATA_UPDATE
void
-ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoAssembly *assm,
+ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm,
gconstpointer dmeta_bytes, int32_t dmeta_len,
- gconstpointer dil_bytes, int32_t dil_len)
+ gconstpointer dil_bytes, int32_t dil_len,
+ gconstpointer dpdb_bytes, int32_t dpdb_len)
{
ERROR_DECL (error);
g_assert (assm);
g_assert (dmeta_len >= 0);
MonoImage *image_base = assm->image;
g_assert (image_base);
+ // TODO: use dpdb_bytes
MonoDomain *domain = mono_domain_get ();
mono_image_load_enc_delta (domain, image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error);
diff --git a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs
index 7188782df8b5e..89d025403bd4d 100644
--- a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs
+++ b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs
@@ -1,79 +1,104 @@
using System;
using System.Reflection;
+using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Collections.Generic;
namespace MonoDelta {
- public class DeltaHelper {
- const string name = "System.Runtime.CompilerServices.RuntimeFeature";
+ public class DeltaHelper {
+ private static Action _updateMethod;
- private static MethodBase _updateMethod;
+ private static Action UpdateMethod => _updateMethod ?? InitUpdateMethod();
- private static MethodBase UpdateMethod => _updateMethod ?? InitUpdateMethod();
+ private static Action InitUpdateMethod ()
+ {
+ var monoType = typeof(System.Reflection.Metadata.AssemblyExtensions);
+ const string methodName = "ApplyUpdate";
+ var mi = monoType.GetMethod (methodName, BindingFlags.Public | BindingFlags.Static);
+ if (mi == null)
+ throw new Exception ($"Couldn't get {methodName} from {monoType.FullName}");
+ _updateMethod = MakeUpdateMethod (mi); //Delegate.CreateDelegate (typeof(Action), mi) as Action;
+ return _updateMethod;
+ }
- private static MethodBase InitUpdateMethod ()
- {
- var monoType = Type.GetType (name, throwOnError: true);
- if (monoType == null)
- throw new Exception ($"Couldn't get the type {name}");
- _updateMethod = monoType.GetMethod ("LoadMetadataUpdate", BindingFlags.NonPublic | BindingFlags.Static);
- if (_updateMethod == null)
- throw new Exception ($"Couldn't get LoadMetadataUpdate from {name}");
- return _updateMethod;
- }
+ private static Action MakeUpdateMethod (MethodInfo applyUpdate)
+ {
+ // Make
+ // void ApplyUpdateArray (Assembly a, byte[] dmeta, byte[] dil, byte[] dpdb)
+ // {
+ // ApplyUpdate (a, (ReadOnlySpan)dmeta, (ReadOnlySpan)dil, (ReadOnlySpan)dpdb);
+ // }
+ var dm = new DynamicMethod ("CallApplyUpdate", typeof(void), new Type[] { typeof(Assembly), typeof(byte[]), typeof(byte[]), typeof(byte[])}, typeof (DeltaHelper).Module);
+ var ilg = dm.GetILGenerator ();
+ var conv = typeof(ReadOnlySpan).GetMethod("op_Implicit", new Type[] {typeof(byte[])});
- private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data)
- {
- UpdateMethod.Invoke (null, new object [] { assm, dmeta_data, dil_data});
- }
+ ilg.Emit (OpCodes.Ldarg_0);
+ ilg.Emit (OpCodes.Ldarg_1);
+ ilg.Emit (OpCodes.Call, conv);
+ ilg.Emit (OpCodes.Ldarg_2);
+ ilg.Emit (OpCodes.Call, conv);
+ ilg.Emit (OpCodes.Ldarg_3);
+ ilg.Emit (OpCodes.Call, conv);
+ ilg.Emit (OpCodes.Call, applyUpdate);
+ ilg.Emit (OpCodes.Ret);
- DeltaHelper () { }
+ return dm.CreateDelegate(typeof(Action)) as Action;
+ }
- public static DeltaHelper Make ()
- {
- return new DeltaHelper ();
- }
+ private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data, byte[] dpdb_data)
+ {
+ UpdateMethod (assm, dmeta_data, dil_data, dpdb_data);
+ }
- public static void InjectUpdate (string assemblyName, string dmeta_base64, string dil_base64) {
- var an = new AssemblyName (assemblyName);
- Assembly assm = null;
- /* TODO: non-default ALCs */
- foreach (var candidate in AssemblyLoadContext.Default.Assemblies) {
- if (candidate.GetName().Name == an.Name) {
- assm = candidate;
- break;
- }
- }
- if (assm == null)
- throw new ArgumentException ("assemblyName");
- var dmeta_data = Convert.FromBase64String (dmeta_base64);
- var dil_data = Convert.FromBase64String (dil_base64);
- LoadMetadataUpdate (assm, dmeta_data, dil_data);
- }
+ DeltaHelper () { }
- private Dictionary assembly_count = new Dictionary ();
+ public static DeltaHelper Make ()
+ {
+ return new DeltaHelper ();
+ }
- public void Update (Assembly assm) {
- int count;
- if (!assembly_count.TryGetValue (assm, out count))
- count = 1;
- else
- count++;
- assembly_count [assm] = count;
+ public static void InjectUpdate (string assemblyName, string dmeta_base64, string dil_base64) {
+ var an = new AssemblyName (assemblyName);
+ Assembly assm = null;
+ /* TODO: non-default ALCs */
+ foreach (var candidate in AssemblyLoadContext.Default.Assemblies) {
+ if (candidate.GetName().Name == an.Name) {
+ assm = candidate;
+ break;
+ }
+ }
+ if (assm == null)
+ throw new ArgumentException ("assemblyName");
+ var dmeta_data = Convert.FromBase64String (dmeta_base64);
+ var dil_data = Convert.FromBase64String (dil_base64);
+ byte[] dpdb_data = null;
+ LoadMetadataUpdate (assm, dmeta_data, dil_data, dpdb_data);
+ }
- /* FIXME WASM: Location is empty on wasm. Make up a name based on Name */
- string basename = assm.Location;
- if (basename == "")
- basename = assm.GetName().Name + ".dll";
- Console.WriteLine ($"Apply Delta Update for {basename}, revision {count}");
+ private Dictionary assembly_count = new Dictionary ();
- string dmeta_name = $"{basename}.{count}.dmeta";
- string dil_name = $"{basename}.{count}.dil";
- byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name);
- byte[] dil_data = System.IO.File.ReadAllBytes (dil_name);
+ public void Update (Assembly assm) {
+ int count;
+ if (!assembly_count.TryGetValue (assm, out count))
+ count = 1;
+ else
+ count++;
+ assembly_count [assm] = count;
- LoadMetadataUpdate (assm, dmeta_data, dil_data);
- }
- }
+ /* FIXME WASM: Location is empty on wasm. Make up a name based on Name */
+ string basename = assm.Location;
+ if (basename == "")
+ basename = assm.GetName().Name + ".dll";
+ Console.WriteLine ($"Apply Delta Update for {basename}, revision {count}");
+
+ string dmeta_name = $"{basename}.{count}.dmeta";
+ string dil_name = $"{basename}.{count}.dil";
+ byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name);
+ byte[] dil_data = System.IO.File.ReadAllBytes (dil_name);
+ byte[] dpdb_data = null; // TODO also use the dpdb data
+
+ LoadMetadataUpdate (assm, dmeta_data, dil_data, dpdb_data);
+ }
+ }
}