I have a piece of C++ code converted to JavaScript via Emscripten. I would like the converted C++ code to call back to the JavaScript code that calls it. Something like:
JavaScript:
function callback(message) {
alert(message);
}
ccall("my_c_function", ..., callback);
C++:
void my_c_function(whatever_type_t *callback) {
callback("Hello World!");
}
Is this possible somehow?
I believe the accepted answer is a bit outdated.
Please refer to this bullet point in the "Interacting with code" emscripten tutorial.
E.g.
C:
void invoke_function_pointer(void(*f)(void)) {
(*f)();
}
JS:
var pointer = Runtime.addFunction(function() {
console.log('I was called from C world!');
});
Module.ccall('invoke_function_pointer', 'number', ['number'], [pointer]);
Runtime.removeFunction(pointer);
This way the C-code does not need to be aware of that it is transpiled to JS and any bridges required can purely be controlled from JS.
(code hacked into message composer; may contain errors)
There is a new way of achieving your requirement which is via embind.
Consider the following piece of C++ code.
#include <emscripten/bind.h>
using namespace emscripten;
void cbTest(emscripten::val cb)
{
cb();
}
EMSCRIPTEN_BINDINGS(my_module) {
function("cbTest", &cbTest);
}
The cbTest C++ function takes in a emscripten::val. This can be an object of any kind. For us this is a function object.
This is how you will call it from JS
var cbFunc = function() {
console.log("Hi, this is a cb");
}
Module.cbTest(cbFunc);
P.S This api is still under construction.
A thing that is frequently done in Emscripten is to map strong types to simple ones.
JS:
function callback(message) {
alert(message);
}
var func_map = {
0: callback
};
// C/C++ functions get a _ prefix added
function _invoke_callback(callback_id, text_ptr) {
func_map[callback_id](Pointer_stringify(text_ptr));
}
ccall("my_c_function", ..., 0);
C++:
// In C/C++ you only need to declare the func signature and
// make sure C is used to prevent name mangling
extern "C" void invoke_callback(int callback_id, const char* text);
void my_c_function(int callback_id) {
invoke_callback( callback_id, "Hello World!" );
}
And of course, you can add some glue code, so this gets very seamless.
I needed to write something very similar to what is described in the question. My code ended up looking like this:
C:
void call(void (*back)(char*)){
back("Hello!");
}
JS:
function back(text){
alert(Pointer_stringify(text));
}
var pointer = Runtime.addFunction(back);
var call = Module.cwrap('call', 'void', ['pointer']);
call(pointer);
Runtime.removeFunction(pointer);
Note that the pointer returned to the callback has to be dereferenced with Pointer_stringify.
You can find example code like this on GitHub.
Here's what I have gathered from several posts and by looking at Emscripten bundled code:
In C++:
#include <iostream>
#include <functional>
extern "C" {
void registerCallback(void(*back)(const char*));
void triggerCallback(char* message); // for invoking it from JS, just for this example
}
// global
std::function<void(const char*)> gCallback;
void registerCallback(void(*back)(const char*)){
gCallback = back;
}
void triggerCallback(char* message){
if (gCallback) {
gCallback(message);
} else {
std::cerr << "Cannot pass '"<< message <<"' to undefined callback\n";
}
}
An important thing, which was missing in other posts, is to compile C++ with RESERVED_FUNCTION_POINTERS=... flag, e.g.:
em++ -std=c++11 -s RESERVED_FUNCTION_POINTERS=20 source.cpp -s EXPORTED_FUNCTIONS="['_registerCallback','_triggerCallback']" -o try.html
After loading try.html into a browser, you can execute the following JS code in its console:
// Register a callback function
function callback(text){ alert("In JS: "+Pointer_stringify(text)); }
var cb = Runtime.addFunction(callback);
_registerCallback(cb);
// Invoke it with some "C string"
var jsStr = "XOXOXO";
var cStr = allocate(intArrayFromString(jsStr), 'i8', ALLOC_NORMAL)
_triggerCallback(cStr);
// Free Emscripten heap and release the function pointer
_free(cStr);
Runtime.removeFunction(cb);
You should see an alert with "In JS: XOXOXO".
Related
I needed to extend my C app with a scripting language so I tried MuJS, it's written in simple C, but it lacks documentation about specifics. Digging through the code I couldn't figure out how to do this basic task of creating a simple object with a function inside it.
expected result:
myobject: {
myfn: function () {}
}
I tried this but it resulted in an "uncaught exception".
js_newobject(J);
{
js_newcfunction(J, myfn, "myfn", 2);
js_setproperty(J, -2, "myfn");
}
js_setglobal(J, "myobject");
As you mentionned it, MuJS is poorly documented. You must consider switching to a better documented and widely used alternatives such as LuaJIT. Look at an example here .
You're probably doing something else wrong, because the code you wrote should work as expected.
Here's a full example with your code that compiles and runs without errors:
#include <stdio.h>
#include "mujs.h"
void myfn(js_State *J)
{
printf("Hello, world!\n");
}
int main(int argc, char **argv)
{
js_State *J = js_newstate(NULL, NULL, 0);
js_newobject(J);
js_newcfunction(J, myfn, "myfn", 0);
js_setproperty(J, -2, "myfn");
js_setglobal(J, "myobject");
js_dostring(J, "myobject.myfn()");
js_freestate(J);
}
I'm trying to embed an custom function to my project, that uses the V8 engine, and apparently I can't make it working. I've used code, that I've found, but it seems to be outdated, or I just do it wrong. My point is to include a custom javascript file. My current code (for testing) is this :
HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
global->Set(v8::String::NewFromUtf8(isolate, "test", v8::NewStringType::kNormal).ToLocalChecked(),
v8::FunctionTemplate::New(isolate, test));
Handle<Context> context = Context::New(isolate);
Persistent<Context> persistent_context(isolate, context);
Context::Scope context_scope(context);
const char* data = "test";
Handle<String> source = String::NewFromUtf8(isolate, data);
Handle<Script> script = Script::Compile(source);
if (!script.IsEmpty())
Handle<Value> result = script->Run();
Test Code (obviously just for testing):
void test(const v8::FunctionCallbackInfo<v8::Value>& args) {
MessageBoxA(NULL,"test", "", 0);
}
But the engine returns this error :
Uncaught ReferenceError: test is not defined
So my question is if I even do it correct, I would be able to make the including myself I hope, but I just can't get the function to get executed.
Here's some code from a project that works:
Isolate::Scope iscope( isolate_ );
HandleScope hs( isolate_ );
Local<Object> test = Object::New(isolate_);
test->Set(String::NewFromUtf8(isolate_, "javaScriptFunctionName"), Function::New(isolate_, CPP_FN_CALLBACK));
global_template->Set(String::NewFromUtf8(isolate_, "test"), test);
That will result in an object for window.test with a function called window.test.javaScriptFunctionName()
Dynamic library is created using some C files. This dynamic library is used by a C++ function. One of the C-functions calls a C++ function. Which in turn will invoke a Javascript Function Callback. I am getting segmentation fault in C++ function.
Here is the code,
In test.h,
#ifdef __cplusplus
extern "C" {
#endif
void cppFunc(void);
#ifdef __cplusplus
}
#endif
Here is the code in test.c
#include "test.h"
void cFunc(void){
/* some code */
cppFunc();
}
Here is the code in test.cpp
#include "test.h"
static Nan::Persistent<v8::Function> callback;
void storeFunc(const v8::FunctionCallbackInfo<v8::Value>& args){
if(args[0]->IsFunction()){
Local<Function> cb = Local<Function>::Cast(args[0]);
callback.Reset(cb);
}
}
void cppFunc(void){
Isolate *isolate = Isolate::GetCurrent();
Local<Function> c_back = Local<Function>::New(isolate, callback);
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), c_back, 0, {});
}
void init(Handle<Object> exports, Handle<Object> module)
{
NODE_SET_METHOD(exports, "FuncStore", storeFunc);
}
NODE_MODULE(test, init)
test.node is created by using "node-gyp configure build" command. No errors or warnings are shown.
Following is my test.js file,
var sample = require("./build/Release/test.node");
sample.FuncStore(function(){
console.log("Callback has been called!!");
});
On executing test.js by "node test.js", a segmentation fault occurs when cFunc() calls cppFunc(). Segmentation fault is happening exactly at the line "Local c_back = Local::New(isolate, callback);" in cppFunc().
What could be the reason?
One possibility is that the pointer "isolate" is NULL. Accessing a pointer with no value or an invalid value can cause a segmentation fault. You should check for it's existence by performing:
Isolate *isolate = Isolate::GetCurrent();
if (isolate) {
//it is now safe to use isolate
Local<Function> c_back = Local<Function>::New(isolate, callback);
Nan::MakeCallback(Nan::GetCurrentContext()->Global(), c_back, 0, {});
}
else {
//possibly generate your own warning here
}
As a side note, it seems Isolate::GetCurrent is deprecated https://github.com/nodejs/node/commit/409d413363
Is it possible to call a JavaScript function (written over, say, node.js) from C?
(There are plenty of tutorials on calling C/C++ from node.js. But not the other way around.)
I've been working on the same problem recently, and found a tractable solution using QuickJS and esbuild. It's not the prettiest, but it works quite well!
To call JS from C, the general process is:
Get QuickJS and esbuild
esbuild your desired library/script into an ESM format using CommonJS. This will output one big script with all needed dependencies included.
output=/path/to/esbuild/output
npx esbuild --bundle /path/to/original/node-library --format=esm --outfile="$output"
Patch the output of esbuild to make it compatible with QuickJS:
sed -i 's/Function(\"return this\")()/globalThis/g' $output
sed -i 's#export default#//export default#g' $output
Load the script text into an object file using your linker:
ld -r -b binary my_obj_file.o $output
Depending on your compiler, this will automatically create 3 symbols in the object file:
- name_start
- name_end
- name_size
name in this context is automatically generated from the filename you provided as the last argument to ld. It replaces all non-alphanumeric characters with underscores, so my-cool-lib.mjs gives a name of my_cool_lib_mjs.
You can use ld_magic.h (here) for a cross platform way to access this data from your C code.
After the object file is generated, you should see the patched esbuild output if you run strings:
% strings foo_lib_js.o
var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
// src/foo.js
var require_foo = __commonJS({
"src/foo.js"(exports, module) {
function foo(bar, baz) {
return bar + baz;
}
module.exports = foo;
//export default require_foo();
_binary_my_foo_lib_mjs_end
_binary_my_foo_lib_mjs_start
_binary_my_foo_lib_mjs_size
.symtab
.strtab
.shstrtab
.data
Link the object file into your binary:
gcc my_obj_file.o <other object files> -o my_static_binary
You can also link the object file into a shared library, for use in other applications:
gcc -shared -o my_shared_library.so my_obj_file.o <other object files>
The source of this repo shows how to do this with a CMake project.
How to actually call the JS functions
Let's say you have a NodeJS library with a function you want to call from C:
// Let's say this lives in foo.js, and esbuild output goes in my-lib-foo.mjs
function foo(bar, baz) {
return bar + baz
}
module.exports = foo;
esbuild creates a series of require_thing() functions, which can be used to get the underlying thing(param1, param2...) function object which you can make calls with.
A simple loader in QuickJS looks like this:
JSValue commonjs_module_data_to_function(JSContext *ctx, const uint8_t *data, size_t data_length, const char *function_name)
{
JSValue result = JS_UNDEFINED;
char * module_function_name = NULL;
// Make sure you properly free all JSValues created from this procedure
if(data == NULL) {
goto done;
}
/**
* To pull the script objects, including require_thing() etc, into global scope,
* load the patched NodeJS script from the object file embedded in the binary
*/
result = JS_Eval(ctx, data, data_length, "<embed>", JS_EVAL_TYPE_GLOBAL);
if(JS_IsException(result)) {
printf("failed to parse module function '%s'\n", function_name);
goto cleanup_fail;
}
JSValue global = JS_GetGlobalObject(ctx);
/**
* Automatically create the require_thing() function name
*/
asprintf(&module_function_name, "require_%s", function_name);
JSValue module = JS_GetPropertyStr(ctx, global, module_function_name);
if(JS_IsException(module)) {
printf("failed to find %s module function\n", function_name);
goto cleanup_fail;
}
result = JS_Call(ctx, module, global, 0, NULL);
if(JS_IsException(result)) {
goto cleanup_fail;
}
/* don't lose the object we've built by passing over failure case */
goto done;
cleanup_fail:
/* nothing to do, cleanup context elsewhere */
result = JS_UNDEFINED;
done:
free(module_function_name);
return result;
}
If you wanted to, for example, get the foo(bar, baz) function mentioned above, you would write a function like this:
#include <stdio.h>
#include <inttypes.h>
// A simple helper for getting a JSContext
JSContext * easy_context(void)
{
JSRuntime *runtime = JS_NewRuntime();
if(runtime == NULL) {
puts("unable to create JS Runtime");
goto cleanup_content_fail;
}
JSContext *ctx = JS_NewContext(runtime);
if(ctx == NULL) {
puts("unable to create JS context");
goto cleanup_runtime_fail;
}
return ctx;
cleanup_runtime_fail:
free(runtime);
cleanup_content_fail:
return NULL;
}
int call_foo(int bar, int baz)
{
JSContext *ctx = easy_context();
JSValue global = JS_GetGlobalObject(ctx);
/**
* esbuild output was to my-foo-lib.mjs, so symbols will be named with my_foo_lib_mjs
*/
JSValue foo_fn = commonjs_module_data_to_function(
ctx
, _binary_my_foo_lib_mjs_start // gcc/Linux-specific naming
, _binary_my_foo_lib_mjs_size
, "foo"
);
/**
* To create more complex objects as arguments, use
* JS_ParseJSON(ctx, json_str, strlen(json_str), "<input>");
* You can also pass callback functions by loading them just like we loaded foo_fn
*/
JSValue args[] = {
JS_NewInt32(ctx, bar),
JS_NewInt32(ctx, baz)
};
JSValue js_result = JS_Call(ctx
, foo_fn
, global
, sizeof(args)/sizeof(*args)
, args
);
int32_t c_result = -1;
JS_ToInt32(ctx, &c_result, js_result);
return c_result;
}
Check out a minimal example project using CMake here: https://github.com/ijustlovemath/jescx/blob/master/README.md
You could use Emscripten.
Emscripten is an LLVM-to-JavaScript compiler.
It takes LLVM bitcode - which can be generated from C/C++, using llvm-gcc (DragonEgg) or clang, or any other language that can be converted into LLVM - and compiles that into JavaScript, which can be run on the web (or anywhere else JavaScript can run).
Also see this: How to execute Javascript function in C++
I have a C++ method (which role is killing some processes). She needs 2 parameters : a hostname and a port.
On the other hand, I am developing a web-application (using Nodejs and AngularJS), running on Google Chrome.
When I click on a button through the browser, I would like to be able to call the C++ function, through my app.js file.
I haven't found how to "bind" javascript with C++.
EDIT : I think this link could be very useful
How can I use a C++ library from node.js?
You can use Google's V8. V8 is Google's open source JavaScript engine.
V8 can run standalone, or can be embedded into any C++ application.
http://code.google.com/p/v8/
Following example from github demonstrates, binding a C++ class with Google V8.
v8_wrap_class.cpp - Author is nicholas
/*
* v8_wrap_class.cpp
*
* Created on: 14/01/2013
* Author: nicholas
* License: public domain
*/
#include <v8.h>
#include <cstdio>
#include <string>
#include <stdexcept>
#include <memory>
using namespace v8;
/*
var Simple = function(v)
{
this.value = v;
}
Simple.prototype.func = function()
{
alert(this.value);
}
var obj = new Simple(4);
obj.func();
*/
struct Simple
{
double value;
Simple(double v)
: value(v)
{
fprintf(stderr, "Simple::ctor\n");
}
void func()
{
fprintf(stderr, "Simple::func(%f)\n", value);
}
~Simple()
{
fprintf(stderr, "Simple::dtor\n");
}
};
namespace js
{
/*
* Retrieve the c++ object pointer from the js object
*/
template <typename T>
T* unwrap(const Arguments& args)
{
auto self = args.Holder();
auto wrap = Local<External>::Cast(self->GetInternalField(0));
return static_cast<T*>(wrap->Value());
}
/*
* Construct a new c++ object and wrap it in a js object
*/
template <typename T, typename... Args>
Persistent<Object> make_object(Handle<Object> object, Args&&... args)
{
auto x = new T(std::forward<Args>(args)...);
auto obj = Persistent<Object>::New(object);
obj->SetInternalField(0, External::New(x));
obj.MakeWeak(x, [](Persistent<Value> obj, void* data)
{
auto x = static_cast<T*>(data);
delete x;
obj.Dispose();
obj.Clear();
});
return obj;
}
}
void bind_Simple(Local<Object> global)
{
// Name the class in js
auto name = String::NewSymbol("Simple");
auto tpl = FunctionTemplate::New([&](const Arguments& args) -> Handle<Value>
{
if (!args.IsConstructCall())
return ThrowException(String::New("Cannot call constructor as function"));
HandleScope scope;
// Read and pass constructor arguments
js::make_object<Simple>(args.This(), args[0]->NumberValue());
return args.This();
});
tpl->SetClassName(name);
tpl->InstanceTemplate()->SetInternalFieldCount(1);
auto prototype = tpl->PrototypeTemplate();
// Add object properties to the prototype
// Methods, Properties, etc.
prototype->Set(String::New("func"), FunctionTemplate::New([](const Arguments& args) -> Handle<Value>
{
auto s = js::unwrap<Simple>(args);
s->func();
return {};
})->GetFunction());
auto constructor = Persistent<Function>::New(tpl->GetFunction());
global->Set(name, constructor);
}
int main()
{
std::string js_source = R"(
for(var i = 0; i < 1000; ++i)
{
var s = new Simple(4);
s.value = 5;
s.func();
}
)";
/*
* This code is mostly uninteresting.
* Just run the vm with the script provided.
*/
{
HandleScope handle_scope;
Handle<ObjectTemplate> global_template = ObjectTemplate::New();
Persistent<Context> context = Context::New(0, global_template);
Context::Scope context_scope(context);
auto global = context->Global();
// Wrap the class and bind to the global scope.
bind_Simple(global);
{
HandleScope handle_scope;
TryCatch trycatch;
Local<String> source = String::New(js_source.c_str(), js_source.size());
Local<Script> script = Script::Compile(source);
if (script.IsEmpty())
{
Handle<Value> exception = trycatch.Exception();
String::AsciiValue exception_str(exception);
throw std::runtime_error(*exception_str);
}
Local<Value> result = script->Run();
if (result.IsEmpty())
{
Local<Value> exception = trycatch.Exception();
String::AsciiValue exception_str(exception);
throw std::runtime_error(*exception_str);
}
}
context.Dispose();
context.Clear();
}
// Run the GC until there is nothing to reclaim.
while (!V8::IdleNotification())
;
return 0;
}
This answer gives four appraoches to using C++ in javascript. The methods shown try to keep the original C++ in a standard and simple C++ implementation.
Two methods using WASM and two using SWIG and JRPC are used to give examples and ideas on executing C++ in javascript. In detail, this answer gives one example for executing in the browser and one for executing in nodejs using WASM. The end of this answer also lists two other ways to execute C++ in javascript, one of which calls nodejs from the browser which is slightly more complex but possible using jrpc-oo.
If you want to execute in the browser or nodejs, then you can compile your C++ to WASM and load that module in the browser or nodejs, executing the C++ there. This WASM repo exemplifies how to do that. I will expand the key code in the WASM repo here.
Create some C++ code and declare your WASM binding, for example (from the files include/Test.H and src/Test.C) :
class Test {
public:
void sayHello(){
printf("Hi, my name is test\n");
}
};
#include <emscripten/bind.h>
EMSCRIPTEN_BINDINGS(Test_ex) {
emscripten::class_<Test>("Test")
.function("sayHello", &Test::sayHello)
;
}
Compile that down and you can now run that in nodejs (from the file nodejs/WASMTestNode.js) :
libWASM = require('./libwasmNode.js');
libWASM().then((mod)=>{
libWASM = mod;
let test = new libWASM.Test;
test.sayHello();
});
In the browser you can use the WASM code as well. To do that firs import the WASM library (from the file webcomponent/libWASM.js) :
import modProm from './libwasm.js';
The create your webcomponent and compile your WASM then execute the C++ method Test::sayHello (from the file webcomponent/libWASM.js) :
import { LitElement } from 'lit';
export class LibWASM extends LitElement {
constructor() {
super();
modProm().then((mod)=>{
this.libWASM = mod; // for rendered wasm that delay
this.WASMReady();
})
}
WASMReady(){
console.log('LibWASM.libWASM module compiled and ready to go.')
let test = new this.libWASM.Test;
test.sayHello();
}
}
This code example is implemented in the reference repo.
Alternatively a third way to do this is to only use C++, SWIG and nodejs from this reference repo.
If you want to execute nodejs from the browser or use a a different method of integrating C++ into nodejs, you can also look at this SWIG and jrpc-oo reference for doing the same thing, but not only in nodejs, also from the browser calling nodejs.
There are also other ways to execute C++ form javascript, however the methods demonstrated in this answer are reasonably straightforward and either rely on WASM binding or SWIG abstraction which leaves your original code as standard C++.