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 index d5bd6ce27fd0e..277098ffc77ff 100644 --- 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 @@ -7,6 +7,10 @@ namespace System.Reflection.Metadata.ApplyUpdate.Test { public class AddNestedClass { + public static Action X; // make the linker happy + public static Delegate Y; + public event Action Evt; + public void R () { Evt ("123"); } public AddNestedClass() { } 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 index 04fc6992631d9..fcca03192d58a 100644 --- 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 @@ -7,22 +7,46 @@ namespace System.Reflection.Metadata.ApplyUpdate.Test { public class AddNestedClass { + public static Action X; // make the linker happy + public static Delegate Y; + public event Action Evt; + public void R () { Evt ("123"); } public AddNestedClass() { } public string TestMethod() { - var n = new Nested(); - n.f = "123"; - return n.M(); + var n = new Nested(); + n.Eff = "123"; + n.g = 456; + n.Evt += new Action (n.DefaultHandler); + n.RaiseEvt(); + return n.M() + n.buf; } - private class Nested { + private class Nested { public Nested() { } - internal string f; + internal T f; + internal U g; + public T Eff { + get => f; + set { f = value; } + } public string M () { - return f + "456"; + return Eff.ToString() + g.ToString(); + } + + public event Action Evt; + + public void RaiseEvt () { + Evt ("789"); + } + + public string buf; + + public void DefaultHandler (string s) { + this.buf = s; } } } diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/ReflectionAddNewType.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/ReflectionAddNewType.cs new file mode 100644 index 0000000000000..7d720e44585f3 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/ReflectionAddNewType.cs @@ -0,0 +1,35 @@ +// 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.ReflectionAddNewType; + +public interface IExistingInterface { + public string ItfMethod(int i); +} + +public struct QExistingStruct +{ +} + +public enum FExistingEnum { + One, Two +} + +public class ZExistingClass +{ + public class PreviousNestedClass { + public static DateTime Now; // make the linker happy + public static ICloneable C; + public event EventHandler E; + public void R() { E(this,"123"); } + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple=true, Inherited=false)] +public class CustomNoteAttribute : Attribute { + public CustomNoteAttribute(string note) {Note = note;} + + public string Note; +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/ReflectionAddNewType_v1.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/ReflectionAddNewType_v1.cs new file mode 100644 index 0000000000000..02762f11f0512 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/ReflectionAddNewType_v1.cs @@ -0,0 +1,89 @@ +// 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.ReflectionAddNewType; + +public interface IExistingInterface { + public string ItfMethod(int i); +} + +public struct QExistingStruct +{ +} + +public enum FExistingEnum { + One, Two +} + +public class ZExistingClass +{ + public class PreviousNestedClass { + public static DateTime Now; // make the linker happy + public static ICloneable C; + public event EventHandler E; + public void R() { E(this,"123"); } + } + public class NewNestedClass {}; + + + public string NewMethod (string s, int i) => s + i.ToString(); + + // Mono doesn't support instance fields yet +#if false + public int NewField; +#endif + + public static DateTime NewStaticField; + + public static double NewProp { get; set; } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple=true, Inherited=false)] +public class CustomNoteAttribute : Attribute { + public CustomNoteAttribute(string note) {Note = note;} + + public string Note; +} + +[CustomNote("123")] +public class NewToplevelClass : IExistingInterface, ICloneable { + public string ItfMethod(int i) { + return i.ToString(); + } + + [CustomNote("abcd")] + public void SomeMethod(int x) {} + + public virtual object Clone() { + return new NewToplevelClass(); + } + + public class AlsoNested { } + + [CustomNote("hijkl")] + public float NewProp {get; set;} + + public byte[] OtherNewProp {get; set;} + + public event EventHandler NewEvent; + public event EventHandler OtherNewEvent; +} + +public class NewGenericClass : NewToplevelClass { + public override object Clone() { + return new NewGenericClass(); + } +} + +public struct NewToplevelStruct { +} + +public interface INewInterface : IExistingInterface { + public int AddedItfMethod (string s); +} + +public enum NewEnum { + Red, Yellow, Green +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType.csproj new file mode 100644 index 0000000000000..9eb6b1380827a --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType.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.ReflectionAddNewType/deltascript.json b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/deltascript.json new file mode 100644 index 0000000000000..212d612c5dea6 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType/deltascript.json @@ -0,0 +1,6 @@ +{ + "changes": [ + {"document": "ReflectionAddNewType.cs", "update": "ReflectionAddNewType_v1.cs"}, + ] +} + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs index 527073494eebb..ce69168955a0e 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Reflection; using System.Runtime.CompilerServices; using Xunit; @@ -306,7 +307,6 @@ public static void TestAddStaticField() }); } - [ActiveIssue("https://github.com/dotnet/runtime/issues/63643", TestRuntimes.Mono)] [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsSupported))] public static void TestAddNestedClass() { @@ -325,7 +325,7 @@ public static void TestAddNestedClass() r = x.TestMethod(); - Assert.Equal("123456", r); + Assert.Equal("123456789", r); }); } @@ -427,5 +427,205 @@ public static void TestStaticLambdaRegression() }); } + + private static bool ContainsTypeWithName(Type[] types, string fullName) + { + foreach (var ty in types) { + if (ty.FullName == fullName) + return true; + } + return false; + } + + internal static Type CheckReflectedType(Assembly assm, Type[] allTypes, string nameSpace, string typeName, Action moreChecks = null, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0) + { + var fullName = $"{nameSpace}.{typeName}"; + var ty = assm.GetType(fullName); + Assert.True(ty != null, $"{callerFilePath}:{callerLineNumber}: expected Assembly.GetType for '{typeName}' to return non-null in {callerMemberName}"); + int nestedIdx = typeName.LastIndexOf('+'); + string comparisonName = typeName; + if (nestedIdx != -1) + comparisonName = typeName.Substring(nestedIdx+1); + Assert.True(comparisonName == ty.Name, $"{callerFilePath}:{callerLineNumber}: returned type has unexpected name '{ty.Name}' (expected: '{comparisonName}') in {callerMemberName}"); + Assert.True(ContainsTypeWithName (allTypes, fullName), $"{callerFilePath}:{callerLineNumber}: expected Assembly.GetTypes to contain '{fullName}', but it didn't in {callerMemberName}"); + if (moreChecks != null) + moreChecks(ty); + return ty; + } + + + internal static void CheckCustomNoteAttribute(MemberInfo subject, string expectedAttributeValue, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0) + { + var attrData = subject.GetCustomAttributesData(); + CustomAttributeData noteData = null; + foreach (var cad in attrData) + { + if (cad.AttributeType.FullName.Contains("CustomNoteAttribute")) + noteData = cad; + } + Assert.True(noteData != null, $"{callerFilePath}:{callerLineNumber}: expected a CustomNoteAttribute attributes on '{subject.Name}', but got null, in {callerMemberName}"); + Assert.True(1 == noteData.ConstructorArguments.Count, $"{callerFilePath}:{callerLineNumber}: expected exactly 1 constructor argument on CustomNoteAttribute, got {noteData.ConstructorArguments.Count}, in {callerMemberName}"); + object argVal = noteData.ConstructorArguments[0].Value; + Assert.True(expectedAttributeValue.Equals(argVal), $"{callerFilePath}:{callerLineNumber}: expected '{expectedAttributeValue}' as CustomNoteAttribute argument, got '{argVal}', in {callerMemberName}"); + + var attrs = subject.GetCustomAttributes(false); + object note = null; + foreach (var attr in attrs) + { + if (attr.GetType().FullName.Contains("CustomNoteAttribute")) + note = attr; + } + Assert.True(note != null, $"{callerFilePath}:{callerLineNumber}: expected a CustomNoteAttribute object on '{subject.Name}', but got null, in {callerMemberName}"); + object v = note.GetType().GetField("Note").GetValue(note); + Assert.True(expectedAttributeValue.Equals(v), $"{callerFilePath}:{callerLineNumber}: expected '{expectedAttributeValue}' in CustomNoteAttribute Note field, but got '{v}', in {callerMemberName}"); + } + + [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsSupported))] + public static void TestReflectionAddNewType() + { + ApplyUpdateUtil.TestCase(static () => + { + const string ns = "System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType"; + var assm = typeof(System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType.ZExistingClass).Assembly; + + var allTypes = assm.GetTypes(); + + CheckReflectedType(assm, allTypes, ns, "ZExistingClass"); + CheckReflectedType(assm, allTypes, ns, "ZExistingClass+PreviousNestedClass"); + + ApplyUpdateUtil.ApplyUpdate(assm); + + allTypes = assm.GetTypes(); + + CheckReflectedType(assm, allTypes, ns, "ZExistingClass", static (ty) => + { + var allMethods = ty.GetMethods(); + + MethodInfo newMethod = null; + foreach (var meth in allMethods) + { + if (meth.Name == "NewMethod") + newMethod = meth; + } + Assert.NotNull (newMethod); + + Assert.Equal (newMethod, ty.GetMethod ("NewMethod")); + + var allFields = ty.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public); + + // Mono doesn't do instance fields yet +#if false + FieldInfo newField = null; +#endif + FieldInfo newStaticField = null; + foreach (var fld in allFields) + { +#if false + if (fld.Name == "NewField") + newField = fld; +#endif + if (fld.Name == "NewStaticField") + newStaticField = fld; + } +#if false + Assert.NotNull(newField); + Assert.Equal(newField, ty.GetField("NewField")); +#endif + + Assert.NotNull(newStaticField); + Assert.Equal(newStaticField, ty.GetField("NewStaticField", BindingFlags.Static | BindingFlags.Public)); + + }); + CheckReflectedType(assm, allTypes, ns, "ZExistingClass+PreviousNestedClass"); + CheckReflectedType(assm, allTypes, ns, "IExistingInterface"); + + CheckReflectedType(assm, allTypes, ns, "ZExistingClass+NewNestedClass"); + + var newTy = CheckReflectedType(assm, allTypes, ns, "NewToplevelClass", static (ty) => + { + CheckCustomNoteAttribute(ty, "123"); + + var nested = ty.GetNestedType("AlsoNested"); + var allNested = ty.GetNestedTypes(); + + Assert.Equal("AlsoNested", nested.Name); + Assert.Same(ty, nested.DeclaringType); + + Assert.Equal(1, allNested.Length); + Assert.Same(nested, allNested[0]); + + var allInterfaces = ty.GetInterfaces(); + + Assert.Equal (2, allInterfaces.Length); + bool hasICloneable = false, hasINewInterface = false; + for (int i = 0; i < allInterfaces.Length; ++i) { + var itf = allInterfaces[i]; + if (itf.Name == "ICloneable") + hasICloneable = true; + if (itf.Name == "IExistingInterface") + hasINewInterface = true; + } + Assert.True(hasICloneable); + Assert.True(hasINewInterface); + + var allProperties = ty.GetProperties(); + + PropertyInfo newProp = null; + foreach (var prop in allProperties) + { + if (prop.Name == "NewProp") + newProp = prop; + } + Assert.NotNull(newProp); + + Assert.Equal(newProp, ty.GetProperty("NewProp")); + MethodInfo newPropGet = newProp.GetGetMethod(); + Assert.NotNull(newPropGet); + MethodInfo newPropSet = newProp.GetSetMethod(); + Assert.NotNull(newPropSet); + + Assert.Equal("get_NewProp", newPropGet.Name); + + CheckCustomNoteAttribute (newProp, "hijkl"); + + var allEvents = ty.GetEvents(); + + EventInfo newEvt = null; + foreach (var evt in allEvents) + { + if (evt.Name == "NewEvent") + newEvt = evt; + } + Assert.NotNull(newEvt); + + Assert.Equal(newEvt, ty.GetEvent("NewEvent")); + MethodInfo newEvtAdd = newEvt.GetAddMethod(); + Assert.NotNull(newEvtAdd); + MethodInfo newEvtRemove = newEvt.GetRemoveMethod(); + Assert.NotNull(newEvtRemove); + + Assert.Equal("add_NewEvent", newEvtAdd.Name); + }); + CheckReflectedType(assm, allTypes, ns, "NewGenericClass`1"); + CheckReflectedType(assm, allTypes, ns, "NewToplevelStruct"); + CheckReflectedType(assm, allTypes, ns, "INewInterface"); + CheckReflectedType(assm, allTypes, ns, "NewEnum", static (ty) => { + var names = Enum.GetNames (ty); + Assert.Equal(3, names.Length); + var vals = Enum.GetValues (ty); + Assert.Equal(3, vals.Length); + + Assert.NotNull(Enum.Parse (ty, "Red")); + Assert.NotNull(Enum.Parse (ty, "Yellow")); + }); + + // make some instances using reflection and use them through known interfaces + var o = Activator.CreateInstance(newTy); + + var i = (System.Reflection.Metadata.ApplyUpdate.Test.ReflectionAddNewType.IExistingInterface)o; + + Assert.Equal("123", i.ItfMethod(123)); + }); + } } } 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 6c5468b82c147..50941fe7b3ca7 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 @@ -61,6 +61,7 @@ + diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index d53619b8bc8f1..7451304a4b51d 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -8012,7 +8012,7 @@ type_commands_internal (int command, MonoClass *klass, MonoDomain *domain, guint buffer_add_string (buf, p->name); buffer_add_methodid (buf, domain, p->get); buffer_add_methodid (buf, domain, p->set); - buffer_add_int (buf, p->attrs); + buffer_add_int (buf, p->attrs & ~MONO_PROPERTY_META_FLAG_MASK); i ++; } g_assert (i == nprops); diff --git a/src/mono/mono/component/hot_reload-internals.h b/src/mono/mono/component/hot_reload-internals.h index 36d3606e17e4a..a668eb295c438 100644 --- a/src/mono/mono/component/hot_reload-internals.h +++ b/src/mono/mono/component/hot_reload-internals.h @@ -17,7 +17,7 @@ typedef struct _MonoClassRuntimeMetadataUpdateInfo { gboolean inited; } MonoClassRuntimeMetadataUpdateInfo; -/* Class-specific metadata update info. See +/* Class-specific metadata update info for an existing class. 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. @@ -27,11 +27,39 @@ struct _MonoClassMetadataUpdateInfo { * 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. */ + GSList *added_fields; /* a set of MonoClassMetadataUpdateField* values for every added field. */ + GSList *added_props; /* a set of MonoClassMetadataUpdateProperty* values */ + GSList *added_events; /* a set of MonoClassMetadataUpdateEvent* values */ MonoClassRuntimeMetadataUpdateInfo runtime; }; +/* + * Added type skeleton. + * + * When a hot reload delta is adding brand new class, the runtime allows a lot more leeway than when + * new members are added to existing classes. Anything that is possible to write in a baseline + * assembly is possible with an added class. One complication is that the EnCLog first contains a + * row with a new TypeDef table token, but that table row has zeros for the field and method token + * ids. Instead, each method and field is added by an EnCLog entry with a ENC_FUNC_ADD_METHOD or + * ENC_FUNC_ADD_FIELD function code. We don't want to materialzie the MonoClass for the new type + * definition until we've see all the added methods and fields. Instead when we process the log we + * collect a skeleton for the new class and then use it to create the MonoClass. + * + * We assume that the new methods and fields for a given class form a contiguous run (ie first and + * count are sufficient to identify all the rows belonging to the new class). + */ +typedef struct _MonoAddedDefSkeleton { + uint32_t typedef_token; /* which type is it */ + uint32_t first_method_idx, first_field_idx; + uint32_t method_count; + uint32_t field_count; + uint32_t first_prop_idx; + uint32_t prop_count; + uint32_t first_event_idx; + uint32_t event_count; +} MonoAddedDefSkeleton; + /* Keep in sync with Mono.HotReload.FieldStore in managed */ typedef struct _MonoHotReloadFieldStoreObject { @@ -44,10 +72,18 @@ typedef struct _MonoClassMetadataUpdateField { 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; +typedef struct _MonoClassMetadataUpdateProperty { + MonoProperty prop; + uint32_t generation; /* when this prop was added */ + uint32_t token; /* the Property table token where this prop was defined. */ +} MonoClassMetadataUpdateProperty; + +typedef struct _MonoClassMetadataUpdateEvent { + MonoEvent evt; + uint32_t generatino; /* when this event was added */ + uint32_t token; /* the Event table token where this event was defined. */ +} MonoClassMetadataUpdateEvent; + #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 2377b948a7b0a..5043423c203b0 100644 --- a/src/mono/mono/component/hot_reload-stub.c +++ b/src/mono/mono/component/hot_reload-stub.c @@ -86,6 +86,21 @@ 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 gboolean +hot_reload_stub_get_typedef_skeleton (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_method_idx, uint32_t *method_count, uint32_t *first_field_idx, uint32_t *field_count); + +static gboolean +hot_reload_stub_get_typedef_skeleton_properties (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_prop_idx, uint32_t *prop_count); + +static gboolean +hot_reload_stub_get_typedef_skeleton_events (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_event_idx, uint32_t *event_count); + +static MonoMethod * +hot_reload_stub_added_methods_iter (MonoClass *klass, gpointer *iter); + +static MonoClassField * +hot_reload_stub_added_fields_iter (MonoClass *klass, gboolean lazy, gpointer *iter); + static MonoComponentHotReload fn_table = { { MONO_COMPONENT_ITF_VERSION, &hot_reload_stub_available }, &hot_reload_stub_set_fastpath_data, @@ -111,6 +126,11 @@ static MonoComponentHotReload fn_table = { &hot_reload_stub_get_field, &hot_reload_stub_get_static_field_addr, &hot_reload_stub_find_method_by_name, + &hot_reload_stub_get_typedef_skeleton, + &hot_reload_stub_get_typedef_skeleton_properties, + &hot_reload_stub_get_typedef_skeleton_events, + &hot_reload_stub_added_methods_iter, + &hot_reload_stub_added_fields_iter, }; static bool @@ -263,6 +283,36 @@ hot_reload_stub_find_method_by_name (MonoClass *klass, const char *name, int par return NULL; } +static gboolean +hot_reload_stub_get_typedef_skeleton (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_method_idx, uint32_t *method_count, uint32_t *first_field_idx, uint32_t *field_count) +{ + return FALSE; +} + +static gboolean +hot_reload_stub_get_typedef_skeleton_properties (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_prop_idx, uint32_t *prop_count) +{ + return FALSE; +} + +static gboolean +hot_reload_stub_get_typedef_skeleton_events (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_event_idx, uint32_t *event_count) +{ + return FALSE; +} + +static MonoMethod * +hot_reload_stub_added_methods_iter (MonoClass *klass, gpointer *iter) +{ + return NULL; +} + +static MonoClassField * +hot_reload_stub_added_fields_iter (MonoClass *klass, gboolean lazy, gpointer *iter) +{ + 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 584b98892fe9c..69e21daaceb9a 100644 --- a/src/mono/mono/component/hot_reload.c +++ b/src/mono/mono/component/hot_reload.c @@ -11,6 +11,7 @@ #include "mono/component/hot_reload-internals.h" #include +#include #include "mono/metadata/assembly-internals.h" #include "mono/metadata/mono-hash-internals.h" #include "mono/metadata/metadata-internals.h" @@ -32,9 +33,6 @@ #include -#define ALLOW_CLASS_ADD -#define ALLOW_METHOD_ADD -#define ALLOW_FIELD_ADD #undef ALLOW_INSTANCE_FIELD_ADD typedef struct _BaselineInfo BaselineInfo; @@ -121,6 +119,21 @@ 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 gboolean +hot_reload_get_typedef_skeleton (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_method_idx, uint32_t *method_count, uint32_t *first_field_idx, uint32_t *field_count); + +static gboolean +hot_reload_get_typedef_skeleton_properties (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_prop_idx, uint32_t *prop_count); + +static gboolean +hot_reload_get_typedef_skeleton_events (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_event_idx, uint32_t *event_count); + +static MonoMethod * +hot_reload_added_methods_iter (MonoClass *klass, gpointer *iter); + +static MonoClassField * +hot_reload_added_fields_iter (MonoClass *klass, gboolean lazy, gpointer *iter); + 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, uint32_t field_flags, MonoError *error); @@ -149,6 +162,11 @@ static MonoComponentHotReload fn_table = { &hot_reload_get_field, &hot_reload_get_static_field_addr, &hot_reload_find_method_by_name, + &hot_reload_get_typedef_skeleton, + &hot_reload_get_typedef_skeleton_properties, + &hot_reload_get_typedef_skeleton_events, + &hot_reload_added_methods_iter, + &hot_reload_added_fields_iter, }; MonoComponentHotReload * @@ -240,6 +258,9 @@ struct _BaselineInfo { /* Parents for added methods, fields, etc */ GHashTable *member_parent; /* maps added methoddef or fielddef tokens to typedef tokens */ + + /* Skeletons for all newly-added types from every generation. Accessing the array requires the image lock. */ + GArray *skeletons; }; @@ -352,9 +373,7 @@ 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); + /* added_members, added_fields, added_props, added_events list nodes are allocated from the class mempool, don't free them here */ if (info->runtime.static_fields) { mono_g_hash_table_destroy (info->runtime.static_fields); @@ -376,6 +395,10 @@ baseline_info_destroy (BaselineInfo *info) g_slist_foreach (info->klass_info, klass_info_destroy, NULL); g_slist_free (info->klass_info); } + + if (info->skeletons) + g_array_free (info->skeletons, TRUE); + g_free (info); } @@ -574,13 +597,20 @@ 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); + +/* Add member->parent reverse lookup for newly-added classes */ +static void +add_member_parent (BaselineInfo *base_info, uint32_t typedef_token, uint32_t member_token); + /* 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); +/* Add method->parent reverse lookup for existing classes */ static void add_method_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t method_token, MonoDebugInformationEnc* pdb_address); +/* Add field->parent reverse lookup for existing classes */ static void add_field_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t field_token); @@ -1363,21 +1393,9 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de if (token_table != MONO_TABLE_METHOD) continue; -#ifndef ALLOW_METHOD_ADD - - 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; - } - -#endif - -#ifdef ALLOW_METHOD_ADD /* adding a new parameter to a new method is ok */ if (func_code == ENC_FUNC_ADD_PARAM && is_addition) continue; -#endif g_assert (func_code == 0); /* anything else doesn't make sense here */ } @@ -1399,60 +1417,53 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de /* okay, supported */ break; case MONO_TABLE_METHOD: -#ifdef ALLOW_METHOD_ADD if (func_code == ENC_FUNC_ADD_PARAM) continue; /* ok, allowed */ -#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; + /* ok */ + g_assert (func_code == ENC_FUNC_DEFAULT); break; -#endif + case MONO_TABLE_PROPERTYMAP: { + if (func_code == ENC_FUNC_ADD_PROPERTY) { + g_assert (i + 1 < rows); + i++; /* skip the next record */ + continue; + } + 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; + continue; + } + /* new rows, ok */ + break; + } case MONO_TABLE_PROPERTY: { - /* modifying a property, ok */ - 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); - mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support adding new properties. token=0x%08x", log_token); - unsupported_edits = TRUE; - continue; + /* ok */ + g_assert (func_code == ENC_FUNC_DEFAULT); + break; + } + case MONO_TABLE_EVENTMAP: { + if (func_code == ENC_FUNC_ADD_EVENT) { + g_assert (i + 1 < rows); + i++; /* skip the next record */ + continue; + } + 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; + continue; + } + /* new rows, ok */ + break; } case MONO_TABLE_METHODSEMANTICS: { 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)); - g_assert (mapped_token != -1); - mono_metadata_decode_row (&image_dmeta->tables [MONO_TABLE_METHODSEMANTICS], mapped_token - 1, sema_cols, MONO_METHOD_SEMA_SIZE); - - switch (sema_cols [MONO_METHOD_SEMA_SEMANTICS]) { - case METHOD_SEMANTIC_GETTER: - case METHOD_SEMANTIC_SETTER: { - int prop_method_index = sema_cols [MONO_METHOD_SEMA_METHOD]; - /* ok, if it's pointing to an existing getter/setter */ - 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); - unsupported_edits = TRUE; - continue; - } - default: - mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x adding new non-getter/setter property or event methods is not supported.", i, log_token); - mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support adding new non-getter/setter property or event methods. token=0x%08x", log_token); - unsupported_edits = TRUE; - continue; - } + /* new rows are fine */ + break; } else { 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); @@ -1489,11 +1500,16 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de * (its parent is set to 0). Once we support custom attribute * changes, we will support this kind of deletion for real. */ + /* FIXME: https://github.com/dotnet/roslyn/issues/60125 + * Roslyn seems to emit CA rows out of order which puts rows with unexpected Parent values in the wrong place. + */ +#ifdef DISALLOW_BROKEN_PARENT if (ca_base_cols [MONO_CUSTOM_ATTR_PARENT] != ca_upd_cols [MONO_CUSTOM_ATTR_PARENT] && ca_upd_cols [MONO_CUSTOM_ATTR_PARENT] != 0) { mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing CA table cols with a different Parent. token=0x%08x", log_token); unsupported_edits = TRUE; continue; } +#endif break; } else { /* Added a row. ok */ @@ -1526,14 +1542,7 @@ 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 - /* only allow adding methods to existing classes for now */ - if ( -#ifndef ALLOW_CLASS_ADD - !new_class && -#endif - func_code == ENC_FUNC_ADD_METHOD) { + if (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]; @@ -1548,13 +1557,8 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de 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) { + + if (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]; @@ -1569,7 +1573,6 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *de i++; /* skip the next record */ continue; } -#endif /* fallthru */ } default: @@ -1662,10 +1665,295 @@ dump_assembly_ref_names (MonoImage *image) } } +static void +skeleton_add_member (MonoAddedDefSkeleton *sk, uint32_t member_token) +{ + uint32_t table = mono_metadata_token_table (member_token); + uint32_t idx = mono_metadata_token_index (member_token); + + switch (table) { + case MONO_TABLE_METHOD: + if (!sk->first_method_idx) { + sk->first_method_idx = idx; + sk->method_count = 1; + } else { + /* current token is contiguous with previously seen ones */ + g_assert (sk->first_method_idx + sk->method_count == idx); + sk->method_count++; + } + break; + case MONO_TABLE_FIELD: + if (!sk->first_field_idx) { + sk->first_field_idx = idx; + sk->field_count = 1; + } else { + /* current token is contiguous with previously seen ones */ + g_assert (sk->first_field_idx + sk->field_count == idx); + sk->field_count++; + } + break; + case MONO_TABLE_PROPERTY: + if (!sk->first_prop_idx) { + sk->first_prop_idx = idx; + sk->prop_count = 1; + } else { + g_assert (sk->first_prop_idx + sk->prop_count == idx); + sk->prop_count++; + } + break; + case MONO_TABLE_EVENT: + if (!sk->first_event_idx) { + sk->first_event_idx = idx; + sk->event_count = 1; + } else { + g_assert (sk->first_event_idx + sk->event_count == idx); + sk->event_count++; + } + break; + default: + g_error ("Expected method or field def token, got 0x%08x", member_token); + g_assert_not_reached(); + } +} + +typedef struct Pass2Context { + GArray *skeletons; /* Skeleton for each new added type definition */ + GArray *nested_class_worklist; /* List of tokens in the NestedClass table to re-visit at the end of the pass */ +} Pass2Context; + +static void +pass2_context_init (Pass2Context *ctx) +{ + ctx->skeletons = g_array_new (FALSE, TRUE, sizeof(MonoAddedDefSkeleton)); + ctx->nested_class_worklist = g_array_new (FALSE, TRUE, sizeof(uint32_t)); +} + +static void +pass2_context_destroy (Pass2Context *ctx) +{ + if (ctx->skeletons) + g_array_free (ctx->skeletons, TRUE); + if (ctx->nested_class_worklist) + g_array_free (ctx->nested_class_worklist, TRUE); +} + +static void +pass2_context_add_skeleton (Pass2Context *ctx, uint32_t typedef_token) +{ + MonoAddedDefSkeleton sk = {0, }; + g_assert (mono_metadata_token_table (typedef_token) == MONO_TABLE_TYPEDEF); + sk.typedef_token = typedef_token; + g_array_append_val (ctx->skeletons, sk); +} + +static MonoAddedDefSkeleton* +pass2_context_get_skeleton (Pass2Context *ctx, uint32_t typedef_token) +{ + for (int i = 0 ; i < ctx->skeletons->len; ++i) { + MonoAddedDefSkeleton *sk = &((MonoAddedDefSkeleton*)ctx->skeletons->data)[i]; + if (sk->typedef_token == typedef_token) + return sk; + } + return NULL; +} + +static void +pass2_context_add_skeleton_member (Pass2Context *ctx, uint32_t typedef_token, uint32_t member_token) +{ + MonoAddedDefSkeleton* sk = pass2_context_get_skeleton (ctx, typedef_token); + g_assert (sk); + skeleton_add_member (sk, member_token); +} + +static gboolean +pass2_context_is_skeleton (Pass2Context *ctx, uint32_t typedef_token) +{ + return pass2_context_get_skeleton (ctx, typedef_token) != NULL; +} + +/* + * After processing all the EnCLog entries, transfer the skeletons from the Pass2Context to the BaselineInfo + * and make them available for mono_class_create_from_typedef to consume. + */ +static gboolean +baseline_info_consume_skeletons (Pass2Context *ctx, MonoImage *base_image, BaselineInfo *base_info, MonoError *error) +{ + mono_image_lock (base_image); + if (!base_info->skeletons) + base_info->skeletons = g_array_new (FALSE, TRUE, sizeof(MonoAddedDefSkeleton)); + g_array_append_vals (base_info->skeletons, ctx->skeletons->data, ctx->skeletons->len); + mono_image_unlock (base_image); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pass2: Added %d type definition skeletons. Total now %d.", ctx->skeletons->len, base_info->skeletons->len); + return TRUE; +} + +static gboolean +hot_reload_get_typedef_skeleton (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_method_idx, uint32_t *method_count, uint32_t *first_field_idx, uint32_t *field_count) +{ + BaselineInfo *info = baseline_info_lookup (base_image); + if (!info || !info->skeletons) + return FALSE; + gboolean found = FALSE; + mono_image_lock (base_image); + for (int i = 0; i < info->skeletons->len; ++i) { + MonoAddedDefSkeleton *sk = &((MonoAddedDefSkeleton*)info->skeletons->data)[i]; + if (sk->typedef_token == typedef_token) { + found = TRUE; + g_assert (first_method_idx); + *first_method_idx = sk->first_method_idx; + g_assert (method_count); + *method_count = sk->method_count; + g_assert (first_field_idx); + *first_field_idx = sk->first_field_idx; + g_assert (field_count); + *field_count = sk->field_count; + break; + } + } + mono_image_unlock (base_image); + return found; +} + +static gboolean +hot_reload_get_typedef_skeleton_properties (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_prop_idx, uint32_t *prop_count) +{ + BaselineInfo *info = baseline_info_lookup (base_image); + if (!info || !info->skeletons) + return FALSE; + gboolean found = FALSE; + mono_image_lock (base_image); + for (int i = 0; i < info->skeletons->len; ++i) { + MonoAddedDefSkeleton *sk = &((MonoAddedDefSkeleton*)info->skeletons->data)[i]; + if (sk->typedef_token == typedef_token) { + found = TRUE; + g_assert (first_prop_idx); + *first_prop_idx = sk->first_prop_idx; + g_assert (prop_count); + *prop_count = sk->prop_count; + break; + } + } + mono_image_unlock (base_image); + return found; +} + +static gboolean +hot_reload_get_typedef_skeleton_events (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_event_idx, uint32_t *event_count) +{ + BaselineInfo *info = baseline_info_lookup (base_image); + if (!info || !info->skeletons) + return FALSE; + gboolean found = FALSE; + mono_image_lock (base_image); + for (int i = 0; i < info->skeletons->len; ++i) { + MonoAddedDefSkeleton *sk = &((MonoAddedDefSkeleton*)info->skeletons->data)[i]; + if (sk->typedef_token == typedef_token) { + found = TRUE; + g_assert (first_event_idx); + *first_event_idx = sk->first_event_idx; + g_assert (event_count); + *event_count = sk->event_count; + break; + } + } + mono_image_unlock (base_image); + return found; +} + +/** + * Adds the given newly-added class to the image, updating various data structures (e.g. name cache). + */ +static void +add_typedef_to_image_metadata (MonoImage *image_base, uint32_t log_token) +{ + uint32_t cols[MONO_TYPEDEF_SIZE]; + uint32_t row = mono_metadata_token_index (log_token); + mono_metadata_decode_row (&image_base->tables[MONO_TABLE_TYPEDEF], row - 1, cols, MONO_TYPEDEF_SIZE); + uint32_t visib = cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; + + /* + * Add the class name to the name chache. Except if the + * class is nested (has one of the nested visibility flags), since those are + * looked up differently. + */ + gboolean isNested = (visib >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visib <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM); + if (!isNested) { + const char *name_space = mono_metadata_string_heap (image_base, cols[MONO_TYPEDEF_NAMESPACE]); + const char *name = mono_metadata_string_heap (image_base, cols[MONO_TYPEDEF_NAME]); + mono_image_add_to_name_cache (image_base, name_space, name, row); + } +} + +/** + * Given a row in the NestedClass table, add it to the worklist if the enclosing type isn't a skeleton. + */ +static void +add_nested_class_to_worklist (Pass2Context *ctx, MonoImage *image_base, uint32_t log_token) +{ + uint32_t cols[MONO_TABLE_NESTEDCLASS]; + mono_metadata_decode_row (&image_base->tables[MONO_TABLE_NESTEDCLASS], mono_metadata_token_index (log_token) - 1, cols, MONO_NESTED_CLASS_SIZE); + /* if the enclosing clas hasn't been created yet, don't do anything. mono_class_setup_nested_types() will pick up the new row */ + if (pass2_context_is_skeleton (ctx, cols[MONO_NESTED_CLASS_ENCLOSING])) + return; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "EnC: adding nested class row 0x%08x (enclosing: 0x%08x, nested: 0x%08x) to worklist", log_token, cols[MONO_NESTED_CLASS_ENCLOSING], cols[MONO_NESTED_CLASS_NESTED]); + + g_array_append_val (ctx->nested_class_worklist, log_token); +} + +/** + * For any enclosing classes that already had their nested classes property initialized, + * add any newly-added classes the the nested classes list. + * + * This has to be called late because we construct the nested class - which + * could have been a skeleton. + */ +static gboolean +pass2_update_nested_classes (Pass2Context *ctx, MonoImage *image_base, MonoError *error) +{ + if (!ctx->nested_class_worklist) + return TRUE; + GArray *arr = ctx->nested_class_worklist; + for (int i = 0; i < arr->len; ++i) { + uint32_t log_token = g_array_index (arr, uint32_t, i); + + uint32_t cols[MONO_TABLE_NESTEDCLASS]; + mono_metadata_decode_row (&image_base->tables[MONO_TABLE_NESTEDCLASS], mono_metadata_token_index (log_token) - 1, cols, MONO_NESTED_CLASS_SIZE); + /* if the enclosing clas hasn't been created yet, don't do anything. mono_class_setup_nested_types() will pick up the new row */ + uint32_t enclosing_typedef_row = cols[MONO_NESTED_CLASS_ENCLOSING]; + if (pass2_context_is_skeleton (ctx, enclosing_typedef_row)) + continue; + + MonoClass *enclosing_klass = mono_class_get_checked (image_base, mono_metadata_make_token (MONO_TABLE_TYPEDEF, enclosing_typedef_row), error); + return_val_if_nok (error, FALSE); + g_assert (enclosing_klass); + /* enclosing class is set up, but noone asked for its nested classes yet. Nothing to do here. */ + if (!m_class_is_nested_classes_inited (enclosing_klass)) + continue; + MonoClass *klass = mono_class_get_checked (image_base, mono_metadata_make_token (MONO_TABLE_TYPEDEF, cols[MONO_NESTED_CLASS_NESTED]), error); + return_val_if_nok (error, FALSE); + g_assert (klass); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "EnC: worklist row 0x%08x adding to enclosing 0x%08x, the nested class 0x%08x", log_token, cols[MONO_NESTED_CLASS_ENCLOSING], cols[MONO_NESTED_CLASS_NESTED]); + GList *nested_classes = mono_class_get_nested_classes_property (enclosing_klass); + + /* make a list node for the new class */ + GList *new_list = mono_g_list_prepend_image (image_base, NULL, klass); + + /* either set the property to the new node, or append the new node to the existing list */ + if (!nested_classes) { + mono_class_set_nested_classes_property (enclosing_klass, new_list); + } else { + g_list_concat (nested_classes, new_list); + } + } + + return TRUE; + +} /* do actuall enclog application */ static gboolean -apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, MonoError *error) +apply_enclog_pass2 (Pass2Context *ctx, MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, MonoError *error) { MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG]; int rows = table_info_get_rows (table_enclog); @@ -1695,9 +1983,9 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen 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 + uint32_t add_member_typedef = 0; + uint32_t add_property_propertymap = 0; + uint32_t add_event_eventmap = 0; gboolean assemblyref_updated = FALSE; for (int i = 0; i < rows ; ++i) { @@ -1720,15 +2008,9 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen switch (func_code) { case ENC_FUNC_DEFAULT: /* default */ break; -#ifdef ALLOW_METHOD_ADD case ENC_FUNC_ADD_METHOD: { 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; + add_member_typedef = log_token; break; } @@ -1736,19 +2018,24 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen g_assert (token_table == MONO_TABLE_METHOD); 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; + add_member_typedef = log_token; break; } -#endif + + case ENC_FUNC_ADD_PROPERTY: { + g_assert (token_table == MONO_TABLE_PROPERTYMAP); + add_property_propertymap = log_token; + break; + } + + case ENC_FUNC_ADD_EVENT: { + g_assert (token_table = MONO_TABLE_EVENTMAP); + add_event_eventmap = log_token; + break; + } + default: g_error ("EnC: unsupported FuncCode, should be caught by pass1"); break; @@ -1794,22 +2081,30 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen break; } case MONO_TABLE_METHOD: { -#ifdef ALLOW_METHOD_ADD /* if adding a param, handle it with the next record */ if (func_code == ENC_FUNC_ADD_PARAM) break; 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_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); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Debug info for method 0x%08x has ppdb idx 0x%08x", log_token, method_debug_information ? method_debug_information->idx : 0); - add_method_to_baseline (base_info, delta_info, add_member_klass, log_token, method_debug_information); - add_member_klass = NULL; + g_assertf (add_member_typedef, "EnC: new method added but I don't know the class, should be caught by pass1"); + if (pass2_context_is_skeleton (ctx, add_member_typedef)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new method 0x%08x to new class 0x%08x", log_token, add_member_typedef); + pass2_context_add_skeleton_member (ctx, add_member_typedef, log_token); + add_member_parent (base_info, add_member_typedef, log_token); + } else { + MonoClass *add_member_klass = mono_class_get_checked (image_base, add_member_typedef, 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", add_member_typedef, mono_error_get_message (error)); + return FALSE; + } + + 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); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Debug info for method 0x%08x has ppdb idx 0x%08x", log_token, method_debug_information ? method_debug_information->idx : 0); + add_method_to_baseline (base_info, delta_info, add_member_klass, log_token, method_debug_information); + } + add_member_typedef = 0; } -#endif if (!base_info->method_table_update) base_info->method_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); @@ -1829,56 +2124,63 @@ 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); } -#if defined(ALLOW_METHOD_ADD) || defined(ALLOW_FIELD_ADD) - add_member_klass = NULL; -#endif + add_member_typedef = 0; break; } 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)); + g_assert (add_member_typedef); + if (pass2_context_is_skeleton (ctx, add_member_typedef)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new field 0x%08x to new class 0x%08x", log_token, add_member_typedef); + pass2_context_add_skeleton_member (ctx, add_member_typedef, log_token); + add_member_parent (base_info, add_member_typedef, log_token); + } else { + MonoClass *add_member_klass = mono_class_get_checked (image_base, add_member_typedef, 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", add_member_typedef, mono_error_get_message (error)); + return FALSE; + } + + 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); + 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); #ifndef ALLOW_INSTANCE_FIELD_ADD - 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; - } + 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; + } #endif - add_field_to_baseline (base_info, delta_info, add_member_klass, log_token); + 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, field_flags, 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; + /* 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, field_flags, 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 + + add_member_typedef = 0; break; } case MONO_TABLE_TYPEDEF: { -#ifdef ALLOW_CLASS_ADD if (is_addition) { /* Adding a new class. ok */ switch (func_code) { - case ENC_FUNC_DEFAULT: + case ENC_FUNC_DEFAULT: { /* ok, added a new class */ - /* TODO: do things here */ + pass2_context_add_skeleton (ctx, log_token); + add_typedef_to_image_metadata (image_base, log_token); break; + } case ENC_FUNC_ADD_METHOD: case ENC_FUNC_ADD_FIELD: /* ok, adding a new field or method to a new class */ @@ -1887,7 +2189,6 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen * 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: @@ -1895,30 +2196,128 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen } 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_NESTEDCLASS: { + g_assert (is_addition); + add_nested_class_to_worklist (ctx, image_base, log_token); + break; + } + case MONO_TABLE_PROPERTYMAP: { + switch (func_code) { + case ENC_FUNC_DEFAULT: + /* adding a new property map - parent could be new or existing class, but it didn't have a propertymap before */ + g_assert (is_addition); + break; + case ENC_FUNC_ADD_PROPERTY: + /* adding a new property to a propertymap. could be new or existing propertymap. */ + break; + default: + g_assert_not_reached (); /* unexpected func code */ + } + uint32_t cols[MONO_PROPERTY_MAP_SIZE]; + mono_metadata_decode_row (&image_base->tables [MONO_TABLE_PROPERTYMAP], token_index - 1, cols, MONO_PROPERTY_MAP_SIZE); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "EnC: propertymap parent = 0x%08x, props = 0x%08x\n", cols[MONO_PROPERTY_MAP_PARENT], cols [MONO_PROPERTY_MAP_PROPERTY_LIST]); break; } case MONO_TABLE_PROPERTY: { /* allow updates to existing properties. */ - /* FIXME: use DeltaInfo:prev_gen_rows instead of image_base */ - g_assert (token_index <= table_info_get_rows (&image_base->tables [token_table])); + if (!is_addition) + /* FIXME: use DeltaInfo:prev_gen_rows instead of image_base */ + g_assert (token_index <= table_info_get_rows (&image_base->tables [token_table])); + else { + g_assert (add_property_propertymap != 0); + + uint32_t parent_type_token = mono_metadata_decode_row_col (&image_base->tables [MONO_TABLE_PROPERTYMAP], mono_metadata_token_index (add_property_propertymap) - 1, MONO_PROPERTY_MAP_PARENT); + parent_type_token = mono_metadata_make_token (MONO_TABLE_TYPEDEF, parent_type_token); + + g_assert (parent_type_token != 0); + + if (pass2_context_is_skeleton (ctx, parent_type_token)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new property 0x%08x to new class 0x%08x", log_token, parent_type_token); + pass2_context_add_skeleton_member (ctx, parent_type_token, log_token); + add_member_parent (base_info, parent_type_token, log_token); + break; + } else { + MonoClass *add_property_klass = mono_class_get_checked (image_base, parent_type_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", parent_type_token, mono_error_get_message (error)); + return FALSE; + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new property 0x%08x to class %s.%s", log_token, m_class_get_name_space (add_property_klass), m_class_get_name (add_property_klass)); + + /* TODO: metadata-update: add a new MonoClassMetadataUpdatePropertyInfo to added_props */ + break; + } + + add_property_propertymap = 0; + } /* assuming that property attributes and type haven't changed. */ break; } + case MONO_TABLE_EVENTMAP: { + switch (func_code) { + case ENC_FUNC_DEFAULT: + /* adding a new eventmap - parent could be new or existing class, but it didn't have an eventmap before */ + g_assert (is_addition); + break; + case ENC_FUNC_ADD_EVENT: + /* adding a new event to an eventmap. could be new or existing eventmap. */ + break; + default: + g_assert_not_reached (); /* unexpected func code */ + } + uint32_t cols[MONO_EVENT_MAP_SIZE]; + mono_metadata_decode_row (&image_base->tables [MONO_TABLE_EVENTMAP], token_index - 1, cols, MONO_EVENT_MAP_SIZE); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "EnC: eventmap parent = 0x%08x, evts = 0x%08x\n", cols[MONO_EVENT_MAP_PARENT], cols [MONO_EVENT_MAP_EVENTLIST]); + break; + + } + case MONO_TABLE_EVENT: { + if (!is_addition) + /* FIXME: use DeltaInfo:prev_gen_rows instead of image_base */ + g_assert (token_index <= table_info_get_rows (&image_base->tables [token_table])); + else { + g_assert (add_event_eventmap != 0); + + uint32_t parent_type_token = mono_metadata_decode_row_col (&image_base->tables [MONO_TABLE_EVENTMAP], mono_metadata_token_index (add_event_eventmap) - 1, MONO_EVENT_MAP_PARENT); + parent_type_token = mono_metadata_make_token (MONO_TABLE_TYPEDEF, parent_type_token); + + g_assert (parent_type_token != 0); + + if (pass2_context_is_skeleton (ctx, parent_type_token)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new event 0x%08x to new class 0x%08x", log_token, parent_type_token); + pass2_context_add_skeleton_member (ctx, parent_type_token, log_token); + add_member_parent (base_info, parent_type_token, log_token); + break; + } else { + MonoClass *add_event_klass = mono_class_get_checked (image_base, parent_type_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", parent_type_token, mono_error_get_message (error)); + return FALSE; + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new event 0x%08x to class %s.%s", log_token, m_class_get_name_space (add_event_klass), m_class_get_name (add_event_klass)); + + /* TODO: metadata-update: add a new MonoEventInfo to the bag on the class? */ + break; + } + + add_event_eventmap = 0; + } + /* assuming that event attributes and type haven't changed. */ + break; + } case MONO_TABLE_CUSTOMATTRIBUTE: { /* ok, pass1 checked for disallowed modifications */ break; } case MONO_TABLE_PARAM: { /* ok, pass1 checked for disallowed modifications */ - /* ALLOW_METHOD_ADD: FIXME: here we would really like to update the method's paramlist column to point to the new params. */ /* if there were multiple added methods, this comes in as several method * additions, followed by the parameter additions. * @@ -1940,6 +2339,11 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen */ break; } + case MONO_TABLE_INTERFACEIMPL: { + g_assert (is_addition); + /* added rows ok (for added classes). will be processed when the MonoClass is created. */ + break; + } default: { g_assert (token_index > table_info_get_rows (&image_base->tables [token_table])); if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) @@ -1947,6 +2351,14 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen } } } + + + if (!baseline_info_consume_skeletons (ctx, image_base, base_info, error)) + return FALSE; + + if (!pass2_update_nested_classes (ctx, image_base, error)) + return FALSE; + return TRUE; } @@ -2093,12 +2505,16 @@ hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) dump_update_summary (image_base, image_dmeta); - if (!apply_enclog_pass2 (image_base, base_info, generation, image_dmeta, delta_info, dil_bytes, dil_length, error)) { + Pass2Context pass2ctx = {0,}; + pass2_context_init (&pass2ctx); + if (!apply_enclog_pass2 (&pass2ctx, image_base, base_info, generation, image_dmeta, delta_info, dil_bytes, dil_length, error)) { + pass2_context_destroy (&pass2ctx); mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error applying delta image to base=%s, due to: %s", basename, mono_error_get_message (error)); hot_reload_update_cancel (generation); return; } mono_error_assert_ok (error); + pass2_context_destroy (&pass2ctx); MonoAssemblyLoadContext *alc = mono_image_get_alc (image_base); hot_reload_update_publish (alc, generation); @@ -2267,7 +2683,7 @@ hot_reload_table_num_rows_slow (MonoImage *base, int table_index) { BaselineInfo *base_info = baseline_info_lookup (base); if (!base_info) - return FALSE; + return 0; uint32_t current_gen = hot_reload_get_thread_generation (); @@ -2283,21 +2699,29 @@ hot_reload_table_num_rows_slow (MonoImage *base, int table_index) return rows; } +static void +add_member_parent (BaselineInfo *base_info, uint32_t typedef_token, uint32_t member_token) +{ + if (!base_info->member_parent) { + base_info->member_parent = g_hash_table_new (g_direct_hash, g_direct_equal); + } + g_hash_table_insert (base_info->member_parent, GUINT_TO_POINTER (member_token), GUINT_TO_POINTER (typedef_token)); +} + + static void add_member_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t member_token) { /* 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); - } 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))); + add_member_parent (base_info, m_class_get_type_token (klass), member_token); } + static void add_method_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t method_token, MonoDebugInformationEnc* pdb_address) { @@ -2416,11 +2840,10 @@ 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); + GSList *added_fields = info->added_fields; + + for (GSList *p = added_fields; p; p = p->next) { + MonoClassMetadataUpdateField *field = (MonoClassMetadataUpdateField *)p->data; if (field->token == fielddef_token) return &field->field; } @@ -2431,11 +2854,7 @@ hot_reload_get_field (MonoClass *klass, uint32_t fielddef_token) { 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, uint32_t field_flags, 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. - - if (!m_class_is_inited (parent_klass)) - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding fielddef 0x%08x to uninited class 0x%08x", fielddef_token, m_class_get_type_token (parent_klass)); + g_assert (m_class_is_inited (parent_klass)); MonoClassMetadataUpdateInfo *parent_info = mono_class_get_or_add_metadata_update_info (parent_klass); @@ -2455,11 +2874,7 @@ metadata_update_field_setup_basic_info_and_resolve (MonoImage *image_base, Basel 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); + parent_info->added_fields = g_slist_prepend_mem_manager (m_class_get_mem_manager (parent_klass), parent_info->added_fields, field); return field; } @@ -2605,3 +3020,70 @@ hot_reload_find_method_by_name (MonoClass *klass, const char *name, int param_co return res; } + +static MonoMethod * +hot_reload_added_methods_iter (MonoClass *klass, gpointer *iter) +{ + g_assert (iter); + // invariant: idx is one past the method we previously returned. + uint32_t idx = GPOINTER_TO_UINT (*iter); + g_assert (idx >= mono_class_get_method_count (klass)); + + GSList *members = hot_reload_get_added_members (klass); + if (!members) + return NULL; + // expect to only see class defs here. Rationale: adding methods to generic classes is not + // allowed (if a generation adds a new generic class, it won't be here - those methods will + // be in the normal iteration code, not here. + g_assert (m_class_get_class_kind (klass) == MONO_CLASS_DEF); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Iterating added methods of 0x%08x idx = %u", m_class_get_type_token (klass), idx); + + // go through the added members incrementing cur_count until it's equal to idx or we run out + // of added members. + uint32_t cur_count = mono_class_get_method_count (klass); + 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; + if (cur_count == idx) { + // found a method, advance iter and return the method. + *iter = GUINT_TO_POINTER (1+idx); + ERROR_DECL (local_error); + MonoMethod *method = mono_get_method_checked (m_class_get_image (klass), token, klass, NULL, local_error); + mono_error_cleanup (local_error); + return method; + } + cur_count++; + } + // ran out of added methods, iteration is finished. + return NULL; +} + +static MonoClassField * +hot_reload_added_fields_iter (MonoClass *klass, gboolean lazy G_GNUC_UNUSED, gpointer *iter) +{ + MonoClassMetadataUpdateInfo *info = mono_class_get_metadata_update_info (klass); + if (!info) + return NULL; + + GSList *added_fields = info->added_fields; + + // invariant: idx is one past the field we previously returned. + uint32_t idx = GPOINTER_TO_UINT(*iter); + + g_assert (idx >= mono_class_get_field_count (klass)); + + uint32_t field_idx = idx - mono_class_get_field_count (klass); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Iterating added fields of 0x%08x idx = %u", m_class_get_type_token (klass), field_idx); + + GSList *field_node = g_slist_nth (added_fields, field_idx); + /* we reached the end, we're done */ + if (!field_node) + return NULL; + MonoClassMetadataUpdateField *field = (MonoClassMetadataUpdateField *)field_node->data; + idx++; + *iter = GUINT_TO_POINTER (idx); + return &field->field; +} diff --git a/src/mono/mono/component/hot_reload.h b/src/mono/mono/component/hot_reload.h index 790234e557718..62c7c305c5ed7 100644 --- a/src/mono/mono/component/hot_reload.h +++ b/src/mono/mono/component/hot_reload.h @@ -40,6 +40,11 @@ typedef struct _MonoComponentHotReload { 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); + gboolean (*get_typedef_skeleton) (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_method_idx, uint32_t *method_count, uint32_t *first_field_idx, uint32_t *field_count); + gboolean (*get_typedef_skeleton_properties) (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_prop_idx, uint32_t *prop_count); + gboolean (*get_typedef_skeleton_events) (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_event_idx, uint32_t *event_count); + MonoMethod* (*added_methods_iter) (MonoClass *klass, gpointer *iter); + MonoClassField* (*added_fields_iter) (MonoClass *klass, gboolean lazy, gpointer *iter); } MonoComponentHotReload; MONO_COMPONENT_EXPORT_ENTRYPOINT diff --git a/src/mono/mono/metadata/class-accessors.c b/src/mono/mono/metadata/class-accessors.c index 457292de55074..d161a74c39575 100644 --- a/src/mono/mono/metadata/class-accessors.c +++ b/src/mono/mono/metadata/class-accessors.c @@ -602,6 +602,7 @@ mono_class_get_metadata_update_info (MonoClass *klass) return (MonoClassMetadataUpdateInfo *)get_pointer_property (klass, PROP_METADATA_UPDATE_INFO); case MONO_CLASS_GINST: case MONO_CLASS_GPARAM: + case MONO_CLASS_ARRAY: case MONO_CLASS_POINTER: case MONO_CLASS_GC_FILLER: return NULL; diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c index 12e4d336f7e33..1bb2f0211b708 100644 --- a/src/mono/mono/metadata/class-init.c +++ b/src/mono/mono/metadata/class-init.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -652,6 +653,15 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError 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); + } else if (G_UNLIKELY (cols [MONO_TYPEDEF_FIELD_LIST] == 0 && cols [MONO_TYPEDEF_METHOD_LIST] == 0 && image->has_updates)) { + uint32_t first_field_idx, first_method_idx, field_count, method_count; + if (mono_metadata_update_get_typedef_skeleton (image, type_token, &first_method_idx, &method_count, &first_field_idx, &field_count)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Creating class '%s.%s' from skeleton (first_method_idx = 0x%08x, count = 0x%08x, first_field_idx = 0x%08x, count=0x%08x)", nspace, name, first_method_idx, method_count, first_field_idx, field_count); + mono_class_set_first_field_idx (klass, first_field_idx - 1); + mono_class_set_first_method_idx (klass, first_method_idx - 1); + mono_class_set_field_count (klass, field_count); + mono_class_set_method_count (klass, method_count); + } } /* reserve space to store vector pointer in arrays */ diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index ccaf81e98ea17..0d0ae1389d852 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -192,9 +192,18 @@ struct _MonoProperty { const char *name; MonoMethod *get; MonoMethod *set; - guint32 attrs; + guint32 attrs; /* upper bits store non-ECMA flags */ }; +/* non-ECMA flags for the MonoProperty attrs field */ +enum { + /* added by metadata-update after class was created; + * not in MonoClassPropertyInfo array - don't do ptr arithmetic */ + MONO_PROPERTY_META_FLAG_FROM_UPDATE = 0x00010000, + MONO_PROPERTY_META_FLAG_MASK = 0x00010000, +}; + + struct _MonoEvent { MonoClass *parent; const char *name; @@ -204,9 +213,19 @@ struct _MonoEvent { #ifndef MONO_SMALL_CONFIG MonoMethod **other; #endif - guint32 attrs; + guint32 attrs; /* upper bits store non-ECMA flags */ +}; + +/* non-ECMA flags for the MonoEvent attrs field */ +enum { + /* added by metadata-update after class was created; + * not in MonoClassEventInfo array - don't do ptr arithmetic */ + MONO_EVENT_META_FLAG_FROM_UPDATE = 0x00010000, + + MONO_EVENT_META_FLAG_MASK = 0x00010000, }; + /* type of exception being "on hold" for later processing (see exception_type) */ typedef enum { MONO_EXCEPTION_NONE = 0, @@ -1371,12 +1390,14 @@ mono_class_get_first_field_idx (MonoClass *klass); void mono_class_set_first_field_idx (MonoClass *klass, guint32 idx); +MONO_COMPONENT_API guint32 mono_class_get_method_count (MonoClass *klass); void mono_class_set_method_count (MonoClass *klass, guint32 count); +MONO_COMPONENT_API guint32 mono_class_get_field_count (MonoClass *klass); @@ -1401,9 +1422,11 @@ mono_class_get_exception_data (MonoClass *klass); void mono_class_set_exception_data (MonoClass *klass, MonoErrorBoxed *value); +MONO_COMPONENT_API GList* mono_class_get_nested_classes_property (MonoClass *klass); +MONO_COMPONENT_API void mono_class_set_nested_classes_property (MonoClass *klass, GList *value); @@ -1600,6 +1623,18 @@ m_field_is_from_update (MonoClassField *field) return (m_field_get_meta_flags (field) & MONO_CLASS_FIELD_META_FLAG_FROM_UPDATE) != 0; } +static inline gboolean +m_property_is_from_update (MonoProperty *prop) +{ + return (prop->attrs & MONO_PROPERTY_META_FLAG_FROM_UPDATE) != 0; +} + +static inline gboolean +m_event_is_from_update (MonoEvent *evt) +{ + return (evt->attrs & MONO_EVENT_META_FLAG_FROM_UPDATE) != 0; +} + /* * Memory allocation for images/classes/methods * diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 9d400cb90b0aa..dca80a49c89b7 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -2541,10 +2541,9 @@ mono_class_get_field_token (MonoClassField *field) static int mono_field_get_index (MonoClassField *field) { + g_assert (!m_field_is_from_update (field)); int index = field - m_class_get_fields (m_field_get_parent (field)); g_assert (index >= 0 && index < mono_class_get_field_count (m_field_get_parent (field))); - /* TODO: metadata-update: check if the field was added */ - g_assert (!m_class_get_image (m_field_get_parent (field))->has_updates); return index; } @@ -2654,6 +2653,8 @@ mono_class_get_event_token (MonoEvent *event) MonoClassEventInfo *info = mono_class_get_event_info (klass); if (info) { for (i = 0; i < info->count; ++i) { + /* TODO: metadata-update: get tokens for added props, too */ + g_assert (!m_event_is_from_update (&info->events[i])); if (&info->events [i] == event) return mono_metadata_make_token (MONO_TABLE_EVENT, info->first + i + 1); } @@ -2697,6 +2698,8 @@ mono_class_get_property_token (MonoProperty *prop) gpointer iter = NULL; MonoClassPropertyInfo *info = mono_class_get_property_info (klass); while ((p = mono_class_get_properties (klass, &iter))) { + /* TODO: metadata-update: get tokens for added props, too */ + g_assert (!m_property_is_from_update (p)); if (&info->properties [i] == prop) return mono_metadata_make_token (MONO_TABLE_PROPERTY, info->first + i + 1); @@ -3088,7 +3091,7 @@ mono_image_init_name_cache (MonoImage *image) mono_image_unlock (image); } -/*FIXME Only dynamic assemblies should allow this operation.*/ +/*FIXME Only dynamic assemblies or metadata-update should allow this operation.*/ /** * mono_image_add_to_name_cache: */ @@ -5037,10 +5040,9 @@ mono_class_get_fields (MonoClass* klass, gpointer *iter) MonoClassField* mono_class_get_fields_internal (MonoClass *klass, gpointer *iter) { - MonoClassField* field; if (!iter) return NULL; - /* TODO: metadata-update - also iterate over the added fields */ + MonoImage *image = m_class_get_image (klass); if (!*iter) { mono_class_setup_fields (klass); if (mono_class_has_failure (klass)) @@ -5048,19 +5050,28 @@ mono_class_get_fields_internal (MonoClass *klass, gpointer *iter) /* start from the first */ if (mono_class_get_field_count (klass)) { MonoClassField *klass_fields = m_class_get_fields (klass); - *iter = &klass_fields [0]; + uint32_t idx = 0; + *iter = GUINT_TO_POINTER (idx + 1); return &klass_fields [0]; } else { /* no fields */ - return NULL; + if (G_LIKELY (!image->has_updates)) + return NULL; + else + *iter = 0; } } - field = (MonoClassField *)*iter; - field++; - if (field < &m_class_get_fields (klass) [mono_class_get_field_count (klass)]) { - *iter = field; + // invariant: idx is one past the field we previously returned + uint32_t idx = GPOINTER_TO_UINT(*iter); + if (idx < mono_class_get_field_count (klass)) { + MonoClassField *field = &m_class_get_fields (klass) [idx]; + ++idx; + *iter = GUINT_TO_POINTER (idx); return field; } + if (G_UNLIKELY (image->has_updates)) { + return mono_metadata_update_added_fields_iter (klass, FALSE, iter); + } return NULL; } @@ -5079,9 +5090,9 @@ mono_class_get_fields_internal (MonoClass *klass, gpointer *iter) MonoMethod* mono_class_get_methods (MonoClass* klass, gpointer *iter) { - MonoMethod** method; if (!iter) return NULL; + MonoImage *image = m_class_get_image (klass); if (!*iter) { mono_class_setup_methods (klass); @@ -5090,23 +5101,33 @@ mono_class_get_methods (MonoClass* klass, gpointer *iter) * We can't fail lookup of methods otherwise the runtime will burst in flames on all sort of places. * FIXME we should better report this error to the caller */ - if (!klass_methods) + if (!klass_methods && !image->has_updates) return NULL; + uint32_t idx = 0; /* start from the first */ if (mono_class_get_method_count (klass)) { - *iter = &klass_methods [0]; + // idx is 1 more than the method we just returned + *iter = GUINT_TO_POINTER (idx + 1); return klass_methods [0]; } else { /* no method */ - return NULL; + if (G_LIKELY (!image->has_updates)) + return NULL; + else + *iter = 0; } } - method = (MonoMethod **)*iter; - method++; - if (method < &m_class_get_methods (klass) [mono_class_get_method_count (klass)]) { - *iter = method; - return *method; + // idx is 1 more than the method we returned on the previous iteration + uint32_t idx = GPOINTER_TO_UINT (*iter); + if (idx < mono_class_get_method_count (klass)) { + // if we're still in range, return the next method and advance iter 1 past it. + MonoMethod *method = m_class_get_methods (klass) [idx]; + idx++; + *iter = GUINT_TO_POINTER (idx); + return method; } + if (G_UNLIKELY (image->has_updates)) + return mono_metadata_update_added_methods_iter (klass, iter); return NULL; } @@ -5603,7 +5624,7 @@ mono_property_get_parent (MonoProperty *prop) guint32 mono_property_get_flags (MonoProperty *prop) { - return prop->attrs; + return prop->attrs & ~MONO_PROPERTY_META_FLAG_MASK; } /** @@ -5673,7 +5694,7 @@ mono_event_get_parent (MonoEvent *event) guint32 mono_event_get_flags (MonoEvent *event) { - return event->attrs; + return event->attrs & ~MONO_EVENT_META_FLAG_MASK; } /** @@ -6561,11 +6582,9 @@ mono_field_resolve_flags (MonoClassField *field) MonoClassField* mono_class_get_fields_lazy (MonoClass* klass, gpointer *iter) { - MonoClassField* field; if (!iter) return NULL; - /* TODO: metadata-update: iterate over the added fields, too */ - g_assert (!m_class_get_image (klass)->has_updates); + MonoImage *image = m_class_get_image (klass); if (!*iter) { mono_class_setup_basic_field_info (klass); MonoClassField *klass_fields = m_class_get_fields (klass); @@ -6573,18 +6592,28 @@ mono_class_get_fields_lazy (MonoClass* klass, gpointer *iter) return NULL; /* start from the first */ if (mono_class_get_field_count (klass)) { - *iter = &klass_fields [0]; - return (MonoClassField *)*iter; + uint32_t idx = 0; + *iter = GUINT_TO_POINTER (idx+1); + return &klass_fields [0]; } else { /* no fields */ - return NULL; + if (G_LIKELY(!image->has_updates)) + return NULL; + else + *iter = 0; } } - field = (MonoClassField *)*iter; - field++; - if (field < &m_class_get_fields (klass) [mono_class_get_field_count (klass)]) { - *iter = field; - return (MonoClassField *)*iter; + // invariant: idx is one past the field we previously returned + uint32_t idx = GPOINTER_TO_UINT(*iter); + if (idx < mono_class_get_field_count (klass)) { + MonoClassField *field = &m_class_get_fields (klass) [idx]; + ++idx; + *iter = GUINT_TO_POINTER (idx); + return field; + } + if (G_UNLIKELY (image->has_updates)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Lazy iterating added fields %s", m_class_get_name(klass)); + return mono_metadata_update_added_fields_iter (klass, TRUE, iter); } return NULL; } diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index bdb83dad31a54..dd8524bf08ed9 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -2349,7 +2349,7 @@ ves_icall_RuntimePropertyInfo_get_property_info (MonoReflectionPropertyHandle pr } if ((req_info & PInfo_Attributes) != 0) - info->attrs = pproperty->attrs; + info->attrs = (pproperty->attrs & ~MONO_PROPERTY_META_FLAG_MASK); if ((req_info & PInfo_GetMethod) != 0) { MonoClass *property_klass = MONO_HANDLE_GETVAL (property, klass); @@ -2414,7 +2414,7 @@ ves_icall_RuntimeEventInfo_get_event_info (MonoReflectionMonoEventHandle ref_eve return_if_nok (error); MONO_STRUCT_SETREF_INTERNAL (info, name, MONO_HANDLE_RAW (ev_name)); - info->attrs = event->attrs; + info->attrs = event->attrs & ~MONO_EVENT_META_FLAG_MASK; MonoReflectionMethodHandle rm; if (event->add) { @@ -5048,9 +5048,8 @@ image_get_type (MonoImage *image, MonoTableInfo *tdef, int table_idx, int count, static MonoArrayHandle mono_module_get_types (MonoImage *image, MonoArrayHandleOut exceptions, MonoBoolean exportedOnly, MonoError *error) { - /* FIXME: metadata-update */ MonoTableInfo *tdef = &image->tables [MONO_TABLE_TYPEDEF]; - int rows = table_info_get_rows (tdef); + int rows = mono_metadata_table_num_rows (image, MONO_TABLE_TYPEDEF); int i, count; /* we start the count from 1 because we skip the special type */ diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index bf94ff432dfd7..56a59d9c233be 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -723,6 +723,7 @@ mono_image_strdup_vprintf (MonoImage *image, const char *format, va_list args); char* mono_image_strdup_printf (MonoImage *image, const char *format, ...) MONO_ATTR_FORMAT_PRINTF(2,3); +MONO_COMPONENT_API GList* mono_g_list_prepend_image (MonoImage *image, GList *list, gpointer data); diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index ac31818c5e499..bddbfa129e8f5 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -182,3 +182,33 @@ mono_metadata_update_find_method_by_name (MonoClass *klass, const char *name, in { return mono_component_hot_reload()->find_method_by_name (klass, name, param_count, flags, error); } + +gboolean +mono_metadata_update_get_typedef_skeleton (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_method_idx, uint32_t *method_count, uint32_t *first_field_idx, uint32_t *field_count) +{ + return mono_component_hot_reload()->get_typedef_skeleton (base_image, typedef_token, first_method_idx, method_count, first_field_idx, field_count); +} + +gboolean +metadata_update_get_typedef_skeleton_properties (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_prop_idx, uint32_t *prop_count) +{ + return mono_component_hot_reload()->get_typedef_skeleton_properties (base_image, typedef_token, first_prop_idx, prop_count); +} + +gboolean +metadata_update_get_typedef_skeleton_events (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_event_idx, uint32_t *event_count) +{ + return mono_component_hot_reload()->get_typedef_skeleton_events (base_image, typedef_token, first_event_idx, event_count); +} + +MonoMethod * +mono_metadata_update_added_methods_iter (MonoClass *klass, gpointer *iter) +{ + return mono_component_hot_reload()->added_methods_iter (klass, iter); +} + +MonoClassField * +mono_metadata_update_added_fields_iter (MonoClass *klass, gboolean lazy, gpointer *iter) +{ + return mono_component_hot_reload()->added_fields_iter (klass, lazy, iter); +} diff --git a/src/mono/mono/metadata/metadata-update.h b/src/mono/mono/metadata/metadata-update.h index ee999a4f0a10f..1cf2200e6d511 100644 --- a/src/mono/mono/metadata/metadata-update.h +++ b/src/mono/mono/metadata/metadata-update.h @@ -70,7 +70,22 @@ mono_metadata_update_get_field_idx (MonoClassField *field); MonoClassField * mono_metadata_update_get_field (MonoClass *klass, uint32_t fielddef_token); +gboolean +mono_metadata_update_get_typedef_skeleton (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_method_idx, uint32_t *method_count, uint32_t *first_field_idx, uint32_t *field_count); + +gboolean +metadata_update_get_typedef_skeleton_properties (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_prop_idx, uint32_t *prop_count); + +gboolean +metadata_update_get_typedef_skeleton_events (MonoImage *base_image, uint32_t typedef_token, uint32_t *first_event_idx, uint32_t *event_count); + gpointer mono_metadata_update_get_static_field_addr (MonoClassField *field); +MonoMethod * +mono_metadata_update_added_methods_iter (MonoClass *klass, gpointer *iter); + +MonoClassField * +mono_metadata_update_added_fields_iter (MonoClass *klass, gboolean lazy, gpointer *iter); + #endif /*__MONO_METADATA_UPDATE_H__*/ diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index ded8a8447f26f..80b55fa08ceea 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -39,6 +39,7 @@ #include #include #include +#include /* Auxiliary structure used for caching inflated signatures */ typedef struct { @@ -4884,18 +4885,26 @@ mono_metadata_interfaces_from_typedef_full (MonoImage *meta, guint32 index, Mono error_init (error); - if (!tdef->base) + if (!tdef->base && !meta->has_updates) return TRUE; loc.idx = mono_metadata_token_index (index); loc.col_idx = MONO_INTERFACEIMPL_CLASS; loc.t = tdef; - /* FIXME: metadata-update */ + gboolean found = tdef->base && mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator) != NULL; - if (!mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator)) + if (!found && !meta->has_updates) return TRUE; + if (G_UNLIKELY (meta->has_updates)) { + if (!found && !mono_metadata_update_metadata_linear_search (meta, tdef, &loc, table_locator)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "NO Found interfaces for class 0x%08x", index); + return TRUE; + } + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Found interfaces for class 0x%08x starting at 0x%08x", index, loc.result); + } + start = loc.result; /* * We may end up in the middle of the rows... @@ -4907,7 +4916,7 @@ mono_metadata_interfaces_from_typedef_full (MonoImage *meta, guint32 index, Mono break; } pos = start; - int rows = table_info_get_rows (tdef); + int rows = mono_metadata_table_num_rows (meta, MONO_TABLE_INTERFACEIMPL); while (pos < rows) { mono_metadata_decode_row (tdef, pos, cols, MONO_INTERFACEIMPL_SIZE); if (cols [MONO_INTERFACEIMPL_CLASS] != loc.idx) @@ -5022,9 +5031,7 @@ mono_metadata_nesting_typedef (MonoImage *meta, guint32 index, guint32 start_ind start = start_index; - /* FIXME: metadata-udpate */ - - int rows = table_info_get_rows (tdef); + int rows = mono_metadata_table_num_rows (meta, MONO_TABLE_NESTEDCLASS); while (start <= rows) { if (class_index == mono_metadata_decode_row_col (tdef, start - 1, MONO_NESTED_CLASS_ENCLOSING)) break; @@ -6259,8 +6266,6 @@ mono_metadata_get_constant_index (MonoImage *meta, guint32 token, guint32 hint) loc.col_idx = MONO_CONSTANT_PARENT; loc.t = tdef; - /* FIXME: metadata-update */ - /* FIXME: Index translation */ if ((hint > 0) && (hint < table_info_get_rows (tdef)) && (mono_metadata_decode_row_col (tdef, hint - 1, MONO_CONSTANT_PARENT) == index)) @@ -6269,6 +6274,11 @@ mono_metadata_get_constant_index (MonoImage *meta, guint32 token, guint32 hint) if (tdef->base && mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator)) { return loc.result + 1; } + + if (G_UNLIKELY (meta->has_updates)) { + if (mono_metadata_update_metadata_linear_search (meta, tdef, &loc, table_locator)) + return loc.result + 1; + } return 0; } @@ -6289,18 +6299,29 @@ mono_metadata_events_from_typedef (MonoImage *meta, guint32 index, guint *end_id *end_idx = 0; - if (!tdef->base) + if (!tdef->base && !meta->has_updates) return 0; loc.t = tdef; loc.col_idx = MONO_EVENT_MAP_PARENT; loc.idx = index + 1; - /* 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) { + uint32_t count; + if (metadata_update_get_typedef_skeleton_events (meta, mono_metadata_make_token (MONO_TABLE_TYPEDEF, index + 1), &start, &count)) { + *end_idx = start + count - 1; + return start - 1; + } else { + return 0; + } + } + } + start = mono_metadata_decode_row_col (tdef, loc.result, MONO_EVENT_MAP_EVENTLIST); if (loc.result + 1 < table_info_get_rows (tdef)) { end = mono_metadata_decode_row_col (tdef, loc.result + 1, MONO_EVENT_MAP_EVENTLIST) - 1; @@ -6329,7 +6350,7 @@ mono_metadata_methods_from_event (MonoImage *meta, guint32 index, guint *end_i MonoTableInfo *msemt = &meta->tables [MONO_TABLE_METHODSEMANTICS]; *end_idx = 0; - if (!msemt->base) + if (!msemt->base && !meta->has_updates) return 0; if (meta->uncompressed_metadata) @@ -6339,11 +6360,16 @@ mono_metadata_methods_from_event (MonoImage *meta, guint32 index, guint *end_i loc.col_idx = MONO_METHOD_SEMA_ASSOCIATION; loc.idx = ((index + 1) << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_EVENT; /* Method association coded index */ - /* FIXME: metadata-update */ + gboolean found = msemt->base && mono_binary_search (&loc, msemt->base, table_info_get_rows (msemt), msemt->row_size, table_locator) != NULL; - if (!mono_binary_search (&loc, msemt->base, table_info_get_rows (msemt), msemt->row_size, table_locator)) + if (!found && !meta->has_updates) return 0; + if (G_UNLIKELY (meta->has_updates)) { + if (!found && !mono_metadata_update_metadata_linear_search (meta, msemt, &loc, table_locator)) + return 0; + } + start = loc.result; /* * We may end up in the middle of the rows... @@ -6355,7 +6381,7 @@ mono_metadata_methods_from_event (MonoImage *meta, guint32 index, guint *end_i break; } end = start + 1; - int rows = table_info_get_rows (msemt); + int rows = mono_metadata_table_num_rows (meta, MONO_TABLE_METHODSEMANTICS); while (end < rows) { mono_metadata_decode_row (msemt, end, cols, MONO_METHOD_SEMA_SIZE); if (cols [MONO_METHOD_SEMA_ASSOCIATION] != loc.idx) @@ -6383,23 +6409,35 @@ mono_metadata_properties_from_typedef (MonoImage *meta, guint32 index, guint *en *end_idx = 0; - if (!tdef->base) + if (!tdef->base && !meta->has_updates) return 0; loc.t = tdef; loc.col_idx = MONO_PROPERTY_MAP_PARENT; loc.idx = index + 1; - /* FIXME: metadata-update */ + gboolean found = tdef->base && mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator) != NULL; - if (!mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, table_locator)) + if (!found && !meta->has_updates) return 0; + if (G_UNLIKELY (meta->has_updates)) { + if (!found) { + uint32_t count; + if (metadata_update_get_typedef_skeleton_properties (meta, mono_metadata_make_token (MONO_TABLE_TYPEDEF, index + 1), &start, &count)) { + *end_idx = start + count - 1; + return start - 1; + } else { + return 0; + } + } + } + start = mono_metadata_decode_row_col (tdef, loc.result, MONO_PROPERTY_MAP_PROPERTY_LIST); - if (loc.result + 1 < table_info_get_rows (tdef)) { + if (loc.result + 1 < mono_metadata_table_num_rows (meta, MONO_TABLE_PROPERTYMAP)) { end = mono_metadata_decode_row_col (tdef, loc.result + 1, MONO_PROPERTY_MAP_PROPERTY_LIST) - 1; } else { - end = table_info_get_rows (&meta->tables [MONO_TABLE_PROPERTY]); + end = mono_metadata_table_num_rows (meta, MONO_TABLE_PROPERTY); } *end_idx = end; @@ -6423,7 +6461,7 @@ mono_metadata_methods_from_property (MonoImage *meta, guint32 index, guint *en MonoTableInfo *msemt = &meta->tables [MONO_TABLE_METHODSEMANTICS]; *end_idx = 0; - if (!msemt->base) + if (!msemt->base && !meta->has_updates) return 0; if (meta->uncompressed_metadata) @@ -6433,11 +6471,16 @@ mono_metadata_methods_from_property (MonoImage *meta, guint32 index, guint *en loc.col_idx = MONO_METHOD_SEMA_ASSOCIATION; loc.idx = ((index + 1) << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_PROPERTY; /* Method association coded index */ - /* FIXME: metadata-update */ + gboolean found = msemt->base && mono_binary_search (&loc, msemt->base, table_info_get_rows (msemt), msemt->row_size, table_locator) != NULL; - if (!mono_binary_search (&loc, msemt->base, table_info_get_rows (msemt), msemt->row_size, table_locator)) + if (!found && !meta->has_updates) return 0; + if (G_UNLIKELY (meta->has_updates)) { + if (!found && !mono_metadata_update_metadata_linear_search (meta, msemt, &loc, table_locator)) + return 0; + } + start = loc.result; /* * We may end up in the middle of the rows... @@ -6449,7 +6492,7 @@ mono_metadata_methods_from_property (MonoImage *meta, guint32 index, guint *en break; } end = start + 1; - int rows = table_info_get_rows (msemt); + int rows = mono_metadata_table_num_rows (meta, MONO_TABLE_METHODSEMANTICS); while (end < rows) { mono_metadata_decode_row (msemt, end, cols, MONO_METHOD_SEMA_SIZE); if (cols [MONO_METHOD_SEMA_ASSOCIATION] != loc.idx) @@ -7104,7 +7147,7 @@ mono_metadata_get_generic_param_row (MonoImage *image, guint32 token, guint32 *o locator_t loc; g_assert (owner); - if (!tdef->base) + if (!tdef->base && !image->has_updates) return 0; if (mono_metadata_token_table (token) == MONO_TABLE_TYPEDEF) @@ -7121,11 +7164,15 @@ mono_metadata_get_generic_param_row (MonoImage *image, guint32 token, guint32 *o loc.col_idx = MONO_GENERICPARAM_OWNER; 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 && !image->has_updates) return 0; + if (G_UNLIKELY (image->has_updates)) { + if (!found && !mono_metadata_update_metadata_linear_search (image, 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_GENERICPARAM_OWNER) == loc.idx)) loc.result --;