internal RoAssembly ResolveAssembly(RoAssemblyName refName)
        {
            Debug.Assert(refName != null);

            RoAssembly?assembly = TryResolveAssembly(refName, out Exception? e);

            return(assembly ?? throw e !);
        }
Beispiel #2
0
        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();
            }
        }
        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();
            }
        }
        internal RoAssembly ResolveToAssemblyOrExceptionAssembly(RoAssemblyName refName)
        {
            Debug.Assert(refName != null);

            if (_binds.TryGetValue(refName, out RoAssembly? prior))
            {
                return(prior);
            }

            RoAssembly assembly = TryFindAssemblyByCallingResolveHandler(refName);

            return(_binds.GetOrAdd(refName, assembly));
        }
        internal RoAssembly?TryResolveAssembly(RoAssemblyName refName, out Exception?e)
        {
            e = null;

            RoAssembly result = ResolveToAssemblyOrExceptionAssembly(refName);

            if (result is RoExceptionAssembly exceptionAssembly)
            {
                e = exceptionAssembly.Exception;
                return(null);
            }
            return(result);
        }
        /// <summary>
        /// Resolves the supplied assembly name to an assembly. If an assembly was previously bound by to this name, that assembly is returned.
        /// Otherwise, the MetadataLoadContext calls the specified MetadataAssemblyResolver. If the resolver returns null, this method throws a FileNotFoundException.
        ///
        /// Note that this behavior matches the behavior of AssemblyLoadContext.LoadFromAssemblyName() resolve event but does not match the behavior of
        /// Assembly.ReflectionOnlyLoad(). (the latter gives up without raising its resolve event.)
        /// </summary>
        public Assembly LoadFromAssemblyName(AssemblyName assemblyName)
        {
            if (assemblyName is null)
            {
                throw new ArgumentNullException(nameof(assemblyName));
            }

            if (IsDisposed)
            {
                throw new ObjectDisposedException(nameof(MetadataLoadContext));
            }
            RoAssemblyName refName = assemblyName.ToRoAssemblyName();

            return(ResolveAssembly(refName));
        }
        /// <summary>
        /// Resolves the supplied assembly name to an assembly. If an assembly was previously bound by to this name, that assembly is returned.
        /// Otherwise, the TypeLoader raises the Resolving event. If the event handler returns null, this method throws a FileNotFoundException.
        ///
        /// Note that this behavior matches the behavior of AssemblyLoadContext.LoadFromAssemblyName() but does not match the behavior of
        /// Assembly.ReflectionOnlyLoad(). (the latter gives up without raising the resolve event.)
        /// </summary>
        public Assembly LoadFromAssemblyName(AssemblyName assemblyName)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(nameof(TypeLoader));
            }

            if (assemblyName == null)
            {
                throw new ArgumentNullException(nameof(assemblyName));
            }

            RoAssemblyName refName = assemblyName.ToRoAssemblyName();

            return(ResolveAssembly(refName));
        }
        private RoAssembly TryFindAssemblyByCallingResolveHandler(RoAssemblyName refName)
        {
            Debug.Assert(refName != null);

            Assembly?assembly = resolver?.Resolve(this, refName.ToAssemblyName());

            if (assembly == null)
            {
                return(new RoExceptionAssembly(new FileNotFoundException(SR.Format(SR.FileNotFoundAssembly, refName.FullName))));
            }

            if (!(assembly is RoAssembly roAssembly && roAssembly.Loader == this))
            {
                throw new FileLoadException(SR.ExternalAssemblyReturnedByMetadataAssemblyResolver);
            }

            return(roAssembly);
        }
        private RoAssembly TryFindAssemblyByRaisingResolveEvent(RoAssemblyName refName)
        {
            Debug.Assert(refName != null);

            Assembly assembly = Resolving?.Invoke(this, refName.ToAssemblyName());

            if (assembly == null)
            {
                return(new RoExceptionAssembly(new FileNotFoundException(SR.Format(SR.FileNotFoundAssembly, refName.FullName))));
            }

            if (!(assembly is RoAssembly roAssembly && roAssembly.Loader == this))
            {
                throw new FileLoadException(SR.ExternalAssemblyReturnedByResolveHandler);
            }

            return(roAssembly);
        }
        internal unsafe InternalManifestResourceInfo GetInternalManifestResourceInfo(string resourceName)
        {
            MetadataReader reader = Reader;

            InternalManifestResourceInfo     result            = default;
            ManifestResourceHandleCollection manifestResources = reader.ManifestResources;

            foreach (ManifestResourceHandle resourceHandle in manifestResources)
            {
                ManifestResource resource = resourceHandle.GetManifestResource(reader);
                if (resource.Name.Equals(resourceName, reader))
                {
                    result.Found = true;
                    if (resource.Implementation.IsNil)
                    {
                        checked
                        {
                            // Embedded data resource
                            result.ResourceLocation = ResourceLocation.Embedded | ResourceLocation.ContainedInManifestFile;
                            PEReader pe = _guardedPEReader.PEReader;

                            PEMemoryBlock resourceDirectory = pe.GetSectionData(pe.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress);
                            BlobReader    blobReader        = resourceDirectory.GetReader((int)resource.Offset, resourceDirectory.Length - (int)resource.Offset);
                            uint          length            = blobReader.ReadUInt32();
                            result.PointerToResource = blobReader.CurrentPointer;

                            // Length check the size of the resource to ensure it fits in the PE file section, note, this is only safe as its in a checked region
                            if (length + sizeof(int) > blobReader.Length)
                            {
                                throw new BadImageFormatException();
                            }
                            result.SizeOfResource = length;
                        }
                    }
                    else
                    {
                        if (resource.Implementation.Kind == HandleKind.AssemblyFile)
                        {
                            // Get file name
                            result.ResourceLocation = default;
                            AssemblyFile file = ((AssemblyFileHandle)resource.Implementation).GetAssemblyFile(reader);
                            result.FileName = file.Name.GetString(reader);
                            if (file.ContainsMetadata)
                            {
                                EcmaModule module = (EcmaModule)Assembly.GetModule(result.FileName);
                                if (module == null)
                                {
                                    throw new BadImageFormatException(SR.Format(SR.ManifestResourceInfoReferencedBadModule, result.FileName));
                                }
                                result = module.GetInternalManifestResourceInfo(resourceName);
                            }
                        }
                        else if (resource.Implementation.Kind == HandleKind.AssemblyReference)
                        {
                            // Resolve assembly reference
                            result.ResourceLocation = ResourceLocation.ContainedInAnotherAssembly;
                            RoAssemblyName destinationAssemblyName = ((AssemblyReferenceHandle)resource.Implementation).ToRoAssemblyName(reader);
                            result.ReferencedAssembly = Loader.ResolveAssembly(destinationAssemblyName);
                        }
                    }
                }
            }

            return(result);
        }