#nullable enable using System; using System.IO; using System.Linq; using System.Threading.Tasks; using Mono.Cecil; using ShrinkSDK.CodeGen; if (args.Length == 2 && args[0] == "--marker") { using var exported = AssemblyDefinition.ReadAssembly(args[1]); var marker = exported.CustomAttributes.SingleOrDefault(attribute => attribute.AttributeType.FullName == "ShrinkSDK.Runtime.ShrinkCodeGenWovenAttribute"); Require(marker != null, "exported assembly has no ShrinkSDK CodeGen marker"); Require(exported.MainModule.Types.Any(type => type.FullName == "ShrinkEventBus.Generated.ShrinkGeneratedStaticBindings"), "exported assembly has no static EventBus bootstrap"); Console.WriteLine($"PASS exported assembly woven by {marker!.ConstructorArguments[0].Value}"); return; } if ((args.Length == 3 || args.Length == 4) && args[0] == "--resign") { var signedAssembly = Path.GetFullPath(args[1]); var keepOutput = args.Length == 4; var output = keepOutput ? Path.GetFullPath(args[3]) : Path.Combine(Path.GetTempPath(), "ShrinkSDK.CodeGen.Resigned." + Guid.NewGuid().ToString("N") + ".dll"); var outputPdb = Path.ChangeExtension(output, ".pdb"); try { var result = ShrinkAssemblyWeaver.Weave(signedAssembly, Path.ChangeExtension(signedAssembly, ".pdb"), BuildReferences(Path.GetDirectoryName(signedAssembly)!), output, outputPdb, ShrinkCodeGenPlatform.EngineNeutral, args[2]); Require(result.Succeeded && result.Changed, "signed assembly re-weave failed: " + string.Join(" | ", result.Diagnostics.Select(item => item.Message))); using var resigned = AssemblyDefinition.ReadAssembly(output); Require(resigned.Name.HasPublicKey, "re-signed output has no public key"); Console.WriteLine("PASS signed assembly woven and re-signed with explicit key"); } finally { if (!keepOutput) { if (File.Exists(output)) File.Delete(output); if (File.Exists(outputPdb)) File.Delete(outputPdb); } } return; } if (args.Length != 1 || !File.Exists(args[0])) throw new ArgumentException("Pass the unwoven CodeGenFixture assembly path."); var sourceAssembly = Path.GetFullPath(args[0]); var sourceDirectory = Path.GetDirectoryName(sourceAssembly)!; var sourcePdb = Path.ChangeExtension(sourceAssembly, ".pdb"); var references = BuildReferences(sourceDirectory); var root = Path.Combine(Path.GetTempPath(), "ShrinkSDK.CodeGen.Validation", Guid.NewGuid().ToString("N")); Directory.CreateDirectory(root); try { var woven = Path.Combine(root, "fixture.woven.dll"); var wovenPdb = Path.Combine(root, "fixture.woven.pdb"); var first = ShrinkAssemblyWeaver.Weave(sourceAssembly, sourcePdb, references, woven, wovenPdb); Require(first.Succeeded && first.Changed, "single weave did not change the assembly: " + string.Join(" | ", first.Diagnostics.Select(item => item.Message))); Require(first.InstanceSubscribers == 1 && first.StaticSubscribers == 1 && first.RegistryEntries == 5, $"unexpected generated counts: {first.InstanceSubscribers}/{first.StaticSubscribers}/{first.RegistryEntries}"); VerifyStructure(woven); var second = ShrinkAssemblyWeaver.Weave(woven, wovenPdb, references.Append(woven), Path.Combine(root, "twice.dll"), Path.Combine(root, "twice.pdb")); Require(second.Succeeded && !second.Changed, "repeated weave was not idempotent"); var corruptPdb = Path.Combine(root, "corrupt.pdb"); File.WriteAllText(corruptPdb, "not a portable pdb"); var corrupt = ShrinkAssemblyWeaver.Weave(sourceAssembly, corruptPdb, references, Path.Combine(root, "corrupt.dll"), Path.Combine(root, "corrupt.out.pdb")); Require(!corrupt.Succeeded && !File.Exists(Path.Combine(root, "corrupt.dll")), "damaged PDB was not rejected atomically"); var missing = ShrinkAssemblyWeaver.Weave(sourceAssembly, null, Array.Empty(), Path.Combine(root, "missing.dll"), null); Require(!missing.Succeeded, "missing dependencies were not rejected"); var signedInput = Path.Combine(root, "signed.dll"); using (var assembly = AssemblyDefinition.ReadAssembly(sourceAssembly)) { assembly.Name.PublicKey = Enumerable.Range(0, 160).Select(value => (byte)(value + 1)).ToArray(); assembly.Name.Attributes |= AssemblyAttributes.PublicKey; assembly.Write(signedInput); } var signed = ShrinkAssemblyWeaver.Weave(signedInput, null, references, Path.Combine(root, "signed.out.dll"), null); Require(!signed.Succeeded && signed.Diagnostics.Any(item => item.Message.Contains("Signed assemblies")), "signed assembly was not rejected"); var parallel = Enumerable.Range(0, 4).Select(index => Task.Run(() => { var output = Path.Combine(root, $"parallel-{index}.dll"); var outputPdb = Path.Combine(root, $"parallel-{index}.pdb"); return ShrinkAssemblyWeaver.Weave(sourceAssembly, sourcePdb, references, output, outputPdb); })).ToArray(); await Task.WhenAll(parallel); Require(parallel.All(task => task.Result.Succeeded && task.Result.Changed), "parallel weaving of independent assemblies failed"); Console.WriteLine("PASS Cecil structure + single/repeat/parallel weave + corrupt PDB/signed/missing dependency guards"); } finally { Directory.Delete(root, true); } static void VerifyStructure(string path) { using var assembly = AssemblyDefinition.ReadAssembly(path); var module = assembly.MainModule; var instance = module.Types.Single(type => type.Name == "InstanceSubscriber"); Require(instance.Interfaces.Any(item => item.InterfaceType.FullName == "ShrinkEventBus.IShrinkGeneratedSubscriber"), "generated subscriber interface is missing"); Require(instance.Methods.Any(method => method.Name == "ShrinkEventBus.IShrinkGeneratedSubscriber.AttachGenerated"), "AttachGenerated is missing"); Require(module.Types.Any(type => type.FullName == "ShrinkEventBus.Generated.ShrinkGeneratedStaticBindings"), "static binding bootstrap is missing"); Require(module.Types.Any(type => type.FullName == "ShrinkNetwork.Integration.Generated.ShrinkGeneratedNetworkEventBindings"), "Network/EventBus generated bootstrap is missing"); var moduleInitializer = module.Types.Single(type => type.Name == "").Methods.Single(method => method.Name == ".cctor"); Require(moduleInitializer.Body.Instructions.Any(instruction => instruction.Operand is MethodReference method && method.Name == "Register"), "module initializer does not call static registration"); var attributes = assembly.CustomAttributes.Select(attribute => attribute.AttributeType.FullName).ToArray(); foreach (var expected in new[] { "ShrinkSDK.Runtime.ShrinkCodeGenWovenAttribute", "ShrinkCommand.ShrinkCommandStaticRegistryAttribute", "ShrinkNetwork.ShrinkNetworkMessageRegistryAttribute", "ShrinkNetwork.ShrinkNetworkStaticSubscriberRegistryAttribute" }) Require(attributes.Contains(expected), $"assembly registry is missing: {expected}"); } static void Require(bool condition, string message) { if (!condition) throw new InvalidOperationException(message); } static string[] BuildReferences(string sourceDirectory) { var referencePackRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages", "microsoft.netcore.app.ref"); var referencePack = Directory.GetDirectories(referencePackRoot) .Select(path => Path.Combine(path, "ref", "net8.0")) .Where(Directory.Exists) .OrderByDescending(path => path, StringComparer.OrdinalIgnoreCase) .First(); return Directory.GetFiles(sourceDirectory, "*.dll") .Concat(Directory.GetFiles(referencePack, "*.dll")) .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); }