diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 810d20a80bb57..e75535db53b4e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - 790c182422d28de1900142c3a008c995e7d8078a + a7de1bebfa53d0f958e588633664ce008b78adf2 https://github.com/dotnet/msquic @@ -242,9 +242,9 @@ https://github.com/dotnet/runtime e680411c22e33f45821f4ae64365a2970b2430a6 - + https://github.com/dotnet/linker - 9d4b3f3e0c100fe5ac4dc7f40d14d792178dbd0c + 13a94b5bdc9d01ecd9eb2bd699bd34d597c3ec19 https://github.com/dotnet/xharness @@ -278,9 +278,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization 5e0b0da43f660de5798186f4fd3bc900fc90576c - + https://github.com/dotnet/hotreload-utils - d4a9c1673071b9ef797eefc18a7586c92fcd34a1 + 0595e15def6557a758879ba1b48f95b9e08a6463 https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index 8108e43a6771b..b85deb8f8ff90 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -174,10 +174,10 @@ 7.0.0-preview-20221010.1 - 7.0.100-1.23211.1 + 7.0.100-1.23321.1 $(MicrosoftNETILLinkTasksVersion) - 7.0.0-rtm.23218.4 + 7.0.0-rtm.23315.2 2.1.1 7.0.0-alpha.1.22459.1 diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index e3122f12da18c..7653205e83780 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -10280,7 +10280,7 @@ static int __cdecl cmp_mark_list_item (const void* vkey, const void* vdatum) #endif // _DEBUG #ifdef USE_REGIONS -uint8_t** gc_heap::get_region_mark_list (uint8_t* start, uint8_t* end, uint8_t*** mark_list_end_ptr) +uint8_t** gc_heap::get_region_mark_list (BOOL& use_mark_list, uint8_t* start, uint8_t* end, uint8_t*** mark_list_end_ptr) { size_t region_number = get_basic_region_index_for_address (start); size_t source_number = region_number; @@ -10410,6 +10410,13 @@ void gc_heap::merge_mark_lists (size_t total_mark_list_size) // blast this piece to the mark list append_to_mark_list(source[lowest_source], x); +#ifdef USE_REGIONS + if (mark_list_index > mark_list_end) + { + use_mark_list = false; + return nullptr; + } +#endif //USE_REGIONS piece_count++; source[lowest_source] = x; @@ -10429,6 +10436,13 @@ void gc_heap::merge_mark_lists (size_t total_mark_list_size) } // we're left with just one source that we copy append_to_mark_list(source[0], source_end[0]); +#ifdef USE_REGIONS + if (mark_list_index > mark_list_end) + { + use_mark_list = false; + return nullptr; + } +#endif //USE_REGIONS piece_count++; } @@ -10485,7 +10499,7 @@ static uint8_t** binary_search (uint8_t** left, uint8_t** right, uint8_t* e) return a + l; } -uint8_t** gc_heap::get_region_mark_list (uint8_t* start, uint8_t* end, uint8_t*** mark_list_end_ptr) +uint8_t** gc_heap::get_region_mark_list (BOOL& use_mark_list, uint8_t* start, uint8_t* end, uint8_t*** mark_list_end_ptr) { // do a binary search over the sorted marked list to find start and end of the // mark list for this region @@ -29235,7 +29249,7 @@ void gc_heap::plan_phase (int condemned_gen_number) uint8_t** mark_list_index = nullptr; uint8_t** mark_list_next = nullptr; if (use_mark_list) - mark_list_next = get_region_mark_list (x, end, &mark_list_index); + mark_list_next = get_region_mark_list (use_mark_list, x, end, &mark_list_index); #else // USE_REGIONS assert (!marked (x)); uint8_t** mark_list_next = &mark_list[0]; @@ -29523,7 +29537,7 @@ void gc_heap::plan_phase (int condemned_gen_number) current_brick = brick_of (x); #ifdef USE_REGIONS if (use_mark_list) - mark_list_next = get_region_mark_list (x, end, &mark_list_index); + mark_list_next = get_region_mark_list (use_mark_list, x, end, &mark_list_index); if (should_sweep_in_plan (seg1)) { @@ -29593,7 +29607,7 @@ void gc_heap::plan_phase (int condemned_gen_number) current_brick = brick_of (x); if (use_mark_list) - mark_list_next = get_region_mark_list (x, end, &mark_list_index); + mark_list_next = get_region_mark_list (use_mark_list, x, end, &mark_list_index); if (should_sweep_in_plan (seg1)) { diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index fde1cb3c36f2d..ee0ea0dfa4a54 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -3433,7 +3433,7 @@ class gc_heap #ifdef USE_REGIONS PER_HEAP - uint8_t** get_region_mark_list (uint8_t* start, uint8_t* end, uint8_t*** mark_list_end); + uint8_t** get_region_mark_list (BOOL& use_mark_list, uint8_t* start, uint8_t* end, uint8_t*** mark_list_end); #endif //USE_REGIONS #ifdef BACKGROUND_GC diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp index a7961985d4b6e..bedb711b0fccb 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp +++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp @@ -4332,7 +4332,7 @@ HRESULT ProfToEEInterfaceImpl::GetILFunctionBody(ModuleID moduleId, PEAssembly *pPEAssembly = pModule->GetPEAssembly(); - if (!pPEAssembly->HasLoadedPEImage()) + if (!pPEAssembly->IsLoaded()) return (CORPROF_E_DATAINCOMPLETE); LPCBYTE pbMethod = NULL; @@ -4442,7 +4442,7 @@ HRESULT ProfToEEInterfaceImpl::GetILFunctionBodyAllocator(ModuleID modul Module * pModule = (Module *) moduleId; if (pModule->IsBeingUnloaded() || - !pModule->GetPEAssembly()->HasLoadedPEImage()) + !pModule->GetPEAssembly()->IsLoaded()) { return (CORPROF_E_DATAINCOMPLETE); } diff --git a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs index ca97f1f8e0165..1c7e8683c4563 100644 --- a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs @@ -322,7 +322,8 @@ public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegistere .Execute() .Should().Pass() .And.HaveStdOutContaining("Hello World") - .And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion); + .And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion) + .And.NotHaveStdErr(); // Verify running from within the working directory Command.Create(appExe) @@ -336,7 +337,8 @@ public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegistere .Execute() .Should().Pass() .And.HaveStdOutContaining("Hello World") - .And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion); + .And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion) + .And.NotHaveStdErr(); } } diff --git a/src/installer/tests/HostActivation.Tests/StartupHooks.cs b/src/installer/tests/HostActivation.Tests/StartupHooks.cs index 7f71bb83baa10..cbe7fd3e07c15 100644 --- a/src/installer/tests/HostActivation.Tests/StartupHooks.cs +++ b/src/installer/tests/HostActivation.Tests/StartupHooks.cs @@ -177,7 +177,8 @@ public void Muxer_activation_of_Empty_StartupHook_Variable_Succeeds() .CaptureStdErr() .Execute() .Should().Pass() - .And.HaveStdOutContaining("Hello World"); + .And.HaveStdOutContaining("Hello World") + .And.NotHaveStdErr(); } // Run the app with a startup hook assembly that depends on assemblies not on the TPA list diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj index 1aaf66e347bec..3f5e05d8f9745 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj @@ -25,7 +25,7 @@ $(AdditionalRuntimeIdentifiers);$(OutputRID) 4 - true + false diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs index 8ceb38f13eb8c..f2d02cde7296b 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs @@ -64,21 +64,17 @@ internal Deflater(CompressionLevel compressionLevel, int windowBits) ZLibNative.CompressionStrategy strategy = ZLibNative.CompressionStrategy.DefaultStrategy; - ZLibNative.ZLibStreamHandle? zlibStream = null; ZErrorCode errC; try { - errC = ZLibNative.CreateZLibStreamForDeflate(out zlibStream, zlibCompressionLevel, + errC = ZLibNative.CreateZLibStreamForDeflate(out _zlibStream, zlibCompressionLevel, windowBits, memLevel, strategy); } catch (Exception cause) { - zlibStream?.Dispose(); throw new ZLibException(SR.ZLibErrorDLLLoadError, cause); } - _zlibStream = zlibStream; - switch (errC) { case ZErrorCode.Ok: diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Inflater.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Inflater.cs index 493a6f47d8cb2..4544353ac5406 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Inflater.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Inflater.cs @@ -234,20 +234,16 @@ public void Dispose() [MemberNotNull(nameof(_zlibStream))] private void InflateInit(int windowBits) { - ZLibNative.ZLibStreamHandle? zlibStream = null; ZLibNative.ErrorCode error; try { - error = ZLibNative.CreateZLibStreamForInflate(out zlibStream, windowBits); + error = ZLibNative.CreateZLibStreamForInflate(out _zlibStream, windowBits); } catch (Exception exception) // could not load the ZLib dll { - zlibStream?.Dispose(); throw new ZLibException(SR.ZLibErrorDLLLoadError, exception); } - _zlibStream = zlibStream; - switch (error) { case ZLibNative.ErrorCode.Ok: // Successful initialization diff --git a/src/libraries/System.Reflection.Extensions/tests/RuntimeReflectionExtensionTests.cs b/src/libraries/System.Reflection.Extensions/tests/RuntimeReflectionExtensionTests.cs index 0dd03017c9e4b..5b8aa82795c00 100644 --- a/src/libraries/System.Reflection.Extensions/tests/RuntimeReflectionExtensionTests.cs +++ b/src/libraries/System.Reflection.Extensions/tests/RuntimeReflectionExtensionTests.cs @@ -101,6 +101,9 @@ public void GetRuntimeProperties() Assert.Contains("CanRead", propertyNames); Assert.Contains("CanWrite", propertyNames); Assert.Contains("CanSeek", propertyNames); + + List props = typeof(TestClass).GetRuntimeProperties().ToList(); + Assert.Equal(2, props.Count); } [Fact] @@ -359,6 +362,16 @@ private class TestDerived : TestBase public override void Foo() { throw null; } } + private class TestClassBase + { + internal int TestClassBaseProperty { get; set; } + } + + private class TestClass : TestClassBase + { + internal int TestClassProperty { get; set; } + } + abstract class TestTypeBase : IDisposable { public abstract bool CanRead { get; } diff --git a/src/libraries/System.Reflection/tests/AssemblyTests.cs b/src/libraries/System.Reflection/tests/AssemblyTests.cs index bd86b761db560..a3808541dcf8d 100644 --- a/src/libraries/System.Reflection/tests/AssemblyTests.cs +++ b/src/libraries/System.Reflection/tests/AssemblyTests.cs @@ -148,7 +148,7 @@ public void ExportedTypes(Type type, bool expected) [Fact] [SkipOnPlatform(TestPlatforms.Browser, "entry assembly won't be xunit.console on browser")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/36892", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/36892", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst | TestPlatforms.Android)] public void GetEntryAssembly() { Assert.NotNull(Assembly.GetEntryAssembly()); @@ -872,6 +872,7 @@ public static void AssemblyGetForwardedTypes() [Fact] [ActiveIssue("https://github.com/dotnet/runtimelab/issues/155", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] + [ActiveIssue("https://github.com/dotnet/runtimelab/issues/77821", TestPlatforms.Android)] public static void AssemblyGetForwardedTypesLoadFailure() { Assembly a = typeof(TypeInForwardedAssembly).Assembly; diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index f9f9f94a8204e..a4f337987354a 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -68,6 +68,8 @@ + + @@ -171,7 +173,6 @@ - diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 09362a731d647..f8592363402cf 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -4032,8 +4032,6 @@ property_accessor_nonpublic (MonoMethod* accessor, gboolean start_klass) GPtrArray* ves_icall_RuntimeType_GetPropertiesByName_native (MonoQCallTypeHandle type_handle, gchar *propname, guint32 bflags, guint32 mlisttype, MonoError *error) { - // Fetch non-public properties as well because they can hide public properties with the same name in base classes - bflags |= BFLAGS_NonPublic; MonoType *type = type_handle.type; if (m_type_is_byref (type)) @@ -4072,11 +4070,9 @@ ves_icall_RuntimeType_GetPropertiesByName_native (MonoQCallTypeHandle type_handl (prop->set && ((prop->set->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == METHOD_ATTRIBUTE_PUBLIC))) { if (bflags & BFLAGS_Public) match++; - } else if (bflags & BFLAGS_NonPublic) { - if (property_accessor_nonpublic(prop->get, startklass == klass) || + } else if (property_accessor_nonpublic(prop->get, startklass == klass) || property_accessor_nonpublic(prop->set, startklass == klass)) { match++; - } } if (!match) continue; @@ -4106,8 +4102,6 @@ ves_icall_RuntimeType_GetPropertiesByName_native (MonoQCallTypeHandle type_handl g_hash_table_insert (properties, prop, prop); } if (!(bflags & BFLAGS_DeclaredOnly) && (klass = m_class_get_parent (klass))) { - // BFLAGS_NonPublic should be excluded for base classes - bflags &= ~BFLAGS_NonPublic; goto handle_parent; } diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 0a4ef9fefbb61..fb76aef549639 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -2023,7 +2024,7 @@ static clockid_t clock_id = CLOCK_MONOTONIC; enum { JIT_DUMP_MAGIC = 0x4A695444, - JIT_DUMP_VERSION = 2, + JIT_DUMP_VERSION = 1, #if HOST_X86 ELF_MACHINE = EM_386, #elif HOST_AMD64 @@ -2039,7 +2040,8 @@ enum { #elif HOST_RISCV ELF_MACHINE = EM_RISCV, #endif - JIT_CODE_LOAD = 0 + JIT_CODE_LOAD = 0, + JIT_DEBUG_INFO = 2 }; typedef struct { @@ -2070,7 +2072,22 @@ typedef struct // Null terminated function name // Native code } JitCodeLoadRecord; +typedef struct +{ + guint64 code_addr; + guint32 line; + guint32 discrim; + char name[]; +} DebugEntry; +typedef struct +{ + RecordHeader header; + guint64 code_addr; + guint64 nr_entry; + DebugEntry debug_entry[]; +} JitCodeDebug; +static void add_basic_JitCodeDebug_info (JitCodeDebug *record); static void add_file_header_info (FileHeader *header); static void add_basic_JitCodeLoadRecord_info (JitCodeLoadRecord *record); @@ -2090,7 +2107,7 @@ mono_enable_jit_dump (void) g_snprintf (name, sizeof (name), "/tmp/jit-%d.dump", perf_dump_pid); unlink (name); - perf_dump_file = fopen (name, "w"); + perf_dump_file = fopen (name, "w+"); add_file_header_info (&header); if (perf_dump_file) { @@ -2136,7 +2153,72 @@ mono_emit_jit_dump (MonoJitInfo *jinfo, gpointer code) record.code_index = ++code_index; - // TODO: write debugInfo and unwindInfo immediately before the JitCodeLoadRecord (while lock is held). + DebugEntry ent; + JitCodeDebug rec; + MonoDebugMethodInfo *minfo; + MonoDebugMethodJitInfo *dmji; + MonoDebugSourceLocation *loc; + int i; + + memset (&rec, 0, sizeof (rec)); + + //populating info relating debug methods + minfo = mono_debug_lookup_method (jinfo->d.method); + dmji = mono_debug_find_method (jinfo->d.method, NULL); + + add_basic_JitCodeDebug_info (&rec); + rec.code_addr = (guint64)dmji->code_start; + rec.header.total_size = sizeof (rec) + sizeof (ent) + 1; + rec.nr_entry = 1; + for (i = 0; i < dmji->num_line_numbers; ++i){ + + loc = mono_debug_lookup_source_location_by_il (jinfo->d.method, dmji->line_numbers[i].il_offset, NULL); + + if(!loc) + continue; + + if(!loc->source_file){ + mono_debug_free_source_location (loc); + continue; + } + + rec.header.total_size += sizeof (ent) + strlen (loc->source_file) + 1; + rec.nr_entry++; + } + + fwrite (&rec, sizeof (rec), 1, perf_dump_file); + + + for( i = 0; i < dmji->num_line_numbers; ++i){ + + //get the line number using il offset + loc = mono_debug_lookup_source_location_by_il (jinfo->d.method, dmji->line_numbers[i].il_offset, NULL); + + if(!loc) + continue; + + if(!loc->source_file){ + + mono_debug_free_source_location (loc); + continue; + } + + ent.code_addr = (guint64)dmji->code_start + dmji->line_numbers[i].native_offset; + ent.discrim = 0; + ent.line = (guint32)loc->row; + + fwrite (&ent, sizeof(ent), 1, perf_dump_file); + fwrite (loc->source_file, strlen (loc->source_file) + 1, 1, perf_dump_file); + } + + + ent.code_addr = (guint64)jinfo->code_start + jinfo->code_size; + ent.discrim = 0; + ent.line = 0; + fwrite (&ent, sizeof (ent), 1, perf_dump_file); + fwrite ("", 1, 1, perf_dump_file); + + // TODO: write unwindInfo immediately before the JitCodeLoadRecord (while lock is held). record.header.timestamp = mono_clock_get_time_ns (clock_id); @@ -2147,7 +2229,13 @@ mono_emit_jit_dump (MonoJitInfo *jinfo, gpointer code) mono_os_mutex_unlock (&perf_dump_mutex); } } +static void +add_basic_JitCodeDebug_info (JitCodeDebug *record) +{ + record->header.id = JIT_DEBUG_INFO; + record->header.timestamp = mono_clock_get_time_ns (clock_id); +} static void add_basic_JitCodeLoadRecord_info (JitCodeLoadRecord *record) { diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index a79dc376d7506..5a2b3d2577f46 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -1475,61 +1475,56 @@ public IEnumerable Add(SessionId id, string name, byte[] assembly_da } } - public async IAsyncEnumerable Load(SessionId id, string[] loaded_files, ExecutionContext context, bool useDebuggerProtocol, [EnumeratorCancellation] CancellationToken token) + public async IAsyncEnumerable Load(SessionId id, string[] loaded_files, ExecutionContext context, bool tryUseDebuggerProtocol, [EnumeratorCancellation] CancellationToken token) { var asm_files = new List(); List steps = new List(); - if (!useDebuggerProtocol) + var pdb_files = new List(); + foreach (string file_name in loaded_files) { - var pdb_files = new List(); - foreach (string file_name in loaded_files) - { - if (file_name.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase)) - pdb_files.Add(file_name); - else - asm_files.Add(file_name); - } + if (file_name.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase)) + pdb_files.Add(file_name); + else + asm_files.Add(file_name); + } - foreach (string url in asm_files) + foreach (string url in asm_files) + { + try { - try - { - string candidate_pdb = Path.ChangeExtension(url, "pdb"); - string pdb = pdb_files.FirstOrDefault(n => n == candidate_pdb); + string candidate_pdb = Path.ChangeExtension(url, "pdb"); + string pdb = pdb_files.FirstOrDefault(n => n == candidate_pdb); - steps.Add( - new DebugItem - { - Url = url, - Data = Task.WhenAll(MonoProxy.HttpClient.GetByteArrayAsync(url, token), pdb != null ? MonoProxy.HttpClient.GetByteArrayAsync(pdb, token) : Task.FromResult(null)) - }); - } - catch (Exception e) - { - logger.LogDebug($"Failed to read {url} ({e.Message})"); - } + steps.Add( + new DebugItem + { + Url = url, + Data = Task.WhenAll(MonoProxy.HttpClient.GetByteArrayAsync(url, token), pdb != null ? MonoProxy.HttpClient.GetByteArrayAsync(pdb, token) : Task.FromResult(null)) + }); } - } - else - { - foreach (string file_name in loaded_files) + catch (Exception e) { - if (file_name.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase)) - continue; - try + if (tryUseDebuggerProtocol) { - string unescapedFileName = Uri.UnescapeDataString(file_name); - steps.Add( + try + { + string unescapedFileName = Uri.UnescapeDataString(url); + steps.Add( new DebugItem { - Url = file_name, + Url = url, Data = context.SdbAgent.GetBytesFromAssemblyAndPdb(Path.GetFileName(unescapedFileName), token) }); + } + catch (Exception ex) + { + logger.LogDebug($"Failed to get bytes using debugger protocol {url} ({ex.Message})"); + } } - catch (Exception e) + else { - logger.LogDebug($"Failed to read {file_name} ({e.Message})"); + logger.LogDebug($"Failed to read {url} ({e.Message})"); } } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index a7c75a428e97a..4ab32b55a5669 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -407,6 +408,7 @@ public ExecutionContext(MonoSDBHelper sdbAgent, int id, object auxData, PauseOnE AuxData = auxData; SdbAgent = sdbAgent; PauseOnExceptions = pauseOnExceptions; + Destroyed = false; } public string DebugId { get; set; } @@ -440,6 +442,8 @@ public ExecutionContext(MonoSDBHelper sdbAgent, int id, object auxData, PauseOnE internal int TempBreakpointForSetNextIP { get; set; } internal bool FirstBreakpoint { get; set; } + internal bool Destroyed { get; set; } + public DebugStore Store { get @@ -486,4 +490,70 @@ public PerScopeCache() { } } + + internal sealed class ConcurrentExecutionContextDictionary + { + private ConcurrentDictionary> contexts = new (); + public ExecutionContext GetCurrentContext(SessionId sessionId) + => TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context) + ? context + : throw new KeyNotFoundException($"No execution context found for session {sessionId}"); + + public bool TryGetCurrentExecutionContextValue(SessionId id, out ExecutionContext executionContext, bool ignoreDestroyedContext = true) + { + executionContext = null; + if (!contexts.TryGetValue(id, out ConcurrentBag contextBag)) + return false; + if (contextBag.IsEmpty) + return false; + IEnumerable validContexts = null; + if (ignoreDestroyedContext) + validContexts = contextBag.Where(context => context.Destroyed == false); + else + validContexts = contextBag; + if (!validContexts.Any()) + return false; + int maxId = validContexts.Max(context => context.Id); + executionContext = contextBag.FirstOrDefault(context => context.Id == maxId); + return executionContext != null; + } + + public void OnDefaultContextUpdate(SessionId sessionId, ExecutionContext newContext) + { + if (TryGetAndAddContext(sessionId, newContext, out ExecutionContext previousContext)) + { + foreach (KeyValuePair kvp in previousContext.BreakpointRequests) + { + newContext.BreakpointRequests[kvp.Key] = kvp.Value.Clone(); + } + newContext.PauseOnExceptions = previousContext.PauseOnExceptions; + } + } + + public bool TryGetAndAddContext(SessionId sessionId, ExecutionContext newExecutionContext, out ExecutionContext previousExecutionContext) + { + bool hasExisting = TryGetCurrentExecutionContextValue(sessionId, out previousExecutionContext, ignoreDestroyedContext: false); + ConcurrentBag bag = contexts.GetOrAdd(sessionId, _ => new ConcurrentBag()); + bag.Add(newExecutionContext); + return hasExisting; + } + + public void DestroyContext(SessionId sessionId, int id) + { + if (!contexts.TryGetValue(sessionId, out ConcurrentBag contextBag)) + return; + foreach (ExecutionContext context in contextBag.Where(x => x.Id == id).ToList()) + context.Destroyed = true; + } + + public void ClearContexts(SessionId sessionId) + { + if (!contexts.TryGetValue(sessionId, out ConcurrentBag contextBag)) + return; + foreach (ExecutionContext context in contextBag) + context.Destroyed = true; + } + + public bool ContainsKey(SessionId sessionId) => contexts.ContainsKey(sessionId); + } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FirefoxMonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FirefoxMonoProxy.cs index deb9761130b3c..f9e7cf6640e14 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FirefoxMonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FirefoxMonoProxy.cs @@ -23,7 +23,7 @@ public FirefoxMonoProxy(ILogger logger, string loggerId = null, ProxyOptions opt public FirefoxExecutionContext GetContextFixefox(SessionId sessionId) { - if (contexts.TryGetValue(sessionId, out ExecutionContext context)) + if (Contexts.TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context)) return context as FirefoxExecutionContext; throw new ArgumentException($"Invalid Session: \"{sessionId}\"", nameof(sessionId)); } @@ -254,7 +254,7 @@ protected override async Task AcceptEvent(SessionId sessionId, JObject arg } if (args["frame"] != null && args["type"] == null) { - OnDefaultContextUpdate(sessionId, new FirefoxExecutionContext(new MonoSDBHelper (this, logger, sessionId), 0, args["frame"]["consoleActor"].Value())); + Contexts.OnDefaultContextUpdate(sessionId, new FirefoxExecutionContext(new MonoSDBHelper (this, logger, sessionId), 0, args["frame"]["consoleActor"].Value())); return false; } @@ -317,7 +317,7 @@ await Task.WhenAll( } case "target-available-form": { - OnDefaultContextUpdate(sessionId, new FirefoxExecutionContext(new MonoSDBHelper (this, logger, sessionId), 0, args["target"]["consoleActor"].Value())); + Contexts.OnDefaultContextUpdate(sessionId, new FirefoxExecutionContext(new MonoSDBHelper (this, logger, sessionId), 0, args["target"]["consoleActor"].Value())); break; } } @@ -334,7 +334,7 @@ protected override async Task AcceptCommand(MessageId sessionId, JObject a { case "resume": { - if (!contexts.TryGetValue(sessionId, out ExecutionContext context)) + if (!Contexts.TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context)) return false; context.PausedOnWasm = false; if (context.CallStack == null) @@ -396,7 +396,7 @@ protected override async Task AcceptCommand(MessageId sessionId, JObject a } case "setBreakpoint": { - if (!contexts.TryGetValue(sessionId, out ExecutionContext context)) + if (!Contexts.TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context)) return false; var req = JObject.FromObject(new { @@ -436,7 +436,7 @@ protected override async Task AcceptCommand(MessageId sessionId, JObject a } case "removeBreakpoint": { - if (!contexts.TryGetValue(sessionId, out ExecutionContext context)) + if (!Contexts.TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context)) return false; Result resp = await SendCommand(sessionId, "", args, token); diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 15682c6dad1e3..781b4c97ff1f2 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -20,7 +20,7 @@ internal class MonoProxy : DevToolsProxy { private IList urlSymbolServerList; private HashSet sessions = new HashSet(); - protected Dictionary contexts = new Dictionary(); + internal ConcurrentExecutionContextDictionary Contexts = new (); public static HttpClient HttpClient => new HttpClient(); @@ -39,26 +39,11 @@ public MonoProxy(ILogger logger, IList urlSymbolServerList, int runtimeI _defaultPauseOnExceptions = PauseOnExceptionsKind.Unset; } - internal ExecutionContext GetContext(SessionId sessionId) - { - if (contexts.TryGetValue(sessionId, out ExecutionContext context)) - return context; - - throw new ArgumentException($"Invalid Session: \"{sessionId}\"", nameof(sessionId)); - } - - private bool UpdateContext(SessionId sessionId, ExecutionContext executionContext, out ExecutionContext previousExecutionContext) - { - bool previous = contexts.TryGetValue(sessionId, out previousExecutionContext); - contexts[sessionId] = executionContext; - return previous; - } - internal virtual Task SendMonoCommand(SessionId id, MonoCommands cmd, CancellationToken token) => SendCommand(id, "Runtime.evaluate", JObject.FromObject(cmd), token); internal void SendLog(SessionId sessionId, string message, CancellationToken token, string type = "warning") { - if (!contexts.TryGetValue(sessionId, out ExecutionContext context)) + if (!Contexts.TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context)) return; /*var o = JObject.FromObject(new { @@ -93,7 +78,7 @@ protected override async Task AcceptEvent(SessionId sessionId, JObject par case "Runtime.consoleAPICalled": { // Don't process events from sessions we aren't tracking - if (!contexts.TryGetValue(sessionId, out ExecutionContext context)) + if (!Contexts.TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context)) return false; string type = args["type"]?.ToString(); if (type == "debug") @@ -169,10 +154,22 @@ protected override async Task AcceptEvent(SessionId sessionId, JObject par return true; } + case "Runtime.executionContextDestroyed": + { + Contexts.DestroyContext(sessionId, args["executionContextId"].Value()); + return false; + } + + case "Runtime.executionContextsCleared": + { + Contexts.ClearContexts(sessionId); + return false; + } + case "Debugger.paused": { // Don't process events from sessions we aren't tracking - if (!contexts.ContainsKey(sessionId)) + if (!Contexts.ContainsKey(sessionId)) return false; if (args?["callFrames"]?.Value()?.Count == 0) @@ -254,7 +251,7 @@ protected virtual async Task SendResume(SessionId id, CancellationToken token) } protected async Task IsRuntimeAlreadyReadyAlready(SessionId sessionId, CancellationToken token) { - if (contexts.TryGetValue(sessionId, out ExecutionContext context) && context.IsRuntimeReady) + if (Contexts.TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context) && context.IsRuntimeReady) return true; Result res = await SendMonoCommand(sessionId, MonoCommands.IsRuntimeReady(RuntimeId), token); @@ -277,7 +274,7 @@ protected override async Task AcceptCommand(MessageId id, JObject parms, C if (id == SessionId.Null) await AttachToTarget(id, token); - if (!contexts.TryGetValue(id, out ExecutionContext context)) + if (!Contexts.TryGetCurrentExecutionContextValue(id, out ExecutionContext context)) { if (method == "Debugger.setPauseOnExceptions") { @@ -595,7 +592,7 @@ protected override async Task AcceptCommand(MessageId id, JObject parms, C private async Task ApplyUpdates(MessageId id, JObject args, CancellationToken token) { - var context = GetContext(id); + var context = Contexts.GetCurrentContext(id); string moduleGUID = args["moduleGUID"]?.Value(); string dmeta = args["dmeta"]?.Value(); string dil = args["dil"]?.Value(); @@ -664,7 +661,7 @@ internal async Task GetMethodLocation(MessageId id, JObject args, Cancel private async Task CallOnFunction(MessageId id, JObject args, CancellationToken token) { - var context = GetContext(id); + var context = Contexts.GetCurrentContext(id); if (!DotnetObjectId.TryParse(args["objectId"], out DotnetObjectId objectId)) { return false; } @@ -728,7 +725,7 @@ private async Task CallOnFunction(MessageId id, JObject args, Cancellation private async Task OnSetVariableValue(MessageId id, int scopeId, string varName, JToken varValue, CancellationToken token) { - ExecutionContext context = GetContext(id); + ExecutionContext context = Contexts.GetCurrentContext(id); Frame scope = context.CallStack.FirstOrDefault(s => s.Id == scopeId); if (scope == null) return false; @@ -748,7 +745,7 @@ private async Task OnSetVariableValue(MessageId id, int scopeId, string va internal async Task> RuntimeGetObjectMembers(SessionId id, DotnetObjectId objectId, JToken args, CancellationToken token, bool sortByAccessLevel = false) { - var context = GetContext(id); + var context = Contexts.GetCurrentContext(id); GetObjectCommandOptions getObjectOptions = GetObjectCommandOptions.WithProperties; if (args != null) { @@ -1072,7 +1069,7 @@ internal async Task OnReceiveDebuggerAgentEvent(SessionId sessionId, JObje if (!res.IsOk) return false; - ExecutionContext context = GetContext(sessionId); + ExecutionContext context = Contexts.GetCurrentContext(sessionId); byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value()); using var retDebuggerCmdReader = new MonoBinaryReader(newBytes); retDebuggerCmdReader.ReadBytes(11); //skip HEADER_LEN @@ -1148,7 +1145,7 @@ internal async Task OnReceiveDebuggerAgentEvent(SessionId sessionId, JObje internal async Task LoadSymbolsOnDemand(AssemblyInfo asm, int method_token, SessionId sessionId, CancellationToken token) { - ExecutionContext context = GetContext(sessionId); + ExecutionContext context = Contexts.GetCurrentContext(sessionId); if (urlSymbolServerList.Count == 0) return null; if (asm.TriedToLoadSymbolsOnDemand || !asm.CodeViewInformationAvailable) @@ -1189,29 +1186,17 @@ internal async Task LoadSymbolsOnDemand(AssemblyInfo asm, int method return null; } - protected void OnDefaultContextUpdate(SessionId sessionId, ExecutionContext context) - { - if (UpdateContext(sessionId, context, out ExecutionContext previousContext)) - { - foreach (KeyValuePair kvp in previousContext.BreakpointRequests) - { - context.BreakpointRequests[kvp.Key] = kvp.Value.Clone(); - } - context.PauseOnExceptions = previousContext.PauseOnExceptions; - } - } - protected async Task OnDefaultContext(SessionId sessionId, ExecutionContext context, CancellationToken token) { Log("verbose", "Default context created, clearing state and sending events"); - OnDefaultContextUpdate(sessionId, context); + Contexts.OnDefaultContextUpdate(sessionId, context); if (await IsRuntimeAlreadyReadyAlready(sessionId, token)) await RuntimeReady(sessionId, token); } protected async Task OnResume(MessageId msg_id, CancellationToken token) { - ExecutionContext context = GetContext(msg_id); + ExecutionContext context = Contexts.GetCurrentContext(msg_id); if (context.CallStack != null) { // Stopped on managed code @@ -1219,13 +1204,13 @@ protected async Task OnResume(MessageId msg_id, CancellationToken token) } //discard managed frames - GetContext(msg_id).ClearState(); + Contexts.GetCurrentContext(msg_id).ClearState(); } protected async Task Step(MessageId msgId, StepKind kind, CancellationToken token) { - ExecutionContext context = GetContext(msgId); + ExecutionContext context = Contexts.GetCurrentContext(msgId); if (context.CallStack == null) return false; @@ -1295,7 +1280,7 @@ private async Task OnAssemblyLoadedJSEvent(SessionId sessionId, JObject ev var assembly_data = Convert.FromBase64String(assembly_b64); var pdb_data = string.IsNullOrEmpty(pdb_b64) ? null : Convert.FromBase64String(pdb_b64); - var context = GetContext(sessionId); + var context = Contexts.GetCurrentContext(sessionId); foreach (var source in store.Add(sessionId, assembly_name, assembly_data, pdb_data, token)) { await OnSourceFileAdded(sessionId, source, context, token); @@ -1314,7 +1299,7 @@ private async Task OnSetEntrypointBreakpoint(SessionId sessionId, JObject args, { try { - ExecutionContext context = GetContext(sessionId); + ExecutionContext context = Contexts.GetCurrentContext(sessionId); var argsNew = JObject.FromObject(new { @@ -1380,7 +1365,7 @@ private async Task OnEvaluateOnCallFrame(MessageId msg_id, int scopeId, st { try { - ExecutionContext context = GetContext(msg_id); + ExecutionContext context = Contexts.GetCurrentContext(msg_id); if (context.CallStack == null) return false; @@ -1426,7 +1411,7 @@ internal async Task GetScopeProperties(SessionId msg_id, int scopeId, Ca { try { - ExecutionContext context = GetContext(msg_id); + ExecutionContext context = Contexts.GetCurrentContext(msg_id); Frame scope = context.CallStack.FirstOrDefault(s => s.Id == scopeId); if (scope == null) return Result.Err(JObject.FromObject(new { message = $"Could not find scope with id #{scopeId}" })); @@ -1457,7 +1442,7 @@ internal async Task GetScopeProperties(SessionId msg_id, int scopeId, Ca private async Task SetMonoBreakpoint(SessionId sessionId, string reqId, SourceLocation location, string condition, CancellationToken token) { - var context = GetContext(sessionId); + var context = Contexts.GetCurrentContext(sessionId); var bp = new Breakpoint(reqId, location, condition, BreakpointState.Pending); string asm_name = bp.Location.IlLocation.Method.Assembly.Name; int method_token = bp.Location.IlLocation.Method.Token; @@ -1487,14 +1472,23 @@ internal virtual async Task OnSourceFileAdded(SessionId sessionId, SourceFile so { if (req.TryResolve(source)) { - await SetBreakpoint(sessionId, context.store, req, true, false, token); + try + { + await SetBreakpoint(sessionId, context.store, req, true, false, token); + } + catch (DebuggerAgentException e) + { + //it's not a wasm page then the command throws an error + if (!e.Message.Contains("getDotnetRuntime is not defined")) + logger.LogDebug($"Unexpected error on OnSourceFileAdded {e}"); + } } } } internal virtual async Task LoadStore(SessionId sessionId, bool tryUseDebuggerProtocol, CancellationToken token) { - ExecutionContext context = GetContext(sessionId); + ExecutionContext context = Contexts.GetCurrentContext(sessionId); if (Interlocked.CompareExchange(ref context.store, new DebugStore(this, logger), null) != null) return await context.Source.Task; @@ -1555,7 +1549,7 @@ protected async Task RuntimeReady(SessionId sessionId, CancellationT { try { - ExecutionContext context = GetContext(sessionId); + ExecutionContext context = Contexts.GetCurrentContext(sessionId); if (Interlocked.CompareExchange(ref context.ready, new TaskCompletionSource(), null) != null) return await context.ready.Task; await context.SdbAgent.SendDebuggerAgentCommand(CmdEventRequest.ClearAllBreakpoints, null, token); @@ -1607,7 +1601,7 @@ private static IEnumerable> GetBPReqLocation private async Task ResetBreakpoint(SessionId msg_id, DebugStore store, MethodInfo method, CancellationToken token) { - ExecutionContext context = GetContext(msg_id); + ExecutionContext context = Contexts.GetCurrentContext(msg_id); foreach (var req in context.BreakpointRequests.Values) { if (req.Method != null) @@ -1632,7 +1626,7 @@ protected async Task RemoveBreakpoint(SessionId msg_id, JObject args, bool isEnC { string bpid = args?["breakpointId"]?.Value(); - ExecutionContext context = GetContext(msg_id); + ExecutionContext context = Contexts.GetCurrentContext(msg_id); if (!context.BreakpointRequests.TryGetValue(bpid, out BreakpointRequest breakpointRequest)) return; @@ -1654,7 +1648,7 @@ protected async Task RemoveBreakpoint(SessionId msg_id, JObject args, bool isEnC protected async Task SetBreakpoint(SessionId sessionId, DebugStore store, BreakpointRequest req, bool sendResolvedEvent, bool fromEnC, CancellationToken token) { - ExecutionContext context = GetContext(sessionId); + ExecutionContext context = Contexts.GetCurrentContext(sessionId); if ((!fromEnC && req.Locations.Any()) || (fromEnC && req.Locations.Any(bp => bp.State == BreakpointState.Active))) { if (!fromEnC) @@ -1737,7 +1731,7 @@ private static bool IsNestedMethod(DebugStore store, Frame scope, SourceLocation private async Task OnSetNextIP(MessageId sessionId, SourceLocation targetLocation, CancellationToken token) { DebugStore store = await RuntimeReady(sessionId, token); - ExecutionContext context = GetContext(sessionId); + ExecutionContext context = Contexts.GetCurrentContext(sessionId); Frame scope = context.CallStack.First(); SourceLocation foundLocation = DebugStore.FindBreakpointLocations(targetLocation, targetLocation, scope.Method.Info) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 04fd3d8eb6173..4368c4e8730e8 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -1581,7 +1581,7 @@ public async Task GetAssemblyFromType(int type_id, CancellationToken token) public JToken GetEvaluationResultProperties(string id) { - ExecutionContext context = proxy.GetContext(sessionId); + ExecutionContext context = proxy.Contexts.GetCurrentContext(sessionId); var resolver = new MemberReferenceResolver(proxy, context, sessionId, context.CallStack.First().Id, logger); var evaluationResult = resolver.TryGetEvaluationResult(id); return evaluationResult["value"]; @@ -1602,7 +1602,7 @@ public async Task GetValueFromDebuggerDisplayAttribute(DotnetObjectId do var stringId = getCAttrsRetReader.ReadInt32(); var dispAttrStr = await GetStringValue(stringId, token); - ExecutionContext context = proxy.GetContext(sessionId); + ExecutionContext context = proxy.Contexts.GetCurrentContext(sessionId); GetMembersResult members = await GetTypeMemberValues( dotnetObjectId, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.ForDebuggerDisplayAttribute, diff --git a/src/native/corehost/hostmisc/pal.windows.cpp b/src/native/corehost/hostmisc/pal.windows.cpp index bbb6fabb30b79..f5155933cba81 100644 --- a/src/native/corehost/hostmisc/pal.windows.cpp +++ b/src/native/corehost/hostmisc/pal.windows.cpp @@ -570,14 +570,18 @@ bool pal::getenv(const char_t* name, string_t* recv) auto err = GetLastError(); if (err != ERROR_ENVVAR_NOT_FOUND) { - trace::error(_X("Failed to read environment variable [%s], HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError())); + trace::warning(_X("Failed to read environment variable [%s], HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(err)); } return false; } auto buf = new char_t[length]; if (::GetEnvironmentVariableW(name, buf, length) == 0) { - trace::error(_X("Failed to read environment variable [%s], HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError())); + auto err = GetLastError(); + if (err != ERROR_ENVVAR_NOT_FOUND) + { + trace::warning(_X("Failed to read environment variable [%s], HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(err)); + } return false; } diff --git a/src/tasks/MonoTargetsTasks/ILStrip/AssemblyStripper/AssemblyStripper.cs b/src/tasks/MonoTargetsTasks/ILStrip/AssemblyStripper/AssemblyStripper.cs index f1835b1fa5ecc..c61cf219f7165 100644 --- a/src/tasks/MonoTargetsTasks/ILStrip/AssemblyStripper/AssemblyStripper.cs +++ b/src/tasks/MonoTargetsTasks/ILStrip/AssemblyStripper/AssemblyStripper.cs @@ -12,6 +12,18 @@ namespace AssemblyStripper { + class CustomAttrRowComparer : IComparer + { + public int Compare(object left, object right) + { + CustomAttributeRow row_left = (CustomAttributeRow)left; + CustomAttributeRow row_right = (CustomAttributeRow)right; + var leftParentCodedIdx = Utilities.CompressMetadataToken(CodedIndex.HasCustomAttribute, row_left.Parent); + var rightParentCodedIdx = Utilities.CompressMetadataToken(CodedIndex.HasCustomAttribute, row_right.Parent); + return leftParentCodedIdx.CompareTo(rightParentCodedIdx); + } + } + public class AssemblyStripper { AssemblyDefinition assembly; @@ -40,6 +52,7 @@ void Strip() PatchMethods(); PatchFields(); PatchResources(); + SortCustomAttributes(); Write(); } @@ -192,6 +205,20 @@ void PatchResources() } } + // Types that are trimmed away also have their respective rows removed from the + // custom attribute table. This introduces holes in their places, causing the table + // to no longer be sorted by Parent, corrupting the assembly. Runtimes assume ordering + // and may fail to locate the attributes set for a particular type. This step sorts + // the custom attribute table again. + void SortCustomAttributes() + { + CustomAttributeTable table = (CustomAttributeTable)stripped_tables[CustomAttributeTable.RId]; + if (table == null) + return; + + table.Rows.Sort(new CustomAttrRowComparer()); + } + void Write() { stripped.MetadataRoot.Accept(metadata_writer); @@ -209,7 +236,6 @@ public static void StripAssembly(string assemblyFile, string outputPath) { AssemblyDefinition assembly = AssemblyFactory.GetAssembly(assemblyFile); AssemblyStripper.StripAssembly(assembly, outputPath); - } } }