Git Product home page Git Product logo

v8pp's Introduction

Build status NPM Join the chat at https://gitter.im/pmed/v8pp

v8pp

Header-only library to expose C++ classes and functions into V8 to use them in JavaScript code. v8pp requires a compiler with C++17 support. The library has been tested on:

  • Microsoft Visual C++ 2019 (Windows 10)
  • GCC 5.4.0 (Ubuntu 16.04)
  • Clang 5.0.0 (Ubuntu 16.04)

Building and testing

The library has a set of tests that can be configured, built, and run with CMake:

~/v8pp$ mkdir out; cd out
~/v8pp/out$ cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON ..
~/v8pp/out$ make
~/v8pp/out$ ctest -V

The full list of project options can be listed with cmake command:

~/v8pp/out$ cmake -LH ..

Some of them could be:

// Build documentation BUILD_DOCUMENTATION:BOOL=OFF

// Build shared library BUILD_SHARED_LIBS:BOOL=ON

// Build and run tests BUILD_TESTING:BOOL=OFF

// Header-only library V8PP_HEADER_ONLY:BOOL=0

// v8::Isolate data slot number, used in v8pp for shared data V8PP_ISOLATE_DATA_SLOT:STRING=0

// v8pp plugin initialization procedure name V8PP_PLUGIN_INIT_PROC_NAME:STRING=v8pp_module_init

// v8pp plugin filename suffix V8PP_PLUGIN_SUFFIX:STRING=.dylib

// Use new V8 ABI with V8_COMPRESS_POINTERS and V8_31BIT_SMIS_ON_64BIT_ARCH V8_COMPRESS_POINTERS:BOOL=ON

Binding example

v8pp supports V8 versions after 6.3 with v8::Isolate usage in API. There are 2 targets for binding:

  • v8pp::module, a wrapper class around v8::ObjectTemplate
  • v8pp::class_, a template class wrapper around v8::FunctionTemplate

Both of them require a pointer to v8::Isolate instance. They allows to bind from C++ code such items as variables, functions, constants with a function set(name, item):

v8::Isolate* isolate;

int var;
int get_var() { return var + 1; }
void set_var(int x) { var = x + 1; }

struct X
{
    X(int v, bool u) : var(v) {}
    int var;
    int get() const { return var; }
    void set(int x) { var = x; } 
};

// bind free variables and functions
v8pp::module mylib(isolate);
mylib
    // set read-only attribute
    .const_("PI", 3.1415)
    // set variable available in JavaScript with name `var`
    .var("var", var)
    // set function get_var as `fun`
    .function("fun", &get_var)
    // set property `prop` with getter get_var() and setter set_var()
    .property("prop", get_var, set_var);

// bind class
v8pp::class_<X> X_class(isolate);
X_class
    // specify X constructor signature
    .ctor<int, bool>()
    // bind variable
    .var("var", &X::var)
    // bind function
    .function("fun", &X::set)
    // bind read-only property
    .property("prop",&X::get);

// set class into the module template
mylib.class_("X", X_class);

// set bindings in global object as `mylib`
isolate->GetCurrentContext()->Global()->Set(
    v8::String::NewFromUtf8(isolate, "mylib"), mylib.new_instance());

After that bindings will be available in JavaScript:

mylib.var = mylib.PI + mylib.fun();
var x = new mylib.X(1, true);
mylib.prop = x.prop + x.fun();

Node.js and io.js addons

The library is suitable to make Node.js and io.js addons. See addons document.

void RegisterModule(v8::Local<v8::Object> exports)
{
    v8pp::module addon(v8::Isolate::GetCurrent());

    // set bindings... 
    addon
        .function("fun", &function)
        .class_("cls", my_class)
        ;

    // set bindings as exports object prototype
    exports->SetPrototype(addon.new_instance());
}

