// dnlib: See LICENSE.txt for more info using System; using System.IO; namespace dnlib.DotNet.Writer { /// /// Helps map s to tokens /// public interface ISignatureWriterHelper : IWriterError { /// /// Returns a TypeDefOrRef encoded token /// /// A TypeDefOrRef type uint ToEncodedToken(ITypeDefOrRef typeDefOrRef); } /// /// Writes signatures /// public struct SignatureWriter : IDisposable { readonly ISignatureWriterHelper helper; RecursionCounter recursionCounter; readonly MemoryStream outStream; readonly DataWriter writer; readonly bool disposeStream; /// /// Write a signature /// /// Helper /// The type /// The signature as a byte array public static byte[] Write(ISignatureWriterHelper helper, TypeSig typeSig) { using (var writer = new SignatureWriter(helper)) { writer.Write(typeSig); return writer.GetResult(); } } internal static byte[] Write(ISignatureWriterHelper helper, TypeSig typeSig, DataWriterContext context) { using (var writer = new SignatureWriter(helper, context)) { writer.Write(typeSig); return writer.GetResult(); } } /// /// Write a signature /// /// Helper /// The signature /// The signature as a byte array public static byte[] Write(ISignatureWriterHelper helper, CallingConventionSig sig) { using (var writer = new SignatureWriter(helper)) { writer.Write(sig); return writer.GetResult(); } } internal static byte[] Write(ISignatureWriterHelper helper, CallingConventionSig sig, DataWriterContext context) { using (var writer = new SignatureWriter(helper, context)) { writer.Write(sig); return writer.GetResult(); } } SignatureWriter(ISignatureWriterHelper helper) { this.helper = helper; recursionCounter = new RecursionCounter(); outStream = new MemoryStream(); writer = new DataWriter(outStream); disposeStream = true; } SignatureWriter(ISignatureWriterHelper helper, DataWriterContext context) { this.helper = helper; recursionCounter = new RecursionCounter(); outStream = context.OutStream; writer = context.Writer; disposeStream = false; outStream.SetLength(0); outStream.Position = 0; } byte[] GetResult() => outStream.ToArray(); uint WriteCompressedUInt32(uint value) => writer.WriteCompressedUInt32(helper, value); int WriteCompressedInt32(int value) => writer.WriteCompressedInt32(helper, value); void Write(TypeSig typeSig) { const ElementType DEFAULT_ELEMENT_TYPE = ElementType.Boolean; if (typeSig is null) { helper.Error("TypeSig is null"); writer.WriteByte((byte)DEFAULT_ELEMENT_TYPE); return; } if (!recursionCounter.Increment()) { helper.Error("Infinite recursion"); writer.WriteByte((byte)DEFAULT_ELEMENT_TYPE); return; } uint count; switch (typeSig.ElementType) { case ElementType.Void: case ElementType.Boolean: case ElementType.Char: case ElementType.I1: case ElementType.U1: case ElementType.I2: case ElementType.U2: case ElementType.I4: case ElementType.U4: case ElementType.I8: case ElementType.U8: case ElementType.R4: case ElementType.R8: case ElementType.String: case ElementType.TypedByRef: case ElementType.I: case ElementType.U: case ElementType.Object: case ElementType.Sentinel: writer.WriteByte((byte)typeSig.ElementType); break; case ElementType.Ptr: case ElementType.ByRef: case ElementType.SZArray: case ElementType.Pinned: writer.WriteByte((byte)typeSig.ElementType); Write(typeSig.Next); break; case ElementType.ValueType: case ElementType.Class: writer.WriteByte((byte)typeSig.ElementType); Write(((TypeDefOrRefSig)typeSig).TypeDefOrRef); break; case ElementType.Var: case ElementType.MVar: writer.WriteByte((byte)typeSig.ElementType); WriteCompressedUInt32(((GenericSig)typeSig).Number); break; case ElementType.Array: writer.WriteByte((byte)typeSig.ElementType); var ary = (ArraySig)typeSig; Write(ary.Next); WriteCompressedUInt32(ary.Rank); if (ary.Rank == 0) break; count = WriteCompressedUInt32((uint)ary.Sizes.Count); for (uint i = 0; i < count; i++) WriteCompressedUInt32(ary.Sizes[(int)i]); count = WriteCompressedUInt32((uint)ary.LowerBounds.Count); for (uint i = 0; i < count; i++) WriteCompressedInt32(ary.LowerBounds[(int)i]); break; case ElementType.GenericInst: writer.WriteByte((byte)typeSig.ElementType); var gis = (GenericInstSig)typeSig; Write(gis.GenericType); count = WriteCompressedUInt32((uint)gis.GenericArguments.Count); for (uint i = 0; i < count; i++) Write(gis.GenericArguments[(int)i]); break; case ElementType.ValueArray: writer.WriteByte((byte)typeSig.ElementType); Write(typeSig.Next); WriteCompressedUInt32((typeSig as ValueArraySig).Size); break; case ElementType.FnPtr: writer.WriteByte((byte)typeSig.ElementType); Write((typeSig as FnPtrSig).Signature); break; case ElementType.CModReqd: case ElementType.CModOpt: writer.WriteByte((byte)typeSig.ElementType); Write((typeSig as ModifierSig).Modifier); Write(typeSig.Next); break; case ElementType.Module: writer.WriteByte((byte)typeSig.ElementType); WriteCompressedUInt32((typeSig as ModuleSig).Index); Write(typeSig.Next); break; case ElementType.End: case ElementType.R: case ElementType.Internal: default: helper.Error("Unknown or unsupported element type"); writer.WriteByte((byte)DEFAULT_ELEMENT_TYPE); break; } recursionCounter.Decrement(); } void Write(ITypeDefOrRef tdr) { if (tdr is null) { helper.Error("TypeDefOrRef is null"); WriteCompressedUInt32(0); return; } uint encodedToken = helper.ToEncodedToken(tdr); if (encodedToken > 0x1FFFFFFF) { helper.Error("Encoded token doesn't fit in 29 bits"); encodedToken = 0; } WriteCompressedUInt32(encodedToken); } void Write(CallingConventionSig sig) { if (sig is null) { helper.Error("sig is null"); return; } if (!recursionCounter.Increment()) { helper.Error("Infinite recursion"); return; } MethodBaseSig mbs; FieldSig fs; LocalSig ls; GenericInstMethodSig gim; if ((mbs = sig as MethodBaseSig) is not null) Write(mbs); else if ((fs = sig as FieldSig) is not null) Write(fs); else if ((ls = sig as LocalSig) is not null) Write(ls); else if ((gim = sig as GenericInstMethodSig) is not null) Write(gim); else { helper.Error("Unknown calling convention sig"); writer.WriteByte((byte)sig.GetCallingConvention()); } recursionCounter.Decrement(); } void Write(MethodBaseSig sig) { if (sig is null) { helper.Error("sig is null"); return; } if (!recursionCounter.Increment()) { helper.Error("Infinite recursion"); return; } writer.WriteByte((byte)sig.GetCallingConvention()); if (sig.Generic) WriteCompressedUInt32(sig.GenParamCount); uint numParams = (uint)sig.Params.Count; if (sig.ParamsAfterSentinel is not null) numParams += (uint)sig.ParamsAfterSentinel.Count; uint count = WriteCompressedUInt32(numParams); Write(sig.RetType); for (uint i = 0; i < count && i < (uint)sig.Params.Count; i++) Write(sig.Params[(int)i]); if (sig.ParamsAfterSentinel is not null && sig.ParamsAfterSentinel.Count > 0) { writer.WriteByte((byte)ElementType.Sentinel); for (uint i = 0, j = (uint)sig.Params.Count; i < (uint)sig.ParamsAfterSentinel.Count && j < count; i++, j++) Write(sig.ParamsAfterSentinel[(int)i]); } recursionCounter.Decrement(); } void Write(FieldSig sig) { if (sig is null) { helper.Error("sig is null"); return; } if (!recursionCounter.Increment()) { helper.Error("Infinite recursion"); return; } writer.WriteByte((byte)sig.GetCallingConvention()); Write(sig.Type); recursionCounter.Decrement(); } void Write(LocalSig sig) { if (sig is null) { helper.Error("sig is null"); return; } if (!recursionCounter.Increment()) { helper.Error("Infinite recursion"); return; } writer.WriteByte((byte)sig.GetCallingConvention()); uint count = WriteCompressedUInt32((uint)sig.Locals.Count); if (count >= 0x10000) { // ldloc 0xFFFF is invalid, see the ldloc documentation helper.Error("Too many locals, max number of locals is 65535 (0xFFFF)"); } for (uint i = 0; i < count; i++) Write(sig.Locals[(int)i]); recursionCounter.Decrement(); } void Write(GenericInstMethodSig sig) { if (sig is null) { helper.Error("sig is null"); return; } if (!recursionCounter.Increment()) { helper.Error("Infinite recursion"); return; } writer.WriteByte((byte)sig.GetCallingConvention()); uint count = WriteCompressedUInt32((uint)sig.GenericArguments.Count); for (uint i = 0; i < count; i++) Write(sig.GenericArguments[(int)i]); recursionCounter.Decrement(); } /// public void Dispose() { if (!disposeStream) return; if (outStream is not null) outStream.Dispose(); } } }