void CFCPerl_write_bindings(CFCPerl *self) { CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); CFCPerlClass **registry = CFCPerlClass_registry(); char *hand_rolled_xs = CFCUtil_strdup(""); char *generated_xs = CFCUtil_strdup(""); char *xs_init = CFCUtil_strdup(""); // Pound-includes for generated headers. for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; // TODO: Don't include headers for parcels the source parcels don't // depend on. const char *include_h = CFCClass_include_h(klass); generated_xs = CFCUtil_cat(generated_xs, "#include \"", include_h, "\"\n", NULL); } generated_xs = CFCUtil_cat(generated_xs, "\n", NULL); for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass)) { continue; } // 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]); generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n", NULL); FREEMEM(xsub_def); // Add XSUB initialization at boot. xs_init = S_add_xs_init(xs_init, xsub); } 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]); generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n", NULL); FREEMEM(xsub_def); // Add XSUB initialization at boot. xs_init = S_add_xs_init(xs_init, xsub); } FREEMEM(methods); } // Hand-rolled XS. for (size_t i = 0; registry[i] != NULL; i++) { const char *xs = CFCPerlClass_get_xs_code(registry[i]); hand_rolled_xs = CFCUtil_cat(hand_rolled_xs, xs, "\n", NULL); } // Write out if there have been any changes. char *xs_file_contents = S_xs_file_contents(self, generated_xs, xs_init, hand_rolled_xs); CFCUtil_write_if_changed(self->xs_path, xs_file_contents, strlen(xs_file_contents)); FREEMEM(xs_file_contents); FREEMEM(hand_rolled_xs); FREEMEM(xs_init); FREEMEM(generated_xs); FREEMEM(ordered); }
CFCParcel* CFCParcel_init(CFCParcel *self, const char *name, const char *nickname, CFCVersion *version, CFCVersion *major_version, CFCFileSpec *file_spec) { // Validate name. if (!name || !S_validate_name_or_nickname(name)) { CFCUtil_die("Invalid name: '%s'", name ? name : "[NULL]"); } self->name = CFCUtil_strdup(name); // Validate or derive nickname. if (nickname) { if (!S_validate_name_or_nickname(nickname)) { CFCUtil_die("Invalid nickname: '%s'", nickname); } self->nickname = CFCUtil_strdup(nickname); } else { // Default nickname to name. self->nickname = CFCUtil_strdup(name); } // Default to version v0. if (version) { self->version = (CFCVersion*)CFCBase_incref((CFCBase*)version); } else { self->version = CFCVersion_new("v0"); } if (major_version) { self->major_version = (CFCVersion*)CFCBase_incref((CFCBase*)major_version); } else { self->major_version = CFCVersion_new("v0"); } // Set file_spec. self->file_spec = (CFCFileSpec*)CFCBase_incref((CFCBase*)file_spec); // Derive prefix, Prefix, PREFIX. size_t nickname_len = strlen(self->nickname); size_t prefix_len = nickname_len ? nickname_len + 1 : 0; size_t amount = prefix_len + 1; self->prefix = (char*)MALLOCATE(amount); self->Prefix = (char*)MALLOCATE(amount); self->PREFIX = (char*)MALLOCATE(amount); memcpy(self->Prefix, self->nickname, nickname_len); if (nickname_len) { self->Prefix[nickname_len] = '_'; self->Prefix[nickname_len + 1] = '\0'; } else { self->Prefix[nickname_len] = '\0'; } for (size_t i = 0; i < amount; i++) { self->prefix[i] = CFCUtil_tolower(self->Prefix[i]); self->PREFIX[i] = CFCUtil_toupper(self->Prefix[i]); } self->prefix[prefix_len] = '\0'; self->Prefix[prefix_len] = '\0'; self->PREFIX[prefix_len] = '\0'; // Derive privacy symbol. size_t privacy_sym_len = nickname_len + 4; self->privacy_sym = (char*)MALLOCATE(privacy_sym_len + 1); memcpy(self->privacy_sym, "CFP_", 4); for (size_t i = 0; i < nickname_len; i++) { self->privacy_sym[i+4] = CFCUtil_toupper(self->nickname[i]); } self->privacy_sym[privacy_sym_len] = '\0'; // Initialize flags. self->is_installed = false; // Initialize arrays. self->classes = (CFCClass**)CALLOCATE(1, sizeof(CFCClass*)); self->num_classes = 0; self->prereqs = (CFCPrereq**)CALLOCATE(1, sizeof(CFCPrereq*)); self->num_prereqs = 0; return self; }
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[] = "%s" " Cfish_ZCB_Assign_Str(alias, \"%s\", %u);\n" " cfish_VTable_add_alias_to_registry(%s,\n" " (cfish_CharBuf*)alias);\n"; char *new_alias_adds = CFCUtil_sprintf(pattern, alias_adds, alias, (unsigned)alias_len, vtable_var); FREEMEM(alias_adds); alias_adds = new_alias_adds; } 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/CharBuf.h\"\n" "#include \"Clownfish/VTable.h\"\n" "%s\n" "\n" "void\n" "%s() {\n" "%s" "\n" " cfish_ZombieCharBuf *alias = CFISH_ZCB_WRAP_STR(\"\", 0);\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); }
CFCClass* CFCClass_do_create(CFCClass *self, struct CFCParcel *parcel, const char *exposure, const char *name, const char *nickname, CFCDocuComment *docucomment, CFCFileSpec *file_spec, const char *parent_class_name, int is_final, int is_inert, int is_abstract) { CFCUTIL_NULL_CHECK(parcel); CFCUTIL_NULL_CHECK(name); exposure = exposure ? exposure : "parcel"; // Validate. if (!S_validate_exposure(exposure)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid exposure: '%s'", exposure); } if (!CFCClass_validate_class_name(name)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid name: '%s'", name); } const char *last_colon = strrchr(name, ':'); const char *struct_sym = last_colon ? last_colon + 1 : name; // Derive nickname if necessary, then validate. const char *real_nickname = NULL; if (nickname) { real_nickname = nickname; } else { real_nickname = struct_sym; } if (!S_validate_nickname(real_nickname)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid nickname: '%s'", real_nickname); } // Default parent class name is "Clownfish::Obj". if (!is_inert && !parent_class_name && strcmp(name, "Clownfish::Obj") != 0 ) { parent_class_name = "Clownfish::Obj"; } // Assign. self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel); self->exposure = CFCUtil_strdup(exposure); self->name = CFCUtil_strdup(name); self->nickname = CFCUtil_strdup(real_nickname); self->tree_grown = false; self->parent = NULL; self->children = (CFCClass**)CALLOCATE(1, sizeof(CFCClass*)); self->num_kids = 0; self->functions = (CFCFunction**)CALLOCATE(1, sizeof(CFCFunction*)); self->num_functions = 0; self->fresh_methods = (CFCMethod**)CALLOCATE(1, sizeof(CFCMethod*)); self->num_fresh_meths = 0; self->methods = NULL; self->num_methods = 0; self->fresh_vars = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*)); self->num_fresh_vars = 0; self->member_vars = NULL; self->num_member_vars = 0; self->inert_vars = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*)); self->num_inert_vars = 0; self->parent_class_name = CFCUtil_strdup(parent_class_name); self->docucomment = (CFCDocuComment*)CFCBase_incref((CFCBase*)docucomment); self->file_spec = (CFCFileSpec*)CFCBase_incref((CFCBase*)file_spec); // Cache several derived symbols. const char *prefix = CFCClass_get_prefix(self); self->struct_sym = CFCUtil_strdup(struct_sym); self->full_struct_sym = CFCUtil_sprintf("%s%s", prefix, struct_sym); self->ivars_struct = CFCUtil_sprintf("%sIVARS", struct_sym); self->full_ivars_struct = CFCUtil_sprintf("%s%s", prefix, self->ivars_struct); self->ivars_func = CFCUtil_sprintf("%s_IVARS", self->nickname); self->full_ivars_func = CFCUtil_sprintf("%s%s", prefix, self->ivars_func); self->full_ivars_offset = CFCUtil_sprintf("%s_OFFSET", self->full_ivars_func); const char *PREFIX = CFCClass_get_PREFIX(self); size_t struct_sym_len = strlen(struct_sym); char *short_class_var = (char*)MALLOCATE(struct_sym_len + 1); size_t i; for (i = 0; i < struct_sym_len; i++) { short_class_var[i] = toupper(struct_sym[i]); } short_class_var[struct_sym_len] = '\0'; self->short_class_var = short_class_var; self->full_class_var = CFCUtil_sprintf("%s%s", PREFIX, short_class_var); self->privacy_symbol = CFCUtil_sprintf("C_%s", self->full_class_var); // Build the relative path to the autogenerated C header file. if (file_spec) { const char *path_part = CFCFileSpec_get_path_part(self->file_spec); self->include_h = CFCUtil_sprintf("%s.h", path_part); } else { self->include_h = CFCUtil_strdup("class.h"); } self->is_final = !!is_final; self->is_inert = !!is_inert; self->is_abstract = !!is_abstract; // Check for include flag mismatch. if (!CFCClass_included(self) && CFCParcel_included(parcel)) { CFCUtil_die("Class %s from source dir found in parcel %s from" " include dir", name, CFCParcel_get_name(parcel)); } // Skip class if it's from an include dir and the parcel was already // processed in another source or include dir. const char *class_source_dir = CFCClass_get_source_dir(self); const char *parcel_source_dir = CFCParcel_get_source_dir(parcel); if (!CFCClass_included(self) || !class_source_dir || !parcel_source_dir || strcmp(class_source_dir, parcel_source_dir) == 0 ) { char *error; CFCUTIL_TRY { // Store in registry. S_register(self); } CFCUTIL_CATCH(error); if (error) { CFCBase_decref((CFCBase*)self); CFCUtil_rethrow(error); } CFCParcel_add_struct_sym(parcel, self->struct_sym); }