Skip to content

Commit 93dc4e9

Browse files
committed
Add initial QuickJS primitive type support and test suite.
1 parent c18d047 commit 93dc4e9

File tree

8 files changed

+259
-18
lines changed

8 files changed

+259
-18
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,11 @@ public NAPICallbacks(BindingContext context)
336336
{
337337
}
338338

339+
public NAPICallbacks(BindingContext context, IEnumerable<TranslationUnit> units)
340+
: base(context, units)
341+
{
342+
}
343+
339344
public override void GenerateFunctionGroup(List<Function> @group)
340345
{
341346
var function = @group.First();
@@ -596,7 +601,7 @@ private void GenerateOverloadCalls(IList<Function> @group, DFSM stateMachine)
596601
}
597602
}
598603

599-
public void GenerateFunctionCall(Function function)
604+
public virtual void GenerateFunctionCall(Function function)
600605
{
601606
var @params = GenerateFunctionParamsMarshal(function.Parameters, function);
602607

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

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,26 +145,50 @@ public bool VisitPrimitiveType(PrimitiveType primitive)
145145
{
146146
case PrimitiveType.Void:
147147
return true;
148+
148149
case PrimitiveType.Bool:
150+
Context.Before.WriteLine($"JS_NewBool(ctx, {Context.ArgName});");
151+
break;
152+
149153
case PrimitiveType.Char:
150154
case PrimitiveType.Char16:
155+
case PrimitiveType.Char32:
151156
case PrimitiveType.WideChar:
152157
case PrimitiveType.SChar:
153158
case PrimitiveType.UChar:
154159
case PrimitiveType.Short:
155160
case PrimitiveType.UShort:
156161
case PrimitiveType.Int:
157-
case PrimitiveType.UInt:
162+
case PrimitiveType.Long:
158163
Context.Before.WriteLine($"JS_NewInt32(ctx, {Context.ArgName});");
159164
break;
160-
case PrimitiveType.Long:
165+
166+
case PrimitiveType.UInt:
161167
case PrimitiveType.ULong:
168+
Context.Before.WriteLine($"JS_NewUint32(ctx, {Context.ArgName});");
169+
break;
170+
162171
case PrimitiveType.LongLong:
172+
Context.Before.WriteLine($"JS_NewBigInt64(ctx, {Context.ArgName});");
173+
break;
174+
163175
case PrimitiveType.ULongLong:
176+
Context.Before.WriteLine($"JS_NewBigUint64(ctx, {Context.ArgName});");
177+
break;
178+
164179
case PrimitiveType.Float:
165180
case PrimitiveType.Double:
181+
Context.Before.WriteLine($"JS_NewFloat64(ctx, {Context.ArgName});");
182+
break;
183+
166184
case PrimitiveType.LongDouble:
185+
throw new NotImplementedException();
186+
167187
case PrimitiveType.Null:
188+
Context.Before.WriteLine($"JS_NULL;");
189+
break;
190+
191+
default:
168192
throw new NotImplementedException();
169193
}
170194

@@ -474,23 +498,68 @@ public bool VisitPrimitiveType(PrimitiveType primitive)
474498
{
475499
case PrimitiveType.Void:
476500
return true;
501+
477502
case PrimitiveType.Bool:
478-
//JS_ToBool
503+
Context.Before.WriteLine($"{Context.ArgName} = JS_ToBool(ctx, argv[{Context.ParameterIndex}]);");
504+
Context.Before.WriteLine($"if ({Context.ArgName} == -1)");
505+
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
506+
return true;
507+
479508
case PrimitiveType.Char:
509+
case PrimitiveType.SChar:
480510
case PrimitiveType.UChar:
511+
Context.Before.WriteLine($"int32_t _{Context.ArgName};");
512+
Context.Before.WriteLine($"if (JS_ToInt32(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
513+
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
514+
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
515+
return true;
516+
481517
case PrimitiveType.Short:
482518
case PrimitiveType.UShort:
519+
Context.Before.WriteLine($"int32_t _{Context.ArgName};");
520+
Context.Before.WriteLine($"if (JS_ToInt32(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
521+
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
522+
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
523+
return true;
524+
483525
case PrimitiveType.Int:
526+
case PrimitiveType.Long:
484527
Context.Before.WriteLine($"if (JS_ToInt32(ctx, &{Context.ArgName}, argv[{Context.ParameterIndex}]))");
485528
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
486529
return true;
530+
487531
case PrimitiveType.UInt:
488-
case PrimitiveType.Long:
489532
case PrimitiveType.ULong:
533+
Context.Before.WriteLine($"if (JS_ToUint32(ctx, &{Context.ArgName}, argv[{Context.ParameterIndex}]))");
534+
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
535+
return true;
536+
490537
case PrimitiveType.LongLong:
538+
Context.Before.WriteLine($"int64_t _{Context.ArgName};");
539+
Context.Before.WriteLine($"if (JS_ToInt64Ext(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
540+
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
541+
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
542+
return true;
543+
491544
case PrimitiveType.ULongLong:
545+
Context.Before.WriteLine($"int64_t _{Context.ArgName};");
546+
Context.Before.WriteLine($"if (JS_ToInt64Ext(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
547+
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
548+
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
549+
return true;
550+
492551
case PrimitiveType.Float:
552+
Context.Before.WriteLine($"double _{Context.ArgName};");
553+
Context.Before.WriteLine($"if (JS_ToFloat64(ctx, &_{Context.ArgName}, argv[{Context.ParameterIndex}]))");
554+
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
555+
Context.Before.WriteLine($"{Context.ArgName} = ({type})_{Context.ArgName};");
556+
return true;
557+
493558
case PrimitiveType.Double:
559+
Context.Before.WriteLine($"if (JS_ToFloat64(ctx, &{Context.ArgName}, argv[{Context.ParameterIndex}]))");
560+
Context.Before.WriteLineIndent("return JS_EXCEPTION;");
561+
return true;
562+
494563
case PrimitiveType.WideChar:
495564
default:
496565
throw new NotImplementedException();

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ namespace CppSharp.Generators.Cpp
88
/// Generates QuickJS C/C++ module init files.
99
/// QuickJS documentation: https://bellard.org/quickjs/
1010
/// </summary>
11-
public class QuickJSModule : CCodeGenerator
11+
public class QuickJSModule : NAPICodeGenerator
1212
{
13-
readonly Module module;
13+
private readonly Module module;
1414

1515
public QuickJSModule(BindingContext context, Module module)
1616
: base(context, module.Units.GetGenerated())
1717
{
1818
this.module = module;
19-
CTypePrinter.PushContext(TypePrinterContextKind.Managed);
2019
}
2120

2221
public override string FileExtension { get; } = "cpp";
@@ -92,7 +91,7 @@ public override void Process()
9291
WriteLine("JSModuleDef* m;");
9392
WriteLine($"m = JS_NewCModule(ctx, module_name, js_{moduleName}_init);");
9493
WriteLine("if (!m)");
95-
WriteLineIndent("return NULL;");
94+
WriteLineIndent("return nullptr;");
9695
NewLine();
9796

9897
WriteLine($"JS_AddModuleExportList(ctx, m, js_{moduleName}_funcs," +
@@ -133,6 +132,9 @@ public override bool VisitTranslationUnit(TranslationUnit unit)
133132

134133
public override bool VisitFunctionDecl(Function function)
135134
{
135+
if (!function.IsGenerated)
136+
return true;
137+
136138
WriteLine($"JS_CFUNC_DEF(\"{function.Name}\"," +
137139
$" {function.Parameters.Count}, js_{function.Name}),");
138140

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

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using CppSharp.AST;
6+
using CppSharp.AST.Extensions;
57
using CppSharp.Generators.C;
68

79
namespace CppSharp.Generators.Cpp
@@ -10,12 +12,11 @@ namespace CppSharp.Generators.Cpp
1012
/// Generates QuickJS C/C++ source files.
1113
/// QuickJS documentation: https://bellard.org/quickjs/
1214
/// </summary>
13-
public class QuickJSSources : CppSources
15+
public class QuickJSSources : NAPICallbacks
1416
{
1517
public QuickJSSources(BindingContext context, IEnumerable<TranslationUnit> units)
1618
: base(context, units)
1719
{
18-
CTypePrinter.PushContext(TypePrinterContextKind.Managed);
1920
}
2021

2122
public override void Process()
@@ -34,8 +35,8 @@ public override void Process()
3435
{
3536
WriteInclude(new CInclude()
3637
{
37-
File = unit.FileName,
38-
Kind = CInclude.IncludeKind.Quoted
38+
File = unit.IncludePath,
39+
Kind = CInclude.IncludeKind.Angled
3940
});
4041
}
4142

@@ -45,10 +46,10 @@ public override void Process()
4546
VisitNamespace(TranslationUnit);
4647
}
4748

48-
public override ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex,
49+
public override NAPICallbacks.ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex,
4950
Function function = null)
5051
{
51-
var paramMarshal = new ParamMarshal { Name = param.Name, Param = param };
52+
var paramMarshal = new NAPICallbacks.ParamMarshal { Name = param.Name, Param = param };
5253

5354
var argName = Generator.GeneratedIdentifier(param.Name);
5455

@@ -98,18 +99,35 @@ public override void GenerateFunctionCallReturnMarshal(Function function)
9899
WriteLine($"return {marshal.Context.Return};");
99100
}
100101

101-
public override bool VisitFunctionDecl(Function function)
102+
public override void GenerateFunctionGroup(List<Function> @group)
102103
{
104+
GenerateFunctionCallback(@group);
105+
}
106+
107+
public override void GenerateMethodGroup(List<Method> @group)
108+
{
109+
GenerateFunctionCallback(@group.OfType<Function>().ToList());
110+
}
111+
public override void GenerateFunctionCallback(List<Function> @group)
112+
{
113+
var function = @group.First();
114+
115+
PushBlock();
103116
Write("extern \"C\" ");
104117
WriteLine($"JSValue js_{function.Name}(JSContext* ctx, JSValueConst this_val,");
105118
WriteLineIndent("int argc, JSValueConst* argv)");
106119
WriteOpenBraceAndIndent();
107120

108121
GenerateFunctionCall(function);
109122

110-
UnindentAndWriteCloseBrace();
123+
var needsReturn = !function.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void);
124+
if (!needsReturn)
125+
{
126+
WriteLine("return JS_UNDEFINED;");
127+
}
111128

112-
return true;
129+
UnindentAndWriteCloseBrace();
130+
PopBlock(NewLineKind.BeforeNextBlock);
113131
}
114132
}
115133
}

tests2/quickjs/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
gen
2+
*.so
3+
*.dylib
4+
*.dll

tests2/quickjs/premake5.lua

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
qjs_inc_dir = path.getabsolute("../../deps/txiki.js/deps/quickjs/include")
2+
qjs_lib_dir = path.getabsolute("../../deps/txiki.js/deps/quickjs/include")
3+
4+
workspace "qjs"
5+
configurations { "release" }
6+
location "gen"
7+
symbols "On"
8+
optimize "Off"
9+
10+
project "test"
11+
kind "SharedLib"
12+
language "C++"
13+
files {"gen/**.cpp"}
14+
includedirs { qjs_inc_dir, ".." }
15+
libdirs { qjs_lib_dir }
16+
filter { "kind:StaticLib" }
17+
links { "quickjs" }
18+
filter { "kind:SharedLib" }
19+
defines { "JS_SHARED_LIBRARY" }
20+
filter { "kind:SharedLib", "system:macosx" }
21+
linkoptions { "-undefined dynamic_lookup" }

tests2/quickjs/test.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as test from "./libtest.so";
2+
3+
function assert(actual, expected, message) {
4+
if (arguments.length == 1)
5+
expected = true;
6+
7+
if (actual === expected)
8+
return;
9+
10+
if (actual !== null && expected !== null
11+
&& typeof actual == 'object' && typeof expected == 'object'
12+
&& actual.toString() === expected.toString())
13+
return;
14+
15+
throw Error("assertion failed: got |" + actual + "|" +
16+
", expected |" + expected + "|" +
17+
(message ? " (" + message + ")" : ""));
18+
}
19+
20+
const eq = assert;
21+
const floateq = (actual, expected) => { assert(Math.abs(actual - expected) < Number.EPSILON) }
22+
23+
const ascii = v => v.charCodeAt(0)
24+
25+
function builtins()
26+
{
27+
eq(test.ReturnsVoid(), undefined)
28+
29+
eq(test.ReturnsBool(), true)
30+
eq(test.PassAndReturnsBool(false), false)
31+
32+
eq(test.ReturnsNullptr(), null)
33+
//eq(test.PassAndReturnsNullptr(null), null)
34+
eq(test.ReturnsNullptr(), null)
35+
36+
eq(test.ReturnsChar (), ascii('a'));
37+
eq(test.ReturnsSChar(), ascii('a'));
38+
eq(test.ReturnsUChar(), ascii('a'));
39+
40+
eq(test.PassAndReturnsChar (ascii('a')), ascii('a'));
41+
eq(test.PassAndReturnsSChar(ascii('b')), ascii('b'));
42+
eq(test.PassAndReturnsUChar(ascii('c')), ascii('c'));
43+
44+
// TODO: add wchar_t tests
45+
46+
eq(test.ReturnsFloat (), 5.0);
47+
eq(test.ReturnsDouble(), -5.0);
48+
//eq(test.ReturnsLongDouble(), -5.0);
49+
50+
//floateq(test.PassAndReturnsFloat (1.32), 1.32);
51+
floateq(test.PassAndReturnsDouble(1.32), 1.32);
52+
//float(test.PassAndReturnsLongDouble(1.32), 1.32);
53+
54+
eq(test.ReturnsInt8 (), -5);
55+
eq(test.ReturnsUInt8 (), 5);
56+
eq(test.ReturnsInt16 (), -5);
57+
eq(test.ReturnsUInt16(), 5);
58+
eq(test.ReturnsInt32 (), -5);
59+
eq(test.ReturnsUInt32(), 5);
60+
eq(test.ReturnsInt64 (), -5n);
61+
eq(test.ReturnsUInt64(), 5n);
62+
63+
const int8 = { min: -(2**7), max: (2**7) - 1 };
64+
eq(test.PassAndReturnsInt8(int8.min), int8.min);
65+
eq(test.PassAndReturnsInt8(int8.max), int8.max);
66+
67+
const uint8 = { min: 0, max: (2**8) - 1 };
68+
eq(test.PassAndReturnsUInt8(uint8.min), uint8.min);
69+
eq(test.PassAndReturnsUInt8(uint8.max), uint8.max);
70+
71+
const int16 = { min: -(2**15), max: (2**15) - 1 };
72+
eq(test.PassAndReturnsInt16(int16.min), int16.min);
73+
eq(test.PassAndReturnsInt16(int16.max), int16.max);
74+
75+
const uint16 = { min: 0, max: (2**16) - 1 };
76+
eq(test.PassAndReturnsUInt16(uint16.min), uint16.min);
77+
eq(test.PassAndReturnsUInt16(uint16.max), uint16.max);
78+
79+
const int32 = { min: -(2**31), max: (2**31) - 1 };
80+
eq(test.PassAndReturnsInt32(int32.min), int32.min);
81+
eq(test.PassAndReturnsInt32(int32.max), int32.max);
82+
83+
const uint32 = { min: 0, max: (2**32) - 1 };
84+
eq(test.PassAndReturnsUInt32(uint32.min), uint32.min);
85+
eq(test.PassAndReturnsUInt32(uint32.max), uint32.max);
86+
87+
const int64 = { min: BigInt(2**63) * -1n, max: BigInt(2**63) - 1n };
88+
eq(test.PassAndReturnsInt64(int64.min), int64.min);
89+
eq(test.PassAndReturnsInt64(int64.max), int64.max);
90+
91+
const uint64 = { min: BigInt(0), max: BigInt(2**64) - 1n };
92+
eq(test.PassAndReturnsUInt64(uint64.min), uint64.min);
93+
eq(test.PassAndReturnsUInt64(uint64.max), uint64.max);
94+
}
95+
96+
builtins();

0 commit comments

Comments
 (0)