/*
* PERWAPI - An API for Reading and Writing PE Files
*
* Copyright (c) Diane Corney, Queensland University of Technology, 2004.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the PERWAPI Copyright as included with this
* distribution in the file PERWAPIcopyright.rtf.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY as is explained in the copyright notice.
*
* The author may be contacted at d.corney@qut.edu.au
*
* Version Date: 26/01/07
*/
using System;
using System.IO;
using System.Collections;
using System.Security.Cryptography;
namespace QUT.PERWAPI
{
/**************************************************************************/
///
/// Base class for all IL types
///
public abstract class Type : MetaDataElement
{
protected byte typeIndex;
/*-------------------- Constructors ---------------------------------*/
internal Type(byte tyIx) { typeIndex = tyIx; }
internal byte GetTypeIndex() { return typeIndex; }
internal virtual bool SameType(Type tstType)
{
return this == tstType;
}
internal virtual void TypeSig(MemoryStream str)
{
throw new TypeSignatureException(this.GetType().AssemblyQualifiedName +
" doesn't have a type signature!!");
}
public virtual string TypeName()
{
return "NoTypeName";
}
internal virtual void WriteType(CILWriter output)
{
throw new NotYetImplementedException("Writing types for CIL");
}
internal virtual void WriteName(CILWriter output)
{
WriteType(output);
}
internal virtual Type AddTypeSpec(MetaDataOut md)
{
if (!isDef()) BuildMDTables(md);
return this;
}
}
/**************************************************************************/
///
/// Descriptor for a custom modifier of a type (modopt or modreq)
///
public class CustomModifiedType : Type
{
Type type;
Class cmodType;
/*-------------------- Constructors ---------------------------------*/
///
/// Create a new custom modifier for a type
///
/// the type to be modified
/// the modifier
/// the type reference to be associated with the type
public CustomModifiedType(Type type, CustomModifier cmod, Class cmodType)
: base((byte)cmod)
{
this.type = type;
this.cmodType = cmodType;
}
/*------------------------- public set and get methods --------------------------*/
public void SetModifiedType(Type modType) { type = modType; }
public Type GetModifiedType() { return type; }
public void SetModifingType(Class mod) { cmodType = mod; }
public Class GetModifingType() { return cmodType; }
public void SetModifier(CustomModifier cmod) { typeIndex = (byte)cmod; }
public CustomModifier GetModifier() { return (CustomModifier)typeIndex; }
/*----------------------------- internal functions ------------------------------*/
internal override bool SameType(Type tstType)
{
if (this == tstType) return true;
if (tstType is CustomModifiedType)
{
CustomModifiedType cmTstType = (CustomModifiedType)tstType;
return type.SameType(cmTstType.type) &&
cmodType.SameType(cmTstType.cmodType);
}
return false;
}
internal sealed override void TypeSig(MemoryStream str)
{
str.WriteByte(typeIndex);
MetaDataOut.CompressNum(BlobUtil.CompressUInt(cmodType.TypeDefOrRefToken()), str);
type.TypeSig(str);
}
internal sealed override void BuildTables(MetaDataOut md)
{
if (!(cmodType is ClassDef))
cmodType.BuildMDTables(md);
if (!(type is ClassDef))
type.BuildMDTables(md);
}
}
/**************************************************************************/
internal class Pinned : Type
{
internal Pinned() : base((byte)ElementType.Pinned) { }
}
/**************************************************************************/
internal class Sentinel : Type
{
internal Sentinel() : base((byte)ElementType.Sentinel) { }
}
/**************************************************************************/
public abstract class TypeSpec : Type
{
uint sigIx = 0;
internal bool typeSpecAdded = false; // so that MetaDataOut can reset it
/*-------------------- Constructors ---------------------------------*/
internal TypeSpec(byte typeIx)
: base(typeIx)
{
tabIx = MDTable.TypeSpec;
}
internal static void Read(PEReader buff, TableRow[] specs)
{
for (int i = 0; i < specs.Length; i++)
{
specs[i] = new UnresolvedTypeSpec(buff, i);
//specs[i] = buff.GetBlobType(null,null,buff.GetBlobIx());
//if (specs[i] is GenericParam) {
// Console.WriteLine("GenericParam in TypeSpec table at pos " + i);
//}
}
}
internal override sealed Type AddTypeSpec(MetaDataOut md)
{
if (typeSpecAdded) return this;
md.AddToTable(MDTable.TypeSpec, this);
BuildMDTables(md);
typeSpecAdded = true;
return this;
}
internal override void BuildSignatures(MetaDataOut md)
{
MemoryStream str = new MemoryStream();
TypeSig(str);
sigIx = md.AddToBlobHeap(str.ToArray());
done = false;
}
internal static uint Size(MetaData md)
{
return md.BlobIndexSize();
}
internal sealed override void Write(PEWriter output)
{
//Console.WriteLine("Writing the blob index for a TypeSpec");
output.BlobIndex(sigIx);
}
internal sealed override uint GetCodedIx(CIx code)
{
switch (code)
{
case (CIx.TypeDefOrRef): return 2;
case (CIx.HasCustomAttr): return 13;
case (CIx.MemberRefParent): return 4;
}
return 0;
}
}
/**************************************************************************/
///
/// Descriptor for the Primitive types defined in IL
///
public class PrimitiveType : TypeSpec
{
private string name;
private int systemTypeIndex;
internal static int NumSystemTypes = 18;
public static readonly PrimitiveType Void = new PrimitiveType(0x01, "Void", 0);
public static readonly PrimitiveType Boolean = new PrimitiveType(0x02, "Boolean", 1);
public static readonly PrimitiveType Char = new PrimitiveType(0x03, "Char", 2);
public static readonly PrimitiveType Int8 = new PrimitiveType(0x04, "SByte", 3);
public static readonly PrimitiveType UInt8 = new PrimitiveType(0x05, "Byte", 4);
public static readonly PrimitiveType Int16 = new PrimitiveType(0x06, "Int16", 5);
public static readonly PrimitiveType UInt16 = new PrimitiveType(0x07, "UInt16", 6);
public static readonly PrimitiveType Int32 = new PrimitiveType(0x08, "Int32", 7);
public static readonly PrimitiveType UInt32 = new PrimitiveType(0x09, "UInt32", 8);
public static readonly PrimitiveType Int64 = new PrimitiveType(0x0A, "Int64", 9);
public static readonly PrimitiveType UInt64 = new PrimitiveType(0x0B, "UInt64", 10);
public static readonly PrimitiveType Float32 = new PrimitiveType(0x0C, "Single", 11);
public static readonly PrimitiveType Float64 = new PrimitiveType(0x0D, "Double", 12);
public static readonly PrimitiveType String = new PrimitiveType(0x0E, "String", 13);
internal static readonly PrimitiveType Class = new PrimitiveType(0x12);
public static readonly PrimitiveType TypedRef = new PrimitiveType(0x16, "TypedReference", 14);
public static readonly PrimitiveType IntPtr = new PrimitiveType(0x18, "IntPtr", 15);
public static readonly PrimitiveType UIntPtr = new PrimitiveType(0x19, "UIntPtr", 16);
public static readonly PrimitiveType Object = new PrimitiveType(0x1C, "Object", 17);
internal static readonly PrimitiveType ClassType = new PrimitiveType(0x50);
internal static readonly PrimitiveType SZArray = new PrimitiveType(0x1D);
public static readonly PrimitiveType NativeInt = IntPtr;
public static readonly PrimitiveType NativeUInt = UIntPtr;
internal static PrimitiveType[] primitives = {null,Void,Boolean,Char,Int8,UInt8,
Int16,UInt16,Int32,UInt32,Int64,
UInt64,Float32,Float64,String};
/*-------------------- Constructors ---------------------------------*/
internal PrimitiveType(byte typeIx) : base(typeIx) { }
internal PrimitiveType(byte typeIx, string name, int STIx)
: base(typeIx)
{
this.name = name;
this.systemTypeIndex = STIx;
}
internal string GetName() { return name; }
public override string TypeName()
{
if (typeIndex == 0x0E) return "System.String";
return name;
}
internal int GetSystemTypeIx() { return systemTypeIndex; }
internal sealed override void TypeSig(MemoryStream str)
{
str.WriteByte(typeIndex);
}
internal override void WriteType(CILWriter output)
{
//if (typeIndex == 0x0E) {
// output.Write("[mscorlib]System.String");
//} else
switch (typeIndex)
{
case (0x1C): output.Write("[mscorlib]System.Object"); break;
case (0x02): output.Write("bool"); break;
case (0x0C): output.Write("float32"); break;
case (0x0D): output.Write("float64"); break;
default: output.Write(name.ToLower()); break;
}
}
internal sealed override bool SameType(Type tstType)
{
if (tstType is SystemClass)
return tstType.SameType(this);
return this == tstType;
}
/* now done in MetaDataOut.WriteTildeStream
internal static void ClearAddedFlags() { // KJG 18-April-2005
for (int i = 0; i < primitives.Length; i++) {
if (primitives[i] != null) primitives[i].typeSpecAdded = false;
}
}
*/
}
/**************************************************************************/
///
/// Descriptor for
///
public class GenericParam : Type
{
private static readonly byte VAR = 0x13;
private static readonly byte MVAR = 0x1E;
ushort flags, index, kind = 0;
uint parentIx, nameIx;
string name;
MetaDataElement parent;
private ArrayList constraints = new ArrayList();
internal static bool extraField = true;
/*-------------------- Constructors ---------------------------------*/
private GenericParam(uint index, byte elemIx)
: base(elemIx)
{
this.index = (ushort)index;
sortTable = true;
}
internal GenericParam(string name, MetaDataElement parent, int index)
: base(VAR)
{
this.name = name;
this.parent = parent;
this.index = (ushort)index;
if (parent is Method) typeIndex = MVAR;
sortTable = true;
tabIx = MDTable.GenericParam;
}
internal GenericParam(PEReader buff)
: base(VAR)
{
index = buff.ReadUInt16();
flags = buff.ReadUInt16();
parentIx = buff.GetCodedIndex(CIx.TypeOrMethodDef);
name = buff.GetString();
if (extraField) kind = buff.ReadUInt16();
sortTable = true;
tabIx = MDTable.GenericParam;
// resolve generic param immediately for signature resolution
parent = buff.GetCodedElement(CIx.TypeOrMethodDef, parentIx);
if (parent != null)
{
if (parent is MethodDef)
{
typeIndex = MVAR;
((MethodDef)parent).AddGenericParam(this);
}
else
{
((ClassDef)parent).AddGenericParam(this);
}
}
}
internal GenericParam(string name)
: base(MVAR)
{
this.name = name;
sortTable = true;
tabIx = MDTable.GenericParam;
}
internal static GenericParam AnonMethPar(uint ix)
{
return new GenericParam(ix, MVAR);
}
internal static GenericParam AnonClassPar(uint ix)
{
return new GenericParam(ix, VAR);
}
internal static void Read(PEReader buff, TableRow[] gpars)
{
for (int i = 0; i < gpars.Length; i++)
gpars[i] = new GenericParam(buff);
}
/*------------------------- public set and get methods --------------------------*/
///
/// Set the attribute for this generic parameter
///
/// the attribute
public void SetAttribute(GenericParamAttr attr)
{
flags = (ushort)attr;
}
///
/// Get the attribute for this generic parameter
///
public GenericParamAttr GetAttribute()
{
return (GenericParamAttr)flags;
}
///
/// Add a type constraint to this generic parameter
///
/// class constraining the parameter type
public void AddConstraint(Class cType)
{
constraints.Add(cType);
}
///
/// Remove a constraint from this generic parameter
///
/// class type of constraint
public void RemoveConstraint(Class cType)
{
for (int i = 0; i < constraints.Count; i++)
{
if (constraints[i] == cType)
{
constraints.RemoveAt(i);
return;
}
}
}
///
/// Return a constraint from the list
///
/// constraint index
///
public Class GetConstraint(int i)
{
return (Class)constraints[i];
}
///
/// Get the number of constrains on this GenericParam
///
///
public int GetConstraintCount()
{
return constraints.Count;
}
///
/// Get the name of this generic parameter
///
/// generic parameter name
public string GetName() { return name; }
public MetaDataElement GetParent() { return parent; }
public Class[] GetClassConstraints()
{
return (Class[])constraints.ToArray(typeof(Class)); // KJG 20-May-2005
}
/*----------------------------- internal functions ------------------------------*/
internal uint Index
{
get { return index; }
set { index = (ushort)value; }
}
internal void SetClassParam(Class paren, int ix)
{
typeIndex = VAR;
parent = paren;
index = (ushort)ix;
}
internal void SetMethParam(Method paren, int ix)
{
typeIndex = MVAR;
parent = paren;
index = (ushort)ix;
}
internal void CheckParent(MethodDef paren, PEReader buff)
{
if (paren == buff.GetCodedElement(CIx.TypeOrMethodDef, parentIx))
{
parent = paren;
paren.InsertGenericParam(this);
}
}
internal override void TypeSig(MemoryStream str)
{
str.WriteByte(typeIndex);
str.WriteByte((byte)index);
}
internal static uint Size(MetaData md)
{
if (extraField)
return 6 + md.CodedIndexSize(CIx.TypeOrMethodDef) + md.StringsIndexSize();
else
return 4 + md.CodedIndexSize(CIx.TypeOrMethodDef) + md.StringsIndexSize();
}
internal override Type AddTypeSpec(MetaDataOut md)
{
// check that this generic parameter belongs to the "current" method ??
GenericParTypeSpec tSpec = new GenericParTypeSpec(this);
md.AddToTable(MDTable.TypeSpec, tSpec);
return tSpec;
}
internal override uint SortKey()
{
return (parent.Row << MetaData.CIxShiftMap[(uint)CIx.TypeOrMethodDef])
| parent.GetCodedIx(CIx.TypeOrMethodDef);
}
internal override void BuildTables(MetaDataOut md)
{
if (parent is MethodRef || parent is ClassRef) return; // don't add it - fix by CK
md.AddToTable(MDTable.GenericParam, this);
nameIx = md.AddToStringsHeap(name);
for (int i = 0; i < constraints.Count; i++)
{
Class cClass = (Class)constraints[i];
constraints[i] = new GenericParamConstraint(this, cClass);
if (cClass is ClassRef) cClass.BuildMDTables(md);
// Fix by CK - should be BuildTables too??
if (cClass is ClassSpec) md.AddToTable(MDTable.TypeSpec, cClass);
}
}
internal override void BuildCILInfo(CILWriter output)
{
for (int i = 0; i < constraints.Count; i++)
{
Class cClass = (Class)constraints[i];
if (!cClass.isDef())
{
cClass.BuildCILInfo(output);
}
}
}
internal void AddConstraints(MetaDataOut md)
{
for (int i = 0; i < constraints.Count; i++)
{
md.AddToTable(MDTable.GenericParamConstraint, (GenericParamConstraint)constraints[i]);
}
}
internal override void Write(PEWriter output)
{
output.Write(index);
output.Write(flags);
output.WriteCodedIndex(CIx.TypeOrMethodDef, parent);
output.StringsIndex(nameIx);
if (extraField) output.Write(kind);
}
}
/**************************************************************************/
internal class UnresolvedTypeSpec : TypeSpec
{
uint blobIx;
internal UnresolvedTypeSpec(PEReader buff, int i)
: base(0)
{
blobIx = buff.GetBlobIx();
Row = (uint)i + 1;
this.unresolved = true;
}
internal override void Resolve(PEReader buff)
{
buff.InsertInTable(MDTable.TypeSpec, Row, buff.GetBlobType(blobIx));
this.unresolved = false;
}
}
/**************************************************************************/
///
///
///
public class GenericParTypeSpec : TypeSpec
{
GenericParam gPar;
bool isClassPar;
uint index;
internal GenericParTypeSpec(GenericParam gPar)
: base(gPar.GetTypeIndex())
{
this.gPar = gPar;
}
internal GenericParTypeSpec(int gpTypeIx, uint ix)
: base((byte)gpTypeIx)
{
isClassPar = gpTypeIx == (int)ElementType.Var;
index = ix;
}
internal GenericParam GetGenericParam(MethodDef meth)
{
if (gPar == null)
{
if (isClassPar)
{
ClassDef methClass = (ClassDef)meth.GetParent();
gPar = methClass.GetGenericParam((int)index);
}
else
{
gPar = meth.GetGenericParam((int)index);
}
}
return gPar;
}
internal override void TypeSig(MemoryStream str)
{
gPar.TypeSig(str);
}
}
/**************************************************************************/
///
/// The IL Array type
///
public abstract class Array : TypeSpec
{
///
/// The element type of the array
///
protected Type elemType;
/*-------------------- Constructors ---------------------------------*/
internal Array(Type eType, byte TypeId)
: base(TypeId)
{
elemType = eType;
tabIx = MDTable.TypeSpec;
}
public Type ElemType() { return elemType; }
internal sealed override void BuildTables(MetaDataOut md)
{
if (!(elemType is ClassDef))
elemType.BuildMDTables(md);
}
internal sealed override void BuildCILInfo(CILWriter output)
{
if (!(elemType is ClassDef))
elemType.BuildCILInfo(output);
}
}
/**************************************************************************/
///
/// Multi dimensional array with explicit bounds
///
public class BoundArray : Array
{
int[] lowerBounds;
int[] sizes;
uint numDims;
/*-------------------- Constructors ---------------------------------*/
///
/// Create a new multi dimensional array type
/// eg. elemType[1..5,3..10,5,,] would be
/// new BoundArray(elemType,5,[1,3,0],[5,10,4])
///
/// the type of the elements
/// the number of dimensions
/// lower bounds of dimensions
/// upper bounds of dimensions
public BoundArray(
Type elementType,
int dimensions,
int[] loBounds,
int[] upBounds) : base(elementType, 0x14)
{
numDims = (uint)dimensions;
lowerBounds = loBounds;
if (loBounds.Length > dimensions)
throw new TypeSignatureException("Array cannot have more bounds than rank");
if (upBounds != null)
{
if (upBounds.Length > loBounds.Length)
throw new TypeSignatureException("Array cannot have more upper than lower bounds");
sizes = new int[upBounds.Length];
for (int i = 0; i < upBounds.Length; i++)
{
sizes[i] = upBounds[i] - loBounds[i] + 1;
}
}
}
///
/// Create a new multi dimensional array type with low bounds
/// specified but no sizes specified. C# arrays T[,] do this
/// with implicit low bounds of zero, but no sizes
///
/// the type of the elements
/// the number of dimensions
/// the low bounds of the dimensions
public BoundArray(Type elementType, int dimensions, int[] bounds)
: base(elementType, 0x14)
{
if (bounds.Length > dimensions)
throw new TypeSignatureException("Array cannot have more bounds than rank");
numDims = (uint)dimensions;
lowerBounds = bounds;
}
///
/// Create a new multi dimensional array type
/// eg. elemType[,,] would be new BoundArray(elemType,3)
///
/// the type of the elements
/// the number of dimensions
public BoundArray(Type elementType, int dimensions)
: base(elementType, 0x14)
{
numDims = (uint)dimensions;
}
internal override bool SameType(Type tstType)
{
if (this == tstType) return true;
if (!(tstType is BoundArray)) return false;
BoundArray bArray = (BoundArray)tstType;
if (elemType.SameType(bArray.ElemType()))
return SameBounds(numDims, lowerBounds, sizes);
return false;
}
internal bool SameBounds(uint dims, int[] lbounds, int[] sizs)
{
if (dims != numDims) return false;
if (lowerBounds != null)
{
if ((lbounds == null) || (lowerBounds.Length != lbounds.Length)) return false;
for (int i = 0; i < lowerBounds.Length; i++)
if (lowerBounds[i] != lbounds[i]) return false;
}
else
if (lbounds != null) return false;
if (sizes != null)
{
if ((sizs == null) || (sizes.Length != sizs.Length)) return false;
for (int i = 0; i < sizes.Length; i++)
if (sizes[i] != sizs[i]) return false;
}
else
if (sizs != null) return false;
return true;
}
internal sealed override void TypeSig(MemoryStream str)
{
str.WriteByte(typeIndex);
elemType.TypeSig(str);
MetaDataOut.CompressNum(BlobUtil.CompressUInt(numDims), str);
if ((sizes != null) && (sizes.Length > 0))
{
MetaDataOut.CompressNum(BlobUtil.CompressUInt((uint)sizes.Length), str);
for (int i = 0; i < sizes.Length; i++)
{
MetaDataOut.CompressNum(BlobUtil.CompressUInt((uint)sizes[i]), str);
}
}
else str.WriteByte(0);
if ((lowerBounds != null) && (lowerBounds.Length > 0))
{
MetaDataOut.CompressNum(BlobUtil.CompressUInt((uint)lowerBounds.Length), str);
for (int i = 0; i < lowerBounds.Length; i++)
{
MetaDataOut.CompressNum(BlobUtil.CompressInt(lowerBounds[i]), str);
}
}
else str.WriteByte(0);
}
}
/**************************************************************************/
///
/// Single dimensional array with zero lower bound
///
public class ZeroBasedArray : Array
{
/*-------------------- Constructors ---------------------------------*/
///
/// Create a new array - elementType[]
///
/// the type of the array elements
public ZeroBasedArray(Type elementType) : base(elementType, (byte)ElementType.SZArray) { }
internal sealed override void TypeSig(MemoryStream str)
{
str.WriteByte(typeIndex);
elemType.TypeSig(str);
}
internal override bool SameType(Type tstType)
{
if (this == tstType) return true;
if (!(tstType is ZeroBasedArray)) return false;
//return elemType == ((ZeroBasedArray)tstType).ElemType();
return elemType.SameType(((ZeroBasedArray)tstType).ElemType());
}
internal override void WriteType(CILWriter output)
{
elemType.WriteType(output);
output.Write("[]");
}
}
/**************************************************************************/
///
/// Descriptor for a FunctionPointer type
///
///
public class MethPtrType : TypeSpec
{
// MethPtrType == FNPTR
Method meth;
MethSig mSig;
/*-------------------- Constructors ---------------------------------*/
///
/// Create a new function pointer type
///
/// the function to be referenced
public MethPtrType(Method meth)
: base((byte)ElementType.FnPtr)
{
this.meth = meth;
}
internal MethPtrType(MethSig msig)
: base((byte)ElementType.FnPtr)
{
mSig = msig;
}
internal sealed override void TypeSig(MemoryStream str)
{
str.WriteByte(typeIndex);
if (meth == null)
mSig.TypeSig(str);
else
meth.TypeSig(str);
}
internal override bool SameType(Type tstType)
{
if (this == tstType) return true;
if (tstType is MethPtrType)
{
MethPtrType mpType = (MethPtrType)tstType;
}
return false;
}
internal sealed override void BuildTables(MetaDataOut md)
{
Type[] types = meth.GetParTypes();
if (types != null)
for (int i = 0; i < types.Length; i++)
types[i].BuildMDTables(md);
types = meth.GetOptParTypes();
if (types != null)
for (int i = 0; i < types.Length; i++)
types[i].BuildMDTables(md);
}
internal sealed override void BuildCILInfo(CILWriter output)
{
Type[] types = meth.GetParTypes();
if (types != null)
for (int i = 0; i < types.Length; i++)
types[i].BuildCILInfo(output);
types = meth.GetOptParTypes();
if (types != null)
for (int i = 0; i < types.Length; i++)
types[i].BuildCILInfo(output);
}
/* internal sealed override void BuildSignatures(MetaDataOut md) {
if (sigIx == 0) {
MemoryStream sig = new MemoryStream();
TypeSig(sig);
sigIx = md.AddToBlobHeap(sig.ToArray());
}
done = false;
}
*/
}
/**************************************************************************/
///
/// Descriptor for an pointer (type * or type &)
///
public abstract class PtrType : TypeSpec
{
protected Type baseType;
/*-------------------- Constructors ---------------------------------*/
internal PtrType(Type bType, byte typeIx)
: base(typeIx)
{
baseType = bType;
}
public Type GetBaseType() { return baseType; }
internal sealed override void TypeSig(MemoryStream str)
{
str.WriteByte(typeIndex);
baseType.TypeSig(str);
}
internal sealed override void BuildTables(MetaDataOut md)
{
if (!(baseType is ClassDef))
baseType.BuildMDTables(md);
}
internal sealed override void BuildCILInfo(CILWriter output)
{
if (!(baseType is ClassDef))
baseType.BuildCILInfo(output);
}
}
/**************************************************************************/
///
/// Descriptor for a managed pointer (type & or byref)
///
public class ManagedPointer : PtrType
{ // & (BYREF)
/*-------------------- Constructors ---------------------------------*/
///
/// Create new managed pointer to baseType
///
/// the base type of the pointer
public ManagedPointer(Type baseType) : base(baseType, 0x10) { }
}
/**************************************************************************/
///
/// Descriptor for an unmanaged pointer (type *)
///
public class UnmanagedPointer : PtrType
{ // PTR
/*-------------------- Constructors ---------------------------------*/
///
/// Create a new unmanaged pointer to baseType
///
/// the base type of the pointer
public UnmanagedPointer(Type baseType) : base(baseType, 0x0F) { }
}
}