Add initial Emscripten generator.

This commit is contained in:
Joao Matos 2023-02-03 17:49:54 +00:00
Родитель db7949b263
Коммит e66fad0d32
21 изменённых файлов: 719 добавлений и 33 удалений

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

@ -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; }

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

@ -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"

114
tests2/emscripten/test.mjs Normal file
Просмотреть файл

@ -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();

30
tests2/emscripten/test.sh Executable file
Просмотреть файл

@ -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