diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis.cs index 186144e290e354..a5ec0d11b1f5c0 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis.cs @@ -7,19 +7,21 @@ namespace System.Reflection.Metadata.ApplyUpdate.Test { public class AddLambdaCapturingThis { - public AddLambdaCapturingThis () { - field = "abcd"; - } + public AddLambdaCapturingThis() + { + field = "abcd"; + } - public string GetField => field; + public string GetField => field; - private string field; + private string field; - public string TestMethod () { - // capture 'this' but no locals - Func fn = s => field; - return "123"; - } + public string TestMethod() + { + // capture 'this' but no locals + Func fn = s => field; + return "123"; + } } } diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis_v1.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis_v1.cs index 22dd869021dc3f..44ff73ab1d591c 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis_v1.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis_v1.cs @@ -7,23 +7,21 @@ namespace System.Reflection.Metadata.ApplyUpdate.Test { public class AddLambdaCapturingThis { - public AddLambdaCapturingThis () { - field = "abcd"; - } + public AddLambdaCapturingThis() + { + field = "abcd"; + } - public string GetField => field; + public string GetField => field; - private string field; - - public string TestMethod () { - // capture 'this' but no locals - Func fn = s => NewMethod (s + field, 42); - return fn ("123"); - } - - private string NewMethod (string s, int i) { - return i.ToString() + s; - } + private string field; + public string TestMethod() + { + // capture 'this' but no locals + Func fn = s => field; + Func fn2 = s => "42" + s + field; + return fn2 ("123"); + } } } diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/AddNestedClass.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/AddNestedClass.cs new file mode 100644 index 00000000000000..d5bd6ce27fd0eb --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/AddNestedClass.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + public class AddNestedClass + { + public AddNestedClass() + { + } + + public string TestMethod() + { + return "123"; + } + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/AddNestedClass_v1.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/AddNestedClass_v1.cs new file mode 100644 index 00000000000000..04fc6992631d9a --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/AddNestedClass_v1.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + public class AddNestedClass + { + public AddNestedClass() + { + } + + public string TestMethod() + { + var n = new Nested(); + n.f = "123"; + return n.M(); + } + + private class Nested { + public Nested() { } + internal string f; + public string M () { + return f + "456"; + } + } + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass.csproj new file mode 100644 index 00000000000000..26f33484620943 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass.csproj @@ -0,0 +1,11 @@ + + + System.Runtime.Loader.Tests + $(NetCoreAppCurrent) + true + deltascript.json + + + + + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/deltascript.json b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/deltascript.json new file mode 100644 index 00000000000000..e70a3b3bbafbc9 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass/deltascript.json @@ -0,0 +1,6 @@ +{ + "changes": [ + {"document": "AddNestedClass.cs", "update": "AddNestedClass_v1.cs"}, + ] +} + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/AddStaticField.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/AddStaticField.cs new file mode 100644 index 00000000000000..6ac1360c3581bf --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/AddStaticField.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + public class AddStaticField + { + public AddStaticField () { + } + + public string GetField => s_field; + + private static string s_field; + + public void TestMethod () { + s_field = "abcd"; + } + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/AddStaticField_v1.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/AddStaticField_v1.cs new file mode 100644 index 00000000000000..f9282469a4fce7 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/AddStaticField_v1.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + public class AddStaticField + { + public AddStaticField () { + } + + public string GetField => s_field2; + + private static string s_field; + + private static string s_field2; + + public void TestMethod () { + s_field = "spqr"; + s_field2 = "4567"; + } + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField.csproj new file mode 100644 index 00000000000000..0a3f60146cac36 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField.csproj @@ -0,0 +1,11 @@ + + + System.Runtime.Loader.Tests + $(NetCoreAppCurrent) + true + deltascript.json + + + + + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/deltascript.json b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/deltascript.json new file mode 100644 index 00000000000000..3c0d7a0fc68665 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField/deltascript.json @@ -0,0 +1,6 @@ +{ + "changes": [ + {"document": "AddStaticField.cs", "update": "AddStaticField_v1.cs"}, + ] +} + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/AddStaticLambda.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/AddStaticLambda.cs new file mode 100644 index 00000000000000..9e759e6075b5b7 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/AddStaticLambda.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + public class AddStaticLambda + { + public string TestMethod() + { + return "abcd"; + } + + public string Double(Func f) => f("") + f("1"); + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/AddStaticLambda_v1.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/AddStaticLambda_v1.cs new file mode 100644 index 00000000000000..6f9e94b08a7f43 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/AddStaticLambda_v1.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + public class AddStaticLambda + { + public string TestMethod() + { + return Double(static (s) => s + "abcd"); + } + + public string Double(Func f) => f("") + f("1"); + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda.csproj new file mode 100644 index 00000000000000..3b402585aa2982 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda.csproj @@ -0,0 +1,11 @@ + + + System.Runtime.Loader.Tests + $(NetCoreAppCurrent) + true + deltascript.json + + + + + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/deltascript.json b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/deltascript.json new file mode 100644 index 00000000000000..1d60513f4a43f9 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda/deltascript.json @@ -0,0 +1,6 @@ +{ + "changes": [ + {"document": "AddStaticLambda.cs", "update": "AddStaticLambda_v1.cs"}, + ] +} + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs index dd5c49c3963352..9e28894b6bbf37 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs @@ -71,6 +71,7 @@ void LambdaBodyChange() [ActiveIssue("https://github.com/dotnet/runtime/issues/54617", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] void LambdaCapturesThis() { + // Tests that changes to the body of a lambda that captures 'this' is supported. ApplyUpdateUtil.TestCase(static () => { var assm = typeof (ApplyUpdate.Test.LambdaCapturesThis).Assembly; @@ -263,25 +264,92 @@ public void AsyncMethodChanges() }); } - [ActiveIssue ("https://github.com/dotnet/runtime/issues/50249", TestRuntimes.Mono)] - [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsSupported))] - public static void TestAddLambdaCapturingThis() - { - ApplyUpdateUtil.TestCase(static () => - { - var assm = typeof(System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis).Assembly; + [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsSupported))] + public static void TestAddLambdaCapturingThis() + { + // Test that adding a lambda that captures 'this' (to a method that already has a lambda that captures 'this') is supported + ApplyUpdateUtil.TestCase(static () => + { + var assm = typeof(System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis).Assembly; + + var x = new System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis(); + + Assert.Equal("123", x.TestMethod()); + + ApplyUpdateUtil.ApplyUpdate(assm); + + string result = x.TestMethod(); + Assert.Equal("42123abcd", result); + }); + } + + [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsSupported))] + public static void TestAddStaticField() + { + // Test that adding a new static field to an existing class is supported + ApplyUpdateUtil.TestCase(static () => + { + var assm = typeof(System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField).Assembly; + + var x = new System.Reflection.Metadata.ApplyUpdate.Test.AddStaticField(); + + x.TestMethod(); + + Assert.Equal ("abcd", x.GetField); + + ApplyUpdateUtil.ApplyUpdate(assm); + + x.TestMethod(); + + string result = x.GetField; + Assert.Equal("4567", result); + }); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/63643", TestRuntimes.Mono)] + [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsSupported))] + public static void TestAddNestedClass() + { + // Test that adding a new nested class to an existing class is supported + ApplyUpdateUtil.TestCase(static () => + { + var assm = typeof(System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass).Assembly; + + var x = new System.Reflection.Metadata.ApplyUpdate.Test.AddNestedClass(); + + var r = x.TestMethod(); + + Assert.Equal ("123", r); + + ApplyUpdateUtil.ApplyUpdate(assm); + + r = x.TestMethod(); - var x = new System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis(); + Assert.Equal("123456", r); + }); + } + + [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsSupported))] + public static void TestAddStaticLambda() + { + // Test that adding a new static lambda to an existing method body is supported + ApplyUpdateUtil.TestCase(static () => + { + var assm = typeof(System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda).Assembly; - Assert.Equal("123", x.TestMethod()); + var x = new System.Reflection.Metadata.ApplyUpdate.Test.AddStaticLambda(); - ApplyUpdateUtil.ApplyUpdate(assm); + var r = x.TestMethod(); - string result = x.TestMethod(); - Assert.Equal("42123abcd", result); - }); - } + Assert.Equal ("abcd", r); + ApplyUpdateUtil.ApplyUpdate(assm); + + r = x.TestMethod(); + + Assert.Equal("abcd1abcd", r); + }); + } class NonRuntimeAssembly : Assembly { diff --git a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj index d678320e017407..e21bc7e673f5ff 100644 --- a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj +++ b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj @@ -53,6 +53,9 @@ + + + diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index cb143f5d381be6..da99b06e81b8ea 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -290,6 +290,7 @@ + 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 4a8ffce0341091..2f578bdf50ab68 100644 --- a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml +++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml @@ -647,5 +647,8 @@ + + + diff --git a/src/mono/System.Private.CoreLib/src/Mono/HotReload.cs b/src/mono/System.Private.CoreLib/src/Mono/HotReload.cs new file mode 100644 index 00000000000000..c1cd977ceb6b20 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/Mono/HotReload.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Mono.HotReload; + +// TODO: this is just a sketch, instance field additions aren't supported by Mono yet until https://github.com/dotnet/runtime/issues/63643 is fixed +#if false +internal class InstanceFieldTable +{ + // Q: Does CoreCLR EnC allow adding fields to a valuetype? + // A: No, see EEClass::AddField - if the type has layout or is a valuetype, you can't add fields to it. + + // See EnCAddedField::Allocate for a description of the CoreCLR version of this. + // + // This is substantially the same design, except instead of using dependent handles + // (ephemerons) directly from native (storing a linked list of ephemerons off the sync block + // of the instance), we use a ConditionalWeakTable from managed that's keyed on the + // instances (so the value dies when the instance dies) and whose value is another + // dictionary, keyed on the fielddef token with values that are storage for the actual field values. + // + // for reference types, the storage just stores it as an object. For valuetypes and + // primitives, the storage stores the value as a boxed value. + // + // The whole thing is basically a ConditionalWeakTable> but + // with locking on the inner dictionary. + // + + // This should behave somewhat like EditAndContinueModule::ResolveOrAddField (and EnCAddedField::Allocate) + // we want to create some storage space that has the same lifetime as the instance object. + + // // TODO: should the linker keep this if Hot Reload stuff is enabled? Hot Reload is predicated on the linker not rewriting user modules, but maybe trimming SPC is ok? + internal static FieldStore GetInstanceFieldFieldStore(object inst, RuntimeTypeHandle type, uint fielddef_token) + => _singleton.GetOrCreateInstanceFields(inst).LookupOrAdd(type, fielddef_token); + + private static InstanceFieldTable _singleton = new(); + + private ConditionalWeakTable _table; + + private InstanceFieldTable() + { + _table = new(); + } + + private InstanceFields GetOrCreateInstanceFields(object key) + => _table.GetOrCreateValue(key); + + private class InstanceFields + { + private Dictionary _fields; + private object _lock; + + public InstanceFields() + { + _fields = new(); + _lock = new(); + } + + public FieldStore LookupOrAdd(RuntimeTypeHandle type, uint key) + { + if (_fields.TryGetValue(key, out FieldStore? v)) + return v; + lock (_lock) + { + if (_fields.TryGetValue (key, out FieldStore? v2)) + return v2; + + FieldStore s = FieldStore.Create(type); + _fields.Add(key, s); + return s; + } + } + } + +} +#endif + +// This is similar to System.Diagnostics.EditAndContinueHelper in CoreCLR, except instead of +// having the allocation logic in native (see EditAndContinueModule::ResolveOrAllocateField, +// and EnCSyncBlockInfo::ResolveOrAllocateField), the logic is in managed. +// +// Additionally Mono uses this for storing added static fields. +[StructLayout(LayoutKind.Sequential)] +internal class FieldStore +{ + // keep in sync with hot_reload-internals.h + private object? _loc; + + private FieldStore (object? loc) + { + _loc = loc; + } + + public object? Location => _loc; + + public static FieldStore Create (RuntimeTypeHandle type) + { + Type t = Type.GetTypeFromHandle(type) ?? throw new ArgumentException(nameof(type), "Type handle was null"); + object? loc; + if (t.IsPrimitive || t.IsValueType) + loc = RuntimeHelpers.GetUninitializedObject(t); + else if (t.IsClass || t.IsInterface) + loc = null; + else + throw new ArgumentException("EnC: Expected a primitive, valuetype, class or interface field"); + /* FIXME: do we want FieldStore to be pinned? */ + return new FieldStore(loc); + } +} diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs index 973eb43d91ca76..afac9bf5e88da7 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.cs @@ -55,7 +55,8 @@ public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDel private static string InitializeApplyUpdateCapabilities() { - return ApplyUpdateEnabled(justComponentCheck: 1) != 0 ? "Baseline" : string.Empty ; + const string caps = "Baseline AddMethodToExistingType AddStaticFieldToExistingType NewTypeDefinition"; + return ApplyUpdateEnabled(justComponentCheck: 1) != 0 ? caps : string.Empty ; } [MethodImpl (MethodImplOptions.InternalCall)] diff --git a/src/mono/mono/component/CMakeLists.txt b/src/mono/mono/component/CMakeLists.txt index 595f6d889442bf..faab69a9b68e9c 100644 --- a/src/mono/mono/component/CMakeLists.txt +++ b/src/mono/mono/component/CMakeLists.txt @@ -41,6 +41,7 @@ list(APPEND components ) set(${MONO_HOT_RELOAD_COMPONENT_NAME}-sources ${MONO_COMPONENT_PATH}/hot_reload.c + ${MONO_COMPONENT_PATH}/hot_reload-internals.h ${MONO_COMPONENT_PATH}/hot_reload.h ) set(${MONO_HOT_RELOAD_COMPONENT_NAME}-stub-sources diff --git a/src/mono/mono/component/hot_reload-internals.h b/src/mono/mono/component/hot_reload-internals.h new file mode 100644 index 00000000000000..36d3606e17e4a2 --- /dev/null +++ b/src/mono/mono/component/hot_reload-internals.h @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifndef _MONO_COMPONENT_HOT_RELOAD_INTERNALS_H +#define _MONO_COMPONENT_HOT_RELOAD_INTERNALS_H + +#include +#include "mono/metadata/object-forward.h" +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/class-internals.h" + +/* Execution-time info for an updated class. */ +typedef struct _MonoClassRuntimeMetadataUpdateInfo { + MonoCoopMutex static_fields_lock; /* protects the static_fields hashtable. Values can be used outside the lock (since they're allocated pinned). */ + MonoGHashTable *static_fields; /* key is field token, value is a pinned managed object: either a boxed valuetype (the static field address is the value address) or a Mono.HotReload.FieldStore object (in which case the static field address is the address of the _loc field in the object.) */ + gboolean inited; +} MonoClassRuntimeMetadataUpdateInfo; + +/* Class-specific metadata update info. See + * mono_class_get_metadata_update_info() Note that this info is associated with + * class _definitions_ that can be edited, so primitives, generic instances, + * arrays, pointers, etc do not have this info. + */ +struct _MonoClassMetadataUpdateInfo { + /* FIXME: use a struct that allocates out of the MonoClass mempool! or maybe add the GArray + * to the BaselineInfo for the image and cleanup from there. */ + GSList *added_members; /* a set of Method or Field table tokens of any methods or fields added to this class, allocated from the MonoClass mempool */ + + GPtrArray *added_fields; /* a set of MonoClassMetadataUpdateField* values for every added field. */ + + MonoClassRuntimeMetadataUpdateInfo runtime; +}; + + +/* Keep in sync with Mono.HotReload.FieldStore in managed */ +typedef struct _MonoHotReloadFieldStoreObject { + MonoObject object; + MonoObject *_loc; +} MonoHotReloadFieldStoreObject; + +typedef struct _MonoClassMetadataUpdateField { + MonoClassField field; + uint32_t generation; /* when this field was added */ + uint32_t token; /* the Field table token where this field was defined. (this won't make + * sense for generic instances, once EnC is supported there) */ + /* if non-zero the EnC update came before the parent class was initialized. The field is + * stored in the instance at this offset. MonoClassField:offset is -1. Not used for static + * fields. */ + int before_init_instance_offset; +} MonoClassMetadataUpdateField; + +#endif/*_MONO_COMPONENT_HOT_RELOAD_INTERNALS_H*/ diff --git a/src/mono/mono/component/hot_reload-stub.c b/src/mono/mono/component/hot_reload-stub.c index 6064a6a78f8758..2377b948a7b0ad 100644 --- a/src/mono/mono/component/hot_reload-stub.c +++ b/src/mono/mono/component/hot_reload-stub.c @@ -65,12 +65,27 @@ hot_reload_stub_has_modified_rows (const MonoTableInfo *table); static int hot_reload_stub_table_num_rows_slow (MonoImage *image, int table_index); -static GArray* -hot_reload_stub_get_added_methods (MonoClass *klass); - static uint32_t hot_reload_stub_method_parent (MonoImage *image, uint32_t method_index); +static void* +hot_reload_stub_metadata_linear_search (MonoImage *base_image, MonoTableInfo *base_table, const void *key, BinarySearchComparer comparer); + +static uint32_t +hot_reload_stub_field_parent (MonoImage *image, uint32_t field_index); + +static uint32_t +hot_reload_stub_get_field_idx (MonoClassField *field); + +static MonoClassField * +hot_reload_stub_get_field (MonoClass *klass, uint32_t fielddef_token); + +static gpointer +hot_reload_stub_get_static_field_addr (MonoClassField *field); + +static MonoMethod * +hot_reload_stub_find_method_by_name (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error); + static MonoComponentHotReload fn_table = { { MONO_COMPONENT_ITF_VERSION, &hot_reload_stub_available }, &hot_reload_stub_set_fastpath_data, @@ -89,8 +104,13 @@ static MonoComponentHotReload fn_table = { &hot_reload_stub_get_updated_method_ppdb, &hot_reload_stub_has_modified_rows, &hot_reload_stub_table_num_rows_slow, - &hot_reload_stub_get_added_methods, &hot_reload_stub_method_parent, + &hot_reload_stub_metadata_linear_search, + &hot_reload_stub_field_parent, + &hot_reload_stub_get_field_idx, + &hot_reload_stub_get_field, + &hot_reload_stub_get_static_field_addr, + &hot_reload_stub_find_method_by_name, }; static bool @@ -200,18 +220,49 @@ hot_reload_stub_table_num_rows_slow (MonoImage *image, int table_index) g_assert_not_reached (); /* should always take the fast path */ } -static GArray* -hot_reload_stub_get_added_methods (MonoClass *klass) +static uint32_t +hot_reload_stub_method_parent (MonoImage *image, uint32_t method_index) +{ + return 0; +} + + +static void* +hot_reload_stub_metadata_linear_search (MonoImage *base_image, MonoTableInfo *base_table, const void *key, BinarySearchComparer comparer) { return NULL; } static uint32_t -hot_reload_stub_method_parent (MonoImage *image, uint32_t method_index) +hot_reload_stub_field_parent (MonoImage *image, uint32_t field_index) { return 0; } +static uint32_t +hot_reload_stub_get_field_idx (MonoClassField *field) +{ + return 0; +} + +static MonoClassField * +hot_reload_stub_get_field (MonoClass *klass, uint32_t fielddef_token) +{ + return NULL; +} + +static gpointer +hot_reload_stub_get_static_field_addr (MonoClassField *field) +{ + return NULL; +} + +static MonoMethod * +hot_reload_stub_find_method_by_name (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error) +{ + return NULL; +} + MONO_COMPONENT_EXPORT_ENTRYPOINT MonoComponentHotReload * diff --git a/src/mono/mono/component/hot_reload.c b/src/mono/mono/component/hot_reload.c index bd8c3db635c88b..e44ae56e5f4afe 100644 --- a/src/mono/mono/component/hot_reload.c +++ b/src/mono/mono/component/hot_reload.c @@ -8,8 +8,11 @@ #include #include "mono/utils/mono-compiler.h" +#include "mono/component/hot_reload-internals.h" + #include #include "mono/metadata/assembly-internals.h" +#include "mono/metadata/mono-hash-internals.h" #include "mono/metadata/metadata-internals.h" #include "mono/metadata/metadata-update.h" #include "mono/metadata/object-internals.h" @@ -22,14 +25,18 @@ #include "mono/metadata/debug-internals.h" #include "mono/metadata/mono-debug.h" #include "mono/metadata/debug-mono-ppdb.h" +#include "mono/utils/bsearch.h" #include #include -#undef ALLOW_METHOD_ADD +#define ALLOW_CLASS_ADD +#define ALLOW_METHOD_ADD +#define ALLOW_FIELD_ADD +typedef struct _BaselineInfo BaselineInfo; typedef struct _DeltaInfo DeltaInfo; static void @@ -89,12 +96,32 @@ hot_reload_has_modified_rows (const MonoTableInfo *table); static int hot_reload_table_num_rows_slow (MonoImage *image, int table_index); -static GArray* -hot_reload_get_added_methods (MonoClass *klass); +static GSList* +hot_reload_get_added_members (MonoClass *klass); static uint32_t hot_reload_method_parent (MonoImage *base, uint32_t method_token); +static void* +hot_reload_metadata_linear_search (MonoImage *base_image, MonoTableInfo *base_table, const void *key, BinarySearchComparer comparer); + +static uint32_t +hot_reload_field_parent (MonoImage *base, uint32_t field_token); + +static uint32_t +hot_reload_get_field_idx (MonoClassField *field); + +static MonoClassField * +hot_reload_get_field (MonoClass *klass, uint32_t fielddef_token); + +static gpointer +hot_reload_get_static_field_addr (MonoClassField *field); + +static MonoMethod * +hot_reload_find_method_by_name (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error); + +static MonoClassMetadataUpdateField * +metadata_update_field_setup_basic_info_and_resolve (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, DeltaInfo *delta_info, MonoClass *parent_klass, uint32_t fielddef_token, MonoError *error); static MonoComponentHotReload fn_table = { { MONO_COMPONENT_ITF_VERSION, &hot_reload_available }, @@ -114,8 +141,13 @@ static MonoComponentHotReload fn_table = { &hot_reload_get_updated_method_ppdb, &hot_reload_has_modified_rows, &hot_reload_table_num_rows_slow, - &hot_reload_get_added_methods, &hot_reload_method_parent, + &hot_reload_metadata_linear_search, + &hot_reload_field_parent, + &hot_reload_get_field_idx, + &hot_reload_get_field, + &hot_reload_get_static_field_addr, + &hot_reload_find_method_by_name, }; MonoComponentHotReload * @@ -190,7 +222,7 @@ struct _DeltaInfo { /* Additional informaiton for baseline MonoImages */ -typedef struct _BaselineInfo { +struct _BaselineInfo { /* List of DeltaInfos of deltas*/ GList *delta_info; /* Tail of delta_info for fast appends */ @@ -202,9 +234,12 @@ typedef struct _BaselineInfo { /* TRUE if any published update modified an existing row */ gboolean any_modified_rows [MONO_TABLE_NUM]; - GHashTable *added_methods; /* maps each MonoClass to a GArray of added method tokens */ - GHashTable *method_parent; /* maps added methoddef tokens to typedef tokens */ -} BaselineInfo; + /* A list of MonoClassMetadataUpdateInfo* that need to be cleaned up */ + GSList *klass_info; + + /* Parents for added methods, fields, etc */ + GHashTable *member_parent; /* maps added methoddef or fielddef tokens to typedef tokens */ +}; #define DOTNET_MODIFIABLE_ASSEMBLIES "DOTNET_MODIFIABLE_ASSEMBLIES" @@ -312,11 +347,34 @@ baseline_info_init (MonoImage *image_base) return baseline_info; } +static void +klass_info_destroy (gpointer value, gpointer user_data G_GNUC_UNUSED) +{ + MonoClassMetadataUpdateInfo *info = (MonoClassMetadataUpdateInfo *)value; + /* added_members is allocated from the class mempool, don't free it here */ + /* The MonoClassMetadataUpdateField is allocated from the class mempool, don't free it here */ + g_ptr_array_free (info->added_fields, TRUE); + + if (info->runtime.static_fields) { + mono_g_hash_table_destroy (info->runtime.static_fields); + info->runtime.static_fields = NULL; + } + + mono_coop_mutex_destroy (&info->runtime.static_fields_lock); + + /* The MonoClassMetadataUpdateInfo itself is allocated from the class mempool, don't free it here */ +} + static void baseline_info_destroy (BaselineInfo *info) { if (info->method_table_update) g_hash_table_destroy (info->method_table_update); + + if (info->klass_info) { + g_slist_foreach (info->klass_info, klass_info_destroy, NULL); + g_slist_free (info->klass_info); + } g_free (info); } @@ -515,9 +573,15 @@ image_open_dmeta_from_data (MonoImage *base_image, uint32_t generation, gconstpo static DeltaInfo* image_append_delta (MonoImage *base, BaselineInfo *base_info, MonoImage *delta, DeltaInfo *delta_info); +/* common method, don't use directly, use add_method_to_baseline, add_field_to_baseline, etc */ +static void +add_member_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t member_token); + static void add_method_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t method_token, MonoDebugInformationEnc* pdb_address); +static void +add_field_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t field_token); void hot_reload_init (void) @@ -660,6 +724,33 @@ hot_reload_update_cancel (uint32_t generation) publish_unlock (); } +static void +add_class_info_to_baseline (MonoClass *klass, MonoClassMetadataUpdateInfo *klass_info) +{ + MonoImage *image = m_class_get_image (klass); + BaselineInfo *baseline_info = baseline_info_lookup (image); + baseline_info->klass_info = g_slist_prepend (baseline_info->klass_info, klass_info); +} + +static MonoClassMetadataUpdateInfo * +mono_class_get_or_add_metadata_update_info (MonoClass *klass) +{ + MonoClassMetadataUpdateInfo *info = NULL; + info = mono_class_get_metadata_update_info (klass); + if (info) + return info; + mono_loader_lock (); + info = mono_class_get_metadata_update_info (klass); + if (!info) { + info = mono_class_alloc0 (klass, sizeof (MonoClassMetadataUpdateInfo)); + add_class_info_to_baseline (klass, info); + mono_class_set_metadata_update_info (klass, info); + } + mono_loader_unlock (); + g_assert (info); + return info; +} + /* * Given a baseline and an (optional) previous delta, allocate space for new tables for the current delta. * @@ -695,12 +786,6 @@ delta_info_initialize_mutants (const MonoImage *base, const BaselineInfo *base_i guint32 rows = count->prev_gen_rows + count->inserted_rows; - MonoTableInfo *tbl = &delta->mutants [i]; - tbl->row_size = base->tables[i].row_size; - tbl->size_bitfield = base->tables[i].size_bitfield; - tbl->rows_ = rows; - - tbl->base = mono_mempool_alloc (delta->pool, tbl->row_size * rows); const MonoTableInfo *prev_table; if (!prev_delta || prev_delta->mutants [i].base == NULL) prev_table = &base->tables [i]; @@ -708,6 +793,21 @@ delta_info_initialize_mutants (const MonoImage *base, const BaselineInfo *base_i prev_table = &prev_delta->mutants [i]; g_assert (prev_table != NULL); + + MonoTableInfo *tbl = &delta->mutants [i]; + if (prev_table->rows_ == 0) { + /* table was empty in the baseline and it was empty in the prior generation, but now we have some rows. Use the format of the mutant table. */ + g_assert (prev_table->row_size == 0); + tbl->row_size = delta->delta_image->tables [i].row_size; + tbl->size_bitfield = delta->delta_image->tables [i].size_bitfield; + } else { + tbl->row_size = prev_table->row_size; + tbl->size_bitfield = prev_table->size_bitfield; + } + tbl->rows_ = rows; + g_assert (tbl->rows_ > 0 && tbl->row_size != 0); + + tbl->base = mono_mempool_alloc (delta->pool, tbl->row_size * rows); g_assert (table_info_get_rows (prev_table) == count->prev_gen_rows); /* copy the old rows and zero out the new ones */ @@ -877,8 +977,13 @@ dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) } +/* + * Finds the latest mutated version of the table given by tbl_index + * + * On success returns TRUE, modifies *t and optionally updates *delta_out + */ static gboolean -effective_table_mutant (MonoImage *base, BaselineInfo *info, int tbl_index, const MonoTableInfo **t, int idx) +effective_table_mutant (MonoImage *base, BaselineInfo *info, int tbl_index, const MonoTableInfo **t, DeltaInfo **delta_out) { GList *ptr =info->delta_info_last; uint32_t exposed_gen = hot_reload_get_thread_generation (); @@ -899,6 +1004,8 @@ effective_table_mutant (MonoImage *base, BaselineInfo *info, int tbl_index, cons g_assert (delta_info != NULL); *t = &delta_info->mutants [tbl_index]; + if (delta_out) + *delta_out = delta_info; return TRUE; } @@ -917,7 +1024,7 @@ hot_reload_effective_table_slow (const MonoTableInfo **t, int idx) if (!info) return; - gboolean success = effective_table_mutant (base, info, tbl_index, t, idx); + gboolean success = effective_table_mutant (base, info, tbl_index, t, NULL); g_assert (success); } @@ -1129,7 +1236,7 @@ funccode_to_str (int func_code) * */ static void -delta_info_mutate_row (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *cur_delta, guint32 log_token) +delta_info_mutate_row (MonoImage *image_dmeta, DeltaInfo *cur_delta, guint32 log_token) { int token_table = mono_metadata_token_table (log_token); int token_index = mono_metadata_token_index (log_token); /* 1-based */ @@ -1139,16 +1246,16 @@ delta_info_mutate_row (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo int delta_index = hot_reload_relative_delta_index (image_dmeta, cur_delta, log_token); /* The complication here is that we want the mutant table to look like the table in - * image_base with respect to column widths, but the delta tables are generally coming in + * the baseline image with respect to column widths, but the delta tables are generally coming in * uncompressed (4-byte columns). So we have to copy one column at a time and adjust the * widths as we go. */ - guint32 dst_bitfield = image_base->tables [token_table].size_bitfield; + guint32 dst_bitfield = cur_delta->mutants [token_table].size_bitfield; guint32 src_bitfield = image_dmeta->tables [token_table].size_bitfield; const char *src_base = image_dmeta->tables [token_table].base + (delta_index - 1) * image_dmeta->tables [token_table].row_size; - char *dst_base = (char*)cur_delta->mutants [token_table].base + (token_index - 1) * image_base->tables [token_table].row_size; + char *dst_base = (char*)cur_delta->mutants [token_table].base + (token_index - 1) * cur_delta->mutants [token_table].row_size; guint32 src_offset = 0, dst_offset = 0; for (int col = 0; col < mono_metadata_table_count (dst_bitfield); ++col) { @@ -1190,12 +1297,11 @@ delta_info_mutate_row (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo default: g_assert_not_reached (); } - } src_offset += src_col_size; dst_offset += dst_col_size; } - g_assert (dst_offset == image_base->tables [token_table].row_size); + g_assert (dst_offset == cur_delta->mutants [token_table].row_size); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "mutate: table=0x%02x row=0x%04x delta row=0x%04x %s", token_table, token_index, delta_index, modified ? "Mod" : "Add"); } @@ -1215,7 +1321,7 @@ prepare_mutated_rows (const MonoTableInfo *table_enclog, MonoImage *image_base, if (func_code != ENC_FUNC_DEFAULT) continue; - delta_info_mutate_row (image_base, image_dmeta, delta_info, log_token); + delta_info_mutate_row (image_dmeta, delta_info, log_token); } } @@ -1248,7 +1354,10 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de int token_table = mono_metadata_token_table (log_token); int token_index = mono_metadata_token_index (log_token); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x (%s idx=0x%02x) (base table has 0x%04x rows)\tfunc=0x%02x (\"%s\")\n", i, log_token, mono_meta_table_name (token_table), token_index, table_info_get_rows (&image_base->tables [token_table]), func_code, funccode_to_str (func_code)); + gboolean is_addition = token_index-1 >= delta_info->count[token_table].prev_gen_rows ; + + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x (%s idx=0x%02x) (base table has 0x%04x rows; prev gen had 0x%04x rows)\t%s\tfunc=0x%02x (\"%s\")\n", i, log_token, mono_meta_table_name (token_table), token_index, table_info_get_rows (&image_base->tables [token_table]), delta_info->count[token_table].prev_gen_rows, (is_addition ? "ADD" : "UPDATE"), func_code, funccode_to_str (func_code)); if (token_table != MONO_TABLE_METHOD) @@ -1256,7 +1365,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de #ifndef ALLOW_METHOD_ADD - if (token_index > table_info_get_rows (&image_base->tables [token_table])) { + if (is_addition) { mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "\tcannot add new method with token 0x%08x", log_token); mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: cannot add new method with token 0x%08x", log_token); unsupported_edits = TRUE; @@ -1266,8 +1375,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de #ifdef ALLOW_METHOD_ADD /* adding a new parameter to a new method is ok */ - /* FIXME: total rows, not just the baseline rows */ - if (func_code == ENC_FUNC_ADD_PARAM && token_index > table_info_get_rows (&image_base->tables [token_table])) + if (func_code == ENC_FUNC_ADD_PARAM && is_addition) continue; #endif @@ -1284,6 +1392,8 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de int token_table = mono_metadata_token_table (log_token); int token_index = mono_metadata_token_index (log_token); + gboolean is_addition = token_index-1 >= delta_info->count[token_table].prev_gen_rows ; + switch (token_table) { case MONO_TABLE_ASSEMBLYREF: /* okay, supported */ @@ -1295,9 +1405,20 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de #endif /* handled above */ break; + case MONO_TABLE_FIELD: +#ifdef ALLOW_FIELD_ADD + if (func_code == ENC_FUNC_DEFAULT) + continue; /* ok, allowed */ +#else + /* adding or modifying a field */ + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x we do not support adding or modifying fields.", i, log_token); + mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support adding or modifying fields. token=0x%08x", log_token); + unsupported_edits = TRUE; + break; +#endif case MONO_TABLE_PROPERTY: { /* modifying a property, ok */ - if (token_index <= table_info_get_rows (&image_base->tables [token_table])) + if (!is_addition) break; /* adding a property */ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x we do not support adding new properties.", i, log_token); @@ -1306,8 +1427,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de continue; } case MONO_TABLE_METHODSEMANTICS: { - /* FIXME: this should get the current table size, not the base stable size */ - if (token_index > table_info_get_rows (&image_base->tables [token_table])) { + if (is_addition) { /* new rows are fine, as long as they point at existing methods */ guint32 sema_cols [MONO_METHOD_SEMA_SIZE]; int mapped_token = hot_reload_relative_delta_index (image_dmeta, delta_info, mono_metadata_make_token (token_table, token_index)); @@ -1319,7 +1439,8 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de case METHOD_SEMANTIC_SETTER: { int prop_method_index = sema_cols [MONO_METHOD_SEMA_METHOD]; /* ok, if it's pointing to an existing getter/setter */ - if (prop_method_index < table_info_get_rows (&image_base->tables [MONO_TABLE_METHOD])) + gboolean is_prop_method_add = prop_method_index-1 >= delta_info->count[MONO_TABLE_METHOD].prev_gen_rows; + if (!is_prop_method_add) break; mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x adding new getter/setter method 0x%08x to a property is not supported", i, log_token, prop_method_index); mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support adding a new getter or setter to a property, token=0x%08x", log_token); @@ -1340,8 +1461,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de } } case MONO_TABLE_CUSTOMATTRIBUTE: { - /* FIXME: this should get the current table size, not the base stable size */ - if (token_index <= table_info_get_rows (&image_base->tables [token_table])) { + if (!is_addition) { /* modifying existing rows is ok, as long as the parent and ctor are the same */ guint32 ca_upd_cols [MONO_CUSTOM_ATTR_SIZE]; guint32 ca_base_cols [MONO_CUSTOM_ATTR_SIZE]; @@ -1375,8 +1495,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de } } case MONO_TABLE_PARAM: { - /* FIXME: this should get the current table size, not the base stable size */ - if (token_index <= table_info_get_rows (&image_base->tables [token_table])) { + if (!is_addition) { /* We only allow modifications where the parameter name doesn't change. */ uint32_t base_param [MONO_PARAM_SIZE]; uint32_t upd_param [MONO_PARAM_SIZE]; @@ -1401,11 +1520,14 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de break; /* added a row. ok */ } case MONO_TABLE_TYPEDEF: { + gboolean new_class G_GNUC_UNUSED = is_addition; #ifdef ALLOW_METHOD_ADD - /* FIXME: wrong for cumulative updates - need to look at DeltaInfo:count.prev_gen_rows */ - gboolean new_class = token_index > table_info_get_rows (&image_base->tables [token_table]); /* only allow adding methods to existing classes for now */ - if (!new_class && func_code == ENC_FUNC_ADD_METHOD) { + if ( +#ifndef ALLOW_CLASS_ADD + !new_class && +#endif + func_code == ENC_FUNC_ADD_METHOD) { /* next record should be a MONO_TABLE_METHOD addition (func == default) */ g_assert (i + 1 < rows); guint32 next_cols [MONO_ENCLOG_SIZE]; @@ -1415,7 +1537,29 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de int next_table = mono_metadata_token_table (next_token); int next_index = mono_metadata_token_index (next_token); g_assert (next_table == MONO_TABLE_METHOD); - g_assert (next_index > table_info_get_rows (&image_base->tables [next_table])); + /* expecting an added method */ + g_assert (next_index-1 >= delta_info->count[next_table].prev_gen_rows); + i++; /* skip the next record */ + continue; + } +#endif +#ifdef ALLOW_FIELD_ADD + if ( +#ifndef ALLOW_CLASS_ADD + !new_class && +#endif + func_code == ENC_FUNC_ADD_FIELD) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x AddField to klass 0x%08x, skipping next EnClog record", i, log_token, token_index); + g_assert (i + 1 < rows); + guint32 next_cols [MONO_ENCLOG_SIZE]; + mono_metadata_decode_row (table_enclog, i + 1, next_cols, MONO_ENCLOG_SIZE); + g_assert (next_cols [MONO_ENCLOG_FUNC_CODE] == ENC_FUNC_DEFAULT); + int next_token = next_cols [MONO_ENCLOG_TOKEN]; + int next_table = mono_metadata_token_table (next_token); + int next_index = mono_metadata_token_index (next_token); + g_assert (next_table == MONO_TABLE_FIELD); + /* expecting an added field */ + g_assert (next_index-1 >= delta_info->count[next_table].prev_gen_rows); i++; /* skip the next record */ continue; } @@ -1423,8 +1567,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de /* fallthru */ } default: - /* FIXME: this bounds check is wrong for cumulative updates - need to look at the DeltaInfo:count.prev_gen_rows */ - if (token_index <= table_info_get_rows (&image_base->tables [token_table])) { + if (!is_addition) { mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x we do not support patching of existing table cols.", i, log_token); mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing table cols. token=0x%08x", log_token); unsupported_edits = TRUE; @@ -1544,9 +1687,12 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen * have it in writable memory (and not mmap-ed pages), so we can rewrite the table values. */ + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Pass 2 begin: base '%s' delta image=%p", image_base->name, image_dmeta); +#if defined(ALLOW_METHOD_ADD) || defined(ALLOW_FIELD_ADD) + MonoClass *add_member_klass = NULL; +#endif #ifdef ALLOW_METHOD_ADD - MonoClass *add_method_klass = NULL; uint32_t add_param_method_index = 0; #endif @@ -1560,7 +1706,11 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen int token_table = mono_metadata_token_table (log_token); int token_index = mono_metadata_token_index (log_token); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "enclog i=%d: token=0x%08x (table=%s): %d", i, log_token, mono_meta_table_name (token_table), func_code); + + gboolean is_addition = token_index-1 >= delta_info->count[token_table].prev_gen_rows ; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "enclog i=%d: token=0x%08x (table=%s): %d:\t%s", i, log_token, mono_meta_table_name (token_table), func_code, (is_addition ? "ADD" : "UPDATE")); + /* TODO: See CMiniMdRW::ApplyDelta for how to drive this. */ @@ -1570,14 +1720,12 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen #ifdef ALLOW_METHOD_ADD case ENC_FUNC_ADD_METHOD: { g_assert (token_table == MONO_TABLE_TYPEDEF); - /* FIXME: this bounds check is wrong for cumulative updates - need to look at the DeltaInfo:count.prev_gen_rows */ - /* should've been caught by pass1 if we're adding a new method to a new class. */ MonoClass *klass = mono_class_get_checked (image_base, log_token, error); if (!is_ok (error)) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Can't get class with token 0x%08x due to: %s", log_token, mono_error_get_message (error)); return FALSE; } - add_method_klass = klass; + add_member_klass = klass; break; } @@ -1586,6 +1734,18 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen add_param_method_index = token_index; break; } +#endif +#ifdef ALLOW_FIELD_ADD + case ENC_FUNC_ADD_FIELD: { + g_assert (token_table == MONO_TABLE_TYPEDEF); + MonoClass *klass = mono_class_get_checked (image_base, log_token, error); + if (!is_ok (error)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Can't get class with token 0x%08x due to: %s", log_token, mono_error_get_message (error)); + return FALSE; + } + add_member_klass = klass; + break; + } #endif default: g_error ("EnC: unsupported FuncCode, should be caught by pass1"); @@ -1637,14 +1797,14 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen if (func_code == ENC_FUNC_ADD_PARAM) break; - if (token_index > table_info_get_rows (&image_base->tables [token_table])) { - if (!add_method_klass) + if (is_addition) { + if (!add_member_klass) g_error ("EnC: new method added but I don't know the class, should be caught by pass1"); - g_assert (add_method_klass); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new method 0x%08x to class %s.%s", token_index, m_class_get_name_space (add_method_klass), m_class_get_name (add_method_klass)); + g_assert (add_member_klass); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new method 0x%08x to class %s.%s", log_token, m_class_get_name_space (add_member_klass), m_class_get_name (add_member_klass)); MonoDebugInformationEnc *method_debug_information = hot_reload_get_method_debug_information (delta_info->ppdb_file, token_index); - add_method_to_baseline (base_info, delta_info, add_method_klass, token_index, method_debug_information); - add_method_klass = NULL; + add_method_to_baseline (base_info, delta_info, add_member_klass, log_token, method_debug_information); + add_member_klass = NULL; } #endif @@ -1666,17 +1826,78 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen /* rva points probably into image_base IL stream. can this ever happen? */ g_print ("TODO: this case is still a bit contrived. token=0x%08x with rva=0x%04x\n", log_token, rva); } -#ifdef ALLOW_METHOD_ADD - add_method_klass = NULL; +#if defined(ALLOW_METHOD_ADD) || defined(ALLOW_FIELD_ADD) + add_member_klass = NULL; #endif break; } - case MONO_TABLE_TYPEDEF: { - /* TODO: throw? */ - /* TODO: happens changing the class (adding field or method). we ignore it, but dragons are here */ + case MONO_TABLE_FIELD: { +#ifdef ALLOW_FIELD_ADD + g_assert (is_addition); + g_assert (add_member_klass); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new field 0x%08x to class %s.%s", log_token, m_class_get_name_space (add_member_klass), m_class_get_name (add_member_klass)); + + uint32_t mapped_token = hot_reload_relative_delta_index (image_dmeta, delta_info, log_token); + uint32_t field_flags = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_FIELD], mapped_token - 1, MONO_FIELD_FLAGS); + + if ((field_flags & FIELD_ATTRIBUTE_STATIC) == 0) { + /* TODO: implement instance (and literal?) fields */ + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_METADATA_UPDATE, "Adding non-static fields isn't implemented yet (token 0x%08x, class %s.%s)", log_token, m_class_get_name_space (add_member_klass), m_class_get_name (add_member_klass)); + mono_error_set_not_implemented (error, "Adding non-static fields isn't implemented yet (token 0x%08x, class %s.%s)", log_token, m_class_get_name_space (add_member_klass), m_class_get_name (add_member_klass)); + return FALSE; + } - /* existing entries are supposed to be patched */ - g_assert (token_index <= table_info_get_rows (&image_base->tables [token_table])); + add_field_to_baseline (base_info, delta_info, add_member_klass, log_token); + + /* This actually does more than mono_class_setup_basic_field_info and + * resolves MonoClassField:type and sets MonoClassField:offset to -1 to make + * it easier to spot that the field is special. + */ + metadata_update_field_setup_basic_info_and_resolve (image_base, base_info, generation, delta_info, add_member_klass, log_token, error); + if (!is_ok (error)) { + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_METADATA_UPDATE, "Could not setup field (token 0x%08x) due to: %s", log_token, mono_error_get_message (error)); + return FALSE; + } + + add_member_klass = NULL; +#else + g_assert_not_reached (); +#endif + break; + } + case MONO_TABLE_TYPEDEF: { +#ifdef ALLOW_CLASS_ADD + if (is_addition) { + /* Adding a new class. ok */ + switch (func_code) { + case ENC_FUNC_DEFAULT: + /* ok, added a new class */ + /* TODO: do things here */ + break; + case ENC_FUNC_ADD_METHOD: + case ENC_FUNC_ADD_FIELD: + /* ok, adding a new field or method to a new class */ + /* TODO: do we need to do anything special? Conceptually + * this is the same as modifying an existing class - + * especially since from the next generation's point of view + * that's what adding a field/method will be. */ + break; + case ENC_FUNC_ADD_PROPERTY: + case ENC_FUNC_ADD_EVENT: + g_assert_not_reached (); /* FIXME: implement me */ + default: + g_assert_not_reached (); /* unknown func_code */ + } + break; + } +#endif + /* modifying an existing class by adding a method or field, etc. */ + g_assert (!is_addition); +#if !defined(ALLOW_METHOD_ADD) && !defined(ALLOW_FIELD_ADD) + g_assert_not_reached (); +#else + g_assert (func_code != ENC_FUNC_DEFAULT); +#endif break; } case MONO_TABLE_PROPERTY: { @@ -1709,7 +1930,7 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen * * So by the time we see the param additions, the methods are already in. * - * FIXME: we need a lookaside table (like method_parent) for every place + * FIXME: we need a lookaside table (like member_parent) for every place * that looks at MONO_METHOD_PARAMLIST */ break; @@ -1909,8 +2130,9 @@ hot_reload_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx) if (G_UNLIKELY (gen > 0)) { loc = get_method_update_rva (base_image, info, idx, TRUE); } - /* Check the method_parent table as a way of checking if the method was added by a later generation. If so, still look for its PPDB info in our update tables */ - if (G_UNLIKELY (loc == 0 && info->method_parent && GPOINTER_TO_UINT (g_hash_table_lookup (info->method_parent, GUINT_TO_POINTER (idx))) > 0)) { + /* Check the member_parent table as a way of checking if the method was added by a later generation. If so, still look for its PPDB info in our update tables */ + uint32_t token = mono_metadata_make_token (MONO_TABLE_METHOD, mono_metadata_token_index (idx)); + if (G_UNLIKELY (loc == 0 && info->member_parent && GPOINTER_TO_UINT (g_hash_table_lookup (info->member_parent, GUINT_TO_POINTER (token))) > 0)) { loc = get_method_update_rva (base_image, info, idx, TRUE); } } @@ -2039,52 +2261,321 @@ hot_reload_table_num_rows_slow (MonoImage *base, int table_index) } static void -add_method_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t method_token, MonoDebugInformationEnc* pdb_address) +add_member_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t member_token) { - if (!base_info->added_methods) { - base_info->added_methods = g_hash_table_new (g_direct_hash, g_direct_equal); - } - if (!base_info->method_parent) { - base_info->method_parent = g_hash_table_new (g_direct_hash, g_direct_equal); - } - /* FIXME: locking for readers/writers of the GArray */ - GArray *arr = g_hash_table_lookup (base_info->added_methods, klass); - if (!arr) { - arr = g_array_new (FALSE, FALSE, sizeof(uint32_t)); - g_hash_table_insert (base_info->added_methods, klass, arr); + /* Check they really passed a table token, not just a table row index */ + g_assert (mono_metadata_token_table (member_token) != 0); + + if (!base_info->member_parent) { + base_info->member_parent = g_hash_table_new (g_direct_hash, g_direct_equal); } - g_array_append_val (arr, method_token); - g_hash_table_insert (base_info->method_parent, GUINT_TO_POINTER (method_token), GUINT_TO_POINTER (m_class_get_type_token (klass))); + MonoClassMetadataUpdateInfo *klass_info = mono_class_get_or_add_metadata_update_info (klass); + GSList *members = klass_info->added_members; + klass_info->added_members = g_slist_prepend_mem_manager (m_class_get_mem_manager (klass), members, GUINT_TO_POINTER (member_token)); + g_hash_table_insert (base_info->member_parent, GUINT_TO_POINTER (member_token), GUINT_TO_POINTER (m_class_get_type_token (klass))); +} + +static void +add_method_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t method_token, MonoDebugInformationEnc* pdb_address) +{ + add_member_to_baseline (base_info, delta_info, klass, method_token); if (pdb_address) set_delta_method_debug_info (delta_info, method_token, pdb_address); } -static GArray* -hot_reload_get_added_methods (MonoClass *klass) +static GSList* +hot_reload_get_added_members (MonoClass *klass) { /* FIXME: locking for the GArray? */ MonoImage *image = m_class_get_image (klass); if (!image->has_updates) return NULL; - BaselineInfo *base_info = baseline_info_lookup (image); - if (!base_info || base_info->added_methods == NULL) + MonoClassMetadataUpdateInfo *klass_info = mono_class_get_metadata_update_info (klass); + if (!klass_info) return NULL; - - return g_hash_table_lookup (base_info->added_methods, klass); + return klass_info->added_members; } static uint32_t -hot_reload_method_parent (MonoImage *base_image, uint32_t method_token) +hot_reload_member_parent (MonoImage *base_image, uint32_t member_token) { + /* make sure they passed a token, not just a table row index */ + g_assert (mono_metadata_token_table (member_token) != 0); + if (!base_image->has_updates) return 0; BaselineInfo *base_info = baseline_info_lookup (base_image); - if (!base_info || base_info->method_parent == NULL) + if (!base_info || base_info->member_parent == NULL) return 0; - uint32_t res = GPOINTER_TO_UINT (g_hash_table_lookup (base_info->method_parent, GUINT_TO_POINTER (method_token))); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "method_parent lookup: 0x%08x returned 0x%08x\n", method_token, res); + uint32_t res = GPOINTER_TO_UINT (g_hash_table_lookup (base_info->member_parent, GUINT_TO_POINTER (member_token))); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "member_parent lookup: 0x%08x returned 0x%08x\n", member_token, res); + + return res; +} + +static uint32_t +hot_reload_method_parent (MonoImage *base_image, uint32_t method_token) +{ + /* the callers might pass just an index without a table */ + uint32_t lookup_token = mono_metadata_make_token (MONO_TABLE_METHOD, mono_metadata_token_index (method_token)); + + return hot_reload_member_parent (base_image, lookup_token); +} + +static void +add_field_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t field_token) +{ + add_member_to_baseline (base_info, delta_info, klass, field_token); +} + +static uint32_t +hot_reload_field_parent (MonoImage *base_image, uint32_t field_token) +{ + /* the callers might pass just an index without a table */ + uint32_t lookup_token = mono_metadata_make_token (MONO_TABLE_FIELD, mono_metadata_token_index (field_token)); + + return hot_reload_member_parent (base_image, lookup_token); +} + + +/* HACK - keep in sync with locator_t in metadata/metadata.c */ +typedef struct { + int idx; /* The index that we are trying to locate */ + int col_idx; /* The index in the row where idx may be stored */ + MonoTableInfo *t; /* pointer to the table */ + guint32 result; +} upd_locator_t; + +void* +hot_reload_metadata_linear_search (MonoImage *base_image, MonoTableInfo *base_table, const void *key, BinarySearchComparer comparer) +{ + BaselineInfo *base_info = baseline_info_lookup (base_image); + g_assert (base_info); + + g_assert (base_image->tables < base_table && base_table < &base_image->tables [MONO_TABLE_LAST]); + + int tbl_index; + { + size_t s = ALIGN_TO (sizeof (MonoTableInfo), sizeof (gpointer)); + tbl_index = ((intptr_t) base_table - (intptr_t) base_image->tables) / s; + } + + DeltaInfo *delta_info = NULL; + const MonoTableInfo *latest_mod_table = base_table; + gboolean success = effective_table_mutant (base_image, base_info, tbl_index, &latest_mod_table, &delta_info); + g_assert (success); + uint32_t rows = table_info_get_rows (latest_mod_table); + + upd_locator_t *loc = (upd_locator_t*)key; + g_assert (loc); + loc->result = 0; + /* HACK: this is so that the locator can compute the row index of the given row. but passing the mutant table to other metadata functions could backfire. */ + loc->t = (MonoTableInfo*)latest_mod_table; + for (uint32_t idx = 0; idx < rows; ++idx) { + const char *row = latest_mod_table->base + idx * latest_mod_table->row_size; + if (!comparer (loc, row)) + return (void*)row; + } + return NULL; +} + +static uint32_t +hot_reload_get_field_idx (MonoClassField *field) +{ + g_assert (m_field_is_from_update (field)); + MonoClassMetadataUpdateField *field_info = (MonoClassMetadataUpdateField*)field; + return mono_metadata_token_index (field_info->token); +} + +static MonoClassField * +hot_reload_get_field (MonoClass *klass, uint32_t fielddef_token) { + MonoClassMetadataUpdateInfo *info = mono_class_get_or_add_metadata_update_info (klass); + g_assert (mono_metadata_token_table (fielddef_token) == MONO_TABLE_FIELD); + /* FIXME: this needs locking in the multi-threaded case. There could be an update happening that resizes the array. */ + GPtrArray *added_fields = info->added_fields; + uint32_t count = added_fields->len; + for (uint32_t i = 0; i < count; ++i) { + MonoClassMetadataUpdateField *field = (MonoClassMetadataUpdateField *)g_ptr_array_index (added_fields, i); + if (field->token == fielddef_token) + return &field->field; + } + return NULL; +} + + +static MonoClassMetadataUpdateField * +metadata_update_field_setup_basic_info_and_resolve (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, DeltaInfo *delta_info, MonoClass *parent_klass, uint32_t fielddef_token, MonoError *error) +{ + // TODO: hang a "pending field" struct off the parent_klass if !parent_klass->fields + // In that case we can do things simpler, maybe by just creating the MonoClassField array as usual, and just relying on the normal layout algorithm to make space for the instance. + + MonoClassMetadataUpdateInfo *parent_info = mono_class_get_or_add_metadata_update_info (parent_klass); + + MonoClassMetadataUpdateField *field = mono_class_alloc0 (parent_klass, sizeof (MonoClassMetadataUpdateField)); + + m_field_set_parent (&field->field, parent_klass); + m_field_set_meta_flags (&field->field, MONO_CLASS_FIELD_META_FLAG_FROM_UPDATE); + /* It's a special field */ + field->field.offset = -1; + field->generation = generation; + field->token = fielddef_token; + + uint32_t name_idx = mono_metadata_decode_table_row_col (image_base, MONO_TABLE_FIELD, mono_metadata_token_index (fielddef_token) - 1, MONO_FIELD_NAME); + field->field.name = mono_metadata_string_heap (image_base, name_idx); + + mono_field_resolve_type (&field->field, error); + if (!is_ok (error)) + return NULL; + + if (!parent_info->added_fields) { + parent_info->added_fields = g_ptr_array_new (); + } + + g_ptr_array_add (parent_info->added_fields, field); + + return field; +} + +static void +ensure_class_runtime_info_inited (MonoClass *klass, MonoClassRuntimeMetadataUpdateInfo *runtime_info) +{ + if (runtime_info->inited) + return; + mono_loader_lock (); + if (runtime_info->inited) { + mono_loader_unlock (); + return; + } + + mono_coop_mutex_init (&runtime_info->static_fields_lock); + + /* FIXME: is it ok to re-use MONO_ROOT_SOURCE_STATIC here? */ + runtime_info->static_fields = mono_g_hash_table_new_type_internal (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_STATIC, NULL, "Hot Reload Static Fields"); + + runtime_info->inited = TRUE; + + mono_loader_unlock (); +} + +static void +class_runtime_info_static_fields_lock (MonoClassRuntimeMetadataUpdateInfo *runtime_info) +{ + mono_coop_mutex_lock (&runtime_info->static_fields_lock); +} + +static void +class_runtime_info_static_fields_unlock (MonoClassRuntimeMetadataUpdateInfo *runtime_info) +{ + mono_coop_mutex_unlock (&runtime_info->static_fields_lock); +} + +static GENERATE_GET_CLASS_WITH_CACHE_DECL (hot_reload_field_store); + +static GENERATE_GET_CLASS_WITH_CACHE(hot_reload_field_store, "Mono.HotReload", "FieldStore"); + + +static MonoObject* +create_static_field_storage (MonoType *t, MonoError *error) +{ + MonoClass *klass; + if (!mono_type_is_reference (t)) + klass = mono_class_from_mono_type_internal (t); + else + klass = mono_class_get_hot_reload_field_store_class (); + + return mono_object_new_pinned (klass, error); +} + +static gpointer +hot_reload_get_static_field_addr (MonoClassField *field) +{ + g_assert (m_field_is_from_update (field)); + MonoClassMetadataUpdateField *f = (MonoClassMetadataUpdateField *)field; + g_assert ((f->field.type->attrs & FIELD_ATTRIBUTE_STATIC) != 0); + g_assert (!m_type_is_byref(f->field.type)); // byref fields only in ref structs, which aren't allowed in EnC updates + + MonoClass *parent = m_field_get_parent (&f->field); + MonoClassMetadataUpdateInfo *parent_info = mono_class_get_or_add_metadata_update_info (parent); + MonoClassRuntimeMetadataUpdateInfo *runtime_info = &parent_info->runtime; + + ensure_class_runtime_info_inited (parent, runtime_info); + + MonoObject *obj = NULL; + class_runtime_info_static_fields_lock (runtime_info); + obj = (MonoObject*) mono_g_hash_table_lookup (runtime_info->static_fields, GUINT_TO_POINTER (f->token)); + class_runtime_info_static_fields_unlock (runtime_info); + if (!obj) { + ERROR_DECL (error); + obj = create_static_field_storage (f->field.type, error); + class_runtime_info_static_fields_lock (runtime_info); + mono_error_assert_ok (error); + MonoObject *obj2 = (MonoObject*) mono_g_hash_table_lookup (runtime_info->static_fields, GUINT_TO_POINTER (f->token)); + if (!obj2) { + // Noone else created it, use ours + mono_g_hash_table_insert_internal (runtime_info->static_fields, GUINT_TO_POINTER (f->token), obj); + } else { + /* beaten by another thread, silently drop our storage object and use theirs */ + obj = obj2; + } + class_runtime_info_static_fields_unlock (runtime_info); + } + g_assert (obj); + + gpointer addr = NULL; + if (!mono_type_is_reference (f->field.type)) { + // object is just the boxed value + addr = mono_object_unbox_internal (obj); + } else { + // object is a Mono.HotReload.FieldStore, and the static field value is obj._loc + MonoHotReloadFieldStoreObject *store = (MonoHotReloadFieldStoreObject *)obj; + addr = (gpointer)&store->_loc; + } + g_assert (addr); + + return addr; +} + +static MonoMethod * +hot_reload_find_method_by_name (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error) +{ + GSList *members = hot_reload_get_added_members (klass); + if (!members) + return NULL; + + MonoImage *image = m_class_get_image (klass); + MonoMethod *res = NULL; + for (GSList *ptr = members; ptr; ptr = ptr->next) { + uint32_t token = GPOINTER_TO_UINT(ptr->data); + if (mono_metadata_token_table (token) != MONO_TABLE_METHOD) + continue; + uint32_t idx = mono_metadata_token_index (token); + uint32_t cols [MONO_METHOD_SIZE]; + mono_metadata_decode_table_row (image, MONO_TABLE_METHOD, idx - 1, cols, MONO_METHOD_SIZE); + + if (!strcmp (mono_metadata_string_heap (image, cols [MONO_METHOD_NAME]), name)) { + ERROR_DECL (local_error); + MonoMethod *method = mono_get_method_checked (image, MONO_TOKEN_METHOD_DEF | idx, klass, NULL, local_error); + if (!method) { + mono_error_cleanup (local_error); + continue; + } + if (param_count == -1) { + res = method; + break; + } + MonoMethodSignature *sig = mono_method_signature_checked (method, local_error); + if (!sig) { + mono_error_cleanup (error); + continue; + } + if ((method->flags & flags) == flags && sig->param_count == param_count) { + res = method; + break; + } + } + } return res; } diff --git a/src/mono/mono/component/hot_reload.h b/src/mono/mono/component/hot_reload.h index 416ff56e70eab0..790234e5577183 100644 --- a/src/mono/mono/component/hot_reload.h +++ b/src/mono/mono/component/hot_reload.h @@ -8,7 +8,9 @@ #include #include "mono/metadata/object-forward.h" #include "mono/metadata/metadata-internals.h" +#include "mono/metadata/class-internals.h" #include "mono/metadata/metadata-update.h" +#include "mono/utils/bsearch.h" #include "mono/utils/mono-error.h" #include "mono/utils/mono-compiler.h" #include "mono/component/component.h" @@ -31,8 +33,13 @@ typedef struct _MonoComponentHotReload { gpointer (*get_updated_method_ppdb) (MonoImage *base_image, uint32_t idx); gboolean (*has_modified_rows) (const MonoTableInfo *table); gboolean (*table_num_rows_slow) (MonoImage *base_image, int table_index); - GArray* (*get_added_methods) (MonoClass *klass); uint32_t (*method_parent) (MonoImage *base_image, uint32_t method_index); + void* (*metadata_linear_search) (MonoImage *base_image, MonoTableInfo *base_table, const void *key, BinarySearchComparer comparer); + uint32_t (*field_parent) (MonoImage *base_image, uint32_t method_index); + uint32_t (*get_field_idx) (MonoClassField *field); + MonoClassField* (*get_field) (MonoClass *klass, uint32_t fielddef_token); + gpointer (*get_static_field_addr) (MonoClassField *field); + MonoMethod* (*find_method_by_name) (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error); } MonoComponentHotReload; MONO_COMPONENT_EXPORT_ENTRYPOINT diff --git a/src/mono/mono/metadata/class-accessors.c b/src/mono/mono/metadata/class-accessors.c index 76065cb45c456f..609ad55b404944 100644 --- a/src/mono/mono/metadata/class-accessors.c +++ b/src/mono/mono/metadata/class-accessors.c @@ -28,7 +28,8 @@ typedef enum { PROP_DIM_CONFLICTS = 10, /* GSList of MonoMethod* */ PROP_FIELD_DEF_VALUES_2BYTESWIZZLE = 11, /* MonoFieldDefaultValue* with default values swizzled at 2 byte boundaries*/ PROP_FIELD_DEF_VALUES_4BYTESWIZZLE = 12, /* MonoFieldDefaultValue* with default values swizzled at 4 byte boundaries*/ - PROP_FIELD_DEF_VALUES_8BYTESWIZZLE = 13 /* MonoFieldDefaultValue* with default values swizzled at 8 byte boundaries*/ + PROP_FIELD_DEF_VALUES_8BYTESWIZZLE = 13, /* MonoFieldDefaultValue* with default values swizzled at 8 byte boundaries*/ + PROP_METADATA_UPDATE_INFO = 14, /* MonoClassMetadataUpdateInfo* */ } InfrequentDataKind; /* Accessors based on class kind*/ @@ -589,6 +590,67 @@ mono_class_publish_gc_descriptor (MonoClass *klass, MonoGCDescriptor gc_descr) return ret; } +MonoClassMetadataUpdateInfo* +mono_class_get_metadata_update_info (MonoClass *klass) +{ + switch (m_class_get_class_kind (klass)) { + case MONO_CLASS_GTD: + return NULL; + case MONO_CLASS_DEF: + return (MonoClassMetadataUpdateInfo *)get_pointer_property (klass, PROP_METADATA_UPDATE_INFO); + case MONO_CLASS_GINST: + case MONO_CLASS_GPARAM: + case MONO_CLASS_POINTER: + case MONO_CLASS_GC_FILLER: + return NULL; + default: + g_assert_not_reached (); + } +} + +/* + * LOCKING: assumes the loader lock is held + */ +void +mono_class_set_metadata_update_info (MonoClass *klass, MonoClassMetadataUpdateInfo *value) +{ + switch (m_class_get_class_kind (klass)) { + case MONO_CLASS_GTD: + g_assertf (0, "%s: EnC metadata update info on generic types is not supported", __func__); + break; + case MONO_CLASS_DEF: + set_pointer_property (klass, PROP_METADATA_UPDATE_INFO, value); + return; + case MONO_CLASS_GINST: + case MONO_CLASS_GPARAM: + case MONO_CLASS_POINTER: + case MONO_CLASS_GC_FILLER: + g_assert_not_reached (); + break; + default: + g_assert_not_reached (); + } +} + +gboolean +mono_class_has_metadata_update_info (MonoClass *klass) +{ + switch (m_class_get_class_kind (klass)) { + case MONO_CLASS_GTD: + return FALSE; + case MONO_CLASS_DEF: + return get_pointer_property (klass, PROP_METADATA_UPDATE_INFO) != NULL; + case MONO_CLASS_GINST: + case MONO_CLASS_GPARAM: + case MONO_CLASS_POINTER: + case MONO_CLASS_GC_FILLER: + return FALSE; + default: + g_assert_not_reached (); + } +} + + #ifdef MONO_CLASS_DEF_PRIVATE #define MONO_CLASS_GETTER(funcname, rettype, optref, argtype, fieldname) rettype funcname (argtype *klass) { return optref klass-> fieldname ; } #define MONO_CLASS_OFFSET(funcname, argtype, fieldname) intptr_t funcname (void) { return MONO_STRUCT_OFFSET (argtype, fieldname); } diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c index 6164476ba9a914..750dcff22d70bc 100644 --- a/src/mono/mono/metadata/class-init.c +++ b/src/mono/mono/metadata/class-init.c @@ -434,7 +434,7 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError error_init (error); /* FIXME: metadata-update - this function needs extensive work */ - if (mono_metadata_token_table (type_token) != MONO_TABLE_TYPEDEF || tidx > table_info_get_rows (tt)) { + if (mono_metadata_token_table (type_token) != MONO_TABLE_TYPEDEF || mono_metadata_table_bounds_check (image, MONO_TABLE_TYPEDEF, tidx)) { mono_error_set_bad_image (error, image, "Invalid typedef token %x", type_token); return NULL; } @@ -614,27 +614,33 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError /* * Compute the field and method lists */ - int first_field_idx; - first_field_idx = cols [MONO_TYPEDEF_FIELD_LIST] - 1; - mono_class_set_first_field_idx (klass, first_field_idx); - int first_method_idx; - first_method_idx = cols [MONO_TYPEDEF_METHOD_LIST] - 1; - mono_class_set_first_method_idx (klass, first_method_idx); - - if (table_info_get_rows (tt) > tidx){ - mono_metadata_decode_row (tt, tidx, cols_next, MONO_TYPEDEF_SIZE); - field_last = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1; - method_last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1; - } else { - field_last = table_info_get_rows (&image->tables [MONO_TABLE_FIELD]); - method_last = table_info_get_rows (&image->tables [MONO_TABLE_METHOD]); - } + /* + * EnC metadata-update: new classes are added with method and field indices set to 0, new + * methods are added using the EnCLog AddMethod or AddField functions that will be added to + * MonoClassMetadataUpdateInfo + */ + if (G_LIKELY (cols [MONO_TYPEDEF_FIELD_LIST] != 0 || cols [MONO_TYPEDEF_METHOD_LIST] != 0)) { + int first_field_idx; + first_field_idx = cols [MONO_TYPEDEF_FIELD_LIST] - 1; + mono_class_set_first_field_idx (klass, first_field_idx); + int first_method_idx; + first_method_idx = cols [MONO_TYPEDEF_METHOD_LIST] - 1; + mono_class_set_first_method_idx (klass, first_method_idx); + if (table_info_get_rows (tt) > tidx) { + mono_metadata_decode_row (tt, tidx, cols_next, MONO_TYPEDEF_SIZE); + field_last = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1; + method_last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1; + } else { + field_last = table_info_get_rows (&image->tables [MONO_TABLE_FIELD]); + method_last = table_info_get_rows (&image->tables [MONO_TABLE_METHOD]); + } - if (cols [MONO_TYPEDEF_FIELD_LIST] && - cols [MONO_TYPEDEF_FIELD_LIST] <= table_info_get_rows (&image->tables [MONO_TABLE_FIELD])) - mono_class_set_field_count (klass, field_last - first_field_idx); - if (cols [MONO_TYPEDEF_METHOD_LIST] <= table_info_get_rows (&image->tables [MONO_TABLE_METHOD])) - mono_class_set_method_count (klass, method_last - first_method_idx); + if (cols [MONO_TYPEDEF_FIELD_LIST] && + cols [MONO_TYPEDEF_FIELD_LIST] <= table_info_get_rows (&image->tables [MONO_TABLE_FIELD])) + mono_class_set_field_count (klass, field_last - first_field_idx); + if (cols [MONO_TYPEDEF_METHOD_LIST] <= table_info_get_rows (&image->tables [MONO_TABLE_METHOD])) + mono_class_set_method_count (klass, method_last - first_method_idx); + } /* reserve space to store vector pointer in arrays */ if (mono_is_corlib_image (image) && !strcmp (nspace, "System") && !strcmp (name, "Array")) { @@ -4080,16 +4086,3 @@ mono_classes_init (void) mono_counters_register ("MonoClass size", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &classes_size); } - -void -m_field_set_parent (MonoClassField *field, MonoClass *klass) -{ - uintptr_t old_flags = m_field_get_meta_flags (field); - field->parent_and_flags = ((uintptr_t)klass) | old_flags; -} - -void -m_field_set_meta_flags (MonoClassField *field, unsigned int flags) -{ - field->parent_and_flags |= (field->parent_and_flags & ~MONO_CLASS_FIELD_META_FLAG_MASK) | flags; -} diff --git a/src/mono/mono/metadata/class-inlines.h b/src/mono/mono/metadata/class-inlines.h index a9aa3c992a5416..e45350a8bc4aae 100644 --- a/src/mono/mono/metadata/class-inlines.h +++ b/src/mono/mono/metadata/class-inlines.h @@ -253,4 +253,18 @@ m_method_is_wrapper (MonoMethod *method) return method->wrapper_type != 0; } + +static inline void +m_field_set_parent (MonoClassField *field, MonoClass *klass) +{ + uintptr_t old_flags = m_field_get_meta_flags (field); + field->parent_and_flags = ((uintptr_t)klass) | old_flags; +} + +static inline void +m_field_set_meta_flags (MonoClassField *field, unsigned int flags) +{ + field->parent_and_flags |= (field->parent_and_flags & ~MONO_CLASS_FIELD_META_FLAG_MASK) | flags; +} + #endif diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index 01104274c1f3b9..4a7992875bf684 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -1231,7 +1231,7 @@ mono_class_get_generic_container (MonoClass *klass); gpointer mono_class_alloc (MonoClass *klass, int size); -gpointer +MONO_COMPONENT_API gpointer mono_class_alloc0 (MonoClass *klass, int size); #define mono_class_alloc0(klass, size) (g_cast (mono_class_alloc0 ((klass), (size)))) @@ -1455,6 +1455,18 @@ mono_class_set_dim_conflicts (MonoClass *klass, GSList *conflicts); GSList* mono_class_get_dim_conflicts (MonoClass *klass); +/* opaque struct of class specific hot reload info */ +typedef struct _MonoClassMetadataUpdateInfo MonoClassMetadataUpdateInfo; + +MONO_COMPONENT_API gboolean +mono_class_has_metadata_update_info (MonoClass *klass); + +MONO_COMPONENT_API MonoClassMetadataUpdateInfo * +mono_class_get_metadata_update_info (MonoClass *klass); + +MONO_COMPONENT_API void +mono_class_set_metadata_update_info (MonoClass *klass, MonoClassMetadataUpdateInfo *value); + MONO_COMPONENT_API MonoMethod * mono_class_get_method_from_name_checked (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error); @@ -1492,7 +1504,7 @@ mono_class_get_default_finalize_method (void); const char * mono_field_get_rva (MonoClassField *field, int swizzle); -void +MONO_COMPONENT_API void mono_field_resolve_type (MonoClassField *field, MonoError *error); gboolean @@ -1575,12 +1587,6 @@ m_field_get_meta_flags (MonoClassField *field) return (unsigned int)(field->parent_and_flags & MONO_CLASS_FIELD_META_FLAG_MASK); } -void -m_field_set_parent (MonoClassField *field, MonoClass *klass); - -void -m_field_set_meta_flags (MonoClassField *field, unsigned int flags); - static inline gboolean m_field_get_offset (MonoClassField *field) { diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 04af2642f252de..895a654cbfeaad 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -2401,6 +2402,10 @@ mono_class_get_field_idx (MonoClass *klass, int idx) return &klass_fields [idx - first_field_idx]; } } + if (G_UNLIKELY (m_class_get_image (klass)->has_updates && mono_class_has_metadata_update_info (klass))) { + uint32_t token = mono_metadata_make_token (MONO_TABLE_FIELD, idx + 1); + return mono_metadata_update_get_field (klass, token); + } } klass = m_class_get_parent (klass); } @@ -5715,6 +5720,14 @@ mono_find_method_in_metadata (MonoClass *klass, const char *name, int param_coun } } + if (G_UNLIKELY (!res && klass_image->has_updates)) { + if (mono_class_has_metadata_update_info (klass)) { + ERROR_DECL (error); + res = mono_metadata_update_find_method_by_name (klass, name, param_count, flags, error); + mono_error_cleanup (error); + } + } + return res; } @@ -5777,7 +5790,8 @@ mono_class_get_method_from_name_checked (MonoClass *klass, const char *name, FIXME we should better report this error to the caller */ MonoMethod **klass_methods = m_class_get_methods (klass); - if (!klass_methods) + gboolean has_updates = m_class_get_image (klass)->has_updates; + if (!klass_methods && !has_updates) return NULL; int mcount = mono_class_get_method_count (klass); for (i = 0; i < mcount; ++i) { @@ -5791,6 +5805,9 @@ mono_class_get_method_from_name_checked (MonoClass *klass, const char *name, break; } } + if (G_UNLIKELY (!res && has_updates && mono_class_has_metadata_update_info (klass))) { + res = mono_metadata_update_find_method_by_name (klass, name, param_count, flags, error); + } } else { res = mono_find_method_in_metadata (klass, name, param_count, flags); @@ -6417,11 +6434,18 @@ mono_field_resolve_type (MonoClassField *field, MonoError *error) MonoImage *image = m_class_get_image (klass); MonoClass *gtd = mono_class_is_ginst (klass) ? mono_class_get_generic_type_definition (klass) : NULL; MonoType *ftype; - int field_idx = field - m_class_get_fields (klass); + int field_idx; + + if (G_UNLIKELY (m_field_is_from_update (field))) { + field_idx = -1; + } else { + field_idx = field - m_class_get_fields (klass); + } error_init (error); if (gtd) { + g_assert (field_idx != -1); MonoClassField *gfield = &m_class_get_fields (gtd) [field_idx]; MonoType *gtype = mono_field_get_type_checked (gfield, error); if (!is_ok (error)) { @@ -6440,7 +6464,13 @@ mono_field_resolve_type (MonoClassField *field, MonoError *error) const char *sig; guint32 cols [MONO_FIELD_SIZE]; MonoGenericContainer *container = NULL; - int idx = mono_class_get_first_field_idx (klass) + field_idx; + int idx; + + if (G_UNLIKELY (m_field_is_from_update (field))) { + idx = mono_metadata_update_get_field_idx (field) - 1; + } else { + idx = mono_class_get_first_field_idx (klass) + field_idx; + } /*FIXME, in theory we do not lazy load SRE fields*/ g_assert (!image_is_dynamic (image)); diff --git a/src/mono/mono/metadata/custom-attrs.c b/src/mono/mono/metadata/custom-attrs.c index 580a51b497d37d..59a3ce25983e13 100644 --- a/src/mono/mono/metadata/custom-attrs.c +++ b/src/mono/mono/metadata/custom-attrs.c @@ -25,6 +25,7 @@ #include "mono/metadata/tabledefs.h" #include "mono/metadata/tokentype.h" #include "mono/metadata/icall-decl.h" +#include "mono/metadata/metadata-update.h" #include "mono/utils/checked-build.h" #define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a)) @@ -150,6 +151,8 @@ free_param_data (MonoMethodSignature *sig, void **params) { */ static guint32 find_field_index (MonoClass *klass, MonoClassField *field) { + if (G_UNLIKELY (m_field_is_from_update (field))) + return mono_metadata_update_get_field_idx (field); int fcount = mono_class_get_field_count (klass); MonoClassField *klass_fields = m_class_get_fields (klass); int index = field - klass_fields; @@ -1626,8 +1629,6 @@ mono_custom_attrs_from_index_checked (MonoImage *image, guint32 idx, gboolean ig error_init (error); ca = &image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; - /* FIXME: metadata-update */ - int rows = table_info_get_rows (ca); i = mono_metadata_custom_attrs_from_index (image, idx); if (!i) @@ -1635,9 +1636,17 @@ mono_custom_attrs_from_index_checked (MonoImage *image, guint32 idx, gboolean ig i --; // initial size chosen arbitrarily, but default is 16 which is rather small attr_array = g_array_sized_new (TRUE, TRUE, sizeof (guint32), 128); - while (i < rows) { - if (mono_metadata_decode_row_col (ca, i, MONO_CUSTOM_ATTR_PARENT) != idx) - break; + while (!mono_metadata_table_bounds_check (image, MONO_TABLE_CUSTOMATTRIBUTE, i + 1)) { + if (mono_metadata_decode_row_col (ca, i, MONO_CUSTOM_ATTR_PARENT) != idx) { + if (G_LIKELY (!image->has_updates)) { + break; + } else { + // if there are updates, the new custom attributes are not sorted, + // so we have to go until the end. + ++i; + continue; + } + } attr_array = g_array_append_val (attr_array, i); ++i; } diff --git a/src/mono/mono/metadata/loader-internals.h b/src/mono/mono/metadata/loader-internals.h index bc79d38d4eb5bf..772c2dc875ebb7 100644 --- a/src/mono/mono/metadata/loader-internals.h +++ b/src/mono/mono/metadata/loader-internals.h @@ -356,6 +356,18 @@ mono_mem_manager_get_generic (MonoImage **images, int nimages); MonoMemoryManager* mono_mem_manager_merge (MonoMemoryManager *mm1, MonoMemoryManager *mm2); +static inline GSList* +g_slist_prepend_mem_manager (MonoMemoryManager *memory_manager, GSList *list, gpointer data) +{ + GSList *new_list; + + new_list = (GSList *) mono_mem_manager_alloc (memory_manager, sizeof (GSList)); + new_list->data = data; + new_list->next = list; + + return new_list; +} + G_END_DECLS #endif diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 800ffb9f047270..e24823ead81f3b 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -12,6 +12,7 @@ #include "mono/metadata/metadata-update.h" #include "mono/metadata/components.h" +#include "mono/metadata/class-internals.h" #include "mono/component/hot_reload.h" gboolean @@ -147,3 +148,37 @@ mono_metadata_table_num_rows_slow (MonoImage *base_image, int table_index) { return mono_component_hot_reload()->table_num_rows_slow (base_image, table_index); } + +void* +mono_metadata_update_metadata_linear_search (MonoImage *base_image, MonoTableInfo *base_table, const void *key, BinarySearchComparer comparer) +{ + return mono_component_hot_reload()->metadata_linear_search (base_image, base_table, key, comparer); +} + +/* + * Returns the (1-based) table row index of the fielddef of the given field + * (which must have m_field_is_from_update set). + */ +uint32_t +mono_metadata_update_get_field_idx (MonoClassField *field) +{ + return mono_component_hot_reload()->get_field_idx (field); +} + +MonoClassField * +mono_metadata_update_get_field (MonoClass *klass, uint32_t fielddef_token) +{ + return mono_component_hot_reload()->get_field (klass, fielddef_token); +} + +gpointer +mono_metadata_update_get_static_field_addr (MonoClassField *field) +{ + return mono_component_hot_reload()->get_static_field_addr (field); +} + +MonoMethod * +mono_metadata_update_find_method_by_name (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error) +{ + return mono_component_hot_reload()->find_method_by_name (klass, name, param_count, flags, error); +} diff --git a/src/mono/mono/metadata/metadata-update.h b/src/mono/mono/metadata/metadata-update.h index 15d0d51e10f4a7..ee999a4f0a10f7 100644 --- a/src/mono/mono/metadata/metadata-update.h +++ b/src/mono/mono/metadata/metadata-update.h @@ -6,6 +6,7 @@ #define __MONO_METADATA_UPDATE_H__ #include "mono/utils/mono-forward.h" +#include "mono/utils/bsearch.h" #include "mono/metadata/loader-internals.h" #include "mono/metadata/metadata-internals.h" @@ -57,4 +58,19 @@ mono_metadata_update_table_bounds_check (MonoImage *base_image, int table_index, gboolean mono_metadata_update_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); +void* +mono_metadata_update_metadata_linear_search (MonoImage *base_image, MonoTableInfo *base_table, const void *key, BinarySearchComparer comparer); + +MonoMethod* +mono_metadata_update_find_method_by_name (MonoClass *klass, const char *name, int param_count, int flags, MonoError *error); + +uint32_t +mono_metadata_update_get_field_idx (MonoClassField *field); + +MonoClassField * +mono_metadata_update_get_field (MonoClass *klass, uint32_t fielddef_token); + +gpointer +mono_metadata_update_get_static_field_addr (MonoClassField *field); + #endif /*__MONO_METADATA_UPDATE_H__*/ diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index 04a43f5e0076fa..8aa7a921ab48c4 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -4801,7 +4801,12 @@ mono_metadata_typedef_from_field (MonoImage *meta, guint32 index) if (meta->uncompressed_metadata) loc.idx = search_ptr_table (meta, MONO_TABLE_FIELD_POINTER, loc.idx); - /* FIXME: metadata-update */ + /* if it's not in the base image, look in the hot reload table */ + gboolean added = (loc.idx > table_info_get_rows (&meta->tables [MONO_TABLE_FIELD])); + if (added) { + uint32_t res = mono_component_hot_reload()->field_parent (meta, loc.idx); + return res; /* 0 if not found, otherwise 1-based */ + } if (!mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, typedef_locator)) return 0; @@ -4974,18 +4979,22 @@ mono_metadata_nested_in_typedef (MonoImage *meta, guint32 index) MonoTableInfo *tdef = &meta->tables [MONO_TABLE_NESTEDCLASS]; locator_t loc; - if (!tdef->base) + if (!tdef->base && !meta->has_updates) return 0; loc.idx = mono_metadata_token_index (index); loc.col_idx = MONO_NESTED_CLASS_NESTED; loc.t = tdef; - /* FIXME: metadata-update */ - - if (!mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator)) + gboolean found = tdef->base && mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator) != NULL; + if (!found && !meta->has_updates) return 0; + if (G_UNLIKELY (meta->has_updates)) { + if (!found && !mono_metadata_update_metadata_linear_search (meta, tdef, &loc, table_locator)) + return 0; + } + /* loc_result is 0..1, needs to be mapped to table index (that is +1) */ return mono_metadata_decode_row_col (tdef, loc.result, MONO_NESTED_CLASS_ENCLOSING) | MONO_TOKEN_TYPE_DEF; } @@ -5077,19 +5086,24 @@ mono_metadata_custom_attrs_from_index (MonoImage *meta, guint32 index) MonoTableInfo *tdef = &meta->tables [MONO_TABLE_CUSTOMATTRIBUTE]; locator_t loc; - if (!tdef->base) + if (!tdef->base && !meta->has_updates) return 0; loc.idx = index; loc.col_idx = MONO_CUSTOM_ATTR_PARENT; loc.t = tdef; - /* FIXME: metadata-update */ /* FIXME: Index translation */ - if (!mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator)) + gboolean found = tdef->base && mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator) != NULL; + if (!found && !meta->has_updates) return 0; + if (G_UNLIKELY (meta->has_updates)) { + if (!found && !mono_metadata_update_metadata_linear_search (meta, tdef, &loc, table_locator)) + return 0; + } + /* Find the first entry by searching backwards */ while ((loc.result > 0) && (mono_metadata_decode_row_col (tdef, loc.result - 1, MONO_CUSTOM_ATTR_PARENT) == index)) loc.result --; diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 6c6d089ee2122a..c1c4d939df2a1c 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -1666,7 +1666,7 @@ mono_class_set_ref_info (MonoClass *klass, MonoObjectHandle obj); void mono_class_free_ref_info (MonoClass *klass); -MonoObject * +MONO_COMPONENT_API MonoObject * mono_object_new_pinned (MonoClass *klass, MonoError *error); MonoObjectHandle diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 284e15b1ade8e3..55afb98f2f194f 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -2854,6 +2855,9 @@ mono_static_field_get_addr (MonoVTable *vt, MonoClassField *field) g_assert (field->type->attrs & FIELD_ATTRIBUTE_STATIC); if (field->offset == -1) { + if (G_UNLIKELY (m_field_is_from_update (field))) { + return mono_metadata_update_get_static_field_addr (field); + } /* Special static */ ERROR_DECL (error); gpointer addr = mono_special_static_field_get_offset (field, error); diff --git a/src/mono/mono/utils/mono-error-internals.h b/src/mono/mono/utils/mono-error-internals.h index a26558689ae1c8..f43727dde76bf3 100644 --- a/src/mono/mono/utils/mono-error-internals.h +++ b/src/mono/mono/utils/mono-error-internals.h @@ -202,6 +202,7 @@ mono_error_set_generic_error (MonoError *error, const char * name_space, const c void mono_error_set_execution_engine (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3); +MONO_COMPONENT_API void mono_error_set_not_implemented (MonoError *error, const char *msg_format, ...) MONO_ATTR_FORMAT_PRINTF(2,3);