-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Contributes to dotnet/xamarin#13 and dotnet/aspnetcore#5456
Overview
The goal of Mono method body replacement is to improve the inner dev loop experience for customers using Xamarin to create mobile apps and those using Blazor to create WebAssembly projects.
Method body replacement is a subset of a more general hot reload experience with a restricted subset of allowed edits. Non-supported ("rude") edits will generally be rejected by the tooling outside the runtime, however the runtime
itself should be transactional in that a change is either committed by the runtime in total or else it is rejected and the runtime state is unchanged.
The changes are delivered to the runtime using the EnC "dmeta" and "DIL" (and "DPDB") delta files. If the changes are accepted, then future invocations of methods affected by the delta will use the new method bodies. Currently executing versions of the methods will use the previous versions.
Multiple versions of changes can be applied in succession. Once a change is applied, it cannot be unapplied (but of course a new change can be used to undo the previous change). For collectible ALCs unloading the assembly will unload all its changes.
Method body replacement will only be supported with the Mono interpreter with inlining turned off. The DOTNET_MODIFIABLE_ASSEMBLIES
environment variable must be set to the (case-insensitive) value debug
. The assemblies that are eligible for modifications must be compiled with the /debug
option.
Method body replacement will not interfere with debugging.
Metadata deltas will be injected either using the debugger or using a managed API. If the debugger is not involved, the runtime will pause the executing user threads at critical points during the update.
End to end scenarios
- CLI (
dotnet-watch
)- Blazor WebAssembly
-
MAUIpostponed to .net7
- Visual Studio "Apply Changes" during debugging
- Android
- iOS
-
WebAssemblypostponed to .net7
Tasks
Priority 0
-
DONE PR #45612 Put the metadata update work-in-progress under a compile-time feature flag and merge to dotnet/runtime master.
-
Fix potential race when one thread is doing a metadata update and another is transforming some method. May need some kind of rwlock. (Fixed as part of [Mono] Initial metadata update support #45612 - we now use an explicit call to expose the update to a thread (at entry to the interpreter transformer)
-
Support preemptive suspend, or elsePR [mono] Set thread suspend default in mono.proj; default to hybrid #46873 use hybrid suspend by default, and communicate to downstream projects that MBR requires hybrid/coop suspend (mostly affects XM and XI - single-threaded wasm is ok) -
Write up a list of edits that we expect to support and coordinate with Roslyn team to implement a delta generator that rejects unsupported edits. Summary ecma335 gist
-
Measure perf impact of metadata update infrastructure on the interpreter. (That is, if there are no updates coming, what is the overhead?) [mono] Enable MBR by default if interpreter is enabled #46842 (comment)
-
set compile time feature flag to on by default; put functionality under a runtime feature flag
- Enable on Android, and Browser [mono] Compile runtime with MBR support by default on Android and Browser #47785
- Enable on iOS, tvOS and MacCatalyst [mbr] Turn on hot reload for iOS, tvOS and MacCatalyst #50458 - samples [mbr] Add Apple sample #50740
- Enable on Desktop - [mono] Always build with hot reload support (in a component) #54124
-
Investigate workload dev/production packs - turn off MBR in productionWe're using the mono runtime components infrastructure for this
-
Debugger
- Apply metadata updates using debugger-libs ([mono] Add debugger-agent command to apply hot reload update #48458).
- debugger-libs PR Add Module.ApplyChanges mono/debugger-libs#342
- mono/mono (Mono.Debugger.Soft) PR [sdb] Add Module.ApplyChanges mono/mono#20889
- dotnet/runtime (debugger agent) PR [sdb] Add MODULE_APPLY_CHANGES command for hot reload #49043
- Notify debugger client about updated sequence points, breakpoints, etc (depends on runtime dpdb consumption) [mono][debugger] Support debug after hot-reload #55220
- Add support for consuming "dpdb" debugger file deltas
- When applying updates through the debugger, ensure debugger engine and debugger are in sync after the delta. [mono][debugger] Support debug after hot-reload #55220
-
If there is a breakpoint in an old version of a method we need to send back info identifying the method version, not just a token so that the debugger frontend can display a view of the old code. This may require debugger-libs and soft debugger protocol workWe invalidate old breakpoints and set them in the updated method body.
- Apply hot reload changes through WebAssembly debugger [mono][debugger] Support debug after hot-reload #55220
- Apply metadata updates using debugger-libs ([mono] Add debugger-agent command to apply hot reload update #48458).
-
Propose and implement required runtime APIs (Coordinate with Support Dev inner loop improvements in CoreCLR VM #45629 so that ideally Mono and CoreCLR implement the same API.)
- (P0) Issue #45689 Public (or at least well-known) managed API for applying updates. Mono PR [mono] Implement public hot reload API #48380
- (P0) Issue #47274 Startup runtime with hot reload enabled if
DOTNET_MODIFIABLE_ASSEMBLIES=debug
is set. PR [mono][mbr] Implement DOTNET_MODIFIABLE_ASSEMBLIES support #49507
-
Properly detect cases we don't support, backout metadata updates and raise a single well-defined managed exception that leaves the runtime in a functional state.No need to roll back, update failures are fatal and don't need to be rolled back. -
set up testing infrastructure and run CI
- publish external testing artifact that our CI will depend on ⟶ https://github.com/dotnet/hotreload-utils
- Public hotreload-utils to DARC and consume in dotnet/runtime
- msbuild task for working with pre-canned deltas
- Add testing infrastructure to
System.Runtime.Loader.Tests
testsuite [System.Runtime.Loader] Add hot reload test infrastructure #51144 - Enable testing on WebAssembly
-
Enable testing on Android, iOSnet7 - Enable testing on Desktop mono
- convert samples (
src/mono/netcore/samples/mbr
) into functional tests
-
Managed API to return hot reload supported edits internal runtime API to return hot reload capabilities #50111
Priority 1
-
Investigate MonoImage refcount mistake - fix the shutdown hack ((The shutdown code is dead on netcore).exe_image
) -
Support more edits
- modify bodies of property getters and setters [mbr] Add support for editing property getters/setters #51011
If
GetValueToDisplay
is defined as a property instead of a method, then changes to its code can no longer be applied (error:"EnC: we do not support patching of existing table cols."
). - [hot_reload] Mono should ignore Param table row updates #56574
- modify bodies of property getters and setters [mbr] Add support for editing property getters/setters #51011
-
(P1) Add a notification mechanism for EnC and HotReload #49361 Managed events that fire when a delta is applied
-
Use MetadataUpdateHandlerAttribute to clear Mono's reflection cache Use MetadataUpdateHandlerAttribute to clear Mono's reflection cache #50978net7 -
[mono] block debugger attach after managed
ApplyUpdate
is called #55228 PR: [mono] Disable unsupported apply update scenarios #55698 -
Hot Reload Manager support may need Add support for host startup hooks to Mono #47462 - add support for host startup hooks to monoMAUI and Blazor WebAssembly will inject the hot reload manager using their own mechanisms. -
Feature flag API to enable IL trimming of hot reload support code Consider a hot reload feature switch for linking #51159
-
Block applying changes to assemblies that have AOT images loaded.There's no supported hybrid interp/AOT scenarios.
Future (.net7 +)
Moved to #57365
Bugs
- [mono] ApplyDelta results in BadImageFormatException #49227 -> PR [metadata_update] Fix bounds check error #49328
- This was previously reported as:
Most changes to a
protected override void BuildRenderTree (Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder);
method on a component class ends up producing a kind of change that the runtime can't apply and crashes with errors like"BadImageFormatException: Image with invalid assemblyref token 0000000f".
- This was previously reported as:
- PR [mbr] Return -1 if relative index is not in given generation #49795
- This was previously reported as:
Changing logic in basic ways usually does not work fine. For example, changing
return "my string";
tovar other = "test"; return "my string" + other;
leads to a crash with error"TypeLoadException: Could not resolve type with token 0100004c from typeref".
- and as Blazor Wasm rendering incorrect components (ie multiple
<Counter/>
components would render as another previously added component)
- This was previously reported as:
- WebAssembly hot reload reverts to old version of a type #50190 - editing type 'B' (referenced from A) then editing 'A' causes changes to 'B' to be reverted - PR [mbr] Fix encmap boundary and method RVA lookup. #50248 (also backported to 6.0 preview3 branch - [release/6.0-preview3] [mbr] Fix encmap boundary and method RVA lookup. #50254)
- [metadata-update] Make a copy of the DIL when applying updates #52344 - A GC after applying an update can collect the updated method bodies (!!). This manifests in a variety of ways but is usually a
BadImageFormatException
thrown from the caller of an updated method. - [mono] Allow CustomAttribute table modifications if Parent stays unchanged #55097