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 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); }
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 void S_add_load_method(CFCClass *klass) { CFCMethod *method = S_make_method_obj(klass, "Load"); CFCClass_add_method(klass, method); CFCBase_decref((CFCBase*)method); const char *full_func_sym = CFCMethod_implementing_func_sym(method); const char *full_struct = CFCClass_full_struct_sym(klass); const char *vtable_var = CFCClass_full_vtable_var(klass); CFCClass *parent = CFCClass_get_parent(klass); const char *prefix = CFCClass_get_prefix(klass); const char *class_cnick = CFCClass_get_cnick(klass); char buf[BUF_SIZE]; if (parent && CFCClass_has_attribute(parent, "dumpable")) { char *full_typedef = CFCMethod_full_typedef(method, klass); char *full_meth = CFCMethod_full_method_sym(method, klass); const char pattern[] = "cfish_Obj*\n" "%s(%s *self, cfish_Obj *dump)\n" "{\n" " cfish_Hash *source = (cfish_Hash*)CFISH_CERTIFY(dump, CFISH_HASH);\n" " %s super_load = CFISH_SUPER_METHOD_PTR(%s, %s);\n" " %s *loaded = (%s*)super_load(self, dump);\n" " %sIVARS *ivars = %s%s_IVARS(loaded);\n"; char *autocode = CFCUtil_sprintf(pattern, full_func_sym, full_struct, full_typedef, vtable_var, full_meth, full_struct, full_struct, full_struct, prefix, class_cnick); CFCClass_append_autocode(klass, autocode); FREEMEM(full_meth); FREEMEM(full_typedef); FREEMEM(autocode); CFCVariable **fresh = CFCClass_fresh_member_vars(klass); for (size_t i = 0; fresh[i] != NULL; i++) { S_process_load_member(klass, fresh[i], buf, BUF_SIZE); } FREEMEM(fresh); } else { const char pattern[] = "cfish_Obj*\n" "%s(%s *self, cfish_Obj *dump)\n" "{\n" " cfish_Hash *source = (cfish_Hash*)CFISH_CERTIFY(dump, CFISH_HASH);\n" " cfish_CharBuf *class_name = (cfish_CharBuf*)CFISH_CERTIFY(\n" " Cfish_Hash_Fetch_Str(source, \"_class\", 6), CFISH_CHARBUF);\n" " cfish_VTable *vtable = cfish_VTable_singleton(class_name, NULL);\n" " %s *loaded = (%s*)Cfish_VTable_Make_Obj(vtable);\n" " %sIVARS *ivars = %s%s_IVARS(loaded);\n" " CHY_UNUSED_VAR(self);\n"; char *autocode = CFCUtil_sprintf(pattern, full_func_sym, full_struct, full_struct, full_struct, full_struct, prefix, class_cnick); CFCClass_append_autocode(klass, autocode); FREEMEM(autocode); CFCVariable **members = CFCClass_member_vars(klass); for (size_t i = 0; members[i] != NULL; i++) { S_process_load_member(klass, members[i], buf, BUF_SIZE); } } CFCClass_append_autocode(klass, " return (cfish_Obj*)loaded;\n}\n\n"); }
static void S_write_parcel_c(CFCBindCore *self, CFCParcel *parcel) { CFCHierarchy *hierarchy = self->hierarchy; const char *prefix = CFCParcel_get_prefix(parcel); // Aggregate C code for the parcel. char *privacy_syms = CFCUtil_strdup(""); char *includes = CFCUtil_strdup(""); char *c_data = CFCUtil_strdup(""); CFCBindSpecs *specs = CFCBindSpecs_new(); CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); for (int i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; const char *class_prefix = CFCClass_get_prefix(klass); if (strcmp(class_prefix, prefix) != 0) { continue; } const char *include_h = CFCClass_include_h(klass); includes = CFCUtil_cat(includes, "#include \"", include_h, "\"\n", NULL); CFCBindClass *class_binding = CFCBindClass_new(klass); char *class_c_data = CFCBindClass_to_c_data(class_binding); c_data = CFCUtil_cat(c_data, class_c_data, "\n", NULL); FREEMEM(class_c_data); CFCBindSpecs_add_class(specs, klass); const char *privacy_sym = CFCClass_privacy_symbol(klass); privacy_syms = CFCUtil_cat(privacy_syms, "#define ", privacy_sym, "\n", NULL); CFCBase_decref((CFCBase*)class_binding); } char *spec_defs = CFCBindSpecs_defs(specs); char *spec_init_func = CFCBindSpecs_init_func_def(specs); FREEMEM(ordered); // Bootstrapping code for prerequisite parcels. // // bootstrap_inheritance() first calls bootstrap_inheritance() for all // parcels from which classes are inherited. Then the Classes of the parcel // are initialized. It aborts on recursive invocation. // // bootstrap_parcel() first calls bootstrap_inheritance() of its own // parcel. Then it calls bootstrap_parcel() for all prerequisite parcels. // Finally, it calls init_parcel(). Recursive invocation is allowed. char *inh_bootstrap = CFCUtil_strdup(""); char *prereq_bootstrap = CFCUtil_strdup(""); CFCParcel **inh_parcels = CFCParcel_inherited_parcels(parcel); for (size_t i = 0; inh_parcels[i]; ++i) { const char *inh_prefix = CFCParcel_get_prefix(inh_parcels[i]); inh_bootstrap = CFCUtil_cat(inh_bootstrap, " ", inh_prefix, "bootstrap_inheritance();\n", NULL); } FREEMEM(inh_parcels); CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(parcel); for (size_t i = 0; prereq_parcels[i]; ++i) { const char *prereq_prefix = CFCParcel_get_prefix(prereq_parcels[i]); prereq_bootstrap = CFCUtil_cat(prereq_bootstrap, " ", prereq_prefix, "bootstrap_parcel();\n", NULL); } FREEMEM(prereq_parcels); char pattern[] = "%s\n" "\n" "#include <stdio.h>\n" "#include <stdlib.h>\n" "\n" "%s" "\n" "#include \"Clownfish/Class.h\"\n" // Needed for bootstrap. "#include \"Clownfish/Err.h\"\n" // Needed for abstract methods. "%s\n" "\n" "%s\n" "\n" "/* ClassSpec and MethSpec structs for initialization.\n" " */\n" "\n" "%s" // spec_defs "\n" "/* Code to initialize ClassSpec and MethSpec structs.\n" " */\n" "\n" "%s" // spec_init_func "\n" "static int bootstrap_state = 0;\n" "\n" "void\n" "%sbootstrap_inheritance() {\n" " if (bootstrap_state == 1) {\n" " fprintf(stderr, \"Cycle in class inheritance between\"\n" " \" parcels detected.\\n\");\n" " abort();\n" " }\n" " if (bootstrap_state >= 2) { return; }\n" " bootstrap_state = 1;\n" "%s" // Bootstrap inherited parcels. " S_bootstrap_specs();\n" " bootstrap_state = 2;\n" "}\n" "\n" "void\n" "%sbootstrap_parcel() {\n" " if (bootstrap_state >= 3) { return; }\n" " %sbootstrap_inheritance();\n" " bootstrap_state = 3;\n" "%s" // Finish bootstrapping of all prerequisite parcels. " %sinit_parcel();\n" "}\n" "\n" "%s\n"; char *file_content = CFCUtil_sprintf(pattern, self->c_header, privacy_syms, includes, c_data, spec_defs, spec_init_func, prefix, inh_bootstrap, prefix, prefix, prereq_bootstrap, prefix, self->c_footer); // Unlink then open file. const char *src_dest = CFCHierarchy_get_source_dest(hierarchy); char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sparcel.c", src_dest, prefix); remove(filepath); CFCUtil_write_file(filepath, file_content, strlen(file_content)); FREEMEM(filepath); CFCBase_decref((CFCBase*)specs); FREEMEM(privacy_syms); FREEMEM(includes); FREEMEM(c_data); FREEMEM(spec_defs); FREEMEM(spec_init_func); FREEMEM(inh_bootstrap); FREEMEM(prereq_bootstrap); FREEMEM(file_content); }
char* CFCGoTypeMap_go_type_name(CFCType *type, CFCParcel *current_parcel) { if (CFCType_cfish_obj(type)) { return CFCUtil_strdup("interface{}"); } else if (CFCType_cfish_string(type)) { return CFCUtil_strdup("string"); } else if (CFCType_cfish_blob(type)) { return CFCUtil_strdup("[]byte"); } else if (CFCType_cfish_vector(type)) { return CFCUtil_strdup("[]interface{}"); } else if (CFCType_cfish_hash(type)) { return CFCUtil_strdup("map[string]interface{}"); } else if (CFCType_is_object(type)) { // Divide the specifier into prefix and struct name. const char *specifier = CFCType_get_specifier(type); size_t prefix_len = 0; for (size_t max = strlen(specifier); prefix_len < max; prefix_len++) { if (isupper(specifier[prefix_len])) { break; } } if (!prefix_len) { CFCUtil_die("Can't convert object type name '%s'", specifier); } const char *struct_sym = specifier + prefix_len; // Find the parcel that the type lives in. CFCParcel** all_parcels = CFCParcel_all_parcels(); CFCParcel *parcel = NULL; for (int i = 0; all_parcels[i] != NULL; i++) { const char *candidate = CFCParcel_get_prefix(all_parcels[i]); if (strncmp(candidate, specifier, prefix_len) == 0 && strlen(candidate) == prefix_len ) { parcel = all_parcels[i]; break; } } if (!parcel) { CFCUtil_die("Can't find parcel for type '%s'", specifier); } // If the type lives in this parcel, return only the struct sym // without a go package prefix. if (parcel == current_parcel) { return CFCUtil_strdup(struct_sym); } // The type lives in another parcel, so prefix its Go package name. // TODO: Stop downcasing once Clownfish parcel names are constrained // to lower case. const char *package_name = CFCParcel_get_name(parcel); if (strrchr(package_name, '.')) { package_name = strrchr(package_name, '.') + 1; } char *result = CFCUtil_sprintf("%s.%s", package_name, struct_sym); for (int i = 0; result[i] != '.'; i++) { result[i] = tolower(result[i]); } return result; } else if (CFCType_is_primitive(type)) { const char *specifier = CFCType_get_specifier(type); for (int i = 0; i < num_conversions; i++) { if (strcmp(specifier, conversions[i].c) == 0) { return CFCUtil_strdup(conversions[i].go); } } } return NULL; }
static void S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) { const char *parcel_name = CFCParcel_get_name(parcel); char *pymod_name = CFCUtil_strdup(parcel_name); // TODO: Stop lowercasing when parcels are restricted to lowercase. for (int i = 0; pymod_name[i] != '\0'; i++) { pymod_name[i] = tolower(pymod_name[i]); } const char *last_dot = strrchr(pymod_name, '.'); const char *last_component = last_dot != NULL ? last_dot + 1 : pymod_name; char *helper_mod_name = CFCUtil_sprintf("%s._%s", pymod_name, last_component); for (int i = 0; helper_mod_name[i] != '\0'; i++) { helper_mod_name[i] = tolower(helper_mod_name[i]); } CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); CFCParcel **parcels = CFCParcel_all_parcels(); char *callbacks = S_gen_callbacks(self, parcel, ordered); char *type_linkups = S_gen_type_linkups(self, parcel, ordered); char *pound_includes = CFCUtil_strdup(""); char *class_bindings = S_gen_class_bindings(self, parcel, pymod_name, ordered); char *parcel_boots = CFCUtil_strdup(""); char *pytype_ready_calls = CFCUtil_strdup(""); char *module_adds = CFCUtil_strdup(""); // Add parcel bootstrapping calls. for (size_t i = 0; parcels[i]; ++i) { if (!CFCParcel_included(parcels[i])) { const char *prefix = CFCParcel_get_prefix(parcels[i]); parcel_boots = CFCUtil_cat(parcel_boots, " ", prefix, "bootstrap_parcel();\n", NULL); } } for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass)) { continue; } const char *struct_sym = CFCClass_get_struct_sym(klass); const char *include_h = CFCClass_include_h(klass); pound_includes = CFCUtil_cat(pound_includes, "#include \"", include_h, "\"\n", NULL); // The PyType_Ready invocations for instantiable classes are handled // via bootstrapping of Clownfish Class objects. Since inert classes // do not at present have Class objects, we need to handle their // PyType_Ready calls independently. if (CFCClass_inert(klass)) { pytype_ready_calls = CFCUtil_cat(pytype_ready_calls, " if (PyType_Ready(&", struct_sym, "_pytype_struct) < 0) { return NULL; }\n", NULL); } module_adds = CFCUtil_cat(module_adds, " PyModule_AddObject(module, \"", struct_sym, "\", (PyObject*)&", struct_sym, "_pytype_struct);\n", NULL); } const char pattern[] = "%s\n" "\n" "#include \"Python.h\"\n" "#include \"cfish_parcel.h\"\n" "#include \"CFBind.h\"\n" "%s\n" "\n" "%s\n" // callbacks "\n" "static PyModuleDef module_def = {\n" " PyModuleDef_HEAD_INIT,\n" " \"%s\",\n" // module name " NULL,\n" // docstring " -1,\n" " NULL, NULL, NULL, NULL, NULL\n" "};\n" "\n" "%s" // class bindings "\n" "%s" // S_link_py_types function "\n" "PyMODINIT_FUNC\n" "PyInit__%s(void) {\n" " cfish_Class_bootstrap_hook1 = CFBind_class_bootstrap_hook1;\n" "\n" "%s\n" // PyType_Ready calls "\n" " S_link_py_types();\n" "\n" "%s\n" // parcel boots "\n" " PyObject *module = PyModule_Create(&module_def);\n" "%s\n" // Add types to module "\n" " return module;\n" "}\n" "\n" "%s\n" "\n"; char *content = CFCUtil_sprintf(pattern, self->header, pound_includes, callbacks, helper_mod_name, class_bindings, type_linkups, last_component, pytype_ready_calls, parcel_boots, module_adds, self->footer); char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "_%s.c", dest, last_component); CFCUtil_write_if_changed(filepath, content, strlen(content)); FREEMEM(filepath); FREEMEM(content); FREEMEM(module_adds); FREEMEM(pytype_ready_calls); FREEMEM(parcel_boots); FREEMEM(class_bindings); FREEMEM(helper_mod_name); FREEMEM(pymod_name); FREEMEM(pound_includes); FREEMEM(type_linkups); FREEMEM(callbacks); FREEMEM(ordered); }
CFCMethod* CFCMethod_init(CFCMethod *self, CFCParcel *parcel, const char *exposure, const char *class_name, const char *class_cnick, const char *macro_sym, CFCType *return_type, CFCParamList *param_list, CFCDocuComment *docucomment, int is_final, int is_abstract) { // Validate macro_sym, derive micro_sym. if (!S_validate_macro_sym(macro_sym)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid macro_sym: '%s'", macro_sym ? macro_sym : "[NULL]"); } char *micro_sym = CFCUtil_strdup(macro_sym); for (size_t i = 0; micro_sym[i] != '\0'; i++) { micro_sym[i] = tolower(micro_sym[i]); } // Super-init and clean up derived micro_sym. CFCFunction_init((CFCFunction*)self, parcel, exposure, class_name, class_cnick, micro_sym, return_type, param_list, docucomment, false); FREEMEM(micro_sym); // Verify that the first element in the arg list is a self. CFCVariable **args = CFCParamList_get_variables(param_list); if (!args[0]) { CFCUtil_die("Missing 'self' argument"); } CFCType *type = CFCVariable_get_type(args[0]); const char *specifier = CFCType_get_specifier(type); const char *prefix = CFCMethod_get_prefix(self); const char *last_colon = strrchr(class_name, ':'); const char *struct_sym = last_colon ? last_colon + 1 : class_name; if (strcmp(specifier, struct_sym) != 0) { char *wanted = CFCUtil_sprintf("%s%s", prefix, struct_sym); int mismatch = strcmp(wanted, specifier); FREEMEM(wanted); if (mismatch) { CFCUtil_die("First arg type doesn't match class: '%s' '%s'", class_name, specifier); } } self->macro_sym = CFCUtil_strdup(macro_sym); self->full_override_sym = NULL; self->host_alias = NULL; self->is_final = is_final; self->is_abstract = is_abstract; self->is_excluded = false; // Derive name of implementing function. self->short_imp_func = CFCUtil_sprintf("%s_%s_IMP", CFCMethod_get_class_cnick(self), self->macro_sym); self->imp_func = CFCUtil_sprintf("%s%s", CFCMethod_get_PREFIX(self), self->short_imp_func); // Assume that this method is novel until we discover when applying // inheritance that it overrides another. self->is_novel = true; return self; }
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_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; }
char* CFCPerlConstructor_xsub_def(CFCPerlConstructor *self) { const char *c_name = self->sub.c_name; CFCParamList *param_list = self->sub.param_list; char *name_list = CFCPerlSub_arg_name_list((CFCPerlSub*)self); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char *func_sym = CFCFunction_full_func_sym(self->init_func); char *arg_decls = CFCPerlSub_arg_declarations((CFCPerlSub*)self); char *allot_params = CFCPerlSub_build_allot_params((CFCPerlSub*)self); CFCVariable *self_var = arg_vars[0]; CFCType *self_type = CFCVariable_get_type(self_var); const char *self_type_str = CFCType_to_c(self_type); // Compensate for swallowed refcounts. char *refcount_mods = CFCUtil_strdup(""); for (size_t 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)) { const char *name = CFCVariable_micro_sym(var); refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_INCREF(arg_", name, ");", NULL); } } const char pattern[] = "XS(%s);\n" "XS(%s) {\n" " dXSARGS;\n" " %s arg_self;\n" "%s" " bool args_ok;\n" " %s retval;\n" "\n" " CFISH_UNUSED_VAR(cv);\n" " if (items < 1) { CFISH_THROW(CFISH_ERR, \"Usage: %%s(class_name, ...)\", GvNAME(CvGV(cv))); }\n" " SP -= items;\n" "\n" " %s\n" // Create "self" last, so that earlier exceptions while fetching // params don't trigger a bad invocation of DESTROY. " arg_self = (%s)XSBind_new_blank_obj(ST(0));%s\n" "\n" " retval = %s(%s);\n" " if (retval) {\n" " ST(0) = (SV*)CFISH_Obj_To_Host((cfish_Obj*)retval);\n" " CFISH_Obj_Dec_RefCount((cfish_Obj*)retval);\n" " }\n" " else {\n" " ST(0) = newSV(0);\n" " }\n" " sv_2mortal(ST(0));\n" " XSRETURN(1);\n" "}\n\n"; char *xsub_def = CFCUtil_sprintf(pattern, c_name, c_name, self_type_str, arg_decls, self_type_str, allot_params, self_type_str, refcount_mods, func_sym, name_list); FREEMEM(refcount_mods); FREEMEM(arg_decls); FREEMEM(allot_params); FREEMEM(name_list); return xsub_def; }
void CFCBindFile_write_h(CFCFile *file, const char *dest, const char *header, const char *footer) { CFCUTIL_NULL_CHECK(file); CFCUTIL_NULL_CHECK(dest); CFCUTIL_NULL_CHECK(header); CFCUTIL_NULL_CHECK(footer); // Make directories. char *h_path = CFCFile_h_path(file, dest); char *h_dir = CFCUtil_strdup(h_path); for (size_t len = strlen(h_dir); len--;) { if (h_dir[len] == CHY_DIR_SEP_CHAR) { h_dir[len] = 0; break; } } if (!CFCUtil_is_dir(h_dir)) { CFCUtil_make_path(h_dir); if (!CFCUtil_is_dir(h_dir)) { CFCUtil_die("Can't make path %s", h_dir); } } FREEMEM(h_dir); // Create the include-guard strings. const char *include_guard_start = CFCFile_guard_start(file); const char *include_guard_close = CFCFile_guard_close(file); // Aggregate block content. char *content = CFCUtil_strdup(""); CFCBase **blocks = CFCFile_blocks(file); for (int i = 0; blocks[i] != NULL; i++) { const char *cfc_class = CFCBase_get_cfc_class(blocks[i]); if (strcmp(cfc_class, "Clownfish::CFC::Model::Parcel") == 0) { CFCParcel *parcel = (CFCParcel*)blocks[i]; const char *prefix = CFCParcel_get_prefix(parcel); content = CFCUtil_cat(content, "#include \"", prefix, "parcel.h\"\n\n", NULL); } else if (strcmp(cfc_class, "Clownfish::CFC::Model::Class") == 0) { CFCBindClass *class_binding = CFCBindClass_new((CFCClass*)blocks[i]); char *c_header = CFCBindClass_to_c_header(class_binding); content = CFCUtil_cat(content, c_header, "\n", NULL); FREEMEM(c_header); CFCBase_decref((CFCBase*)class_binding); } else if (strcmp(cfc_class, "Clownfish::CFC::Model::CBlock") == 0) { const char *block_contents = CFCCBlock_get_contents((CFCCBlock*)blocks[i]); content = CFCUtil_cat(content, block_contents, "\n", NULL); } else { CFCUtil_die("Unexpected class: %s", cfc_class); } } char pattern[] = "%s\n" "\n" "%s\n" "\n" "#ifdef __cplusplus\n" "extern \"C\" {\n" "#endif\n" "\n" "%s\n" "\n" "#ifdef __cplusplus\n" "}\n" "#endif\n" "\n" "%s\n" "\n" "%s\n" "\n"; char *file_content = CFCUtil_sprintf(pattern, header, include_guard_start, content, include_guard_close, footer); // Unlink then write file. remove(h_path); CFCUtil_write_file(h_path, file_content, strlen(file_content)); FREEMEM(content); FREEMEM(file_content); FREEMEM(h_path); }
CFCClass* CFCClass_do_create(CFCClass *self, struct CFCParcel *parcel, const char *exposure, const char *class_name, const char *cnick, const char *micro_sym, CFCDocuComment *docucomment, CFCFileSpec *file_spec, const char *parent_class_name, int is_final, int is_inert) { CFCUTIL_NULL_CHECK(class_name); exposure = exposure ? exposure : "parcel"; micro_sym = micro_sym ? micro_sym : "class"; parcel = parcel ? parcel : CFCParcel_default_parcel(); CFCSymbol_init((CFCSymbol*)self, parcel, exposure, class_name, cnick, micro_sym); if (!is_inert && !parent_class_name && strcmp(class_name, "Clownfish::Obj") != 0 ) { parent_class_name = "Clownfish::Obj"; } self->parent = NULL; self->tree_grown = false; self->children = (CFCClass**)CALLOCATE(1, sizeof(CFCClass*)); self->num_kids = 0; self->functions = (CFCFunction**)CALLOCATE(1, sizeof(CFCFunction*)); self->num_functions = 0; self->methods = (CFCMethod**)CALLOCATE(1, sizeof(CFCMethod*)); self->num_methods = 0; self->member_vars = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*)); self->num_member_vars = 0; self->inert_vars = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*)); self->num_inert_vars = 0; self->parent_class_name = CFCUtil_strdup(parent_class_name); self->docucomment = (CFCDocuComment*)CFCBase_incref((CFCBase*)docucomment); self->file_spec = (CFCFileSpec*)CFCBase_incref((CFCBase*)file_spec); // Cache several derived symbols. const char *last_colon = strrchr(class_name, ':'); self->struct_sym = last_colon ? CFCUtil_strdup(last_colon + 1) : CFCUtil_strdup(class_name); const char *prefix = CFCClass_get_prefix(self); size_t struct_sym_len = strlen(self->struct_sym); self->short_vtable_var = (char*)MALLOCATE(struct_sym_len + 1); size_t i; for (i = 0; i < struct_sym_len; i++) { self->short_vtable_var[i] = toupper(self->struct_sym[i]); } self->short_vtable_var[struct_sym_len] = '\0'; self->full_struct_sym = CFCUtil_sprintf("%s%s", prefix, self->struct_sym); self->ivars_struct = CFCUtil_sprintf("%sIVARS", self->struct_sym); self->full_ivars_struct = CFCUtil_sprintf("%sIVARS", self->full_struct_sym); self->ivars_func = CFCUtil_sprintf("%s_IVARS", CFCClass_get_cnick(self)); self->full_ivars_func = CFCUtil_sprintf("%s%s_IVARS", prefix, CFCClass_get_cnick(self)); self->full_ivars_offset = CFCUtil_sprintf("%s_OFFSET", self->full_ivars_func); size_t full_struct_sym_len = strlen(self->full_struct_sym); self->full_vtable_var = (char*)MALLOCATE(full_struct_sym_len + 1); for (i = 0; self->full_struct_sym[i] != '\0'; i++) { self->full_vtable_var[i] = toupper(self->full_struct_sym[i]); } self->full_vtable_var[i] = '\0'; self->privacy_symbol = CFCUtil_sprintf("C_%s", self->full_vtable_var); // Build the relative path to the autogenerated C header file. if (file_spec) { const char *path_part = CFCFileSpec_get_path_part(self->file_spec); self->include_h = CFCUtil_sprintf("%s.h", path_part); } else { self->include_h = CFCUtil_strdup("class.h"); } self->is_final = !!is_final; self->is_inert = !!is_inert; if (file_spec && CFCFileSpec_included(file_spec)) { if (!CFCParcel_included(parcel)) { CFCUtil_die("Class %s from include dir found in parcel %s from" " source dir", class_name, CFCParcel_get_name(parcel)); } } else { if (CFCParcel_included(parcel)) { CFCUtil_die("Class %s from source dir found in parcel %s from" " include dir", class_name, CFCParcel_get_name(parcel)); } } // Store in registry. S_register(self); return self; }
static void S_find_prereq(CFCHierarchy *self, CFCParcel *parent, CFCPrereq *prereq) { const char *name = CFCPrereq_get_name(prereq); CFCVersion *min_version = CFCPrereq_get_version(prereq); // Check whether prereq was processed already. CFCParcel **parcels = CFCParcel_all_parcels(); for (int i = 0; parcels[i]; ++i) { CFCParcel *parcel = parcels[i]; const char *other_name = CFCParcel_get_name(parcel); if (strcmp(other_name, name) == 0) { CFCVersion *other_version = CFCParcel_get_version(parcel); CFCVersion *major_version = CFCParcel_get_major_version(parcel); if (CFCVersion_compare_to(major_version, min_version) <= 0 && CFCVersion_compare_to(min_version, other_version) <= 0 ) { // Compatible version found. return; } else { CFCUtil_die("Parcel %s %s required by %s not compatible with" " version %s required by %s", name, other_version, "[TODO]", CFCVersion_get_vstring(min_version), CFCParcel_get_name(parent)); } } } CFCParcel *parcel = NULL; // TODO: Decide whether to prefer higher versions from directories // that come later in the list of include dirs or stop processing once // a suitable version was found in a dir. for (size_t i = 0; self->includes[i] != NULL; i++) { char *name_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s", self->includes[i], name); if (CFCUtil_is_dir(name_dir)) { void *dirhandle = CFCUtil_opendir(name_dir); const char *entry = NULL; while (NULL != (entry = CFCUtil_dirnext(dirhandle))) { if (!CFCVersion_is_vstring(entry)) { continue; } char *version_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s", name_dir, entry); if (CFCUtil_is_dir(version_dir)) { parcel = S_audition_parcel(version_dir, entry, min_version, parcel); } FREEMEM(version_dir); } CFCUtil_closedir(dirhandle, name_dir); } FREEMEM(name_dir); } if (parcel == NULL) { CFCUtil_die("Parcel %s %s required by %s not found", name, CFCVersion_get_vstring(min_version), CFCParcel_get_name(parent)); } // Make sure to register prereq parcels first, so that prereq // parent classes precede subclasses. S_find_prereqs(self, parcel); CFCParcel_register(parcel); CFCBase_decref((CFCBase*)parcel); }
static void S_resolve(CFCUri *self, const char *parcel, const char *struct_sym, const char *callable) { // Try to find a CFCClass. CFCClass *doc_class = self->doc_class; CFCClass *klass = NULL; if (parcel) { char *full_struct_sym = CFCUtil_sprintf("%s_%s", parcel, struct_sym); klass = CFCClass_fetch_by_struct_sym(full_struct_sym); FREEMEM(full_struct_sym); } else if (struct_sym && doc_class) { const char *prefix = CFCClass_get_prefix(doc_class); char *full_struct_sym = CFCUtil_sprintf("%s%s", prefix, struct_sym); klass = CFCClass_fetch_by_struct_sym(full_struct_sym); FREEMEM(full_struct_sym); } else if (callable) { klass = doc_class; } if (klass) { if (!CFCClass_public(klass)) { CFCUtil_warn("Non-public class '%s' in Clownfish URI: %s", CFCClass_get_struct_sym(klass), self->string); } self->type = CFC_URI_CLASS; self->klass = klass; CFCBase_incref((CFCBase*)klass); if (callable) { if (islower(callable[0])) { CFCFunction *function = CFCClass_function(klass, callable); if (!function) { CFCUtil_warn("Unknown function '%s' in Clownfish URI: %s", callable, self->string); } else if (!CFCFunction_public(function)) { CFCUtil_warn("Non-public function '%s' in Clownfish URI:" " %s", callable, self->string); } self->type = CFC_URI_FUNCTION; self->callable = CFCUtil_strdup(callable); } else { CFCMethod *method = CFCClass_method(klass, callable); if (!method) { CFCUtil_warn("Unknown method '%s' in Clownfish URI: %s", callable, self->string); } else if (!CFCMethod_public(method)) { CFCUtil_warn("Non-public method '%s' in Clownfish URI:" " %s", callable, self->string); } self->type = CFC_URI_METHOD; self->callable = CFCUtil_strdup(callable); } } return; } // Try to find a CFCDocument. if (!parcel && struct_sym && !callable) { CFCDocument *doc = CFCDocument_fetch(struct_sym); if (doc) { self->type = CFC_URI_DOCUMENT; self->document = doc; CFCBase_incref((CFCBase*)doc); return; } } S_set_error(self, "Couldn't resolve Clownfish URI"); }
static void S_run_object_tests(CFCTest *test) { static const char *modifiers[4] = { "const", "incremented", "decremented", "nullable" }; static int flags[4] = { CFCTYPE_CONST, CFCTYPE_INCREMENTED, CFCTYPE_DECREMENTED, CFCTYPE_NULLABLE }; static int (*accessors[4])(CFCType *type) = { CFCType_const, CFCType_incremented, CFCType_decremented, CFCType_nullable }; { CFCParser *parser = CFCParser_new(); CFCParcel *neato_parcel = CFCTest_parse_parcel(test, parser, "parcel Neato;"); static const char *specifiers[4] = { "Foo", "FooJr", "FooIII", "Foo4th" }; for (int i = 0; i < 4; ++i) { const char *specifier = specifiers[i]; char *class_code = CFCUtil_sprintf("class %s {}", specifier); CFCClass *klass = CFCTest_parse_class(test, parser, class_code); FREEMEM(class_code); static const char *prefixes[2] = { "", "neato_" }; char *expect = CFCUtil_sprintf("neato_%s", specifier); for (int j = 0; j < 2; ++j) { char *src = CFCUtil_sprintf("%s%s*", prefixes[j], specifier); CFCType *type = CFCTest_parse_type(test, parser, src); CFCType_resolve(type); STR_EQ(test, CFCType_get_specifier(type), expect, "object_type_specifier: %s", src); OK(test, CFCType_is_object(type), "%s is_object", src); INT_EQ(test, CFCType_get_indirection(type), 1, "%s indirection", src); FREEMEM(src); CFCBase_decref((CFCBase*)type); } FREEMEM(expect); for (int j = 0; j < 4; ++j) { char *src = CFCUtil_sprintf("%s %s*", modifiers[j], specifier); CFCType *type = CFCTest_parse_type(test, parser, src); OK(test, CFCType_is_object(type), "%s is_object", src); OK(test, accessors[j](type), "%s accessor", src); FREEMEM(src); CFCBase_decref((CFCBase*)type); } CFCBase_decref((CFCBase*)klass); CFCClass_clear_registry(); } CFCBase_decref((CFCBase*)neato_parcel); CFCBase_decref((CFCBase*)parser); } CFCParcel *neato_parcel = CFCParcel_new("Neato", NULL, NULL, NULL); CFCClass *foo_class = CFCClass_create(neato_parcel, NULL, "Foo", NULL, NULL, NULL, NULL, NULL, false, false, false); CFCType *foo = CFCType_new_object(0, neato_parcel, "Foo", 1); CFCType_resolve(foo); { CFCType *another_foo = CFCType_new_object(0, neato_parcel, "Foo", 1); CFCType_resolve(another_foo); OK(test, CFCType_equals(foo, another_foo), "equals"); CFCBase_decref((CFCBase*)another_foo); } { CFCClass *bar_class = CFCClass_create(neato_parcel, NULL, "Bar", NULL, NULL, NULL, NULL, NULL, false, false, false); CFCType *bar = CFCType_new_object(0, neato_parcel, "Bar", 1); CFCType_resolve(bar); OK(test, !CFCType_equals(foo, bar), "different specifier spoils equals"); CFCBase_decref((CFCBase*)bar); CFCBase_decref((CFCBase*)bar_class); } { CFCParcel *foreign_parcel = CFCParcel_new("Foreign", NULL, NULL, NULL); CFCClass *foreign_foo_class = CFCClass_create(foreign_parcel, NULL, "Foreign::Foo", NULL, NULL, NULL, NULL, NULL, false, false, false); CFCType *foreign_foo = CFCType_new_object(0, foreign_parcel, "Foo", 1); CFCType_resolve(foreign_foo); OK(test, !CFCType_equals(foo, foreign_foo), "different parcel spoils equals"); STR_EQ(test, CFCType_get_specifier(foreign_foo), "foreign_Foo", "prepend parcel prefix to specifier"); CFCBase_decref((CFCBase*)foreign_parcel); CFCBase_decref((CFCBase*)foreign_foo_class); CFCBase_decref((CFCBase*)foreign_foo); } { for (int i = 0; i < 4; ++i) { CFCType *modified_foo = CFCType_new_object(flags[i], neato_parcel, "Foo", 1); CFCType_resolve(modified_foo); OK(test, accessors[i](modified_foo), "%s", modifiers[i]); OK(test, !accessors[i](foo), "not %s", modifiers[i]); OK(test, !CFCType_equals(foo, modified_foo), "different %s spoils equals", modifiers[i]); OK(test, !CFCType_similar(foo, modified_foo), "different %s spoils similar", modifiers[i]); CFCBase_decref((CFCBase*)modified_foo); } } { CFCType *string_type = CFCType_new_object(0, neato_parcel, "String", 1); OK(test, CFCType_is_string_type(string_type), "%s", "is_string_type"); OK(test, !CFCType_is_string_type(foo), "not %s", "not is_string_type"); CFCBase_decref((CFCBase*)string_type); } CFCBase_decref((CFCBase*)neato_parcel); CFCBase_decref((CFCBase*)foo_class); CFCBase_decref((CFCBase*)foo); CFCClass_clear_registry(); CFCParcel_reap_singletons(); }
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 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; }
char* CFCPerlPod_gen_subroutine_pod(CFCCallable *func, const char *alias, CFCClass *klass, const char *code_sample, const char *class_name, int is_constructor) { const char *func_name = CFCCallable_get_name(func); // Only allow "public" subs to be exposed as part of the public API. if (!CFCCallable_public(func)) { CFCUtil_die("%s#%s is not public", class_name, func_name); } char *pod = CFCUtil_sprintf("=head2 %s\n\n", alias); // Add code sample. if (!code_sample) { char *auto_sample = S_gen_code_sample(func, alias, klass, is_constructor); pod = CFCUtil_cat(pod, auto_sample, "\n", NULL); FREEMEM(auto_sample); } else { pod = CFCUtil_cat(pod, code_sample, "\n", NULL); } // Get documentation, which may be inherited. CFCDocuComment *docucomment = CFCCallable_get_docucomment(func); if (!docucomment) { CFCClass *parent = klass; while (NULL != (parent = CFCClass_get_parent(parent))) { CFCCallable *parent_func = (CFCCallable*)CFCClass_method(parent, func_name); if (!parent_func) { break; } docucomment = CFCCallable_get_docucomment(parent_func); if (docucomment) { break; } } } if (!docucomment) { return pod; } // Incorporate "description" text from DocuComment. const char *long_doc = CFCDocuComment_get_description(docucomment); if (long_doc && strlen(long_doc)) { char *perlified = CFCPerlPod_md_to_pod(long_doc, klass, 3); pod = CFCUtil_cat(pod, perlified, NULL); FREEMEM(perlified); } // Add params in a list. const char**param_names = CFCDocuComment_get_param_names(docucomment); const char**param_docs = CFCDocuComment_get_param_docs(docucomment); if (param_names[0]) { pod = CFCUtil_cat(pod, "=over\n\n", NULL); for (size_t i = 0; param_names[i] != NULL; i++) { char *perlified = CFCPerlPod_md_to_pod(param_docs[i], klass, 3); pod = CFCUtil_cat(pod, "=item *\n\nB<", param_names[i], "> - ", perlified, NULL); FREEMEM(perlified); } pod = CFCUtil_cat(pod, "=back\n\n", NULL); } // Add return value description, if any. const char *retval_doc = CFCDocuComment_get_retval(docucomment); if (retval_doc && strlen(retval_doc)) { char *perlified = CFCPerlPod_md_to_pod(retval_doc, klass, 3); pod = CFCUtil_cat(pod, "Returns: ", perlified, NULL); FREEMEM(perlified); } return pod; }
char* CFCPerlClass_create_pod(CFCPerlClass *self) { CFCPerlPod *pod_spec = self->pod_spec; const char *class_name = self->class_name; CFCClass *client = self->client; if (!pod_spec) { return NULL; } if (!client) { CFCUtil_die("No client for %s", class_name); } CFCDocuComment *docucom = CFCClass_get_docucomment(client); if (!docucom) { CFCUtil_die("No DocuComment for %s", class_name); } // Get the class's brief description. const char *raw_brief = CFCDocuComment_get_brief(docucom); char *brief = CFCPerlPod_md_to_pod(pod_spec, client, raw_brief); // Get the class's long description. char *description; const char *pod_description = CFCPerlPod_get_description(pod_spec); if (pod_description && strlen(pod_description)) { description = CFCUtil_sprintf("%s\n", pod_description); } else { const char *raw_description = CFCDocuComment_get_long(docucom); description = CFCPerlPod_md_to_pod(pod_spec, client, raw_description); } // Create SYNOPSIS. const char *raw_synopsis = CFCPerlPod_get_synopsis(pod_spec); char *synopsis = CFCUtil_strdup(""); if (raw_synopsis && strlen(raw_synopsis)) { synopsis = CFCUtil_cat(synopsis, "=head1 SYNOPSIS\n\n", raw_synopsis, "\n", NULL); } // Create CONSTRUCTORS. char *constructor_pod = CFCPerlPod_constructors_pod(pod_spec, client); // Create METHODS, possibly including an ABSTRACT METHODS section. char *methods_pod = CFCPerlPod_methods_pod(pod_spec, client); // Build an INHERITANCE section describing class ancestry. char *inheritance = CFCUtil_strdup(""); if (CFCClass_get_parent(client)) { inheritance = CFCUtil_cat(inheritance, "=head1 INHERITANCE\n\n", class_name, NULL); CFCClass *ancestor = client; while (NULL != (ancestor = CFCClass_get_parent(ancestor))) { const char *ancestor_klass = CFCClass_get_class_name(ancestor); if (CFCPerlClass_singleton(ancestor_klass)) { inheritance = CFCUtil_cat(inheritance, " isa L<", ancestor_klass, ">", NULL); } else { inheritance = CFCUtil_cat(inheritance, " isa ", ancestor_klass, NULL); } } inheritance = CFCUtil_cat(inheritance, ".\n\n", NULL); } // Put it all together. const char pattern[] = "=head1 NAME\n" "\n" "%s - %s" "%s" "=head1 DESCRIPTION\n" "\n" "%s" "%s" "%s" "%s" "=cut\n" "\n"; char *pod = CFCUtil_sprintf(pattern, class_name, brief, synopsis, description, constructor_pod, methods_pod, inheritance); FREEMEM(brief); FREEMEM(synopsis); FREEMEM(description); FREEMEM(constructor_pod); FREEMEM(methods_pod); FREEMEM(inheritance); return pod; }
// Convert a single node. static char* S_node_to_pod(cmark_node *node, CFCClass *klass, int header_level) { char *result = CFCUtil_strdup(""); if (node == NULL) { return result; } int found_matching_code_block = false; cmark_iter *iter = cmark_iter_new(node); cmark_event_type ev_type; while (CMARK_EVENT_DONE != (ev_type = cmark_iter_next(iter))) { cmark_node *node = cmark_iter_get_node(iter); cmark_node_type type = cmark_node_get_type(node); switch (type) { case CMARK_NODE_DOCUMENT: break; case CMARK_NODE_PARAGRAPH: if (ev_type == CMARK_EVENT_EXIT) { result = CFCUtil_cat(result, "\n\n", NULL); } break; case CMARK_NODE_BLOCK_QUOTE: case CMARK_NODE_LIST: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "=over\n\n", NULL); } else { result = CFCUtil_cat(result, "=back\n\n", NULL); } break; case CMARK_NODE_ITEM: // TODO: Ordered lists. if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "=item *\n\n", NULL); } break; case CMARK_NODE_HEADER: if (ev_type == CMARK_EVENT_ENTER) { int extra_level = cmark_node_get_header_level(node) - 1; char *header = CFCUtil_sprintf("=head%d ", header_level + extra_level); result = CFCUtil_cat(result, header, NULL); FREEMEM(header); } else { result = CFCUtil_cat(result, "\n\n", NULL); } break; case CMARK_NODE_CODE_BLOCK: { int is_host = CFCMarkdown_code_block_is_host(node, "perl"); if (is_host) { found_matching_code_block = true; const char *content = cmark_node_get_literal(node); char *copy = CFCUtil_strdup(content); // Chomp trailing newline. size_t len = strlen(copy); if (len > 0 && copy[len-1] == '\n') { copy[len-1] = '\0'; } char *indented = CFCUtil_global_replace(copy, "\n", "\n "); result = CFCUtil_cat(result, " ", indented, "\n\n", NULL); FREEMEM(indented); FREEMEM(copy); } if (CFCMarkdown_code_block_is_last(node)) { if (!found_matching_code_block) { result = CFCUtil_cat(result, " Code example for Perl is missing\n\n"); } else { // Reset. found_matching_code_block = false; } } break; } case CMARK_NODE_HTML: { const char *html = cmark_node_get_literal(node); result = CFCUtil_cat(result, "=begin html\n\n", html, "\n=end\n\n", NULL); break; } case CMARK_NODE_HRULE: break; case CMARK_NODE_TEXT: { const char *content = cmark_node_get_literal(node); char *escaped = S_pod_escape(content); result = CFCUtil_cat(result, escaped, NULL); FREEMEM(escaped); break; } case CMARK_NODE_LINEBREAK: // POD doesn't support line breaks. Start a new paragraph. result = CFCUtil_cat(result, "\n\n", NULL); break; case CMARK_NODE_SOFTBREAK: result = CFCUtil_cat(result, "\n", NULL); break; case CMARK_NODE_CODE: { const char *content = cmark_node_get_literal(node); char *escaped = S_pod_escape(content); result = CFCUtil_cat(result, "C<", escaped, ">", NULL); FREEMEM(escaped); break; } case CMARK_NODE_INLINE_HTML: { const char *html = cmark_node_get_literal(node); CFCUtil_warn("Inline HTML not supported in POD: %s", html); break; } case CMARK_NODE_LINK: if (ev_type == CMARK_EVENT_ENTER) { char *pod = S_convert_link(node, klass, header_level); result = CFCUtil_cat(result, pod, NULL); FREEMEM(pod); cmark_iter_reset(iter, node, CMARK_EVENT_EXIT); } break; case CMARK_NODE_IMAGE: CFCUtil_warn("Images not supported in POD"); break; case CMARK_NODE_STRONG: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "B<", NULL); } else { result = CFCUtil_cat(result, ">", NULL); } break; case CMARK_NODE_EMPH: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "I<", NULL); } else { result = CFCUtil_cat(result, ">", NULL); } break; default: CFCUtil_die("Invalid cmark node type: %d", (int)type); break; } } cmark_iter_free(iter); return result; }
/* Write the "parcel.h" header file, which contains common symbols needed by * all classes, plus typedefs for all class structs. */ static void S_write_parcel_h(CFCBindCore *self, CFCParcel *parcel) { CFCHierarchy *hierarchy = self->hierarchy; const char *prefix = CFCParcel_get_prefix(parcel); const char *PREFIX = CFCParcel_get_PREFIX(parcel); const char *privacy_sym = CFCParcel_get_privacy_sym(parcel); // Declare object structs and class singletons for all instantiable // classes. char *typedefs = CFCUtil_strdup(""); char *class_decls = CFCUtil_strdup(""); CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); for (int i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; const char *class_prefix = CFCClass_get_prefix(klass); if (strcmp(class_prefix, prefix) != 0) { continue; } if (!CFCClass_inert(klass)) { const char *full_struct = CFCClass_full_struct_sym(klass); typedefs = CFCUtil_cat(typedefs, "typedef struct ", full_struct, " ", full_struct, ";\n", NULL); const char *class_var = CFCClass_full_class_var(klass); class_decls = CFCUtil_cat(class_decls, "extern ", PREFIX, "VISIBLE cfish_Class *", class_var, ";\n", NULL); } } FREEMEM(ordered); // Special includes and macros for Clownfish parcel. const char *cfish_includes = "#include <stdarg.h>\n" "#include <stddef.h>\n" "\n" "#include \"cfish_platform.h\"\n" "#include \"cfish_hostdefs.h\"\n"; // Special definitions for Clownfish parcel. const char *cfish_defs_1 = "#define CFISH_UNUSED_VAR(var) ((void)var)\n" "#define CFISH_UNREACHABLE_RETURN(type) return (type)0\n" "\n" "/* Generic method pointer.\n" " */\n" "typedef void\n" "(*cfish_method_t)(const void *vself);\n" "\n" "/* Access the function pointer for a given method from the class.\n" " */\n" "#define CFISH_METHOD_PTR(_class, _full_meth) \\\n" " ((_full_meth ## _t)cfish_method(_class, _full_meth ## _OFFSET))\n" "\n" "static CFISH_INLINE cfish_method_t\n" "cfish_method(const void *klass, uint32_t offset) {\n" " union { char *cptr; cfish_method_t *fptr; } ptr;\n" " ptr.cptr = (char*)klass + offset;\n" " return ptr.fptr[0];\n" "}\n" "\n" "typedef struct cfish_Dummy {\n" " CFISH_OBJ_HEAD\n" " void *klass;\n" "} cfish_Dummy;\n" "\n" "/* Access the function pointer for a given method from the object.\n" " */\n" "static CFISH_INLINE cfish_method_t\n" "cfish_obj_method(const void *object, uint32_t offset) {\n" " cfish_Dummy *dummy = (cfish_Dummy*)object;\n" " return cfish_method(dummy->klass, offset);\n" "}\n" "\n" "/* Access the function pointer for the given method in the\n" " * superclass. */\n" "#define CFISH_SUPER_METHOD_PTR(_class, _full_meth) \\\n" " ((_full_meth ## _t)cfish_super_method(_class, \\\n" " _full_meth ## _OFFSET))\n" "\n" "extern CFISH_VISIBLE uint32_t cfish_Class_offset_of_parent;\n" "static CFISH_INLINE cfish_method_t\n" "cfish_super_method(const void *klass, uint32_t offset) {\n" " char *class_as_char = (char*)klass;\n" " cfish_Class **parent_ptr\n" " = (cfish_Class**)(class_as_char + cfish_Class_offset_of_parent);\n" " return cfish_method(*parent_ptr, offset);\n" "}\n" "\n" "typedef void\n" "(*cfish_destroy_t)(void *vself);\n" "extern CFISH_VISIBLE uint32_t CFISH_Obj_Destroy_OFFSET;\n" "\n" "/** Invoke the [](.Destroy) method found in `klass` on\n" " * `self`.\n" " *\n" " * TODO: Eliminate this function if we can arrive at a proper SUPER syntax.\n" " */\n" "static CFISH_INLINE void\n" "cfish_super_destroy(void *vself, cfish_Class *klass) {\n" " cfish_Obj *self = (cfish_Obj*)vself;\n" " if (self != NULL) {\n" " cfish_destroy_t super_destroy\n" " = (cfish_destroy_t)cfish_super_method(klass, CFISH_Obj_Destroy_OFFSET);\n" " super_destroy(self);\n" " }\n" "}\n" "\n" "#define CFISH_SUPER_DESTROY(_self, _class) \\\n" " cfish_super_destroy(_self, _class)\n" "\n" "extern CFISH_VISIBLE cfish_Obj*\n" "cfish_inc_refcount(void *vself);\n" "\n" "/** NULL-safe invocation invocation of `cfish_inc_refcount`.\n" " *\n" " * @return NULL if `self` is NULL, otherwise the return value\n" " * of `cfish_inc_refcount`.\n" " */\n" "static CFISH_INLINE cfish_Obj*\n" "cfish_incref(void *vself) {\n" " if (vself != NULL) { return cfish_inc_refcount(vself); }\n" " else { return NULL; }\n" "}\n" "\n" "#define CFISH_INCREF(_self) cfish_incref(_self)\n" "#define CFISH_INCREF_NN(_self) cfish_inc_refcount(_self)\n" "\n" "extern CFISH_VISIBLE uint32_t\n" "cfish_dec_refcount(void *vself);\n" "\n" "/** NULL-safe invocation of `cfish_dec_refcount`.\n" " *\n" " * @return NULL if `self` is NULL, otherwise the return value\n" " * of `cfish_dec_refcount`.\n" " */\n" "static CFISH_INLINE uint32_t\n" "cfish_decref(void *vself) {\n" " if (vself != NULL) { return cfish_dec_refcount(vself); }\n" " else { return 0; }\n" "}\n" "\n" "#define CFISH_DECREF(_self) cfish_decref(_self)\n" "#define CFISH_DECREF_NN(_self) cfish_dec_refcount(_self)\n" "\n" "extern CFISH_VISIBLE uint32_t\n" "cfish_get_refcount(void *vself);\n" "\n" "#define CFISH_REFCOUNT_NN(_self) \\\n" " cfish_get_refcount(_self)\n" "\n" "/* Flags for internal use. */\n" "#define CFISH_fREFCOUNTSPECIAL 0x00000001\n" "#define CFISH_fFINAL 0x00000002\n" ; const char *cfish_defs_2 = "#ifdef CFISH_USE_SHORT_NAMES\n" " #define UNUSED_VAR CFISH_UNUSED_VAR\n" " #define UNREACHABLE_RETURN CFISH_UNREACHABLE_RETURN\n" " #define METHOD_PTR CFISH_METHOD_PTR\n" " #define SUPER_METHOD_PTR CFISH_SUPER_METHOD_PTR\n" " #define SUPER_DESTROY(_self, _class) CFISH_SUPER_DESTROY(_self, _class)\n" " #define INCREF(_self) CFISH_INCREF(_self)\n" " #define INCREF_NN(_self) CFISH_INCREF_NN(_self)\n" " #define DECREF(_self) CFISH_DECREF(_self)\n" " #define DECREF_NN(_self) CFISH_DECREF_NN(_self)\n" " #define REFCOUNT_NN(_self) CFISH_REFCOUNT_NN(_self)\n" "#endif\n" "\n"; char *extra_defs; char *extra_includes; if (CFCParcel_is_cfish(parcel)) { const char *spec_typedefs = CFCBindSpecs_get_typedefs(); extra_defs = CFCUtil_sprintf("%s%s%s", cfish_defs_1, spec_typedefs, cfish_defs_2); extra_includes = CFCUtil_strdup(cfish_includes); } else { extra_defs = CFCUtil_strdup(""); extra_includes = CFCUtil_strdup(""); // Include parcel.h of prerequisite parcels. CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(parcel); for (size_t i = 0; prereq_parcels[i]; ++i) { const char *prereq_prefix = CFCParcel_get_prefix(prereq_parcels[i]); extra_includes = CFCUtil_cat(extra_includes, "#include \"", prereq_prefix, "parcel.h\"\n", NULL); } FREEMEM(prereq_parcels); } const char pattern[] = "%s\n" "#ifndef CFISH_%sPARCEL_H\n" "#define CFISH_%sPARCEL_H 1\n" "\n" "#ifdef __cplusplus\n" "extern \"C\" {\n" "#endif\n" "\n" "%s" // Extra includes. "\n" "#ifdef %s\n" " #define %sVISIBLE CFISH_EXPORT\n" "#else\n" " #define %sVISIBLE CFISH_IMPORT\n" "#endif\n" "\n" "%s" // Typedefs. "\n" "%s" // Class singletons. "\n" "%s" // Extra definitions. "%sVISIBLE void\n" "%sbootstrap_inheritance();\n" "\n" "%sVISIBLE void\n" "%sbootstrap_parcel();\n" "\n" "void\n" "%sinit_parcel();\n" "\n" "#ifdef __cplusplus\n" "}\n" "#endif\n" "\n" "#endif /* CFISH_%sPARCEL_H */\n" "\n" "%s\n" "\n"; char *file_content = CFCUtil_sprintf(pattern, self->c_header, PREFIX, PREFIX, extra_includes, privacy_sym, PREFIX, PREFIX, typedefs, class_decls, extra_defs, PREFIX, prefix, PREFIX, prefix, prefix, PREFIX, self->c_footer); // Unlink then write file. const char *inc_dest = CFCHierarchy_get_include_dest(hierarchy); char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sparcel.h", inc_dest, prefix); remove(filepath); CFCUtil_write_file(filepath, file_content, strlen(file_content)); FREEMEM(filepath); FREEMEM(typedefs); FREEMEM(class_decls); FREEMEM(extra_defs); FREEMEM(extra_includes); FREEMEM(file_content); }
static char* S_convert_link(cmark_node *link, CFCClass *doc_class, int header_level) { cmark_node *child = cmark_node_first_child(link); const char *uri = cmark_node_get_url(link); char *text = S_nodes_to_pod(child, doc_class, header_level); char *retval; if (!CFCUri_is_clownfish_uri(uri)) { retval = S_pod_link(text, uri); FREEMEM(text); return retval; } char *new_uri = NULL; char *new_text = NULL; CFCUri *uri_obj = CFCUri_new(uri, doc_class); CFCUriType type = CFCUri_get_type(uri_obj); switch (type) { case CFC_URI_ERROR: { const char *error = CFCUri_get_error(uri_obj); new_text = CFCUtil_sprintf("[%s]", error); break; } case CFC_URI_NULL: // Change all instances of NULL to 'undef' new_text = CFCUtil_strdup("undef"); break; case CFC_URI_CLASS: { CFCClass *klass = CFCUri_get_class(uri_obj); if (klass != doc_class) { const char *class_name = CFCClass_get_name(klass); new_uri = CFCUtil_strdup(class_name); } if (text[0] == '\0') { const char *src = CFCClass_included(klass) ? CFCClass_get_name(klass) : CFCClass_get_struct_sym(klass); new_text = CFCUtil_strdup(src); } break; } case CFC_URI_FUNCTION: case CFC_URI_METHOD: { CFCClass *klass = CFCUri_get_class(uri_obj); const char *name = CFCUri_get_callable_name(uri_obj); // Convert "Err_get_error" to "Clownfish->error". if (strcmp(CFCClass_full_struct_sym(klass), "cfish_Err") == 0 && strcmp(name, "get_error") == 0 ) { new_text = CFCUtil_strdup("Clownfish->error"); break; } char *perl_name = CFCUtil_strdup(name); for (size_t i = 0; perl_name[i] != '\0'; ++i) { perl_name[i] = CFCUtil_tolower(perl_name[i]); } // The Perl POD only contains sections for novel methods. Link // to the class where the method is declared first. if (type == CFC_URI_METHOD) { CFCClass *parent = CFCClass_get_parent(klass); while (parent && CFCClass_method(parent, name)) { klass = parent; parent = CFCClass_get_parent(klass); } } if (klass == doc_class) { new_uri = CFCUtil_sprintf("/%s", perl_name); } else { const char *class_name = CFCClass_get_name(klass); new_uri = CFCUtil_sprintf("%s/%s", class_name, perl_name); } if (text[0] == '\0') { new_text = CFCUtil_sprintf("%s()", perl_name); } FREEMEM(perl_name); break; } case CFC_URI_DOCUMENT: { CFCDocument *doc = CFCUri_get_document(uri_obj); const char *path_part = CFCDocument_get_path_part(doc); new_uri = CFCUtil_global_replace(path_part, CHY_DIR_SEP, "::"); if (text[0] == '\0') { const char *name = CFCDocument_get_name(doc); new_text = CFCUtil_strdup(name); } break; } } if (new_text) { FREEMEM(text); text = new_text; } if (new_uri) { retval = S_pod_link(text, new_uri); FREEMEM(new_uri); FREEMEM(text); } else { retval = text; } CFCBase_decref((CFCBase*)uri_obj); return retval; }
static void S_add_dump_method(CFCClass *klass) { CFCMethod *method = S_make_method_obj(klass, "Dump"); CFCClass_add_method(klass, method); CFCBase_decref((CFCBase*)method); const char *full_func_sym = CFCMethod_implementing_func_sym(method); const char *full_struct = CFCClass_full_struct_sym(klass); const char *vtable_var = CFCClass_full_vtable_var(klass); const char *prefix = CFCClass_get_prefix(klass); const char *class_cnick = CFCClass_get_cnick(klass); CFCClass *parent = CFCClass_get_parent(klass); char buf[BUF_SIZE]; if (parent && CFCClass_has_attribute(parent, "dumpable")) { char *full_typedef = CFCMethod_full_typedef(method, klass); char *full_meth = CFCMethod_full_method_sym(method, klass); const char pattern[] = "cfish_Obj*\n" "%s(%s *self)\n" "{\n" " %sIVARS *ivars = %s%s_IVARS(self);\n" " %s super_dump = CFISH_SUPER_METHOD_PTR(%s, %s);\n" " cfish_Hash *dump = (cfish_Hash*)super_dump(self);\n"; char *autocode = CFCUtil_sprintf(pattern, full_func_sym, full_struct, full_struct, prefix, class_cnick, full_typedef, vtable_var, full_meth); CFCClass_append_autocode(klass, autocode); FREEMEM(full_meth); FREEMEM(full_typedef); FREEMEM(autocode); CFCVariable **fresh = CFCClass_fresh_member_vars(klass); for (size_t i = 0; fresh[i] != NULL; i++) { S_process_dump_member(klass, fresh[i], buf, BUF_SIZE); } FREEMEM(fresh); } else { const char pattern[] = "cfish_Obj*\n" "%s(%s *self)\n" "{\n" " %sIVARS *ivars = %s%s_IVARS(self);\n" " cfish_Hash *dump = cfish_Hash_new(0);\n" " Cfish_Hash_Store_Str(dump, \"_class\", 6,\n" " (cfish_Obj*)Cfish_CB_Clone(Cfish_Obj_Get_Class_Name((cfish_Obj*)self)));\n"; char *autocode = CFCUtil_sprintf(pattern, full_func_sym, full_struct, full_struct, prefix, class_cnick); CFCClass_append_autocode(klass, autocode); FREEMEM(autocode); CFCVariable **members = CFCClass_member_vars(klass); for (size_t i = 0; members[i] != NULL; i++) { S_process_dump_member(klass, members[i], buf, BUF_SIZE); } } CFCClass_append_autocode(klass, " return (cfish_Obj*)dump;\n}\n\n"); }
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); }
/* Generate the code which parses arguments passed from Python and converts * them to Clownfish-flavored C values. */ static char* S_gen_arg_parsing(CFCParamList *param_list, int first_tick, char **error) { char *content = NULL; CFCVariable **vars = CFCParamList_get_variables(param_list); const char **vals = CFCParamList_get_initial_values(param_list); int num_vars = CFCParamList_num_vars(param_list); char *declarations = CFCUtil_strdup(""); char *keywords = CFCUtil_strdup(""); char *format_str = CFCUtil_strdup(""); char *targets = CFCUtil_strdup(""); int optional_started = 0; for (int i = first_tick; i < num_vars; i++) { CFCVariable *var = vars[i]; const char *val = vals[i]; const char *var_name = CFCVariable_get_name(var); keywords = CFCUtil_cat(keywords, "\"", var_name, "\", ", NULL); // Build up ParseTuple format string. if (val == NULL) { if (optional_started) { // problem! *error = "Required after optional param"; goto CLEAN_UP_AND_RETURN; } } else { if (!optional_started) { optional_started = 1; format_str = CFCUtil_cat(format_str, "|", NULL); } } format_str = CFCUtil_cat(format_str, "O&", NULL); char *declaration = S_gen_declaration(var, val); declarations = CFCUtil_cat(declarations, declaration, NULL); FREEMEM(declaration); char *target = S_gen_target(var, val); targets = CFCUtil_cat(targets, target, NULL); FREEMEM(target); } char parse_pattern[] = "%s" " char *keywords[] = {%sNULL};\n" " char *fmt = \"%s\";\n" " int ok = PyArg_ParseTupleAndKeywords(args, kwargs, fmt,\n" " keywords%s);\n" " if (!ok) { return NULL; }\n" ; content = CFCUtil_sprintf(parse_pattern, declarations, keywords, format_str, targets); CLEAN_UP_AND_RETURN: FREEMEM(declarations); FREEMEM(keywords); FREEMEM(format_str); FREEMEM(targets); return content; }
void CFCPerl_write_bindings(CFCPerl *self, const char *boot_class, CFCParcel **parcels) { CFCUTIL_NULL_CHECK(boot_class); CFCUTIL_NULL_CHECK(parcels); CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); CFCPerlClass **registry = CFCPerlClass_registry(); char *privacy_syms = CFCUtil_strdup(""); char *includes = CFCUtil_strdup(""); char *generated_xs = CFCUtil_strdup(""); char *class_specs = CFCUtil_strdup(""); char *xsub_specs = CFCUtil_strdup(""); char *bootstrap_calls = CFCUtil_strdup(""); char *hand_rolled_xs = CFCUtil_strdup(""); for (size_t i = 0; parcels[i]; ++i) { CFCParcel *parcel = parcels[i]; // Set host_module_name for parcel. if (!CFCParcel_included(parcel) && CFCParcel_is_installed(parcel)) { CFCParcel_set_host_module_name(parcel, boot_class); } // Bake the parcel privacy defines into the XS, so it can be compiled // without any extra compiler flags. const char *privacy_sym = CFCParcel_get_privacy_sym(parcel); privacy_syms = CFCUtil_cat(privacy_syms, "#define ", privacy_sym, "\n", NULL); // Bootstrap calls. const char *prefix = CFCParcel_get_prefix(parcel); includes = CFCUtil_cat(includes, "#include \"", prefix, "perl.h\"\n", NULL); bootstrap_calls = CFCUtil_cat(bootstrap_calls, " ", prefix, "bootstrap_perl();\n", NULL); } for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; CFCParcel *parcel = CFCClass_get_parcel(klass); int found = false; for (size_t j = 0; parcels[j]; j++) { if (parcel == parcels[j]) { found = true; break; } } if (!found) { continue; } // Pound-includes for generated headers. const char *include_h = CFCClass_include_h(klass); includes = CFCUtil_cat(includes, "#include \"", include_h, "\"\n", NULL); if (CFCClass_inert(klass)) { continue; } int num_xsubs = 0; // Constructors. CFCPerlConstructor **constructors = CFCPerlClass_constructor_bindings(klass); for (size_t j = 0; constructors[j] != NULL; j++) { CFCPerlSub *xsub = (CFCPerlSub*)constructors[j]; // Add the XSUB function definition. char *xsub_def = CFCPerlConstructor_xsub_def(constructors[j], klass); generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n", NULL); FREEMEM(xsub_def); // Add XSUB initialization at boot. xsub_specs = S_add_xsub_spec(xsub_specs, xsub); num_xsubs += 1; CFCBase_decref((CFCBase*)constructors[j]); } FREEMEM(constructors); // Methods. CFCPerlMethod **methods = CFCPerlClass_method_bindings(klass); for (size_t j = 0; methods[j] != NULL; j++) { CFCPerlSub *xsub = (CFCPerlSub*)methods[j]; // Add the XSUB function definition. char *xsub_def = CFCPerlMethod_xsub_def(methods[j], klass); generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n", NULL); FREEMEM(xsub_def); // Add XSUB initialization at boot. xsub_specs = S_add_xsub_spec(xsub_specs, xsub); num_xsubs += 1; CFCBase_decref((CFCBase*)methods[j]); } FREEMEM(methods); // Append XSBind_ClassSpec entry. const char *class_name = CFCClass_get_name(klass); CFCClass *parent = CFCClass_get_parent(klass); char *parent_name; if (parent) { parent_name = CFCUtil_sprintf("\"%s\"", CFCClass_get_name(parent)); } else { parent_name = CFCUtil_strdup("NULL"); } char *class_spec = CFCUtil_sprintf("{ \"%s\", %s, %d }", class_name, parent_name, num_xsubs); const char *sep = class_specs[0] == '\0' ? "" : ",\n"; class_specs = CFCUtil_cat(class_specs, sep, " ", class_spec, NULL); FREEMEM(class_spec); FREEMEM(parent_name); } // Hand-rolled XS. for (size_t i = 0; registry[i] != NULL; i++) { CFCPerlClass *perl_class = registry[i]; CFCParcel *parcel = CFCPerlClass_get_parcel(perl_class); int found = false; for (size_t j = 0; parcels[j]; j++) { if (parcel == parcels[j]) { found = true; break; } } if (!found) { continue; } const char *xs = CFCPerlClass_get_xs_code(perl_class); hand_rolled_xs = CFCUtil_cat(hand_rolled_xs, xs, "\n", NULL); } const char pattern[] = "%s" // Header. "\n" "%s" // Privacy syms. "\n" "#include \"XSBind.h\"\n" "%s" // Includes. "\n" "#ifndef XS_INTERNAL\n" " #define XS_INTERNAL XS\n" "#endif\n" "\n" "%s" // Generated XS. "\n" "MODULE = %s PACKAGE = %s\n" // Boot class. "\n" "BOOT:\n" "{\n" " static const cfish_XSBind_ClassSpec class_specs[] = {\n" "%s\n" // Class specs. " };\n" " static const cfish_XSBind_XSubSpec xsub_specs[] = {\n" "%s\n" // XSUB specs. " };\n" " size_t num_classes\n" " = sizeof(class_specs) / sizeof(class_specs[0]);\n" " const char* file = __FILE__;\n" "\n" "%s" // Bootstrap calls. "\n" " cfish_XSBind_bootstrap(aTHX_ num_classes, class_specs,\n" " xsub_specs, file);\n" "}\n" "\n" "%s" // Hand-rolled XS. "\n" "%s"; // Footer char *contents = CFCUtil_sprintf(pattern, self->c_header, privacy_syms, includes, generated_xs, boot_class, boot_class, class_specs, xsub_specs, bootstrap_calls, hand_rolled_xs, self->c_footer); // Derive path to generated .xs file. char *xs_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.xs", self->lib_dir, boot_class); S_replace_double_colons(xs_path, CHY_DIR_SEP_CHAR); // Write out if there have been any changes. CFCUtil_write_if_changed(xs_path, contents, strlen(contents)); FREEMEM(xs_path); FREEMEM(contents); FREEMEM(hand_rolled_xs); FREEMEM(bootstrap_calls); FREEMEM(xsub_specs); FREEMEM(class_specs); FREEMEM(generated_xs); FREEMEM(includes); FREEMEM(privacy_syms); FREEMEM(ordered); }
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 void S_write_boot_c(CFCPerl *self) { CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); CFCParcel **parcels = CFCParcel_all_parcels(); char *pound_includes = CFCUtil_strdup(""); char *bootstrap_code = CFCUtil_strdup(""); char *alias_adds = CFCUtil_strdup(""); char *isa_pushes = CFCUtil_strdup(""); for (size_t i = 0; parcels[i]; ++i) { if (!CFCParcel_included(parcels[i])) { const char *prefix = CFCParcel_get_prefix(parcels[i]); bootstrap_code = CFCUtil_cat(bootstrap_code, " ", prefix, "bootstrap_parcel();\n", NULL); } } for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass)) { continue; } const char *class_name = CFCClass_get_class_name(klass); const char *include_h = CFCClass_include_h(klass); pound_includes = CFCUtil_cat(pound_includes, "#include \"", include_h, "\"\n", NULL); if (CFCClass_inert(klass)) { continue; } // Add aliases for selected KinoSearch classes which allow old indexes // to be read. CFCPerlClass *class_binding = CFCPerlClass_singleton(class_name); if (class_binding) { const char *vtable_var = CFCClass_full_vtable_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]; size_t alias_len = strlen(alias); const char pattern[] = " cfish_VTable_add_alias_to_registry(" "%s, \"%s\", %u);\n"; char *alias_add = CFCUtil_sprintf(pattern, vtable_var, alias, (unsigned)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); } CFCClass *parent = CFCClass_get_parent(klass); if (parent) { const char *parent_class_name = CFCClass_get_class_name(parent); isa_pushes = CFCUtil_cat(isa_pushes, " isa = get_av(\"", class_name, "::ISA\", 1);\n", NULL); isa_pushes = CFCUtil_cat(isa_pushes, " av_push(isa, newSVpv(\"", parent_class_name, "\", 0));\n", NULL); } } const char pattern[] = "%s\n" "\n" "#include \"cfish_parcel.h\"\n" "#include \"EXTERN.h\"\n" "#include \"perl.h\"\n" "#include \"XSUB.h\"\n" "#include \"boot.h\"\n" "#include \"Clownfish/String.h\"\n" "#include \"Clownfish/VTable.h\"\n" "%s\n" "\n" "void\n" "%s() {\n" "%s" "\n" "%s" "\n" " AV *isa;\n" "%s" "}\n" "\n" "%s\n" "\n"; char *content = CFCUtil_sprintf(pattern, self->header, pound_includes, self->boot_func, bootstrap_code, alias_adds, isa_pushes, self->footer); const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy); char *boot_c_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "boot.c", src_dest); CFCUtil_write_file(boot_c_path, content, strlen(content)); FREEMEM(boot_c_path); FREEMEM(content); FREEMEM(isa_pushes); FREEMEM(alias_adds); FREEMEM(bootstrap_code); FREEMEM(pound_includes); FREEMEM(parcels); FREEMEM(ordered); }