private RoAssembly LoadFromStreamCore(Stream peStream) { PEReader peReader = new PEReader(peStream); PEReader peReaderToDispose = peReader; // Ensure peReader is disposed immediately if we throw an exception before we're done. try { if (!peReader.HasMetadata) { throw new BadImageFormatException(SR.NoMetadataInPeImage); } string location = (peStream is FileStream fs) ? (fs.Name ?? string.Empty) : string.Empty; MetadataReader reader = peReader.GetMetadataReader(); RoAssembly newAssembly = new EcmaAssembly(this, peReader, reader, location); AssemblyNameData defNameData = newAssembly.GetAssemblyNameDataNoCopy(); byte[] pkt = defNameData.PublicKeyToken ?? Array.Empty <byte>(); if (pkt.Length == 0 && defNameData.PublicKey != null && defNameData.PublicKey.Length != 0) { pkt = defNameData.PublicKey.ComputePublicKeyToken(); } RoAssemblyName defName = new RoAssemblyName(defNameData.Name, defNameData.Version, defNameData.CultureName, pkt); Guid mvid = newAssembly.ManifestModule.ModuleVersionId; LoadedAssemblyEntry candidate = new LoadedAssemblyEntry(newAssembly, mvid); LoadedAssemblyEntry winner = _loadedAssemblies.GetOrAdd(defName, candidate); if (winner.Assembly == newAssembly) { // We won the race. RegisterForDisposal(peReader); peReaderToDispose = null; return(_binds.GetOrAdd(defName, winner.Assembly)); // What if we lost the race to bind the defName in the _binds list? Should we ignore it and return the newly created assembly // (like Assembly.LoadModule()) does or return the prior assembly (like we do if we lose the race to commit into _loadedAssemblies?) // There's no perfect answer here. Fundamentally, the dilemma comes about because our apis don't lets apps properly separate // the act of creating an Assembly object from the act of committing the TypeLoader to bind to it. // // We will choose to return the prior winner so that the api is consistent with itself. This is how other LoadFrom() // apis work and they're used a lot more than LoadModule(). } else { // We lost the race but check for a MVID mismatch. if (mvid != winner.Mvid) { throw new FileLoadException(SR.Format(SR.FileLoadDuplicateAssemblies, defName)); } } return(winner.Assembly); } finally { peReaderToDispose?.Dispose(); } }
/// <summary> /// Collate an AssemblyData object for a single assembly. /// </summary> /// <param name="asm">the assembly to collect data on.</param> /// <returns>A pair of the name and data of the given assembly.</returns> public KeyValuePair <string, AssemblyData> AssembleAssembly(Assembly asm) { AssemblyName asmName = asm.GetName(); var asmNameData = new AssemblyNameData() { Name = asmName.Name, Version = asmName.Version, Culture = string.IsNullOrEmpty(asmName.CultureName) ? null : asmName.CultureName, PublicKeyToken = asmName.GetPublicKeyToken(), }; Type[] types = asm.GetTypes(); JsonDictionary <string, JsonDictionary <string, TypeData> > namespacedTypes = null; if (types.Length > 0) { namespacedTypes = new JsonDictionary <string, JsonDictionary <string, TypeData> >(); foreach (Type type in types) { if (!type.IsPublic) { continue; } // Some types don't have a namespace, but we still want to file them string typeNamespace = type.Namespace ?? ""; if (!namespacedTypes.TryGetValue(typeNamespace, out JsonDictionary <string, TypeData> typeDictionary)) { typeDictionary = new JsonDictionary <string, TypeData>(); namespacedTypes.Add(typeNamespace, typeDictionary); } TypeData typeData; try { typeData = AssembleType(type); } catch (Exception e) when(e is ReflectionTypeLoadException || e is FileNotFoundException) { continue; } typeDictionary[type.Name] = typeData; } } var asmData = new AssemblyData() { AssemblyName = asmNameData, Types = namespacedTypes != null && namespacedTypes.Count > 0 ? namespacedTypes : null, }; return(new KeyValuePair <string, AssemblyData>(asmName.Name, asmData)); }
private RoAssembly LoadFromStreamCore(Stream peStream) { PEReader peReader = new PEReader(peStream); PEReader?peReaderToDispose = peReader; // Ensure peReader is disposed immediately if we throw an exception before we're done. try { if (!peReader.HasMetadata) { throw new BadImageFormatException(SR.NoMetadataInPeImage); } string location = (peStream is FileStream fs) ? (fs.Name ?? string.Empty) : string.Empty; MetadataReader reader = peReader.GetMetadataReader(); RoAssembly candidate = new EcmaAssembly(this, peReader, reader, location); AssemblyNameData defNameData = candidate.GetAssemblyNameDataNoCopy(); byte[] pkt = defNameData.PublicKeyToken ?? Array.Empty <byte>(); if (pkt.Length == 0 && defNameData.PublicKey != null && defNameData.PublicKey.Length != 0) { pkt = defNameData.PublicKey.ComputePublicKeyToken() !; } RoAssemblyName defName = new RoAssemblyName(defNameData.Name, defNameData.Version, defNameData.CultureName, pkt, defNameData.Flags); RoAssembly winner = _loadedAssemblies.GetOrAdd(defName, candidate); if (winner == candidate) { // We won the race. RegisterForDisposal(peReader); peReaderToDispose = null; // We do not add to the _binds list because the binding list is only for assemblies that have been resolved through // the Resolve method. This allows the resolver to have complete control over selecting the appropriate assembly // based on Version, CultureName and PublicKeyToken. return(winner); } else { // We lost the race but check for a Mvid mismatch. if (candidate.ManifestModule.ModuleVersionId != winner.ManifestModule.ModuleVersionId) { throw new FileLoadException(SR.Format(SR.FileLoadDuplicateAssemblies, defName)); } } return(winner); } finally { peReaderToDispose?.Dispose(); } }
private static object Union(AssemblyNameData thisAsmName, AssemblyNameData thatAsmName) { if (thatAsmName.Version > thisAsmName.Version) { thisAsmName.Version = thatAsmName.Version; } if (thisAsmName.PublicKeyToken == null && thatAsmName.PublicKeyToken != null) { thisAsmName.PublicKeyToken = thatAsmName.PublicKeyToken; } return(thisAsmName); }
/// <summary> /// Collate an AssemblyData object for a single assembly. /// </summary> /// <param name="asm">the assembly to collect data on.</param> /// <returns>A pair of the name and data of the given assembly.</returns> public static KeyValuePair <string, AssemblyData> AssembleAssembly(Assembly asm) { AssemblyName asmName = asm.GetName(); var asmNameData = new AssemblyNameData() { Name = asmName.Name, Version = asmName.Version, Culture = string.IsNullOrEmpty(asmName.CultureName) ? null : asmName.CultureName, PublicKeyToken = asmName.GetPublicKeyToken(), }; Type[] types = asm.GetTypes(); JsonCaseInsensitiveStringDictionary <JsonCaseInsensitiveStringDictionary <TypeData> > namespacedTypes = null; if (types.Any()) { namespacedTypes = new JsonCaseInsensitiveStringDictionary <JsonCaseInsensitiveStringDictionary <TypeData> >(); foreach (Type type in asm.GetTypes()) { if (!type.IsPublic) { continue; } // Some types don't have a namespace, but we still want to file them string typeNamespace = type.Namespace ?? ""; if (!namespacedTypes.ContainsKey(typeNamespace)) { namespacedTypes.Add(typeNamespace, new JsonCaseInsensitiveStringDictionary <TypeData>()); } TypeData typeData = AssembleType(type); namespacedTypes[typeNamespace][type.Name] = typeData; } } var asmData = new AssemblyData() { AssemblyName = asmNameData, Types = namespacedTypes }; return(new KeyValuePair <string, AssemblyData>(asmName.Name, asmData)); }
protected sealed override AssemblyNameData[] ComputeAssemblyReferences() { MetadataReader reader = Reader; AssemblyNameData[] assemblyReferences = new AssemblyNameData[reader.AssemblyReferences.Count]; int index = 0; foreach (AssemblyReferenceHandle handle in reader.AssemblyReferences) { AssemblyReference ar = handle.GetAssemblyReference(reader); AssemblyNameData data = new AssemblyNameData(); AssemblyNameFlags flags = ar.Flags.ToAssemblyNameFlags(); data.Flags = flags; data.Name = ar.Name.GetString(reader); data.Version = ar.Version.AdjustForUnspecifiedVersionComponents(); data.CultureName = ar.Culture.GetStringOrNull(reader) ?? string.Empty; if ((flags & AssemblyNameFlags.PublicKey) != 0) { byte[] pk = ar.PublicKeyOrToken.GetBlobBytes(reader); data.PublicKey = pk; if (pk.Length != 0) { // AssemblyName will automatically compute the PKT on demand but given that we're doing all this work and caching it, we might // as well do this now. data.PublicKeyToken = pk.ComputePublicKeyToken(); } } else { data.PublicKeyToken = ar.PublicKeyOrToken.GetBlobBytes(reader); } assemblyReferences[index++] = data; } return(assemblyReferences); }
protected sealed override AssemblyNameData ComputeNameData() { MetadataReader reader = Reader; AssemblyDefinition ad = AssemblyDefinition; AssemblyNameData data = new AssemblyNameData { Name = ad.Name.GetString(reader), Version = ad.Version, CultureName = ad.Culture.GetStringOrNull(reader) ?? string.Empty }; byte[] pk = ad.PublicKey.GetBlobBytes(reader); data.PublicKey = pk; if (pk.Length != 0) { // AssemblyName will automatically compute the PKT on demand but given that we're doing all this work and caching it, we might // as well do this now. data.PublicKeyToken = pk.ComputePublicKeyToken(); } AssemblyNameFlags anFlagsAndContentType = ad.Flags.ToAssemblyNameFlags() | AssemblyNameFlags.PublicKey; data.Flags = anFlagsAndContentType.ExtractAssemblyNameFlags(); // We've finished setting the AssemblyName properties that actually pertain to binding and the Ecma-355 // concept of an assembly name. // // The rest of the properties are properties historically set by the runtime Reflection and thumbtacked // onto the AssemblyName object in the CLR tradition of treating AssemblyName as a dumping ground for all // kinds of info. Nevertheless, some of this info is potentially useful and not exposed elsewhere on Assembly // so we'll be nice and set it. data.HashAlgorithm = ad.HashAlgorithm.ToConfigurationAssemblyHashAlgorithm(); data.ContentType = anFlagsAndContentType.ExtractAssemblyContentType(); ManifestModule.GetPEKind(out PortableExecutableKinds peKind, out ImageFileMachine machine); switch (machine) { case ImageFileMachine.AMD64: data.ProcessorArchitecture = ProcessorArchitecture.Amd64; break; case ImageFileMachine.ARM: data.ProcessorArchitecture = ProcessorArchitecture.Arm; break; case ImageFileMachine.IA64: data.ProcessorArchitecture = ProcessorArchitecture.IA64; break; case ImageFileMachine.I386: if ((peKind & PortableExecutableKinds.Required32Bit) != 0) { data.ProcessorArchitecture = ProcessorArchitecture.X86; } else { data.ProcessorArchitecture = ProcessorArchitecture.MSIL; } break; default: // No real precedent for what to do here - CLR will never get as far as giving you an Assembly object // if the PE file specifies an unsupported machine. Since this Reflection implementation is a metadata inspection // layer, and the library will never make any decisions based on this value, throwing isn't really the best response here. data.ProcessorArchitecture = ProcessorArchitecture.None; break; } return(data); }