using Mono.Cecil; using Mono.Cecil.Cil; using XFESystem.Collections; namespace XFESystem.Tools.JintCleanupPatch; public static partial class Program { private const string WeakMarker = "XFESystem.JintOwnedWeakCollectionsV1"; private static void ValidateWeakCollections(ModuleDefinition module) { var engine=module.GetType("Jint.Engine"); string owner=typeof(IWeakCollectionOwner).FullName!,scope=typeof(WeakCollectionScope).FullName!,table=typeof(OwnedWeakTable<,>).FullName!; var reference=module.AssemblyReferences.SingleOrDefault(item=>item.Name==typeof(WeakCollectionScope).Assembly.GetName().Name); if(reference is null||reference.Version!=typeof(WeakCollectionScope).Assembly.GetName().Version|| engine.Interfaces.Count(item=>item.InterfaceType.FullName==owner)!=1|| engine.Fields.Count(item=>item.Name=="_xfeWeakCollections"&&item.FieldType.FullName==scope)!=1|| engine.Methods.Count(item=>item.Name=="get_WeakCollections"&&item.ReturnType.FullName==scope&&item.Overrides.Count==1)!=1) throw new InvalidOperationException("The per-engine weak storage owner binding is incomplete."); foreach(string name in new[]{"Jint.Native.JsWeakMap","Jint.Native.JsWeakSet"}) { var type=module.GetType(name);var field=type?.Fields.SingleOrDefault(item=>item.Name=="_table"); if(field?.FieldType is not GenericInstanceType generic||generic.ElementType.FullName!=table|| generic.GenericArguments.Count!=2||generic.GenericArguments.Any(item=>item.FullName!="Jint.Native.JsValue")) throw new InvalidOperationException("The weak collection storage replacement is incomplete."); var calls=type!.Methods.Where(item=>item.HasBody).SelectMany(item=>item.Body.Instructions) .Select(item=>item.Operand).OfType().ToArray(); if(calls.Any(item=>item.DeclaringType.FullName.StartsWith("System.Runtime.CompilerServices.ConditionalWeakTable`2",StringComparison.Ordinal))|| calls.Count(item=>item.DeclaringType.FullName==generic.FullName)!=(name.EndsWith("JsWeakMap",StringComparison.Ordinal)?11:4)|| calls.Count(item=>item.DeclaringType.FullName==engine.FullName&&item.Name=="get_WeakCollections")!=1) throw new InvalidOperationException("The weak collection call-site replacement is incomplete."); } } private static void PatchWeakCollections(ModuleDefinition module) { var engine = module.GetType("Jint.Engine") ?? throw new InvalidOperationException("Pinned Engine is absent."); var scopeType = module.ImportReference(typeof(WeakCollectionScope)); var ownerType = module.ImportReference(typeof(IWeakCollectionOwner)); var tableType = module.ImportReference(typeof(OwnedWeakTable<,>)); var scopeField = new FieldDefinition("_xfeWeakCollections", FieldAttributes.Private, scopeType); engine.Fields.Add(scopeField); engine.Interfaces.Add(new(ownerType)); var getter = new MethodDefinition("get_WeakCollections", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot | MethodAttributes.HideBySig | MethodAttributes.SpecialName, scopeType); getter.Overrides.Add(module.ImportReference(typeof(IWeakCollectionOwner).GetProperty(nameof(IWeakCollectionOwner.WeakCollections))!.GetMethod!)); var il = getter.Body.GetILProcessor(); var present = Instruction.Create(OpCodes.Ldarg_0); il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Ldfld, scopeField)); il.Append(Instruction.Create(OpCodes.Brtrue_S, present)); il.Append(Instruction.Create(OpCodes.Ldarg_0)); il.Append(Instruction.Create(OpCodes.Newobj, module.ImportReference(typeof(WeakCollectionScope).GetConstructor(Type.EmptyTypes)!))); il.Append(Instruction.Create(OpCodes.Stfld, scopeField)); il.Append(present); il.Append(Instruction.Create(OpCodes.Ldfld, scopeField)); il.Append(Instruction.Create(OpCodes.Ret)); engine.Methods.Add(getter); engine.Properties.Add(new PropertyDefinition("WeakCollections", PropertyAttributes.None, scopeType) { GetMethod = getter }); foreach (string name in new[] { "Jint.Native.JsWeakMap", "Jint.Native.JsWeakSet" }) { var type = module.GetType(name) ?? throw new InvalidOperationException("Pinned weak collection is absent: " + name); var field = type.Fields.Single(item => item.Name == "_table"); if (field.FieldType is not GenericInstanceType old || old.ElementType.FullName != "System.Runtime.CompilerServices.ConditionalWeakTable`2" || old.GenericArguments.Count != 2 || old.GenericArguments.Any(item => item.FullName != "Jint.Native.JsValue")) throw new InvalidOperationException("Pinned weak collection storage shape changed."); var replacement = new GenericInstanceType(tableType); foreach (var argument in old.GenericArguments) replacement.GenericArguments.Add(argument); field.FieldType = replacement; int constructors = 0, calls = 0; foreach (var method in type.Methods.Where(item => item.HasBody)) { foreach (var instruction in method.Body.Instructions.ToArray()) { if (instruction.Operand is not MethodReference target || target.DeclaringType.FullName != old.FullName) continue; if (target.Name is not (".ctor" or "TryGetValue" or "Remove" or "Add" or "AddOrUpdate")) throw new InvalidOperationException("Unsupported original weak storage call: " + target.FullName); if (target.Name == ".ctor") { if (method.Name != ".ctor" || method.Parameters.Count != 1 || method.Parameters[0].ParameterType.FullName != engine.FullName || target.Parameters.Count != 0) throw new InvalidOperationException("Pinned weak constructor changed."); var processor = method.Body.GetILProcessor(); processor.InsertBefore(instruction, Instruction.Create(OpCodes.Ldarg_1)); processor.InsertBefore(instruction, Instruction.Create(OpCodes.Callvirt, getter)); ++constructors; } var reference = new MethodReference(target.Name, target.ReturnType, replacement) { HasThis = true }; if (target.Name == ".ctor") reference.Parameters.Add(new(scopeType)); else foreach (var parameter in target.Parameters) reference.Parameters.Add(new(parameter.ParameterType)); instruction.Operand = reference; ++calls; } } if (constructors != 1 || calls != (name.EndsWith("JsWeakMap", StringComparison.Ordinal) ? 11 : 4)) throw new InvalidOperationException($"Pinned weak storage call count changed: {name}: {constructors}/{calls}."); } module.Types.Add(new TypeDefinition("XFESystem", "JintOwnedWeakCollectionsV1", TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.Sealed, module.TypeSystem.Object)); ValidateWeakCollections(module); } }