ftk / quickjspp Goto Github PK
View Code? Open in Web Editor NEWQuickJS C++ wrapper
QuickJS C++ wrapper
Is there currently a way to get the current qjs::Context
or qjs::Context::Module
from inside a registered class?
Nice work btw!
First up let me say how much I love this project! It's embarrassingly better than my own feeble attempt to wrap C++ types for use with QuickJS, and I've learned several useful template meta-programming tricks from reading the source code.
I hit a snag when I tried to implement a C++ object property that could be set (in JS) to a string, or an integer, or null. Obviously I chose to implement this property as having type std::variant<std::string, int>
on the C++ side. But how to represent null? I tried using void*, shared_ptr, optional... but couldn't get anything to work. RTFM'ing it seems std::monostate
is the thing to use, i.e. my property type becomes std::variant<std::string, int, std::monostate>
.
To get this to work I had to implement a js_traits
that can wrap and unwrap std::monostate
and also alter the existing js_traits<std::variant<...>>::unwrap()
to use std::monostate when given a JS_NULL.
My question is: Have I done the right thing? Is this the best way to implement support for nullable property values?
Can someone solve this problem? thanks
SINEPAN-MB0:build sine$ cmake ..
CMake Error at CMakeLists.txt:17 (set_target_properties):
INTERFACE_LIBRARY targets may only have whitelisted properties. The
property "PUBLIC_HEADER" is not allowed.
-- Configuring incomplete, errors occurred!
See also "/Users/sine/code2/quickjspp/build/CMakeFiles/CMakeOutput.log".
See also "/Users/sine/code2/quickjspp/build/CMakeFiles/CMakeError.log".
The quickjs-ng branch looks more friendly and active, and can be compiled and run directly under Windows. It has also added many patches, but it seems that many interfaces have changed and cannot be directly replaced
There are some methods which return std::unique_ptr object in my program, while the program can't compile successly.
From the code of quickjspp.h, I find I must implement js_traits<std::unique_ptr>
first, but I am not familiar with template.
So I am looking for helps for that.
Example:
// C++
class ExampleClass {
public:
void exampleMethod(){
log( "exampleMethod base" );
}
}
module.class_<ExampleClass>( "ExampleClass" )
.fun<&ExampleClass::exampleMethod>( "exampleMethod" );
// JS
class ExtClass extends ExampleClass {
exampleMethod() {
console.log("exampleMethod ext");
super.exampleMethod();
}
}
I would like this to happen:
// JS
myClass = new ExtClass();
// C++
myClassOpaque->exampleMethod();
// Should log:
// exampleMethod ext
// exampleMethod base
I suspect that inside ExampleClass::exampleMethod (and every method where I want this behaviour) I need to check whether the instance has a JS object associated and then manually call exampleMethod on that JS object?
But I'm hoping there is an easier way?
When there's a Javascript exception thrown in a function, the program terminates when trying to create the qjs::Value.
Example:
try
{
qjs::Value function = context.eval("() => { let a = b; };", "<test>");
auto native = function.as<std::function<qjs::Value()>>();
qjs::Value result = native();
}
catch(exception)
{
auto exc = context.getException();
std::cerr << (exc.isError() ? "Error: " : "Throw: ") << (std::string)exc << std::endl;
if((bool)exc["stack"])
std::cerr << (std::string)exc["stack"] << std::endl;
js_std_free_handlers(rt);
return 1;
}
Expected:
Error: ReferenceError: b is not defined
at <anonymous> (<test>)
Actual:
terminate called after throwing an instance of 'qjs::exception'
Work around:
If you remove the JS_IsException
check from the qjs::Value constructor (quickjspp.hpp:811) and instead handle it after it's constructed it works as expected.
try
{
qjs::Value function = context.eval("() => { let a = b; };", "<test>");
auto native = function.as<std::function<qjs::Value()>>();
qjs::Value result = native();
if(JS_IsException(result.v)) throw exception{};
}
catch(exception)
I have C++ class:
class Foo {
int get_bar();
void set_bar( int v );
}
and I'd like to wrap it as JS class that has bar
defined as instance property.
As far as I can see .fun()
allows to wrap either member variables or methods of a class, but not virtual properties represented by getter/setter pair, am I correct?
How we can build this project in Visual Studio, can you help please?
Hi, I'm developing a scripting-capable utility. I now use successfully wren-lang with wrenbind17 but I would like to try QuickJS.
However, I was unable to define a generic (variadic) datatype that allows me to call C++ functions from JS with compound objects such as Lists/Arrays or Map. Is it possible to do such a thing?
namespace js {
struct List {
// ??? DATA
template <typename T> T get(const size_t ix) const;
size_t size() const;
};
struct Map {
// ??? DATA
template <typename K, typename V> V get(const K key) const;
size_t size() const;
};
using Variant = std::variant<std::nullptr_t, bool, int, double, std::string, js::List, js::Map>;
struct any : public Variant {
using Variant::variant;
using Variant::operator=;
enum class Type : int {
null_t = 0, bool_t, int_t, double_t, string_t, list_t, map_t
};
any(const Variant& p) : Variant(p) {}
Type type() const { return (Type)this->index(); }
template <class T> bool is() const {
const Type t = type();
if constexpr (std::is_same<T, std::nullptr_t>::value)
return t == Type::null_t;
if constexpr (std::is_same<T, bool>::value)
return t == Type::bool_t;
if constexpr (std::is_same<T, int>::value)
return t == Type::int_t;
if constexpr (std::is_same<T, double>::value)
return t == Type::double_t;
if constexpr (std::is_same<T, std::string>::value)
return t == Type::string_t;
if constexpr (std::is_same<T, js::List>::value)
return t == Type::list_t;
if constexpr (std::is_same<T, js::Map>::value)
return t == Type::map_t;
return false;
}
template <class T> T as() const { return std::get<T>(*this); }
};
}
So js::List
is just a list/array of js::any
and js::Map
is a map of <std::string, js::any>
.
I'd like to do something like this:
/* C++ function definition*/
void f(std::string s, js::any a){
if(a.is<js::Map>()){
// Code for accessing map elements for example convert to nlohmann_json
} else if(a.is<js::List>()){
auto v = a.as<js::list>().get(0); //v is also a js::any.
} else { ... }
}
/* JS function call*/
module.f("~/example.dat", {
"parms" : {"hello":"HI!"},
"array" : [false, "str1", true, "str2"],
"id" : 38,
});
Is it possible to do this in quickjspp?
Hi,
Is there anyway you could provide instructions to build this? Including flags.
I also found the documentation for QuickJS itself quite lacking.
Miguel
Hello,
how can I bind class member operators like +, -, * etc.? Is it possible?
I'm trying to integrate javascript into clang-tidy and quickjspp seems like it might be a good way to do that. The problem is though that I can't use a library which requires throwing exceptions.
Hi,
I have two files, test_a.js and test_b.js. Here they are:
test_a.js
var test_object = {
foo: "test_a.js",
}
function func_a(obj) {
if ("foo" in obj)
{
System.print(obj.foo);
}
}
test_b.js
var test_object = {
foo: "test_b.js",
}
I load test_a.js along with native C++ function print
(imported as module System
) into context context
, and test_b.js into context context2
. Both belong to the same runtime.
My goal is to call func_a
from test_a.js in context
with the object test_object
from test_b.js from context2
as an argument.
However, trying this (ignoring my contexts and methods which are actually from my own wrapper class):
qjs::Value test_obj_b = context2.get()->eval("test_object");
auto func_a = context.get()->eval("func_a").as<std::function<void(const qjs::Value&)>>();
func_a(test_obj_b);
results in the following:
Assertion failed: ctx == v.ctx, file ../include/quickjspp/quickjspp.hpp, line 1807
Apparently when wrapping a JSValue into a Value, a check is performed to make sure the value is from the same context as the context it's being used in. However, I was under the impression from quickjs's documentation that sharing objects between contexts was possible ("There can be several JSContexts per JSRuntime and they can share objects[...]"), so I'm not sure if I'm doing something wrong with quickjspp, if this is intended behavior, or if I'm just missing some crucial piece of information in my basic idea.
EDIT: if I remove the assertion from quickjspp.hpp, things behave the way I expect, and "test_b.js" is output to the console. I imagine this might have consequences, though?
In you example, you register a C++ class like so:
module.class_<MyClass>("MyClass")
.constructor<>()
.constructor<std::vector<int>>("MyClassA")
.fun<&MyClass::member_variable>("member_variable")
.fun<&MyClass::member_function>("member_function");
and then you import the module in JS. I tried to extend this class with a small test script
class test extends my.MyClass {
constructor() {
super();
console.log('test.constructor');
}
};
but i get TypeError: parent prototype must be an object or null
.
Is this the expected behavior and I can't extend c++ module registered classes or is it a bug?
Thanks
I have a function written in JavaScript. I have created a callback in C++ as following:
void callbackFunction( int val ) {
try {
auto cb = (std::function<void(int val)>) context.eval("run");
cb(val);
}
catch {
...
}
}
I want to put callbackFunction() on a thread, std::thread(callbackFunction, val).detach()
. But this always end up with terminate called after throwing an instance of 'qjs::exception'
How can I use multithreading in quickjs? Is there any workaround?
Hi, I compiled the quickjspp
executable for running test scripts:
./quickjspp test.js
With test following:
// test.js
import * as os from 'os'
os.setTimeout(1000, () => {
console.log('timeout!')
})
This is supposed to set a 1s timer for the official QuickJS build. While nothing happens and the script returns. No exception is thrown in context.evalFile
.
However the native module "os" can be found in the script:
import * as os from 'os'
console.log(os.setTimeout)
This yields:
function setTimeout() {
[native code]
}
Help needed, thanks!
Hey! Great library.
Is it possible currently to define a native C++ class's base/parent class? Furthermore, is it possible for that definition to include the parents base class and so on. I noticed an in-progress/todo commented function base
, I tried to use it and it seemed to work to some extent but there are issues. The prototype properties seem to be there form the parent, but using any of those methods or properties causes an exception "ParentClass object expected"
In essence I would like to do:
module.class_<Point>("Point")
.constructor<int, int>()
.fun<&Point::x>("x")
.fun<&Point::y>("y")
.fun<&Point::norm>("norm");
module.class_<Point3D>("Point3D")
.base<Point>()
.constructor<int, int, int>()
.fun<&Point3D::z>("z")
where
class Point3D : public Point { }
then in JS:
const point = new Point3D(1, 2, 3);
console.log('point xyz', point.x, point.y, point.z);
First off I'm not sure if this is a quickjshpp bug or a quickjs bug. If I can I'll try test in quickjs too but time is limited right now. So here's the problem:
I have a native object that I have exposed to JS, lets call it "events". It is a property of a global "program" (native c++ instance exposed to JS as global variable). If I were to assign a new method to it, such as:
program.events.addListener = function() { }
and then try to call it on the next line, it is undefined:
program.events.addListener(); // results in error
console.log('addListener', program.events.addListener) // results undefined
The following code prints garbage, while in my opinion it should error:
#include "quickjspp.hpp"
#include <cstdio>
int main()
{
qjs::Runtime runtime;
qjs::Context context(runtime);
context.global().add("f_with_3_args", [](int a, int b, int c) {
printf("%d %d %d\n", a, b, c);
});
context.eval("f_with_3_args(1);");
return 0;
}
The values from b and c are obtained from random areas of memory, potentially resulting in a segmentation fault.
cmake -G "NMake Makefiles" .
nmake .
Scanning dependencies of target quickjs [ 2%] Building C object quickjs/CMakeFiles/quickjs.dir/quickjs.c.obj quickjs.c C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(117): warning C4013: '__builtin_clz' undefined; assuming extern returning int C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(123): warning C4013: '__builtin_clzll' undefined; assuming extern returning int C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(129): warning C4013: '__builtin_ctz' undefined; assuming extern returning int C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(135): warning C4013: '__builtin_ctzll' undefined; assuming extern returning int C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(138): error C2061: syntax error: identifier 'packed_u64' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(138): error C2059: syntax error: ';' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(138): error C2449: found '{' at file scope (missing function header?) C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(140): error C2059: syntax error: '}' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(146): error C2061: syntax error: identifier 'packed_u16' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(146): error C2059: syntax error: ';' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(146): error C2449: found '{' at file scope (missing function header?) C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(148): error C2059: syntax error: '}' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(157): error C2037: left of 'v' specifies undefined struct/union 'packed_u64' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(157): warning C4033: 'get_i64' must return a value C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(162): error C2037: left of 'v' specifies undefined struct/union 'packed_u64' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(167): error C2037: left of 'v' specifies undefined struct/union 'packed_u32' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(167): warning C4033: 'get_u32' must return a value C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(172): error C2037: left of 'v' specifies undefined struct/union 'packed_u32' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(172): warning C4033: 'get_i32' must return a value C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(177): error C2037: left of 'v' specifies undefined struct/union 'packed_u32' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(182): error C2037: left of 'v' specifies undefined struct/union 'packed_u16' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(182): warning C4033: 'get_u16' must return a value C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(187): error C2037: left of 'v' specifies undefined struct/union 'packed_u16' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(187): warning C4033: 'get_i16' must return a value C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(192): error C2037: left of 'v' specifies undefined struct/union 'packed_u16' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(265): error C2143: syntax error: missing ')' before '(' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(265): error C2091: function returns function C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(265): error C2059: syntax error: 'constant' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(265): error C2059: syntax error: ')' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(265): error C2146: syntax error: missing ')' before identifier 'dbuf_printf' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(265): error C2061: syntax error: identifier 'dbuf_printf' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(265): error C2059: syntax error: ';' C:\Users\user\Downloads\quickjspp\quickjs\cutils.h(265): error C2059: syntax error: '<parameter-list>' C:\Users\user\Downloads\quickjspp\quickjs\quickjs.h(518): warning C4244: 'function': conversion from 'int64_t' to 'int32_t', possible loss of data C:\Users\user\Downloads\quickjspp\quickjs\quickjs.h(520): warning C4244: 'function': conversion from 'int64_t' to 'double', possible loss of data C:\Users\user\Downloads\quickjspp\quickjs\quickjs.h(663): error C2440: 'type cast': cannot convert from 'JSValue' to 'JSValue' C:\Users\user\Downloads\quickjspp\quickjs\quickjs.h(663): warning C4033: 'JS_DupValue' must return a value C:\Users\user\Downloads\quickjspp\quickjs\quickjs.h(672): error C2440: 'type cast': cannot convert from 'JSValue' to 'JSValue' C:\Users\user\Downloads\quickjspp\quickjs\quickjs.h(672): warning C4033: 'JS_DupValueRT' must return a value C:\Users\user\Downloads\quickjspp\quickjs\quickjs.c(111): fatal error C1083: Cannot open include file: 'pthread.h': No such file or directory NMAKE : fatal error U1077: 'C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1424~1.283\bin\Hostx64\x64\cl.exe' : return code '0x2' Stop. NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\bin\HostX64\x64\nmake.exe"' : return code '0x2' Stop. NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\bin\HostX64\x64\nmake.exe"' : return code '0x2' Stop.
Hi,
I'm binding a simple c++ class (in this case called Coord), which is throwing an error.
My code is:
auto& module = context->addModule( "geom" );
module.class_<Coord>( "Coord" );
The stack trace is:
find_hashed_shape_proto(JSRuntime * rt, JSObject * proto) Line 4725 C
JS_NewObjectProtoClass(JSContext * ctx, unsigned __int64 proto_val, unsigned int class_id) Line 4937 C
JS_NewObject(JSContext * ctx) Line 5034 C
qjs::Context::newObject() Line 1435 C++
qjs::Context::Module::class_registrar<derive::geom::Coord>::class_registrar(const char * name, qjs::Context::Module & module, qjs::Context & context) Line 1303 C++
qjs::Context::Module::class_<derive::geom::Coord>(const char * name) Line 1386 C++
And where I think the problem might be is that in the function (quickjs.c) -
JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, JSClassID class_id)
The call to get_proto_obj is returning null (proto == null)
proto = get_proto_obj(proto_val);
Any ideas where to even start understanding why?
:)
If you call qjs::Context::registerClass
after you have torn down and recreated a QuickJS instance it will assert with an error saying that the Class ID is too high.
This is happening because js_traits<std::shared_ptr<T>>::QJSClassId
is a static member and does not get reset when the qjs::Context is destroyed.
I patched it to use a map in the context to keep track of the assigned IDs and if the map has been reset then reset the js_traits<std::shared_ptr<T>>::QJSClassId
to zero.
Not sure of the best way to go about this but it was a workaround for me.
template <class T>
void registerClass(const char * name, JSValue proto = JS_NULL)
{
auto hashId = typeid(T).hash_code();
if(this->classIds.count(hashId) == 0 && js_traits<std::shared_ptr<T>>::QJSClassId != 0)
{
js_traits<std::shared_ptr<T>>::QJSClassId = 0;
}
js_traits<std::shared_ptr<T>>::register_class(ctx, name, proto);
this->classIds[hashId] = js_traits<std::shared_ptr<T>>::QJSClassId;
}
i try to use this lib in MSVC.
i use the msvc
branch, it tell me the qjs::rest not find, and the quickjs dir not have the CmakeLists.txt, seems like this branch cannot work ?
Hi,
Could you please update quickjs version to 2019-10-27?
Problem Description:
I found that some test cases in repo QuickJS failed to execute.
Expectation:
All test cases in the directory tests/ of QuickJS would succeed to execute.
Actual test results:
command | test result | remark |
---|---|---|
./qjs path/to/quickjs/tests/microbench.js | pass | - |
./qjs path/to/quickjs/tests/test_builtin.js | pass | - |
./qjs path/to/quickjs/tests/test_std.js | pass | - |
./qjs path/to/quickjs/tests/test_bignum.js | failed | Error: SyntaxError: invalid number literal |
./qjs path/to/quickjs/tests/test_closure.js | failed | Error: SyntaxError: invalid keyword: with |
./qjs path/to/quickjs/tests/test_language.js | failed | Error: SyntaxError: invalid keyword: with |
./qjs path/to/quickjs/tests/test_loop.js | failed | Error: SyntaxError: a declaration in the head of a for-in loop can't have an initializer |
./qjs path/to/quickjs/tests/test_worker.js | failed | SegmentFault |
./qjs path/to/quickjs/tests/test_op_overloading.js | failed | Error: ReferenceError: 'Operators' is not defined |
./qjs path/to/quickjs/tests/test_qjscalc.js | failed | Error: Error: assertion failed: got |
test_bignum.js
Error: SyntaxError: invalid number literal
at /home/oem/path/to/quickjs/tests/test_bignum.js:155
test_closure.js
Error: SyntaxError: invalid keyword: with
at /home/oem/path/to/quickjs/tests/test_closure.js:157
test_language.js
Error: SyntaxError: invalid keyword: with
at /home/oem/path/to/quickjs/tests/test_language.js:379
Error: SyntaxError: a declaration in the head of a for-in loop can't have an initializer
at /home/oem/path/to/quickjs/tests/test_loop.js:144
test_op_overloading.js
Error: ReferenceError: 'Operators' is not defined
at test_operators_create (/home/oem/path/to/quickjs/tests/test_op_overloading.js:39)
at (/home/oem/path/to/quickjs/tests/test_op_overloading.js:205)
test_qjscalc.js
Error: Error: assertion failed: got |false|, expected |true|
at assert (/home/oem/path/to/quickjs/tests/test_qjscalc.js:18)
at test_integer (/home/oem/path/to/quickjs/tests/test_qjscalc.js:52)
at (/home/oem/path/to/quickjs/tests/test_qjscalc.js:245)
Development environment: Ubuntu 20.04
Problem recurrence steps:
git clone https://github.com/bellard/quickjs.git
git checkout b5e62895c619d4ffc75c9d822c8d85f1ece77e5b // checkout into the same version with quickjspp
git log
commit b5e62895c619d4ffc75c9d822c8d85f1ece77e5b (HEAD)
Author: bellard <[email protected]>
Date: Sat Mar 27 11:17:31 2021 +0100
2021-03-27 release
git clone https://github.com/ftk/quickjspp.git
cd quickjspp/
mkdir build
cd build/
cmake ..
make -j16
cd quickjspp/build/
./qjs path/to/quickjs/tests/xxx.js
This test calls the quickjs original C function: js_init_module_os()
to load the os module....
https://github.com/ftk/quickjspp/blob/master/test/class.cpp
however, seems there is also a fair amount of rigaramole going on there to get that function to work with quickjspp context...
my question is there a simpler way to import the os module?
I want to use the context outside of main()
Hi FTK,
I understand that this error is indicating something else is wrong somewhere.
I seem to get errors like this for any getters that return a DisplayObject*
, so I'm guessing my error is in my qjs code for DisplayObject
somewhere. When I comment out the lines that appear to be the issue, similar build errors occur just with other parts of the code.
Any idea why these errors would usually occur? What sort of problems should I be looking for in my code?
My code looks something like this (these are just snippets of what is in Script.cpp
):
// Dispatcher
module.class_<Dispatcher>( "Dispatcher" )
.constructor<>()
.fun<&Dispatcher::dispatch>( "dispatch" )
.fun<&Dispatcher::listen>( "listen" )
.fun<&Dispatcher::remove>( "remove" )
.fun<&Dispatcher::clear>( "clear" );
// Display object
typedef HitArea*( DisplayObject::* DisplayObject_getHitAreaF )( void );
typedef void( DisplayObject::* DisplayObject_setHitAreaF )( HitArea* );
module.class_<DisplayObject>("DisplayObject")
.base<Dispatcher>()
.constructor<>()
.property<&DisplayObject::parent>( "parent" )
.property<&DisplayObject::numChildren>( "numChildren" )
.property<&DisplayObject::first>( "first" )
.property<&DisplayObject::last>( "last" )
.property<&DisplayObject::first>( "next" )
.property<&DisplayObject::last>( "prev" )
.property<(DisplayObject_getHitAreaF)&DisplayObject::hitArea, (DisplayObject_setHitAreaF)&DisplayObject::hitArea>( "hitArea" )
.fun<&DisplayObject::x>( "x" )
.fun<&DisplayObject::y>( "y" )
.fun<&DisplayObject::width>( "width" )
.fun<&DisplayObject::height>( "height" )
.fun<&DisplayObject::scaleX>( "scaleX" )
.fun<&DisplayObject::scaleY>( "scaleY" )
.fun<&DisplayObject::rotation>( "rotation" )
.fun<&DisplayObject::originX>( "originX" )
.fun<&DisplayObject::originY>( "originY" )
.fun<&DisplayObject::visible>( "visible" )
.fun<&DisplayObject::alpha>( "alpha" )
.fun<&DisplayObject::mouse>( "mouse" )
.fun<&DisplayObject::addChild>( "addChild" )
.fun<&DisplayObject::addChildAt>( "addChildAt" )
.fun<&DisplayObject::removeChild>( "removeChild" )
.fun<&DisplayObject::removeChildAt>( "removeChildAt" )
.fun<&DisplayObject::removeAllChildren>( "removeAllChildren" )
.fun<&DisplayObject::childAt>( "childAt" )
.fun<&DisplayObject::indexOfChild>( "indexOfChild" )
.fun<&DisplayObject::addBefore>( "addBefore" )
.fun<&DisplayObject::addAfter>( "addAfter" )
.fun<&DisplayObject::remove>( "remove" );
// Player
typedef int( Player::* Player_getIntF )( );
typedef void( Player::* Player_setIntF )( int );
typedef colorARGB( Player::* Player_getColorARGBF )( );
typedef void( Player::* Player_setColorARGBF )(colorARGB);
module.class_<Player>("Player")
.constructor<int, int, string, bool>()
.property<&Player::stage>("stage") // Get
.property<(Player_getIntF)&Player::displayMode, (Player_setIntF)&Player::displayMode>("displayMode")
.property<(Player_getIntF)&Player::scaleMode, (Player_setIntF)&Player::scaleMode>("scaleMode")
.property<(Player_getIntF)&Player::alignMode, (Player_setIntF)&Player::alignMode>("alignMode")
.property<(Player_getColorARGBF)&Player::backgroundColor, (Player_setColorARGBF)&Player::backgroundColor>("backgroundColor")
.property<(Player_getColorARGBF)&Player::letterboxColor, (Player_setColorARGBF)&Player::letterboxColor>("letterboxColor")
.fun<&Player::run>("run");
And the errors I get are like this:
1>Script.cpp
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(622,48): error C2039: 'wrap': is not a member of 'qjs::js_traits<derive::display::DisplayObject *,void>'
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(622): message : see declaration of 'qjs::js_traits<derive::display::DisplayObject *,void>'
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(703): message : see reference to function template instantiation 'JSValue qjs::detail::wrap_this_call<R,qjs::shared_ptr<T>,,derive::display::DisplayObject*(__cdecl derive::Player::* )(void)>(JSContext *,Callable &&,JSValue,int,JSValue *) noexcept' being compiled
1> with
1> [
1> R=derive::display::DisplayObject *,
1> T=derive::Player,
1> Callable=derive::display::DisplayObject *(__cdecl derive::Player::* )(void)
1> ]
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(703): message : while compiling class template member function 'JSValue qjs::js_traits<fgetter,void>::wrap(JSContext *,qjs::fwrapper<derive::display::DisplayObject *derive::Player::stage(void),true>) noexcept'
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(1130): message : see reference to function template instantiation 'JSValue qjs::js_traits<fgetter,void>::wrap(JSContext *,qjs::fwrapper<derive::display::DisplayObject *derive::Player::stage(void),true>) noexcept' being compiled
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(1131): message : see reference to class template instantiation 'qjs::js_traits<fgetter,void>' being compiled
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(1701): message : see reference to function template instantiation 'qjs::Value &qjs::Value::add_getter<derive::display::DisplayObject *derive::Player::stage(void)>(const char *)' being compiled
1>C:\Projects\derive\derive\src\Script.cpp(368): message : see reference to function template instantiation 'qjs::Context::Module::class_registrar<derive::Player> &qjs::Context::Module::class_registrar<derive::Player>::property<derive::display::DisplayObject *derive::Player::stage(void),nullptr>(const char *)' being compiled
1>C:\Projects\derive\derive\src\Script.cpp(374): message : see reference to function template instantiation 'qjs::Context::Module::class_registrar<derive::Player> &qjs::Context::Module::class_registrar<derive::Player>::property<derive::display::DisplayObject *derive::Player::stage(void),nullptr>(const char *)' being compiled
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(611,1): error C3861: 'wrap': identifier not found
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(622,48): error C2039: 'wrap': is not a member of 'qjs::js_traits<derive::geom::HitArea *,void>'
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(622): message : see declaration of 'qjs::js_traits<derive::geom::HitArea *,void>'
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(703): message : see reference to function template instantiation 'JSValue qjs::detail::wrap_this_call<R,qjs::shared_ptr<T>,,DisplayObject_getHitAreaF>(JSContext *,Callable &&,JSValue,int,JSValue *) noexcept' being compiled
1> with
1> [
1> R=derive::geom::HitArea *,
1> T=derive::display::DisplayObject,
1> Callable=DisplayObject_getHitAreaF
1> ]
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(703): message : while compiling class template member function 'JSValue qjs::js_traits<fgetter,void>::wrap(JSContext *,qjs::fwrapper<{&derive::display::DisplayObject::[thunk]: __cdecl derive::display::DisplayObject::`vcall'{216,{flat}}' }',0},true>) noexcept'
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(1113): message : see reference to function template instantiation 'JSValue qjs::js_traits<fgetter,void>::wrap(JSContext *,qjs::fwrapper<{&derive::display::DisplayObject::[thunk]: __cdecl derive::display::DisplayObject::`vcall'{216,{flat}}' }',0},true>) noexcept' being compiled
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(1114): message : see reference to class template instantiation 'qjs::js_traits<fgetter,void>' being compiled
1>C:\Projects\derive\dependencies\quickjspp\quickjspp.hpp(1703): message : see reference to function template instantiation 'qjs::Value &qjs::Value::add_getter_setter<{&derive::display::DisplayObject::[thunk]: __cdecl derive::display::DisplayObject::`vcall'{216,{flat}}' }',0},{&derive::display::DisplayObject::[thunk]: __cdecl derive::display::DisplayObject::`vcall'{208,{flat}}' }',0}>(const char *)' being compiled
1>C:\Projects\derive\derive\src\Script.cpp(310): message : see reference to function template instantiation 'qjs::Context::Module::class_registrar<derive::display::DisplayObject> &qjs::Context::Module::class_registrar<derive::display::DisplayObject>::property<{&derive::display::DisplayObject::[thunk]: __cdecl derive::display::DisplayObject::`vcall'{216,{flat}}' }',0},{&derive::display::DisplayObject::[thunk]: __cdecl derive::display::DisplayObject::`vcall'{208,{flat}}' }',0}>(const char *)' being compiled
1>C:\Projects\derive\derive\src\Script.cpp(332): message : see reference to function template instantiation 'qjs::Context::Module::class_registrar<derive::display::DisplayObject> &qjs::Context::Module::class_registrar<derive::display::DisplayObject>::property<{&derive::display::DisplayObject::[thunk]: __cdecl derive::display::DisplayObject::`vcall'{216,{flat}}' }',0},{&derive::display::DisplayObject::[thunk]: __cdecl derive::display::DisplayObject::`vcall'{208,{flat}}' }',0}>(const char *)' being compiled
1>main.cpp
Hello,
I'm trying to compile quickJS with VS2019 and the I narrowed down the errors to only one type, related to this templated function
template <typename R, typename... Args, R (* F)(Args...), bool PassThis>
struct js_traits<fwrapper<F, PassThis>>
template <typename R, class T, typename... Args, R (T::*F)(Args...)>
struct js_traits<fwrapper<F>>
template <typename R, class T, typename... Args, R (T::*F)(Args...) const>
struct js_traits<fwrapper<F>>
The error I get is
E0842 template parameter "R" is not used in or cannot be deduced from the template argument list of class template "qjs::js_traits<qjs::fwrapper<F, false>>"
When I comment out those 3 functions, it actually compiles (I couldn't test because I have yet to compile the static quickjs.lib file
So I have 2 questions :
Thank you !
Hi FTK. Any chance of an updated MSVC branch? :)
i.e. quickjspp.hpp to latest (and other files), and quickjs patches applied?
Relates to #23
Is it possible to set a member of std::vector in qjs?
Currently only you can only write the full vector.
Test case:
template<typename _NumberT>
class Vector
{
public:
std::vector<_NumberT> pos;
...
// Does not change value
for (let i = 0; i < vec.pos.length; i++)
vec.pos[i] = 2;
// Works fine
vec.pos = [1,2,3,4];
Last commit was on Jul 22, 2022.
There is no install section in CMakeLists.txt, can I make on for PR...?
Currently, we can only install these files manually like:
https://github.com/tindy2013/subconverter/blob/v0.7.1/scripts/config.termux.sh#L23
I'm not sure how to use a static JS class method (containing the this
keyword) in C++ in a way that allows for passing arbitrary parameters from C++.
I have a context, into which I eval the following:
class FooBar {
static foo = "bar";
static baz() {
return this.foo;
}
}
If I do the following in C++:
auto baz = context->eval("FooBar.baz").as<std::function<void()>>();
baz();
then I will get the following error:
TypeError: cannot read property 'foo' of undefined
at baz (eval: 4)
Evaluating "FooBar.baz()"
works as intended, and replacing this.foo
with FooBar.foo
in FooBar.baz works, but I can't pass parameters to the former from C++ and the latter would fail if FooBar were ever renamed, so it's not ideal.
I've also tried executing FooBar.baz by first getting FooBar as a qjs Value and using Value.evalThis
, but that gave the same results.
For my current use case that involves passing an object from another context to many static methods to be processed, I have a workaround in which I just call a helper function from C++ that puts an arbitrary variable into globalThis (or anywhere else in the context), after which I can use an eval to call hypothetical function "FooBar.qux(arbitrary_variable)"
which is a static method using this
, and that works, but it obviously isn't ideal.
Edit: I thought of a much more satisfactory way to accomplish this for my use case. I can put an object from context A containing all of its public functions into context B, and I also put the an object with all the public variables I need from context C into context B, then I can just call a function from context A on an item from context C using context B, and I get the sandboxing I need, with no issues calling static functions from context A.
Hi,
Thanks for such a nice library.
Could you please add a donate button to readme file.
I will be happy to make a donation to your project.
I'm working on an implementation of the standard 'fetch' API for QuickJS. Like many other JS APIs,fetch
uses optional arguments. It would therefore be helpful if the code in quickjspp.hpp
that unwraps JS arguments for C++ functions could tolerate undefined/optional values rather than throwing an exception, e.g. something like this:
@@ -568,7 +568,5 @@
{
if (size_t(argc) <= I) {
- JS_ThrowTypeError(ctx, "Expected at least %lu arguments but received %d",
- (unsigned long)NArgs, argc);
- throw exception{ctx};
+ return js_traits<std::decay_t<T>>::unwrap(ctx, JS_UNDEFINED);
}
return js_traits<std::decay_t<T>>::unwrap(ctx, argv[I]);
I.e. the type T
can be a std::optional
or some other type whose js_traits
can convert to and from 'undefined'.
Would this be a good idea? What have I not thought of?
Hi again,
I'm trying to implement a callback in javascript that can be triggered from c++.
This is what I am trying to do:
typedef std::function<void(void)> Callback;
class Listener {
private
Callback _callback;
public:
// Javascript can register a callback
void listen( Callback callback ){
_callback = callback;
}
// The callback can later be triggered by either javascript or by c++
void trigger() {
_callback();
}
}
Binding like this compiles ok:
module.class_<Listener >( "Listener" )
.fun<&Listener::listen>( "listen" );
But this javascript code causes a runtime exception:
function test() {
console.log("test");
}
class MyListener extends Listener {
constructor() {
listen( test ); // Both of these calls to 'listen' (either of them) results in an exception
listen( methodTest );
}
methodTest() {
console.log("method test");
}
}
The exception occurs in the Value
constructor:
Value(JSContext * ctx, T&& val) : ctx(ctx)
{
v = js_traits<std::decay_t<T>>::wrap(ctx, std::forward<T>(val));
if(JS_IsException(v))
throw exception{}; // <-- this exception is thrown
}
Any ideas how to get (something like) this to work?
From your example:
auto cb = (std::function<void(const std::string&)>) context.eval("my_callback");
is it possible instead to use lambda or function pointer? I tried
auto cb = (void(*)(const std::string&)) context.eval("my_callback");
It compiles, but on run gives TypeError: <null> object expected
.
Hi ftk,
I have a class with a void* (pointer to any object) property:
class Event {
Event(){}
void* target = nullptr;
}
When I bind this with quickjspp:
module.class_<Event>("Event")
.constructor<>()
.fun<&Event::target>("target");
I get wrap
and unwrap
linkage errors:
lld-link : error : undefined symbol: public: static unsigned __int64 __cdecl qjs::js_traits<void *, void>::wrap(struct JSContext *, void *)
lld-link : error : undefined symbol: public: static void * __cdecl qjs::js_traits<void *, void>::unwrap(struct JSContext *, unsigned __int64)
Any idea how I can fix this without being more specific about the pointer type of target
?
Hello,
Im trying to make an extended Point class that inherits from our C++ Point class, something like this on C++ side:
GFX.class_<Point>("Point")
.constructor<double, double>()
.fun<&Point::x>("x")
.fun<&Point::y>("y");
And this on JS side:
function Point() { this.initialize.apply(this, arguments); }\
Point.prototype = Object.create(GFX.Point.prototype);
Point.prototype.constructor = Point;
Point.prototype.initialize = function(x, y) { GFX.Point.call(this, x, y); };
Point.emptyPoint = new Point(0, 0);
This seems to flop due to "JS_CFUNC_constructor" been used instead of "JS_CFUNC_constructor_or_func" so it raises a constructor called without "new" issue. If I edit the wrap method and place JS_CFUNC_constructor_or_func that issue is gone and a this
proto = detail::GetPropertyPrototype(ctx, this_value);
Casts a trying to access undefined property issue. If I modify the previous code into
JSValue proto ;
if (JS_IsUndefined(this_value))
{
proto = JS_GetClassProto(ctx, js_traits<std::shared_ptr<T>>::QJSClassId);
} else
proto = detail::GetPropertyPrototype(ctx, this_value);
It works and
console.log(Point.emptyPoint instanceof GFX.Point)
console.log(Point.emptyPoint instanceof Point)
Both return TRUE, but when I do
console.log(Point.emptyPoint.x)
I get TypeError: Expected type struct Point, got object with classid 1, Im unsure what to edit at this point to support what we need, any help would be very appreciated.
Hi ftk,
What is your suggested method of handling an enum class?
// c++
enum class DisplayMode {
Cover,
Contain,
LetterBox,
};
// js
myObject.displayMode = DisplayMode.Contain;
Should I convert my native c++ classes to use enums with explicit (integer) values, and then define the enum purely in JS, like this?
// c++
enum DisplayModeT {
Cover = 1,
Contain = 2,
LetterBox = 3,
} DisplayMode;
// js
DisplayMode = {
Cover: 1,
Contain : 2,
LetterBox : 3
};
Object.freeze(DisplayMode);
myObject.displayMode = DisplayMode.Contain;
Or is there a quickjspp way? :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.