|
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
internal sealed class PEAssembly
{
/// <summary>
/// All assemblies this assembly references.
/// </summary>
/// <remarks>
/// A concatenation of assemblies referenced by each module in the order they are listed in <see cref="_modules"/>.
/// </remarks>
internal readonly ImmutableArray<AssemblyIdentity> AssemblyReferences;
/// <summary>
/// The number of assemblies referenced by each module in <see cref="_modules"/>.
/// </summary>
internal readonly ImmutableArray<int> ModuleReferenceCounts;
private readonly ImmutableArray<PEModule> _modules;
/// <summary>
/// Assembly identity read from Assembly table, or null if the table is empty.
/// </summary>
private readonly AssemblyIdentity _identity;
/// <summary>
/// Using <see cref="ThreeState"/> for atomicity.
/// </summary>
private ThreeState _lazyContainsNoPiaLocalTypes;
private ThreeState _lazyDeclaresTheObjectClass;
/// <summary>
/// We need to store reference to the assembly metadata to keep the metadata alive while
/// symbols have reference to PEAssembly.
/// </summary>
private readonly AssemblyMetadata _owner;
//Maps from simple name to list of public keys. If an IVT attribute specifies no public
//key, the list contains one element with an empty value
private Dictionary<string, List<ImmutableArray<byte>>> _lazyInternalsVisibleToMap;
/// <exception cref="BadImageFormatException"/>
internal PEAssembly(AssemblyMetadata owner, ImmutableArray<PEModule> modules)
{
Debug.Assert(!modules.IsDefault);
Debug.Assert(modules.Length > 0);
_identity = modules[0].ReadAssemblyIdentityOrThrow();
// Pre-calculate size to ensure this code only requires a single array allocation.
var totalRefCount = modules.Sum(static module => module.ReferencedAssemblies.Length);
var refCounts = ArrayBuilder<int>.GetInstance(modules.Length);
var refs = ArrayBuilder<AssemblyIdentity>.GetInstance(totalRefCount);
for (int i = 0; i < modules.Length; i++)
{
ImmutableArray<AssemblyIdentity> refsForModule = modules[i].ReferencedAssemblies;
refCounts.Add(refsForModule.Length);
refs.AddRange(refsForModule);
}
_modules = modules;
this.AssemblyReferences = refs.ToImmutableAndFree();
this.ModuleReferenceCounts = refCounts.ToImmutableAndFree();
_owner = owner;
}
internal EntityHandle Handle
{
get
{
return EntityHandle.AssemblyDefinition;
}
}
internal PEModule ManifestModule
{
get { return Modules[0]; }
}
internal ImmutableArray<PEModule> Modules
{
get
{
return _modules;
}
}
internal AssemblyIdentity Identity
{
get
{
return _identity;
}
}
internal bool ContainsNoPiaLocalTypes()
{
if (_lazyContainsNoPiaLocalTypes == ThreeState.Unknown)
{
foreach (PEModule module in Modules)
{
if (module.ContainsNoPiaLocalTypes())
{
_lazyContainsNoPiaLocalTypes = ThreeState.True;
return true;
}
}
_lazyContainsNoPiaLocalTypes = ThreeState.False;
}
return _lazyContainsNoPiaLocalTypes == ThreeState.True;
}
private Dictionary<string, List<ImmutableArray<byte>>> BuildInternalsVisibleToMap()
{
var ivtMap = new Dictionary<string, List<ImmutableArray<byte>>>(StringComparer.OrdinalIgnoreCase);
foreach (string attrVal in Modules[0].GetInternalsVisibleToAttributeValues(Handle))
{
AssemblyIdentity identity;
if (AssemblyIdentity.TryParseDisplayName(attrVal, out identity))
{
List<ImmutableArray<byte>> keys;
if (ivtMap.TryGetValue(identity.Name, out keys))
keys.Add(identity.PublicKey);
else
{
keys = new List<ImmutableArray<byte>>();
keys.Add(identity.PublicKey);
ivtMap[identity.Name] = keys;
}
}
else
{
// Dev10 C# reports WRN_InvalidAssemblyName and Dev10 VB reports ERR_FriendAssemblyNameInvalid but
// we have no way to do that from here. Since the absence of these diagnostics does not impact the
// user experience enough to justify the work required to produce them, we will simply omit them
// (DevDiv #15099, #14348).
}
}
return ivtMap;
}
internal IEnumerable<ImmutableArray<byte>> GetInternalsVisibleToPublicKeys(string simpleName)
{
EnsureInternalsVisibleToMapInitialized();
List<ImmutableArray<byte>> result;
_lazyInternalsVisibleToMap.TryGetValue(simpleName, out result);
return result ?? SpecializedCollections.EmptyEnumerable<ImmutableArray<byte>>();
}
internal IEnumerable<string> GetInternalsVisibleToAssemblyNames()
{
EnsureInternalsVisibleToMapInitialized();
return _lazyInternalsVisibleToMap.Keys;
}
private void EnsureInternalsVisibleToMapInitialized()
{
if (_lazyInternalsVisibleToMap == null)
Interlocked.CompareExchange(ref _lazyInternalsVisibleToMap, BuildInternalsVisibleToMap(), null);
}
internal bool DeclaresTheObjectClass
{
get
{
if (_lazyDeclaresTheObjectClass == ThreeState.Unknown)
{
var value = _modules[0].MetadataReader.DeclaresTheObjectClass();
_lazyDeclaresTheObjectClass = value.ToThreeState();
}
return _lazyDeclaresTheObjectClass == ThreeState.True;
}
}
public AssemblyMetadata GetNonDisposableMetadata() => _owner.Copy();
}
}
|