提交 5e6d159f 编写于 作者: A Andrew Casey

Ask for bytes of not-yet-loaded winmds

When we try to bind something that is from a not-yet-loaded winmd, we
generally see ErrorCode.ERR_DottedTypeNameNotFoundInNS because the
namespace Windows.X is only provided by Windows.X.winmd.  When this
occurs, heuristically ask the debugger for an assembly with name Windows.X
and content type AssemblyContentType.WindowsRuntime.

The VB equivalent of ERR_DottedTypeNameNotFoundInNS is ERR_NameNotMember2.
However, VB also has a second error code to consider: ERR_UndefinedType1,
which is only produced in type-only contexts.  Since the argument to
ERR_UndefinedType1 is the qualified name, as a string, we have to do some
extra work to determine the next part that needs to be resolved.

Caveat: This doesn't work end-to-end (DevDiv #1130191).
上级 44d08053
......@@ -550,6 +550,21 @@ internal static ImmutableArray<AssemblyIdentity> GetMissingAssemblyIdentitiesHel
}
}
break;
case ErrorCode.ERR_DottedTypeNameNotFoundInNS:
if (arguments.Count == 2)
{
var namespaceName = arguments[0] as string;
var containingNamespace = arguments[1] as NamespaceSymbol;
if (namespaceName != null && (object)containingNamespace != null &&
containingNamespace.ConstituentNamespaces.Any(n => n.ContainingAssembly.Identity.IsWindowsAssemblyIdentity()))
{
// This is just a heuristic, but it has the advantage of being portable, particularly
// across different versions of (desktop) windows.
var identity = new AssemblyIdentity($"{containingNamespace.ToDisplayString()}.{namespaceName}", contentType: System.Reflection.AssemblyContentType.WindowsRuntime);
return ImmutableArray.Create(identity);
}
}
break;
case ErrorCode.ERR_DynamicAttributeMissing:
case ErrorCode.ERR_DynamicRequiredTypesMissing:
// MSDN says these might come from System.Dynamic.Runtime
......
......@@ -380,7 +380,87 @@ static void M()
Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single());
}
[WorkItem(1114866)]
[ConditionalFact(typeof(OSVersionWin8))]
public void NotYetLoadedWinMds()
{
var source =
@"class C
{
static void M(Windows.Storage.StorageFolder f)
{
}
}";
var comp = CreateCompilationWithMscorlib(source, WinRtRefs, TestOptions.DebugDll);
var runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage");
Assert.True(runtimeAssemblies.Any());
var context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies));
const string expectedError = "error CS0234: The type or namespace name 'UI' does not exist in the namespace 'Windows' (are you missing an assembly reference?)";
var expectedMissingAssemblyIdentity = new AssemblyIdentity("Windows.UI", contentType: System.Reflection.AssemblyContentType.WindowsRuntime);
ResultProperties resultProperties;
string actualError;
ImmutableArray<AssemblyIdentity> actualMissingAssemblyIdentities;
context.CompileExpression(
InspectionContextFactory.Empty,
"typeof(@Windows.UI.Colors)",
DkmEvaluationFlags.None,
DiagnosticFormatter.Instance,
out resultProperties,
out actualError,
out actualMissingAssemblyIdentities,
EnsureEnglishUICulture.PreferredOrNull,
testData: null);
Assert.Equal(expectedError, actualError);
Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single());
}
/// <remarks>
/// Windows.UI.Xaml is the only (win8) winmd with more than two parts.
/// </remarks>
[WorkItem(1114866)]
[ConditionalFact(typeof(OSVersionWin8))]
public void NotYetLoadedWinMds_MultipleParts()
{
var source =
@"class C
{
static void M(Windows.UI.Colors c)
{
}
}";
var comp = CreateCompilationWithMscorlib(source, WinRtRefs, TestOptions.DebugDll);
var runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.UI");
Assert.True(runtimeAssemblies.Any());
var context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies));
const string expectedError = "error CS0234: The type or namespace name 'Xaml' does not exist in the namespace 'Windows.UI' (are you missing an assembly reference?)";
var expectedMissingAssemblyIdentity = new AssemblyIdentity("Windows.UI.Xaml", contentType: System.Reflection.AssemblyContentType.WindowsRuntime);
ResultProperties resultProperties;
string actualError;
ImmutableArray<AssemblyIdentity> actualMissingAssemblyIdentities;
context.CompileExpression(
InspectionContextFactory.Empty,
"typeof(Windows.@UI.Xaml.Application)",
DkmEvaluationFlags.None,
DiagnosticFormatter.Instance,
out resultProperties,
out actualError,
out actualMissingAssemblyIdentities,
EnsureEnglishUICulture.PreferredOrNull,
testData: null);
Assert.Equal(expectedError, actualError);
Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single());
}
private EvaluationContext CreateMethodContextWithReferences(Compilation comp, string methodName, params MetadataReference[] references)
{
return CreateMethodContextWithReferences(comp, methodName, ImmutableArray.CreateRange(references));
}
private EvaluationContext CreateMethodContextWithReferences(Compilation comp, string methodName, ImmutableArray<MetadataReference> references)
{
byte[] exeBytes;
byte[] pdbBytes;
......@@ -388,7 +468,7 @@ private EvaluationContext CreateMethodContextWithReferences(Compilation comp, st
var result = comp.EmitAndGetReferences(out exeBytes, out pdbBytes, out unusedReferences);
Assert.True(result);
var runtime = CreateRuntimeInstance(GetUniqueName(), ImmutableArray.CreateRange(references), exeBytes, new SymReader(pdbBytes));
var runtime = CreateRuntimeInstance(GetUniqueName(), references, exeBytes, new SymReader(pdbBytes));
return CreateMethodContext(runtime, methodName);
}
......
......@@ -194,6 +194,12 @@ internal static bool IsWindowsAssemblyName(string assemblyName)
return assemblyName.Equals("windows", StringComparison.OrdinalIgnoreCase);
}
internal static bool IsWindowsAssemblyIdentity(this AssemblyIdentity assemblyIdentity)
{
return IsWindowsAssemblyName(assemblyIdentity.Name) &&
assemblyIdentity.ContentType == System.Reflection.AssemblyContentType.WindowsRuntime;
}
internal static LocalInfo<TTypeSymbol> GetLocalInfo<TModuleSymbol, TTypeSymbol, TMethodSymbol, TFieldSymbol, TSymbol>(
this MetadataDecoder<TModuleSymbol, TTypeSymbol, TMethodSymbol, TFieldSymbol, TSymbol> metadataDecoder,
byte[] signature)
......
......@@ -562,17 +562,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
End Sub
Friend Overrides Function GetMissingAssemblyIdentities(diagnostic As Diagnostic) As ImmutableArray(Of AssemblyIdentity)
Return GetMissingAssemblyIdentitiesHelper(CType(diagnostic.Code, ERRID), diagnostic.Arguments)
Return GetMissingAssemblyIdentitiesHelper(CType(diagnostic.Code, ERRID), diagnostic.Arguments, Me.Compilation.GlobalNamespace)
End Function
''' <remarks>
''' Friend for testing.
''' </remarks>
Friend Shared Function GetMissingAssemblyIdentitiesHelper(code As ERRID, arguments As IReadOnlyList(Of Object)) As ImmutableArray(Of AssemblyIdentity)
Select Case code
Friend Shared Function GetMissingAssemblyIdentitiesHelper(code As ERRID, arguments As IReadOnlyList(Of Object), globalNamespace As NamespaceSymbol) As ImmutableArray(Of AssemblyIdentity)
Select Case code
Case ERRID.ERR_UnreferencedAssemblyEvent3, ERRID.ERR_UnreferencedAssembly3
For Each argument As Object In arguments
Dim identity = If(TryCast(argument, AssemblyIdentity), TryCast(argument, AssemblySymbol)?.Identity)
Dim identity = If(TryCast(argument, AssemblyIdentity), TryCast(argument, AssemblySymbol)?.Identity)
If IsValidMissingAssemblyIdentity(identity) Then
Return ImmutableArray.Create(identity)
End If
......@@ -584,6 +584,49 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Return ImmutableArray.Create(identity)
End If
End If
Case ERRID.ERR_NameNotMember2
If arguments.Count = 2 Then
Dim namespaceName = TryCast(arguments(0), String)
Dim containingNamespace = TryCast(arguments(1), NamespaceSymbol)
If namespaceName IsNot Nothing AndAlso containingNamespace IsNot Nothing AndAlso HasConstituentFromWindowsAssembly(containingNamespace) Then
' This is just a heuristic, but it has the advantage of being portable, particularly
' across different versions of (desktop) windows.
Dim identity = New AssemblyIdentity($"{containingNamespace.ToDisplayString}.{namespaceName}", contentType:=AssemblyContentType.WindowsRuntime)
Return ImmutableArray.Create(identity)
End If
End If
Case ERRID.ERR_UndefinedType1
If arguments.Count = 1 Then
Dim qualifiedName = TryCast(arguments(0), String)
If Not String.IsNullOrEmpty(qualifiedName) Then
Dim nameParts = qualifiedName.Split("."c)
Dim numParts = nameParts.Length
Dim pos = 0
If CaseInsensitiveComparison.Comparer.Equals(nameParts(0), "global") Then
pos = 1
Debug.Assert(pos < numParts)
End If
Dim currNamespace = globalNamespace
While pos < numParts
Dim nextNamespace = currNamespace.GetMembers(nameParts(pos)).OfType(Of NamespaceSymbol).SingleOrDefault()
If nextNamespace Is Nothing Then
Exit While
End If
pos += 1
currNamespace = nextNamespace
End While
If currNamespace IsNot globalNamespace AndAlso HasConstituentFromWindowsAssembly(currNamespace) AndAlso pos < numParts Then
Dim nextNamePart = nameParts(pos)
If nextNamePart.All(AddressOf SyntaxFacts.IsIdentifierPartCharacter) Then
' This is just a heuristic, but it has the advantage of being portable, particularly
' across different versions of (desktop) windows.
Dim identity = New AssemblyIdentity($"{currNamespace.ToDisplayString}.{nameParts(pos)}", contentType:=AssemblyContentType.WindowsRuntime)
Return ImmutableArray.Create(identity)
End If
End If
End If
End If
Case ERRID.ERR_XmlFeaturesNotAvailable
Return ImmutableArray.Create(SystemIdentity, SystemCoreIdentity, SystemXmlIdentity, SystemXmlLinqIdentity)
Case ERRID.ERR_MissingRuntimeHelper
......@@ -593,6 +636,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Return Nothing
End Function
Private Shared Function HasConstituentFromWindowsAssembly(namespaceSymbol As NamespaceSymbol) As Boolean
Return namespaceSymbol.ConstituentNamespaces.Any(Function(n) n.ContainingAssembly.Identity.IsWindowsAssemblyIdentity)
End Function
Private Shared Function IsValidMissingAssemblyIdentity(identity As AssemblyIdentity) As Boolean
Return identity IsNot Nothing AndAlso Not identity.Equals(MissingCorLibrarySymbol.Instance.Identity)
End Function
......
......@@ -229,6 +229,43 @@ End Class
Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single())
End Sub
<WorkItem(1114866)>
<ConditionalFact(GetType(OSVersionWin8))>
Public Sub ERR_UndefinedType1()
Dim source = "
Class C
Sub M()
End Sub
End Class
"
Dim comp = CreateCompilationWithReferences({VisualBasicSyntaxTree.ParseText(source)}, {MscorlibRef}.Concat(WinRtRefs), TestOptions.DebugDll)
Dim runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.UI")
Assert.True(runtimeAssemblies.Any())
Dim context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies))
Dim globalNamespace = context.Compilation.GlobalNamespace
Dim expectedIdentity = New AssemblyIdentity("Windows.Storage", contentType:=Reflection.AssemblyContentType.WindowsRuntime)
Dim actualIdentity = EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Windows.Storage"}, globalNamespace).Single()
Assert.Equal(expectedIdentity, actualIdentity)
actualIdentity = EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Global.Windows.Storage"}, globalNamespace).Single()
Assert.Equal(expectedIdentity, actualIdentity)
actualIdentity = EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Global.Windows.Storage.Additional"}, globalNamespace).Single()
Assert.Equal(expectedIdentity, actualIdentity)
expectedIdentity = New AssemblyIdentity("Windows.UI.Xaml", contentType:=Reflection.AssemblyContentType.WindowsRuntime)
actualIdentity = EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Windows.UI.Xaml"}, globalNamespace).Single()
Assert.Equal(expectedIdentity, actualIdentity)
Assert.True(EvaluationContext.GetMissingAssemblyIdentitiesHelper(ERRID.ERR_UndefinedType1, {"Windows.UI.Xaml(Of T)"}, globalNamespace).IsDefault)
End Sub
<WorkItem(1124725, "DevDiv")>
<WorkItem(597, "GitHub")>
<Fact>
......@@ -264,19 +301,131 @@ End Class
Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single())
End Sub
<WorkItem(1114866)>
<ConditionalFact(GetType(OSVersionWin8))>
Public Sub NotYetLoadedWinMds()
Dim source = "
Class C
Shared Sub M(f As Windows.Storage.StorageFolder)
End Sub
End Class
"
Dim comp = CreateCompilationWithReferences({VisualBasicSyntaxTree.ParseText(source)}, {MscorlibRef}.Concat(WinRtRefs), TestOptions.DebugDll)
Dim runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage")
Assert.True(runtimeAssemblies.Any())
Dim context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies))
Const expectedError = "(1,1): error BC30456: 'UI' is not a member of 'Windows'."
Dim expectedMissingAssemblyIdentity = New AssemblyIdentity("Windows.UI", contentType:=System.Reflection.AssemblyContentType.WindowsRuntime)
Dim resultProperties As ResultProperties = Nothing
Dim actualError As String = Nothing
Dim actualMissingAssemblyIdentities As ImmutableArray(Of AssemblyIdentity) = Nothing
context.CompileExpression(
InspectionContextFactory.Empty,
"Windows.UI.Colors",
DkmEvaluationFlags.None,
DiagnosticFormatter.Instance,
resultProperties,
actualError,
actualMissingAssemblyIdentities,
EnsureEnglishUICulture.PreferredOrNull,
testData:=Nothing)
Assert.Equal(expectedError, actualError)
Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single())
End Sub
''' <remarks>
''' Windows.UI.Xaml is the only (win8) winmd with more than two parts.
''' </remarks>
<WorkItem(1114866)>
<ConditionalFact(GetType(OSVersionWin8))>
Public Sub NotYetLoadedWinMds_MultipleParts()
Dim source = "
Class C
Shared Sub M(c As Windows.UI.Colors)
End Sub
End Class
"
Dim comp = CreateCompilationWithReferences({VisualBasicSyntaxTree.ParseText(source)}, {MscorlibRef}.Concat(WinRtRefs), TestOptions.DebugDll)
Dim runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.UI")
Assert.True(runtimeAssemblies.Any())
Dim context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies))
Const expectedError = "(1,1): error BC30456: 'Xaml' is not a member of 'Windows.UI'."
Dim expectedMissingAssemblyIdentity = New AssemblyIdentity("Windows.UI.Xaml", contentType:=System.Reflection.AssemblyContentType.WindowsRuntime)
Dim resultProperties As ResultProperties = Nothing
Dim actualError As String = Nothing
Dim actualMissingAssemblyIdentities As ImmutableArray(Of AssemblyIdentity) = Nothing
context.CompileExpression(
InspectionContextFactory.Empty,
"Windows.[UI].Xaml.Application",
DkmEvaluationFlags.None,
DiagnosticFormatter.Instance,
resultProperties,
actualError,
actualMissingAssemblyIdentities,
EnsureEnglishUICulture.PreferredOrNull,
testData:=Nothing)
Assert.Equal(expectedError, actualError)
Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single())
End Sub
<WorkItem(1114866)>
<ConditionalFact(GetType(OSVersionWin8))>
Public Sub NotYetLoadedWinMds_GetType()
Dim source = "
Class C
Shared Sub M(f As Windows.Storage.StorageFolder)
End Sub
End Class
"
Dim comp = CreateCompilationWithReferences({VisualBasicSyntaxTree.ParseText(source)}, {MscorlibRef}.Concat(WinRtRefs), TestOptions.DebugDll)
Dim runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage")
Assert.True(runtimeAssemblies.Any())
Dim context = CreateMethodContextWithReferences(comp, "C.M", ImmutableArray.Create(MscorlibRef).Concat(runtimeAssemblies))
Const expectedError = "(1,9): error BC30002: Type 'Windows.UI.Colors' is not defined."
Dim expectedMissingAssemblyIdentity = New AssemblyIdentity("Windows.UI", contentType:=System.Reflection.AssemblyContentType.WindowsRuntime)
Dim resultProperties As ResultProperties = Nothing
Dim actualError As String = Nothing
Dim actualMissingAssemblyIdentities As ImmutableArray(Of AssemblyIdentity) = Nothing
context.CompileExpression(
InspectionContextFactory.Empty,
"GetType([Windows].UI.Colors)",
DkmEvaluationFlags.None,
DiagnosticFormatter.Instance,
resultProperties,
actualError,
actualMissingAssemblyIdentities,
EnsureEnglishUICulture.PreferredOrNull,
testData:=Nothing)
Assert.Equal(expectedError, actualError)
Assert.Equal(expectedMissingAssemblyIdentity, actualMissingAssemblyIdentities.Single())
End Sub
Private Function CreateMethodContextWithReferences(comp As Compilation, methodName As String, ParamArray references As MetadataReference()) As EvaluationContext
Return CreateMethodContextWithReferences(comp, methodName, ImmutableArray.CreateRange(references))
End Function
Private Function CreateMethodContextWithReferences(comp As Compilation, methodName As String, references As ImmutableArray(Of MetadataReference)) As EvaluationContext
Dim exeBytes As Byte() = Nothing
Dim pdbBytes As Byte() = Nothing
Dim unusedReferences As ImmutableArray(Of MetadataReference) = Nothing
Dim result = comp.EmitAndGetReferences(exeBytes, pdbBytes, unusedReferences)
Assert.True(result)
Dim runtime = CreateRuntimeInstance(GetUniqueName(), ImmutableArray.CreateRange(references), exeBytes, New SymReader(pdbBytes))
Dim runtime = CreateRuntimeInstance(GetUniqueName(), references, exeBytes, New SymReader(pdbBytes))
Return CreateMethodContext(runtime, methodName)
End Function
Private Shared Function GetMissingAssemblyIdentities(code As ERRID, ParamArray arguments As Object()) As ImmutableArray(Of AssemblyIdentity)
Return EvaluationContext.GetMissingAssemblyIdentitiesHelper(code, arguments)
Return EvaluationContext.GetMissingAssemblyIdentitiesHelper(code, arguments, globalNamespace:=Nothing)
End Function
End Class
End Namespace
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册