Skip to content

Commit

Permalink
Merge pull request #1432 from microsoft/manodasanw/naot-winrt-exports…
Browse files Browse the repository at this point in the history
…-authoringtestfixes2

Fix test issues discovered from enabling AOT for authoring scenarios
  • Loading branch information
manodasanW authored Jan 9, 2024
2 parents f02bf33 + 2453f07 commit 9fea72b
Show file tree
Hide file tree
Showing 34 changed files with 871 additions and 388 deletions.
102 changes: 77 additions & 25 deletions src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs

Large diffs are not rendered by default.

30 changes: 24 additions & 6 deletions src/Authoring/WinRT.SourceGenerator/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ private static bool IsFundamentalType(ISymbol type)
}

public static bool IsWinRTType(ISymbol type)
{
return IsWinRTType(type, null);
}

public static bool IsWinRTType(ISymbol type, Func<ISymbol, bool> isAuthoringWinRTType)
{
bool isProjectedType = type.GetAttributes().
Any(attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WindowsRuntimeTypeAttribute") == 0) ||
Expand All @@ -206,8 +211,11 @@ public static bool IsWinRTType(ISymbol type)
// Ensure all generic parameters are WinRT types.
if (isProjectedType && type is INamedTypeSymbol namedType && namedType.IsGenericType && !namedType.IsDefinition)
{
isProjectedType = namedType.TypeArguments.All(IsWinRTType);
isProjectedType = namedType.TypeArguments.All(t =>
IsWinRTType(t, isAuthoringWinRTType) ||
(isAuthoringWinRTType != null && isAuthoringWinRTType(t)));
}

return isProjectedType;
}

Expand All @@ -218,13 +226,23 @@ public static bool IsInternalInterfaceFromReferences(INamedTypeSymbol iface, IAs
(iface.IsGenericType && iface.TypeArguments.Any(typeArgument => IsInternalInterfaceFromReferences(typeArgument as INamedTypeSymbol, currentAssembly)));
}

// Checks whether the symbol references any generic that hasn't been instantiated.
// For instance, List<T> where T is a generic.
public static bool HasNonInstantiatedGeneric(ITypeSymbol symbol)
// Checks whether the symbol references any generic that hasn't been instantiated
// and is used by a WinRT interface. For instance, List<T> where T is a generic.
// If the generic isn't used by any WinRT interface, this returns false as for
// instance, we can still generate the vtable attribute for it.
public static bool HasNonInstantiatedWinRTGeneric(ITypeSymbol symbol)
{
return symbol is INamedTypeSymbol namedType &&
(namedType.TypeKind == TypeKind.TypeParameter ||
namedType.TypeArguments.Any(argument => argument.TypeKind == TypeKind.TypeParameter));
(IsArgumentTypeParameter(namedType) ||
(namedType.TypeArguments.Any(IsArgumentTypeParameter) &&
namedType.AllInterfaces.Any(iface => iface.TypeArguments.Any(IsArgumentTypeParameter) &&
// Checks if without the non-instantiated generic, whether it would be a WinRT type.
IsWinRTType(iface.OriginalDefinition, null))));

static bool IsArgumentTypeParameter(ITypeSymbol argument)
{
return argument.TypeKind == TypeKind.TypeParameter;
}
}

public static bool HasPrivateclass(ITypeSymbol symbol)
Expand Down
28 changes: 24 additions & 4 deletions src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2663,8 +2663,13 @@ public void FinalizeGeneration()

public void GenerateWinRTExposedClassAttributes(GeneratorExecutionContext context)
{
static bool IsWinRTType(ISymbol symbol)
bool IsWinRTType(ISymbol symbol)
{
if (!SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, context.Compilation.Assembly))
{
return GeneratorHelper.IsWinRTType(symbol, IsWinRTType);
}

if (symbol is INamedTypeSymbol namedType)
{
if (namedType.TypeKind == TypeKind.Interface)
Expand All @@ -2687,6 +2692,8 @@ static bool IsWinRTType(ISymbol symbol)
}

List<VtableAttribute> vtableAttributesToAdd = new();
HashSet<VtableAttribute> vtableAttributesToAddOnLookupTable = new();

foreach (var typeDeclaration in typeDefinitionMapping.Values)
{
if (typeDeclaration.IsComponentType &&
Expand All @@ -2695,15 +2702,28 @@ typeDeclaration.Node is INamedTypeSymbol symbol &&
!symbol.IsStatic)
{
vtableAttributesToAdd.Add(WinRTAotSourceGenerator.GetVtableAttributeToAdd(symbol, IsWinRTType, context.Compilation.Assembly, true, typeDeclaration.DefaultInterface));
WinRTAotSourceGenerator.AddVtableAdapterTypeForKnownInterface(symbol, context.Compilation, IsWinRTType, vtableAttributesToAddOnLookupTable);
}
}

if (vtableAttributesToAdd.Any())
if (vtableAttributesToAdd.Any() || vtableAttributesToAddOnLookupTable.Any())
{
WinRTAotSourceGenerator.GenerateVtableAttributes(context.AddSource, vtableAttributesToAdd.ToImmutableArray());
WinRTAotSourceGenerator.GenerateCCWForGenericInstantiation(
context.AddSource,
vtableAttributesToAdd.SelectMany(static (vtableAttribute, _) => vtableAttribute.GenericInterfaces).ToImmutableArray());
vtableAttributesToAdd.SelectMany(static (vtableAttribute, _) => vtableAttribute.GenericInterfaces).
Union(vtableAttributesToAddOnLookupTable.SelectMany(static (vtableAttribute, _) => vtableAttribute.GenericInterfaces)).
Distinct().
ToImmutableArray());
}

if (vtableAttributesToAdd.Any())
{
WinRTAotSourceGenerator.GenerateVtableAttributes(context.AddSource, vtableAttributesToAdd.ToImmutableArray(), false);
}

if (vtableAttributesToAddOnLookupTable.Any())
{
WinRTAotSourceGenerator.GenerateVtableLookupTable(context.AddSource, (vtableAttributesToAddOnLookupTable.ToImmutableArray(), (true, true)), true);
}
}

