using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using Unity.Pipeline.HotReload; using Unity.Pipeline.Models; using Unity.Pipeline.Threading; using UnityEngine; namespace Unity.Pipeline.Compilation { /// /// Compiles hot reload source files using shared RoslynCompilationService. /// Creates versioned DLLs and manages hot reload assembly lifecycle. /// Fixed deadlock issue by adding main thread detection like EvalCodeCompiler. /// public static class HotReloadCompiler { #if UNITY_EDITOR || (UNITY_STANDALONE && DEBUG) private static readonly Dictionary _versionTracker = new(); private const string HotReloadTempDir = "Temp/HotReload"; /// /// Compile a hot reload source file and apply the overrides immediately. /// Fixed deadlock by detecting main thread and running synchronously when needed. /// /// Hot reload file to compile /// Optional directory to save compiled assembly to disk. If null, assembly stays in memory only. public static Task CompileAndApplyAsync(string filename, string assemblyDir = null) { // Hot reload commands are MainThreadRequired, so we are already on the main thread. // Run synchronously (no background thread / dispatcher). Returns a completed Task for // signature compatibility. return Task.FromResult(CompileAndApplyOnMainThread(filename, assemblyDir)); } public static string GetHotReloadPath(string filePath) { if (Path.IsPathRooted(filePath)) return filePath; // Relative paths are resolved against the current working directory (the Unity // project root). Override files can live in any folder; there is no special // "HotReload" location. return Path.GetFullPath(filePath); } /// /// Compile and apply on main thread synchronously to avoid deadlocks. /// /// Hot reload file to compile /// Optional directory to save compiled assembly to disk. If null, assembly stays in memory only. public static HotReloadCompileResult CompileAndApplyOnMainThread(string filename, string assemblyDir = null) { var stopwatch = System.Diagnostics.Stopwatch.StartNew(); try { // Resolve the override file path (absolute, or relative to the project root). var hotReloadPath = GetHotReloadPath(filename); if (!File.Exists(hotReloadPath)) { return HotReloadCompileResult.Failure( "File Not Found", $"Hot reload file not found: {hotReloadPath}", stopwatch.ElapsedMilliseconds); } Debug.Log($"HotReload: Starting synchronous compilation of {filename}"); // Read source code var sourceCode = File.ReadAllText(hotReloadPath); if (string.IsNullOrWhiteSpace(sourceCode)) { return HotReloadCompileResult.Failure( "Empty File", $"Hot reload file is empty: {hotReloadPath}", stopwatch.ElapsedMilliseconds); } // Generate versioned assembly name var baseFilename = Path.GetFileNameWithoutExtension(filename); var nextVersion = GetNextVersion(baseFilename); var assemblyName = $"{baseFilename}_{nextVersion:D3}"; // Ensure temp directory exists Directory.CreateDirectory(HotReloadTempDir); // Compile using shared RoslynCompilationService var compilationResult = CompileHotReloadAssembly(sourceCode, assemblyName); if (!compilationResult.Success) { return HotReloadCompileResult.Failure( "Compilation Failed", "Hot reload file compilation failed", stopwatch.ElapsedMilliseconds, compilationResult.Diagnostics.Select(d => d.Message).ToList()); } // Register hot reload methods from compiled assembly var registeredMethods = RegisterHotReloadMethods(compilationResult.Assembly, assemblyName, out var skippedOverrides); // Save assembly to disk if assemblyDir is specified string actualAssemblyPath = null; if (!string.IsNullOrWhiteSpace(assemblyDir)) { Directory.CreateDirectory(assemblyDir); actualAssemblyPath = Path.Combine(assemblyDir, $"{assemblyName}.dll"); File.WriteAllBytes(actualAssemblyPath, compilationResult.AssemblyBytes); Debug.Log($"HotReload: Assembly saved to disk: {actualAssemblyPath}"); } else { actualAssemblyPath = Path.Combine(HotReloadTempDir, $"{assemblyName}.dll"); // For compatibility (in-memory) } stopwatch.Stop(); Debug.Log($"HotReload: Successfully compiled {filename} -> {assemblyName}.dll with {registeredMethods.Count} methods in {stopwatch.ElapsedMilliseconds}ms"); var compileResult = HotReloadCompileResult.Success( assemblyName, actualAssemblyPath, registeredMethods, stopwatch.ElapsedMilliseconds); compileResult.Diagnostics = skippedOverrides; return compileResult; } catch (Exception ex) { stopwatch.Stop(); Debug.LogError($"HotReload: Synchronous compilation failed for {filename}: {ex.Message}"); return HotReloadCompileResult.Failure( "Exception", ex.ToString(), stopwatch.ElapsedMilliseconds); } } /// /// Internal async compilation implementation for background threads. /// /// Hot reload file to compile /// Optional directory to save compiled assembly to disk. If null, assembly stays in memory only. private static async Task CompileAndApplyInternalAsync(string filename, string assemblyDir = null) { var stopwatch = System.Diagnostics.Stopwatch.StartNew(); try { // Resolve the override file path (absolute, or relative to the project root). var hotReloadPath = GetHotReloadPath(filename); if (!File.Exists(hotReloadPath)) { return HotReloadCompileResult.Failure( "File Not Found", $"Hot reload file not found: {hotReloadPath}", stopwatch.ElapsedMilliseconds); } Debug.Log($"HotReload: Starting async compilation of {filename}"); // Read source code var sourceCode = File.ReadAllText(hotReloadPath); if (string.IsNullOrWhiteSpace(sourceCode)) { return HotReloadCompileResult.Failure( "Empty File", $"Hot reload file is empty: {hotReloadPath}", stopwatch.ElapsedMilliseconds); } // Generate versioned assembly name var baseFilename = Path.GetFileNameWithoutExtension(filename); var nextVersion = GetNextVersion(baseFilename); var assemblyName = $"{baseFilename}_{nextVersion:D3}"; // Ensure temp directory exists Directory.CreateDirectory(HotReloadTempDir); // Compile using shared RoslynCompilationService async var compilationResult = await CompileHotReloadAssemblyAsync(sourceCode, assemblyName); if (!compilationResult.Success) { return HotReloadCompileResult.Failure( "Compilation Failed", "Hot reload file compilation failed", stopwatch.ElapsedMilliseconds, compilationResult.Diagnostics.Select(d => d.Message).ToList()); } // Register hot reload methods from compiled assembly var registeredMethods = RegisterHotReloadMethods(compilationResult.Assembly, assemblyName, out var skippedOverrides); // Save assembly to disk if assemblyDir is specified string actualAssemblyPath = null; if (!string.IsNullOrWhiteSpace(assemblyDir)) { Directory.CreateDirectory(assemblyDir); actualAssemblyPath = Path.Combine(assemblyDir, $"{assemblyName}.dll"); File.WriteAllBytes(actualAssemblyPath, compilationResult.AssemblyBytes); Debug.Log($"HotReload: Assembly saved to disk: {actualAssemblyPath}"); } else { actualAssemblyPath = Path.Combine(HotReloadTempDir, $"{assemblyName}.dll"); // For compatibility (in-memory) } stopwatch.Stop(); Debug.Log($"HotReload: Successfully compiled {filename} -> {assemblyName}.dll with {registeredMethods.Count} methods in {stopwatch.ElapsedMilliseconds}ms"); var compileResult = HotReloadCompileResult.Success( assemblyName, actualAssemblyPath, registeredMethods, stopwatch.ElapsedMilliseconds); compileResult.Diagnostics = skippedOverrides; return compileResult; } catch (Exception ex) { stopwatch.Stop(); Debug.LogError($"HotReload: Async compilation failed for {filename}: {ex.Message}"); return HotReloadCompileResult.Failure( "Exception", ex.ToString(), stopwatch.ElapsedMilliseconds); } } /// /// Compile hot reload source code directly (for in-place editing workflow). /// /// Hot reload source code to compile /// Base filename for versioned assembly naming /// Optional directory to save compiled assembly to disk public static Task CompileSourceCodeAsync(string sourceCode, string baseFileName, string assemblyDir = null, bool emitPdb = false, string documentPath = null) { // Runs synchronously on the calling (main) thread; returns a completed Task. return Task.FromResult(CompileSourceCodeOnMainThread(sourceCode, baseFileName, assemblyDir, emitPdb, documentPath)); } /// /// Compile source code on main thread synchronously to avoid deadlocks. /// /// Emit a portable PDB and load symbols so breakpoints can bind. /// Source document path recorded in the PDB (the original .cs file). public static HotReloadCompilationResult CompileSourceCodeOnMainThread(string sourceCode, string baseFileName, string assemblyDir = null, bool emitPdb = false, string documentPath = null) { var stopwatch = System.Diagnostics.Stopwatch.StartNew(); try { Debug.Log($"HotReload: Starting synchronous source code compilation for {baseFileName}"); if (string.IsNullOrWhiteSpace(sourceCode)) { return HotReloadCompilationResult.Failure( "Empty Source Code", "Source code cannot be empty", stopwatch.ElapsedMilliseconds); } // Generate versioned assembly name var nextVersion = GetNextVersion(baseFileName); var assemblyName = $"{baseFileName}_{nextVersion:D3}"; // Ensure temp directory exists Directory.CreateDirectory(HotReloadTempDir); // Compile using shared RoslynCompilationService var compilationResult = CompileHotReloadAssembly(sourceCode, assemblyName, emitPdb, documentPath); if (!compilationResult.Success) { return HotReloadCompilationResult.Failure( "Compilation Failed", "Hot reload source code compilation failed", stopwatch.ElapsedMilliseconds, compilationResult.Diagnostics.Select(d => d.Message).ToList()); } // Register hot reload methods from compiled assembly var registeredMethods = RegisterHotReloadMethods(compilationResult.Assembly, assemblyName, out var skippedOverrides); // Save assembly to disk if assemblyDir is specified string actualAssemblyPath = null; if (!string.IsNullOrWhiteSpace(assemblyDir)) { Directory.CreateDirectory(assemblyDir); actualAssemblyPath = Path.Combine(assemblyDir, $"{assemblyName}.dll"); File.WriteAllBytes(actualAssemblyPath, compilationResult.AssemblyBytes); Debug.Log($"HotReload: Assembly saved to disk: {actualAssemblyPath}"); // Symbols are loaded in-memory; also write the .pdb next to the .dll for convenience. if (compilationResult.PdbBytes != null) { var pdbPath = Path.Combine(assemblyDir, $"{assemblyName}.pdb"); File.WriteAllBytes(pdbPath, compilationResult.PdbBytes); Debug.Log($"HotReload: Symbols saved to disk: {pdbPath}"); } } else { actualAssemblyPath = Path.Combine(HotReloadTempDir, $"{assemblyName}.dll"); // For compatibility (in-memory) } stopwatch.Stop(); Debug.Log($"HotReload: Successfully compiled source code -> {assemblyName}.dll with {registeredMethods.Count} methods in {stopwatch.ElapsedMilliseconds}ms"); var sourceCompileResult = HotReloadCompilationResult.Success( assemblyName, actualAssemblyPath, registeredMethods, stopwatch.ElapsedMilliseconds); sourceCompileResult.Diagnostics = skippedOverrides; return sourceCompileResult; } catch (Exception ex) { stopwatch.Stop(); Debug.LogError($"HotReload: Synchronous source code compilation failed for {baseFileName}: {ex.Message}"); return HotReloadCompilationResult.Failure( "Exception", ex.ToString(), stopwatch.ElapsedMilliseconds); } } /// /// Internal async source code compilation implementation for background threads. /// private static async Task CompileSourceCodeInternalAsync(string sourceCode, string baseFileName, string assemblyDir = null) { var stopwatch = System.Diagnostics.Stopwatch.StartNew(); try { Debug.Log($"HotReload: Starting async source code compilation for {baseFileName}"); if (string.IsNullOrWhiteSpace(sourceCode)) { return HotReloadCompilationResult.Failure( "Empty Source Code", "Source code cannot be empty", stopwatch.ElapsedMilliseconds); } // Generate versioned assembly name var nextVersion = GetNextVersion(baseFileName); var assemblyName = $"{baseFileName}_{nextVersion:D3}"; // Ensure temp directory exists Directory.CreateDirectory(HotReloadTempDir); // Compile using shared RoslynCompilationService async var compilationResult = await CompileHotReloadAssemblyAsync(sourceCode, assemblyName); if (!compilationResult.Success) { return HotReloadCompilationResult.Failure( "Compilation Failed", "Hot reload source code compilation failed", stopwatch.ElapsedMilliseconds, compilationResult.Diagnostics.Select(d => d.Message).ToList()); } // Register hot reload methods from compiled assembly var registeredMethods = RegisterHotReloadMethods(compilationResult.Assembly, assemblyName, out var skippedOverrides); // Save assembly to disk if assemblyDir is specified string actualAssemblyPath = null; if (!string.IsNullOrWhiteSpace(assemblyDir)) { Directory.CreateDirectory(assemblyDir); actualAssemblyPath = Path.Combine(assemblyDir, $"{assemblyName}.dll"); File.WriteAllBytes(actualAssemblyPath, compilationResult.AssemblyBytes); Debug.Log($"HotReload: Assembly saved to disk: {actualAssemblyPath}"); } else { actualAssemblyPath = Path.Combine(HotReloadTempDir, $"{assemblyName}.dll"); // For compatibility (in-memory) } stopwatch.Stop(); Debug.Log($"HotReload: Successfully compiled source code -> {assemblyName}.dll with {registeredMethods.Count} methods in {stopwatch.ElapsedMilliseconds}ms"); var sourceCompileResult = HotReloadCompilationResult.Success( assemblyName, actualAssemblyPath, registeredMethods, stopwatch.ElapsedMilliseconds); sourceCompileResult.Diagnostics = skippedOverrides; return sourceCompileResult; } catch (Exception ex) { stopwatch.Stop(); Debug.LogError($"HotReload: Async source code compilation failed for {baseFileName}: {ex.Message}"); return HotReloadCompilationResult.Failure( "Exception", ex.ToString(), stopwatch.ElapsedMilliseconds); } } /// /// Clean up old hot reload DLL versions, keeping only the latest for each base filename. /// public static HotReloadCleanupResult CleanupHotReloadDlls(string assemblyDir = null) { var stopwatch = System.Diagnostics.Stopwatch.StartNew(); var deletedFiles = new List(); try { // Use specified assemblyDir or default to HotReloadTempDir var targetDir = !string.IsNullOrWhiteSpace(assemblyDir) ? assemblyDir : HotReloadTempDir; if (!Directory.Exists(targetDir)) { return new HotReloadCleanupResult { Success = true, DeletedFiles = deletedFiles, Message = $"No hot reload directory found: {targetDir}", ExecutionTimeMs = stopwatch.ElapsedMilliseconds }; } var dllFiles = Directory.GetFiles(targetDir, "*.dll"); var groupedFiles = dllFiles .Select(f => new { FilePath = f, FileName = Path.GetFileNameWithoutExtension(f), BaseFilename = ExtractBaseFilename(Path.GetFileNameWithoutExtension(f)), Version = ExtractVersion(Path.GetFileNameWithoutExtension(f)) }) .Where(f => f.Version > 0) // Only versioned hot reload DLLs .GroupBy(f => f.BaseFilename) .ToList(); foreach (var group in groupedFiles) { var sortedFiles = group.OrderByDescending(f => f.Version).ToList(); // Keep the latest version, delete older ones for (int i = 1; i < sortedFiles.Count; i++) { var fileToDelete = sortedFiles[i]; File.Delete(fileToDelete.FilePath); deletedFiles.Add(fileToDelete.FileName + ".dll"); Debug.Log($"HotReload: Deleted old version {fileToDelete.FileName}.dll"); } } // Clear registry and reset version tracker HotReloadRegistry.ClearAllOverrides(); _versionTracker.Clear(); stopwatch.Stop(); Debug.Log($"HotReload: Cleanup completed - deleted {deletedFiles.Count} files in {stopwatch.ElapsedMilliseconds}ms"); return new HotReloadCleanupResult { Success = true, DeletedFiles = deletedFiles, Message = $"Deleted {deletedFiles.Count} old DLL versions", ExecutionTimeMs = stopwatch.ElapsedMilliseconds }; } catch (Exception ex) { stopwatch.Stop(); Debug.LogError($"HotReload: Cleanup failed: {ex.Message}"); return new HotReloadCleanupResult { Success = false, DeletedFiles = deletedFiles, Message = $"Cleanup failed: {ex.Message}", ExecutionTimeMs = stopwatch.ElapsedMilliseconds }; } } /// /// Get the next version number for a base filename. /// private static int GetNextVersion(string baseFilename) { if (!_versionTracker.ContainsKey(baseFilename)) { _versionTracker[baseFilename] = 0; } return ++_versionTracker[baseFilename]; } /// /// Wrap hot reload user code with proper using statements and namespace. /// /// /// Compile hot reload source code using shared RoslynCompilationService. /// Synchronous compilation for main thread use. /// private static CompilationResult CompileHotReloadAssembly(string sourceCode, string assemblyName, bool emitPdb = false, string documentPath = null) { var request = new CompilationRequest { SourceCode = sourceCode, AssemblyName = assemblyName, // Ensure Unity.Pipeline assemblies are included (contains HotReloadOverrideMethodAttribute) // Also include test assemblies for testing scenarios AdditionalAssemblyPrefixes = new[] { "Unity.Pipeline", "Unity.Pipeline.Tests" }, EmitDebugInformation = emitPdb, DocumentPath = documentPath }; return RoslynCompilationService.Compile(request); } /// /// Compile hot reload source code using shared RoslynCompilationService. /// Async compilation for background thread use. /// private static async Task CompileHotReloadAssemblyAsync(string sourceCode, string assemblyName) { var request = new CompilationRequest { SourceCode = sourceCode, AssemblyName = assemblyName, // Ensure Unity.Pipeline assemblies are included (contains HotReloadOverrideMethodAttribute) // Also include test assemblies for testing scenarios AdditionalAssemblyPrefixes = new[] { "Unity.Pipeline", "Unity.Pipeline.Tests" } }; return await RoslynCompilationService.CompileAsync(request); } /// /// Register hot reload methods from compiled assembly with the registry. /// Also registers any [HotReloadWithOverrides] target methods found in the assembly. /// Only overrides that actually bind are returned; overrides that were skipped (e.g. the /// target is not currently [HotReloadWithOverrides], or a signature mismatch) are reported via /// with a user-facing reason. /// private static List RegisterHotReloadMethods(Assembly assembly, string assemblyId, out List skipped) { var registeredMethods = new List(); skipped = new List(); try { Debug.Log($"HotReload: Registering methods from assembly {assemblyId} with {assembly.GetTypes().Length} types"); // Register assembly types for discovery foreach (var type in assembly.GetTypes()) { Debug.Log($"HotReload: Processing type {type.Name} in {type.Namespace}"); HotReloadRegistry.RegisterHotReloadType(type, assemblyId); // First, scan for [HotReloadWithOverrides] methods to register as targets var allMethods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); foreach (var method in allMethods) { var reloadableAttr = method.GetCustomAttribute(); if (reloadableAttr != null) { Debug.Log($"HotReload: Found reloadable method {method.Name} with ID {reloadableAttr.Id}"); HotReloadRegistry.RegisterReloadableMethod(method, reloadableAttr); } } // Then, find methods with [HotReloadOverrideMethod] attribute for overrides var staticMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Static); Debug.Log($"HotReload: Found {staticMethods.Length} static methods in {type.Name}"); foreach (var method in staticMethods) { var customAttributes = method.GetCustomAttributes(true); Debug.Log($"HotReload: Method {method.Name} has {customAttributes.Length} custom attributes: {string.Join(", ", customAttributes.Select(a => a.GetType().Name))}"); // Try multiple ways to find the attribute var hotReloadAttr = method.GetCustomAttribute(); if (hotReloadAttr == null) { // Try by name in case of type loading issues var attrByName = customAttributes.FirstOrDefault(a => a.GetType().Name == "HotReloadOverrideMethodAttribute"); if (attrByName != null) { Debug.Log($"HotReload: Found HotReloadOverrideMethodAttribute by name on {method.Name}"); // Cast to the attribute type hotReloadAttr = attrByName as HotReloadOverrideMethodAttribute; } } if (hotReloadAttr != null) { Debug.Log($"HotReload: Registering method override {method.Name} -> {hotReloadAttr.TargetMethodId}"); if (HotReloadRegistry.RegisterMethodOverride(method, hotReloadAttr, type, out var skipReason)) { registeredMethods.Add(hotReloadAttr.TargetMethodId); } else { skipped.Add($"{method.Name} -> {hotReloadAttr.TargetMethodId}: {skipReason}"); } } } } Debug.Log($"HotReload: Registered {registeredMethods.Count} override methods: {string.Join(", ", registeredMethods)}"); } catch (Exception ex) { Debug.LogError($"HotReload: Error registering methods from assembly {assemblyId}: {ex.Message}"); Debug.LogError($"HotReload: Stack trace: {ex.StackTrace}"); } return registeredMethods; } /// /// Extract base filename from versioned filename (e.g., "PlayerMovement_001" -> "PlayerMovement"). /// private static string ExtractBaseFilename(string versionedFilename) { var lastUnderscoreIndex = versionedFilename.LastIndexOf('_'); if (lastUnderscoreIndex > 0) { var potentialVersion = versionedFilename.Substring(lastUnderscoreIndex + 1); if (int.TryParse(potentialVersion, out _)) { return versionedFilename.Substring(0, lastUnderscoreIndex); } } return versionedFilename; } /// /// Extract version number from versioned filename (e.g., "PlayerMovement_001" -> 1). /// private static int ExtractVersion(string versionedFilename) { var lastUnderscoreIndex = versionedFilename.LastIndexOf('_'); if (lastUnderscoreIndex > 0) { var potentialVersion = versionedFilename.Substring(lastUnderscoreIndex + 1); if (int.TryParse(potentialVersion, out var version)) { return version; } } return 0; } #else // Hot reload requires Roslyn compilation, which is only available in the Editor and in // Desktop development builds. In all other builds the methods below are compiled instead, // matching the public surface used by callers (HotReloadCommands, InPlaceReloadProcessor) // so the project still builds, and returning a clear "not supported" failure at runtime. const string NotSupportedMessage = "Hot reload compilation is only supported on Desktop development builds (Windows/Mac/Linux)."; /// /// Hot reload compilation not supported on this build. /// public static Task CompileAndApplyAsync(string filename, string assemblyDir = null) { return Task.FromResult(HotReloadCompileResult.Failure("Platform Not Supported", NotSupportedMessage)); } /// /// Hot reload source compilation (in-place editing) not supported on this build. /// public static HotReloadCompilationResult CompileSourceCodeOnMainThread(string sourceCode, string baseFileName, string assemblyDir = null, bool emitPdb = false, string documentPath = null) { return HotReloadCompilationResult.Failure("Platform Not Supported", NotSupportedMessage); } /// /// Hot reload source compilation (in-place editing) not supported on this build. /// public static Task CompileSourceCodeAsync(string sourceCode, string baseFileName, string assemblyDir = null, bool emitPdb = false, string documentPath = null) { return Task.FromResult(HotReloadCompilationResult.Failure("Platform Not Supported", NotSupportedMessage)); } /// /// Hot reload cleanup not supported on this build. /// public static HotReloadCleanupResult CleanupHotReloadDlls(string assemblyDir = null) { return new HotReloadCleanupResult { Success = false, Message = NotSupportedMessage, ExecutionTimeMs = 0 }; } #endif } /// /// Result from hot reload compilation operation. /// public class HotReloadCompileResult { public bool IsSuccess { get; set; } public string AssemblyName { get; set; } public string OutputPath { get; set; } public List RegisteredMethods { get; set; } = new List(); public long ExecutionTimeMs { get; set; } public string Error { get; set; } public string ErrorDetails { get; set; } public List Diagnostics { get; set; } = new List(); public static HotReloadCompileResult Success(string assemblyName, string outputPath, List registeredMethods, long executionTimeMs) { return new HotReloadCompileResult { IsSuccess = true, AssemblyName = assemblyName, OutputPath = outputPath, RegisteredMethods = registeredMethods, ExecutionTimeMs = executionTimeMs }; } public static HotReloadCompileResult Failure(string error, string errorDetails, long executionTimeMs = 0, List diagnostics = null) { return new HotReloadCompileResult { IsSuccess = false, Error = error, ErrorDetails = errorDetails, ExecutionTimeMs = executionTimeMs, Diagnostics = diagnostics ?? new List() }; } } /// /// Result from hot reload cleanup operation. /// public class HotReloadCleanupResult { public bool Success { get; set; } public List DeletedFiles { get; set; } = new List(); public string Message { get; set; } public long ExecutionTimeMs { get; set; } } /// /// Result from hot reload source code compilation operation (for in-place editing). /// public class HotReloadCompilationResult { public bool IsSuccess { get; set; } public string AssemblyName { get; set; } public string OutputPath { get; set; } public List RegisteredMethods { get; set; } = new List(); public long ExecutionTimeMs { get; set; } public string Error { get; set; } public string ErrorDetails { get; set; } public List Diagnostics { get; set; } = new List(); public static HotReloadCompilationResult Success(string assemblyName, string outputPath, List registeredMethods, long executionTimeMs) { return new HotReloadCompilationResult { IsSuccess = true, AssemblyName = assemblyName, OutputPath = outputPath, RegisteredMethods = registeredMethods, ExecutionTimeMs = executionTimeMs }; } public static HotReloadCompilationResult Failure(string error, string errorDetails, long executionTimeMs = 0, List diagnostics = null) { return new HotReloadCompilationResult { IsSuccess = false, Error = error, ErrorDetails = errorDetails, ExecutionTimeMs = executionTimeMs, Diagnostics = diagnostics ?? new List() }; } } }