Conversation
| this.resolver = resolver; | ||
| } | ||
|
|
||
| internal LazyMetadataWrapper(ImmutableDictionary<string, object?> metadata, Direction direction, Resolver resolver) |
There was a problem hiding this comment.
Why is this constructor still needed?
|
|
||
| // Update our metadata dictionary with the substitution to avoid | ||
| // the translation costs next time. | ||
| this.underlyingMetadata = this.underlyingMetadata.SetItem(key, value); |
There was a problem hiding this comment.
Why is this no longer applicable?
There was a problem hiding this comment.
Who are you asking? I see a code comment below that seems to justify it.
There was a problem hiding this comment.
It added this comment after I asked this. :) I could not get it to resolve the question.
0ed26d5 to
a906f7a
Compare
| serializedMetadata = new LazyMetadataWrapper(metadata.ToImmutableDictionary(), LazyMetadataWrapper.Direction.ToSubstitutedValue, this.Resolver); | ||
| // Both Dictionary and ImmutableDictionary implement IDictionary, | ||
| // which Dictionary's copy constructor accepts. | ||
| var metadataCopy = metadata is IDictionary<string, object?> dict |
There was a problem hiding this comment.
Is this guaranateed to be Dictionary<string, object?>? This also does not copy the metadata on the second path.
381a867 to
700ed7e
Compare
| private static readonly object BoxedTrue = true; | ||
| private static readonly object BoxedFalse = false; | ||
|
|
||
| private static readonly IReadOnlyDictionary<string, object?> EmptyMetadata = new Dictionary<string, object?>(); |
There was a problem hiding this comment.
Make absolutely sure it's read-only and empty, and even avoid allocating the dictionary.
| private static readonly IReadOnlyDictionary<string, object?> EmptyMetadata = new Dictionary<string, object?>(); | |
| private static readonly IReadOnlyDictionary<string, object?> EmptyMetadata = ImmutableDictionary<string, object?>.Empty; |
| // Dictionary has no constructor accepting IReadOnlyDictionary, so enumerate manually. | ||
| var metadataCopy = new Dictionary<string, object?>(metadata.Count); | ||
| foreach (var entry in metadata) | ||
| { | ||
| metadataCopy.Add(entry.Key, entry.Value); | ||
| } |
There was a problem hiding this comment.
No constructor, perhaps. But you can use this extension method, can't you?
| // Dictionary has no constructor accepting IReadOnlyDictionary, so enumerate manually. | |
| var metadataCopy = new Dictionary<string, object?>(metadata.Count); | |
| foreach (var entry in metadata) | |
| { | |
| metadataCopy.Add(entry.Key, entry.Value); | |
| } | |
| Dictionary<string, object?> metadataCopy = metadata.ToDictionary(); |
| // Dictionary has no constructor accepting IReadOnlyDictionary, so enumerate manually. | ||
| var dict = new Dictionary<string, object?>(newMetadata.Count); | ||
| foreach (var kvp in newMetadata) | ||
| { | ||
| dict.Add(kvp.Key, kvp.Value); | ||
| } | ||
|
|
||
| return new LazyMetadataWrapper(dict, oldVersion.direction, this.resolver); |
There was a problem hiding this comment.
| // Dictionary has no constructor accepting IReadOnlyDictionary, so enumerate manually. | |
| var dict = new Dictionary<string, object?>(newMetadata.Count); | |
| foreach (var kvp in newMetadata) | |
| { | |
| dict.Add(kvp.Key, kvp.Value); | |
| } | |
| return new LazyMetadataWrapper(dict, oldVersion.direction, this.resolver); | |
| return new LazyMetadataWrapper(newMetadata.ToDictionary(), oldVersion.direction, this.resolver); |
|
|
||
| // Update our metadata dictionary with the substitution to avoid | ||
| // the translation costs next time. | ||
| this.underlyingMetadata = this.underlyingMetadata.SetItem(key, value); |
There was a problem hiding this comment.
Who are you asking? I see a code comment below that seems to justify it.
| /// These involve CLR metadata resolution or reflection emit, so they are worth caching. | ||
| /// Allocated on first access to avoid paying the cost during composition cache loading. | ||
| /// </summary> | ||
| private sealed class ResolvedMembers |
There was a problem hiding this comment.
Wouldn't a nullable struct be even better?
| /// <summary> | ||
| /// Caches for resolved reflection objects and the lazy factory delegate. | ||
| /// These involve CLR metadata resolution or reflection emit, so they are worth caching. | ||
| /// Allocated on first access to avoid paying the cost during composition cache loading. | ||
| /// </summary> |
There was a problem hiding this comment.
We were already caching these. Why create a nested type? Was it to shrink the size of RuntimeImport objects? Particularly ones that never get accessed?
There was a problem hiding this comment.
Yes to reduce the size of RuntimeImport so we only allocate when the data is accessed.
700ed7e to
c270888
Compare
ImmutableDictionary allocates SortedInt32KeyNode tree nodes on construction and a new dictionary on every SetItem call. Dictionary avoids both. The old code wrote substituted values back via ImmutableDictionary.SetItem, which was structurally thread-safe (new dict + atomic reference swap). With Dictionary, concurrent writes would corrupt internal state. The write-back is removed; substitution cost without caching is negligible since TypeRef.Resolve() already caches its result and Enum.ToObject is trivial.
Build metadata into Dictionary directly instead of ImmutableDictionary.Builder. Return ImmutableDictionary.Empty singleton for empty import metadata (majority of imports have no metadata), avoiding a LazyMetadataWrapper allocation. Add ToDictionary extension for IReadOnlyDictionary since the BCL Dictionary constructor does not accept IReadOnlyDictionary on netstandard2.0.
Remove 5 cached fields from RuntimeImport that read already-cached TypeRef.ResolvedType (IsLazy, ImportingSiteElementType, MetadataType, isMetadataTypeInitialized, NullableBool). Retain ImportingMember and ImportingParameter caches since they involve CLR metadata resolution. Move lazy factory delegate creation into a static ConcurrentDictionary cache in LazyServices, keyed by (exportType, metadataViewType). This shares delegates across all imports with the same Lazy<T> or Lazy<T, TMetadata> signature, eliminating per-import MakeGenericMethod and CreateDelegate calls.
Avoid per-call boxing for CreationPolicy enum (3 values) and small integers (0, 1, -1) that appear frequently in metadata.
Avoid Array.CreateInstance + Array.SetValue reflection overhead for object[], string[], and Type[] arrays in metadata deserialization.
c270888 to
d44ac2b
Compare
No description provided.