Expand Down
92 changes: 76 additions & 16 deletions src/Perf/IIDOptimizer/SignatureEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,8 @@ private void GenerateGuidFactoryFromComplexSignature(TypeDefinition implementati
il.Emit(OpCodes.Stloc, destination);

// SHA1.HashData(fullSignatureBuffer, destination);
var sha1Type = CecilExtensions.FindTypeReference(module, "System.Security.Cryptography", "SHA1", "System.Security.Cryptography.Algorithms", false);
var sha1Type = module.ImportReference(
new TypeReference("System.Security.Cryptography", "SHA1", module, new AssemblyNameReference("System.Security.Cryptography.Algorithms", default), false).Resolve());
var hashDataMethod = module.ImportReference(
new MethodReference("HashData", module.ImportReference(module.TypeSystem.Int32), sha1Type)
{
Expand All @@ -427,30 +428,89 @@ private void GenerateGuidFactoryFromComplexSignature(TypeDefinition implementati
}
});

var spanToReadOnlySpan = module.ImportReference(
new MethodReference("op_Implicit",
new GenericInstanceType(module.ImportReference(readOnlySpanOfByte.Resolve()))
// HashData is not defined in .NET Standard
if (hashDataMethod.Resolve() is null)
{
var spanToArrayMethod = module.ImportReference(
new MethodReference("ToArray", new ArrayType(span.Resolve().GenericParameters[0]), spanOfByte)
{
HasThis = true,
});

// byte[] arrayToHash = data.ToArray();
var arrayToHash = new VariableDefinition(new ArrayType(module.TypeSystem.Byte));
staticCtor.Body.Variables.Add(arrayToHash);
il.Emit(OpCodes.Ldloca, fullSignatureBuffer);
il.Emit(OpCodes.Call, spanToArrayMethod);
il.Emit(OpCodes.Stloc, arrayToHash);

// using (SHA1 sha = new SHA1CryptoServiceProvider())
// destination = sha.ComputeHash(data);
var sha1CryptoServiceProvider = module.ImportReference(
new TypeReference("System.Security.Cryptography", "SHA1CryptoServiceProvider", module, new AssemblyNameReference("netstandard", default), false).Resolve());
var sha = new VariableDefinition(sha1CryptoServiceProvider);
staticCtor.Body.Variables.Add(sha);
var sha1CryptoServiceProviderCtor = module.ImportReference(
new MethodReference(".ctor", module.TypeSystem.Void, sha1CryptoServiceProvider)
{
GenericArguments = { span.Resolve().GenericParameters[0] }
HasThis = true
},
spanOfByte)
{
HasThis = false,
Parameters =
sha1CryptoServiceProvider);
il.Emit(OpCodes.Newobj, sha1CryptoServiceProviderCtor);
il.Emit(OpCodes.Stloc, sha);

var computeHashMethod = module.ImportReference(
new MethodReference("ComputeHash", new ArrayType(module.TypeSystem.Byte), sha1CryptoServiceProvider)
{
HasThis = true,
Parameters =
{
new ParameterDefinition(new ArrayType(module.TypeSystem.Byte))
}
});
il.Emit(OpCodes.Ldloc, sha);
il.Emit(OpCodes.Ldloc, arrayToHash);
il.Emit(OpCodes.Callvirt, computeHashMethod);
il.Emit(OpCodes.Newobj, spanOfByteArrayCtor);
il.Emit(OpCodes.Stloc, destination);

var disposable = module.ImportReference(
new TypeReference("System", "IDisposable", module, new AssemblyNameReference("netstandard", default), false).Resolve());
var disposeMethod = module.ImportReference(
new MethodReference("Dispose", module.TypeSystem.Void, disposable)
{
HasThis = true,
});
il.Emit(OpCodes.Ldloc, sha);
il.Emit(OpCodes.Callvirt, disposeMethod);
}
else
{
var spanToReadOnlySpan = module.ImportReference(
new MethodReference("op_Implicit",
new GenericInstanceType(module.ImportReference(readOnlySpanOfByte.Resolve()))
{
GenericArguments = { span.Resolve().GenericParameters[0] }
},
spanOfByte)
{
HasThis = false,
Parameters =
{
new ParameterDefinition(
new GenericInstanceType(span)
{
GenericArguments = { span.Resolve().GenericParameters[0] }
})
}
});
}
});

il.Emit(OpCodes.Ldloc, fullSignatureBuffer);
il.Emit(OpCodes.Call, spanToReadOnlySpan);
il.Emit(OpCodes.Ldloc, destination);
il.Emit(OpCodes.Call, hashDataMethod);
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldloc, fullSignatureBuffer);
il.Emit(OpCodes.Call, spanToReadOnlySpan);
il.Emit(OpCodes.Ldloc, destination);
il.Emit(OpCodes.Call, hashDataMethod);
il.Emit(OpCodes.Pop);
}

