зеркало из https://github.com/mono/CppSharp.git
Add initial Emscripten generator.
This commit is contained in:
Родитель
db7949b263
Коммит
e66fad0d32
|
@ -39,6 +39,7 @@ There is also experimental support for these JavaScript-related targets:
|
|||
- N-API (Node.js)
|
||||
- QuickJS
|
||||
- TypeScript
|
||||
- Emscripten
|
||||
|
||||
# 3. Native Targets
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CppSharp.Generators;
|
||||
using Mono.Options;
|
||||
|
||||
namespace CppSharp
|
||||
|
@ -15,11 +16,11 @@ namespace CppSharp
|
|||
{
|
||||
var showHelp = false;
|
||||
|
||||
optionSet.Add("I=", "the {PATH} of a folder to search for include files", (i) => { AddIncludeDirs(i, errorMessages); });
|
||||
optionSet.Add("I=", "the {PATH} of a folder to search for include files", i => { AddIncludeDirs(i, errorMessages); });
|
||||
optionSet.Add("l=", "{LIBRARY} that that contains the symbols of the generated code", l => options.Libraries.Add(l));
|
||||
optionSet.Add("L=", "the {PATH} of a folder to search for additional libraries", l => options.LibraryDirs.Add(l));
|
||||
optionSet.Add("D:", "additional define with (optional) value to add to be used while parsing the given header files", (n, v) => AddDefine(n, v, errorMessages));
|
||||
optionSet.Add("A=", "additional Clang arguments to pass to the compiler while parsing the given header files", (v) => AddArgument(v, errorMessages));
|
||||
optionSet.Add("A=", "additional Clang arguments to pass to the compiler while parsing the given header files", v => AddArgument(v, errorMessages));
|
||||
|
||||
optionSet.Add("o=|output=", "the {PATH} for the generated bindings file (doesn't need the extension since it will depend on the generator)", v => HandleOutputArg(v, errorMessages));
|
||||
optionSet.Add("on=|outputnamespace=", "the {NAMESPACE} that will be used for the generated code", on => options.OutputNamespace = on);
|
||||
|
@ -30,8 +31,8 @@ namespace CppSharp
|
|||
optionSet.Add("d|debug", "enables debug mode which generates more verbose code to aid debugging", v => options.Debug = true);
|
||||
optionSet.Add("c|compile", "enables automatic compilation of the generated code", v => options.Compile = true);
|
||||
optionSet.Add("g=|gen=|generator=", "the {TYPE} of generated code: 'csharp' or 'cli' ('cli' supported only for Windows)", g => { GetGeneratorKind(g, errorMessages); });
|
||||
optionSet.Add("p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux'", p => { GetDestinationPlatform(p, errorMessages); });
|
||||
optionSet.Add("a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64'", a => { GetDestinationArchitecture(a, errorMessages); });
|
||||
optionSet.Add("p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux' or 'emscripten'", p => { GetDestinationPlatform(p, errorMessages); });
|
||||
optionSet.Add("a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64' or 'wasm32' or 'wasm64'", a => { GetDestinationArchitecture(a, errorMessages); });
|
||||
optionSet.Add("prefix=", "sets a string prefix to the names of generated files", a => { options.Prefix = a; });
|
||||
|
||||
optionSet.Add("exceptions", "enables support for C++ exceptions in the parser", v => { options.EnableExceptions = true; });
|
||||
|
@ -208,26 +209,29 @@ namespace CppSharp
|
|||
switch (generator.ToLower())
|
||||
{
|
||||
case "csharp":
|
||||
options.Kind = CppSharp.Generators.GeneratorKind.CSharp;
|
||||
options.Kind = GeneratorKind.CSharp;
|
||||
return;
|
||||
case "cli":
|
||||
options.Kind = CppSharp.Generators.GeneratorKind.CLI;
|
||||
options.Kind = GeneratorKind.CLI;
|
||||
return;
|
||||
case "c":
|
||||
options.Kind = CppSharp.Generators.GeneratorKind.C;
|
||||
options.Kind = GeneratorKind.C;
|
||||
return;
|
||||
case "cpp":
|
||||
options.Kind = CppSharp.Generators.GeneratorKind.CPlusPlus;
|
||||
options.Kind = GeneratorKind.CPlusPlus;
|
||||
return;
|
||||
case "napi":
|
||||
options.Kind = CppSharp.Generators.GeneratorKind.NAPI;
|
||||
options.Kind = GeneratorKind.NAPI;
|
||||
return;
|
||||
case "qjs":
|
||||
options.Kind = CppSharp.Generators.GeneratorKind.QuickJS;
|
||||
options.Kind = GeneratorKind.QuickJS;
|
||||
return;
|
||||
case "ts":
|
||||
case "typescript":
|
||||
options.Kind = CppSharp.Generators.GeneratorKind.TypeScript;
|
||||
options.Kind = GeneratorKind.TypeScript;
|
||||
return;
|
||||
case "emscripten":
|
||||
options.Kind = GeneratorKind.Emscripten;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -247,6 +251,9 @@ namespace CppSharp
|
|||
case "linux":
|
||||
options.Platform = TargetPlatform.Linux;
|
||||
return;
|
||||
case "emscripten":
|
||||
options.Platform = TargetPlatform.Emscripten;
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessages.Add($"Unknown target platform: {platform}. Defaulting to {options.Platform}");
|
||||
|
@ -262,6 +269,12 @@ namespace CppSharp
|
|||
case "x64":
|
||||
options.Architecture = TargetArchitecture.x64;
|
||||
return;
|
||||
case "wasm32":
|
||||
options.Architecture = TargetArchitecture.WASM32;
|
||||
return;
|
||||
case "wasm64":
|
||||
options.Architecture = TargetArchitecture.WASM64;
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessages.Add($"Unknown target architecture: {architecture}. Defaulting to {options.Architecture}");
|
||||
|
@ -286,7 +299,7 @@ namespace CppSharp
|
|||
return;
|
||||
}
|
||||
|
||||
Generator gen = new Generator(options);
|
||||
var gen = new Generator(options);
|
||||
|
||||
bool validOptions = gen.ValidateOptions(errorMessages);
|
||||
PrintErrorMessages(errorMessages);
|
||||
|
|
|
@ -43,28 +43,51 @@ namespace CppSharp
|
|||
{
|
||||
var tripleBuilder = new StringBuilder();
|
||||
|
||||
if (options.Architecture == TargetArchitecture.x64)
|
||||
tripleBuilder.Append("x86_64-");
|
||||
else if (options.Architecture == TargetArchitecture.x86)
|
||||
tripleBuilder.Append("i686-");
|
||||
|
||||
if (options.Platform == TargetPlatform.Windows)
|
||||
switch (options.Architecture)
|
||||
{
|
||||
tripleBuilder.Append("pc-win32-msvc");
|
||||
abi = CppAbi.Microsoft;
|
||||
case TargetArchitecture.x64:
|
||||
tripleBuilder.Append("x86_64-");
|
||||
break;
|
||||
case TargetArchitecture.x86:
|
||||
tripleBuilder.Append("i686-");
|
||||
break;
|
||||
case TargetArchitecture.WASM32:
|
||||
tripleBuilder.Append("wasm32-");
|
||||
break;
|
||||
case TargetArchitecture.WASM64:
|
||||
tripleBuilder.Append("wasm64-");
|
||||
break;
|
||||
}
|
||||
else if (options.Platform == TargetPlatform.MacOS)
|
||||
{
|
||||
tripleBuilder.Append("apple-darwin12.4.0");
|
||||
abi = CppAbi.Itanium;
|
||||
}
|
||||
else if (options.Platform == TargetPlatform.Linux)
|
||||
{
|
||||
tripleBuilder.Append("linux-gnu");
|
||||
abi = CppAbi.Itanium;
|
||||
|
||||
if (options.Cpp11ABI)
|
||||
tripleBuilder.Append("-cxx11abi");
|
||||
switch (options.Platform)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
tripleBuilder.Append("pc-win32-msvc");
|
||||
abi = CppAbi.Microsoft;
|
||||
break;
|
||||
case TargetPlatform.MacOS:
|
||||
tripleBuilder.Append("apple-darwin12.4.0");
|
||||
abi = CppAbi.Itanium;
|
||||
break;
|
||||
case TargetPlatform.Linux:
|
||||
{
|
||||
tripleBuilder.Append("linux-gnu");
|
||||
abi = CppAbi.Itanium;
|
||||
|
||||
if (options.Cpp11ABI)
|
||||
tripleBuilder.Append("-cxx11abi");
|
||||
break;
|
||||
}
|
||||
case TargetPlatform.Emscripten:
|
||||
{
|
||||
if (options.Architecture != TargetArchitecture.WASM32 &&
|
||||
options.Architecture != TargetArchitecture.WASM64)
|
||||
throw new Exception("Emscripten target is only compatible with WASM architectures");
|
||||
|
||||
tripleBuilder.Append("unknown-emscripten");
|
||||
abi = CppAbi.Itanium;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
triple = tripleBuilder.ToString();
|
||||
|
|
|
@ -6,7 +6,9 @@ namespace CppSharp
|
|||
enum TargetArchitecture
|
||||
{
|
||||
x86,
|
||||
x64
|
||||
x64,
|
||||
WASM32,
|
||||
WASM64
|
||||
}
|
||||
|
||||
class Options
|
||||
|
|
|
@ -11,7 +11,8 @@ namespace CppSharp
|
|||
MacOS,
|
||||
iOS,
|
||||
WatchOS,
|
||||
TVOS
|
||||
TVOS,
|
||||
Emscripten,
|
||||
}
|
||||
|
||||
public static class Platform
|
||||
|
|
|
@ -8,6 +8,7 @@ using CppSharp.Generators.C;
|
|||
using CppSharp.Generators.CLI;
|
||||
using CppSharp.Generators.Cpp;
|
||||
using CppSharp.Generators.CSharp;
|
||||
using CppSharp.Generators.Emscripten;
|
||||
using CppSharp.Generators.TS;
|
||||
using CppSharp.Parser;
|
||||
using CppSharp.Passes;
|
||||
|
@ -43,6 +44,8 @@ namespace CppSharp
|
|||
return new CLIGenerator(Context);
|
||||
case GeneratorKind.CSharp:
|
||||
return new CSharpGenerator(Context);
|
||||
case GeneratorKind.Emscripten:
|
||||
return new EmscriptenGenerator(Context);
|
||||
case GeneratorKind.QuickJS:
|
||||
return new QuickJSGenerator(Context);
|
||||
case GeneratorKind.NAPI:
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace CppSharp.Generators
|
|||
CSharp = 2,
|
||||
C,
|
||||
CPlusPlus,
|
||||
Emscripten,
|
||||
ObjectiveC,
|
||||
Java,
|
||||
Swift,
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CppSharp.AST;
|
||||
using CppSharp.Generators.Cpp;
|
||||
|
||||
namespace CppSharp.Generators.Emscripten
|
||||
{
|
||||
/// <summary>
|
||||
/// Emscripten generator responsible for driving the generation of binding files.
|
||||
/// Embind documentation: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
|
||||
/// </summary>
|
||||
public class EmscriptenGenerator : CppGenerator
|
||||
{
|
||||
public EmscriptenGenerator(BindingContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<GeneratorOutput> Generate()
|
||||
{
|
||||
var outputs = base.Generate();
|
||||
|
||||
foreach (var module in Context.Options.Modules)
|
||||
{
|
||||
if (module == Context.Options.SystemModule)
|
||||
continue;
|
||||
|
||||
var output = GenerateModule(module);
|
||||
if (output != null)
|
||||
{
|
||||
OnUnitGenerated(output);
|
||||
outputs.Add(output);
|
||||
}
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public override List<CodeGenerator> Generate(IEnumerable<TranslationUnit> units)
|
||||
{
|
||||
var outputs = new List<CodeGenerator>();
|
||||
|
||||
var header = new EmscriptenHeaders(Context, units);
|
||||
outputs.Add(header);
|
||||
|
||||
var source = new EmscriptenSources(Context, units);
|
||||
outputs.Add(source);
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public override GeneratorOutput GenerateModule(Module module)
|
||||
{
|
||||
if (module == Context.Options.SystemModule)
|
||||
return null;
|
||||
|
||||
var moduleGen = new EmscriptenModule(Context, module);
|
||||
|
||||
var output = new GeneratorOutput
|
||||
{
|
||||
TranslationUnit = new TranslationUnit
|
||||
{
|
||||
FilePath = $"{module.LibraryName}_embind_module.cpp",
|
||||
Module = module
|
||||
},
|
||||
Outputs = new List<CodeGenerator> { moduleGen }
|
||||
};
|
||||
|
||||
output.Outputs[0].Process();
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System.Collections.Generic;
|
||||
using CppSharp.AST;
|
||||
|
||||
namespace CppSharp.Generators.Emscripten
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates Emscripten Embind C/C++ header files.
|
||||
/// Embind documentation: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
|
||||
/// </summary>
|
||||
public class EmscriptenHeaders : EmscriptenCodeGenerator
|
||||
{
|
||||
public EmscriptenHeaders(BindingContext context, IEnumerable<TranslationUnit> units)
|
||||
: base(context, units)
|
||||
{
|
||||
CTypePrinter.PushContext(TypePrinterContextKind.Managed);
|
||||
}
|
||||
|
||||
//public override bool ShouldGenerateNamespaces => false;
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
GenerateFilePreamble(CommentKind.BCPL);
|
||||
|
||||
PushBlock(BlockKind.Includes);
|
||||
WriteLine("#pragma once");
|
||||
NewLine();
|
||||
PopBlock();
|
||||
|
||||
var name = GetTranslationUnitName(TranslationUnit);
|
||||
WriteLine($"extern \"C\" void embind_init_{name}();");
|
||||
}
|
||||
|
||||
public override bool VisitClassDecl(Class @class)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool VisitEvent(Event @event)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool VisitFieldDecl(Field field)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using CppSharp.Generators.C;
|
||||
|
||||
namespace CppSharp.Generators.Emscripten
|
||||
{
|
||||
public class EmscriptenMarshalNativeToManagedPrinter : MarshalPrinter<MarshalContext, CppTypePrinter>
|
||||
{
|
||||
public EmscriptenMarshalNativeToManagedPrinter(MarshalContext marshalContext)
|
||||
: base(marshalContext)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class EmscriptenMarshalManagedToNativePrinter : MarshalPrinter<MarshalContext, CppTypePrinter>
|
||||
{
|
||||
public EmscriptenMarshalManagedToNativePrinter(MarshalContext ctx)
|
||||
: base(ctx)
|
||||
{
|
||||
Context.MarshalToNative = this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using CppSharp.AST;
|
||||
using CppSharp.Generators.C;
|
||||
|
||||
namespace CppSharp.Generators.Emscripten
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates Emscripten module init files.
|
||||
/// Embind documentation: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
|
||||
/// </summary>
|
||||
public class EmscriptenModule : EmscriptenCodeGenerator
|
||||
{
|
||||
private readonly Module module;
|
||||
|
||||
public EmscriptenModule(BindingContext context, Module module)
|
||||
: base(context, module.Units.GetGenerated())
|
||||
{
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public override string FileExtension { get; } = "cpp";
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
GenerateFilePreamble(CommentKind.BCPL);
|
||||
NewLine();
|
||||
|
||||
PushBlock(BlockKind.Includes);
|
||||
{
|
||||
WriteInclude(new CInclude()
|
||||
{
|
||||
File = "emscripten/bind.h",
|
||||
Kind = CInclude.IncludeKind.Angled
|
||||
});
|
||||
|
||||
foreach (var unit in TranslationUnits)
|
||||
WriteInclude(GetIncludeFileName(Context, unit), CInclude.IncludeKind.Quoted);
|
||||
}
|
||||
PopBlock(NewLineKind.Always);
|
||||
|
||||
WriteLine("extern \"C\" {");
|
||||
NewLine();
|
||||
|
||||
PushBlock(BlockKind.ForwardReferences);
|
||||
{
|
||||
foreach (var unit in TranslationUnits.Where(unit => unit.IsGenerated))
|
||||
{
|
||||
var name = GetTranslationUnitName(unit);
|
||||
WriteLine($"void embind_init_{name}();");
|
||||
}
|
||||
}
|
||||
PopBlock(NewLineKind.Always);
|
||||
|
||||
var moduleName = module.LibraryName;
|
||||
WriteLine($"void embind_init_{moduleName}()");
|
||||
WriteOpenBraceAndIndent();
|
||||
|
||||
foreach (var unit in TranslationUnits)
|
||||
{
|
||||
var name = GetTranslationUnitName(unit);
|
||||
WriteLine($"embind_init_{name}();");
|
||||
}
|
||||
|
||||
UnindentAndWriteCloseBrace();
|
||||
|
||||
NewLine();
|
||||
WriteLine("}");
|
||||
NewLine();
|
||||
|
||||
WriteLine($"static struct EmBindInit_{moduleName} : emscripten::internal::InitFunc {{");
|
||||
WriteLineIndent($"EmBindInit_{moduleName}() : InitFunc(embind_init_{moduleName}) {{}}");
|
||||
WriteLine($"}} EmBindInit_{moduleName}_instance;");
|
||||
}
|
||||
|
||||
public static string GetIncludeFileName(BindingContext context, TranslationUnit unit)
|
||||
{
|
||||
// TODO: Replace with GetIncludePath
|
||||
string file;
|
||||
if (context.Options.GenerateName != null)
|
||||
file = context.Options.GenerateName(unit);
|
||||
else
|
||||
file = Path.GetFileNameWithoutExtension(unit.FileName)
|
||||
.Replace('\\', '/');
|
||||
|
||||
return $"{file}.h";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CppSharp.AST;
|
||||
using CppSharp.AST.Extensions;
|
||||
using CppSharp.Generators.C;
|
||||
using CppSharp.Generators.Cpp;
|
||||
|
||||
namespace CppSharp.Generators.Emscripten
|
||||
{
|
||||
public class EmscriptenCodeGenerator : MethodGroupCodeGenerator
|
||||
{
|
||||
protected EmscriptenCodeGenerator(BindingContext context, IEnumerable<TranslationUnit> units)
|
||||
: base(context, units)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual MarshalPrinter<MarshalContext, CppTypePrinter> GetMarshalManagedToNativePrinter(MarshalContext ctx)
|
||||
{
|
||||
return new EmscriptenMarshalManagedToNativePrinter(ctx);
|
||||
}
|
||||
|
||||
public virtual MarshalPrinter<MarshalContext, CppTypePrinter> GetMarshalNativeToManagedPrinter(MarshalContext ctx)
|
||||
{
|
||||
return new EmscriptenMarshalNativeToManagedPrinter(ctx);
|
||||
}
|
||||
|
||||
public override bool VisitClassTemplateDecl(ClassTemplate template)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool VisitFunctionTemplateDecl(FunctionTemplate template)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates Emscripten C/C++ source files.
|
||||
/// Embind documentation: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
|
||||
/// </summary>
|
||||
public class EmscriptenSources : EmscriptenCodeGenerator
|
||||
{
|
||||
public override string FileExtension => "cpp";
|
||||
|
||||
public EmscriptenSources(BindingContext context, IEnumerable<TranslationUnit> units)
|
||||
: base(context, units)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Process()
|
||||
{
|
||||
GenerateFilePreamble(CommentKind.BCPL);
|
||||
|
||||
PushBlock(BlockKind.Includes);
|
||||
{
|
||||
WriteInclude(new CInclude()
|
||||
{
|
||||
File = "emscripten/bind.h",
|
||||
Kind = CInclude.IncludeKind.Angled
|
||||
});
|
||||
|
||||
foreach (var unit in TranslationUnits)
|
||||
{
|
||||
WriteInclude(unit.IncludePath, CInclude.IncludeKind.Angled);
|
||||
}
|
||||
}
|
||||
PopBlock(NewLineKind.Always);
|
||||
|
||||
var name = GetTranslationUnitName(TranslationUnit);
|
||||
WriteLine($"extern \"C\" void embind_init_{name}()");
|
||||
WriteOpenBraceAndIndent();
|
||||
VisitNamespace(TranslationUnit);
|
||||
UnindentAndWriteCloseBrace();
|
||||
}
|
||||
|
||||
public override bool VisitClassDecl(Class @class)
|
||||
{
|
||||
if (@class.IsIncomplete)
|
||||
return true;
|
||||
|
||||
PushBlock();
|
||||
Write($"emscripten::class_<{@class.QualifiedOriginalName}");
|
||||
if (@class.HasBaseClass)
|
||||
Write($", emscripten::base<{@class.BaseClass.QualifiedOriginalName}>");
|
||||
WriteLine($">(\"{@class.Name}\")");
|
||||
|
||||
VisitClassDeclContext(@class);
|
||||
|
||||
WriteLineIndent(";");
|
||||
PopBlock(NewLineKind.BeforeNextBlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void VisitClassConstructors(IEnumerable<Method> ctors)
|
||||
{
|
||||
var overloadCheck = new HashSet<int>();
|
||||
foreach (var ctor in ctors)
|
||||
{
|
||||
if (overloadCheck.Contains(ctor.Parameters.Count))
|
||||
{
|
||||
Console.WriteLine($"Ignoring overloaded ctor: {ctor.QualifiedOriginalName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var cppTypePrinter = new CppTypePrinter(Context)
|
||||
{
|
||||
PrintFlavorKind = CppTypePrintFlavorKind.Cpp
|
||||
};
|
||||
cppTypePrinter.PushContext(TypePrinterContextKind.Native);
|
||||
|
||||
var parameters = ctor.Parameters.Select(p => p.Type.Visit(cppTypePrinter).Type);
|
||||
WriteLineIndent($".constructor<{string.Join(", ", parameters)}>()");
|
||||
|
||||
overloadCheck.Add(ctor.Parameters.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool VisitMethodDecl(Method method)
|
||||
{
|
||||
Indent();
|
||||
var ret = VisitFunctionDecl(method);
|
||||
Unindent();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override bool VisitFieldDecl(Field field)
|
||||
{
|
||||
WriteLineIndent($".field(\"{field.Name}\", &{field.Class.QualifiedOriginalName}::{field.OriginalName})");
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void GenerateMethodGroup(List<Method> @group)
|
||||
{
|
||||
if (@group.Count > 1)
|
||||
{
|
||||
Console.WriteLine($"Ignoring method group: {@group.First().QualifiedOriginalName}");
|
||||
return;
|
||||
}
|
||||
|
||||
base.GenerateMethodGroup(@group);
|
||||
}
|
||||
|
||||
public override void GenerateFunctionGroup(List<Function> @group)
|
||||
{
|
||||
if (@group.Count > 1)
|
||||
{
|
||||
Console.WriteLine($"Ignoring function group: {@group.First().QualifiedOriginalName}");
|
||||
return;
|
||||
}
|
||||
|
||||
base.GenerateFunctionGroup(@group);
|
||||
}
|
||||
|
||||
public override void VisitDeclContextFunctions(DeclarationContext context)
|
||||
{
|
||||
PushBlock();
|
||||
base.VisitDeclContextFunctions(context);
|
||||
PopBlock(NewLineKind.BeforeNextBlock);
|
||||
}
|
||||
|
||||
public override bool VisitFunctionDecl(Function function)
|
||||
{
|
||||
var prefix = function is Method ? ".function" : "emscripten::function";
|
||||
Write($"{prefix}(\"{function.Name}\", &{function.QualifiedOriginalName}");
|
||||
|
||||
var hasPointers = function.ReturnType.Type.IsPointer() ||
|
||||
function.Parameters.Any(p => p.Type.IsPointer());
|
||||
if (hasPointers)
|
||||
Write(", emscripten::allow_raw_pointers()");
|
||||
|
||||
WriteLine(function is not Method ? ");" : $")");
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool VisitEnumDecl(Enumeration @enum)
|
||||
{
|
||||
if (@enum.IsIncomplete)
|
||||
return false;
|
||||
|
||||
PushBlock();
|
||||
|
||||
WriteLine($"emscripten::enum_<{@enum.Name}>(\"{@enum.QualifiedOriginalName}\")");
|
||||
foreach (var item in @enum.Items)
|
||||
{
|
||||
WriteLineIndent(@enum.IsScoped
|
||||
? $".value(\"{item.Name}\", {@enum.QualifiedOriginalName}::{item.OriginalName})"
|
||||
: $".value(\"{item.Name}\", {item.QualifiedOriginalName})");
|
||||
}
|
||||
WriteLineIndent(";");
|
||||
|
||||
PopBlock(NewLineKind.BeforeNextBlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool VisitTypedefDecl(TypedefDecl typedef)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using CppSharp.Generators.C;
|
||||
|
||||
namespace CppSharp.Generators.Emscripten
|
||||
{
|
||||
public class EmscriptenTypePrinter : CppTypePrinter
|
||||
{
|
||||
public EmscriptenTypePrinter(BindingContext context) : base(context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ using CppSharp.Generators;
|
|||
using CppSharp.Generators.C;
|
||||
using CppSharp.Generators.CLI;
|
||||
using CppSharp.Generators.CSharp;
|
||||
using CppSharp.Generators.Emscripten;
|
||||
using CppSharp.Types;
|
||||
|
||||
namespace CppSharp.Passes
|
||||
|
@ -204,6 +205,9 @@ namespace CppSharp.Passes
|
|||
case GeneratorKind.C:
|
||||
typePrinter = new CppTypePrinter(Context) { PrintFlavorKind = CppTypePrintFlavorKind.C };
|
||||
break;
|
||||
case GeneratorKind.Emscripten:
|
||||
typePrinter = new EmscriptenTypePrinter(Context);
|
||||
break;;
|
||||
case GeneratorKind.CPlusPlus:
|
||||
case GeneratorKind.QuickJS:
|
||||
case GeneratorKind.NAPI:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
void ReturnsVoid () { }
|
||||
|
@ -40,8 +41,10 @@ int16_t ReturnsInt16 () { return -5; }
|
|||
uint16_t ReturnsUInt16 () { return 5; }
|
||||
int32_t ReturnsInt32 () { return -5; }
|
||||
uint32_t ReturnsUInt32 () { return 5; }
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
int64_t ReturnsInt64 () { return -5; }
|
||||
uint64_t ReturnsUInt64 () { return 5; }
|
||||
#endif
|
||||
|
||||
int8_t PassAndReturnsInt8 (int8_t v) { return v; }
|
||||
uint8_t PassAndReturnsUInt8 (uint8_t v) { return v; }
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
gen
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
workspace "emscripten"
|
||||
configurations { "debug", "release" }
|
||||
location "gen"
|
||||
symbols "On"
|
||||
optimize "Off"
|
||||
|
||||
project "test"
|
||||
kind "SharedLib"
|
||||
language "C++"
|
||||
files
|
||||
{
|
||||
"gen/**.cpp",
|
||||
}
|
||||
includedirs
|
||||
{
|
||||
"..",
|
||||
"../../include"
|
||||
}
|
||||
linkoptions { "--bind -sENVIRONMENT=node -sMODULARIZE=1 -sEXPORT_ALL -sEXPORT_ES6=1 -sUSE_ES6_IMPORT_META=1" }
|
||||
targetextension ".mjs"
|
|
@ -0,0 +1,114 @@
|
|||
import wasmModule from "./gen/bin/debug/libtest.mjs";
|
||||
import { eq, ascii, floateq } from "./utils.mjs"
|
||||
|
||||
const test = await wasmModule({
|
||||
onRuntimeInitialized() {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
function builtins() {
|
||||
eq(test.ReturnsVoid(), undefined)
|
||||
|
||||
eq(test.ReturnsBool(), true)
|
||||
eq(test.PassAndReturnsBool(false), false)
|
||||
|
||||
eq(test.ReturnsNullptr(), null)
|
||||
eq(test.PassAndReturnsNullptr(null), null)
|
||||
|
||||
eq(test.ReturnsChar(), ascii('a'));
|
||||
eq(test.ReturnsSChar(), ascii('a'));
|
||||
eq(test.ReturnsUChar(), ascii('a'));
|
||||
|
||||
eq(test.PassAndReturnsChar(ascii('a')), ascii('a'));
|
||||
eq(test.PassAndReturnsSChar(ascii('b')), ascii('b'));
|
||||
eq(test.PassAndReturnsUChar(ascii('c')), ascii('c'));
|
||||
|
||||
// TODO: add wchar_t tests
|
||||
|
||||
eq(test.ReturnsFloat(), 5.0);
|
||||
eq(test.ReturnsDouble(), -5.0);
|
||||
//eq(test.ReturnsLongDouble(), -5.0);
|
||||
|
||||
floateq(test.PassAndReturnsFloat(1.32), 1.32);
|
||||
floateq(test.PassAndReturnsDouble(1.32), 1.32);
|
||||
//float(test.PassAndReturnsLongDouble(1.32), 1.32);
|
||||
|
||||
eq(test.ReturnsInt8(), -5);
|
||||
eq(test.ReturnsUInt8(), 5);
|
||||
eq(test.ReturnsInt16(), -5);
|
||||
eq(test.ReturnsUInt16(), 5);
|
||||
eq(test.ReturnsInt32(), -5);
|
||||
eq(test.ReturnsUInt32(), 5);
|
||||
|
||||
// TODO:
|
||||
// https://github.com/WebAssembly/proposals/issues/7
|
||||
// https://github.com/emscripten-core/emscripten/issues/11140
|
||||
//eq(test.ReturnsInt64(), -5n);
|
||||
//eq(test.ReturnsUInt64(), 5n);
|
||||
|
||||
const int8 = { min: -(2 ** 7), max: (2 ** 7) - 1 };
|
||||
eq(test.PassAndReturnsInt8(int8.min), int8.min);
|
||||
eq(test.PassAndReturnsInt8(int8.max), int8.max);
|
||||
|
||||
const uint8 = { min: 0, max: (2 ** 8) - 1 };
|
||||
eq(test.PassAndReturnsUInt8(uint8.min), uint8.min);
|
||||
eq(test.PassAndReturnsUInt8(uint8.max), uint8.max);
|
||||
|
||||
const int16 = { min: -(2 ** 15), max: (2 ** 15) - 1 };
|
||||
eq(test.PassAndReturnsInt16(int16.min), int16.min);
|
||||
eq(test.PassAndReturnsInt16(int16.max), int16.max);
|
||||
|
||||
const uint16 = { min: 0, max: (2 ** 16) - 1 };
|
||||
eq(test.PassAndReturnsUInt16(uint16.min), uint16.min);
|
||||
eq(test.PassAndReturnsUInt16(uint16.max), uint16.max);
|
||||
|
||||
const int32 = { min: -(2 ** 31), max: (2 ** 31) - 1 };
|
||||
eq(test.PassAndReturnsInt32(int32.min), int32.min);
|
||||
eq(test.PassAndReturnsInt32(int32.max), int32.max);
|
||||
|
||||
const uint32 = { min: 0, max: (2 ** 32) - 1 };
|
||||
eq(test.PassAndReturnsUInt32(uint32.min), uint32.min);
|
||||
eq(test.PassAndReturnsUInt32(uint32.max), uint32.max);
|
||||
|
||||
//const int64 = { min: BigInt(2 ** 63) * -1n, max: BigInt(2 ** 63) - 1n };
|
||||
//eq(test.PassAndReturnsInt64(int64.min), int64.min);
|
||||
//eq(test.PassAndReturnsInt64(int64.max), int64.max);
|
||||
|
||||
//const uint64 = { min: BigInt(0), max: BigInt(2 ** 64) - 1n };
|
||||
//eq(test.PassAndReturnsUInt64(uint64.min), uint64.min);
|
||||
//eq(test.PassAndReturnsUInt64(uint64.max), uint64.max);
|
||||
}
|
||||
|
||||
function enums() {
|
||||
eq(test.Enum0.Item0.value, 0);
|
||||
eq(test.Enum0.Item1.value, 1);
|
||||
eq(test.Enum0.Item2.value, 5);
|
||||
|
||||
eq(test.ReturnsEnum(), test.Enum0.Item0);
|
||||
eq(test.PassAndReturnsEnum(test.Enum0.Item1), test.Enum0.Item1);
|
||||
}
|
||||
|
||||
function classes() {
|
||||
var c = new test.Class();
|
||||
eq(typeof (c), "object")
|
||||
eq(c.ReturnsVoid(), undefined)
|
||||
eq(c.ReturnsInt(), 0)
|
||||
eq(c.PassAndReturnsClassPtr(null), null)
|
||||
|
||||
var c1 = new test.ClassWithSingleInheritance();
|
||||
eq(c1.__proto__.constructor.name, 'ClassWithSingleInheritance')
|
||||
eq(c1.__proto__.__proto__.constructor.name, 'Class')
|
||||
eq(c1.ReturnsVoid(), undefined);
|
||||
eq(c1.ReturnsInt(), 0);
|
||||
eq(c1.ChildMethod(), 2);
|
||||
|
||||
var classWithField = new test.ClassWithField();
|
||||
eq(classWithField.ReturnsField(), 10);
|
||||
eq(classWithField.Field, 10);
|
||||
}
|
||||
|
||||
|
||||
builtins();
|
||||
enums();
|
||||
classes();
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
dir=$(cd "$(dirname "$0")"; pwd)
|
||||
rootdir="$dir/../.."
|
||||
dotnet_configuration=Release
|
||||
configuration=debug
|
||||
platform=x64
|
||||
jsinterp=node
|
||||
|
||||
red=`tput setaf 1`
|
||||
green=`tput setaf 2`
|
||||
reset=`tput sgr0`
|
||||
|
||||
generate=true
|
||||
|
||||
if [ $generate = true ]; then
|
||||
echo "${green}Generating bindings${reset}"
|
||||
dotnet $rootdir/bin/${dotnet_configuration}_${platform}/CppSharp.CLI.dll \
|
||||
--gen=emscripten --platform=emscripten --arch=wasm32 \
|
||||
-I$dir/.. -I$rootdir/include -o $dir/gen -m tests $dir/../*.h
|
||||
fi
|
||||
|
||||
echo "${green}Building generated binding files${reset}"
|
||||
premake=$rootdir/build/premake.sh
|
||||
config=$configuration $premake --file=$dir/premake5.lua gmake
|
||||
emmake make -C $dir/gen
|
||||
echo
|
||||
|
||||
echo "${green}Executing JS tests with Node${reset}"
|
||||
$jsinterp $dir/test.mjs
|
|
@ -0,0 +1,21 @@
|
|||
export function assert(actual, expected, message) {
|
||||
if (arguments.length == 1)
|
||||
expected = true;
|
||||
|
||||
if (actual === expected)
|
||||
return;
|
||||
|
||||
if (actual !== null && expected !== null
|
||||
&& typeof actual == 'object' && typeof expected == 'object'
|
||||
&& actual.toString() === expected.toString())
|
||||
return;
|
||||
|
||||
throw Error("assertion failed: got |" + actual + "|" +
|
||||
", expected |" + expected + "|" +
|
||||
(message ? " (" + message + ")" : ""));
|
||||
}
|
||||
|
||||
export const eq = assert;
|
||||
export const floateq = (actual, expected) => { assert(Math.abs(actual - expected) < 0.01) }
|
||||
|
||||
export const ascii = v => v.charCodeAt(0)
|
|
@ -4,3 +4,4 @@ dir=$(cd "$(dirname "$0")"; pwd)
|
|||
|
||||
$dir/napi/test.sh
|
||||
$dir/quickjs/test.sh
|
||||
$dir/emscripten/test.sh
|
||||
|
|
Загрузка…
Ссылка в новой задаче