8
8
using System . Diagnostics ;
9
9
using System . Linq ;
10
10
using System . Reflection ;
11
+ using System . Reflection . Metadata ;
11
12
using System . Runtime . InteropServices ;
12
13
using System . Threading . Tasks ;
13
14
using Microsoft . AspNetCore . Components . HotReload ;
@@ -25,6 +26,7 @@ public static class WebAssemblyHotReload
25
26
{
26
27
private static readonly ConcurrentDictionary < Guid , List < ( byte [ ] metadataDelta , byte [ ] ilDelta ) > > _deltas = new ( ) ;
27
28
private static readonly ConcurrentDictionary < Assembly , Assembly > _appliedAssemblies = new ( ) ;
29
+ private static ( List < Action < Type [ ] ? > > BeforeUpdates , List < Action < Type [ ] ? > > AfterUpdates ) ? _handlerActions ;
28
30
29
31
static WebAssemblyHotReload ( )
30
32
{
@@ -50,7 +52,7 @@ static WebAssemblyHotReload()
50
52
// A delta for this specific Module exists and we haven't called ApplyUpdate on this instance of Assembly as yet.
51
53
foreach ( var ( metadataDelta , ilDelta ) in CollectionsMarshal . AsSpan ( result ) )
52
54
{
53
- System . Reflection . Metadata . AssemblyExtensions . ApplyUpdate ( loadedAssembly , metadataDelta , ilDelta , ReadOnlySpan < byte > . Empty ) ;
55
+ ApplyUpdate ( loadedAssembly , metadataDelta , ilDelta ) ;
54
56
}
55
57
}
56
58
} ;
@@ -80,7 +82,7 @@ public static void ApplyHotReloadDelta(string moduleIdString, byte[] metadataDel
80
82
81
83
if ( assembly is not null )
82
84
{
83
- System . Reflection . Metadata . AssemblyExtensions . ApplyUpdate ( assembly , metadataDelta , ilDeta , ReadOnlySpan < byte > . Empty ) ;
85
+ ApplyUpdate ( assembly , metadataDelta , ilDeta ) ;
84
86
_appliedAssemblies . TryAdd ( assembly , assembly ) ;
85
87
}
86
88
@@ -95,9 +97,87 @@ public static void ApplyHotReloadDelta(string moduleIdString, byte[] metadataDel
95
97
( metadataDelta , ilDeta )
96
98
} ;
97
99
}
100
+ }
101
+
102
+ private static void ApplyUpdate ( Assembly assembly , byte [ ] metadataDelta , byte [ ] ilDeta )
103
+ {
104
+ _handlerActions ??= GetMetadataUpdateHandlerActions ( ) ;
105
+ var ( beforeUpdates , afterUpdates ) = _handlerActions . Value ;
106
+
107
+ beforeUpdates . ForEach ( a => a ( null ) ) ;
108
+ System . Reflection . Metadata . AssemblyExtensions . ApplyUpdate ( assembly , metadataDelta , ilDeta , ReadOnlySpan < byte > . Empty ) ;
109
+ afterUpdates . ForEach ( a => a ( null ) ) ;
110
+ }
111
+
112
+ private static ( List < Action < Type [ ] ? > > BeforeUpdates , List < Action < Type [ ] ? > > AfterUpdates ) GetMetadataUpdateHandlerActions ( )
113
+ {
114
+ var beforeUpdates = new List < Action < Type [ ] ? > > ( ) ;
115
+ var afterUpdates = new List < Action < Type [ ] ? > > ( ) ;
116
+
117
+ foreach ( var assembly in AppDomain . CurrentDomain . GetAssemblies ( ) )
118
+ {
119
+ foreach ( var attribute in assembly . GetCustomAttributes < MetadataUpdateHandlerAttribute > ( ) )
120
+ {
121
+ var handlerType = attribute . HandlerType ;
122
+
123
+ var methodFound = false ;
124
+ if ( GetUpdateMethod ( handlerType , "BeforeUpdate" ) is MethodInfo beforeUpdate )
125
+ {
126
+ beforeUpdates . Add ( CreateAction ( beforeUpdate ) ) ;
127
+ methodFound = true ;
128
+ }
129
+
130
+ if ( GetUpdateMethod ( handlerType , "AfterUpdate" ) is MethodInfo afterUpdate )
131
+ {
132
+ afterUpdates . Add ( CreateAction ( afterUpdate ) ) ;
133
+ methodFound = true ;
134
+ }
135
+
136
+ if ( ! methodFound )
137
+ {
138
+ Debug . WriteLine ( $ "No BeforeUpdate or AfterUpdate method found on '{ handlerType } '.") ;
139
+ }
140
+
141
+ static Action < Type [ ] ? > CreateAction ( MethodInfo update )
142
+ {
143
+ var action = update . CreateDelegate < Action < Type [ ] ? > > ( ) ;
144
+ return types =>
145
+ {
146
+ try
147
+ {
148
+ action ( types ) ;
149
+ }
150
+ catch ( Exception ex )
151
+ {
152
+ Debug . WriteLine ( $ "Exception from '{ action } ': { ex } ") ;
153
+ }
154
+ } ;
155
+ }
156
+ }
157
+ }
158
+
159
+ static MethodInfo ? GetUpdateMethod ( Type handlerType , string name )
160
+ {
161
+ var bindingFlags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Static ;
162
+ var updateMethod = handlerType . GetMethod ( name , bindingFlags , new [ ] { typeof ( Type [ ] ) } ) ;
163
+ if ( updateMethod is not null )
164
+ {
165
+ return updateMethod ;
166
+ }
167
+
168
+ var methods = handlerType . GetMethods ( bindingFlags )
169
+ . Where ( m => m . Name == name )
170
+ . ToArray ( ) ;
171
+
172
+ if ( methods . Length > 0 )
173
+ {
174
+ Debug . WriteLine ( $ "MetadataUpdateHandler type '{ handlerType } ' has a method named '{ name } ' that does not match the required signature.") ;
175
+ }
176
+
177
+ return null ;
178
+ }
98
179
99
- // Remove this once there's a runtime API to subscribe to.
100
- typeof ( ComponentBase ) . Assembly . GetType ( "Microsoft.AspNetCore.Components.HotReload.HotReloadManager" ) ! . GetMethod ( "DeltaApplied" , BindingFlags . Public | BindingFlags . Static ) ! . Invoke ( null , null ) ;
180
+ return ( beforeUpdates , afterUpdates ) ;
101
181
}
102
182
}
103
183
}
0 commit comments