static char* S_primitive_callback_def(CFCMethod *method, const char *callback_start, const char *refcount_mods) { const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *micro_sym = CFCMethod_micro_sym(method); char callback_func[50]; if (CFCType_is_integer(return_type)) { strcpy(callback_func, "S_finish_callback_i64"); } else if (CFCType_is_floating(return_type)) { strcpy(callback_func, "S_finish_callback_f64"); } else { CFCUtil_die("Unexpected type: %s", ret_type_str); } char pattern[] = "%s\n" "%s(%s) {\n" "%s" " %s retval = (%s)%s(\"%s\");%s\n" " return retval;\n" "}\n"; char *callback_def = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params, callback_start, ret_type_str, ret_type_str, callback_func, micro_sym, refcount_mods); return callback_def; }
/* Create a macro definition that aliases to a function name directly, since * this method may not be overridden. */ static char* S_final_method_def(CFCMethod *method, CFCClass *klass) { const char *self_type = CFCType_to_c(CFCMethod_self_type(method)); const char *full_func_sym = CFCMethod_implementing_func_sym(method); const char *arg_names = CFCParamList_name_list(CFCMethod_get_param_list(method)); size_t meth_sym_size = CFCMethod_full_method_sym(method, klass, NULL, 0); char *full_meth_sym = (char*)MALLOCATE(meth_sym_size); CFCMethod_full_method_sym(method, klass, full_meth_sym, meth_sym_size); size_t offset_sym_size = CFCMethod_full_offset_sym(method, klass, NULL, 0); char *full_offset_sym = (char*)MALLOCATE(offset_sym_size); CFCMethod_full_offset_sym(method, klass, full_offset_sym, offset_sym_size); const char pattern[] = "extern size_t %s;\n#define %s(%s) \\\n %s((%s)%s)\n"; size_t size = sizeof(pattern) + strlen(full_offset_sym) + strlen(full_meth_sym) + strlen(arg_names) + strlen(full_func_sym) + strlen(self_type) + strlen(arg_names) + 20; char *method_def = (char*)MALLOCATE(size); sprintf(method_def, pattern, full_offset_sym, full_meth_sym, arg_names, full_func_sym, self_type, arg_names); FREEMEM(full_offset_sym); FREEMEM(full_meth_sym); return method_def; }
static char* S_invalid_callback_def(CFCMethod *method) { char *full_method_sym = CFCMethod_full_method_sym(method, NULL); const char *override_sym = CFCMethod_full_override_sym(method); CFCParamList *param_list = CFCMethod_get_param_list(method); const char *params = CFCParamList_to_c(param_list); CFCVariable **param_vars = CFCParamList_get_variables(param_list); // Thwart compiler warnings. CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); char *unused = S_build_unused_vars(param_vars); char *unreachable = S_maybe_unreachable(return_type); char pattern[] = "%s\n" "%s(%s) {%s\n" " CFISH_THROW(CFISH_ERR, \"Can't override %s via binding\");%s\n" "}\n"; char *callback_def = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params, unused, full_method_sym, unreachable); FREEMEM(full_method_sym); FREEMEM(unreachable); FREEMEM(unused); return callback_def; }
char* CFCBindMeth_abstract_method_def(CFCMethod *method) { CFCParamList *param_list = CFCMethod_get_param_list(method); const char *params = CFCParamList_to_c(param_list); const char *full_func_sym = CFCMethod_imp_func(method); const char *vtable_var = CFCType_get_vtable_var(CFCMethod_self_type(method)); CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *macro_sym = CFCMethod_get_macro_sym(method); // Thwart compiler warnings. CFCVariable **param_vars = CFCParamList_get_variables(param_list); char *unused = S_build_unused_vars(param_vars + 1); char *return_statement = S_maybe_unreachable(return_type); char pattern[] = "%s\n" "%s(%s) {\n" " cfish_String *klass = self ? CFISH_Obj_Get_Class_Name((cfish_Obj*)self) : %s->name;%s\n" " CFISH_THROW(CFISH_ERR, \"Abstract method '%s' not defined by %%o\", klass);%s\n" "}\n"; char *abstract_def = CFCUtil_sprintf(pattern, ret_type_str, full_func_sym, params, vtable_var, unused, macro_sym, return_statement); FREEMEM(unused); FREEMEM(return_statement); return abstract_def; }
static char* S_meth_top(CFCMethod *method) { CFCParamList *param_list = CFCMethod_get_param_list(method); if (CFCParamList_num_vars(param_list) == 1) { char pattern[] = "(PyObject *self, PyObject *unused) {\n" " CFISH_UNUSED_VAR(unused);\n" ; return CFCUtil_sprintf(pattern); } else { char *error = NULL; char *arg_parsing = S_gen_arg_parsing(param_list, 1, &error); if (error) { CFCUtil_die("%s in %s", error, CFCMethod_get_name(method)); } if (!arg_parsing) { return NULL; } char *decs = S_gen_decs(param_list, 1); char pattern[] = "(PyObject *self, PyObject *args, PyObject *kwargs) {\n" "%s" // decs "%s" ; char *result = CFCUtil_sprintf(pattern, decs, arg_parsing); FREEMEM(arg_parsing); return result; } }
char* CFCBindMeth_imp_declaration(CFCMethod *method) { CFCType *return_type = CFCMethod_get_return_type(method); CFCParamList *param_list = CFCMethod_get_param_list(method); const char *ret_type_str = CFCType_to_c(return_type); const char *full_imp_sym = CFCMethod_imp_func(method); const char *param_list_str = CFCParamList_to_c(param_list); char *buf = CFCUtil_sprintf("%s\n%s(%s);", ret_type_str, full_imp_sym, param_list_str); return buf; }
char* CFCBindMeth_abstract_method_def(CFCMethod *method, CFCClass *klass) { CFCType *ret_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(ret_type); CFCType *type = CFCMethod_self_type(method); const char *class_var = CFCType_get_class_var(type); const char *meth_name = CFCMethod_get_name(method); CFCParamList *param_list = CFCMethod_get_param_list(method); const char *params = CFCParamList_to_c(param_list); CFCVariable **vars = CFCParamList_get_variables(param_list); const char *invocant = CFCVariable_get_name(vars[0]); // All variables other than the invocant are unused, and the return is // unreachable. char *unused = CFCUtil_strdup(""); for (int i = 1; vars[i] != NULL; i++) { const char *var_name = CFCVariable_get_name(vars[i]); size_t size = strlen(unused) + strlen(var_name) + 80; unused = (char*)REALLOCATE(unused, size); strcat(unused, "\n CFISH_UNUSED_VAR("); strcat(unused, var_name); strcat(unused, ");"); } char *unreachable; if (!CFCType_is_void(ret_type)) { unreachable = CFCUtil_sprintf(" CFISH_UNREACHABLE_RETURN(%s);\n", ret_type_str); } else { unreachable = CFCUtil_strdup(""); } char *full_func_sym = CFCMethod_imp_func(method, klass); char pattern[] = "%s\n" "%s(%s) {\n" "%s" " cfish_Err_abstract_method_call((cfish_Obj*)%s, %s, \"%s\");\n" "%s" "}\n"; char *abstract_def = CFCUtil_sprintf(pattern, ret_type_str, full_func_sym, params, unused, invocant, class_var, meth_name, unreachable); FREEMEM(unused); FREEMEM(unreachable); FREEMEM(full_func_sym); return abstract_def; }
char* CFCPyMethod_callback_def(CFCMethod *method, CFCClass *invoker) { CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **vars = CFCParamList_get_variables(param_list); CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *params = CFCParamList_to_c(param_list); char *override_sym = CFCMethod_full_override_sym(method, invoker); char *content; if (CFCMethod_can_be_bound(method)) { char *py_args = S_build_py_args(param_list); char *invocation = S_build_pymeth_invocation(method); char *refcount_mods = S_callback_refcount_mods(param_list); const char *maybe_return = CFCType_is_void(return_type) ? "" : " return cfcb_RESULT;\n"; const char pattern[] = "%s\n" "%s(%s) {\n" "%s\n" "%s\n" "%s" "%s" "}\n"; content = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params, py_args, invocation, refcount_mods, maybe_return); } else { char *unused = S_build_unused_vars(vars); char *unreachable = S_maybe_unreachable(return_type); char *meth_sym = CFCMethod_full_method_sym(method, invoker); const char pattern[] = "%s\n" "%s(%s) {%s\n" " CFISH_THROW(CFISH_ERR, \"Can't override %s via binding\");%s\n" "}\n"; content = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params, unused, meth_sym, unreachable); FREEMEM(meth_sym); FREEMEM(unused); FREEMEM(unreachable); } FREEMEM(override_sym); return content; }
char* CFCBindMeth_callback_dec(CFCMethod *method) { CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); char pattern[] = "%s\n" "%s(%s);\n"; char *callback_dec = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params); return callback_dec; }
static char* S_obj_callback_def(CFCMethod *method, const char *callback_params, const char *refcount_mods) { const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *cb_func_name = CFCType_is_string_type(return_type) ? "cfish_Host_callback_str" : "cfish_Host_callback_obj"; char *nullable_check = CFCUtil_strdup(""); if (!CFCType_nullable(return_type)) { const char *macro_sym = CFCMethod_get_macro_sym(method); char pattern[] = "\n if (!retval) { CFISH_THROW(CFISH_ERR, " "\"%s() for class '%%o' cannot return NULL\", " "Cfish_Obj_Get_Class_Name((cfish_Obj*)self)); }"; size_t size = sizeof(pattern) + strlen(macro_sym) + 30; nullable_check = (char*)REALLOCATE(nullable_check, size); sprintf(nullable_check, pattern, macro_sym); } char pattern[] = "%s\n" "%s(%s) {\n" " %s retval = (%s)%s(%s);%s%s\n" " return retval;\n" "}\n"; size_t size = sizeof(pattern) + strlen(ret_type_str) + strlen(override_sym) + strlen(params) + strlen(ret_type_str) + strlen(ret_type_str) + strlen(cb_func_name) + strlen(callback_params) + strlen(nullable_check) + strlen(refcount_mods) + 30; char *callback_def = (char*)MALLOCATE(size); sprintf(callback_def, pattern, ret_type_str, override_sym, params, ret_type_str, ret_type_str, cb_func_name, callback_params, nullable_check, refcount_mods); FREEMEM(nullable_check); return callback_def; }
char* CFCBindMeth_typedef_dec(struct CFCMethod *method, CFCClass *klass) { const char *params_end = CFCParamList_to_c(CFCMethod_get_param_list(method)); while (*params_end && *params_end != '*') { params_end++; } const char *self_struct = CFCClass_full_struct_sym(klass); const char *ret_type = CFCType_to_c(CFCMethod_get_return_type(method)); char *full_typedef = CFCMethod_full_typedef(method, klass); char *buf = CFCUtil_sprintf("typedef %s\n(*%s)(%s%s);\n", ret_type, full_typedef, self_struct, params_end); FREEMEM(full_typedef); return buf; }
static char* S_virtual_method_def(CFCMethod *method, CFCClass *klass) { CFCParamList *param_list = CFCMethod_get_param_list(method); const char *PREFIX = CFCClass_get_PREFIX(klass); const char *invoker_struct = CFCClass_full_struct_sym(klass); char *full_meth_sym = CFCMethod_full_method_sym(method, klass); char *full_offset_sym = CFCMethod_full_offset_sym(method, klass); char *full_typedef = CFCMethod_full_typedef(method, klass); // Prepare parameter lists, minus invoker. The invoker gets forced to // "self" later. if (CFCParamList_variadic(param_list)) { CFCUtil_die("Variadic methods not supported"); } const char *arg_names_minus_invoker = CFCParamList_name_list(param_list); const char *params_minus_invoker = CFCParamList_to_c(param_list); while (*arg_names_minus_invoker && *arg_names_minus_invoker != ',') { arg_names_minus_invoker++; } while (*params_minus_invoker && *params_minus_invoker != ',') { params_minus_invoker++; } // Prepare a return statement... or not. CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *maybe_return = CFCType_is_void(return_type) ? "" : "return "; const char pattern[] = "extern %sVISIBLE size_t %s;\n" "static CFISH_INLINE %s\n" "%s(%s *self%s) {\n" " const %s method = (%s)cfish_obj_method(self, %s);\n" " %smethod(self%s);\n" "}\n"; char *method_def = CFCUtil_sprintf(pattern, PREFIX, full_offset_sym, ret_type_str, full_meth_sym, invoker_struct, params_minus_invoker, full_typedef, full_typedef, full_offset_sym, maybe_return, arg_names_minus_invoker); FREEMEM(full_offset_sym); FREEMEM(full_meth_sym); FREEMEM(full_typedef); return method_def; }
char* CFCPyMethod_wrapper(CFCMethod *method, CFCClass *invoker) { CFCParamList *param_list = CFCMethod_get_param_list(method); CFCType *return_type = CFCMethod_get_return_type(method); char *meth_sym = CFCMethod_full_method_sym(method, invoker); char *meth_top = S_meth_top(method); char *increfs = S_gen_arg_increfs(param_list, 1); char *decrefs = S_gen_decrefs(param_list, 1); char *invocation = S_gen_meth_invocation(method, invoker); char *ret; if (CFCType_is_void(return_type)) { ret = CFCUtil_strdup(" Py_RETURN_NONE;\n"); } else if (CFCType_incremented(return_type)) { ret = CFCUtil_strdup(" return CFBind_cfish_to_py_zeroref((cfish_Obj*)retvalCF);\n"); } else { char *conv = CFCPyTypeMap_c_to_py(return_type, "retvalCF"); ret = CFCUtil_sprintf(" return %s;\n", conv); FREEMEM(conv); } char pattern[] = "static PyObject*\n" "S_%s%s" "%s" // increfs "%s" // invocation "%s" // decrefs " if (CFBind_migrate_cferr()) {\n" " return NULL;\n" " }\n" "%s" // ret "}\n" ; char *wrapper = CFCUtil_sprintf(pattern, meth_sym, meth_top, increfs, invocation, decrefs, ret); FREEMEM(ret); FREEMEM(invocation); FREEMEM(decrefs); FREEMEM(increfs); FREEMEM(meth_sym); FREEMEM(meth_top); return wrapper; }
static char* S_void_callback_def(CFCMethod *method, const char *callback_start, const char *refcount_mods) { const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); const char *micro_sym = CFCMethod_micro_sym(method); const char pattern[] = "void\n" "%s(%s) {\n" "%s" " S_finish_callback_void(\"%s\");%s\n" "}\n"; char *callback_def = CFCUtil_sprintf(pattern, override_sym, params, callback_start, micro_sym, refcount_mods); return callback_def; }
char* CFCBindMeth_typedef_dec(struct CFCMethod *method, CFCClass *klass) { const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); const char *ret_type = CFCType_to_c(CFCMethod_get_return_type(method)); size_t full_typedef_size = CFCMethod_full_typedef(method, klass, NULL, 0); char *full_typedef = (char*)MALLOCATE(full_typedef_size); CFCMethod_full_typedef(method, klass, full_typedef, full_typedef_size); size_t size = strlen(params) + strlen(ret_type) + strlen(full_typedef) + 20 + sizeof("\0"); char *buf = (char*)MALLOCATE(size); sprintf(buf, "typedef %s\n(*%s)(%s);\n", ret_type, full_typedef, params); FREEMEM(full_typedef); return buf; }
char* CFCPyMethod_pymethoddef(CFCMethod *method, CFCClass *invoker) { CFCParamList *param_list = CFCMethod_get_param_list(method); const char *flags = CFCParamList_num_vars(param_list) == 1 ? "METH_NOARGS" : "METH_KEYWORDS|METH_VARARGS"; char *meth_sym = CFCMethod_full_method_sym(method, invoker); char *micro_sym = CFCUtil_strdup(CFCSymbol_get_name((CFCSymbol*)method)); for (int i = 0; micro_sym[i] != 0; i++) { micro_sym[i] = tolower(micro_sym[i]); } char pattern[] = "{\"%s\", (PyCFunction)S_%s, %s, NULL},"; char *py_meth_def = CFCUtil_sprintf(pattern, micro_sym, meth_sym, flags); FREEMEM(meth_sym); FREEMEM(micro_sym); return py_meth_def; }
static char* S_primitive_callback_def(CFCMethod *method, const char *callback_params, const char *refcount_mods) { const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); char cb_func_name[40]; if (CFCType_is_floating(return_type)) { strcpy(cb_func_name, "cfish_Host_callback_f64"); } else if (CFCType_is_integer(return_type)) { strcpy(cb_func_name, "cfish_Host_callback_i64"); } else if (strcmp(ret_type_str, "void*") == 0) { strcpy(cb_func_name, "cfish_Host_callback_host"); } else { CFCUtil_die("unrecognized type: %s", ret_type_str); } char pattern[] = "%s\n" "%s(%s) {\n" " return (%s)%s(%s);%s\n" "}\n"; size_t size = sizeof(pattern) + strlen(ret_type_str) + strlen(override_sym) + strlen(params) + strlen(ret_type_str) + strlen(cb_func_name) + strlen(callback_params) + strlen(refcount_mods) + 20; char *callback_def = (char*)MALLOCATE(size); sprintf(callback_def, pattern, ret_type_str, override_sym, params, ret_type_str, cb_func_name, callback_params, refcount_mods); return callback_def; }
static char* S_void_callback_def(CFCMethod *method, const char *callback_params, const char *refcount_mods) { const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); const char pattern[] = "void\n" "%s(%s) {\n" " cfish_Host_callback(%s);%s\n" "}\n"; size_t size = sizeof(pattern) + strlen(override_sym) + strlen(params) + strlen(callback_params) + strlen(refcount_mods) + 200; char *callback_def = (char*)MALLOCATE(size); sprintf(callback_def, pattern, override_sym, params, callback_params, refcount_mods); return callback_def; }
/* Create a macro definition that aliases to a function name directly, since * this method may not be overridden. */ static char* S_final_method_def(CFCMethod *method, CFCClass *klass) { const char *self_type = CFCType_to_c(CFCMethod_self_type(method)); const char *full_func_sym = CFCMethod_imp_func(method); const char *arg_names = CFCParamList_name_list(CFCMethod_get_param_list(method)); char *full_meth_sym = CFCMethod_full_method_sym(method, klass); char *full_offset_sym = CFCMethod_full_offset_sym(method, klass); const char pattern[] = "extern size_t %s;\n" "#define %s(%s) \\\n" " %s((%s)%s)\n"; char *method_def = CFCUtil_sprintf(pattern, full_offset_sym, full_meth_sym, arg_names, full_func_sym, self_type, arg_names); FREEMEM(full_offset_sym); FREEMEM(full_meth_sym); return method_def; }
CFCPerlMethod* CFCPerlMethod_init(CFCPerlMethod *self, CFCMethod *method, const char *alias) { CFCParamList *param_list = CFCMethod_get_param_list(method); const char *class_name = CFCMethod_get_class_name(method); int use_labeled_params = CFCParamList_num_vars(param_list) > 2 ? 1 : 0; // The Clownfish destructor needs to be spelled DESTROY for Perl. if (!alias) { alias = CFCMethod_micro_sym(method); } static const char destroy_uppercase[] = "DESTROY"; if (strcmp(alias, "destroy") == 0) { alias = destroy_uppercase; } CFCPerlSub_init((CFCPerlSub*)self, param_list, class_name, alias, use_labeled_params); self->method = (CFCMethod*)CFCBase_incref((CFCBase*)method); return self; }
static char* S_gen_meth_invocation(CFCMethod *method, CFCClass *invoker) { CFCParamList *param_list = CFCMethod_get_param_list(method); char *full_meth = CFCMethod_full_method_sym(method, invoker); char *meth_type_c = CFCMethod_full_typedef(method, invoker); const char *class_var = CFCClass_full_class_var(invoker); char *first_arg = CFCUtil_sprintf("(%s*)self", CFCClass_full_struct_sym(invoker)); char *arg_list = S_gen_arg_list(param_list, first_arg); CFCType *return_type = CFCMethod_get_return_type(method); char *maybe_declare; const char *maybe_assign; if (CFCType_is_void(return_type)) { maybe_declare = CFCUtil_strdup(""); maybe_assign = ""; } else { maybe_declare = CFCUtil_sprintf(" %s retvalCF;\n", CFCType_to_c(return_type)); maybe_assign = "retvalCF = "; } const char pattern[] = "%s" " %s method = CFISH_METHOD_PTR(%s, %s);\n" " CFBIND_TRY(%smethod(%s));\n" ; char *content = CFCUtil_sprintf(pattern, maybe_declare, meth_type_c, class_var, full_meth, maybe_assign, arg_list); FREEMEM(arg_list); FREEMEM(first_arg); FREEMEM(maybe_declare); FREEMEM(full_meth); FREEMEM(meth_type_c); return content; }
static char* S_callback_refcount_mods(CFCMethod *method) { char *refcount_mods = CFCUtil_strdup(""); CFCType *return_type = CFCMethod_get_return_type(method); CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); // `XSBind_perl_to_cfish()` returns an incremented object. If this method // does not return an incremented object, we must cancel out that // refcount. (No function can return a decremented object.) if (CFCType_is_object(return_type) && !CFCType_incremented(return_type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_DECREF(retval);", NULL); } // Adjust refcounts of arguments per method signature, so that Perl code // does not have to. for (int i = 0; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; CFCType *type = CFCVariable_get_type(var); const char *name = CFCVariable_micro_sym(var); if (!CFCType_is_object(type)) { continue; } else if (CFCType_incremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_INCREF(", name, ");", NULL); } else if (CFCType_decremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_DECREF(", name, ");", NULL); } } return refcount_mods; }
static char* S_callback_refcount_mods(CFCMethod *method) { char *refcount_mods = CFCUtil_strdup(""); CFCType *return_type = CFCMethod_get_return_type(method); CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); // Host_callback_obj returns an incremented object. If this method does // not return an incremented object, we must cancel out that refcount. // (No function can return a decremented object.) if (CFCType_is_object(return_type) && !CFCType_incremented(return_type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_DECREF(retval);", NULL); } // The Host_callback_xxx functions have no effect on the refcounts of // arguments, so we need to adjust them after the fact. for (int i = 0; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; CFCType *type = CFCVariable_get_type(var); const char *name = CFCVariable_micro_sym(var); if (!CFCType_is_object(type)) { continue; } else if (CFCType_incremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_INCREF(", name, ");", NULL); } else if (CFCType_decremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_DECREF(", name, ");", NULL); } } return refcount_mods; }
static char* S_obj_callback_def(CFCMethod *method, const char *callback_start, const char *refcount_mods) { const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *micro_sym = CFCMethod_micro_sym(method); const char *nullable = CFCType_nullable(return_type) ? "true" : "false"; char pattern[] = "%s\n" "%s(%s) {\n" "%s" " %s retval = (%s)S_finish_callback_obj(self, \"%s\", %s);%s\n" " return retval;\n" "}\n"; char *callback_def = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params, callback_start, ret_type_str, ret_type_str, micro_sym, nullable, refcount_mods); return callback_def; }
static char* S_method_def(CFCMethod *method, CFCClass *klass, int optimized_final_meth) { CFCParamList *param_list = CFCMethod_get_param_list(method); const char *PREFIX = CFCClass_get_PREFIX(klass); const char *invoker_struct = CFCClass_full_struct_sym(klass); const char *self_name = CFCParamList_param_name(param_list, 0); char *full_meth_sym = CFCMethod_full_method_sym(method, klass); char *full_offset_sym = CFCMethod_full_offset_sym(method, klass); char *full_typedef = CFCMethod_full_typedef(method, klass); char *full_imp_sym = CFCMethod_imp_func(method, klass); // Prepare parameter lists, minus the type of the invoker. if (CFCParamList_variadic(param_list)) { CFCUtil_die("Variadic methods not supported"); } const char *arg_names = CFCParamList_name_list(param_list); const char *params_end = CFCParamList_to_c(param_list); while (*params_end && *params_end != '*') { params_end++; } // Prepare a return statement... or not. CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *maybe_return = CFCType_is_void(return_type) ? "" : "return "; const char innards_pattern[] = " const %s method = (%s)cfish_obj_method(%s, %s);\n" " %smethod(%s);\n" ; char *innards = CFCUtil_sprintf(innards_pattern, full_typedef, full_typedef, self_name, full_offset_sym, maybe_return, arg_names); if (optimized_final_meth) { CFCParcel *parcel = CFCClass_get_parcel(klass); const char *privacy_sym = CFCParcel_get_privacy_sym(parcel); char *invoker_cast = CFCUtil_strdup(""); if (!CFCMethod_is_fresh(method, klass)) { CFCType *self_type = CFCMethod_self_type(method); invoker_cast = CFCUtil_cat(invoker_cast, "(", CFCType_to_c(self_type), ")", NULL); } const char pattern[] = "#ifdef %s\n" " %s%s(%s%s);\n" "#else\n" "%s" "#endif\n" ; char *temp = CFCUtil_sprintf(pattern, privacy_sym, maybe_return, full_imp_sym, invoker_cast, arg_names, innards); FREEMEM(innards); innards = temp; FREEMEM(invoker_cast); } const char pattern[] = "extern %sVISIBLE uint32_t %s;\n" "static CFISH_INLINE %s\n" "%s(%s%s) {\n" "%s" "}\n"; char *method_def = CFCUtil_sprintf(pattern, PREFIX, full_offset_sym, ret_type_str, full_meth_sym, invoker_struct, params_end, innards); FREEMEM(innards); FREEMEM(full_imp_sym); FREEMEM(full_offset_sym); FREEMEM(full_meth_sym); FREEMEM(full_typedef); return method_def; }
static char* S_xsub_body(CFCPerlMethod *self) { CFCMethod *method = self->method; CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char *name_list = CFCParamList_name_list(param_list); char *body = CFCUtil_strdup(""); CFCParcel *parcel = CFCMethod_get_parcel(method); const char *class_name = CFCMethod_get_class_name(method); CFCClass *klass = CFCClass_fetch_singleton(parcel, class_name); if (!klass) { CFCUtil_die("Can't find a CFCClass for '%s'", class_name); } // Extract the method function pointer. char *full_typedef = CFCMethod_full_typedef(method, klass); char *full_meth = CFCMethod_full_method_sym(method, klass); char *method_ptr = CFCUtil_sprintf("%s method = CFISH_METHOD_PTR(%s, %s);\n ", full_typedef, CFCClass_full_vtable_var(klass), full_meth); body = CFCUtil_cat(body, method_ptr, NULL); FREEMEM(full_typedef); FREEMEM(full_meth); FREEMEM(method_ptr); // Compensate for functions which eat refcounts. for (int i = 0; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; CFCType *type = CFCVariable_get_type(var); if (CFCType_is_object(type) && CFCType_decremented(type)) { body = CFCUtil_cat(body, "CFISH_INCREF(", CFCVariable_micro_sym(var), ");\n ", NULL); } } if (CFCType_is_void(CFCMethod_get_return_type(method))) { // Invoke method in void context. body = CFCUtil_cat(body, "method(", name_list, ");\n XSRETURN(0);", NULL); } else { // Return a value for method invoked in a scalar context. CFCType *return_type = CFCMethod_get_return_type(method); const char *type_str = CFCType_to_c(return_type); char *assignment = CFCPerlTypeMap_to_perl(return_type, "retval"); if (!assignment) { CFCUtil_die("Can't find typemap for '%s'", type_str); } body = CFCUtil_cat(body, type_str, " retval = method(", name_list, ");\n ST(0) = ", assignment, ";", NULL); if (CFCType_is_object(return_type) && CFCType_incremented(return_type) ) { body = CFCUtil_cat(body, "\n CFISH_DECREF(retval);", NULL); } body = CFCUtil_cat(body, "\n sv_2mortal( ST(0) );\n XSRETURN(1);", NULL); FREEMEM(assignment); } return body; }
static char* S_xsub_def_positional_args(CFCPerlMethod *self) { CFCMethod *method = self->method; CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char **arg_inits = CFCParamList_get_initial_values(param_list); unsigned num_vars = (unsigned)CFCParamList_num_vars(param_list); char *body = S_xsub_body(self); // Determine how many args are truly required and build an error check. unsigned min_required = 0; for (unsigned i = 0; i < num_vars; i++) { if (arg_inits[i] == NULL) { min_required = i + 1; } } char *xs_name_list = num_vars > 0 ? CFCUtil_strdup(CFCVariable_micro_sym(arg_vars[0])) : CFCUtil_strdup(""); for (unsigned i = 1; i < num_vars; i++) { const char *var_name = CFCVariable_micro_sym(arg_vars[i]); if (i < min_required) { xs_name_list = CFCUtil_cat(xs_name_list, ", ", var_name, NULL); } else { xs_name_list = CFCUtil_cat(xs_name_list, ", [", var_name, "]", NULL); } } const char num_args_pattern[] = "if (items %s %u) { CFISH_THROW(CFISH_ERR, \"Usage: %%s(%s)\", GvNAME(CvGV(cv))); }"; char *num_args_check; if (min_required < num_vars) { num_args_check = CFCUtil_sprintf(num_args_pattern, "<", min_required, xs_name_list); } else { num_args_check = CFCUtil_sprintf(num_args_pattern, "!=", num_vars, xs_name_list); } // Var assignments. char *var_assignments = CFCUtil_strdup(""); for (unsigned i = 0; i < num_vars; i++) { CFCVariable *var = arg_vars[i]; const char *val = arg_inits[i]; const char *var_name = CFCVariable_micro_sym(var); CFCType *var_type = CFCVariable_get_type(var); const char *type_c = CFCType_to_c(var_type); if (i == 0) { // self const char *meth_micro_sym = CFCMethod_micro_sym(self->method); char *statement = S_self_assign_statement(self, var_type, meth_micro_sym); var_assignments = CFCUtil_cat(var_assignments, statement, NULL); FREEMEM(statement); } else { char perl_stack_var[30]; sprintf(perl_stack_var, "ST(%u)", i); char *conversion = CFCPerlTypeMap_from_perl(var_type, perl_stack_var); if (!conversion) { CFCUtil_die("Can't map type '%s'", type_c); } if (val) { char pattern[] = "\n %s %s = ( items >= %u && XSBind_sv_defined(ST(%u)) )" " ? %s : %s;"; char *statement = CFCUtil_sprintf(pattern, type_c, var_name, i, i, conversion, val); var_assignments = CFCUtil_cat(var_assignments, statement, NULL); FREEMEM(statement); } else { var_assignments = CFCUtil_cat(var_assignments, "\n ", type_c, " ", var_name, " = ", conversion, ";", NULL); } FREEMEM(conversion); } } char pattern[] = "XS(%s);\n" "XS(%s) {\n" " dXSARGS;\n" " CHY_UNUSED_VAR(cv);\n" " SP -= items;\n" " %s;\n" "\n" " /* Extract vars from Perl stack. */\n" " %s\n" "\n" " /* Execute */\n" " %s\n" "}\n"; char *xsub = CFCUtil_sprintf(pattern, self->sub.c_name, self->sub.c_name, num_args_check, var_assignments, body); FREEMEM(num_args_check); FREEMEM(var_assignments); FREEMEM(body); return xsub; }
static char* S_callback_start(CFCMethod *method) { CFCParamList *param_list = CFCMethod_get_param_list(method); static const char pattern[] = " dSP;\n" " EXTEND(SP, %d);\n" " ENTER;\n" " SAVETMPS;\n" " PUSHMARK(SP);\n" " mPUSHs((SV*)Cfish_Obj_To_Host((cfish_Obj*)self));\n"; int num_args = (int)CFCParamList_num_vars(param_list) - 1; int num_to_extend = num_args == 0 ? 1 : num_args == 1 ? 2 : 1 + (num_args * 2); char *params = CFCUtil_sprintf(pattern, num_to_extend); // Iterate over arguments, mapping them to Perl scalars. CFCVariable **arg_vars = CFCParamList_get_variables(param_list); for (int i = 1; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; const char *name = CFCVariable_micro_sym(var); CFCType *type = CFCVariable_get_type(var); const char *c_type = CFCType_to_c(type); // Add labels when there are two or more parameters. if (num_args > 1) { char num_buf[20]; sprintf(num_buf, "%d", (int)strlen(name)); params = CFCUtil_cat(params, " mPUSHp(\"", name, "\", ", num_buf, ");\n", NULL); } if (CFCType_is_string_type(type)) { // Convert Clownfish string type to UTF-8 Perl string scalars. params = CFCUtil_cat(params, " mPUSHs(XSBind_cb_to_sv(", "(cfish_CharBuf*)", name, "));\n", NULL); } else if (CFCType_is_object(type)) { // Wrap other Clownfish object types in Perl objects. params = CFCUtil_cat(params, " mPUSHs(XSBind_cfish_to_perl(", "(cfish_Obj*)", name, "));\n", NULL); } else if (CFCType_is_integer(type)) { // Convert primitive integer types to IV Perl scalars. int width = (int)CFCType_get_width(type); if (width != 0 && width <= 4) { params = CFCUtil_cat(params, " mPUSHi(", name, ");\n", NULL); } else { // If the Perl IV integer type is not wide enough, use // doubles. This may be lossy if the value is above 2**52, // but practically speaking, it's important to handle numbers // between 2**32 and 2**52 cleanly. params = CFCUtil_cat(params, " if (sizeof(IV) >= sizeof(", c_type, ")) { mPUSHi(", name, "); }\n", " else { mPUSHn((double)", name, "); } // lossy \n", NULL); } } else if (CFCType_is_floating(type)) { // Convert primitive floating point types to NV Perl scalars. params = CFCUtil_cat(params, " mPUSHn(", name, ");\n", NULL); } else { // Can't map variable type. Signal to caller. FREEMEM(params); return NULL; } } // Restore the Perl stack pointer. params = CFCUtil_cat(params, " PUTBACK;\n", NULL); return params; }
static char* S_virtual_method_def(CFCMethod *method, CFCClass *klass) { CFCParamList *param_list = CFCMethod_get_param_list(method); const char *invoker_struct = CFCClass_full_struct_sym(klass); const char *common_struct = CFCType_get_specifier(CFCMethod_self_type(method)); const char *visibility = CFCClass_included(klass) ? "CHY_IMPORT" : "CHY_EXPORT"; size_t meth_sym_size = CFCMethod_full_method_sym(method, klass, NULL, 0); char *full_meth_sym = (char*)MALLOCATE(meth_sym_size); CFCMethod_full_method_sym(method, klass, full_meth_sym, meth_sym_size); size_t offset_sym_size = CFCMethod_full_offset_sym(method, klass, NULL, 0); char *full_offset_sym = (char*)MALLOCATE(offset_sym_size); CFCMethod_full_offset_sym(method, klass, full_offset_sym, offset_sym_size); size_t full_typedef_size = CFCMethod_full_typedef(method, klass, NULL, 0); char *full_typedef = (char*)MALLOCATE(full_typedef_size); CFCMethod_full_typedef(method, klass, full_typedef, full_typedef_size); // Prepare parameter lists, minus invoker. The invoker gets forced to // "self" later. const char *arg_names_minus_invoker = CFCParamList_name_list(param_list); const char *params_minus_invoker = CFCParamList_to_c(param_list); while (*arg_names_minus_invoker && *arg_names_minus_invoker != ',') { arg_names_minus_invoker++; } while (*params_minus_invoker && *params_minus_invoker != ',') { params_minus_invoker++; } // Prepare a return statement... or not. CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *maybe_return = CFCType_is_void(return_type) ? "" : "return "; const char pattern[] = "extern %s size_t %s;\n" "static CHY_INLINE %s\n" "%s(const %s *self%s) {\n" " char *const method_address = *(char**)self + %s;\n" " const %s method = *((%s*)method_address);\n" " %smethod((%s*)self%s);\n" "}\n"; size_t size = sizeof(pattern) + strlen(visibility) + strlen(full_offset_sym) + strlen(ret_type_str) + strlen(full_meth_sym) + strlen(invoker_struct) + strlen(params_minus_invoker) + strlen(full_offset_sym) + strlen(full_typedef) + strlen(full_typedef) + strlen(maybe_return) + strlen(common_struct) + strlen(arg_names_minus_invoker) + 40; char *method_def = (char*)MALLOCATE(size); sprintf(method_def, pattern, visibility, full_offset_sym, ret_type_str, full_meth_sym, invoker_struct, params_minus_invoker, full_offset_sym, full_typedef, full_typedef, maybe_return, common_struct, arg_names_minus_invoker); FREEMEM(full_offset_sym); FREEMEM(full_meth_sym); FREEMEM(full_typedef); return method_def; }
static char* S_callback_params(CFCMethod *method) { const char *micro_sym = CFCSymbol_micro_sym((CFCSymbol*)method); CFCParamList *param_list = CFCMethod_get_param_list(method); unsigned num_params = CFCParamList_num_vars(param_list) - 1; size_t needed = strlen(micro_sym) + 30; char *params = (char*)MALLOCATE(needed); // TODO: use something other than micro_sym here. sprintf(params, "self, \"%s\", %u", micro_sym, num_params); // Iterate over arguments, mapping them to various arg wrappers which // conform to Host's callback interface. CFCVariable **arg_vars = CFCParamList_get_variables(param_list); for (int i = 1; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; const char *name = CFCVariable_micro_sym(var); size_t name_len = strlen(name); CFCType *type = CFCVariable_get_type(var); const char *c_type = CFCType_to_c(type); size_t size = strlen(params) + strlen(c_type) + name_len * 2 + 30; char *new_buf = (char*)MALLOCATE(size); if (CFCType_is_string_type(type)) { sprintf(new_buf, "%s, CFISH_ARG_STR(\"%s\", %s)", params, name, name); } else if (CFCType_is_object(type)) { sprintf(new_buf, "%s, CFISH_ARG_OBJ(\"%s\", %s)", params, name, name); } else if (CFCType_is_integer(type)) { int width = CFCType_get_width(type); if (width) { if (width <= 4) { sprintf(new_buf, "%s, CFISH_ARG_I32(\"%s\", %s)", params, name, name); } else { sprintf(new_buf, "%s, CFISH_ARG_I64(\"%s\", %s)", params, name, name); } } else { sprintf(new_buf, "%s, CFISH_ARG_I(%s, \"%s\", %s)", params, c_type, name, name); } } else if (CFCType_is_floating(type)) { sprintf(new_buf, "%s, CFISH_ARG_F64(\"%s\", %s)", params, name, name); } else { // Can't map variable type. Signal to caller. FREEMEM(params); FREEMEM(new_buf); return NULL; } FREEMEM(params); params = new_buf; } return params; }