Function Binding
Arguments Handling
Suppose you need to write a C function to concat two JavaScript string:
ts
export declare function concatString (str1: string, str2: string): string
export declare function concatString (str1: string, str2: string): string
js
Module.onRuntimeInitialized = function () {
const result = Module.emnapiExports.concatString('Hello ', '世界')
console.log(result) // 'Hello 世界'
}
Module.onRuntimeInitialized = function () {
const result = Module.emnapiExports.concatString('Hello ', '世界')
console.log(result) // 'Hello 世界'
}
Node-API Implementation
c
#include <node_api.h>
#include <string.h>
#include <stdlib.h>
#define NAPI_CALL(env, the_call) /* ... */
static napi_value js_concat_string(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
if (argc < 2) {
napi_throw_type_error(env, NULL, "Wrong number of arguments");
return NULL;
}
napi_valuetype valuetype0, valuetype1;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
if (valuetype0 != napi_string || valuetype1 != napi_string) {
napi_throw_type_error(env, NULL, "Wrong arguments");
return NULL;
}
size_t len1 = 0;
size_t len2 = 0;
NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], NULL, 0, &len1));
NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], NULL, 0, &len2));
size_t total_buffer_size = len1 + len2 + 1;
char* str = (char*) malloc(total_buffer_size);
if (str == NULL) {
napi_throw_error(env, NULL, "malloc failed");
return NULL;
}
napi_status status = napi_get_value_string_utf8(env, args[0],
str, total_buffer_size, &len1);
if (status != napi_ok) {
free(str);
NAPI_CALL(env, status);
}
status = napi_get_value_string_utf8(env, args[1],
str + len1, total_buffer_size - len1, &len2);
if (status != napi_ok) {
free(str);
NAPI_CALL(env, status);
}
*(str + len1 + len2) = '\0';
napi_value ret;
status = napi_create_string_utf8(env, str, len1 + len2, &ret);
free(str);
NAPI_CALL(env, status);
return ret;
}
NAPI_MODULE_INIT() {
napi_value concat_string_fn;
NAPI_CALL(env, napi_create_function(env, "concatString", NAPI_AUTO_LENGTH,
js_concat_string, NULL, &concat_string_fn));
NAPI_CALL(env, napi_set_named_property(env, exports,
"concatString", concat_string_fn));
return exports;
}
#include <node_api.h>
#include <string.h>
#include <stdlib.h>
#define NAPI_CALL(env, the_call) /* ... */
static napi_value js_concat_string(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
if (argc < 2) {
napi_throw_type_error(env, NULL, "Wrong number of arguments");
return NULL;
}
napi_valuetype valuetype0, valuetype1;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
if (valuetype0 != napi_string || valuetype1 != napi_string) {
napi_throw_type_error(env, NULL, "Wrong arguments");
return NULL;
}
size_t len1 = 0;
size_t len2 = 0;
NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], NULL, 0, &len1));
NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], NULL, 0, &len2));
size_t total_buffer_size = len1 + len2 + 1;
char* str = (char*) malloc(total_buffer_size);
if (str == NULL) {
napi_throw_error(env, NULL, "malloc failed");
return NULL;
}
napi_status status = napi_get_value_string_utf8(env, args[0],
str, total_buffer_size, &len1);
if (status != napi_ok) {
free(str);
NAPI_CALL(env, status);
}
status = napi_get_value_string_utf8(env, args[1],
str + len1, total_buffer_size - len1, &len2);
if (status != napi_ok) {
free(str);
NAPI_CALL(env, status);
}
*(str + len1 + len2) = '\0';
napi_value ret;
status = napi_create_string_utf8(env, str, len1 + len2, &ret);
free(str);
NAPI_CALL(env, status);
return ret;
}
NAPI_MODULE_INIT() {
napi_value concat_string_fn;
NAPI_CALL(env, napi_create_function(env, "concatString", NAPI_AUTO_LENGTH,
js_concat_string, NULL, &concat_string_fn));
NAPI_CALL(env, napi_set_named_property(env, exports,
"concatString", concat_string_fn));
return exports;
}
node-addon-api Implementation
WARNING
You can not use node-addon-api if the runtime does not support FinalizationRegistry
and WeakRef
.
cpp
#include <napi.h>
Napi::Value JsConcatString(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 2) {
Napi::TypeError e = Napi::TypeError::New(env, "Wrong number of arguments");
NAPI_THROW(e, Napi::Value());
}
if (!info[0].IsString() || !info[1].IsString()) {
Napi::TypeError e = Napi::TypeError::New(env, "Wrong arguments");
NAPI_THROW(e, Napi::Value());
}
std::string str1 = info[0].As<Napi::String>().Utf8Value();
std::string str2 = info[1].As<Napi::String>().Utf8Value();
std::string result = str1 + str2;
return Napi::String::New(env, result);
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "concatString"),
Napi::Function::New(env, JsConcatString, "concatString"));
return exports;
}
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
#include <napi.h>
Napi::Value JsConcatString(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 2) {
Napi::TypeError e = Napi::TypeError::New(env, "Wrong number of arguments");
NAPI_THROW(e, Napi::Value());
}
if (!info[0].IsString() || !info[1].IsString()) {
Napi::TypeError e = Napi::TypeError::New(env, "Wrong arguments");
NAPI_THROW(e, Napi::Value());
}
std::string str1 = info[0].As<Napi::String>().Utf8Value();
std::string str2 = info[1].As<Napi::String>().Utf8Value();
std::string result = str1 + str2;
return Napi::String::New(env, result);
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "concatString"),
Napi::Function::New(env, JsConcatString, "concatString"));
return exports;
}
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
Passing Callback
Let's change the example above, passing a JavaScript callback to receive the concat result.
ts
export declare function concatString (
str1: string,
str2: string,
callback: (result: string) => void
): void
export declare function concatString (
str1: string,
str2: string,
callback: (result: string) => void
): void
js
Module.onRuntimeInitialized = function () {
Module.emnapiExports.concatString('Hello ', '世界', (result) => {
console.log(result) // 'Hello 世界'
})
}
Module.onRuntimeInitialized = function () {
Module.emnapiExports.concatString('Hello ', '世界', (result) => {
console.log(result) // 'Hello 世界'
})
}
Node-API Implementation
c
static napi_value js_concat_string(napi_env env, napi_callback_info info) {
size_t argc = 3;
napi_value args[3];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
if (argc < 3) {
napi_throw_type_error(env, NULL, "Wrong number of arguments");
return NULL;
}
napi_valuetype valuetype0, valuetype1, valuetype2;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
if (valuetype0 != napi_string || valuetype1 != napi_string || valuetype2 != napi_function) {
napi_throw_type_error(env, NULL, "Wrong arguments");
return NULL;
}
size_t len1 = 0;
size_t len2 = 0;
NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], NULL, 0, &len1));
NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], NULL, 0, &len2));
size_t total_buffer_size = len1 + len2 + 1;
char* str = (char*) malloc(total_buffer_size);
if (str == NULL) {
napi_throw_error(env, NULL, "malloc failed");
return NULL;
}
napi_status status = napi_get_value_string_utf8(env, args[0],
str, total_buffer_size, &len1);
if (status != napi_ok) {
free(str);
NAPI_CALL(env, status);
}
status = napi_get_value_string_utf8(env, args[1],
str + len1, total_buffer_size - len1, &len2);
if (status != napi_ok) {
free(str);
NAPI_CALL(env, status);
}
*(str + len1 + len2) = '\0';
napi_value ret;
status = napi_create_string_utf8(env, str, len1 + len2, &ret);
free(str);
NAPI_CALL(env, status);
napi_value undefined;
NAPI_CALL(env, napi_get_undefined(env, &undefined));
napi_value ignored;
NAPI_CALL(env, napi_call_function(env, undefined, args[2], 1, &ret, &ignored));
return undefined;
}
static napi_value js_concat_string(napi_env env, napi_callback_info info) {
size_t argc = 3;
napi_value args[3];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
if (argc < 3) {
napi_throw_type_error(env, NULL, "Wrong number of arguments");
return NULL;
}
napi_valuetype valuetype0, valuetype1, valuetype2;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
if (valuetype0 != napi_string || valuetype1 != napi_string || valuetype2 != napi_function) {
napi_throw_type_error(env, NULL, "Wrong arguments");
return NULL;
}
size_t len1 = 0;
size_t len2 = 0;
NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], NULL, 0, &len1));
NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], NULL, 0, &len2));
size_t total_buffer_size = len1 + len2 + 1;
char* str = (char*) malloc(total_buffer_size);
if (str == NULL) {
napi_throw_error(env, NULL, "malloc failed");
return NULL;
}
napi_status status = napi_get_value_string_utf8(env, args[0],
str, total_buffer_size, &len1);
if (status != napi_ok) {
free(str);
NAPI_CALL(env, status);
}
status = napi_get_value_string_utf8(env, args[1],
str + len1, total_buffer_size - len1, &len2);
if (status != napi_ok) {
free(str);
NAPI_CALL(env, status);
}
*(str + len1 + len2) = '\0';
napi_value ret;
status = napi_create_string_utf8(env, str, len1 + len2, &ret);
free(str);
NAPI_CALL(env, status);
napi_value undefined;
NAPI_CALL(env, napi_get_undefined(env, &undefined));
napi_value ignored;
NAPI_CALL(env, napi_call_function(env, undefined, args[2], 1, &ret, &ignored));
return undefined;
}
node-addon-api Implementation
WARNING
You can not use node-addon-api if the runtime does not support FinalizationRegistry
and WeakRef
.
cpp
Napi::Value JsConcatString(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 3) {
Napi::TypeError e = Napi::TypeError::New(env, "Wrong number of arguments");
NAPI_THROW(e, Napi::Value());
}
if (!info[0].IsString() || !info[1].IsString() || !info[2].IsFunction()) {
Napi::TypeError e = Napi::TypeError::New(env, "Wrong arguments");
NAPI_THROW(e, Napi::Value());
}
Napi::Value undefined = env.Undefined();
std::string str1 = info[0].As<Napi::String>().Utf8Value();
std::string str2 = info[1].As<Napi::String>().Utf8Value();
std::string result = str1 + str2;
info[2].As<Napi::Function>().Call(undefined, { Napi::String::New(env, result) })
return undefined;
}
Napi::Value JsConcatString(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 3) {
Napi::TypeError e = Napi::TypeError::New(env, "Wrong number of arguments");
NAPI_THROW(e, Napi::Value());
}
if (!info[0].IsString() || !info[1].IsString() || !info[2].IsFunction()) {
Napi::TypeError e = Napi::TypeError::New(env, "Wrong arguments");
NAPI_THROW(e, Napi::Value());
}
Napi::Value undefined = env.Undefined();
std::string str1 = info[0].As<Napi::String>().Utf8Value();
std::string str2 = info[1].As<Napi::String>().Utf8Value();
std::string result = str1 + str2;
info[2].As<Napi::Function>().Call(undefined, { Napi::String::New(env, result) })
return undefined;
}