static char* S_parent_offset(CFCBindSpecs *self, CFCMethod *method, CFCClass *klass, const char *meth_type, int meth_index) { CFCClass *parent = CFCClass_get_parent(klass); if (!parent) { return CFCUtil_strdup("NULL"); } char *parent_offset = NULL; char *parent_offset_sym = CFCMethod_full_offset_sym(method, parent); if (CFCClass_in_same_parcel(klass, parent)) { parent_offset = CFCUtil_sprintf("&%s", parent_offset_sym); } else { parent_offset = CFCUtil_strdup("NULL"); char pattern[] = " %s_specs[%d].parent_offset = &%s;\n"; char *code = CFCUtil_sprintf(pattern, meth_type, meth_index, parent_offset_sym); self->init_code = CFCUtil_cat(self->init_code, code, NULL); FREEMEM(code); } FREEMEM(parent_offset_sym); return parent_offset; }
CFCMethod* CFCClass_find_novel_method(CFCClass *self, const char *sym) { if (!self->tree_grown) { CFCUtil_die("Can't call original_method before grow_tree"); } CFCClass *ancestor = self; do { CFCMethod *method = CFCClass_method(ancestor, sym); if (method && CFCMethod_novel(method)) { return method; } } while (NULL != (ancestor = CFCClass_get_parent(ancestor))); return NULL; }
char* CFCBindMeth_method_def(CFCMethod *method, CFCClass *klass) { // If the method is final and the class where it is declared final is in // the same parcel as the invocant, we can optimize the call by resolving // to the implementing function directly. if (CFCMethod_final(method)) { CFCClass *ancestor = klass; while (ancestor && !CFCMethod_is_fresh(method, ancestor)) { ancestor = CFCClass_get_parent(ancestor); } if (CFCClass_get_parcel(ancestor) == CFCClass_get_parcel(klass)) { return S_optimized_final_method_def(method, klass); } } return S_virtual_method_def(method, klass); }
char* CFCBindMeth_inherited_spec_def(CFCMethod *method, CFCClass *klass) { char *full_offset_sym = CFCMethod_full_offset_sym(method, klass); CFCClass *parent = CFCClass_get_parent(klass); char *parent_offset_sym = CFCMethod_full_offset_sym(method, parent); char pattern[] = " {\n" " &%s, /* offset */\n" " &%s /* parent_offset */\n" " }"; char *def = CFCUtil_sprintf(pattern, full_offset_sym, parent_offset_sym); FREEMEM(full_offset_sym); FREEMEM(parent_offset_sym); return def; }
char* CFCBindMeth_overridden_spec_def(CFCMethod *method, CFCClass *klass) { const char *imp_func = CFCMethod_imp_func(method); char *full_offset_sym = CFCMethod_full_offset_sym(method, NULL); CFCClass *parent = CFCClass_get_parent(klass); char *parent_offset_sym = CFCMethod_full_offset_sym(method, parent); char pattern[] = " {\n" " &%s, /* offset */\n" " &%s, /* parent_offset */\n" " (cfish_method_t)%s /* func */\n" " }"; char *def = CFCUtil_sprintf(pattern, full_offset_sym, parent_offset_sym, imp_func); FREEMEM(full_offset_sym); FREEMEM(parent_offset_sym); return def; }
void CFCDumpable_add_dumpables(CFCDumpable *self, CFCClass *klass) { (void)self; if (!CFCClass_has_attribute(klass, "dumpable")) { CFCUtil_die("Class %s isn't dumpable", CFCClass_get_class_name(klass)); } // Inherit Dump/Load from parent if no fresh member vars. CFCClass *parent = CFCClass_get_parent(klass); if (parent && CFCClass_has_attribute(parent, "dumpable")) { CFCVariable **fresh = CFCClass_fresh_member_vars(klass); int needs_autogenerated_dumpables = fresh[0] != NULL ? true : false; FREEMEM(fresh); if (!needs_autogenerated_dumpables) { return; } } if (!CFCClass_fresh_method(klass, "Dump")) { S_add_dump_method(klass); } if (!CFCClass_fresh_method(klass, "Load")) { S_add_load_method(klass); } }
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_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"); }
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; }
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); }
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; }
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* CFCPerlPod_methods_pod(CFCPerlPod *self, CFCClass *klass) { const char *class_name = CFCClass_get_name(klass); char *abstract_pod = CFCUtil_strdup(""); char *methods_pod = CFCUtil_strdup(""); // Start with methods that don't map to a Clownfish method. for (size_t i = 0; i < self->num_methods; i++) { NamePod meth_spec = self->methods[i]; CFCMethod *method = CFCClass_method(klass, meth_spec.func); if (method) { continue; } if (!meth_spec.pod) { CFCUtil_die("No POD specified for method '%s' in class '%s'", meth_spec.alias, CFCClass_get_name(klass)); } methods_pod = CFCUtil_cat(methods_pod, meth_spec.pod, "\n", NULL); } CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { CFCMethod *method = fresh_methods[meth_num]; const char *name = CFCMethod_get_name(method); char *meth_pod = NULL; // Try to find custom POD for method. NamePod *meth_spec = NULL; for (size_t j = 0; j < self->num_methods; j++) { NamePod *candidate = &self->methods[j]; const char *other_name = candidate->func; if (other_name && strcmp(other_name, name) == 0) { meth_spec = candidate; break; } } if (meth_spec) { // Found custom POD. if (meth_spec->pod) { meth_pod = CFCUtil_sprintf("%s\n", meth_spec->pod); } else { meth_pod = CFCPerlPod_gen_subroutine_pod((CFCCallable*)method, meth_spec->alias, klass, meth_spec->sample, class_name, false); } } else { // No custom POD found. Add POD for public methods with Perl // bindings. if (!CFCMethod_public(method) || CFCMethod_excluded_from_host(method) || !CFCMethod_can_be_bound(method) ) { continue; } // Only add POD for novel methods and the first implementation // of abstract methods. if (!CFCMethod_novel(method)) { if (CFCMethod_abstract(method)) { continue; } CFCClass *parent = CFCClass_get_parent(klass); CFCMethod *parent_method = CFCClass_method(parent, name); if (!CFCMethod_abstract(parent_method)) { continue; } } char *perl_name = CFCPerlMethod_perl_name(method); meth_pod = CFCPerlPod_gen_subroutine_pod((CFCCallable*)method, perl_name, klass, NULL, class_name, false); FREEMEM(perl_name); } if (CFCMethod_abstract(method)) { abstract_pod = CFCUtil_cat(abstract_pod, meth_pod, NULL); } else { methods_pod = CFCUtil_cat(methods_pod, meth_pod, NULL); } FREEMEM(meth_pod); } char *pod = CFCUtil_strdup(""); if (strlen(abstract_pod)) { pod = CFCUtil_cat(pod, "=head1 ABSTRACT METHODS\n\n", abstract_pod, NULL); } FREEMEM(abstract_pod); if (strlen(methods_pod)) { pod = CFCUtil_cat(pod, "=head1 METHODS\n\n", methods_pod, NULL); } FREEMEM(methods_pod); return pod; }
static void S_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); }
char* CFCPerlPod_gen_subroutine_pod(CFCPerlPod *self, CFCFunction *func, const char *alias, CFCClass *klass, const char *code_sample, const char *class_name, int is_constructor) { // Only allow "public" subs to be exposed as part of the public API. if (!CFCFunction_public(func)) { CFCUtil_die("%s#%s is not public", class_name, alias); } CFCParamList *param_list = CFCFunction_get_param_list(func); int num_vars = (int)CFCParamList_num_vars(param_list); char *pod = CFCUtil_sprintf("=head2 %s", alias); // Get documentation, which may be inherited. CFCDocuComment *docucomment = CFCFunction_get_docucomment(func); if (!docucomment) { const char *micro_sym = CFCFunction_micro_sym(func); CFCClass *parent = klass; while (NULL != (parent = CFCClass_get_parent(parent))) { CFCFunction *parent_func = (CFCFunction*)CFCClass_method(parent, micro_sym); if (!parent_func) { break; } docucomment = CFCFunction_get_docucomment(parent_func); if (docucomment) { break; } } } if (!docucomment) { CFCUtil_die("No DocuComment for '%s' in '%s'", alias, class_name); } // Build string summarizing arguments to use in header. if (num_vars > 2 || (is_constructor && num_vars > 1)) { pod = CFCUtil_cat(pod, "( I<[labeled params]> )\n\n", NULL); } else if (num_vars == 2) { // Kill self param. const char *name_list = CFCParamList_name_list(param_list); const char *after_comma = strchr(name_list, ',') + 1; while (isspace(*after_comma)) { after_comma++; } pod = CFCUtil_cat(pod, "(", after_comma, ")\n\n", NULL); } else { // num_args == 1, leave off 'self'. pod = CFCUtil_cat(pod, "()\n\n", NULL); } // Add code sample. if (code_sample && strlen(code_sample)) { pod = CFCUtil_cat(pod, code_sample, "\n", NULL); } // 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(self, klass, long_doc); 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(self, klass, param_docs[i]); 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(self, klass, retval_doc); pod = CFCUtil_cat(pod, "Returns: ", perlified, NULL); FREEMEM(perlified); } return pod; }
static void S_run_include_tests(CFCTest *test) { { CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST); CFCHierarchy_add_source_dir(hierarchy, T_CFEXT); CFCHierarchy_add_include_dir(hierarchy, T_CFBASE); const char **include_dirs = CFCHierarchy_get_include_dirs(hierarchy); STR_EQ(test, include_dirs[0], T_CFBASE, "include_dirs[0]"); OK(test, include_dirs[1] == NULL, "include_dirs[1]"); CFCHierarchy_build(hierarchy); CFCClass **classes = CFCHierarchy_ordered_classes(hierarchy); CFCClass *rottweiler = NULL;; int num_classes; int num_source_classes = 0; for (num_classes = 0; classes[num_classes]; ++num_classes) { CFCClass *klass = classes[num_classes]; int expect_included = 1; const char *class_name = CFCClass_get_name(klass); if (strcmp(class_name, "Animal::Rottweiler") == 0) { rottweiler = klass; expect_included = 0; ++num_source_classes; } INT_EQ(test, CFCClass_included(klass), expect_included, "included"); } INT_EQ(test, num_classes, 5, "class count"); INT_EQ(test, num_source_classes, 1, "source class count"); STR_EQ(test, CFCClass_get_name(CFCClass_get_parent(rottweiler)), "Animal::Dog", "parent of included class"); FREEMEM(classes); CFCBase_decref((CFCBase*)hierarchy); CFCClass_clear_registry(); CFCParcel_reap_singletons(); } { CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST); CFCHierarchy_add_source_dir(hierarchy, T_CFBASE); CFCHierarchy_add_source_dir(hierarchy, T_CFEXT); CFCHierarchy_build(hierarchy); CFCClass **classes = CFCHierarchy_ordered_classes(hierarchy); CFCClass *rottweiler = NULL;; int num_classes; for (num_classes = 0; classes[num_classes]; ++num_classes) { CFCClass *klass = classes[num_classes]; const char *class_name = CFCClass_get_name(klass); if (strcmp(class_name, "Animal::Rottweiler") == 0) { rottweiler = klass; } OK(test, !CFCClass_included(klass), "not included"); } INT_EQ(test, num_classes, 5, "class count"); OK(test, rottweiler != NULL, "found rottweiler"); STR_EQ(test, CFCClass_get_name(CFCClass_get_parent(rottweiler)), "Animal::Dog", "parent of class from second source"); FREEMEM(classes); CFCBase_decref((CFCBase*)hierarchy); CFCClass_clear_registry(); CFCParcel_reap_singletons(); } { CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST); CFCHierarchy_add_include_dir(hierarchy, T_CFBASE); CFCHierarchy_add_include_dir(hierarchy, T_CFEXT); CFCHierarchy_add_prereq(hierarchy, "AnimalExtension"); CFCHierarchy_build(hierarchy); CFCParcel *animal = CFCParcel_fetch("Animal"); OK(test, animal != NULL, "parcel Animal registered"); OK(test, CFCParcel_required(animal), "parcel Animal required"); CFCParcel *animal_ext = CFCParcel_fetch("AnimalExtension"); OK(test, animal_ext != NULL, "parcel AnimalExtension registered"); OK(test, CFCParcel_required(animal_ext), "parcel AnimalExtension required"); CFCClass **classes = CFCHierarchy_ordered_classes(hierarchy); int num_classes = 0; while (classes[num_classes]) { ++num_classes; } INT_EQ(test, num_classes, 5, "class count"); FREEMEM(classes); CFCBase_decref((CFCBase*)hierarchy); CFCClass_clear_registry(); CFCParcel_reap_singletons(); } rmdir(T_CFDEST_INCLUDE); rmdir(T_CFDEST_SOURCE); rmdir(T_CFDEST); }
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; }
void CFCBindSpecs_add_class(CFCBindSpecs *self, CFCClass *klass) { if (CFCClass_inert(klass)) { return; } const char *class_name = CFCClass_get_name(klass); const char *class_var = CFCClass_full_class_var(klass); const char *ivars_offset_name = CFCClass_full_ivars_offset(klass); const char *flags = CFCClass_final(klass) ? "cfish_ClassSpec_FINAL" : "0"; char *ivars_size = S_ivars_size(klass); char *parent_ptr = NULL; CFCClass *parent = CFCClass_get_parent(klass); if (!parent) { parent_ptr = CFCUtil_strdup("NULL"); } else { if (CFCClass_in_same_parcel(klass, parent)) { parent_ptr = CFCUtil_sprintf("&%s", CFCClass_full_class_var(parent)); } else { parent_ptr = CFCUtil_strdup("NULL"); const char *class_name = CFCClass_get_name(klass); const char *parent_var = CFCClass_full_class_var(parent); const char *pattern = " /* %s */\n" " class_specs[%d].parent = &%s;\n"; char *init_code = CFCUtil_sprintf(pattern, class_name, self->num_specs, parent_var); self->init_code = CFCUtil_cat(self->init_code, init_code, NULL); FREEMEM(init_code); } } int num_new_novel = 0; int num_new_overridden = 0; int num_new_inherited = 0; CFCMethod **methods = CFCClass_methods(klass); for (int meth_num = 0; methods[meth_num] != NULL; meth_num++) { CFCMethod *method = methods[meth_num]; if (CFCMethod_is_fresh(method, klass)) { if (CFCMethod_novel(method)) { int meth_index = self->num_novel + num_new_novel; S_add_novel_meth(self, method, klass, meth_index); ++num_new_novel; } else { int meth_index = self->num_overridden + num_new_overridden; S_add_overridden_meth(self, method, klass, meth_index); ++num_new_overridden; } } else { int meth_index = self->num_inherited + num_new_inherited; S_add_inherited_meth(self, method, klass, meth_index); ++num_new_inherited; } } char pattern[] = " {\n" " &%s, /* class */\n" " %s, /* parent */\n" " \"%s\", /* name */\n" " %s, /* ivars_size */\n" " &%s, /* ivars_offset_ptr */\n" " %d, /* num_novel */\n" " %d, /* num_overridden */\n" " %d, /* num_inherited */\n" " %s /* flags */\n" " }"; char *class_spec = CFCUtil_sprintf(pattern, class_var, parent_ptr, class_name, ivars_size, ivars_offset_name, num_new_novel, num_new_overridden, num_new_inherited, flags); const char *sep = self->num_specs == 0 ? "" : ",\n"; self->class_specs = CFCUtil_cat(self->class_specs, sep, class_spec, NULL); self->num_novel += num_new_novel; self->num_overridden += num_new_overridden; self->num_inherited += num_new_inherited; self->num_specs += 1; FREEMEM(class_spec); FREEMEM(parent_ptr); FREEMEM(ivars_size); }