CFCPerlSub* CFCPerlSub_init(CFCPerlSub *self, CFCParamList *param_list, const char *class_name, const char *alias, int use_labeled_params) { CFCUTIL_NULL_CHECK(param_list); CFCUTIL_NULL_CHECK(class_name); CFCUTIL_NULL_CHECK(alias); self->param_list = (CFCParamList*)CFCBase_incref((CFCBase*)param_list); self->class_name = CFCUtil_strdup(class_name); self->alias = CFCUtil_strdup(alias); self->use_labeled_params = use_labeled_params; self->perl_name = CFCUtil_sprintf("%s::%s", class_name, alias); size_t c_name_len = strlen(self->perl_name) + sizeof("XS_") + 1; self->c_name = (char*)MALLOCATE(c_name_len); int j = 3; memcpy(self->c_name, "XS_", j); for (int i = 0, max = (int)strlen(self->perl_name); i < max; i++) { char c = self->perl_name[i]; if (c == ':') { while (self->perl_name[i + 1] == ':') { i++; } self->c_name[j++] = '_'; } else { self->c_name[j++] = c; } } self->c_name[j] = 0; // NULL-terminate. return self; }
CFCPerl* CFCPerl_init(CFCPerl *self, CFCHierarchy *hierarchy, const char *lib_dir, const char *boot_class, const char *header, const char *footer) { CFCUTIL_NULL_CHECK(hierarchy); CFCUTIL_NULL_CHECK(lib_dir); CFCUTIL_NULL_CHECK(boot_class); CFCUTIL_NULL_CHECK(header); CFCUTIL_NULL_CHECK(footer); self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); self->lib_dir = CFCUtil_strdup(lib_dir); self->boot_class = CFCUtil_strdup(boot_class); self->header = CFCUtil_strdup(header); self->footer = CFCUtil_strdup(footer); // Derive path to generated .xs file. self->xs_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.xs", lib_dir, boot_class); S_replace_double_colons(self->xs_path, CHY_DIR_SEP_CHAR); // Derive the name of the bootstrap function. self->boot_func = CFCUtil_sprintf("cfish_%s_bootstrap", boot_class); for (int i = 0; self->boot_func[i] != 0; i++) { if (!isalnum(self->boot_func[i])) { self->boot_func[i] = '_'; } } return self; }
CFCPerlConstructor* CFCPerlConstructor_init(CFCPerlConstructor *self, CFCClass *klass, const char *alias, const char *initializer) { CFCUTIL_NULL_CHECK(alias); CFCUTIL_NULL_CHECK(klass); const char *class_name = CFCClass_get_class_name(klass); initializer = initializer ? initializer : "init"; // Find the implementing function. self->init_func = NULL; CFCFunction **funcs = CFCClass_functions(klass); for (size_t i = 0; funcs[i] != NULL; i++) { CFCFunction *func = funcs[i]; const char *func_name = CFCFunction_micro_sym(func); if (strcmp(initializer, func_name) == 0) { self->init_func = (CFCFunction*)CFCBase_incref((CFCBase*)func); break; } } if (!self->init_func) { CFCUtil_die("Missing or invalid '%s' function for '%s'", initializer, class_name); } CFCParamList *param_list = CFCFunction_get_param_list(self->init_func); CFCPerlSub_init((CFCPerlSub*)self, param_list, class_name, alias, true); return self; }
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; }
CFCBindCore* CFCBindCore_init(CFCBindCore *self, CFCHierarchy *hierarchy, const char *header, const char *footer) { CFCUTIL_NULL_CHECK(hierarchy); CFCUTIL_NULL_CHECK(header); CFCUTIL_NULL_CHECK(footer); self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); self->c_header = CFCUtil_make_c_comment(header); self->c_footer = CFCUtil_make_c_comment(footer); return self; }
CFCGoClass* CFCGoClass_new(CFCParcel *parcel, const char *class_name) { CFCUTIL_NULL_CHECK(parcel); CFCUTIL_NULL_CHECK(class_name); CFCGoClass *self = (CFCGoClass*)CFCBase_allocate(&CFCGOCLASS_META); self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel); self->class_name = CFCUtil_strdup(class_name); // Client may be NULL, since fetch_singleton() does not always succeed. CFCClass *client = CFCClass_fetch_singleton(parcel, class_name); self->client = (CFCClass*)CFCBase_incref((CFCBase*)client); return self; }
CFCC* CFCC_init(CFCC *self, CFCHierarchy *hierarchy, const char *header, const char *footer) { CFCUTIL_NULL_CHECK(hierarchy); CFCUTIL_NULL_CHECK(header); CFCUTIL_NULL_CHECK(footer); self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); self->html_gen = CFCCHtml_new(hierarchy, header, footer); self->c_header = CFCUtil_make_c_comment(header); self->c_footer = CFCUtil_make_c_comment(footer); self->man_header = CFCUtil_make_troff_comment(header); self->man_footer = CFCUtil_make_troff_comment(footer); return self; }
CFCCallable* CFCCallable_init(CFCCallable *self, const char *exposure, const char *name, CFCType *return_type, CFCParamList *param_list, CFCDocuComment *docucomment) { exposure = exposure ? exposure : "parcel"; CFCUTIL_NULL_CHECK(return_type); CFCUTIL_NULL_CHECK(param_list); CFCSymbol_init((CFCSymbol*)self, exposure, name); self->return_type = (CFCType*)CFCBase_incref((CFCBase*)return_type); self->param_list = (CFCParamList*)CFCBase_incref((CFCBase*)param_list); self->docucomment = (CFCDocuComment*)CFCBase_incref((CFCBase*)docucomment); return self; }
void CFCClass_add_child(CFCClass *self, CFCClass *child) { CFCUTIL_NULL_CHECK(child); if (self->tree_grown) { CFCUtil_die("Can't call add_child after grow_tree"); } if (self->is_inert) { CFCUtil_die("Can't inherit from inert class %s", CFCClass_get_class_name(self)); } if (child->is_inert) { CFCUtil_die("Inert class %s can't inherit", CFCClass_get_class_name(child)); } self->num_kids++; size_t size = (self->num_kids + 1) * sizeof(CFCClass*); self->children = (CFCClass**)REALLOCATE(self->children, size); self->children[self->num_kids - 1] = (CFCClass*)CFCBase_incref((CFCBase*)child); self->children[self->num_kids] = NULL; // Add parcel dependency. CFCParcel *parcel = CFCClass_get_parcel(self); CFCParcel *child_parcel = CFCClass_get_parcel(child); CFCParcel_add_inherited_parcel(child_parcel, parcel); }
void CFCGoClass_spec_method(CFCGoClass *self, const char *name, const char *sig) { CFCUTIL_NULL_CHECK(sig); S_lazy_init_method_bindings(self); if (!name) { CFCGoMethod *meth_binding = CFCGoMethod_new(NULL); CFCGoMethod_customize(meth_binding, sig); size_t size = (self->num_bound + 2) * sizeof(CFCGoMethod*); self->method_bindings = (CFCGoMethod**)REALLOCATE(self->method_bindings, size); self->method_bindings[self->num_bound] = meth_binding; self->num_bound++; self->method_bindings[self->num_bound] = NULL; } else { CFCGoMethod *binding = NULL; for (int i = 0; self->method_bindings[i] != NULL; i++) { CFCGoMethod *candidate = self->method_bindings[i]; CFCMethod *meth = CFCGoMethod_get_client(candidate); if (meth && strcmp(name, CFCMethod_get_name(meth)) == 0) { binding = candidate; break; } } if (!binding) { CFCUtil_die("Can't find a method named '%s'", name); } CFCGoMethod_customize(binding, sig); } }
static void S_process_dump_member(CFCClass *klass, CFCVariable *member, char *buf, size_t buf_size) { CFCUTIL_NULL_CHECK(member); CFCType *type = CFCVariable_get_type(member); const char *name = CFCVariable_micro_sym(member); unsigned name_len = (unsigned)strlen(name); const char *specifier = CFCType_get_specifier(type); // Skip the VTable. if (strcmp(specifier, "cfish_VTable") == 0) { return; } if (CFCType_is_integer(type) || CFCType_is_floating(type)) { char int_pattern[] = " Cfish_Hash_Store_Str(dump, \"%s\", %u, (cfish_Obj*)cfish_CB_newf(\"%%i64\", (int64_t)ivars->%s));\n"; char float_pattern[] = " Cfish_Hash_Store_Str(dump, \"%s\", %u, (cfish_Obj*)cfish_CB_newf(\"%%f64\", (double)ivars->%s));\n"; char bool_pattern[] = " Cfish_Hash_Store_Str(dump, \"%s\", %u, (cfish_Obj*)cfish_Bool_singleton(ivars->%s));\n"; const char *pattern; if (strcmp(specifier, "bool") == 0) { pattern = bool_pattern; } else if (CFCType_is_integer(type)) { pattern = int_pattern; } else { pattern = float_pattern; } size_t needed = strlen(pattern) + name_len * 2 + 20; if (buf_size < needed) { CFCUtil_die("Buffer not big enough (%lu < %lu)", (unsigned long)buf_size, (unsigned long)needed); } sprintf(buf, pattern, name, name_len, name); } else if (CFCType_is_object(type)) { char pattern[] = " if (ivars->%s) {\n" " Cfish_Hash_Store_Str(dump, \"%s\", %u, Cfish_Obj_Dump((cfish_Obj*)ivars->%s));\n" " }\n"; size_t needed = strlen(pattern) + name_len * 3 + 20; if (buf_size < needed) { CFCUtil_die("Buffer not big enough (%lu < %lu)", (unsigned long)buf_size, (unsigned long)needed); } sprintf(buf, pattern, name, name, name_len, name); } else { CFCUtil_die("Don't know how to dump a %s", CFCType_get_specifier(type)); } CFCClass_append_autocode(klass, buf); }
CFCUri* CFCUri_init(CFCUri *self, const char *uri, CFCClass *doc_class) { CFCUTIL_NULL_CHECK(uri); self->string = CFCUtil_strdup(uri); self->doc_class = (CFCClass*)CFCBase_incref((CFCBase*)doc_class); return self; }
CFCPython* CFCPython_new(CFCHierarchy *hierarchy) { CFCUTIL_NULL_CHECK(hierarchy); CFCPython *self = (CFCPython*)CFCBase_allocate(&CFCPYTHON_META); self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); self->header = CFCUtil_strdup(""); self->footer = CFCUtil_strdup(""); return self; }
CFCPerl* CFCPerl_init(CFCPerl *self, CFCHierarchy *hierarchy, const char *lib_dir, const char *header, const char *footer) { CFCUTIL_NULL_CHECK(hierarchy); CFCUTIL_NULL_CHECK(lib_dir); CFCUTIL_NULL_CHECK(header); CFCUTIL_NULL_CHECK(footer); self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); self->lib_dir = CFCUtil_strdup(lib_dir); self->header = CFCUtil_strdup(header); self->footer = CFCUtil_strdup(footer); self->c_header = CFCUtil_make_c_comment(header); self->c_footer = CFCUtil_make_c_comment(footer); self->pod_header = CFCUtil_make_perl_comment(header); self->pod_footer = CFCUtil_make_perl_comment(footer); return self; }
static void S_process_load_member(CFCClass *klass, CFCVariable *member, char *buf, size_t buf_size) { CFCUTIL_NULL_CHECK(member); CFCType *type = CFCVariable_get_type(member); const char *type_str = CFCType_to_c(type); const char *name = CFCVariable_micro_sym(member); unsigned name_len = (unsigned)strlen(name); char extraction[200]; const char *specifier = CFCType_get_specifier(type); // Skip the VTable. if (strcmp(specifier, "cfish_VTable") == 0) { return; } if (2 * strlen(type_str) + 100 > sizeof(extraction)) { // play it safe CFCUtil_die("type_str too long: '%s'", type_str); } if (CFCType_is_integer(type)) { if (strcmp(specifier, "bool") == 0) { sprintf(extraction, "Cfish_Obj_To_Bool(var)"); } else { sprintf(extraction, "(%s)Cfish_Obj_To_I64(var)", type_str); } } else if (CFCType_is_floating(type)) { sprintf(extraction, "(%s)Cfish_Obj_To_F64(var)", type_str); } else if (CFCType_is_object(type)) { const char *vtable_var = CFCType_get_vtable_var(type); sprintf(extraction, "(%s*)CFISH_CERTIFY(Cfish_Obj_Load(var, var), %s)", specifier, vtable_var); } else { CFCUtil_die("Don't know how to load %s", specifier); } const char *pattern = " {\n" " cfish_Obj *var = Cfish_Hash_Fetch_Str(source, \"%s\", %u);\n" " if (var) { ivars->%s = %s; }\n" " }\n"; size_t needed = sizeof(pattern) + (name_len * 2) + strlen(extraction) + 20; if (buf_size < needed) { CFCUtil_die("Buffer not big enough (%lu < %lu)", (unsigned long)buf_size, (unsigned long)needed); } sprintf(buf, pattern, name, name_len, name, extraction); CFCClass_append_autocode(klass, buf); }
CFCGoClass* CFCGoClass_singleton(const char *class_name) { CFCUTIL_NULL_CHECK(class_name); for (size_t i = 0; i < registry_size; i++) { CFCGoClass *existing = registry[i]; if (strcmp(class_name, existing->class_name) == 0) { return existing; } } return NULL; }
CFCPerlClass* CFCPerlClass_init(CFCPerlClass *self, CFCParcel *parcel, const char *class_name) { CFCUTIL_NULL_CHECK(parcel); CFCUTIL_NULL_CHECK(class_name); self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel); self->class_name = CFCUtil_strdup(class_name); // Client may be NULL, since fetch_singleton() does not always succeed. CFCClass *client = CFCClass_fetch_singleton(parcel, class_name); self->client = (CFCClass*)CFCBase_incref((CFCBase*)client); self->pod_spec = NULL; self->xs_code = NULL; self->cons_aliases = NULL; self->cons_inits = NULL; self->num_cons = 0; self->exclude_cons = 0; self->class_aliases = (char**)CALLOCATE(1, sizeof(char*)); self->num_class_aliases = 0; return self; }
void CFCPerlPod_add_method(CFCPerlPod *self, const char *alias, const char *method, const char *sample, const char *pod) { CFCUTIL_NULL_CHECK(alias); self->num_methods++; size_t size = self->num_methods * sizeof(NamePod); self->methods = (NamePod*)REALLOCATE(self->methods, size); NamePod *slot = &self->methods[self->num_methods - 1]; slot->alias = CFCUtil_strdup(alias); slot->func = method ? CFCUtil_strdup(method) : NULL; slot->sample = sample ? CFCUtil_strdup(sample) : NULL; slot->pod = pod ? CFCUtil_strdup(pod) : NULL; }
void CFCClass_add_inert_var(CFCClass *self, CFCVariable *var) { CFCUTIL_NULL_CHECK(var); if (self->tree_grown) { CFCUtil_die("Can't call add_inert_var after grow_tree"); } self->num_inert_vars++; size_t size = (self->num_inert_vars + 1) * sizeof(CFCVariable*); self->inert_vars = (CFCVariable**)REALLOCATE(self->inert_vars, size); self->inert_vars[self->num_inert_vars - 1] = (CFCVariable*)CFCBase_incref((CFCBase*)var); self->inert_vars[self->num_inert_vars] = NULL; }
void CFCClass_add_function(CFCClass *self, CFCFunction *func) { CFCUTIL_NULL_CHECK(func); if (self->tree_grown) { CFCUtil_die("Can't call add_function after grow_tree"); } self->num_functions++; size_t size = (self->num_functions + 1) * sizeof(CFCFunction*); self->functions = (CFCFunction**)REALLOCATE(self->functions, size); self->functions[self->num_functions - 1] = (CFCFunction*)CFCBase_incref((CFCBase*)func); self->functions[self->num_functions] = NULL; }
void CFCClass_add_method(CFCClass *self, CFCMethod *method) { CFCUTIL_NULL_CHECK(method); if (self->tree_grown) { CFCUtil_die("Can't call add_method after grow_tree"); } if (self->is_inert) { CFCUtil_die("Can't add_method to an inert class"); } self->num_methods++; size_t size = (self->num_methods + 1) * sizeof(CFCMethod*); self->methods = (CFCMethod**)REALLOCATE(self->methods, size); self->methods[self->num_methods - 1] = (CFCMethod*)CFCBase_incref((CFCBase*)method); self->methods[self->num_methods] = NULL; }
static void S_lazy_init_method_bindings(CFCGoClass *self) { if (self->method_bindings) { return; } CFCUTIL_NULL_CHECK(self->client); size_t num_bound = 0; CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client); CFCGoMethod **bound = (CFCGoMethod**)CALLOCATE(1, sizeof(CFCGoMethod*)); // Iterate over the class's fresh methods. for (size_t i = 0; fresh_methods[i] != NULL; i++) { CFCMethod *method = fresh_methods[i]; // Skip methods which have been explicitly excluded. if (CFCMethod_excluded_from_host(method)) { continue; } // Skip methods that shouldn't be bound. if (!CFCMethod_can_be_bound(method)) { continue; } // Only include novel methods. if (!CFCMethod_novel(method)) { continue; } const char *sym = CFCMethod_get_name(method); if (!CFCClass_fresh_method(self->client, sym)) { continue; } /* Create the binding, add it to the array. */ CFCGoMethod *meth_binding = CFCGoMethod_new(method); size_t size = (num_bound + 2) * sizeof(CFCGoMethod*); bound = (CFCGoMethod**)REALLOCATE(bound, size); bound[num_bound] = meth_binding; num_bound++; bound[num_bound] = NULL; } self->method_bindings = bound; self->num_bound = num_bound; }
static void S_add_tree(CFCHierarchy *self, CFCClass *klass) { CFCUTIL_NULL_CHECK(klass); const char *full_struct_sym = CFCClass_full_struct_sym(klass); for (size_t i = 0; self->trees[i] != NULL; i++) { const char *existing = CFCClass_full_struct_sym(self->trees[i]); if (strcmp(full_struct_sym, existing) == 0) { CFCUtil_die("Tree '%s' alread added", full_struct_sym); } } self->num_trees++; size_t size = (self->num_trees + 1) * sizeof(CFCClass*); self->trees = (CFCClass**)REALLOCATE(self->trees, size); self->trees[self->num_trees - 1] = (CFCClass*)CFCBase_incref((CFCBase*)klass); self->trees[self->num_trees] = NULL; }
static void S_add_file(CFCHierarchy *self, CFCFile *file) { CFCUTIL_NULL_CHECK(file); CFCClass **classes = CFCFile_classes(file); self->num_files++; size_t size = (self->num_files + 1) * sizeof(CFCFile*); self->files = (CFCFile**)REALLOCATE(self->files, size); self->files[self->num_files - 1] = (CFCFile*)CFCBase_incref((CFCBase*)file); self->files[self->num_files] = NULL; for (size_t i = 0; classes[i] != NULL; i++) { CFCClass *klass = classes[i]; const char *parent_name = CFCClass_get_parent_class_name(klass); if (!parent_name) { S_add_tree(self, klass); } } }
CFCClass* CFCClass_fetch_singleton(CFCParcel *parcel, const char *class_name) { CFCUTIL_NULL_CHECK(class_name); // Build up the key. const char *last_colon = strrchr(class_name, ':'); const char *struct_sym = last_colon ? last_colon + 1 : class_name; const char *prefix = parcel ? CFCParcel_get_prefix(parcel) : ""; size_t prefix_len = strlen(prefix); size_t struct_sym_len = strlen(struct_sym); if (prefix_len + struct_sym_len > MAX_SINGLETON_LEN) { CFCUtil_die("names too long: '%s', '%s'", prefix, struct_sym); } char key[MAX_SINGLETON_LEN + 1]; sprintf(key, "%s%s", prefix, struct_sym); for (size_t i = 0; i < registry_size; i++) { if (strcmp(registry[i].key, key) == 0) { return registry[i].klass; } } return NULL; }
void CFCPython_set_footer(CFCPython *self, const char *footer) { CFCUTIL_NULL_CHECK(footer); free(self->footer); self->footer = CFCUtil_make_c_comment(footer); }
void CFCPython_set_header(CFCPython *self, const char *header) { CFCUTIL_NULL_CHECK(header); free(self->header); self->header = CFCUtil_make_c_comment(header); }
CFCSymbol* CFCSymbol_init(CFCSymbol *self, struct CFCParcel *parcel, const char *exposure, const char *class_name, const char *class_cnick, const char *micro_sym) { // Validate. CFCUTIL_NULL_CHECK(parcel); if (!S_validate_exposure(exposure)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid exposure: '%s'", exposure ? exposure : "[NULL]"); } if (class_name && !S_validate_class_name(class_name)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid class_name: '%s'", class_name); } if (!micro_sym || !S_validate_identifier(micro_sym)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid micro_sym: '%s'", micro_sym ? micro_sym : "[NULL]"); } // Derive class_cnick if necessary, then validate. const char *real_cnick = NULL; if (class_name) { if (class_cnick) { real_cnick = class_cnick; } else { const char *last_colon = strrchr(class_name, ':'); real_cnick = last_colon ? last_colon + 1 : class_name; } } else if (class_cnick) { // Sanity check class_cnick without class_name. CFCBase_decref((CFCBase*)self); CFCUtil_die("Can't supply class_cnick without class_name"); } else { real_cnick = NULL; } if (real_cnick && !S_validate_class_cnick(real_cnick)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid class_cnick: '%s'", real_cnick); } // Assign. self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel); self->exposure = CFCUtil_strdup(exposure); self->class_name = CFCUtil_strdup(class_name); self->class_cnick = CFCUtil_strdup(real_cnick); self->micro_sym = CFCUtil_strdup(micro_sym); // Derive short_sym. size_t class_cnick_len = self->class_cnick ? strlen(self->class_cnick) : 0; size_t short_sym_len = class_cnick_len + strlen("_") + strlen(self->micro_sym); self->short_sym = (char*)MALLOCATE(short_sym_len + 1); if (self->class_cnick) { memcpy((void*)self->short_sym, self->class_cnick, class_cnick_len); } self->short_sym[class_cnick_len] = '_'; memcpy(&self->short_sym[class_cnick_len + 1], self->micro_sym, strlen(micro_sym)); self->short_sym[short_sym_len] = '\0'; // Derive full_sym; const char *prefix = CFCParcel_get_prefix(self->parcel); size_t prefix_len = strlen(prefix); size_t full_sym_len = prefix_len + short_sym_len; self->full_sym = (char*)MALLOCATE(full_sym_len + 1); memcpy(self->full_sym, prefix, prefix_len); memcpy(&self->full_sym[prefix_len], self->short_sym, short_sym_len); self->full_sym[full_sym_len] = '\0'; return self; }
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); }
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); }