1# Copyright 2020 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Script generating a wrapper API for LibUV. 16 17Note: This scriptis highly specific to LibUV's source code and does not 18generalize to any other library 19""" 20 21import os 22import re 23import sys 24from typing import List 25 26 27def get_var_type(string: str) -> str: 28 """Gets the type from an argument variable. 29 30 Args: 31 string: Input variable declaration 32 33 Returns: 34 The type of the argument variable as a string, e.g. "int x" -> "int". 35 """ 36 37 var = string.strip() 38 39 # Unnamed variable 40 if var in ("void", "...") or var[-1] == "*": 41 return var 42 43 return " ".join(var.split(" ")[:-1]).strip() 44 45 46def get_var_name(string: str) -> str: 47 """Gets the name from an argument variable. 48 49 Args: 50 string: Input variable declaration 51 52 Returns: 53 The name of the arguments variable as a string, e.g. "int x" -> "x". 54 """ 55 56 var = string.strip() 57 58 # Not an actual variable 59 if var in ("void", "..."): 60 return "" 61 62 # Unnamed variable, use an arbitrary name 63 if var[-1] == "*": 64 return var.split("_")[1] 65 66 return var.split(" ")[-1].strip() 67 68 69def fix_method_type(string: str) -> str: 70 """Fixes the method type. 71 72 Args: 73 string: A parameter type declaration 74 75 Returns: 76 A fixed up string replacing pointers to concrete types with pointers to 77 void, e.g. "const int*" -> "const void*". 78 """ 79 80 method_type = string.strip() 81 82 # Const pointer 83 if "*" in method_type and "const" in method_type: 84 return "const void*" 85 86 # Regular pointer 87 if "*" in method_type: 88 return "void*" 89 90 # Not a pointer 91 return method_type 92 93 94def fix_argument(string: str) -> str: 95 """Fixes an argument. 96 97 Args: 98 string: An argument type to fix 99 100 Returns: 101 The fixed up argument as a string, e.g. "const int* x" -> "const void* x". 102 """ 103 104 arg_type = get_var_type(string) 105 arg_name = get_var_name(string) 106 107 # Array argument, becomes a pointer 108 if "[" in arg_name: 109 arg_type += "*" 110 arg_name = arg_name.split("[")[0] + arg_name.split("]")[-1] 111 112 # Pointer (in LibUV, types endind in "_cb" or "_func" are pointers) 113 if "*" in arg_type or "_cb" in arg_type or "_func" in arg_type: 114 if "const" in arg_type: 115 return "const void* " + arg_name 116 return "void* " + arg_name 117 118 # Not a pointer 119 return arg_type + " " + arg_name 120 121 122def fix_call_argument(string: str) -> str: 123 """Fixes an argument in a call the orignal method. 124 125 Args: 126 string: A method call argument 127 128 Returns: 129 The fixed call argument, e.g. "const int* x" -> 130 "reinterpret_cast<const int*>(x)". 131 """ 132 133 arg_type = get_var_type(string) 134 arg_name = get_var_name(string) 135 136 # Array argument, becomes a pointer 137 if "[" in arg_name: 138 arg_type += "*" 139 arg_name = arg_name.split("[")[0] + arg_name.split("]")[-1] 140 141 # Pointer (in LibUV, types endind in "_cb" or "_func" are pointers) 142 if "*" in arg_type or "_cb" in arg_type or "_func" in arg_type: 143 return "reinterpret_cast<" + arg_type + ">(" + arg_name + ")" 144 145 # Not a pointer 146 return arg_name 147 148 149def read_file(filename: str) -> str: 150 """Returns contents of filename as a string. 151 152 Args: 153 filename: The name of the file to read 154 155 Returns: 156 The contents of the file as a string. 157 """ 158 159 file = open(filename, "r") 160 return str(file.read()) 161 162 163def clean_file(text: str) -> str: 164 """Prepares the file for parsing. 165 166 In particular, removes comments and macros from text 167 Additionally, moves pointer asterisks next to its type 168 169 Args: 170 text: The contents of the text file to prepare 171 172 Returns: 173 The cleaned up file contents. 174 """ 175 176 result = text 177 result = re.sub(r"//.*?\n", "", result, flags=re.S) 178 result = re.sub(r"/\*.*?\*/", "", result, flags=re.S) 179 result = re.sub(r"#.*?\n", "", result, flags=re.S) 180 result = result.replace(" *", "* ") 181 return result 182 183 184def get_signatures(text: str) -> str: 185 """Gets the signatures of all the methods in the header. 186 187 Note: This method only works on a certain version of LibUV's header. 188 189 Args: 190 text: The contents of the header file 191 192 Returns: 193 The extracted method signatures. 194 """ 195 196 signatures = [x.split(";")[0].strip() for x in text.split("UV_EXTERN")[1:]] 197 method_types = [ 198 " ".join(s.split("(")[0].split(" ")[:-1]).strip() for s in signatures 199 ] 200 names = [s.split("(")[0].split(" ")[-1].strip() for s in signatures] 201 arguments = [s.split("(")[1][:-1] for s in signatures] 202 arguments_lists = [[x.strip() for x in a.split(",")] for a in arguments] 203 return zip(method_types, names, arguments_lists) 204 205 206def append_method(method_type: str, name: str, arguments_list: List[str], 207 header: List[str], source: List[str]) -> None: 208 """Writes the method to the header and the source list of lines. 209 210 Args: 211 method_type: The return type of the method as a string 212 name: The name of the method 213 arguments_list: A list of method aruments 214 header: A list that receives method wrapper declarations 215 source: A list that receives the declarations of the method wrappers 216 """ 217 218 header.append( 219 fix_method_type(method_type) + " sapi_" + name + "(" + 220 ", ".join(map(fix_argument, arguments_list)) + ");") 221 source.append( 222 fix_method_type(method_type) + " sapi_" + name + "(" + 223 ", ".join(map(fix_argument, arguments_list)) + ") {\n" + " return " + 224 name + "(" + ", ".join(map(fix_call_argument, arguments_list)) + ");\n" + 225 "}") 226 227 228def append_text(text: str, file: List[str]) -> None: 229 """Writes text to file list of lines. 230 231 Useful for additional methods, includes, extern "C"... 232 233 Args: 234 text: The text to append to the file 235 file: A list receiving file lines 236 """ 237 238 file.append(text) 239 240 241def generate_wrapper() -> None: 242 """Generates the wrapper.""" 243 244 header_file = open(sys.argv[2], "w") 245 source_file = open(sys.argv[3], "w") 246 247 text = read_file(sys.argv[1]) 248 text = clean_file(text) 249 signatures = get_signatures(text) 250 251 header = [] 252 source = [] 253 254 append_text("#include <uv.h>", header) 255 append_text("#include <cstddef>", header) 256 append_text("extern \"C\" {", header) 257 append_text("#include \"" + os.path.abspath(header_file.name) + "\"", source) 258 259 for (method_type, name, arguments_list) in signatures: 260 # These wrapper methods are manually added at the end 261 if name in ("uv_once", "uv_loop_configure"): 262 continue 263 append_method(method_type, name, arguments_list, header, source) 264 265 # Add sapi_uv_once (uv_once uses a differnet kind of callback) 266 append_text("void sapi_uv_once(void* guard, void (*callback)(void));", header) 267 append_text( 268 "void sapi_uv_once(void* guard, void (*callback)(void)) {\n" + 269 " return uv_once(reinterpret_cast<uv_once_t*>(guard)," + "callback);\n" + 270 "}", source) 271 272 # Add sapi_uv_loop_configure (uv_loop_configure is variadic) 273 append_text( 274 "int sapi_uv_loop_configure(void* loop, uv_loop_option option)" + ";", 275 header) 276 append_text( 277 "int sapi_uv_loop_configure(void* loop, uv_loop_option option)" + 278 " {\n return uv_loop_configure(" + 279 "reinterpret_cast<uv_loop_t*>(loop), option);\n" + "}", source) 280 281 # Add sapi_uv_loop_configure_int (uv_loop_configure is variadic) 282 append_text( 283 "int sapi_uv_loop_configure_int(void* loop, " + 284 "uv_loop_option option, int ap);", header) 285 append_text( 286 "int sapi_uv_loop_configure_int(void* loop, " + 287 "uv_loop_option option, int ap) {\n" + " return uv_loop_configure(" + 288 "reinterpret_cast<uv_loop_t*>(loop), option, ap);\n}", source) 289 290 append_text("} // extern \"C\"\n", header) 291 292 header_file.write("\n\n".join(header)) 293 source_file.write("\n\n".join(source)) 294 295 296generate_wrapper() 297