// Fix endianness, bytes
var memoryExtensions = CecilExtensions.FindTypeReference(module, "System", "MemoryExtensions", "System.Memory", false);
Expand Down
7 changes: 5 additions & 2 deletions src/Perf/IIDOptimizer/SignatureGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ public SignaturePart GetSignatureParts(TypeReference type)
var typeDef = type.Resolve();

var helperType = new TypeReference($"ABI.{typeDef.Namespace}", typeDef.Name, typeDef.Module, assembly.MainModule);

if (helperType.Resolve() is not null)
if (helperType.Resolve() is not null ||
// Handle custom mapped built-in structs such as System.Numerics.Vector3 which have their ABI type defined in WinRT.Runtime.
// This is handled separately due to the need for the is public check which isn't needed if in same module as in the initial case.
((helperType = typeDef.GetCswinrtAbiTypeDefinition(winRTRuntimeAssembly)) is not null &&
((TypeDefinition)helperType).Attributes.HasFlag(TypeAttributes.Public)))
{
if (type.IsGenericInstance)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Tests/AuthoringTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ public void Dispose()
}
}

internal sealed class NonProjectedDisposableClass : IDisposable
internal sealed partial class NonProjectedDisposableClass : IDisposable
{
public bool IsDisposed { get; set; }

Expand Down
Loading

0 comments on commit 9fea72b

Please sign in to comment.