@@ -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
212208async 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
222228run ( ) . catch ( ( err ) => {
0 commit comments