提交 9ee9c480 编写于 作者: P pgavlin

Fix some issues with assembly name parsing and InternalsVisibleTo calculation.

- Assembly name parsing was incorrectly handling boundary conditions involving whitespace and unclosed quotes
- InternalsVisibleTo calculation was case-sensitive if the grantor was an assembly defined in source
***NO_CI***
 (changeset 1389170)
上级 d8ccabfe
......@@ -1984,7 +1984,7 @@ private void DecodeTypeForwardedToAttribute(ref DecodeWellKnownAttributeArgument
if (lazyInternalsVisibleToMap == null)
{
Interlocked.CompareExchange(ref lazyInternalsVisibleToMap,
new ConcurrentDictionary<string, ConcurrentDictionary<ImmutableArray<byte>, Tuple<Location, String>>>(), null);
new ConcurrentDictionary<string, ConcurrentDictionary<ImmutableArray<byte>, Tuple<Location, String>>>(StringComparer.OrdinalIgnoreCase), null);
}
//later, once the identity is established we confirm that if the assembly being
......
......@@ -1761,6 +1761,57 @@ public A GetA()
CompileAndVerify(cb, verify:false).Diagnostics.Verify();
}
[Fact, WorkItem(1072350, "DevDiv")]
public void Bug1072350()
{
const string sourceA = @"
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""X "")]
internal class A
{
internal static int I = 42;
}";
const string sourceB = @"
class B
{
static void Main()
{
System.Console.Write(A.I);
}
}";
var ca = CreateCompilationWithMscorlib(sourceA, options: TestOptions.ReleaseDll, assemblyName: "ClassLibrary2");
CompileAndVerify(ca);
var cb = CreateCompilationWithMscorlib(sourceB, options: TestOptions.ReleaseExe, assemblyName: "X", references: new[] { new CSharpCompilationReference(ca)});
CompileAndVerify(cb, expectedOutput: "42", emitOptions: TestEmitters.CCI).Diagnostics.Verify();
}
[Fact, WorkItem(1072339, "DevDiv")]
public void Bug1072339()
{
const string sourceA = @"
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""x"")]
internal class A
{
internal static int I = 42;
}";
const string sourceB = @"
class B
{
static void Main()
{
System.Console.Write(A.I);
}
}";
var ca = CreateCompilationWithMscorlib(sourceA, options: TestOptions.ReleaseDll, assemblyName: "ClassLibrary2");
CompileAndVerify(ca);
var cb = CreateCompilationWithMscorlib(sourceB, options: TestOptions.ReleaseExe, assemblyName: "X", references: new[] { new CSharpCompilationReference(ca)});
CompileAndVerify(cb, expectedOutput: "42", emitOptions: TestEmitters.CCI).Diagnostics.Verify();
}
#endregion
}
......@@ -184,6 +184,7 @@ public void TryParseDisplayName_QuotingAndEscaping()
TestParseSimpleName("a\\", expected: null);
// double quotes (unescaped can't be in the middle):
TestParseSimpleName("\"a\"", expected: "a");
TestParseSimpleName("\"a'a\", Version=1.0.0.0", expected: "a'a");
TestParseSimpleName("\\\"aa\\\", Version=1.0.0.0", expected: "\"aa\"");
TestParseSimpleName("\\\"a'a\\\", Version=1.0.0.0", expected: null);
......@@ -206,6 +207,7 @@ public void TryParseDisplayName_QuotingAndEscaping()
TestParseSimpleName("\"\"a\"\", Version=1.0.0.0", expected: null);
// single quotes (unescaped can't be in the middle):
TestParseSimpleName("'a'", expected: "a");
TestParseSimpleName("'a\"a', Version=1.0.0.0", expected: "a\"a");
TestParseSimpleName("\\'aa\\', Version=1.0.0.0", expected: "'aa'");
TestParseSimpleName("\\'a\"a\\', Version=1.0.0.0", expected: null);
......@@ -227,6 +229,24 @@ public void TryParseDisplayName_QuotingAndEscaping()
TestParseSimpleName("'', Version=1.0.0.0", expected: null);
TestParseSimpleName("''a'', Version=1.0.0.0", expected: null);
// Unicode quotes
TestParseSimpleName("\u201ca\u201d", expected: "\u201ca\u201d");
TestParseSimpleName("\\u201c;a\\u201d;", expected: "\u201ca\u201d");
TestParseSimpleName("\u201ca", expected: "\u201ca");
TestParseSimpleName("\\u201c;a", expected: "\u201ca");
TestParseSimpleName("a\u201d", expected: "a\u201d");
TestParseSimpleName("a\\u201d;", expected: "a\u201d");
TestParseSimpleName("\u201ca\u201d ", expected: "\u201ca\u201d");
TestParseSimpleName("\\u201c;a\\u201d; ", expected: "\u201ca\u201d");
TestParseSimpleName("\u2018a\u2019", expected: "\u2018a\u2019");
TestParseSimpleName("\\u2018;a\\u2019;", expected: "\u2018a\u2019");
TestParseSimpleName("\u2018a", expected: "\u2018a");
TestParseSimpleName("\\u2018;a", expected: "\u2018a");
TestParseSimpleName("a\u2019", expected: "a\u2019");
TestParseSimpleName("a\\u2019;", expected: "a\u2019");
TestParseSimpleName("\u2018a\u2019 ", expected: "\u2018a\u2019");
TestParseSimpleName("\\u2018;a\\u2019; ", expected: "\u2018a\u2019");
// NUL characters in the name:
TestParseSimpleName(" \0 , Version=1.0.0.0", expected: null);
TestParseSimpleName("zzz, Version=1.0.0\0.0", null);
......@@ -239,11 +259,29 @@ public void TryParseDisplayName_QuotingAndEscaping()
TestParseSimpleName(" , Version=1.0.0.0", expected: null);
// single or double quote if name starts or ends with whitespace:
TestParseSimpleName("\" a \"", expected: " a ");
TestParseSimpleName("' a '", expected: " a ");
TestParseSimpleName("'\r\t\n', Version=1.0.0.0", expected: "\r\t\n");
TestParseSimpleName("\"\r\t\n\", Version=1.0.0.0", expected: "\r\t\n");
TestParseSimpleName("x\n\t\nx, Version=1.0.0.0", expected: "x\n\t\nx");
// Missing parts
TestParseSimpleName("=", null);
TestParseSimpleName(",", null);
TestParseSimpleName("a,", null);
TestParseSimpleName("a ,", null);
TestParseSimpleName("\"a\"=", expected: null);
TestParseSimpleName("\"a\" =", expected: null);
TestParseSimpleName("\"a\",", expected: null);
TestParseSimpleName("\"a\" ,", expected: null);
TestParseSimpleName("'a'=", expected: null);
TestParseSimpleName("'a' =", expected: null);
TestParseSimpleName("'a',", expected: null);
TestParseSimpleName("'a' ,", expected: null);
// skips initial and trailing whitespace characters (' ', \t, \r, \n):
TestParseSimpleName(" \"a\" ", expected: "a");
TestParseSimpleName(" 'a' ", expected: "a");
TestParseSimpleName(" x, Version=1.0.0.0", expected: "x");
TestParseSimpleName(" x\t\r\n , Version=1.0.0.0", expected: "x");
TestParseSimpleName("\u0008x, Version=1.0.0.0", expected: "\u0008x");
......@@ -255,6 +293,12 @@ public void TryParseDisplayName_QuotingAndEscaping()
TestParseSimpleName(" \"aa\" x , Version=1.0.0.0", expected: null);
TestParseSimpleName(" \"aa\" \"\" , Version=1.0.0.0", expected: null);
TestParseSimpleName(" \"aa\" \'\' , Version=1.0.0.0", expected: null);
TestParseSimpleName(" A", "A");
TestParseSimpleName("A ", "A");
TestParseSimpleName(" A ", "A");
TestParseSimpleName(" A, Version=1.0.0.0", "A");
TestParseSimpleName("A , Version=1.0.0.0", "A");
TestParseSimpleName( "A , Version=1.0.0.0", "A");
// invalid characters:
foreach (var c in ClrInvalidCharacters)
......
......@@ -173,8 +173,8 @@ public static bool TryParseDisplayName(string displayName, out AssemblyIdentity
}
int position = 0;
string simpleName = TryParseNameToken(displayName, ',', ref position);
if (simpleName == null)
string simpleName;
if (!TryParseNameToken(displayName, ref position, out simpleName))
{
return false;
}
......@@ -191,18 +191,34 @@ public static bool TryParseDisplayName(string displayName, out AssemblyIdentity
while (position < displayName.Length)
{
string propertyName = TryParseNameToken(displayName, '=', ref position);
if (propertyName == null)
// Parse ',' name '=' value
if (displayName[position] != ',')
{
return false;
}
string propertyValue = TryParseNameToken(displayName, ',', ref position);
if (propertyValue == null)
position++;
string propertyName;
if (!TryParseNameToken(displayName, ref position, out propertyName))
{
return false;
}
if (position >= displayName.Length || displayName[position] != '=')
{
return false;
}
position++;
string propertyValue;
if (!TryParseNameToken(displayName, ref position, out propertyValue))
{
return false;
}
// Process property
if (string.Equals(propertyName, "Version", StringComparison.OrdinalIgnoreCase))
{
if ((seen & AssemblyIdentityParts.Version) != 0)
......@@ -381,21 +397,26 @@ public static bool TryParseDisplayName(string displayName, out AssemblyIdentity
return true;
}
private static string TryParseNameToken(string displayName, char terminator, ref int position)
private static bool TryParseNameToken(string displayName, ref int position, out string value)
{
Debug.Assert(displayName.IndexOf('\0') == -1);
int i = position;
// skip leading whitespace:
while (i < displayName.Length && IsWhiteSpace(displayName[i]))
while (true)
{
i++;
}
if (i == displayName.Length)
{
value = null;
return false;
}
else if (!IsWhiteSpace(displayName[i]))
{
break;
}
if (i == displayName.Length)
{
return null;
i++;
}
char quote;
......@@ -410,75 +431,94 @@ private static string TryParseNameToken(string displayName, char terminator, ref
int valueStart = i;
int valueEnd = displayName.Length;
int escapeCount = 0;
bool containsEscapes = false;
while (i < displayName.Length)
while (true)
{
if (i >= displayName.Length)
{
i = displayName.Length;
break;
}
char c = displayName[i];
if (c == '\\')
{
escapeCount++;
containsEscapes = true;
i += 2;
continue;
}
if (quote == 0)
if (quote == '\0')
{
if (c == terminator)
if (IsNameTokenTerminator(c))
{
int j = i - 1;
while (j >= valueStart && IsWhiteSpace(displayName[j]))
{
j--;
}
valueEnd = j + 1;
break;
}
if (IsQuote(c) || IsNameTokenTerminator(c))
else if (IsQuote(c))
{
return null;
value = null;
return false;
}
}
else if (c == quote)
{
valueEnd = i;
i++;
break;
}
// skip any whitespace following the quote
while (i < displayName.Length && IsWhiteSpace(displayName[i]))
{
i++;
}
i++;
}
if (i < displayName.Length && displayName[i] != terminator)
if (quote == '\0')
{
int j = i - 1;
while (j >= valueStart && IsWhiteSpace(displayName[j]))
{
j--;
}
valueEnd = j + 1;
}
else
{
// skip any whitespace following the quote and check for the terminator
while (i < displayName.Length)
{
char c = displayName[i];
if (!IsWhiteSpace(c))
{
return null;
if (!IsNameTokenTerminator(c))
{
value = null;
return false;
}
break;
}
break;
i++;
}
i++;
}
Debug.Assert(i >= displayName.Length || IsNameTokenTerminator(displayName[i]));
position = (i >= displayName.Length) ? displayName.Length : i + 1;
Debug.Assert(i == displayName.Length || IsNameTokenTerminator(displayName[i]));
position = i;
// empty
if (valueEnd == valueStart)
{
return null;
value = null;
return false;
}
if (escapeCount == 0)
if (!containsEscapes)
{
return displayName.Substring(valueStart, valueEnd - valueStart);
value = displayName.Substring(valueStart, valueEnd - valueStart);
return true;
}
else
{
return Unescape(displayName, valueStart, valueEnd);
return TryUnescape(displayName, valueStart, valueEnd, out value);
}
}
......@@ -687,7 +727,7 @@ private static bool CanBeEscaped(char c)
}
}
private static string Unescape(string str, int start, int end)
private static bool TryUnescape(string str, int start, int end, out string value)
{
var sb = PooledStringBuilder.GetInstance();
......@@ -697,10 +737,10 @@ private static string Unescape(string str, int start, int end)
char c = str[i++];
if (c == '\\')
{
Debug.Assert(CanBeEscaped(c));
if (!Unescape(sb.Builder, str, ref i))
{
return null;
value = null;
return false;
}
}
else
......@@ -709,7 +749,8 @@ private static string Unescape(string str, int start, int end)
}
}
return sb.ToStringAndFree();
value = sb.ToStringAndFree();
return true;
}
private static bool Unescape(StringBuilder sb, string str, ref int i)
......@@ -775,4 +816,4 @@ private static bool Unescape(StringBuilder sb, string str, ref int i)
}
}
}
}
\ No newline at end of file
}
......@@ -935,7 +935,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
If m_lazyInternalsVisibleToMap Is Nothing Then
Interlocked.CompareExchange(m_lazyInternalsVisibleToMap,
New ConcurrentDictionary(Of String, ConcurrentDictionary(Of ImmutableArray(Of Byte), Tuple(Of Location, String))), Nothing)
New ConcurrentDictionary(Of String, ConcurrentDictionary(Of ImmutableArray(Of Byte), Tuple(Of Location, String)))(StringComparer.OrdinalIgnoreCase), Nothing)
End If
'later, once the identity is established we confirm that if the assembly being
......
......@@ -4,6 +4,7 @@ Imports System.Collections.Immutable
Imports System.IO
Imports System.Reflection.Metadata
Imports System.Reflection.PortableExecutable
Imports System.Xml.Linq
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.Emit
......@@ -1555,4 +1556,64 @@ End Class
CompileAndVerify(cb, verify:=False).Diagnostics.Verify()
End Sub
<Fact, WorkItem(1072350, "DevDiv")>
Public Sub Bug1072350()
Dim sourceA As XElement = _
<compilation name="ClassLibrary2">
<file name="a.vb"><![CDATA[
<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("X ")>
Friend Class A
Friend Shared I As Integer = 42
End Class]]>
</file>
</compilation>
Dim sourceB As XElement = _
<compilation name="X">
<file name="b.vb"><![CDATA[
Class B
Shared Sub Main()
System.Console.Write(A.I)
End Sub
End Class]]>
</file>
</compilation>
Dim ca = CreateCompilationWithMscorlib(sourceA, options:=TestOptions.ReleaseDll)
CompileAndVerify(ca)
Dim cb = CreateCompilationWithMscorlib(sourceB, options:=TestOptions.ReleaseExe, references:={ new VisualBasicCompilationReference(ca) })
CompileAndVerify(cb, expectedOutput:="42", emitOptions:=TestEmitters.CCI).Diagnostics.Verify()
End Sub
<Fact, WorkItem(1072339, "DevDiv")>
Public Sub Bug1072339()
Dim sourceA As XElement = _
<compilation name="ClassLibrary2">
<file name="a.vb"><![CDATA[
<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("x")>
Friend Class A
Friend Shared I As Integer = 42
End Class]]>
</file>
</compilation>
Dim sourceB As XElement = _
<compilation name="x">
<file name="b.vb"><![CDATA[
Class B
Shared Sub Main()
System.Console.Write(A.I)
End Sub
End Class]]>
</file>
</compilation>
Dim ca = CreateCompilationWithMscorlib(sourceA, options:=TestOptions.ReleaseDll)
CompileAndVerify(ca)
Dim cb = CreateCompilationWithMscorlib(sourceB, options:=TestOptions.ReleaseExe, references:={ new VisualBasicCompilationReference(ca) })
CompileAndVerify(cb, expectedOutput:="42", emitOptions:=TestEmitters.CCI).Diagnostics.Verify()
End Sub
End Class
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册