internal static TypeReference GetTypeReference(ModuleDefinition module, object param) { return(param switch { Type paramType => module.ImportReference(paramType), InlineSignature paramSig => paramSig.ToFunctionPointer(module), ModifierType paramMod => paramMod.ToTypeReference(module), _ => throw new NotSupportedException($"Unsupported inline signature parameter type: {param} ({param?.GetType().FullDescription()})"), });
// Based on https://github.com/MonoMod/MonoMod.Common/blob/fb7fed148af165905ee0f2db1bb4c78a0137fb89/Utils/ReflectionHelper.ParseCallSite.cs // ... which is based on https://github.com/jbevain/cecil/blob/96026325ee1cb6627a3e4a32b924ab2905f02553/Mono.Cecil/AssemblyReader.cs#L3448 internal static InlineSignature ImportCallSite(Module moduleFrom, byte[] data) { var callsite = new InlineSignature(); // Based on https://github.com/jbevain/cecil/blob/96026325ee1cb6627a3e4a32b924ab2905f02553/Mono.Cecil/AssemblyReader.cs#L3448 using var stream = new MemoryStream(data, false); using var reader = new BinaryReader(stream); ReadMethodSignature(callsite); return(callsite); void ReadMethodSignature(InlineSignature method) { var callConv = reader.ReadByte(); if ((callConv & 0x20) != 0) { method.HasThis = true; callConv = (byte)(callConv & ~0x20); } if ((callConv & 0x40) != 0) { method.ExplicitThis = true; callConv = (byte)(callConv & ~0x40); } method.CallingConvention = (CallingConvention)callConv + 1; if ((callConv & 0x10) != 0) { _ = ReadCompressedUInt32(); // Generic-ness shouldn't apply to CallSites. } var paramCount = ReadCompressedUInt32(); method.ReturnType = ReadTypeSignature(); for (var i = 0; i < paramCount; i++) { method.Parameters.Add(ReadTypeSignature()); } } uint ReadCompressedUInt32() { var first = reader.ReadByte(); if ((first & 0x80) == 0) { return(first); } if ((first & 0x40) == 0) { return(((uint)(first & ~0x80) << 8) | reader.ReadByte()); } return(((uint)(first & ~0xc0) << 24) | (uint)reader.ReadByte() << 16 | (uint)reader.ReadByte() << 8 | reader.ReadByte()); } int ReadCompressedInt32() { var b = reader.ReadByte(); _ = reader.BaseStream.Seek(-1, SeekOrigin.Current); var u = (int)ReadCompressedUInt32(); var v = u >> 1; if ((u & 1) == 0) { return(v); } switch (b & 0xc0) { case 0: case 0x40: return(v - 0x40); case 0x80: return(v - 0x2000); default: return(v - 0x10000000); } } Type GetTypeDefOrRef() { var tokenData = ReadCompressedUInt32(); var rid = tokenData >> 2; var token = (tokenData & 3) switch { 0 => (uint)TokenType.TypeDef | rid, 1 => (uint)TokenType.TypeRef | rid, 2 => (uint)TokenType.TypeSpec | rid, _ => (uint)0, }; return(moduleFrom.ResolveType((int)token)); } object ReadTypeSignature() { var etype = (MetadataType)reader.ReadByte(); switch (etype) { case MetadataType.ValueType: case MetadataType.Class: return(GetTypeDefOrRef()); case MetadataType.Pointer: return(((Type)ReadTypeSignature()).MakePointerType()); case MetadataType.FunctionPointer: var fptr = new InlineSignature(); ReadMethodSignature(fptr); /* Note: Inline function pointer signatures cannot be made pointer / byref / array / ... types themselves. * That's because InlineSignature does not extend Type, unlike Cecil, where function pointers are literal types. * Unfortunately System.Reflection does not allow for creating function pointer types on demand. * Maybe System.Reflection.Emit / Mono.Cecil can be used to emit those types instead? * In the worst case, void* might be used instead of (fnptrtype)* * -ade */ return(fptr); case MetadataType.ByReference: return(((Type)ReadTypeSignature()).MakePointerType()); // System.Reflection lacks PinnedType. /* * case MetadataType.Pinned: * throw new NotSupportedException(); */ case (MetadataType)0x1d: // SzArray return(((Type)ReadTypeSignature()).MakeArrayType()); case MetadataType.Array: var atype = (Type)ReadTypeSignature(); var rank = ReadCompressedUInt32(); // The following information cannot be used with System.Reflection, // but it still needs to be skipped over. var sizes = ReadCompressedUInt32(); for (var i = 0; i < sizes; i++) { _ = ReadCompressedUInt32(); } var lowBounds = ReadCompressedUInt32(); for (var i = 0; i < lowBounds; i++) { _ = ReadCompressedInt32(); } return(atype.MakeArrayType((int)rank)); case MetadataType.OptionalModifier: return(new InlineSignature.ModifierType() { IsOptional = true, Modifier = GetTypeDefOrRef(), Type = ReadTypeSignature() }); case MetadataType.RequiredModifier: return(new InlineSignature.ModifierType() { IsOptional = false, Modifier = GetTypeDefOrRef(), Type = ReadTypeSignature() }); // System.Reflection lacks SentinelType. /* * case MetadataType.Sentinel: * throw new NotSupportedException(); */ case MetadataType.Var: case MetadataType.MVar: case MetadataType.GenericInstance: throw new NotSupportedException($"Unsupported generic callsite element: {etype}"); case MetadataType.Object: return(typeof(object)); case MetadataType.Void: return(typeof(void)); case MetadataType.TypedByReference: return(typeof(TypedReference)); case MetadataType.IntPtr: return(typeof(IntPtr)); case MetadataType.UIntPtr: return(typeof(UIntPtr)); case MetadataType.Boolean: return(typeof(bool)); case MetadataType.Char: return(typeof(char)); case MetadataType.SByte: return(typeof(sbyte)); case MetadataType.Byte: return(typeof(byte)); case MetadataType.Int16: return(typeof(short)); case MetadataType.UInt16: return(typeof(ushort)); case MetadataType.Int32: return(typeof(int)); case MetadataType.UInt32: return(typeof(uint)); case MetadataType.Int64: return(typeof(long)); case MetadataType.UInt64: return(typeof(ulong)); case MetadataType.Single: return(typeof(float)); case MetadataType.Double: return(typeof(double)); case MetadataType.String: return(typeof(string)); default: throw new NotSupportedException($"Unsupported callsite element: {etype}"); } } }