|
39 | 39 | using Java.Interop.Tools.Diagnostics;
|
40 | 40 |
|
41 | 41 | using Mono.Cecil;
|
| 42 | +using Mono.Cecil.Cil; |
42 | 43 |
|
43 | 44 | namespace Java.Interop.Tools.Cecil {
|
44 | 45 |
|
@@ -149,59 +150,100 @@ public bool AddToCache (AssemblyDefinition assembly)
|
149 | 150 |
|
150 | 151 | protected virtual AssemblyDefinition ReadAssembly (string file)
|
151 | 152 | {
|
152 |
| - bool haveDebugSymbols = loadDebugSymbols && |
153 |
| - (File.Exists (Path.ChangeExtension (file, ".pdb")) || |
154 |
| - File.Exists (file + ".mdb")); |
155 | 153 | var reader_parameters = new ReaderParameters () {
|
156 | 154 | ApplyWindowsRuntimeProjections = loadReaderParameters.ApplyWindowsRuntimeProjections,
|
157 | 155 | AssemblyResolver = this,
|
158 | 156 | MetadataImporterProvider = loadReaderParameters.MetadataImporterProvider,
|
159 | 157 | InMemory = loadReaderParameters.InMemory,
|
160 | 158 | MetadataResolver = loadReaderParameters.MetadataResolver,
|
161 | 159 | ReadingMode = loadReaderParameters.ReadingMode,
|
162 |
| - ReadSymbols = haveDebugSymbols, |
163 | 160 | ReadWrite = loadReaderParameters.ReadWrite,
|
164 | 161 | ReflectionImporterProvider = loadReaderParameters.ReflectionImporterProvider,
|
165 | 162 | SymbolReaderProvider = loadReaderParameters.SymbolReaderProvider,
|
166 |
| - SymbolStream = loadReaderParameters.SymbolStream, |
167 | 163 | };
|
168 | 164 | try {
|
169 | 165 | return LoadFromMemoryMappedFile (file, reader_parameters);
|
170 | 166 | } catch (Exception ex) {
|
171 | 167 | logger (
|
172 | 168 | TraceLevel.Verbose,
|
173 | 169 | $"Failed to read '{file}' with debugging symbols. Retrying to load it without it. Error details are logged below.");
|
174 |
| - logger (TraceLevel.Verbose, $"{ex.ToString ()}"); |
| 170 | + logger (TraceLevel.Verbose, ex.ToString ()); |
175 | 171 | reader_parameters.ReadSymbols = false;
|
176 | 172 | return LoadFromMemoryMappedFile (file, reader_parameters);
|
| 173 | + } finally { |
| 174 | + reader_parameters.SymbolStream?.Dispose (); |
177 | 175 | }
|
178 | 176 | }
|
179 | 177 |
|
180 | 178 | AssemblyDefinition LoadFromMemoryMappedFile (string file, ReaderParameters options)
|
181 | 179 | {
|
182 | 180 | // We can't use MemoryMappedFile when ReadWrite is true
|
183 | 181 | if (options.ReadWrite) {
|
| 182 | + if (loadDebugSymbols) { |
| 183 | + LoadSymbols (file, options, File.OpenRead); |
| 184 | + } |
184 | 185 | return AssemblyDefinition.ReadAssembly (file, options);
|
185 | 186 | }
|
186 | 187 |
|
187 |
| - MemoryMappedViewStream? viewStream = null; |
| 188 | + // We know the capacity for disposables |
| 189 | + var disposables = new List<IDisposable> ( |
| 190 | + (1 + (loadDebugSymbols ? 1 : 0)) * OpenMemoryMappedViewStream_disposables_Add_calls); |
188 | 191 | try {
|
189 |
| - // Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict |
190 |
| - using var fileStream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false); |
191 |
| - using var mappedFile = MemoryMappedFile.CreateFromFile ( |
192 |
| - fileStream, null, fileStream.Length, MemoryMappedFileAccess.Read, HandleInheritability.None, true); |
193 |
| - viewStream = mappedFile.CreateViewStream (0, 0, MemoryMappedFileAccess.Read); |
| 192 | + if (loadDebugSymbols) { |
| 193 | + LoadSymbols (file, options, f => OpenMemoryMappedViewStream (f, disposables)); |
| 194 | + } |
194 | 195 |
|
| 196 | + var viewStream = OpenMemoryMappedViewStream (file, disposables); |
195 | 197 | AssemblyDefinition result = ModuleDefinition.ReadModule (viewStream, options).Assembly;
|
196 |
| - viewStreams.Add (viewStream); |
197 |
| - |
198 |
| - // We transferred the ownership of the viewStream to the collection. |
199 |
| - viewStream = null; |
200 | 198 |
|
| 199 | + // Transfer ownership to `viewStreams` collection |
| 200 | + viewStreams.Add (viewStream); |
| 201 | + disposables.Remove (viewStream); |
| 202 | + if (options.SymbolStream is MemoryMappedViewStream m) { |
| 203 | + viewStreams.Add (m); |
| 204 | + disposables.Remove (m); |
| 205 | + options.SymbolStream = null; // Prevents caller from disposing |
| 206 | + } |
201 | 207 | return result;
|
202 | 208 | } finally {
|
203 |
| - viewStream?.Dispose (); |
| 209 | + for (int i = disposables.Count - 1; i >= 0; i--) { |
| 210 | + disposables [i].Dispose (); |
| 211 | + } |
| 212 | + } |
| 213 | + } |
| 214 | + |
| 215 | + static void LoadSymbols (string assemblyPath, ReaderParameters options, Func<string, Stream> getStream) |
| 216 | + { |
| 217 | + var symbolStream = options.SymbolStream; |
| 218 | + if (symbolStream == null) { |
| 219 | + var symbolPath = Path.ChangeExtension (assemblyPath, ".pdb"); |
| 220 | + if (File.Exists (symbolPath)) { |
| 221 | + symbolStream = getStream (symbolPath); |
| 222 | + } |
204 | 223 | }
|
| 224 | + options.ReadSymbols = symbolStream != null; |
| 225 | + options.SymbolStream = symbolStream; |
| 226 | + } |
| 227 | + |
| 228 | + /// <summary> |
| 229 | + /// Number of times OpenMemoryMappedViewStream() calls disposables.Add() |
| 230 | + /// </summary> |
| 231 | + const int OpenMemoryMappedViewStream_disposables_Add_calls = 3; |
| 232 | + |
| 233 | + static MemoryMappedViewStream OpenMemoryMappedViewStream (string file, List<IDisposable> disposables) |
| 234 | + { |
| 235 | + // Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict |
| 236 | + var fileStream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: false); |
| 237 | + disposables.Add (fileStream); |
| 238 | + |
| 239 | + var mappedFile = MemoryMappedFile.CreateFromFile ( |
| 240 | + fileStream, null, fileStream.Length, MemoryMappedFileAccess.Read, HandleInheritability.None, leaveOpen: true); |
| 241 | + disposables.Add (mappedFile); |
| 242 | + |
| 243 | + var viewStream = mappedFile.CreateViewStream (0, 0, MemoryMappedFileAccess.Read); |
| 244 | + disposables.Add (viewStream); |
| 245 | + |
| 246 | + return viewStream; |
205 | 247 | }
|
206 | 248 |
|
207 | 249 | public AssemblyDefinition GetAssembly (string fileName)
|
|
0 commit comments