Skip to content

Commit 3d206dd

Browse files
committed
feat: add generate_checked_functions script to dynamically create checked function wrappers
1 parent 89b4043 commit 3d206dd

1 file changed

Lines changed: 179 additions & 0 deletions

File tree

ci/generate_checked_functions.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
from pycparser import c_parser, c_ast, parse_file
2+
import os
3+
4+
# Define the Result struct as a string
5+
RESULT_STRUCT = """
6+
typedef struct {
7+
int error_code; // Error code (0 for success, non-zero for errors)
8+
union {
9+
bool bool_value;
10+
void *ptr_value;
11+
int int_value;
12+
// Add other types as needed
13+
} value;
14+
} Result;
15+
"""
16+
17+
# Input and output file paths
18+
INPUT_HEADER = "core/iwasm/include/wasm_export.h"
19+
OUTPUT_HEADER = "core/iwasm/include/wasm_export_checked.h"
20+
21+
22+
# Helper function to determine if a parameter is a pointer
23+
def is_pointer(param):
24+
return isinstance(param.type, c_ast.PtrDecl)
25+
26+
27+
# Updated generate_checked_function to dynamically update Result definition for new return types
28+
29+
30+
def generate_checked_function(func):
31+
global RESULT_STRUCT # Access the global Result definition
32+
33+
func_name = func.name # Access the name directly from Decl
34+
new_func_name = f"{func_name}_checked"
35+
36+
# Extract parameters
37+
params = func.type.args.params if func.type.args else []
38+
39+
# Determine the return type
40+
return_type = "void" # Default to void if no return type is specified
41+
if isinstance(func.type.type, c_ast.TypeDecl):
42+
return_type = " ".join(func.type.type.type.names)
43+
44+
# Check if the return type is already in Result, if not, add it
45+
if return_type not in ["bool", "void*", "int", "uint32_t"]:
46+
# Add a new field to the Result struct dynamically
47+
RESULT_STRUCT = RESULT_STRUCT.replace(
48+
"// Add other types as needed",
49+
f" {return_type} {return_type}_value;\n // Add other types as needed",
50+
)
51+
52+
# Start building the new function
53+
new_func = [f"Result {new_func_name}("]
54+
param_list = []
55+
for param in params:
56+
if isinstance(param, c_ast.EllipsisParam):
57+
# Handle variadic arguments (e.g., ...)
58+
param_list.append("...")
59+
new_func.append(" ...,")
60+
continue
61+
62+
param_name = param.name if param.name else ""
63+
param_list.append(param_name)
64+
param_type = (
65+
" ".join(param.type.type.names)
66+
if isinstance(param.type, c_ast.TypeDecl)
67+
else "void*"
68+
)
69+
new_func.append(f" {param_type} {param_name},")
70+
if param_list:
71+
new_func[-1] = new_func[-1].rstrip(",") # Remove trailing comma
72+
new_func.append(") {")
73+
74+
# Add null checks for pointer parameters
75+
for param in params:
76+
if isinstance(param, c_ast.EllipsisParam):
77+
continue # Skip variadic arguments
78+
if is_pointer(param):
79+
new_func.append(f" if ({param.name} == NULL) {{")
80+
new_func.append(f" Result res = {{ .error_code = -1 }};")
81+
new_func.append(f" return res;")
82+
new_func.append(f" }}")
83+
84+
# Call the original function
85+
if return_type == "void":
86+
new_func.append(f" {func_name}({', '.join(param_list)});")
87+
new_func.append(f" Result res = {{ .error_code = 0 }};")
88+
else:
89+
new_func.append(
90+
f" {return_type} original_result = {func_name}({', '.join(param_list)});"
91+
)
92+
new_func.append(f" Result res;")
93+
new_func.append(f" if (original_result == 0) {{")
94+
new_func.append(f" res.error_code = 0;")
95+
if return_type == "bool":
96+
new_func.append(f" res.value.bool_value = original_result;")
97+
elif return_type == "void*":
98+
new_func.append(f" res.value.ptr_value = original_result;")
99+
elif return_type == "uint32_t":
100+
new_func.append(f" res.value.int_value = original_result;")
101+
else:
102+
new_func.append(
103+
f" res.value.{return_type}_value = original_result;"
104+
)
105+
new_func.append(f" }} else {{")
106+
new_func.append(f" res.error_code = -2;")
107+
new_func.append(f" }}")
108+
109+
new_func.append(f" return res;")
110+
new_func.append("}")
111+
112+
return "\n".join(new_func)
113+
114+
115+
# Updated process_header to scan all return types and create a proper Result type
116+
117+
def process_header():
118+
global RESULT_STRUCT # Access the global Result definition
119+
120+
# Parse the header file with preprocessing
121+
ast = parse_file(
122+
INPUT_HEADER,
123+
use_cpp=True,
124+
cpp_path="gcc",
125+
cpp_args=[
126+
"-E",
127+
"-D__attribute__(x)=",
128+
"-D__asm__(x)=",
129+
"-D__asm(x)=",
130+
"-D__builtin_va_list=int",
131+
"-D__extension__=",
132+
"-D__inline__=",
133+
"-D__restrict=",
134+
"-D__restrict__=",
135+
"-D_Static_assert(x, y)=",
136+
"-D__signed=",
137+
"-D__volatile__(x)=",
138+
"-Dstatic_assert(x, y)=",
139+
],
140+
)
141+
142+
# Collect all function declarations
143+
functions = [
144+
node
145+
for node in ast.ext
146+
if isinstance(node, c_ast.Decl) and isinstance(node.type, c_ast.FuncDecl)
147+
]
148+
149+
# Scan all return types and update Result struct
150+
return_types = set()
151+
for func in functions:
152+
if isinstance(func.type.type, c_ast.TypeDecl):
153+
return_type = " ".join(func.type.type.type.names)
154+
return_types.add(return_type)
155+
156+
# Update the Result struct with all return types
157+
for return_type in return_types:
158+
if return_type not in ["bool", "void*", "int", "uint32_t"]:
159+
RESULT_STRUCT = RESULT_STRUCT.replace(
160+
"// Add other types as needed",
161+
f" {return_type} {return_type}_value;\n // Add other types as needed",
162+
)
163+
164+
# Generate the new header file
165+
with open(OUTPUT_HEADER, "w") as f:
166+
f.write("#ifndef WASM_EXPORT_CHECKED_H\n#define WASM_EXPORT_CHECKED_H\n\n")
167+
168+
# Write the updated Result struct
169+
f.write(RESULT_STRUCT + "\n")
170+
171+
for func in functions:
172+
new_func = generate_checked_function(func)
173+
f.write(new_func + "\n\n")
174+
175+
f.write("#endif // WASM_EXPORT_CHECKED_H\n")
176+
177+
178+
if __name__ == "__main__":
179+
process_header()

0 commit comments

Comments
 (0)