From ae745c0432117fbd95886175ee06bcacb7b49656 Mon Sep 17 00:00:00 2001 From: loicb Date: Fri, 5 Jun 2026 11:27:54 +0800 Subject: [PATCH 1/9] fix(magic-unity): select workaround signature assemblies from player compilation references (#23) The workaround generator walked the type reference closure from Assembly-CSharp against the editor's full desktop BCL, trimmed only by a Unity prefix check. On Windows that reaches editor-only assemblies like Mono.WebBrowser, and signatures referencing them break the IL2CPP build. Select assemblies from the CompilationPipeline player compilation references instead, log the reference set and each skip, and fail the build if the set ever looks degenerate. Closes #23 --- .../GenerateGenericWorkaroundMethods.cs | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs index 3bc258d1..6fae0abf 100644 --- a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs +++ b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs @@ -5,6 +5,7 @@ using Mono.Cecil; using Mono.Cecil.Rocks; using Mono.Cecil.Cil; +using UnityEditor.Compilation; namespace Magic.Unity { @@ -21,10 +22,35 @@ struct DynamicCallSiteInfo static List AllMethods = new List(); static TypeDefinition MagicRuntimeDelegateHelpers = null; - static bool ShouldCollectReferencedAssembly(AssemblyDefinition assy) + static HashSet PlayerReferenceNames = null; + + // The collected assemblies feed AllMethods, the pool of candidate dynamic + // dispatch targets whose GetMethodDelegateFast instantiations get emitted + // into the shipped .clj.dlls. A signature referencing an assembly absent + // from the player build breaks the IL2CPP build; the type-reference + // closure resolves against the editor's full desktop BCL, which on + // Windows reaches editor-only assemblies like Mono.WebBrowser. So collect + // an assembly only if player scripts compile against it. Desktop-only BCL + // assemblies are never player compilation references, while UnityEngine + // modules, the player BCL profile, plugins (including .clj.dlls), and + // user script assemblies all are. + static HashSet CollectPlayerReferenceNames() { - return !assy.FullName.StartsWith("Unity") || assy.FullName.StartsWith("UnityEngine"); + var names = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var assembly in CompilationPipeline.GetAssemblies(AssembliesType.PlayerWithoutTestAssemblies)) + { + names.Add(assembly.name); + foreach (var reference in assembly.allReferences) + { + names.Add(System.IO.Path.GetFileNameWithoutExtension(reference)); + } + } + return names; + } + static bool ShouldCollectReferencedAssembly(AssemblyDefinition assy) + { + return PlayerReferenceNames.Contains(assy.Name.Name); } static HashSet CollectAllReferencedAssemblies(AssemblyDefinition assydef, HashSet seen = null) @@ -51,7 +77,7 @@ static HashSet CollectAllReferencedAssemblies(AssemblyDefini } else { - UnityEngine.Debug.Log($"[CollectAllReferencedAssemblies] Skip {resolved.Module.Assembly}"); + UnityEngine.Debug.Log($"[CollectAllReferencedAssemblies] Skip {resolved.Module.Assembly} (not a player compilation reference)"); } } } @@ -66,6 +92,18 @@ static HashSet CollectAllReferencedAssemblies(AssemblyDefini public static void Init() { + PlayerReferenceNames = CollectPlayerReferenceNames(); + // A degenerate reference set would silently turn workaround + // generation into a no-op: the build stays green and devices throw + // ExecutionEngineException at runtime. Fail the build instead. Any + // sane player reference set contains a core library and at least + // one UnityEngine module. + if (!(PlayerReferenceNames.Contains("mscorlib") || PlayerReferenceNames.Contains("netstandard")) + || !PlayerReferenceNames.Any(n => n.StartsWith("UnityEngine"))) + { + throw new InvalidOperationException($"[Magic.Unity] player compilation reference set looks degenerate ({PlayerReferenceNames.Count} entries), refusing to generate IL2CPP workarounds from it"); + } + UnityEngine.Debug.Log($"[CollectAllReferencedAssemblies] player compilation references: {string.Join(",", PlayerReferenceNames.OrderBy(n => n))}"); var assemblyCSharp = AssemblyDefinition.ReadAssembly("Library/ScriptAssemblies/Assembly-CSharp.dll"); var referencedAssemblies = CollectAllReferencedAssemblies(assemblyCSharp); UnityEngine.Debug.Log($"[CollectAllReferencedAssemblies] {string.Join(",", referencedAssemblies)}"); From 2ee4eaee1e531e58ea9896a5813ed30eb1d1ac02 Mon Sep 17 00:00:00 2001 From: loicb Date: Fri, 5 Jun 2026 15:49:06 +0800 Subject: [PATCH 2/9] fix(magic-unity): compile against stock ClojureCLR in coexisting editors (#24) A stock ClojureCLR in Assets makes Unity dedup Clojure.dll in favor of the Assets copy, so the package compiles against stock and fails on fork-only bootstrap API (CS0117), and the Editor pre-build rewrite resolves against the wrong DLLs. Bind the fork-only bootstrap members in Magic.Unity.cs via reflection; with stock present the bootstrap is skipped. Anchor the Editor rewrite to the package install path instead of typeof/Assembly.Load and drop the Clojure and Magic.Runtime precompiled references from the Editor asmdef. Closes #24 --- .../GenerateGenericWorkaroundMethods.cs | 4 +- magic-unity/Editor/IL2CPPWorkarounds.cs | 3 +- magic-unity/Editor/Magic.Unity.Editor.asmdef | 4 +- magic-unity/Editor/PackageExportPath.cs | 25 +++++++++ magic-unity/Editor/PackageExportPath.cs.meta | 11 ++++ magic-unity/Runtime/Magic.Unity.cs | 51 +++++++++++++++---- 6 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 magic-unity/Editor/PackageExportPath.cs create mode 100644 magic-unity/Editor/PackageExportPath.cs.meta diff --git a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs index 6fae0abf..3feb80f8 100644 --- a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs +++ b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs @@ -60,7 +60,7 @@ static HashSet CollectAllReferencedAssemblies(AssemblyDefini seen.Add(assydef); var resolver = assydef.MainModule.AssemblyResolver as DefaultAssemblyResolver; resolver.AddSearchDirectory("Library/ScriptAssemblies"); - resolver.AddSearchDirectory(System.IO.Path.GetDirectoryName(typeof(clojure.lang.RT).Assembly.Location)); + resolver.AddSearchDirectory(PackageExportPath.ExportDirectory); resolver.AddSearchDirectory(System.IO.Path.GetDirectoryName(typeof(string).Assembly.Location)); resolver.AddSearchDirectory(System.IO.Path.GetDirectoryName(typeof(UnityEngine.GameObject).Assembly.Location)); @@ -115,7 +115,7 @@ public static void Init() .ToList(); MagicRuntimeDelegateHelpers = AssemblyDefinition - .ReadAssembly(typeof(Magic.Runtime).Assembly.Location) + .ReadAssembly(PackageExportPath.MagicRuntimeDll) .MainModule .Types .Where(t => t.FullName == "Magic.DelegateHelpers").Single(); diff --git a/magic-unity/Editor/IL2CPPWorkarounds.cs b/magic-unity/Editor/IL2CPPWorkarounds.cs index 95473de2..90851835 100644 --- a/magic-unity/Editor/IL2CPPWorkarounds.cs +++ b/magic-unity/Editor/IL2CPPWorkarounds.cs @@ -8,7 +8,6 @@ using UnityEditor; using UnityEditor.Build; using UnityEngine; -using System.Reflection; namespace Magic.Unity { @@ -40,7 +39,7 @@ public static void RewriteAssemblies(IEnumerable files) static void RewriteAssembly(string file) { - var runtimeLocation = Path.GetDirectoryName(Assembly.Load("Magic.Runtime").Location); + var runtimeLocation = PackageExportPath.ExportDirectory; Debug.LogFormat($"[Magic.Unity] runtime location {runtimeLocation}"); var resolver = new DefaultAssemblyResolver(); diff --git a/magic-unity/Editor/Magic.Unity.Editor.asmdef b/magic-unity/Editor/Magic.Unity.Editor.asmdef index 197fa240..fc693059 100644 --- a/magic-unity/Editor/Magic.Unity.Editor.asmdef +++ b/magic-unity/Editor/Magic.Unity.Editor.asmdef @@ -12,9 +12,7 @@ "Mono.Cecil.dll", "Mono.Cecil.Rocks.dll", "Mono.Cecil.Mdb.dll", - "Mono.Cecil.Pdb.dll", - "Clojure.dll", - "Magic.Runtime.dll" + "Mono.Cecil.Pdb.dll" ], "autoReferenced": true, "defineConstraints": [], diff --git a/magic-unity/Editor/PackageExportPath.cs b/magic-unity/Editor/PackageExportPath.cs new file mode 100644 index 00000000..01e6b659 --- /dev/null +++ b/magic-unity/Editor/PackageExportPath.cs @@ -0,0 +1,25 @@ +using System.IO; +using UnityEditor.PackageManager; + +namespace Magic.Unity +{ + // The pre-build rewrite must read the package's own runtime DLLs. They + // cannot be located through typeof(...).Assembly.Location or + // Assembly.Load: when a consumer keeps a stock ClojureCLR in Assets, + // Unity dedups Clojure.dll by file name and those anchors bind to the + // stock copy. Resolve the package install path instead; resolvedPath is + // the physical location for git, registry, local and embedded packages. + internal static class PackageExportPath + { + internal static string ExportDirectory + { + get + { + var package = PackageInfo.FindForAssembly(typeof(PackageExportPath).Assembly); + return Path.Combine(package.resolvedPath, "Runtime", "Infrastructure", "Export"); + } + } + + internal static string MagicRuntimeDll => Path.Combine(ExportDirectory, "Magic.Runtime.dll"); + } +} diff --git a/magic-unity/Editor/PackageExportPath.cs.meta b/magic-unity/Editor/PackageExportPath.cs.meta new file mode 100644 index 00000000..b9737b2c --- /dev/null +++ b/magic-unity/Editor/PackageExportPath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fe3abe2787ece4661b1208b2730c70c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/magic-unity/Runtime/Magic.Unity.cs b/magic-unity/Runtime/Magic.Unity.cs index 106b2bc6..82c3af94 100644 --- a/magic-unity/Runtime/Magic.Unity.cs +++ b/magic-unity/Runtime/Magic.Unity.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using clojure.lang; namespace Magic.Unity @@ -23,20 +24,50 @@ public static void Boot() if (!_booted) { _booted = true; + BootMagicRuntime(); + RequireVar = RT.var("clojure.core", "require"); + } + } + + // The bootstrap below uses API that only exists in MAGIC's Clojure + // fork: RuntimeBootstrapFlag.CodeLoadOrder, RT.Initialize with the + // doRuntimePostBoostrap parameter, and RT.TryLoadInitType. A consumer + // may keep a stock ClojureCLR in Assets for in-editor runtime + // compilation; Unity dedups managed plugins by file name, so this + // file can end up compiled against stock Clojure.dll. Bind the + // fork-only members via reflection: when the fork is present this + // runs the exact same bootstrap as before, when it is absent (stock) + // the bootstrap is skipped and stock self-initializes on first + // RT.var. + static void BootMagicRuntime() + { + var clojureAssembly = typeof(RT).Assembly; + var bootstrapFlagType = clojureAssembly.GetType("clojure.lang.RuntimeBootstrapFlag"); + var codeLoadOrderField = bootstrapFlagType?.GetField("CodeLoadOrder", BindingFlags.Public | BindingFlags.Static); + var codeSourceType = bootstrapFlagType?.GetNestedType("CodeSource"); + var initializeMethod = typeof(RT).GetMethod("Initialize", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(bool), typeof(bool) }, null); + var tryLoadInitTypeMethod = typeof(RT).GetMethod("TryLoadInitType", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null); + if (codeLoadOrderField == null || codeSourceType == null || initializeMethod == null || tryLoadInitTypeMethod == null) + { + return; + } #if UNITY_EDITOR - RuntimeBootstrapFlag.CodeLoadOrder = new[] { - RuntimeBootstrapFlag.CodeSource.InitType, - RuntimeBootstrapFlag.CodeSource.FileSystem - }; + SetCodeLoadOrder(codeLoadOrderField, codeSourceType, new[] { "InitType", "FileSystem" }); #elif ENABLE_IL2CPP - RuntimeBootstrapFlag.CodeLoadOrder = new[] { - RuntimeBootstrapFlag.CodeSource.InitType - }; + SetCodeLoadOrder(codeLoadOrderField, codeSourceType, new[] { "InitType" }); #endif - RT.Initialize(doRuntimePostBoostrap: false); - RT.TryLoadInitType("clojure/core"); - RequireVar = RT.var("clojure.core", "require"); + initializeMethod.Invoke(null, new object[] { true, false }); + tryLoadInitTypeMethod.Invoke(null, new object[] { "clojure/core" }); + } + + static void SetCodeLoadOrder(FieldInfo codeLoadOrderField, Type codeSourceType, string[] sourceNames) + { + var codeLoadOrder = Array.CreateInstance(codeSourceType, sourceNames.Length); + for (var i = 0; i < sourceNames.Length; i++) + { + codeLoadOrder.SetValue(Enum.Parse(codeSourceType, sourceNames[i]), i); } + codeLoadOrderField.SetValue(null, codeLoadOrder); } /// From 465a960099449af5fa8ec9162ace40e1cca1e5d4 Mon Sep 17 00:00:00 2001 From: loicb Date: Fri, 5 Jun 2026 18:42:30 +0800 Subject: [PATCH 3/9] fix(magic-unity): keep fork clj.dlls out of stock ClojureCLR editors (#25) Stock clojure.lang.RT probes Assembly.Load("clojure.core.clj") during init, finds the fork-compiled DLL, and throws a TypeLoadException storm. While a strong-named Clojure.dll is present under Assets, an asset preprocessor now imports fork clj.dll plugins with editor loading off, and a reconcile step reimports them when that state transitions, in either direction. The IL2CPP rewrite and link.xml generator discover clj assemblies from player compilation references instead of the editor domain, so a coexisting editor cannot silently produce a build without workarounds or link.xml entries. Closes #25 --- magic-unity/Editor/IL2CPPWorkarounds.cs | 10 +- magic-unity/Editor/LinkXmlGenerator.cs | 6 +- magic-unity/Editor/MagicPreprocessor.cs | 1 + magic-unity/Editor/PackageExportPath.cs | 27 +- magic-unity/Editor/PlayerCljAssemblies.cs | 37 +++ .../Editor/PlayerCljAssemblies.cs.meta | 11 + magic-unity/Editor/StockClojureCoexistence.cs | 258 ++++++++++++++++++ .../Editor/StockClojureCoexistence.cs.meta | 11 + 8 files changed, 345 insertions(+), 16 deletions(-) create mode 100644 magic-unity/Editor/PlayerCljAssemblies.cs create mode 100644 magic-unity/Editor/PlayerCljAssemblies.cs.meta create mode 100644 magic-unity/Editor/StockClojureCoexistence.cs create mode 100644 magic-unity/Editor/StockClojureCoexistence.cs.meta diff --git a/magic-unity/Editor/IL2CPPWorkarounds.cs b/magic-unity/Editor/IL2CPPWorkarounds.cs index 90851835..c1679c62 100644 --- a/magic-unity/Editor/IL2CPPWorkarounds.cs +++ b/magic-unity/Editor/IL2CPPWorkarounds.cs @@ -21,17 +21,15 @@ static bool IsIL2CPPEnabled() public static void RewriteAssemblies() { - var cljAssemblies = AppDomain.CurrentDomain - .GetAssemblies() - .Where(a => a.FullName.Contains(".clj")) - .Select(a => a.Location); - RewriteAssemblies(cljAssemblies); + RewriteAssemblies(PlayerCljAssemblies.Paths()); } public static void RewriteAssemblies(IEnumerable files) { + var fileList = files.ToList(); + Debug.LogFormat("[Magic.Unity] rewriting {0} clj assemblies", fileList.Count); GenerateGenericWorkaroundMethods.Init(); - foreach (var file in files) + foreach (var file in fileList) { RewriteAssembly(file); } diff --git a/magic-unity/Editor/LinkXmlGenerator.cs b/magic-unity/Editor/LinkXmlGenerator.cs index 993e1e43..7b4ed31e 100644 --- a/magic-unity/Editor/LinkXmlGenerator.cs +++ b/magic-unity/Editor/LinkXmlGenerator.cs @@ -12,10 +12,8 @@ static class LinkXmlGenerator { public static void BuildLinkXml() { - var cljAssemblies = AppDomain.CurrentDomain - .GetAssemblies() - .Where(a => a.FullName.Contains(".clj")) - .Select(a => a.FullName) + var cljAssemblies = PlayerCljAssemblies.Paths() + .Select(Path.GetFileNameWithoutExtension) .Concat(new[] { "Clojure", "Magic.Runtime" }); BuildLinkXml(cljAssemblies); diff --git a/magic-unity/Editor/MagicPreprocessor.cs b/magic-unity/Editor/MagicPreprocessor.cs index a69efd15..da965abd 100644 --- a/magic-unity/Editor/MagicPreprocessor.cs +++ b/magic-unity/Editor/MagicPreprocessor.cs @@ -28,6 +28,7 @@ public void OnPreprocessBuild(BuildReport report) try { + StockClojureCoexistence.Reconcile(); IL2CPPWorkarounds.RewriteAssemblies(); LinkXmlGenerator.BuildLinkXml(); } catch (Exception e) diff --git a/magic-unity/Editor/PackageExportPath.cs b/magic-unity/Editor/PackageExportPath.cs index 01e6b659..4192c76c 100644 --- a/magic-unity/Editor/PackageExportPath.cs +++ b/magic-unity/Editor/PackageExportPath.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using UnityEditor.PackageManager; @@ -11,15 +12,29 @@ namespace Magic.Unity // the physical location for git, registry, local and embedded packages. internal static class PackageExportPath { - internal static string ExportDirectory + static PackageInfo Package => PackageInfo.FindForAssembly(typeof(PackageExportPath).Assembly); + + internal static string ExportDirectory => Path.Combine(Package.resolvedPath, "Runtime", "Infrastructure", "Export"); + + internal static string MagicRuntimeDll => Path.Combine(ExportDirectory, "Magic.Runtime.dll"); + + internal static string ExportAssetPath => Package.assetPath + "/Runtime/Infrastructure/Export"; + + // Asset paths under Packages/ are virtual; map them to the physical + // location before any File or Cecil access. Assets/ paths and + // absolute paths pass through. + internal static string PhysicalPath(string assetPath) { - get + if (assetPath.StartsWith("Packages/", StringComparison.Ordinal)) { - var package = PackageInfo.FindForAssembly(typeof(PackageExportPath).Assembly); - return Path.Combine(package.resolvedPath, "Runtime", "Infrastructure", "Export"); + var package = PackageInfo.FindForAssetPath(assetPath); + if (package != null) + { + var relative = assetPath.Substring(package.assetPath.Length + 1); + return Path.Combine(package.resolvedPath, relative); + } } + return assetPath; } - - internal static string MagicRuntimeDll => Path.Combine(ExportDirectory, "Magic.Runtime.dll"); } } diff --git a/magic-unity/Editor/PlayerCljAssemblies.cs b/magic-unity/Editor/PlayerCljAssemblies.cs new file mode 100644 index 00000000..d64976ba --- /dev/null +++ b/magic-unity/Editor/PlayerCljAssemblies.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor.Compilation; + +namespace Magic.Unity +{ + // The pre-build rewrite and the link.xml generator used to discover clj + // assemblies by scanning the loaded AppDomain. In a coexisting editor + // the fork runtime DLLs are excluded from the editor domain (see + // StockClojureCoexistence), which would turn that scan into a silent + // no-op: no workarounds generated, no link.xml entries, devices fail at + // runtime. Discover them from player compilation references instead; + // that is the set that actually ships, independent of editor state. + internal static class PlayerCljAssemblies + { + internal static List Paths() + { + var paths = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var assembly in CompilationPipeline.GetAssemblies(AssembliesType.PlayerWithoutTestAssemblies)) + { + foreach (var reference in assembly.allReferences) + { + if (reference.EndsWith(".clj.dll", StringComparison.OrdinalIgnoreCase)) + { + paths.Add(PackageExportPath.PhysicalPath(reference)); + } + } + } + if (paths.Count == 0) + { + throw new InvalidOperationException("[Magic.Unity] no .clj.dll player compilation references found, refusing to continue with an empty clj assembly set"); + } + return paths.OrderBy(p => p, StringComparer.OrdinalIgnoreCase).ToList(); + } + } +} diff --git a/magic-unity/Editor/PlayerCljAssemblies.cs.meta b/magic-unity/Editor/PlayerCljAssemblies.cs.meta new file mode 100644 index 00000000..06830c1f --- /dev/null +++ b/magic-unity/Editor/PlayerCljAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57669782459ef435cacc7ef76edd336a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/magic-unity/Editor/StockClojureCoexistence.cs b/magic-unity/Editor/StockClojureCoexistence.cs new file mode 100644 index 00000000..fda47bbd --- /dev/null +++ b/magic-unity/Editor/StockClojureCoexistence.cs @@ -0,0 +1,258 @@ +using System; +using System.IO; +using System.Linq; +using Mono.Cecil; +using UnityEditor; +using UnityEngine; + +namespace Magic.Unity +{ + // Stock clojure.lang.RT probes Assembly.Load("clojure.core.clj") during + // init; finding the fork-compiled DLL throws a TypeLoadException storm. + // So while a foreign (strong-named) Clojure.dll is present under + // Assets, fork .clj.dll plugins are imported with editor loading off. + // Only the clj.dlls: excluding the fork Clojure.dll itself would leave + // Magic.Unity with no Clojure to compile against once the stock copy + // is removed, breaking the revert under batch mode. + // + // Separate bootstrap class: import callbacks touch the helper class, + // and a static ctor that throws mid import (asset database calls do) + // would poison it for the whole domain. + [InitializeOnLoad] + internal static class StockClojureCoexistenceBootstrap + { + static StockClojureCoexistenceBootstrap() + { + // delayCall may never fire before a batch run ends; the direct + // call throws when the domain load is inside an import batch. + if (Application.isBatchMode) + { + try + { + StockClojureCoexistence.Reconcile(); + } + catch (UnityException) + { + EditorApplication.delayCall += StockClojureCoexistence.Reconcile; + } + } + else + { + EditorApplication.delayCall += StockClojureCoexistence.Reconcile; + } + } + } + + internal static class StockClojureCoexistence + { + // The state file shares the artifact database's lifetime: a Library + // wipe loses both, and the rebuilt artifacts go through the + // preprocessor with the current state anyway. + public static void Reconcile() + { + var foreign = ForeignClojurePresent(); + var current = foreign ? "foreign-clojure" : "pure-magic"; + if (LastAppliedState() == current) + { + return; + } + Debug.Log(foreign + ? "[Magic.Unity/StockClojureCoexistence] foreign Clojure.dll detected, reimporting fork clj.dll plugins with editor loading off" + : "[Magic.Unity/StockClojureCoexistence] no foreign Clojure.dll present, reimporting fork clj.dll plugins with their pristine settings"); + foreach (var importer in PluginImporter.GetAllImporters()) + { + if (!importer.isNativePlugin && importer.assetPath.EndsWith(".clj.dll", StringComparison.OrdinalIgnoreCase)) + { + AssetDatabase.ImportAsset(importer.assetPath); + } + } + try + { + File.WriteAllText(StateFilePath, current); + } + catch (Exception e) + { + Debug.LogWarning($"[Magic.Unity/StockClojureCoexistence] could not write {StateFilePath}: {e.Message}"); + } + } + + static string StateFilePath => + Path.Combine(Path.GetDirectoryName(Application.dataPath), "Library", "MagicUnityCoexistenceState.txt"); + + static string LastAppliedState() + { + try + { + return File.Exists(StateFilePath) ? File.ReadAllText(StateFilePath).Trim() : ""; + } + catch (Exception) + { + return ""; + } + } + + // Filesystem scan, not a PluginImporter scan: the asset database is + // not queryable inside import callbacks. A vendored fork copy in + // Assets has no strong name and stays non-foreign. + internal static bool ForeignClojurePresent() + { + try + { + return Directory.EnumerateFiles(Application.dataPath, "Clojure.dll", SearchOption.AllDirectories).Any(IsStockClojure); + } + catch (Exception e) + { + Debug.LogWarning($"[Magic.Unity/StockClojureCoexistence] foreign Clojure scan failed: {e.Message}"); + return false; + } + } + + static bool IsStockClojure(string path) + { + try + { + var token = System.Reflection.AssemblyName.GetAssemblyName(path).GetPublicKeyToken(); + return token != null && token.Length > 0; + } + catch (Exception e) + { + Debug.LogWarning($"[Magic.Unity/StockClojureCoexistence] could not inspect {path}: {e.Message}"); + return false; + } + } + + internal static bool ReferencesForkClojure(string path) + { + try + { + using (var assembly = AssemblyDefinition.ReadAssembly(path)) + { + var clojureReference = assembly.MainModule.AssemblyReferences.FirstOrDefault(r => r.Name == "Clojure"); + return clojureReference != null + && (clojureReference.PublicKeyToken == null || clojureReference.PublicKeyToken.Length == 0); + } + } + catch (Exception e) + { + Debug.LogWarning($"[Magic.Unity/StockClojureCoexistence] could not inspect {path}: {e.Message}"); + return false; + } + } + + internal static System.Collections.Generic.IEnumerable ValidBuildTargets() + { + foreach (BuildTarget target in Enum.GetValues(typeof(BuildTarget))) + { + if ((int)target < 0) + { + continue; + } + var field = typeof(BuildTarget).GetField(target.ToString()); + if (field == null || field.GetCustomAttributes(typeof(ObsoleteAttribute), false).Any()) + { + continue; + } + yield return target; + } + } + } + + // Importer changes made here are written back to the .meta on mutable + // installs (the userData marker drives the restore) and stay + // artifact-only on immutable installs, where the pristine .meta is the + // restore state. + internal class CljPluginPreprocessor : AssetPostprocessor + { + void OnPreprocessAsset() + { + if (!assetPath.EndsWith(".clj.dll", StringComparison.OrdinalIgnoreCase)) + { + return; + } + var importer = assetImporter as PluginImporter; + if (importer == null) + { + return; + } + if (StockClojureCoexistence.ForeignClojurePresent()) + { + DisableEditorLoading(importer); + } + else + { + RestoreEditorLoading(importer); + } + } + + // SetExcludeEditorFromAnyPlatform is not honored by the plugin + // loader, hence the switch to explicit per-platform compatibility. + // Restore goes by marker only, never touching a plugin the consumer + // made editor-only themselves. + const string AnyPlatformMarker = "Magic.Unity.StockClojureCoexistence:any-platform"; + const string CustomPlatformsMarker = "Magic.Unity.StockClojureCoexistence:custom-platforms"; + + static void DisableEditorLoading(PluginImporter importer) + { + if (HasMarker(importer) + || (!importer.GetCompatibleWithAnyPlatform() && !importer.GetCompatibleWithEditor())) + { + return; + } + if (!StockClojureCoexistence.ReferencesForkClojure(PackageExportPath.PhysicalPath(importer.assetPath))) + { + return; + } + string marker; + if (importer.GetCompatibleWithAnyPlatform()) + { + importer.SetCompatibleWithAnyPlatform(false); + foreach (var target in StockClojureCoexistence.ValidBuildTargets()) + { + try { importer.SetCompatibleWithPlatform(target, true); } catch { } + } + marker = AnyPlatformMarker; + } + else + { + marker = CustomPlatformsMarker; + } + AddMarker(importer, marker); + importer.SetCompatibleWithEditor(false); + Debug.Log($"[Magic.Unity/StockClojureCoexistence] editor loading off for {importer.assetPath}"); + } + + static void RestoreEditorLoading(PluginImporter importer) + { + if (!HasMarker(importer)) + { + return; + } + var wasAnyPlatform = importer.userData.Contains(AnyPlatformMarker); + RemoveMarker(importer); + importer.SetCompatibleWithEditor(true); + if (wasAnyPlatform) + { + importer.SetCompatibleWithAnyPlatform(true); + importer.SetExcludeEditorFromAnyPlatform(false); + } + Debug.Log($"[Magic.Unity/StockClojureCoexistence] editor loading on for {importer.assetPath}"); + } + + static bool HasMarker(PluginImporter importer) + { + return importer.userData != null && importer.userData.Contains("Magic.Unity.StockClojureCoexistence:"); + } + + static void AddMarker(PluginImporter importer, string marker) + { + importer.userData = string.IsNullOrEmpty(importer.userData) ? marker : marker + ";" + importer.userData; + } + + static void RemoveMarker(PluginImporter importer) + { + importer.userData = importer.userData + .Replace(AnyPlatformMarker + ";", "").Replace(AnyPlatformMarker, "") + .Replace(CustomPlatformsMarker + ";", "").Replace(CustomPlatformsMarker, ""); + } + } +} diff --git a/magic-unity/Editor/StockClojureCoexistence.cs.meta b/magic-unity/Editor/StockClojureCoexistence.cs.meta new file mode 100644 index 00000000..3a070498 --- /dev/null +++ b/magic-unity/Editor/StockClojureCoexistence.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9043d4d40082b4d17a1f739ceb0c212c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 1c9627e8f366f5537fad30b9dd4880114e9affd6 Mon Sep 17 00:00:00 2001 From: loicb Date: Fri, 5 Jun 2026 18:50:10 +0800 Subject: [PATCH 4/9] fix(magic-unity): search project-local player reference dirs in the workaround resolver (#26) The Cecil resolver in GenerateGenericWorkaroundMethods only searched four directories, so player assemblies under Library/PackageCache or in Assets plugin folders failed to resolve and silently dropped out of the reference walk, leaving their generic value-type signatures without IL2CPP workarounds. Add the directory of every player compilation reference to the resolver, excluding editor install paths: resolving the BCL profile facades there would expand the workaround closure into desktop BCL assemblies and force them into every player build. Smoke emission stays byte-identical. Closes #26 --- .../GenerateGenericWorkaroundMethods.cs | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs index 3feb80f8..87094239 100644 --- a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs +++ b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs @@ -23,6 +23,7 @@ struct DynamicCallSiteInfo static TypeDefinition MagicRuntimeDelegateHelpers = null; static HashSet PlayerReferenceNames = null; + static HashSet PlayerReferenceDirectories = null; // The collected assemblies feed AllMethods, the pool of candidate dynamic // dispatch targets whose GetMethodDelegateFast instantiations get emitted @@ -48,6 +49,33 @@ static HashSet CollectPlayerReferenceNames() return names; } + // Player assemblies live in more places than the four directories + // below: UPM packages resolve under Library/PackageCache and + // precompiled plugins sit anywhere in Assets. Without their + // directories the resolver silently drops those assemblies from the + // reference walk and their signatures get no workarounds. Editor + // install paths stay excluded on purpose: searching the BCL profile + // directories makes the netstandard facade resolve, which expands + // the workaround closure into desktop BCL assemblies (System.Data + // and friends) and forces them into every player build. + static HashSet CollectPlayerReferenceDirectories() + { + var editorInstall = System.IO.Path.GetDirectoryName(UnityEditor.EditorApplication.applicationPath); + var directories = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var assembly in CompilationPipeline.GetAssemblies(AssembliesType.PlayerWithoutTestAssemblies)) + { + foreach (var reference in assembly.allReferences) + { + var physical = System.IO.Path.GetFullPath(PackageExportPath.PhysicalPath(reference)); + if (!physical.StartsWith(editorInstall, StringComparison.OrdinalIgnoreCase)) + { + directories.Add(System.IO.Path.GetDirectoryName(physical)); + } + } + } + return directories; + } + static bool ShouldCollectReferencedAssembly(AssemblyDefinition assy) { return PlayerReferenceNames.Contains(assy.Name.Name); @@ -63,7 +91,11 @@ static HashSet CollectAllReferencedAssemblies(AssemblyDefini resolver.AddSearchDirectory(PackageExportPath.ExportDirectory); resolver.AddSearchDirectory(System.IO.Path.GetDirectoryName(typeof(string).Assembly.Location)); resolver.AddSearchDirectory(System.IO.Path.GetDirectoryName(typeof(UnityEngine.GameObject).Assembly.Location)); - + foreach (var directory in PlayerReferenceDirectories) + { + resolver.AddSearchDirectory(directory); + } + foreach (var tr in assydef.MainModule.GetTypeReferences()) { try @@ -93,6 +125,7 @@ static HashSet CollectAllReferencedAssemblies(AssemblyDefini public static void Init() { PlayerReferenceNames = CollectPlayerReferenceNames(); + PlayerReferenceDirectories = CollectPlayerReferenceDirectories(); // A degenerate reference set would silently turn workaround // generation into a no-op: the build stays green and devices throw // ExecutionEngineException at runtime. Fail the build instead. Any From 971c209ceff3db53006a053074a19b5771cdf778 Mon Sep 17 00:00:00 2001 From: loicb Date: Fri, 5 Jun 2026 19:03:03 +0800 Subject: [PATCH 5/9] fix(magic-unity): include csc.rsp references in the workaround signature allowlist CompilationPipeline allReferences never lists references added through compiler response files, but player code compiles and ships against them, verified with csc.rsp -r:System.Web.dll on the .NET 4.x API profile. Signatures touching such assemblies were silently dropped from IL2CPP workaround generation. Parse the -r:/-reference: entries of each player assembly's compilerOptions.ResponseFiles into the allowlist. Smoke emission stays byte-identical on the netstandard profile, where the compat shims already cover these references. --- .../GenerateGenericWorkaroundMethods.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs index 87094239..925a0b41 100644 --- a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs +++ b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs @@ -45,6 +45,57 @@ static HashSet CollectPlayerReferenceNames() { names.Add(System.IO.Path.GetFileNameWithoutExtension(reference)); } + foreach (var reference in ResponseFileReferenceNames(assembly)) + { + names.Add(reference); + } + } + return names; + } + + // allReferences never lists references added through compiler + // response files (csc.rsp -r:System.Web.dll), but player code + // compiles and ships against them, so their signatures deserve + // workarounds too. + static IEnumerable ResponseFileReferenceNames(UnityEditor.Compilation.Assembly assembly) + { + var names = new List(); + foreach (var responseFile in assembly.compilerOptions.ResponseFiles) + { + string[] lines; + try + { + lines = System.IO.File.ReadAllLines(responseFile); + } + catch (Exception e) + { + UnityEngine.Debug.LogWarning($"[Magic.Unity] could not read response file {responseFile}: {e.Message}"); + continue; + } + foreach (var rawLine in lines) + { + var line = rawLine.Trim(); + string value = null; + if (line.StartsWith("-r:") || line.StartsWith("/r:")) + { + value = line.Substring(3); + } + else if (line.StartsWith("-reference:") || line.StartsWith("/reference:")) + { + value = line.Substring(11); + } + if (value == null) + { + continue; + } + // extern alias form: -r:alias=Assembly.dll + var aliasSeparator = value.IndexOf('='); + if (aliasSeparator >= 0) + { + value = value.Substring(aliasSeparator + 1); + } + names.Add(System.IO.Path.GetFileNameWithoutExtension(value.Trim().Trim('"'))); + } } return names; } From 50222d2ce5a44ebacd36f029306d98ebf3fce812 Mon Sep 17 00:00:00 2001 From: loicb Date: Sun, 7 Jun 2026 15:37:58 +0800 Subject: [PATCH 6/9] fix(compiler): emit castclass on deftype set! of hinted mutable fields (#27) The deftype set! compiler emitted stfld without converting the stack value to the field type, unlike the generic set! compiler. A value from an invoke return keeps its static type (often Object), and stfld of that into a type-hinted mutable field is unverifiable IL: Mono accepts it, IL2CPP rejects the generated C++ with incompatible pointer type errors. Add the same convert step the generic set! compiler already has, and a smoke regression case that sets a hinted mutable field from a conj return. Closes #27 --- magic-compiler/src/magic/core.clj | 5 +++++ .../Assets/Clojure/smoke/polymorphism.clj | 21 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/magic-compiler/src/magic/core.clj b/magic-compiler/src/magic/core.clj index 7a803a77..cb6b19da 100644 --- a/magic-compiler/src/magic/core.clj +++ b/magic-compiler/src/magic/core.clj @@ -2161,6 +2161,11 @@ (when value-used? [(il/stloc val-local) (il/ldloc val-local)]) + ;; without this the stack value keeps its static type + ;; (often Object from an invoke return) and stfld into + ;; a hinted field emits unverifiable IL: Mono accepts + ;; it, IL2CPP rejects the generated C++. + (convert val (.FieldType field)) (when (volatile? field) (il/volatile)) (il/stfld field) diff --git a/magic-unity-smoke/Assets/Clojure/smoke/polymorphism.clj b/magic-unity-smoke/Assets/Clojure/smoke/polymorphism.clj index d3d010cf..d76b69b0 100644 --- a/magic-unity-smoke/Assets/Clojure/smoke/polymorphism.clj +++ b/magic-unity-smoke/Assets/Clojure/smoke/polymorphism.clj @@ -1,9 +1,12 @@ (ns smoke.polymorphism "Polymorphism mechanisms: protocols, reify, deftype, defrecord, and multimethods. Exercises protocol dispatch, generic sharing, - and the protocol method-cache under IL2CPP. No develop-branch - fix is bound specifically to one of these checks; the suite is - broad construct coverage to catch dispatch-related regressions.") + and the protocol method-cache under IL2CPP. The mutable-field + set! check is bound to the deftype set! castclass fix: stfld of + an invoke return into a type-hinted mutable field used to emit + without castclass, unverifiable IL that Mono accepts and IL2CPP + rejects at C++ compile time. The rest of the suite is broad + construct coverage to catch dispatch-related regressions.") (defn- pass [n] {:name n :pass? true}) (defn- fail [n detail] {:name n :pass? false :detail detail}) @@ -31,6 +34,15 @@ (area [_] (* w h)) (label [_] "box")) +(defprotocol IAccum + (add-item [a x]) + (item-vec [a])) + +(deftype Accum [^:unsynchronized-mutable ^clojure.lang.PersistentVector items] + IAccum + (add-item [this x] (set! items (conj items x)) this) + (item-vec [_] items)) + (defmulti animal-sound :kind) (defmethod animal-sound :dog [_] "woof") (defmethod animal-sound :cat [_] "meow") @@ -57,6 +69,9 @@ (ToString [] "i-am-proxy"))] (.ToString o)) "i-am-proxy") + (check "deftype set! hinted mutable field from invoke return" + #(item-vec (-> (->Accum []) (add-item 1) (add-item 2))) + [1 2]) (check "multimethod :dog" #(animal-sound {:kind :dog}) "woof") (check "multimethod default" From 44a7cc8ce6bd5aba4da56adbd82201b460202546 Mon Sep 17 00:00:00 2001 From: loicb Date: Sun, 7 Jun 2026 15:38:08 +0800 Subject: [PATCH 7/9] chore(bootstrap): refresh magic.core DLL for deftype set! castclass fix (#27) --- nostrand/references/magic.core.clj.dll | Bin 302592 -> 302592 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/nostrand/references/magic.core.clj.dll b/nostrand/references/magic.core.clj.dll index 427cc7a8a8749f2d59652c1e26ebbde5031defe3..dabe2b0c0556ee252ed172daad588c1b8e52f79d 100755 GIT binary patch delta 63253 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

p_E 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

Ji_t$?5e@7nrjXgmmezdY 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 From e17e19856a42b7d3945fb8081a6fe3da0e18bd87 Mon Sep 17 00:00:00 2001 From: loicb Date: Sun, 7 Jun 2026 15:38:34 +0800 Subject: [PATCH 8/9] fix(magic-unity): log response file references on their own line The full player reference list exceeds Unity's log line limit on large projects, so the csc.rsp contribution to the workaround allowlist was unverifiable from the editor log. Log the response file references on their own short line, "(none)" when empty. --- magic-unity/Editor/GenerateGenericWorkaroundMethods.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs index 925a0b41..bdc1498e 100644 --- a/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs +++ b/magic-unity/Editor/GenerateGenericWorkaroundMethods.cs @@ -38,6 +38,7 @@ struct DynamicCallSiteInfo static HashSet CollectPlayerReferenceNames() { var names = new HashSet(StringComparer.OrdinalIgnoreCase); + var responseFileNames = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var assembly in CompilationPipeline.GetAssemblies(AssembliesType.PlayerWithoutTestAssemblies)) { names.Add(assembly.name); @@ -48,8 +49,12 @@ static HashSet CollectPlayerReferenceNames() foreach (var reference in ResponseFileReferenceNames(assembly)) { names.Add(reference); + responseFileNames.Add(reference); } } + // The full reference list below can exceed Unity's log line limit, + // so the response file contribution gets its own observable line. + UnityEngine.Debug.Log($"[Magic.Unity] response file references: {(responseFileNames.Count == 0 ? "(none)" : string.Join(",", responseFileNames.OrderBy(n => n)))}"); return names; } From 147779d75f3ffa72d29f08f94263ac60d0ef8f47 Mon Sep 17 00:00:00 2001 From: loicb Date: Sun, 7 Jun 2026 23:37:23 +0800 Subject: [PATCH 9/9] chore(release): v0.6.0 --- CHANGELOG.md | 14 ++++++++++++++ magic-unity/README.md | 1 + magic-unity/package.json | 2 +- version.edn | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab7b1435..26cdac68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## v0.6.0 - 2026-06-07 + +Stock-ClojureCLR coexistence for Unity consumers that keep ClojureCLR as the editor runtime, plus IL2CPP workaround-selection fixes. + +### Compiler +- `set!` on a hinted mutable deftype field emits a `castclass`, fixing unverifiable IL that IL2CPP's transpiler rejects - [#27](https://github.com/flybot-sg/magic/issues/27). + +### Magic.Unity +- Coexistence: while a strong-named `Clojure.dll` is under `Assets`, fork `.clj.dll` plugins are excluded from the editor (and restored when it leaves), keeping stock RT's `clojure.core.clj` probe away from fork assemblies - [#25](https://github.com/flybot-sg/magic/issues/25). Editor scripts compile against the stock assembly in that state - [#24](https://github.com/flybot-sg/magic/issues/24). +- IL2CPP workaround signatures come from player compilation references instead of an editor AppDomain scan, keeping editor-only assemblies (e.g. `Mono.WebBrowser` on Windows) out of the signature pool - [#23](https://github.com/flybot-sg/magic/issues/23). +- The workaround resolver searches project-local player reference directories - [#26](https://github.com/flybot-sg/magic/issues/26). +- `csc.rsp` `-r:` references count as player references and are logged for build-log verification. +- README documents the benign coexistence console lines (`Assembly is incompatible with the editor`). + ## v0.5.0 - 2026-06-04 Consumer quality-of-life fixes from the 0.4.0 rollout. diff --git a/magic-unity/README.md b/magic-unity/README.md index b77f7559..63cb4e89 100644 --- a/magic-unity/README.md +++ b/magic-unity/README.md @@ -28,6 +28,7 @@ See [magic-unity-smoke](../magic-unity-smoke) for a working IL2CPP regression pr 1. You compile your own Clojure namespaces to `.clj.dll` outside Unity via `nos dotnet/build`, writing them into `Assets/Plugins/Magic/` (see [magic-unity-smoke/dotnet.clj](../magic-unity-smoke/dotnet.clj) for the canonical task definition). The package does not include a compiler. 2. Unity opens the project. The prebuilt runtime + stdlib from `Runtime/Infrastructure/Export/` and your own `.clj.dll`s are both loaded as plain .NET assemblies. `Magic.Unity.Clojure.Boot()` initialises the runtime; `Require` / `GetVar` let C# scripts call into Clojure. 3. On every build, `MagicPreprocessor` runs first. When the build target uses IL2CPP, it rewrites the `.clj.dll` bodies in place so the IL2CPP transpiler can consume them (and writes `link.xml` entries); on a Mono build the preprocessor only sweeps any leftover IL2CPP-only workarounds from a previous build. The runtime DLLs are loaded the same way under either backend. +4. Coexistence with stock ClojureCLR: if a strong-named `Clojure.dll` is found under `Assets` (projects that keep ClojureCLR for Editor work and MAGIC for shipped builds), every MAGIC-compiled `.clj.dll` is imported with Editor loading off, because the stock runtime probe-loads `clojure.core.clj` at init and a MAGIC DLL answering that probe fails to load. With the exclusion active, the Editor logs `Assembly '...clj.dll' will not be loaded due to errors: Assembly is incompatible with the editor` for the package's `Export` DLLs on every domain reload. These lines are expected and benign in this setup: they are Unity reporting the intended Editor exclusion, not a real failure. Player builds are unaffected. ## API diff --git a/magic-unity/package.json b/magic-unity/package.json index 25423779..21d4efa9 100644 --- a/magic-unity/package.json +++ b/magic-unity/package.json @@ -1,6 +1,6 @@ { "name": "sg.flybot.magic.unity", - "version": "0.5.0", + "version": "0.6.0", "displayName": "MAGIC Unity Integration", "description": "The integration of the MAGIC Clojure compiler into Unity", "unity": "2021.2", diff --git a/version.edn b/version.edn index 879b88c2..e07bf7ac 100644 --- a/version.edn +++ b/version.edn @@ -1 +1 @@ -{:version "0.5.0"} +{:version "0.6.0"}