mirror of https://github.com/mono/CppSharp.git
21 changed files with 719 additions and 33 deletions
@ -0,0 +1,73 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,11 @@
|
||||
using CppSharp.Generators.C; |
||||
|
||||
namespace CppSharp.Generators.Emscripten |
||||
{ |
||||
public class EmscriptenTypePrinter : CppTypePrinter |
||||
{ |
||||
public EmscriptenTypePrinter(BindingContext context) : base(context) |
||||
{ |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
gen |
||||
*.so |
||||
*.dylib |
||||
*.dll |
@ -0,0 +1,21 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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) |
Loading…
Reference in new issue