Skip to content
Draft
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
b029d16
qjs::shared_ptr
ftk Nov 27, 2021
8a4e7a8
qjs::shared_ptr test
ftk Nov 27, 2021
ffc7991
remove/break most of std::shared_ptr
ftk Nov 27, 2021
39c4030
Merge branch 'master' into sharedptr
ftk Nov 29, 2021
4900fe1
fix mark(WIP)
ftk Nov 29, 2021
34d58cd
restore construction
ftk Nov 29, 2021
35b18d6
replace the rest of std::shared_ptr
ftk Nov 29, 2021
60402bb
replace the rest of std::shared_ptr
ftk Nov 29, 2021
3f38a4b
restore inheritance
ftk Nov 30, 2021
45a8f96
update class example
ftk Nov 30, 2021
7040e79
enable_shared_from_this
ftk Nov 30, 2021
ab4b6d8
restore getter/setter
ftk Nov 30, 2021
2522501
Merge branch 'master' into sharedptr
ftk Dec 4, 2021
a965a1e
fix memory leak in Value::evalThis
ftk Dec 4, 2021
fa6c664
example of JS-overridden methods
ftk Dec 4, 2021
35005e3
minor cleanup
ftk Dec 4, 2021
0bafecb
catch exception in function:wrap
ftk Dec 4, 2021
6d15bf6
fix exception safety, memory leak in make_shared
ftk Dec 4, 2021
9eb2eb3
fix segfault in enable_shared_from_this
ftk Dec 4, 2021
bcda392
mark unimplemented traits as deleted
ftk Dec 5, 2021
0953d93
js_traits for T&
ftk Dec 5, 2021
9a1ca73
fix printf error in error handling
ftk Dec 13, 2021
e0cef4f
improve conversion test coverage
ftk Dec 13, 2021
68cbee1
detect gcc-8 in cmake
ftk Dec 17, 2021
b15e3f9
add support for downloading cross-compiled quickjs with cmake
ftk Dec 17, 2021
96fdd79
syntax error(?) fix
ftk Dec 17, 2021
47c9957
fix for MSVC
ftk Dec 17, 2021
43d0e6a
update CI
ftk Dec 17, 2021
71046d5
enable exception handling for MSVC
ftk Dec 17, 2021
1f56f4d
fix cmake compiler detection
ftk Dec 17, 2021
e2102e8
fix exceptions causing std::terminate during class registering
ftk Dec 18, 2021
91b5392
cmake CI install
ftk Dec 18, 2021
4f8deb8
comparison fix
ftk Dec 18, 2021
9f70ee8
update valgrind CI
ftk Dec 18, 2021
c62f91d
update quickjs dll CI
ftk Dec 18, 2021
cacdb63
add msvc patch
ftk Dec 18, 2021
7f76abd
Merge branch 'ci' into sharedptr
ftk Dec 18, 2021
ccf8074
fix MSVC compilation
ftk Dec 18, 2021
4572d24
add static assert for mark<>
ftk Dec 18, 2021
3ece2fb
Merge branch 'master' into sharedptr
ftk Feb 5, 2022
3742bf6
fix link in cmake, show message
ftk Apr 16, 2022
95db90d
msvc constexpr fix
ftk Apr 16, 2022
56278b2
add missing js_traits<T*>::wrap (#55)
ftk May 9, 2022
f0ed358
Merge branch 'master' into sharedptr
ftk Jul 3, 2022
5a36840
fix js inheritance example
ftk Jul 3, 2022
6f8d313
fix functions with arguments
ftk Jul 3, 2022
1ac22ad
example passing c++ objects to js code
ftk Jul 3, 2022
6965292
mark ESFT::shared_this in register_class instead of class_registrar
ftk Jul 21, 2022
8cfdb24
support for call and exotic methods
ftk Jul 21, 2022
5feb2b1
exotic class example
ftk Jul 21, 2022
592edbb
fix getclassid patch (JSValue->JSValueConst)
ftk Jul 21, 2022
7855b5a
add .static_fun to class_registrar for registering JS static properties
ftk Jul 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions .github/workflows/cxx.yml
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
compiler: [linux-gcc,linux-clang,linux-gcc8,linux-gcc10,linux-gcc-ubsan,linux-gcc-asan,linux-gcc-32,linux-gcc-x32,linux-clang11,linux-clang12,macos-clang]
compiler: [ linux-gcc,linux-clang,linux-gcc8,linux-gcc10,linux-gcc-ubsan,linux-gcc-asan,linux-gcc-32,linux-gcc-x32,linux-clang11,linux-clang12,macos-clang,windows-msvc,windows-msvc-32,windows-clang,windows-clang-32 ]
build_type: [Debug]
include:
- compiler: linux-gcc
@@ -21,7 +21,7 @@ jobs:
- compiler: linux-gcc8
os: ubuntu-18.04
preconfigure: sudo apt-get update && sudo apt-get install -y gcc-8 g++-8
cmake_opts: -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_REQUIRED_LIBRARIES=stdc++fs
cmake_opts: -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8

- compiler: linux-gcc10
os: ubuntu-20.04
@@ -61,6 +61,20 @@ jobs:
- compiler: macos-clang
os: macos-latest

- compiler: windows-msvc
os: windows-latest

- compiler: windows-msvc-32
os: windows-latest
cmake_opts: -A Win32

- compiler: windows-clang
os: windows-latest
cmake_opts: -T ClangCL

- compiler: windows-clang-32
os: windows-latest
cmake_opts: -T ClangCL -A Win32
steps:
- uses: actions/checkout@v2
with:
@@ -70,15 +84,21 @@ jobs:
- name: configure
run: cmake -S . -B build_dir -Wdev -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_C_FLAGS="${{ matrix.cflags }}" -DCMAKE_CXX_FLAGS="${{ matrix.cflags }}" -DCMAKE_EXE_LINKER_FLAGS="${{ matrix.cflags }}" ${{ matrix.cmake_opts }}
- name: build
run: cmake --build build_dir --verbose
run: cmake --build build_dir --verbose --config ${{ matrix.build_type }}
- name: test
run: ctest --extra-verbose
run: ctest --extra-verbose --build-config ${{ matrix.build_type }}
working-directory: build_dir
env:
# extra options for address sanitizer
ASAN_OPTIONS: strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
- name: install
run: cmake --install build_dir --prefix install_prefix --config ${{ matrix.build_type }}
- uses: actions/upload-artifact@v2
with:
name: ${{ matrix.compiler }}
path: install_prefix

build_windows:
build_mingw:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
@@ -90,9 +110,6 @@ jobs:
os: windows-2019
cmake_opts: -G "Ninja" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++

# - compiler: windows-msvs # not supported
# os: windows-latest

defaults:
run:
shell: msys2 {0}
@@ -117,4 +134,10 @@ jobs:
working-directory: build_dir
env:
# extra options for address sanitizer
ASAN_OPTIONS: strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
ASAN_OPTIONS: strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
- name: install
run: cmake --install build_dir --prefix install_prefix
- uses: actions/upload-artifact@v2
with:
name: ${{ matrix.compiler }}-${{ matrix.build_type }}
path: install_prefix
6 changes: 5 additions & 1 deletion .github/workflows/valgrind.yml
Original file line number Diff line number Diff line change
@@ -5,14 +5,18 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: install
run: sudo apt-get update; sudo apt-get install -y valgrind
- name: configure
run: echo 'include(Dart)' >> CMakeLists.txt && cmake -S . -B build_dir -Wdev -DCMAKE_BUILD_TYPE=Debug -DMEMORYCHECK_COMMAND_OPTIONS="--leak-check=full --track-origins=yes --error-exitcode=1"
run: echo 'include(Dart)' >> CMakeLists.txt && cmake -S . -B build_dir -Wdev -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DMEMORYCHECK_COMMAND_OPTIONS="--leak-check=full --track-origins=yes --error-exitcode=1"
- name: build
run: cmake --build build_dir --verbose
- name: test
17 changes: 11 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -4,18 +4,23 @@ project(quickjspp LANGUAGES CXX)
#set(CMAKE_CXX_STANDARD 17)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)

if(CMAKE_COMPILER_IS_GNUCC)
add_compile_options(-Wall -Wno-unused-parameter)
endif()

add_subdirectory(quickjs)

add_library(quickjspp INTERFACE)
target_link_libraries(quickjspp INTERFACE quickjs ${CMAKE_REQUIRED_LIBRARIES})
target_compile_features(quickjspp INTERFACE cxx_std_17)
target_link_libraries(quickjspp INTERFACE quickjs)
target_compile_features(quickjspp INTERFACE cxx_std_20)
target_include_directories(quickjspp INTERFACE .)
set_target_properties(quickjspp PROPERTIES PUBLIC_HEADER quickjspp.hpp)

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(quickjspp INTERFACE -Wall -Wno-unused-parameter) # enable warnings
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
target_link_libraries(quickjspp INTERFACE stdc++fs) # required for gcc-8
endif()
elseif(MSVC)
add_compile_options(/EHsc) # enable exceptions
endif()

add_executable(qjs qjs.cpp)
target_link_libraries(qjs quickjspp)

74 changes: 54 additions & 20 deletions quickjs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,27 +1,61 @@
project(quickjs LANGUAGES C)

file(STRINGS VERSION version)
if(MSVC)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(arch 64)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(arch 32)
endif()

set(quickjs_src quickjs.c libbf.c libunicode.c libregexp.c cutils.c quickjs-libc.c)
set(quickjs_def CONFIG_VERSION="${version}" _GNU_SOURCE CONFIG_BIGNUM)
message(VERBOSE "Compiling QuickJS with MSVC is not supported")
message(VERBOSE "However we can download QuickJS DLL that was cross-compiled with MinGW-w64 by GH-action")
message(VERBOSE "https://nightly.link/ftk/quickjspp/workflows/quickjsdll/master/")
message(VERBOSE "If you don't want to download DLLs from the internet, see .github/workflows/quickjsdll.yml for instructions on how to compile it yourself")

add_library(quickjs ${quickjs_src})
target_compile_definitions(quickjs PRIVATE ${quickjs_def} )
set_target_properties(quickjs PROPERTIES PUBLIC_HEADER "quickjs.h;quickjs-libc.h")
if(NOT EXISTS ${CMAKE_BINARY_DIR}/libquickjs${arch}.dll)
cmake_minimum_required(VERSION 3.18)
file(DOWNLOAD
"https://nightly.link/ftk/quickjspp/workflows/quickjsdll/ci/dlls%20-flto%20-O3%20-DNDEBUG.zip"
libquickjs.zip
TLS_VERIFY ON SHOW_PROGRESS)
file(ARCHIVE_EXTRACT INPUT libquickjs.zip DESTINATION ${CMAKE_BINARY_DIR})
endif()
add_library(quickjs SHARED IMPORTED GLOBAL)
add_library(quickjs-dumpleaks ALIAS quickjs)
set_target_properties(quickjs PROPERTIES
IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/libquickjs${arch}.dll
IMPORTED_IMPLIB ${CMAKE_BINARY_DIR}/libquickjs${arch}.a
)
include(GNUInstallDirs)
install(FILES quickjs.h quickjs-libc.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/quickjs)
install(FILES ${CMAKE_BINARY_DIR}/libquickjs32.dll ${CMAKE_BINARY_DIR}/libquickjs64.dll
DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ${CMAKE_BINARY_DIR}/libquickjs32.a ${CMAKE_BINARY_DIR}/libquickjs64.a
DESTINATION ${CMAKE_INSTALL_LIBDIR}/quickjs)
else(MSVC)
# compile quickjs
file(STRINGS VERSION version)
set(quickjs_src quickjs.c libbf.c libunicode.c libregexp.c cutils.c quickjs-libc.c)
set(quickjs_def CONFIG_VERSION="${version}" _GNU_SOURCE CONFIG_BIGNUM)

add_library(quickjs-dumpleaks ${quickjs_src})
target_compile_definitions(quickjs-dumpleaks PRIVATE ${quickjs_def} DUMP_LEAKS=1)
add_library(quickjs ${quickjs_src})
target_compile_definitions(quickjs PRIVATE ${quickjs_def})
set_target_properties(quickjs PROPERTIES PUBLIC_HEADER "quickjs.h;quickjs-libc.h")

if(UNIX OR MINGW)
find_package(Threads)
target_link_libraries(quickjs ${CMAKE_DL_LIBS} m Threads::Threads)
target_link_libraries(quickjs-dumpleaks ${CMAKE_DL_LIBS} m Threads::Threads)
endif()
add_library(quickjs-dumpleaks ${quickjs_src})
target_compile_definitions(quickjs-dumpleaks PRIVATE ${quickjs_def} DUMP_LEAKS=1)

# install
include(GNUInstallDirs)
install(TARGETS quickjs
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/quickjs
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/quickjs
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/quickjs
)
if(UNIX OR MINGW)
find_package(Threads)
target_link_libraries(quickjs ${CMAKE_DL_LIBS} m Threads::Threads)
target_link_libraries(quickjs-dumpleaks ${CMAKE_DL_LIBS} m Threads::Threads)
endif()

# install
include(GNUInstallDirs)
install(TARGETS quickjs
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/quickjs
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/quickjs
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/quickjs
)
endif(MSVC)
2 changes: 1 addition & 1 deletion quickjs/patches/getclassid.patch
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ index 48aeffc62..8afc4caa1 100644
#endif
}
+
+JSClassID JS_GetClassID(JSValue v)
+JSClassID JS_GetClassID(JSValueConst v)
+{
+ JSObject *p;
+
25 changes: 25 additions & 0 deletions quickjs/patches/msvc-header-fix.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
diff --git a/quickjs/quickjs.h b/quickjs/quickjs.h
--- a/quickjs/quickjs.h (revision 96fdd7947760bed361be7ea1c749f41c4a3ab62e)
+++ b/quickjs/quickjs.h (revision 47c9957e9b830cfcde70aebf6005a26f45ec0bbe)
@@ -215,12 +215,18 @@
#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
#define JS_VALUE_GET_PTR(v) ((v).u.ptr)

+#ifdef __cplusplus
+#define JS_MKVAL(tag, val) JSValue{ JSValueUnion{ .int32 = val }, tag }
+#define JS_MKPTR(tag, p) JSValue{ JSValueUnion{ .ptr = p }, tag }
+#define JS_NAN JSValue{ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
+#else
#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
-
-#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
-
#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
+#endif
+
+#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
+

static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
{
2 changes: 1 addition & 1 deletion quickjs/quickjs.c
Original file line number Diff line number Diff line change
@@ -54083,7 +54083,7 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx)
#endif
}

