Coder Social home page Coder Social logo

quickjspp's People

Contributors

dodola avatar drecouse avatar ftk avatar i404788 avatar jprendes avatar microctar avatar tindy2013 avatar wflohry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quickjspp's Issues

Enum classes

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? :)

how to resolve "The property "PUBLIC_HEADER" is not allowed"

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".

How to implement a variadic (const) compound type for List and Map

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?

Native inheritance/base classes and multi inheritance

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);

How to trigger method overridden in js from c?

  • Define a class in C++ and expose it to JS
  • Overwrite the method in JS
  • Call the method in C++. How can I make this call the JS method?

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?

Does QuickJS support multithreading?

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?

wrap is not a member of qjs::js_traits<MyCustomClass,void>

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

Instructions for Build

Hi,

Is there anyway you could provide instructions to build this? Including flags.

I also found the documentation for QuickJS itself quite lacking.

Miguel

Share Value between contexts

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?

Number of arguments are not checked when calling a function

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.

How to define virtual (computable) property?

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?

std::variant and JS null

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?

os.setTimeout not working

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!

Extending example module class

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

JavaScript inheritance from C++ class

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.

Donation Button

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.

Returning Javascript exception from std::function wrapper terminates program.

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)

Updated MSVC branch

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

operators binding

Hello,
how can I bind class member operators like +, -, * etc.? Is it possible?

Compile without exceptions

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.

How to implement js_traits<std::unique_ptr>?

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.

'this' is undefined when calling static JS class method from C++

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.

MSVC cannot find qjs::rest

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 ?

Binding a callback (std::function) causes runtime error

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?

Write access into std::vector

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];

Binding a void* property causes wrap/unwrap linkage error

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?

Optional arguments

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?

Assigning native C++ object a new property doesnt get remembered

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

Using lambda or function pointer for JS-callback

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.

Assertion failed: class_id < rt->class_count

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;
    }

Test cases of QuickJS execution failed

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:

  1. Download QuickJS
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
  1. Download and build quickjspp
git clone https://github.com/ftk/quickjspp.git
cd quickjspp/
mkdir build
cd build/
cmake ..
make -j16
  1. run test cases
cd quickjspp/build/
./qjs path/to/quickjs/tests/xxx.js

Fails to build on windows

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.

Binding to a class throws an exception

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?

:)

Compiling on windows : Error

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 :

  • Do you have an idea on how to make it working with MSVC ?
  • Are those functions critical to use QuickJSPP or are they just helpers ? (I'm actually just getting started on QuickJS, we're building a JUCE module)

Thank you !

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.