/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace dnSpy.Debugger.DotNet.Metadata.Impl { readonly struct DmdMarshalBlobReader : IDisposable { readonly DmdModule module; readonly DmdDataStream reader; readonly IList genericTypeArguments; public static DmdMarshalType? Read(DmdModule module, DmdDataStream reader, IList? genericTypeArguments) { using (var marshalReader = new DmdMarshalBlobReader(module, reader, genericTypeArguments)) return marshalReader.Read(); } DmdMarshalBlobReader(DmdModule module, DmdDataStream reader, IList? genericTypeArguments) { this.module = module; this.reader = reader; this.genericTypeArguments = genericTypeArguments ?? Array.Empty(); } DmdMarshalType? Read() { const int DEFAULT = 0; try { var nativeType = (UnmanagedType)reader.ReadByte(); UnmanagedType nt; int size; switch (nativeType) { case UnmanagedType.ByValTStr: size = CanRead ? (int)reader.ReadCompressedUInt32() : DEFAULT; return DmdMarshalType.CreateFixedSysString(size); case UnmanagedType.SafeArray: var vt = CanRead ? (VarEnum)reader.ReadCompressedUInt32() : DEFAULT; var udtName = CanRead ? ReadUTF8String() : null; var udtRef = udtName is null ? null : DmdTypeNameParser.Parse(module, udtName, genericTypeArguments); return DmdMarshalType.CreateSafeArray(vt, udtRef); case UnmanagedType.ByValArray: size = CanRead ? (int)reader.ReadCompressedUInt32() : DEFAULT; nt = CanRead ? (UnmanagedType)reader.ReadCompressedUInt32() : DEFAULT; return DmdMarshalType.CreateFixedArray(size, nt); case UnmanagedType.LPArray: nt = CanRead ? (UnmanagedType)reader.ReadCompressedUInt32() : DEFAULT; int paramNum = CanRead ? (int)reader.ReadCompressedUInt32() : DEFAULT; size = CanRead ? (int)reader.ReadCompressedUInt32() : DEFAULT; bool hasFlags = CanRead; int flags = hasFlags ? (int)reader.ReadCompressedUInt32() : DEFAULT; const int ntaSizeParamIndexSpecified = 1; if (hasFlags && (flags & ntaSizeParamIndexSpecified) == 0) paramNum = 0; return DmdMarshalType.CreateArray(nt, (short)paramNum, size); case UnmanagedType.CustomMarshaler: var guid = ReadUTF8String(); var nativeTypeName = ReadUTF8String(); var custMarshalerName = ReadUTF8String(); var cmRef = custMarshalerName.Length == 0 ? null : DmdTypeNameParser.Parse(module, custMarshalerName, genericTypeArguments); var cookie = ReadUTF8String(); return DmdMarshalType.CreateCustomMarshaler(custMarshalerName, cmRef, cookie); case UnmanagedType.IUnknown: case UnmanagedType.IDispatch: case UnmanagedType.Interface: int iidParamIndex = CanRead ? (int)reader.ReadCompressedUInt32() : DEFAULT; return DmdMarshalType.CreateInterface(nativeType, iidParamIndex); default: return DmdMarshalType.Create(nativeType); } } catch (ArgumentException) { } catch (IOException) { } return null; } bool CanRead => reader.Position < reader.Length; string ReadUTF8String() { uint len = reader.ReadCompressedUInt32(); return len == 0 ? string.Empty : Encoding.UTF8.GetString(reader.ReadBytes((int)len)); } public void Dispose() => reader?.Dispose(); } }