Pushed initial work-in-progress code for managed-to-native bindings generator.

This commit is contained in:
Joao Matos 2016-07-25 16:28:25 +01:00
Родитель b0d4259c48
Коммит d965d8797e
31 изменённых файлов: 4489 добавлений и 20 удалений

34
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,34 @@
*.pidb
configure
install-sh
aclocal.m4
config.status
config.log
autom4te.cache
missing
*Makefile
*Makefile.in
*.dll
*.exe
bin/
*.userprefs
tests/output
src/generator/generator
*.pc
.DS_Store
*.user
*.suo
*.DotSettings
*.sdf
*.opensdf
*.pdb
*.config
*.vcxproj
*.filters
*.sln
*.metagen
*.csproj
*.ilk
*.manifest
/build/vs2015
/ikvm

3
.gitmodules поставляемый Normal file
Просмотреть файл

@ -0,0 +1,3 @@
[submodule "ikvm"]
path = ikvm
url = git@github.com:mono/ikvm-fork.git

35
LICENSE
Просмотреть файл

@ -1,21 +1,20 @@
The MIT License (MIT)
Copyright (c) 2016 Microsoft
Copyright (c) 2016 João Matos
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -1,2 +1,21 @@
# MonoManagedToNative
Generates bindings that allows calling managed code from C and other languages.
native-binder
==============
.NET assembly to Mono native C/C++ API bindings generator.
· Create a native API to access some C# APIs
· native-binder Xamarin.Foo.dll –type:Xamarin.Foo.Authenticator –language c –output outdir
· Allow for Obj-C binding, Java bindings
· Start with desktop
· Then work with products
Can I use it yet?
-----------------
The project is in a development state and should not be used in production scenarios yet.
Dependencies
------------
* Cecil

52
binder/Binder.cs Normal file
Просмотреть файл

@ -0,0 +1,52 @@
using System;
namespace MonoManagedToNative
{
public class Binder
{
static string Language;
static string MonoIncDir;
static string OutputDir;
static void ParseCommandLineArgs(string[] args)
{
var showHelp = args.Length == 0;
var optionSet = new Mono.Options.OptionSet() {
{ "language=", "output language", v => Language = v },
{ "o|out=", "output directory", v => OutputDir = v },
{ "mono=", "Mono include directory", v => MonoIncDir = v },
{ "h|help", "show this message and exit", v => showHelp = v != null },
};
try
{
optionSet.Parse(args);
}
catch (Mono.Options.OptionException e)
{
Console.WriteLine(e.Message);
Environment.Exit(0);
}
if (showHelp)
{
// Print usage and exit.
Console.WriteLine("{0} [--gen=c/c++] [--out=dir] "
+ "[--mono=dir]",
AppDomain.CurrentDomain.FriendlyName);
Environment.Exit(0);
}
}
static void Main(string[] args)
{
ParseCommandLineArgs(args);
var options = new Options();
var driver = new Driver(options);
driver.Run();
}
}
}

121
binder/Diagnostics.cs Normal file
Просмотреть файл

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace MonoManagedToNative
{
/// <summary>
/// Represents the kind of the diagnostic.
/// </summary>
public enum DiagnosticKind
{
Debug,
Message,
Warning,
Error
}
/// <summary>
/// Keeps information related to a single diagnostic.
/// </summary>
public struct DiagnosticInfo
{
public DiagnosticKind Kind;
public string Message;
public string File;
public int Line;
public int Column;
}
public interface IDiagnostics
{
void Emit(DiagnosticInfo info);
void PushIndent(int level = 4);
void PopIndent();
}
public static class DiagnosticExtensions
{
public static void Debug(this IDiagnostics consumer,
string msg, params object[] args)
{
var diagInfo = new DiagnosticInfo
{
Kind = DiagnosticKind.Debug,
Message = string.Format(msg, args)
};
consumer.Emit(diagInfo);
}
public static void Message(this IDiagnostics consumer,
string msg, params object[] args)
{
var diagInfo = new DiagnosticInfo
{
Kind = DiagnosticKind.Message,
Message = string.Format(msg, args)
};
consumer.Emit(diagInfo);
}
public static void Warning(this IDiagnostics consumer,
string msg, params object[] args)
{
var diagInfo = new DiagnosticInfo
{
Kind = DiagnosticKind.Warning,
Message = string.Format(msg, args)
};
consumer.Emit(diagInfo);
}
public static void Error(this IDiagnostics consumer,
string msg, params object[] args)
{
var diagInfo = new DiagnosticInfo
{
Kind = DiagnosticKind.Error,
Message = string.Format(msg, args)
};
consumer.Emit(diagInfo);
}
}
public class TextDiagnosticPrinter : IDiagnostics
{
public bool Verbose;
public Stack<int> Indents;
public TextDiagnosticPrinter()
{
Indents = new Stack<int>();
}
public void Emit(DiagnosticInfo info)
{
if (info.Kind == DiagnosticKind.Debug && !Verbose)
return;
var currentIndent = Indents.Sum();
var message = new string(' ', currentIndent) + info.Message;
Console.WriteLine(message);
Debug.WriteLine(message);
}
public void PushIndent(int level)
{
Indents.Push(level);
}
public void PopIndent()
{
Indents.Pop();
}
}
}

162
binder/Driver.cs Normal file
Просмотреть файл

@ -0,0 +1,162 @@
using System.Collections.Generic;
using System.IO;
using MonoManagedToNative.Generators;
using System;
namespace MonoManagedToNative
{
public class Driver
{
public Options Options { get; private set; }
public IDiagnostics Diagnostics { get; private set; }
public List<IKVM.Reflection.Assembly> Assemblies { get; private set; }
public ProjectOutput Output { get; private set; }
public Driver(Options options, IDiagnostics diagnostics = null)
{
Options = options;
Diagnostics = diagnostics;
if (Diagnostics == null)
Diagnostics = new TextDiagnosticPrinter();
if (Options.OutputDir == null)
Options.OutputDir = Directory.GetCurrentDirectory();
Assemblies = new List<IKVM.Reflection.Assembly>();
}
bool Parse()
{
var parser = new Parser(Options, Diagnostics);
parser.OnAssemblyParsed += HandleAssemblyParsed;
return parser.Parse(Options.Project);
}
void Process()
{
}
void Generate()
{
Output = new ProjectOutput();
Generator generator = null;
switch (Options.Language)
{
case GeneratorKind.C:
generator = new CGenerator(this);
break;
default:
throw new NotImplementedException();
}
foreach (var assembly in Assemblies)
{
var templates = generator.Generate(assembly);
foreach (var template in templates)
{
template.Process();
var text = template.Generate();
var path = string.Format("{0}.{1}", template.Name, template.FileExtension);
Output.WriteOutput(path, text);
}
}
}
void WriteFiles()
{
if (!Directory.Exists(Options.OutputDir))
Directory.CreateDirectory(Options.OutputDir);
foreach (var output in Output.Files)
{
var path = output.Key;
var outputPath = Path.Combine(Options.OutputDir,
Path.GetDirectoryName(path));
// Make sure the target directory exists.
Directory.CreateDirectory(outputPath);
var fullPath = Path.Combine(outputPath, Path.GetFileName(path));
var outputStream = output.Value;
outputStream.Position = 0;
using (var outputFile = File.Create(fullPath))
outputStream.CopyTo(outputFile);
Diagnostics.Message("Generated: {0}", path);
}
}
public void Run()
{
Options.Project.BuildInputs();
Diagnostics.Message("Parsing assemblies...");
Diagnostics.PushIndent();
if (!Parse())
return;
Diagnostics.PopIndent();
Diagnostics.Message("Processing assemblies...");
Diagnostics.PushIndent();
Process();
Diagnostics.PopIndent();
Diagnostics.Message("Generating binding code...");
Diagnostics.PushIndent();
Generate();
WriteFiles();
Diagnostics.PopIndent();
}
void HandleParserResult<T>(ParserResult<T> result)
{
var file = result.Input.FullPath;
if (file.StartsWith(result.Input.BasePath))
{
file = file.Substring(result.Input.BasePath.Length);
file = file.TrimStart('\\');
}
switch (result.Kind)
{
case ParserResultKind.Success:
Diagnostics.Message("Parsed '{0}'", file);
break;
case ParserResultKind.Error:
Diagnostics.Message("Error parsing '{0}'", file);
break;
case ParserResultKind.FileNotFound:
Diagnostics.Message("File '{0}' was not found", file);
break;
}
foreach (var diag in result.Diagnostics)
{
Diagnostics.Message(string.Format("{0}({1},{2}): {3}: {4}",
diag.FileName, diag.LineNumber, diag.ColumnNumber,
diag.Level.ToString().ToLower(), diag.Message));
}
}
void HandleAssemblyParsed(ParserResult<IKVM.Reflection.Assembly> result)
{
HandleParserResult(result);
if (result.Kind != ParserResultKind.Success)
return;
Assemblies.Add(result.Output);
}
}
}

