mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
Resolving one assembly resolves its whole reference closure, and every reference in it asked the same framework directories the same questions. The worst of it was the scan for the closest version folder of a shared framework: a directory listing plus a recursive file search, repeated per reference and per runtime pack - 42 scans for two distinct answers when decompiling ICSharpCode.ILSpyX.dll. The scan result is only safe to keep for a bounded time: a runtime can be installed or removed while ILSpy runs, and reloading an assembly list has to see that. So it is kept for the length of an explicitly opened scope, which the type system opens around the closure it resolves and closes again afterwards; outside a scope the file system is read as before. The scope owns what was read, so two of them on one resolver do not stack - the first to end takes it, and the other reads the file system again. BeginSnapshot is on IAssemblyResolver rather than an interface of its own: it is core functionality of a resolver, and one implementation is not an abstraction. This breaks the interface for implementors outside this repository, who opt out by returning null - which is what the three resolvers here that hold nothing do. The remaining probes cost nothing to fix: the preferred runtime pack was listed among the defaults it already belongs to, so its directory was scanned twice for every reference that is not in it, and one package folder was probed once per assembly the package contains. Measured over 27 references with a fresh resolver each time: 3.3 ms per assembly before, 3.1 ms without a scope, 1.1 ms with one. Assisted-by: Claude:claude-opus-5:Claude Code