JSClassID JS_GetClassID(JSValue v)
JSClassID JS_GetClassID(JSValueConst v)
{
JSObject *p;

8 changes: 7 additions & 1 deletion quickjs/quickjs.h
Original file line number Diff line number Diff line change
@@ -215,12 +215,18 @@ typedef struct JSValue {
#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
#define JS_VALUE_GET_PTR(v) ((v).u.ptr)

#ifdef __cplusplus
#define JS_MKVAL(tag, val) JSValue{ JSValueUnion{ .int32 = val }, tag }
#define JS_MKPTR(tag, p) JSValue{ JSValueUnion{ .ptr = p }, tag }
#define JS_NAN JSValue{ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
#else
#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
#endif

#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)

#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }

static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
{
680 changes: 402 additions & 278 deletions quickjspp.hpp

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
macro(make_test name)
add_executable(${name} ${name}.cpp)
target_link_libraries(${name} quickjspp quickjs-dumpleaks)
add_test(${name} ${name})
add_test(NAME ${name} COMMAND ${name} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# try to compile with -DCONFIG_CHECK_JSVALUE
add_library(${name}-checkjsv OBJECT ${name}.cpp)
target_compile_definitions(${name}-checkjsv PRIVATE CONFIG_CHECK_JSVALUE=1)
@@ -11,6 +11,7 @@ endmacro()
foreach(test
value class exception example multicontext conversions point variant function_call inheritance jobs unhandled_rejection module_loader
enum
exotic
)
make_test(${test})
endforeach()
63 changes: 41 additions & 22 deletions test/class.cpp
Original file line number Diff line number Diff line change
@@ -3,9 +3,9 @@
#include <iostream>


#define TYPES bool, int32_t, double, std::shared_ptr<test>, const std::shared_ptr<test>&, std::string, const std::string&
#define TYPES bool, int32_t, double, qjs::shared_ptr<test>, const qjs::shared_ptr<test>&, std::string, const std::string&

class base_test
class base_test : public qjs::enable_shared_from_this<base_test>
{
public:
std::vector<std::vector<int>> base_field;
@@ -15,6 +15,10 @@ class base_test
std::swap(x, base_field[0][0]);
return x;
}

// TODO: make it work in inherited classes
base_test * self() { return this; }
bool check_self(base_test * self) { return(this == self); }
};

class test : public base_test
@@ -23,7 +27,7 @@ class test : public base_test
bool b;
mutable int32_t i;
double d = 7.;
std::shared_ptr<test> spt;
qjs::shared_ptr<test> spt;
std::string s;

test(int32_t i, TYPES) : i(i)
@@ -40,7 +44,7 @@ class test : public base_test
int32_t fi(TYPES) const { i++; return i; }
bool fb(TYPES) { i++; return b; }
double fd(TYPES) const { i++; return d; }
const std::shared_ptr<test>& fspt(TYPES) { i++; return spt; }
const qjs::shared_ptr<test>& fspt(TYPES) { i++; return spt; }
const std::string& fs(TYPES) { i++; return s; }
void f(TYPES) { i++; }

@@ -51,7 +55,7 @@ class test : public base_test
static void fstatic(TYPES) {}
static bool bstatic;
};
bool test::bstatic;
bool test::bstatic = true;

void f(TYPES) {}

@@ -65,24 +69,27 @@ void qjs_glue(qjs::Context::Module& m) {
;

m.class_<::test>("test")
//.base<::base_test>()
.constructor<::int32_t, bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test> const &, ::std::string, ::std::string const &>("Test")
.base<::base_test>()
.constructor<::int32_t, bool, ::int32_t, double, ::qjs::shared_ptr<test>, ::qjs::shared_ptr<test> const &, ::std::string, ::std::string const &>("Test")
.static_fun<&::test::fstatic>("fstatic") // (bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test>const &, ::std::string, ::std::string const &)
.static_fun<&::test::bstatic>("bstatic") // bool
.constructor<::int32_t>("TestSimple")
.fun<&::test::fi>("fi") // (bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test> const &, ::std::string, ::std::string const &)
.fun<&::test::fb>("fb") // (bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test> const &, ::std::string, ::std::string const &)
.fun<&::test::fd>("fd") // (bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test> const &, ::std::string, ::std::string const &)
.fun<&::test::fspt>("fspt") // (bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test> const&, ::std::string, ::std::string const &)
.fun<&::test::fs>("fs") // (bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test> const &, ::std::string, ::std::string const &)
.fun<&::test::f>("f") // (bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test> const &, ::std::string, ::std::string const &)
.fun<&::test::fstatic>("fstatic") // (bool, ::int32_t, double, ::std::shared_ptr<test>, ::std::shared_ptr<test>const &, ::std::string, ::std::string const &)
.fun<&::test::bstatic>("bstatic") // bool
.fun<&::test::b>("b") // bool
.fun<&::test::i>("i") // ::int32_t
.fun<&::test::d>("d") // double
.fun<&::test::spt>("spt") // ::std::shared_ptr<test>
.fun<&::test::s>("s") // ::std::string
.fun<&::test::self>("self")
.fun<&::test::check_self>("check_self")
.property<&test::get_d, &test::set_d>("property_rw")
.property<&test::get_d>("property_ro")
.mark<&::test::spt>()
;
} // qjs_glue

@@ -100,13 +107,11 @@ int main()

try
{
qjs_glue(context.addModule("test"));

js_std_init_handlers(rt);
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, nullptr, js_module_loader, nullptr);
js_std_add_helpers(ctx, 0, nullptr);

qjs_glue(context.addModule("test"));

/* system modules */
js_init_module_std(ctx, "std");
js_init_module_os(ctx, "os");
@@ -117,12 +122,18 @@ int main()
"import * as test from 'test';\n"
"globalThis.std = std;\n"
"globalThis.test = test;\n"
"globalThis.os = os;\n";
"globalThis.os = os;\n"
"globalThis.assert = function(b, str)\n"
"{\n"
" if (b) {\n"
" std.printf('OK' + str + '\\n'); return;\n"
" } else {\n"
" throw Error(\"assertion failed: \" + str);\n"
" }\n"
"}";
context.eval(str, "<input>", JS_EVAL_TYPE_MODULE);


context.global()["assert"] = [](bool t) { if(!t) std::exit(2); };


auto xxx = context.eval("\"use strict\";"
"var b = new test.base_test();"
@@ -137,28 +148,36 @@ int main()
"q.d = 456.789;"
"q.s = \"STRING\";"
"q.spt = t;"
//"q.base_field = 105.5;"
"t.spt = q;"
"q.base_field = [[5],[1,2,3,4],[6]];"
"assert(q.b === q.fb(t.vb, t.vi, t.vd, t, t, t.vs, \"test\"));"
"assert(q.d === q.fd(t.vb, t.vi, t.vd, t, t, t.vs, \"test\"));"
"assert(q.s === q.fs(t.vb, t.vi, t.vd, t, t, \"test\", t.vs));"
//"assert(105.5 === q.base_method(5.1));"
//"assert(5.1 === q.base_field);"
"assert(q.spt !== q.fspt(t.vb, t.vi, t.vd, t, t, t.vs, \"test\"));" // different objects
"assert(5 === q.base_method(7));"
"assert(7 === q.base_field[0][0]);"
"assert(q.spt === q.fspt(t.vb, t.vi, t.vd, t, t, t.vs, \"test\"));" // same objects
"assert(q.check_self(q));"
"assert(!t.check_self(q));"
"q.fi(t.vb, t.vi, t.vd, t, t, t.vs, \"test\")");
assert((int)xxx == 18);
auto yyy = context.eval("q.fi.bind(t)(t.vb, t.vi, t.vd, t, t, t.vs, \"test\")");
assert((int)yyy == 13);

auto f = context.eval("q.fi.bind(q)").as<std::function<int32_t(TYPES)>>();
int zzz = f(false, 1, 0., context.eval("q").as<std::shared_ptr<test>>(),
context.eval("t").as<std::shared_ptr<test>>(), "test string", std::string{"test"});
int zzz = f(false, 1, 0., context.eval("q").as<qjs::shared_ptr<test>>(),
context.eval("t").as<qjs::shared_ptr<test>>(), "test string", std::string{"test"});
assert(zzz == 19);

zzz = (int)context.eval("q.property_rw = q.property_ro - q.property_rw + 1;"
"assert(q.property_ro === 1);"
"q.i"
);
assert(zzz == 23);

auto qbase = context.eval("q").as<qjs::shared_ptr<base_test>>();
assert(qbase->base_field[0][0] == 7);

assert((bool)context.eval("test.Test.bstatic === true"));
}
catch(exception)
{
4 changes: 4 additions & 0 deletions test/conversions.cpp
Original file line number Diff line number Diff line change
@@ -37,9 +37,13 @@ int main()
qjs::Context context(runtime);
try
{
test_num<int8_t>(context);
test_num<uint8_t>(context);
test_num<int16_t>(context);
test_num<uint16_t>(context);
test_num<int32_t>(context);
test_num<uint32_t>(context);
test_num<bool>(context);

// int64 is represented as double...
test_conv<int64_t>(context, -(1ll << 52));
5 changes: 5 additions & 0 deletions test/example.cpp
Original file line number Diff line number Diff line change
@@ -48,6 +48,11 @@ int main()
// callback
auto cb = (std::function<void(const std::string&)>) context.eval("my_callback");
cb("world");

// passing c++ objects to JS
auto lambda = context.eval("x=>my.println(x.member_function('lambda'))").as<std::function<void(qjs::shared_ptr<MyClass>)>>();
auto v3 = qjs::make_shared<MyClass>(context.ctx, std::vector{1,2,3});
lambda(v3);
}
catch(qjs::exception)
{
91 changes: 91 additions & 0 deletions test/exotic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/* Example of how to register a class without class_registrar and how to use exotic methods */

#include <iostream>
#include <stdio.h>
#include "quickjspp.hpp"

std::string atom_to_string(JSContext * ctx, JSAtom atom)
{
JSValue v = JS_AtomToString(ctx, atom);
auto str = qjs::js_traits<std::string>::unwrap(ctx, v);
JS_FreeValue(ctx, v);
return str;
}

class ExoticClass : public qjs::enable_shared_from_this<ExoticClass>
{
public:
std::function<bool(JSContext * ctx, JSAtom atom)> has_property;
std::function<qjs::Value(JSContext * ctx, JSAtom atom, JSValueConst receiver)> get_property;

static void register_class(JSContext * ctx)
{
// we are passing pointer to exotic - requires static lifetime
static JSClassExoticMethods exotic{
// see JSClassExoticMethods for more methods
.has_property = [](JSContext * ctx, JSValueConst obj, JSAtom atom) -> int {
try
{
auto ptr = qjs::js_traits<qjs::shared_ptr<ExoticClass>>::unwrap(ctx, obj);
return ptr->has_property(ctx, atom);
}
catch(qjs::exception)
{
return -1;
}
},
.get_property = [](JSContext * ctx, JSValueConst obj, JSAtom atom,
JSValueConst receiver) -> JSValue {
try
{
auto ptr = qjs::js_traits<qjs::shared_ptr<ExoticClass>>::unwrap(ctx, obj);
return ptr->get_property(ctx, atom, receiver);
}
catch(qjs::exception)
{
return JS_EXCEPTION;
}
}
};
qjs::js_traits<qjs::shared_ptr<ExoticClass>>::register_class(ctx, "ExoticClass", JS_NULL, nullptr, &exotic);
}
};


void println(qjs::rest<std::string> args)
{
for(auto const& arg: args) std::cout << arg << " ";
std::cout << "\n";
}

int main()
{
qjs::Runtime runtime;
qjs::Context context(runtime);
try
{
ExoticClass::register_class(context.ctx);

auto e = qjs::make_shared<ExoticClass>(context.ctx);
e->has_property = [](JSContext * ctx, JSAtom) { return true; };
e->get_property = [](JSContext * ctx, JSAtom prop, JSValueConst receiver) {
return qjs::Value{ctx, "got " + atom_to_string(ctx, prop)};
};

context.global()["e"] = e;
context.global()["println"] = qjs::fwrapper<&println>{"println"};

// prints: got property1
context.eval("println(e.property1);");

// prints: got property2
context.eval("println(e.property2);");

} catch(qjs::exception)
{
auto exc = context.getException();
std::cerr << (std::string) exc << std::endl;
if((bool) exc["stack"]) std::cerr << (std::string) exc["stack"] << std::endl;
return 1;
}
}
70 changes: 36 additions & 34 deletions test/inheritance.cpp
Original file line number Diff line number Diff line change
@@ -82,40 +82,42 @@ int main()
qjs::Runtime runtime;
qjs::Context context(runtime);

auto& test = context.addModule("test");
test.class_<A>("A")
.constructor<int>()
.fun<&A::a>("a")
.fun<&A::vfunc_a1>("vfunc_a1")
.fun<&A::vfunc_a2>("vfunc_a2")
.fun<&A::non_vfunc_a1>("non_vfunc_a1")
.fun<&A::non_vfunc_a2>("non_vfunc_a2");

test.class_<B>("B")
.constructor<int>()
.fun<&B::b>("b")
.fun<&B::vfunc_b1>("vfunc_b1")
.fun<&B::vfunc_b2>("vfunc_b2")
.fun<&B::non_vfunc_b1>("non_vfunc_b1")
.fun<&B::non_vfunc_b2>("non_vfunc_b2");

test.class_<C>("C")
.constructor<int, int, int, int>()
.base<A>()
.fun<&C::c>("c")
.fun<&C::b>("b")
.fun<&C::h>("h")
.fun<&C::vfunc_b1>("vfunc_b1")
.fun<&C::vfunc_b2>("vfunc_b2")
.fun<&C::vfunc_h1>("vfunc_h1")
.fun<&C::vfunc_h2>("vfunc_h2")
.fun<&C::non_vfunc_a1>("non_vfunc_a1")
.fun<&C::non_vfunc_b1>("non_vfunc_b1")
.fun<&C::non_vfunc_b2>("non_vfunc_b2")
.fun<&C::non_vfunc_h1>("non_vfunc_h1")
.fun<&C::non_vfunc_h2>("non_vfunc_h2");

try {
try
{
auto& test = context.addModule("test");
test.class_<A>("A")
.constructor<int>()
.fun<&A::a>("a")
.fun<&A::vfunc_a1>("vfunc_a1")
.fun<&A::vfunc_a2>("vfunc_a2")
.fun<&A::non_vfunc_a1>("non_vfunc_a1")
.fun<&A::non_vfunc_a2>("non_vfunc_a2");

test.class_<B>("B")
.constructor<int>()
.fun<&B::b>("b")
.fun<&B::vfunc_b1>("vfunc_b1")
.fun<&B::vfunc_b2>("vfunc_b2")
.fun<&B::non_vfunc_b1>("non_vfunc_b1")
.fun<&B::non_vfunc_b2>("non_vfunc_b2");

test.class_<C>("C")
.constructor<int, int, int, int>()
.base<A>()
.fun<&C::c>("c")
.fun<&C::b>("b")
.fun<&C::h>("h")
.fun<&C::vfunc_b1>("vfunc_b1")
.fun<&C::vfunc_b2>("vfunc_b2")
.fun<&C::vfunc_h1>("vfunc_h1")
.fun<&C::vfunc_h2>("vfunc_h2")
.fun<&C::non_vfunc_a1>("non_vfunc_a1")
.fun<&C::non_vfunc_b1>("non_vfunc_b1")
.fun<&C::non_vfunc_b2>("non_vfunc_b2")
.fun<&C::non_vfunc_h1>("non_vfunc_h1")
.fun<&C::non_vfunc_h2>("non_vfunc_h2");


context.eval(R"xxx(
import { A, B, C } from "test";
20 changes: 17 additions & 3 deletions test/point.cpp
Original file line number Diff line number Diff line change
@@ -2,15 +2,19 @@
#include <iostream>
#include <cmath>

class Point
class Point : public qjs::enable_shared_from_this<Point>
{
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}

double norm() const
double norm()
{
return std::sqrt((double)x * x + double(y) * y);
try {
auto js_norm = shared_from_this().evalThis("this.norm.bind(this)").as<std::function<double ()>>();
return js_norm();
} catch(qjs::exception e) {}
return std::sqrt((double) x * x + double(y) * y);
}
};

@@ -49,6 +53,10 @@ class ColorPoint extends Point {
get_color() {
return this.color;
}
norm() {
assert(this.color);
return 1.0;
}
};
function main()
@@ -66,10 +74,16 @@ function main()
assert(pt2.x === 2);
assert(pt2.color === 0xffffff);
assert(pt2.get_color() === 0xffffff);
assert(pt2.norm() === 1.0);
globalThis.pt2 = pt2;
}
main();
)xxx", "<eval>", JS_EVAL_TYPE_MODULE);
auto pt2 = context.eval("pt2").as<qjs::shared_ptr<Point>>();
assert(pt2->x == 2);
assert(pt2->y == 3);
assert(pt2->norm() == 1);
}
catch(qjs::exception)
{
12 changes: 7 additions & 5 deletions test/variant.cpp
Original file line number Diff line number Diff line change
@@ -7,17 +7,18 @@ struct A {};

struct B {};

using var = std::variant<bool, int, double, std::string, std::vector<int>, std::shared_ptr<A>, std::shared_ptr<B>>;
using var = std::variant<bool, int, double, std::string, std::vector<int>, qjs::shared_ptr<A>, qjs::shared_ptr<B>>;

static JSContext * jsctx;

auto f(var v1, const var& /*v2*/) -> var
{
return std::visit([](auto&& v) -> var {
using T = std::decay_t<decltype(v)>;
if constexpr (std::is_same_v<T, std::shared_ptr<A>>)
return std::make_shared<B>();
else if constexpr (std::is_same_v<T, std::shared_ptr<B>>)
return std::make_shared<A>();
if constexpr (std::is_same_v<T, qjs::shared_ptr<A>>)
return qjs::make_shared<B>(jsctx);
else if constexpr (std::is_same_v<T, qjs::shared_ptr<B>>)
return qjs::make_shared<A>(jsctx);
else if constexpr (std::is_same_v<T, std::vector<int>>)
{
v.push_back(0);
@@ -65,6 +66,7 @@ int main()
{
qjs::Runtime runtime;
qjs::Context context(runtime);
jsctx = context.ctx;

try
{