3

I need some help to solve my problem. Basically, I want to "call" javascript function from C++ addon, I've googled about it and found something like this.

Here is my .js file

const myaddon = process.binding('myaddon');

function Foo() {
    console.log("wooo");
}
myaddon.getfoo(Foo);
myaddon.callfoo(); // just to test if C++ Call js function work

Here is my .cpp

Local<Function> pfOnScriptInit;
Local<Object> globel;
void test(const FunctionCallbackInfo<Value>& args)
{
    pfOnScriptInit = Local<Function>::Cast(args[0]);
}
void call(const FunctionCallbackInfo<Value>& args)
{
    pfOnScriptInit->Call(globel, 0, nullptr);
}
void initAll(Local<Object> target, Local<Value> unused, Local<Context> context, void* priv) 
{
    node::Environment* env = node::Environment::GetCurrent(context);

    globel = env->context()->Global();
    env->SetMethod(target, "getfoo", test); 
    env->SetMethod(target, "callfoo", call);
}
NODE_MODULE_CONTEXT_AWARE_BUILTIN(fivemp, node::fivemp::initAll)

I got this error when calling myaddon.callfoo(); function:

TypeError: Illegal Invocation

1 Answer 1

5

This question is kinda old, but it's a common question, which has been asked a couple times. These answers don't have a proper answer in my opinion. Hence, here I go!

Before I answer your question, let me say that you should use Native Abstractions for Node.js to write a Node.js addon. This will make your addon portable across Node.js versions.

Also, you should take a look at the official addon documentation here, and to the repo with Nan examples.

That said, your code has a big and common mistake: you are confusingly using a local object as persistent. In other words, declaring a Local<Function> out of a function scope is plain wrong. These two cannot be declared anywhere but in function scope:

Local<Function> pfOnScriptInit;
Local<Object> globel;

And of course, you cannot assign a Local<Function> like this:

pfOnScriptInit = Local<Function>::Cast(args[0]);

This local object is going to be reclaimed by v8's garbage collector - it won't exist anymore. So, instead you need to use a persistent type. Here is an example, written using Nan, which might show you how it's done:

#include <nan.h>

static Nan::CopyablePersistentTraits<v8::Function>::CopyablePersistent _cb;

static void SetCallback(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  _cb = Nan::Persistent<v8::Function>(info[0].As<v8::Function>());
}

static void RunCallback(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  Nan::MakeCallback(Nan::GetCurrentContext()->Global(), Nan::New(_cb), 0, 0);
  _cb.Reset();
}

void RunThisCallback(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  v8::Local<v8::Function> cb = info[0].As<v8::Function>();
  Nan::MakeCallback(Nan::GetCurrentContext()->Global(), cb, 0, 0);
}

static void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
  Nan::SetMethod(exports, "setCallback", SetCallback);
  Nan::SetMethod(exports, "call", RunCallback);
  Nan::SetMethod(exports, "callThis", RunThisCallback);
}

NODE_MODULE(addon, Init)

And here's the JS side:

var addon = require('bindings')('addon');

function foo() {
        console.log("woohoo!");
}

addon.setCallback(foo);
addon.call();
addon.callThis(() => console.log("this too!"));

For more information about v8's local vs persistent, you should take a look at v8's documentation. There is a also a good book about Node.js addons by Scott Frees, here https://scottfrees.com/ebooks/nodecpp/.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.