Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge release/dev16.11-vs-deps to release/dev17.0-vs-deps #54483

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ private static QuickAttributeChecker CreatePredefinedQuickAttributeChecker()
var result = new QuickAttributeChecker();
result.AddName(AttributeDescription.TypeIdentifierAttribute.Name, QuickAttributes.TypeIdentifier);
result.AddName(AttributeDescription.TypeForwardedToAttribute.Name, QuickAttributes.TypeForwardedTo);
result.AddName(AttributeDescription.AssemblyKeyNameAttribute.Name, QuickAttributes.AssemblyKeyName);
result.AddName(AttributeDescription.AssemblyKeyFileAttribute.Name, QuickAttributes.AssemblyKeyFile);
result.AddName(AttributeDescription.AssemblySignatureKeyAttribute.Name, QuickAttributes.AssemblySignatureKey);

#if DEBUG
result._sealed = true;
Expand Down Expand Up @@ -138,6 +141,9 @@ internal enum QuickAttributes : byte
{
None = 0,
TypeIdentifier = 1 << 0,
TypeForwardedTo = 2 << 0
TypeForwardedTo = 1 << 1,
AssemblyKeyName = 1 << 2,
AssemblyKeyFile = 1 << 3,
AssemblySignatureKey = 1 << 4,
}
}
167 changes: 145 additions & 22 deletions src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,19 +204,26 @@ internal override Symbol GetSpecialTypeMember(SpecialMember member)
return _compilation.IsMemberMissing(member) ? null : base.GetSpecialTypeMember(member);
}

