--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Reflection.Metadata.ApplyUpdate.Test
+{
+ public class FirstCallAfterUpdate {
+ public FirstCallAfterUpdate() {}
+ public string Method1 (string s) {
+ return s + " STRING";
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Reflection.Metadata.ApplyUpdate.Test
+{
+ public class FirstCallAfterUpdate {
+ public FirstCallAfterUpdate() {}
+ public string Method1(string s) {
+ return "NEW " + s;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Reflection.Metadata.ApplyUpdate.Test
+{
+ public class FirstCallAfterUpdate {
+ public FirstCallAfterUpdate() {}
+ public string Method1(string s) {
+ return s + "EST STRING";
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <RootNamespace>System.Runtime.Loader.Tests</RootNamespace>
+ <TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
+ <TestRuntime>true</TestRuntime>
+ <DeltaScript>deltascript.json</DeltaScript>
+ <SkipTestUtilitiesReference>true</SkipTestUtilitiesReference>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="FirstCallAfterUpdate.cs" />
+ </ItemGroup>
+</Project>
--- /dev/null
+{
+ "changes": [
+ {"document": "FirstCallAfterUpdate.cs", "update": "FirstCallAfterUpdate_v1.cs"},
+ {"document": "FirstCallAfterUpdate.cs", "update": "FirstCallAfterUpdate_v2.cs"},
+ ]
+}
+
});
}
+ [ConditionalFact(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/54617", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))]
+ void FirstCallAfterUpdate()
+ {
+ /* Tests that updating a method that has not been called before works correctly and that
+ * the JIT/interpreter doesn't have to rely on cached baseline data. */
+ ApplyUpdateUtil.TestCase(static () =>
+ {
+ var assm = typeof (ApplyUpdate.Test.FirstCallAfterUpdate).Assembly;
+
+ var o = new ApplyUpdate.Test.FirstCallAfterUpdate ();
+
+ ApplyUpdateUtil.ApplyUpdate(assm);
+ ApplyUpdateUtil.ApplyUpdate(assm);
+
+ string r = o.Method1("NEW");
+
+ Assert.Equal("NEWEST STRING", r);
+ });
+ }
[ConditionalFact(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/52993", TestRuntimes.Mono)]
<ProjectReference Include="ApplyUpdate\System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate\System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate.csproj" />
<ProjectReference Include="ApplyUpdate\System.Reflection.Metadata.ApplyUpdate.Test.LambdaBodyChange\System.Reflection.Metadata.ApplyUpdate.Test.LambdaBodyChange.csproj" />
<ProjectReference Include="ApplyUpdate\System.Reflection.Metadata.ApplyUpdate.Test.LambdaCapturesThis\System.Reflection.Metadata.ApplyUpdate.Test.LambdaCapturesThis.csproj" />
+ <ProjectReference Include="ApplyUpdate\System.Reflection.Metadata.ApplyUpdate.Test.FirstCallAfterUpdate\System.Reflection.Metadata.ApplyUpdate.Test.FirstCallAfterUpdate.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetOS)' == 'Browser'">
<TrimmerRootAssembly Condition="$([System.String]::Copy('%(ResolvedFileToPublish.FileName)%(ResolvedFileToPublish.Extension)').EndsWith('System.Reflection.Metadata.ApplyUpdate.Test.CustomAttributeUpdate.dll'))" Include="%(ResolvedFileToPublish.FullPath)" />
<TrimmerRootAssembly Condition="$([System.String]::Copy('%(ResolvedFileToPublish.FileName)%(ResolvedFileToPublish.Extension)').EndsWith('System.Reflection.Metadata.ApplyUpdate.Test.LambdaBodyChange.dll'))" Include="%(ResolvedFileToPublish.FullPath)" />
<TrimmerRootAssembly Condition="$([System.String]::Copy('%(ResolvedFileToPublish.FileName)%(ResolvedFileToPublish.Extension)').EndsWith('System.Reflection.Metadata.ApplyUpdate.Test.LambdaCapturesThis.dll'))" Include="%(ResolvedFileToPublish.FullPath)" />
+ <TrimmerRootAssembly Condition="$([System.String]::Copy('%(ResolvedFileToPublish.FileName)%(ResolvedFileToPublish.Extension)').EndsWith('System.Reflection.Metadata.ApplyUpdate.Test.FirstCallAfterUpdate.dll'))" Include="%(ResolvedFileToPublish.FullPath)" />
</ItemGroup>
</Target>
if (G_LIKELY (*idx < table_info_get_rows (*t) && !any_modified))
return;
+ /* FIXME: when adding methods this won't work anymore. We will need to update the delta
+ * images' suppressed columns (see the Note in pass2 about
+ * CMiniMdRW::m_SuppressedDeltaColumns) with the baseline values. */
+ /* The only column from the updates that matters the RVA, which is looked up elsewhere. */
+ if (tbl_index == MONO_TABLE_METHOD)
+ return;
+
GList *list = info->delta_image;
MonoImage *dmeta;
int ridx;
MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG];
int rows = table_info_get_rows (table_enclog);
+ /* NOTE: Suppressed colums
+ *
+ * Certain column values in some tables in the deltas are not meant to be applied over the
+ * previous generation. See CMiniMdRW::m_SuppressedDeltaColumns in CoreCLR. For example the
+ * MONO_METHOD_PARAMLIST column in MONO_TABLE_METHOD is always 0 in an update - for modified
+ * rows the previous value must be carried over. For added rows, it is supposed to be
+ * initialized to the end of the param table and updated with the "Param create" func code
+ * in subsequent EnCLog records.
+ *
+ * For mono's immutable model (where we don't change the baseline image data), we will need
+ * to mutate the delta image tables to incorporate the suppressed column values from the
+ * previous generation.
+ *
+ * For Baseline capabilities, the only suppressed column is MONO_METHOD_PARAMLIST - which we
+ * can ignore because we don't do anything with param updates and the only column we care
+ * about is MONO_METHOD_RVA which gets special case treatment with set_update_method().
+ *
+ * But when we implement additional capabilities (for example UpdateParameters), we will
+ * need to start mutating the delta image tables to pick up the suppressed column values.
+ * Fortunately whether we get the delta from the debugger or from the runtime API, we always
+ * have it in writable memory (and not mmap-ed pages), so we can rewrite the table values.
+ */
+
gboolean assemblyref_updated = FALSE;
for (int i = 0; i < rows ; ++i) {
guint32 cols [MONO_ENCLOG_SIZE];