From d46d85b6f72134cd4bb2dfe2d27be04d221869fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Thu, 20 Feb 2020 17:18:31 +0000 Subject: [PATCH] Add support for Interop generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rework stubs generator to support generation of Interop stubs. - Adjust templates as required. - Added parameter reference extensions. - Add new type reference extensions. - Rework code to take advantage of the new extensions. - Update console test app. - Bump version to 2.20.0. Signed-off-by: José Simões --- source/MetadataProcessor.Console/Program.cs | 20 +- .../ParameterDefintionExtensions.cs | 35 +++ .../Extensions/TypeReferenceExtensions.cs | 214 ++++++++++++++++ .../MetadataProcessor.Core.csproj | 1 + .../SkeletonGenerator/AssemblyClass.cs | 24 +- .../SkeletonGenerator/AssemblyClassStubs.cs | 12 +- .../SkeletonGenerator/AssemblyLookupTable.cs | 4 +- .../SkeletonGenerator/SkeletonTemplates.cs | 138 +++++++++- .../nanoDumperGenerator.cs | 80 +----- .../nanoSkeletonGenerator.cs | 235 ++++++++++++++++-- source/version.json | 2 +- 11 files changed, 644 insertions(+), 121 deletions(-) create mode 100644 source/MetadataProcessor.Core/Extensions/ParameterDefintionExtensions.cs diff --git a/source/MetadataProcessor.Console/Program.cs b/source/MetadataProcessor.Console/Program.cs index 2983d85a..c3c636e2 100644 --- a/source/MetadataProcessor.Console/Program.cs +++ b/source/MetadataProcessor.Console/Program.cs @@ -127,17 +127,11 @@ public void GenerateSkeleton( string file, string name, string project, - bool withoutInteropCode) + bool withoutInteropCode, + bool isCoreLibrary) { try { - if (!withoutInteropCode) - { - System.Console.Error.WriteLine("Generator for Interop stubs is not supported yet."); - - Environment.Exit(1); - } - if (Verbose) System.Console.WriteLine("Generating skeleton files..."); var skeletonGenerator = new nanoSkeletonGenerator( @@ -145,7 +139,8 @@ public void GenerateSkeleton( file, name, project, - withoutInteropCode); + withoutInteropCode, + isCoreLibrary); skeletonGenerator.GenerateSkeleton(); } @@ -185,6 +180,8 @@ public static void Main(string[] args) { FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); + bool isCoreLibrary = false; + // output header to console System.Console.WriteLine($"nanoFramework MetadataProcessor Utility v{fileVersion.ToString()}"); System.Console.WriteLine("Copyright (c) 2019 nanoFramework project contributors"); @@ -222,8 +219,6 @@ public static void Main(string[] args) } else if (arg == "-compile" && i + 2 < args.Length) { - bool isCoreLibrary = false; - if (!bool.TryParse(args[i + 2], out isCoreLibrary)) { System.Console.Error.WriteLine("Bad parameter for compile. IsCoreLib options has to be 'true' or 'false'."); @@ -273,7 +268,8 @@ public static void Main(string[] args) file, name, project, - withoutInteropCode); + withoutInteropCode, + isCoreLibrary); i += 4; } diff --git a/source/MetadataProcessor.Core/Extensions/ParameterDefintionExtensions.cs b/source/MetadataProcessor.Core/Extensions/ParameterDefintionExtensions.cs new file mode 100644 index 00000000..c94131c2 --- /dev/null +++ b/source/MetadataProcessor.Core/Extensions/ParameterDefintionExtensions.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +using Mono.Cecil; +using System.Text; +using System.Linq; + +namespace nanoFramework.Tools.MetadataProcessor.Core.Extensions +{ + internal static class ParameterDefintionExtensions + { + public static string TypeToString(this ParameterDefinition parameter) + { + if(parameter.ParameterType is ByReferenceType byReference) + { + // pointer to native type + return " *" + byReference.TypeSignatureAsString(); + } + else if (parameter.ParameterType is ArrayType arrayType) + { + return "CLR_RT_TypedArray_" + arrayType.ElementType.TypeSignatureAsString(); + } + else if (parameter.ParameterType.IsValueType) + { + return parameter.ParameterType.Resolve().TypeSignatureAsString(); + } + else + { + return parameter.ParameterType.TypeSignatureAsString(); + } + } + } +} diff --git a/source/MetadataProcessor.Core/Extensions/TypeReferenceExtensions.cs b/source/MetadataProcessor.Core/Extensions/TypeReferenceExtensions.cs index 4a1e6fe5..40fb4001 100644 --- a/source/MetadataProcessor.Core/Extensions/TypeReferenceExtensions.cs +++ b/source/MetadataProcessor.Core/Extensions/TypeReferenceExtensions.cs @@ -4,6 +4,7 @@ // using Mono.Cecil; +using System.Text; namespace nanoFramework.Tools.MetadataProcessor.Core.Extensions { @@ -13,5 +14,218 @@ public static bool IsToInclude(this TypeReference value) { return !nanoTablesContext.IgnoringAttributes.Contains(value.FullName); } + + public static string TypeSignatureAsString(this TypeReference type) + { + if (type.MetadataType == MetadataType.IntPtr) + { + return "I"; + } + + if (type.MetadataType == MetadataType.UIntPtr) + { + return "U"; + } + + nanoCLR_DataType dataType; + if (nanoSignaturesTable.PrimitiveTypes.TryGetValue(type.FullName, out dataType)) + { + switch (dataType) + { + case nanoCLR_DataType.DATATYPE_VOID: + case nanoCLR_DataType.DATATYPE_BOOLEAN: + case nanoCLR_DataType.DATATYPE_CHAR: + case nanoCLR_DataType.DATATYPE_I1: + case nanoCLR_DataType.DATATYPE_U1: + case nanoCLR_DataType.DATATYPE_I2: + case nanoCLR_DataType.DATATYPE_U2: + case nanoCLR_DataType.DATATYPE_I4: + case nanoCLR_DataType.DATATYPE_U4: + case nanoCLR_DataType.DATATYPE_I8: + case nanoCLR_DataType.DATATYPE_U8: + case nanoCLR_DataType.DATATYPE_R4: + case nanoCLR_DataType.DATATYPE_BYREF: + case nanoCLR_DataType.DATATYPE_OBJECT: + return dataType.ToString().Replace("DATATYPE_", ""); + + case nanoCLR_DataType.DATATYPE_LAST_PRIMITIVE: + return "STRING"; + + case nanoCLR_DataType.DATATYPE_LAST_PRIMITIVE_TO_PRESERVE: + return "R8"; + + case nanoCLR_DataType.DATATYPE_LAST_PRIMITIVE_TO_MARSHAL: + return "TIMESPAN"; + + case nanoCLR_DataType.DATATYPE_REFLECTION: + return type.FullName.Replace(".", ""); + } + } + + if (type.MetadataType == MetadataType.Class) + { + StringBuilder classSig = new StringBuilder("CLASS ["); + classSig.Append(type.MetadataToken.ToInt32().ToString("x8")); + classSig.Append("]"); + + return classSig.ToString(); + } + + if (type.MetadataType == MetadataType.ValueType) + { + StringBuilder valueTypeSig = new StringBuilder("VALUETYPE ["); + valueTypeSig.Append(type.MetadataToken.ToInt32().ToString("x8")); + valueTypeSig.Append("]"); + + return valueTypeSig.ToString(); + } + + if (type.IsArray) + { + StringBuilder arraySig = new StringBuilder("SZARRAY "); + arraySig.Append(type.GetElementType().TypeSignatureAsString()); + + return arraySig.ToString(); + } + + return ""; + } + + public static string ToNativeTypeAsString(this TypeReference type) + { + nanoCLR_DataType dataType; + if (nanoSignaturesTable.PrimitiveTypes.TryGetValue(type.FullName, out dataType)) + { + switch (dataType) + { + case nanoCLR_DataType.DATATYPE_VOID: + return "void"; + case nanoCLR_DataType.DATATYPE_BOOLEAN: + return "bool"; + case nanoCLR_DataType.DATATYPE_CHAR: + return "char"; + case nanoCLR_DataType.DATATYPE_I1: + return "int8_t"; + case nanoCLR_DataType.DATATYPE_U1: + return "uint8_t"; + case nanoCLR_DataType.DATATYPE_I2: + return "int16_t"; + case nanoCLR_DataType.DATATYPE_U2: + return "uint16_t"; + case nanoCLR_DataType.DATATYPE_I4: + return "int32_t"; + case nanoCLR_DataType.DATATYPE_U4: + return "uint32_t"; + case nanoCLR_DataType.DATATYPE_I8: + return "int64_t"; + case nanoCLR_DataType.DATATYPE_U8: + return "uint64_t"; + case nanoCLR_DataType.DATATYPE_R4: + return "float"; + case nanoCLR_DataType.DATATYPE_BYREF: + return ""; + + // system.String + case nanoCLR_DataType.DATATYPE_LAST_PRIMITIVE: + return "const char*"; + + // System.Double + case nanoCLR_DataType.DATATYPE_LAST_PRIMITIVE_TO_PRESERVE: + return "double"; + + default: + return "UNSUPPORTED"; + } + } + + if (type.MetadataType == MetadataType.Class) + { + return "UNSUPPORTED"; + } + + if (type.MetadataType == MetadataType.ValueType) + { + return "UNSUPPORTED"; + } + + if (type.IsArray) + { + StringBuilder arraySig = new StringBuilder("CLR_RT_TypedArray_"); + arraySig.Append(type.GetElementType().ToCLRTypeAsString()); + + return arraySig.ToString(); + } + + return ""; + } + + public static string ToCLRTypeAsString(this TypeReference type) + { + nanoCLR_DataType dataType; + if (nanoSignaturesTable.PrimitiveTypes.TryGetValue(type.FullName, out dataType)) + { + switch (dataType) + { + case nanoCLR_DataType.DATATYPE_VOID: + return "void"; + case nanoCLR_DataType.DATATYPE_BOOLEAN: + return "bool"; + case nanoCLR_DataType.DATATYPE_CHAR: + return "CHAR"; + case nanoCLR_DataType.DATATYPE_I1: + return "INT8"; + case nanoCLR_DataType.DATATYPE_U1: + return "UINT8"; + case nanoCLR_DataType.DATATYPE_I2: + return "INT16"; + case nanoCLR_DataType.DATATYPE_U2: + return "UINT16"; + case nanoCLR_DataType.DATATYPE_I4: + return "INT32"; + case nanoCLR_DataType.DATATYPE_U4: + return "UINT32"; + case nanoCLR_DataType.DATATYPE_I8: + return "INT64"; + case nanoCLR_DataType.DATATYPE_U8: + return "UINT64"; + case nanoCLR_DataType.DATATYPE_R4: + return "float"; + case nanoCLR_DataType.DATATYPE_BYREF: + return "NONE"; + + // system.String + case nanoCLR_DataType.DATATYPE_LAST_PRIMITIVE: + return "LPCSTR"; + + // System.Double + case nanoCLR_DataType.DATATYPE_LAST_PRIMITIVE_TO_PRESERVE: + return "double"; + + default: + return "UNSUPPORTED"; + } + } + + if (type.MetadataType == MetadataType.Class) + { + return "UNSUPPORTED"; + } + + if (type.MetadataType == MetadataType.ValueType) + { + return "UNSUPPORTED"; + } + + if (type.IsArray) + { + StringBuilder arraySig = new StringBuilder(); + arraySig.Append(type.GetElementType().ToCLRTypeAsString()); + arraySig.Append("_ARRAY"); + + return arraySig.ToString(); + } + + return ""; + } } } diff --git a/source/MetadataProcessor.Core/MetadataProcessor.Core.csproj b/source/MetadataProcessor.Core/MetadataProcessor.Core.csproj index 76960fd9..fc052c9a 100644 --- a/source/MetadataProcessor.Core/MetadataProcessor.Core.csproj +++ b/source/MetadataProcessor.Core/MetadataProcessor.Core.csproj @@ -77,6 +77,7 @@ + diff --git a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClass.cs b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClass.cs index 82c86fbb..dc408ed5 100644 --- a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClass.cs +++ b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClass.cs @@ -13,6 +13,8 @@ public class AssemblyDeclaration public string ShortName; public string ShortNameUpper; + public bool IsCoreLib = false; + public List Classes = new List(); } @@ -20,10 +22,11 @@ public class Class { public string Name; public string AssemblyName; + public string ShortNameUpper; public List StaticFields = new List(); public List InstanceFields = new List(); - public List Methods = new List(); + public List Methods = new List(); } public class StaticField @@ -40,8 +43,25 @@ public class InstanceField public string FieldWarning; } - public class Method + public class MethodStub + { + public string Declaration; + public string DeclarationForUserCode; + public string CallFromMarshalling; + public string ReturnType; + public string MarshallingReturnType; + public bool HasReturnType; + public bool IsStatic = false; + + public List ParameterDeclaration = new List(); + } + + public class ParameterDeclaration { + public string Index; + public string Type; + public string Name; + public string MarshallingDeclaration; public string Declaration; } } diff --git a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClassStubs.cs b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClassStubs.cs index d1c8d42d..a4e89e6b 100644 --- a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClassStubs.cs +++ b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClassStubs.cs @@ -11,6 +11,16 @@ public class AssemblyClassStubs { public string HeaderFileName; - public List Functions = new List(); + public string ClassHeaderFileName; + + public string ClassName; + + public string ShortNameUpper; + + public string RootNamespace; + + public string ProjectName; + + public List Functions = new List(); } } diff --git a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyLookupTable.cs b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyLookupTable.cs index 79dbcb9c..5dae9118 100644 --- a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyLookupTable.cs +++ b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyLookupTable.cs @@ -10,6 +10,8 @@ namespace nanoFramework.Tools.MetadataProcessor.Core { public class AssemblyLookupTable { + public bool IsCoreLib; + public string Name; public string AssemblyName; public string HeaderFileName; @@ -17,6 +19,6 @@ public class AssemblyLookupTable public Version NativeVersion; - public List LookupTable = new List(); + public List LookupTable = new List(); } } diff --git a/source/MetadataProcessor.Core/SkeletonGenerator/SkeletonTemplates.cs b/source/MetadataProcessor.Core/SkeletonGenerator/SkeletonTemplates.cs index d78dc667..8a6172af 100644 --- a/source/MetadataProcessor.Core/SkeletonGenerator/SkeletonTemplates.cs +++ b/source/MetadataProcessor.Core/SkeletonGenerator/SkeletonTemplates.cs @@ -8,7 +8,9 @@ namespace nanoFramework.Tools.MetadataProcessor internal partial class SkeletonTemplates { internal const string AssemblyHeaderTemplate = -@"//-----------------------------------------------------------------------------{{#newline}} +@" +{{#if IsCoreLib}} +//-----------------------------------------------------------------------------{{#newline}} //{{#newline}} // ** WARNING! ** {{#newline}} // This file was generated automatically by a tool.{{#newline}} @@ -19,6 +21,15 @@ internal partial class SkeletonTemplates // re-run.{{#newline}} //{{#newline}} //-----------------------------------------------------------------------------{{#newline}} +{{#else}} +//-----------------------------------------------------------------------------{{#newline}} +//{{#newline}} +// ** DO NOT EDIT THIS FILE! **{{#newline}} +// This file was generated by a tool{{#newline}} +// re-running the tool will overwrite this file.{{#newline}} +//{{#newline}} +//-----------------------------------------------------------------------------{{#newline}} +{{/if}} {{#newline}} #ifndef _{{ShortNameUpper}}_H_{{#newline}} @@ -27,7 +38,10 @@ internal partial class SkeletonTemplates #include {{#newline}} #include {{#newline}} +{{#if IsCoreLib}} #include {{#newline}} +{{#else}} +{{/if}} {{#newline}} {{#each Classes}} @@ -61,7 +75,20 @@ struct Library_{{AssemblyName}}_{{Name}}{{#newline}} "; internal const string AssemblyLookupTemplate = -@"#include ""{{HeaderFileName}}.h""{{#newline}} +@" +{{#if IsCoreLib}} +{{#else}} +//-----------------------------------------------------------------------------{{#newline}} +//{{#newline}} +// ** DO NOT EDIT THIS FILE! **{{#newline}} +// This file was generated by a tool{{#newline}} +// re-running the tool will overwrite this file.{{#newline}} +//{{#newline}} +//-----------------------------------------------------------------------------{{#newline}} +{{#newline}} +{{/if}} + +#include ""{{HeaderFileName}}.h""{{#newline}} {{#newline}} static const CLR_RT_MethodHandler method_lookup[] ={{#newline}} @@ -77,14 +104,11 @@ struct Library_{{AssemblyName}}_{{Name}}{{#newline}} ""{{Name}}"",{{#newline}} {{NativeCRC32}},{{#newline}} method_lookup,{{#newline}} - ////////////////////////////////////////////////////////////////////////////////////{{#newline}} - // check if the version bellow matches the one in AssemblyNativeVersion attribute //{{#newline}} - ////////////////////////////////////////////////////////////////////////////////////{{#newline}} { {{NativeVersion.Major}}, {{NativeVersion.Minor}}, {{NativeVersion.Build}}, {{NativeVersion.Revision}} }{{#newline}} };{{#newline}} "; - internal const string ClassStubTemplate = + internal const string ClassWithoutInteropStubTemplate = @"//----------------------------------------------------------------------------- // // ** WARNING! ** @@ -108,6 +132,108 @@ struct Library_{{AssemblyName}}_{{Name}}{{#newline}} NANOCLR_NOCLEANUP(); } +{{/each}}"; + + internal const string ClassStubTemplate = +@"//----------------------------------------------------------------------------- +// +// ** WARNING! ** +// This file was generated automatically by a tool. +// Re-running the tool will overwrite this file. +// You should copy this file to a custom location +// before adding any customization in the copy to +// prevent loss of your changes when the tool is +// re-run. +// +//----------------------------------------------------------------------------- + +#include ""{{HeaderFileName}}.h"" +#include ""{{HeaderFileName}}_{{ClassHeaderFileName}}.h"" + +using namespace {{RootNamespace}}::{{ProjectName}}; + +{{#each Functions}} +{{ReturnType}} {{ClassName}}::{{DeclarationForUserCode}} +{ +{{#each ParameterDeclaration}} + (void){{Name}};{{/each}} + (void)hr; +{{#if HasReturnType}} {{ReturnType}} retValue = 0;{{/if}} + + //////////////////////////////// + // implementation starts here // + + + // implementation ends here // + //////////////////////////////// + +{{#if HasReturnType}} return retValue;{{/if}} +} +{{/each}}"; + + internal const string ClassHeaderTemplate = +@"//----------------------------------------------------------------------------- +// +// ** WARNING! ** +// This file was generated automatically by a tool. +// Re-running the tool will overwrite this file. +// You should copy this file to a custom location +// before adding any customization in the copy to +// prevent loss of your changes when the tool is +// re-run. +// +//----------------------------------------------------------------------------- + +#ifndef _{{ShortNameUpper}}_H_ +#define _{{ShortNameUpper}}_H_ + +namespace {{RootNamespace}} +{ + namespace {{ProjectName}} + { + struct {{ClassName}} + { + // Helper Functions to access fields of managed object + // Declaration of stubs. These functions are implemented by Interop code developers +{{#each Functions}} + {{#if IsStatic}}static {{/if}}{{ReturnType}} {{DeclarationForUserCode}}; +{{/each}} + }; + } +} + +#endif //_{{ShortNameUpper}}_H_ +"; + + internal const string ClassMarshallingCodeTemplate = +@"//----------------------------------------------------------------------------- +// +// ** DO NOT EDIT THIS FILE! ** +// This file was generated by a tool +// re-running the tool will overwrite this file. +// +//----------------------------------------------------------------------------- + +#include ""{{HeaderFileName}}.h"" +#include ""{{HeaderFileName}}_{{ClassHeaderFileName}}.h"" + +using namespace {{RootNamespace}}::{{ProjectName}}; + +{{#each Functions}} +HRESULT {{Declaration}}( CLR_RT_StackFrame& stack ) +{ + NANOCLR_HEADER(); hr = S_OK; + { +{{#each ParameterDeclaration}} + {{Declaration}} + NANOCLR_CHECK_HRESULT( {{MarshallingDeclaration}} ); +{{/each}} + {{#if HasReturnType}}{{ReturnType}} retValue = {{/if}}{{ClassName}}::{{CallFromMarshalling}}; + NANOCLR_CHECK_HRESULT( hr ); +{{#if HasReturnType}} SetResult_{{MarshallingReturnType}}( stack, retValue );{{/if}} + } + NANOCLR_NOCLEANUP(); +} {{/each}}"; } } diff --git a/source/MetadataProcessor.Core/nanoDumperGenerator.cs b/source/MetadataProcessor.Core/nanoDumperGenerator.cs index 16f53793..15f182f4 100644 --- a/source/MetadataProcessor.Core/nanoDumperGenerator.cs +++ b/source/MetadataProcessor.Core/nanoDumperGenerator.cs @@ -153,7 +153,7 @@ private void DumpTypeDefinitions(DumpAllTable dumpTable) Name = f.Name, Flags = att.ToString("x8"), Attributes = att.ToString("x8"), - Signature = PrintSignatureForType(f.FieldType) + Signature = f.FieldType.TypeSignatureAsString() }; typeDef.FieldDefinitions.Add(fieldDef); @@ -310,87 +310,15 @@ private void DumpAssemblyReferences(DumpAllTable dumpTable) } } - private string PrintSignatureForType(TypeReference type) - { - if(type.MetadataType == MetadataType.IntPtr) - { - return "I"; - } - - if (type.MetadataType == MetadataType.UIntPtr) - { - return "U"; - } - - nanoCLR_DataType dataType; - if (nanoSignaturesTable.PrimitiveTypes.TryGetValue(type.FullName, out dataType)) - { - switch(dataType) - { - case nanoCLR_DataType.DATATYPE_VOID: - case nanoCLR_DataType.DATATYPE_BOOLEAN: - case nanoCLR_DataType.DATATYPE_CHAR: - case nanoCLR_DataType.DATATYPE_I1: - case nanoCLR_DataType.DATATYPE_U1: - case nanoCLR_DataType.DATATYPE_I2: - case nanoCLR_DataType.DATATYPE_U2: - case nanoCLR_DataType.DATATYPE_I4: - case nanoCLR_DataType.DATATYPE_U4: - case nanoCLR_DataType.DATATYPE_I8: - case nanoCLR_DataType.DATATYPE_U8: - case nanoCLR_DataType.DATATYPE_R4: - case nanoCLR_DataType.DATATYPE_R8: - case nanoCLR_DataType.DATATYPE_BYREF: - case nanoCLR_DataType.DATATYPE_OBJECT: - return dataType.ToString().Replace("DATATYPE_", ""); - - case nanoCLR_DataType.DATATYPE_LAST_PRIMITIVE: - return "STRING"; - - case nanoCLR_DataType.DATATYPE_REFLECTION: - return type.FullName.Replace(".", ""); - } - } - - if (type.MetadataType == MetadataType.Class) - { - StringBuilder classSig = new StringBuilder("CLASS ["); - classSig.Append(type.MetadataToken.ToInt32().ToString("x8")); - classSig.Append("]"); - - return classSig.ToString(); - } - - if (type.MetadataType == MetadataType.ValueType) - { - StringBuilder valueTypeSig = new StringBuilder("VALUETYPE ["); - valueTypeSig.Append(type.MetadataToken.ToInt32().ToString("x8")); - valueTypeSig.Append("]"); - - return valueTypeSig.ToString(); - } - - if (type.IsArray) - { - StringBuilder arraySig = new StringBuilder("SZARRAY "); - arraySig.Append(PrintSignatureForType(type.GetElementType())); - - return arraySig.ToString(); - } - - - return ""; - } - private string PrintSignatureForMethod(MethodReference method) { - var sig = new StringBuilder(PrintSignatureForType(method.ReturnType)); + var sig = new StringBuilder(method.ReturnType.TypeSignatureAsString()); sig.Append("( "); foreach(var p in method.Parameters) { - sig.Append(PrintSignatureForType(p.ParameterType)); + sig.Append(p.ParameterType.TypeSignatureAsString()); sig.Append(", "); } @@ -417,7 +345,7 @@ private string PrintSignatureForLocalVar(Collection variable foreach (var l in variables) { - sig.Append(PrintSignatureForType(l.VariableType)); + sig.Append(l.VariableType.TypeSignatureAsString()); sig.Append(", "); } diff --git a/source/MetadataProcessor.Core/nanoSkeletonGenerator.cs b/source/MetadataProcessor.Core/nanoSkeletonGenerator.cs index 94b8325b..ef02542c 100644 --- a/source/MetadataProcessor.Core/nanoSkeletonGenerator.cs +++ b/source/MetadataProcessor.Core/nanoSkeletonGenerator.cs @@ -9,6 +9,7 @@ using System; using System.IO; using System.Linq; +using System.Text; namespace nanoFramework.Tools.MetadataProcessor.Core { @@ -22,7 +23,7 @@ public sealed class nanoSkeletonGenerator private readonly string _name; private readonly string _project; private readonly bool _withoutInteropCode; - + private readonly bool _isCoreLib; private string _assemblyName; public nanoSkeletonGenerator( @@ -30,13 +31,15 @@ public nanoSkeletonGenerator( string path, string name, string project, - bool withoutInteropCode) + bool withoutInteropCode, + bool isCoreLib) { _tablesContext = tablesContext; _path = path; _name = name; _project = project; _withoutInteropCode = withoutInteropCode; + _isCoreLib = isCoreLib; } public void GenerateSkeleton() @@ -50,7 +53,7 @@ public void GenerateSkeleton() // generate .cpp with the lookup definition GenerateAssemblyLookup(); - // generate _.cpp files with the type definition and stubs. + // generate stub files for classes, headers and marshalling code, if required GenerateStubs(); } @@ -63,10 +66,30 @@ private void GenerateStubs() { var className = NativeMethodsCrc.GetClassName(c); - var classStubs = new AssemblyClassStubs() + var classStubs = new AssemblyClassStubs(); + + if (!_withoutInteropCode) { - HeaderFileName = _project - }; + // Interop code needs to use the root namespace + + classStubs.HeaderFileName = $"{_assemblyName}_{_project}"; + classStubs.ClassHeaderFileName = className; + classStubs.ClassName = c.Name; + classStubs.ShortNameUpper = $"{_assemblyName}_{_project}_{className}".ToUpper(); + classStubs.RootNamespace = _assemblyName; + classStubs.ProjectName = _project; + } + else + { + // projects with Interop can use a simplified naming + + classStubs.HeaderFileName = _project; + classStubs.ClassHeaderFileName = className; + classStubs.ClassName = c.Name; + classStubs.ShortNameUpper = $"{_assemblyName}_{_project}_{className}".ToUpper(); + classStubs.RootNamespace = _assemblyName; + classStubs.ProjectName = _project; + } foreach (var m in nanoTablesContext.GetOrderedMethods(c.Methods)) { @@ -76,26 +99,159 @@ private void GenerateStubs() if (rva == 0xFFFF && !m.IsAbstract) { - classStubs.Functions.Add(new Method() + var newMethod = new MethodStub() { Declaration = $"Library_{_project}_{className}::{NativeMethodsCrc.GetMethodName(m)}" - }); + }; + + if(!_withoutInteropCode) + { + // process with Interop code + + newMethod.IsStatic = m.IsStatic; + newMethod.HasReturnType = ( + m.MethodReturnType != null && + m.MethodReturnType.ReturnType.FullName != "System.Void"); + + StringBuilder declaration = new StringBuilder(); + + newMethod.ReturnType = m.MethodReturnType.ReturnType.ToNativeTypeAsString(); + + newMethod.MarshallingReturnType = newMethod.ReturnType; + + declaration.Append($"{m.Name}"); + declaration.Append("( "); + + StringBuilder marshallingCall = new StringBuilder($"{m.Name}"); + marshallingCall.Append("( "); + + // loop through the parameters + if (m.HasParameters) + { + int parameterIndex = 0; + + foreach (var item in m.Parameters) + { + // get the parameter type + var parameterType = item.ParameterType.ToNativeTypeAsString(); + + // compose the function declaration + declaration.Append($"{parameterType} param{parameterIndex.ToString()}, "); + + // compose the function call + marshallingCall.Append($"param{parameterIndex.ToString()}, "); + + // compose the variable block + var parameterDeclaration = new ParameterDeclaration() + { + Index = parameterIndex.ToString(), + Name = $"param{parameterIndex.ToString()}", + }; + + if(item.ParameterType.IsByReference) + { + // declaration like + // INT8 param1; + // UINT8 heapblock1[CLR_RT_HEAP_BLOCK_SIZE]; + + parameterDeclaration.Type = parameterType; + + parameterDeclaration.Declaration = + $"{parameterType} {parameterDeclaration.Name};" + Environment.NewLine + + $" UINT8 heapblock{parameterIndex.ToString()}[CLR_RT_HEAP_BLOCK_SIZE];"; + + parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{parameterType}_ByRef( stack, heapblock{(parameterIndex + (m.IsStatic ? 0 : 1)).ToString()}, {parameterDeclaration.Name} )"; + + } + else if (item.ParameterType.IsArray) + { + // declaration like + // CLR_RT_TypedArray_UINT8 param0; + + parameterDeclaration.Type = parameterType; + parameterDeclaration.Declaration = $"{parameterType} {parameterDeclaration.Name};"; + parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{item.ParameterType.GetElementType().ToCLRTypeAsString()}_ARRAY( stack, {(parameterIndex + (m.IsStatic ? 0 : 1)).ToString()}, {parameterDeclaration.Name} )"; + } + else + { + // declaration like + // INT8 param1; + + parameterDeclaration.Type = parameterType; + parameterDeclaration.Declaration = $"{parameterType} {parameterDeclaration.Name};"; + parameterDeclaration.MarshallingDeclaration = $"Interop_Marshal_{parameterType}( stack, {(parameterIndex + (m.IsStatic ? 0 : 1)).ToString()}, {parameterDeclaration.Name} )"; + } + + newMethod.ParameterDeclaration.Add(parameterDeclaration); + } + + declaration.Append("HRESULT &hr )"); + marshallingCall.Append("hr )"); + } + else + { + declaration.Append(" HRESULT &hr )"); + marshallingCall.Append(" hr )"); + } + + newMethod.DeclarationForUserCode = declaration.ToString(); + newMethod.CallFromMarshalling = marshallingCall.ToString(); + } + + classStubs.Functions.Add(newMethod); } } // anything to add to the header? if (classStubs.Functions.Count > 0) { - FormatCompiler compiler = new FormatCompiler + if (_withoutInteropCode) { - RemoveNewLines = false - }; - Generator generator = compiler.Compile(SkeletonTemplates.ClassStubTemplate); + FormatCompiler compiler = new FormatCompiler + { + RemoveNewLines = false + }; + Generator generator = compiler.Compile(SkeletonTemplates.ClassWithoutInteropStubTemplate); - using (var headerFile = File.CreateText(Path.Combine(_path, $"{_project}_{className}.cpp"))) + using (var headerFile = File.CreateText(Path.Combine(_path, $"{_project}_{className}.cpp"))) + { + var output = generator.Render(classStubs); + headerFile.Write(output); + } + } + else { - var output = generator.Render(classStubs); - headerFile.Write(output); + FormatCompiler compiler = new FormatCompiler + { + RemoveNewLines = false + }; + + // user code stub + Generator generator = compiler.Compile(SkeletonTemplates.ClassStubTemplate); + + using (var headerFile = File.CreateText(Path.Combine(_path, $"{_assemblyName}_{_project}_{className}.cpp"))) + { + var output = generator.Render(classStubs); + headerFile.Write(output); + } + + // marshal code + generator = compiler.Compile(SkeletonTemplates.ClassMarshallingCodeTemplate); + + using (var headerFile = File.CreateText(Path.Combine(_path, $"{_assemblyName}_{_project}_{className}_mrsh.cpp"))) + { + var output = generator.Render(classStubs); + headerFile.Write(output); + } + + // class header + generator = compiler.Compile(SkeletonTemplates.ClassHeaderTemplate); + + using (var headerFile = File.CreateText(Path.Combine(_path, $"{_assemblyName}_{_project}_{className}.h"))) + { + var output = generator.Render(classStubs); + headerFile.Write(output); + } } } } @@ -110,6 +266,7 @@ private void GenerateAssemblyLookup() var assemblyLookup = new AssemblyLookupTable() { + IsCoreLib = _isCoreLib, Name = _name, AssemblyName = _assemblyName, HeaderFileName = _project, @@ -139,7 +296,7 @@ private void GenerateAssemblyLookup() if ((rva == 0xFFFF && !m.IsAbstract)) { - assemblyLookup.LookupTable.Add(new Method() + assemblyLookup.LookupTable.Add(new MethodStub() { Declaration = $"Library_{_project}_{className}::{NativeMethodsCrc.GetMethodName(m)}" }); @@ -152,7 +309,7 @@ private void GenerateAssemblyLookup() if (!c.IsClassToExclude()) { - assemblyLookup.LookupTable.Add(new Method() + assemblyLookup.LookupTable.Add(new MethodStub() { Declaration = "NULL" //Declaration = $"**Library_{_project}_{NativeMethodsCrc.GetClassName(c)}::{NativeMethodsCrc.GetMethodName(m)}" @@ -172,7 +329,7 @@ private void GenerateAssemblyLookup() { foreach (var m in nanoTablesContext.GetOrderedMethods(c.Methods)) { - assemblyLookup.LookupTable.Add(new Method() + assemblyLookup.LookupTable.Add(new MethodStub() { Declaration = "NULL" //Declaration = $"**Library_{_project}_{NativeMethodsCrc.GetClassName(c)}::{NativeMethodsCrc.GetMethodName(m)}" @@ -185,8 +342,20 @@ private void GenerateAssemblyLookup() FormatCompiler compiler = new FormatCompiler(); Generator generator = compiler.Compile(SkeletonTemplates.AssemblyLookupTemplate); + string filePath; + + if (!_withoutInteropCode) + { + // Interop code needs to use the root namespace + filePath = Path.Combine(_path, $"{_assemblyName}_{_project}.cpp"); + } + else + { + // projects with Interop can use a simplified naming + filePath = Path.Combine(_path, $"{_project}.cpp"); + } - using (var headerFile = File.CreateText(Path.Combine(_path, $"{_project}.cpp"))) + using (var headerFile = File.CreateText(filePath)) { var output = generator.Render(assemblyLookup); headerFile.Write(output); @@ -201,7 +370,8 @@ private void GenerateAssemblyHeader() { Name = _name.Replace('.', '_'), ShortName = _project, - ShortNameUpper = _project.ToUpperInvariant() + ShortNameUpper = _project.ToUpperInvariant(), + IsCoreLib = _isCoreLib }; foreach (var c in _tablesContext.TypeDefinitionTable.Items) @@ -215,6 +385,15 @@ private void GenerateAssemblyHeader() Name = NativeMethodsCrc.GetClassName(c) }; + // If class name starts from , + // then we need to exclude this class as actually this is static data object + // described in metadata. + if (classData.Name.StartsWith("")) + { + // Go to the next class. This metadata describes global variable, not a type + continue; + } + // static fields int fieldCount = 0; var staticFields = c.Fields.Where(f => f.IsStatic && !f.IsLiteral); @@ -273,7 +452,7 @@ private void GenerateAssemblyHeader() if( rva == 0xFFFF && !m.IsAbstract) { - classData.Methods.Add(new Method() + classData.Methods.Add(new MethodStub() { Declaration = NativeMethodsCrc.GetMethodName(m) }); @@ -296,8 +475,20 @@ private void GenerateAssemblyHeader() Generator generator = compiler.Compile(SkeletonTemplates.AssemblyHeaderTemplate); Directory.CreateDirectory(_path); + string filePath; + + if (!_withoutInteropCode) + { + // Interop code needs to use the root namespace + filePath = Path.Combine(_path, $"{_assemblyName}_{_project}.h"); + } + else + { + // projects with Interop can use a simplified naming + filePath = Path.Combine(_path, $"{_project}.h"); + } - using (var headerFile = File.CreateText(Path.Combine(_path, $"{_project}.h"))) + using (var headerFile = File.CreateText(filePath)) { var output = generator.Render(assemblyData); headerFile.Write(output); diff --git a/source/version.json b/source/version.json index 49193d14..f80dc452 100644 --- a/source/version.json +++ b/source/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "2.19.0", + "version": "2.20.0", "release": { "branchName" : "release-v{version}", "versionIncrement" : "minor",