|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Roslyn.Utilities;
using System.Diagnostics;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.UnitTests
{
/// <summary>
/// Encapsulates assembly portability information inside an opaque class for use
/// by AssemblyIdentity in comparing assembly identities.
/// </summary>
internal sealed class FusionAssemblyPortabilityPolicy : IDisposable
{
// Pointer to unmanaged small CLR struct that must be allocated and deallocated by
// CLR calls
private IntPtr _assemblyConfigCookie;
private readonly ImmutableArray<byte> _fileHash;
private FusionAssemblyPortabilityPolicy(IntPtr asmConfigCookie, ImmutableArray<byte> fileHash)
{
_assemblyConfigCookie = asmConfigCookie;
_fileHash = fileHash;
}
/// <summary>
/// Loads the assembly portability policy from the given path using the CLR API.
/// If any problems are encountered by the CLR, the errors are passed through via CLR exception.
/// Can throw IO exceptions if any are encountered during file access.
/// </summary>
/// <param name="appConfigPath">Absolute path to the config file.</param>
/// <returns></returns>
public static FusionAssemblyPortabilityPolicy LoadFromFile(string appConfigPath)
{
Debug.Assert(PathUtilities.IsAbsolute(appConfigPath));
IntPtr asmConfigCookie;
// May throw CLR exception
CreateAssemblyConfigCookie(appConfigPath, out asmConfigCookie);
var hash = CryptographicHashProvider.ComputeSha1(File.ReadAllBytes(appConfigPath));
return new FusionAssemblyPortabilityPolicy(asmConfigCookie, hash);
}
internal IntPtr ConfigCookie
{
get { return _assemblyConfigCookie; }
}
[DllImport("clr", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = false)]
private static extern void CreateAssemblyConfigCookie([MarshalAs(UnmanagedType.LPWStr)] string configPath, out IntPtr assemblyConfig);
[DllImport("clr", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, PreserveSig = true)]
private static extern int DestroyAssemblyConfigCookie(IntPtr assemblyConfig);
public void Dispose()
{
DisposeInternal();
GC.SuppressFinalize(this);
}
private void DisposeInternal()
{
IntPtr ptr = Interlocked.Exchange(ref _assemblyConfigCookie, IntPtr.Zero);
if (ptr != IntPtr.Zero)
{
DestroyAssemblyConfigCookie(ptr);
}
}
public override bool Equals(object obj)
{
return Equals(obj as FusionAssemblyPortabilityPolicy);
}
public bool Equals(FusionAssemblyPortabilityPolicy other)
{
if (ReferenceEquals(this, other))
{
return true;
}
// This is not foolproof, but we just assume that if both assembly
// policies have the same path and the same timestamp that they are the same.
// We can't do any better because we don't have access to the config cookie internals.
return (object)other != null &&
Enumerable.SequenceEqual(_fileHash, other._fileHash);
}
public override int GetHashCode()
{
// Modified FNV hash
return Hash.GetFNVHashCode(_fileHash);
}
~FusionAssemblyPortabilityPolicy()
{
DisposeInternal();
}
}
}
|