diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8b6406b6..b1283c56 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -145,7 +145,7 @@ jobs: nuGetFeedType: external packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg' publishFeedCredentials: 'AzureArtifacts' - continueOnError: true + continueOnError: true condition: succeeded() displayName: Push NuGet packages to Azure Artifacts diff --git a/source/MetadataProcessor.Console/MetadataProcessor.Console.csproj b/source/MetadataProcessor.Console/MetadataProcessor.Console.csproj index 20b0bdb0..cbcf5a70 100644 --- a/source/MetadataProcessor.Console/MetadataProcessor.Console.csproj +++ b/source/MetadataProcessor.Console/MetadataProcessor.Console.csproj @@ -35,6 +35,7 @@ ..\packages\Costura.Fody.3.3.3\lib\net40\Costura.dll + ..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.dll diff --git a/source/MetadataProcessor.Console/Program.cs b/source/MetadataProcessor.Console/Program.cs index 927624bc..3eb785cb 100644 --- a/source/MetadataProcessor.Console/Program.cs +++ b/source/MetadataProcessor.Console/Program.cs @@ -5,6 +5,7 @@ // using Mono.Cecil; +using nanoFramework.Tools.MetadataProcessor.Core; using System; using System.Collections.Generic; using System.Globalization; @@ -22,6 +23,7 @@ private sealed class MetadataProcessor new Dictionary(StringComparer.Ordinal); private AssemblyDefinition _assemblyDefinition; + private nanoAssemblyBuilder _assemblyBuilder; private List _classNamesToExclude = new List(); @@ -52,17 +54,17 @@ public void Compile(string fileName) { if (Verbose) System.Console.WriteLine("Compiling assembly..."); - var builder = new nanoAssemblyBuilder(_assemblyDefinition, _classNamesToExclude, Minimize, Verbose); + _assemblyBuilder = new nanoAssemblyBuilder(_assemblyDefinition, _classNamesToExclude, Minimize, Verbose); using (var stream = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite)) using (var writer = new BinaryWriter(stream)) { - builder.Write(GetBinaryWriter(writer)); + _assemblyBuilder.Write(GetBinaryWriter(writer)); } using (var writer = XmlWriter.Create(Path.ChangeExtension(fileName, "pdbx"))) { - builder.Write(writer); + _assemblyBuilder.Write(writer); } } catch (Exception) @@ -90,6 +92,41 @@ public void AddClassToExclude( { _classNamesToExclude.Add(className); } + + public void GenerateSkeleton( + string file, + string name, + string project, + bool interopCode) + { + try + { + if (interopCode) + { + 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( + _assemblyBuilder.TablesContext, + file, + name, + project, + interopCode); + + skeletonGenerator.GenerateSkeleton(); + } + catch (Exception ex) + { + System.Console.Error.WriteLine( + "Unable to generate skeleton files"); + + Environment.Exit(1); + } + } } public static void Main(string[] args) @@ -124,6 +161,7 @@ public static void Main(string[] args) System.Console.WriteLine("-compile Compiles an assembly into nanoCLR format."); System.Console.WriteLine("-loadHints Loads one (or more) assembly file(s) as a dependency(ies)."); System.Console.WriteLine("-excludeClassByName Removes the class from an assembly."); + System.Console.WriteLine("-generateskeleton Generate skeleton files with stubs to add native code for an assembly."); System.Console.WriteLine("-minimize Minimizes the assembly, removing unwanted elements."); System.Console.WriteLine("-verbose Outputs each command before executing it."); System.Console.WriteLine(""); @@ -136,7 +174,7 @@ public static void Main(string[] args) { md.Compile(args[++i]); } - else if (arg == "-excludeclassbyName" && i + 1 < args.Length) + else if (arg == "-excludeclassbyname" && i + 1 < args.Length) { md.AddClassToExclude(args[++i]); } @@ -153,6 +191,29 @@ public static void Main(string[] args) md.AddLoadHint(args[i + 1], args[i + 2]); i += 2; } + else if (arg == "-generateskeleton" && i + 2 < args.Length) + { + // fill in arguments + string file = args[i + 1]; + string name = args[i + 2]; + string project = args[i + 3]; + bool interopCode = false; + + if (!bool.TryParse(args[i + 4], out interopCode)) + { + System.Console.Error.WriteLine("Bad parameter for generateSkeleton. Generate code without Interop support has to be 'true' or 'false'."); + + Environment.Exit(1); + } + + md.GenerateSkeleton( + file, + name, + project, + interopCode); + + i += 4; + } else { System.Console.Error.WriteLine("Unknown command line option '{0}' ignored.", arg); diff --git a/source/MetadataProcessor.Core/Extensions/TypeDefinitionExtensions.cs b/source/MetadataProcessor.Core/Extensions/TypeDefinitionExtensions.cs new file mode 100644 index 00000000..1f987770 --- /dev/null +++ b/source/MetadataProcessor.Core/Extensions/TypeDefinitionExtensions.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +using Mono.Cecil; + +namespace nanoFramework.Tools.MetadataProcessor.Core.Extensions +{ + internal static class TypeDefinitionExtensions + { + public static bool IncludeInStub(this TypeDefinition value) + { + var typeDefFlags = nanoTypeDefinitionTable.GetFlags(value); + + if (typeDefFlags.HasFlag( + nanoTypeDefinitionFlags.TD_Delegate | + nanoTypeDefinitionFlags.TD_MulticastDelegate)) + { + return false; + } + + // Only generate a stub for classes and value types. + if (value.IsClass || + value.IsValueType) + { + return true; + } + + return false; + } + } +} diff --git a/source/MetadataProcessor.Core/MetadataProcessor.Core.csproj b/source/MetadataProcessor.Core/MetadataProcessor.Core.csproj index 0d6cd659..38615351 100644 --- a/source/MetadataProcessor.Core/MetadataProcessor.Core.csproj +++ b/source/MetadataProcessor.Core/MetadataProcessor.Core.csproj @@ -33,6 +33,7 @@ true + ..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.dll @@ -45,17 +46,34 @@ ..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.Rocks.dll + + ..\packages\Stubble.Core.1.6.3\lib\net45\Stubble.Core.dll + + + ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + + + + @@ -82,13 +100,16 @@ + + + diff --git a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClass.cs b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClass.cs new file mode 100644 index 00000000..82c86fbb --- /dev/null +++ b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClass.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; + +namespace nanoFramework.Tools.MetadataProcessor.Core +{ + public class AssemblyDeclaration + { + public string Name; + public string ShortName; + public string ShortNameUpper; + + public List Classes = new List(); + } + + public class Class + { + public string Name; + public string AssemblyName; + + public List StaticFields = new List(); + public List InstanceFields = new List(); + public List Methods = new List(); + } + + public class StaticField + { + public string Name; + public int ReferenceIndex; + } + + public class InstanceField + { + public string Name; + public int ReferenceIndex; + + public string FieldWarning; + } + + public class Method + { + public string Declaration; + } +} diff --git a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClassStubs.cs b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClassStubs.cs new file mode 100644 index 00000000..d1c8d42d --- /dev/null +++ b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyClassStubs.cs @@ -0,0 +1,16 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; + +namespace nanoFramework.Tools.MetadataProcessor.Core +{ + public class AssemblyClassStubs + { + public string HeaderFileName; + + public List Functions = new List(); + } +} diff --git a/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyLookupTable.cs b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyLookupTable.cs new file mode 100644 index 00000000..79dbcb9c --- /dev/null +++ b/source/MetadataProcessor.Core/SkeletonGenerator/AssemblyLookupTable.cs @@ -0,0 +1,22 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections.Generic; + +namespace nanoFramework.Tools.MetadataProcessor.Core +{ + public class AssemblyLookupTable + { + public string Name; + public string AssemblyName; + public string HeaderFileName; + public string NativeCRC32; + + public Version NativeVersion; + + public List LookupTable = new List(); + } +} diff --git a/source/MetadataProcessor.Core/SkeletonGenerator/SkeletonTemplates.cs b/source/MetadataProcessor.Core/SkeletonGenerator/SkeletonTemplates.cs new file mode 100644 index 00000000..6945b7b2 --- /dev/null +++ b/source/MetadataProcessor.Core/SkeletonGenerator/SkeletonTemplates.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +namespace nanoFramework.Tools.MetadataProcessor +{ + internal partial class SkeletonTemplates + { + internal static string AssemblyHeaderTemplate = +@"//----------------------------------------------------------------------------- +// +// ** 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_ + +#include +#include +#include + +{{#Classes}} +struct Library_{{AssemblyName}}_{{Name}} +{ + {{#StaticFields}} + static const int FIELD_STATIC__{{Name}} = {{ReferenceIndex}}; + {{/StaticFields}} + {{#InstanceFields}} + {{#FieldWarning}} + {{FieldWarning}} + {{/FieldWarning}} + static const int FIELD__{{Name}} = {{ReferenceIndex}}; + {{/InstanceFields}} + + {{#Methods}} + NANOCLR_NATIVE_DECLARE({{Declaration}}); + {{/Methods}} + + //--// + +}; + +{{/Classes}} + +extern const CLR_RT_NativeAssemblyData g_CLR_AssemblyNative_{{Name}}; + +#endif //_{{ShortNameUpper}}_H_ +"; + + internal static string AssemblyLookupTemplate = +@"#include ""{{HeaderFileName}}.h\"" + +static const CLR_RT_MethodHandler method_lookup[] = +{ +{{#LookupTable}} + {{Declaration}}, +{{/LookupTable}} +}; + +const CLR_RT_NativeAssemblyData g_CLR_AssemblyNative_{{AssemblyName}} = +{ + ""{{Name}}"", + {{NativeCRC32}}, + method_lookup, + //////////////////////////////////////////////////////////////////////////////////// + // check if the version bellow matches the one in AssemblyNativeVersion attribute // + //////////////////////////////////////////////////////////////////////////////////// + { {{NativeVersion.Major}}, {{NativeVersion.Minor}}, {{NativeVersion.Revision}}, {{NativeVersion.Build}} } +}; +"; + + internal static 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\"" + +{{#Functions}} +HRESULT {{Declaration}}( CLR_RT_StackFrame& stack ) +{ + NANOCLR_HEADER(); + + NANOCLR_SET_AND_LEAVE(stack.NotImplementedStub()); + + NANOCLR_NOCLEANUP(); +} + +{{/Functions}} +"; + } +} diff --git a/source/MetadataProcessor.Core/Tables/nanoByteCodeTable.cs b/source/MetadataProcessor.Core/Tables/nanoByteCodeTable.cs index b659ff59..119af1f8 100644 --- a/source/MetadataProcessor.Core/Tables/nanoByteCodeTable.cs +++ b/source/MetadataProcessor.Core/Tables/nanoByteCodeTable.cs @@ -78,7 +78,6 @@ public ushort GetMethodId( var rva = method.HasBody ? _lastAvailableRva : (ushort)0xFFFF; var id = (ushort)_methods.Count; - _context.NativeMethodsCrc.UpdateCrc(method); var byteCode = CreateByteCode(method); _methods.Add(method); diff --git a/source/MetadataProcessor.Core/Tables/nanoFieldDefinitionTable.cs b/source/MetadataProcessor.Core/Tables/nanoFieldDefinitionTable.cs index a5a4e191..3d6baea0 100644 --- a/source/MetadataProcessor.Core/Tables/nanoFieldDefinitionTable.cs +++ b/source/MetadataProcessor.Core/Tables/nanoFieldDefinitionTable.cs @@ -84,13 +84,6 @@ public bool TryGetFieldReferenceId( bool trackMaxReferenceId, out ushort referenceId) { - // compare against classes to exclude - if (_context.ClassNamesToExclude.Contains(field.FullName)) - { - referenceId = 0; - return false; - } - var found = TryGetIdByValue(field, out referenceId); if (trackMaxReferenceId && found) { diff --git a/source/MetadataProcessor.Core/Tables/nanoMethodDefinitionTable.cs b/source/MetadataProcessor.Core/Tables/nanoMethodDefinitionTable.cs index 40c4aa28..0d4d1267 100644 --- a/source/MetadataProcessor.Core/Tables/nanoMethodDefinitionTable.cs +++ b/source/MetadataProcessor.Core/Tables/nanoMethodDefinitionTable.cs @@ -54,13 +54,6 @@ public bool TryGetMethodReferenceId( MethodDefinition methodDefinition, out ushort referenceId) { - // compare against classes to exclude - if (_context.ClassNamesToExclude.Contains(methodDefinition.FullName)) - { - referenceId = 0; - return false; - } - return TryGetIdByValue(methodDefinition, out referenceId); } diff --git a/source/MetadataProcessor.Core/Tables/nanoSignaturesTable.cs b/source/MetadataProcessor.Core/Tables/nanoSignaturesTable.cs index eced3911..59056e5b 100644 --- a/source/MetadataProcessor.Core/Tables/nanoSignaturesTable.cs +++ b/source/MetadataProcessor.Core/Tables/nanoSignaturesTable.cs @@ -40,33 +40,33 @@ public int GetHashCode(byte[] that) } } - private static readonly IDictionary _primitiveTypes = + internal static readonly IDictionary PrimitiveTypes = new Dictionary(StringComparer.Ordinal); static nanoSignaturesTable() { - _primitiveTypes.Add(typeof(void).FullName, nanoCLR_DataType.DATATYPE_VOID); + PrimitiveTypes.Add(typeof(void).FullName, nanoCLR_DataType.DATATYPE_VOID); - _primitiveTypes.Add(typeof(sbyte).FullName, nanoCLR_DataType.DATATYPE_I1); - _primitiveTypes.Add(typeof(short).FullName, nanoCLR_DataType.DATATYPE_I2); - _primitiveTypes.Add(typeof(int).FullName, nanoCLR_DataType.DATATYPE_I4); - _primitiveTypes.Add(typeof(long).FullName, nanoCLR_DataType.DATATYPE_I8); + PrimitiveTypes.Add(typeof(sbyte).FullName, nanoCLR_DataType.DATATYPE_I1); + PrimitiveTypes.Add(typeof(short).FullName, nanoCLR_DataType.DATATYPE_I2); + PrimitiveTypes.Add(typeof(int).FullName, nanoCLR_DataType.DATATYPE_I4); + PrimitiveTypes.Add(typeof(long).FullName, nanoCLR_DataType.DATATYPE_I8); - _primitiveTypes.Add(typeof(byte).FullName, nanoCLR_DataType.DATATYPE_U1); - _primitiveTypes.Add(typeof(ushort).FullName, nanoCLR_DataType.DATATYPE_U2); - _primitiveTypes.Add(typeof(uint).FullName, nanoCLR_DataType.DATATYPE_U4); - _primitiveTypes.Add(typeof(ulong).FullName, nanoCLR_DataType.DATATYPE_U8); + PrimitiveTypes.Add(typeof(byte).FullName, nanoCLR_DataType.DATATYPE_U1); + PrimitiveTypes.Add(typeof(ushort).FullName, nanoCLR_DataType.DATATYPE_U2); + PrimitiveTypes.Add(typeof(uint).FullName, nanoCLR_DataType.DATATYPE_U4); + PrimitiveTypes.Add(typeof(ulong).FullName, nanoCLR_DataType.DATATYPE_U8); - _primitiveTypes.Add(typeof(float).FullName, nanoCLR_DataType.DATATYPE_R4); - _primitiveTypes.Add(typeof(double).FullName, nanoCLR_DataType.DATATYPE_R8); + PrimitiveTypes.Add(typeof(float).FullName, nanoCLR_DataType.DATATYPE_R4); + PrimitiveTypes.Add(typeof(double).FullName, nanoCLR_DataType.DATATYPE_R8); - _primitiveTypes.Add(typeof(char).FullName, nanoCLR_DataType.DATATYPE_CHAR); - _primitiveTypes.Add(typeof(string).FullName, nanoCLR_DataType.DATATYPE_STRING); - _primitiveTypes.Add(typeof(bool).FullName, nanoCLR_DataType.DATATYPE_BOOLEAN); + PrimitiveTypes.Add(typeof(char).FullName, nanoCLR_DataType.DATATYPE_CHAR); + PrimitiveTypes.Add(typeof(string).FullName, nanoCLR_DataType.DATATYPE_STRING); + PrimitiveTypes.Add(typeof(bool).FullName, nanoCLR_DataType.DATATYPE_BOOLEAN); - _primitiveTypes.Add(typeof(object).FullName, nanoCLR_DataType.DATATYPE_OBJECT); - _primitiveTypes.Add(typeof(IntPtr).FullName, nanoCLR_DataType.DATATYPE_I4); - _primitiveTypes.Add(typeof(UIntPtr).FullName, nanoCLR_DataType.DATATYPE_U4); + PrimitiveTypes.Add(typeof(object).FullName, nanoCLR_DataType.DATATYPE_OBJECT); + PrimitiveTypes.Add(typeof(IntPtr).FullName, nanoCLR_DataType.DATATYPE_I4); + PrimitiveTypes.Add(typeof(UIntPtr).FullName, nanoCLR_DataType.DATATYPE_U4); } /// @@ -225,7 +225,7 @@ public void WriteDataType( bool expandEnumType) { nanoCLR_DataType dataType; - if (_primitiveTypes.TryGetValue(typeDefinition.FullName, out dataType)) + if (PrimitiveTypes.TryGetValue(typeDefinition.FullName, out dataType)) { writer.WriteByte((byte)dataType); return; @@ -309,7 +309,7 @@ private byte[] GetSignature( } } - private byte[] GetSignature( + internal byte[] GetSignature( IMethodSignature methodReference) { using (var buffer = new MemoryStream()) @@ -441,7 +441,7 @@ private void WriteAttributeArgumentValue( CustomAttributeArgument argument) { nanoCLR_DataType dataType; - if (_primitiveTypes.TryGetValue(argument.Type.FullName, out dataType)) + if (PrimitiveTypes.TryGetValue(argument.Type.FullName, out dataType)) { switch (dataType) { diff --git a/source/MetadataProcessor.Core/Tables/nanoTablesContext.cs b/source/MetadataProcessor.Core/Tables/nanoTablesContext.cs index 92994196..08390ba3 100644 --- a/source/MetadataProcessor.Core/Tables/nanoTablesContext.cs +++ b/source/MetadataProcessor.Core/Tables/nanoTablesContext.cs @@ -103,8 +103,6 @@ public nanoTablesContext( } } - NativeMethodsCrc = new NativeMethodsCrc(assemblyDefinition); - var mainModule = AssemblyDefinition.MainModule; // External references @@ -116,28 +114,17 @@ public nanoTablesContext( .Where(item => !IsAttribute(item)) .ToList(); - // copy collection to remove classes to exclude - TypeReference[] typeReferencesCopy = new TypeReference[typeReferences.Count]; - typeReferences.CopyTo(typeReferencesCopy); - - // compare against classes to remove - foreach (TypeReference t in typeReferencesCopy) - { - if (ClassNamesToExclude.Contains(t.FullName)) - { - typeReferences.Remove(t); - } - } - TypeReferencesTable = new nanoTypeReferenceTable( typeReferences, this); var typeReferencesNames = new HashSet( typeReferences.Select(item => item.FullName), StringComparer.Ordinal); + var memberReferences = mainModule.GetMemberReferences() .Where(item => typeReferencesNames.Contains(item.DeclaringType.FullName)) .ToList(); + FieldReferencesTable = new nanoFieldReferenceTable( memberReferences.OfType(), this); MethodReferencesTable = new nanoMethodReferenceTable( @@ -158,6 +145,12 @@ public nanoTablesContext( MethodDefinitionTable = new nanoMethodDefinitionTable(methods, this); + NativeMethodsCrc = new NativeMethodsCrc( + assemblyDefinition, + ClassNamesToExclude); + + NativeMethodsCrc.UpdateCrc(TypeDefinitionTable); + AttributesTable = new nanoAttributesTable( GetAttributes(types, applyAttributesCompression), GetAttributes(fields, applyAttributesCompression), @@ -388,7 +381,7 @@ private static IEnumerable GetTypesList( } } - private static IEnumerable GetOrderedMethods( + internal static IEnumerable GetOrderedMethods( IEnumerable methods) { var ordered = methods diff --git a/source/MetadataProcessor.Core/Tables/nanoTypeDefinitionTable.cs b/source/MetadataProcessor.Core/Tables/nanoTypeDefinitionTable.cs index 4ab64a5a..47372019 100644 --- a/source/MetadataProcessor.Core/Tables/nanoTypeDefinitionTable.cs +++ b/source/MetadataProcessor.Core/Tables/nanoTypeDefinitionTable.cs @@ -42,6 +42,8 @@ public int GetHashCode(TypeDefinition item) private IDictionary>> _byteCodeOffsets = new Dictionary>>(); + public List TypeDefinitions { get; } + /// /// Creates new instance of object. /// @@ -54,6 +56,8 @@ public nanoTypeDefinitionTable( nanoTablesContext context) : base(items, new TypeDefinitionEqualityComparer(), context) { + TypeDefinitions = items + .Select(t => t).ToList(); } /// @@ -139,7 +143,7 @@ protected override void WriteSingleItem( writer.WriteBytes(stream.ToArray()); } - writer.WriteUInt16(GetFlags(item)); // flags + writer.WriteUInt16((ushort)GetFlags(item)); // flags } private void WriteClassFields( @@ -259,120 +263,93 @@ private ushort GetTypeReferenceOrDefinitionId( return 0xFFFF; } - private ushort GetFlags( + internal static nanoTypeDefinitionFlags GetFlags( TypeDefinition definition) { - const ushort TD_Scope_Public = 0x0001; // Class is public scope. - const ushort TD_Scope_NestedPublic = 0x0002; // Class is nested with public visibility. - const ushort TD_Scope_NestedPrivate = 0x0003; // Class is nested with private visibility. - const ushort TD_Scope_NestedFamily = 0x0004; // Class is nested with family visibility. - const ushort TD_Scope_NestedAssembly = 0x0005; // Class is nested with assembly visibility. - const ushort TD_Scope_NestedFamANDAssem = 0x0006; // Class is nested with family and assembly visibility. - const ushort TD_Scope_NestedFamORAssem = 0x0007; // Class is nested with family or assembly visibility. - - const ushort TD_Serializable = 0x0008; - - const ushort TD_Semantics_ValueType = 0x0010; - const ushort TD_Semantics_Interface = 0x0020; - const ushort TD_Semantics_Enum = 0x0030; - - const ushort TD_Abstract = 0x0040; - const ushort TD_Sealed = 0x0080; - - const ushort TD_SpecialName = 0x0100; - const ushort TD_Delegate = 0x0200; - const ushort TD_MulticastDelegate = 0x0400; - const ushort TD_Patched = 0x0800; - - const ushort TD_BeforeFieldInit = 0x1000; - const ushort TD_HasSecurity = 0x2000; - const ushort TD_HasFinalizer = 0x4000; - const ushort TD_HasAttributes = 0x8000; - - var flags = 0x0000; + var flags = nanoTypeDefinitionFlags.TD_Scope_None; if (definition.IsPublic) { - flags = TD_Scope_Public; + flags = nanoTypeDefinitionFlags.TD_Scope_Public; } else if (definition.IsNestedPublic) { - flags = TD_Scope_NestedPublic; + flags = nanoTypeDefinitionFlags.TD_Scope_NestedPublic; } else if (definition.IsNestedPrivate) { - flags = TD_Scope_NestedPrivate; + flags = nanoTypeDefinitionFlags.TD_Scope_NestedPrivate; } else if (definition.IsNestedFamily) { - flags = TD_Scope_NestedFamily; + flags = nanoTypeDefinitionFlags.TD_Scope_NestedFamily; } else if (definition.IsNestedAssembly) { - flags = TD_Scope_NestedAssembly; + flags = nanoTypeDefinitionFlags.TD_Scope_NestedAssembly; } else if (definition.IsNestedFamilyAndAssembly) { - flags = TD_Scope_NestedFamANDAssem; + flags = nanoTypeDefinitionFlags.TD_Scope_NestedFamANDAssem; } else if (definition.IsNestedFamilyOrAssembly) { - flags = TD_Scope_NestedFamORAssem; + flags = nanoTypeDefinitionFlags.TD_Scope_NestedFamORAssem; } if (definition.IsSerializable) { - flags |= TD_Serializable; + flags |= nanoTypeDefinitionFlags.TD_Serializable; } if (definition.IsEnum) { - flags |= TD_Semantics_Enum; - flags |= TD_Serializable; + flags |= nanoTypeDefinitionFlags.TD_Semantics_Enum; + flags |= nanoTypeDefinitionFlags.TD_Serializable; } else if (definition.IsValueType) { - flags |= TD_Semantics_ValueType; + flags |= nanoTypeDefinitionFlags.TD_Semantics_ValueType; } else if (definition.IsInterface) { - flags |= TD_Semantics_Interface; + flags |= nanoTypeDefinitionFlags.TD_Semantics_Interface; } if (definition.IsAbstract) { - flags |= TD_Abstract; + flags |= nanoTypeDefinitionFlags.TD_Abstract; } if (definition.IsSealed) { - flags |= TD_Sealed; + flags |= nanoTypeDefinitionFlags.TD_Sealed; } if (definition.IsSpecialName) { - flags |= TD_SpecialName; + flags |= nanoTypeDefinitionFlags.TD_SpecialName; } if (definition.IsBeforeFieldInit) { - flags |= TD_BeforeFieldInit; + flags |= nanoTypeDefinitionFlags.TD_BeforeFieldInit; } if (definition.HasSecurity) { - flags |= TD_HasSecurity; + flags |= nanoTypeDefinitionFlags.TD_HasSecurity; } if (definition.HasCustomAttributes) { - flags |= TD_HasAttributes; + flags |= nanoTypeDefinitionFlags.TD_HasAttributes; } var baseType = definition.BaseType; if (baseType != null && baseType.FullName == "System.MulticastDelegate") { - flags |= TD_MulticastDelegate; + flags |= nanoTypeDefinitionFlags.TD_MulticastDelegate; } - return (ushort)flags; + return flags; } } } diff --git a/source/MetadataProcessor.Core/Utility/NativeMethodsCrc.cs b/source/MetadataProcessor.Core/Utility/NativeMethodsCrc.cs index 5fa45c43..47b6dd9e 100644 --- a/source/MetadataProcessor.Core/Utility/NativeMethodsCrc.cs +++ b/source/MetadataProcessor.Core/Utility/NativeMethodsCrc.cs @@ -5,30 +5,32 @@ // using Mono.Cecil; +using nanoFramework.Tools.MetadataProcessor.Core.Extensions; using System; using System.Collections.Generic; -using System.Globalization; using System.Text; namespace nanoFramework.Tools.MetadataProcessor { /// - /// Helper class for calculating native methods CRC value. Really caclulates CRC32 value + /// Helper class for calculating native methods CRC value. Really calculates CRC32 value /// for native method signatures (not methods itself) and signatures treated as string - /// values, formatted by weird rules incompartible with all rest codebase. + /// values, formatted by weird rules backported from .NETMF original implementation. /// public sealed class NativeMethodsCrc { - private readonly HashSet _generatedNames = new HashSet(StringComparer.Ordinal); - private readonly byte[] _null = Encoding.ASCII.GetBytes("NULL"); private readonly byte[] _name; + private readonly List _classNamesToExclude; + public NativeMethodsCrc( - AssemblyDefinition assembly) + AssemblyDefinition assembly, + List classNamesToExclude) { _name = Encoding.ASCII.GetBytes(assembly.Name.Name); + _classNamesToExclude = classNamesToExclude; } public uint Current { get; private set; } @@ -36,8 +38,9 @@ public NativeMethodsCrc( public void UpdateCrc(MethodDefinition method) { var type = method.DeclaringType; - if ((type.IsClass || type.IsValueType) && - (method.RVA == 0xFFFFFFF && !method.IsAbstract)) + + if (type.IncludeInStub() && + (method.RVA == 0 && !method.IsAbstract) ) { Current = Crc32.Compute(_name, Current); Current = Crc32.Compute(Encoding.ASCII.GetBytes(GetClassName(type)), Current); @@ -49,16 +52,16 @@ public void UpdateCrc(MethodDefinition method) } } - private string GetClassName( + internal static string GetClassName( TypeDefinition type) { return (type != null - ? string.Concat(GetClassName(type.DeclaringType), type.Namespace, type.Name) - .Replace(".", string.Empty) + ? string.Join("_", GetClassName(type.DeclaringType), type.Namespace, type.Name) + .Replace(".", "_").TrimStart('_') : string.Empty); } - private string GetMethodName( + internal static string GetMethodName( MethodDefinition method) { var name = string.Concat(method.Name, (method.IsStatic ? "___STATIC__" : "___"), @@ -66,18 +69,10 @@ private string GetMethodName( var originalName = name.Replace(".", string.Empty); - var index = 1; - name = originalName; - while (_generatedNames.Add(name)) - { - name = string.Concat(originalName, index.ToString(CultureInfo.InvariantCulture)); - ++index; - } - - return name; + return originalName; } - private IEnumerable GetAllParameters( + private static IEnumerable GetAllParameters( MethodDefinition method) { yield return GetParameterType(method.ReturnType); @@ -91,10 +86,73 @@ private IEnumerable GetAllParameters( } } - private string GetParameterType( + private static string GetParameterType( TypeReference parameterType) { - return parameterType.Name.ToUpper(); + var typeName = ""; + bool continueProcessing = true; + + // special processing for arrays + if(parameterType.IsArray) + { + typeName += nanoCLR_DataType.DATATYPE_SZARRAY + "_" + GetnanoClrTypeName(parameterType.GetElementType()); + continueProcessing = false; + } + else if (parameterType.IsByReference) + { + typeName += nanoCLR_DataType.DATATYPE_BYREF + "_" + GetnanoClrTypeName(parameterType.GetElementType()); + continueProcessing = false; + } + else if(!parameterType.IsPrimitive && + parameterType.IsValueType) + { + // TBD + continueProcessing = false; + } + + if (continueProcessing) + { + typeName = GetnanoClrTypeName(parameterType); + } + + // clear 'DATATYPE_' prefixes + // and make it upper case + return typeName.Replace("DATATYPE_", "").ToUpper(); + } + + internal static string GetnanoClrTypeName(TypeReference parameterType) + { + // try getting primitive type + + nanoCLR_DataType myType; + if(nanoSignaturesTable.PrimitiveTypes.TryGetValue(parameterType.FullName, out myType)) + { + return myType.ToString(); + } + else + { + // type is not primitive, get full qualified type name + return parameterType.FullName; + } + } + + internal void UpdateCrc(nanoTypeDefinitionTable typeDefinitionTable) + { + foreach (var c in typeDefinitionTable.TypeDefinitions) + { + if (c.IncludeInStub() && !IsClassToExclude(c)) + { + foreach (var m in nanoTablesContext.GetOrderedMethods(c.Methods)) + { + UpdateCrc(m); + } + } + } + } + + private bool IsClassToExclude(TypeDefinition td) + { + return _classNamesToExclude.Contains(td.FullName); } } } diff --git a/source/MetadataProcessor.Core/Utility/nanoTypeDefinitionFlags.cs b/source/MetadataProcessor.Core/Utility/nanoTypeDefinitionFlags.cs new file mode 100644 index 00000000..95838405 --- /dev/null +++ b/source/MetadataProcessor.Core/Utility/nanoTypeDefinitionFlags.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +using System; + +namespace nanoFramework.Tools.MetadataProcessor +{ + /// + /// This list contains the type definition flags + /// + [Flags] + internal enum nanoTypeDefinitionFlags : ushort + { + // these where defined @ struct CLR_RECORD_TYPEDEF + TD_Scope_None = 0x0000, + + // Class is public scope. + TD_Scope_Public = 0x0001, + // Class is nested with public visibility. + TD_Scope_NestedPublic = 0x0002, + // Class is nested with private visibility. + TD_Scope_NestedPrivate = 0x0003, + // Class is nested with family visibility. + TD_Scope_NestedFamily = 0x0004, + // Class is nested with assembly visibility. + TD_Scope_NestedAssembly = 0x0005, + // Class is nested with family and assembly visibility. + TD_Scope_NestedFamANDAssem = 0x0006, + // Class is nested with family or assembly visibility. + TD_Scope_NestedFamORAssem = 0x0007, + + /// + /// Mask for scope flags + /// + TD_Scope = + (TD_Scope_Public | + TD_Scope_NestedPublic | + TD_Scope_NestedPrivate | + TD_Scope_NestedFamily | + TD_Scope_NestedAssembly | + TD_Scope_NestedFamANDAssem | + TD_Scope_NestedFamORAssem ), + + TD_Serializable = 0x0008, + + TD_Semantics_ValueType = 0x0010, + TD_Semantics_Interface = 0x0020, + TD_Semantics_Enum = 0x0030, + + /// + /// Mask for semantics flags + /// + TD_Semantics = (TD_Semantics_ValueType | TD_Semantics_Interface | TD_Semantics_Enum), + + TD_Abstract = 0x0040, + TD_Sealed = 0x0080, + + TD_SpecialName = 0x0100, + TD_Delegate = 0x0200, + TD_MulticastDelegate = 0x0400, + + TD_Patched = 0x0800, + + TD_BeforeFieldInit = 0x1000, + TD_HasSecurity = 0x2000, + TD_HasFinalizer = 0x4000, + TD_HasAttributes = 0x8000, + } +} diff --git a/source/MetadataProcessor.Core/nanoAssemblyBuilder.cs b/source/MetadataProcessor.Core/nanoAssemblyBuilder.cs index 61639bd5..4aa1d8f2 100644 --- a/source/MetadataProcessor.Core/nanoAssemblyBuilder.cs +++ b/source/MetadataProcessor.Core/nanoAssemblyBuilder.cs @@ -22,6 +22,8 @@ public sealed class nanoAssemblyBuilder private readonly bool _minimize; private readonly bool _verbose; + public nanoTablesContext TablesContext => _tablesContext; + /// /// Creates new instance of object. /// diff --git a/source/MetadataProcessor.Core/nanoSkeletonGenerator.cs b/source/MetadataProcessor.Core/nanoSkeletonGenerator.cs new file mode 100644 index 00000000..0bb7cfff --- /dev/null +++ b/source/MetadataProcessor.Core/nanoSkeletonGenerator.cs @@ -0,0 +1,263 @@ +// +// Copyright (c) 2019 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +using Mono.Cecil; +using nanoFramework.Tools.MetadataProcessor.Core.Extensions; +using Stubble.Core.Builders; +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace nanoFramework.Tools.MetadataProcessor.Core +{ + /// + /// Generator of skeleton files from a .NET nanoFramework assembly. + /// + public sealed class nanoSkeletonGenerator + { + private readonly nanoTablesContext _tablesContext; + private readonly string _path; + private readonly string _name; + private readonly string _project; + private readonly bool _interopCode; + + private string _assemblyName; + + public nanoSkeletonGenerator( + nanoTablesContext tablesContext, + string path, + string name, + string project, + bool interopCode) + { + _tablesContext = tablesContext; + _path = path; + _name = name; + _project = project; + _interopCode = interopCode; + } + + public void GenerateSkeleton() + { + // replaces "." with "_" so the assembly name can be part of C++ identifier name + _assemblyName = _name.Replace('.', '_'); + + // create .h with the structs declarations + GenerateAssemblyHeader(); + + // generate .cpp with the lookup definition + GenerateAssemblyLookup(); + + // generate _.cpp files with the type definition and stubs. + GenerateStubs(); + } + + private void GenerateStubs() + { + foreach (var c in _tablesContext.TypeDefinitionTable.TypeDefinitions) + { + if (c.IncludeInStub() && !IsClassToExclude(c)) + { + var className = NativeMethodsCrc.GetClassName(c); + + var classStubs = new AssemblyClassStubs() + { + HeaderFileName = _project + }; + + foreach (var m in nanoTablesContext.GetOrderedMethods(c.Methods)) + { + var rva = _tablesContext.ByteCodeTable.GetMethodRva(m); + + // check method inclusion + if (rva == 0xFFFF && + !m.IsAbstract) + { + classStubs.Functions.Add(new Method() + { + Declaration = $"Library_{_project}_{className}::{NativeMethodsCrc.GetMethodName(m)}" + }); + } + } + + // anything to add to the header? + if (classStubs.Functions.Count > 0) + { + var stubble = new StubbleBuilder().Build(); + + using (var headerFile = File.CreateText(Path.Combine(_path, $"{_project}_{className}.cpp"))) + { + var output = stubble.Render(SkeletonTemplates.ClassStubTemplate, classStubs); + headerFile.Write(output); + } + } + } + } + } + + private void GenerateAssemblyLookup() + { + // grab native version from assembly attribute + var nativeVersionAttribute = _tablesContext.AssemblyDefinition.CustomAttributes.FirstOrDefault(a => a.AttributeType.Name == "AssemblyNativeVersionAttribute"); + Version nativeVersion = new Version((string)nativeVersionAttribute.ConstructorArguments[0].Value); + + var assemblyLookup = new AssemblyLookupTable() + { + Name = _name, + AssemblyName = _assemblyName, + HeaderFileName = _project, + NativeVersion = nativeVersion, + NativeCRC32 = "0x" + _tablesContext.NativeMethodsCrc.Current.ToString("X") + }; + + + foreach (var c in _tablesContext.TypeDefinitionTable.TypeDefinitions) + { + if (c.IncludeInStub() && !IsClassToExclude(c)) + { + var className = NativeMethodsCrc.GetClassName(c); + + foreach (var m in nanoTablesContext.GetOrderedMethods(c.Methods)) + { + var rva = _tablesContext.ByteCodeTable.GetMethodRva(m); + + // check method inclusion + if ((rva == 0xFFFF && + !m.IsAbstract)) + { + assemblyLookup.LookupTable.Add(new Method() + { + Declaration = $"Library_{_project}_{className}::{NativeMethodsCrc.GetMethodName(m)}" + }); + } + else + { + assemblyLookup.LookupTable.Add(new Method() + { + Declaration = "NULL" + }); + } + } + } + } + + var stubble = new StubbleBuilder().Build(); + + using (var headerFile = File.CreateText(Path.Combine(_path, $"{_project}.cpp"))) + { + var output = stubble.Render(SkeletonTemplates.AssemblyLookupTemplate, assemblyLookup); + headerFile.Write(output); + } + } + + private void GenerateAssemblyHeader() + { + int staticFieldCount = 0; + + var assemblyData = new AssemblyDeclaration() + { + Name = _name, + ShortName = _project, + ShortNameUpper = _project.ToUpperInvariant() + }; + + foreach (var c in _tablesContext.TypeDefinitionTable.TypeDefinitions) + { + if (c.IncludeInStub() && !IsClassToExclude(c)) + { + var classData = new Class() + { + AssemblyName = _project, + Name = NativeMethodsCrc.GetClassName(c) + }; + + // static fields + int fieldCount = 0; + foreach (var f in c.Fields.Where(f => f.IsStatic)) + { + classData.StaticFields.Add(new StaticField() + { + Name = f.Name, + ReferenceIndex = staticFieldCount + fieldCount++ + }); + } + + // instance fields + fieldCount = 0; + foreach (var f in c.Fields.Where(f => !f.IsStatic)) + { + // sanity check for field name + // like auto-vars and such + if (f.Name.IndexOfAny(new char[] { '<', '>' }) > 0) + { + classData.InstanceFields.Add(new InstanceField() + { + FieldWarning = $"*** Something wrong with field '{f.Name}'. Possibly its backing field is missing (mandatory for nanoFramework).\n" + }); + } + else + { + ushort fieldRefId; + if (_tablesContext.FieldsTable.TryGetFieldReferenceId(f, false, out fieldRefId)) + { + classData.InstanceFields.Add(new InstanceField() + { + Name = f.Name, + ReferenceIndex = fieldRefId + 1 + }); + } + fieldCount++; + } + } + + // methods + if(c.HasMethods) + { + foreach (var m in nanoTablesContext.GetOrderedMethods(c.Methods)) + { + var rva = _tablesContext.ByteCodeTable.GetMethodRva(m); + + if( rva == 0xFFFF && + !m.IsAbstract) + { + classData.Methods.Add(new Method() + { + Declaration = NativeMethodsCrc.GetMethodName(m) + }); + } + } + + } + + // anything to add to the header? + if( classData.StaticFields.Count > 0 || + classData.InstanceFields.Count > 0 || + classData.Methods.Count > 0) + { + assemblyData.Classes.Add(classData); + } + } + + staticFieldCount += c.Fields.Count(f => f.IsStatic); + } + + var stubble = new StubbleBuilder().Build(); + + Directory.CreateDirectory(_path); + + using (var headerFile = File.CreateText(Path.Combine(_path, $"{_project}.h"))) + { + var output = stubble.Render(SkeletonTemplates.AssemblyHeaderTemplate, assemblyData); + headerFile.Write(output); + } + } + + private bool IsClassToExclude(TypeDefinition td) + { + return _tablesContext.ClassNamesToExclude.Contains(td.FullName); + } + } +} diff --git a/source/MetadataProcessor.Core/packages.config b/source/MetadataProcessor.Core/packages.config index 778a66e9..15570e2b 100644 --- a/source/MetadataProcessor.Core/packages.config +++ b/source/MetadataProcessor.Core/packages.config @@ -2,4 +2,8 @@ + + + + \ No newline at end of file