static int S_compare_class_name(const void *va, const void *vb) { const char *a = CFCClass_get_name(*(CFCClass**)va); const char *b = CFCClass_get_name(*(CFCClass**)vb); return strcmp(a, b); }
char* CFCPerlPod_constructors_pod(CFCPerlPod *self, CFCClass *klass) { if (!self->num_constructors) { return CFCUtil_strdup(""); } const char *class_name = CFCClass_get_name(klass); char *pod = CFCUtil_strdup("=head1 CONSTRUCTORS\n\n"); for (size_t i = 0; i < self->num_constructors; i++) { NamePod slot = self->constructors[i]; if (slot.pod) { pod = CFCUtil_cat(pod, slot.pod, "\n", NULL); } else { const char *func_name = slot.func ? slot.func : slot.alias; CFCFunction *pod_func = CFCClass_function(klass, func_name); if (!pod_func) { CFCUtil_die("Can't find constructor '%s' in class '%s'", func_name, CFCClass_get_name(klass)); } char *sub_pod = CFCPerlPod_gen_subroutine_pod((CFCCallable*)pod_func, slot.alias, klass, slot.sample, class_name, true); pod = CFCUtil_cat(pod, sub_pod, NULL); FREEMEM(sub_pod); } } return pod; }
int S_do_propagate_modified(CFCHierarchy *self, CFCClass *klass, int modified) { const char *path_part = CFCClass_get_path_part(klass); CFCUTIL_NULL_CHECK(path_part); CFCFile *file = S_fetch_file(self, path_part); CFCUTIL_NULL_CHECK(file); const char *source_path = CFCFile_get_path(file); char *h_path = CFCFile_h_path(file, self->inc_dest); if (!CFCUtil_current(source_path, h_path)) { modified = true; } FREEMEM(h_path); if (modified) { CFCFile_set_modified(file, modified); } // Proceed to the next generation. int somebody_is_modified = modified; CFCClass **children = CFCClass_children(klass); for (size_t i = 0; children[i] != NULL; i++) { CFCClass *kid = children[i]; if (CFCClass_final(klass)) { CFCUtil_die("Attempt to inherit from final class '%s' by '%s'", CFCClass_get_name(klass), CFCClass_get_name(kid)); } if (S_do_propagate_modified(self, kid, modified)) { somebody_is_modified = 1; } } return somebody_is_modified; }
void CFCParcel_add_class(CFCParcel *self, CFCClass *klass) { // Ensure unique class name. const char *class_name = CFCClass_get_name(klass); CFCClass *other = S_fetch_class(self, class_name, 2); if (other) { CFCUtil_die("Two classes with name %s", class_name); } const char *struct_sym = CFCClass_get_struct_sym(klass); const char *nickname = CFCClass_get_nickname(klass); for (size_t i = 0; self->classes[i]; ++i) { CFCClass *other = self->classes[i]; // Ensure unique struct symbol and nickname in parcel. if (strcmp(struct_sym, CFCClass_get_struct_sym(other)) == 0) { CFCUtil_die("Class name conflict between %s and %s", CFCClass_get_name(klass), CFCClass_get_name(other)); } if (strcmp(nickname, CFCClass_get_nickname(other)) == 0) { CFCUtil_die("Class nickname conflict between %s and %s", CFCClass_get_name(klass), CFCClass_get_name(other)); } } size_t num_classes = self->num_classes; size_t size = (num_classes + 2) * sizeof(CFCClass*); CFCClass **classes = (CFCClass**)REALLOCATE(self->classes, size); classes[num_classes] = (CFCClass*)CFCBase_incref((CFCBase*)klass); classes[num_classes+1] = NULL; self->classes = classes; self->num_classes = num_classes + 1; }
char* CFCPyMethod_constructor_wrapper(CFCFunction *init_func, CFCClass *invoker) { CFCParamList *param_list = CFCFunction_get_param_list(init_func); const char *self_type = CFCType_to_c(CFCFunction_get_return_type(init_func)); char *func_sym = CFCFunction_full_func_sym(init_func, invoker); char *decs = S_gen_decs(param_list, 1); char *increfs = S_gen_arg_increfs(param_list, 1); char *decrefs = S_gen_decrefs(param_list, 1); const char *class_var = CFCClass_full_class_var(invoker); const char *struct_sym = CFCClass_full_struct_sym(invoker); char *error = NULL; char *arg_parsing = S_gen_arg_parsing(param_list, 1, &error); if (error) { CFCUtil_die("%s in constructor for %s", error, CFCClass_get_name(invoker)); } if (!arg_parsing) { CFCUtil_die("Unexpected arg parsing error for %s", CFCClass_get_name(invoker)); } char *first_arg = CFCUtil_sprintf("(%s)CFISH_Class_Make_Obj(%s)", self_type, class_var); char *arg_list = S_gen_arg_list(param_list, first_arg); char pattern[] = "static PyObject*\n" "S_%s_PY_NEW(PyTypeObject *type, PyObject *args, PyObject *kwargs) {\n" "%s" // decs "%s" // arg_parsing "%s" // increfs " %s self = NULL;\n" " CFBIND_TRY(self = %s(%s));\n" "%s" // decrefs " if (CFBind_migrate_cferr()) {\n" " return NULL;\n" " }\n" " return (PyObject*)self;\n" "}\n" ; char *wrapper = CFCUtil_sprintf(pattern, struct_sym, decs, arg_parsing, increfs, self_type, func_sym, arg_list, decrefs); FREEMEM(arg_list); FREEMEM(first_arg); FREEMEM(func_sym); FREEMEM(decrefs); FREEMEM(increfs); FREEMEM(decs); FREEMEM(arg_parsing); return wrapper; }
static CFCClass* S_fetch_class(CFCParcel *self, const char *class_name, int level) { // level == 0: Only search parcel. // level == 1: Search parcel and direct prereqs. // level == 2: Search parcel an indirect prereqs. for (size_t i = 0; self->classes[i]; ++i) { CFCClass *klass = self->classes[i]; if (strcmp(CFCClass_get_name(klass), class_name) == 0) { return klass; } } if (level == 0) { return NULL; } if (level == 1) { level = 0; } for (size_t i = 0; self->prereqs[i]; ++i) { const char *prereq_name = CFCPrereq_get_name(self->prereqs[i]); CFCParcel *prereq_parcel = CFCParcel_fetch(prereq_name); CFCClass *klass = S_fetch_class(prereq_parcel, class_name, level); if (klass) { return klass; } } return NULL; }
static char* S_gen_code_sample(CFCFunction *func, const char *alias, CFCClass *klass, int is_constructor) { char *invocant = NULL; if (is_constructor) { invocant = CFCUtil_strdup(CFCClass_get_name(klass)); } else { char *lower = S_camel_to_lower(CFCClass_get_struct_sym(klass)); invocant = CFCUtil_sprintf("$%s", lower); FREEMEM(lower); } CFCParamList *param_list = CFCFunction_get_param_list(func); size_t num_vars = CFCParamList_num_vars(param_list); size_t start = is_constructor ? 0 : 1; char *sample = NULL; if (start == num_vars) { sample = CFCUtil_sprintf(" %s->%s();\n", invocant, alias); } else if (is_constructor || num_vars - start >= 2) { sample = S_gen_labeled_sample(invocant, alias, param_list, start); } else { sample = S_gen_positional_sample(invocant, alias, param_list, start); } return sample; }
static char* S_gen_code_sample(CFCCallable *func, const char *alias, CFCClass *klass, int is_constructor) { char *prologue = CFCUtil_sprintf(""); char *class_var_name = S_camel_to_lower(CFCClass_get_struct_sym(klass)); CFCType *ret_type = CFCCallable_get_return_type(func); if (!CFCType_is_void(ret_type)) { char *ret_name = S_perl_var_name(ret_type, is_constructor); if (!is_constructor && strcmp(ret_name, class_var_name) == 0) { // Return type equals `klass`. Use a generic variable name // to avoid confusing code samples like // `my $string = $string->trim`. prologue = CFCUtil_cat(prologue, "my $result = ", NULL); } else { prologue = CFCUtil_cat(prologue, "my $", ret_name, " = ", NULL); } FREEMEM(ret_name); } if (is_constructor) { const char *invocant = CFCClass_get_name(klass); prologue = CFCUtil_cat(prologue, invocant, NULL); } else { prologue = CFCUtil_cat(prologue, "$", class_var_name, NULL); } prologue = CFCUtil_cat(prologue, "->", alias, NULL); CFCParamList *param_list = CFCCallable_get_param_list(func); int num_vars = CFCParamList_num_vars(param_list); int start = is_constructor ? 0 : 1; char *sample = NULL; if (start == num_vars) { sample = CFCUtil_sprintf(" %s();\n", prologue); } else if (is_constructor || num_vars - start >= 2) { sample = S_gen_labeled_sample(prologue, param_list, start); } else { sample = S_gen_positional_sample(prologue, param_list, start); } FREEMEM(class_var_name); FREEMEM(prologue); return sample; }
char* CFCC_link_text(CFCUri *uri_obj) { char *link_text = NULL; CFCUriType type = CFCUri_get_type(uri_obj); switch (type) { case CFC_URI_ERROR: { const char *error = CFCUri_get_error(uri_obj); link_text = CFCUtil_sprintf("[%s]", error); break; } case CFC_URI_NULL: link_text = CFCUtil_strdup("NULL"); break; case CFC_URI_CLASS: { CFCClass *klass = CFCUri_get_class(uri_obj); const char *src = CFCClass_included(klass) ? CFCClass_get_name(klass) : CFCClass_get_struct_sym(klass); link_text = CFCUtil_strdup(src); break; } case CFC_URI_FUNCTION: case CFC_URI_METHOD: { const char *name = CFCUri_get_callable_name(uri_obj); link_text = CFCUtil_sprintf("%s()", name); break; } case CFC_URI_DOCUMENT: { CFCDocument *doc = CFCUri_get_document(uri_obj); const char *name = CFCDocument_get_name(doc); link_text = CFCUtil_strdup(name); break; } default: CFCUtil_die("Unsupported node type: %d", type); break; } return link_text; }
static char* S_gen_class_bindings(CFCPython *self, CFCParcel *parcel, const char *pymod_name, CFCClass **ordered) { char *bindings = CFCUtil_strdup(""); for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass)) { continue; } const char *class_name = CFCClass_get_name(klass); CFCPyClass *class_binding = CFCPyClass_singleton(class_name); if (!class_binding) { // No binding spec'd out, so create one using defaults. class_binding = CFCPyClass_new(klass); CFCPyClass_add_to_registry(class_binding); } char *code = CFCPyClass_gen_binding_code(class_binding); bindings = CFCUtil_cat(bindings, code, NULL); FREEMEM(code); } return bindings; }
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_run_basic_tests(CFCTest *test) { CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST); STR_EQ(test, CFCHierarchy_get_dest(hierarchy), T_CFDEST, "get_dest"); STR_EQ(test, CFCHierarchy_get_include_dest(hierarchy), T_CFDEST_INCLUDE, "get_include_dest"); STR_EQ(test, CFCHierarchy_get_source_dest(hierarchy), T_CFDEST_SOURCE, "get_source_dest"); CFCHierarchy_add_source_dir(hierarchy, T_CFBASE); const char **source_dirs = CFCHierarchy_get_source_dirs(hierarchy); STR_EQ(test, source_dirs[0], T_CFBASE, "source_dirs[0]"); OK(test, source_dirs[1] == NULL, "source_dirs[1]"); CFCHierarchy_build(hierarchy); CFCFile **files = CFCHierarchy_files(hierarchy); CFCFile *animal = NULL; CFCFile *dog = NULL; CFCFile *util = NULL; for (int i = 0; i < 3; ++i) { CFCFile *file = files[i]; OK(test, file != NULL, "files[%d]", i); OK(test, !CFCFile_get_modified(file), "start off not modified"); CFCBase **blocks = CFCFile_blocks(file); for (int j = 0; blocks[j]; ++j) { CFCBase *block = blocks[j]; const char *cfc_class_name = CFCBase_get_cfc_class(block); if (strcmp(cfc_class_name, "Clownfish::CFC::Model::Class") == 0) { CFCClass *klass = (CFCClass*)block; const char *class_name = CFCClass_get_name(klass); if (strcmp(class_name, "Animal") == 0) { animal = file; } else if (strcmp(class_name, "Animal::Dog") == 0) { dog = file; } else if (strcmp(class_name, "Animal::Util") == 0) { util = file; } } } } OK(test, files[3] == NULL, "recursed and found all three files"); { CFCClass **ordered_classes = CFCHierarchy_ordered_classes(hierarchy); OK(test, ordered_classes[0] != NULL, "ordered_classes[0]"); OK(test, ordered_classes[1] != NULL, "ordered_classes[1]"); OK(test, ordered_classes[2] != NULL, "ordered_classes[2]"); OK(test, ordered_classes[3] != NULL, "ordered_classes[3]"); OK(test, ordered_classes[4] == NULL, "all classes"); FREEMEM(ordered_classes); } // Generate fake C files, with times set to two seconds ago. time_t now = time(NULL); time_t past_time = now - 2; static const char *const h_paths[] = { T_CFDEST_INCLUDE CHY_DIR_SEP "Animal.h", T_CFDEST_INCLUDE CHY_DIR_SEP "Animal" CHY_DIR_SEP "Dog.h", T_CFDEST_INCLUDE CHY_DIR_SEP "Animal" CHY_DIR_SEP "Util.h" }; OK(test, CFCUtil_make_path(T_CFDEST_INCLUDE CHY_DIR_SEP "Animal"), "make_path"); for (int i = 0; i < 3; ++i) { const char *h_path = h_paths[i]; const char *content = "#include <stdio.h>\n"; CFCUtil_write_file(h_path, content, strlen(content)); CFCTest_set_file_times(h_path, past_time); } char *cfh_path = CFCFile_cfh_path(animal, T_CFBASE); CFCTest_set_file_times(cfh_path, now); FREEMEM(cfh_path); CFCHierarchy_propagate_modified(hierarchy, 0); OK(test, CFCFile_get_modified(animal), "Animal modified"); OK(test, CFCFile_get_modified(dog), "Parent's modification propagates to child's file"); OK(test, !CFCFile_get_modified(util), "Modification doesn't propagate to inert class"); for (int i = 0; i < 3; ++i) { remove(h_paths[i]); } rmdir(T_CFDEST_INCLUDE CHY_DIR_SEP "Animal"); rmdir(T_CFDEST_INCLUDE); rmdir(T_CFDEST_SOURCE); rmdir(T_CFDEST); CFCBase_decref((CFCBase*)hierarchy); CFCClass_clear_registry(); CFCParcel_reap_singletons(); }
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); }
void CFCParcel_connect_and_sort_classes(CFCParcel *self) { size_t num_classes = self->num_classes; size_t alloc_size = (num_classes + 1) * sizeof(CFCClass*); CFCClass **classes = self->classes; CFCClass **sorted = (CFCClass**)MALLOCATE(alloc_size); // Perform a depth-first search of the classes in the parcel, and store // the classes in the order that they were visited. This makes sure // that subclasses are sorted after their parents. // // To avoid a recursive algorithm, the end of the sorted array is used // as a stack for classes that have yet to be visited. // // Root and child classes are sorted by name to get a deterministic // order. // Set up parent/child relationship of classes and find subtree roots // in parcel. size_t todo = num_classes; for (size_t i = 0; i < num_classes; i++) { CFCClass *klass = classes[i]; CFCClass *parent = NULL; const char *parent_name = CFCClass_get_parent_class_name(klass); if (parent_name) { parent = S_fetch_class(self, parent_name, 1); if (!parent) { CFCUtil_die("Parent class '%s' of '%s' not found in parcel" " '%s' or its prerequisites", parent_name, CFCClass_get_name(klass), self->name); } CFCClass_add_child(parent, klass); } if (!parent || !CFCClass_in_parcel(parent, self)) { // Subtree root. sorted[--todo] = klass; } // Resolve types. CFCClass_resolve_types(klass); } qsort(&sorted[todo], num_classes - todo, sizeof(sorted[0]), S_compare_class_name); size_t num_sorted = 0; while (todo < num_classes) { CFCClass *klass = sorted[todo++]; sorted[num_sorted++] = klass; // Push children on stack. Since this function is called first for // prereq parcels, we can be sure that the class doesn't have // children from another parcel yet. CFCClass **children = CFCClass_children(klass); size_t prev_todo = todo; for (size_t i = 0; children[i]; i++) { if (todo <= num_sorted) { CFCUtil_die("Internal error in CFCParcel_sort_classes"); } sorted[--todo] = children[i]; } qsort(&sorted[todo], prev_todo - todo, sizeof(sorted[0]), S_compare_class_name); } if (num_sorted != num_classes) { CFCUtil_die("Internal error in CFCParcel_sort_classes"); } sorted[num_classes] = NULL; FREEMEM(self->classes); self->classes = sorted; }
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 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); }
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); }
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; }