Skip to content

Commit 0d8cca1

Browse files
committed
Update weak_node_api to use injection
1 parent bf48ac8 commit 0d8cca1

File tree

4 files changed

+104
-90
lines changed

4 files changed

+104
-90
lines changed

packages/react-native-node-api-modules/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ android/build/
2020
/weak-node-api/build/
2121
/weak-node-api/*.xcframework
2222
/weak-node-api/*.android.node
23-
/weak-node-api/weak-node-api.cpp
23+
/weak-node-api/weak_node_api.cpp
24+
/weak-node-api/weak_node_api.hpp

packages/react-native-node-api-modules/scripts/copy-node-api-headers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import fs from "node:fs";
33
import path from "node:path";
44
import { include_dir as includeSourcePath } from "node-api-headers";
55

6-
const includeDestinationPath = path.join(__dirname, "../include");
6+
const includeDestinationPath = path.join(__dirname, "../weak-node-api/include");
77
assert(fs.existsSync(includeSourcePath), `Expected ${includeSourcePath}`);
88
console.log(`Copying ${includeSourcePath} to ${includeDestinationPath}`);
99
fs.cpSync(includeSourcePath, includeDestinationPath, { recursive: true });

packages/react-native-node-api-modules/scripts/generate-weak-node-api.ts

Lines changed: 92 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -79,71 +79,7 @@ type FunctionDecl = {
7979
fallbackReturnStatement: string;
8080
};
8181

82-
export function generateNodeApiFunctionStubBody({
83-
name,
84-
returnType,
85-
argumentTypes,
86-
libraryPath,
87-
fallbackReturnStatement,
88-
}: FunctionDecl) {
89-
const returnStatement =
90-
name === "napi_fatal_error"
91-
? "abort();"
92-
: returnType === "void"
93-
? ""
94-
: `return real_func(${argumentTypes
95-
.map((t, index) => `arg${index}`)
96-
.join(", ")}); // Call the real function`;
97-
return `
98-
static ${name}_t real_func = NULL;
99-
100-
if (!real_func) {
101-
void* handle = dlopen("${libraryPath}", RTLD_LAZY | RTLD_GLOBAL);
102-
if (!handle) {
103-
fprintf(stderr, "Failed to load ${libraryPath} while deferring ${name}: %s\\n", dlerror());
104-
${fallbackReturnStatement}
105-
}
106-
107-
real_func = (${name}_t)dlsym(handle, "${name}");
108-
if (!real_func) {
109-
fprintf(stderr, "Failed to find symbol while deferring ${name}: %s\\n", dlerror());
110-
${fallbackReturnStatement}
111-
}
112-
}
113-
114-
${returnStatement}
115-
`;
116-
}
117-
118-
export function generateNodeApiFunction(decl: FunctionDecl) {
119-
const { name, returnType, argumentTypes, fallbackReturnStatement } = decl;
120-
return `
121-
typedef ${returnType} (*${name}_t)(${argumentTypes.join(", ")});
122-
${returnType} ${name}(${argumentTypes
123-
.map((type, index) => `${type} arg${index}`)
124-
.join(", ")}) {
125-
fprintf(stdout, "Calling ${name} [weak-node-api]\\n");
126-
#ifdef NODE_API_REEXPORT
127-
${generateNodeApiFunctionStubBody(decl)}
128-
#else
129-
fprintf(stderr, "Returning generic error for ${name}\\n");
130-
${fallbackReturnStatement}
131-
#endif
132-
}`;
133-
}
134-
135-
/**
136-
* Generates source code for a version script for the given Node API version.
137-
* @param version
138-
*/
139-
export function generateFakeNodeApiSource(version: NodeApiVersion) {
140-
const lines = [
141-
"// This file is generated by react-native-node-api-modules",
142-
"#include <node_api.h>", // Node-API
143-
"#include <dlfcn.h>", // dlopen(), dlsym()
144-
"#include <stdio.h>", // fprintf()
145-
"#include <stdlib.h>", // abort()
146-
];
82+
export function getNodeApiFunctions(version: NodeApiVersion) {
14783
const root = getNodeApiHeaderAST(version);
14884
assert.equal(root.kind, "TranslationUnitDecl");
14985
assert(Array.isArray(root.inner));
@@ -154,6 +90,8 @@ export function generateFakeNodeApiSource(version: NodeApiVersion) {
15490
const runtimeSymbols = new Set(symbolsPerInterface.node_api_symbols);
15591
const allSymbols = new Set([...engineSymbols, ...runtimeSymbols]);
15692

93+
const nodeApiFunctions: FunctionDecl[] = [];
94+
15795
for (const node of root.inner) {
15896
const { name, kind } = node;
15997
if (kind === "FunctionDecl" && name && allSymbols.has(name)) {
@@ -182,21 +120,19 @@ export function generateFakeNodeApiSource(version: NodeApiVersion) {
182120
`Expected return type to be napi_status, got ${returnType}`
183121
);
184122

185-
lines.push(
186-
generateNodeApiFunction({
187-
name,
188-
returnType,
189-
argumentTypes: argumentTypes.split(",").map((arg) => arg.trim()),
190-
// Defer to the right library
191-
libraryPath: engineSymbols.has(name)
192-
? "libhermes.so"
193-
: "libnode-api-host.so",
194-
fallbackReturnStatement:
195-
returnType === "void"
196-
? "abort();"
197-
: "return napi_status::napi_generic_failure;",
198-
})
199-
);
123+
nodeApiFunctions.push({
124+
name,
125+
returnType,
126+
argumentTypes: argumentTypes.split(",").map((arg) => arg.trim()),
127+
// Defer to the right library
128+
libraryPath: engineSymbols.has(name)
129+
? "libhermes.so"
130+
: "libnode-api-host.so",
131+
fallbackReturnStatement:
132+
returnType === "void"
133+
? "abort();"
134+
: "return napi_status::napi_generic_failure;",
135+
});
200136
}
201137
}
202138
for (const knownSymbol of allSymbols) {
@@ -206,17 +142,87 @@ export function generateFakeNodeApiSource(version: NodeApiVersion) {
206142
);
207143
}
208144
}
209-
return lines.join("\n");
145+
146+
return nodeApiFunctions;
147+
}
148+
149+
/**
150+
* Generates source code for a version script for the given Node API version.
151+
*/
152+
export function generateHeader(nodeApiFunctions: FunctionDecl[]) {
153+
return [
154+
"// This file is generated by react-native-node-api-modules",
155+
"#include <node_api.h>", // Node-API
156+
"#include <stdio.h>", // fprintf()
157+
"#include <stdlib.h>", // abort()
158+
"namespace node_api::internal {",
159+
// Generate the struct of function pointers
160+
"struct NodeApiHost {",
161+
...nodeApiFunctions.map(
162+
({ returnType, name, argumentTypes }) =>
163+
`${returnType} (*${name})(${argumentTypes.join(", ")});`
164+
),
165+
"};",
166+
"void inject_host(const NodeApiHost& host);",
167+
"} // namespace node_api::internal",
168+
].join("\n");
169+
}
170+
171+
/**
172+
* Generates source code for a version script for the given Node API version.
173+
*/
174+
export function generateSource(nodeApiFunctions: FunctionDecl[]) {
175+
return [
176+
"// This file is generated by react-native-node-api-modules",
177+
"#include <weak_node_api.hpp>", // Generated header
178+
"namespace node_api::internal {",
179+
// Generate the struct of function pointers
180+
"NodeApiHost g_host;",
181+
"void inject_host(const NodeApiHost& host) {",
182+
" g_host = host;",
183+
"};",
184+
"} // namespace node_api::internal",
185+
"using node_api::internal::g_host;",
186+
// Generate function calling into the host
187+
...nodeApiFunctions.flatMap(({ returnType, name, argumentTypes }) => {
188+
return [
189+
`${returnType} ${name}(${argumentTypes
190+
.map((type, index) => `${type} arg${index}`)
191+
.join(", ")}) {`,
192+
`if (g_host.${name} == nullptr) {`,
193+
` fprintf(stderr, "Node-API function '${name}' called before it was injected!\\n");`,
194+
" abort();",
195+
"}",
196+
(returnType === "void" ? "" : "return ") +
197+
"g_host." +
198+
name +
199+
"(" +
200+
argumentTypes.map((_, index) => `arg${index}`).join(", ") +
201+
");",
202+
"};",
203+
];
204+
}),
205+
].join("\n");
210206
}
211207

212208
async function run() {
213-
const sourceCode = generateFakeNodeApiSource("v10");
214209
await fs.promises.mkdir(WEAK_NODE_API_PATH, { recursive: true });
215-
await fs.promises.writeFile(
216-
path.join(WEAK_NODE_API_PATH, "weak-node-api.cpp"),
217-
sourceCode,
218-
"utf-8"
210+
211+
const nodeApiFunctions = getNodeApiFunctions("v10");
212+
213+
const header = generateHeader(nodeApiFunctions);
214+
const headerPath = path.join(
215+
WEAK_NODE_API_PATH,
216+
"include",
217+
"weak_node_api.hpp"
219218
);
219+
await fs.promises.writeFile(headerPath, header, "utf-8");
220+
cp.spawnSync("clang-format", ["-i", headerPath], { stdio: "inherit" });
221+
222+
const source = generateSource(nodeApiFunctions);
223+
const sourcePath = path.join(WEAK_NODE_API_PATH, "weak_node_api.cpp");
224+
await fs.promises.writeFile(sourcePath, source, "utf-8");
225+
cp.spawnSync("clang-format", ["-i", sourcePath], { stdio: "inherit" });
220226
}
221227

222228
run().catch((err) => {
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
cmake_minimum_required(VERSION 3.15)
22
project(weak-node-api)
33

4-
add_library(${PROJECT_NAME} SHARED weak-node-api.cpp ${CMAKE_JS_SRC})
5-
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/../include)
4+
add_library(${PROJECT_NAME} SHARED
5+
weak_node_api.cpp
6+
${CMAKE_JS_SRC}
7+
)
8+
9+
target_include_directories(${PROJECT_NAME}
10+
PUBLIC
11+
${CMAKE_CURRENT_SOURCE_DIR}/include
12+
)
613
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
714
target_compile_definitions(${PROJECT_NAME} PRIVATE NAPI_VERSION=8)

0 commit comments

Comments
 (0)