| 1 | /* |
| 2 | * Copyright 2011,2015 Sven Verdoolaege. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * |
| 11 | * 2. Redistributions in binary form must reproduce the above |
| 12 | * copyright notice, this list of conditions and the following |
| 13 | * disclaimer in the documentation and/or other materials provided |
| 14 | * with the distribution. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY SVEN VERDOOLAEGE ''AS IS'' AND ANY |
| 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SVEN VERDOOLAEGE OR |
| 20 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
| 23 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | * |
| 28 | * The views and conclusions contained in the software and documentation |
| 29 | * are those of the authors and should not be interpreted as |
| 30 | * representing official policies, either expressed or implied, of |
| 31 | * Sven Verdoolaege. |
| 32 | */ |
| 33 | |
| 34 | #include "isl_config.h" |
| 35 | |
| 36 | #include <stdarg.h> |
| 37 | #include <stdio.h> |
| 38 | |
| 39 | #include <algorithm> |
| 40 | #include <iostream> |
| 41 | #include <map> |
| 42 | #include <vector> |
| 43 | |
| 44 | #include "python.h" |
| 45 | #include "generator.h" |
| 46 | |
| 47 | /* Argument format for Python methods with a fixed number of arguments. |
| 48 | */ |
| 49 | static const char *fixed_arg_fmt = "arg%d" ; |
| 50 | /* Argument format for Python methods with a variable number of arguments. |
| 51 | */ |
| 52 | static const char *var_arg_fmt = "args[%d]" ; |
| 53 | |
| 54 | /* Drop the "isl_" initial part of the type name "name". |
| 55 | */ |
| 56 | static string type2python(string name) |
| 57 | { |
| 58 | return name.substr(pos: 4); |
| 59 | } |
| 60 | |
| 61 | /* Print the arguments of a method with "n_arg" arguments, starting at "first". |
| 62 | */ |
| 63 | void python_generator::print_method_arguments(int first, int n_arg) |
| 64 | { |
| 65 | for (int i = first; i < n_arg; ++i) { |
| 66 | if (i > first) |
| 67 | printf(format: ", " ); |
| 68 | printf(format: "arg%d" , i); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /* Print the start of a definition for method "name" |
| 73 | * (without specifying the arguments). |
| 74 | * If "is_static" is set, then mark the python method as static. |
| 75 | * |
| 76 | * If the method is called "from", then rename it to "convert_from" |
| 77 | * because "from" is a python keyword. |
| 78 | */ |
| 79 | static void print_method_def(bool is_static, const string &name) |
| 80 | { |
| 81 | const char *s; |
| 82 | |
| 83 | if (is_static) |
| 84 | printf(format: " @staticmethod\n" ); |
| 85 | |
| 86 | s = name.c_str(); |
| 87 | if (name == "from" ) |
| 88 | s = "convert_from" ; |
| 89 | |
| 90 | printf(format: " def %s" , s); |
| 91 | } |
| 92 | |
| 93 | /* Print the header of the method "name" with "n_arg" arguments. |
| 94 | * If "is_static" is set, then mark the python method as static. |
| 95 | */ |
| 96 | void python_generator::(bool is_static, const string &name, |
| 97 | int n_arg) |
| 98 | { |
| 99 | print_method_def(is_static, name); |
| 100 | printf(format: "(" ); |
| 101 | print_method_arguments(first: 0, n_arg); |
| 102 | printf(format: "):\n" ); |
| 103 | } |
| 104 | |
| 105 | /* Print formatted output with the given indentation. |
| 106 | */ |
| 107 | static void print_indent(int indent, const char *format, ...) |
| 108 | { |
| 109 | va_list args; |
| 110 | |
| 111 | printf(format: "%*s" , indent, " " ); |
| 112 | va_start(args, format); |
| 113 | vprintf(format: format, arg: args); |
| 114 | va_end(args); |
| 115 | } |
| 116 | |
| 117 | /* Print a check that the argument in position "pos" is of type "type" |
| 118 | * with the given indentation. |
| 119 | * If this fails and if "upcast" is set, then convert the first |
| 120 | * argument to "super" and call the method "name" on it, passing |
| 121 | * the remaining of the "n" arguments. |
| 122 | * If the check fails and "upcast" is not set, then simply raise |
| 123 | * an exception. |
| 124 | * If "upcast" is not set, then the "super", "name" and "n" arguments |
| 125 | * to this function are ignored. |
| 126 | * "fmt" is the format for printing Python method arguments. |
| 127 | */ |
| 128 | void python_generator::print_type_check(int indent, const string &type, |
| 129 | const char *fmt, int pos, bool upcast, const string &super, |
| 130 | const string &name, int n) |
| 131 | { |
| 132 | print_indent(indent, format: "try:\n" ); |
| 133 | print_indent(indent, format: " if not " ); |
| 134 | printf(format: fmt, pos); |
| 135 | printf(format: ".__class__ is %s:\n" , type.c_str()); |
| 136 | print_indent(indent, format: " " ); |
| 137 | printf(format: fmt, pos); |
| 138 | printf(format: " = %s(" , type.c_str()); |
| 139 | printf(format: fmt, pos); |
| 140 | printf(format: ")\n" ); |
| 141 | print_indent(indent, format: "except:\n" ); |
| 142 | if (upcast) { |
| 143 | print_indent(indent, format: " return %s(" , |
| 144 | type2python(name: super).c_str()); |
| 145 | printf(format: fmt, 0); |
| 146 | printf(format: ").%s(" , name.c_str()); |
| 147 | for (int i = 1; i < n; ++i) { |
| 148 | if (i != 1) |
| 149 | printf(format: ", " ); |
| 150 | printf(format: fmt, i); |
| 151 | } |
| 152 | printf(format: ")\n" ); |
| 153 | } else |
| 154 | print_indent(indent, format: " raise\n" ); |
| 155 | } |
| 156 | |
| 157 | /* For each of the "n" initial arguments of the function "method" |
| 158 | * that refer to an isl structure, |
| 159 | * including the object on which the method is called, |
| 160 | * check if the corresponding actual argument is of the right type. |
| 161 | * If not, try and convert it to the right type. |
| 162 | * If that doesn't work and if "super" contains at least one element, |
| 163 | * try and convert self to the type of the first superclass in "super" and |
| 164 | * call the corresponding method. |
| 165 | * If "first_is_ctx" is set, then the first argument is skipped. |
| 166 | */ |
| 167 | void python_generator::print_type_checks(const string &cname, |
| 168 | FunctionDecl *method, bool first_is_ctx, int n, |
| 169 | const vector<string> &super) |
| 170 | { |
| 171 | for (int i = first_is_ctx; i < n; ++i) { |
| 172 | ParmVarDecl *param = method->getParamDecl(i); |
| 173 | string type; |
| 174 | |
| 175 | if (!is_isl_type(param->getOriginalType())) |
| 176 | continue; |
| 177 | type = type2python(extract_type(param->getOriginalType())); |
| 178 | if (!first_is_ctx && i > 0 && super.size() > 0) |
| 179 | print_type_check(indent: 8, type, fmt: fixed_arg_fmt, |
| 180 | pos: i - first_is_ctx, upcast: true, |
| 181 | super: super[0], name: cname, n); |
| 182 | else |
| 183 | print_type_check(indent: 8, type, fmt: fixed_arg_fmt, |
| 184 | pos: i - first_is_ctx, upcast: false, super: "" , name: cname, n: -1); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /* Print a call to the *_copy function corresponding to "type". |
| 189 | */ |
| 190 | void python_generator::print_copy(QualType type) |
| 191 | { |
| 192 | string type_s = extract_type(type); |
| 193 | |
| 194 | printf(format: "isl.%s_copy" , type_s.c_str()); |
| 195 | } |
| 196 | |
| 197 | /* Construct a wrapper for callback argument "param" (at position "arg"). |
| 198 | * Assign the wrapper to "cb{arg}". |
| 199 | * |
| 200 | * The wrapper converts the arguments of the callback to python types, |
| 201 | * taking a copy if the C callback does not take its arguments. |
| 202 | * If any exception is thrown, the wrapper keeps track of it in exc_info[0] |
| 203 | * and returns a value indicating an error. Otherwise the wrapper |
| 204 | * returns a value indicating success. |
| 205 | * In case the C callback is expected to return an isl_stat, |
| 206 | * the error value is -1 and the success value is 0. |
| 207 | * In case the C callback is expected to return an isl_bool, |
| 208 | * the error value is -1 and the success value is 1 or 0 depending |
| 209 | * on the result of the Python callback. |
| 210 | * Otherwise, None is returned to indicate an error and |
| 211 | * a copy of the object in case of success. |
| 212 | */ |
| 213 | void python_generator::print_callback(ParmVarDecl *param, int arg) |
| 214 | { |
| 215 | QualType type = param->getOriginalType(); |
| 216 | const FunctionProtoType *fn = extract_prototype(type); |
| 217 | QualType return_type = fn->getReturnType(); |
| 218 | unsigned n_arg = fn->getNumArgs(); |
| 219 | |
| 220 | printf(format: " exc_info = [None]\n" ); |
| 221 | printf(format: " fn = CFUNCTYPE(" ); |
| 222 | if (is_isl_stat(return_type) || is_isl_bool(return_type)) |
| 223 | printf(format: "c_int" ); |
| 224 | else |
| 225 | printf(format: "c_void_p" ); |
| 226 | for (unsigned i = 0; i < n_arg - 1; ++i) { |
| 227 | if (!is_isl_type(fn->getArgType(i))) |
| 228 | die(msg: "Argument has non-isl type" ); |
| 229 | printf(format: ", c_void_p" ); |
| 230 | } |
| 231 | printf(format: ", c_void_p)\n" ); |
| 232 | printf(format: " def cb_func(" ); |
| 233 | for (unsigned i = 0; i < n_arg; ++i) { |
| 234 | if (i) |
| 235 | printf(format: ", " ); |
| 236 | printf(format: "cb_arg%d" , i); |
| 237 | } |
| 238 | printf(format: "):\n" ); |
| 239 | for (unsigned i = 0; i < n_arg - 1; ++i) { |
| 240 | string arg_type; |
| 241 | arg_type = type2python(extract_type(fn->getArgType(i))); |
| 242 | printf(format: " cb_arg%d = %s(ctx=arg0.ctx, ptr=" , |
| 243 | i, arg_type.c_str()); |
| 244 | if (!callback_takes_argument(param, i)) |
| 245 | print_copy(fn->getArgType(i)); |
| 246 | printf(format: "(cb_arg%d))\n" , i); |
| 247 | } |
| 248 | printf(format: " try:\n" ); |
| 249 | if (is_isl_stat(return_type)) |
| 250 | printf(format: " arg%d(" , arg); |
| 251 | else |
| 252 | printf(format: " res = arg%d(" , arg); |
| 253 | for (unsigned i = 0; i < n_arg - 1; ++i) { |
| 254 | if (i) |
| 255 | printf(format: ", " ); |
| 256 | printf(format: "cb_arg%d" , i); |
| 257 | } |
| 258 | printf(format: ")\n" ); |
| 259 | printf(format: " except BaseException as e:\n" ); |
| 260 | printf(format: " exc_info[0] = e\n" ); |
| 261 | if (is_isl_stat(return_type) || is_isl_bool(return_type)) |
| 262 | printf(format: " return -1\n" ); |
| 263 | else |
| 264 | printf(format: " return None\n" ); |
| 265 | if (is_isl_stat(return_type)) { |
| 266 | printf(format: " return 0\n" ); |
| 267 | } else if (is_isl_bool(return_type)) { |
| 268 | printf(format: " return 1 if res else 0\n" ); |
| 269 | } else { |
| 270 | printf(format: " return " ); |
| 271 | print_copy(return_type); |
| 272 | printf(format: "(res.ptr)\n" ); |
| 273 | } |
| 274 | printf(format: " cb%d = fn(cb_func)\n" , arg); |
| 275 | } |
| 276 | |
| 277 | /* Print the argument at position "arg" in call to "fd". |
| 278 | * "fmt" is the format for printing Python method arguments. |
| 279 | * "skip" is the number of initial arguments of "fd" that are |
| 280 | * skipped in the Python method. |
| 281 | * |
| 282 | * If the (first) argument is an isl_ctx, then print "ctx", |
| 283 | * assuming that the caller has made the context available |
| 284 | * in a "ctx" variable. |
| 285 | * Otherwise, if the argument is a callback, then print a reference to |
| 286 | * the corresponding callback wrapper. |
| 287 | * Otherwise, if the argument is marked as consuming a reference, |
| 288 | * then pass a copy of the pointer stored in the corresponding |
| 289 | * argument passed to the Python method. |
| 290 | * Otherwise, if the argument is a string, then the python string is first |
| 291 | * encoded as a byte sequence, using 'ascii' as encoding. This assumes |
| 292 | * that all strings passed to isl can be converted to 'ascii'. |
| 293 | * Otherwise, if the argument is a pointer, then pass this pointer itself. |
| 294 | * Otherwise, pass the argument directly. |
| 295 | */ |
| 296 | void python_generator::print_arg_in_call(FunctionDecl *fd, const char *fmt, |
| 297 | int arg, int skip) |
| 298 | { |
| 299 | ParmVarDecl *param = fd->getParamDecl(arg); |
| 300 | QualType type = param->getOriginalType(); |
| 301 | if (is_isl_ctx(type)) { |
| 302 | printf(format: "ctx" ); |
| 303 | } else if (is_callback(type)) { |
| 304 | printf(format: "cb%d" , arg - skip); |
| 305 | } else if (takes(param)) { |
| 306 | print_copy(type); |
| 307 | printf(format: "(" ); |
| 308 | printf(format: fmt, arg - skip); |
| 309 | printf(format: ".ptr)" ); |
| 310 | } else if (is_string(type)) { |
| 311 | printf(format: fmt, arg - skip); |
| 312 | printf(format: ".encode('ascii')" ); |
| 313 | } else if (type->isPointerType()) { |
| 314 | printf(format: fmt, arg - skip); |
| 315 | printf(format: ".ptr" ); |
| 316 | } else { |
| 317 | printf(format: fmt, arg - skip); |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | /* Generate code that raises the exception captured in "exc_info", if any, |
| 322 | * with the given indentation. |
| 323 | */ |
| 324 | static void print_rethrow(int indent, const char *exc_info) |
| 325 | { |
| 326 | print_indent(indent, format: "if %s is not None:\n" , exc_info); |
| 327 | print_indent(indent, format: " raise %s\n" , exc_info); |
| 328 | } |
| 329 | |
| 330 | /* Print code with the given indentation that checks |
| 331 | * whether any of the persistent callbacks of "clazz" |
| 332 | * is set and if it failed with an exception. If so, the 'exc_info' |
| 333 | * field contains the exception and is raised again. |
| 334 | * The field is cleared because the callback and its data may get reused. |
| 335 | * "fmt" is the format for printing Python method arguments. |
| 336 | */ |
| 337 | static void print_persistent_callback_failure_check(int indent, |
| 338 | const isl_class &clazz, const char *fmt) |
| 339 | { |
| 340 | const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks; |
| 341 | set<FunctionDecl *>::const_iterator in; |
| 342 | |
| 343 | for (in = callbacks.begin(); in != callbacks.end(); ++in) { |
| 344 | string callback_name = clazz.persistent_callback_name(*in); |
| 345 | |
| 346 | print_indent(indent, format: "if hasattr(" ); |
| 347 | printf(format: fmt, 0); |
| 348 | printf(format: ", '%s') and " , callback_name.c_str()); |
| 349 | printf(format: fmt, 0); |
| 350 | printf(format: ".%s['exc_info'] != None:\n" , callback_name.c_str()); |
| 351 | print_indent(indent, format: " exc_info = " ); |
| 352 | printf(format: fmt, 0); |
| 353 | printf(format: ".%s['exc_info'][0]\n" , callback_name.c_str()); |
| 354 | print_indent(indent, format: " " ); |
| 355 | printf(format: fmt, 0); |
| 356 | printf(format: ".%s['exc_info'][0] = None\n" , callback_name.c_str()); |
| 357 | print_rethrow(indent: indent + 4, exc_info: "exc_info" ); |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | /* Print the return statement of the python method corresponding |
| 362 | * to the C function "method" with the given indentation. |
| 363 | * If the object on which the method was called |
| 364 | * may have a persistent callback, then first check if any of those failed. |
| 365 | * "fmt" is the format for printing Python method arguments. |
| 366 | * |
| 367 | * If the method returns a new instance of the same object type and |
| 368 | * if the class has any persistent callbacks, then the data |
| 369 | * for these callbacks are copied from the original to the new object. |
| 370 | * If the method it itself setting a persistent callback, |
| 371 | * then keep track of the constructed C callback (such that it doesn't |
| 372 | * get destroyed) and the data structure that holds the captured exception |
| 373 | * (such that it can be raised again). |
| 374 | * The callback appears in position 1 and the C callback is therefore |
| 375 | * called "cb1". |
| 376 | * |
| 377 | * If the return type is a (const) char *, then convert the result |
| 378 | * to a Python string, raising an error on NULL and freeing |
| 379 | * the C string if needed. For python 3 compatibility, the string returned |
| 380 | * by isl is explicitly decoded as an 'ascii' string. This is correct |
| 381 | * as all strings returned by isl are expected to be 'ascii'. |
| 382 | * |
| 383 | * If the return type is isl_stat, isl_bool or isl_size, then |
| 384 | * raise an error on isl_stat_error, isl_bool_error or isl_size_error. |
| 385 | * In case of isl_bool, the result is converted to |
| 386 | * a Python boolean. |
| 387 | * In case of isl_size, the result is converted to a Python int. |
| 388 | */ |
| 389 | void python_generator::print_method_return(int indent, const isl_class &clazz, |
| 390 | FunctionDecl *method, const char *fmt) |
| 391 | { |
| 392 | QualType return_type = method->getReturnType(); |
| 393 | |
| 394 | if (!is_static(clazz, method)) |
| 395 | print_persistent_callback_failure_check(indent, clazz, fmt); |
| 396 | |
| 397 | if (is_isl_type(return_type)) { |
| 398 | string type; |
| 399 | |
| 400 | type = type2python(extract_type(return_type)); |
| 401 | print_indent(indent, |
| 402 | format: "obj = %s(ctx=ctx, ptr=res)\n" , type.c_str()); |
| 403 | if (is_mutator(clazz, method) && |
| 404 | clazz.has_persistent_callbacks()) |
| 405 | print_indent(indent, format: "obj.copy_callbacks(arg0)\n" ); |
| 406 | if (clazz.persistent_callbacks.count(method)) { |
| 407 | string callback_name; |
| 408 | |
| 409 | callback_name = clazz.persistent_callback_name(method); |
| 410 | print_indent(indent, format: "obj.%s = { 'func': cb1, " |
| 411 | "'exc_info': exc_info }\n" , |
| 412 | callback_name.c_str()); |
| 413 | } |
| 414 | print_indent(indent, format: "return obj\n" ); |
| 415 | } else if (is_string(return_type)) { |
| 416 | print_indent(indent, format: "if res == 0:\n" ); |
| 417 | print_indent(indent, format: " raise Error\n" ); |
| 418 | print_indent(indent, format: "string = " |
| 419 | "cast(res, c_char_p).value.decode('ascii')\n" ); |
| 420 | |
| 421 | if (gives(method)) |
| 422 | print_indent(indent, format: "libc.free(res)\n" ); |
| 423 | |
| 424 | print_indent(indent, format: "return string\n" ); |
| 425 | } else if (is_isl_neg_error(return_type)) { |
| 426 | print_indent(indent, format: "if res < 0:\n" ); |
| 427 | print_indent(indent, format: " raise Error\n" ); |
| 428 | if (is_isl_bool(return_type)) |
| 429 | print_indent(indent, format: "return bool(res)\n" ); |
| 430 | else if (is_isl_size(return_type)) |
| 431 | print_indent(indent, format: "return int(res)\n" ); |
| 432 | } else { |
| 433 | print_indent(indent, format: "return res\n" ); |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | /* Print a python "get" method corresponding to the C function "fd" |
| 438 | * in class "clazz" using a name that includes the "get_" prefix. |
| 439 | * |
| 440 | * This method simply calls the variant without the "get_" prefix and |
| 441 | * returns its result. |
| 442 | * Note that static methods are not considered to be "get" methods. |
| 443 | */ |
| 444 | void python_generator::print_get_method(const isl_class &clazz, |
| 445 | FunctionDecl *fd) |
| 446 | { |
| 447 | string get_name = clazz.base_method_name(fd); |
| 448 | string name = clazz.method_name(fd); |
| 449 | int num_params = fd->getNumParams(); |
| 450 | |
| 451 | print_method_header(is_static: false, name: get_name, n_arg: num_params); |
| 452 | printf(format: " return arg0.%s(" , name.c_str()); |
| 453 | print_method_arguments(first: 1, n_arg: num_params); |
| 454 | printf(format: ")\n" ); |
| 455 | } |
| 456 | |
| 457 | /* Print a call to "method", along with the corresponding |
| 458 | * return statement, with the given indentation. |
| 459 | * "drop_ctx" is set if the first argument is an isl_ctx. |
| 460 | * |
| 461 | * A "ctx" variable is first initialized as it may be needed |
| 462 | * in the first call to print_arg_in_call and in print_method_return. |
| 463 | * |
| 464 | * If the method has any callback function, then any exception |
| 465 | * thrown in any callback also need to be rethrown. |
| 466 | */ |
| 467 | void python_generator::print_method_call(int indent, const isl_class &clazz, |
| 468 | FunctionDecl *method, const char *fmt, int drop_ctx) |
| 469 | { |
| 470 | string fullname = method->getName().str(); |
| 471 | int num_params = method->getNumParams(); |
| 472 | int drop_user = 0; |
| 473 | |
| 474 | if (drop_ctx) { |
| 475 | print_indent(indent, format: "ctx = Context.getDefaultInstance()\n" ); |
| 476 | } else { |
| 477 | print_indent(indent, format: "ctx = " ); |
| 478 | printf(format: fmt, 0); |
| 479 | printf(format: ".ctx\n" ); |
| 480 | } |
| 481 | print_indent(indent, format: "res = isl.%s(" , fullname.c_str()); |
| 482 | for (int i = 0; i < num_params; ++i) { |
| 483 | if (i > 0) |
| 484 | printf(format: ", " ); |
| 485 | print_arg_in_call(method, fmt, i, drop_ctx + drop_user); |
| 486 | if (!is_callback_arg(method, i)) |
| 487 | continue; |
| 488 | ++drop_user; |
| 489 | ++i; |
| 490 | printf(format: ", None" ); |
| 491 | } |
| 492 | printf(format: ")\n" ); |
| 493 | |
| 494 | if (drop_user > 0) |
| 495 | print_rethrow(indent, exc_info: "exc_info[0]" ); |
| 496 | |
| 497 | print_method_return(indent, clazz, method, fmt); |
| 498 | } |
| 499 | |
| 500 | /* Print a python method corresponding to the C function "method". |
| 501 | * "super" contains the superclasses of the class to which the method belongs, |
| 502 | * with the first element corresponding to the annotation that appears |
| 503 | * closest to the annotated type. This superclass is the least |
| 504 | * general extension of the annotated type in the linearization |
| 505 | * of the class hierarchy. |
| 506 | * |
| 507 | * If the first argument of "method" is something other than an instance |
| 508 | * of the class, then mark the python method as static. |
| 509 | * If, moreover, this first argument is an isl_ctx, then remove |
| 510 | * it from the arguments of the Python method. |
| 511 | * |
| 512 | * If the function has any callback arguments, then it also has corresponding |
| 513 | * "user" arguments. Since Python has closures, there is no need for such |
| 514 | * user arguments in the Python interface, so we simply drop them. |
| 515 | * We also create a wrapper ("cb{arg}") for each callback. |
| 516 | * |
| 517 | * If the function consumes a reference, then we pass it a copy of |
| 518 | * the actual argument. |
| 519 | * |
| 520 | * For methods that are identified as "get" methods, also |
| 521 | * print a variant of the method using a name that includes |
| 522 | * the "get_" prefix. |
| 523 | */ |
| 524 | void python_generator::print_method(const isl_class &clazz, |
| 525 | FunctionDecl *method, vector<string> super) |
| 526 | { |
| 527 | string cname = clazz.method_name(method); |
| 528 | int num_params = method->getNumParams(); |
| 529 | int drop_user = 0; |
| 530 | int drop_ctx = first_arg_is_isl_ctx(method); |
| 531 | |
| 532 | for (int i = 1; i < num_params; ++i) { |
| 533 | if (is_callback_arg(method, i)) |
| 534 | drop_user += 1; |
| 535 | } |
| 536 | |
| 537 | print_method_header(is_static: is_static(clazz, method), name: cname, |
| 538 | n_arg: num_params - drop_ctx - drop_user); |
| 539 | |
| 540 | print_type_checks(cname, method, drop_ctx, |
| 541 | num_params, super); |
| 542 | drop_user = 0; |
| 543 | for (int i = 1; i < num_params; ++i) { |
| 544 | ParmVarDecl *param = method->getParamDecl(i); |
| 545 | QualType type = param->getOriginalType(); |
| 546 | if (!is_callback(type)) |
| 547 | continue; |
| 548 | print_callback(param, i - drop_ctx - drop_user); |
| 549 | drop_user += 1; |
| 550 | } |
| 551 | print_method_call(8, clazz, method, fixed_arg_fmt, drop_ctx); |
| 552 | |
| 553 | if (clazz.is_get_method(method)) |
| 554 | print_get_method(clazz, method); |
| 555 | } |
| 556 | |
| 557 | /* Print a condition that checks whether Python method argument "i" |
| 558 | * corresponds to the C function argument type "type". |
| 559 | */ |
| 560 | static void print_argument_check(QualType type, int i) |
| 561 | { |
| 562 | if (generator::is_isl_type(type)) { |
| 563 | string type_str; |
| 564 | type_str = generator::extract_type(type); |
| 565 | type_str = type2python(name: type_str); |
| 566 | printf(format: "args[%d].__class__ is %s" , i, type_str.c_str()); |
| 567 | } else if (type->isPointerType()) { |
| 568 | printf(format: "type(args[%d]) == str" , i); |
| 569 | } else { |
| 570 | printf(format: "type(args[%d]) == int" , i); |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | /* Is any element of "vector" set? |
| 575 | */ |
| 576 | static bool any(const std::vector<bool> &vector) |
| 577 | { |
| 578 | return std::find(first: vector.begin(), last: vector.end(), val: true) != vector.end(); |
| 579 | } |
| 580 | |
| 581 | /* Print a test that checks whether the arguments passed |
| 582 | * to the Python method correspond to the arguments |
| 583 | * expected by "fd" and |
| 584 | * check if the object on which the method is called, if any, |
| 585 | * is of the right type. |
| 586 | * "drop_ctx" is set if the first argument of "fd" is an isl_ctx, |
| 587 | * which does not appear as an argument to the Python method. |
| 588 | * |
| 589 | * If an automatic conversion function is available for any |
| 590 | * of the argument types, then also allow the argument |
| 591 | * to be of the type as prescribed by the second input argument |
| 592 | * of the conversion function. |
| 593 | * The corresponding arguments are then converted to the expected types |
| 594 | * if needed. |
| 595 | * The object on which the method is called is also converted if needed. |
| 596 | * The argument tuple first needs to be converted to a list |
| 597 | * in order to be able to modify the entries. |
| 598 | */ |
| 599 | void python_generator::print_argument_checks(const isl_class &clazz, |
| 600 | FunctionDecl *fd, int drop_ctx) |
| 601 | { |
| 602 | int num_params = fd->getNumParams(); |
| 603 | bool is_static = generator::is_static(clazz, fd); |
| 604 | int first = is_static ? drop_ctx : 1; |
| 605 | std::vector<bool> convert(num_params); |
| 606 | |
| 607 | printf(format: " if len(args) == %d" , num_params - drop_ctx); |
| 608 | for (int i = first; i < num_params; ++i) { |
| 609 | ParmVarDecl *param = fd->getParamDecl(i); |
| 610 | QualType type = param->getOriginalType(); |
| 611 | const Type *ptr = type.getTypePtr(); |
| 612 | |
| 613 | printf(format: " and " ); |
| 614 | if (conversions.count(ptr) == 0) { |
| 615 | print_argument_check(type, i - drop_ctx); |
| 616 | } else { |
| 617 | QualType type2 = conversions.at(ptr)->getOriginalType(); |
| 618 | convert[i] = true; |
| 619 | printf(format: "(" ); |
| 620 | print_argument_check(type, i - drop_ctx); |
| 621 | printf(format: " or " ); |
| 622 | print_argument_check(type2, i - drop_ctx); |
| 623 | printf(format: ")" ); |
| 624 | } |
| 625 | } |
| 626 | printf(format: ":\n" ); |
| 627 | |
| 628 | if (is_static && !any(vector: convert)) |
| 629 | return; |
| 630 | print_indent(indent: 12, format: "args = list(args)\n" ); |
| 631 | first = is_static ? drop_ctx : 0; |
| 632 | for (int i = first; i < num_params; ++i) { |
| 633 | bool is_self = !is_static && i == 0; |
| 634 | ParmVarDecl *param = fd->getParamDecl(i); |
| 635 | string type; |
| 636 | |
| 637 | if (!is_self && !convert[i]) |
| 638 | continue; |
| 639 | type = type2python(extract_type(param->getOriginalType())); |
| 640 | print_type_check(indent: 12, type, fmt: var_arg_fmt, |
| 641 | pos: i - drop_ctx, upcast: false, super: "" , name: "" , n: -1); |
| 642 | } |
| 643 | } |
| 644 | |
| 645 | /* Print part of an overloaded python method corresponding to the C function |
| 646 | * "method". |
| 647 | * "drop_ctx" is set if the first argument of "method" is an isl_ctx. |
| 648 | * |
| 649 | * In particular, print code to test whether the arguments passed to |
| 650 | * the python method correspond to the arguments expected by "method" |
| 651 | * and to call "method" if they do. |
| 652 | */ |
| 653 | void python_generator::print_method_overload(const isl_class &clazz, |
| 654 | FunctionDecl *method) |
| 655 | { |
| 656 | int drop_ctx = first_arg_is_isl_ctx(method); |
| 657 | |
| 658 | print_argument_checks(clazz, method, drop_ctx); |
| 659 | print_method_call(12, clazz, method, var_arg_fmt, drop_ctx); |
| 660 | } |
| 661 | |
| 662 | /* Print a python method with a name derived from "fullname" |
| 663 | * corresponding to the C functions "methods". |
| 664 | * "super" contains the superclasses of the class to which the method belongs. |
| 665 | * |
| 666 | * If "methods" consists of a single element that is not marked overloaded, |
| 667 | * the use print_method to print the method. |
| 668 | * Otherwise, print an overloaded method with pieces corresponding |
| 669 | * to each function in "methods". |
| 670 | */ |
| 671 | void python_generator::print_method(const isl_class &clazz, |
| 672 | const string &fullname, const function_set &methods, |
| 673 | vector<string> super) |
| 674 | { |
| 675 | string cname; |
| 676 | function_set::const_iterator it; |
| 677 | FunctionDecl *any_method; |
| 678 | |
| 679 | any_method = *methods.begin(); |
| 680 | if (methods.size() == 1 && !is_overload(any_method)) { |
| 681 | print_method(clazz, fullname: any_method, methods: super); |
| 682 | return; |
| 683 | } |
| 684 | |
| 685 | cname = clazz.method_name(any_method); |
| 686 | |
| 687 | print_method_def(is_static(clazz, any_method), cname); |
| 688 | printf(format: "(*args):\n" ); |
| 689 | |
| 690 | for (it = methods.begin(); it != methods.end(); ++it) |
| 691 | print_method_overload(clazz, *it); |
| 692 | printf(format: " raise Error\n" ); |
| 693 | } |
| 694 | |
| 695 | /* Print a python method "name" corresponding to "fd" setting |
| 696 | * the enum value "value". |
| 697 | * "super" contains the superclasses of the class to which the method belongs, |
| 698 | * with the first element corresponding to the annotation that appears |
| 699 | * closest to the annotated type. |
| 700 | * |
| 701 | * The last argument of the C function does not appear in the method call, |
| 702 | * but is fixed to "value" instead. |
| 703 | * Other than that, the method printed here is similar to one |
| 704 | * printed by python_generator::print_method, except that |
| 705 | * some of the special cases do not occur. |
| 706 | */ |
| 707 | void python_generator::print_set_enum(const isl_class &clazz, |
| 708 | FunctionDecl *fd, int value, const string &name, |
| 709 | const vector<string> &super) |
| 710 | { |
| 711 | string fullname = fd->getName().str(); |
| 712 | int num_params = fd->getNumParams(); |
| 713 | |
| 714 | print_method_header(is_static: is_static(clazz, fd), name, n_arg: num_params - 1); |
| 715 | |
| 716 | print_type_checks(name, fd, false, num_params - 1, super); |
| 717 | printf(format: " ctx = arg0.ctx\n" ); |
| 718 | printf(format: " res = isl.%s(" , fullname.c_str()); |
| 719 | for (int i = 0; i < num_params - 1; ++i) { |
| 720 | if (i) |
| 721 | printf(format: ", " ); |
| 722 | print_arg_in_call(fd, fixed_arg_fmt, i, 0); |
| 723 | } |
| 724 | printf(format: ", %d" , value); |
| 725 | printf(format: ")\n" ); |
| 726 | print_method_return(8, clazz, fd, fixed_arg_fmt); |
| 727 | } |
| 728 | |
| 729 | /* Print python methods corresponding to "fd", which sets an enum. |
| 730 | * "super" contains the superclasses of the class to which the method belongs, |
| 731 | * with the first element corresponding to the annotation that appears |
| 732 | * closest to the annotated type. |
| 733 | * |
| 734 | * A method is generated for each value in the enum, setting |
| 735 | * the enum to that value. |
| 736 | */ |
| 737 | void python_generator::print_set_enum(const isl_class &clazz, |
| 738 | FunctionDecl *fd, const vector<string> &super) |
| 739 | { |
| 740 | vector<set_enum>::const_iterator it; |
| 741 | const vector<set_enum> &set_enums = clazz.set_enums.at(fd); |
| 742 | |
| 743 | for (it = set_enums.begin(); it != set_enums.end(); ++it) |
| 744 | print_set_enum(clazz, fd, it->value, it->method_name, super); |
| 745 | } |
| 746 | |
| 747 | /* Print part of the constructor for this isl_class. |
| 748 | * |
| 749 | * In particular, check if the actual arguments correspond to the |
| 750 | * formal arguments of "cons" and if so call "cons" and put the |
| 751 | * result in self.ptr and a reference to the default context in self.ctx. |
| 752 | */ |
| 753 | void python_generator::print_constructor(const isl_class &clazz, |
| 754 | FunctionDecl *cons) |
| 755 | { |
| 756 | string fullname = cons->getName().str(); |
| 757 | string cname = clazz.method_name(cons); |
| 758 | int num_params = cons->getNumParams(); |
| 759 | int drop_ctx = first_arg_is_isl_ctx(cons); |
| 760 | |
| 761 | print_argument_checks(clazz, cons, drop_ctx); |
| 762 | printf(format: " self.ctx = Context.getDefaultInstance()\n" ); |
| 763 | printf(format: " self.ptr = isl.%s(" , fullname.c_str()); |
| 764 | if (drop_ctx) |
| 765 | printf(format: "self.ctx" ); |
| 766 | for (int i = drop_ctx; i < num_params; ++i) { |
| 767 | if (i) |
| 768 | printf(format: ", " ); |
| 769 | print_arg_in_call(cons, var_arg_fmt, i, drop_ctx); |
| 770 | } |
| 771 | printf(format: ")\n" ); |
| 772 | printf(format: " return\n" ); |
| 773 | } |
| 774 | |
| 775 | /* The definition of the part of constructor for the "id" class |
| 776 | * that construct an object from a name and a user object, |
| 777 | * without the initial newline. |
| 778 | * |
| 779 | * Just like the parts generated by python_generator::print_constructor, |
| 780 | * the result of the isl_id_alloc call is stored in self.ptr and |
| 781 | * a reference to the default context is stored in self.ctx. |
| 782 | * Also, just like any other constructor or method with a string argument, |
| 783 | * the python string is first encoded as a byte sequence, |
| 784 | * using 'ascii' as encoding. |
| 785 | * |
| 786 | * Since the isl_id keeps a reference to the Python user object, |
| 787 | * the reference count of the Python object needs to be incremented, |
| 788 | * but only if the construction of the isl_id is successful. |
| 789 | * The reference count of the Python object is decremented again |
| 790 | * by Context.free_user when the reference count of the isl_id |
| 791 | * drops to zero. |
| 792 | */ |
| 793 | static const char *const id_constructor_user = &R"( |
| 794 | if len(args) == 2 and type(args[0]) == str: |
| 795 | self.ctx = Context.getDefaultInstance() |
| 796 | name = args[0].encode('ascii') |
| 797 | self.ptr = isl.isl_id_alloc(self.ctx, name, args[1]) |
| 798 | self.ptr = isl.isl_id_set_free_user(self.ptr, Context.free_user) |
| 799 | if self.ptr is not None: |
| 800 | pythonapi.Py_IncRef(py_object(args[1])) |
| 801 | return |
| 802 | )" [1]; |
| 803 | |
| 804 | /* Print any special constructor parts of this class that are not |
| 805 | * automatically derived from the C interface. |
| 806 | * |
| 807 | * In particular, print a special constructor part for the "id" class. |
| 808 | */ |
| 809 | void python_generator::print_special_constructors(const isl_class &clazz) |
| 810 | { |
| 811 | if (clazz.name != "isl_id" ) |
| 812 | return; |
| 813 | |
| 814 | printf(format: "%s" , id_constructor_user); |
| 815 | } |
| 816 | |
| 817 | /* The definition of an "id" method |
| 818 | * for retrieving the user object associated to the identifier, |
| 819 | * without the initial newline. |
| 820 | * |
| 821 | * The isl_id needs to have been created by the constructor |
| 822 | * in id_constructor_user. That is, it needs to have a user pointer and |
| 823 | * it needs to have its free_user callback set to Context.free_user. |
| 824 | * The functions need to be cast to c_void_p to be able to compare |
| 825 | * the addresses. |
| 826 | * |
| 827 | * Return None if any of the checks fail. |
| 828 | * Note that isl_id_get_user returning NULL automatically results in None. |
| 829 | */ |
| 830 | static const char *const id_user = &R"( |
| 831 | def user(self): |
| 832 | free_user = cast(Context.free_user, c_void_p) |
| 833 | id_free_user = cast(isl.isl_id_get_free_user(self.ptr), c_void_p) |
| 834 | if id_free_user.value != free_user.value: |
| 835 | return None |
| 836 | return isl.isl_id_get_user(self.ptr) |
| 837 | )" [1]; |
| 838 | |
| 839 | /* Print any special methods of this class that are not |
| 840 | * automatically derived from the C interface. |
| 841 | * |
| 842 | * In particular, print a special method for the "id" class. |
| 843 | */ |
| 844 | void python_generator::print_special_methods(const isl_class &clazz) |
| 845 | { |
| 846 | if (clazz.name != "isl_id" ) |
| 847 | return; |
| 848 | |
| 849 | printf(format: "%s" , id_user); |
| 850 | } |
| 851 | |
| 852 | /* If "clazz" has a type function describing subclasses, |
| 853 | * then add constructors that allow each of these subclasses |
| 854 | * to be treated as an object to the superclass. |
| 855 | */ |
| 856 | void python_generator::print_upcast_constructors(const isl_class &clazz) |
| 857 | { |
| 858 | map<int, string>::const_iterator i; |
| 859 | |
| 860 | if (!clazz.fn_type) |
| 861 | return; |
| 862 | |
| 863 | for (i = clazz.type_subclasses.begin(); |
| 864 | i != clazz.type_subclasses.end(); ++i) { |
| 865 | printf(format: " if len(args) == 1 and " |
| 866 | "isinstance(args[0], %s):\n" , |
| 867 | type2python(name: i->second).c_str()); |
| 868 | printf(format: " self.ctx = args[0].ctx\n" ); |
| 869 | printf(format: " self.ptr = isl.%s_copy(args[0].ptr)\n" , |
| 870 | clazz.name.c_str()); |
| 871 | printf(format: " return\n" ); |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | /* Print the header of the class "name" with superclasses "super". |
| 876 | * The order of the superclasses is the opposite of the order |
| 877 | * in which the corresponding annotations appear in the source code. |
| 878 | * If "clazz" is a subclass derived from a type function, |
| 879 | * then the immediate superclass is recorded in "clazz" itself. |
| 880 | */ |
| 881 | void python_generator::(const isl_class &clazz, |
| 882 | const string &name, const vector<string> &super) |
| 883 | { |
| 884 | printf(format: "class %s" , name.c_str()); |
| 885 | if (super.size() > 0) { |
| 886 | printf(format: "(" ); |
| 887 | for (unsigned i = 0; i < super.size(); ++i) { |
| 888 | if (i > 0) |
| 889 | printf(format: ", " ); |
| 890 | printf(format: "%s" , type2python(name: super[i]).c_str()); |
| 891 | } |
| 892 | printf(format: ")" ); |
| 893 | } else if (clazz.is_type_subclass()) { |
| 894 | printf(format: "(%s)" , type2python(name: clazz.superclass_name).c_str()); |
| 895 | } else { |
| 896 | printf(format: "(object)" ); |
| 897 | } |
| 898 | printf(format: ":\n" ); |
| 899 | } |
| 900 | |
| 901 | /* Tell ctypes about the return type of "fd". |
| 902 | * In particular, if "fd" returns a pointer to an isl object, |
| 903 | * then tell ctypes it returns a "c_void_p". |
| 904 | * If "fd" returns a char *, then simply tell ctypes. |
| 905 | * |
| 906 | * Nothing needs to be done for functions returning |
| 907 | * isl_bool, isl_stat or isl_size since they are represented by an int and |
| 908 | * ctypes assumes that a function returns int by default. |
| 909 | */ |
| 910 | void python_generator::print_restype(FunctionDecl *fd) |
| 911 | { |
| 912 | string fullname = fd->getName().str(); |
| 913 | QualType type = fd->getReturnType(); |
| 914 | if (is_isl_type(type)) |
| 915 | printf(format: "isl.%s.restype = c_void_p\n" , fullname.c_str()); |
| 916 | else if (is_string(type)) |
| 917 | printf(format: "isl.%s.restype = POINTER(c_char)\n" , fullname.c_str()); |
| 918 | } |
| 919 | |
| 920 | /* Tell ctypes about the types of the arguments of the function "fd". |
| 921 | * |
| 922 | * Any callback argument is followed by a user pointer argument. |
| 923 | * Each such pair or arguments is handled together. |
| 924 | */ |
| 925 | void python_generator::print_argtypes(FunctionDecl *fd) |
| 926 | { |
| 927 | string fullname = fd->getName().str(); |
| 928 | int n = fd->getNumParams(); |
| 929 | |
| 930 | printf(format: "isl.%s.argtypes = [" , fullname.c_str()); |
| 931 | for (int i = 0; i < n; ++i) { |
| 932 | ParmVarDecl *param = fd->getParamDecl(i); |
| 933 | QualType type = param->getOriginalType(); |
| 934 | if (i) |
| 935 | printf(format: ", " ); |
| 936 | if (is_isl_ctx(type)) |
| 937 | printf(format: "Context" ); |
| 938 | else if (is_isl_type(type)) |
| 939 | printf(format: "c_void_p" ); |
| 940 | else if (is_callback(type)) |
| 941 | printf(format: "c_void_p, c_void_p" ); |
| 942 | else if (is_string(type)) |
| 943 | printf(format: "c_char_p" ); |
| 944 | else if (is_long(type)) |
| 945 | printf(format: "c_long" ); |
| 946 | else |
| 947 | printf(format: "c_int" ); |
| 948 | |
| 949 | if (is_callback(type)) |
| 950 | ++i; |
| 951 | } |
| 952 | printf(format: "]\n" ); |
| 953 | } |
| 954 | |
| 955 | /* Print type definitions for the method 'fd'. |
| 956 | */ |
| 957 | void python_generator::print_method_type(FunctionDecl *fd) |
| 958 | { |
| 959 | print_restype(fd); |
| 960 | print_argtypes(fd); |
| 961 | } |
| 962 | |
| 963 | /* If "clazz" has a type function describing subclasses or |
| 964 | * if it is one of those type subclasses, then print a __new__ method. |
| 965 | * |
| 966 | * In the superclass, the __new__ method constructs an object |
| 967 | * of the subclass type specified by the type function, |
| 968 | * raising an error on an error type. |
| 969 | * In the subclass, the __new__ method reverts to the original behavior. |
| 970 | */ |
| 971 | void python_generator::print_new(const isl_class &clazz, |
| 972 | const string &python_name) |
| 973 | { |
| 974 | if (!clazz.fn_type && !clazz.is_type_subclass()) |
| 975 | return; |
| 976 | |
| 977 | printf(format: " def __new__(cls, *args, **keywords):\n" ); |
| 978 | |
| 979 | if (clazz.fn_type) { |
| 980 | map<int, string>::const_iterator i; |
| 981 | |
| 982 | printf(format: " if \"ptr\" in keywords:\n" ); |
| 983 | printf(" type = isl.%s(keywords[\"ptr\"])\n" , |
| 984 | clazz.fn_type->getNameAsString().c_str()); |
| 985 | |
| 986 | for (i = clazz.type_subclasses.begin(); |
| 987 | i != clazz.type_subclasses.end(); ++i) { |
| 988 | printf(format: " if type == %d:\n" , i->first); |
| 989 | printf(format: " return %s(**keywords)\n" , |
| 990 | type2python(name: i->second).c_str()); |
| 991 | } |
| 992 | printf(format: " raise Error\n" ); |
| 993 | } |
| 994 | |
| 995 | printf(format: " return super(%s, cls).__new__(cls)\n" , |
| 996 | python_name.c_str()); |
| 997 | } |
| 998 | |
| 999 | /* Print declarations for methods printing the class representation, |
| 1000 | * provided there is a corresponding *_to_str function. |
| 1001 | * |
| 1002 | * In particular, provide an implementation of __str__ and __repr__ methods to |
| 1003 | * override the default representation used by python. Python uses __str__ to |
| 1004 | * pretty print the class (e.g., when calling print(obj)) and uses __repr__ |
| 1005 | * when printing a precise representation of an object (e.g., when dumping it |
| 1006 | * in the REPL console). |
| 1007 | * |
| 1008 | * Check the type of the argument before calling the *_to_str function |
| 1009 | * on it in case the method was called on an object from a subclass. |
| 1010 | * |
| 1011 | * The return value of the *_to_str function is decoded to a python string |
| 1012 | * assuming an 'ascii' encoding. This is necessary for python 3 compatibility. |
| 1013 | */ |
| 1014 | void python_generator::print_representation(const isl_class &clazz, |
| 1015 | const string &python_name) |
| 1016 | { |
| 1017 | if (!clazz.fn_to_str) |
| 1018 | return; |
| 1019 | |
| 1020 | printf(format: " def __str__(arg0):\n" ); |
| 1021 | print_type_check(indent: 8, type: python_name, fmt: fixed_arg_fmt, pos: 0, upcast: false, super: "" , name: "" , n: -1); |
| 1022 | printf(format: " ptr = isl.%s(arg0.ptr)\n" , |
| 1023 | string(clazz.fn_to_str->getName()).c_str()); |
| 1024 | printf(format: " res = cast(ptr, c_char_p).value.decode('ascii')\n" ); |
| 1025 | printf(format: " libc.free(ptr)\n" ); |
| 1026 | printf(format: " return res\n" ); |
| 1027 | printf(format: " def __repr__(self):\n" ); |
| 1028 | printf(format: " s = str(self)\n" ); |
| 1029 | printf(format: " if '\"' in s:\n" ); |
| 1030 | printf(format: " return 'isl.%s(\"\"\"%%s\"\"\")' %% s\n" , |
| 1031 | python_name.c_str()); |
| 1032 | printf(format: " else:\n" ); |
| 1033 | printf(format: " return 'isl.%s(\"%%s\")' %% s\n" , |
| 1034 | python_name.c_str()); |
| 1035 | } |
| 1036 | |
| 1037 | /* If "clazz" has any persistent callbacks, then print the definition |
| 1038 | * of a "copy_callbacks" function that copies the persistent callbacks |
| 1039 | * from one object to another. |
| 1040 | */ |
| 1041 | void python_generator::print_copy_callbacks(const isl_class &clazz) |
| 1042 | { |
| 1043 | const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks; |
| 1044 | set<FunctionDecl *>::const_iterator in; |
| 1045 | |
| 1046 | if (!clazz.has_persistent_callbacks()) |
| 1047 | return; |
| 1048 | |
| 1049 | printf(format: " def copy_callbacks(self, obj):\n" ); |
| 1050 | for (in = callbacks.begin(); in != callbacks.end(); ++in) { |
| 1051 | string callback_name = clazz.persistent_callback_name(*in); |
| 1052 | |
| 1053 | printf(format: " if hasattr(obj, '%s'):\n" , |
| 1054 | callback_name.c_str()); |
| 1055 | printf(format: " self.%s = obj.%s\n" , |
| 1056 | callback_name.c_str(), callback_name.c_str()); |
| 1057 | } |
| 1058 | } |
| 1059 | |
| 1060 | /* Print code to set method type signatures. |
| 1061 | * |
| 1062 | * To be able to call C functions it is necessary to explicitly set their |
| 1063 | * argument and result types. Do this for all exported constructors and |
| 1064 | * methods (including those that set a persistent callback and |
| 1065 | * those that set an enum value), |
| 1066 | * as well as for the *_to_str and the type function, if they exist. |
| 1067 | * Assuming each exported class has a *_copy and a *_free method, |
| 1068 | * also unconditionally set the type of such methods. |
| 1069 | */ |
| 1070 | void python_generator::print_method_types(const isl_class &clazz) |
| 1071 | { |
| 1072 | function_set::const_iterator in; |
| 1073 | map<string, function_set>::const_iterator it; |
| 1074 | map<FunctionDecl *, vector<set_enum> >::const_iterator ie; |
| 1075 | const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks; |
| 1076 | |
| 1077 | for (in = clazz.constructors.begin(); in != clazz.constructors.end(); |
| 1078 | ++in) |
| 1079 | print_method_type(*in); |
| 1080 | |
| 1081 | for (in = callbacks.begin(); in != callbacks.end(); ++in) |
| 1082 | print_method_type(*in); |
| 1083 | for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it) |
| 1084 | for (in = it->second.begin(); in != it->second.end(); ++in) |
| 1085 | print_method_type(*in); |
| 1086 | for (ie = clazz.set_enums.begin(); ie != clazz.set_enums.end(); ++ie) |
| 1087 | print_method_type(ie->first); |
| 1088 | |
| 1089 | print_method_type(clazz.fn_copy); |
| 1090 | print_method_type(clazz.fn_free); |
| 1091 | if (clazz.fn_to_str) |
| 1092 | print_method_type(clazz.fn_to_str); |
| 1093 | if (clazz.fn_type) |
| 1094 | print_method_type(clazz.fn_type); |
| 1095 | } |
| 1096 | |
| 1097 | /* Print out the definition of this isl_class. |
| 1098 | * |
| 1099 | * We first check if this isl_class is a subclass of one or more other classes. |
| 1100 | * If it is, we make sure those superclasses are printed out first. |
| 1101 | * |
| 1102 | * Then we print a constructor with several cases, one for constructing |
| 1103 | * a Python object from a return value, one for each function that |
| 1104 | * was marked as a constructor, a class specific constructor, if any, and |
| 1105 | * one for each type based subclass. |
| 1106 | * |
| 1107 | * Next, we print out some common methods, class specific methods and |
| 1108 | * the methods corresponding |
| 1109 | * to functions that are not marked as constructors, including those |
| 1110 | * that set a persistent callback and those that set an enum value. |
| 1111 | * |
| 1112 | * Finally, we tell ctypes about the types of the arguments of the |
| 1113 | * constructor functions and the return types of those function returning |
| 1114 | * an isl object. |
| 1115 | */ |
| 1116 | void python_generator::print(const isl_class &clazz) |
| 1117 | { |
| 1118 | string p_name = type2python(name: clazz.subclass_name); |
| 1119 | vector<string> super = find_superclasses(clazz.type); |
| 1120 | const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks; |
| 1121 | |
| 1122 | for (unsigned i = 0; i < super.size(); ++i) |
| 1123 | if (done.find(x: super[i]) == done.end()) |
| 1124 | print(clazz: classes[super[i]]); |
| 1125 | if (clazz.is_type_subclass() && done.find(x: clazz.name) == done.end()) |
| 1126 | print(clazz: classes[clazz.name]); |
| 1127 | done.insert(x: clazz.subclass_name); |
| 1128 | |
| 1129 | printf(format: "\n" ); |
| 1130 | print_class_header(clazz, name: p_name, super); |
| 1131 | printf(format: " def __init__(self, *args, **keywords):\n" ); |
| 1132 | |
| 1133 | printf(format: " if \"ptr\" in keywords:\n" ); |
| 1134 | printf(format: " self.ctx = keywords[\"ctx\"]\n" ); |
| 1135 | printf(format: " self.ptr = keywords[\"ptr\"]\n" ); |
| 1136 | printf(format: " return\n" ); |
| 1137 | |
| 1138 | for (const auto &cons : clazz.constructors) |
| 1139 | print_constructor(clazz, cons); |
| 1140 | print_special_constructors(clazz); |
| 1141 | print_upcast_constructors(clazz); |
| 1142 | printf(format: " raise Error\n" ); |
| 1143 | printf(format: " def __del__(self):\n" ); |
| 1144 | printf(format: " if hasattr(self, 'ptr'):\n" ); |
| 1145 | printf(format: " isl.%s_free(self.ptr)\n" , clazz.name.c_str()); |
| 1146 | |
| 1147 | print_new(clazz, python_name: p_name); |
| 1148 | print_representation(clazz, python_name: p_name); |
| 1149 | print_copy_callbacks(clazz); |
| 1150 | |
| 1151 | print_special_methods(clazz); |
| 1152 | for (const auto &callback : callbacks) |
| 1153 | print_method(clazz, callback, super); |
| 1154 | for (const auto &kvp : clazz.methods) |
| 1155 | print_method(clazz, fullname: kvp.first, methods: kvp.second, super); |
| 1156 | for (const auto &kvp : clazz.set_enums) |
| 1157 | print_set_enum(clazz, kvp.first, super); |
| 1158 | |
| 1159 | printf(format: "\n" ); |
| 1160 | |
| 1161 | print_method_types(clazz); |
| 1162 | } |
| 1163 | |
| 1164 | /* Generate a python interface based on the extracted types and |
| 1165 | * functions. |
| 1166 | * |
| 1167 | * Print out each class in turn. If one of these is a subclass of some |
| 1168 | * other class, make sure the superclass is printed out first. |
| 1169 | * functions. |
| 1170 | */ |
| 1171 | void python_generator::generate() |
| 1172 | { |
| 1173 | map<string, isl_class>::iterator ci; |
| 1174 | |
| 1175 | for (ci = classes.begin(); ci != classes.end(); ++ci) { |
| 1176 | if (done.find(x: ci->first) == done.end()) |
| 1177 | print(ci->second); |
| 1178 | } |
| 1179 | } |
| 1180 | |