diff --git a/Assets/Tests/Editor/HotReload/HotReloadCompilationAssembliesTests.cs b/Assets/Tests/Editor/HotReload/HotReloadCompilationAssembliesTests.cs
new file mode 100644
index 000000000..753ae17ec
--- /dev/null
+++ b/Assets/Tests/Editor/HotReload/HotReloadCompilationAssembliesTests.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+using NUnit.Framework;
+
+using UnityEditor;
+using UnityEditor.Compilation;
+
+using io.github.hatayama.UnityCliLoop.FirstPartyTools;
+
+using UnityCompilationAssembly = UnityEditor.Compilation.Assembly;
+
+namespace io.github.hatayama.UnityCliLoop.Tests.Editor.HotReload
+{
+ ///
+ /// EditMode coverage for the per-domain compilation assembly list: one shared memo, dropped by the
+ /// Unity events that can change the list.
+ ///
+ public sealed class HotReloadCompilationAssembliesTests
+ {
+ // Why string names: the handlers are private to the production class; a rename fails these tests
+ // instead of silently passing them.
+ private const string CompilationStartedHandlerName = "OnCompilationStarted";
+ private const string ProjectChangedHandlerName = "OnProjectChanged";
+
+ ///
+ /// Verifies that the static constructor subscribed the memo's invalidation to compile start and
+ /// project change.
+ ///
+ [Test]
+ public void StaticConstructor_SubscribesInvalidationToCompilationStartedAndProjectChanged()
+ {
+ HotReloadCompilationAssemblies.Current();
+
+ Assert.That(
+ StaticEventHasHandler(typeof(CompilationPipeline), CompilationStartedHandlerName),
+ Is.True,
+ "OnCompilationStarted must be subscribed on CompilationPipeline.");
+ Assert.That(
+ StaticEventHasHandler(typeof(EditorApplication), ProjectChangedHandlerName),
+ Is.True,
+ "OnProjectChanged must be subscribed on EditorApplication.");
+ }
+
+ ///
+ /// Verifies that two calls share one kept list instead of asking Unity each time.
+ ///
+ [Test]
+ public void Current_CalledTwice_ReturnsSameInstance()
+ {
+ IReadOnlyList first = HotReloadCompilationAssemblies.Current();
+ IReadOnlyList second = HotReloadCompilationAssemblies.Current();
+
+ Assert.That(first, Is.Not.Empty);
+ Assert.That(second, Is.SameAs(first));
+ }
+
+ // Why: CompilationPipeline stores handlers on Delegate fields, but EditorApplication.projectChanged
+ // lives on EventWithPerformanceTracker. A Delegate-only scan cannot see it.
+ private static bool StaticEventHasHandler(Type eventOwner, string handlerName)
+ {
+ FieldInfo[] fields = eventOwner.GetFields(
+ BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
+ for (int index = 0; index < fields.Length; index++)
+ {
+ object value = fields[index].GetValue(null);
+ if (ContainsProductionHandler(value, handlerName))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool ContainsProductionHandler(object source, string handlerName)
+ {
+ if (source == null)
+ {
+ return false;
+ }
+
+ Delegate current = source as Delegate;
+ if (current != null)
+ {
+ return InvocationListContains(current, handlerName);
+ }
+
+ return EnumeratorContainsHandler(source, handlerName);
+ }
+
+ private static bool InvocationListContains(Delegate current, string handlerName)
+ {
+ Delegate[] listeners = current.GetInvocationList();
+ for (int listenerIndex = 0; listenerIndex < listeners.Length; listenerIndex++)
+ {
+ if (IsProductionHandler(listeners[listenerIndex], handlerName))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool EnumeratorContainsHandler(object source, string handlerName)
+ {
+ string typeName = source.GetType().Name;
+ if (typeName.IndexOf("EventWithPerformanceTracker", StringComparison.Ordinal) < 0)
+ {
+ return false;
+ }
+
+ MethodInfo getEnumerator = source.GetType().GetMethod(
+ "GetEnumerator",
+ BindingFlags.Instance | BindingFlags.Public);
+ if (getEnumerator == null || getEnumerator.GetParameters().Length != 0)
+ {
+ return false;
+ }
+
+ object enumerator = getEnumerator.Invoke(source, null);
+ if (enumerator == null)
+ {
+ return false;
+ }
+
+ MethodInfo moveNext = enumerator.GetType().GetMethod("MoveNext");
+ PropertyInfo currentProperty = enumerator.GetType().GetProperty("Current");
+ if (moveNext == null || currentProperty == null)
+ {
+ return false;
+ }
+
+ while ((bool)moveNext.Invoke(enumerator, null))
+ {
+ Delegate listener = currentProperty.GetValue(enumerator) as Delegate;
+ if (IsProductionHandler(listener, handlerName))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool IsProductionHandler(Delegate listener, string handlerName)
+ {
+ if (listener == null)
+ {
+ return false;
+ }
+
+ MethodInfo listenerMethod = listener.Method;
+ return listenerMethod.DeclaringType == typeof(HotReloadCompilationAssemblies)
+ && listenerMethod.Name == handlerName;
+ }
+ }
+}
diff --git a/Assets/Tests/Editor/HotReload/HotReloadCompilationAssembliesTests.cs.meta b/Assets/Tests/Editor/HotReload/HotReloadCompilationAssembliesTests.cs.meta
new file mode 100644
index 000000000..0abc5271e
--- /dev/null
+++ b/Assets/Tests/Editor/HotReload/HotReloadCompilationAssembliesTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 585d8186a697a4e21b54c6109d15ba69
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tests/Editor/HotReload/HotReloadCompilationAssemblyCacheTests.cs b/Assets/Tests/Editor/HotReload/HotReloadCompilationAssemblyCacheTests.cs
new file mode 100644
index 000000000..b26b95ea2
--- /dev/null
+++ b/Assets/Tests/Editor/HotReload/HotReloadCompilationAssemblyCacheTests.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Collections.Generic;
+
+using NUnit.Framework;
+
+using UnityEditor.Compilation;
+
+using io.github.hatayama.UnityCliLoop.FirstPartyTools;
+
+using UnityCompilationAssembly = UnityEditor.Compilation.Assembly;
+
+namespace io.github.hatayama.UnityCliLoop.Tests.Editor.HotReload
+{
+ ///
+ /// EditMode coverage for the per-domain memo of the compilation assembly list: when it asks Unity,
+ /// when it reuses the last answer, and why an empty answer is never kept.
+ ///
+ public sealed class HotReloadCompilationAssemblyCacheTests
+ {
+ private int _fetchCount;
+ private Queue _answers;
+
+ [SetUp]
+ public void SetUp()
+ {
+ _fetchCount = 0;
+ _answers = new Queue();
+ }
+
+ ///
+ /// Verifies that a second Current() reuses the first answer without asking Unity again.
+ ///
+ [Test]
+ public void Current_CalledTwice_FetchesOnceAndReturnsSameInstance()
+ {
+ _answers.Enqueue(new[] { CreateAssembly("First") });
+ HotReloadCompilationAssemblyCache cache = CreateCache();
+
+ IReadOnlyList first = cache.Current();
+ IReadOnlyList second = cache.Current();
+
+ Assert.That(_fetchCount, Is.EqualTo(1));
+ Assert.That(second, Is.SameAs(first));
+ Assert.That(cache.IsFilled, Is.True);
+ }
+
+ ///
+ /// Verifies that Invalidate() drops the memo so the next Current() asks Unity for a new list.
+ ///
+ [Test]
+ public void Current_AfterInvalidate_FetchesAgainAndReturnsNewAnswer()
+ {
+ UnityCompilationAssembly[] firstAnswer = { CreateAssembly("First") };
+ UnityCompilationAssembly[] secondAnswer = { CreateAssembly("Second") };
+ _answers.Enqueue(firstAnswer);
+ _answers.Enqueue(secondAnswer);
+ HotReloadCompilationAssemblyCache cache = CreateCache();
+ cache.Current();
+
+ cache.Invalidate();
+ IReadOnlyList afterInvalidate = cache.Current();
+
+ Assert.That(_fetchCount, Is.EqualTo(2));
+ Assert.That(afterInvalidate, Is.SameAs(secondAnswer));
+ }
+
+ ///
+ /// Verifies that an empty answer, which Unity gives while a compile is in flight, is not kept.
+ ///
+ [Test]
+ public void Current_EmptyAnswer_IsNotMemoized()
+ {
+ _answers.Enqueue(Array.Empty());
+ _answers.Enqueue(Array.Empty());
+ HotReloadCompilationAssemblyCache cache = CreateCache();
+
+ IReadOnlyList first = cache.Current();
+ bool filledAfterEmpty = cache.IsFilled;
+ cache.Current();
+
+ Assert.That(first, Is.Empty);
+ Assert.That(filledAfterEmpty, Is.False);
+ Assert.That(_fetchCount, Is.EqualTo(2));
+ }
+
+ ///
+ /// Verifies that the first non-empty answer after an empty one is kept.
+ ///
+ [Test]
+ public void Current_NonEmptyAfterEmpty_IsMemoized()
+ {
+ UnityCompilationAssembly[] nonEmpty = { CreateAssembly("First") };
+ _answers.Enqueue(Array.Empty());
+ _answers.Enqueue(nonEmpty);
+ HotReloadCompilationAssemblyCache cache = CreateCache();
+
+ cache.Current();
+ cache.Current();
+ IReadOnlyList third = cache.Current();
+
+ Assert.That(_fetchCount, Is.EqualTo(2));
+ Assert.That(third, Is.SameAs(nonEmpty));
+ }
+
+ ///
+ /// Verifies that FindByName returns the assembly with that name and asks Unity only once across lookups.
+ ///
+ [Test]
+ public void FindByName_KnownName_ReturnsAssemblyAndFetchesOnce()
+ {
+ UnityCompilationAssembly target = CreateAssembly("Second");
+ _answers.Enqueue(new[] { CreateAssembly("First"), target });
+ HotReloadCompilationAssemblyCache cache = CreateCache();
+
+ UnityCompilationAssembly first = cache.FindByName("Second");
+ UnityCompilationAssembly second = cache.FindByName("Second");
+
+ Assert.That(first, Is.SameAs(target));
+ Assert.That(second, Is.SameAs(target));
+ Assert.That(_fetchCount, Is.EqualTo(1));
+ }
+
+ ///
+ /// Verifies that FindByName matches the whole name with case, not a prefix or a case-folded name,
+ /// and that it looks at the first assembly too.
+ ///
+ [Test]
+ public void FindByName_SimilarNames_ReturnsExactMatchOnly()
+ {
+ UnityCompilationAssembly lowerCase = CreateAssembly("foo");
+ UnityCompilationAssembly prefixed = CreateAssembly("Foo.Editor");
+ UnityCompilationAssembly exact = CreateAssembly("Foo");
+ _answers.Enqueue(new[] { lowerCase, prefixed, exact });
+ HotReloadCompilationAssemblyCache cache = CreateCache();
+
+ UnityCompilationAssembly foundUpper = cache.FindByName("Foo");
+ UnityCompilationAssembly foundLower = cache.FindByName("foo");
+
+ Assert.That(foundUpper, Is.SameAs(exact));
+ Assert.That(foundLower, Is.SameAs(lowerCase));
+ }
+
+ ///
+ /// Verifies that FindByName returns null for a name no compilation assembly has.
+ ///
+ [Test]
+ public void FindByName_UnknownName_ReturnsNull()
+ {
+ _answers.Enqueue(new[] { CreateAssembly("First") });
+ HotReloadCompilationAssemblyCache cache = CreateCache();
+
+ UnityCompilationAssembly found = cache.FindByName("Missing");
+
+ Assert.That(found, Is.Null);
+ }
+
+ private HotReloadCompilationAssemblyCache CreateCache()
+ {
+ return new HotReloadCompilationAssemblyCache(Fetch);
+ }
+
+ private UnityCompilationAssembly[] Fetch()
+ {
+ _fetchCount++;
+ return _answers.Dequeue();
+ }
+
+ private static UnityCompilationAssembly CreateAssembly(string assemblyName)
+ {
+ return new UnityCompilationAssembly(
+ assemblyName,
+ HotReloadConstants.ScriptAssembliesRelativeDirectory + "/" + assemblyName + HotReloadConstants.CompiledAssemblyExtension,
+ Array.Empty(),
+ Array.Empty(),
+ Array.Empty(),
+ Array.Empty(),
+ AssemblyFlags.EditorAssembly);
+ }
+ }
+}
diff --git a/Assets/Tests/Editor/HotReload/HotReloadCompilationAssemblyCacheTests.cs.meta b/Assets/Tests/Editor/HotReload/HotReloadCompilationAssemblyCacheTests.cs.meta
new file mode 100644
index 000000000..b20a4b48a
--- /dev/null
+++ b/Assets/Tests/Editor/HotReload/HotReloadCompilationAssemblyCacheTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 23e6c40d20f324b7891035e1cd3ec444
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadCallSiteScanner.cs b/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadCallSiteScanner.cs
index 030713d35..24d6e9100 100644
--- a/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadCallSiteScanner.cs
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadCallSiteScanner.cs
@@ -174,7 +174,7 @@ private static Dictionary GetReferencedDllFileNamesByAssembly(
}
Dictionary graph = new Dictionary(StringComparer.Ordinal);
- foreach (UnityCompilationAssembly assembly in CompilationPipeline.GetAssemblies())
+ foreach (UnityCompilationAssembly assembly in HotReloadCompilationAssemblies.Current())
{
graph[assembly.name] = CollectReferenceFileNames(assembly.allReferences);
}
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadChangedFileAggregator.cs b/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadChangedFileAggregator.cs
index e3bb7d24a..d6c368e2d 100644
--- a/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadChangedFileAggregator.cs
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadChangedFileAggregator.cs
@@ -2,8 +2,6 @@
using System.Collections.Generic;
using System.IO;
-using UnityEditor.Compilation;
-
using UnityEngine;
using UnityCompilationAssembly = UnityEditor.Compilation.Assembly;
@@ -64,7 +62,7 @@ internal static HotReloadChangedFileAggregationResult Detect()
string projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
List snapshotAssemblies = CollectSnapshotAssemblies(
projectRoot,
- CompilationPipeline.GetAssemblies());
+ HotReloadCompilationAssemblies.Current());
return DetectFromSnapshotDirectories(projectRoot, snapshotAssemblies);
}
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadNewSourceMembershipValidator.cs b/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadNewSourceMembershipValidator.cs
index 72fe13b7e..c99bca4b6 100644
--- a/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadNewSourceMembershipValidator.cs
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadNewSourceMembershipValidator.cs
@@ -105,7 +105,7 @@ private static HotReloadFailureDescription DescribeMembershipChange(
HotReloadNewSourceMembershipEvidence evidence)
{
string projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
- UnityCompilationAssembly compilationAssembly = FindCompilationAssembly(evidence.AssemblyName);
+ UnityCompilationAssembly compilationAssembly = HotReloadCompilationAssemblies.FindByName(evidence.AssemblyName);
if (compilationAssembly == null)
{
return HotReloadFailureDescription.Declaration(
@@ -422,20 +422,6 @@ internal static bool EvidenceMatches(
return true;
}
- private static UnityCompilationAssembly FindCompilationAssembly(string assemblyName)
- {
- UnityCompilationAssembly[] assemblies = CompilationPipeline.GetAssemblies();
- for (int index = 0; index < assemblies.Length; index++)
- {
- if (assemblies[index].name == assemblyName)
- {
- return assemblies[index];
- }
- }
-
- return null;
- }
-
private static string NormalizeProjectRelativePath(string path)
{
return string.IsNullOrEmpty(path) ? null : path.Replace('\\', '/');
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadPatchTargetSupport.cs b/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadPatchTargetSupport.cs
index 1ab778337..f688f1b75 100644
--- a/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadPatchTargetSupport.cs
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/HotReloadPatchTargetSupport.cs
@@ -57,7 +57,7 @@ internal static HotReloadPatchTargetResolution ResolvePatchTarget(
}
string assemblyName = Path.GetFileNameWithoutExtension(rawAssemblyName);
- UnityCompilationAssembly compilationAssembly = FindCompilationAssembly(assemblyName);
+ UnityCompilationAssembly compilationAssembly = HotReloadCompilationAssemblies.FindByName(assemblyName);
// Why gate on compilationAssembly == null: the unimported-asmdef flag is only
// consumed on that branch, and walking ancestor directories on every successful
// resolve (including loose Assembly-CSharp scripts) is wasted disk I/O.
@@ -208,19 +208,6 @@ private static HotReloadFailureDescription DescribeBusyRefusal(
return editorStateSnapshotCapture.CaptureCurrent().GetBusyFailure();
}
- private static UnityCompilationAssembly FindCompilationAssembly(string assemblyName)
- {
- foreach (UnityCompilationAssembly assembly in CompilationPipeline.GetAssemblies())
- {
- if (assembly.name == assemblyName)
- {
- return assembly;
- }
- }
-
- return null;
- }
-
// Why Path.Combine then GetFullPath: Unity Assembly.sourceFiles are project-relative
// (slash-separated). The worker cwd is Library/UloopHotReload/Worker//, so it
// can only open absolute paths. Normalization matches HotReloadSourceSnapshotter.
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblies.cs b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblies.cs
new file mode 100644
index 000000000..842fe71c1
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblies.cs
@@ -0,0 +1,57 @@
+using System.Collections.Generic;
+
+using UnityEditor;
+using UnityEditor.Compilation;
+
+using UnityCompilationAssembly = UnityEditor.Compilation.Assembly;
+
+namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
+{
+ ///
+ /// The compilation assembly list for hot reload, asked of Unity once per domain.
+ /// Why: on a project with several hundred assemblies one CompilationPipeline.GetAssemblies() call
+ /// takes 340-620 ms, while the list only changes through an import or a compile. The startup
+ /// snapshot capture is the first caller after a domain reload, so runs find the list already kept.
+ ///
+ internal static class HotReloadCompilationAssemblies
+ {
+ private static readonly HotReloadCompilationAssemblyCache Cache =
+ new HotReloadCompilationAssemblyCache(CompilationPipeline.GetAssemblies);
+
+ static HotReloadCompilationAssemblies()
+ {
+ // Why: a compile that fails keeps the domain alive, so without this the list would keep
+ // the asmdefs and source files from before that compile until the next reload.
+ CompilationPipeline.compilationStarted += OnCompilationStarted;
+ // Why: an import does not always compile (Play Mode with Recompile After Finished Playing
+ // postpones it), yet it can change the source files of an assembly.
+ EditorApplication.projectChanged += OnProjectChanged;
+ }
+
+ ///
+ /// Returns the compilation assemblies. The list is shared; callers only read it.
+ ///
+ internal static IReadOnlyList Current()
+ {
+ return Cache.Current();
+ }
+
+ ///
+ /// Returns the first compilation assembly with this name, or null when none has it.
+ ///
+ internal static UnityCompilationAssembly FindByName(string assemblyName)
+ {
+ return Cache.FindByName(assemblyName);
+ }
+
+ private static void OnCompilationStarted(object context)
+ {
+ Cache.Invalidate();
+ }
+
+ private static void OnProjectChanged()
+ {
+ Cache.Invalidate();
+ }
+ }
+}
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblies.cs.meta b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblies.cs.meta
new file mode 100644
index 000000000..34d698d2d
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblies.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 74af2888c9ea646518605a0fe11f27cf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblyCache.cs b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblyCache.cs
new file mode 100644
index 000000000..5eac8cd97
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblyCache.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+
+using UnityEngine;
+
+using UnityCompilationAssembly = UnityEditor.Compilation.Assembly;
+
+namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
+{
+ ///
+ /// Keeps the last non-empty compilation assembly list so repeated lookups ask Unity only once
+ /// until the owner invalidates it. Main thread only, like the Unity call it wraps.
+ ///
+ internal sealed class HotReloadCompilationAssemblyCache
+ {
+ private readonly Func _fetch;
+ private UnityCompilationAssembly[] _assemblies;
+
+ internal HotReloadCompilationAssemblyCache(Func fetch)
+ {
+ Debug.Assert(fetch != null, "fetch must not be null.");
+ _fetch = fetch;
+ }
+
+ internal bool IsFilled => _assemblies != null;
+
+ ///
+ /// Returns the memoized list, asking Unity when nothing is kept. The list is shared; callers only read it.
+ ///
+ internal IReadOnlyList Current()
+ {
+ if (_assemblies != null)
+ {
+ return _assemblies;
+ }
+
+ UnityCompilationAssembly[] fetched = _fetch();
+ Debug.Assert(fetched != null, "fetch must not return null.");
+
+ // Why not memoize an empty result: GetAssemblies() answers nothing while a compile is in
+ // flight, and keeping that would hide every assembly for the rest of the domain's life.
+ if (fetched.Length > 0)
+ {
+ _assemblies = fetched;
+ }
+
+ return fetched;
+ }
+
+ ///
+ /// Returns the first compilation assembly with this name, or null when none has it.
+ ///
+ internal UnityCompilationAssembly FindByName(string assemblyName)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(assemblyName), "assemblyName must not be null or empty.");
+
+ IReadOnlyList assemblies = Current();
+ for (int index = 0; index < assemblies.Count; index++)
+ {
+ if (assemblies[index].name == assemblyName)
+ {
+ return assemblies[index];
+ }
+ }
+
+ return null;
+ }
+
+ internal void Invalidate()
+ {
+ _assemblies = null;
+ }
+ }
+}
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblyCache.cs.meta b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblyCache.cs.meta
new file mode 100644
index 000000000..7f7ac07f1
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadCompilationAssemblyCache.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d7365883bccb142a7b7e503e8080d096
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadSourceSnapshotter.cs b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadSourceSnapshotter.cs
index 6f0e49fa0..6064b1ff9 100644
--- a/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadSourceSnapshotter.cs
+++ b/Packages/src/Editor/FirstPartyTools/HotReload/Shared/HotReloadSourceSnapshotter.cs
@@ -6,7 +6,6 @@
using Mono.Cecil;
-using UnityEditor.Compilation;
using UnityEditor.PackageManager;
using UnityEngine;
@@ -33,7 +32,7 @@ internal static class HotReloadSourceSnapshotter
internal static void CaptureAfterDomainReload()
{
string projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
- CaptureAssemblies(projectRoot, CompilationPipeline.GetAssemblies());
+ CaptureAssemblies(projectRoot, HotReloadCompilationAssemblies.Current());
}
// Why separate from CaptureAfterDomainReload: the project root and the compilation assemblies come