39 sealed class LazyForwardedType
49 internal LazyForwardedType(
int index)
59 var typeName = module.GetTypeName(module.ExportedTypeTable.records[index].TypeNamespace, module.ExportedTypeTable.records[index].TypeName);
60 return module.Universe.GetMissingTypeOrThrow(module, module,
null, typeName).SetCyclicTypeForwarder();
62 else if (type ==
null)
65 type = module.ResolveExportedType(index);
73 readonly Stream stream;
74 readonly
string location;
78 string imageRuntimeVersion;
79 int metadataStreamVersion;
83 uint userStringHeapOffset;
84 uint userStringHeapSize;
85 byte[] lazyUserStringHeap;
88 Assembly[] assemblyRefs;
93 MemberInfo[] memberRefs;
94 Dictionary<StringHandle, string> strings =
new Dictionary<StringHandle, string>();
95 Dictionary<TypeName, Type> types =
new Dictionary<TypeName, Type>();
96 Dictionary<TypeName, LazyForwardedType> forwardedTypes =
new Dictionary<TypeName, LazyForwardedType>();
109 this.stream = universe !=
null && universe.MetadataOnly ? null : stream;
110 this.location = location;
111 Read(stream, mapped);
116 this.assembly = assembly;
119 void Read(Stream stream,
bool mapped)
121 var br =
new BinaryReader(stream);
122 peFile.Read(br, mapped);
123 stream.Seek(peFile.RvaToFileOffset(peFile.GetComDescriptorVirtualAddress()), SeekOrigin.Begin);
126 stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress), SeekOrigin.Begin);
127 foreach (var sh
in ReadStreamHeaders(br, out imageRuntimeVersion))
132 stringHeap = ReadHeap(stream, sh.Offset, sh.Size);
135 blobHeap = ReadHeap(stream, sh.Offset, sh.Size);
138 userStringHeapOffset = sh.Offset;
139 userStringHeapSize = sh.Size;
142 guidHeap = ReadHeap(stream, sh.Offset, sh.Size);
146 stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + sh.Offset), SeekOrigin.Begin);
157 internal void SetAssembly(Assembly assembly)
159 this.assembly = assembly;
162 static StreamHeader[] ReadStreamHeaders(BinaryReader br, out
string version)
164 var signature = br.ReadUInt32();
165 if (signature != 0x424A5342)
166 throw new BadImageFormatException(
"Invalid metadata signature");
174 var Length = br.ReadUInt32();
175 var buf = br.ReadBytes((
int)Length);
176 version = Encoding.UTF8.GetString(buf).TrimEnd(
'\u0000');
180 var streams = br.ReadUInt16();
182 for (
int i = 0; i < streamHeaders.Length; i++)
185 streamHeaders[i].Read(br);
188 return streamHeaders;
191 void ReadTables(BinaryReader br)
193 var tables = GetTables();
197 var majorVersion = br.ReadByte();
198 var minorVersion = br.ReadByte();
199 metadataStreamVersion = majorVersion << 16 | minorVersion;
200 var heapSizes = br.ReadByte();
204 ulong valid = br.ReadUInt64();
205 ulong sorted = br.ReadUInt64();
206 for (
int i = 0; i < 64; i++)
208 if ((valid & (1UL << i)) != 0)
210 tables[i].Sorted = (sorted & (1UL << i)) != 0;
211 tables[i].RowCount = br.ReadInt32();
216 for (
int i = 0; i < 64; i++)
217 if ((valid & (1UL << i)) != 0)
221 throw new NotImplementedException(
"ParamPtr table support has not yet been implemented.");
224 byte[] ReadHeap(Stream stream, uint offset, uint size)
226 var buf =
new byte[size];
227 stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + offset), SeekOrigin.Begin);
229 for (var pos = 0; pos < buf.Length;)
231 var read = stream.Read(buf, pos, buf.Length - pos);
233 throw new BadImageFormatException();
241 internal void SeekRVA(
int rva)
243 GetStream().Seek(peFile.RvaToFileOffset((uint)rva), SeekOrigin.Begin);
246 internal Stream GetStream()
248 return stream ??
throw new InvalidOperationException(
"Operation not available when UniverseOptions.MetadataOnly is enabled.");
251 internal override void GetTypesImpl(List<Type> list)
255 foreach (var type
in typeDefs)
256 if (type != moduleType)
260 void PopulateTypeDef()
262 if (typeDefs ==
null)
265 for (
int i = 0; i < typeDefs.Length; i++)
269 if (type.IsModulePseudoType)
271 else if (!type.IsNestedByFlags)
272 types.Add(type.TypeName, type);
282 forwardedTypes.Add(typeName,
new LazyForwardedType(i));
288 internal override string GetString(StringHandle handle)
293 if (!strings.TryGetValue(handle, out var str))
296 while (stringHeap[MetadataTokens.GetHeapOffset(handle) + len] != 0)
299 str = Encoding.UTF8.GetString(stringHeap, MetadataTokens.GetHeapOffset(handle), len);
300 strings.Add(handle, str);
306 static int ReadCompressedUInt(
byte[] buffer,
ref int offset)
308 var b1 = buffer[offset++];
313 else if ((b1 & 0xC0) == 0x80)
315 var b2 = buffer[offset++];
316 return ((b1 & 0x3F) << 8) | b2;
320 var b2 = buffer[offset++];
321 var b3 = buffer[offset++];
322 var b4 = buffer[offset++];
323 return ((b1 & 0x3F) << 24) + (b2 << 16) + (b3 << 8) + b4;
327 internal byte[] GetBlobCopy(BlobHandle handle)
329 var idx = MetadataTokens.GetHeapOffset(handle);
330 var len = ReadCompressedUInt(blobHeap,
ref idx);
331 var buf =
new byte[len];
332 Buffer.BlockCopy(blobHeap, idx, buf, 0, len);
336 internal override ByteReader GetBlobReader(BlobHandle handle)
343 if ((metadataToken >> 24) != 0x70)
344 throw TokenOutOfRangeException(metadataToken);
346 var h = MetadataTokens.StringHandle(metadataToken);
348 if (strings.TryGetValue(h, out var str) ==
false)
350 lazyUserStringHeap ??= ReadHeap(GetStream(), userStringHeapOffset, userStringHeapSize);
352 var index = metadataToken & 0xFFFFFF;
353 var len = ReadCompressedUInt(lazyUserStringHeap,
ref index) & ~1;
354 var sb =
new StringBuilder(len / 2);
355 for (
int i = 0; i < len; i += 2)
357 var ch = (char)(lazyUserStringHeap[index + i] | lazyUserStringHeap[index + i + 1] << 8);
368 internal override Type ResolveType(
int metadataToken,
IGenericContext context)
370 int index = (metadataToken & 0xFFFFFF) - 1;
372 throw TokenOutOfRangeException(metadataToken);
377 return typeDefs[index];
384 if (typeRefs[index] ==
null)
386 var scope =
TypeRefTable.records[index].ResolutionScope;
391 var assembly = ResolveAssemblyRef((scope & 0xFFFFFF) - 1);
393 typeRefs[index] = assembly.ResolveType(
this, typeName);
398 var outer = ResolveType(scope,
null);
400 typeRefs[index] = outer.ResolveNestedType(
this, typeName);
410 if (scope == 0 || scope == 1)
413 throw new NotImplementedException(
"self reference scope?");
417 module = ResolveModuleRef(
ModuleRefTable.records[(scope & 0xFFFFFF) - 1]);
421 typeRefs[index] = module.FindType(typeName) ?? module.Universe.GetMissingTypeOrThrow(
this, module,
null, typeName);
425 throw new NotImplementedException(
"ResolutionScope = " + scope.ToString(
"X"));
429 return typeRefs[index];
436 var type = typeSpecs[index];
439 var tc = context ==
null ? null :
new TrackingGenericContext(context);
440 typeSpecs[index] =
MarkerType.LazyResolveInProgress;
448 typeSpecs[index] =
null;
451 if (tc ==
null || !tc.IsUsed)
452 typeSpecs[index] = type;
454 else if (type ==
MarkerType.LazyResolveInProgress)
456 if (Universe.MissingMemberResolution)
459 .GetMissingTypeOrThrow(
this,
this,
null,
new TypeName(
null,
"Cyclic TypeSpec " + metadataToken.ToString(
"X")))
461 .SetMetadataTokenForMissing(metadataToken, 0);
464 throw new BadImageFormatException(
"Cyclic TypeSpec " + metadataToken.ToString(
"X"));
470 throw TokenOutOfRangeException(metadataToken);
473 Module ResolveModuleRef(StringHandle moduleNameIndex)
475 var moduleName = GetString(moduleNameIndex);
476 return assembly.GetModule(moduleName) ??
throw new FileNotFoundException(moduleName);
491 this.context = context;
499 public Type GetGenericTypeArgument(
int index)
505 public Type GetGenericMethodArgument(
int index)
508 return context.GetGenericMethodArgument(index);
513 TypeName GetTypeName(StringHandle typeNamespace, StringHandle typeName)
515 return new TypeName(GetString(typeNamespace), GetString(typeName));
518 internal Assembly ResolveAssemblyRef(
int index)
522 return assemblyRefs[index];
527 const int PublicKey = 0x0001;
535 rec.Culture.IsNil ?
"neutral" : GetString(rec.Culture),
536 rec.PublicKeyOrToken.IsNil ? Array.Empty<
byte>() : (rec.Flags & PublicKey) == 0 ? GetBlobCopy(rec.PublicKeyOrToken) :
AssemblyName.ComputePublicKeyToken(GetBlobCopy(rec.PublicKeyOrToken)),
539 return Universe.Load(name,
this,
true);
542 public override Guid
ModuleVersionId => GuidFromSpan(guidHeap.AsSpan(16 * (MetadataTokens.GetHeapOffset(
ModuleTable.records[0].Mvid) - 1), 16));
549 static Guid GuidFromSpan(ReadOnlySpan<byte> b)
552 var _a = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
553 var _b = (short)((b[5] << 8) | b[4]);
554 var _c = (short)((b[7] << 8) | b[6]);
563 return new Guid(_a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k);
571 public override string Name => location ==
null ?
"<Unknown>" : Path.GetFileName(location);
573 public override Assembly Assembly => assembly;
575 internal override Type FindType(
TypeName typeName)
579 if (!types.TryGetValue(typeName, out var type))
580 if (forwardedTypes.TryGetValue(typeName, out var fw))
581 return fw.GetType(
this);
586 internal override Type FindTypeIgnoreCase(
TypeName lowerCaseName)
590 foreach (var type
in types.Values)
591 if (type.TypeName.ToLowerInvariant() == lowerCaseName)
594 foreach (var name
in forwardedTypes.Keys)
595 if (name.ToLowerInvariant() == lowerCaseName)
596 return forwardedTypes[name].GetType(
this);
601 Exception TokenOutOfRangeException(
int metadataToken)
603 return new ArgumentOutOfRangeException(
"metadataToken", String.Format(
"Token 0x{0:x8} is not valid in the scope of module {1}.", metadataToken,
this.Name));
606 public override MemberInfo
ResolveMember(
int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
608 switch (metadataToken >> 24)
611 return ResolveField(metadataToken, genericTypeArguments, genericMethodArguments);
613 int index = (metadataToken & 0xFFFFFF) - 1;
617 return GetMemberRef(index, genericTypeArguments, genericMethodArguments);
620 return ResolveMethod(metadataToken, genericTypeArguments, genericMethodArguments);
624 return ResolveType(metadataToken, genericTypeArguments, genericMethodArguments);
626 throw TokenOutOfRangeException(metadataToken);
630 internal FieldInfo GetFieldAt(
TypeDefImpl owner,
int index)
632 fields ??=
new FieldInfo[
FieldTable.records.Length];
633 fields[index] ??=
new FieldDefImpl(
this, owner ?? FindFieldOwner(index), index);
634 return fields[index];
637 public override FieldInfo
ResolveField(
int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
639 int index = (metadataToken & 0xFFFFFF) - 1;
642 throw TokenOutOfRangeException(metadataToken);
646 return GetFieldAt(
null, index);
650 var field = GetMemberRef(index, genericTypeArguments, genericMethodArguments) as
FieldInfo;
654 throw new ArgumentException(String.Format(
"Token 0x{0:x8} is not a valid FieldInfo token in the scope of module {1}.", metadataToken,
this.Name),
"metadataToken");
658 throw TokenOutOfRangeException(metadataToken);
668 var end = TypeDefTable.records.Length > i + 1 ?
TypeDefTable.records[i + 1].FieldList - 1 :
FieldTable.records.Length;
669 if (field <= fieldIndex && fieldIndex < end)
676 throw new InvalidOperationException();
679 internal MethodBase GetMethodAt(TypeDefImpl owner,
int index)
682 if (methods[index] ==
null)
684 var method =
new MethodDefImpl(
this, owner ?? FindMethodOwner(index), index);
688 return methods[index];
691 public override MethodBase
ResolveMethod(
int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
693 int index = (metadataToken & 0xFFFFFF) - 1;
696 throw TokenOutOfRangeException(metadataToken);
700 return GetMethodAt(
null, index);
704 var method = GetMemberRef(index, genericTypeArguments, genericMethodArguments) as
MethodBase;
708 throw new ArgumentException(String.Format(
"Token 0x{0:x8} is not a valid MethodBase token in the scope of module {1}.", metadataToken,
this.Name),
"metadataToken");
714 return method.MakeGenericMethod(
Signature.ReadMethodSpec(
this, instantiation,
new GenericContext(genericTypeArguments, genericMethodArguments)));
718 throw TokenOutOfRangeException(metadataToken);
722 public override Type[]
__ResolveOptionalParameterTypes(
int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments, out CustomModifiers[] customModifiers)
724 int index = (metadataToken & 0xFFFFFF) - 1;
727 throw TokenOutOfRangeException(metadataToken);
732 return Signature.ReadOptionalParameterTypes(
this, GetBlobReader(sig),
new GenericContext(genericTypeArguments, genericMethodArguments), out customModifiers);
739 customModifiers = Array.Empty<CustomModifiers>();
740 return Type.EmptyTypes;
744 throw TokenOutOfRangeException(metadataToken);
750 int index = (typeSpecToken & 0xFFFFFF) - 1;
752 throw TokenOutOfRangeException(typeSpecToken);
754 return CustomModifiers.Read(
this,
ByteReader.FromBlob(blobHeap,
TypeSpecTable.records[index]),
new GenericContext(genericTypeArguments, genericMethodArguments));
759 get {
return GetString(
ModuleTable.records[0].Name); }
769 if (method <= methodIndex && methodIndex < end)
776 throw new InvalidOperationException();
779 MemberInfo GetMemberRef(
int index,
Type[] genericTypeArguments,
Type[] genericMethodArguments)
783 if (memberRefs[index] ==
null)
791 return GetMethodAt(
null, (owner & 0xFFFFFF) - 1);
793 memberRefs[index] = ResolveTypeMemberRef(ResolveModuleType(owner), name, ByteReader.FromBlob(blobHeap, sig));
797 memberRefs[index] = ResolveTypeMemberRef(ResolveType(owner), name, ByteReader.FromBlob(blobHeap, sig));
801 var type = ResolveType(owner, genericTypeArguments, genericMethodArguments);
804 var methodSig =
MethodSignature.ReadSig(
this, ByteReader.FromBlob(blobHeap, sig),
new GenericContext(genericTypeArguments, genericMethodArguments));
805 return type.FindMethod(name, methodSig) ?? Universe.GetMissingMethodOrThrow(
this, type, name, methodSig);
807 else if (type.IsConstructedGenericType)
809 var member = ResolveTypeMemberRef(type.GetGenericTypeDefinition(), name, ByteReader.FromBlob(blobHeap, sig));
812 member = mb.BindTypeParameters(type);
816 member = fi.BindTypeParameters(type);
822 return ResolveTypeMemberRef(type, name, ByteReader.FromBlob(blobHeap, sig));
826 throw new BadImageFormatException();
830 return memberRefs[index];
833 Type ResolveModuleType(
int token)
835 int index = (token & 0xFFFFFF) - 1;
837 var module = assembly.GetModule(name);
838 if (module ==
null || module.IsResource())
839 throw new BadImageFormatException();
841 return module.GetModuleType();
844 MemberInfo ResolveTypeMemberRef(
Type type,
string name, ByteReader sig)
850 var field = type.FindField(name, fieldSig);
851 if (field ==
null && Universe.MissingMemberResolution)
852 return Universe.GetMissingFieldOrThrow(
this, type, name, fieldSig);
854 while (field ==
null && (type = type.BaseType) !=
null)
855 field = type.FindField(name, fieldSig);
860 throw new MissingFieldException(org.ToString(), name);
866 var method = type.FindMethod(name, methodSig);
867 if (method ==
null && Universe.MissingMemberResolution)
868 return Universe.GetMissingMethodOrThrow(
this, type, name, methodSig);
870 while (method ==
null && (type = type.BaseType) !=
null)
871 method = type.FindMethod(name, methodSig);
876 throw new MissingMethodException(org.ToString(), name);
880 internal ByteReader GetStandAloneSig(
int index)
887 int index = (metadataToken & 0xFFFFFF) - 1;
890 var br = GetStandAloneSig(index);
891 return br.ReadBytes(br.Length);
895 throw TokenOutOfRangeException(metadataToken);
901 int index = (metadataToken & 0xFFFFFF) - 1;
903 return MethodSignature.ReadStandAloneMethodSig(
this, GetStandAloneSig(index),
new GenericContext(genericTypeArguments, genericMethodArguments));
905 throw TokenOutOfRangeException(metadataToken);
908 internal MethodInfo GetEntryPoint()
910 if (cliHeader.EntryPointToken != 0 && (cliHeader.Flags &
CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0)
911 return (MethodInfo)
ResolveMethod((
int)cliHeader.EntryPointToken);
916 internal string[] GetManifestResourceNames()
925 internal ManifestResourceInfo GetManifestResourceInfo(
string resourceName)
931 var info =
new ManifestResourceInfo(
this, i);
932 var
asm = info.ReferencedAssembly;
933 if (
asm !=
null && !
asm.__IsMissing &&
asm.GetManifestResourceInfo(resourceName) ==
null)
943 internal Stream GetManifestResourceStream(
string resourceName)
951 var info =
new ManifestResourceInfo(
this, i);
955 var fileName = Path.Combine(Path.GetDirectoryName(location), info.FileName);
956 if (
System.IO.File.Exists(fileName))
960 var fs =
new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
972 var
asm = info.ReferencedAssembly;
976 return asm.GetManifestResourceStream(resourceName);
978 throw new BadImageFormatException();
982 var br =
new BinaryReader(stream);
983 var length = br.ReadInt32();
984 return new MemoryStream(br.ReadBytes(length));
993 var list =
new List<AssemblyName>();
998 name.Version =
new Version(
1006 byte[] keyOrToken = GetBlobCopy(
AssemblyRefTable.records[i].PublicKeyOrToken);
1007 const int PublicKey = 0x0001;
1009 name.SetPublicKey(keyOrToken);
1011 name.SetPublicKeyToken(keyOrToken);
1015 name.SetPublicKeyToken(Array.Empty<
byte>());
1021 name.CultureName =
"";
1030 return list.ToArray();
1037 for (
int i = 0; i < assemblies.Length; i++)
1038 if (assemblyRefs[i] ==
null)
1039 assemblyRefs[i] = assemblies[i];
1045 for (
int i = 0; i < arr.Length; i++)
1054 for (
int i = 0; i < arr.Length; i++)
1055 arr[i] = ResolveType((
TypeRefTable.Index << 24) + i + 1);
1063 for (
int i = 0; i < arr.Length; i++)
1064 arr[i] = ResolveExportedType(i);
1069 private Type ResolveExportedType(
int index)
1075 switch (implementation >> 24)
1078 return ResolveAssemblyRef((implementation & 0xFFFFFF) - 1).ResolveType(
this, typeName).SetMetadataTokenForMissing(token, flags);
1080 return ResolveExportedType((implementation & 0xFFFFFF) - 1).ResolveNestedType(
this, typeName).SetMetadataTokenForMissing(token, flags);
1082 Module module = assembly.GetModule(GetString(
FileTable.records[(implementation & 0xFFFFFF) - 1].Name));
1083 return module.FindType(typeName) ?? module.Universe.GetMissingTypeOrThrow(
this, module,
null, typeName).SetMetadataTokenForMissing(token, flags);
1085 throw new BadImageFormatException();
1089 internal override Type GetModuleType()
1097 get {
return imageRuntimeVersion; }
1102 get {
return metadataStreamVersion; }
1107 peFile.GetDataDirectoryEntry(index, out rva, out length);
1112 return peFile.RvaToFileOffset((uint)rva);
1115 public override bool __GetSectionInfo(
int rva, out
string name, out
int characteristics, out
int virtualAddress, out
int virtualSize, out
int pointerToRawData, out
int sizeOfRawData)
1117 return peFile.GetSectionInfo(rva, out name, out characteristics, out virtualAddress, out virtualSize, out pointerToRawData, out sizeOfRawData);
1124 var totalBytesRead = 0;
1127 var read = stream.Read(data, offset, length);
1133 totalBytesRead += read;
1136 return totalBytesRead;
1139 public override void GetPEKind(out PortableExecutableKinds peKind, out ImageFileMachine machine)
1142 if ((cliHeader.Flags &
CliHeader.COMIMAGE_FLAGS_ILONLY) != 0)
1143 peKind |= PortableExecutableKinds.ILOnly;
1145 switch (cliHeader.Flags & (
CliHeader.COMIMAGE_FLAGS_32BITREQUIRED |
CliHeader.COMIMAGE_FLAGS_32BITPREFERRED))
1147 case CliHeader.COMIMAGE_FLAGS_32BITREQUIRED:
1148 peKind |= PortableExecutableKinds.Required32Bit;
1150 case CliHeader.COMIMAGE_FLAGS_32BITREQUIRED |
CliHeader.COMIMAGE_FLAGS_32BITPREFERRED:
1151 peKind |= PortableExecutableKinds.Preferred32Bit;
1160 peKind |= PortableExecutableKinds.PE32Plus;
1162 machine = (ImageFileMachine)peFile.FileHeader.Machine;
1167 get {
return peFile.OptionalHeader.Subsystem; }
1173 switch ((multiple ? 1 : 0) + (security ? 2 : 0))
1176 typeName =
new TypeName(
"System.Runtime.CompilerServices",
"AssemblyAttributesGoHere");
1179 typeName =
new TypeName(
"System.Runtime.CompilerServices",
"AssemblyAttributesGoHereM");
1182 typeName =
new TypeName(
"System.Runtime.CompilerServices",
"AssemblyAttributesGoHereS");
1186 typeName =
new TypeName(
"System.Runtime.CompilerServices",
"AssemblyAttributesGoHereSM");
1190 var list =
new List<CustomAttributeData>();
1197 list.Add(
new CustomAttributeData(
this, i));
1204 internal override void Dispose()
1212 manifestModule.ExportTypes(typeDefs, fileToken);
1217 return peFile.OptionalHeader.ImageBase;
1222 return peFile.OptionalHeader.SizeOfStackReserve;
1227 return peFile.OptionalHeader.FileAlignment;
1232 return (DllCharacteristics)peFile.OptionalHeader.DllCharacteristics;
1237 get {
return (cliHeader.Flags &
CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) != 0 ? (int)cliHeader.EntryPointToken : 0; }
1242 get {
return (cliHeader.Flags &
CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0 ? (int)cliHeader.EntryPointToken : 0; }