Skip to content

Commit 5bb6c79

Browse files
Fix cross-AppDomain TaskItem modifier cache regression (#13493)
1 parent 9b5c9da commit 5bb6c79

File tree

1 file changed

+11
-30
lines changed

1 file changed

+11
-30
lines changed

src/Framework/ItemSpecModifiers.cs

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
@@ -53,34 +53,15 @@ internal static class ItemSpecModifiers
5353
];
5454

5555
/// <summary>
56-
/// <para>
57-
/// Caches derivable item-spec modifier results for a single item spec.
58-
/// Stored on item instances (e.g., TaskItem, ProjectItemInstance.TaskItem)
59-
/// alongside the item spec, replacing the former <c>string _fullPath</c> field.
60-
/// </para>
61-
/// <para>
62-
/// Time-based modifiers (ModifiedTime, CreatedTime, AccessedTime) and RecursiveDir
63-
/// are intentionally excluded — time-based modifiers hit the file system and should
64-
/// not be cached, and RecursiveDir requires wildcard context that only the caller has.
65-
/// </para>
66-
/// <para>
67-
/// DefiningProject* modifiers are cached separately in a static shared cache
68-
/// (<see cref="s_definingProjectCache"/>) keyed by the defining project path,
69-
/// since many items share the same defining project.
70-
/// </para>
56+
/// Per-item cache for the FullPath modifier. Other derivable modifiers (RootDir,
57+
/// Filename, Extension, RelativeDir, Directory) are intentionally NOT cached here
58+
/// because TaskItem is a MarshalByRefObject on .NET Framework, and copying a
59+
/// multi-field struct cross-AppDomain causes allocation regression in VS.
7160
/// </summary>
7261
internal struct Cache
7362
{
7463
public string? FullPath;
75-
public string? RootDir;
76-
public string? Filename;
77-
public string? Extension;
78-
public string? RelativeDir;
79-
public string? Directory;
80-
81-
/// <summary>
82-
/// Clears all cached values. Called when the item spec changes.
83-
/// </summary>
64+
8465
public void Clear()
8566
=> this = default;
8667
}
@@ -420,19 +401,19 @@ public static string GetItemSpecModifier(
420401
return cache.FullPath ??= ComputeFullPath(currentDirectory, itemSpec);
421402

422403
case ItemSpecModifierKind.RootDir:
423-
return cache.RootDir ??= ComputeRootDir(cache.FullPath ??= ComputeFullPath(currentDirectory, itemSpec));
404+
return ComputeRootDir(cache.FullPath ??= ComputeFullPath(currentDirectory, itemSpec));
424405

425406
case ItemSpecModifierKind.Filename:
426-
return cache.Filename ??= ComputeFilename(itemSpec);
407+
return ComputeFilename(itemSpec);
427408

428409
case ItemSpecModifierKind.Extension:
429-
return cache.Extension ??= ComputeExtension(itemSpec);
410+
return ComputeExtension(itemSpec);
430411

431412
case ItemSpecModifierKind.RelativeDir:
432-
return cache.RelativeDir ??= ComputeRelativeDir(itemSpec);
413+
return ComputeRelativeDir(itemSpec);
433414

434415
case ItemSpecModifierKind.Directory:
435-
return cache.Directory ??= ComputeDirectory(cache.FullPath ??= ComputeFullPath(currentDirectory, itemSpec));
416+
return ComputeDirectory(cache.FullPath ??= ComputeFullPath(currentDirectory, itemSpec));
436417

437418
case ItemSpecModifierKind.RecursiveDir:
438419
return string.Empty;

0 commit comments

Comments
 (0)