// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.PortableExecutable; using Microsoft.CodeAnalysis.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// /// Represents a .NET assembly, consisting of one or more modules. /// internal abstract class AssemblySymbol : Symbol, IAssemblySymbol { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Changes to the public interface of this class should remain synchronized with the VB version. // Do not make any changes to the public interface without making the corresponding change // to the VB version. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! /// /// The system assembly, which provides primitive types like Object, String, etc., e.g. mscorlib.dll. /// The value is provided by ReferenceManager and must not be modified. For SourceAssemblySymbol, non-missing /// coreLibrary must match one of the referenced assemblies returned by GetReferencedAssemblySymbols() method of /// the main module. If there is no existing assembly that can be used as a source for the primitive types, /// the value is a Compilation.MissingCorLibrary. /// private AssemblySymbol corLibrary; /// /// The system assembly, which provides primitive types like Object, String, etc., e.g. mscorlib.dll. /// The value is MissingAssemblySymbol if none of the referenced assemblies can be used as a source for the /// primitive types and the owning assembly cannot be used as the source too. Otherwise, it is one of /// the referenced assemblies returned by GetReferencedAssemblySymbols() method or the owning assembly. /// internal AssemblySymbol CorLibrary { get { return corLibrary; } } /// /// A helper method for ReferenceManager to set the system assembly, which provides primitive /// types like Object, String, etc., e.g. mscorlib.dll. /// internal void SetCorLibrary(AssemblySymbol corLibrary) { Debug.Assert((object)this.corLibrary == null); this.corLibrary = corLibrary; } /// /// Simple name the assembly. /// /// /// This is equivalent to ., but may be /// much faster to retrieve for source code assemblies, since it does not require binding /// the assembly-level attributes that contain the version number and other assembly /// information. /// public override string Name { get { return Identity.Name; } } /// /// Gets the identity of this assembly. /// public abstract AssemblyIdentity Identity { get; } /// /// Target architecture of the machine. /// internal Machine Machine { get { return Modules[0].Machine; } } /// /// Indicates that this PE file makes Win32 calls. See CorPEKind.pe32BitRequired for more information (http://msdn.microsoft.com/en-us/library/ms230275.aspx). /// internal bool Bit32Required { get { return Modules[0].Bit32Required; } } /// /// Gets the merged root namespace that contains all namespaces and types defined in the modules /// of this assembly. If there is just one module in this assembly, this property just returns the /// GlobalNamespace of that module. /// public abstract NamespaceSymbol GlobalNamespace { get; } /// /// Given a namespace symbol, returns the corresponding assembly specific namespace symbol /// internal NamespaceSymbol GetAssemblyNamespace(NamespaceSymbol namespaceSymbol) { if (namespaceSymbol.IsGlobalNamespace) { return this.GlobalNamespace; } NamespaceSymbol container = namespaceSymbol.ContainingNamespace; if ((object)container == null) { return this.GlobalNamespace; } if (namespaceSymbol.NamespaceKind == NamespaceKind.Assembly && namespaceSymbol.ContainingAssembly == this) { // this is already the correct assembly namespace return namespaceSymbol; } NamespaceSymbol assemblyContainer = GetAssemblyNamespace(container); if ((object)assemblyContainer == (object)container) { // Trivial case, container isn't merged. return namespaceSymbol; } if ((object)assemblyContainer == null) { return null; } return assemblyContainer.GetNestedNamespace(namespaceSymbol.Name); } /// /// Gets a read-only list of all the modules in this assembly. (There must be at least one.) The first one is the main module /// that holds the assembly manifest. /// public abstract ImmutableArray Modules { get; } internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument argument) { return visitor.VisitAssembly(this, argument); } public override void Accept(CSharpSymbolVisitor visitor) { visitor.VisitAssembly(this); } public override TResult Accept(CSharpSymbolVisitor visitor) { return visitor.VisitAssembly(this); } public sealed override SymbolKind Kind { get { return SymbolKind.Assembly; } } public sealed override AssemblySymbol ContainingAssembly { get { return null; } } // Only the compiler can create AssemblySymbols. internal AssemblySymbol() { } /// /// Does this symbol represent a missing assembly. /// internal abstract bool IsMissing { get; } public sealed override Accessibility DeclaredAccessibility { get { return Accessibility.NotApplicable; } } public sealed override bool IsStatic { get { return false; } } public sealed override bool IsVirtual { get { return false; } } public sealed override bool IsOverride { get { return false; } } public sealed override bool IsAbstract { get { return false; } } public sealed override bool IsSealed { get { return false; } } public sealed override bool IsExtern { get { return false; } } /// /// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute. /// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet. /// internal sealed override ObsoleteAttributeData ObsoleteAttributeData { get { return null; } } public override ImmutableArray DeclaringSyntaxReferences { get { return ImmutableArray.Empty; } } /// /// True if the assembly contains interactive code. /// public virtual bool IsInteractive { get { return false; } } public sealed override Symbol ContainingSymbol { get { return null; } } /// /// Lookup a top level type referenced from metadata, names should be /// compared case-sensitively. /// /// /// Full type name with generic name mangling. /// /// /// Take forwarded types into account. /// /// internal NamedTypeSymbol LookupTopLevelMetadataType(ref MetadataTypeName emittedName, bool digThroughForwardedTypes) { return LookupTopLevelMetadataTypeWithCycleDetection(ref emittedName, visitedAssemblies: null, digThroughForwardedTypes: digThroughForwardedTypes); } /// /// Lookup a top level type referenced from metadata, names should be /// compared case-sensitively. Detect cycles during lookup. /// /// /// Full type name, possibly with generic name mangling. /// /// /// List of assemblies lookup has already visited (since type forwarding can introduce cycles). /// /// /// Take forwarded types into account. /// internal abstract NamedTypeSymbol LookupTopLevelMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList visitedAssemblies, bool digThroughForwardedTypes); /// /// Returns the type symbol for a forwarded type based its canonical CLR metadata name. /// The name should refer to a non-nested type. If type with this name is not forwarded, /// null is returned. /// public NamedTypeSymbol ResolveForwardedType(string fullyQualifiedMetadataName) { if (fullyQualifiedMetadataName == null) { throw new ArgumentNullException("fullyQualifiedMetadataName"); } var emittedName = MetadataTypeName.FromFullName(fullyQualifiedMetadataName); return TryLookupForwardedMetadataType(ref emittedName); } /// /// Look up the given metadata type, if it is forwarded. /// internal NamedTypeSymbol TryLookupForwardedMetadataType(ref MetadataTypeName emittedName) { return TryLookupForwardedMetadataTypeWithCycleDetection(ref emittedName, visitedAssemblies: null); } /// /// Look up the given metadata type, if it is forwarded. /// internal virtual NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList visitedAssemblies) { return null; } internal ErrorTypeSymbol CreateCycleInTypeForwarderErrorTypeSymbol(ref MetadataTypeName emittedName) { DiagnosticInfo diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_CycleInTypeForwarder, emittedName.FullName, this.Name); return new MissingMetadataTypeSymbol.TopLevelWithCustomErrorInfo(this.Modules[0], ref emittedName, diagnosticInfo); } /// /// Lookup declaration for predefined CorLib type in this Assembly. /// /// /// /// internal abstract NamedTypeSymbol GetDeclaredSpecialType(SpecialType type); /// /// Register declaration of predefined CorLib type in this Assembly. /// /// internal virtual void RegisterDeclaredSpecialType(NamedTypeSymbol corType) { throw ExceptionUtilities.Unreachable; } /// /// Continue looking for declaration of predefined CorLib type in this Assembly /// while symbols for new type declarations are constructed. /// internal virtual bool KeepLookingForDeclaredSpecialTypes { get { throw ExceptionUtilities.Unreachable; } } /// /// Return an array of assemblies involved in canonical type resolution of /// NoPia local types defined within this assembly. In other words, all /// references used by previous compilation referencing this assembly. /// /// internal abstract ImmutableArray GetNoPiaResolutionAssemblies(); internal abstract void SetNoPiaResolutionAssemblies(ImmutableArray assemblies); /// /// Return an array of assemblies referenced by this assembly, which are linked (/l-ed) by /// each compilation that is using this AssemblySymbol as a reference. /// If this AssemblySymbol is linked too, it will be in this array too. /// internal abstract ImmutableArray GetLinkedReferencedAssemblies(); internal abstract void SetLinkedReferencedAssemblies(ImmutableArray assemblies); internal abstract IEnumerable> GetInternalsVisibleToPublicKeys(string simpleName); internal abstract bool AreInternalsVisibleToThisAssembly(AssemblySymbol other); /// /// Assembly is /l-ed by compilation that is using it as a reference. /// internal abstract bool IsLinked { get; } /// /// Returns true and a string from the first GuidAttribute on the assembly, /// the string might be null or an invalid guid representation. False, /// if there is no GuidAttribute with string argument. /// internal virtual bool GetGuidString(out string guidString) { return GetGuidStringDefaultImplementation(out guidString); } /// /// Gets the set of type identifiers from this assembly. /// /// /// These names are the simple identifiers for the type, and do not include namespaces, /// outer type names, or type parameters. /// /// This functionality can be used for features that want to quickly know if a name could be /// a type for performance reasons. For example, classification does not want to incur an /// expensive binding call cost if it knows that there is no type with the name that they /// are looking at. /// public abstract ICollection TypeNames { get; } /// /// Gets the set of namespace names from this assembly. /// public abstract ICollection NamespaceNames { get; } /// /// Returns true if this assembly might contain extension methods. If this property /// returns false, there are no extension methods in this assembly. /// /// /// This property allows the search for extension methods to be narrowed quickly. /// public abstract bool MightContainExtensionMethods { get; } /// /// Gets the symbol for the pre-defined type from core library associated with this assembly. /// /// The symbol for the pre-defined type or null if the type is not defined in the core library. internal NamedTypeSymbol GetSpecialType(SpecialType type) { return CorLibrary.GetDeclaredSpecialType(type); } internal NamedTypeSymbol GetWellKnownType(WellKnownType type) { return this.GetTypeByMetadataName(WellKnownTypes.GetMetadataName(type)); } internal static TypeSymbol DynamicType { get { return DynamicTypeSymbol.Instance; } } /// /// The NamedTypeSymbol for the .NET System.Object type, which could have a TypeKind of /// Error if there was no COR Library in a compilation using the assembly. /// internal NamedTypeSymbol ObjectType { get { return GetSpecialType(SpecialType.System_Object); } } /// /// Get symbol for predefined type from Cor Library used by this assembly. /// /// /// internal NamedTypeSymbol GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode type) { return GetSpecialType(SpecialTypes.GetTypeFromMetadataName(type)); } /// /// Lookup a type within the assembly using the canonical CLR metadata name of the type. /// /// Type name. /// Symbol for the type or null if type cannot be found or is ambiguous. public NamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName) { if (fullyQualifiedMetadataName == null) { throw new ArgumentNullException("fullyQualifiedMetadataName"); } return this.GetTypeByMetadataName(fullyQualifiedMetadataName, includeReferences: false, isWellKnownType: false); } /// /// Lookup a type within the assembly using its canonical CLR metadata name. /// /// /// /// If search within assembly fails, lookup in assemblies referenced by the primary module. /// For source assembly, this is equivalent to all assembly references given to compilation. /// /// /// Extra restrictions apply when searching for a well-known type. In particular, the type must be public. /// /// /// While resolving the name, consider only types following CLS-compliant generic type names and arity encoding (ECMA-335, section 10.7.2). /// I.e. arity is inferred from the name and matching type must have the same emitted name and arity. /// /// /// A diagnostic bag to receive warnings if we should allow multiple definitions and pick one. /// /// Null if the type can't be found. internal NamedTypeSymbol GetTypeByMetadataName( string metadataName, bool includeReferences, bool isWellKnownType, bool useCLSCompliantNameArityEncoding = false, DiagnosticBag warnings = null) { NamedTypeSymbol type = null; MetadataTypeName mdName; if (metadataName.IndexOf('+') >= 0) { var parts = metadataName.Split(_nestedTypeNameSeparators); if (parts.Length > 0) { mdName = MetadataTypeName.FromFullName(parts[0], useCLSCompliantNameArityEncoding); type = GetTopLevelTypeByMetadataName(ref mdName, assemblyOpt: null, includeReferences: includeReferences, isWellKnownType: isWellKnownType, warnings: warnings); for (int i = 1; (object)type != null && !type.IsErrorType() && i < parts.Length; i++) { mdName = MetadataTypeName.FromTypeName(parts[i]); NamedTypeSymbol temp = type.LookupMetadataType(ref mdName); type = (!isWellKnownType || IsValidWellKnownType(temp)) ? temp : null; } } } else { mdName = MetadataTypeName.FromFullName(metadataName, useCLSCompliantNameArityEncoding); type = GetTopLevelTypeByMetadataName(ref mdName, assemblyOpt: null, includeReferences: includeReferences, isWellKnownType: isWellKnownType, warnings: warnings); } return ((object)type == null || type.IsErrorType()) ? null : type; } private static readonly char[] _nestedTypeNameSeparators = new char[] { '+' }; /// /// Resolves to a available in this assembly /// its referenced assemblies. /// /// The type to resolve. /// Use referenced assemblies for resolution. /// The resolved symbol if successful or null on failure. internal TypeSymbol GetTypeByReflectionType(Type type, bool includeReferences) { System.Reflection.TypeInfo typeInfo = type.GetTypeInfo(); Debug.Assert(!typeInfo.IsByRef); // not supported rigth now (we don't accept open types as submission results nor host types): Debug.Assert(!typeInfo.ContainsGenericParameters); if (typeInfo.IsArray) { TypeSymbol symbol = GetTypeByReflectionType(typeInfo.GetElementType(), includeReferences); if ((object)symbol == null) { return null; } int rank = typeInfo.GetArrayRank(); return new ArrayTypeSymbol(this, symbol, ImmutableArray.Empty, rank); } else if (typeInfo.IsPointer) { TypeSymbol symbol = GetTypeByReflectionType(typeInfo.GetElementType(), includeReferences); if ((object)symbol == null) { return null; } return new PointerTypeSymbol(symbol); } else if (typeInfo.DeclaringType != null) { Debug.Assert(!typeInfo.IsArray); // consolidated generic arguments (includes arguments of all declaring types): Type[] genericArguments = typeInfo.GenericTypeArguments; int typeArgumentIndex = 0; var currentTypeInfo = typeInfo.IsGenericType ? typeInfo.GetGenericTypeDefinition().GetTypeInfo() : typeInfo; var nestedTypes = ArrayBuilder.GetInstance(); while (true) { Debug.Assert(currentTypeInfo.IsGenericTypeDefinition || !currentTypeInfo.IsGenericType); nestedTypes.Add(currentTypeInfo); if (currentTypeInfo.DeclaringType == null) { break; } currentTypeInfo = currentTypeInfo.DeclaringType.GetTypeInfo(); } int i = nestedTypes.Count - 1; var symbol = (NamedTypeSymbol)GetTypeByReflectionType(nestedTypes[i].AsType(), includeReferences); if ((object)symbol == null) { return null; } while (--i >= 0) { int forcedArity = nestedTypes[i].GenericTypeParameters.Length - nestedTypes[i + 1].GenericTypeParameters.Length; MetadataTypeName mdName = MetadataTypeName.FromTypeName(nestedTypes[i].Name, forcedArity: forcedArity); symbol = symbol.LookupMetadataType(ref mdName); if ((object)symbol == null || symbol.IsErrorType()) { return null; } symbol = ApplyGenericArguments(symbol, genericArguments, ref typeArgumentIndex, includeReferences); if ((object)symbol == null) { return null; } } nestedTypes.Free(); Debug.Assert(typeArgumentIndex == genericArguments.Length); return symbol; } else { AssemblyIdentity assemblyId = AssemblyIdentity.FromAssemblyDefinition(typeInfo.Assembly); MetadataTypeName mdName = MetadataTypeName.FromNamespaceAndTypeName( typeInfo.Namespace ?? string.Empty, typeInfo.Name, forcedArity: typeInfo.GenericTypeArguments.Length); NamedTypeSymbol symbol = GetTopLevelTypeByMetadataName(ref mdName, assemblyId, includeReferences, isWellKnownType: false); if ((object)symbol == null || symbol.IsErrorType()) { return null; } int typeArgumentIndex = 0; Type[] genericArguments = typeInfo.GenericTypeArguments; symbol = ApplyGenericArguments(symbol, genericArguments, ref typeArgumentIndex, includeReferences); Debug.Assert(typeArgumentIndex == genericArguments.Length); return symbol; } } private NamedTypeSymbol ApplyGenericArguments(NamedTypeSymbol symbol, Type[] typeArguments, ref int currentTypeArgument, bool includeReferences) { int remainingTypeArguments = typeArguments.Length - currentTypeArgument; // in case we are specializing a nested generic definition we might have more arguments than the current symbol: Debug.Assert(remainingTypeArguments >= symbol.Arity); if (remainingTypeArguments == 0) { return symbol; } TypeSymbol[] typeArgumentSymbols = new TypeSymbol[symbol.TypeArgumentsNoUseSiteDiagnostics.Length]; for (int i = 0; i < typeArgumentSymbols.Length; i++) { var argSymbol = GetTypeByReflectionType(typeArguments[currentTypeArgument++], includeReferences); if ((object)argSymbol == null) { return null; } typeArgumentSymbols[i] = argSymbol; } return symbol.ConstructIfGeneric(typeArgumentSymbols.AsImmutableOrNull()); } internal NamedTypeSymbol GetTopLevelTypeByMetadataName( ref MetadataTypeName metadataName, AssemblyIdentity assemblyOpt, bool includeReferences, bool isWellKnownType, DiagnosticBag warnings = null) { NamedTypeSymbol result; // First try this assembly result = GetTopLevelTypeByMetadataName(this, ref metadataName, assemblyOpt); if (isWellKnownType && !IsValidWellKnownType(result)) { result = null; } // ignore any types of the same name that might be in referenced assemblies (prefer the current assembly): if ((object)result != null || !includeReferences) { return result; } Debug.Assert(this is SourceAssemblySymbol, "Never include references for a non-source assembly, because they don't know about aliases."); // Lookup in references foreach (var reference in GetUnaliasedReferencedAssemblies()) { Debug.Assert(!(this is SourceAssemblySymbol && reference.IsMissing)); // Non-source assemblies can have missing references NamedTypeSymbol candidate = GetTopLevelTypeByMetadataName(reference, ref metadataName, assemblyOpt); if (isWellKnownType && !IsValidWellKnownType(candidate)) { candidate = null; } if ((object)candidate == null) { continue; } Debug.Assert(candidate != result); if ((object)result != null) { // duplicate if (warnings == null) { return null; } else { // The predefined type '{0}' is defined in multiple assemblies in the global alias; using definition from '{1}' warnings.Add(ErrorCode.WRN_MultiplePredefTypes, NoLocation.Singleton, result, result.ContainingAssembly); return result; } } result = candidate; } return result; } private bool IsValidWellKnownType(NamedTypeSymbol result) { if ((object)result == null || result.TypeKind == TypeKind.Error) { return false; } Debug.Assert((object)result.ContainingType == null || IsValidWellKnownType(result.ContainingType), "Checking the containing type is the caller's responsibility."); return result.DeclaredAccessibility == Accessibility.Public || IsSymbolAccessible(result, this); } /// /// Return a list of assembly symbols than can be accessed without using an alias. /// For example: /// 1) /r:A.dll /r:B.dll -> A, B /// 2) /r:Foo=A.dll /r:B.dll -> B /// 3) /r:Foo=A.dll /r:A.dll -> A /// /// Note that it only makes sense to call this method on a SourceAssemblySymbol since /// alias information is per-compilation. /// private ImmutableArray GetUnaliasedReferencedAssemblies() { CSharpCompilation compilation = this.DeclaringCompilation; Debug.Assert(compilation != null, "There's an answer, but we don't expect this to happen"); // if (compilation == null) return this.Modules[0].GetReferencedAssemblySymbols(); ArrayBuilder references = null; foreach (var pair in compilation.GetBoundReferenceManager().ReferencedAssembliesMap) { MetadataReference reference = pair.Key; CSharpCompilation.ReferenceManager.ReferencedAssembly referencedAssembly = pair.Value; if (reference.Properties.Kind == MetadataImageKind.Assembly) { if (referencedAssembly.DeclarationsAccessibleWithoutAlias()) { if (references == null) { references = ArrayBuilder.GetInstance(); } references.Add(referencedAssembly.Symbol); } } } return references == null ? ImmutableArray.Empty : references.ToImmutableAndFree(); } private static NamedTypeSymbol GetTopLevelTypeByMetadataName(AssemblySymbol assembly, ref MetadataTypeName metadataName, AssemblyIdentity assemblyOpt) { var result = assembly.LookupTopLevelMetadataType(ref metadataName, digThroughForwardedTypes: false); if (!IsAcceptableMatchForGetTypeByMetadataName(result)) { return null; } if (assemblyOpt != null && !assemblyOpt.Equals(assembly.Identity)) { return null; } return result; } private static bool IsAcceptableMatchForGetTypeByMetadataName(NamedTypeSymbol candidate) { return candidate.Kind != SymbolKind.ErrorType || !(candidate is MissingMetadataTypeSymbol); } /// /// Lookup member declaration in predefined CorLib type in this Assembly. Only valid if this /// assembly is the Cor Library /// internal virtual Symbol GetDeclaredSpecialTypeMember(SpecialMember member) { return null; } /// /// Lookup member declaration in predefined CorLib type used by this Assembly. /// internal virtual Symbol GetSpecialTypeMember(SpecialMember member) { return CorLibrary.GetDeclaredSpecialTypeMember(member); } protected enum IVTConclusion { // This indicates that friend access should be granted. Match, // This indicates that friend access should be granted for the purposes of error recovery, // but the program is wrong. // // That's because this indicates that a strong-named assembly has referred to a weak-named assembly // which has extended friend access to the strong-named assembly. This will ultimately // result in an error because strong-named assemblies may not refer to weak-named assemblies. // In Roslyn we give a new error, CS7029, before emit time. In the dev10 compiler we error at // emit time. OneSignedOneNot, // This indicates that friend access should not be granted because the other assembly grants // friend access to a strong-named assembly, and either this assembly is weak-named, or // it is strong-named and the names don't match. PublicKeyDoesntMatch, // This indicates that friend access should not be granted because the other assembly // does not name this assembly as a friend in any way whatsoever. NoRelationshipClaimed } internal abstract ImmutableArray PublicKey { get; } protected IVTConclusion PerformIVTCheck(ImmutableArray key, AssemblyIdentity otherIdentity) { // This gets a bit complicated. Let's break it down. // // First off, let's assume that the "other" assembly is Smith.DLL, that the "this" // assembly is "Jones.DLL", and that Smith has named Jones as a friend. Whether we // allow Jones to see internals of Smith depends on these four factors: // // q1) Is Smith strong-named? // q2) Did Smith name Jones as a friend via a strong name? // q3) Is Jones strong-named? // q4) Does Smith give a strong-name for Jones that matches our strong name? // // Before we dive into the details, we should mention two additional facts: // // * If the answer to q1 is "yes", and Smith was compiled by the C# compiler, then q2 must be "yes" also. // Strong-named Smith must only be friends with strong-named Jones. See the blog article // http://blogs.msdn.com/b/ericlippert/archive/2009/06/04/alas-smith-and-jones.aspx // for an explanation of why this feature is desirable. // // Now, just because the compiler enforces this rule does not mean that we will never run into // a scenario where Smith is strong-named and names Jones via a weak name. Not all assemblies // were compiled with the C# compiler. We still need to deal sensibly with this situation. // We do so by ignoring the problem; if strong-named Smith extends friendship to weak-named // Jones then we're done; any assembly named Jones is a friend of Smith. // // Incidentally, the compiler produces error CS1726, ERR_FriendAssemblySNReq, when compiling // a strong-named Smith that names a weak-named Jones as its friend. // // * If the answer to q1 is "no" and the answer to q3 is "yes" then we are in a situation where // strong-named Jones is referencing weak-named Smith, which is illegal. In the dev 10 compiler // we do not give an error about this until emit time. In Roslyn we have a new error, CS7029, // which we give before emit time when we detect that weak-named Smith has given friend access // to strong-named Jones, which then references Smith. However, we still want to give friend // access to Jones for the purposes of semantic analysis. // // TODO: Roslyn does not yet give an error in other circumstances whereby a strong-named assembly // TODO: references a weak-named assembly. // // Let's make a chart that illustrates all the possible answers to these four questions, and // what the resulting accessibility should be: // // case q1 q2 q3 q4 Result Explanation // 1 YES YES YES YES SUCCESS Smith has named this strong-named Jones as a friend. // 2 YES YES YES NO NO MATCH Smith has named a different strong-named Jones as a friend. // 3 YES YES NO NO NO MATCH Smith has named a strong-named Jones as a friend, but this Jones is weak-named. // 4 YES NO YES NO SUCCESS Smith has improperly (*) named any Jones as its friend. But we honor its offer of friendship. // 5 YES NO NO NO SUCCESS Smith has improperly (*) named any Jones as its friend. But we honor its offer of friendship. // 6 NO YES YES YES SUCCESS, BAD REF Smith has named this strong-named Jones as a friend, but Jones should not be referring to a weak-named Smith. // 7 NO YES YES NO NO MATCH Smith has named a different strong-named Jones as a friend. // 8 NO YES NO NO NO MATCH Smith has named a strong-named Jones as a friend, but this Jones is weak-named. // 9 NO NO YES NO SUCCESS, BAD REF Smith has named any Jones as a friend, but Jones should not be referring to a weak-named Smith. // 10 NO NO NO NO SUCCESS Smith has named any Jones as its friend. // // (*) Smith was not built with C#, which would have prevented this. // // This method never returns NoRelationshipClaimed because if control got here, then we know that // Smith named Jones as a friend somehow. // // All that said, we also have an easy out here. Suppose Smith names Jones as a friend, and Jones is // being compiled as a module, not as an assembly. You can only strong-name an assembly. So if this module // is named Jones, and Smith is extending friend access to Jones, then we are going to optimistically // assume that Jones is going to be compiled into an assembly with a matching strong name, if necessary. CSharpCompilation compilation = this.DeclaringCompilation; if (compilation != null && compilation.Options.OutputKind.IsNetModule()) { return IVTConclusion.Match; } bool q1 = otherIdentity.IsStrongName; bool q2 = !key.IsDefaultOrEmpty; bool q3 = !this.PublicKey.IsDefaultOrEmpty; bool q4 = (q2 & q3) && ByteSequenceComparer.Equals(key, this.PublicKey); // Cases 2, 3, 7 and 8: if (q2 && !q4) { return IVTConclusion.PublicKeyDoesntMatch; } // Cases 6 and 9: if (!q1 && q3) { return IVTConclusion.OneSignedOneNot; } // Cases 1, 4, 5 and 10: return IVTConclusion.Match; } #region IAssemblySymbol Members INamespaceSymbol IAssemblySymbol.GlobalNamespace { get { return this.GlobalNamespace; } } IEnumerable IAssemblySymbol.Modules { get { return this.Modules; } } INamedTypeSymbol IAssemblySymbol.ResolveForwardedType(string fullyQualifiedMetadataName) { return ResolveForwardedType(fullyQualifiedMetadataName); } bool IAssemblySymbol.GivesAccessTo(IAssemblySymbol assemblyWantingAccess) { if (Equals(this, assemblyWantingAccess)) { return true; } var assembly = assemblyWantingAccess as AssemblySymbol; if (assembly == null) { return false; } var myKeys = GetInternalsVisibleToPublicKeys(assembly.Identity.Name); foreach (var key in myKeys) { IVTConclusion conclusion = assembly.PerformIVTCheck(key, this.Identity); Debug.Assert(conclusion != IVTConclusion.NoRelationshipClaimed); if (conclusion == IVTConclusion.Match || conclusion == IVTConclusion.OneSignedOneNot) { return true; } } return false; } INamedTypeSymbol IAssemblySymbol.GetTypeByMetadataName(string metadataName) { return this.GetTypeByMetadataName(metadataName); } #endregion #region ISymbol Members public override void Accept(SymbolVisitor visitor) { visitor.VisitAssembly(this); } public override TResult Accept(SymbolVisitor visitor) { return visitor.VisitAssembly(this); } #endregion } }