Browse Source

Add initial QuickJS primitive type support and test suite.

pull/1550/head
Joao Matos 5 years ago committed by João Matos
parent
commit
93dc4e98ef
  1. 7
      src/Generator/Generators/C/NAPI/NAPISources.cs
  2. 77
      src/Generator/Generators/C/QuickJS/QuickJSMarshal.cs
  3. 10
      src/Generator/Generators/C/QuickJS/QuickJSModule.cs
  4. 36
      src/Generator/Generators/C/QuickJS/QuickJSSources.cs
  5. 4
      tests2/quickjs/.gitignore
  6. 21
      tests2/quickjs/premake5.lua
  7. 96
      tests2/quickjs/test.js
  8. 26
      tests2/quickjs/test.sh

7
src/Generator/Generators/C/NAPI/NAPISources.cs

@ -336,6 +336,11 @@ namespace CppSharp.Generators.Cpp @@ -336,6 +336,11 @@ namespace CppSharp.Generators.Cpp
{
}
public NAPICallbacks(BindingContext context, IEnumerable<TranslationUnit> units)
: base(context, units)
{
}
public override void GenerateFunctionGroup(List<Function> @group)
{
var function = @group.First();
@ -596,7 +601,7 @@ namespace CppSharp.Generators.Cpp @@ -596,7 +601,7 @@ namespace CppSharp.Generators.Cpp
}
}
public void GenerateFunctionCall(Function function)
public virtual void GenerateFunctionCall(Function function)
{
var @params = GenerateFunctionParamsMarshal(function.Parameters, function);

77
src/Generator/Generators/C/QuickJS/QuickJSMarshal.cs

@ -145,26 +145,50 @@ namespace CppSharp.Generators.Cpp @@ -145,26 +145,50 @@ namespace CppSharp.Generators.Cpp
{
case PrimitiveType.Void:
return true;
case PrimitiveType.Bool:
Context.Before.WriteLine($"JS_NewBool(ctx, {Context.ArgName});");
break;
case PrimitiveType.Char:
case PrimitiveType.Char16:
case PrimitiveType.Char32:
case PrimitiveType.WideChar:
case PrimitiveType.SChar:
case PrimitiveType.UChar:
case PrimitiveType.Short:
case PrimitiveType.UShort:
case PrimitiveType.Int:
case PrimitiveType.UInt:
case PrimitiveType.Long:
Context.Before.WriteLine($"JS_NewInt32(ctx, {Context.ArgName});");
break;
case PrimitiveType.Long:
case PrimitiveType.UInt:
case PrimitiveType.ULong:
Context.Before.WriteLine($"JS_NewUint32(ctx, {Context.ArgName});");
break;
case PrimitiveType.LongLong:
Context.Before.WriteLine($"JS_NewBigInt64(ctx, {Context.ArgName});");
break;
case PrimitiveType.ULongLong:
Context.Before.WriteLine($"JS_NewBigUint64(ctx, {Context.ArgName});");
break;
case PrimitiveType.Float:
case PrimitiveType.Double:
Context.Before.WriteLine($"JS_NewFloat64(ctx, {Context.ArgName});");
break;
case PrimitiveType.LongDouble:
throw new NotImplementedException();
case PrimitiveType.Null:
Context.Before.WriteLine($"JS_NULL;");
break;
default:
throw new NotImplementedException();
}
@ -474,23 +498,68 @@ namespace CppSharp.Generators.Cpp @@ -474,23 +498,68 @@ namespace CppSharp.Generators.Cpp
{
case PrimitiveType.Void:
return true;
case PrimitiveType.Bool:
//JS_ToBool
Context.Before.WriteLine($"{Context.ArgName} = JS_ToBool(ctx, argv[{Context.ParameterIndex}]);");
Context.Before.WriteLine($"if ({Context.ArgName} == -1)");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
return true;
case PrimitiveType.Char:
case PrimitiveType.SChar:
case PrimitiveType.UChar:
Context.Before.WriteLine($"int32_t _{Context.ArgName};");
Context.Before.WriteLine($"if (JS_ToInt32(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
return true;
case PrimitiveType.Short:
case PrimitiveType.UShort:
Context.Before.WriteLine($"int32_t _{Context.ArgName};");
Context.Before.WriteLine($"if (JS_ToInt32(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
return true;
case PrimitiveType.Int:
case PrimitiveType.Long:
Context.Before.WriteLine($"if (JS_ToInt32(ctx, &{Context.ArgName}, argv[{Context.ParameterIndex}]))");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
return true;
case PrimitiveType.UInt:
case PrimitiveType.Long:
case PrimitiveType.ULong:
Context.Before.WriteLine($"if (JS_ToUint32(ctx, &{Context.ArgName}, argv[{Context.ParameterIndex}]))");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
return true;
case PrimitiveType.LongLong:
Context.Before.WriteLine($"int64_t _{Context.ArgName};");
Context.Before.WriteLine($"if (JS_ToInt64Ext(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
return true;
case PrimitiveType.ULongLong:
Context.Before.WriteLine($"int64_t _{Context.ArgName};");
Context.Before.WriteLine($"if (JS_ToInt64Ext(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
return true;
case PrimitiveType.Float:
Context.Before.WriteLine($"double _{Context.ArgName};");
Context.Before.WriteLine($"if (JS_ToFloat64(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
return true;
case PrimitiveType.Double:
Context.Before.WriteLine($"if (JS_ToFloat64(ctx, &{Context.ArgName}, argv[{Context.ParameterIndex}]))");
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
return true;
case PrimitiveType.WideChar:
default:
throw new NotImplementedException();

10
src/Generator/Generators/C/QuickJS/QuickJSModule.cs

@ -8,15 +8,14 @@ namespace CppSharp.Generators.Cpp @@ -8,15 +8,14 @@ namespace CppSharp.Generators.Cpp
/// Generates QuickJS C/C++ module init files.
/// QuickJS documentation: https://bellard.org/quickjs/
/// </summary>
public class QuickJSModule : CCodeGenerator
public class QuickJSModule : NAPICodeGenerator
{
readonly Module module;
private readonly Module module;
public QuickJSModule(BindingContext context, Module module)
: base(context, module.Units.GetGenerated())
{
this.module = module;
CTypePrinter.PushContext(TypePrinterContextKind.Managed);
}
public override string FileExtension { get; } = "cpp";
@ -92,7 +91,7 @@ namespace CppSharp.Generators.Cpp @@ -92,7 +91,7 @@ namespace CppSharp.Generators.Cpp
WriteLine("JSModuleDef* m;");
WriteLine($"m = JS_NewCModule(ctx, module_name, js_{moduleName}_init);");
WriteLine("if (!m)");
WriteLineIndent("return NULL;");
WriteLineIndent("return nullptr;");
NewLine();
WriteLine($"JS_AddModuleExportList(ctx, m, js_{moduleName}_funcs," +
@ -133,6 +132,9 @@ namespace CppSharp.Generators.Cpp @@ -133,6 +132,9 @@ namespace CppSharp.Generators.Cpp
public override bool VisitFunctionDecl(Function function)
{
if (!function.IsGenerated)
return true;
WriteLine($"JS_CFUNC_DEF(\"{function.Name}\"," +
$" {function.Parameters.Count}, js_{function.Name}),");

36
src/Generator/Generators/C/QuickJS/QuickJSSources.cs

@ -1,7 +1,9 @@ @@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using CppSharp.Generators.C;
namespace CppSharp.Generators.Cpp
@ -10,12 +12,11 @@ namespace CppSharp.Generators.Cpp @@ -10,12 +12,11 @@ namespace CppSharp.Generators.Cpp
/// Generates QuickJS C/C++ source files.
/// QuickJS documentation: https://bellard.org/quickjs/
/// </summary>
public class QuickJSSources : CppSources
public class QuickJSSources : NAPICallbacks
{
public QuickJSSources(BindingContext context, IEnumerable<TranslationUnit> units)
: base(context, units)
{
CTypePrinter.PushContext(TypePrinterContextKind.Managed);
}
public override void Process()
@ -34,8 +35,8 @@ namespace CppSharp.Generators.Cpp @@ -34,8 +35,8 @@ namespace CppSharp.Generators.Cpp
{
WriteInclude(new CInclude()
{
File = unit.FileName,
Kind = CInclude.IncludeKind.Quoted
File = unit.IncludePath,
Kind = CInclude.IncludeKind.Angled
});
}
@ -45,10 +46,10 @@ namespace CppSharp.Generators.Cpp @@ -45,10 +46,10 @@ namespace CppSharp.Generators.Cpp
VisitNamespace(TranslationUnit);
}
public override ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex,
public override NAPICallbacks.ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex,
Function function = null)
{
var paramMarshal = new ParamMarshal { Name = param.Name, Param = param };
var paramMarshal = new NAPICallbacks.ParamMarshal { Name = param.Name, Param = param };
var argName = Generator.GeneratedIdentifier(param.Name);
@ -98,8 +99,20 @@ namespace CppSharp.Generators.Cpp @@ -98,8 +99,20 @@ namespace CppSharp.Generators.Cpp
WriteLine($"return {marshal.Context.Return};");
}
public override bool VisitFunctionDecl(Function function)
public override void GenerateFunctionGroup(List<Function> @group)
{
GenerateFunctionCallback(@group);
}
public override void GenerateMethodGroup(List<Method> @group)
{
GenerateFunctionCallback(@group.OfType<Function>().ToList());
}
public override void GenerateFunctionCallback(List<Function> @group)
{
var function = @group.First();
PushBlock();
Write("extern \"C\" ");
WriteLine($"JSValue js_{function.Name}(JSContext* ctx, JSValueConst this_val,");
WriteLineIndent("int argc, JSValueConst* argv)");
@ -107,9 +120,14 @@ namespace CppSharp.Generators.Cpp @@ -107,9 +120,14 @@ namespace CppSharp.Generators.Cpp
GenerateFunctionCall(function);
UnindentAndWriteCloseBrace();
var needsReturn = !function.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void);
if (!needsReturn)
{
WriteLine("return JS_UNDEFINED;");
}
return true;
UnindentAndWriteCloseBrace();
PopBlock(NewLineKind.BeforeNextBlock);
}
}
}

4
tests2/quickjs/.gitignore vendored

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
gen
*.so
*.dylib
*.dll

21
tests2/quickjs/premake5.lua

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
qjs_inc_dir = path.getabsolute("../../deps/txiki.js/deps/quickjs/include")
qjs_lib_dir = path.getabsolute("../../deps/txiki.js/deps/quickjs/include")
workspace "qjs"
configurations { "release" }
location "gen"
symbols "On"
optimize "Off"
project "test"
kind "SharedLib"
language "C++"
files {"gen/**.cpp"}
includedirs { qjs_inc_dir, ".." }
libdirs { qjs_lib_dir }
filter { "kind:StaticLib" }
links { "quickjs" }
filter { "kind:SharedLib" }
defines { "JS_SHARED_LIBRARY" }
filter { "kind:SharedLib", "system:macosx" }
linkoptions { "-undefined dynamic_lookup" }

96
tests2/quickjs/test.js

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
import * as test from "./libtest.so";
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 + ")" : ""));
}
const eq = assert;
const floateq = (actual, expected) => { assert(Math.abs(actual - expected) < Number.EPSILON) }
const ascii = v => v.charCodeAt(0)
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.ReturnsNullptr(), 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);
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);
}
builtins();

26
tests2/quickjs/test.sh

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -e
dir=$(cd "$(dirname "$0")"; pwd)
rootdir="$dir/../.."
configuration=Release
platform=x64
jsinterp="$rootdir/deps/quickjs/qjs"
red=`tput setaf 1`
green=`tput setaf 2`
reset=`tput sgr0`
echo "${green}Generating bindings${reset}"
#dotnet $rootdir/bin/${configuration}_${platform}/CppSharp.CLI.dll \
# --gen=qjs -I$dir/.. -o $dir/gen -m tests $dir/../*.h
echo "${green}Building generated binding files${reset}"
premake=$rootdir/build/premake.sh
$premake --file=$dir/premake5.lua gmake
make -C $dir/gen
echo
echo "${green}Executing JS tests with QuickJS${reset}"
cp $dir/gen/bin/release/libtest.so $dir
#cp $dir/gen/bin/release/libtest.dylib $dir
$jsinterp $dir/test.js
Loading…
Cancel
Save