Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/host/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ add_library(node-api-host SHARED
../cpp/Logger.cpp
../cpp/CxxNodeApiHostModule.cpp
../cpp/WeakNodeApiInjector.cpp
../cpp/node_api_impl.cpp
../cpp/node_api_impl.hpp
)

target_include_directories(node-api-host PRIVATE
Expand Down
108 changes: 108 additions & 0 deletions packages/host/cpp/node_api_impl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "node_api_impl.hpp"
#include <string>

auto ArrayType = napi_uint8_array;

napi_status NAPI_CDECL callstack::nodeapihost::napi_create_buffer(
napi_env env, size_t length, void** data, napi_value* result) {
napi_value buffer;
const auto status = napi_create_arraybuffer(env, length, data, &buffer);
if (status != napi_ok) {
return status;
}

return napi_create_typedarray(env, ArrayType, length, buffer, 0, result);
}

napi_status NAPI_CDECL callstack::nodeapihost::napi_create_buffer_copy(
napi_env env,
size_t length,
const void* data,
void** result_data,
napi_value* result) {
if (!length || !data || !result) {
return napi_invalid_arg;
}

void* buffer = nullptr;
if (const auto status = ::napi_create_buffer(env, length, &buffer, result);
status != napi_ok) {
return status;
}

std::memcpy(buffer, data, length);
return napi_ok;
}

napi_status callstack::nodeapihost::napi_is_buffer(
napi_env env, napi_value value, bool* result) {
if (!result) {
return napi_invalid_arg;
}

if (!value) {
*result = false;
return napi_ok;
}

napi_valuetype type{};
if (const auto status = napi_typeof(env, value, &type); status != napi_ok) {
return status;
}

if (type != napi_object && type != napi_external) {
*result = false;
return napi_ok;
}

auto isArrayBuffer{false};
if (const auto status = napi_is_arraybuffer(env, value, &isArrayBuffer);
status != napi_ok) {
return status;
}
auto isTypedArray{false};
if (const auto status = napi_is_typedarray(env, value, &isTypedArray);
status != napi_ok) {
return status;
}

*result = isArrayBuffer || isTypedArray;
return napi_ok;
}

napi_status callstack::nodeapihost::napi_get_buffer_info(
napi_env env, napi_value value, void** data, size_t* length) {
if (!data || !length) {
return napi_invalid_arg;
}
*data = nullptr;
*length = 0;
if (!value) {
return napi_ok;
}

auto isArrayBuffer{false};
if (const auto status = napi_is_arraybuffer(env, value, &isArrayBuffer);
status == napi_ok && isArrayBuffer) {
return napi_get_arraybuffer_info(env, value, data, length);
}

auto isTypedArray{false};
if (const auto status = napi_is_typedarray(env, value, &isTypedArray);
status == napi_ok && isTypedArray) {
return napi_get_typedarray_info(
env, value, &ArrayType, length, data, nullptr, nullptr);
}

return napi_ok;
}

napi_status callstack::nodeapihost::napi_create_external_buffer(napi_env env,
size_t length,
void* data,
node_api_basic_finalize basic_finalize_cb,
void* finalize_hint,
napi_value* result) {
return napi_create_external_arraybuffer(
env, data, length, basic_finalize_cb, finalize_hint, result);
}
25 changes: 25 additions & 0 deletions packages/host/cpp/node_api_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "node_api.h"

namespace callstack::nodeapihost {
napi_status napi_create_buffer(
napi_env env, size_t length, void** data, napi_value* result);

napi_status napi_create_buffer_copy(napi_env env,
size_t length,
const void* data,
void** result_data,
napi_value* result);

napi_status napi_is_buffer(napi_env env, napi_value value, bool* result);

napi_status napi_get_buffer_info(
napi_env env, napi_value value, void** data, size_t* length);

napi_status napi_create_external_buffer(napi_env env,
size_t length,
void* data,
node_api_basic_finalize basic_finalize_cb,
void* finalize_hint,
napi_value* result);

} // namespace callstack::nodeapihost
17 changes: 15 additions & 2 deletions packages/host/scripts/generate-weak-node-api-injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import { FunctionDecl, getNodeApiFunctions } from "./node-api-functions";

export const CPP_SOURCE_PATH = path.join(__dirname, "../cpp");

// TODO: Remove when all runtime Node API functions are implemented
const implementedRuntimeFunctions = [
"napi_create_buffer",
"napi_create_buffer_copy",
"napi_is_buffer",
"napi_get_buffer_info",
"napi_create_external_buffer",
];

/**
* Generates source code which injects the Node API functions from the host.
*/
Expand All @@ -15,7 +24,8 @@ export function generateSource(functions: FunctionDecl[]) {
#include <Logger.hpp>
#include <dlfcn.h>
#include <weak_node_api.hpp>

#include <node_api_impl.hpp>

#if defined(__APPLE__)
#define WEAK_NODE_API_LIBRARY_NAME "@rpath/weak-node-api.framework/weak-node-api"
#elif defined(__ANDROID__)
Expand Down Expand Up @@ -43,7 +53,10 @@ export function generateSource(functions: FunctionDecl[]) {
log_debug("Injecting WeakNodeApiHost");
inject_weak_node_api_host(WeakNodeApiHost {
${functions
.filter(({ kind }) => kind === "engine")
.filter(
({ kind, name }) =>
kind === "engine" || implementedRuntimeFunctions.includes(name)
)
.flatMap(({ name }) => `.${name} = ${name},`)
.join("\n")}
});
Expand Down
5 changes: 4 additions & 1 deletion packages/node-addon-examples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ module.exports = {
"5-async-work": {
// TODO: This crashes (SIGABRT)
// "async_work_thread_safe_function": () => require("./examples/5-async-work/async_work_thread_safe_function/napi/index.js"),
}
},
"tests": {
"buffers": () => require("./tests/buffers/addon.js"),
},
};
2 changes: 1 addition & 1 deletion packages/node-addon-examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"scripts": {
"copy-examples": "tsx scripts/copy-examples.mts",
"gyp-to-cmake": "gyp-to-cmake ./examples",
"gyp-to-cmake": "gyp-to-cmake .",
"build": "tsx scripts/build-examples.mts",
"copy-and-build": "npm run copy-examples && npm run gyp-to-cmake && npm run build",
"verify": "tsx scripts/verify-prebuilds.mts",
Expand Down
10 changes: 8 additions & 2 deletions packages/node-addon-examples/scripts/cmake-projects.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ import { readdirSync, statSync } from "node:fs";
import path from "node:path";

export const EXAMPLES_DIR = path.resolve(import.meta.dirname, "../examples");
export const TESTS_DIR = path.resolve(import.meta.dirname, "../tests");
export const DIRS = [EXAMPLES_DIR, TESTS_DIR];

export function findCMakeProjects(dir = EXAMPLES_DIR): string[] {
export function findCMakeProjectsRecursively(dir): string[] {
let results: string[] = [];
const files = readdirSync(dir);

for (const file of files) {
const fullPath = path.join(dir, file);
if (statSync(fullPath).isDirectory()) {
results = results.concat(findCMakeProjects(fullPath));
results = results.concat(findCMakeProjectsRecursively(fullPath));
} else if (file === "CMakeLists.txt") {
results.push(dir);
}
}

return results;
}

export function findCMakeProjects(): string[] {
return DIRS.flatMap(findCMakeProjectsRecursively);
}
1 change: 1 addition & 0 deletions packages/node-addon-examples/tests/buffers/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
15 changes: 15 additions & 0 deletions packages/node-addon-examples/tests/buffers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.15)
project(tests-buffers)

add_compile_definitions(NAPI_VERSION=8)

add_library(addon SHARED addon.c ${CMAKE_JS_SRC})
set_target_properties(addon PROPERTIES PREFIX "" SUFFIX ".node")
target_include_directories(addon PRIVATE ${CMAKE_JS_INC})
target_link_libraries(addon PRIVATE ${CMAKE_JS_LIB})
target_compile_features(addon PRIVATE cxx_std_17)

if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET)
# Generate node.lib
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
endif()
Loading
Loading