v8pp also provides

  • v8pp - a static library to add several global functions (load/require to the v8 JavaScript context. require() is a system for loading plugins from shared libraries.
  • test - A binary for running JavaScript files in a context which has v8pp module loading functions provided.

v8pp module example

#include <iostream>

#include <v8pp/module.hpp>

namespace console {

void log(v8::FunctionCallbackInfo<v8::Value> const& args)
{
    v8::HandleScope handle_scope(args.GetIsolate());

    for (int i = 0; i < args.Length(); ++i)
    {
        if (i > 0) std::cout << ' ';
        v8::String::Utf8Value str(args[i]);
        std::cout <<  *str;
    }
    std::cout << std::endl;
}

v8::Local<v8::Value> init(v8::Isolate* isolate)
{
    v8pp::module m(isolate);
    m.function("log", &log);
    return m.new_instance();
}

} // namespace console

Turning a v8pp module into a v8pp plugin

V8PP_PLUGIN_INIT(v8::Isolate* isolate)
{
    return console::init(isolate);
}

v8pp class binding example

#include <v8pp/module.hpp>
#include <v8pp/class.hpp>

#include <fstream>

namespace file {

bool rename(char const* src, char const* dest)
{
    return std::rename(src, dest) == 0;
}

class file_base
{
public:
    bool is_open() const { return stream_.is_open(); }
    bool good() const { return stream_.good(); }
    bool eof() const { return stream_.eof(); }
    void close() { stream_.close(); }

protected:
    std::fstream stream_;
};

class file_writer : public file_base
{
public:
    explicit file_writer(v8::FunctionCallbackInfo<v8::Value> const& args)
    {
        if (args.Length() == 1)
        {
            v8::String::Utf8Value str(args[0]);
            open(*str);
        }
    }

    bool open(char const* path)
    {
        stream_.open(path, std::ios_base::out);
        return stream_.good();
    }

    void print(v8::FunctionCallbackInfo<v8::Value> const& args)
    {
        v8::HandleScope scope(args.GetIsolate());

        for (int i = 0; i < args.Length(); ++i)
        {
            if (i > 0) stream_ << ' ';
            v8::String::Utf8Value str(args[i]);
            stream_ << *str;
        }
    }

    void println(v8::FunctionCallbackInfo<v8::Value> const& args)
    {
        print(args);
        stream_ << std::endl;
    }
};

class file_reader : public file_base
{
public:
    explicit file_reader(char const* path)
    {
        open(path);
    }

    bool open(const char* path)
    {
        stream_.open(path, std::ios_base::in);
        return stream_.good();
    }

    v8::Local<v8::Value> getline(v8::Isolate* isolate)
    {
        if ( stream_.good() && ! stream_.eof())
        {
            std::string line;
            std::getline(stream_, line);
            return v8pp::to_v8(isolate, line);
        }
        else
        {
            return v8::Undefined(isolate);
        }
    }
};

v8::Local<v8::Value> init(v8::Isolate* isolate)
{
    v8::EscapableHandleScope scope(isolate);

    // file_base binding, no .ctor() specified, object creation disallowed in JavaScript
    v8pp::class_<file_base> file_base_class(isolate);
    file_base_class
        .function("close", &file_base::close)
        .function("good", &file_base::good)
        .function("is_open", &file_base::is_open)
        .function("eof", &file_base::eof)
        ;

    // .ctor<> template arguments declares types of file_writer constructor
    // file_writer inherits from file_base_class
    v8pp::class_<file_writer> file_writer_class(isolate);
    file_writer_class
        .ctor<v8::FunctionCallbackInfo<v8::Value> const&>()
        .inherit<file_base>()
        .function("open", &file_writer::open)
        .function("print", &file_writer::print)
        .function("println", &file_writer::println)
        ;

    // .ctor<> template arguments declares types of file_reader constructor.
    // file_base inherits from file_base_class
    v8pp::class_<file_reader> file_reader_class(isolate);
    file_reader_class
        .ctor<char const*>()
        .inherit<file_base>()
        .function("open", &file_reader::open)
        .function("getln", &file_reader::getline)
        ;

    // Create a module to add classes and functions to and return a
    // new instance of the module to be embedded into the v8 context
    v8pp::module m(isolate);
    m.function("rename", &rename)
     .class_("writer", file_writer_class)
     .class_("reader", file_reader_class)
        ;

    return scope.Escape(m.new_instance());
}

} // namespace file

V8PP_PLUGIN_INIT(v8::Isolate* isolate)
{
    return file::init(isolate);
}

Creating a v8 context capable of using require() function

#include <v8pp/context.hpp>

v8pp::context context;
context.set_lib_path("path/to/plugins/lib");
// script can now use require() function. An application
// that uses v8pp::context must link against v8pp library.
v8::HandleScope scope(context.isolate());
context.run_file("some_file.js");

Using require() from JavaScript

// Load the file module from the class binding example and the
// console module.
var file    = require('file'),
    console = require('console')

var writer = new file.writer("file")
if (writer.is_open()) {
    writer.println("some text")
    writer.close()
    if (! file.rename("file", "newfile"))
        console.log("could not rename file")
}
else console.log("could not open `file'")

console.log("exit")

Create a handle to an externally referenced C++ class.

// Memory for C++ class will remain when JavaScript object is deleted.
// Useful for classes you only wish to inject.
typedef v8pp::class_<my_class> my_class_wrapper;
v8::Local<v8::Value> val = my_class_wrapper::reference_external(isolate, &my_class::instance());
// Assuming my_class::instance() returns reference to class

Import externally created C++ class into v8pp.

// Memory for c++ object will be reclaimed by JavaScript using "delete" when
// JavaScript class is deleted.
typedef v8pp::class_<my_class> my_class_wrapper;
v8::Local<v8::Value> val = my_class_wrapper::import_external(isolate, new my_class);

Compile-time configuration

The library uses several preprocessor macros, defined in v8pp/config.hpp file:

  • V8PP_ISOLATE_DATA_SLOT - A v8::Isolate data slot number, used to store v8pp internal data
  • V8PP_PLUGIN_INIT_PROC_NAME - Plugin initialization procedure name that should be exported from a v8pp plugin.
  • V8PP_PLUGIN_SUFFIX - Plugin filename suffix that would be added if the plugin name used in require() doesn't end with it.
  • V8PP_HEADER_ONLY - Use header-only implemenation, enabled by default.

v8pp alternatives

v8pp's People

Contributors

arghness avatar borisschaeling avatar cr-mitmit avatar gitter-badger avatar ike2016 avatar jacobsologub avatar jcmonnin avatar jleybovich avatar mrsharpoblunto avatar ottumm avatar picohz avatar pmed avatar rubenlg avatar tim-janik avatar wflohry avatar yarikth 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

v8pp's Issues

Question: function that returns object by value

Hi,

First, I'm not sure if the issue tracker is the right way to ask questions about the project. I haven't seen a mailing list, so I guess it's ok?

Please consider the example based on 'wrapping.md':

struct X
{
    bool b;
    X(bool b) : b(b) {}
};

static bool test1()
{
    return true;
}

static X test2()
{
    return X(true);
}

void v8ppTestBind(v8::Isolate* isolate)
{
    v8pp::class_<X> X_class(isolate);
    X_class
        .ctor<bool>()
        .set("b", &X::b);
    
    v8pp::module module(isolate);
    module
        .set("X", X_class)
        .set("test1", test1)
        .set("test2", test2);

    isolate->GetCurrentContext()->Global()->Set(v8::String::NewFromUtf8(isolate, "module"), module.new_instance());
}

Executing module.X(true) gives [object X].

Executing module.test1() gives true.

Executing module.test2() gives undefined.

Why is test2 not returning a wrapped object of type X?

console.log & util.inspect don't show native members

Hi,

With Node 5.3, let's say I have a simple C++ class 'IScreen' with JS binding defined like that:

        v8pp::class_<IScreen> JS_IScreen(v8::Isolate::GetCurrent());
        JS_IScreen.set("init", &IScreen::init);
        JS_IScreen.set("inited", v8pp::property(&IScreen::isInited));
        JS_IScreen.set("shutdown", &IScreen::shutdown);

I have also a node module which references this class to let it be instaciable in JS code.

But when I try to 'dump' the object to the console of node, No 'native bound' fields are listed.
This is the case when I'm using console.log(myObj) or even util.inspect(myObj, { showHidden:true }). On the contrary, iterating on the attributes of the object instance reveals the presence of the members:

var screen = new IScreen();
screen.customJsMember = 'sampleText';

console.log(require('util').inspect(screen, {showHidden:true}));
> { customJsMember: 'sampleText' }

for (var k in screen) console.log(k+"="+screen[k]);
> customJsMember=sampleText
> inited=true
> init=function init() { [native code] }
> shutdown=function shutdown() { [native code] }

I checked in v8pp code, but there's no presence of somehting like PropertyAttribute::DontEnum that I could remove to achieve a full display of all object members.

So here's my question: is there a simple way to output by default such 'native bound' members, as they are for other 'regular JS' members ?

Thanks in advance.

Build as a static library

Add a build configuration option to switch between header-only and static library.

De-templatize source code in order to speedup the library compilation time.

class template has already been defined

Using the most current stable version of v8 I get the follow errors

v8pp/convert.hpp(417): error C2953: 'v8pp::convertv8::Local<T,void>' : class template has already been defined
v8pp/convert.hpp(377) : see declaration of 'v8pp::convertv8::Local<T,void>'
v8pp/convert.hpp(449): error C2953: 'v8pp::is_wrapped_classv8::Local' : class template has already been defined
v8pp/convert.hpp(446) : see declaration of 'v8pp::is_wrapped_classv8::Local'

compilation error: 'this' was not captured for lambda function

Hi,

and first, thanks a lot for this amazing piece of code :-)

When I try to build my app including the v8pp 'class' headers, I get the following error.
Would you have any idea about what's happening ?

Thanks in advance.

compile output:
g++ -c -m64 -pipe -g -w -std=c++11 -I../../3rdparty/desktop/include/v8 -Isrc -o build/desktop/obj/javascriptTest.o src/javascriptTest.cpp

In file included from src/javascriptTest.cpp:21:0:
../../main/src/javascript/v8pp/class.hpp: In instantiation of ‘v8pp::detail::class_singleton<T>::class_singleton(v8::Isolate*, v8pp::detail::class_info::type_index) [with T = Point; v8pp::detail::class_info::type_index = unsigned int]::<lambda(const v8::FunctionCallbackInfo<v8::Value>&)>’:
../../main/src/javascript/v8pp/class.hpp:178:5:   required from ‘struct v8pp::detail::class_singleton<T>::class_singleton(v8::Isolate*, v8pp::detail::class_info::type_index) [with T = Point; v8pp::detail::class_info::type_index = unsigned int]::<lambda(const class v8::FunctionCallbackInfo<v8::Value>&)>’
../../main/src/javascript/v8pp/class.hpp:189:5:   required from ‘v8pp::detail::class_singleton<T>::class_singleton(v8::Isolate*, v8pp::detail::class_info::type_index) [with T = Point; v8pp::detail::class_info::type_index = unsigned int]’
../../main/src/javascript/v8pp/class.hpp:261:4:   required from ‘static v8pp::detail::class_singleton<T>& v8pp::detail::class_singleton<T>::instance(v8::Isolate*) [with T = Point; v8pp::detail::class_singleton<T> = v8pp::detail::class_singleton<Point>]’
../../main/src/javascript/v8pp/class.hpp:369:56:   required from ‘v8pp::class_<T>::class_(v8::Isolate*) [with T = Point]’
src/javascriptTest.cpp:71:42:   required from here
../../main/src/javascript/v8pp/class.hpp:183:74: error: ‘this’ was not captured for this lambda function
../../main/src/javascript/v8pp/class.hpp:183:74: error: return-statement with a value, in function returning 'void' [-fpermissive]
make: *** [build/desktop/obj/javascriptTest.o] Error 1
details:
  • file included from src/javascriptTest.cpp:21:0 being: v8pp/class.hpp
  • src/javascriptTest.cpp:71:42: required from here being
v8pp::class_<Point> point_class(isolate);

