Skip to content

Commit

Permalink
Merge pull request #446 from microsoft/fix429
Browse files Browse the repository at this point in the history
Fix cross-winmd type references
  • Loading branch information
AArnott committed Nov 9, 2021
2 parents a7738b4 + a6cf8d7 commit 412cada
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 149 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ csharp_prefer_braces = true:silent
# SA1130: Use lambda syntax
dotnet_diagnostic.SA1130.severity = silent

# SA1649 : Filenames must match first declared type
dotnet_diagnostic.SA1649.severity = none # Disabled because of https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3395

# IDE0005: Using directive is unnecessary.
dotnet_diagnostic.IDE0005.severity = warning

Expand Down
228 changes: 141 additions & 87 deletions src/Microsoft.Windows.CsWin32/Generator.cs

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions src/Microsoft.Windows.CsWin32/HandleTypeHandleInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ internal override TypeSyntaxAndMarshaling ToTypeSyntax(TypeSyntaxSettings inputs
var ns = qualifiedName.Left.ToString();

// Look for WinRT namespaces
if (ns.StartsWith("Windows.Foundation") || ns.StartsWith("Windows.UI") || ns.StartsWith("Windows.Graphics") || ns.StartsWith("Windows.System"))
if (ns.StartsWith("global::Windows.Foundation") || ns.StartsWith("global::Windows.UI") || ns.StartsWith("global::Windows.Graphics") || ns.StartsWith("global::Windows.System"))
{
// We only want to marshal WinRT objects, not interfaces. We don't have a good way of knowing
// whether it's an interface or an object. "isInterface" comes back as false for a WinRT interface,
Expand All @@ -121,7 +121,13 @@ internal override TypeSyntaxAndMarshaling ToTypeSyntax(TypeSyntaxSettings inputs
bool isInterfaceName = InterfaceNameMatcher.IsMatch(objName);
if (!isInterfaceName)
{
return new TypeSyntaxAndMarshaling(syntax, new MarshalAsAttribute(UnmanagedType.CustomMarshaler) { MarshalCookie = nameSyntax.ToString(), MarshalType = Generator.WinRTCustomMarshalerFullName });
string marshalCookie = nameSyntax.ToString();
if (marshalCookie.StartsWith(Generator.GlobalNamespacePrefix, StringComparison.Ordinal))
{
marshalCookie = marshalCookie.Substring(Generator.GlobalNamespacePrefix.Length);
}

return new TypeSyntaxAndMarshaling(syntax, new MarshalAsAttribute(UnmanagedType.CustomMarshaler) { MarshalCookie = marshalCookie, MarshalType = Generator.WinRTCustomMarshalerFullName });
}
}
}
Expand Down
46 changes: 46 additions & 0 deletions src/Microsoft.Windows.CsWin32/MetadataQualifiedTokens.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.Windows.CsWin32
{
using System.Reflection.Metadata;

#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
#pragma warning disable SA1649 // Filenames must match first declared type

internal record struct QualifiedTypeReferenceHandle(Generator Generator, TypeReferenceHandle ReferenceHandle)
{
internal MetadataReader Reader => this.Generator.Reader;

internal QualifiedTypeReference Resolve() => new(this.Generator, this.Generator.Reader.GetTypeReference(this.ReferenceHandle));
}

internal record struct QualifiedTypeReference(Generator Generator, TypeReference Reference)
{
internal MetadataReader Reader => this.Generator.Reader;
}

internal record struct QualifiedTypeDefinitionHandle(Generator Generator, TypeDefinitionHandle DefinitionHandle)
{
internal MetadataReader Reader => this.Generator.Reader;

internal QualifiedTypeDefinition Resolve() => new(this.Generator, this.Generator.Reader.GetTypeDefinition(this.DefinitionHandle));
}

internal record struct QualifiedTypeDefinition(Generator Generator, TypeDefinition Definition)
{
internal MetadataReader Reader => this.Generator.Reader;
}

internal record struct QualifiedMethodDefinitionHandle(Generator Generator, MethodDefinitionHandle MethodHandle)
{
internal MetadataReader Reader => this.Generator.Reader;

internal QualifiedMethodDefinition Resolve() => new(this.Generator, this.Generator.Reader.GetMethodDefinition(this.MethodHandle));
}

internal record struct QualifiedMethodDefinition(Generator Generator, MethodDefinition Method)
{
internal MetadataReader Reader => this.Generator.Reader;
}
}
58 changes: 42 additions & 16 deletions src/Microsoft.Windows.CsWin32/SuperGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,47 +51,73 @@ public static SuperGenerator Combine(IEnumerable<Generator> generators)
/// <summary>
/// Looks up the <see cref="Generator"/> that owns a referenced type.
/// </summary>
/// <param name="requestingGenerator">The generator asking the question.</param>
/// <param name="typeRef">The type reference from the requesting generator.</param>
/// <param name="typeRef">The generator and type reference from the requesting generator.</param>
/// <param name="targetGenerator">Receives the generator that owns the referenced type.</param>
/// <returns><see langword="true"/> if a matching generator was found; otherwise <see langword="false"/>.</returns>
internal bool TryGetTargetGenerator(Generator requestingGenerator, TypeReference typeRef, [NotNullWhen(true)] out Generator? targetGenerator)
internal bool TryGetTargetGenerator(QualifiedTypeReference typeRef, [NotNullWhen(true)] out Generator? targetGenerator)
{
if (typeRef.ResolutionScope.Kind != HandleKind.AssemblyReference)
if (typeRef.Reference.ResolutionScope.Kind != HandleKind.AssemblyReference)
{
targetGenerator = null;
return false;
}

AssemblyReference assemblyRef = requestingGenerator.Reader.GetAssemblyReference((AssemblyReferenceHandle)typeRef.ResolutionScope);
string scope = requestingGenerator.Reader.GetString(assemblyRef.Name);
AssemblyReference assemblyRef = typeRef.Generator.Reader.GetAssemblyReference((AssemblyReferenceHandle)typeRef.Reference.ResolutionScope);
string scope = typeRef.Generator.Reader.GetString(assemblyRef.Name);
return this.TryGetGenerator(scope, out targetGenerator);
}

internal bool TryGetGenerator(string winmdName, [NotNullWhen(true)] out Generator? targetGenerator)
{
// Workaround the fact that these winmd references may oddly have .winmd included as a suffix.
if (scope.EndsWith(".winmd", StringComparison.OrdinalIgnoreCase))
if (winmdName.EndsWith(".winmd", StringComparison.OrdinalIgnoreCase))
{
scope = scope.Substring(0, scope.Length - ".winmd".Length);
winmdName = winmdName.Substring(0, winmdName.Length - ".winmd".Length);
}

return this.Generators.TryGetValue(scope, out targetGenerator);
return this.Generators.TryGetValue(winmdName, out targetGenerator);
}

internal bool TryGetTypeDefinitionHandle(QualifiedTypeReferenceHandle typeRefHandle, out QualifiedTypeDefinitionHandle typeDefHandle)
{
if (typeRefHandle.Generator.TryGetTypeDefHandle(typeRefHandle.ReferenceHandle, out TypeDefinitionHandle localHandle))
{
typeDefHandle = new QualifiedTypeDefinitionHandle(typeRefHandle.Generator, localHandle);
return true;
}

QualifiedTypeReference typeRef = typeRefHandle.Resolve();
if (this.TryGetTargetGenerator(typeRef, out Generator? targetGenerator))
{
var @namespace = typeRef.Generator.Reader.GetString(typeRef.Reference.Namespace);
var @name = typeRef.Generator.Reader.GetString(typeRef.Reference.Name);
if (targetGenerator.TryGetTypeDefHandle(@namespace, name, out TypeDefinitionHandle targetTypeDefHandle))
{
typeDefHandle = new QualifiedTypeDefinitionHandle(targetGenerator, targetTypeDefHandle);
return true;
}
}

typeDefHandle = default;
return false;
}

/// <summary>
/// Requests generation of a type referenced across metadata files.
/// </summary>
/// <param name="requestingGenerator">The generator making the request.</param>
/// <param name="typeRef">The referenced type.</param>
/// <param name="typeRef">The referenced type, with generator.</param>
/// <returns><see langword="true" /> if a matching generator was found; <see langword="false" /> otherwise.</returns>
internal bool TryRequestInteropType(Generator requestingGenerator, TypeReference typeRef)
internal bool TryRequestInteropType(QualifiedTypeReference typeRef)
{
if (typeRef.ResolutionScope.Kind != HandleKind.AssemblyReference)
if (typeRef.Reference.ResolutionScope.Kind != HandleKind.AssemblyReference)
{
throw new ArgumentException("Only type references across assemblies should be requested.", nameof(typeRef));
}

if (this.TryGetTargetGenerator(requestingGenerator, typeRef, out Generator? generator))
if (this.TryGetTargetGenerator(typeRef, out Generator? generator))
{
string ns = requestingGenerator.Reader.GetString(typeRef.Namespace);
string name = requestingGenerator.Reader.GetString(typeRef.Name);
string ns = typeRef.Generator.Reader.GetString(typeRef.Reference.Namespace);
string name = typeRef.Generator.Reader.GetString(typeRef.Reference.Name);
generator.RequestInteropType(ns, name);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/// <inheritdoc cref="CoCreateInstance(Guid*, object, win32.System.Com.CLSCTX, Guid*, out object)"/>
internal static unsafe win32.Foundation.HRESULT CoCreateInstance<T>(in Guid rclsid, object pUnkOuter, win32.System.Com.CLSCTX dwClsContext, out T ppv)
/// <inheritdoc cref="CoCreateInstance(Guid*, object, global::Windows.Win32.System.Com.CLSCTX, Guid*, out object)"/>
internal static unsafe global::Windows.Win32.Foundation.HRESULT CoCreateInstance<T>(in Guid rclsid, object pUnkOuter, global::Windows.Win32.System.Com.CLSCTX dwClsContext, out T ppv)
where T : class
{
win32.Foundation.HRESULT hr = CoCreateInstance(rclsid, pUnkOuter, dwClsContext, typeof(T).GUID, out object o);
global::Windows.Win32.Foundation.HRESULT hr = CoCreateInstance(rclsid, pUnkOuter, dwClsContext, typeof(T).GUID, out object o);
ppv = (T)o;
return hr;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/// <inheritdoc cref="CoCreateInstance(Guid*, win32.System.Com.IUnknown*, win32.System.Com.CLSCTX, Guid*, void**)"/>
internal static unsafe win32.Foundation.HRESULT CoCreateInstance<T>(in Guid rclsid, win32.System.Com.IUnknown* pUnkOuter, win32.System.Com.CLSCTX dwClsContext, out T* ppv)
/// <inheritdoc cref="CoCreateInstance(Guid*, global::Windows.Win32.System.Com.IUnknown*, global::Windows.Win32.System.Com.CLSCTX, Guid*, void**)"/>
internal static unsafe global::Windows.Win32.Foundation.HRESULT CoCreateInstance<T>(in Guid rclsid, global::Windows.Win32.System.Com.IUnknown* pUnkOuter, global::Windows.Win32.System.Com.CLSCTX dwClsContext, out T* ppv)
where T : unmanaged
{
win32.Foundation.HRESULT hr = CoCreateInstance(rclsid, pUnkOuter, dwClsContext, typeof(T).GUID, out void* o);
global::Windows.Win32.Foundation.HRESULT hr = CoCreateInstance(rclsid, pUnkOuter, dwClsContext, typeof(T).GUID, out void* o);
ppv = (T*)o;
return hr;
}
Loading

0 comments on commit 412cada

Please sign in to comment.