Просмотреть файл

@ -0,0 +1,259 @@
using System;
using IKVM.Reflection;
using CppSharp.AST;
namespace MonoManagedToNative.Generators
{
public class AstGenerator
{
public TranslationUnit unit;
Options options;
public AstGenerator(Options options)
{
unit = new TranslationUnit();
this.options = options;
}
public TranslationUnit Visit(Assembly assembly)
{
var name = options.LibraryName ?? assembly.GetName().Name;
unit.Name = name;
foreach (var type in assembly.ExportedTypes)
{
var decl = Visit(type.GetTypeInfo());
unit.Declarations.Add(decl);
}
return unit;
}
public Declaration Visit(TypeInfo typeInfo)
{
if (typeInfo.IsClass || typeInfo.IsInterface || typeInfo.IsValueType)
return VisitRecord(typeInfo);
else if (typeInfo.IsEnum)
return VisitEnum(typeInfo);
throw new Exception("Could not visit type: " + typeInfo.ToString());
}
public Class VisitRecord(TypeInfo type)
{
var @class = new Class { Name = type.Name };
VisitMembers(type, @class);
return @class;
}
public Enumeration VisitEnum(TypeInfo @enum)
{
return null;
}
public void VisitMembers(TypeInfo type, Class @class)
{
foreach (var ctor in type.DeclaredConstructors)
{
var decl = VisitConstructor(ctor, @class);
@class.Declarations.Add(decl);
}
foreach (var method in type.DeclaredMethods)
{
var decl = VisitMethod(method, @class);
@class.Declarations.Add(decl);
}
foreach (var field in type.DeclaredFields)
{
var decl = VisitField(field);
@class.Declarations.Add(decl);
}
foreach (var @event in type.DeclaredEvents)
{
var decl = VisitEvent(@event);
@class.Declarations.Add(decl);
}
foreach (var property in type.DeclaredProperties)
{
var decl = VisitProperty(property);
@class.Declarations.Add(decl);
}
foreach (var decl in @class.Declarations)
decl.Namespace = @class;
}
public Method VisitConstructor(ConstructorInfo ctor, Class @class)
{
var method = VisitMethodBase(ctor);
var ptrType = new QualifiedType(
new PointerType(new QualifiedType(new TagType(@class))));
method.ReturnType = ptrType;
method.Name = "new";
return method;
}
string GetInternalMethodName(MethodBase method)
{
var @params = string.Empty;
var sig = method.ToString();
return string.Format("{0}:{1}({2})", method.DeclaringType.FullName,
method.Name, @params);
}
Method VisitMethod(MethodInfo methodInfo, Class @class)
{
var method = VisitMethodBase(methodInfo);
method.ReturnType = VisitType(methodInfo.ReturnType);
var ptrType = new QualifiedType(
new PointerType(new QualifiedType(new TagType(@class))));
var param = new Parameter { Name = "object", Namespace = @class,
QualifiedType = ptrType };
method.Parameters.Add(param);
return method;
}
QualifiedType VisitType(IKVM.Reflection.Type managedType)
{
CppSharp.AST.Type type = null;
switch (IKVM.Reflection.Type.GetTypeCode(managedType))
{
case TypeCode.Empty:
type = new BuiltinType(PrimitiveType.Null);
break;
case TypeCode.Object:
if (managedType.FullName == "System.Void")
{
type = new BuiltinType(PrimitiveType.Void);
break;
}
throw new NotSupportedException();
case TypeCode.DBNull:
throw new NotSupportedException();
case TypeCode.Boolean:
type = new BuiltinType(PrimitiveType.Bool);
break;
case TypeCode.Char:
type = new BuiltinType(PrimitiveType.WideChar);
break;
case TypeCode.SByte:
type = new BuiltinType(PrimitiveType.Char);
break;
case TypeCode.Byte:
type = new BuiltinType(PrimitiveType.UChar);
break;
case TypeCode.Int16:
type = new BuiltinType(PrimitiveType.Short);
break;
case TypeCode.UInt16:
type = new BuiltinType(PrimitiveType.UShort);
break;
case TypeCode.Int32:
type = new BuiltinType(PrimitiveType.Int);
break;
case TypeCode.UInt32:
type = new BuiltinType(PrimitiveType.UInt);
break;
case TypeCode.Int64:
type = new BuiltinType(PrimitiveType.LongLong);
break;
case TypeCode.UInt64:
type = new BuiltinType(PrimitiveType.ULongLong);
break;
case TypeCode.Single:
type = new BuiltinType(PrimitiveType.Float);
break;
case TypeCode.Double:
type = new BuiltinType(PrimitiveType.Double);
break;
case TypeCode.Decimal:
type = new CILType(typeof(decimal));
break;
case TypeCode.DateTime:
type = new CILType(typeof(DateTime));
break;
case TypeCode.String:
type = new CILType(typeof(string));
break;
}
return new QualifiedType(type);
}
/// <summary>
/// Converts from a .NET member acccess mask to a C/C++ access specifier.
/// </summary>
/// <returns></returns>
AccessSpecifier ConvertToAccessSpecifier(MethodAttributes mask)
{
switch (mask)
{
case MethodAttributes.PrivateScope:
case MethodAttributes.Private:
return AccessSpecifier.Private;
case MethodAttributes.FamANDAssem:
case MethodAttributes.Assembly:
case MethodAttributes.Family:
case MethodAttributes.FamORAssem:
return AccessSpecifier.Internal;
case MethodAttributes.Public:
return AccessSpecifier.Public;
}
throw new NotImplementedException();
}
public Method VisitMethodBase(MethodBase methodBase)
{
var method = new Method
{
Kind = methodBase.IsConstructor ?
CXXMethodKind.Constructor : CXXMethodKind.Normal
};
method.Name = methodBase.Name;
method.OriginalName = GetInternalMethodName(methodBase);
foreach (var param in methodBase.GetParameters())
{
var paramDecl = VisitParameter(param);
method.Parameters.Add(paramDecl);
}
method.IsStatic = methodBase.IsStatic;
method.IsVirtual = methodBase.IsVirtual;
method.IsPure = methodBase.IsAbstract;
//method.IsFinal = methodBase.IsFinal;
var memberAccessMask = (methodBase.Attributes & MethodAttributes.MemberAccessMask);
method.Access = ConvertToAccessSpecifier(memberAccessMask);
return method;
}
public Parameter VisitParameter(ParameterInfo param)
{
throw new NotImplementedException();
}
public Field VisitField(FieldInfo field)
{
throw new NotImplementedException();
}
public Event VisitEvent(EventInfo @event)
{
throw new NotImplementedException();
}
public Property VisitProperty(PropertyInfo @property)
{
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -0,0 +1,227 @@
using System;
using System.Collections.Generic;
using IKVM.Reflection;
using CppSharp.AST;
using CppSharp.Types;
namespace MonoManagedToNative.Generators
{
public class CGenerator : Generator
{
public CGenerator(Driver driver) : base(driver) { }
public override List<Template> Generate(Assembly assembly)
{
var moduleGen = new AstGenerator(Driver.Options);
var module = moduleGen.Visit(assembly) as TranslationUnit;
var headers = new CHeaders(Driver, module);
var sources = new CSources(Driver, module);
var templates = new List<Template> { headers, sources };
foreach (var template in templates)
template.Assembly = assembly;
return templates;
}
}
public class CBlockKind
{
public const int Includes = BlockKind.LAST + 11;
public const int Function = BlockKind.LAST + 12;
public const int Class = BlockKind.LAST + 13;
public const int Typedef = BlockKind.LAST + 13;
}
public abstract class CTemplate : Template, IDeclVisitor<bool>
{
public TranslationUnit Unit;
public CTemplate(Driver driver, TranslationUnit unit) : base(driver)
{
Unit = unit;
}
public override string Name
{
get { return Unit.Name; }
}
public string GeneratedIdentifier(string id)
{
return "__" + id;
}
string PrintCILType(CILType type, TypeQualifiers quals)
{
if (type.Type == typeof(string))
return "const char*";
throw new NotImplementedException();
}
public CppTypePrinter CTypePrinter
{
get
{
var cTypePrinter = new CppTypePrinter { PrintScopeKind = CppTypePrintScopeKind.Qualified };
cTypePrinter.CILTypePrinter += PrintCILType;
return cTypePrinter;
}
}
public void VisitDeclContext(DeclarationContext ctx)
{
foreach (var decl in ctx.Declarations)
decl.Visit(this);
}
public void GenerateFilePreamble()
{
WriteLine("/*");
WriteLine(" * This is autogenerated code.");
WriteLine(" * Do not edit this file or all your changes will be lost after re-generation.");
WriteLine(" */");
}
public void GenerateMethodSignature(Method method)
{
var @class = method.Namespace as Class;
var retType = method.ReturnType.Visit(CTypePrinter);
Write("{0} {1}_{2}(", retType, @class.QualifiedName, method.Name);
Write(GenerateParametersList(method.Parameters));
Write(")");
}
public string GenerateParametersList(List<Parameter> parameters)
{
var types = new List<string>();
foreach (var param in parameters)
types.Add(CTypePrinter.VisitParameter(param));
return string.Join(", ", types);
}
#region IDeclVisitor methods
public virtual bool VisitClassDecl(Class @class)
{
throw new NotImplementedException();
}
public virtual bool VisitClassTemplateDecl(ClassTemplate template)
{
throw new NotImplementedException();
}
public virtual bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecialization specialization)
{
throw new NotImplementedException();
}
public virtual bool VisitDeclaration(Declaration decl)
{
throw new NotImplementedException();
}
public virtual bool VisitEnumDecl(Enumeration @enum)
{
throw new NotImplementedException();
}
public virtual bool VisitEnumItemDecl(Enumeration.Item item)
{
throw new NotImplementedException();
}
public virtual bool VisitEvent(Event @event)
{
throw new NotImplementedException();
}
public virtual bool VisitFieldDecl(Field field)
{
throw new NotImplementedException();
}
public virtual bool VisitFriend(Friend friend)
{
throw new NotImplementedException();
}
public virtual bool VisitFunctionDecl(Function function)
{
throw new NotImplementedException();
}
public virtual bool VisitFunctionTemplateDecl(FunctionTemplate template)
{
throw new NotImplementedException();
}
public virtual bool VisitMacroDefinition(MacroDefinition macro)
{
throw new NotImplementedException();
}
public virtual bool VisitMethodDecl(Method method)
{
throw new NotImplementedException();
}
public virtual bool VisitNamespace(Namespace @namespace)
{
throw new NotImplementedException();
}
public virtual bool VisitNonTypeTemplateParameterDecl(NonTypeTemplateParameter nonTypeTemplateParameter)
{
throw new NotImplementedException();
}
public virtual bool VisitParameterDecl(Parameter parameter)
{
throw new NotImplementedException();
}
public virtual bool VisitProperty(Property property)
{
throw new NotImplementedException();
}
public virtual bool VisitTemplateParameterDecl(TypeTemplateParameter templateParameter)
{
throw new NotImplementedException();
}
public virtual bool VisitTemplateTemplateParameterDecl(TemplateTemplateParameter templateTemplateParameter)
{
throw new NotImplementedException();
}
public virtual bool VisitTypeAliasDecl(TypeAlias typeAlias)
{
throw new NotImplementedException();
}
public virtual bool VisitTypeAliasTemplateDecl(TypeAliasTemplate typeAliasTemplate)
{
throw new NotImplementedException();
}
public virtual bool VisitTypedefDecl(TypedefDecl typedef)
{
throw new NotImplementedException();
}
public virtual bool VisitVariableDecl(Variable variable)
{
throw new NotImplementedException();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,56 @@
using CppSharp.AST;
namespace MonoManagedToNative.Generators
{
public class CHeaders : CTemplate
{
public CHeaders(Driver driver, TranslationUnit unit) : base(driver, unit)
{
}
public override string FileExtension
{
get { return "h"; }
}
public override void Process()
{
GenerateFilePreamble();
PushBlock(CBlockKind.Includes);
WriteLine("#pragma once");
WriteLine("#include <stdbool.h>");
PopBlock(NewLineKind.BeforeNextBlock);
VisitDeclContext(Unit);
}
public override bool VisitClassDecl(Class @class)
{
PushBlock(CBlockKind.Class);
PushBlock();
WriteLine("typedef struct {0} {0};", @class.Name);
PopBlock(NewLineKind.BeforeNextBlock);
VisitDeclContext(@class);
PopBlock(NewLineKind.BeforeNextBlock);
return true;
}
public override bool VisitMethodDecl(Method method)
{
PushBlock(CBlockKind.Function);
GenerateMethodSignature(method);
WriteLine(";");
PopBlock();
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,154 @@
using CppSharp.AST;
using CppSharp.AST.Extensions;
namespace MonoManagedToNative.Generators
{
public class CSources : CTemplate
{
public CSources(Driver driver, TranslationUnit unit) : base(driver, unit)
{
}
public override string FileExtension
{
get { return "c"; }
}
public string AssemblyId
{
get
{
return GeneratedIdentifier(Assembly.GetName().Name).Replace('.', '_');
}
}
public override void Process()
{
GenerateFilePreamble();
PushBlock();
WriteLine("#include \"{0}.h\"", Unit.Name);
WriteLine("#include <mono/jit/jit.h>");
WriteLine("#include <mono/metadata/assembly.h>");
//WriteLine("#include <mono/metadata/domain.h>");
WriteLine("#include <mono/metadata/object.h>");
WriteLine("#include <mono/metadata/mono-config.h>");
WriteLine("#include <mono/metadata/debug-helpers.h>");
PopBlock(NewLineKind.BeforeNextBlock);
PushBlock();
WriteLine("extern MonoDomain* {0};", GeneratedIdentifier("mono_domain"));
WriteLine("extern bool {0};", GeneratedIdentifier("mono_initialized"));
WriteLine("extern MonoAssembly* {0}_assembly;", AssemblyId);
PopBlock(NewLineKind.BeforeNextBlock);
GenerateMonoInitialization();
GenerateAssemblyLoad();
VisitDeclContext(Unit);
}
public override bool VisitClassDecl(Class @class)
{
PushBlock();
WriteLine("static MonoClass* {0}_class = 0;", @class.QualifiedName);
WriteLine("static MonoObject* {0}_instance = 0;", @class.QualifiedName);
PopBlock(NewLineKind.BeforeNextBlock);
VisitDeclContext(@class);
return true;
}
public void GenerateMonoInitialization()
{
PushBlock();
WriteLine("static void {0}()", GeneratedIdentifier("initialize_mono"));
WriteStartBraceIndent();
WriteLine("if ({0})", GeneratedIdentifier("mono_initialized"));
WriteLineIndent("return;");
var domainName = "mono_managed_to_native_binding";
var version = "v4.0.30319";
WriteLine("{0} = mono_jit_init_version(\"{1}\", \"{2}\");",
GeneratedIdentifier("mono_domain"), domainName, version);
WriteCloseBraceIndent();
PopBlock(NewLineKind.BeforeNextBlock);
}
public void GenerateAssemblyLoad()
{
var assemblyName = Assembly.GetName().Name;
var assemblyLookupId = GeneratedIdentifier(string.Format("lookup_{0}_assembly",
assemblyName.Replace('.', '_')));
PushBlock();
WriteLine("static void {0}()", assemblyLookupId);
WriteStartBraceIndent();
var monoAssemblyName = string.Format("{0}_assembly", AssemblyId);
WriteLine("if ({0})", monoAssemblyName);
WriteLineIndent("return;");
WriteLine("{0} = mono_domain_assembly_open({1}, \"{2}.dll\");",
monoAssemblyName, GeneratedIdentifier("mono_domain"), assemblyName);
WriteCloseBraceIndent();
PopBlock(NewLineKind.BeforeNextBlock);
}
public void GenerateMethodLookup(Method method)
{
var methodId = GeneratedIdentifier("method");
WriteLine("const char {0}[] = \"{1}\";", methodId, method.OriginalName);
var descId = GeneratedIdentifier("desc");
WriteLine("MonoMethodDesc* {0} = mono_method_desc_new({1}, /*include_namespace=*/true);",
descId, methodId);
var createInstanceId = GeneratedIdentifier("createInstance");
var @class = method.Namespace as Class;
var classId = string.Format("{0}_class", @class.QualifiedName);
WriteLine("MonoMethod* {0} = mono_method_desc_search_in_class({1}, {2});",
createInstanceId, descId, classId);
WriteLine("mono_method_desc_free({0});", descId);
}
public void GenerateMethodInvocation(Method method)
{
//MonoObject* result;
//mono_runtime_invoke(lldbDebuggerCreateInstanceMethod, &result, args, nullptr);
}
public override bool VisitMethodDecl(Method method)
{
PushBlock();
GenerateMethodSignature(method);
NewLine();
WriteStartBraceIndent();
GenerateMethodLookup(method);
var retType = method.ReturnType;
var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void);
GenerateMethodInvocation(method);
if (needsReturn)
WriteLine("return 0;");
WriteCloseBraceIndent();
PopBlock(NewLineKind.BeforeNextBlock);
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,480 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using Type = CppSharp.AST.Type;
namespace CppSharp.Types
{
public enum TypePrinterContextKind
{
Normal,
Template
}
public abstract class TypePrinterContext
{
protected TypePrinterContext()
{
Kind = TypePrinterContextKind.Normal;
}
protected TypePrinterContext(TypePrinterContextKind kind)
{
Kind = kind;
}
public string GetTemplateParameterList()
{
if (Kind == TypePrinterContextKind.Template)
{
var template = (Template)Declaration;
return string.Join(", ", template.Parameters.Select(p => p.Name));
}
var type = Type.Desugar();
IEnumerable<TemplateArgument> templateArgs;
var templateSpecializationType = type as TemplateSpecializationType;
if (templateSpecializationType != null)
templateArgs = templateSpecializationType.Arguments;
else
templateArgs = ((ClassTemplateSpecialization)((TagType)type).Declaration).Arguments;
var paramsList = new List<string>();
foreach (var arg in templateArgs.Where(a => a.Kind == TemplateArgument.ArgumentKind.Type))
{
var argType = arg.Type.Type.IsPointerToPrimitiveType()
? new CILType(typeof(System.IntPtr))
: arg.Type.Type;
paramsList.Add(argType.ToString());
}
return string.Join(", ", paramsList);
}
public TypePrinterContextKind Kind;
public Declaration Declaration;
public Parameter Parameter;
public Type Type;
}
public interface ITypePrinter
{
string ToString(Type type);
}
public interface ITypePrinter<out T> : ITypePrinter, ITypeVisitor<T>
{
T VisitParameters(IEnumerable<Parameter> @params, bool hasNames);
T VisitParameter(Parameter param, bool hasName = true);
T VisitDelegate(FunctionType function);
}
public enum CppTypePrintScopeKind
{
Local,
Qualified,
GlobalQualified
}
public enum CppTypePrintFlavorKind
{
C,
Cpp,
}
public class CppTypePrinter : ITypePrinter<string>, IDeclVisitor<string>
{
public CppTypePrintFlavorKind PrintFlavorKind;
public CppTypePrintScopeKind PrintScopeKind;
public bool PrintLogicalNames;
public bool PrintTypeQualifiers;
public CppTypePrinter(bool printTypeQualifiers = true)
{
PrintFlavorKind = CppTypePrintFlavorKind.Cpp;
PrintScopeKind = CppTypePrintScopeKind.GlobalQualified;
PrintTypeQualifiers = printTypeQualifiers;
}
public bool ResolveTypedefs { get; set; }
public string VisitTagType(TagType tag, TypeQualifiers quals)
{
return tag.Declaration.Visit(this);
}
public string VisitArrayType(ArrayType array, TypeQualifiers quals)
{
var typeName = array.Type.Visit(this);
switch (array.SizeType)
{
case ArrayType.ArraySize.Constant:
return string.Format("{0}[{1}]", typeName, array.Size);
case ArrayType.ArraySize.Variable:
case ArrayType.ArraySize.Dependent:
case ArrayType.ArraySize.Incomplete:
return string.Format("{0}[]", typeName);
}
throw new NotSupportedException();
}
static string ConvertModifierToString(PointerType.TypeModifier modifier)
{
switch (modifier)
{
case PointerType.TypeModifier.Value: return string.Empty;
case PointerType.TypeModifier.Pointer: return "*";
case PointerType.TypeModifier.LVReference: return "&";
case PointerType.TypeModifier.RVReference: return "&&";
}
return string.Empty;
}
public string VisitPointerType(PointerType pointer, TypeQualifiers quals)
{
var pointee = pointer.Pointee;
var function = pointee as FunctionType;
if (function != null)
{
var arguments = function.Parameters;
var returnType = function.ReturnType;
var args = string.Empty;
if (arguments.Count > 0)
args = VisitParameters(function.Parameters, hasNames: false);
return string.Format("{0} (*)({1})", returnType.Visit(this), args);
}
var pointeeType = pointer.Pointee.Visit(this, quals);
var mod = ConvertModifierToString(pointer.Modifier);
var s = PrintTypeQualifiers && quals.IsConst ? "const " : string.Empty;
s += string.Format("{0}{1}", pointeeType, mod);
return s;
}
public string VisitMemberPointerType(MemberPointerType member, TypeQualifiers quals)
{
return string.Empty;
}
public string VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals)
{
return VisitPrimitiveType(builtin.Type);
}
public string VisitPrimitiveType(PrimitiveType primitive)
{
switch (primitive)
{
case PrimitiveType.Bool: return "bool";
case PrimitiveType.Void: return "void";
case PrimitiveType.Char16:
case PrimitiveType.WideChar: return "wchar_t";
case PrimitiveType.Char: return "char";
case PrimitiveType.UChar: return "unsigned char";
case PrimitiveType.Short: return "short";
case PrimitiveType.UShort: return "unsigned short";
case PrimitiveType.Int: return "int";
case PrimitiveType.UInt: return "unsigned int";
case PrimitiveType.Long: return "long";
case PrimitiveType.ULong: return "unsigned long";
case PrimitiveType.LongLong: return "long long";
case PrimitiveType.ULongLong: return "unsigned long long";
case PrimitiveType.Float: return "float";
case PrimitiveType.Double: return "double";
case PrimitiveType.LongDouble: return "long double";
case PrimitiveType.IntPtr: return "void*";
case PrimitiveType.UIntPtr: return "uintptr_t";
case PrimitiveType.Null: return "std::nullptr_t";
}
throw new NotSupportedException();
}
public string VisitTypedefType(TypedefType typedef, TypeQualifiers quals)
{
if (ResolveTypedefs)
{
var decl = typedef.Declaration;
FunctionType func;
return decl.Type.IsPointerTo(out func) ? VisitDeclaration(decl) : decl.Type.Visit(this);
}
return GetDeclName(typedef.Declaration);
}
public string VisitAttributedType(AttributedType attributed, TypeQualifiers quals)
{
return attributed.Modified.Visit(this);
}
public string VisitDecayedType(DecayedType decayed, TypeQualifiers quals)
{
return decayed.Decayed.Visit(this);
}
public string VisitTemplateSpecializationType(TemplateSpecializationType template, TypeQualifiers quals)
{
var specialization = template.GetClassTemplateSpecialization();
if (specialization == null)
return string.Empty;
return VisitClassTemplateSpecializationDecl(specialization);
}
public string VisitDependentTemplateSpecializationType(
DependentTemplateSpecializationType template, TypeQualifiers quals)
{
if (template.Desugared != null)
return template.Desugared.Visit(this);
return string.Empty;
}
public string VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals)
{
if (param.Parameter.Name == null)
return string.Empty;
return param.Parameter.Name;
}
public string VisitTemplateParameterSubstitutionType(
TemplateParameterSubstitutionType param, TypeQualifiers quals)
{
return param.Replacement.Visit(this);
}
public string VisitInjectedClassNameType(InjectedClassNameType injected, TypeQualifiers quals)
{
return injected.Class.Visit(this);
}
public string VisitDependentNameType(DependentNameType dependent, TypeQualifiers quals)
{
return dependent.Desugared != null ? dependent.Desugared.Visit(this) : string.Empty;
}
public string VisitPackExpansionType(PackExpansionType packExpansionType, TypeQualifiers quals)
{
return string.Empty;
}
public Func<CILType , TypeQualifiers, string> CILTypePrinter;
public string VisitCILType(CILType type, TypeQualifiers quals)
{
if (CILTypePrinter != null)
return CILTypePrinter(type, quals);
return string.Empty;
}
public string VisitPrimitiveType(PrimitiveType type, TypeQualifiers quals)
{
throw new System.NotImplementedException();
}
public string VisitDeclaration(Declaration decl, TypeQualifiers quals)
{
throw new System.NotImplementedException();
}
public string VisitFunctionType(FunctionType function, TypeQualifiers quals)
{
var arguments = function.Parameters;
var returnType = function.ReturnType;
var args = string.Empty;
if (arguments.Count > 0)
args = VisitParameters(function.Parameters, hasNames: false);
return string.Format("{0} ({1})", returnType.Visit(this), args);
}
public string VisitParameters(IEnumerable<Parameter> @params,
bool hasNames)
{
var args = new List<string>();
foreach (var param in @params)
args.Add(VisitParameter(param, hasNames));
return string.Join(", ", args);
}
public string VisitParameter(Parameter arg, bool hasName = true)
{
var type = arg.Type.Visit(this, arg.QualifiedType.Qualifiers);
var name = arg.Name;
if (hasName && !string.IsNullOrEmpty(name))
return string.Format("{0} {1}", type, name);
return type;
}
public string VisitDelegate(FunctionType function)
{
throw new System.NotImplementedException();
}
public string GetDeclName(Declaration declaration)
{
switch (PrintScopeKind)
{
case CppTypePrintScopeKind.Local:
return PrintLogicalNames ? declaration.LogicalOriginalName
: declaration.OriginalName;
case CppTypePrintScopeKind.Qualified:
return PrintLogicalNames ? declaration.QualifiedLogicalOriginalName
: declaration.QualifiedOriginalName;
case CppTypePrintScopeKind.GlobalQualified:
return "::" + (PrintLogicalNames ? declaration.QualifiedLogicalOriginalName
: declaration.QualifiedOriginalName);
}
throw new NotSupportedException();
}
public string VisitDeclaration(Declaration decl)
{
return GetDeclName(decl);
}
public string VisitClassDecl(Class @class)
{
return VisitDeclaration(@class);
}
public string VisitClassTemplateSpecializationDecl(ClassTemplateSpecialization specialization)
{
return string.Format("{0}<{1}>", specialization.TemplatedDecl.Visit(this),
string.Join(", ",
specialization.Arguments.Where(
a => a.Type.Type != null &&
!(a.Type.Type is DependentNameType)).Select(a => a.Type.Visit(this))));
}
public string VisitFieldDecl(Field field)
{
return VisitDeclaration(field);
}
public string VisitFunctionDecl(Function function)
{
return VisitDeclaration(function);
}
public string VisitMethodDecl(Method method)
{
return VisitDeclaration(method);
}
public string VisitParameterDecl(Parameter parameter)
{
return VisitParameter(parameter, hasName: false);
}
public string VisitTypedefDecl(TypedefDecl typedef)
{
return VisitDeclaration(typedef);
}
public string VisitTypeAliasDecl(TypeAlias typeAlias)
{
return VisitDeclaration(typeAlias);
}
public string VisitEnumDecl(Enumeration @enum)
{
return VisitDeclaration(@enum);
}
public string VisitEnumItemDecl(Enumeration.Item item)
{
return VisitDeclaration(item);
}
public string VisitVariableDecl(Variable variable)
{
return VisitDeclaration(variable);
}
public string VisitClassTemplateDecl(ClassTemplate template)
{
return VisitDeclaration(template);
}
public string VisitFunctionTemplateDecl(FunctionTemplate template)
{
return VisitDeclaration(template);
}
public string VisitMacroDefinition(MacroDefinition macro)
{
throw new NotImplementedException();
}
public string VisitNamespace(Namespace @namespace)
{
return VisitDeclaration(@namespace);
}
public string VisitEvent(Event @event)
{
return string.Empty;
}
public string VisitProperty(Property property)
{
return VisitDeclaration(property);
}
public string VisitFriend(Friend friend)
{
throw new NotImplementedException();
}
public string ToString(Type type)
{
return type.Visit(this);
}
public string VisitTemplateTemplateParameterDecl(TemplateTemplateParameter templateTemplateParameter)
{
return templateTemplateParameter.Name;
}
public string VisitTemplateParameterDecl(TypeTemplateParameter templateParameter)
{
if (templateParameter.DefaultArgument.Type == null)
return templateParameter.Name;
return string.Format("{0} = {1}", templateParameter.Name,
templateParameter.DefaultArgument.Visit(this));
}
public string VisitNonTypeTemplateParameterDecl(NonTypeTemplateParameter nonTypeTemplateParameter)
{
if (nonTypeTemplateParameter.DefaultArgument == null)
return nonTypeTemplateParameter.Name;
return string.Format("{0} = {1}", nonTypeTemplateParameter.Name,
nonTypeTemplateParameter.DefaultArgument.String);
}
public string VisitTypeAliasTemplateDecl(TypeAliasTemplate typeAliasTemplate)
{
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
using IKVM.Reflection;
using System.Collections.Generic;
namespace MonoManagedToNative.Generators
{
public enum GeneratorKind
{
C
}
/// <summary>
/// Generators are the base class for each language backend.
/// </summary>
public abstract class Generator
{
public Driver Driver { get; private set; }
protected Generator(Driver driver)
{
Driver = driver;
}
/// <summary>
/// Generates the outputs for a given translation unit.
/// </summary>
public abstract List<Template> Generate(Assembly assembly);
}
}

Просмотреть файл

@ -0,0 +1,29 @@
using IKVM.Reflection;
namespace MonoManagedToNative.Generators
{
public abstract class Template : BlockGenerator
{
public Driver Driver { get; private set; }
public Options Options { get; private set; }
public IDiagnostics Diagnostics
{
get { return Driver.Diagnostics; }
}
public Assembly Assembly { get; set; }
protected Template(Driver driver)
{
Driver = driver;
Options = driver.Options;
}
public abstract string Name { get; }
public abstract string FileExtension { get; }
public abstract void Process();
}
}

28
binder/Options.cs Normal file
Просмотреть файл

@ -0,0 +1,28 @@
using MonoManagedToNative.Generators;
namespace MonoManagedToNative
{
public class Options
{
public Options()
{
Project = new Project();
}
public Project Project;
// General options
public bool ShowHelpText;
public bool OutputDebug;
// Parser options
public bool IgnoreParseErrors;
// Generator options
public string LibraryName;
public GeneratorKind Language;
public string OutputNamespace;
public string OutputDir;
}
}

130
binder/Parser.cs Normal file
Просмотреть файл

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace MonoManagedToNative
{
public enum ParserDiagnosticLevel
{
Ignored,
Note,
Warning,
Error,
Fatal
}
public struct ParserDiagnostic
{
public string FileName;
public string Message;
public ParserDiagnosticLevel Level;
public int LineNumber;
public int ColumnNumber;
}
public enum ParserResultKind
{
Success,
Error,
FileNotFound
}
public class ParserResult<T>
{
public ParserResult()
{
Kind = ParserResultKind.Success;
Diagnostics = new List<ParserDiagnostic>();
}
public ProjectInput Input;
public ParserResultKind Kind;
public List<ParserDiagnostic> Diagnostics;
public string Message;
public T Output;
public bool HasErrors
{
get
{
return Diagnostics.Any(diagnostic =>
diagnostic.Level == ParserDiagnosticLevel.Error ||
diagnostic.Level == ParserDiagnosticLevel.Fatal);
}
}
}
public delegate void ParserHandler<T>(ProjectInput input, ParserResult<T> result);
public class Parser
{
private readonly Options Options;
private readonly IDiagnostics Diagnostics;
public delegate void ParsedDelegate<T>(ParserResult<T> result);
public ParsedDelegate<IKVM.Reflection.Assembly> OnAssemblyParsed = delegate { };
IKVM.Reflection.Universe Universe;
public Parser(Options options, IDiagnostics diagostics)
{
Options = options;
Diagnostics = diagostics;
Universe = new IKVM.Reflection.Universe(IKVM.Reflection.UniverseOptions.MetadataOnly);
}
public void ParseAssembly(ProjectInput input, ParserResult<IKVM.Reflection.Assembly> result)
{
try
{
try
{
var res = Universe.LoadFile(input.FullPath);
result.Output = res;
}
catch (Exception ex)
{
result.Message = ex.ToString();
result.Kind = ParserResultKind.Error;
}
}
finally
{
OnAssemblyParsed(result);
}
}
ParserResult<T> ParseInput<T>(ProjectInput input, ParserHandler<T> handler)
{
var result = new ParserResult<T>
{
Input = input
};
if (!File.Exists(input.FullPath))
{
result.Kind = ParserResultKind.FileNotFound;
return result;
}
handler(input, result);
return result;
}
public bool Parse(Project project)
{
var hasErrors = false;
foreach (var input in project.AssemblyInputs)
{
var result = ParseInput<IKVM.Reflection.Assembly>(input, ParseAssembly);
hasErrors |= result.HasErrors;
}
return !hasErrors;
}
}
}

106
binder/Project.cs Normal file
Просмотреть файл

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace MonoManagedToNative
{
/// <summary>
/// Represents an input file in the binding project.
/// </summary>
public class ProjectInput
{
/// <summary>
/// Full path to the input file.
/// </summary>
public string FullPath;
/// <summary>
/// Base path to the input file.
/// </summary>
public string BasePath;
}
/// <summary>
/// Represents the output generated for the binding project.
/// </summary>
public class ProjectOutput
{
public Dictionary<string, Stream> Files;
public ProjectOutput()
{
Files = new Dictionary<string, Stream>();
}
public Stream GetOutput(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new NotSupportedException("Invalid path");
var stream = new MemoryStream();
Files[path] = stream;
return stream;
}
public Stream WriteOutput(string path, string content)
{
var stream = GetOutput(path);
var bytes = Encoding.UTF8.GetBytes(content);
stream.Write(bytes, 0, bytes.Length);
return stream;
}
}
/// <summary>
/// Represents a binding project.
/// </summary>
public class Project
{
public string Name;
public string OutputPath;
public List<string> AssemblyDirs;
public List<string> Assemblies;
internal List<ProjectInput> AssemblyInputs;
public Project()
{
AssemblyDirs = new List<string>();
Assemblies = new List<string>();
AssemblyInputs = new List<ProjectInput>();
}
public void BuildInputs()
{
foreach (var path in AssemblyDirs)
{
if (!Directory.Exists(path)) continue;
var files = Directory.EnumerateFiles(path, "*.dll");
foreach (var file in files)
{
var matches = false;
foreach (var assembly in Assemblies)
matches |= Regex.IsMatch(Path.GetFileName(file), assembly);
if (!matches) continue;
var input = new ProjectInput
{
BasePath = path,
FullPath = file,
};
AssemblyInputs.Add(input);
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,95 @@
using IKVM.Reflection;
using System;
namespace MonoManagedToNative.Generators
{
public class ReflectionVisitor<T>
{
public virtual T Visit(Assembly assembly)
{
foreach (var type in assembly.ExportedTypes)
{
Visit(type.GetTypeInfo());
}
return default(T);
}
public T Visit(TypeInfo typeInfo)
{
if (typeInfo.IsClass)
return VisitClass(typeInfo);
else if (typeInfo.IsEnum)
return VisitEnum(typeInfo);
else if (typeInfo.IsInterface)
return VisitInterface(typeInfo);
else if (typeInfo.IsValueType)
return VisitStruct(typeInfo);
throw new Exception("Could not visit type " + typeInfo.ToString());
}
public virtual void VisitMembers(TypeInfo type)
{
foreach (var ctor in type.DeclaredConstructors)
VisitConstructor(ctor);
foreach (var method in type.DeclaredMethods)
VisitMethod(method);
foreach (var field in type.DeclaredFields)
VisitField(field);
foreach (var @event in type.DeclaredEvents)
VisitEvent(@event);
foreach (var property in type.DeclaredProperties)
VisitProperty(property);
}
public virtual T VisitClass(TypeInfo @class)
{
return default(T);
}
public virtual T VisitEnum(TypeInfo @enum)
{
return default(T);
}
public virtual T VisitInterface(TypeInfo @interface)
{
return default(T);
}
public virtual T VisitStruct(TypeInfo @struct)
{
return default(T);
}
public virtual T VisitConstructor(ConstructorInfo ctor)
{
return default(T);
}
public virtual T VisitMethod(MethodInfo method)
{
return default(T);
}
public virtual T VisitField(FieldInfo field)
{
return default(T);
}
public virtual T VisitEvent(EventInfo @event)
{
return default(T);
}
public virtual T VisitProperty(PropertyInfo @property)
{
return default(T);
}
}
}

Просмотреть файл

@ -0,0 +1,87 @@
using MonoManagedToNative.Generators;
using System;
using System.IO;
using System.Reflection;
namespace MonoManagedToNative.Tests
{
/// <summary>
/// The main base class for a generator-based tests project.
/// </summary>
public abstract class TestsGenerator
{
readonly string name;
readonly GeneratorKind languageKind;
readonly Driver driver;
readonly Options options;
protected TestsGenerator(string name, GeneratorKind languageKind)
{
this.name = name;
this.languageKind = languageKind;
options = new Options();
driver = new Driver(options);
Setup();
}
public virtual void Setup()
{
var outputDir = GetOutputDirectory();
options.OutputDir = Path.Combine(outputDir, "gen", name);
options.LibraryName = name;
driver.Diagnostics.Message("");
driver.Diagnostics.Message("Generating bindings for {0} ({1})",
options.LibraryName, options.Language.ToString());
var currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
options.Project.AssemblyDirs.Add(currentDir);
//options.Project.Assemblies.Add(Path.Combine(currentDir, name + ".Managed.dll"));
options.Project.Assemblies.Add(name + ".Managed.dll");
}
public virtual void Generate()
{
driver.Run();
}
#region Helpers
public static string GetTestsDirectory(string name)
{
var directory = Directory.GetParent(Directory.GetCurrentDirectory());
while (directory != null)
{
var path = Path.Combine(directory.FullName, "tests", name);
if (Directory.Exists(path))
return path;
directory = directory.Parent;
}
throw new Exception(string.Format(
"Tests directory for project '{0}' was not found", name));
}
static string GetOutputDirectory()
{
var directory = Directory.GetParent(Directory.GetCurrentDirectory());
while (directory != null)
{
var path = Path.Combine(directory.FullName, "obj");
if (Directory.Exists(path))
return directory.FullName;
directory = directory.Parent;
}
throw new Exception("Could not find tests output directory");
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,399 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MonoManagedToNative.Generators
{
public enum NewLineKind
{
Never,
Always,
BeforeNextBlock,
IfNotEmpty
}
public class BlockKind
{
public const int Unknown = 0;
public const int BlockComment = 1;
public const int InlineComment = 2;
public const int Header = 3;
public const int Footer = 4;
public const int LAST = 5;
}
public class Block : ITextGenerator
{
public TextGenerator Text { get; set; }
public int Kind { get; set; }
public NewLineKind NewLineKind { get; set; }
public object Object { get; set; }
public Block Parent { get; set; }
public List<Block> Blocks { get; set; }
private bool hasIndentChanged;
private bool isSubBlock;
public Func<bool> CheckGenerate;
public Block() : this(BlockKind.Unknown)
{
}
public Block(int kind)
{
Kind = kind;
Blocks = new List<Block>();
Text = new TextGenerator();
hasIndentChanged = false;
isSubBlock = false;
}
public void AddBlock(Block block)
{
if (Text.StringBuilder.Length != 0 || hasIndentChanged)
{
hasIndentChanged = false;
var newBlock = new Block { Text = Text.Clone(), isSubBlock = true };
Text.StringBuilder.Clear();
AddBlock(newBlock);
}
block.Parent = this;
Blocks.Add(block);
}
public IEnumerable<Block> FindBlocks(int kind)
{
foreach (var block in Blocks)
{
if (block.Kind == kind)
yield return block;
foreach (var childBlock in block.FindBlocks(kind))
yield return childBlock;
}
}
public virtual string Generate()
{
if (CheckGenerate != null && !CheckGenerate())
return "";
if (Blocks.Count == 0)
return Text.ToString();
var builder = new StringBuilder();
uint totalIndent = 0;
Block previousBlock = null;
var blockIndex = 0;
foreach (var childBlock in Blocks)
{
var childText = childBlock.Generate();
var nextBlock = (++blockIndex < Blocks.Count)
? Blocks[blockIndex]
: null;
var skipBlock = false;
if (nextBlock != null)
{
var nextText = nextBlock.Generate();
if (string.IsNullOrEmpty(nextText) &&
childBlock.NewLineKind == NewLineKind.IfNotEmpty)
skipBlock = true;
}
if (skipBlock)
continue;
if (string.IsNullOrEmpty(childText))
continue;
var lines = childText.SplitAndKeep(Environment.NewLine).ToList();
if (previousBlock != null &&
previousBlock.NewLineKind == NewLineKind.BeforeNextBlock)
builder.AppendLine();
if (childBlock.isSubBlock)
totalIndent = 0;
foreach (var line in lines)
{
if (string.IsNullOrEmpty(line))
continue;
if (!string.IsNullOrWhiteSpace(line))
builder.Append(new string(' ', (int)totalIndent));
builder.Append(line);
if (!line.EndsWith(Environment.NewLine, StringComparison.Ordinal))
builder.AppendLine();
}
if (childBlock.NewLineKind == NewLineKind.Always)
builder.AppendLine();
totalIndent += childBlock.Text.Indent;
previousBlock = childBlock;
}
if (Text.StringBuilder.Length != 0)
builder.Append(Text.StringBuilder);
return builder.ToString();
}
public StringBuilder GenerateUnformatted(Options options)
{
if (CheckGenerate != null && !CheckGenerate())
return new StringBuilder(0);
if (Blocks.Count == 0)
return Text.StringBuilder;
var builder = new StringBuilder();
Block previousBlock = null;
var blockIndex = 0;
foreach (var childBlock in Blocks)
{
var childText = childBlock.GenerateUnformatted(options);
var nextBlock = (++blockIndex < Blocks.Count)
? Blocks[blockIndex]
: null;
if (nextBlock != null)
{
var nextText = nextBlock.GenerateUnformatted(options);
if (nextText.Length == 0 &&
childBlock.NewLineKind == NewLineKind.IfNotEmpty)
continue;
}
if (childText.Length == 0)
continue;
if (previousBlock != null &&
previousBlock.NewLineKind == NewLineKind.BeforeNextBlock)
builder.AppendLine();
builder.Append(childText);
if (childBlock.NewLineKind == NewLineKind.Always)
builder.AppendLine();
previousBlock = childBlock;
}
if (Text.StringBuilder.Length != 0)
builder.Append(Text.StringBuilder);
return builder;
}
public bool IsEmpty
{
get
{
if (Blocks.Any(block => !block.IsEmpty))
return false;
return string.IsNullOrEmpty(Text.ToString());
}
}
#region ITextGenerator implementation
public uint Indent { get { return Text.Indent; } }
public void Write(string msg, params object[] args)
{
Text.Write(msg, args);
}
public void WriteLine(string msg, params object[] args)
{
Text.WriteLine(msg, args);
}
public void WriteLineIndent(string msg, params object[] args)
{
Text.WriteLineIndent(msg, args);
}
public void NewLine()
{
Text.NewLine();
}
public void NewLineIfNeeded()
{
Text.NewLineIfNeeded();
}
public void NeedNewLine()
{
Text.NeedNewLine();
}
public void ResetNewLine()
{
Text.ResetNewLine();
}
public void PushIndent(uint indent = 4u)
{
hasIndentChanged = true;
Text.PushIndent(indent);
}
public void PopIndent()
{
hasIndentChanged = true;
Text.PopIndent();
}
public void WriteStartBraceIndent()
{
Text.WriteStartBraceIndent();
}
public void WriteCloseBraceIndent()
{
Text.WriteCloseBraceIndent();
}
#endregion
}
public abstract class BlockGenerator : ITextGenerator
{
public Block RootBlock { get; private set; }
public Block ActiveBlock { get; private set; }
protected BlockGenerator()
{
RootBlock = new Block();
ActiveBlock = RootBlock;
}
public string Generate()
{
return RootBlock.Generate();
}
#region Block helpers
public void AddBlock(Block block)
{
ActiveBlock.AddBlock(block);
}
public void PushBlock(int kind = 0, object obj = null)
{
var block = new Block { Kind = kind, Object = obj };
PushBlock(block);
}
public void PushBlock(Block block)
{
block.Parent = ActiveBlock;
ActiveBlock.AddBlock(block);
ActiveBlock = block;
}
public Block PopBlock(NewLineKind newLineKind = NewLineKind.Never)
{
var block = ActiveBlock;
ActiveBlock.NewLineKind = newLineKind;
ActiveBlock = ActiveBlock.Parent;
return block;
}
public IEnumerable<Block> FindBlocks(int kind)
{
return RootBlock.FindBlocks(kind);
}
public Block FindBlock(int kind)
{
return FindBlocks(kind).SingleOrDefault();
}
#endregion
#region ITextGenerator implementation
public uint Indent { get { return ActiveBlock.Indent; } }
public void Write(string msg, params object[] args)
{
ActiveBlock.Write(msg, args);
}
public void WriteLine(string msg, params object[] args)
{
ActiveBlock.WriteLine(msg, args);
}
public void WriteLineIndent(string msg, params object[] args)
{
ActiveBlock.WriteLineIndent(msg, args);
}
public void NewLine()
{
ActiveBlock.NewLine();
}
public void NewLineIfNeeded()
{
ActiveBlock.NewLineIfNeeded();
}
public void NeedNewLine()
{
ActiveBlock.NeedNewLine();
}
public void ResetNewLine()
{
ActiveBlock.ResetNewLine();
}
public void PushIndent(uint indent = 4u)
{
ActiveBlock.PushIndent(indent);
}
public void PopIndent()
{
ActiveBlock.PopIndent();
}
public void WriteStartBraceIndent()
{
ActiveBlock.WriteStartBraceIndent();
}
public void WriteCloseBraceIndent()
{
ActiveBlock.WriteCloseBraceIndent();
}
#endregion
}
}

1434
binder/Utils/Options.cs Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MonoManagedToNative
{
public interface ITextGenerator
{
uint Indent { get; }
void Write(string msg, params object[] args);
void WriteLine(string msg, params object[] args);
void WriteLineIndent(string msg, params object[] args);
void NewLine();
void NewLineIfNeeded();
void NeedNewLine();
void ResetNewLine();
void PushIndent(uint indent = TextGenerator.DefaultIndent);
void PopIndent();
void WriteStartBraceIndent();
void WriteCloseBraceIndent();
}
public class TextGenerator : ITextGenerator
{
public const uint DefaultIndent = 4;
public StringBuilder StringBuilder;
protected bool IsStartOfLine;
protected bool NeedsNewLine;
protected readonly Stack<uint> CurrentIndent;
public uint Indent
{
get { return (uint)CurrentIndent.Sum(u => (int)u); }
}
public TextGenerator()
{
StringBuilder = new StringBuilder();
IsStartOfLine = false;
CurrentIndent = new Stack<uint>();
}
public TextGenerator(TextGenerator generator)
{
StringBuilder = new StringBuilder(generator);
IsStartOfLine = generator.IsStartOfLine;
NeedsNewLine = generator.NeedsNewLine;
CurrentIndent = new Stack<uint>(generator.CurrentIndent);
}
public TextGenerator Clone()
{
return new TextGenerator(this);
}
public void Write(string msg, params object[] args)
{
if (string.IsNullOrEmpty(msg))
return;
if (args.Length > 0)
msg = string.Format(msg, args);
foreach (var line in msg.SplitAndKeep(Environment.NewLine))
{
if (IsStartOfLine && !string.IsNullOrWhiteSpace(line))
StringBuilder.Append(new string(' ', (int)CurrentIndent.Sum(u => u)));
if (line.Length > 0)
IsStartOfLine = line.EndsWith(Environment.NewLine);
StringBuilder.Append(line);
}
}
public void WriteLine(string msg, params object[] args)
{
Write(msg, args);
NewLine();
}
public void WriteLineIndent(string msg, params object[] args)
{
PushIndent();
WriteLine(msg, args);
PopIndent();
}
public void NewLine()
{
StringBuilder.AppendLine(string.Empty);
IsStartOfLine = true;
}
public void NewLineIfNeeded()
{
if (!NeedsNewLine) return;
NewLine();
NeedsNewLine = false;
}
public void NeedNewLine()
{
NeedsNewLine = true;
}
public void ResetNewLine()
{
NeedsNewLine = false;
}
public void PushIndent(uint indent = DefaultIndent)
{
CurrentIndent.Push(indent);
}
public void PopIndent()
{
CurrentIndent.Pop();
}
public void WriteStartBraceIndent()
{
WriteLine("{");
PushIndent();
}
public void WriteCloseBraceIndent()
{
PopIndent();
WriteLine("}");
}
public override string ToString()
{
return StringBuilder.ToString();
}
public static implicit operator string(TextGenerator tg)
{
return tg.ToString();
}
}
}

67
binder/Utils/Utils.cs Normal file
Просмотреть файл

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace MonoManagedToNative
{
public static class StringExtensions
{
public static string Repeat(this char chatToRepeat, int repeat)
{
return new string(chatToRepeat, repeat);
}
public static string Repeat(this string stringToRepeat, int repeat)
{
var builder = new StringBuilder(repeat * stringToRepeat.Length);
for (var i = 0; i < repeat; i++)
{
builder.Append(stringToRepeat);
}
return builder.ToString();
}
public static IEnumerable<string> SplitAndKeep(this string s, string separator)
{
string[] obj = s.Split(new string[] { separator }, StringSplitOptions.None);
for (int i = 0; i < obj.Length; i++)
{
string result = i == obj.Length - 1 ? obj[i] : obj[i] + separator;
yield return result;
}
}
}
public static class Helpers
{
public static void CopyDirectory(string sourceDir, string targetDir)
{
var source = new DirectoryInfo(sourceDir);
var target = new DirectoryInfo(targetDir);
CopyAll(source, target);
}
public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
// Check if the target directory exists, if not, create it.
if (!Directory.Exists(target.FullName))
Directory.CreateDirectory(target.FullName);
// Copy each file into it's new directory.
foreach (var file in source.GetFiles())
file.CopyTo(Path.Combine(target.ToString(), file.Name), true);
// Copy each subdirectory using recursion.
foreach (var sourceSubDir in source.GetDirectories())
{
var nextTargetSubDir =
target.CreateSubdirectory(sourceSubDir.Name);
CopyAll(sourceSubDir, nextTargetSubDir);
}
}
}
}

Просмотреть файл

@ -0,0 +1,32 @@
@echo off
goto menu
:menu
echo Build Project Generator:
echo.
echo [0] Clean
echo [1] Visual C++ 2015
echo [2] GNU Make
echo.
:choice
set /P C="Choice: "
if "%C%"=="2" goto gmake
if "%C%"=="1" goto vs2015
if "%C%"=="0" goto clean
:clean
"premake5" --file=premake5.lua clean
goto quit
:vs2015
"premake5" --file=premake5.lua vs2015
goto quit
:gmake
"premake5" --file=premake5.lua gmake
goto quit
:quit
pause
:end

91
build/Helpers.lua Normal file
Просмотреть файл

@ -0,0 +1,91 @@
-- This module checks for the all the project dependencies.
action = _ACTION or ""
depsdir = path.getabsolute("../deps");
srcdir = path.getabsolute("../src");
incdir = path.getabsolute("../include");
bindir = path.getabsolute("../bin");
examplesdir = path.getabsolute("../examples");
testsdir = path.getabsolute("../tests");
builddir = path.getabsolute("./" .. action);
libdir = path.join(builddir, "lib", "%{cfg.buildcfg}_%{cfg.platform}");
gendir = path.join(builddir, "gen");
function string.starts(str, start)
return string.sub(str, 1, string.len(start)) == start
end
function SafePath(path)
return "\"" .. path .. "\""
end
msvc_buildflags = { }
gcc_buildflags = { "-std=c++11 -fpermissive" }
msvc_cpp_defines = { }
function SetupNativeProject()
location (path.join(builddir, "projects"))
local c = configuration "Debug"
defines { "DEBUG" }
configuration "Release"
defines { "NDEBUG" }
optimize "On"
-- Compiler-specific options
configuration "vs*"
buildoptions { msvc_buildflags }
defines { msvc_cpp_defines }
configuration { "gmake" }
buildoptions { gcc_buildflags }
configuration { "macosx" }
buildoptions { gcc_buildflags, "-stdlib=libc++" }
links { "c++" }
-- OS-specific options
configuration "Windows"
defines { "WIN32", "_WINDOWS" }
configuration(c)
end
function SetupManagedProject()
dotnetframework "4.6"
location (path.join(builddir, "projects"))
local c = configuration "vs*"
location "."
configuration(c)
end
function IncludeDir(dir)
local deps = os.matchdirs(dir .. "/*")
for i,dep in ipairs(deps) do
local fp = path.join(dep, "premake5.lua")
fp = path.join(os.getcwd(), fp)
if os.isfile(fp) then
include(dep)
return
end
fp = path.join(dep, "premake4.lua")
fp = path.join(os.getcwd(), fp)
if os.isfile(fp) then
--print(string.format(" including %s", dep))
include(dep)
end
end
end

121
build/Tests.lua Normal file
Просмотреть файл

@ -0,0 +1,121 @@
-- Tests/examples helpers
function SetupTestProject(name, extraFiles)
SetupTestGeneratorProject(name)
SetupTestNativeProject(name)
SetupTestProjectsCSharp(name, nil, extraFiles)
end
function SetupManagedTestProject()
kind "SharedLib"
language "C#"
flags { "Unsafe" }
SetupManagedProject()
end
function SetupTestGeneratorProject(name, depends)
project(name .. ".Gen")
SetupManagedTestProject()
kind "ConsoleApp"
files { name .. ".Gen.cs" }
dependson { name .. ".Managed" }
linktable = {
"System.Core",
"native-binder",
}
if depends ~= nil then
table.insert(linktable, depends .. ".Gen")
end
links(linktable)
end
function SetupTestGeneratorBuildEvent(name)
local runtimeExe = os.is("windows") and "" or "mono --debug "
if string.starts(action, "vs") then
local exePath = SafePath("$(TargetDir)" .. name .. ".Gen.exe")
prebuildcommands { runtimeExe .. exePath }
else
local exePath = SafePath("%{cfg.buildtarget.directory}/" .. name .. ".Gen.exe")
prebuildcommands { runtimeExe .. exePath }
end
end
local function SetupMono()
local monoDir = ''
-- Find system-specific Mono include/library paths.
-- For Windows, first search the default Mono install location.
local monoDefaultDir = "C:\\Program Files (x86)\\Mono"
if os.isdir(monoDefaultDir) then
monoDir = monoDefaultDir
end
if not os.isdir(monoDir) then
error("Could not find Mono install location, please specify it manually")
end
includedirs { path.join(monoDir, "include", "mono-2.0") }
libdirs { path.join(monoDir, "lib") }
links { "monosgen-2.0" }
end
function SetupTestNativeProject(name, depends)
if string.starts(action, "vs") and not os.is("windows") then
return
end
project(name .. ".C")
SetupNativeProject()
kind "SharedLib"
language "C"
flags { common_flags }
files
{
path.join(gendir, name, name .. ".h"),
path.join(gendir, name, name .. ".c")
}
dependson { name .. ".Gen" }
if depends ~= nil then
links { depends .. ".C" }
end
SetupTestGeneratorBuildEvent(name)
SetupMono()
end
function SetupTestProjectsCSharp(name, depends, extraFiles)
project(name .. ".Managed")
SetupManagedTestProject()
files
{
files { name .. ".cs" }
}
if extraFiles ~= nil then
for _, file in pairs(extraFiles) do
files { file .. ".cs" }
end
end
linktable = { }
if depends ~= nil then
table.insert(linktable, depends .. ".Managed")
end
links(linktable)
project(name .. ".Tests.Managed")
SetupManagedTestProject()
files { name .. ".Tests.cs" }
links { name .. ".Managed" }
dependson { name .. ".C" }
end

59
build/premake5.lua Normal file
Просмотреть файл

@ -0,0 +1,59 @@
-- This is the starting point of the build scripts for the project.
-- It defines the common build settings that all the projects share
-- and calls the build scripts of all the sub-projects.
dofile "Helpers.lua"
dofile "Tests.lua"
solution "native-binder"
configurations { "Debug", "Release" }
flags { "Unicode", "Symbols" }
location (builddir)
objdir (path.join(builddir, "obj"))
targetdir (libdir)
libdirs { libdir }
debugdir (bindir)
startproject "native-binder"
configuration "Release"
flags { "Optimize" }
configuration {}
project "native-binder"
SetupManagedProject()
kind "ConsoleApp"
language "C#"
location "../binder"
files { "../binder/**.cs" }
libdirs { "../deps" }
links
{
"System",
"System.Core",
"IKVM.Reflection",
"CppSharp.AST"
}
external "IKVM.Reflection"
location ("../ikvm/reflect")
uuid "4CB170EF-DFE6-4A56-9E1B-A85449E827A7"
language "C#"
kind "SharedLib"
group "Examples"
print("Searching for example projects...")
IncludeDir(examplesdir)
group "Tests"
print("Searching for tests projects...")
IncludeDir(testsdir)

17
tests/Basic/Basic.Gen.cs Normal file
Просмотреть файл

@ -0,0 +1,17 @@
using MonoManagedToNative.Generators;
namespace MonoManagedToNative.Tests
{
public class BasicTestsGenerator : TestsGenerator
{
public BasicTestsGenerator(GeneratorKind kind)
: base("Basic", kind)
{
}
public static void Main(string[] args)
{
new BasicTestsGenerator(GeneratorKind.C).Generate();
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
//using System;
//using EmptyTest;
//using CppSharp.Utils;
//using NUnit.Framework;
//[TestFixture]
//public class EmptyTests : GeneratorTestFixture
//{
//}
//

21
tests/Basic/Basic.cs Normal file
Просмотреть файл

@ -0,0 +1,21 @@
using System;
public class BuiltinTypes
{
public void ReturnsVoid() { }
public bool ReturnsBool() { return true; }
public sbyte ReturnsSByte() { return -5; }
public byte ReturnsByte() { return 5; }
public short ReturnsShort() { return -5; }
public ushort ReturnsUShort() { return 5; }
public int ReturnsInt() { return -5; }
public uint ReturnsUInt() { return 5; }
public long ReturnsLong() { return -5; }
public ulong ReturnsULong() { return 5; }
public string ReturnsString() { return "Mono"; }
}
public static class StaticClass
{
public static void ReturnsVoid() { }
}

2
tests/Basic/premake5.lua Normal file
Просмотреть файл

@ -0,0 +1,2 @@
group "Tests/Basic"
SetupTestProject("Basic")