using System; using System.Collections.Generic; using System.Linq; using Mono.Cecil; namespace Mirror.Weaver { public static class Extensions { public static bool Is(this TypeReference td, Type t) { if (t.IsGenericType) { return td.GetElementType().FullName == t.FullName; } return td.FullName == t.FullName; } public static bool Is(this TypeReference td) => Is(td, typeof(T)); public static bool IsDerivedFrom(this TypeReference tr) => IsDerivedFrom(tr, typeof(T)); public static bool IsDerivedFrom(this TypeReference tr, Type baseClass) { if (tr == null) return false; TypeDefinition td = tr.Resolve(); if (td == null) return false; if (!td.IsClass) return false; // are ANY parent classes of baseClass? TypeReference parent = td.BaseType; if (parent == null) return false; if (parent.Is(baseClass)) return true; if (parent.CanBeResolved()) return IsDerivedFrom(parent.Resolve(), baseClass); return false; } public static TypeReference GetEnumUnderlyingType(this TypeDefinition td) { foreach (FieldDefinition field in td.Fields) { if (!field.IsStatic) return field.FieldType; } throw new ArgumentException($"Invalid enum {td.FullName}"); } public static bool ImplementsInterface(this TypeDefinition td) { TypeDefinition typedef = td; while (typedef != null) { if (typedef.Interfaces.Any(iface => iface.InterfaceType.Is())) return true; try { TypeReference parent = typedef.BaseType; typedef = parent?.Resolve(); } catch (AssemblyResolutionException) { // this can happen for plugins. //Console.WriteLine("AssemblyResolutionException: "+ ex.ToString()); break; } } return false; } public static bool IsMultidimensionalArray(this TypeReference tr) => tr is ArrayType arrayType && arrayType.Rank > 1; // Does type use netId as backing field public static bool IsNetworkIdentityField(this TypeReference tr) => tr.Is() || tr.Is() || tr.IsDerivedFrom(); public static bool CanBeResolved(this TypeReference parent) { while (parent != null) { if (parent.Scope.Name == "Windows") { return false; } if (parent.Scope.Name == "mscorlib") { TypeDefinition resolved = parent.Resolve(); return resolved != null; } try { parent = parent.Resolve().BaseType; } catch { return false; } } return true; } // Makes T => Variable and imports function public static MethodReference MakeGeneric(this MethodReference generic, ModuleDefinition module, TypeReference variableReference) { GenericInstanceMethod instance = new GenericInstanceMethod(generic); instance.GenericArguments.Add(variableReference); MethodReference readFunc = module.ImportReference(instance); return readFunc; } // Given a method of a generic class such as ArraySegment`T.get_Count, // and a generic instance such as ArraySegment`int // Creates a reference to the specialized method ArraySegment`int`.get_Count // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error public static MethodReference MakeHostInstanceGeneric(this MethodReference self, ModuleDefinition module, GenericInstanceType instanceType) { MethodReference reference = new MethodReference(self.Name, self.ReturnType, instanceType) { CallingConvention = self.CallingConvention, HasThis = self.HasThis, ExplicitThis = self.ExplicitThis }; foreach (ParameterDefinition parameter in self.Parameters) reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); foreach (GenericParameter generic_parameter in self.GenericParameters) reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference)); return module.ImportReference(reference); } // Given a field of a generic class such as Writer.write, // and a generic instance such as ArraySegment`int // Creates a reference to the specialized method ArraySegment`int`.get_Count // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error public static FieldReference SpecializeField(this FieldReference self, ModuleDefinition module, GenericInstanceType instanceType) { FieldReference reference = new FieldReference(self.Name, self.FieldType, instanceType); return module.ImportReference(reference); } public static CustomAttribute GetCustomAttribute(this ICustomAttributeProvider method) { return method.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Is()); } public static bool HasCustomAttribute(this ICustomAttributeProvider attributeProvider) { return attributeProvider.CustomAttributes.Any(attr => attr.AttributeType.Is()); } public static T GetField(this CustomAttribute ca, string field, T defaultValue) { foreach (CustomAttributeNamedArgument customField in ca.Fields) if (customField.Name == field) return (T)customField.Argument.Value; return defaultValue; } public static MethodDefinition GetMethod(this TypeDefinition td, string methodName) { return td.Methods.FirstOrDefault(method => method.Name == methodName); } public static List GetMethods(this TypeDefinition td, string methodName) { return td.Methods.Where(method => method.Name == methodName).ToList(); } public static MethodDefinition GetMethodInBaseType(this TypeDefinition td, string methodName) { TypeDefinition typedef = td; while (typedef != null) { foreach (MethodDefinition md in typedef.Methods) { if (md.Name == methodName) return md; } try { TypeReference parent = typedef.BaseType; typedef = parent?.Resolve(); } catch (AssemblyResolutionException) { // this can happen for plugins. break; } } return null; } // Finds public fields in type and base type public static IEnumerable FindAllPublicFields(this TypeReference variable) { return FindAllPublicFields(variable.Resolve()); } // Finds public fields in type and base type public static IEnumerable FindAllPublicFields(this TypeDefinition typeDefinition) { while (typeDefinition != null) { foreach (FieldDefinition field in typeDefinition.Fields) { if (field.IsStatic || field.IsPrivate) continue; if (field.IsNotSerialized) continue; yield return field; } try { typeDefinition = typeDefinition.BaseType?.Resolve(); } catch (AssemblyResolutionException) { break; } } } public static bool ContainsClass(this ModuleDefinition module, string nameSpace, string className) => module.GetTypes().Any(td => td.Namespace == nameSpace && td.Name == className); public static AssemblyNameReference FindReference(this ModuleDefinition module, string referenceName) { foreach (AssemblyNameReference reference in module.AssemblyReferences) { if (reference.Name == referenceName) return reference; } return null; } } }