xref: /aosp_15_r20/external/sandboxed-api/oss-internship-2020/libuv/generator/wrapper_generator.py (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
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