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