diff --git a/nuget/Microsoft.Windows.CsWinRT.nuspec b/nuget/Microsoft.Windows.CsWinRT.nuspec
index a12f00fa0..f6ef235a1 100644
--- a/nuget/Microsoft.Windows.CsWinRT.nuspec
+++ b/nuget/Microsoft.Windows.CsWinRT.nuspec
@@ -19,6 +19,7 @@
+
@@ -34,5 +35,6 @@
+
diff --git a/nuget/Microsoft.Windows.CsWinRT.targets b/nuget/Microsoft.Windows.CsWinRT.targets
index bc6db1428..2cbff2808 100644
--- a/nuget/Microsoft.Windows.CsWinRT.targets
+++ b/nuget/Microsoft.Windows.CsWinRT.targets
@@ -68,7 +68,10 @@ Copyright (C) Microsoft Corporation. All rights reserved.
$(CsWinRTGeneratedFilesDir)cswinrt.rsp
- "$(CsWinRTExe)" %40"$(CsWinRTResponseFile)"
+
+ "$(CsWinRTExe)" %40"$(CsWinRTResponseFile)"
$(WindowsSDKVersion.TrimEnd('\'))
$(TargetPlatformVersion)
-input $(CsWinRTWindowsMetadata)
@@ -126,6 +129,75 @@ $(CsWinRTIncludeWinRTInterop)
+
+
+
+
+
+ $(CsWinRTPath)build\tools\IIDOptimizer\
+
+ @(BuiltProjectOutputGroupKeyOutput->'%(Identity)')
+
+ $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', 'IIDOptimizer'))
+
+ @(ReferencePathWithRefAssemblies->'--refs
%(Identity)', '
')
+
+
+--target
+$(CsWinRTIIDOptimizerTargetAssembly)
+--outdir
+$(IIDOptimizerInterimDir)
+$(GuidPatchTargetAssemblyReferences)
+
+
+
+
+
+
+ $(IIDOptimizerInterimDir)cswinrt_iidoptimizer.rsp
+ "$(CsWinRTIIDOptimizerPath)IIDOptimizer.exe" %40"$(CsWinRTIIDOptimizerResponseFile)"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $([System.IO.Directory]::GetParent($(CsWinRTIIDOptimizerTargetAssembly)))
+
+
+
+
+
+
diff --git a/nuget/NOTICE.txt b/nuget/NOTICE.txt
new file mode 100644
index 000000000..7d966c8da
--- /dev/null
+++ b/nuget/NOTICE.txt
@@ -0,0 +1,42 @@
+NOTICES AND INFORMATION
+Do Not Translate or Localize
+
+This software incorporates material from third parties.
+Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com,
+or you may send a check or money order for US $5.00, including the product name,
+the open source component name, platform, and version number, to:
+
+Source Code Compliance Team
+Microsoft Corporation
+One Microsoft Way
+Redmond, WA 98052
+USA
+
+Notwithstanding any other terms, you may reverse engineer this software to the extent
+required to debug changes to any libraries licensed under the GNU Lesser General Public License.
+
+---------------------------------------------------------
+
+Mono.Cecil 0.11.4 - MIT
+
+Copyright (c) 2008 - 2015 Jb Evain
+Copyright (c) 2008 - 2011 Novell, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/Benchmarks/Benchmarks.csproj b/src/Benchmarks/Benchmarks.csproj
index 68326eed4..d322d4fae 100644
--- a/src/Benchmarks/Benchmarks.csproj
+++ b/src/Benchmarks/Benchmarks.csproj
@@ -10,7 +10,7 @@
true
Benchmarks.manifest
$(TargetFramework)
- netstandard2.0
+ netstandard2.0
false
9.0
@@ -22,7 +22,7 @@
-
+
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 402249987..cf12ec67f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -26,6 +26,10 @@
$(DefineConstants);MANUAL_IUNKNOWN
+
+ $(MSBuildThisFileDirectory)Perf\IIDOptimizer\bin\$(Configuration)\net5.0\
+
+
0.0.0.0
0.0.0-private.0
diff --git a/src/Perf/IIDOptimizer/CecilExtensions.cs b/src/Perf/IIDOptimizer/CecilExtensions.cs
new file mode 100644
index 000000000..753f020ce
--- /dev/null
+++ b/src/Perf/IIDOptimizer/CecilExtensions.cs
@@ -0,0 +1,110 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using System;
+using System.Linq;
+
+namespace GuidPatch
+{
+ static class CecilExtensions
+ {
+ internal static Guid? ReadGuidFromAttribute(this TypeReference type, TypeReference guidAttributeType, AssemblyDefinition winrtRuntimeAssembly)
+ {
+ TypeDefinition def = type.Resolve();
+ var guidAttr = def.CustomAttributes.FirstOrDefault(attr => attr.AttributeType.Resolve() == guidAttributeType);
+ if (guidAttr is null)
+ {
+ TypeDefinition abiType = def.GetCswinrtAbiTypeDefinition(winrtRuntimeAssembly);
+ if (abiType is not null)
+ {
+ return abiType.ReadGuidFromAttribute(guidAttributeType, winrtRuntimeAssembly);
+ }
+ return null;
+ }
+ return new Guid((string)guidAttr.ConstructorArguments[0].Value);
+ }
+
+ internal static TypeDefinition GetCswinrtAbiTypeDefinition(this TypeReference type, AssemblyDefinition winrtRuntimeAssembly)
+ {
+ var resolvedType = type.Resolve();
+
+ return resolvedType.Module.GetType($"ABI.{resolvedType.FullName}") ??
+ winrtRuntimeAssembly.MainModule.GetType($"ABI.{resolvedType.FullName}");
+ }
+
+ internal static MethodDefinition CreateIIDDataGetter(TypeReference type, Guid iidValue, TypeDefinition dataBlockType, TypeDefinition parentType, TypeReference readOnlySpanOfByte, MethodReference readOnlySpanOfByteCtor)
+ {
+ var guidDataMethod = new MethodDefinition($"{type.FullName}", MethodAttributes.Assembly | MethodAttributes.Static, readOnlySpanOfByte);
+
+ WriteIIDDataGetterBody(guidDataMethod, type, iidValue, dataBlockType, parentType, readOnlySpanOfByteCtor);
+ return guidDataMethod;
+ }
+
+ internal static void WriteIIDDataGetterBody(MethodDefinition method, TypeReference type, Guid iidValue, TypeDefinition dataBlockType, TypeDefinition parentType, MethodReference readOnlySpanOfByteCtor)
+ {
+ var guidDataField = new FieldDefinition($"{type.FullName}", FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly | FieldAttributes.HasFieldRVA, dataBlockType)
+ {
+ InitialValue = iidValue.ToByteArray()
+ };
+ parentType.Fields.Add(guidDataField);
+
+ var ilProcessor = method.Body.GetILProcessor();
+ ilProcessor.Clear();
+ ilProcessor.Append(Instruction.Create(OpCodes.Ldsflda, guidDataField));
+ ilProcessor.Append(Instruction.Create(OpCodes.Ldc_I4, 16));
+ ilProcessor.Append(Instruction.Create(OpCodes.Newobj, readOnlySpanOfByteCtor));
+ ilProcessor.Append(Instruction.Create(OpCodes.Ret));
+ }
+
+ internal static TypeDefinition GetOrCreateDataBlockType(TypeDefinition parentType, int size)
+ {
+ if (size < 0 || size > ushort.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
+
+ string typeName = $"__StaticDataBlock<>Size={size}";
+
+ var typeRef = new TypeReference(null, typeName, parentType.Module, parentType.Module)
+ {
+ DeclaringType = parentType
+ };
+
+ if (typeRef.Resolve() is TypeDefinition td)
+ {
+ return td;
+ }
+
+ td = new TypeDefinition(null, typeName, TypeAttributes.AutoClass | TypeAttributes.Sealed | TypeAttributes.NestedAssembly | TypeAttributes.SequentialLayout | TypeAttributes.AnsiClass, new TypeReference("System", "ValueType", parentType.Module, parentType.Module.TypeSystem.CoreLibrary))
+ {
+ PackingSize = 1,
+ ClassSize = size
+ };
+
+ parentType.NestedTypes.Add(td);
+
+ return td;
+ }
+
+ internal static TypeReference? FindTypeReference(ModuleDefinition module, string ns, string name, string basicAssemblyName, bool isValueType)
+ {
+ foreach (var asm in module.AssemblyReferences)
+ {
+ if (asm.Name == basicAssemblyName || asm.Name.StartsWith($"{basicAssemblyName},"))
+ {
+ TypeReference typeRef = new TypeReference(ns, name, module, asm, isValueType);
+ if (typeRef.Resolve() != null)
+ {
+ return module.ImportReference(typeRef);
+ }
+ break;
+ }
+ }
+ var resolved = module.AssemblyResolver.Resolve(new AssemblyNameReference(basicAssemblyName, default));
+ if (resolved is null)
+ {
+ return null;
+ }
+ return resolved.MainModule.Types.FirstOrDefault(t => t.Namespace == ns && t.Name == name);
+ }
+ }
+}
diff --git a/src/Perf/IIDOptimizer/GuidPatcher.cs b/src/Perf/IIDOptimizer/GuidPatcher.cs
new file mode 100644
index 000000000..54110a889
--- /dev/null
+++ b/src/Perf/IIDOptimizer/GuidPatcher.cs
@@ -0,0 +1,365 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+
+namespace GuidPatch
+{
+ class GuidPatcher
+ {
+ private readonly AssemblyDefinition assembly;
+ private readonly AssemblyDefinition winRTRuntimeAssembly;
+ private readonly TypeDefinition guidType;
+ private readonly GenericInstanceType readOnlySpanOfByte;
+ private readonly MethodReference readOnlySpanOfByteCtor;
+ private readonly MethodReference guidCtor;
+ private readonly TypeDefinition? guidAttributeType;
+ private readonly MethodDefinition getTypeFromHandleMethod;
+ private readonly TypeDefinition? guidGeneratorType;
+ private readonly MethodDefinition? getIidMethod;
+ private readonly MethodDefinition? createIidMethod;
+ private readonly MethodDefinition? getHelperTypeMethod;
+
+ private readonly Dictionary ClosedTypeGuidDataMapping = new Dictionary();
+ private readonly TypeDefinition guidImplementationDetailsType;
+ private readonly TypeDefinition guidDataBlockType;
+ private SignatureGenerator signatureGenerator;
+
+ public GuidPatcher(AssemblyDefinition winRTRuntime, AssemblyDefinition targetAssembly)
+ {
+ assembly = targetAssembly;
+
+ winRTRuntimeAssembly = winRTRuntime;
+
+ guidImplementationDetailsType = new TypeDefinition(null, "", TypeAttributes.AutoClass | TypeAttributes.Sealed, assembly.MainModule.TypeSystem.Object);
+
+ assembly.MainModule.Types.Add(guidImplementationDetailsType);
+
+ guidDataBlockType = CecilExtensions.GetOrCreateDataBlockType(guidImplementationDetailsType, Unsafe.SizeOf());
+
+ var systemType = new TypeReference("System", "Type", assembly.MainModule, assembly.MainModule.TypeSystem.CoreLibrary).Resolve();
+
+ guidType = new TypeReference("System", "Guid", assembly.MainModule, assembly.MainModule.TypeSystem.CoreLibrary).Resolve();
+
+ readOnlySpanOfByte = new GenericInstanceType(new TypeReference("System", "ReadOnlySpan`1", assembly.MainModule, assembly.MainModule.TypeSystem.CoreLibrary, true))
+ {
+ GenericArguments =
+ {
+ assembly.MainModule.TypeSystem.Byte
+ }
+ };
+
+ readOnlySpanOfByteCtor = assembly.MainModule.ImportReference(new MethodReference(".ctor", assembly.MainModule.TypeSystem.Void, readOnlySpanOfByte)
+ {
+ Parameters =
+ {
+ new ParameterDefinition(new PointerType(assembly.MainModule.TypeSystem.Void)),
+ new ParameterDefinition(assembly.MainModule.TypeSystem.Int32),
+ },
+ HasThis = true
+ });
+
+ guidCtor = assembly.MainModule.ImportReference(guidType.Methods.First(m => m.IsConstructor && m.Parameters.Count == 1 && m.Parameters[0].ParameterType.Resolve() == readOnlySpanOfByte.Resolve()));
+
+ getTypeFromHandleMethod = systemType.Methods.First(m => m.Name == "GetTypeFromHandle");
+
+ guidGeneratorType = null;
+
+ TypeDefinition? typeExtensionsType = null;
+
+ foreach (var asm in assembly.MainModule.AssemblyReferences)
+ {
+ if (asm.Name == "WinRT.Runtime")
+ {
+ guidGeneratorType =
+ new TypeReference("WinRT", "GuidGenerator", assembly.MainModule, asm).Resolve();
+ typeExtensionsType = new TypeReference("WinRT", "TypeExtensions", assembly.MainModule, asm).Resolve();
+ }
+ else if (asm.Name == "System.Runtime.InteropServices")
+ {
+ guidAttributeType = new TypeReference("System.Runtime.InteropServices", "GuidAttribute", assembly.MainModule, asm).Resolve();
+ }
+ }
+
+ if (guidGeneratorType is not null && typeExtensionsType is not null)
+ {
+ getIidMethod = guidGeneratorType.Methods.First(m => m.Name == "GetIID");
+ createIidMethod = guidGeneratorType.Methods.First(m => m.Name == "CreateIID");
+ getHelperTypeMethod = typeExtensionsType.Methods.First(m => m.Name == "GetHelperType");
+ }
+
+ signatureGenerator = new SignatureGenerator(assembly, guidAttributeType!, winRTRuntimeAssembly);
+ }
+
+ public int ProcessAssembly()
+ {
+ if (guidGeneratorType is null || guidAttributeType is null)
+ {
+ return 0;
+ }
+
+ int numPatches = 0;
+ var methods = from module in assembly.Modules
+ from type in module.Types
+ from method in type.Methods
+ where method.HasBody
+ select method;
+
+ foreach (var method in methods)
+ {
+ numPatches += ProcessMethodBody(method.Body, getTypeFromHandleMethod, getIidMethod!, createIidMethod!);
+ }
+
+ return numPatches;
+ }
+
+ public void SaveAssembly(string targetDirectory)
+ {
+ var writerParameters = new WriterParameters
+ {
+ WriteSymbols = true
+ };
+
+ assembly.Write($"{targetDirectory}{Path.DirectorySeparatorChar}{assembly.Name.Name}.dll", writerParameters);
+ }
+
+ enum State
+ {
+ Start,
+ Ldtoken,
+ GetTypeFromHandle,
+ GetHelperTypeOptional
+ }
+
+ private bool PatchNonGenericTypeIID(MethodBody body, int startILIndex, TypeReference type, int numberOfInstructionsToOverwrite)
+ {
+ if (numberOfInstructionsToOverwrite < 2)
+ {
+ return false;
+ }
+
+ if (!ClosedTypeGuidDataMapping.TryGetValue(type, out var guidDataMethod))
+ {
+ Guid? guidValue = type.ReadGuidFromAttribute(guidAttributeType!, winRTRuntimeAssembly);
+ if (guidValue == null)
+ {
+ return false;
+ }
+
+ guidDataMethod = CecilExtensions.CreateIIDDataGetter(type, guidValue.Value, guidDataBlockType, guidImplementationDetailsType, readOnlySpanOfByte, readOnlySpanOfByteCtor);
+
+ guidImplementationDetailsType.Methods.Add(guidDataMethod);
+ ClosedTypeGuidDataMapping[type] = guidDataMethod;
+ }
+
+ ReplaceWithCallToGuidDataGetter(body, startILIndex, numberOfInstructionsToOverwrite, guidDataMethod);
+
+ return true;
+ }
+
+ private bool PatchGenericTypeIID(MethodBody body, int startILIndex, TypeReference type, int numberOfInstructionsToOverwrite)
+ {
+ SignaturePart rootSignaturePart = signatureGenerator.GetSignatureParts(type);
+
+ var guidDataMethod = new MethodDefinition($"{type.FullName}", MethodAttributes.Assembly | MethodAttributes.Static, readOnlySpanOfByte);
+
+ guidImplementationDetailsType.Methods.Add(guidDataMethod);
+
+ var emitter = new SignatureEmitter(type, guidDataMethod);
+ VisitSignature(rootSignaturePart, emitter);
+
+ emitter.EmitGuidGetter(guidDataBlockType, guidImplementationDetailsType, readOnlySpanOfByte, readOnlySpanOfByteCtor, guidGeneratorType!);
+
+ MethodReference guidDataMethodReference = guidDataMethod;
+ if (guidDataMethodReference.HasGenericParameters)
+ {
+ var genericGuidDataMethodReference = new GenericInstanceMethod(guidDataMethodReference);
+ foreach (var param in guidDataMethodReference.GenericParameters)
+ {
+ genericGuidDataMethodReference.GenericArguments.Add(emitter.GenericParameterMapping[param]);
+ }
+ guidDataMethodReference = genericGuidDataMethodReference;
+ }
+
+ ReplaceWithCallToGuidDataGetter(body, startILIndex, numberOfInstructionsToOverwrite, guidDataMethodReference);
+ return true;
+ }
+
+ private void ReplaceWithCallToGuidDataGetter(MethodBody body, int startILIndex, int numberOfInstructionsToOverwrite, MethodReference guidDataMethod)
+ {
+ var il = body.GetILProcessor();
+ il.Replace(startILIndex, Instruction.Create(OpCodes.Call, guidDataMethod));
+ il.Replace(startILIndex + 1, Instruction.Create(OpCodes.Newobj, guidCtor));
+ for (int i = 2; i < numberOfInstructionsToOverwrite; i++)
+ {
+ il.Replace(startILIndex + i, Instruction.Create(OpCodes.Nop));
+ }
+ }
+
+ private void VisitSignature(SignaturePart rootSignaturePart, SignatureEmitter emitter)
+ {
+ switch (rootSignaturePart)
+ {
+ case BasicSignaturePart basic:
+ {
+ emitter.PushString(basic.Type switch
+ {
+ SignatureType.@string => "string",
+ SignatureType.iinspectable => "cinterface(IInspectable)",
+ _ => basic.Type.ToString()
+ });
+ }
+ break;
+ case SignatureWithChildren group:
+ {
+ emitter.PushString($"{group.GroupingName}(");
+ emitter.PushString(group.ThisEntitySignature);
+ foreach (var item in group.ChildrenSignatures)
+ {
+ emitter.PushString(";");
+ VisitSignature(item, emitter);
+ }
+ emitter.PushString(")");
+ }
+ break;
+ case GuidSignature guid:
+ {
+ emitter.PushString(guid.IID.ToString("B"));
+ }
+ break;
+ case NonGenericDelegateSignature del:
+ {
+ emitter.PushString($"delegate({del.DelegateIID:B}");
+ }
+ break;
+ case UninstantiatedGeneric gen:
+ {
+ emitter.PushGenericParameter(gen.OriginalGenericParameter);
+ }
+ break;
+ case CustomSignatureMethod custom:
+ {
+ emitter.PushCustomSignature(custom.Method);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private int ProcessMethodBody(MethodBody body, MethodDefinition getTypeFromHandleMethod, MethodDefinition getIidMethod, MethodDefinition createIidMethod)
+ {
+ int numberOfReplacements = 0;
+ TypeReference? type = null;
+ State state = State.Start;
+ int startIlIndex = -1;
+ int numberOfInstructionsToOverwrite = 3;
+ for (int i = 0; i < body.Instructions.Count; i++)
+ {
+ var instruction = body.Instructions[i];
+ switch (state)
+ {
+ case State.Start:
+ if (instruction.OpCode.Code != Code.Ldtoken)
+ {
+ continue;
+ }
+ // Do safe cast in case we are given a FieldReference and
+ // skip if so since we dont think we'll get a RuntimeTypeHandle for it
+ var typeMaybe = instruction.Operand as TypeReference;
+ if (typeMaybe != null && !typeMaybe.IsGenericParameter)
+ {
+ state = State.Ldtoken;
+ type = typeMaybe;
+ startIlIndex = i;
+ }
+ break;
+ case State.Ldtoken:
+ {
+ if (instruction.OpCode.Code != Code.Call)
+ {
+ state = State.Start;
+ type = null;
+ continue;
+ }
+ var method = ((MethodReference)instruction.Operand).Resolve();
+ if (method == getTypeFromHandleMethod)
+ {
+ state = State.GetTypeFromHandle;
+ }
+ }
+ break;
+ case State.GetTypeFromHandle:
+ {
+ if (instruction.OpCode.Code != Code.Call)
+ {
+ state = State.Start;
+ type = null;
+ continue;
+ }
+ var method = ((MethodReference)instruction.Operand).Resolve();
+ if (method == getHelperTypeMethod)
+ {
+ numberOfInstructionsToOverwrite++;
+ state = State.GetHelperTypeOptional;
+ continue;
+ }
+ else
+ {
+ goto case State.GetHelperTypeOptional;
+ }
+ }
+ case State.GetHelperTypeOptional:
+ {
+ if (instruction.OpCode.Code != Code.Call)
+ {
+ state = State.Start;
+ type = null;
+ continue;
+ }
+ var method = ((MethodReference)instruction.Operand).Resolve();
+ if (method == getIidMethod || method == createIidMethod)
+ {
+ try
+ {
+ bool didPatch = false;
+ if (type!.IsGenericInstance)
+ {
+ didPatch = PatchGenericTypeIID(body, startIlIndex, type, numberOfInstructionsToOverwrite);
+ }
+ else
+ {
+ didPatch = PatchNonGenericTypeIID(body, startIlIndex, type, numberOfInstructionsToOverwrite);
+ }
+
+ if (didPatch)
+ {
+ numberOfReplacements++;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Exception thrown during patching {body.Method.FullName}: {ex}");
+ }
+ }
+ else
+ {
+ state = State.Start;
+ type = null;
+ startIlIndex = -1;
+ }
+ }
+ break;
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+ return numberOfReplacements;
+ }
+ }
+}
diff --git a/src/Perf/IIDOptimizer/IIDOptimizer.csproj b/src/Perf/IIDOptimizer/IIDOptimizer.csproj
new file mode 100644
index 000000000..a3b0e10ca
--- /dev/null
+++ b/src/Perf/IIDOptimizer/IIDOptimizer.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net5.0
+ enable
+
+
+
+
+
+
+
+
diff --git a/src/Perf/IIDOptimizer/Program.cs b/src/Perf/IIDOptimizer/Program.cs
new file mode 100644
index 000000000..a3742ed71
--- /dev/null
+++ b/src/Perf/IIDOptimizer/Program.cs
@@ -0,0 +1,103 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using System;
+using System.IO;
+using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using System.Linq;
+
+namespace GuidPatch
+{
+ class Program
+ {
+ static readonly Option _targetAssembly =
+ new Option
+ (
+ alias: "--targetAssembly",
+ description: "The assembly to perform GUID lookup optimizations on.",
+ argumentType: typeof(string),
+ arity: ArgumentArity.ExactlyOne
+ );
+
+ static readonly Option _outputDir =
+ new Option
+ (
+ alias: "--outputDirectory",
+ description: "The directory to save the patched .dll to.",
+ argumentType: typeof(string),
+ arity: ArgumentArity.ExactlyOne
+ );
+
+ static readonly Option _references =
+ new Option
+ (
+ alias: "--references",
+ description: "Reference assemblies used when compiling the target assembly.",
+ argumentType: typeof(FileInfo[]),
+ arity: ArgumentArity.ZeroOrMore
+ );
+
+ static async Task Main(string[] args)
+ {
+ var rootCommand = new RootCommand { };
+ // friendlier option names
+ _targetAssembly.AddAlias("--target");
+ _outputDir.AddAlias("--outdir");
+ _references.AddAlias("--refs");
+
+ rootCommand.AddOption(_targetAssembly);
+ rootCommand.AddOption(_outputDir);
+ rootCommand.AddOption(_references);
+
+ rootCommand.Handler = CommandHandler.Create>(GuidPatch);
+ await rootCommand.InvokeAsync(args);
+ }
+
+ private static int GuidPatch(string targetAssembly, string outputDirectory, IEnumerable references)
+ {
+ var resolver = new ReferenceAssemblyResolver(references);
+ try
+ {
+ AssemblyDefinition winRTRuntimeAssembly = resolver.Resolve(new AssemblyNameReference("WinRT.Runtime", default));
+
+ var readerParameters = new ReaderParameters(ReadingMode.Deferred)
+ {
+ ReadWrite = true,
+ InMemory = true,
+ AssemblyResolver = resolver,
+ ThrowIfSymbolsAreNotMatching = false,
+ SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
+ ApplyWindowsRuntimeProjections = false,
+ ReadSymbols = true
+ };
+
+ var targetAssemblyDefinition = AssemblyDefinition.ReadAssembly(targetAssembly, readerParameters);
+
+ if (targetAssemblyDefinition.MainModule.Types.Any(typeDef => typeDef.Name == ""))
+ {
+ Console.WriteLine("Target assembly has already been patched. Exiting early as there is no work to do.");
+ return -2;
+ }
+
+ var guidPatcher = new GuidPatcher(winRTRuntimeAssembly, targetAssemblyDefinition);
+
+ int numPatches = guidPatcher.ProcessAssembly();
+
+ guidPatcher.SaveAssembly(outputDirectory);
+
+ Console.WriteLine($"Saved patched .dll to {outputDirectory}");
+ Console.WriteLine($"{numPatches} IID calculations/fetches patched");
+ return 0;
+ }
+ catch (AssemblyResolutionException e)
+ {
+ Console.WriteLine("Failed to resolve an assembly, shutting down.");
+ Console.WriteLine($"\tAssembly : {e.AssemblyReference.Name}");
+ return -1;
+ }
+ }
+ }
+}
diff --git a/src/Perf/IIDOptimizer/Properties/launchSettings.json b/src/Perf/IIDOptimizer/Properties/launchSettings.json
new file mode 100644
index 000000000..f61327d06
--- /dev/null
+++ b/src/Perf/IIDOptimizer/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "IIDOptimizer": {
+ "commandName": "Project",
+ "nativeDebugging": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Perf/IIDOptimizer/ReferenceAssemblyResolver.cs b/src/Perf/IIDOptimizer/ReferenceAssemblyResolver.cs
new file mode 100644
index 000000000..03b0ee82c
--- /dev/null
+++ b/src/Perf/IIDOptimizer/ReferenceAssemblyResolver.cs
@@ -0,0 +1,21 @@
+using Mono.Cecil;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace GuidPatch
+{
+ class ReferenceAssemblyResolver : DefaultAssemblyResolver
+ {
+ public ReferenceAssemblyResolver(IEnumerable references)
+ {
+ // Typically reference assemblies come in "ref packs" so all of the files in `references` live in the same folder,
+ // we can do a small optimization here by only adding unique directories to our custom AssemblyResolver
+ var uniqueDirectories = references
+ .Select((reference) => reference.Directory!.FullName)
+ .Distinct();
+
+ foreach (var dir in uniqueDirectories) { AddSearchDirectory(dir); }
+ }
+ }
+}
diff --git a/src/Perf/IIDOptimizer/SignatureEmitter.cs b/src/Perf/IIDOptimizer/SignatureEmitter.cs
new file mode 100644
index 000000000..2dceaf67e
--- /dev/null
+++ b/src/Perf/IIDOptimizer/SignatureEmitter.cs
@@ -0,0 +1,545 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace GuidPatch
+{
+ sealed class SignatureEmitter
+ {
+ private static Guid WinRTPinterfaceNamespace = new("d57af411-737b-c042-abae-878b1e16adee");
+ private StringBuilder? currentStringBuilder;
+ private readonly List signatureSteps = new();
+ private readonly Dictionary originalGenericParameterToGetterParameterMapping = new();
+ private readonly Dictionary getterParameterToOriginalGenericParameterMapping = new();
+ private readonly TypeReference describedType;
+ private readonly MethodDefinition guidDataGetterMethod;
+
+ // OptimizerDir is the path our current process should use to write logs and patched DLLs to
+ public string OptimizerDir
+ {
+ get { return "obj\\IIDOptimizer"; }
+ }
+ public IReadOnlyDictionary GenericParameterMapping => getterParameterToOriginalGenericParameterMapping;
+
+ record SignatureStep;
+
+ sealed record StringStep(string StaticSignatureString) : SignatureStep;
+
+ sealed record RuntimeGenericSignatureStep(GenericParameter OriginalTypeParameter, GenericParameter NewTypeParameter) : SignatureStep;
+
+ sealed record RuntimeCustomSignatureStep(MethodReference method) : SignatureStep;
+
+ public SignatureEmitter(TypeReference describedType, MethodDefinition guidDataGetterMethod)
+ {
+ this.describedType = describedType;
+ this.guidDataGetterMethod = guidDataGetterMethod;
+ }
+
+ public void PushString(string str)
+ {
+ if (currentStringBuilder is null)
+ {
+ currentStringBuilder = new StringBuilder();
+ }
+ currentStringBuilder.Append(str);
+ }
+
+ public void PushGenericParameter(GenericParameter typeParameter)
+ {
+ if (!originalGenericParameterToGetterParameterMapping.TryGetValue(typeParameter, out var localTypeParameter))
+ {
+ originalGenericParameterToGetterParameterMapping[typeParameter] = localTypeParameter = new GenericParameter(guidDataGetterMethod);
+ getterParameterToOriginalGenericParameterMapping[localTypeParameter] = typeParameter;
+ guidDataGetterMethod.GenericParameters.Add(localTypeParameter);
+ }
+ if (currentStringBuilder is not null)
+ {
+ signatureSteps.Add(new StringStep(currentStringBuilder.ToString()));
+ currentStringBuilder = null;
+ }
+ signatureSteps.Add(new RuntimeGenericSignatureStep(typeParameter, localTypeParameter));
+ }
+
+ public void PushCustomSignature(MethodReference customSignatureMethod)
+ {
+ if (currentStringBuilder is not null)
+ {
+ signatureSteps.Add(new StringStep(currentStringBuilder.ToString()));
+ currentStringBuilder = null;
+ }
+ signatureSteps.Add(new RuntimeCustomSignatureStep(customSignatureMethod));
+ }
+
+ public void EmitGuidGetter(
+ TypeDefinition guidDataBlockType,
+ TypeDefinition implementationDetailsType,
+ TypeReference readOnlySpanofByte,
+ MethodReference readOnlySpanOfByteCtor,
+ TypeDefinition guidGeneratorType)
+ {
+ if (currentStringBuilder is not null)
+ {
+ signatureSteps.Add(new StringStep(currentStringBuilder.ToString()));
+ currentStringBuilder = null;
+ }
+
+ // TODO: Emit IID Generation.
+ if (signatureSteps.Count == 1 && signatureSteps[0] is StringStep str)
+ {
+ GenerateGuidFromSimpleSignature(str, guidDataBlockType, implementationDetailsType, readOnlySpanofByte, readOnlySpanOfByteCtor);
+ }
+ else
+ {
+ GenerateGuidFactoryFromComplexSignature(implementationDetailsType, readOnlySpanofByte, readOnlySpanOfByteCtor, guidGeneratorType);
+ }
+ }
+
+ private void GenerateGuidFromSimpleSignature(StringStep stringStep, TypeDefinition guidDataBlockType, TypeDefinition implementationDetailsType, TypeReference readOnlySpanOfByte, MethodReference readOnlySpanOfByteCtor)
+ {
+ var maxBytes = Encoding.UTF8.GetMaxByteCount(stringStep.StaticSignatureString.Length);
+
+ Span data = new byte[Unsafe.SizeOf() + maxBytes];
+ WinRTPinterfaceNamespace.TryWriteBytes(data);
+ var numBytes = Encoding.UTF8.GetBytes(stringStep.StaticSignatureString, data[Unsafe.SizeOf()..]);
+ data = data[..(Unsafe.SizeOf() + numBytes)];
+
+ Debug.Assert(SHA1.Create().HashSize == 160);
+
+ Span hash = stackalloc byte[160];
+ SHA1.HashData(data, hash);
+
+ if (BitConverter.IsLittleEndian)
+ {
+ // swap bytes of int a
+ byte t = hash[0];
+ hash[0] = hash[3];
+ hash[3] = t;
+ t = hash[1];
+ hash[1] = hash[2];
+ hash[2] = t;
+ // swap bytes of short b
+ t = hash[4];
+ hash[4] = hash[5];
+ hash[5] = t;
+ // swap bytes of short c and encode rfc time/version field
+ t = hash[6];
+ hash[6] = hash[7];
+ hash[7] = (byte)((t & 0x0f) | (5 << 4));
+ // encode rfc clock/reserved field
+ hash[8] = (byte)((hash[8] & 0x3f) | 0x80);
+ }
+
+ var iid = new Guid(hash[0..16]);
+
+ CecilExtensions.WriteIIDDataGetterBody(guidDataGetterMethod, describedType, iid, guidDataBlockType, implementationDetailsType, readOnlySpanOfByteCtor);
+ }
+
+ private void GenerateGuidFactoryFromComplexSignature(TypeDefinition implementationDetailsType, TypeReference readOnlySpanOfByte, MethodReference readOnlySpanOfBytePtrCtor, TypeDefinition guidGeneratorType)
+ {
+ var module = implementationDetailsType.Module;
+
+ var readOnlySpanOfByteArrayCtor = module.ImportReference(
+ new MethodReference(".ctor", module.TypeSystem.Void, readOnlySpanOfByte)
+ {
+ Parameters = { new ParameterDefinition(new ArrayType(readOnlySpanOfByte.Resolve().GenericParameters[0])) },
+ HasThis = true
+ },
+ readOnlySpanOfByte);
+
+ // Create generic class with static array field to cache result.
+ var cacheType = new TypeDefinition(null, $"{describedType.FullName}", TypeAttributes.NestedPrivate | TypeAttributes.Abstract | TypeAttributes.Sealed, module.ImportReference(module.TypeSystem.Object));
+
+ Dictionary getterMethodGensToCacheTypeGens = new();
+
+ for (int i = 0; i < guidDataGetterMethod.GenericParameters.Count; i++)
+ {
+ cacheType.GenericParameters.Add(new GenericParameter(cacheType));
+ getterMethodGensToCacheTypeGens[guidDataGetterMethod.GenericParameters[i]] = cacheType.GenericParameters[i];
+ }
+
+ var instantiatedCacheType = new GenericInstanceType(cacheType);
+ foreach (var arg in guidDataGetterMethod.GenericParameters)
+ {
+ instantiatedCacheType.GenericArguments.Add(arg);
+ }
+
+ var selfInstantiatedCacheType = new GenericInstanceType(cacheType);
+ foreach (var param in cacheType.GenericParameters)
+ {
+ selfInstantiatedCacheType.GenericArguments.Add(param);
+ }
+
+ var cacheField = new FieldDefinition("iidData", FieldAttributes.Static | FieldAttributes.Assembly, new ArrayType(module.ImportReference(module.TypeSystem.Byte)));
+ cacheType.Fields.Add(cacheField);
+ implementationDetailsType.NestedTypes.Add(cacheType);
+
+ var staticCtor = new MethodDefinition(".cctor", MethodAttributes.Static | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.Private, module.TypeSystem.Void);
+
+ cacheType.Methods.Add(staticCtor);
+
+ // In the body of the getter method, return the cache data
+ var getterIL = guidDataGetterMethod.Body.GetILProcessor();
+ getterIL.Emit(OpCodes.Ldsfld, new FieldReference(cacheField.Name, cacheField.FieldType, instantiatedCacheType));
+ getterIL.Emit(OpCodes.Newobj, readOnlySpanOfByteArrayCtor);
+ getterIL.Emit(OpCodes.Ret);
+
+ // In the static constructor, calculate the guid bytes.
+ var il = staticCtor.Body.GetILProcessor();
+ var signatureParts = new VariableDefinition[signatureSteps.Count];
+
+ var systemType = module.ImportReference(
+ new TypeReference("System", "Type", module, module.TypeSystem.CoreLibrary));
+
+ var getTypeFromHandleMethod = module.ImportReference(systemType.Resolve().Methods.First(m => m.Name == "GetTypeFromHandle"));
+ var getSignatureMethod = module.ImportReference(
+ new MethodReference("GetSignature", module.TypeSystem.String, guidGeneratorType)
+ {
+ Parameters = { new ParameterDefinition(systemType) },
+ HasThis = false
+ });
+
+ var encodingType = CecilExtensions.FindTypeReference(module, "System.Text", "Encoding", "System.Runtime", false)!;
+ var utf8EncodingGetter = module.ImportReference(new MethodReference("get_UTF8", encodingType, encodingType));
+ var encodingGetBytes = module.ImportReference(
+ new MethodReference("GetBytes", new ArrayType(module.TypeSystem.Byte), encodingType)
+ {
+ Parameters = { new ParameterDefinition(module.TypeSystem.String) },
+ HasThis = true
+ });
+
+ var fullSignatureLength = new VariableDefinition(module.TypeSystem.Int32);
+ staticCtor.Body.Variables.Add(fullSignatureLength);
+ il.Emit(OpCodes.Ldc_I4, 16);
+ il.Emit(OpCodes.Stloc, fullSignatureLength);
+
+ for (int i = 0; i < signatureSteps.Count; i++)
+ {
+ signatureParts[i] = new VariableDefinition(readOnlySpanOfByte);
+ staticCtor.Body.Variables.Add(signatureParts[i]);
+ switch (signatureSteps[i])
+ {
+ case StringStep(string str):
+ {
+ byte[] segmentBytes = Encoding.UTF8.GetBytes(str);
+ var staticDataField = new FieldDefinition($"", FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA, CecilExtensions.GetOrCreateDataBlockType(implementationDetailsType, segmentBytes.Length))
+ {
+ InitialValue = segmentBytes
+ };
+ cacheType.Fields.Add(staticDataField);
+
+ // Load a ReadOnlySpan of the signature segment into the local for this step.
+ il.Emit(OpCodes.Ldsflda, new FieldReference(staticDataField.Name, staticDataField.FieldType, selfInstantiatedCacheType));
+ il.Emit(OpCodes.Ldc_I4, segmentBytes.Length);
+ il.Emit(OpCodes.Newobj, readOnlySpanOfBytePtrCtor);
+ il.Emit(OpCodes.Stloc, signatureParts[i]);
+ // signatureLength += staticData.Length
+ il.Emit(OpCodes.Ldloc, fullSignatureLength);
+ il.Emit(OpCodes.Ldc_I4, segmentBytes.Length);
+ il.Emit(OpCodes.Add_Ovf);
+ il.Emit(OpCodes.Stloc, fullSignatureLength);
+ }
+ break;
+ case RuntimeGenericSignatureStep(_, GenericParameter localTypeParameter):
+ {
+ // byte[] bytes = Encoding.UTF8.GetBytes(GetSignature(typeof(localTypeParameter)))
+ il.Emit(OpCodes.Call, utf8EncodingGetter);
+ il.Emit(OpCodes.Ldtoken, getterMethodGensToCacheTypeGens[localTypeParameter]);
+ il.Emit(OpCodes.Call, getTypeFromHandleMethod);
+ il.Emit(OpCodes.Call, getSignatureMethod);
+ il.Emit(OpCodes.Callvirt, encodingGetBytes);
+ il.Emit(OpCodes.Dup);
+ // = new ReadOnlySpan(bytes);
+ il.Emit(OpCodes.Newobj, readOnlySpanOfByteArrayCtor);
+ il.Emit(OpCodes.Stloc, signatureParts[i]);
+ // signatureLength += bytes.Length
+ il.Emit(OpCodes.Ldlen);
+ il.Emit(OpCodes.Ldloc, fullSignatureLength);
+ il.Emit(OpCodes.Add_Ovf);
+ il.Emit(OpCodes.Stloc, fullSignatureLength);
+ }
+ break;
+ case RuntimeCustomSignatureStep(MethodReference customSignatureMethod):
+ {
+ // byte[] bytes = Encoding.UTF8.GetBytes(customSignatureMethod())
+ il.Emit(OpCodes.Call, utf8EncodingGetter);
+ il.Emit(OpCodes.Call, customSignatureMethod);
+ il.Emit(OpCodes.Callvirt, encodingGetBytes);
+ il.Emit(OpCodes.Dup);
+ // = new ReadOnlySpan(bytes);
+ il.Emit(OpCodes.Newobj, readOnlySpanOfByteArrayCtor);
+ il.Emit(OpCodes.Stloc, signatureParts[i]);
+ // signatureLength += bytes.Length
+ il.Emit(OpCodes.Ldlen);
+ il.Emit(OpCodes.Ldloc, fullSignatureLength);
+ il.Emit(OpCodes.Add_Ovf);
+ il.Emit(OpCodes.Stloc, fullSignatureLength);
+ }
+ break;
+ default:
+ il.Clear();
+ throw new InvalidOperationException();
+ }
+ }
+
+ var span = CecilExtensions.FindTypeReference(module, "System", "Span`1", "System.Runtime", true);
+
+ if (span is null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ var spanOfByte = new GenericInstanceType(span)
+ {
+ GenericArguments = { module.ImportReference(module.TypeSystem.Byte) }
+ };
+
+ var spanOfBytePtrCtor = module.ImportReference(new MethodReference(".ctor", module.TypeSystem.Void, spanOfByte)
+ {
+ Parameters =
+ {
+ new ParameterDefinition(new PointerType(module.TypeSystem.Void)),
+ new ParameterDefinition(module.TypeSystem.Int32),
+ },
+ HasThis = true
+ });
+
+ var spanOfByteArrayCtor = module.ImportReference(
+ new MethodReference(".ctor", module.TypeSystem.Void, spanOfByte)
+ {
+ Parameters = { new ParameterDefinition(new ArrayType(span.Resolve().GenericParameters[0])) },
+ HasThis = true
+ },
+ spanOfByte);
+
+
+ var copyToMethod = module.ImportReference(
+ new MethodReference("CopyTo", module.TypeSystem.Void, readOnlySpanOfByte)
+ {
+ Parameters =
+ {
+ new ParameterDefinition(
+ module.ImportReference(new GenericInstanceType(span) { GenericArguments = { readOnlySpanOfByte.Resolve().GenericParameters[0] } }))
+ },
+ HasThis = true
+ });
+
+ // return a Span instead of Span
+ var spanOfSpanElement = new GenericInstanceType(span) { GenericArguments = { span.Resolve().GenericParameters[0] } };
+
+ var spanSliceStartMethod = module.ImportReference(
+ new MethodReference("Slice", spanOfSpanElement, spanOfByte)
+ {
+ HasThis = true,
+ Parameters = { new ParameterDefinition(module.TypeSystem.Int32) }
+ });
+
+ var spanSliceStartLengthMethod = module.ImportReference(
+ new MethodReference("Slice", spanOfSpanElement, spanOfByte)
+ {
+ HasThis = true,
+ Parameters = { new ParameterDefinition(module.TypeSystem.Int32), new ParameterDefinition(module.TypeSystem.Int32) }
+ });
+
+ var readOnlySpanOfByteLength = module.ImportReference(new MethodReference("get_Length", module.TypeSystem.Int32, readOnlySpanOfByte) { HasThis = true });
+
+ var fullSignatureBuffer = new VariableDefinition(spanOfByte);
+ staticCtor.Body.Variables.Add(fullSignatureBuffer);
+
+ // fullSignatureBuffer = new Span(new byte[fullSignatureLength]);
+ il.Emit(OpCodes.Ldloc, fullSignatureLength);
+ il.Emit(OpCodes.Newarr, module.ImportReference(module.TypeSystem.Byte));
+ il.Emit(OpCodes.Newobj, spanOfByteArrayCtor);
+ il.Emit(OpCodes.Stloc, fullSignatureBuffer);
+
+ // Write WinRTPinterfaceNamespace bytes to the buffer
+ const string WinRTPinterfaceDataBlockName = "";
+
+ var staticNamespaceBytesField = new FieldReference(WinRTPinterfaceDataBlockName, CecilExtensions.GetOrCreateDataBlockType(implementationDetailsType, 16), implementationDetailsType);
+
+ if (staticNamespaceBytesField.Resolve() is null)
+ {
+ staticNamespaceBytesField = new FieldDefinition(WinRTPinterfaceDataBlockName, FieldAttributes.Static | FieldAttributes.Assembly | FieldAttributes.InitOnly | FieldAttributes.HasFieldRVA, CecilExtensions.GetOrCreateDataBlockType(implementationDetailsType, 16))
+ {
+ InitialValue = WinRTPinterfaceNamespace.ToByteArray()
+ };
+
+ implementationDetailsType.Fields.Add((FieldDefinition)staticNamespaceBytesField);
+ }
+
+ il.Emit(OpCodes.Ldsflda, staticNamespaceBytesField);
+ il.Emit(OpCodes.Ldc_I4, 16);
+ var readOnlySpanTemp = new VariableDefinition(readOnlySpanOfByte);
+ staticCtor.Body.Variables.Add(readOnlySpanTemp);
+ il.Emit(OpCodes.Newobj, readOnlySpanOfBytePtrCtor);
+ il.Emit(OpCodes.Stloc, readOnlySpanTemp);
+ il.Emit(OpCodes.Ldloca, readOnlySpanTemp);
+ il.Emit(OpCodes.Ldloc, fullSignatureBuffer);
+ il.Emit(OpCodes.Call, copyToMethod);
+
+ // int offset = 16;
+ var offset = new VariableDefinition(module.TypeSystem.Int32);
+ staticCtor.Body.Variables.Add(offset);
+ il.Emit(OpCodes.Ldc_I4, 16);
+ il.Emit(OpCodes.Stloc, offset);
+
+ for (int i = 0; i < signatureParts.Length; i++)
+ {
+ // locals[i].CopyTo(fullSignatureBuffer.Slice(offset));
+ il.Emit(OpCodes.Ldloca, signatureParts[i]);
+ il.Emit(OpCodes.Dup);
+ il.Emit(OpCodes.Ldloca, fullSignatureBuffer);
+ il.Emit(OpCodes.Ldloc, offset);
+ il.Emit(OpCodes.Call, spanSliceStartMethod);
+ il.Emit(OpCodes.Call, copyToMethod);
+ // offset += locals[i].Length;
+ il.Emit(OpCodes.Call, readOnlySpanOfByteLength);
+ il.Emit(OpCodes.Ldloc, offset);
+ il.Emit(OpCodes.Add);
+ il.Emit(OpCodes.Stloc, offset);
+ }
+
+ var destination = new VariableDefinition(spanOfByte);
+ staticCtor.Body.Variables.Add(destination);
+
+ // Span destination = stackalloc byte[160];
+ il.Emit(OpCodes.Ldc_I4, 160);
+ il.Emit(OpCodes.Localloc);
+ il.Emit(OpCodes.Ldc_I4, 160);
+ il.Emit(OpCodes.Newobj, spanOfBytePtrCtor);
+ il.Emit(OpCodes.Stloc, destination);
+
+ // SHA1.HashData(fullSignatureBuffer, destination);
+ var sha1Type = CecilExtensions.FindTypeReference(module, "System.Security.Cryptography", "SHA1", "System.Security.Cryptography.Algorithms", false);
+ var hashDataMethod = module.ImportReference(
+ new MethodReference("HashData", module.ImportReference(module.TypeSystem.Int32), sha1Type)
+ {
+ HasThis = false,
+ Parameters =
+ {
+ new ParameterDefinition(readOnlySpanOfByte),
+ new ParameterDefinition(spanOfByte)
+ }
+ });
+
+ 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);
+
+ // Fix endianness, bytes
+ var memoryExtensions = CecilExtensions.FindTypeReference(module, "System", "MemoryExtensions", "System.Memory", false);
+
+ var reverseMethod_Generic = new MethodReference("Reverse", module.TypeSystem.Void, memoryExtensions) { };
+
+ var reverseMethod = new MethodReference("Reverse", module.TypeSystem.Void, memoryExtensions) { };
+
+ var reverseMethodGenericParam = new GenericParameter(reverseMethod);
+ reverseMethod.GenericParameters.Add(reverseMethodGenericParam);
+ reverseMethod.Parameters.Add(new ParameterDefinition(new GenericInstanceType(span) { GenericArguments = { reverseMethodGenericParam } }));
+ reverseMethod = module.ImportReference(
+ new GenericInstanceMethod(reverseMethod)
+ {
+ GenericArguments = { module.TypeSystem.Byte }
+ });
+
+ // Filp endianness to little endian for the int/short components of the guid.
+ il.Emit(OpCodes.Ldloca, destination);
+ il.Emit(OpCodes.Ldc_I4_0);
+ il.Emit(OpCodes.Ldc_I4_4);
+ il.Emit(OpCodes.Call, spanSliceStartLengthMethod);
+ il.Emit(OpCodes.Call, reverseMethod);
+ il.Emit(OpCodes.Ldloca, destination);
+ il.Emit(OpCodes.Ldc_I4_4);
+ il.Emit(OpCodes.Ldc_I4_2);
+ il.Emit(OpCodes.Call, spanSliceStartLengthMethod);
+ il.Emit(OpCodes.Call, reverseMethod);
+ il.Emit(OpCodes.Ldloca, destination);
+ il.Emit(OpCodes.Ldc_I4_6);
+ il.Emit(OpCodes.Ldc_I4_2);
+ il.Emit(OpCodes.Call, spanSliceStartLengthMethod);
+ il.Emit(OpCodes.Call, reverseMethod);
+
+ // Encode rfc time/version/clock/reserved fields
+ var getItemMethod = module.ImportReference(
+ new MethodReference("get_Item", new ByReferenceType(span.Resolve().GenericParameters[0]), spanOfByte)
+ {
+ Parameters = { new ParameterDefinition(module.TypeSystem.Int32) },
+ HasThis = true
+ });
+
+ // t[7] = (byte) ((t[7] & 0x0f) | (5 << 4));
+ il.Emit(OpCodes.Ldloca, destination);
+ il.Emit(OpCodes.Ldc_I4_7);
+ il.Emit(OpCodes.Call, getItemMethod);
+ il.Emit(OpCodes.Dup);
+ il.Emit(OpCodes.Ldind_U1);
+ il.Emit(OpCodes.Ldc_I4, 0x0f);
+ il.Emit(OpCodes.And);
+ il.Emit(OpCodes.Ldc_I4, 5 << 4);
+ il.Emit(OpCodes.Or);
+ il.Emit(OpCodes.Conv_U1);
+ il.Emit(OpCodes.Stind_I1);
+
+ // t[8] = (byte)((t[8] & 0x3f) | 0x80);
+ il.Emit(OpCodes.Ldloca, destination);
+ il.Emit(OpCodes.Ldc_I4_8);
+ il.Emit(OpCodes.Call, getItemMethod);
+ il.Emit(OpCodes.Dup);
+ il.Emit(OpCodes.Ldind_U1);
+ il.Emit(OpCodes.Ldc_I4, 0x3f);
+ il.Emit(OpCodes.And);
+ il.Emit(OpCodes.Ldc_I4, 0x80);
+ il.Emit(OpCodes.Or);
+ il.Emit(OpCodes.Conv_U1);
+ il.Emit(OpCodes.Stind_I1);
+
+ // cacheField = destination.Slice(0, 16).ToArray()
+
+ var toArrayMethod = module.ImportReference(
+ new MethodReference("ToArray", new ArrayType(span.Resolve().GenericParameters[0]), spanOfByte)
+ {
+ HasThis = true
+ });
+
+ var spanTemp = new VariableDefinition(spanOfByte);
+ staticCtor.Body.Variables.Add(spanTemp);
+
+
+ il.Emit(OpCodes.Ldloca, destination);
+ il.Emit(OpCodes.Ldc_I4_0);
+ il.Emit(OpCodes.Ldc_I4, 16);
+ il.Emit(OpCodes.Call, spanSliceStartLengthMethod);
+ il.Emit(OpCodes.Stloc, spanTemp);
+ il.Emit(OpCodes.Ldloca, spanTemp);
+ il.Emit(OpCodes.Call, toArrayMethod);
+ il.Emit(OpCodes.Stsfld, new FieldReference(cacheField.Name, cacheField.FieldType, selfInstantiatedCacheType));
+ il.Emit(OpCodes.Ret);
+ }
+ }
+}
diff --git a/src/Perf/IIDOptimizer/SignatureGenerator.cs b/src/Perf/IIDOptimizer/SignatureGenerator.cs
new file mode 100644
index 000000000..185e06d6e
--- /dev/null
+++ b/src/Perf/IIDOptimizer/SignatureGenerator.cs
@@ -0,0 +1,216 @@
+using Mono.Cecil;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace GuidPatch
+{
+ abstract record SignaturePart;
+
+ enum SignatureType
+ {
+ i1,
+ u1,
+ i2,
+ u2,
+ i4,
+ u4,
+ i8,
+ u8,
+ f4,
+ f8,
+ b1,
+ c2,
+ g16,
+ @string,
+ iinspectable
+ }
+
+ record BasicSignaturePart(SignatureType Type) : SignaturePart;
+
+ sealed record GuidSignature(Guid IID) : SignaturePart;
+
+
+ sealed record CustomSignatureMethod(MethodReference Method) : SignaturePart;
+
+ sealed record NonGenericDelegateSignature(Guid DelegateIID) : SignaturePart;
+
+ sealed record UninstantiatedGeneric(GenericParameter OriginalGenericParameter) : SignaturePart;
+
+ abstract record SignatureWithChildren(string GroupingName, string ThisEntitySignature, IEnumerable ChildrenSignatures) : SignaturePart;
+
+ sealed record GenericSignature(Guid BaseGuid, IEnumerable GenericMemberSignatures) :
+ SignatureWithChildren("pinterface", BaseGuid.ToString("B"), GenericMemberSignatures);
+
+ sealed record ValueTypeSignature(TypeReference Type, IEnumerable StructFieldSignatures) :
+ SignatureWithChildren("struct", Type.FullName, StructFieldSignatures);
+
+ sealed record RuntimeClassSignature(TypeReference RuntimeClass, SignaturePart DefaultInterfaceSignature) :
+ SignatureWithChildren("rc", RuntimeClass.FullName, new[] { DefaultInterfaceSignature });
+
+ sealed record EnumSignature(TypeReference Type, bool IsFlagEnum) :
+ SignatureWithChildren("enum", Type.FullName, new SignaturePart[] { new BasicSignaturePart(IsFlagEnum ? SignatureType.u4 : SignatureType.i4) });
+
+ sealed class SignatureGenerator
+ {
+ private readonly AssemblyDefinition assembly;
+ private readonly TypeDefinition guidAttributeType;
+ private readonly AssemblyDefinition winRTRuntimeAssembly;
+
+ public SignatureGenerator(AssemblyDefinition assembly, TypeDefinition guidAttributeType, AssemblyDefinition runtimeAssembly)
+ {
+ this.assembly = assembly;
+ this.guidAttributeType = guidAttributeType;
+ this.winRTRuntimeAssembly = runtimeAssembly;
+ }
+
+ public SignaturePart GetSignatureParts(TypeReference type)
+ {
+ if (type.IsGenericParameter)
+ {
+ return new UninstantiatedGeneric((GenericParameter)type);
+ }
+
+ var typeDef = type.Resolve();
+
+ var helperType = new TypeReference($"ABI.{typeDef.Namespace}", typeDef.Name, assembly.MainModule, typeDef.Module);
+
+ if (helperType.Resolve() is not null)
+ {
+ if (type.IsGenericInstance)
+ {
+ var helperTypeGeneric = new GenericInstanceType(helperType);
+ foreach (var arg in ((GenericInstanceType)type).GenericArguments)
+ {
+ helperTypeGeneric.GenericArguments.Add(arg);
+ }
+ helperType = helperTypeGeneric;
+ }
+
+ var getGuidSignatureMethod = new MethodReference("GetGuidSignature", assembly.MainModule.TypeSystem.String, helperType)
+ {
+ HasThis = false
+ };
+
+ if (getGuidSignatureMethod.Resolve() is not null)
+ {
+ return new CustomSignatureMethod(assembly.MainModule.ImportReference(getGuidSignatureMethod));
+ }
+ }
+
+ type = typeDef.IsInterface ? (CreateAuthoringMetadataTypeReference(type).Resolve() ?? type) : type;
+ if (typeDef == assembly.MainModule.TypeSystem.Object.Resolve())
+ {
+ return new BasicSignaturePart(SignatureType.iinspectable);
+ }
+
+ if (type.IsGenericInstance)
+ {
+ List signatureParts = new();
+
+ foreach (var arg in ((GenericInstanceType)type).GenericArguments)
+ {
+ signatureParts.Add(GetSignatureParts(arg));
+ }
+
+ Guid? baseGuid = type.ReadGuidFromAttribute(guidAttributeType, winRTRuntimeAssembly);
+ if (baseGuid == null)
+ {
+ throw new InvalidOperationException();
+ }
+ return new GenericSignature(baseGuid.Value, signatureParts);
+ }
+
+ if (type.IsValueType)
+ {
+ switch (type.Name)
+ {
+ case "SByte": return new BasicSignaturePart(SignatureType.i1);
+ case "Byte": return new BasicSignaturePart(SignatureType.u1);
+ case "Int16": return new BasicSignaturePart(SignatureType.i2);
+ case "UInt16": return new BasicSignaturePart(SignatureType.u2);
+ case "Int32": return new BasicSignaturePart(SignatureType.i4);
+ case "UInt32": return new BasicSignaturePart(SignatureType.u4);
+ case "Int64": return new BasicSignaturePart(SignatureType.i8);
+ case "UInt64": return new BasicSignaturePart(SignatureType.u8);
+ case "Single": return new BasicSignaturePart(SignatureType.f4);
+ case "Double": return new BasicSignaturePart(SignatureType.f8);
+ case "Boolean": return new BasicSignaturePart(SignatureType.b1);
+ case "Char": return new BasicSignaturePart(SignatureType.c2);
+ case "Guid": return new BasicSignaturePart(SignatureType.g16);
+ default:
+ {
+ if (typeDef.IsEnum)
+ {
+ var isFlags = typeDef.CustomAttributes.Any(cad => cad.AttributeType.Name == "FlagsAttribute");
+ return new EnumSignature(type, isFlags);
+ }
+ if (!type.IsPrimitive)
+ {
+ var args = type.Resolve().Fields.Where(f => f.IsPublic && !f.IsStatic).Select(fi =>
+ GetSignatureParts(
+ assembly.MainModule.ImportReference(new FieldReference(fi.Name, fi.FieldType, type)).FieldType)).ToArray();
+ return new ValueTypeSignature(type, args);
+ }
+ throw new InvalidOperationException("unsupported value type");
+ }
+ }
+ }
+
+ if (typeDef == assembly.MainModule.TypeSystem.String.Resolve())
+ {
+ return new BasicSignaturePart(SignatureType.@string);
+ }
+
+ if (TryGetDefaultInterfaceTypeForRuntimeClassType(type, out TypeReference? iface))
+ {
+ return new RuntimeClassSignature(type, GetSignatureParts(iface));
+ }
+
+ Guid? guidAttributeValue = type.ReadGuidFromAttribute(guidAttributeType, winRTRuntimeAssembly);
+ if (guidAttributeValue == null)
+ {
+ throw new InvalidOperationException($"Unable to read IID attribute value for {type.FullName}.");
+ }
+
+ if (typeDef.BaseType?.Name == "MulticastDelegate")
+ {
+ return new NonGenericDelegateSignature(guidAttributeValue.Value);
+ }
+ return new GuidSignature(guidAttributeValue.Value);
+ }
+
+ private TypeReference CreateAuthoringMetadataTypeReference(TypeReference type)
+ {
+ return new TypeReference($"ABI.Impl.{type.Name}", type.Name, assembly.MainModule, type.Module);
+ }
+
+ bool TryGetDefaultInterfaceTypeForRuntimeClassType(TypeReference runtimeClassTypeMaybe, [NotNullWhen(true)] out TypeReference? defaultInterface)
+ {
+ defaultInterface = null;
+
+ TypeDefinition rcDef = runtimeClassTypeMaybe.Resolve();
+ rcDef = CreateAuthoringMetadataTypeReference(rcDef).Resolve() ?? rcDef;
+
+ CustomAttribute? runtimeClassAttribute = rcDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.Namespace == "WinRT" && ca.AttributeType.Name == "ProjectedRuntimeClassAttribute");
+
+ if (runtimeClassAttribute is null)
+ {
+ return false;
+ }
+
+ string defaultInterfacePropertyName = (string)runtimeClassAttribute.ConstructorArguments[0].Value;
+
+ var defaultInterfaceProperty = rcDef.Properties.FirstOrDefault(prop => prop.Name == defaultInterfacePropertyName);
+
+ if (defaultInterfaceProperty is null)
+ {
+ return false;
+ }
+
+ defaultInterface = defaultInterfaceProperty.PropertyType;
+ return true;
+ }
+ }
+}
diff --git a/src/Projections/Directory.Build.props b/src/Projections/Directory.Build.props
index 4cf186e3d..2a4d3b6cf 100644
--- a/src/Projections/Directory.Build.props
+++ b/src/Projections/Directory.Build.props
@@ -3,7 +3,11 @@
true
-
+
+
+ true
+
+
diff --git a/src/Projections/Test/Test.csproj b/src/Projections/Test/Test.csproj
index 867f91ae4..2dd8d8a4e 100644
--- a/src/Projections/Test/Test.csproj
+++ b/src/Projections/Test/Test.csproj
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/src/Tests/TestComponentCSharp/ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.cpp b/src/Tests/TestComponentCSharp/ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.cpp
new file mode 100644
index 000000000..638101521
--- /dev/null
+++ b/src/Tests/TestComponentCSharp/ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.cpp
@@ -0,0 +1,19 @@
+#include "pch.h"
+#include "ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.h"
+#include "ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.g.cpp"
+
+namespace winrt::TestComponentCSharp::implementation
+{
+ winrt::event_token ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz::EventForAVeryLongClassName(winrt::Windows::Foundation::TypedEventHandler const& handler)
+ {
+ return _theEvent.add(handler);
+ }
+ void ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz::EventForAVeryLongClassName(winrt::event_token const& token) noexcept
+ {
+ _theEvent.remove(token);
+ }
+ void ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz::InvokeEvent()
+ {
+ _theEvent(*this, *this);
+ }
+}
diff --git a/src/Tests/TestComponentCSharp/ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.h b/src/Tests/TestComponentCSharp/ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.h
new file mode 100644
index 000000000..50b5f6d22
--- /dev/null
+++ b/src/Tests/TestComponentCSharp/ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.g.h"
+
+namespace winrt::TestComponentCSharp::implementation
+{
+ struct ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz : ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzT
+ {
+ ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz() = default;
+
+ winrt::event> _theEvent;
+
+ winrt::event_token EventForAVeryLongClassName(winrt::Windows::Foundation::TypedEventHandler const& handler);
+ void EventForAVeryLongClassName(winrt::event_token const& token) noexcept;
+ void InvokeEvent();
+ };
+}
+namespace winrt::TestComponentCSharp::factory_implementation
+{
+ struct ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz : ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzT
+ {
+ };
+}
diff --git a/src/Tests/TestComponentCSharp/Class.cpp b/src/Tests/TestComponentCSharp/Class.cpp
index f022176e6..152ece65e 100644
--- a/src/Tests/TestComponentCSharp/Class.cpp
+++ b/src/Tests/TestComponentCSharp/Class.cpp
@@ -946,6 +946,21 @@ namespace winrt::TestComponentCSharp::implementation
{
return winrt::single_threaded_vector_view(std::vector{ *this, *this, *this });
}
+
+ // Test IIDOptimizer
+ IVectorView Class::GetEventArgsVector()
+ {
+ auto mock = make(L"name");
+ DataErrorsChangedEventArgs args(detach_abi(mock), take_ownership_from_abi_t());
+ return winrt::single_threaded_vector_view(std::vector{ args });
+ }
+
+ // Test IIDOptimizer
+ IVectorView Class::GetNonGenericDelegateVector()
+ {
+ TestComponentCSharp::ProvideUri handler = [] { return Windows::Foundation::Uri(L"http://microsoft.com"); };
+ return winrt::single_threaded_vector_view(std::vector{ handler });
+ }
void Class::CompleteAsync()
{
diff --git a/src/Tests/TestComponentCSharp/Class.h b/src/Tests/TestComponentCSharp/Class.h
index 226bf7f6e..01718f137 100644
--- a/src/Tests/TestComponentCSharp/Class.h
+++ b/src/Tests/TestComponentCSharp/Class.h
@@ -266,6 +266,10 @@ namespace winrt::TestComponentCSharp::implementation
Windows::Foundation::Collections::IVectorView GetObjectVector();
Windows::Foundation::Collections::IVectorView GetInterfaceVector();
Windows::Foundation::Collections::IVectorView GetClassVector() noexcept;
+
+ // Test IIDOptimizer -- testing the windows projection covers most code paths, and these two types exercise the rest.
+ Windows::Foundation::Collections::IVectorView GetEventArgsVector();
+ Windows::Foundation::Collections::IVectorView GetNonGenericDelegateVector();
Windows::Foundation::Collections::IIterable GetIntIterable();
void SetIntIterable(Windows::Foundation::Collections::IIterable const& value);
diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl
index 061986fc3..9c0a254d3 100644
--- a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl
+++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl
@@ -299,6 +299,10 @@ namespace TestComponentCSharp
Windows.Foundation.Collections.IVectorView GetInterfaceVector();
[noexcept] Windows.Foundation.Collections.IVectorView GetClassVector();
+ // Test IIDOptimizer
+ Windows.Foundation.Collections.IVectorView GetEventArgsVector();
+ Windows.Foundation.Collections.IVectorView GetNonGenericDelegateVector();
+
Windows.Foundation.Collections.IIterable GetIntIterable();
void SetIntIterable(Windows.Foundation.Collections.IIterable value);
@@ -398,6 +402,15 @@ namespace TestComponentCSharp
static Object BadRuntimeClassName{ get; };
}
+ runtimeclass ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ {
+ ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz();
+
+ event Windows.Foundation.TypedEventHandler EventForAVeryLongClassName;
+
+ void InvokeEvent();
+ }
+
[threading(sta), marshaling_behavior(standard)]
runtimeclass NonAgileClass
{
diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj b/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj
index 01d56f880..4112b1cff 100644
--- a/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj
+++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj
@@ -64,6 +64,7 @@
+
@@ -77,6 +78,7 @@
+
Create
diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj.filters b/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj.filters
index 91f509b7c..c1399fa56 100644
--- a/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj.filters
+++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj.filters
@@ -16,6 +16,7 @@
+
@@ -25,6 +26,7 @@
+
diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs
index 44f2515f1..dd62e0805 100644
--- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs
+++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs
@@ -49,6 +49,20 @@ public class TestCSharp
public TestCSharp()
{
TestObject = new Class();
+ }
+
+
+ // Test a fix for a bug in Mono.Cecil that was affecting the IIDOptimizer when it encountered long class names
+ [Fact]
+ public void TestLongClassNameEventSource()
+ {
+ bool flag = false;
+ var long_class_name = new ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz();
+ long_class_name.EventForAVeryLongClassName +=
+ (ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz sender, ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz args)
+ => flag = true;
+ long_class_name.InvokeEvent();
+ Assert.True(flag);
}
[Fact]
diff --git a/src/Tests/UnitTest/UnitTest.csproj b/src/Tests/UnitTest/UnitTest.csproj
index e63aeb190..a00ffb573 100644
--- a/src/Tests/UnitTest/UnitTest.csproj
+++ b/src/Tests/UnitTest/UnitTest.csproj
@@ -9,6 +9,7 @@
true
true
false
+ true
diff --git a/src/WinRT.Runtime/WinRT.Runtime.csproj b/src/WinRT.Runtime/WinRT.Runtime.csproj
index 4a61f108a..125352f7a 100644
--- a/src/WinRT.Runtime/WinRT.Runtime.csproj
+++ b/src/WinRT.Runtime/WinRT.Runtime.csproj
@@ -4,7 +4,7 @@
netstandard2.0;net5.0
WinRT
true
- 8
+ 9
full
true
Microsoft Corporation
diff --git a/src/build.cmd b/src/build.cmd
index f446149f0..f22ea170c 100644
--- a/src/build.cmd
+++ b/src/build.cmd
@@ -1,221 +1,222 @@
-@echo off
-if /i "%cswinrt_echo%" == "on" @echo on
-
-set CsWinRTNet5SdkVersion=5.0.300
-set this_dir=%~dp0
-
-:dotnet
-rem Install required .NET 5 SDK version and add to environment
-set DOTNET_ROOT=%LocalAppData%\Microsoft\dotnet
-set DOTNET_ROOT(86)=%LocalAppData%\Microsoft\dotnet\x86
-set path=%DOTNET_ROOT%;%path%
-
-
-powershell -NoProfile -ExecutionPolicy unrestricted -Command ^
-[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^
-&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) ^
--Version '%CsWinRTNet5SdkVersion%' -InstallDir '%DOTNET_ROOT%' -Architecture 'x64' ^
--AzureFeed 'https://dotnetcli.blob.core.windows.net/dotnet'
-powershell -NoProfile -ExecutionPolicy unrestricted -Command ^
-[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^
-&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) ^
--Version '%CsWinRTNet5SdkVersion%' -InstallDir '%DOTNET_ROOT(86)%' -Architecture 'x86' ^
--AzureFeed 'https://dotnetcli.blob.core.windows.net/dotnet'
-
-:globaljson
-rem Create global.json for current .NET SDK, and with allowPrerelease=true
-set global_json=%this_dir%global.json
-echo { > %global_json%
-echo "sdk": { >> %global_json%
-echo "version": "%CsWinRTNet5SdkVersion%", >> %global_json%
-echo "allowPrerelease": true >> %global_json%
-echo } >> %global_json%
-echo } >> %global_json%
-
-rem Preserve above for Visual Studio launch inheritance
-setlocal ENABLEDELAYEDEXPANSION
-
-:params
-set cswinrt_platform=%1
-set cswinrt_configuration=%2
-set cswinrt_version_number=%3
-set cswinrt_version_string=%4
-set cswinrt_assembly_version=%5
-set "%6"!="" set cswinrt_label=%6
-
-if "%cswinrt_platform%"=="" set cswinrt_platform=x64
-
-if /I "%cswinrt_platform%" equ "all" (
- if "%cswinrt_configuration%"=="" (
- set cswinrt_configuration=all
- )
- call %0 x86 !cswinrt_configuration! !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
- call %0 x64 !cswinrt_configuration! !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
- call %0 arm !cswinrt_configuration! !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
- call %0 arm64 !cswinrt_configuration! !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
- goto :eof
-)
-
-if /I "%cswinrt_configuration%" equ "all" (
- call %0 %cswinrt_platform% Debug !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
- call %0 %cswinrt_platform% Release !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
- goto :eof
-)
-
-if "%cswinrt_configuration%"=="" (
- set cswinrt_configuration=Release
-)
-
-if "%cswinrt_version_number%"=="" set cswinrt_version_number=0.0.0.0
-if "%cswinrt_version_string%"=="" set cswinrt_version_string=0.0.0-private.0
-if "%cswinrt_assembly_version%"=="" set cswinrt_assembly_version=0.0.0.0
-
-if "%cswinrt_baseline_breaking_compat_errors%"=="" set cswinrt_baseline_breaking_compat_errors=false
-if "%cswinrt_baseline_assembly_version_compat_errors%"=="" set cswinrt_baseline_assembly_version_compat_errors=false
-
-rem Generate prerelease targets file to exercise build warnings
-set prerelease_targets=%this_dir%..\nuget\Microsoft.Windows.CsWinRT.Prerelease.targets
-rem Create default %prerelease_targets%
-echo ^ > %prerelease_targets%
-echo ^> %prerelease_targets%
-echo Condition="'$(NetCoreSdkVersion)' ^!= '%CsWinRTNet5SdkVersion%' and '$(Net5SdkVersion)' ^!= '%CsWinRTNet5SdkVersion%'"^> >> %prerelease_targets%
-echo ^ >> %prerelease_targets%
-echo ^ >> %prerelease_targets%
-echo ^ >> %prerelease_targets%
-
-goto :skip_build_tools
-rem VS 16.X BuildTools support (when a prerelease VS is required, until it is deployed to Azure Devops agents)
-msbuild -ver | findstr 16.X >nul
-if ErrorLevel 1 (
- echo Using VS Build Tools 16.X
- if %cswinrt_platform%==x86 (
- set msbuild_path="%this_dir%.buildtools\MSBuild\Current\Bin\\"
- ) else (
- set msbuild_path="%this_dir%.buildtools\MSBuild\Current\Bin\amd64\\"
- )
- if not exist !msbuild_path! (
- if not exist .buildtools md .buildtools
- powershell -NoProfile -ExecutionPolicy unrestricted -File .\get_buildtools.ps1
- )
- set nuget_params=-MSBuildPath !msbuild_path!
-) else (
- set msbuild_path=
- set nuget_params=
-)
-:skip_build_tools
-
-set nuget_dir=%this_dir%.nuget
-
-if not "%cswinrt_label%"=="" goto %cswinrt_label%
-
-:restore
-rem When a preview nuget is required, update -self doesn't work, so manually update
-if exist %nuget_dir%\nuget.exe (
- %nuget_dir%\nuget.exe | findstr 5.9 >nul
- if ErrorLevel 1 (
- echo Updating to nuget 5.9
- rd /s/q %nuget_dir% >nul 2>&1
- )
-)
-if not exist %nuget_dir% md %nuget_dir%
-if not exist %nuget_dir%\nuget.exe powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/v5.8.0-preview.2/nuget.exe -OutFile %nuget_dir%\nuget.exe"
-%nuget_dir%\nuget update -self
-rem Note: packages.config-based (vcxproj) projects do not support msbuild /t:restore
-call %this_dir%get_testwinrt.cmd
-set NUGET_RESTORE_MSBUILD_ARGS=/p:platform="%cswinrt_platform%"
-call :exec %nuget_dir%\nuget.exe restore %nuget_params% %this_dir%cswinrt.sln
-rem: Calling nuget restore again on ObjectLifetimeTests.Lifted.csproj to prevent .props from \microsoft.testplatform.testhost\build\netcoreapp2.1 from being included. Nuget.exe erroneously imports props files. https://github.com/NuGet/Home/issues/9672
-call :exec %msbuild_path%msbuild.exe %this_dir%\Tests\ObjectLifetimeTests\ObjectLifetimeTests.Lifted.csproj /t:restore /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%
-
-:build
-echo Building cswinrt for %cswinrt_platform% %cswinrt_configuration%
-call :exec %msbuild_path%msbuild.exe %cswinrt_build_params% /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;VersionNumber=%cswinrt_version_number%;VersionString=%cswinrt_version_string%;AssemblyVersionNumber=%cswinrt_assembly_version%;GenerateTestProjection=true;BaselineAllAPICompatError=%cswinrt_baseline_breaking_compat_errors%;BaselineAllMatchingRefApiCompatError=%cswinrt_baseline_assembly_version_compat_errors% %this_dir%cswinrt.sln
-if ErrorLevel 1 (
- echo.
- echo ERROR: Build failed
- exit /b !ErrorLevel!
-)
-if "%cswinrt_build_only%"=="true" goto :eof
-
-:test
-:unittest
-rem Build/Run xUnit tests, generating xml output report for Azure Devops reporting, via XunitXml.TestLogger NuGet
-echo Running cswinrt unit tests for %cswinrt_platform% %cswinrt_configuration%
-if %cswinrt_platform%==x86 (
- set dotnet_exe="%DOTNET_ROOT(86)%\dotnet.exe"
-) else (
- set dotnet_exe="%DOTNET_ROOT%\dotnet.exe"
-)
-if not exist %dotnet_exe% (
- if %cswinrt_platform%==x86 (
- set dotnet_exe="%ProgramFiles(x86)%\dotnet\dotnet.exe"
- ) else (
- set dotnet_exe="%ProgramFiles%\dotnet\dotnet.exe"
- )
-)
-
-:objectlifetimetests
-rem Running Object Lifetime Unit Tests
-pushd .
-cd %this_dir%\Tests\ObjectLifetimeTests\bin\%cswinrt_platform%\%cswinrt_configuration%\net5.0-windows10.0.19041.0\win10-%cswinrt_platform%
-sn -Vr Microsoft.Windows.SDK.NET.dll
-vstest.console.exe ObjectLifetimeTests.Lifted.build.appxrecipe /TestAdapterPath:"%USERPROFILE%\.nuget\packages\mstest.testadapter\2.2.4-preview-20210513-02\build\_common" /framework:FrameworkUap10 /logger:trx;LogFileName=%this_dir%\VsTestResults.trx
-popd
-
-
-
-rem WinUI NuGet package's Microsoft.WinUI.AppX.targets attempts to import a file that does not exist, even when
-rem executing "dotnet test --no-build ...", which evidently still needs to parse and load the entire project.
-rem Work around by using a dummy targets file and assigning it to the MsAppxPackageTargets property.
-echo ^ > %temp%\EmptyMsAppxPackage.Targets
-call :exec %dotnet_exe% test --verbosity normal --no-build --logger xunit;LogFilePath=%~dp0unittest_%cswinrt_version_string%.xml %this_dir%Tests/unittest/UnitTest.csproj /nologo /m /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;MsAppxPackageTargets=%temp%\EmptyMsAppxPackage.Targets
-if ErrorLevel 1 (
- echo.
- echo ERROR: Unit test failed, skipping NuGet pack
- exit /b !ErrorLevel!
-)
-
-:hosttest
-rem Run WinRT.Host tests
-echo Running cswinrt host tests for %cswinrt_platform% %cswinrt_configuration%
-call :exec %this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\HostTest\bin\HostTest.exe --gtest_output=xml:%this_dir%hosttest_%cswinrt_version_string%.xml
-if ErrorLevel 1 (
- echo.
- echo ERROR: Host test failed, skipping NuGet pack
- exit /b !ErrorLevel!
-)
-
-
-:authortest
-rem Run Authoring tests
-echo Running cswinrt authoring tests for %cswinrt_platform% %cswinrt_configuration%
-call :exec %this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\AuthoringConsumptionTest\bin\AuthoringConsumptionTest.exe --gtest_output=xml:%this_dir%hosttest_%cswinrt_version_string%.xml
-if ErrorLevel 1 (
- echo.
- echo ERROR: Authoring test failed, skipping NuGet pack
- exit /b !ErrorLevel!
-)
-
-:package
-set cswinrt_bin_dir=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\cswinrt\bin\
-set cswinrt_exe=%cswinrt_bin_dir%cswinrt.exe
-set interop_winmd=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\cswinrt\obj\merged\WinRT.Interop.winmd
-set netstandard2_runtime=%this_dir%WinRT.Runtime\bin\%cswinrt_configuration%\netstandard2.0\WinRT.Runtime.dll
-set net5_runtime=%this_dir%WinRT.Runtime\bin\%cswinrt_configuration%\net5.0\WinRT.Runtime.dll
-set source_generator=%this_dir%Authoring\WinRT.SourceGenerator\bin\%cswinrt_configuration%\netstandard2.0\WinRT.SourceGenerator.dll
-set winrt_host_%cswinrt_platform%=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\WinRT.Host\bin\WinRT.Host.dll
-set winrt_shim=%this_dir%Authoring\WinRT.Host.Shim\bin\%cswinrt_configuration%\net5.0\WinRT.Host.Shim.dll
-echo Creating nuget package
-call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinRT.nuspec -Properties cswinrt_exe=%cswinrt_exe%;interop_winmd=%interop_winmd%;netstandard2_runtime=%netstandard2_runtime%;net5_runtime=%net5_runtime%;source_generator=%source_generator%;cswinrt_nuget_version=%cswinrt_version_string%;winrt_host_x86=%winrt_host_x86%;winrt_host_x64=%winrt_host_x64%;winrt_host_arm=%winrt_host_arm%;winrt_host_arm64=%winrt_host_arm64%;winrt_shim=%winrt_shim% -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis
-goto :eof
-
-:exec
-if /i "%cswinrt_echo%" == "only" (
-echo Command Line:
-echo %*
-echo.
-) else (
-%*
-)
-goto :eof
-
+@echo off
+if /i "%cswinrt_echo%" == "on" @echo on
+
+set CsWinRTNet5SdkVersion=5.0.300
+set this_dir=%~dp0
+
+:dotnet
+rem Install required .NET 5 SDK version and add to environment
+set DOTNET_ROOT=%LocalAppData%\Microsoft\dotnet
+set DOTNET_ROOT(86)=%LocalAppData%\Microsoft\dotnet\x86
+set path=%DOTNET_ROOT%;%path%
+
+
+powershell -NoProfile -ExecutionPolicy unrestricted -Command ^
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^
+&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) ^
+-Version '%CsWinRTNet5SdkVersion%' -InstallDir '%DOTNET_ROOT%' -Architecture 'x64' ^
+-AzureFeed 'https://dotnetcli.blob.core.windows.net/dotnet'
+powershell -NoProfile -ExecutionPolicy unrestricted -Command ^
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^
+&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) ^
+-Version '%CsWinRTNet5SdkVersion%' -InstallDir '%DOTNET_ROOT(86)%' -Architecture 'x86' ^
+-AzureFeed 'https://dotnetcli.blob.core.windows.net/dotnet'
+
+:globaljson
+rem Create global.json for current .NET SDK, and with allowPrerelease=true
+set global_json=%this_dir%global.json
+echo { > %global_json%
+echo "sdk": { >> %global_json%
+echo "version": "%CsWinRTNet5SdkVersion%", >> %global_json%
+echo "allowPrerelease": true >> %global_json%
+echo } >> %global_json%
+echo } >> %global_json%
+
+rem Preserve above for Visual Studio launch inheritance
+setlocal ENABLEDELAYEDEXPANSION
+
+:params
+set cswinrt_platform=%1
+set cswinrt_configuration=%2
+set cswinrt_version_number=%3
+set cswinrt_version_string=%4
+set cswinrt_assembly_version=%5
+set "%6"!="" set cswinrt_label=%6
+
+if "%cswinrt_platform%"=="" set cswinrt_platform=x64
+
+if /I "%cswinrt_platform%" equ "all" (
+ if "%cswinrt_configuration%"=="" (
+ set cswinrt_configuration=all
+ )
+ call %0 x86 !cswinrt_configuration! !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
+ call %0 x64 !cswinrt_configuration! !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
+ call %0 arm !cswinrt_configuration! !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
+ call %0 arm64 !cswinrt_configuration! !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
+ goto :eof
+)
+
+if /I "%cswinrt_configuration%" equ "all" (
+ call %0 %cswinrt_platform% Debug !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
+ call %0 %cswinrt_platform% Release !cswinrt_version_number! !cswinrt_version_string! !cswinrt_assembly_version!
+ goto :eof
+)
+
+if "%cswinrt_configuration%"=="" (
+ set cswinrt_configuration=Release
+)
+
+if "%cswinrt_version_number%"=="" set cswinrt_version_number=0.0.0.0
+if "%cswinrt_version_string%"=="" set cswinrt_version_string=0.0.0-private.0
+if "%cswinrt_assembly_version%"=="" set cswinrt_assembly_version=0.0.0.0
+
+if "%cswinrt_baseline_breaking_compat_errors%"=="" set cswinrt_baseline_breaking_compat_errors=false
+if "%cswinrt_baseline_assembly_version_compat_errors%"=="" set cswinrt_baseline_assembly_version_compat_errors=false
+
+rem Generate prerelease targets file to exercise build warnings
+set prerelease_targets=%this_dir%..\nuget\Microsoft.Windows.CsWinRT.Prerelease.targets
+rem Create default %prerelease_targets%
+echo ^ > %prerelease_targets%
+echo ^> %prerelease_targets%
+echo Condition="'$(NetCoreSdkVersion)' ^!= '%CsWinRTNet5SdkVersion%' and '$(Net5SdkVersion)' ^!= '%CsWinRTNet5SdkVersion%'"^> >> %prerelease_targets%
+echo ^ >> %prerelease_targets%
+echo ^ >> %prerelease_targets%
+echo ^ >> %prerelease_targets%
+
+goto :skip_build_tools
+rem VS 16.X BuildTools support (when a prerelease VS is required, until it is deployed to Azure Devops agents)
+msbuild -ver | findstr 16.X >nul
+if ErrorLevel 1 (
+ echo Using VS Build Tools 16.X
+ if %cswinrt_platform%==x86 (
+ set msbuild_path="%this_dir%.buildtools\MSBuild\Current\Bin\\"
+ ) else (
+ set msbuild_path="%this_dir%.buildtools\MSBuild\Current\Bin\amd64\\"
+ )
+ if not exist !msbuild_path! (
+ if not exist .buildtools md .buildtools
+ powershell -NoProfile -ExecutionPolicy unrestricted -File .\get_buildtools.ps1
+ )
+ set nuget_params=-MSBuildPath !msbuild_path!
+) else (
+ set msbuild_path=
+ set nuget_params=
+)
+:skip_build_tools
+
+set nuget_dir=%this_dir%.nuget
+
+if not "%cswinrt_label%"=="" goto %cswinrt_label%
+
+:restore
+rem When a preview nuget is required, update -self doesn't work, so manually update
+if exist %nuget_dir%\nuget.exe (
+ %nuget_dir%\nuget.exe | findstr 5.9 >nul
+ if ErrorLevel 1 (
+ echo Updating to nuget 5.9
+ rd /s/q %nuget_dir% >nul 2>&1
+ )
+)
+if not exist %nuget_dir% md %nuget_dir%
+if not exist %nuget_dir%\nuget.exe powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/v5.8.0-preview.2/nuget.exe -OutFile %nuget_dir%\nuget.exe"
+%nuget_dir%\nuget update -self
+rem Note: packages.config-based (vcxproj) projects do not support msbuild /t:restore
+call %this_dir%get_testwinrt.cmd
+set NUGET_RESTORE_MSBUILD_ARGS=/p:platform="%cswinrt_platform%"
+call :exec %nuget_dir%\nuget.exe restore %nuget_params% %this_dir%cswinrt.sln
+rem: Calling nuget restore again on ObjectLifetimeTests.Lifted.csproj to prevent .props from \microsoft.testplatform.testhost\build\netcoreapp2.1 from being included. Nuget.exe erroneously imports props files. https://github.com/NuGet/Home/issues/9672
+call :exec %msbuild_path%msbuild.exe %this_dir%\Tests\ObjectLifetimeTests\ObjectLifetimeTests.Lifted.csproj /t:restore /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%
+
+:build
+echo Building cswinrt for %cswinrt_platform% %cswinrt_configuration%
+call :exec %msbuild_path%msbuild.exe %cswinrt_build_params% /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;VersionNumber=%cswinrt_version_number%;VersionString=%cswinrt_version_string%;AssemblyVersionNumber=%cswinrt_assembly_version%;GenerateTestProjection=true;BaselineAllAPICompatError=%cswinrt_baseline_breaking_compat_errors%;BaselineAllMatchingRefApiCompatError=%cswinrt_baseline_assembly_version_compat_errors% %this_dir%cswinrt.sln
+if ErrorLevel 1 (
+ echo.
+ echo ERROR: Build failed
+ exit /b !ErrorLevel!
+)
+if "%cswinrt_build_only%"=="true" goto :eof
+
+:test
+:unittest
+rem Build/Run xUnit tests, generating xml output report for Azure Devops reporting, via XunitXml.TestLogger NuGet
+echo Running cswinrt unit tests for %cswinrt_platform% %cswinrt_configuration%
+if %cswinrt_platform%==x86 (
+ set dotnet_exe="%DOTNET_ROOT(86)%\dotnet.exe"
+) else (
+ set dotnet_exe="%DOTNET_ROOT%\dotnet.exe"
+)
+if not exist %dotnet_exe% (
+ if %cswinrt_platform%==x86 (
+ set dotnet_exe="%ProgramFiles(x86)%\dotnet\dotnet.exe"
+ ) else (
+ set dotnet_exe="%ProgramFiles%\dotnet\dotnet.exe"
+ )
+)
+
+:objectlifetimetests
+rem Running Object Lifetime Unit Tests
+pushd .
+cd %this_dir%\Tests\ObjectLifetimeTests\bin\%cswinrt_platform%\%cswinrt_configuration%\net5.0-windows10.0.19041.0\win10-%cswinrt_platform%
+sn -Vr Microsoft.Windows.SDK.NET.dll
+vstest.console.exe ObjectLifetimeTests.Lifted.build.appxrecipe /TestAdapterPath:"%USERPROFILE%\.nuget\packages\mstest.testadapter\2.2.4-preview-20210513-02\build\_common" /framework:FrameworkUap10 /logger:trx;LogFileName=%this_dir%\VsTestResults.trx
+popd
+
+
+
+rem WinUI NuGet package's Microsoft.WinUI.AppX.targets attempts to import a file that does not exist, even when
+rem executing "dotnet test --no-build ...", which evidently still needs to parse and load the entire project.
+rem Work around by using a dummy targets file and assigning it to the MsAppxPackageTargets property.
+echo ^ > %temp%\EmptyMsAppxPackage.Targets
+call :exec %dotnet_exe% test --verbosity normal --no-build --logger xunit;LogFilePath=%~dp0unittest_%cswinrt_version_string%.xml %this_dir%Tests/unittest/UnitTest.csproj /nologo /m /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;MsAppxPackageTargets=%temp%\EmptyMsAppxPackage.Targets
+if ErrorLevel 1 (
+ echo.
+ echo ERROR: Unit test failed, skipping NuGet pack
+ exit /b !ErrorLevel!
+)
+
+:hosttest
+rem Run WinRT.Host tests
+echo Running cswinrt host tests for %cswinrt_platform% %cswinrt_configuration%
+call :exec %this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\HostTest\bin\HostTest.exe --gtest_output=xml:%this_dir%hosttest_%cswinrt_version_string%.xml
+if ErrorLevel 1 (
+ echo.
+ echo ERROR: Host test failed, skipping NuGet pack
+ exit /b !ErrorLevel!
+)
+
+
+:authortest
+rem Run Authoring tests
+echo Running cswinrt authoring tests for %cswinrt_platform% %cswinrt_configuration%
+call :exec %this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\AuthoringConsumptionTest\bin\AuthoringConsumptionTest.exe --gtest_output=xml:%this_dir%hosttest_%cswinrt_version_string%.xml
+if ErrorLevel 1 (
+ echo.
+ echo ERROR: Authoring test failed, skipping NuGet pack
+ exit /b !ErrorLevel!
+)
+
+:package
+set cswinrt_bin_dir=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\cswinrt\bin\
+set cswinrt_exe=%cswinrt_bin_dir%cswinrt.exe
+set interop_winmd=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\cswinrt\obj\merged\WinRT.Interop.winmd
+set netstandard2_runtime=%this_dir%WinRT.Runtime\bin\%cswinrt_configuration%\netstandard2.0\WinRT.Runtime.dll
+set net5_runtime=%this_dir%WinRT.Runtime\bin\%cswinrt_configuration%\net5.0\WinRT.Runtime.dll
+set source_generator=%this_dir%Authoring\WinRT.SourceGenerator\bin\%cswinrt_configuration%\netstandard2.0\WinRT.SourceGenerator.dll
+set winrt_host_%cswinrt_platform%=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\WinRT.Host\bin\WinRT.Host.dll
+set winrt_shim=%this_dir%Authoring\WinRT.Host.Shim\bin\%cswinrt_configuration%\net5.0\WinRT.Host.Shim.dll
+set guid_patch=%this_dir%Perf\IIDOptimizer\bin\%cswinrt_configuration%\net5.0\*.*
+echo Creating nuget package
+call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinRT.nuspec -Properties cswinrt_exe=%cswinrt_exe%;interop_winmd=%interop_winmd%;netstandard2_runtime=%netstandard2_runtime%;net5_runtime=%net5_runtime%;source_generator=%source_generator%;cswinrt_nuget_version=%cswinrt_version_string%;winrt_host_x86=%winrt_host_x86%;winrt_host_x64=%winrt_host_x64%;winrt_host_arm=%winrt_host_arm%;winrt_host_arm64=%winrt_host_arm64%;winrt_shim=%winrt_shim%;guid_patch=%guid_patch% -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis
+goto :eof
+
+:exec
+if /i "%cswinrt_echo%" == "only" (
+echo Command Line:
+echo %*
+echo.
+) else (
+%*
+)
+goto :eof
+
diff --git a/src/cswinrt.sln b/src/cswinrt.sln
index db2c2c970..ec7fbc5d1 100644
--- a/src/cswinrt.sln
+++ b/src/cswinrt.sln
@@ -8,6 +8,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestComponentCSharp", "Test
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "Tests\UnitTest\UnitTest.csproj", "{9A9F52CA-F624-43A4-B5EF-C50861F584C2}"
+ ProjectSection(ProjectDependencies) = postProject
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9} = {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}
+ {25244CED-966E-45F2-9711-1F51E951FF89} = {25244CED-966E-45F2-9711-1F51E951FF89}
+ EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cswinrt", "cswinrt\cswinrt.vcxproj", "{6ACFD2B2-E8AA-4CD4-AAD8-213CE8BB2637}"
ProjectSection(ProjectDependencies) = postProject
@@ -99,10 +103,17 @@ Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "AuthoringWinUITest (Package
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AuthoringWinUITest", "Tests\AuthoringWinUITest\AuthoringWinUITest\AuthoringWinUITest.vcxproj", "{493C7729-2F21-4198-AB09-BDF56BF501D3}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reunion", "Projections\Reunion\Reunion.csproj", "{B6312AD1-A59E-4F3B-AA39-20B780FE9E15}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ObjectLifetimeTests.Lifted", "Tests\ObjectLifetimeTests\ObjectLifetimeTests.Lifted.csproj", "{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Perf", "Perf", "{539DBDEF-3B49-4503-9BD3-7EB83C2179CB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIDOptimizer", "Perf\IIDOptimizer\IIDOptimizer.csproj", "{AE3B0611-2FBB-42AB-A245-B4E79868A5F9}"
+ ProjectSection(ProjectDependencies) = postProject
+ {25244CED-966E-45F2-9711-1F51E951FF89} = {25244CED-966E-45F2-9711-1F51E951FF89}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reunion", "Projections\Reunion\Reunion.csproj", "{B6312AD1-A59E-4F3B-AA39-20B780FE9E15}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@@ -348,7 +359,6 @@ Global
{0212A7C5-8D3F-443C-9EBC-1F28091FDF88}.Debug|ARM.ActiveCfg = Debug|Win32
{0212A7C5-8D3F-443C-9EBC-1F28091FDF88}.Debug|ARM64.ActiveCfg = Debug|Win32
{0212A7C5-8D3F-443C-9EBC-1F28091FDF88}.Debug|x64.ActiveCfg = Debug|x64
- {0212A7C5-8D3F-443C-9EBC-1F28091FDF88}.Debug|x64.Build.0 = Debug|x64
{0212A7C5-8D3F-443C-9EBC-1F28091FDF88}.Debug|x86.ActiveCfg = Debug|Win32
{0212A7C5-8D3F-443C-9EBC-1F28091FDF88}.Debug|x86.Build.0 = Debug|Win32
{0212A7C5-8D3F-443C-9EBC-1F28091FDF88}.Release|ARM.ActiveCfg = Release|Win32
@@ -406,7 +416,6 @@ Global
{75B1621F-EC51-4D77-BD7E-BEE576B3ADC9}.Debug|ARM64.Build.0 = Debug|arm64
{75B1621F-EC51-4D77-BD7E-BEE576B3ADC9}.Debug|ARM64.Deploy.0 = Debug|arm64
{75B1621F-EC51-4D77-BD7E-BEE576B3ADC9}.Debug|x64.ActiveCfg = Debug|x64
- {75B1621F-EC51-4D77-BD7E-BEE576B3ADC9}.Debug|x64.Build.0 = Debug|x64
{75B1621F-EC51-4D77-BD7E-BEE576B3ADC9}.Debug|x64.Deploy.0 = Debug|x64
{75B1621F-EC51-4D77-BD7E-BEE576B3ADC9}.Debug|x86.ActiveCfg = Debug|x86
{75B1621F-EC51-4D77-BD7E-BEE576B3ADC9}.Debug|x86.Build.0 = Debug|x86
@@ -425,7 +434,6 @@ Global
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Debug|ARM64.ActiveCfg = Debug|arm64
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Debug|ARM64.Build.0 = Debug|arm64
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Debug|x64.ActiveCfg = Debug|x64
- {493C7729-2F21-4198-AB09-BDF56BF501D3}.Debug|x64.Build.0 = Debug|x64
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Debug|x86.ActiveCfg = Debug|Win32
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Debug|x86.Build.0 = Debug|Win32
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Release|ARM.ActiveCfg = Release|Win32
@@ -435,37 +443,48 @@ Global
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Release|x64.Build.0 = Release|x64
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Release|x86.ActiveCfg = Release|Win32
{493C7729-2F21-4198-AB09-BDF56BF501D3}.Release|x86.Build.0 = Release|Win32
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|ARM.ActiveCfg = Debug|x86
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|ARM64.ActiveCfg = Debug|x86
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x64.ActiveCfg = Debug|x64
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x64.Build.0 = Debug|x64
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x86.ActiveCfg = Debug|x86
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x86.Build.0 = Debug|x86
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|ARM.ActiveCfg = Release|x86
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|ARM64.ActiveCfg = Release|x86
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x64.ActiveCfg = Release|x64
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x64.Build.0 = Release|x64
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x86.ActiveCfg = Release|x86
- {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x86.Build.0 = Release|x86
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|ARM.ActiveCfg = Debug|x86
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|ARM64.ActiveCfg = Debug|arm64
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|ARM64.Build.0 = Debug|arm64
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|x64.ActiveCfg = Debug|x64
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|x64.Build.0 = Debug|x64
- {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|x64.Deploy.0 = Debug|x64
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|x86.ActiveCfg = Debug|x86
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|x86.Build.0 = Debug|x86
- {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Debug|x86.Deploy.0 = Debug|x86
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|ARM.ActiveCfg = Release|x86
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|ARM64.ActiveCfg = Release|arm64
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|ARM64.Build.0 = Release|arm64
- {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|ARM64.Deploy.0 = Release|arm64
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x64.ActiveCfg = Release|x64
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x64.Build.0 = Release|x64
- {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x64.Deploy.0 = Release|x64
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x86.ActiveCfg = Release|x86
{BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x86.Build.0 = Release|x86
- {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x86.Deploy.0 = Release|x86
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|ARM.Build.0 = Debug|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|x64.Build.0 = Debug|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|x86.Build.0 = Debug|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|ARM.ActiveCfg = Release|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|ARM.Build.0 = Release|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|ARM64.Build.0 = Release|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|x64.ActiveCfg = Release|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|x64.Build.0 = Release|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|x86.ActiveCfg = Release|Any CPU
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|x86.Build.0 = Release|Any CPU
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|ARM.ActiveCfg = Debug|x86
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|ARM64.ActiveCfg = Debug|x86
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x64.ActiveCfg = Debug|x64
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x64.Build.0 = Debug|x64
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x86.ActiveCfg = Debug|x86
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x86.Build.0 = Debug|x86
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|ARM.ActiveCfg = Release|x86
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|ARM64.ActiveCfg = Release|x86
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x64.ActiveCfg = Release|x64
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x64.Build.0 = Release|x64
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x86.ActiveCfg = Release|x86
+ {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -491,6 +510,7 @@ Global
{FC05C557-C974-4CB3-9DA7-BB5476710E91} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5}
{75B1621F-EC51-4D77-BD7E-BEE576B3ADC9} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5}
{493C7729-2F21-4198-AB09-BDF56BF501D3} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5}
+ {AE3B0611-2FBB-42AB-A245-B4E79868A5F9} = {539DBDEF-3B49-4503-9BD3-7EB83C2179CB}
{B6312AD1-A59E-4F3B-AA39-20B780FE9E15} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp
index d454e3076..4cc1446c0 100644
--- a/src/cswinrt/main.cpp
+++ b/src/cswinrt/main.cpp
@@ -120,6 +120,7 @@ Where is one or more of:
int result{};
writer w;
+ /* Special case the usage exceptions to print CLI options */
try
{
auto start = get_start_time();
diff --git a/src/get_testwinrt.cmd b/src/get_testwinrt.cmd
index ba2838c0c..9188b2f86 100644
--- a/src/get_testwinrt.cmd
+++ b/src/get_testwinrt.cmd
@@ -14,7 +14,7 @@ git checkout -f master
if ErrorLevel 1 popd & exit /b !ErrorLevel!
git fetch -f
if ErrorLevel 1 popd & exit /b !ErrorLevel!
-git reset -q --hard 45c6a357c0293d202a1c090e18d24ce42833fd23
+git reset -q --hard e7682136641caf9713268761ec8188ca452e85f9
if ErrorLevel 1 popd & exit /b !ErrorLevel!
echo Restoring Nuget
%this_dir%.nuget\nuget.exe restore