// Generate C code which initializes method metadata. char* CFCPerlClass_method_metadata_code(CFCPerlClass *self) { const char *class_var = CFCClass_full_class_var(self->client); CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client); char *code = CFCUtil_strdup(""); for (int i = 0; fresh_methods[i] != NULL; i++) { CFCMethod *method = fresh_methods[i]; if (!CFCMethod_novel(method)) { continue; } const char *macro_sym = CFCMethod_get_macro_sym(method); const char *alias = CFCMethod_get_host_alias(method); if (alias) { code = CFCUtil_cat(code, " CFISH_Class_Add_Host_Method_Alias(", class_var, ", \"", alias, "\", \"", macro_sym, "\");\n", NULL); } if (CFCMethod_excluded_from_host(method)) { code = CFCUtil_cat(code, " CFISH_Class_Exclude_Host_Method(", class_var, ", \"", macro_sym, "\");\n", NULL); } } return code; }
static void S_run_overridden_tests(CFCTest *test) { CFCParser *parser = CFCParser_new(); CFCParcel *neato_parcel = CFCTest_parse_parcel(test, parser, "parcel Neato;"); CFCType *return_type = CFCTest_parse_type(test, parser, "Obj*"); CFCParamList *param_list = CFCTest_parse_param_list(test, parser, "(Foo *self)"); CFCMethod *orig = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo", "Return_An_Obj", return_type, param_list, NULL, 0, 0); CFCParamList *overrider_param_list = CFCTest_parse_param_list(test, parser, "(FooJr *self)"); CFCMethod *overrider = CFCMethod_new(neato_parcel, NULL, "Neato::Foo::FooJr", "FooJr", "Return_An_Obj", return_type, overrider_param_list, NULL, 0, 0); CFCMethod_override(overrider, orig); OK(test, !CFCMethod_novel(overrider), "A Method which overrides another is not 'novel'"); CFCBase_decref((CFCBase*)parser); CFCBase_decref((CFCBase*)neato_parcel); CFCBase_decref((CFCBase*)return_type); CFCBase_decref((CFCBase*)param_list); CFCBase_decref((CFCBase*)orig); CFCBase_decref((CFCBase*)overrider_param_list); CFCBase_decref((CFCBase*)overrider); CFCParcel_reap_singletons(); }
CFCMethod* CFCClass_find_novel_method(CFCClass *self, const char *sym) { if (!self->tree_grown) { CFCUtil_die("Can't call original_method before grow_tree"); } CFCClass *ancestor = self; do { CFCMethod *method = CFCClass_method(ancestor, sym); if (method && CFCMethod_novel(method)) { return method; } } while (NULL != (ancestor = CFCClass_get_parent(ancestor))); return NULL; }
static void S_lazy_init_method_bindings(CFCGoClass *self) { if (self->method_bindings) { return; } CFCUTIL_NULL_CHECK(self->client); size_t num_bound = 0; CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client); CFCGoMethod **bound = (CFCGoMethod**)CALLOCATE(1, sizeof(CFCGoMethod*)); // Iterate over the class's fresh methods. for (size_t i = 0; fresh_methods[i] != NULL; i++) { CFCMethod *method = fresh_methods[i]; // Skip methods which have been explicitly excluded. if (CFCMethod_excluded_from_host(method)) { continue; } // Skip methods that shouldn't be bound. if (!CFCMethod_can_be_bound(method)) { continue; } // Only include novel methods. if (!CFCMethod_novel(method)) { continue; } const char *sym = CFCMethod_get_name(method); if (!CFCClass_fresh_method(self->client, sym)) { continue; } /* Create the binding, add it to the array. */ CFCGoMethod *meth_binding = CFCGoMethod_new(method); size_t size = (num_bound + 2) * sizeof(CFCGoMethod*); bound = (CFCGoMethod**)REALLOCATE(bound, size); bound[num_bound] = meth_binding; num_bound++; bound[num_bound] = NULL; } self->method_bindings = bound; self->num_bound = num_bound; }
static char* S_callback_decs(CFCClass *klass) { CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); char *cb_decs = CFCUtil_strdup(""); for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { CFCMethod *method = fresh_methods[meth_num]; // Define callback to NULL. if (CFCMethod_novel(method) && !CFCMethod_final(method)) { const char *override_sym = CFCMethod_full_override_sym(method); cb_decs = CFCUtil_cat(cb_decs, "#define ", override_sym, " NULL\n", NULL); } } return cb_decs; }
char* CFCBindMeth_spec_def(CFCMethod *method) { const char *macro_sym = CFCMethod_get_macro_sym(method); const char *impl_sym = CFCMethod_implementing_func_sym(method); int is_novel = CFCMethod_novel(method); const char *full_override_sym = "NULL"; if ((CFCMethod_public(method) || CFCMethod_abstract(method)) && is_novel) { full_override_sym = CFCMethod_full_override_sym(method); } size_t offset_sym_size = CFCMethod_full_offset_sym(method, NULL, NULL, 0); char *full_offset_sym = (char*)MALLOCATE(offset_sym_size); CFCMethod_full_offset_sym(method, NULL, full_offset_sym, offset_sym_size); char pattern[] = " {\n" " %d, /* is_novel */\n" " \"%s\", /* name */\n" " (cfish_method_t)%s, /* func */\n" " (cfish_method_t)%s, /* callback_func */\n" " &%s /* offset */\n" " }"; size_t size = sizeof(pattern) + 10 /* for is_novel */ + strlen(macro_sym) + strlen(impl_sym) + strlen(full_override_sym) + strlen(full_offset_sym) + 30; char *def = (char*)MALLOCATE(size); sprintf(def, pattern, is_novel, macro_sym, impl_sym, full_override_sym, full_offset_sym); FREEMEM(full_offset_sym); return def; }
char* CFCGoClass_go_typing(CFCGoClass *self) { char *content = NULL; if (!self->client) { CFCUtil_die("Can't find class for %s", self->class_name); } else if (CFCClass_inert(self->client)) { content = CFCUtil_strdup(""); } else { const char *short_struct = CFCClass_get_struct_sym(self->client); CFCClass *parent = CFCClass_get_parent(self->client); char *parent_type_str = NULL; if (parent) { const char *parent_struct = CFCClass_get_struct_sym(parent); CFCParcel *parent_parcel = CFCClass_get_parcel(parent); if (parent_parcel == self->parcel) { parent_type_str = CFCUtil_strdup(parent_struct); } else { char *parent_package = CFCGoTypeMap_go_short_package(parent_parcel); parent_type_str = CFCUtil_sprintf("%s.%s", parent_package, parent_struct); FREEMEM(parent_package); } } char *go_struct_def; if (parent && !self->suppress_struct) { go_struct_def = CFCUtil_sprintf("type %sIMP struct {\n\t%sIMP\n}\n", short_struct, parent_type_str); } else { go_struct_def = CFCUtil_strdup(""); } char *parent_iface; if (parent) { parent_iface = CFCUtil_sprintf("\t%s\n", parent_type_str); } else { parent_iface = CFCUtil_strdup(""); } char *novel_iface = CFCUtil_strdup(""); S_lazy_init_method_bindings(self); for (int i = 0; self->method_bindings[i] != NULL; i++) { CFCGoMethod *meth_binding = self->method_bindings[i]; CFCMethod *method = CFCGoMethod_get_client(meth_binding); if (method) { if (!CFCMethod_novel(method)) { continue; } const char *sym = CFCMethod_get_name(method); if (!CFCClass_fresh_method(self->client, sym)) { continue; } } const char *sig = CFCGoMethod_get_sig(meth_binding, self->client); novel_iface = CFCUtil_cat(novel_iface, "\t", sig, "\n", NULL); } char pattern[] = "type %s interface {\n" "%s" "%s" "}\n" "\n" "%s" ; content = CFCUtil_sprintf(pattern, short_struct, parent_iface, novel_iface, go_struct_def); FREEMEM(parent_type_str); FREEMEM(go_struct_def); FREEMEM(parent_iface); } return content; }
static char* S_gen_callbacks(CFCPython *self, CFCParcel *parcel, CFCClass **ordered) { char *callbacks = CFCUtil_strdup(""); // Generate implementation files containing callback definitions. for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass) || CFCClass_inert(klass) //|| CFCClass_get_parcel(klass) != parcel ) { continue; } CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { CFCMethod *method = fresh_methods[meth_num]; // Define callback. if (CFCMethod_novel(method) && !CFCMethod_final(method)) { char *cb_def = CFCPyMethod_callback_def(method, klass); callbacks = CFCUtil_cat(callbacks, cb_def, "\n", NULL); FREEMEM(cb_def); } } } static const char helpers[] = "static PyObject*\n" "S_pack_tuple(int num_args, ...) {\n" " PyObject *tuple = PyTuple_New(num_args);\n" " va_list args;\n" " va_start(args, num_args);\n" " for (int i = 0; i < num_args; i++) {\n" " PyObject *arg = va_arg(args, PyObject*);\n" " PyTuple_SET_ITEM(tuple, i, arg);\n" " }\n" " va_end(args);\n" " return tuple;\n" "}\n" "#define CFBIND_TRY(routine) \\\n" " do { \\\n" " jmp_buf env; \\\n" " jmp_buf *prev_env = CFBind_swap_env(&env); \\\n" " if (!setjmp(env)) { \\\n" " routine; \\\n" " } \\\n" " CFBind_swap_env(prev_env); \\\n" " } while (0)\n" "\n" "static PyObject*\n" "S_call_pymeth(PyObject *self, const char *meth_name, PyObject *args,\n" " const char *file, int line, const char *func) {\n" " PyObject *callable = PyObject_GetAttrString(self, meth_name);\n" " if (!PyCallable_Check(callable)) {\n" " cfish_String *mess\n" " = cfish_Err_make_mess(file, line, func, \"Attr '%s' not callable\",\n" " meth_name);\n" " cfish_Err_throw_mess(CFISH_ERR, mess);\n" " }\n" " PyObject *result = PyObject_CallObject(callable, args);\n" " Py_DECREF(args);\n" " if (result == NULL) {\n" " cfish_String *mess\n" " = cfish_Err_make_mess(file, line, func,\n" " \"Callback to '%s' failed\", meth_name);\n" " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" " }\n" " return result;\n" "}\n" "\n" "#define CALL_PYMETH_VOID(self, meth_name, args) \\\n" " S_call_pymeth_void(self, meth_name, args, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" "\n" "static void\n" "S_call_pymeth_void(PyObject *self, const char *meth_name, PyObject *args,\n" " const char *file, int line, const char *func) {\n" " PyObject *py_result\n" " = S_call_pymeth(self, meth_name, args, file, line, func);\n" " if (py_result == NULL) {\n" " cfish_String *mess\n" " = cfish_Err_make_mess(file, line, func, \"Call to %s failed\",\n" " meth_name);\n" " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" " }\n" " Py_DECREF(py_result);\n" "}\n" "\n" "#define CALL_PYMETH_BOOL(self, meth_name, args) \\\n" " S_call_pymeth_bool(self, meth_name, args, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" "\n" "static bool\n" "S_call_pymeth_bool(PyObject *self, const char *meth_name, PyObject *args,\n" " const char *file, int line, const char *func) {\n" " PyObject *py_result\n" " = S_call_pymeth(self, meth_name, args, file, line, func);\n" " int truthiness = py_result != NULL\n" " ? PyObject_IsTrue(py_result)\n" " : -1;\n" " if (truthiness == -1) {\n" " cfish_String *mess\n" " = cfish_Err_make_mess(file, line, func, \"Call to %s failed\",\n" " meth_name);\n" " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" " }\n" " Py_DECREF(py_result);\n" " return !!truthiness;\n" "}\n" "\n" "#define CALL_PYMETH_OBJ(self, meth_name, args, ret_class, nullable) \\\n" " S_call_pymeth_obj(self, meth_name, args, ret_class, nullable, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" "\n" "static cfish_Obj*\n" "S_call_pymeth_obj(PyObject *self, const char *meth_name,\n" " PyObject *args, cfish_Class *ret_class, bool nullable,\n" " const char *file, int line, const char *func) {\n" " PyObject *py_result\n" " = S_call_pymeth(self, meth_name, args, file, line, func);\n" " cfish_Obj *result = CFBind_py_to_cfish(py_result, ret_class);\n" " Py_DECREF(py_result);\n" " if (!nullable && result == NULL) {\n" " CFISH_THROW(CFISH_ERR, \"%s cannot return NULL\", meth_name);\n" " }\n" " else if (!cfish_Obj_is_a(result, ret_class)) {\n" " cfish_Class *result_class = cfish_Obj_get_class(result);\n" " CFISH_DECREF(result);\n" " CFISH_THROW(CFISH_ERR, \"%s returned %o instead of %o\", meth_name,\n" " CFISH_Class_Get_Name(result_class),\n" " CFISH_Class_Get_Name(ret_class));\n" " }\n" " return result;\n" "}\n" "\n" "#define CALL_PYMETH_DOUBLE(self, meth_name, args) \\\n" " S_call_pymeth_f64(self, meth_name, args, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" "#define CALL_PYMETH_FLOAT(self, meth_name, args) \\\n" " ((float)S_call_pymeth_f64(self, meth_name, args, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "\n" "static double\n" "S_call_pymeth_f64(PyObject *self, const char *meth_name, PyObject *args,\n" " const char *file, int line, const char *func) {\n" " PyObject *py_result\n" " = S_call_pymeth(self, meth_name, args, file, line, func);\n" " PyErr_Clear();\n" " double result = PyFloat_AsDouble(py_result);\n" " if (PyErr_Occurred()) {\n" " cfish_String *mess\n" " = cfish_Err_make_mess(file, line, func,\n" " \"Converting result of '%s' to double failed\",\n" " meth_name);\n" " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" " }\n" " Py_DECREF(py_result);\n" " return result;\n" "}\n" "\n" "#define CALL_PYMETH_INT64_T(self, meth_name, args) \\\n" " S_call_pymeth_i64(self, meth_name, args, INT64_MAX, INT64_MIN, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" "#define CALL_PYMETH_INT32_T(self, meth_name, args) \\\n" " ((int32_t)S_call_pymeth_i64(self, meth_name, args, INT32_MAX, INT32_MIN, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_INT16_T(self, meth_name, args) \\\n" " ((int16_t)S_call_pymeth_i64(self, meth_name, args, INT16_MAX, INT16_MIN, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_INT8_T(self, meth_name, args) \\\n" " ((int8_t)S_call_pymeth_i64(self, meth_name, args, INT8_MAX, INT8_MIN, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_CHAR(self, meth_name, args) \\\n" " ((char)S_call_pymeth_i64(self, meth_name, args, CHAR_MAX, CHAR_MIN, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_SHORT(self, meth_name, args) \\\n" " ((short)S_call_pymeth_i64(self, meth_name, args, SHRT_MAX, SHRT_MIN, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_INT(self, meth_name, args) \\\n" " ((int16_t)S_call_pymeth_i64(self, meth_name, args, INT_MAX, INT_MIN, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_LONG(self, meth_name, args) \\\n" " ((int16_t)S_call_pymeth_i64(self, meth_name, args, LONG_MAX, LONG_MIN, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "\n" "static int64_t\n" "S_call_pymeth_i64(PyObject *self, const char *meth_name, PyObject *args,\n" " int64_t max, int64_t min,\n" " const char *file, int line, const char *func) {\n" " PyObject *py_result\n" " = S_call_pymeth(self, meth_name, args, file, line, func);\n" " PyErr_Clear();\n" " int64_t result = PyLong_AsLongLong(py_result);\n" " if (PyErr_Occurred() || result > max || result < min) {\n" " cfish_String *mess\n" " = cfish_Err_make_mess(file, line, func,\n" " \"Converting result of '%s' to int64_t failed\",\n" " meth_name);\n" " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" " }\n" " Py_DECREF(py_result);\n" " return result;\n" "}\n" "\n" "#define CALL_PYMETH_UINT64_T(self, meth_name, args) \\\n" " S_call_pymeth_u64(self, meth_name, args, UINT64_MAX, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" "#define CALL_PYMETH_UINT32_T(self, meth_name, args) \\\n" " ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT32_MAX, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_UINT16_T(self, meth_name, args) \\\n" " ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT16_MAX, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_UINT8_T(self, meth_name, args) \\\n" " ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT8_MAX, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" "#define CALL_PYMETH_SIZE_T(self, meth_name, args) \\\n" " S_call_pymeth_u64(self, meth_name, args, SIZE_MAX, \\\n" " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" "\n" "static uint64_t\n" "S_call_pymeth_u64(PyObject *self, const char *meth_name, PyObject *args,\n" " uint64_t max,\n" " const char *file, int line, const char *func) {\n" " PyObject *py_result\n" " = S_call_pymeth(self, meth_name, args, file, line, func);\n" " PyErr_Clear();\n" " uint64_t result = PyLong_AsUnsignedLongLong(py_result);\n" " if (PyErr_Occurred()) {\n" " cfish_String *mess\n" " = cfish_Err_make_mess(file, line, func,\n" " \"Converting result of '%s' to uint64_t failed\",\n" " meth_name);\n" " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" " }\n" " Py_DECREF(py_result);\n" " return result;\n" "}\n" ; static const char pattern[] = "%s\n" "\n" "%s" ; char *content = CFCUtil_sprintf(pattern, helpers, callbacks); FREEMEM(callbacks); return content; }
static void S_write_host_c(CFCPerl *self, CFCParcel *parcel) { CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); const char *prefix = CFCParcel_get_prefix(parcel); const char *privacy_sym = CFCParcel_get_privacy_sym(parcel); char *includes = CFCUtil_strdup(""); char *cb_defs = CFCUtil_strdup(""); char *alias_adds = CFCUtil_strdup(""); for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_inert(klass)) { continue; } const char *class_prefix = CFCClass_get_prefix(klass); if (strcmp(class_prefix, prefix) != 0) { continue; } const char *class_name = CFCClass_get_name(klass); const char *include_h = CFCClass_include_h(klass); includes = CFCUtil_cat(includes, "#include \"", include_h, "\"\n", NULL); // Callbacks. CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { CFCMethod *method = fresh_methods[meth_num]; // Define callback. if (CFCMethod_novel(method) && !CFCMethod_final(method)) { char *cb_def = CFCPerlMethod_callback_def(method, klass); cb_defs = CFCUtil_cat(cb_defs, cb_def, "\n", NULL); FREEMEM(cb_def); } } // Add class aliases. CFCPerlClass *class_binding = CFCPerlClass_singleton(class_name); if (class_binding) { const char *class_var = CFCClass_full_class_var(klass); const char **aliases = CFCPerlClass_get_class_aliases(class_binding); for (size_t j = 0; aliases[j] != NULL; j++) { const char *alias = aliases[j]; int alias_len = (int)strlen(alias); const char pattern[] = " cfish_Class_add_alias_to_registry(" "%s, \"%s\", %d);\n"; char *alias_add = CFCUtil_sprintf(pattern, class_var, alias, alias_len); alias_adds = CFCUtil_cat(alias_adds, alias_add, NULL); FREEMEM(alias_add); } char *metadata_code = CFCPerlClass_method_metadata_code(class_binding); alias_adds = CFCUtil_cat(alias_adds, metadata_code, NULL); FREEMEM(metadata_code); } } const char pattern[] = "%s" "\n" "#define %s\n" // privacy_sym "\n" "#include \"%sperl.h\"\n" "#include \"XSBind.h\"\n" "#include \"Clownfish/Class.h\"\n" "#include \"Clownfish/Err.h\"\n" "#include \"Clownfish/Obj.h\"\n" "%s" "\n" "/* Avoid conflicts with Clownfish bool type. */\n" "#define HAS_BOOL\n" "#define PERL_NO_GET_CONTEXT\n" "#include \"EXTERN.h\"\n" "#include \"perl.h\"\n" "#include \"XSUB.h\"\n" "\n" "static void\n" "S_finish_callback_void(pTHX_ const char *meth_name) {\n" " int count = call_method(meth_name, G_VOID | G_DISCARD);\n" " if (count != 0) {\n" " CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n" " meth_name, (int32_t)count);\n" " }\n" " FREETMPS;\n" " LEAVE;\n" "}\n" "\n" "static CFISH_INLINE SV*\n" "SI_do_callback_sv(pTHX_ const char *meth_name) {\n" " int count = call_method(meth_name, G_SCALAR);\n" " if (count != 1) {\n" " CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n" " meth_name, (int32_t)count);\n" " }\n" " dSP;\n" " SV *return_sv = POPs;\n" " PUTBACK;\n" " return return_sv;\n" "}\n" "\n" "static int64_t\n" "S_finish_callback_i64(pTHX_ const char *meth_name) {\n" " SV *return_sv = SI_do_callback_sv(aTHX_ meth_name);\n" " int64_t retval;\n" " if (sizeof(IV) == 8) {\n" " retval = (int64_t)SvIV(return_sv);\n" " }\n" " else {\n" " if (SvIOK(return_sv)) {\n" " // It's already no more than 32 bits, so don't convert.\n" " retval = SvIV(return_sv);\n" " }\n" " else {\n" " // Maybe lossy.\n" " double temp = SvNV(return_sv);\n" " retval = (int64_t)temp;\n" " }\n" " }\n" " FREETMPS;\n" " LEAVE;\n" " return retval;\n" "}\n" "\n" "static double\n" "S_finish_callback_f64(pTHX_ const char *meth_name) {\n" " SV *return_sv = SI_do_callback_sv(aTHX_ meth_name);\n" " double retval = SvNV(return_sv);\n" " FREETMPS;\n" " LEAVE;\n" " return retval;\n" "}\n" "\n" "static cfish_Obj*\n" "S_finish_callback_obj(pTHX_ void *vself, const char *meth_name,\n" " int nullable) {\n" " SV *return_sv = SI_do_callback_sv(aTHX_ meth_name);\n" " cfish_Obj *retval\n" " = XSBind_perl_to_cfish_nullable(aTHX_ return_sv, CFISH_OBJ);\n" " FREETMPS;\n" " LEAVE;\n" " if (!nullable && !retval) {\n" " CFISH_THROW(CFISH_ERR, \"%%o#%%s cannot return NULL\",\n" " cfish_Obj_get_class_name((cfish_Obj*)vself),\n" " meth_name);\n" " }\n" " return retval;\n" "}\n" "\n" "%s" "\n" "void\n" "%sbootstrap_perl() {\n" " dTHX;\n" " %sbootstrap_parcel();\n" "\n" "%s" "}\n" "\n" "%s"; char *content = CFCUtil_sprintf(pattern, self->c_header, privacy_sym, prefix, includes, cb_defs, prefix, prefix, alias_adds, self->c_footer); const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy); char *host_c_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sperl.c", src_dest, prefix); CFCUtil_write_file(host_c_path, content, strlen(content)); FREEMEM(host_c_path); FREEMEM(content); FREEMEM(alias_adds); FREEMEM(cb_defs); FREEMEM(includes); FREEMEM(ordered); }
char* CFCPerlPod_methods_pod(CFCPerlPod *self, CFCClass *klass) { const char *class_name = CFCClass_get_name(klass); char *abstract_pod = CFCUtil_strdup(""); char *methods_pod = CFCUtil_strdup(""); // Start with methods that don't map to a Clownfish method. for (size_t i = 0; i < self->num_methods; i++) { NamePod meth_spec = self->methods[i]; CFCMethod *method = CFCClass_method(klass, meth_spec.func); if (method) { continue; } if (!meth_spec.pod) { CFCUtil_die("No POD specified for method '%s' in class '%s'", meth_spec.alias, CFCClass_get_name(klass)); } methods_pod = CFCUtil_cat(methods_pod, meth_spec.pod, "\n", NULL); } CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { CFCMethod *method = fresh_methods[meth_num]; const char *name = CFCMethod_get_name(method); char *meth_pod = NULL; // Try to find custom POD for method. NamePod *meth_spec = NULL; for (size_t j = 0; j < self->num_methods; j++) { NamePod *candidate = &self->methods[j]; const char *other_name = candidate->func; if (other_name && strcmp(other_name, name) == 0) { meth_spec = candidate; break; } } if (meth_spec) { // Found custom POD. if (meth_spec->pod) { meth_pod = CFCUtil_sprintf("%s\n", meth_spec->pod); } else { meth_pod = CFCPerlPod_gen_subroutine_pod((CFCCallable*)method, meth_spec->alias, klass, meth_spec->sample, class_name, false); } } else { // No custom POD found. Add POD for public methods with Perl // bindings. if (!CFCMethod_public(method) || CFCMethod_excluded_from_host(method) || !CFCMethod_can_be_bound(method) ) { continue; } // Only add POD for novel methods and the first implementation // of abstract methods. if (!CFCMethod_novel(method)) { if (CFCMethod_abstract(method)) { continue; } CFCClass *parent = CFCClass_get_parent(klass); CFCMethod *parent_method = CFCClass_method(parent, name); if (!CFCMethod_abstract(parent_method)) { continue; } } char *perl_name = CFCPerlMethod_perl_name(method); meth_pod = CFCPerlPod_gen_subroutine_pod((CFCCallable*)method, perl_name, klass, NULL, class_name, false); FREEMEM(perl_name); } if (CFCMethod_abstract(method)) { abstract_pod = CFCUtil_cat(abstract_pod, meth_pod, NULL); } else { methods_pod = CFCUtil_cat(methods_pod, meth_pod, NULL); } FREEMEM(meth_pod); } char *pod = CFCUtil_strdup(""); if (strlen(abstract_pod)) { pod = CFCUtil_cat(pod, "=head1 ABSTRACT METHODS\n\n", abstract_pod, NULL); } FREEMEM(abstract_pod); if (strlen(methods_pod)) { pod = CFCUtil_cat(pod, "=head1 METHODS\n\n", methods_pod, NULL); } FREEMEM(methods_pod); return pod; }
static void S_bequeath_methods(CFCClass *self) { for (size_t child_num = 0; self->children[child_num] != NULL; child_num++) { CFCClass *child = self->children[child_num]; // Create array of methods, preserving exact order so vtables match up. size_t num_methods = 0; size_t max_methods = self->num_methods + child->num_methods; CFCMethod **methods = (CFCMethod**)MALLOCATE( (max_methods + 1) * sizeof(CFCMethod*)); // Gather methods which child inherits or overrides. for (size_t i = 0; i < self->num_methods; i++) { CFCMethod *method = self->methods[i]; const char *macro_sym = CFCMethod_get_macro_sym(method); CFCMethod *child_method = CFCClass_method(child, macro_sym); if (child_method) { CFCMethod_override(child_method, method); methods[num_methods++] = child_method; } else { methods[num_methods++] = method; } } // Append novel child methods to array. Child methods which were just // marked via CFCMethod_override() a moment ago are skipped. for (size_t i = 0; i < child->num_methods; i++) { CFCMethod *method = child->methods[i]; if (CFCMethod_novel(method)) { methods[num_methods++] = method; } } methods[num_methods] = NULL; // Manage refcounts and assign new array. Transform to final methods // if child class is a final class. if (child->is_final) { for (size_t i = 0; i < num_methods; i++) { if (CFCMethod_final(methods[i])) { CFCBase_incref((CFCBase*)methods[i]); } else { methods[i] = CFCMethod_finalize(methods[i]); } } } else { for (size_t i = 0; i < num_methods; i++) { CFCBase_incref((CFCBase*)methods[i]); } } for (size_t i = 0; i < child->num_methods; i++) { CFCBase_decref((CFCBase*)child->methods[i]); } FREEMEM(child->methods); child->methods = methods; child->num_methods = num_methods; // Pass it all down to the next generation. S_bequeath_methods(child); child->tree_grown = true; } }
static void S_write_callbacks_c(CFCPerl *self) { CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); static const char pattern[] = "%s" "\n" "#include \"XSBind.h\"\n" "#include \"callbacks.h\"\n" "\n" "static void\n" "S_finish_callback_void(const char *meth_name) {\n" " int count = call_method(meth_name, G_VOID | G_DISCARD);\n" " if (count != 0) {\n" " CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n" " meth_name, (int32_t)count);\n" " }\n" " FREETMPS;\n" " LEAVE;\n" "}\n" "\n" "static CFISH_INLINE SV*\n" "SI_do_callback_sv(const char *meth_name) {\n" " int count = call_method(meth_name, G_SCALAR);\n" " if (count != 1) {\n" " CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n" " meth_name, (int32_t)count);\n" " }\n" " dSP;\n" " SV *return_sv = POPs;\n" " PUTBACK;\n" " return return_sv;\n" "}\n" "\n" "static int64_t\n" "S_finish_callback_i64(const char *meth_name) {\n" " SV *return_sv = SI_do_callback_sv(meth_name);\n" " int64_t retval;\n" " if (sizeof(IV) == 8) {\n" " retval = (int64_t)SvIV(return_sv);\n" " }\n" " else {\n" " if (SvIOK(return_sv)) {\n" " // It's already no more than 32 bits, so don't convert.\n" " retval = SvIV(return_sv);\n" " }\n" " else {\n" " // Maybe lossy.\n" " double temp = SvNV(return_sv);\n" " retval = (int64_t)temp;\n" " }\n" " }\n" " FREETMPS;\n" " LEAVE;\n" " return retval;\n" "}\n" "\n" "static double\n" "S_finish_callback_f64(const char *meth_name) {\n" " SV *return_sv = SI_do_callback_sv(meth_name);\n" " double retval = SvNV(return_sv);\n" " FREETMPS;\n" " LEAVE;\n" " return retval;\n" "}\n" "\n" "static cfish_Obj*\n" "S_finish_callback_obj(void *vself, const char *meth_name,\n" " int nullable) {\n" " SV *return_sv = SI_do_callback_sv(meth_name);\n" " cfish_Obj *retval = XSBind_perl_to_cfish(return_sv);\n" " FREETMPS;\n" " LEAVE;\n" " if (!nullable && !retval) {\n" " CFISH_THROW(CFISH_ERR, \"%%o#%%s cannot return NULL\",\n" " CFISH_Obj_Get_Class_Name((cfish_Obj*)vself),\n" " meth_name);\n" " }\n" " return retval;\n" "}\n" "\n"; char *content = CFCUtil_sprintf(pattern, self->header); for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_inert(klass)) { continue; } CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { CFCMethod *method = fresh_methods[meth_num]; // Define callback. if (CFCMethod_novel(method) && !CFCMethod_final(method)) { char *cb_def = CFCPerlMethod_callback_def(method); content = CFCUtil_cat(content, cb_def, "\n", NULL); FREEMEM(cb_def); } } FREEMEM(fresh_methods); } content = CFCUtil_cat(content, self->footer, NULL); // Write if changed. const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy); char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "callbacks.c", src_dest); CFCUtil_write_if_changed(filepath, content, strlen(content)); FREEMEM(filepath); FREEMEM(content); FREEMEM(ordered); }
void CFCBindSpecs_add_class(CFCBindSpecs *self, CFCClass *klass) { if (CFCClass_inert(klass)) { return; } const char *class_name = CFCClass_get_name(klass); const char *class_var = CFCClass_full_class_var(klass); const char *ivars_offset_name = CFCClass_full_ivars_offset(klass); const char *flags = CFCClass_final(klass) ? "cfish_ClassSpec_FINAL" : "0"; char *ivars_size = S_ivars_size(klass); char *parent_ptr = NULL; CFCClass *parent = CFCClass_get_parent(klass); if (!parent) { parent_ptr = CFCUtil_strdup("NULL"); } else { if (CFCClass_in_same_parcel(klass, parent)) { parent_ptr = CFCUtil_sprintf("&%s", CFCClass_full_class_var(parent)); } else { parent_ptr = CFCUtil_strdup("NULL"); const char *class_name = CFCClass_get_name(klass); const char *parent_var = CFCClass_full_class_var(parent); const char *pattern = " /* %s */\n" " class_specs[%d].parent = &%s;\n"; char *init_code = CFCUtil_sprintf(pattern, class_name, self->num_specs, parent_var); self->init_code = CFCUtil_cat(self->init_code, init_code, NULL); FREEMEM(init_code); } } int num_new_novel = 0; int num_new_overridden = 0; int num_new_inherited = 0; CFCMethod **methods = CFCClass_methods(klass); for (int meth_num = 0; methods[meth_num] != NULL; meth_num++) { CFCMethod *method = methods[meth_num]; if (CFCMethod_is_fresh(method, klass)) { if (CFCMethod_novel(method)) { int meth_index = self->num_novel + num_new_novel; S_add_novel_meth(self, method, klass, meth_index); ++num_new_novel; } else { int meth_index = self->num_overridden + num_new_overridden; S_add_overridden_meth(self, method, klass, meth_index); ++num_new_overridden; } } else { int meth_index = self->num_inherited + num_new_inherited; S_add_inherited_meth(self, method, klass, meth_index); ++num_new_inherited; } } char pattern[] = " {\n" " &%s, /* class */\n" " %s, /* parent */\n" " \"%s\", /* name */\n" " %s, /* ivars_size */\n" " &%s, /* ivars_offset_ptr */\n" " %d, /* num_novel */\n" " %d, /* num_overridden */\n" " %d, /* num_inherited */\n" " %s /* flags */\n" " }"; char *class_spec = CFCUtil_sprintf(pattern, class_var, parent_ptr, class_name, ivars_size, ivars_offset_name, num_new_novel, num_new_overridden, num_new_inherited, flags); const char *sep = self->num_specs == 0 ? "" : ",\n"; self->class_specs = CFCUtil_cat(self->class_specs, sep, class_spec, NULL); self->num_novel += num_new_novel; self->num_overridden += num_new_overridden; self->num_inherited += num_new_inherited; self->num_specs += 1; FREEMEM(class_spec); FREEMEM(parent_ptr); FREEMEM(ivars_size); }