(with Point being a basic int 2d-coord(x,y) class, no template or anything else)

my environment:
g++ (Debian 4.7.2-5) 4.7.2
#define V8_MAJOR_VERSION 4
#define V8_MINOR_VERSION 5
#define V8_BUILD_NUMBER 103
#define V8_PATCH_LEVEL 30

Memory leak in external_data<T>::set

When running in visual studio with crt heap debugging enabled, I noticed that theres a small memory leak in external_data::set - specifically the allocation on line 53 never gets cleaned up when binding a property using v8pp::property

static v8::Local<v8::External> set(v8::Isolate* isolate, T&& data)
{
	external_data* value = new external_data; // <-- this never gets cleaned up
	try
	{
		new (value->storage()) T(std::forward<T>(data));
	}
	catch (...)
	{
		delete value;
		throw;
	}

How to use ArrayBuffer with v8pp::class_ ?

Hello,

I would like to pass binary data to JavaScript and process them as ArrayBuffer (Uint8Array) in script.

How can I do this with v8pp?

struct X
{
	unsigned char* buffer;
};

// bind class
v8pp::class_<X> X_class(isolate);
X_class
	.set("buffer", ??????);

Thanks!

How to use callback function in js

How to execute callback function in js (function download_callback)?

`
class CDownloadFile
{
public:
...

int start(const char* szUrl, const char* szPath)
{
	CURL *curl;
	CURLcode res;
	
	...
	
	//set callback function here
	res = curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
	
	...
	return 0;
}

static int ProgressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
	std::cout << "dltotal: " << (long)dltotal << " ---- dlnow:" << (long)dlnow << "---- " << nPos <<"%" << std::endl;

            //
           v8::Isolate* isolate = dd->m_isolate;
           v8::Handle<v8::Function> fun = v8::Function::New(isolate, v8_arg_count);//
           v8::Handle<v8::Value> vRet = v8pp::call_v8(isolate, fun, fun, nPos);
	return 0;
}

...

};`

////////////////////////////////////////
`...
v8pp::class_ CDownloadFile_class(isolate);
CDownloadFile_class
.ctor()
.set("start", &CDownloadFile::start)
.set("callback", &CDownloadFile::setCallback)
;

... `

////////////////////////////////////////

`var curl = require('curl');

function download_callback(p)
{
console.log("download_callback execute...");
}

//download file
var download = curl.download();
download.callback(download_callback);
download.start("http://xxxx", "xxxxx.xxx");`

v8pp::set_const does not work in recent v8 versions (>6.0)

v8::Object::ForceSet with isolate as first argument has been removed in v8 6.1.

It looks like v8::Object::CreateDataProperty should be used in today's API. It looks like this is available since 4.5.

Before preparing a pull request, I'd like to ask what is the strategy with regards of the v8 API level. From the point of view of C++ applications using v8, I think it makes sense that actual version of v8pp targets recent versions of v8. If someone chooses not to update v8, he probably doesn't need to update v8pp either.

For node plugins the situation might look different (I don't know that part). It looks like the oldest supported LTS is 4 and node 4.8.7 seems to run v8 version 4.5. So it looks like it is time to use API's like CreateDataProperty (and other changes like using v8::Local instead of v8::Handle). Would pull requests breaking support of v8 before 4.5 be accepted?

undefined V8PP_ISOLATE_DATA_SLOT will cause crash while exiting.

I' upgrading from a old version of v8pp to current so I have this issue.

Finally, I found it is because I didn't set V8PP_ISOLATE_DATA_SLOT and it's using unordered_map.

std::unordered_map<v8::Isolate*, class_singletons> instances maybe not a good way for store v8 object since it will cleanup by CRT after v8 has been shutdown. For me, it crash while exiting.

Maybe these code can be just removed and force to use V8PP_ISOLATE_DATA_SLOT?

v8pp runtime_error crashes Electron DevTools

Constructing an X() object, using the v8pp::class_ example from the README leads to crashes in the DevTools console of an electron window. First, I'm attaching a zip file with a shell script that can be used in a subdirectory of a v8pp checkout to:
a) locally install electron and node-gyp
b) build addon.node using just v8pp and the 'class X' example
c) start electron, create a window with DevTools console and require("addon.node")

At that point entering " x = addon.X(1,false) " in the Console will crash it due to v8pp throwing an uncought exception from convert<...is_wrapped_class...>::from_v8.

I've added a small debugging printout to unwrap_object():

+  printf ("unwrap_object: isolate_=%p context_=%p this=%p proto=%p InternalFieldCount=%d\n",
+    isolate_, *isolate_->GetCurrentContext(), *obj, *obj->GetPrototype(), obj->InternalFieldCount());
   if (obj->InternalFieldCount() == 2)

Which shows multiple lines like this before the console crashes:

unwrap_object: isolate_=0x3b2804280000 context_=0x3b28042c9ae8 this=0x7ffda34670d0 proto=0x3b28042c9ae0 InternalFieldCount=0
unwrap_object: isolate_=0x3b2804280000 context_=0x3b28042c9b08 this=0x3b28042c9af0 proto=0x3b28042c9b00 InternalFieldCount=0
unwrap_object: isolate_=0x3b2804280000 context_=0x3b28042c9b28 this=0x3b28042c9b10 proto=0x3b28042c9b20 InternalFieldCount=0
unwrap_object: isolate_=0x3b2804280000 context_=0x3b28042c9ae8 this=0x7ffda34670d0 proto=0x3b28042c9ae0 InternalFieldCount=0
unwrap_object: isolate_=0x3b2804280000 context_=0x3b28042c9b08 this=0x3b28042c9af0 proto=0x3b28042c9b00 InternalFieldCount=0
unwrap_object: isolate_=0x3b2804280000 context_=0x3b28042c9b28 this=0x3b28042c9b10 proto=0x3b28042c9b20 InternalFieldCount=0

This indicates that the DevTools console is accessing objects withtout internal fields using the v8pp accessors written for wrapped objects (the access apparently happens to auto-display the object property values). A valid access should show InternalFieldCount=2.

I fail to see why that is the case.
I can just speculate that the DevTools console does some magic behind the scenes like "cloning" objects by copying all their Handles without internal fields before running the accessors. Knowing the v8 and DevTools internals might be required to get at the root cause here.

In any case, the crashes can be mitigated by turning the C++ exceptions from v8pp into V8 exceptions for failing property accessors.

ecrash.zip

Using .inherit<>() has no effect in classes without ctor()

C++ classes are wrapped using class_function_template() from class.hpp and calling inherit<>() changes inheritance for js_function_template().
Now classes that also get a ctor() let class_function_template() inherit from js_function_template(), which means that classes without a ctor are not affected at all by inherit<>() calls.

Note that the implementation of js_function_template() may look like it's returning class_function_template() for classes without ctor, but that is not the case because js_func_ is never Empty, due to the initialization in class_singleton(). The code could be changed to test ctor_!=NULL instead of IsEmpty(), but that would make working inheritance depend on the order in which inherit() and ctor() are called on a class_.

I guess the one solution that can work reliably is to simply always have class_function_template() inherit from js_function_template(). That's what PR #55 accomplishes.

Build fails on systems with newer ICU