private string GetWellKnownAttributeDataStringField(Func<CommonAssemblyWellKnownAttributeData, string> fieldGetter, string missingValue = null)
#nullable enable
private string? GetWellKnownAttributeDataStringField(Func<CommonAssemblyWellKnownAttributeData, string> fieldGetter, string? missingValue = null, QuickAttributes? attributeMatchesOpt = null)
{
string fieldValue = missingValue;
string? fieldValue = missingValue;

var data = attributeMatchesOpt is null
? GetSourceDecodedWellKnownAttributeData()
: GetSourceDecodedWellKnownAttributeData(attributeMatchesOpt.Value);

var data = GetSourceDecodedWellKnownAttributeData();
if (data != null)
{
fieldValue = fieldGetter(data);
}

if ((object)fieldValue == (object)missingValue)
if (fieldValue == (object?)missingValue)
{
data = GetNetModuleDecodedWellKnownAttributeData();
data = (attributeMatchesOpt is null || _lazyNetModuleAttributesBag is not null)
? GetNetModuleDecodedWellKnownAttributeData()
: GetLimitedNetModuleDecodedWellKnownAttributeData(attributeMatchesOpt.Value);

if (data != null)
{
fieldValue = fieldGetter(data);
Expand All @@ -225,6 +232,7 @@ private string GetWellKnownAttributeDataStringField(Func<CommonAssemblyWellKnown

return fieldValue;
}
#nullable disable

internal bool RuntimeCompatibilityWrapNonExceptionThrows
{
Expand Down Expand Up @@ -331,15 +339,19 @@ private string AssemblyKeyContainerAttributeSetting
{
get
{
return GetWellKnownAttributeDataStringField(data => data.AssemblyKeyContainerAttributeSetting, WellKnownAttributeData.StringMissingValue);
// We mitigate circularity problems by only actively loading attributes that pass a syntactic check
return GetWellKnownAttributeDataStringField(data => data.AssemblyKeyContainerAttributeSetting,
WellKnownAttributeData.StringMissingValue, QuickAttributes.AssemblyKeyName);
}
}

private string AssemblyKeyFileAttributeSetting
{
get
{
return GetWellKnownAttributeDataStringField(data => data.AssemblyKeyFileAttributeSetting, WellKnownAttributeData.StringMissingValue);
// We mitigate circularity problems by only actively loading attributes that pass a syntactic check
return GetWellKnownAttributeDataStringField(data => data.AssemblyKeyFileAttributeSetting,
WellKnownAttributeData.StringMissingValue, QuickAttributes.AssemblyKeyFile);
}
}

Expand All @@ -355,7 +367,9 @@ public string SignatureKey
{
get
{
return GetWellKnownAttributeDataStringField(data => data.AssemblySignatureKeyAttributeSetting);
// We mitigate circularity problems by only actively loading attributes that pass a syntactic check
return GetWellKnownAttributeDataStringField(data => data.AssemblySignatureKeyAttributeSetting,
missingValue: null, QuickAttributes.AssemblySignatureKey);
}
}

Expand Down Expand Up @@ -456,12 +470,6 @@ public AssemblyFlags AssemblyFlags

private StrongNameKeys ComputeStrongNameKeys()
{
// TODO:
// In order to allow users to escape problems that we create with our provisional granting of IVT access,
// consider not binding the attributes if the command line options were specified, then later bind them
// and report warnings if both were used.
EnsureAttributesAreBound();

// when both attributes and command-line options specified, cmd line wins.
string keyFile = _compilation.Options.CryptoKeyFile;

Expand Down Expand Up @@ -1301,8 +1309,6 @@ private WellKnownAttributeData ValidateAttributeUsageAndDecodeWellKnownAttribute
Debug.Assert(netModuleNames.Any());
Debug.Assert(attributesFromNetModules.Length == netModuleNames.Length);

var tree = CSharpSyntaxTree.Dummy;

int netModuleAttributesCount = attributesFromNetModules.Length;
int sourceAttributesCount = this.GetSourceAttributesBag().Attributes.Length;

Expand Down Expand Up @@ -1432,22 +1438,93 @@ private void LoadAndValidateNetModuleAttributes(ref CustomAttributesBag<CSharpAt
}

diagnostics.Free();
}

Debug.Assert(lazyNetModuleAttributesBag.IsSealed);
Debug.Assert(lazyNetModuleAttributesBag.IsSealed);
}
}

private void EnsureNetModuleAttributesAreBound()
private CommonAssemblyWellKnownAttributeData GetLimitedNetModuleDecodedWellKnownAttributeData(QuickAttributes attributeMatches)
{
if (_lazyNetModuleAttributesBag == null)
Debug.Assert(attributeMatches is QuickAttributes.AssemblyKeyFile
or QuickAttributes.AssemblyKeyName
or QuickAttributes.AssemblySignatureKey);

if (_compilation.Options.OutputKind.IsNetModule())
{
LoadAndValidateNetModuleAttributes(ref _lazyNetModuleAttributesBag);
return null;
}

ImmutableArray<string> netModuleNames;
ImmutableArray<CSharpAttributeData> attributesFromNetModules = GetNetModuleAttributes(out netModuleNames);

WellKnownAttributeData wellKnownData = null;

if (attributesFromNetModules.Any())
{
wellKnownData = limitedDecodeWellKnownAttributes(attributesFromNetModules, netModuleNames, attributeMatches);
}

return (CommonAssemblyWellKnownAttributeData)wellKnownData;

// Similar to ValidateAttributeUsageAndDecodeWellKnownAttributes, but doesn't load assembly-level attributes from source
// and only decodes 3 specific attributes.
WellKnownAttributeData limitedDecodeWellKnownAttributes(ImmutableArray<CSharpAttributeData> attributesFromNetModules,
ImmutableArray<string> netModuleNames, QuickAttributes attributeMatches)
{
Debug.Assert(attributesFromNetModules.Any());
Debug.Assert(netModuleNames.Any());
Debug.Assert(attributesFromNetModules.Length == netModuleNames.Length);

int netModuleAttributesCount = attributesFromNetModules.Length;

HashSet<CSharpAttributeData> uniqueAttributes = null;
CommonAssemblyWellKnownAttributeData result = null;

// Attributes from the second added module should override attributes from the first added module, etc.
// We don't reach here when the attribute was found in source already.
for (int i = netModuleAttributesCount - 1; i >= 0; i--)
{
CSharpAttributeData attribute = attributesFromNetModules[i];
if (!attribute.HasErrors && ValidateAttributeUsageForNetModuleAttribute(attribute, netModuleNames[i], BindingDiagnosticBag.Discarded, ref uniqueAttributes))
{
limitedDecodeWellKnownAttribute(attribute, attributeMatches, ref result);
}
}

WellKnownAttributeData.Seal(result);
return result;
}

// Similar to DecodeWellKnownAttribute but only handles 3 specific attributes and ignores diagnostics.
void limitedDecodeWellKnownAttribute(CSharpAttributeData attribute, QuickAttributes attributeMatches, ref CommonAssemblyWellKnownAttributeData result)
{
if (attributeMatches is QuickAttributes.AssemblySignatureKey &&
attribute.IsTargetAttribute(this, AttributeDescription.AssemblySignatureKeyAttribute))
{
result ??= new CommonAssemblyWellKnownAttributeData();
result.AssemblySignatureKeyAttributeSetting = (string)attribute.CommonConstructorArguments[0].ValueInternal;
}
else if (attributeMatches is QuickAttributes.AssemblyKeyFile &&
attribute.IsTargetAttribute(this, AttributeDescription.AssemblyKeyFileAttribute))
{
result ??= new CommonAssemblyWellKnownAttributeData();
result.AssemblyKeyFileAttributeSetting = (string)attribute.CommonConstructorArguments[0].ValueInternal;
}
else if (attributeMatches is QuickAttributes.AssemblyKeyName &&
attribute.IsTargetAttribute(this, AttributeDescription.AssemblyKeyNameAttribute))
{
result ??= new CommonAssemblyWellKnownAttributeData();
result.AssemblyKeyContainerAttributeSetting = (string)attribute.CommonConstructorArguments[0].ValueInternal;
}
}
}

private CustomAttributesBag<CSharpAttributeData> GetNetModuleAttributesBag()
{
EnsureNetModuleAttributesAreBound();
if (_lazyNetModuleAttributesBag == null)
{
LoadAndValidateNetModuleAttributes(ref _lazyNetModuleAttributesBag);
}
return _lazyNetModuleAttributesBag;
}

Expand Down Expand Up @@ -1563,6 +1640,52 @@ internal CommonAssemblyWellKnownAttributeData GetSourceDecodedWellKnownAttribute
return (CommonAssemblyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
}

#nullable enable
/// <remarks>
/// This implements the same logic as <see cref="GetSourceDecodedWellKnownAttributeData()"/>
/// but loading a smaller set of attributes if possible, to reduce circularity.
/// </remarks>
private CommonAssemblyWellKnownAttributeData? GetSourceDecodedWellKnownAttributeData(QuickAttributes attribute)
{
CustomAttributesBag<CSharpAttributeData>? attributesBag = _lazySourceAttributesBag;
if (attributesBag?.IsDecodedWellKnownAttributeDataComputed == true)
{
return (CommonAssemblyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
}

attributesBag = null;
Func<AttributeSyntax, bool> attributeMatches = attribute switch
{
QuickAttributes.AssemblySignatureKey => isPossibleAssemblySignatureKeyAttribute,
QuickAttributes.AssemblyKeyName => isPossibleAssemblyKeyNameAttribute,
QuickAttributes.AssemblyKeyFile => isPossibleAssemblyKeyFileAttribute,
_ => throw ExceptionUtilities.UnexpectedValue(attribute)
};

LoadAndValidateAttributes(OneOrMany.Create(GetAttributeDeclarations()), ref attributesBag, attributeMatchesOpt: attributeMatches);

return (CommonAssemblyWellKnownAttributeData?)attributesBag?.DecodedWellKnownAttributeData;

bool isPossibleAssemblySignatureKeyAttribute(AttributeSyntax node)
{
QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker;
return checker.IsPossibleMatch(node, QuickAttributes.AssemblySignatureKey);
}

bool isPossibleAssemblyKeyNameAttribute(AttributeSyntax node)
{
QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker;
return checker.IsPossibleMatch(node, QuickAttributes.AssemblyKeyName);
}

bool isPossibleAssemblyKeyFileAttribute(AttributeSyntax node)
{
QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker;
return checker.IsPossibleMatch(node, QuickAttributes.AssemblyKeyFile);
}
}
#nullable disable

/// <summary>
/// This only forces binding of attributes that look like they may be forwarded types attributes (syntactically).
/// </summary>
Expand Down
Loading