From 0a22529b1ba9c4866c902de5c4bbc66f5a1ac6df Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 29 Aug 2020 00:01:52 -0700 Subject: [PATCH] Adding WinMD generator (#383) * Adding WinMD generator * Authoring pr feedback. * Remove workaround --- .../AuthoringSample/AuthoringSample.csproj | 18 + Authoring/AuthoringSample/Program.cs | 209 ++ Authoring/WinRT.SourceGenerator/Generator.cs | 98 + Authoring/WinRT.SourceGenerator/Logger.cs | 30 + .../WinRT.SourceGenerator.csproj | 13 + .../WinRT.SourceGenerator/WinRTTypeWriter.cs | 1787 +++++++++++++++++ cswinrt.sln | 44 + 7 files changed, 2199 insertions(+) create mode 100644 Authoring/AuthoringSample/AuthoringSample.csproj create mode 100644 Authoring/AuthoringSample/Program.cs create mode 100644 Authoring/WinRT.SourceGenerator/Generator.cs create mode 100644 Authoring/WinRT.SourceGenerator/Logger.cs create mode 100644 Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.csproj create mode 100644 Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs diff --git a/Authoring/AuthoringSample/AuthoringSample.csproj b/Authoring/AuthoringSample/AuthoringSample.csproj new file mode 100644 index 000000000..384f9ba86 --- /dev/null +++ b/Authoring/AuthoringSample/AuthoringSample.csproj @@ -0,0 +1,18 @@ + + + + net5.0 + preview + 1.0.0.0 + true + + + + + + + + + + + diff --git a/Authoring/AuthoringSample/Program.cs b/Authoring/AuthoringSample/Program.cs new file mode 100644 index 000000000..7b95d3f1b --- /dev/null +++ b/Authoring/AuthoringSample/Program.cs @@ -0,0 +1,209 @@ +using System; +using System.Reflection; +using Windows.Foundation.Metadata; + +namespace MyTypes +{ + public delegate void ExampleDelegate(UInt32 value); + public delegate int ExampleDelegateDouble(double newvalue); + + public class ExampleClass + { + public ExampleClass() + { + } + + public event ExampleDelegate SampleEvent; + + public ExampleClass(ExampleClass other) + { + other.MethodA(); + } + + public ExampleClass(int alpha) + { + MethodB(alpha, alpha); + } + + public void MethodA() + { + System.Console.WriteLine("Yay!"); + } + + public int MethodB(int param1, double param2) + { + return 4; + } + + public ExampleClass Get() + { + return null; + } + + private static void HelperMethod(ExampleClass instance) + { + instance.MethodA(); + } + + public double ExampleDouble { get; set; } + } + + [Version(3)] + public interface ITest2 + { + [Version(5)] + double GetTest(int test); + int GetTest2(); + double GetSetDouble { get; set; } + } + + internal enum PrivateEnum + { + privatetest, + privatetest2 + } + + public enum TestEnum + { + test, + test2 + } + + public enum NumericEnum : int + { + five = -5, + six = 6 + } + + public enum UnsignedEnum : uint + { + zero, + one, + two, + three + } + + public struct MyStruct + { + public int test, test3; + public double test2; + } + + public class Test : ITest2 + { + public int GetSetInt { get; set; } + public double GetSetDouble { get; set; } + public int GetInt { get; } + + public double GetTest(int test) + { + return test; + } + + public int GetTest2() + { + return 4; + } + } + + public class ExampleClass2 : ExampleClass + { + public void Delta() + { + } + + public int Method() + { + return 4; + } + + public int Method2(int param1, double param2) + { + return 4; + } + + public void Pen() + { + } + + public int Method3(double p1, double param2, int param3) + { + return 4; + } + + public double Method4(double m4, double m5, int m6, int m7) + { + return 4; + } + } + + public class Test3 : Test + { + + } + + + public class Test8 : Windows.Foundation.IWwwFormUrlDecoderEntry + { + public string Name => throw new NotImplementedException(); + + public string Value => throw new NotImplementedException(); + } + + public class NewTest4 : ITest4 + { + public double GetSetDouble { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public event ExampleDelegate TestEvent; + + public double GetTest(int test) + { + throw new NotImplementedException(); + } + + public int GetTest2() + { + throw new NotImplementedException(); + } + + public int GetTest4() + { + throw new NotImplementedException(); + } + } + + public interface ITest4 : ITest2 + { + int GetTest4(); + + public event ExampleDelegate TestEvent; + } + + public class TestCustom + { + public TestCustom(int num) + { + _num = num; + } + + public int GetNum() + { + return _num; + } + + private int _num; + } + + internal class SomeMoreCode + { + public static void Stuff() + { + const ExampleClass instance1 = null; + } + + ExampleClass MyProp2 + { + get { return null; } + } + } +} diff --git a/Authoring/WinRT.SourceGenerator/Generator.cs b/Authoring/WinRT.SourceGenerator/Generator.cs new file mode 100644 index 000000000..14723a291 --- /dev/null +++ b/Authoring/WinRT.SourceGenerator/Generator.cs @@ -0,0 +1,98 @@ +using Microsoft.CodeAnalysis; +using System; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; + +namespace Generator +{ + [Generator] + public class SourceGenerator : ISourceGenerator + { + private string GetAssemblyName(SourceGeneratorContext context) + { + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.AssemblyName", out var assemblyName); + return assemblyName; + } + + private string GetAssemblyVersion(SourceGeneratorContext context) + { + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.AssemblyVersion", out var assemblyVersion); + return assemblyVersion; + } + + public static string GetGeneratedFilesDir(SourceGeneratorContext context) + { + // TODO: determine correct location to write to. + string winmdDir = Path.Combine(Directory.GetCurrentDirectory(), "Generated Files"); + Directory.CreateDirectory(winmdDir); + return winmdDir; + } + + private string GetWinmdOutputFile(SourceGeneratorContext context) + { + return Path.Combine(GetGeneratedFilesDir(context), GetAssemblyName(context) + ".winmd"); + } + + private void GenerateWinMD(MetadataBuilder metadataBuilder, string outputFile) + { + Logger.Log("Writing " + outputFile); + var managedPeBuilder = new ManagedPEBuilder( + new PEHeaderBuilder( + machine: Machine.I386, + imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll | Characteristics.Bit32Machine), + new MetadataRootBuilder(metadataBuilder, "WindowsRuntime 1.4"), + new BlobBuilder(), + flags: CorFlags.ILOnly); + + var peBlob = new BlobBuilder(); + managedPeBuilder.Serialize(peBlob); + + using var fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write); + peBlob.WriteContentTo(fs); + } + + public void Execute(SourceGeneratorContext context) + { + Logger.Initialize(context); + + try + { + string assembly = GetAssemblyName(context); + string version = GetAssemblyVersion(context); + MetadataBuilder metadataBuilder = new MetadataBuilder(); + + var writer = new WinRTTypeWriter( + assembly, + version, + metadataBuilder); + foreach (SyntaxTree tree in context.Compilation.SyntaxTrees) + { + writer.Model = context.Compilation.GetSemanticModel(tree); + writer.Visit(tree.GetRoot()); + } + writer.FinalizeGeneration(); + + GenerateWinMD(metadataBuilder, GetWinmdOutputFile(context)); + } + catch(Exception e) + { + Logger.Log(e.ToString()); + if(e.InnerException != null) + { + Logger.Log(e.InnerException.ToString()); + } + Logger.Close(); + throw; + } + + Logger.Log("Done"); + Logger.Close(); + } + + public void Initialize(InitializationContext context) + { + } + } +} diff --git a/Authoring/WinRT.SourceGenerator/Logger.cs b/Authoring/WinRT.SourceGenerator/Logger.cs new file mode 100644 index 000000000..9dc726af7 --- /dev/null +++ b/Authoring/WinRT.SourceGenerator/Logger.cs @@ -0,0 +1,30 @@ +using Microsoft.CodeAnalysis; +using System.IO; + +namespace Generator +{ + class Logger + { + public static void Initialize(SourceGeneratorContext context) + { + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTEnableLogging", out var enableLogging); + if(enableLogging != null && bool.Parse(enableLogging)) + { + string logFile = Path.Combine(SourceGenerator.GetGeneratedFilesDir(context), "log.txt"); + fileLogger = File.CreateText(logFile); + } + } + + public static void Log(string text) + { + fileLogger?.WriteLine(text); + } + + public static void Close() + { + fileLogger?.Close(); + } + + private static TextWriter fileLogger; + } +} diff --git a/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.csproj b/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.csproj new file mode 100644 index 000000000..e3f170271 --- /dev/null +++ b/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + preview + + + + + + + + diff --git a/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs b/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs new file mode 100644 index 000000000..7c503db9b --- /dev/null +++ b/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs @@ -0,0 +1,1787 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +namespace Generator +{ + class Parameter + { + public Symbol Type; + public string Name; + public ParameterAttributes Attributes; + + public Parameter(Symbol type, string name, ParameterAttributes attributes) + { + Type = type; + Name = name; + Attributes = attributes; + } + + public Parameter(ITypeSymbol type, string name, ParameterAttributes attributes) + :this(new Symbol(type), name, attributes) + { + } + + public Parameter(EntityHandle type, string name, ParameterAttributes attributes) + : this(new Symbol(type), name, attributes) + { + } + + public Parameter(ParameterSyntax parameter, ParameterAttributes attributes, SemanticModel model) + { + Type = new Symbol(model.GetDeclaredSymbol(parameter).Type); + Name = parameter.Identifier.ValueText; + Attributes = attributes; + } + } + + class Symbol + { + public ITypeSymbol Type; + public EntityHandle Handle; + + public Symbol(ITypeSymbol type) + { + Type = type; + Handle = default; + } + + public Symbol(EntityHandle handle) + { + Type = default; + Handle = handle; + } + + public bool IsHandle() + { + return Handle != default; + } + } + + class TypeDeclaration + { + public readonly ISymbol Node; + public TypeDefinitionHandle Handle; + public string DefaultInterface; + + public Dictionary> MethodDefinitions = new Dictionary>(); + public Dictionary> MethodReferences = new Dictionary>(); + public Dictionary FieldDefinitions = new Dictionary(); + public Dictionary PropertyDefinitions = new Dictionary(); + public Dictionary EventDefinitions = new Dictionary(); + + public TypeDeclaration(ISymbol node) + { + Node = node; + } + + public override string ToString() + { + return Node.ToString(); + } + + public void AddMethod(ISymbol node, MethodDefinitionHandle handle) + { + if(!MethodDefinitions.ContainsKey(node)) + { + MethodDefinitions[node] = new List(); + MethodReferences[node] = new List(); + } + + MethodDefinitions[node].Add(handle); + MethodReferences[node].Add(handle); + } + + public void AddMethodReference(ISymbol node, MemberReferenceHandle handle) + { + if (!MethodReferences.ContainsKey(node)) + { + MethodReferences[node] = new List(); + } + + MethodReferences[node].Add(handle); + } + + public List GetMethodDefinitions() + { + return MethodDefinitions.Values.SelectMany(list => list).ToList(); + } + + public List GetMethodReferences() + { + return MethodReferences.Values.SelectMany(list => list).ToList(); + } + + public void AddField(ISymbol node, FieldDefinitionHandle handle) + { + FieldDefinitions[node] = handle; + } + + public List GetFieldDefinitions() + { + return FieldDefinitions.Values.ToList(); + } + + public void AddProperty(ISymbol node, PropertyDefinitionHandle handle) + { + PropertyDefinitions[node] = handle; + } + + public List GetPropertyDefinitions() + { + return PropertyDefinitions.Values.ToList(); + } + + public void AddEvent(ISymbol node, EventDefinitionHandle handle) + { + EventDefinitions[node] = handle; + } + + public List GetEventDefinitions() + { + return EventDefinitions.Values.ToList(); + } + } + + class WinRTTypeWriter : CSharpSyntaxWalker + { + public SemanticModel Model; + + private readonly string assembly; + private readonly string version; + + private readonly Stack namespaces = new Stack(); + private readonly Dictionary typeReferenceMapping; + private readonly Dictionary assemblyReferenceMapping; + private readonly List nodesWithAttributes; + private readonly MetadataBuilder metadataBuilder; + private bool hasConstructor; + + private readonly Dictionary typeDefinitionMapping; + private TypeDeclaration currentTypeDeclaration; + + public WinRTTypeWriter( + string assembly, + string version, + MetadataBuilder metadataBuilder) + { + this.assembly = assembly; + this.version = version; + this.metadataBuilder = metadataBuilder; + typeReferenceMapping = new Dictionary(); + assemblyReferenceMapping = new Dictionary(); + typeDefinitionMapping = new Dictionary(); + nodesWithAttributes = new List(); + + CreteAssembly(); + } + + private void CreteAssembly() + { + Logger.Log("Generating assembly " + assembly + " version " + version); + metadataBuilder.AddAssembly( + metadataBuilder.GetOrAddString(assembly), + new Version(version), + default, + default, + AssemblyFlags.WindowsRuntime, + AssemblyHashAlgorithm.Sha1); + + var moduleDefinition = metadataBuilder.AddModule( + 0, + metadataBuilder.GetOrAddString(assembly + ".winmd"), + metadataBuilder.GetOrAddGuid(Guid.NewGuid()), + default, + default); + assemblyReferenceMapping[assembly] = moduleDefinition; + + metadataBuilder.AddTypeDefinition( + default, + default, + metadataBuilder.GetOrAddString(""), + default, + MetadataTokens.FieldDefinitionHandle(1), + MetadataTokens.MethodDefinitionHandle(1)); + } + + private void EncodeSpecialType(SpecialType specialType, SignatureTypeEncoder typeEncoder) + { + switch (specialType) + { + case SpecialType.System_Boolean: + typeEncoder.Boolean(); + break; + case SpecialType.System_Byte: + typeEncoder.Byte(); + break; + case SpecialType.System_Int16: + typeEncoder.Int16(); + break; + case SpecialType.System_Int32: + typeEncoder.Int32(); + break; + case SpecialType.System_Int64: + typeEncoder.Int64(); + break; + case SpecialType.System_UInt16: + typeEncoder.UInt16(); + break; + case SpecialType.System_UInt32: + typeEncoder.UInt32(); + break; + case SpecialType.System_UInt64: + typeEncoder.UInt64(); + break; + case SpecialType.System_Single: + typeEncoder.Single(); + break; + case SpecialType.System_Double: + typeEncoder.Double(); + break; + case SpecialType.System_Char: + typeEncoder.Char(); + break; + case SpecialType.System_String: + typeEncoder.String(); + break; + case SpecialType.System_Object: + typeEncoder.Object(); + break; + case SpecialType.System_IntPtr: + typeEncoder.IntPtr(); + break; + default: + Logger.Log("TODO special type: " + specialType); + break; + } + + // TODO: handle C# interface mappings for special types + } + + private BlobHandle GetStrongNameKey(string assembly) + { + if(assembly == "mscorlib") + { + byte[] mscorlibStrongName = { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }; + return metadataBuilder.GetOrAddBlob(mscorlibStrongName); + } + + return default; + } + + private EntityHandle GetTypeReference(string @namespace, string name, string assembly) + { + string fullname = QualifiedName(@namespace, name); + if (typeReferenceMapping.ContainsKey(fullname)) + { + return typeReferenceMapping[fullname]; + } + + if (!assemblyReferenceMapping.ContainsKey(assembly)) + { + EntityHandle assemblyReference = metadataBuilder.AddAssemblyReference( + metadataBuilder.GetOrAddString(assembly), + new Version(0xff, 0xff, 0xff, 0xff), + default, + GetStrongNameKey(assembly), + AssemblyFlags.ContentTypeMask, + default); + assemblyReferenceMapping[assembly] = assemblyReference; + } + + var typeRef = metadataBuilder.AddTypeReference( + assemblyReferenceMapping[assembly], + metadataBuilder.GetOrAddString(@namespace), + metadataBuilder.GetOrAddString(name)); + typeReferenceMapping[fullname] = typeRef; + return typeRef; + } + + private EntityHandle GetTypeReference(ISymbol symbol) + { + string @namespace = symbol.ContainingNamespace.ToString(); + string name = symbol.Name; + + string assembly; + var winrtTypeAttribute = symbol.GetAttributes(). + Where(attribute => attribute.AttributeClass.Name == "WindowsRuntimeTypeAttribute"); + if (winrtTypeAttribute.Any()) + { + assembly = (string) winrtTypeAttribute.First().ConstructorArguments[0].Value; + } + else + { + assembly = symbol.ContainingAssembly.Name; + } + + return GetTypeReference(@namespace, name, assembly); + } + + private void EncodeSymbol(Symbol symbol, SignatureTypeEncoder typeEncoder) + { + EntityHandle typeReference = symbol.IsHandle() ? symbol.Handle : GetTypeReference(symbol.Type); + typeEncoder.Type(typeReference, false); + } + + private void EncodeReturnType(Symbol symbol, ReturnTypeEncoder returnTypeEncoder) + { + if(symbol == null) + { + returnTypeEncoder.Void(); + } + else if(symbol.IsHandle() || symbol.Type.SpecialType == SpecialType.None) + { + EncodeSymbol(symbol, returnTypeEncoder.Type()); + } + else if(symbol.Type.SpecialType == SpecialType.System_Void) + { + returnTypeEncoder.Void(); + } + else + { + EncodeSpecialType(symbol.Type.SpecialType, returnTypeEncoder.Type()); + } + } + + private void EncodeParameters(Parameter[] parameters, ParametersEncoder parametersEncoder) + { + foreach (var parameter in parameters) + { + var parameterType = parameter.Type; + var parameterTypeEncoder = parametersEncoder.AddParameter(); + + if (!parameterType.IsHandle() && parameterType.Type.SpecialType != SpecialType.None) + { + EncodeSpecialType(parameterType.Type.SpecialType, parameterTypeEncoder.Type()); + } + else + { + EncodeSymbol(parameterType, parameterTypeEncoder.Type()); + } + } + } + + public MethodDefinitionHandle AddMethodDefinition( + string name, + Parameter[] parameters, + Symbol returnSymbol, + bool isStatic, + bool isInterfaceParent, + bool isSpecialMethod = false, + bool isPublic = true, + bool isOverridable = false) + { + var methodSignature = new BlobBuilder(); + new BlobEncoder(methodSignature) + .MethodSignature( + SignatureCallingConvention.Default, + parameters.Length, + !isStatic) + .Parameters( + parameters.Length, + returnType => EncodeReturnType(returnSymbol, returnType), + parametersEncoder => EncodeParameters(parameters, parametersEncoder) + ); + + List parameterHandles = new List(); + for (int idx = 0; idx < parameters.Length; idx++) + { + parameterHandles.Add(metadataBuilder.AddParameter( + parameters[idx].Attributes, + metadataBuilder.GetOrAddString(parameters[idx].Name), + idx + 1)); + } + + var methodAttributes = + (isPublic ? MethodAttributes.Public : MethodAttributes.Private) | + MethodAttributes.HideBySig; + + var methodImplAttributes = MethodImplAttributes.Managed; + + if (isInterfaceParent) + { + methodAttributes |= MethodAttributes.Abstract; + } + else + { + // TODO check if from overridable interface + if (!isOverridable) + { + methodAttributes |= MethodAttributes.Final; + } + methodImplAttributes |= MethodImplAttributes.Runtime; + } + + if (isSpecialMethod && name == ".ctor") + { + methodAttributes |= MethodAttributes.RTSpecialName; + } + else + { + methodAttributes |= + MethodAttributes.Virtual | + MethodAttributes.NewSlot; + } + + if (isSpecialMethod) + { + methodAttributes |= MethodAttributes.SpecialName; + } + + var methodDefinitionHandle = metadataBuilder.AddMethodDefinition( + methodAttributes, + methodImplAttributes, + metadataBuilder.GetOrAddString(name), + metadataBuilder.GetOrAddBlob(methodSignature), + -1, + parameterHandles.Count == 0 ? + MetadataTokens.ParameterHandle(metadataBuilder.GetRowCount(TableIndex.Param) + 1) : + parameterHandles[0]); + return methodDefinitionHandle; + } + + public override void VisitMethodDeclaration(MethodDeclarationSyntax node) + { + Logger.Log("method: " + node.Identifier.ValueText); + if(!IsPublicNode(node)) + { + return; + } + + base.VisitMethodDeclaration(node); + + int numParameters = node.ParameterList.Parameters.Count; + Parameter[] parameters = new Parameter[numParameters]; + for(int idx = 0; idx < numParameters; idx++) + { + parameters[idx] = new Parameter(node.ParameterList.Parameters[idx], ParameterAttributes.In, Model); + } + + var methodSymbol = Model.GetDeclaredSymbol(node); + var methodDefinitionHandle = AddMethodDefinition( + node.Identifier.ValueText, + parameters, + new Symbol(methodSymbol.ReturnType), + !IsStaticNode(node), + node.Parent is InterfaceDeclarationSyntax); + currentTypeDeclaration.AddMethod(methodSymbol, methodDefinitionHandle); + } + + public override void VisitFieldDeclaration(FieldDeclarationSyntax node) + { + if (!IsPublicNode(node) || node.Parent is not StructDeclarationSyntax) + { + return; + } + + base.VisitFieldDeclaration(node); + + var symbol = Model.GetTypeInfo(node.Declaration.Type).Type; + var fieldSignature = new BlobBuilder(); + var encoder = new BlobEncoder(fieldSignature); + + if (symbol.SpecialType != SpecialType.None) + { + EncodeSpecialType(symbol.SpecialType, encoder.FieldSignature()); + } + else + { + EncodeSymbol(new Symbol(symbol), encoder.FieldSignature()); + } + + foreach (var variable in node.Declaration.Variables) + { + var fieldSymbol = Model.GetDeclaredSymbol(variable); + var fieldDefinitionHandle = metadataBuilder.AddFieldDefinition( + FieldAttributes.Public, + metadataBuilder.GetOrAddString(variable.Identifier.Text), + metadataBuilder.GetOrAddBlob(fieldSignature)); + currentTypeDeclaration.AddField(fieldSymbol, fieldDefinitionHandle); + } + } + + public void AddPropertyDeclaration(IPropertySymbol property, bool isInterfaceParent) + { + Logger.Log("defining property " + property.Name); + Logger.Log("parent: " + property.ContainingType.Name); + + var propertySignature = new BlobBuilder(); + new BlobEncoder(propertySignature) + .PropertySignature(true) + .Parameters( + 0, + returnType => EncodeReturnType(new Symbol(property.Type), returnType), + parameters => { } + ); + + var propertyDefinitonHandle = metadataBuilder.AddProperty( + PropertyAttributes.None, + metadataBuilder.GetOrAddString(property.Name), + metadataBuilder.GetOrAddBlob(propertySignature)); + currentTypeDeclaration.AddProperty(property, propertyDefinitonHandle); + + if (property.SetMethod != null) + { + var setMethod = AddMethodDefinition( + "put_" + property.Name, + new Parameter[] { new Parameter(property.Type, "value", ParameterAttributes.In) }, + null, + false, + isInterfaceParent, + true); + currentTypeDeclaration.AddMethod(property, setMethod); + + metadataBuilder.AddMethodSemantics( + propertyDefinitonHandle, + MethodSemanticsAttributes.Setter, + setMethod); + } + + var getMethod = AddMethodDefinition( + "get_" + property.Name, + new Parameter[0], + new Symbol(property.Type), + false, + isInterfaceParent, + true); + currentTypeDeclaration.AddMethod(property, getMethod); + + metadataBuilder.AddMethodSemantics( + propertyDefinitonHandle, + MethodSemanticsAttributes.Getter, + getMethod); + } + + public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) + { + base.VisitPropertyDeclaration(node); + + var symbol = Model.GetDeclaredSymbol(node); + + var propertySignature = new BlobBuilder(); + new BlobEncoder(propertySignature) + .PropertySignature(true) + .Parameters( + 0, + returnType => EncodeReturnType(new Symbol(symbol.Type), returnType), + parameters => { } + ); + + var propertyDefinitonHandle = metadataBuilder.AddProperty( + PropertyAttributes.None, + metadataBuilder.GetOrAddString(node.Identifier.ValueText), + metadataBuilder.GetOrAddBlob(propertySignature)); + currentTypeDeclaration.AddProperty(symbol, propertyDefinitonHandle); + + if (symbol.SetMethod != null) + { + var setMethod = AddMethodDefinition( + "put_" + symbol.Name, + new Parameter[] { new Parameter(symbol.Type, "value", ParameterAttributes.In ) }, + null, + false, + node.Parent is InterfaceDeclarationSyntax, + true); + currentTypeDeclaration.AddMethod(symbol, setMethod); + + metadataBuilder.AddMethodSemantics( + propertyDefinitonHandle, + MethodSemanticsAttributes.Setter, + setMethod); + } + + var getMethod = AddMethodDefinition( + "get_" + symbol.Name, + new Parameter[0], + new Symbol(symbol.Type), + false, + node.Parent is InterfaceDeclarationSyntax, + true); + currentTypeDeclaration.AddMethod(symbol, getMethod); + + metadataBuilder.AddMethodSemantics( + propertyDefinitonHandle, + MethodSemanticsAttributes.Getter, + getMethod); + } + + private TypeDefinitionHandle AddTypeDefinition( + TypeAttributes typeAttributes, + string @namespace, + string identifier, + EntityHandle baseType) + { + var fieldDefinitions = currentTypeDeclaration.GetFieldDefinitions(); + var methodDefinitions = currentTypeDeclaration.GetMethodDefinitions(); + + var typeDefinitionHandle = metadataBuilder.AddTypeDefinition( + typeAttributes, + metadataBuilder.GetOrAddString(@namespace), + metadataBuilder.GetOrAddString(identifier), + baseType, + fieldDefinitions.Count == 0 ? + MetadataTokens.FieldDefinitionHandle(metadataBuilder.GetRowCount(TableIndex.Field) + 1) : + fieldDefinitions[0], + methodDefinitions.Count == 0 ? + MetadataTokens.MethodDefinitionHandle(metadataBuilder.GetRowCount(TableIndex.MethodDef) + 1) : + methodDefinitions[0] + ); + + var propertyDefinitions = currentTypeDeclaration.GetPropertyDefinitions(); + if (propertyDefinitions.Count != 0) + { + metadataBuilder.AddPropertyMap( + typeDefinitionHandle, + propertyDefinitions[0]); + } + + var eventDefinitions = currentTypeDeclaration.GetEventDefinitions(); + if (eventDefinitions.Count != 0) + { + metadataBuilder.AddEventMap( + typeDefinitionHandle, + eventDefinitions[0]); + } + + return typeDefinitionHandle; + } + + private void ProcessTypeDeclaration(BaseTypeDeclarationSyntax node, Action visitTypeDeclaration) + { + if(!IsPublicNode(node)) + { + return; + } + + var symbol = Model.GetDeclaredSymbol(node); + currentTypeDeclaration = new TypeDeclaration(symbol); + + visitTypeDeclaration(); + + TypeAttributes typeAttributes = + TypeAttributes.Public | + TypeAttributes.WindowsRuntime | + TypeAttributes.AutoLayout | + TypeAttributes.AnsiClass; + + if (IsSealedNode(node) || + (node is EnumDeclarationSyntax || + node is StructDeclarationSyntax)) + { + typeAttributes |= TypeAttributes.Sealed; + } + + EntityHandle baseType = default; + if(node is InterfaceDeclarationSyntax) + { + typeAttributes |= + TypeAttributes.Interface | + TypeAttributes.Abstract; + } + else if(node is ClassDeclarationSyntax) + { + typeAttributes |= + TypeAttributes.Class | + TypeAttributes.BeforeFieldInit; + + // extens + if (node.BaseList != null) + { + foreach (var type in node.BaseList.Types) + { + var typeSymbol = Model.GetTypeInfo(type.Type).Type; + if(typeSymbol.TypeKind == TypeKind.Class) + { + baseType = GetTypeReference(typeSymbol); + break; + } + } + } + else + { + baseType = GetTypeReference("System", "Object", "mscorlib"); + } + } + else if(node is StructDeclarationSyntax) + { + typeAttributes |= TypeAttributes.SequentialLayout; + baseType = GetTypeReference("System", "ValueType", "mscorlib"); + } + else if(node is EnumDeclarationSyntax) + { + baseType = GetTypeReference("System", "Enum", "mscorlib"); + } + + var typeDefinitionHandle = AddTypeDefinition( + typeAttributes, + GetNamespace(), + node.Identifier.ValueText, + baseType); + currentTypeDeclaration.Handle = typeDefinitionHandle; + + if (node.BaseList != null && (node is InterfaceDeclarationSyntax || node is ClassDeclarationSyntax)) + { + foreach (var implementedInterface in symbol.AllInterfaces. + OrderBy(implementedInterface => implementedInterface.ToString())) + { + metadataBuilder.AddInterfaceImplementation( + typeDefinitionHandle, + GetTypeReference(implementedInterface)); + } + } + + typeDefinitionMapping[QualifiedName(node.Identifier.ValueText)] = currentTypeDeclaration; + } + + public override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) + { + ProcessTypeDeclaration(node, () => base.VisitInterfaceDeclaration(node)); + } + + public override void VisitClassDeclaration(ClassDeclarationSyntax node) + { + void processClassDeclaration() + { + hasConstructor = false; + base.VisitClassDeclaration(node); + + // implicit constructor if none defined + if(!hasConstructor) + { + var methodDefinitionHandle = AddMethodDefinition( + ".ctor", + new Parameter[0], + null, + false, + false, + true, + true, + true); + var symbol = Model.GetDeclaredSymbol(node); + currentTypeDeclaration.AddMethod(symbol, methodDefinitionHandle); + } + } + + ProcessTypeDeclaration(node, processClassDeclaration); + + if (IsPublicNode(node)) + { + AddSynthesizedInterfaces(currentTypeDeclaration); + } + } + + public override void VisitStructDeclaration(StructDeclarationSyntax node) + { + ProcessTypeDeclaration(node, () => base.VisitStructDeclaration(node)); + } + + private void EncodeTypedConstant(TypedConstant constant, LiteralEncoder encoder) + { + Logger.Log("typed constant kind: " + constant.Kind); + Logger.Log("typed constant type: " + constant.Type); + Logger.Log("typed constant value: " + constant.Value); + + switch (constant.Kind) + { + case TypedConstantKind.Primitive: + encoder.Scalar().Constant(constant.Value); + break; + case TypedConstantKind.Enum: + encoder.TaggedScalar( + type => type.Enum(constant.Type.ToString()), + scalar => scalar.Constant(constant.Value) + ); + break; + case TypedConstantKind.Type: + encoder.Scalar().SystemType(constant.Type.ToString()); + break; + case TypedConstantKind.Array: + { + LiteralsEncoder arrayEncoder = encoder.Vector().Count(constant.Values.Length); + foreach(var arrayConstant in constant.Values) + { + EncodeTypedConstant(arrayConstant, arrayEncoder.AddLiteral()); + } + break; + } + } + + } + + private void EncodeFixedArguments(IList arguments, FixedArgumentsEncoder argumentsEncoder) + { + foreach(var argument in arguments) + { + EncodeTypedConstant(argument, argumentsEncoder.AddArgument()); + } + } + + private void EncodeCustomElementType(IFieldSymbol field, CustomAttributeElementTypeEncoder typeEncoder) + { + switch (field.Type.SpecialType) + { + case SpecialType.System_Boolean: + typeEncoder.Boolean(); + break; + case SpecialType.System_Byte: + typeEncoder.Byte(); + break; + case SpecialType.System_Int16: + typeEncoder.Int16(); + break; + case SpecialType.System_Int32: + typeEncoder.Int32(); + break; + case SpecialType.System_Int64: + typeEncoder.Int64(); + break; + case SpecialType.System_UInt16: + typeEncoder.UInt16(); + break; + case SpecialType.System_UInt32: + typeEncoder.UInt32(); + break; + case SpecialType.System_UInt64: + typeEncoder.UInt64(); + break; + case SpecialType.System_Single: + typeEncoder.Single(); + break; + case SpecialType.System_Double: + typeEncoder.Double(); + break; + case SpecialType.System_Char: + typeEncoder.Char(); + break; + case SpecialType.System_String: + typeEncoder.String(); + break; + case SpecialType.System_Enum: + typeEncoder.Enum(field.Type.ToString()); + break; + case SpecialType.System_SByte: + typeEncoder.SByte(); + break; + default: + Logger.Log("TODO special type: " + field.Type.SpecialType); + break; + } + } + + private void EncodeNamedArgumentType(INamedTypeSymbol attributeType, string field, NamedArgumentTypeEncoder encoder) + { + Logger.Log("encoding named type"); + var fieldMembers = attributeType.GetMembers(field); + Logger.Log("# fields: " + fieldMembers.Count()); + var fieldSymbol = fieldMembers.First() as IFieldSymbol; + Logger.Log("found field: " + fieldSymbol); + + if(fieldSymbol.Type.SpecialType == SpecialType.System_Object) + { + encoder.Object(); + } + else if(fieldSymbol.Type.SpecialType == SpecialType.System_Array) + { + // TODO array type encoder + encoder.SZArray(); + } + else + { + EncodeCustomElementType(fieldSymbol, encoder.ScalarType()); + } + } + + private void EncodeNamedArguments(INamedTypeSymbol attributeType, IList> namedArguments, CustomAttributeNamedArgumentsEncoder argumentsEncoder) + { + var encoder = argumentsEncoder.Count(namedArguments.Count); + foreach (var argument in namedArguments) + { + Logger.Log("named argument: " + argument.Key); + Logger.Log("value " + argument.Value); + + encoder.AddArgument( + true, + type => EncodeNamedArgumentType(attributeType, argument.Key, type), + name => name.Name(argument.Key), + literal => EncodeTypedConstant(argument.Value, literal) + ); + } + } + + private void EncodeFixedArguments(IList primitiveArguments, FixedArgumentsEncoder argumentsEncoder) + { + foreach (var argument in primitiveArguments) + { + argumentsEncoder.AddArgument().Scalar().Constant(argument); + } + } + + public void AddDefaultVersionAttribute(EntityHandle parentHandle, int version = -1) + { + if(version == -1) + { + version = Version.Parse(this.version).Major; + } + + List arguments = new List + { + version + }; + + AddCustomAttributes("Windows.Foundation.Metadata.VersionAttribute", arguments, parentHandle); + } + + private void AddCustomAttributes(string attributeTypeName, IList primitiveValues, EntityHandle parentHandle) + { + var attributeType = Model.Compilation.GetTypeByMetadataName(attributeTypeName); + Logger.Log("attribute type found " + attributeType); + if (!typeDefinitionMapping.ContainsKey(attributeTypeName)) + { + Logger.Log("adding attribute type"); + AddType(attributeType); + } + + Logger.Log("# constructor found: " + attributeType.Constructors.Length); + var matchingConstructor = attributeType.Constructors.Where(constructor => constructor.Parameters.Length == primitiveValues.Count); + + Logger.Log("# matching constructor found: " + matchingConstructor.Count()); + Logger.Log("matching constructor found: " + matchingConstructor.First()); + + var constructorReference = typeDefinitionMapping[attributeTypeName].MethodReferences[matchingConstructor.First()]; + Logger.Log("found constructor handle: " + constructorReference.Count); + + var attributeSignature = new BlobBuilder(); + new BlobEncoder(attributeSignature) + .CustomAttributeSignature( + fixedArguments => EncodeFixedArguments(primitiveValues, fixedArguments), + namedArguments => { } + ); + + metadataBuilder.AddCustomAttribute( + parentHandle, + constructorReference.First(), + metadataBuilder.GetOrAddBlob(attributeSignature)); + } + + private void AddCustomAttributes(IList attributes, EntityHandle parentHandle) + { + foreach (var attribute in attributes) + { + var attributeType = attribute.AttributeClass; + + Logger.Log("attribute: " + attribute); + Logger.Log("attribute type: " + attributeType); + Logger.Log("attribute constructor: " + attribute.AttributeConstructor); + Logger.Log("atttribute # constructor arguments: " + attribute.ConstructorArguments.Length); + Logger.Log("atttribute # named arguments: " + attribute.NamedArguments.Length); + + if (!typeDefinitionMapping.ContainsKey(attributeType.ToString())) + { + AddType(attributeType); + } + + var constructorReference = typeDefinitionMapping[attributeType.ToString()].MethodReferences[attribute.AttributeConstructor]; + Logger.Log("found # constructors: " + constructorReference.Count); + + var attributeSignature = new BlobBuilder(); + new BlobEncoder(attributeSignature) + .CustomAttributeSignature( + fixedArguments => EncodeFixedArguments(attribute.ConstructorArguments, fixedArguments), + namedArguments => EncodeNamedArguments(attributeType, attribute.NamedArguments, namedArguments) + ); + + metadataBuilder.AddCustomAttribute( + parentHandle, + constructorReference.First(), + metadataBuilder.GetOrAddBlob(attributeSignature)); + } + } + + public override void VisitAttributeList(AttributeListSyntax node) + { + base.VisitAttributeList(node); + + // Skip assembly attributes + if(node.Target != null && node.Target.Identifier.ValueText == "assembly") + { + return; + } + + var parentSymbol = Model.GetDeclaredSymbol(node.Parent); + nodesWithAttributes.Add(parentSymbol); + } + + public override void VisitEnumDeclaration(EnumDeclarationSyntax node) + { + void processEnumDeclaration() + { + base.VisitEnumDeclaration(node); + + var enumTypeFieldAttributes = + FieldAttributes.Private | + FieldAttributes.SpecialName | + FieldAttributes.RTSpecialName; + + var symbol = Model.GetDeclaredSymbol(node); + var enumTypeSymbol = symbol.EnumUnderlyingType; + var fieldSignature = new BlobBuilder(); + var encoder = new BlobEncoder(fieldSignature); + // TODO: special type enforcement for 64 bit + EncodeSpecialType(enumTypeSymbol.SpecialType, encoder.FieldSignature()); + + var fieldDefinitionHandle = metadataBuilder.AddFieldDefinition( + enumTypeFieldAttributes, + metadataBuilder.GetOrAddString("value__"), + metadataBuilder.GetOrAddBlob(fieldSignature)); + currentTypeDeclaration.AddField(symbol, fieldDefinitionHandle); + } + + ProcessTypeDeclaration(node, processEnumDeclaration); + + // TODO:System.flags attribute + } + + public override void VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) + { + base.VisitEnumMemberDeclaration(node); + + var enumFieldAttributes = + FieldAttributes.Public | + FieldAttributes.Static | + FieldAttributes.Literal | + FieldAttributes.HasDefault; + + var symbol = Model.GetDeclaredSymbol(node); + var fieldSignature = new BlobBuilder(); + var encoder = new BlobEncoder(fieldSignature); + EncodeSymbol(new Symbol(symbol.ContainingType), encoder.FieldSignature()); + + var fieldDefinitionHandle = metadataBuilder.AddFieldDefinition( + enumFieldAttributes, + metadataBuilder.GetOrAddString(node.Identifier.Text), + metadataBuilder.GetOrAddBlob(fieldSignature)); + currentTypeDeclaration.AddField(symbol, fieldDefinitionHandle); + + if (symbol.HasConstantValue) + { + metadataBuilder.AddConstant(fieldDefinitionHandle, symbol.ConstantValue); + } + } + + public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node) + { + if (!IsPublicNode(node)) + { + return; + } + + var symbol = Model.GetDeclaredSymbol(node); + currentTypeDeclaration = new TypeDeclaration(symbol); + + base.VisitDelegateDeclaration(node); + + var objType = Model.Compilation.GetTypeByMetadataName(typeof(object).FullName); + var nativeIntType = Model.Compilation.GetTypeByMetadataName(typeof(IntPtr).FullName); + + currentTypeDeclaration.AddMethod( + symbol, + AddMethodDefinition( + ".ctor", + new Parameter[] { + new Parameter(objType, "object", ParameterAttributes.None), + new Parameter(nativeIntType, "method", ParameterAttributes.None) + }, + null, + false, + false, + true, + false, + true + ) + ); + + int numParameters = node.ParameterList.Parameters.Count; + Parameter[] parameters = new Parameter[numParameters]; + for (int idx = 0; idx < numParameters; idx++) + { + parameters[idx] = new Parameter(node.ParameterList.Parameters[idx], ParameterAttributes.In, Model); + } + + currentTypeDeclaration.AddMethod( + symbol, + AddMethodDefinition( + "Invoke", + parameters, + new Symbol(symbol.DelegateInvokeMethod.ReturnType), + false, + false, + true, + true, + true + ) + ); + + TypeAttributes typeAttributes = + TypeAttributes.Public | + TypeAttributes.WindowsRuntime | + TypeAttributes.AutoLayout | + TypeAttributes.AnsiClass | + TypeAttributes.Sealed; + + var typeDefinitionHandle = AddTypeDefinition( + typeAttributes, + GetNamespace(), + node.Identifier.ValueText, + GetTypeReference("System", "MulticastDelegate", "mscorlib")); + currentTypeDeclaration.Handle = typeDefinitionHandle; + } + + public void AddEventDeclaration(IEventSymbol @event, bool isInterfaceParent) + { + Logger.Log("defining event " + @event.Name); + + var delegateSymbolType = @event.Type; + EntityHandle typeReferenceHandle = GetTypeReference(delegateSymbolType); + EntityHandle eventRegistrationTokenTypeHandle = GetTypeReference("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract"); + Symbol eventRegistrationToken = new Symbol(eventRegistrationTokenTypeHandle); + + var eventDefinitionHandle = metadataBuilder.AddEvent( + EventAttributes.None, + metadataBuilder.GetOrAddString(@event.Name), + typeReferenceHandle); + currentTypeDeclaration.AddEvent(@event, eventDefinitionHandle); + + var addMethod = AddMethodDefinition( + "add_" + @event.Name, + new Parameter[] { new Parameter(delegateSymbolType, "handler", ParameterAttributes.In) }, + eventRegistrationToken, + false, + isInterfaceParent, + true); + currentTypeDeclaration.AddMethod(@event, addMethod); + + metadataBuilder.AddMethodSemantics( + eventDefinitionHandle, + MethodSemanticsAttributes.Adder, + addMethod); + + var removeMethod = AddMethodDefinition( + "remove_" + @event.Name, + new Parameter[] { new Parameter(eventRegistrationToken, "token", ParameterAttributes.In) }, + null, + false, + isInterfaceParent, + true); + currentTypeDeclaration.AddMethod(@event, removeMethod); + + metadataBuilder.AddMethodSemantics( + eventDefinitionHandle, + MethodSemanticsAttributes.Remover, + removeMethod); + } + + public override void VisitEventFieldDeclaration(EventFieldDeclarationSyntax node) + { + if (!IsPublicNode(node)) + { + return; + } + + base.VisitEventFieldDeclaration(node); + + var delegateSymbolType = Model.GetTypeInfo(node.Declaration.Type).Type; + EntityHandle typeReferenceHandle = GetTypeReference(delegateSymbolType); + EntityHandle eventRegistrationTokenTypeHandle = GetTypeReference("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract"); + Symbol eventRegistrationToken = new Symbol(eventRegistrationTokenTypeHandle); + + foreach (var declaration in node.Declaration.Variables) + { + var eventSymbol = Model.GetDeclaredSymbol(declaration); + var eventDefinitionHandle = metadataBuilder.AddEvent( + EventAttributes.None, + metadataBuilder.GetOrAddString(declaration.Identifier.ValueText), + typeReferenceHandle); + currentTypeDeclaration.AddEvent(eventSymbol, eventDefinitionHandle); + + var addMethod = AddMethodDefinition( + "add_" + declaration.Identifier.ValueText, + new Parameter[] { new Parameter(delegateSymbolType, "handler", ParameterAttributes.In) }, + eventRegistrationToken, + false, + node.Parent is InterfaceDeclarationSyntax, + true); + currentTypeDeclaration.AddMethod(eventSymbol, addMethod); + + metadataBuilder.AddMethodSemantics( + eventDefinitionHandle, + MethodSemanticsAttributes.Adder, + addMethod); + + var removeMethod = AddMethodDefinition( + "remove_" + declaration.Identifier.ValueText, + new Parameter[] { new Parameter(eventRegistrationToken, "token", ParameterAttributes.In) }, + null, + false, + node.Parent is InterfaceDeclarationSyntax, + true); + currentTypeDeclaration.AddMethod(eventSymbol, removeMethod); + + metadataBuilder.AddMethodSemantics( + eventDefinitionHandle, + MethodSemanticsAttributes.Remover, + removeMethod); + } + } + + public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node) + { + hasConstructor = true; + + if (!IsPublicNode(node)) + { + return; + } + + base.VisitConstructorDeclaration(node); + + var symbol = Model.GetDeclaredSymbol(node); + int numParameters = node.ParameterList.Parameters.Count; + Parameter[] parameters = new Parameter[numParameters]; + for (int idx = 0; idx < numParameters; idx++) + { + parameters[idx] = new Parameter(node.ParameterList.Parameters[idx], ParameterAttributes.In, Model); + } + + var methodDefinitionHandle = AddMethodDefinition( + ".ctor", + parameters, + null, + false, + false, + true, + true, + true); + currentTypeDeclaration.AddMethod(symbol, methodDefinitionHandle); + } + + void AddMethodDeclaration(IMethodSymbol method, bool isInterfaceParent) + { + Logger.Log("add method from symbol: " + method.Name); + + int numParameters = method.Parameters.Count(); + Parameter[] parameters = new Parameter[numParameters]; + for (int idx = 0; idx < numParameters; idx++) + { + parameters[idx] = new Parameter(method.Parameters[idx].Type, method.Parameters[idx].Name, ParameterAttributes.In); + } + + string methodName = method.MethodKind == MethodKind.Constructor ? ".ctor" : method.Name; + + var methodDefinitionHandle = AddMethodDefinition( + methodName, + parameters, + new Symbol(method.ReturnType), + !isInterfaceParent && method.IsStatic, + isInterfaceParent, + method.MethodKind == MethodKind.Constructor, + true, + true); + currentTypeDeclaration.AddMethod(method, methodDefinitionHandle); + } + + void AddFactoryMethod(INamedTypeSymbol classSymbol, IMethodSymbol method) + { + Logger.Log("adding factory method: " + method.Name); + + int numParameters = method.Parameters.Count(); + Parameter[] parameters = new Parameter[numParameters]; + for (int idx = 0; idx < numParameters; idx++) + { + parameters[idx] = new Parameter(method.Parameters[idx].Type, method.Parameters[idx].Name, ParameterAttributes.In); + } + + var methodDefinitionHandle = AddMethodDefinition( + "Create" + classSymbol.Name, + parameters, + new Symbol(classSymbol), + false, + true, + false, + true, + true); + currentTypeDeclaration.AddMethod(method, methodDefinitionHandle); + } + + void AddExternalType(INamedTypeSymbol type) + { + // TODO: check if custom projected interface + // TODO: block or warn type names with namespaces not meeting WinRT requirements. + + currentTypeDeclaration = new TypeDeclaration(type); + bool isInterfaceParent = type.TypeKind == TypeKind.Interface; + + foreach (var member in type.GetMembers()) + { + if(member is IMethodSymbol method && + (method.MethodKind == MethodKind.Ordinary || method.MethodKind == MethodKind.Constructor)) + { + AddMethodDeclaration(method, isInterfaceParent); + } + else if(member is IPropertySymbol property) + { + AddPropertyDeclaration(property, isInterfaceParent); + } + else if(member is IEventSymbol @event) + { + AddEventDeclaration(@event, isInterfaceParent); + } + else + { + Logger.Log("member not recognized: " + member.Kind + " name: " + member.Name); + } + } + + TypeAttributes typeAttributes = + TypeAttributes.Public | + TypeAttributes.WindowsRuntime | + TypeAttributes.AutoLayout | + TypeAttributes.AnsiClass | + TypeAttributes.Interface | + TypeAttributes.Abstract; + + var typeDefinitionHandle = AddTypeDefinition( + typeAttributes, + type.ContainingNamespace.ToString(), + type.Name, + default); + currentTypeDeclaration.Handle = typeDefinitionHandle; + + foreach (var implementedInterface in type.AllInterfaces.OrderBy(implementedInterface => implementedInterface.ToString())) + { + metadataBuilder.AddInterfaceImplementation( + typeDefinitionHandle, + GetTypeReference(implementedInterface)); + } + + typeDefinitionMapping[type.ToString()] = currentTypeDeclaration; + } + + MemberReferenceHandle AddMethodReference( + string name, + Parameter[] parameters, + Symbol returnSymbol, + ISymbol parentType, + bool isStatic) + { + var methodSignature = new BlobBuilder(); + new BlobEncoder(methodSignature) + .MethodSignature( + SignatureCallingConvention.Default, + parameters.Length, + !isStatic) + .Parameters( + parameters.Length, + returnType => EncodeReturnType(returnSymbol, returnType), + parametersEncoder => EncodeParameters(parameters, parametersEncoder) + ); + + var referenceHandle = metadataBuilder.AddMemberReference( + GetTypeReference(parentType), + metadataBuilder.GetOrAddString(name), + metadataBuilder.GetOrAddBlob(methodSignature) + ); + return referenceHandle; + } + + MemberReferenceHandle AddMethodReference(IMethodSymbol method) + { + Logger.Log("adding method reference: " + method.Name); + + bool isInterfaceParent = method.ContainingType.TypeKind == TypeKind.Interface; + int numParameters = method.Parameters.Count(); + Parameter[] parameters = new Parameter[numParameters]; + for (int idx = 0; idx < numParameters; idx++) + { + parameters[idx] = new Parameter(method.Parameters[idx].Type, method.Parameters[idx].Name, ParameterAttributes.In); + } + + string methodName = method.MethodKind == MethodKind.Constructor ? ".ctor" : method.Name; + + var referenceHandle = AddMethodReference( + methodName, + parameters, + new Symbol(method.ReturnType), + method.ContainingType, + !isInterfaceParent && method.IsStatic); + currentTypeDeclaration.AddMethodReference(method, referenceHandle); + return referenceHandle; + } + + public void AddPropertyReference(IPropertySymbol property) + { + Logger.Log("adding property reference: " + property.Name); + + if (property.SetMethod != null) + { + var setMethodReference = AddMethodReference( + "put_" + property.Name, + new Parameter[] { new Parameter(property.Type, "value", ParameterAttributes.In) }, + null, + property.ContainingType, + false); + currentTypeDeclaration.AddMethodReference(property, setMethodReference); + } + + var getMethodReference = AddMethodReference( + "get_" + property.Name, + new Parameter[0], + new Symbol(property.Type), + property.ContainingType, + false); + currentTypeDeclaration.AddMethodReference(property, getMethodReference); + } + + public void AddEventReference(IEventSymbol @event) + { + Logger.Log("adding event reference: " + @event.Name); + + var delegateSymbolType = @event.Type; + EntityHandle eventRegistrationTokenTypeHandle = GetTypeReference("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract"); + Symbol eventRegistrationToken = new Symbol(eventRegistrationTokenTypeHandle); + + var addMethodReference = AddMethodReference( + "add_" + @event.Name, + new Parameter[] { new Parameter(delegateSymbolType, "handler", ParameterAttributes.In) }, + eventRegistrationToken, + @event.ContainingType, + false); + currentTypeDeclaration.AddMethodReference(delegateSymbolType, addMethodReference); + + var removeMethodReference = AddMethodReference( + "remove_" + @event.Name, + new Parameter[] { new Parameter(eventRegistrationToken, "token", ParameterAttributes.In) }, + null, + @event.ContainingType, + false); + currentTypeDeclaration.AddMethodReference(delegateSymbolType, removeMethodReference); + } + + void AddProjectedType(INamedTypeSymbol type) + { + currentTypeDeclaration = new TypeDeclaration(type); + + foreach (var member in type.GetMembers()) + { + if (member is IMethodSymbol method && + (method.MethodKind == MethodKind.Ordinary || method.MethodKind == MethodKind.Constructor)) + { + AddMethodReference(method); + } + else if (member is IPropertySymbol property) + { + AddPropertyReference(property); + } + else if (member is IEventSymbol @event) + { + AddEventReference(@event); + } + else + { + Logger.Log("member not recognized: " + member.Kind + " " + member.Name); + } + } + + typeDefinitionMapping[type.ToString()] = currentTypeDeclaration; + } + + enum SynthesizedInterfaceType + { + Static, + Factory, + Default + } + + string GetSynthesizedInterfaceName(string className, SynthesizedInterfaceType type) + { + // TODO: handle existing types by appending number suffix + return "I" + className + + type switch + { + SynthesizedInterfaceType.Default => "Class", + SynthesizedInterfaceType.Factory => "Factory", + SynthesizedInterfaceType.Static => "Static", + _ => "", + }; + } + + void AddSynthesizedInterfaces(TypeDeclaration classDeclaration) + { + HashSet classMembersFromInterfaces = new HashSet(); + INamedTypeSymbol classSymbol = classDeclaration.Node as INamedTypeSymbol; + foreach (var @interface in classSymbol.AllInterfaces) + { + foreach(var interfaceMember in @interface.GetMembers()) + { + classMembersFromInterfaces.Add(classSymbol.FindImplementationForInterfaceMember(interfaceMember)); + } + } + + AddSynthesizedInterface( + classDeclaration, + SynthesizedInterfaceType.Static, + classMembersFromInterfaces); + + AddSynthesizedInterface( + classDeclaration, + SynthesizedInterfaceType.Factory, + classMembersFromInterfaces); + + AddSynthesizedInterface( + classDeclaration, + SynthesizedInterfaceType.Default, + classMembersFromInterfaces); + + // TODO: address overridable and composable interfaces. + } + + void AddSynthesizedInterface( + TypeDeclaration classDeclaration, + SynthesizedInterfaceType interfaceType, + HashSet classMembersFromInterfaces) + { + currentTypeDeclaration = new TypeDeclaration(null); + + bool hasTypes = false; + INamedTypeSymbol classSymbol = classDeclaration.Node as INamedTypeSymbol; + + // Each class member results in some form of method definition, + // so using that to our advantage to get public members. + foreach (var classMember in classDeclaration.MethodDefinitions) + { + if (interfaceType == SynthesizedInterfaceType.Factory && + classMember.Key is IMethodSymbol constructorMethod && + constructorMethod.MethodKind == MethodKind.Constructor && + constructorMethod.Parameters.Length != 0) + { + hasTypes = true; + AddFactoryMethod(classSymbol, constructorMethod); + } + else if((interfaceType == SynthesizedInterfaceType.Default && !classMembersFromInterfaces.Contains(classMember.Key)) || + (interfaceType == SynthesizedInterfaceType.Static && classMember.Key.IsStatic)) + { + if (classMember.Key is IMethodSymbol method && method.MethodKind == MethodKind.Ordinary) + { + AddMethodDeclaration(method, true); + } + else if (classMember.Key is IPropertySymbol property) + { + AddPropertyDeclaration(property, true); + } + else if (classMember.Key is IEventSymbol @event) + { + AddEventDeclaration(@event, true); + } + else + { + Logger.Log("member for synthesized interface not recognized: " + classMember.Key.Kind + " " + classMember.Key.Name); + continue; + } + + hasTypes = true; + } + } + + TypeAttributes typeAttributes = + TypeAttributes.NotPublic | + TypeAttributes.WindowsRuntime | + TypeAttributes.AutoLayout | + TypeAttributes.AnsiClass | + TypeAttributes.Interface | + TypeAttributes.Abstract; + + if (hasTypes) + { + Logger.Log("writing generated interface " + interfaceType); + var interfaceName = GetSynthesizedInterfaceName(classDeclaration.Node.Name, interfaceType); + var typeDefinitionHandle = AddTypeDefinition( + typeAttributes, + classDeclaration.Node.ContainingNamespace.ToString(), + interfaceName, + default); + currentTypeDeclaration.Handle = typeDefinitionHandle; + + string qualifiedInterfaceName = QualifiedName(classDeclaration.Node.ContainingNamespace.ToString(), interfaceName); + typeDefinitionMapping[qualifiedInterfaceName] = currentTypeDeclaration; + + if(interfaceType == SynthesizedInterfaceType.Default) + { + classDeclaration.DefaultInterface = qualifiedInterfaceName; + metadataBuilder.AddInterfaceImplementation( + classDeclaration.Handle, + GetTypeReference(classDeclaration.Node.ContainingNamespace.ToString(), interfaceName, assembly)); + } + + AddDefaultVersionAttribute(typeDefinitionHandle, GetVersion(classSymbol, true)); + } + } + + private int GetVersion(INamedTypeSymbol type, bool setDefaultIfNotSet = false) + { + var versionAttribute = type.GetAttributes(). + Where(attribute => attribute.AttributeClass.Name == "VersionAttribute"); + if(!versionAttribute.Any()) + { + return setDefaultIfNotSet ? Version.Parse(this.version).Major : - 1; + } + + uint version = (uint) versionAttribute.First().ConstructorArguments[0].Value; + return (int) version; + } + + void AddType(INamedTypeSymbol type) + { + bool isProjectedType = type.GetAttributes(). + Any(attribute => attribute.AttributeClass.Name == "WindowsRuntimeTypeAttribute"); + if (isProjectedType) + { + AddProjectedType(type); + } + else + { + AddExternalType(type); + } + } + + public void FinalizeGeneration() + { + var classTypeDeclarations = typeDefinitionMapping.Values + .Where(declaration => declaration.Node is INamedTypeSymbol symbol && symbol.TypeKind == TypeKind.Class) + .ToList(); + foreach (var classTypeDeclaration in classTypeDeclarations) + { + INamedTypeSymbol classSymbol = classTypeDeclaration.Node as INamedTypeSymbol; + + foreach (var implementedInterface in classSymbol.AllInterfaces) + { + if (!typeDefinitionMapping.ContainsKey(implementedInterface.ToString())) + { + AddType(implementedInterface); + } + + foreach (var interfaceMember in typeDefinitionMapping[implementedInterface.ToString()].MethodReferences) + { + var classMember = classSymbol.FindImplementationForInterfaceMember(interfaceMember.Key); + if (classTypeDeclaration.MethodDefinitions.ContainsKey(classMember)) + { + var interfaceMemberMethodDefinitions = interfaceMember.Value; + var classMemberMethodDefinitions = classTypeDeclaration.MethodDefinitions[classMember]; + for (int idx = 0; idx < interfaceMemberMethodDefinitions.Count; idx++) + { + metadataBuilder.AddMethodImplementation( + classTypeDeclaration.Handle, + classMemberMethodDefinitions[idx], + interfaceMemberMethodDefinitions[idx]); + } + } + } + + if (GetVersion(implementedInterface) == -1) + { + AddDefaultVersionAttribute(typeDefinitionMapping[implementedInterface.ToString()].Handle); + + } + } + + if (classTypeDeclaration.DefaultInterface != null) + { + foreach (var interfaceMember in typeDefinitionMapping[classTypeDeclaration.DefaultInterface].MethodReferences) + { + if (classTypeDeclaration.MethodDefinitions.ContainsKey(interfaceMember.Key)) + { + var interfaceMemberMethodDefinitions = interfaceMember.Value; + var classMemberMethodDefinitions = classTypeDeclaration.MethodDefinitions[interfaceMember.Key]; + for (int idx = 0; idx < interfaceMemberMethodDefinitions.Count; idx++) + { + metadataBuilder.AddMethodImplementation( + classTypeDeclaration.Handle, + classMemberMethodDefinitions[idx], + interfaceMemberMethodDefinitions[idx]); + } + } + } + } + } + + foreach (var node in nodesWithAttributes) + { + EntityHandle parentHandle = default; + if(node is INamedTypeSymbol namedType) + { + parentHandle = typeDefinitionMapping[namedType.ToString()].Handle; + } + else if (node is IMethodSymbol method) + { + parentHandle = typeDefinitionMapping[method.ContainingType.ToString()].MethodDefinitions[method][0]; + } + else if (node is IPropertySymbol property) + { + parentHandle = typeDefinitionMapping[property.ContainingType.ToString()].PropertyDefinitions[property]; + } + else if (node is IEventSymbol @event) + { + parentHandle = typeDefinitionMapping[@event.ContainingType.ToString()].EventDefinitions[@event]; + } + else + { + Logger.Log("node not recognized " + node.Kind + " name: " + node.Name); + } + + AddCustomAttributes(node.GetAttributes(), parentHandle); + } + } + + public bool IsPublicNode(MemberDeclarationSyntax node) + { + return node.Modifiers.Any(modifier => modifier.ValueText == "public") || + (node.Parent is InterfaceDeclarationSyntax && node.Modifiers.Count == 0); + } + + public bool IsStaticNode(MemberDeclarationSyntax node) + { + return node.Modifiers.Any(modifier => modifier.ValueText == "static"); + } + + public bool IsSealedNode(MemberDeclarationSyntax node) + { + return node.Modifiers.Any(modifier => modifier.ValueText == "sealed"); + } + + public string GetNamespace() + { + return string.Join(".", namespaces); + } + + public string QualifiedName(string identifier) + { + return QualifiedName(GetNamespace(), identifier); + } + + public string QualifiedName(string @namespace, string identifier) + { + return string.Join(".", @namespace, identifier); + } + + public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) + { + namespaces.Push(node.Name.ToString()); + base.VisitNamespaceDeclaration(node); + namespaces.Pop(); + } + } +} diff --git a/cswinrt.sln b/cswinrt.sln index bf7735f23..30745a0e2 100644 --- a/cswinrt.sln +++ b/cswinrt.sln @@ -63,6 +63,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BenchmarkComponent", "TestW EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmark", "Projections\Benchmark\Benchmark.csproj", "{03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinRT.SourceGenerator", "Authoring\WinRT.SourceGenerator\WinRT.SourceGenerator.csproj", "{E0C26D3A-504A-4826-BAE2-DE775F865B2A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthoringSample", "Authoring\AuthoringSample\AuthoringSample.csproj", "{41E2A272-150F-42F5-AD40-047AAD9088A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -257,6 +261,46 @@ Global {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|x64.Build.0 = Release|x64 {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|x86.ActiveCfg = Release|x86 {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|x86.Build.0 = Release|x86 + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|ARM.Build.0 = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|ARM64.Build.0 = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|x64.ActiveCfg = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|x64.Build.0 = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|x86.ActiveCfg = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Debug|x86.Build.0 = Debug|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|Any CPU.Build.0 = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|ARM.ActiveCfg = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|ARM.Build.0 = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|ARM64.ActiveCfg = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|ARM64.Build.0 = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|x64.ActiveCfg = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|x64.Build.0 = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|x86.ActiveCfg = Release|Any CPU + {E0C26D3A-504A-4826-BAE2-DE775F865B2A}.Release|x86.Build.0 = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|ARM.ActiveCfg = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|ARM.Build.0 = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|ARM64.Build.0 = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|x64.ActiveCfg = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|x64.Build.0 = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|x86.ActiveCfg = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Debug|x86.Build.0 = Debug|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|Any CPU.Build.0 = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|ARM.ActiveCfg = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|ARM.Build.0 = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|ARM64.ActiveCfg = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|ARM64.Build.0 = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|x64.ActiveCfg = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|x64.Build.0 = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|x86.ActiveCfg = Release|Any CPU + {41E2A272-150F-42F5-AD40-047AAD9088A0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE