Calling a JavaScript function from C - javascript

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++

Related

Creating custom JavaScript functions in V8?

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

Evaluate helpers is same context as tested file

I would like to test prototype functions that I make.
My set up consist of 3 files:
Base.js - Base file, that all my second files have in common
function prop(to, name, func) {
Object.defineProperty(to.prototype, name, {
value: func,
writable: true,
configurable: true
});
return func;
}
Array.js - file that modify the prototype of given Object.
prop(Array, 'hasPresent', function(what) {
return !!~this.indexOf(what)
});
/tests/Array.js - Test itself
describe('hasPresent()',function(){
it('number', function(done){
expect([0,1,2].hasPresent(0)).toBe(true)
done()
})
})
All this is done from nodeJS, that watches file for changes. My concern is, that it will return error from second file (prop is not defined ... at Array.js:1). This says me, that those files are not evaluated in same context. Is there any way to make this work? Or how to get __direcotory variable in test file, when started from node.
My setup in nodejs:
jasmine.loadConfig({
spec_files: ['Base.js','Array.js']
helpers: ['Base.js','Array.js']
})
jasmine.execute(['tests/Array.js']);
PS.: I tried putting eval in tests/Array.js, but the working directory is forgotten when loaded, so I would have to use absolute path, if there is any way to work around this, it would be great.
You just need to use the normal module mechanism for Node.js: https://nodejs.org/api/modules.html
Here is the basic example which you can adapt to your testing:
The contents of foo.js:
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
The contents of circle.js:
const PI = Math.PI;
exports.area = (r) => PI * r * r;
exports.circumference = (r) => 2 * PI * r;

How to call a C++ method from javascript

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

Passing JS function to Emscripten-generated code

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

Google Closure Compiler advanced: remove code blocks at compile time

If I take this code and compile it (advanced optimizations)
/**#constructor*/
function MyObject() {
this.test = 4
this.toString = function () {return 'test object'}
}
window['MyObject'] = MyObject
I get this code
window.MyObject=function(){this.test=4;this.toString=function(){return"test object"}};
Is there any way I can remove the toString function using the Closure Compiler?
toString is implicitly callable, so unless the Closure compiler can prove that the result of MyObject is never coerced to a string it has to preserve it.
You can always mark it as explicit debug code:
this.test = 4;
if (goog.DEBUG) {
this.toString = function () { return "test object"; };
}
then in your non-debug build, compile with
goog.DEBUG = false;
See http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.source.html which does
/**
* #define {boolean} DEBUG is provided as a convenience so that debugging code
* that should not be included in a production js_binary can be easily stripped
* by specifying --define goog.DEBUG=false to the JSCompiler. For example, most
* toString() methods should be declared inside an "if (goog.DEBUG)" conditional
* because they are generally used for debugging purposes and it is difficult
* for the JSCompiler to statically determine whether they are used.
*/
goog.DEBUG = true;
The answer is surprising simple. I was researching this and didn't find the correct answer, so I am adding it here. The solution is to use a JSDoc annotation (see https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler#const-const-type):
/* #const */
const debug = false;
Now anywhere in your code (also inside of nested functions) you can do the following:
if (debug) console.log("hello world");
Or in your case invalidate a complete block
if (debug) {
/* your code to remove */
}
If you set debug to false the Closure compiler can remove it because it knows you declared debug a constant a hence it won't change and the code will never be executed if gated with your debug variable.
Because the #define doesn't work in modules, I wrote a patch that can be run before compilation. It goes:
import { c } from '#artdeco/erte'
import { readFileSync, writeFileSync } from 'fs'
import { join } from 'path'
const [,,version] = process.argv
const PATH = join(__dirname, 'index.js')
let f = readFileSync(PATH, 'utf-8')
const isFree = version == '--free'
if (isFree) {
f = f.replace("\nimport isFree from './paid'", "\n// import isFree from './paid'")
f = f.replace("\n// import isFree from './free'", "\nimport isFree from './free'")
console.log('Set version to %s', c('free', 'red'))
} else {
f = f.replace("\n// import isFree from './paid'", "\nimport isFree from './paid'")
f = f.replace("\nimport isFree from './free'", "\n// import isFree from './free'")
console.log('Set version to %s', c('paid', 'magenta'))
}
writeFileSync(PATH, f)
Usage:
node ./src/version/patch --free
node ./src/version/patch --paid
And the actual ./src/version/index.js that's being patched:
// import isFree from './free'
import isFree from './paid'
With './free':
export default true
With './paid':
export default true
And based on that, you can export a variable from index.js:
export const free = isFree
So this was to allow compiling paid and free packages, but you could extend this code to adjust for debug/production version.
Still, this should be done with -D (#define) but apparently it's very difficult for a trillion-dollar company that Google is.

Categories

Resources