Skip to content

Commit 5cbffb8

Browse files
authored
Implementation of V1 buffer runtime functions (#168)
* Added node_api_impl files * Added test addon for buffers * Injecting runtime functions * Adjusting scripts * Buffer test updated * Cleanup * Added eslint-disable for test addon * Removed debug lines * Moved .gitignore up * Renamed files to RuntimeNodeApi * Removed gc related tests * Cleaned up file * Renamed to IMPLEMENTED_RUNTIME_FUNCTIONS * Added reference to the issue
1 parent b771a27 commit 5cbffb8

File tree

13 files changed

+505
-6
lines changed

13 files changed

+505
-6
lines changed

packages/host/android/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ add_library(node-api-host SHARED
2020
../cpp/Logger.cpp
2121
../cpp/CxxNodeApiHostModule.cpp
2222
../cpp/WeakNodeApiInjector.cpp
23+
../cpp/RuntimeNodeApi.cpp
24+
../cpp/RuntimeNodeApi.hpp
2325
)
2426

2527
target_include_directories(node-api-host PRIVATE

packages/host/cpp/RuntimeNodeApi.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#include "RuntimeNodeApi.hpp"
2+
#include <string>
3+
4+
auto ArrayType = napi_uint8_array;
5+
6+
napi_status NAPI_CDECL callstack::nodeapihost::napi_create_buffer(
7+
napi_env env, size_t length, void** data, napi_value* result) {
8+
napi_value buffer;
9+
const auto status = napi_create_arraybuffer(env, length, data, &buffer);
10+
if (status != napi_ok) {
11+
return status;
12+
}
13+
14+
// Warning: The returned data structure does not fully align with the
15+
// characteristics of a Buffer.
16+
// @see
17+
// https://github.com/callstackincubator/react-native-node-api/issues/171
18+
return napi_create_typedarray(env, ArrayType, length, buffer, 0, result);
19+
}
20+
21+
napi_status NAPI_CDECL callstack::nodeapihost::napi_create_buffer_copy(
22+
napi_env env,
23+
size_t length,
24+
const void* data,
25+
void** result_data,
26+
napi_value* result) {
27+
if (!length || !data || !result) {
28+
return napi_invalid_arg;
29+
}
30+
31+
void* buffer = nullptr;
32+
if (const auto status = ::napi_create_buffer(env, length, &buffer, result);
33+
status != napi_ok) {
34+
return status;
35+
}
36+
37+
std::memcpy(buffer, data, length);
38+
return napi_ok;
39+
}
40+
41+
napi_status callstack::nodeapihost::napi_is_buffer(
42+
napi_env env, napi_value value, bool* result) {
43+
if (!result) {
44+
return napi_invalid_arg;
45+
}
46+
47+
if (!value) {
48+
*result = false;
49+
return napi_ok;
50+
}
51+
52+
napi_valuetype type{};
53+
if (const auto status = napi_typeof(env, value, &type); status != napi_ok) {
54+
return status;
55+
}
56+
57+
if (type != napi_object && type != napi_external) {
58+
*result = false;
59+
return napi_ok;
60+
}
61+
62+
auto isArrayBuffer{false};
63+
if (const auto status = napi_is_arraybuffer(env, value, &isArrayBuffer);
64+
status != napi_ok) {
65+
return status;
66+
}
67+
auto isTypedArray{false};
68+
if (const auto status = napi_is_typedarray(env, value, &isTypedArray);
69+
status != napi_ok) {
70+
return status;
71+
}
72+
73+
*result = isArrayBuffer || isTypedArray;
74+
return napi_ok;
75+
}
76+
77+
napi_status callstack::nodeapihost::napi_get_buffer_info(
78+
napi_env env, napi_value value, void** data, size_t* length) {
79+
if (!data || !length) {
80+
return napi_invalid_arg;
81+
}
82+
*data = nullptr;
83+
*length = 0;
84+
if (!value) {
85+
return napi_ok;
86+
}
87+
88+
auto isArrayBuffer{false};
89+
if (const auto status = napi_is_arraybuffer(env, value, &isArrayBuffer);
90+
status == napi_ok && isArrayBuffer) {
91+
return napi_get_arraybuffer_info(env, value, data, length);
92+
}
93+
94+
auto isTypedArray{false};
95+
if (const auto status = napi_is_typedarray(env, value, &isTypedArray);
96+
status == napi_ok && isTypedArray) {
97+
return napi_get_typedarray_info(
98+
env, value, &ArrayType, length, data, nullptr, nullptr);
99+
}
100+
101+
return napi_ok;
102+
}
103+
104+
napi_status callstack::nodeapihost::napi_create_external_buffer(napi_env env,
105+
size_t length,
106+
void* data,
107+
node_api_basic_finalize basic_finalize_cb,
108+
void* finalize_hint,
109+
napi_value* result) {
110+
return napi_create_external_arraybuffer(
111+
env, data, length, basic_finalize_cb, finalize_hint, result);
112+
}

packages/host/cpp/RuntimeNodeApi.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "node_api.h"
2+
3+
namespace callstack::nodeapihost {
4+
napi_status napi_create_buffer(
5+
napi_env env, size_t length, void** data, napi_value* result);
6+
7+
napi_status napi_create_buffer_copy(napi_env env,
8+
size_t length,
9+
const void* data,
10+
void** result_data,
11+
napi_value* result);
12+
13+
napi_status napi_is_buffer(napi_env env, napi_value value, bool* result);
14+
15+
napi_status napi_get_buffer_info(
16+
napi_env env, napi_value value, void** data, size_t* length);
17+
18+
napi_status napi_create_external_buffer(napi_env env,
19+
size_t length,
20+
void* data,
21+
node_api_basic_finalize basic_finalize_cb,
22+
void* finalize_hint,
23+
napi_value* result);
24+
25+
} // namespace callstack::nodeapihost

packages/host/scripts/generate-weak-node-api-injector.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ import { FunctionDecl, getNodeApiFunctions } from "./node-api-functions";
66

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

9+
// TODO: Remove when all runtime Node API functions are implemented
10+
const IMPLEMENTED_RUNTIME_FUNCTIONS = [
11+
"napi_create_buffer",
12+
"napi_create_buffer_copy",
13+
"napi_is_buffer",
14+
"napi_get_buffer_info",
15+
"napi_create_external_buffer",
16+
];
17+
918
/**
1019
* Generates source code which injects the Node API functions from the host.
1120
*/
@@ -15,7 +24,8 @@ export function generateSource(functions: FunctionDecl[]) {
1524
#include <Logger.hpp>
1625
#include <dlfcn.h>
1726
#include <weak_node_api.hpp>
18-
27+
#include <RuntimeNodeApi.hpp>
28+
1929
#if defined(__APPLE__)
2030
#define WEAK_NODE_API_LIBRARY_NAME "@rpath/weak-node-api.framework/weak-node-api"
2131
#elif defined(__ANDROID__)
@@ -43,7 +53,10 @@ export function generateSource(functions: FunctionDecl[]) {
4353
log_debug("Injecting WeakNodeApiHost");
4454
inject_weak_node_api_host(WeakNodeApiHost {
4555
${functions
46-
.filter(({ kind }) => kind === "engine")
56+
.filter(
57+
({ kind, name }) =>
58+
kind === "engine" || IMPLEMENTED_RUNTIME_FUNCTIONS.includes(name)
59+
)
4760
.flatMap(({ name }) => `.${name} = ${name},`)
4861
.join("\n")}
4962
});

packages/node-addon-examples/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@ module.exports = {
1414
"5-async-work": {
1515
// TODO: This crashes (SIGABRT)
1616
// "async_work_thread_safe_function": () => require("./examples/5-async-work/async_work_thread_safe_function/napi/index.js"),
17-
}
17+
},
18+
"tests": {
19+
"buffers": () => require("./tests/buffers/addon.js"),
20+
},
1821
};

packages/node-addon-examples/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"scripts": {
1212
"copy-examples": "tsx scripts/copy-examples.mts",
13-
"gyp-to-cmake": "gyp-to-cmake ./examples",
13+
"gyp-to-cmake": "gyp-to-cmake .",
1414
"build": "tsx scripts/build-examples.mts",
1515
"copy-and-build": "npm run copy-examples && npm run gyp-to-cmake && npm run build",
1616
"verify": "tsx scripts/verify-prebuilds.mts",

packages/node-addon-examples/scripts/cmake-projects.mts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@ import { readdirSync, statSync } from "node:fs";
22
import path from "node:path";
33

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

6-
export function findCMakeProjects(dir = EXAMPLES_DIR): string[] {
8+
export function findCMakeProjectsRecursively(dir): string[] {
79
let results: string[] = [];
810
const files = readdirSync(dir);
911

1012
for (const file of files) {
1113
const fullPath = path.join(dir, file);
1214
if (statSync(fullPath).isDirectory()) {
13-
results = results.concat(findCMakeProjects(fullPath));
15+
results = results.concat(findCMakeProjectsRecursively(fullPath));
1416
} else if (file === "CMakeLists.txt") {
1517
results.push(dir);
1618
}
1719
}
1820

1921
return results;
2022
}
23+
24+
export function findCMakeProjects(): string[] {
25+
return DIRS.flatMap(findCMakeProjectsRecursively);
26+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(tests-buffers)
3+
4+
add_compile_definitions(NAPI_VERSION=8)
5+
6+
add_library(addon SHARED addon.c ${CMAKE_JS_SRC})
7+
set_target_properties(addon PROPERTIES PREFIX "" SUFFIX ".node")
8+
target_include_directories(addon PRIVATE ${CMAKE_JS_INC})
9+
target_link_libraries(addon PRIVATE ${CMAKE_JS_LIB})
10+
target_compile_features(addon PRIVATE cxx_std_17)
11+
12+
if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET)
13+
# Generate node.lib
14+
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
15+
endif()

0 commit comments

Comments
 (0)