@@ -9,92 +9,114 @@ import {
99
1010export const OUTPUT_PATH = path . join ( import . meta. dirname , "../generated" ) ;
1111
12+ type GenerateFileOptions = {
13+ functions : FunctionDecl [ ] ;
14+ fileName : string ;
15+ generator : ( functions : FunctionDecl [ ] ) => string ;
16+ } ;
17+
18+ async function generateFile ( {
19+ functions,
20+ fileName,
21+ generator,
22+ } : GenerateFileOptions ) {
23+ const output = generator ( functions ) ;
24+ const outputPath = path . join ( OUTPUT_PATH , fileName ) ;
25+ await fs . promises . writeFile ( outputPath , output , "utf-8" ) ;
26+ cp . spawnSync ( "clang-format" , [ "-i" , outputPath ] , { stdio : "inherit" } ) ;
27+ }
28+
29+ async function run ( ) {
30+ await fs . promises . mkdir ( OUTPUT_PATH , { recursive : true } ) ;
31+
32+ const functions = getNodeApiFunctions ( ) ;
33+ await generateFile ( {
34+ functions,
35+ fileName : "weak_node_api.hpp" ,
36+ generator : generateHeader ,
37+ } ) ;
38+ await generateFile ( {
39+ functions,
40+ fileName : "weak_node_api.cpp" ,
41+ generator : generateSource ,
42+ } ) ;
43+ }
44+
45+ export function generateFunctionDecl ( {
46+ returnType,
47+ name,
48+ argumentTypes,
49+ } : FunctionDecl ) {
50+ return `${ returnType } (*${ name } )(${ argumentTypes . join ( ", " ) } );` ;
51+ }
52+
1253/**
1354 * Generates source code for a version script for the given Node API version.
1455 */
1556export function generateHeader ( functions : FunctionDecl [ ] ) {
16- return [
17- " // This file is generated by react-native-node-api" ,
18- " #include <node_api.h>" , // Node-API
19- " #include <stdio.h>" , // fprintf()
20- " #include <stdlib.h>" , // abort()
21- "" ,
57+ return `
58+ // This file is generated by react-native-node-api
59+ #include <node_api.h> // Node-API
60+ #include <stdio.h> // fprintf()
61+ #include <stdlib.h> // abort()
62+
2263 // Ideally we would have just used NAPI_NO_RETURN, but
2364 // __declspec(noreturn) (when building with Microsoft Visual C++) cannot be used on members of a struct
2465 // TODO: If we targeted C++23 we could use std::unreachable()
25- "#if defined(__GNUC__)" ,
26- "#define WEAK_NODE_API_UNREACHABLE __builtin_unreachable();" ,
27- "#else" ,
28- "#define WEAK_NODE_API_UNREACHABLE __assume(0);" ,
29- "#endif" ,
30- "" ,
66+
67+ #if defined(__GNUC__)
68+ #define WEAK_NODE_API_UNREACHABLE __builtin_unreachable();
69+ #else
70+ #define WEAK_NODE_API_UNREACHABLE __assume(0);
71+ #endif
72+
3173 // Generate the struct of function pointers
32- "struct WeakNodeApiHost {" ,
33- ...functions . map ( ( { returnType, name, argumentTypes } ) =>
34- [
35- returnType ,
36- // Signature
37- `(*${ name } )(${ argumentTypes . join ( ", " ) } );` ,
38- ] . join ( " " ) ,
39- ) ,
40- "};" ,
41- "typedef void(*InjectHostFunction)(const WeakNodeApiHost&);" ,
42- `extern "C" void inject_weak_node_api_host(const WeakNodeApiHost& host);` ,
43- ] . join ( "\n" ) ;
74+ struct WeakNodeApiHost {
75+ ${ functions . map ( generateFunctionDecl ) . join ( "\n" ) }
76+ };
77+ typedef void(*InjectHostFunction)(const WeakNodeApiHost&);
78+ extern "C" void inject_weak_node_api_host(const WeakNodeApiHost& host);
79+ ` ;
80+ }
81+
82+ function generateFunctionImpl ( {
83+ returnType,
84+ name,
85+ argumentTypes,
86+ noReturn,
87+ } : FunctionDecl ) {
88+ return `
89+ extern "C" ${ returnType } ${ name } (
90+ ${ argumentTypes . map ( ( type , index ) => `${ type } arg${ index } ` ) . join ( ", " ) }
91+ ) {
92+ if (g_host.${ name } == nullptr) {
93+ fprintf(stderr, "Node-API function '${ name } ' called before it was injected!\\n");
94+ abort();
95+ }
96+ ${ returnType === "void" ? "" : "return " } g_host.${ name } (
97+ ${ argumentTypes . map ( ( _ , index ) => `arg${ index } ` ) . join ( ", " ) }
98+ );
99+ ${ noReturn ? "WEAK_NODE_API_UNREACHABLE" : "" }
100+ };
101+ ` ;
44102}
45103
46104/**
47105 * Generates source code for a version script for the given Node API version.
48106 */
49107export function generateSource ( functions : FunctionDecl [ ] ) {
50- return [
51- "// This file is generated by react-native-node-api" ,
52- `#include "weak_node_api.hpp"` , // Generated header
53- // Generate the struct of function pointers
54- "WeakNodeApiHost g_host;" ,
55- "void inject_weak_node_api_host(const WeakNodeApiHost& host) {" ,
56- " g_host = host;" ,
57- "};" ,
58- `` ,
59- // Generate function calling into the host
60- ...functions . flatMap ( ( { returnType, name, argumentTypes, noReturn } ) => {
61- return [
62- 'extern "C"' ,
63- returnType ,
64- name ,
65- "(" ,
66- argumentTypes . map ( ( type , index ) => `${ type } arg${ index } ` ) . join ( ", " ) ,
67- ") {" ,
68- `if (g_host.${ name } == nullptr) {` ,
69- ` fprintf(stderr, "Node-API function '${ name } ' called before it was injected!\\n");` ,
70- " abort();" ,
71- "}" ,
72- returnType === "void" ? "" : "return " ,
73- `g_host.${ name } ` ,
74- "(" ,
75- argumentTypes . map ( ( _ , index ) => `arg${ index } ` ) . join ( ", " ) ,
76- ");" ,
77- noReturn ? "WEAK_NODE_API_UNREACHABLE" : "" ,
78- "};" ,
79- ] . join ( " " ) ;
80- } ) ,
81- ] . join ( "\n" ) ;
82- }
108+ return `
109+ // This file is generated by react-native-node-api
110+ #include "weak_node_api.hpp" // Generated header
83111
84- async function run ( ) {
85- await fs . promises . mkdir ( OUTPUT_PATH , { recursive : true } ) ;
86-
87- const nodeApiFunctions = getNodeApiFunctions ( ) ;
88-
89- const header = generateHeader ( nodeApiFunctions ) ;
90- const headerPath = path . join ( OUTPUT_PATH , "weak_node_api.hpp" ) ;
91- await fs . promises . writeFile ( headerPath , header , "utf-8" ) ;
92- cp . spawnSync ( "clang-format" , [ "-i" , headerPath ] , { stdio : "inherit" } ) ;
93-
94- const source = generateSource ( nodeApiFunctions ) ;
95- const sourcePath = path . join ( OUTPUT_PATH , "weak_node_api.cpp" ) ;
96- await fs . promises . writeFile ( sourcePath , source , "utf-8" ) ;
97- cp . spawnSync ( "clang-format" , [ "-i" , sourcePath ] , { stdio : "inherit" } ) ;
112+ WeakNodeApiHost g_host;
113+ void inject_weak_node_api_host(const WeakNodeApiHost& host) {
114+ g_host = host;
115+ };
116+
117+ // Generate function calling into the host
118+ ${ functions . map ( generateFunctionImpl ) . join ( "\n" ) }
119+ ` ;
98120}
99121
100122run ( ) . catch ( ( err ) => {
0 commit comments