From b5bb3c9e5e0cb0ddfe99eec38e66ac1dc8f1b57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Mon, 29 Nov 2021 19:31:28 -0500 Subject: [PATCH] Mono hot reload reorg and beginning (disabled) add method support (#61853) This PR does two things: 1. Reorganizes how we keep track of deltas, and how we do metadata lookups after updates 2. Adds an ifdefed-out initial draft of support for adding methods. (It worked for a simple test but it's not ready for general use yet). Details of the delta changes. * Previously Mono used a fully immutable model of metadata changes. Logically each delta brings in modifications to existing table rows and addition of new rows. Previously mono never formed the fully mutated tables - instead, each lookup was essentially responsible for playing all the changes forward every time it needed to lookup a row. This was fine when we primarily supported only row additions (because we could just skip past all the updates that hadn't added the row we wanted yet, and just look in a single delta). But as we started to support more and more modifications (of custom attributes, of property getters & setters, of parameters, etc) we now had to look through the whole list of deltas to make sure we found the latest modification of every row. * The new approach is for each `DeltaInfo` (representing a summary of a particular update to a single assembly) to contain a collection of `mutants` - the fully modified tables with all the newly added rows and all the new modifications. So lookups are fast now - we just go to the latest generation that is visible to a particular thread and look at its mutants tables. The downside is increased memory use. So we allocate the mutants from memory pools owned by each `DeltaInfo`. Right now we never dealloc the pools, but in the future we could. We already iterate over all the threads during our stop-the-world phase - we can record the earliest generation still needed by every thread and delete the pools for all earlier copies. In practice this means we only keep 3 sets of tables: the mmaped baseline tables, the newest mutants, and the mutants from the prior generation for any still-executing methods. * Additionally instead of storing a list of delta images in the `BaselineInfo`, we now store a list of `DeltaInfo` structs which each `DeltaInfo` pointing to a delta image. This means that we can avoid repeated hash table lookups to map from a delta image to its `DeltaInfo` every time the runtime queries us for a table row or for a heap element. --- * checkpoint: allow adding methods to existing classes. Doesn't do anything yet. And needs more sanity checking (roslyn won't give us virtual or abstract methods, but we should check). * fixme in loader * [metadata] Add mono_metadata_table_num_rows Returns the number of rows in a metadata table, taking into account metadata update deltas * add table to ptr table helper * Param attr lookups for deltas can have param_index == 0 The params are added with a subsequent enclog "add param" function. * WIP: start adding support for parameter additions. It "works" in that calling methods appears to work (ie direct token references seem to do the right thing). Primarily this is because the param table additions are not that interesting. All the good stuff is in the method signature (which is just in the blob heap). Presumably anything that actually needs parameter attributes, or anything that uses reflection to look at the parameters, will break. * WIP: add MethodDef -> TypeDef lookup Allows calling non-public methods, which are now assigned the correct parent * Add hot reload test for lambda capturing this Lambdas that only capture `this` (and not local variables or arguments) compile to a private instance method in the enclosing class. So it is enough to support EnC deltas that add methods. * clarify comments about MONO_METHOD_PARAMLIST * [hot_reload] Store debug info of updated methods * [hot_reload] Allocate modifiable tables in DeltaInfo This is the foundation for a new approach for metadata lookups. Instead of using an immutable model (each lookup traverses every delta to find the most up to date version of each table row), we are going to create a complete updated table for each generation and only do the lookup in the latest exposed generation directly. (This is essentially the CoreCLR model). This commit is just the first foundations: we allocate the tables and copy over the previous generations' rows and zero out any rows that will be inserted. Delta applications and lookups have not been updated yet. As a slight optimization, tables that don't have modified or added rows are not copied from the base image. If a generation modifies or adds rows, from that point forward, each subsequent generation will copy the table. We could be a bit more thrifty with copying, but it will complicate lookups. Also eventually we will try to deallocate the pools for generations that are older than no thread needs anymore. Metadata heaps are still looked up in each delta directly - heap combining does not seem necessary yet. * [mini] Allow MONO_VERBOSE_METHOD='*:*' Implement method name wildcard matching for method descriptions Globbing doesn't work because we don't have g_pattern_match_simple in eglib. But a plain '*' wildcard does work. * populate mutated table rows leave suppressed columns unchanged * [hot_reload] Switch lookups to the mutant tables * cleanup: remove of effective_table calculation The row index doesn't change in the new scheme, pass it by value * cleanup: Remove relative_delta_index from component API It's only used internally by the update logic * cleanup: Pass DeltaInfo to relative_delta_index don't compute it from the dmeta image * cleanup: Store a list of DeltaInfo in the BaselineInfo Instead of storing a list of delta images and then relying on delta_info_lookup to find the DeltaInfo, just store them directly on the baseline info. This changes the cleanup responsibilities a bit. Now we destroy the DeltaInfo when we are iterate through the delta infos when we close the baseline image, instead of when we remove the delta_image_to_info hashtable entry. * Turn off method addition support, for now Just want to get the cleanups enabled for now * Fix null ptr when checking for updated ppdb info --- .../AddLambdaCapturingThis.cs | 25 + .../AddLambdaCapturingThis_v1.cs | 29 + ...yUpdate.Test.AddLambdaCapturingThis.csproj | 11 + .../deltascript.json | 6 + .../tests/ApplyUpdateTest.cs | 20 + .../tests/System.Runtime.Loader.Tests.csproj | 2 + src/mono/mono/component/hot_reload-stub.c | 45 +- src/mono/mono/component/hot_reload.c | 704 +++++++++++++----- src/mono/mono/component/hot_reload.h | 6 +- src/mono/mono/metadata/loader.c | 4 + src/mono/mono/metadata/metadata-internals.h | 35 +- src/mono/mono/metadata/metadata-update.c | 20 +- src/mono/mono/metadata/metadata.c | 19 +- 13 files changed, 721 insertions(+), 205 deletions(-) create mode 100644 src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis.cs create mode 100644 src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis_v1.cs create mode 100644 src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis.csproj create mode 100644 src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/deltascript.json diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis.cs new file mode 100644 index 0000000000000..186144e290e35 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + public class AddLambdaCapturingThis + { + public AddLambdaCapturingThis () { + field = "abcd"; + } + + public string GetField => field; + + private string field; + + public string TestMethod () { + // capture 'this' but no locals + Func fn = s => field; + return "123"; + } + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis_v1.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis_v1.cs new file mode 100644 index 0000000000000..22dd869021dc3 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/AddLambdaCapturingThis_v1.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + + +namespace System.Reflection.Metadata.ApplyUpdate.Test +{ + public class AddLambdaCapturingThis + { + public AddLambdaCapturingThis () { + field = "abcd"; + } + + public string GetField => field; + + private string field; + + public string TestMethod () { + // capture 'this' but no locals + Func fn = s => NewMethod (s + field, 42); + return fn ("123"); + } + + private string NewMethod (string s, int i) { + return i.ToString() + s; + } + + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis.csproj new file mode 100644 index 0000000000000..88c636e683cf6 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis.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.AddLambdaCapturingThis/deltascript.json b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/deltascript.json new file mode 100644 index 0000000000000..1e618dede3646 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis/deltascript.json @@ -0,0 +1,6 @@ +{ + "changes": [ + {"document": "AddLambdaCapturingThis.cs", "update": "AddLambdaCapturingThis_v1.cs"}, + ] +} + diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs index 3e240e65caabf..a582e3f4a7bb9 100644 --- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs @@ -263,6 +263,26 @@ public void AsyncMethodChanges() }); } + [ActiveIssue ("https://github.com/dotnet/runtime/issues/50249", TestRuntimes.Mono)] + [ConditionalFact(typeof(ApplyUpdateUtil), nameof(ApplyUpdateUtil.IsSupported))] + public static void TestAddLambdaCapturingThis() + { + ApplyUpdateUtil.TestCase(static () => + { + var assm = typeof(System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis).Assembly; + + var x = new System.Reflection.Metadata.ApplyUpdate.Test.AddLambdaCapturingThis(); + + Assert.Equal("123", x.TestMethod()); + + ApplyUpdateUtil.ApplyUpdate(assm); + + string result = x.TestMethod(); + Assert.Equal("42123abcd", result); + }); + } + + class NonRuntimeAssembly : Assembly { } diff --git a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj index 19e5d6bc5069b..2ecf08c6f3da5 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 @@ -43,6 +43,7 @@ + @@ -60,6 +61,7 @@ + diff --git a/src/mono/mono/component/hot_reload-stub.c b/src/mono/mono/component/hot_reload-stub.c index 35b8fe38a56e4..6064a6a78f875 100644 --- a/src/mono/mono/component/hot_reload-stub.c +++ b/src/mono/mono/component/hot_reload-stub.c @@ -39,10 +39,7 @@ static void hot_reload_stub_cleanup_on_close (MonoImage *image); static void -hot_reload_stub_effective_table_slow (const MonoTableInfo **t, int *idx); - -static int -hot_reload_stub_relative_delta_index (MonoImage *image_dmeta, int token); +hot_reload_stub_effective_table_slow (const MonoTableInfo **t, int idx); static void hot_reload_stub_close_except_pools_all (MonoImage *base_image); @@ -65,6 +62,15 @@ hot_reload_stub_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx); static gboolean hot_reload_stub_has_modified_rows (const MonoTableInfo *table); +static int +hot_reload_stub_table_num_rows_slow (MonoImage *image, int table_index); + +static GArray* +hot_reload_stub_get_added_methods (MonoClass *klass); + +static uint32_t +hot_reload_stub_method_parent (MonoImage *image, uint32_t method_index); + static MonoComponentHotReload fn_table = { { MONO_COMPONENT_ITF_VERSION, &hot_reload_stub_available }, &hot_reload_stub_set_fastpath_data, @@ -74,7 +80,6 @@ static MonoComponentHotReload fn_table = { &hot_reload_stub_get_thread_generation, &hot_reload_stub_cleanup_on_close, &hot_reload_stub_effective_table_slow, - &hot_reload_stub_relative_delta_index, &hot_reload_stub_apply_changes, &hot_reload_stub_close_except_pools_all, &hot_reload_stub_close_all, @@ -83,6 +88,9 @@ static MonoComponentHotReload fn_table = { &hot_reload_stub_delta_heap_lookup, &hot_reload_stub_get_updated_method_ppdb, &hot_reload_stub_has_modified_rows, + &hot_reload_stub_table_num_rows_slow, + &hot_reload_stub_get_added_methods, + &hot_reload_stub_method_parent, }; static bool @@ -135,13 +143,7 @@ hot_reload_stub_cleanup_on_close (MonoImage *image) } void -hot_reload_stub_effective_table_slow (const MonoTableInfo **t, int *idx) -{ - g_assert_not_reached (); -} - -static int -hot_reload_stub_relative_delta_index (MonoImage *image_dmeta, int token) +hot_reload_stub_effective_table_slow (const MonoTableInfo **t, int idx) { g_assert_not_reached (); } @@ -192,6 +194,25 @@ hot_reload_stub_has_modified_rows (const MonoTableInfo *table) return FALSE; } +static int +hot_reload_stub_table_num_rows_slow (MonoImage *image, int table_index) +{ + g_assert_not_reached (); /* should always take the fast path */ +} + +static GArray* +hot_reload_stub_get_added_methods (MonoClass *klass) +{ + return NULL; +} + +static uint32_t +hot_reload_stub_method_parent (MonoImage *image, uint32_t method_index) +{ + return 0; +} + + MONO_COMPONENT_EXPORT_ENTRYPOINT MonoComponentHotReload * mono_component_hot_reload_init (void) diff --git a/src/mono/mono/component/hot_reload.c b/src/mono/mono/component/hot_reload.c index 83faf008067fa..a3caeb13ef3a1 100644 --- a/src/mono/mono/component/hot_reload.c +++ b/src/mono/mono/component/hot_reload.c @@ -28,6 +28,10 @@ #include +#undef ALLOW_METHOD_ADD + +typedef struct _DeltaInfo DeltaInfo; + static void hot_reload_init (void); @@ -53,13 +57,13 @@ static void hot_reload_cleanup_on_close (MonoImage *image); static void -hot_reload_effective_table_slow (const MonoTableInfo **t, int *idx); +hot_reload_effective_table_slow (const MonoTableInfo **t, int idx); static void hot_reload_apply_changes (int origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); static int -hot_reload_relative_delta_index (MonoImage *image_dmeta, int token); +hot_reload_relative_delta_index (MonoImage *image_dmeta, DeltaInfo *delta_info, int token); static void hot_reload_close_except_pools_all (MonoImage *base_image); @@ -82,6 +86,16 @@ hot_reload_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx); static gboolean hot_reload_has_modified_rows (const MonoTableInfo *table); +static int +hot_reload_table_num_rows_slow (MonoImage *image, int table_index); + +static GArray* +hot_reload_get_added_methods (MonoClass *klass); + +static uint32_t +hot_reload_method_parent (MonoImage *base, uint32_t method_token); + + static MonoComponentHotReload fn_table = { { MONO_COMPONENT_ITF_VERSION, &hot_reload_available }, &hot_reload_set_fastpath_data, @@ -91,7 +105,6 @@ static MonoComponentHotReload fn_table = { &hot_reload_get_thread_generation, &hot_reload_cleanup_on_close, &hot_reload_effective_table_slow, - &hot_reload_relative_delta_index, &hot_reload_apply_changes, &hot_reload_close_except_pools_all, &hot_reload_close_all, @@ -100,6 +113,9 @@ static MonoComponentHotReload fn_table = { &hot_reload_delta_heap_lookup, &hot_reload_get_updated_method_ppdb, &hot_reload_has_modified_rows, + &hot_reload_table_num_rows_slow, + &hot_reload_get_added_methods, + &hot_reload_method_parent, }; MonoComponentHotReload * @@ -151,8 +167,9 @@ typedef struct _delta_row_count { } delta_row_count; /* Additional informaiton for MonoImages representing deltas */ -typedef struct _DeltaInfo { +struct _DeltaInfo { uint32_t generation; /* global update ID that added this delta image */ + MonoImage *delta_image; /* DeltaInfo doesn't own the image, the base MonoImage owns the reference */ /* Maps MethodDef token indices to a pointer into the RVA of the delta IL */ GHashTable *method_table_update; @@ -165,25 +182,36 @@ typedef struct _DeltaInfo { delta_row_count count [MONO_TABLE_NUM]; MonoPPDBFile *ppdb_file; -} DeltaInfo; + + MonoMemPool *pool; /* mutated tables are allocated here */ + + MonoTableInfo mutants[MONO_TABLE_NUM]; +}; /* Additional informaiton for baseline MonoImages */ typedef struct _BaselineInfo { - /* List of MonoImages of deltas. Parent image owns 1 refcount ref of the delta image */ - GList *delta_image; - /* Tail of delta_image for fast appends */ - GList *delta_image_last; + /* List of DeltaInfos of deltas*/ + GList *delta_info; + /* Tail of delta_info for fast appends */ + GList *delta_info_last; /* Maps MethodDef token indices to a boolean flag that there's an update for the method */ GHashTable *method_table_update; /* TRUE if any published update modified an existing row */ gboolean any_modified_rows [MONO_TABLE_NUM]; + + GHashTable *added_methods; /* maps each MonoClass to a GArray of added method tokens */ + GHashTable *method_parent; /* maps added methoddef tokens to typedef tokens */ } BaselineInfo; + #define DOTNET_MODIFIABLE_ASSEMBLIES "DOTNET_MODIFIABLE_ASSEMBLIES" +/* See Note: Suppressed Columns */ +static guint16 m_SuppressedDeltaColumns [MONO_TABLE_NUM]; + /** * mono_metadata_update_enable: * \param modifiable_assemblies_out: set to MonoModifiableAssemblies value @@ -206,7 +234,8 @@ hot_reload_update_enabled (int *modifiable_assemblies_out) char *val = g_getenv (DOTNET_MODIFIABLE_ASSEMBLIES); if (val && !g_strcasecmp (val, "debug")) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Metadata update enabled for debuggable assemblies"); - modifiable = MONO_MODIFIABLE_ASSM_DEBUG; + modifiable + = MONO_MODIFIABLE_ASSM_DEBUG; } g_free (val); inited = TRUE; @@ -254,7 +283,7 @@ hot_reload_no_inline (MonoMethod *caller, MonoMethod *callee) * this for deltas. */ static GHashTable *table_to_image; -/* Maps each delta MonoImage to its DeltaInfo */ +/* Maps each delta MonoImage to its DeltaInfo. Doesn't own the DeltaInfo or the images */ static GHashTable *delta_image_to_info; /* Maps each baseline MonoImage to its BaselineInfo */ static GHashTable *baseline_image_to_info; @@ -324,7 +353,7 @@ baseline_info_lookup (MonoImage *base_image) } static DeltaInfo* -delta_info_init (MonoImage *image_dmeta, MonoImage *image_base, MonoPPDBFile *ppdb_file, BaselineInfo *base_info, uint32_t generation); +delta_info_init (MonoImage *image_dmeta, MonoImage *image_base, MonoPPDBFile *ppdb_file, BaselineInfo *base_info, uint32_t generation, DeltaInfo **prev_last_delta); static void free_ppdb_entry (gpointer key, gpointer val, gpointer user_data) @@ -342,6 +371,9 @@ delta_info_destroy (DeltaInfo *dinfo) g_hash_table_destroy (dinfo->method_ppdb_table_update); } mono_ppdb_close (dinfo->ppdb_file); + + if (dinfo->pool) + mono_mempool_destroy (dinfo->pool); g_free (dinfo); } @@ -357,6 +389,7 @@ delta_info_lookup (MonoImage *delta_image) DeltaInfo *result; table_to_image_lock (); result = delta_info_lookup_locked (delta_image); + g_assert (!result || result->delta_image == delta_image); table_to_image_unlock (); return result; } @@ -384,12 +417,6 @@ hot_reload_cleanup_on_close (MonoImage *image) table_to_image_lock (); /* remove all keys (delta images) that map to the given image (base image) */ g_hash_table_foreach_remove (table_to_image, remove_base_image, (gpointer)image); - /* remove delta image info */ - DeltaInfo *delta_info = delta_info_lookup_locked (image); - if (delta_info) { - g_hash_table_remove (delta_image_to_info, image); - delta_info_destroy (delta_info); - } table_to_image_unlock (); } @@ -399,11 +426,16 @@ hot_reload_close_except_pools_all (MonoImage *base_image) BaselineInfo *info = baseline_info_lookup (base_image); if (!info) return; - for (GList *ptr = info->delta_image; ptr; ptr = ptr->next) { - MonoImage *image = (MonoImage *)ptr->data; + for (GList *ptr = info->delta_info; ptr; ptr = ptr->next) { + DeltaInfo *info = (DeltaInfo *)ptr->data; + MonoImage *image = info->delta_image; if (image) { + table_to_image_lock (); + g_hash_table_remove (delta_image_to_info, image); + table_to_image_unlock (); + /* if for some reason the image has other references, break the link to this delta_info that is going away */ if (!mono_image_close_except_pools (image)) - ptr->data = NULL; + info->delta_image = NULL; } } } @@ -414,12 +446,18 @@ hot_reload_close_all (MonoImage *base_image) BaselineInfo *info = baseline_info_lookup (base_image); if (!info) return; - for (GList *ptr = info->delta_image; ptr; ptr = ptr->next) { - MonoImage *image = (MonoImage *)ptr->data; - if (image) + for (GList *ptr = info->delta_info; ptr; ptr = ptr->next) { + DeltaInfo *info = (DeltaInfo *)ptr->data; + if (!info) + continue; + MonoImage *image = info->delta_image; + if (image) { mono_image_close_finish (image); + } + delta_info_destroy (info); + ptr->data = NULL; } - g_list_free (info->delta_image); + g_list_free (info->delta_info); baseline_info_remove (base_image); baseline_info_destroy (info); } @@ -474,17 +512,24 @@ table_info_find_in_base (const MonoTableInfo *table, MonoImage **base_out, int * static MonoImage* image_open_dmeta_from_data (MonoImage *base_image, uint32_t generation, gconstpointer dmeta_bytes, uint32_t dmeta_length); -static void +static DeltaInfo* image_append_delta (MonoImage *base, BaselineInfo *base_info, MonoImage *delta, DeltaInfo *delta_info); -static int -metadata_update_local_generation (MonoImage *base, BaselineInfo *base_info, MonoImage *delta); +static void +add_method_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t method_token, MonoDebugInformationEnc* pdb_address); + void hot_reload_init (void) { table_to_image_init (); mono_native_tls_alloc (&exposed_generation_id, NULL); + + /* See CMiniMdRW::ApplyDelta in metamodelenc.cpp in CoreCLR */ + m_SuppressedDeltaColumns[MONO_TABLE_EVENTMAP] = (1 << MONO_EVENT_MAP_EVENTLIST); + m_SuppressedDeltaColumns[MONO_TABLE_PROPERTYMAP] = (1 << MONO_PROPERTY_MAP_PROPERTY_LIST); + m_SuppressedDeltaColumns[MONO_TABLE_METHOD] = (1 << MONO_METHOD_PARAMLIST); + m_SuppressedDeltaColumns[MONO_TABLE_TYPEDEF] = (1 << MONO_TYPEDEF_FIELD_LIST)|(1<pool); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Initializing mutant tables for image %p (generation %d)", base, delta->generation); + for (int i = 0; i < MONO_TABLE_NUM; ++i) + { + gboolean need_copy = FALSE; + /* if any generation modified any row of this table, make a copy for the current generation. */ + if (base_info->any_modified_rows [i]) + need_copy = TRUE; + delta_row_count *count = &delta->count [i]; + guint32 base_rows = table_info_get_rows (&base->tables [i]); + /* if some previous generation added rows, or we're adding rows, make a copy */ + if (base_rows != count->prev_gen_rows || count->inserted_rows) + need_copy = TRUE; + if (!need_copy) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, " Table 0x%02x unchanged (rows: base %d, prev %d, inserted %d), not copied", i, base_rows, count->prev_gen_rows, count->inserted_rows); + continue; + } else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, " Table 0x%02x changed (rows: base %d, prev %d, inserted %d), IS copied", i, base_rows, count->prev_gen_rows, count->inserted_rows); + } + /* The invariant is that once we made a copy in any previous generation, we'll make + * a copy in this generation. So subsequent generations can copy either from the + * immediately preceeding generation or from the baseline if the preceeding + * generation didn't make a copy. */ + + guint32 rows = count->prev_gen_rows + count->inserted_rows; + + MonoTableInfo *tbl = &delta->mutants [i]; + tbl->row_size = base->tables[i].row_size; + tbl->size_bitfield = base->tables[i].size_bitfield; + tbl->rows_ = rows; + + tbl->base = mono_mempool_alloc (delta->pool, tbl->row_size * rows); + const MonoTableInfo *prev_table; + if (!prev_delta || prev_delta->mutants [i].base == NULL) + prev_table = &base->tables [i]; + else + prev_table = &prev_delta->mutants [i]; + + g_assert (prev_table != NULL); + g_assert (table_info_get_rows (prev_table) == count->prev_gen_rows); + + /* copy the old rows and zero out the new ones */ + memcpy ((char*)tbl->base, prev_table->base, count->prev_gen_rows * tbl->row_size); + memset (((char*)tbl->base) + count->prev_gen_rows * tbl->row_size, 0, count->inserted_rows * tbl->row_size); + } +} + + /** * LOCKING: Assumes the publish_lock is held + * Returns: The previous latest delta, or NULL if this is the first delta */ -void +DeltaInfo * image_append_delta (MonoImage *base, BaselineInfo *base_info, MonoImage *delta, DeltaInfo *delta_info) { - if (!base_info->delta_image) { - base_info->delta_image = base_info->delta_image_last = g_list_alloc (); - base_info->delta_image->data = (gpointer)delta; + if (!base_info->delta_info) { + base_info->delta_info = base_info->delta_info_last = g_list_alloc (); + base_info->delta_info->data = (gpointer)delta_info; mono_memory_write_barrier (); /* Have to set this here so that passes over the metadata in the updater thread start using the slow path */ base->has_updates = TRUE; - return; + return NULL; } - g_assert (delta_info_lookup(((MonoImage*)base_info->delta_image_last->data))->generation < delta_info->generation); + DeltaInfo *prev_last_delta = (DeltaInfo*)base_info->delta_info_last->data; + g_assert (prev_last_delta->generation < delta_info->generation); /* g_list_append returns the given list, not the newly appended */ - GList *l = g_list_append (base_info->delta_image_last, delta); + GList *l = g_list_append (base_info->delta_info_last, delta_info); g_assert (l != NULL && l->next != NULL && l->next->next == NULL); - base_info->delta_image_last = l->next; + base_info->delta_info_last = l->next; mono_memory_write_barrier (); /* Have to set this here so that passes over the metadata in the updater thread start using the slow path */ base->has_updates = TRUE; - + return prev_last_delta; } /** @@ -773,8 +877,33 @@ dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) } +static gboolean +effective_table_mutant (MonoImage *base, BaselineInfo *info, int tbl_index, const MonoTableInfo **t, int idx) +{ + GList *ptr =info->delta_info_last; + uint32_t exposed_gen = hot_reload_get_thread_generation (); + MonoImage *dmeta = NULL; + DeltaInfo *delta_info = NULL; + + /* walk backward from the latest image until we find one that matches the current thread's exposed generation */ + do { + delta_info = (DeltaInfo*)ptr->data; + dmeta = delta_info->delta_image; + if (delta_info->generation <= exposed_gen) + break; + ptr = ptr->prev; + } while (ptr); + if (!ptr) + return FALSE; + g_assert (dmeta != NULL); + g_assert (delta_info != NULL); + + *t = &delta_info->mutants [tbl_index]; + return TRUE; +} + void -hot_reload_effective_table_slow (const MonoTableInfo **t, int *idx) +hot_reload_effective_table_slow (const MonoTableInfo **t, int idx) { /* FIXME: don't let any thread other than the updater thread see values from a delta image * with a generation past update_published @@ -788,61 +917,9 @@ hot_reload_effective_table_slow (const MonoTableInfo **t, int *idx) if (!info) return; - gboolean any_modified = info->any_modified_rows[tbl_index]; - - if (G_LIKELY (*idx < table_info_get_rows (*t) && !any_modified)) - return; - - /* FIXME: when adding methods this won't work anymore. We will need to update the delta - * images' suppressed columns (see the Note in pass2 about - * CMiniMdRW::m_SuppressedDeltaColumns) with the baseline values. */ - /* The only column from the updates that matters the RVA, which is looked up elsewhere. */ - if (tbl_index == MONO_TABLE_METHOD) - return; + gboolean success = effective_table_mutant (base, info, tbl_index, t, idx); - GList *list = info->delta_image; - MonoImage *dmeta = NULL; - int ridx; - MonoTableInfo *table; - int g = 0; - - /* Candidate: the last delta that had updates for the requested row */ - MonoImage *cand_dmeta = NULL; - MonoTableInfo *cand_table = NULL; - int cand_ridx = -1; - int cand_g = 0; - - gboolean cont; - do { - g_assertf (list, "couldn't find idx=0x%08x in assembly=%s", *idx, dmeta && dmeta->name ? dmeta->name : "unknown image"); - dmeta = (MonoImage*)list->data; - list = list->next; - table = &dmeta->tables [tbl_index]; - int rel_row = hot_reload_relative_delta_index (dmeta, mono_metadata_make_token (tbl_index, *idx + 1)); - g_assert (rel_row == -1 || (rel_row > 0 && rel_row <= table_info_get_rows (table))); - g++; - if (rel_row != -1) { - cand_dmeta = dmeta; - cand_table = table; - cand_ridx = rel_row - 1; - cand_g = g; - } - ridx = rel_row - 1; - if (!any_modified) { - /* if the table only got additions, not modifications, don't continue after we find the first image that has the right number of rows */ - cont = ridx < 0 || ridx >= table_info_get_rows (table); - } else { - /* otherwise, keep going in case a later generation modified the row again */ - cont = list != NULL; - } - } while (cont); - - if (cand_ridx != -1) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "effective table for %s: 0x%08x -> 0x%08x (rows = 0x%08x) (gen %d, g %d)", mono_meta_table_name (tbl_index), *idx, cand_ridx, table_info_get_rows (cand_table), metadata_update_local_generation (base, info, cand_dmeta), cand_g); - - *t = cand_table; - *idx = cand_ridx; - } + g_assert (success); } /* @@ -866,29 +943,25 @@ hot_reload_effective_table_slow (const MonoTableInfo **t, int *idx) * relative index faster. */ int -hot_reload_relative_delta_index (MonoImage *image_dmeta, int token) +hot_reload_relative_delta_index (MonoImage *image_dmeta, DeltaInfo *delta_info, int token) { MonoTableInfo *encmap = &image_dmeta->tables [MONO_TABLE_ENCMAP]; + int table = mono_metadata_token_table (token); int index = mono_metadata_token_index (token); - /* this helper expects and returns as "index origin = 1" */ - g_assert (index > 0); + int index_map = delta_info->enc_recs [table]; + int encmap_rows = table_info_get_rows (encmap); if (!table_info_get_rows (encmap) || !image_dmeta->minimal_delta) return mono_metadata_token_index (token); - DeltaInfo *delta_info = delta_info_lookup (image_dmeta); - g_assert (delta_info); - - int index_map = delta_info->enc_recs [table]; - int encmap_rows = table_info_get_rows (encmap); - /* if the table didn't have any updates in this generation and the * table index is bigger than the last table that got updates, * enc_recs will point past the last row */ if (index_map - 1 == encmap_rows) return -1; + guint32 cols[MONO_ENCMAP_SIZE]; mono_metadata_decode_row (encmap, index_map - 1, cols, MONO_ENCMAP_SIZE); int map_entry = cols [MONO_ENCMAP_TOKEN]; @@ -930,7 +1003,7 @@ hot_reload_relative_delta_index (MonoImage *image_dmeta, int token) /* LOCKING: assumes publish_lock is held */ static DeltaInfo* -delta_info_init (MonoImage *image_dmeta, MonoImage *image_base, MonoPPDBFile *ppdb_file, BaselineInfo *base_info, uint32_t generation) +delta_info_init (MonoImage *image_dmeta, MonoImage *image_base, MonoPPDBFile *ppdb_file, BaselineInfo *base_info, uint32_t generation, DeltaInfo **prev_delta_info) { MonoTableInfo *encmap = &image_dmeta->tables [MONO_TABLE_ENCMAP]; g_assert (!delta_info_lookup (image_dmeta)); @@ -942,13 +1015,19 @@ delta_info_init (MonoImage *image_dmeta, MonoImage *image_base, MonoPPDBFile *pp delta_info->generation = generation; delta_info->ppdb_file = ppdb_file; + delta_info->delta_image = image_dmeta; table_to_image_lock (); g_hash_table_insert (delta_image_to_info, image_dmeta, delta_info); table_to_image_unlock (); + delta_info->pool = mono_mempool_new (); + + + g_assert (prev_delta_info); + /* base_image takes ownership of 1 refcount ref of dmeta_image */ - image_append_delta (image_base, base_info, image_dmeta, delta_info); + *prev_delta_info = image_append_delta (image_base, base_info, image_dmeta, delta_info); return delta_info; } @@ -962,19 +1041,15 @@ delta_info_compute_table_records (MonoImage *image_dmeta, MonoImage *image_base, int table, prev_table = -1, idx; /*** Compute logical table sizes ***/ - if (base_info->delta_image == base_info->delta_image_last) { + if (base_info->delta_info == base_info->delta_info_last) { /* this is the first update. */ for (int i = 0; i < MONO_TABLE_NUM; ++i) { delta_info->count[i].prev_gen_rows = table_info_get_rows (&image_base->tables[i]); } } else { - /* Current image_dmeta is image_base->delta_image_last->data, - * find its predecessor - */ - MonoImage *prev_delta = NULL; - g_assert (base_info->delta_image_last->prev != NULL); - prev_delta = (MonoImage*)base_info->delta_image_last->prev->data; - DeltaInfo *prev_gen_info = delta_info_lookup (prev_delta); + g_assert (delta_info == (DeltaInfo*)base_info->delta_info_last->data); + g_assert (base_info->delta_info_last->prev != NULL); + DeltaInfo *prev_gen_info = (DeltaInfo*)base_info->delta_info_last->prev->data; for (int i = 0; i < MONO_TABLE_NUM; ++i) { delta_info->count[i].prev_gen_rows = prev_gen_info->count[i].prev_gen_rows + prev_gen_info->count[i].inserted_rows; } @@ -1025,6 +1100,15 @@ delta_info_compute_table_records (MonoImage *image_dmeta, MonoImage *image_base, return TRUE; } +enum MonoEnCFuncCode { + ENC_FUNC_DEFAULT = 0, + ENC_FUNC_ADD_METHOD = 1, + ENC_FUNC_ADD_FIELD = 2, + ENC_FUNC_ADD_PARAM = 3, + ENC_FUNC_ADD_PROPERTY = 4, + ENC_FUNC_ADD_EVENT = 5, +}; + static const char* funccode_to_str (int func_code) { @@ -1040,11 +1124,106 @@ funccode_to_str (int func_code) return NULL; } +/* + * Apply the row from the delta image given by log_token to the cur_delta mutated table. + * + */ +static void +delta_info_mutate_row (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *cur_delta, guint32 log_token) +{ + int token_table = mono_metadata_token_table (log_token); + int token_index = mono_metadata_token_index (log_token); /* 1-based */ + + gboolean modified = token_index <= cur_delta->count [token_table].prev_gen_rows; + + int delta_index = hot_reload_relative_delta_index (image_dmeta, cur_delta, log_token); + + /* The complication here is that we want the mutant table to look like the table in + * image_base with respect to column widths, but the delta tables are generally coming in + * uncompressed (4-byte columns). So we have to copy one column at a time and adjust the + * widths as we go. + */ + + guint32 dst_bitfield = image_base->tables [token_table].size_bitfield; + guint32 src_bitfield = image_dmeta->tables [token_table].size_bitfield; + + const char *src_base = image_dmeta->tables [token_table].base + (delta_index - 1) * image_dmeta->tables [token_table].row_size; + char *dst_base = (char*)cur_delta->mutants [token_table].base + (token_index - 1) * image_base->tables [token_table].row_size; + + guint32 src_offset = 0, dst_offset = 0; + for (int col = 0; col < mono_metadata_table_count (dst_bitfield); ++col) { + guint32 dst_col_size = mono_metadata_table_size (dst_bitfield, col); + guint32 src_col_size = mono_metadata_table_size (src_bitfield, col); + if ((m_SuppressedDeltaColumns [token_table] & (1 << col)) == 0) { + const char *src = src_base + src_offset; + char *dst = dst_base + dst_offset; + + /* copy src to dst, via a temporary to adjust for size differences */ + /* FIXME: unaligned access, endianness */ + guint32 tmp; + + switch (src_col_size) { + case 1: + tmp = *(guint8*)src; + break; + case 2: + tmp = *(guint16*)src; + break; + case 4: + tmp = *(guint32*)src; + break; + default: + g_assert_not_reached (); + } + + /* FIXME: unaligned access, endianness */ + switch (dst_col_size) { + case 1: + *(guint8*)dst = (guint8)tmp; + break; + case 2: + *(guint16*)dst = (guint16)tmp; + break; + case 4: + *(guint32*)dst = tmp; + break; + default: + g_assert_not_reached (); + } + + } + src_offset += src_col_size; + dst_offset += dst_col_size; + } + g_assert (dst_offset == image_base->tables [token_table].row_size); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "mutate: table=0x%02x row=0x%04x delta row=0x%04x %s", token_table, token_index, delta_index, modified ? "Mod" : "Add"); +} + +static void +prepare_mutated_rows (const MonoTableInfo *table_enclog, MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *delta_info) +{ + int rows = table_info_get_rows (table_enclog); + /* Prepare the mutated metadata tables */ + for (int i = 0; i < rows ; ++i) { + guint32 cols [MONO_ENCLOG_SIZE]; + mono_metadata_decode_row (table_enclog, i, cols, MONO_ENCLOG_SIZE); + + int log_token = cols [MONO_ENCLOG_TOKEN]; + int func_code = cols [MONO_ENCLOG_FUNC_CODE]; + + if (func_code != ENC_FUNC_DEFAULT) + continue; + + delta_info_mutate_row (image_base, image_dmeta, delta_info, log_token); + } +} + /* Run some sanity checks first. If we detect unsupported scenarios, this * function will fail and the metadata update should be aborted. This should * run before anything in the metadata world is updated. */ static gboolean -apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer dil_data, uint32_t dil_length, MonoError *error) +apply_enclog_pass1 (MonoImage *image_base, 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); @@ -1069,18 +1248,29 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer int token_table = mono_metadata_token_table (log_token); int token_index = mono_metadata_token_index (log_token); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x (%s idx=0x%02x) (base table has 0x%04x rows)\tfunc=0x%02x\n", i, log_token, mono_meta_table_name (token_table), token_index, table_info_get_rows (&image_base->tables [token_table]), func_code); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x (%s idx=0x%02x) (base table has 0x%04x rows)\tfunc=0x%02x (\"%s\")\n", i, log_token, mono_meta_table_name (token_table), token_index, table_info_get_rows (&image_base->tables [token_table]), func_code, funccode_to_str (func_code)); if (token_table != MONO_TABLE_METHOD) continue; +#ifndef ALLOW_METHOD_ADD + if (token_index > table_info_get_rows (&image_base->tables [token_table])) { 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 */ + /* FIXME: total rows, not just the baseline rows */ + if (func_code == ENC_FUNC_ADD_PARAM && token_index > table_info_get_rows (&image_base->tables [token_table])) + continue; +#endif + g_assert (func_code == 0); /* anything else doesn't make sense here */ } @@ -1099,6 +1289,10 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer /* 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_PROPERTY: { @@ -1116,7 +1310,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer if (token_index > table_info_get_rows (&image_base->tables [token_table])) { /* 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, mono_metadata_make_token (token_table, token_index)); + 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); @@ -1151,7 +1345,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer /* modifying existing rows is ok, as long as the parent and ctor are the same */ guint32 ca_upd_cols [MONO_CUSTOM_ATTR_SIZE]; guint32 ca_base_cols [MONO_CUSTOM_ATTR_SIZE]; - int mapped_token = hot_reload_relative_delta_index (image_dmeta, mono_metadata_make_token (token_table, token_index)); + 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_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x CUSTOM_ATTR update. mapped index = 0x%08x\n", i, log_token, mapped_token); @@ -1186,7 +1380,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer /* We only allow modifications where the parameter name doesn't change. */ uint32_t base_param [MONO_PARAM_SIZE]; uint32_t upd_param [MONO_PARAM_SIZE]; - int mapped_token = hot_reload_relative_delta_index (image_dmeta, mono_metadata_make_token (token_table, token_index)); + 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_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x PARAM update. mapped index = 0x%08x\n", i, log_token, mapped_token); @@ -1206,6 +1400,28 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer } else break; /* added a row. ok */ } + case MONO_TABLE_TYPEDEF: { +#ifdef ALLOW_METHOD_ADD + /* FIXME: wrong for cumulative updates - need to look at DeltaInfo:count.prev_gen_rows */ + gboolean new_class = token_index > table_info_get_rows (&image_base->tables [token_table]); + /* only allow adding methods to existing classes for now */ + if (!new_class && func_code == ENC_FUNC_ADD_METHOD) { + /* next record should be a MONO_TABLE_METHOD addition (func == default) */ + g_assert (i + 1 < rows); + guint32 next_cols [MONO_ENCLOG_SIZE]; + mono_metadata_decode_row (table_enclog, i + 1, next_cols, MONO_ENCLOG_SIZE); + g_assert (next_cols [MONO_ENCLOG_FUNC_CODE] == ENC_FUNC_DEFAULT); + int next_token = next_cols [MONO_ENCLOG_TOKEN]; + int next_table = mono_metadata_token_table (next_token); + int next_index = mono_metadata_token_index (next_token); + g_assert (next_table == MONO_TABLE_METHOD); + g_assert (next_index > table_info_get_rows (&image_base->tables [next_table])); + i++; /* skip the next record */ + continue; + } +#endif + /* fallthru */ + } default: /* FIXME: this bounds check is wrong for cumulative updates - need to look at the DeltaInfo:count.prev_gen_rows */ if (token_index <= table_info_get_rows (&image_base->tables [token_table])) { @@ -1223,7 +1439,7 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer * the preceeding MONO_TABLE_TYPEDEF enc record that identifies the parent type). */ switch (func_code) { - case 0: /* default */ + case ENC_FUNC_DEFAULT: /* default */ break; default: mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x FunCode %d (%s) not supported (token=0x%08x)", i, log_token, func_code, funccode_to_str (func_code), log_token); @@ -1235,6 +1451,12 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer return !unsupported_edits; } +static void +set_delta_method_debug_info (DeltaInfo *delta_info, uint32_t token_index, MonoDebugInformationEnc *pdb_address) +{ + g_hash_table_insert (delta_info->method_ppdb_table_update, GUINT_TO_POINTER (token_index), (gpointer) pdb_address); +} + static void set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, uint32_t token_index, const char* il_address, MonoDebugInformationEnc* pdb_address) { @@ -1242,7 +1464,7 @@ set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t gene /* FIXME: this is a race if other threads are doing a lookup. */ g_hash_table_insert (base_info->method_table_update, GUINT_TO_POINTER (token_index), GUINT_TO_POINTER (generation)); g_hash_table_insert (delta_info->method_table_update, GUINT_TO_POINTER (token_index), (gpointer) il_address); - g_hash_table_insert (delta_info->method_ppdb_table_update, GUINT_TO_POINTER (token_index), (gpointer) pdb_address); + set_delta_method_debug_info (delta_info, token_index, pdb_address); } static MonoDebugInformationEnc * @@ -1272,6 +1494,26 @@ hot_reload_get_method_debug_information (MonoPPDBFile *ppdb_file, int idx) return NULL; } +static void G_GNUC_UNUSED +dump_assembly_ref_names (MonoImage *image) +{ + if (!mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) + return; + for (int i = 0; i < image->nreferences; ++i) { + ERROR_DECL(local_error); + MonoAssemblyName aname; + mono_assembly_get_assemblyref_checked (image, i, &aname, local_error); + + if (is_ok (local_error)) + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Reference[%02d] = '%s'", i, aname.name); + else { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Reference[%02d] error '%s'", i, mono_error_get_message (local_error)); + mono_error_cleanup (local_error); + } + } +} + + /* 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) @@ -1302,6 +1544,12 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen * have it in writable memory (and not mmap-ed pages), so we can rewrite the table values. */ + + MonoClass *add_method_klass = NULL; +#ifdef ALLOW_METHOD_ADD + uint32_t add_param_method_index = 0; +#endif + gboolean assemblyref_updated = FALSE; for (int i = 0; i < rows ; ++i) { guint32 cols [MONO_ENCLOG_SIZE]; @@ -1317,11 +1565,31 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen /* TODO: See CMiniMdRW::ApplyDelta for how to drive this. */ switch (func_code) { - case 0: /* default */ - break; - default: - g_error ("EnC: unsupported FuncCode, should be caught by pass1"); - break; + case ENC_FUNC_DEFAULT: /* default */ + break; +#ifdef ALLOW_METHOD_ADD + case ENC_FUNC_ADD_METHOD: { + g_assert (token_table == MONO_TABLE_TYPEDEF); + /* FIXME: this bounds check is wrong for cumulative updates - need to look at the DeltaInfo:count.prev_gen_rows */ + /* should've been caught by pass1 if we're adding a new method to a new class. */ + MonoClass *klass = mono_class_get_checked (image_base, log_token, error); + if (!is_ok (error)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Can't get class with token 0x%08x due to: %s", log_token, mono_error_get_message (error)); + return FALSE; + } + add_method_klass = klass; + break; + } + + case ENC_FUNC_ADD_PARAM: { + g_assert (token_table == MONO_TABLE_METHOD); + add_param_method_index = token_index; + break; + } +#endif + default: + g_error ("EnC: unsupported FuncCode, should be caught by pass1"); + break; } switch (token_table) { @@ -1336,8 +1604,8 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen /* FIXME: use DeltaInfo:prev_gen_rows instead of looping */ /* TODO: do we know that there will never be modified rows in ASSEMBLYREF? */ int old_rows = table_info_get_rows (&image_base->tables [MONO_TABLE_ASSEMBLYREF]); - for (GList *l = base_info->delta_image; l; l = l->next) { - MonoImage *delta_child = l->data; + for (GList *l = base_info->delta_info; l; l = l->next) { + MonoImage *delta_child = ((DeltaInfo*)l->data)->delta_image; old_rows += table_info_get_rows (&delta_child->tables [MONO_TABLE_ASSEMBLYREF]); } int new_rows = table_info_get_rows (&image_dmeta->tables [MONO_TABLE_ASSEMBLYREF]); @@ -1356,13 +1624,29 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen image_base->nreferences = old_rows + new_rows; mono_image_unlock (image_base); +#if 0 + dump_assembly_ref_names (image_base); +#endif + g_free (old_array); 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 (token_index > table_info_get_rows (&image_base->tables [token_table])) { - g_error ("EnC: new method added, should be caught by pass1"); + if (!add_method_klass) + g_error ("EnC: new method added but I don't know the class, should be caught by pass1"); + g_assert (add_method_klass); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new method 0x%08x to class %s.%s", token_index, m_class_get_name_space (add_method_klass), m_class_get_name (add_method_klass)); + MonoDebugInformationEnc *method_debug_information = hot_reload_get_method_debug_information (delta_info->ppdb_file, token_index); + add_method_to_baseline (base_info, delta_info, add_method_klass, token_index, method_debug_information); + add_method_klass = NULL; } +#endif if (!base_info->method_table_update) base_info->method_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); @@ -1372,7 +1656,7 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen delta_info->method_ppdb_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); - int mapped_token = hot_reload_relative_delta_index (image_dmeta, mono_metadata_make_token (token_table, token_index)); + int mapped_token = hot_reload_relative_delta_index (image_dmeta, delta_info, mono_metadata_make_token (token_table, token_index)); int rva = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_METHOD], mapped_token - 1, MONO_METHOD_RVA); if (rva < dil_length) { char *il_address = ((char *) dil_data) + rva; @@ -1382,6 +1666,7 @@ 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); } + add_method_klass = NULL; break; } case MONO_TABLE_TYPEDEF: { @@ -1405,6 +1690,26 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen } 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. + * + * 10: 0x02000002 (TypeDef) 0x00000001 (AddMethod) + * 11: 0x06000006 (MethodDef) 0 + * 12: 0x02000002 (TypeDef) 0x00000001 (AddMethod) + * 13: 0x06000007 (MethodDef) 0 + * 14: 0x06000006 (MethodDef) 0x00000003 (AddParameter) + * 15: 0x08000003 (Param) 0 + * 16: 0x06000006 (MethodDef) 0x00000003 (AddParameter) + * 17: 0x08000004 (Param) 0 + * 18: 0x06000007 (MethodDef) 0x00000003 (AddParameter) + * 19: 0x08000005 (Param) 0 + * + * So by the time we see the param additions, the methods are already in. + * + * FIXME: we need a lookaside table (like method_parent) for every place + * that looks at MONO_METHOD_PARAMLIST + */ break; } default: { @@ -1468,6 +1773,7 @@ hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta /* makes a copy of dil_bytes_orig */ gpointer dil_bytes = open_dil_data (image_base, dil_bytes_orig, dil_length); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "delta IL bytes copied to addr=%p", dil_bytes); MonoPPDBFile *ppdb_file = NULL; if (dpdb_length > 0) @@ -1482,7 +1788,8 @@ hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta BaselineInfo *base_info = baseline_info_lookup_or_add (image_base); - DeltaInfo *delta_info = delta_info_init (image_dmeta, image_base, ppdb_file, base_info, generation); + DeltaInfo *prev_delta_info = NULL; + DeltaInfo *delta_info = delta_info_init (image_dmeta, image_base, ppdb_file, base_info, generation, &prev_delta_info); if (image_dmeta->minimal_delta) { @@ -1517,8 +1824,14 @@ hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta return; } + delta_info_initialize_mutants (image_base, base_info, prev_delta_info, delta_info); + + prepare_mutated_rows (table_enclog, image_base, image_dmeta, delta_info); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Populated mutated tables for delta image %p", image_dmeta); + - if (!apply_enclog_pass1 (image_base, image_dmeta, dil_bytes, dil_length, error)) { + if (!apply_enclog_pass1 (image_base, image_dmeta, delta_info, dil_bytes, dil_length, error)) { mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error on sanity-checking delta image to base=%s, due to: %s", basename, mono_error_get_message (error)); hot_reload_update_cancel (generation); return; @@ -1547,35 +1860,6 @@ hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, ">>> EnC delta for base=%s (generation %d) applied", basename, generation); } -/* - * Returns how many times the base image was updated upto and including the given delta. - */ -static int -metadata_update_local_generation (MonoImage *base, BaselineInfo *base_info, MonoImage *delta) -{ - if (delta == base) - return 0; - int index = g_list_index (base_info->delta_image, delta); - g_assert (index != -1); - return 1 + index; -} - -/* - * Returns how many times the given base image has been updated so far. - * - * NOTE: doesn't look at update_published or update_alloc_frontier, and therefore only usable by the - * update originator. - */ -static int -metadata_update_count_updates (MonoImage *base) -{ - BaselineInfo *base_info = baseline_info_lookup (base); - if (!base_info || !base_info->delta_image_last) - return 0; - else - return metadata_update_local_generation (base, base_info, (MonoImage*)base_info->delta_image_last->data); -} - static gpointer get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t idx, gboolean is_pdb) { @@ -1585,9 +1869,8 @@ get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t /* Go through all the updates that the current thread can see and see * if they updated the method. Keep the latest visible update */ - for (GList *ptr = base_info->delta_image; ptr != NULL; ptr = ptr->next) { - MonoImage *image_delta = (MonoImage*) ptr->data; - DeltaInfo *delta_info = delta_info_lookup (image_delta); + for (GList *ptr = base_info->delta_info; ptr != NULL; ptr = ptr->next) { + DeltaInfo *delta_info = (DeltaInfo*)ptr->data; g_assert (delta_info); if (delta_info->generation > cur) break; @@ -1624,6 +1907,10 @@ hot_reload_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx) if (G_UNLIKELY (gen > 0)) { loc = get_method_update_rva (base_image, info, idx, TRUE); } + /* Check the method_parent table as a way of checking if the method was added by a later generation. If so, still look for its PPDB info in our update tables */ + if (G_UNLIKELY (loc == 0 && info->method_parent && GPOINTER_TO_UINT (g_hash_table_lookup (info->method_parent, GUINT_TO_POINTER (idx))) > 0)) { + loc = get_method_update_rva (base_image, info, idx, TRUE); + } } return loc; } @@ -1653,27 +1940,25 @@ hot_reload_table_bounds_check (MonoImage *base_image, int table_index, int token BaselineInfo *base_info = baseline_info_lookup (base_image); g_assert (base_info); - GList *list = base_info->delta_image; + GList *list = base_info->delta_info; MonoImage *dmeta; MonoTableInfo *table; /* result row, 0-based */ int ridx; - int original_token = mono_metadata_make_token (table_index, token_index); - uint32_t exposed_gen = hot_reload_get_thread_generation (); do { if (!list) return TRUE; - dmeta = list->data; - DeltaInfo *delta_info = delta_info_lookup (dmeta); + DeltaInfo *delta_info = (DeltaInfo*)list->data; g_assert (delta_info); + dmeta = delta_info->delta_image; if (delta_info->generation > exposed_gen) return TRUE; list = list->next; - table = &dmeta->tables [table_index]; - /* mono_image_relative_delta_index returns a 1-based index */ - ridx = hot_reload_relative_delta_index (dmeta, original_token) - 1; + + table = &delta_info->mutants [table_index]; + ridx = token_index - 1; } while (ridx < 0 || ridx >= table_info_get_rows (table)); return FALSE; @@ -1688,7 +1973,7 @@ hot_reload_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_ g_assert (orig_index >= heap->size); BaselineInfo *base_info = baseline_info_lookup (base_image); g_assert (base_info); - g_assert (base_info->delta_image); + g_assert (base_info->delta_info); *image_out = base_image; *index_out = orig_index; @@ -1697,13 +1982,15 @@ hot_reload_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_ uint32_t current_gen = hot_reload_get_thread_generation (); GList *cur; - for (cur = base_info->delta_image; cur; cur = cur->next) { - MonoImage *delta_image = (MonoImage*)cur->data; + for (cur = base_info->delta_info; cur; cur = cur->next) { + DeltaInfo *delta_info = (DeltaInfo*)cur->data; + g_assert (delta_info); + MonoImage *delta_image = delta_info->delta_image; + g_assert (delta_image); heap = get_heap (delta_image); *image_out = delta_image; - DeltaInfo *delta_info = delta_info_lookup (delta_image); if (delta_info->generation > current_gen) return FALSE; @@ -1730,3 +2017,74 @@ hot_reload_has_modified_rows (const MonoTableInfo *table) return info->any_modified_rows[tbl_index]; } +static int +hot_reload_table_num_rows_slow (MonoImage *base, int table_index) +{ + BaselineInfo *base_info = baseline_info_lookup (base); + if (!base_info) + return FALSE; + + uint32_t current_gen = hot_reload_get_thread_generation (); + + int rows = table_info_get_rows (&base->tables [table_index]); + GList *cur; + for (cur = base_info->delta_info; cur; cur = cur->next) { + DeltaInfo *delta_info = (DeltaInfo*)cur->data; + g_assert (delta_info); + if (delta_info->generation > current_gen) + break; + rows = delta_info->count [table_index].prev_gen_rows + delta_info->count [table_index].inserted_rows; + } + return rows; +} + +static void +add_method_to_baseline (BaselineInfo *base_info, DeltaInfo *delta_info, MonoClass *klass, uint32_t method_token, MonoDebugInformationEnc* pdb_address) +{ + if (!base_info->added_methods) { + base_info->added_methods = g_hash_table_new (g_direct_hash, g_direct_equal); + } + if (!base_info->method_parent) { + base_info->method_parent = g_hash_table_new (g_direct_hash, g_direct_equal); + } + /* FIXME: locking for readers/writers of the GArray */ + GArray *arr = g_hash_table_lookup (base_info->added_methods, klass); + if (!arr) { + arr = g_array_new (FALSE, FALSE, sizeof(uint32_t)); + g_hash_table_insert (base_info->added_methods, klass, arr); + } + g_array_append_val (arr, method_token); + g_hash_table_insert (base_info->method_parent, GUINT_TO_POINTER (method_token), GUINT_TO_POINTER (m_class_get_type_token (klass))); + + if (pdb_address) + set_delta_method_debug_info (delta_info, method_token, pdb_address); +} + +static GArray* +hot_reload_get_added_methods (MonoClass *klass) +{ + /* FIXME: locking for the GArray? */ + MonoImage *image = m_class_get_image (klass); + if (!image->has_updates) + return NULL; + BaselineInfo *base_info = baseline_info_lookup (image); + if (!base_info || base_info->added_methods == NULL) + return NULL; + + return g_hash_table_lookup (base_info->added_methods, klass); +} + +static uint32_t +hot_reload_method_parent (MonoImage *base_image, uint32_t method_token) +{ + if (!base_image->has_updates) + return 0; + BaselineInfo *base_info = baseline_info_lookup (base_image); + if (!base_info || base_info->method_parent == NULL) + return 0; + + uint32_t res = GPOINTER_TO_UINT (g_hash_table_lookup (base_info->method_parent, GUINT_TO_POINTER (method_token))); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "method_parent lookup: 0x%08x returned 0x%08x\n", method_token, res); + + return res; +} diff --git a/src/mono/mono/component/hot_reload.h b/src/mono/mono/component/hot_reload.h index 37dc1401bbd11..416ff56e70eab 100644 --- a/src/mono/mono/component/hot_reload.h +++ b/src/mono/mono/component/hot_reload.h @@ -21,8 +21,7 @@ typedef struct _MonoComponentHotReload { uint32_t (*thread_expose_published) (void); uint32_t (*get_thread_generation) (void); void (*cleanup_on_close) (MonoImage *image); - void (*effective_table_slow) (const MonoTableInfo **t, int *idx); - int (*relative_delta_index) (MonoImage *image_dmeta, int token); + void (*effective_table_slow) (const MonoTableInfo **t, int idx); void (*apply_changes) (int origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); void (*image_close_except_pools_all) (MonoImage *base_image); void (*image_close_all) (MonoImage *base_image); @@ -31,6 +30,9 @@ typedef struct _MonoComponentHotReload { gboolean (*delta_heap_lookup) (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); gpointer (*get_updated_method_ppdb) (MonoImage *base_image, uint32_t idx); gboolean (*has_modified_rows) (const MonoTableInfo *table); + gboolean (*table_num_rows_slow) (MonoImage *base_image, int table_index); + GArray* (*get_added_methods) (MonoClass *klass); + uint32_t (*method_parent) (MonoImage *base_image, uint32_t method_index); } MonoComponentHotReload; MONO_COMPONENT_EXPORT_ENTRYPOINT diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index fc64623fad652..a3e451d4517f4 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -427,6 +427,8 @@ find_method_in_class (MonoClass *klass, const char *name, const char *qname, con { int i; + /* FIXME: method refs from metadata-upate probably end up here */ + /* Search directly in the metadata to avoid calling setup_methods () */ error_init (error); @@ -1160,6 +1162,8 @@ mono_get_method_checked (MonoImage *image, guint32 token, MonoClass *klass, Mono MonoMethod *result = NULL; gboolean used_context = FALSE; + /* FIXME: method definition lookups for metadata-update probably end up here */ + /* We do everything inside the lock to prevent creation races */ error_init (error); diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index f750438ac7d33..4e5ddf40abe86 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -791,24 +791,21 @@ gboolean mono_metadata_has_updates_api (void); void -mono_image_effective_table_slow (const MonoTableInfo **t, int *idx); +mono_image_effective_table_slow (const MonoTableInfo **t, int idx); gboolean mono_metadata_update_has_modified_rows (const MonoTableInfo *t); static inline void -mono_image_effective_table (const MonoTableInfo **t, int *idx) +mono_image_effective_table (const MonoTableInfo **t, int idx) { if (G_UNLIKELY (mono_metadata_has_updates ())) { - if (G_UNLIKELY (*idx >= table_info_get_rows ((*t)) || mono_metadata_update_has_modified_rows (*t))) { + if (G_UNLIKELY (idx >= table_info_get_rows ((*t)) || mono_metadata_update_has_modified_rows (*t))) { mono_image_effective_table_slow (t, idx); } } } -int -mono_image_relative_delta_index (MonoImage *image_dmeta, int token); - enum MonoEnCDeltaOrigin { MONO_ENC_DELTA_API = 0, MONO_ENC_DELTA_DBG = 1, @@ -848,6 +845,18 @@ mono_metadata_clean_generic_classes_for_image (MonoImage *image); gboolean mono_metadata_table_bounds_check_slow (MonoImage *image, int table_index, int token_index); +int +mono_metadata_table_num_rows_slow (MonoImage *image, int table_index); + +static inline int +mono_metadata_table_num_rows (MonoImage *image, int table_index) +{ + if (G_LIKELY (!image->has_updates)) + return table_info_get_rows (&image->tables [table_index]); + else + return mono_metadata_table_num_rows_slow (image, table_index); +} + /* token_index is 1-based */ static inline gboolean mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index) @@ -1223,4 +1232,18 @@ mono_type_get_array_type_internal (MonoType *type) return type->data.array; } +static inline int +mono_metadata_table_to_ptr_table (int table_num) +{ + switch (table_num) { + case MONO_TABLE_FIELD: return MONO_TABLE_FIELD_POINTER; + case MONO_TABLE_METHOD: return MONO_TABLE_METHOD_POINTER; + case MONO_TABLE_PARAM: return MONO_TABLE_PARAM_POINTER; + case MONO_TABLE_PROPERTY: return MONO_TABLE_PROPERTY_POINTER; + case MONO_TABLE_EVENT: return MONO_TABLE_EVENT_POINTER; + default: + g_assert_not_reached (); + } +} + #endif /* __MONO_METADATA_INTERNALS_H__ */ diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 99a2d99a5d7c3..800ffb9f04727 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -61,17 +61,11 @@ mono_metadata_update_cleanup_on_close (MonoImage *base_image) } void -mono_image_effective_table_slow (const MonoTableInfo **t, int *idx) +mono_image_effective_table_slow (const MonoTableInfo **t, int idx) { mono_component_hot_reload ()->effective_table_slow (t, idx); } -int -mono_image_relative_delta_index (MonoImage *image_dmeta, int token) -{ - return mono_component_hot_reload ()->relative_delta_index (image_dmeta, token); -} - void mono_image_load_enc_delta (int origin, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error) { @@ -141,3 +135,15 @@ mono_metadata_has_updates_api (void) { return mono_metadata_has_updates (); } + +/** + * mono_metadata_table_num_rows: + * + * Returns the number of rows from the specified table that the current thread can see. + * If there's a EnC metadata update, this number may change. + */ +int +mono_metadata_table_num_rows_slow (MonoImage *base_image, int table_index) +{ + return mono_component_hot_reload()->table_num_rows_slow (base_image, table_index); +} diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index 580a0988650e3..5912a12a0c909 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -1303,7 +1303,7 @@ mono_metadata_decode_row (const MonoTableInfo *t, int idx, guint32 *res, int res void mono_metadata_decode_row_slow (const MonoTableInfo *t, int idx, guint32 *res, int res_size) { - mono_image_effective_table (&t, &idx); + mono_image_effective_table (&t, idx); mono_metadata_decode_row_raw (t, idx, res, res_size); } @@ -1359,7 +1359,7 @@ mono_metadata_decode_row_checked (const MonoImage *image, const MonoTableInfo *t { const char *image_name = image && image->name ? image->name : "unknown image"; - mono_image_effective_table (&t, &idx); + mono_image_effective_table (&t, idx); guint32 bitfield = t->size_bitfield; int i, count = mono_metadata_table_count (bitfield); @@ -1448,7 +1448,7 @@ mono_metadata_decode_row_col (const MonoTableInfo *t, int idx, guint col) guint32 mono_metadata_decode_row_col_slow (const MonoTableInfo *t, int idx, guint col) { - mono_image_effective_table (&t, &idx); + mono_image_effective_table (&t, idx); return mono_metadata_decode_row_col_raw (t, idx, col); } @@ -2311,8 +2311,12 @@ mono_metadata_get_param_attrs (MonoImage *m, int def, int param_count) guint lastp, i, param_index = mono_metadata_decode_row_col (methodt, def - 1, MONO_METHOD_PARAMLIST); int *pattrs = NULL; + /* hot reload deltas may specify 0 for the param table index */ + if (param_index == 0) + return NULL; + /* FIXME: metadata-update */ - int rows = table_info_get_rows (methodt); + int rows = mono_metadata_table_num_rows (m, MONO_TABLE_METHOD); if (def < rows) lastp = mono_metadata_decode_row_col (methodt, def, MONO_METHOD_PARAMLIST); else @@ -4829,7 +4833,12 @@ mono_metadata_typedef_from_method (MonoImage *meta, guint32 index) if (meta->uncompressed_metadata) loc.idx = search_ptr_table (meta, MONO_TABLE_METHOD_POINTER, loc.idx); - /* FIXME: metadata-update */ + /* if it's not in the base image, look in the hot reload table */ + gboolean added = (loc.idx > table_info_get_rows (&meta->tables [MONO_TABLE_METHOD])); + if (added) { + uint32_t res = mono_component_hot_reload ()->method_parent (meta, loc.idx); + return res; /* 0 if not found, otherwise 1-based */ + } if (!mono_binary_search (&loc, tdef->base, table_info_get_rows (tdef), tdef->row_size, typedef_locator)) return 0;