// internal for testing internal ComparisonResult Compare(AssemblyIdentity reference, string referenceDisplayName, AssemblyIdentity definition, out bool unificationApplied, bool ignoreVersion) { Debug.Assert((reference != null) ^ (referenceDisplayName != null)); unificationApplied = false; AssemblyIdentityParts parts; if (reference != null) { // fast path bool?eq = TriviallyEquivalent(reference, definition); if (eq.HasValue) { return(eq.Value ? ComparisonResult.Equivalent : ComparisonResult.NotEquivalent); } parts = AssemblyIdentityParts.Name | AssemblyIdentityParts.Version | AssemblyIdentityParts.Culture | AssemblyIdentityParts.PublicKeyToken; } else { if (!AssemblyIdentity.TryParseDisplayName(referenceDisplayName, out reference, out parts) || reference.ContentType != definition.ContentType) { return(ComparisonResult.NotEquivalent); } } Debug.Assert(reference.ContentType == definition.ContentType); bool isDefinitionFxAssembly; if (!ApplyUnificationPolicies(ref reference, ref definition, parts, out isDefinitionFxAssembly)) { return(ComparisonResult.NotEquivalent); } if (ReferenceEquals(reference, definition)) { return(ComparisonResult.Equivalent); } bool compareCulture = (parts & AssemblyIdentityParts.Culture) != 0; bool comparePublicKeyToken = (parts & AssemblyIdentityParts.PublicKeyOrToken) != 0; if (!definition.IsStrongName) { if (reference.IsStrongName) { return(ComparisonResult.NotEquivalent); } if (!AssemblyIdentity.IsFullName(parts)) { if (!SimpleNameComparer.Equals(reference.Name, definition.Name)) { return(ComparisonResult.NotEquivalent); } if (compareCulture && !CultureComparer.Equals(reference.CultureName, definition.CultureName)) { return(ComparisonResult.NotEquivalent); } // version is ignored return(ComparisonResult.Equivalent); } isDefinitionFxAssembly = false; } if (!SimpleNameComparer.Equals(reference.Name, definition.Name)) { return(ComparisonResult.NotEquivalent); } if (compareCulture && !CultureComparer.Equals(reference.CultureName, definition.CultureName)) { return(ComparisonResult.NotEquivalent); } if (comparePublicKeyToken && !AssemblyIdentity.KeysEqual(reference, definition)) { return(ComparisonResult.NotEquivalent); } bool hasSomeVersionParts = (parts & AssemblyIdentityParts.Version) != 0; bool hasPartialVersion = (parts & AssemblyIdentityParts.Version) != AssemblyIdentityParts.Version; // If any version parts were specified then compare the versions. The comparison fails if some version parts are missing. if (definition.IsStrongName && hasSomeVersionParts && (hasPartialVersion || reference.Version != definition.Version)) { // Note: // System.Numerics.Vectors, Version=4.0 is an FX assembly // System.Numerics.Vectors, Version=4.1+ is not an FX assembly // // It seems like a bug in Fusion: it only determines whether the definition is an FX assembly // and calculates the result based upon that, regardless of whether the reference is an FX assembly or not. // We do replicate the behavior. // // As a result unification is asymmetric when comparing the above identities. if (isDefinitionFxAssembly) { unificationApplied = true; return(ComparisonResult.Equivalent); } if (ignoreVersion) { return(ComparisonResult.EquivalentIgnoringVersion); } return(ComparisonResult.NotEquivalent); } return(ComparisonResult.Equivalent); }
internal override bool ApplyUnificationPolicies( ref AssemblyIdentity reference, ref AssemblyIdentity definition, AssemblyIdentityParts referenceParts, out bool isDefinitionFxAssembly) { if (reference.ContentType == AssemblyContentType.Default && SimpleNameComparer.Equals(reference.Name, definition.Name) && SimpleNameComparer.Equals(reference.Name, "mscorlib")) { isDefinitionFxAssembly = true; reference = definition; return(true); } if (!reference.IsRetargetable && definition.IsRetargetable) { // Reference is not retargetable, but definition is retargetable. // Non-equivalent. isDefinitionFxAssembly = false; return(false); } // Notes: // an assembly might be both retargetable and portable // in that case retargetable table acts as an override. // Apply portability policy transforms first (e.g. rewrites references to SL assemblies to their desktop equivalents) // If the reference is partial and is missing version or PKT it is not ported. reference = Port(reference); definition = Port(definition); if (reference.IsRetargetable && !definition.IsRetargetable) { if (!AssemblyIdentity.IsFullName(referenceParts)) { isDefinitionFxAssembly = false; return(false); } // Reference needs to be retargeted before comparison, // unless it's optionally retargetable and we already match the PK bool skipRetargeting = IsOptionallyRetargetableAssembly(reference) && AssemblyIdentity.KeysEqual(reference, definition); if (!skipRetargeting) { reference = Retarget(reference); } } // At this point we are in one of the following states: // // 1) Both ref/def are not retargetable // 2) Both ref/def are retargetable // 3) Ref is retargetable (and has been retargeted) // // We can do a straight compare of ref/def at this point using the // regular rules if (reference.IsRetargetable && definition.IsRetargetable) { isDefinitionFxAssembly = IsRetargetableAssembly(definition); } else { isDefinitionFxAssembly = IsFrameworkAssembly(definition); } return(true); }