using System; using System.Collections.Generic; using System.Linq; using Mono.CecilX; 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) { TypeDefinition td = tr.Resolve(); 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) { return tr is ArrayType arrayType && arrayType.Rank > 1; } /// /// Does type use netId as backing field /// public static bool IsNetworkIdentityField(this TypeReference tr) { return 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, TypeReference variableReference) { GenericInstanceMethod instance = new GenericInstanceMethod(generic); instance.GenericArguments.Add(variableReference); MethodReference readFunc = Weaver.CurrentAssembly.MainModule.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, 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 Weaver.CurrentAssembly.MainModule.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 /// /// /// Generic Instance e.g. Writer /// public static FieldReference SpecializeField(this FieldReference self, GenericInstanceType instanceType) { FieldReference reference = new FieldReference(self.Name, self.FieldType, instanceType); return Weaver.CurrentAssembly.MainModule.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; } } } } }