Not really a big issue and I understand that the library was not tested on non-Ubuntu setups, but commit 5770abb removed linking with icui18n and icuuc shipped with V8 which causes linking errors on systems with ICU 5.7 (such as Manjaro I'm currently on).

Building with V8 obtained from build-v8.sh and re-adding -licuuc -licui18n to LIBS works fine.

Question: ctor with enum as param

I am writing a nodejs addon.

When I attempt to create an std::vector<field> fields; and perform something like _fields = v8pp::from_v8<std::vector<field>>(isolate, args[0], fields);, I get the dreaded Error: expected C++ wrapped object when args[0] contains [{"name":"name","type":"text"},{"name":"birth_date","type":"date"}] for example, vs [], which leads me to believe that conversions aren't being done correctly, due to the fact that the type being passed is a string, whereas the thing it actually wants is a numeric, representing the data_type from the enum.

@pmed do you have any suggestions on how this might be more appropriately/correctly implemented?

Here's are the relevant parts of my implementation:

void InitAll(Handle<Object> exports, Handle<Object> module) {
    //...
    v8pp::class_<field> field_js(isolate);
    field_js
        .ctor<const char *, data_type>()
        .set("name", v8pp::property(&field::name))
        .set("type", v8pp::property(&field::type));
    //...
}

and:

//...
class PUBLIC field {
private:

    struct _field_data_wrapper;
    _field_data_wrapper *_field_data;

public:

    field(const char *name, const data_type type);
//...
}

data_type is an enum:

enum data_type {
//...
}

Example in 'convert.md' does not work

The examples for user defined type do not work.

Problem is here:

static bool is_valid(v8::Isolate*, v8::Handle<v8::Value> value)
{
    return !value.IsEmpty() && value->IsArray() && value->Length() == 3;
}

v8::Value has no method Length() (at least in recent v8 versions).

Related to that there is also the separate pull request about typos.

context.cpp does not compile with ambiguous template instantiation errors

In file included from v8pp/v8pp/context.hpp:17:0,
                 from v8pp/v8pp/context.cpp:9:
v8pp/v8pp/convert.hpp: In substitution of ‘template<class T> typename v8pp::convert<T>::to_type v8pp::to_v8(v8::Isolate*, const T&) [with T = v8::Handle<v8::Value>]’:
v8pp/v8pp/context.cpp:136:50:   required from here
v8pp/v8pp/convert.hpp:627:30: error: ambiguous template instantiation for ‘struct v8pp::convert<v8::Handle<v8::Value>, void>’
 typename convert<T>::to_type to_v8(v8::Isolate* isolate, T const& value)
                              ^
v8pp/v8pp/convert.hpp:432:8: note: candidates are: template<class T> struct v8pp::convert<v8::Handle<T>, typename std::enable_if<(! std::is_same<v8::Handle<T>, v8::Local<T> >::value)>::type> [with T = v8::Value]
 struct convert<v8::Handle<T>, typename std::enable_if<
        ^
v8pp/v8pp/convert.hpp:555:8: note:                 template<class T> struct v8pp::convert<T, typename std::enable_if<v8pp::is_wrapped_class<T>::value>::type> [with T = v8::Handle<v8::Value>]
 struct convert<T, typename std::enable_if<is_wrapped_class<T>::value>::type>
        ^
v8pp/v8pp/context.cpp: In static member function ‘static void v8pp::context::run_file(const v8::FunctionCallbackInfo<v8::Value>&)’:
v8pp/v8pp/context.cpp:136:50: error: no matching function for call to ‘to_v8(v8::Isolate*&, v8::Handle<v8::Value>)’
   result = to_v8(isolate, ctx->run_file(filename));
                                                  ^
In file included from v8pp/v8pp/context.hpp:17:0,
                 from v8pp/v8pp/context.cpp:9:
v8pp/v8pp/convert.hpp:602:31: note: candidate: v8::Handle<v8::String> v8pp::to_v8(v8::Isolate*, const char*, size_t)
 inline v8::Handle<v8::String> to_v8(v8::Isolate* isolate, char const* str, size_t len)
                               ^
v8pp/v8pp/convert.hpp:602:31: note:   candidate expects 3 arguments, 2 provided
v8pp/v8pp/convert.hpp:608:24: note: candidate: template<long unsigned int N> v8::Handle<v8::String> v8pp::to_v8(v8::Isolate*, const char (&)[N], size_t)
 v8::Handle<v8::String> to_v8(v8::Isolate* isolate, char const (&str)[N], size_t len = N - 1)
                        ^
v8pp/v8pp/convert.hpp:608:24: note:   template argument deduction/substitution failed:
v8pp/v8pp/context.cpp:136:50: note:   mismatched types ‘const char [N]’ and ‘v8::Handle<v8::Value>’
   result = to_v8(isolate, ctx->run_file(filename));
                                                  ^
In file included from v8pp/v8pp/context.hpp:17:0,
                 from v8pp/v8pp/context.cpp:9:
v8pp/v8pp/convert.hpp:627:30: note: candidate: template<class T> typename v8pp::convert<T>::to_type v8pp::to_v8(v8::Isolate*, const T&)
 typename convert<T>::to_type to_v8(v8::Isolate* isolate, T const& value)
                              ^
v8pp/v8pp/convert.hpp:627:30: note:   substitution of deduced template arguments resulted in errors seen above
v8pp/v8pp/convert.hpp:633:23: note: candidate: template<class Iterator> v8::Handle<v8::Array> v8pp::to_v8(v8::Isolate*, Iterator, Iterator)
 v8::Handle<v8::Array> to_v8(v8::Isolate* isolate, Iterator begin, Iterator end)
                       ^
v8pp/v8pp/convert.hpp:633:23: note:   template argument deduction/substitution failed:
v8pp/v8pp/context.cpp:136:50: note:   candidate expects 3 arguments, 2 provided
   result = to_v8(isolate, ctx->run_file(filename));
                                                  ^
In file included from v8pp/v8pp/context.hpp:17:0,
                 from v8pp/v8pp/context.cpp:9:
v8pp/v8pp/convert.hpp:646:23: note: candidate: template<class T> v8::Handle<v8::Array> v8pp::to_v8(v8::Isolate*, const std::initializer_list<_Tp>&)
 v8::Handle<v8::Array> to_v8(v8::Isolate* isolate, std::initializer_list<T> const& init)
                       ^
v8pp/v8pp/convert.hpp:646:23: note:   template argument deduction/substitution failed:
v8pp/v8pp/context.cpp:136:50: note:   ‘v8::Handle<v8::Value>’ is not derived from ‘const std::initializer_list<_Tp>’
   result = to_v8(isolate, ctx->run_file(filename));
                                                  ^
v8pp/v8pp/context.cpp: In constructor ‘v8pp::context::context(v8::Isolate*)’:
v8pp/v8pp/context.cpp:168:17: error: ‘struct v8::Isolate::CreateParams’ has no member named ‘array_buffer_allocator’
   create_params.array_buffer_allocator = &array_buffer_allocator_;
                 ^

mac build error: use 'template' keyword to treat 'remove_object' as a dependent template name

When building with xcode (I have 6.1.1) I get the following error:

/Users/rob/Documents/src/test/v8pp/v8pp/class.hpp:226:23: error: use 'template' keyword to treat 'remove_object' as a dependent template name
                                instance(isolate).remove_object<T>(isolate, object, nullptr);
                                                  ^
                                                  template1

If I make this change, adding the template keywoard, then it builds fine:
v8pp/class.hpp, line 226:

- instance(isolate).remove_object<T>(isolate, object, nullptr);
+ instance(isolate).template remove_object<T>(isolate, object, nullptr);

Thanks for taking a look!

class.hpp: C++ objects referenced with reference_external are still deleted when class_::destroy() is called

Calling class_::reference_external "does not take ownership of the C++ pointer", but v8pp still deletes the C++ object when class_::destroy is called, which is clearly not intended and leads to crashes due to double deletion.

The "destroy_after" parameter of class_singleton::wrap is only used to determine what callback is given to SetWeak, and isn't stored anywhere in the actual object registry, and so v8pp isn't actually keeping the necessary ownership information everywhere it needs to.

This affects both the shared_ptr branch and the main branch.

are std::function<R(Args...)> convertible & bindable to/from JS ?

I would have another question (btw, don't know if "issues" is the right place for that ; tell me).

I'm looking at a convenient way to assign JS functions to C++ observables.

class MyObservable {
    private:
        std::list<std::function<bool(int)> > observerFuncs_;
    public:
        MyObservable() : observerFuncs_() {
        }
        void addObserver(std::function<bool(int)> fn) { 
            observerFuncs_.push_back(fn); 
        }
        bool triggerObservers(int value) { 
            bool allOk = true; 
            for (auto fn : observerFuncs_) {
                ok = ok && fn(value); 
            }
            return allOk;
        }
}

then:

v8pp::class_<MyObservable> JS_MyObservable(isolate);
JS_MyObservable.set("addObserver", &MyObservable::addObserver);

The compiler doesn't complain with such std::function<...> parameter, but a JS call to the function always fails:

var obj = new MyObservable();
obj.addObserver(function(myInt) { 
    console.log("myObservable callback trigerred: "+myInt); 
    return (myInt >= 42);
});
  • Do I something wrong ?
  • If not, is there any way to pass such JS function as parameter to a bound C++ function ?

Thanks in advance.

Question: overloaded ctor

Hi,

I need to provide an overloaded ctor in the JavaScript binding of my class. When calling multiple times X_class.ctor<...>() with different template parameters, it compiles. However it seems to consider only the last call.

I've also looked at v8pp::factory documentation, but I've not found a way to handled overloads in the ctor so far.

Is there a way to achieve that?

v8pp::cleanup deletes instances exported with reference_external

I have a stack allocated object. I wan't to access to that particular instance from JavaScript. I don't want v8 to manage the lifetime of that object. I'm using v8pp::class_<X>::reference_external to achieve that. However, when calling v8pp::cleanup(isolate) v8pp tries to delete the instance. I think this is a bug (unless I'm misunderstanding reference_external).

Please check the code below that illustrates the problem. There is also the AddressSanitizer output below. Sample is a bit long because there is the whole boilerplate code to init/destroy v8, which is relevant for that issue (based on https://chromium.googlesource.com/v8/v8/+/master/samples/hello-world.cc):

#include <iostream>
#include "v8pp/module.hpp"
#include "v8pp/class.hpp"
#include <libplatform/libplatform.h>


struct X
{
    bool b;
    X(bool b) : b(b) {}
};


int main(int argc, const char * argv[])
{
    using namespace v8;
    
    // Instance of X that is going to be accessible from JavaScript
    X x(false);
    
    // Initialize V8.
    V8::InitializeICU();
    V8::InitializeExternalStartupData(argv[0]);
    Platform* platform = platform::CreateDefaultPlatform();
    V8::InitializePlatform(platform);
    V8::Initialize();
    
    // Create a new Isolate and make it the current one.
    Isolate::CreateParams create_params;
    create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
    Isolate* isolate = Isolate::New(create_params);
    {
        Isolate::Scope isolate_scope(isolate);
        
        // Create a stack-allocated handle scope.
        HandleScope handle_scope(isolate);
        
        // Create a new context.
        Local<Context> context = Context::New(isolate);
        
        // Enter the context for compiling and running the hello world script.
        Context::Scope context_scope(context);
        
        // Bind custom C++ functions and objects
        v8pp::class_<X> X_class(isolate);
        X_class.set("b", &X::b);
        
        v8pp::module module(isolate);
        module.set("X", X_class);
        
        isolate->GetCurrentContext()->Global()->Set(v8::String::NewFromUtf8(isolate, "module"),
                                                    module.new_instance());
        
        isolate->GetCurrentContext()->Global()->Set(v8::String::NewFromUtf8(isolate, "x"),
                                                    v8pp::class_<X>::reference_external(isolate, &x));
        
        // Create a string containing the JavaScript source code.
        Local<String> source = String::NewFromUtf8(isolate, "x.b", NewStringType::kNormal).ToLocalChecked();
        
        // Compile the source code.
        Local<Script> script = Script::Compile(context, source).ToLocalChecked();
        
        // Run the script to get the result.
        TryCatch trycatch(isolate);
        Local<Value> result = script->Run();
        if (result.IsEmpty()) {
            Local<Value> exception = trycatch.Exception();
            String::Utf8Value exception_str(exception);
            printf("Exception: %s\n", *exception_str);
        } else {
            // Convert the result to an UTF8 string and print it.
            String::Utf8Value utf8(result);
            printf("%s\n", *utf8);
        }
    }
    
    // Dispose the isolate and tear down V8.
    v8pp::cleanup(isolate);
    isolate->Dispose();
    V8::Dispose();
    V8::ShutdownPlatform();
    delete platform;
    delete create_params.array_buffer_allocator;
    return 0;
}
=================================================================
==59589==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x7fff5fbfeb60 in thread T0
    #0 0x100d36bbb in wrap__ZdlPv (libclang_rt.asan_osx_dynamic.dylib+0x57bbb)
    #1 0x10002ca2d in v8pp::factory<X>::destroy(v8::Isolate*, X*) factory.hpp:35
    #2 0x10002c9f3 in v8pp::detail::class_singleton<X>::class_singleton(v8::Isolate*, v8pp::detail::type_info const&)::'lambda'(v8::Isolate*, void*)::operator()(v8::Isolate*, void*) const class.hpp:356
    #3 0x10002c9bf in v8pp::detail::class_singleton<X>::class_singleton(v8::Isolate*, v8pp::detail::type_info const&)::'lambda'(v8::Isolate*, void*)::__invoke(v8::Isolate*, void*) class.hpp:354
    #4 0x10002eb42 in v8pp::detail::class_info::remove_objects(v8::Isolate*, void (*)(v8::Isolate*, void*)) class.hpp:153
    #5 0x10002e07f in v8pp::detail::class_singleton<X>::destroy_objects() class.hpp:505
    #6 0x10002db13 in v8pp::detail::class_singleton<X>::~class_singleton() class.hpp:365
    #7 0x10001c594 in v8pp::detail::class_singleton<X>::~class_singleton() class.hpp:364
    #8 0x10001c5b8 in v8pp::detail::class_singleton<X>::~class_singleton() class.hpp:364
    #9 0x10000980f in std::__1::__vector_base<std::__1::unique_ptr<v8pp::detail::class_info, std::__1::default_delete<v8pp::detail::class_info> >, std::__1::allocator<std::__1::unique_ptr<v8pp::detail::class_info, std::__1::default_delete<v8pp::detail::class_info> > > >::~__vector_base() memory:2525
    #10 0x100009374 in std::__1::vector<std::__1::unique_ptr<v8pp::detail::class_info, std::__1::default_delete<v8pp::detail::class_info> >, std::__1::allocator<std::__1::unique_ptr<v8pp::detail::class_info, std::__1::default_delete<v8pp::detail::class_info> > > >::~vector() iterator:1244
    #11 0x100009354 in std::__1::vector<std::__1::unique_ptr<v8pp::detail::class_info, std::__1::default_delete<v8pp::detail::class_info> >, std::__1::allocator<std::__1::unique_ptr<v8pp::detail::class_info, std::__1::default_delete<v8pp::detail::class_info> > > >::~vector() iterator:1244
    #12 0x100009334 in v8pp::detail::class_singletons::~class_singletons() class.hpp:199
    #13 0x100009314 in v8pp::detail::class_singletons::~class_singletons() class.hpp:199
    #14 0x1000092f8 in std::__1::pair<v8::Isolate* const, v8pp::detail::class_singletons>::~pair() utility:253
    #15 0x1000092d4 in std::__1::pair<v8::Isolate* const, v8pp::detail::class_singletons>::~pair() utility:253
    #16 0x100011992 in std::__1::__hash_table<std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons>, std::__1::__unordered_map_hasher<v8::Isolate*, std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons>, std::__1::hash<v8::Isolate*>, true>, std::__1::__unordered_map_equal<v8::Isolate*, std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons>, std::__1::equal_to<v8::Isolate*>, true>, std::__1::allocator<std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons> > >::erase(std::__1::__hash_const_iterator<std::__1::__hash_node<std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons>, void*>*>) memory:1673
    #17 0x1000112f9 in unsigned long std::__1::__hash_table<std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons>, std::__1::__unordered_map_hasher<v8::Isolate*, std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons>, std::__1::hash<v8::Isolate*>, true>, std::__1::__unordered_map_equal<v8::Isolate*, std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons>, std::__1::equal_to<v8::Isolate*>, true>, std::__1::allocator<std::__1::__hash_value_type<v8::Isolate*, v8pp::detail::class_singletons> > >::__erase_unique<v8::Isolate*>(v8::Isolate* const&) __hash_table:2370
    #18 0x1000064b5 in v8pp::detail::class_singletons::instance(v8pp::detail::class_singletons::operation, v8::Isolate*) unordered_map:1099
    #19 0x10000516b in v8pp::detail::class_singletons::remove_all(v8::Isolate*) class.hpp:257
    #20 0x100004b64 in v8pp::cleanup(v8::Isolate*) class.hpp:749
    #21 0x100003693 in main testExternalReference.cpp:78
    #22 0x7fff8d59b5ac in start (libdyld.dylib+0x35ac)

AddressSanitizer can not describe address in more detail (wild memory access suspected).
SUMMARY: AddressSanitizer: bad-free (libclang_rt.asan_osx_dynamic.dylib+0x57bbb) in wrap__ZdlPv
==59589==ABORTING

how to release memeory in member function?

like the following code, how to release the char pointer str1?

#include <node.h>
#include <v8pp/module.hpp>

using namespace v8;

char const* Method() {
  char* str1 = new char[32];
  if (NULL != str1) strcpy(str1, "world");
  return str1;
}

void init(Handle<Object> exports) {
  v8pp::module addon(Isolate::GetCurrent());
  addon.set("hello", &Method);
  exports->SetPrototype(addon.new_instance());
}

NODE_MODULE(addon, init)

Proposal: an option to use v8pp::class_ concurrently

I wanted to use v8pp::class_ concurrently in separate threads/isolates and
realized that it shouldn’t work because class_singleton had some static variables.

thread1 = std::thread([](){
  v8::Isolate *isolate = v8::Isolate::New(...);
  v8pp::class_<Layer> Layer_class(isolate);
});

thread2 = std::thread([](){
  v8::Isolate *isolate = v8::Isolate::New(...);
  v8pp::class_<Layer> Layer_class(isolate);
});

I tried replacing all static variables with thread_local ones and then my program seemed to work well, but worried that this change may break existing code.
(e.g. Sharing a v8::Isolate among threads with v8::Locker)

Think up two solution:

  1. Creates an option to use thread_local instead of static. (e.g. #define V8PP_USE_THREAD_LOCAL)
  2. Adds an optional argument to all functions under v8pp::class_ to specify a custom shared variable with any storage class.

like this:

thread1 = std::thread([](){
  v8::Isolate *isolate = v8::Isolate::New(...);
  v8pp::environment env;
  v8pp::class_<Layer> Layer_class(isolate, &env);
});

thread2 = std::thread([](){
  v8::Isolate *isolate = v8::Isolate::New(...);
  v8pp::environment env;
  v8pp::class_<Layer> Layer_class(isolate, &env);
});

Solution 1 is ad-hoc but easy.
Solution 2 is more general but affects many api.

Fails to compile under Visual Studio 2015 (class.hpp throws errors)

Binding a class to v8 appears to be temperamental with Visual Studio 2015 and a recent version of v8 (4.5, fetched this morning). I'm experiencing 12 errors, all sourced within class.hpp. (For the record, the actual library compilation does not fail. Attempting to use the library, however, causes the program that tries to use it to fail to compile, so in the solution v8pp compiles but test doesn't.)
The errors are:
Error C2228 left of '.this' must have class/struct/union test C:\Libs\v8pp\v8pp\class.hpp 223
Error C2228 left of '.Isolate' must have class/struct/union test C:\Libs\v8pp\v8pp\class.hpp 224
Error C2228 left of '.GetIsolate' must have class/struct/union test C:\Libs\v8pp\v8pp\class.hpp 224
Error C2143 syntax error: missing ';' before '&' test C:\Libs\v8pp\v8pp\class.hpp 227
Error C2461 'v8pp::detail::class_singleton::wrap::<lambda_d9e6e0bda7b69850ea7c26ca875fd1ab>': constructor syntax missing formal parameters test C:\Libs\v8pp\v8pp\class.hpp 227
Error C2238 unexpected token(s) preceding ';' test C:\Libs\v8pp\v8pp\class.hpp 227
Error C4430 missing type specifier - int assumed. Note: C++ does not support default-int test C:\Libs\v8pp\v8pp\class.hpp 227
Error C2143 syntax error: missing ',' before '&' test C:\Libs\v8pp\v8pp\class.hpp 227
Error C2610 'v8pp::detail::class_singleton::wrap::<lambda_d9e6e0bda7b69850ea7c26ca875fd1ab>::<lambda_d9e6e0bda7b69850ea7c26ca875fd1ab>(const int)': is not a special member function which can be defaulted test C:\Libs\v8pp\v8pp\class.hpp 227
Error C2061 syntax error: identifier '<lambda_d9e6e0bda7b69850ea7c26ca875fd1ab>' test C:\Libs\v8pp\v8pp\class.hpp 227
Error C2535 'v8pp::detail::class_singleton::wrap::<lambda_d9e6e0bda7b69850ea7c26ca875fd1ab>::<lambda_d9e6e0bda7b69850ea7c26ca875fd1ab>(void)': member function already defined or declared test C:\Libs\v8pp\v8pp\class.hpp 227
Error C1903 unable to recover from previous error(s); stopping compilation test C:\Libs\v8pp\v8pp\class.hpp 227

Remote debugging

Did you ever do any work on setting up remote chrome debugging? It was removed from v8 a while ago and some projects like node have their own implementations but I don't see anything that seems pluggable into an arbitrary embedded v8 instance.
If not , if you're interested in collaborating on it let me know. It looks like a lot of work. I would probably yse boost::asio for the socket server and then start doing packet traces to see the order of which Jason requests are used by the protocol and implement responses in that order.

allow conversion between v8::Function and std::function<void ()>

Is there any possibility creating assignable callbacks a la dom, e.g.

class Image {
	std::function<void ()> _onLoad;
	void Load();
	}


	v8pp::class_<Image> imageClass(exports.isolate());
	imageClass.ctor<>();
	imageClass.set("load", &Image::Load);
	imageClass.set("onload", &Image::_onLoad);

	void Image::Load(...) { .... _onLoad(); }

and js code:
	image = new Image()
	image.onload = function() { console.log('boo hoo') } 
	image.load('https://example.com/image.png')

Ideally check for callable member pointer type in convert.hpp.

Question: Array of structs

Hi,

first of all: great library !

Just want to ask I've anyone know if it's possible to introduce an array of structs to v8 like:

struct MyStruct {
      MyStruct(uint32_t id = 0, const std::string& name = "")
        : id(id)
        , name(name)
      {}
      uint32_t id;
      std::string name;
};

namespace v8pp {
template<>
struct convert<MyStruct>
{
  using from_type = MyStruct;
  using to_type = v8::Handle<v8::Object>;
  static bool is_valid(v8::Isolate*, v8::Handle<v8::Value> value) {
    return !value.IsEmpty() && value->IsObject();
  }
  static to_type to_v8(v8::Isolate* isolate, MyStruct const& p)  {
    v8::EscapableHandleScope scope(isolate);
    v8::Local<v8::Object> obj = v8::Object::New(isolate);
    set_option(isolate, obj, "id", p.id);
    set_option(isolate, obj, "name", p.name);
    return scope.Escape(obj);
  }
  static from_type from_v8(v8::Isolate* isolate, v8::Handle<v8::Value> value)  {
    if (!is_valid(isolate, value))
      throw std::runtime_error("excpected object");
    v8::HandleScope scope(isolate);
    v8::Local<v8::Object> obj = value.As<v8::Object>();
    MyStruct result;
    get_option(isolate, obj, "id", result.id);
    get_option(isolate, obj, "name", result.name);
    return result;
  }
};
}

std::vector<MyStruct> myStructVec;
myStructVec.emplace_back(1,"MaxMuster");
v8pp::context context;
auto isolate = context.isolate();
v8::HandleScope scope(isolate);
v8pp::module input(isolate);
input.set("array", myStructVec);
auto currentGlobContext = isolate->GetCurrentContext()->Global();
auto inputModul = v8::String::NewFromUtf8(isolate, "input");
currentGlobContext->Set(inputModul, input.new_instance());

Furthermore it should be possible to access and change the data within every MyStruct entry of the array in Javascript and access the changed data in C++ after the script finished running ?

I've tried it but can't seem to get it done right because the values aren't changed after executing:

input.array[0].id = 9;
input.array[0].name = "TestName";

Thank you very much for your time and help !

Regards,
atvise

classbinding for classes with multiple inheritance

If an object inherits from multiple classes, and the defined method mapping is not part of the first declared inheritance, this leads to accessing the wrong address.

for example:
class CInheriting: public Class1, public Class2 {};

v8pp::class_ CInheriting_class(isolate);
CInheriting_class
.set("Class1Method", &Class1Method) << Works
.set("Class2Method", &Class2Method) << Fails

Tried in VS2017

howto return array of objects

I have to return array of objects, where objects is

struct object_t
{
    int x;
    int y;
    std::string type;
    //...
};

std::vector<object_t> result;

I need return result

class object in class

Hi,
for class like this:
struct A
{
int a;
};

struct B
{
A aa;
int b;
};
how can i wrap class B as v8 object? then i can use it like " b = new b(); b.aa.a = 10;"

TIA

Segmentation fault when cleaning up v8pp::context

Hi,

When trying to execute the following code:

#include <iostream>
#include <v8.h>
#include <libplatform/libplatform.h>
#include <v8pp/context.hpp>

int main(int argc, char* argv[])
{
    v8::V8::InitializeICU();
    v8::V8::InitializeExternalStartupData(argv[0]);
    std::unique_ptr<v8::Platform> platform(v8::platform::CreateDefaultPlatform());
    v8::V8::InitializePlatform(platform.get());
    v8::V8::Initialize();

    v8pp::context context;

    const std::string source = "2 + 6";

    v8::Isolate* isolate = context.isolate();

    v8::HandleScope scope(isolate);
    v8::TryCatch tryCatch;
    v8::Handle<v8::Value> result = context.run_script(source);
    if(tryCatch.HasCaught())
    {
        const std::string msg = v8pp::from_v8<std::string>(isolate, tryCatch.Exception()->ToString());
	throw std::runtime_error(msg);
    }

    const int scriptResult = v8pp::from_v8<int>(isolate, result);

    std::cout << scriptResult;

    v8::V8::Dispose();
    v8::V8::ShutdownPlatform();

    return 0;
}

The program executes correctly and scriptResult contains 8 as expected. However, there is a segmentation fault when the deconstructor is called on v8pp::context context; with the following backtrace:

#0  0x00007ffff77c5177 in v8::internal::MemoryAllocator::Unmapper::FreeQueuedChunks() () from /usr/local/lib/libv8.so
No symbol table info available.
#1  0x00007ffff77c9187 in v8::internal::NewSpace::TearDown() () from /usr/local/lib/libv8.so
No symbol table info available.
#2  0x00007ffff777d5d9 in v8::internal::Heap::TearDown() () from /usr/local/lib/libv8.so
No symbol table info available.
#3  0x00007ffff783e7b4 in v8::internal::Isolate::Deinit() () from /usr/local/lib/libv8.so
No symbol table info available.
#4  0x00007ffff783e4d0 in v8::internal::Isolate::TearDown() () from /usr/local/lib/libv8.so
No symbol table info available.
#5  0x0000000000405eee in v8pp::context::~context() ()
No symbol table info available.
#6  0x0000000000403ec8 in main (argc=1, argv=0x7fffffffe508) at /home/james/workspace/v8test/main.cpp:20
        platform = std::unique_ptr<v8::Platform> containing 0x61fa30
        context = {
          own_isolate_ = true, 
          isolate_ = 0x620ef0, 
          impl_ = {
            <v8::PersistentBase<v8::Context>> = {
              val_ = 0x0
            }, <No data fields>}, 
          modules_ = std::map with 0 elements, 
          lib_path_ = ""
        }
        source = "2 + 6"
        isolate = 0x620ef0
        scope = {
          isolate_ = 0x620ef0, 
          prev_next_ = 0x0, 
          prev_limit_ = 0x0
        }
        tryCatch = {
          isolate_ = 0x620ef0, 
          next_ = 0x0, 
          exception_ = 0x17a028882351, 
          message_obj_ = 0x17a028882351, 
          js_stack_comparable_address_ = 0x7fffffffe288, 
          is_verbose_ = false, 
          can_continue_ = true, 
          capture_message_ = true, 
          rethrow_ = false, 
          has_terminated_ = false
        }
        result = {
          val_ = 0x671380
        }
        scriptResult = 8

Am I doing something wrong or is this a bug?

Question: binding class to global object

Hi,

I need do a binding on an existing codebase. The resulting JavaScript interface needs to be compatible to what we had before (it was using QtScript).

Some classes are added directly in the global scope of JavaScript (ie. var x = X() instead of var x = module.X())

A module is added that way:

isolate->GetCurrentContext()->Global()->Set(v8::String::NewFromUtf8(isolate, "module"), module.new_instance());

I have not found a way to achieve the same using a v8pp::class_. There is no new_instance method in it.

Is there a way to achieve what I want?

shared_ptr branch

Hi,

I'm using the shared_ptr branch. I was trying to merge this branch with the latest master, but there are heavy conflicts that I don't know how to resolve.
What is the plan for shared_ptr? I think this is a very useful feature. It would be great if this could be supported officially.

Forced garbage collection causes assertion failure if you try adding a std::function to a module

If I try adding a std::function to a v8pp module, and then force a final garbage collection before disposing of the isolate (which generally appears to be necessary if you're worried about making sure all embedded C++ objects get garbage collected, since merely disposing of the isolate doesn't guarantee all the weak callbacks v8pp registers are actually called), I get an assertion failure ("Check failed: node_->state() == Node::FREE.") later when trying to actually disposing of the isolate.

This doesn't happen (i.e., no assertion failure triggers) if I add either a C++ function or a lambda to the module instead of a std::function.

Test program illustrating the crash attached: v8_gc_crash.txt

Any idea for using `SetHandler` on class?

To achieve this, I added some code to class.hpp, so I can call klass.index<Type>() to make this class array-like. However I don't think it's an elegant way (my code)...

#define METHOD_CHECKER_ANY(fn, args) \
template<class C, typename=void> struct has_member_##fn : std::false_type {}; \
template<class C> struct has_member_##fn<C, typename std::enable_if< \
  !std::is_same<decltype(std::declval<C>().fn args)*, void>::value>::type> : std::true_type {};

METHOD_CHECKER_ANY(index_query, (0));
METHOD_CHECKER_ANY(index_set, (0, T1()));
METHOD_CHECKER_ANY(index_delete, (0));
METHOD_CHECKER_ANY(index_enum, ());

	template<class T1>
	static auto set_index_set(v8::IndexedPropertySetterCallback &indexset) -> typename std::enable_if_t<has_member_index_set<T>::value> {
		indexset = [](uint32_t index, v8::Local<v8::Value> value, v8::PropertyCallbackInfo <v8::Value> &info) -> void {
			auto klass = v8pp::from_v8<T&>(info.GetIsolate(), info.This());
			
			info.GetReturnValue().Set(v8pp::to_v8<T1>(info.GetIsolate(), klass.index_set(index, v8pp::to_local<T1>(value))));
		};
	}


	template<class T1>
	static auto set_index_set(v8::IndexedPropertySetterCallback &indexset) -> typename std::enable_if_t<!has_member_index_set<T>::value> {

	}
	template<class T1>
	static auto set_index_query(v8::IndexedPropertyQueryCallback &indexquery) -> typename std::enable_if_t<has_member_index_query<T>::value> {
		indexquery = [](uint32_t index, v8::PropertyCallbackInfo <v8::Integer> &info) -> void {
			auto klass = v8pp::from_v8<T&>(info.GetIsolate(), info.This());
			info.GetReturnValue().Set(v8pp::to_v8<int>(info.GetIsolate(), klass.index_query(index)));
		};
	}
	template<class T1>
	static auto set_index_query(v8::IndexedPropertyQueryCallback &indexquery) -> typename std::enable_if_t<!has_member_index_query<T>::value> {

	}
	template<class T1>
	static auto set_index_delete(v8::IndexedPropertyDeleterCallback &indexdelete) -> typename std::enable_if_t<has_member_index_delete<T>::value> {
		indexdelete = [](uint32_t index, v8::PropertyCallbackInfo <v8::Boolean> &info) -> void {
			auto klass = v8pp::from_v8<T&>(info.GetIsolate(), info.This());
			info.GetReturnValue().Set(v8pp::to_v8<bool>(info.GetIsolate(), klass.index_delete(index)));
		};
	}
	template<class T1>
	static auto set_index_delete(v8::IndexedPropertyDeleterCallback &indexdelete) -> typename std::enable_if_t<!has_member_index_delete<T>::value> {

	}
	template<class T1>
	static auto set_index_enum(v8::IndexedPropertyEnumeratorCallback &indexenum) -> typename std::enable_if_t<has_member_index_enum<T>::value> {
		indexenum = [](v8::PropertyCallbackInfo <v8::Array> &info) -> void {
			auto klass = v8pp::from_v8<T&>(info.GetIsolate(), info.This());
			info.GetReturnValue().Set(v8pp::to_v8<v8::Array>(info.GetIsolate(), klass.index_enum()));
		};
	}
	template<class T1>
	static auto set_index_enum(v8::IndexedPropertyEnumeratorCallback &indexenum) -> typename std::enable_if_t<!has_member_index_enum<T>::value> {

	}

	template<class T1> // element type
	void index() {
		v8::IndexedPropertyGetterCallback IndexGet = [](uint32_t index, const v8::PropertyCallbackInfo<v8::Value> &info) -> void {
			auto klass = v8pp::from_v8<T&>(info.GetIsolate(), info.This());
			info.GetReturnValue().Set(v8pp::to_v8<T1>(info.GetIsolate(), klass.index_get(index)));
		};

		v8::IndexedPropertySetterCallback IndexSet = NULL;
		set_index_set<T1>(IndexSet);

		v8::IndexedPropertyQueryCallback IndexQuery = NULL;
		set_index_query<T1>(IndexQuery);

		v8::IndexedPropertyDeleterCallback IndexDelete = NULL;
		set_index_delete<T1>(IndexDelete);

		v8::IndexedPropertyEnumeratorCallback IndexEnum = NULL;
		set_index_enum<T1>(IndexEnum);

		class_function_template()->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(IndexGet, IndexSet, IndexQuery, IndexDelete,IndexEnum));
	}

Is there any better method to do this?

Bind simple function as class member

I would like to bind a simple function (with the v8::FunctionCallbackInfo signature) as a class member. The use case for that is to have a more convenient API to access the class from JavaScript (in some cases I don't want a 1:1 mapping of the C++ methods in JavaScript). It would also be a workaround for method overloads. This is similar to the simple function as ctor in issue #41, but for methods. Please note that it's for a case where the original class cannot be modified.

Is there a way to achieve that with the current version of v8pp?

Below is what I tired. It does compile, but property 'f' of an instance of X is undefined.

// Some class in external library; cannot be modified
struct X
{
    bool b;
    X(bool b) : b(b) {}
};


// Add a function that should appear like a method of X in JavaScript
// (using FunctionCallbackInfo to handle overloads)
static void f(const v8::FunctionCallbackInfo<v8::Value>& args)
{
    // How to get a pointer or reference to class instance here (args.Data() ?)
}

void v8ppTestBind(v8::Isolate* isolate)
{
    v8pp::class_<X> X_class(isolate);
    X_class
        .ctor<bool>()
        .set("b", &X::b)
        .set("f", &f);
    
    v8pp::module module(isolate);
    module.set("X", X_class);
    
    isolate->GetCurrentContext()->Global()->Set(v8::String::NewFromUtf8(isolate, "module"), module.new_instance());
}

Debugging JS running under V8

Perhaps this isn't the appropriate place to post this but googling didn't provide a lot of answers. How does one go about debugging JavaScript running under V8 through v8pp?

Thanks!

Possible bug in `get_option`

When using get_option I encountered strange crashes. I think the Handle/Local returned by get_option is deleted by the HandleScope inside the get_option function.

When commenting out v8::HandleScope scope(isolate); in the function, the spooky behaviour disappears. I this case the the responsibility of the caller's HandleScope to delete the handles. Maybe a fix using EscapableHandleScope would be better?

template<typename T>
bool get_option(v8::Isolate* isolate, v8::Handle<v8::Object> options,
    char const* name, T& value)
{
    char const* dot = strchr(name, '.');
    if (dot)
    {
        std::string const subname(name, dot);
        //v8::HandleScope scope(isolate);   // THIS IS MY FIX
        v8::Local<v8::Object> suboptions;
        return get_option(isolate, options, subname.c_str(), suboptions)
            && get_option(isolate, suboptions, dot + 1, value);
    }
    v8::Local<v8::Value> val = options->Get(v8pp::to_v8(isolate, name));
    if (val.IsEmpty() || val->IsUndefined())
    {
        return false;
    }
    value = from_v8<T>(isolate, val);
    return true;
}

The problem can be reproduced with following code:

context.run_script("test = { test : function() { return 1; } }");
if (try_catch.HasCaught()) {
    throw "error";
}

v8::Local<v8::Function> fTest;
if (!v8pp::get_option(isolate, isolate->GetCurrentContext()->Global(), "test.test", fTest)) {
    throw std::runtime_error("No 'test.test'");
}
if (!fTest->IsFunction()) {
    throw std::runtime_error("It's not a function, something is wrong");
}

fTest seems to be garbage.

Question: mapping nested object's properties and JS classes.

Hi,

I would have a few questions about properties (JsWrapped) objects nested in other JSWrapped objects.
Let's say I have a Sprite c++ class embedding a Coord:

class Coord {
public:
    Coord() : x(0), y(0) {}
    int x,y;
};
class Sprite {
public:
      Sprite() : position() {}
      Coord position;
};

(step 1) I wrap them using the class_ helpers:

v8pp::class_<Coord> JS_Coord(isolate);
JS_Coord.ctor<>();
JS_Coord.set("x", v8pp::property(&Coord::x));
JS_Coord.set("y", v8pp::property(&Coord::y));

v8pp::class_<Sprite> JS_Sprite(isolate);
JS_Sprite.ctor<>();
JS_Sprite.set("position", v8pp::property(&Sprite::position));

(step 2) in my JS script, I instanciate a new Sprite.

s = new Sprite();
console.log("pos type is "+typeof(s.position));

Getting the position returns 'undefined'.

I understood it was because the Coord instance was not known from class_<Coord>::find_object(), because it has not been instanciated by the v8pp wrappers, rather by my Sprite constructor's class, so on "C++ side"

(step 3) I can manage such problem by changing the find_object() function, adding kind of 'auto external's referencement':

static v8::Handle<v8::Object> find_object(v8::Isolate* isolate, T const* obj) {
    // as before
    v8::Handle<v8::Object> jsObj = class_singleton::instance(isolate).find_object(obj);
    // fallback added if not already registered in singleton:
    if (jsObj.IsEmpty()) {
        jsObj = v8pp::class_<T>::reference_external(isolate, ((T*)obj) );
    }
    return jsObj;
}

But now, the problem is that when I delete my initial object (the Sprite), (i suppose) the Coord instance will remain indefinitely registered in the singleton<Coord> (with a dangling pointer).

=> (I'm new to v8pp, so it's probable that) I missed something ?

=> To manage the mapping of the object's lifetimes between "C++ side" and "JS side", should i foresee something the like usage of 'smart pointers' with kind of callback to v8pp when an object is deleted, and also using "smart_ptr aliasing" to recursively delete nested objects (Sprite deleted => delete Coord) ?

Any idea would be highly welcome.
Thanks in advance.

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.