int CFCParcel_equals(CFCParcel *self, CFCParcel *other) { if (strcmp(self->name, other->name)) { return false; } if (strcmp(self->nickname, other->nickname)) { return false; } if (CFCVersion_compare_to(self->version, other->version) != 0) { return false; } if (CFCParcel_included(self) != CFCParcel_included(other)) { return false; } return true; }
void CFCHierarchy_build(CFCHierarchy *self) { // Read .cfp files. for (size_t i = 0; self->sources[i] != NULL; i++) { S_parse_source_cfp_files(self, self->sources[i]); } CFCParcel **parcels = CFCParcel_all_parcels(); // Read .cfh files of included parcels. for (size_t i = 0; parcels[i] != NULL; i++) { CFCParcel *parcel = parcels[i]; if (CFCParcel_included(parcel)) { const char *source_dir = CFCParcel_get_source_dir(parcel); S_parse_cf_files(self, source_dir, true); } } // Read .cfh and .md files of source parcels. for (size_t i = 0; self->sources[i] != NULL; i++) { S_parse_cf_files(self, self->sources[i], false); S_find_doc_files(self->sources[i]); } // It's important that prereq parcels are processed first. for (size_t i = 0; parcels[i] != NULL; i++) { CFCParcel_connect_and_sort_classes(parcels[i]); } for (size_t i = 0; self->trees[i] != NULL; i++) { CFCClass_grow_tree(self->trees[i]); } }
void CFCHierarchy_read_host_data_json(CFCHierarchy *self, const char *host_lang) { CHY_UNUSED_VAR(self); CFCParcel **parcels = CFCParcel_all_parcels(); for (int i = 0; parcels[i]; ++i) { CFCParcel *parcel = parcels[i]; if (CFCParcel_included(parcel)) { CFCParcel_read_host_data_json(parcel, host_lang); } } }
void CFCPerl_write_host_code(CFCPerl *self) { CFCParcel **parcels = CFCParcel_all_parcels(); for (size_t i = 0; parcels[i]; ++i) { CFCParcel *parcel = parcels[i]; if (!CFCParcel_included(parcel)) { S_write_host_h(self, parcel); S_write_host_c(self, parcel); } } }
int CFCBindCore_write_all_modified(CFCBindCore *self, int modified) { CFCHierarchy *hierarchy = self->hierarchy; const char *header = self->c_header; const char *footer = self->c_footer; // Discover whether files need to be regenerated. modified = CFCHierarchy_propagate_modified(hierarchy, modified); // Iterate over all File objects, writing out those which don't have // up-to-date auto-generated files. const char *inc_dest = CFCHierarchy_get_include_dest(hierarchy); CFCFile **files = CFCHierarchy_files(hierarchy); for (int i = 0; files[i] != NULL; i++) { if (CFCFile_get_modified(files[i])) { CFCBindFile_write_h(files[i], inc_dest, header, footer); } } // If any class definition has changed, rewrite the parcel.h and parcel.c // files. if (modified) { S_write_platform_h(self); CFCParcel **parcels = CFCParcel_all_parcels(); for (size_t i = 0; parcels[i]; ++i) { CFCParcel *parcel = parcels[i]; if (CFCParcel_required(parcel)) { S_write_parcel_h(self, parcel); if (!CFCParcel_included(parcel)) { S_write_parcel_c(self, parcel); } } } } return modified; }
static void S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) { const char *parcel_name = CFCParcel_get_name(parcel); char *pymod_name = CFCUtil_strdup(parcel_name); // TODO: Stop lowercasing when parcels are restricted to lowercase. for (int i = 0; pymod_name[i] != '\0'; i++) { pymod_name[i] = tolower(pymod_name[i]); } const char *last_dot = strrchr(pymod_name, '.'); const char *last_component = last_dot != NULL ? last_dot + 1 : pymod_name; char *helper_mod_name = CFCUtil_sprintf("%s._%s", pymod_name, last_component); for (int i = 0; helper_mod_name[i] != '\0'; i++) { helper_mod_name[i] = tolower(helper_mod_name[i]); } CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); CFCParcel **parcels = CFCParcel_all_parcels(); char *callbacks = S_gen_callbacks(self, parcel, ordered); char *type_linkups = S_gen_type_linkups(self, parcel, ordered); char *pound_includes = CFCUtil_strdup(""); char *class_bindings = S_gen_class_bindings(self, parcel, pymod_name, ordered); char *parcel_boots = CFCUtil_strdup(""); char *pytype_ready_calls = CFCUtil_strdup(""); char *module_adds = CFCUtil_strdup(""); // Add parcel bootstrapping calls. for (size_t i = 0; parcels[i]; ++i) { if (!CFCParcel_included(parcels[i])) { const char *prefix = CFCParcel_get_prefix(parcels[i]); parcel_boots = CFCUtil_cat(parcel_boots, " ", prefix, "bootstrap_parcel();\n", NULL); } } for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass)) { continue; } const char *struct_sym = CFCClass_get_struct_sym(klass); const char *include_h = CFCClass_include_h(klass); pound_includes = CFCUtil_cat(pound_includes, "#include \"", include_h, "\"\n", NULL); // The PyType_Ready invocations for instantiable classes are handled // via bootstrapping of Clownfish Class objects. Since inert classes // do not at present have Class objects, we need to handle their // PyType_Ready calls independently. if (CFCClass_inert(klass)) { pytype_ready_calls = CFCUtil_cat(pytype_ready_calls, " if (PyType_Ready(&", struct_sym, "_pytype_struct) < 0) { return NULL; }\n", NULL); } module_adds = CFCUtil_cat(module_adds, " PyModule_AddObject(module, \"", struct_sym, "\", (PyObject*)&", struct_sym, "_pytype_struct);\n", NULL); } const char pattern[] = "%s\n" "\n" "#include \"Python.h\"\n" "#include \"cfish_parcel.h\"\n" "#include \"CFBind.h\"\n" "%s\n" "\n" "%s\n" // callbacks "\n" "static PyModuleDef module_def = {\n" " PyModuleDef_HEAD_INIT,\n" " \"%s\",\n" // module name " NULL,\n" // docstring " -1,\n" " NULL, NULL, NULL, NULL, NULL\n" "};\n" "\n" "%s" // class bindings "\n" "%s" // S_link_py_types function "\n" "PyMODINIT_FUNC\n" "PyInit__%s(void) {\n" " cfish_Class_bootstrap_hook1 = CFBind_class_bootstrap_hook1;\n" "\n" "%s\n" // PyType_Ready calls "\n" " S_link_py_types();\n" "\n" "%s\n" // parcel boots "\n" " PyObject *module = PyModule_Create(&module_def);\n" "%s\n" // Add types to module "\n" " return module;\n" "}\n" "\n" "%s\n" "\n"; char *content = CFCUtil_sprintf(pattern, self->header, pound_includes, callbacks, helper_mod_name, class_bindings, type_linkups, last_component, pytype_ready_calls, parcel_boots, module_adds, self->footer); char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "_%s.c", dest, last_component); CFCUtil_write_if_changed(filepath, content, strlen(content)); FREEMEM(filepath); FREEMEM(content); FREEMEM(module_adds); FREEMEM(pytype_ready_calls); FREEMEM(parcel_boots); FREEMEM(class_bindings); FREEMEM(helper_mod_name); FREEMEM(pymod_name); FREEMEM(pound_includes); FREEMEM(type_linkups); FREEMEM(callbacks); FREEMEM(ordered); }
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); }
CFCClass* CFCClass_do_create(CFCClass *self, struct CFCParcel *parcel, const char *exposure, const char *class_name, const char *cnick, const char *micro_sym, CFCDocuComment *docucomment, CFCFileSpec *file_spec, const char *parent_class_name, int is_final, int is_inert) { CFCUTIL_NULL_CHECK(class_name); exposure = exposure ? exposure : "parcel"; micro_sym = micro_sym ? micro_sym : "class"; parcel = parcel ? parcel : CFCParcel_default_parcel(); CFCSymbol_init((CFCSymbol*)self, parcel, exposure, class_name, cnick, micro_sym); if (!is_inert && !parent_class_name && strcmp(class_name, "Clownfish::Obj") != 0 ) { parent_class_name = "Clownfish::Obj"; } self->parent = NULL; self->tree_grown = false; self->children = (CFCClass**)CALLOCATE(1, sizeof(CFCClass*)); self->num_kids = 0; self->functions = (CFCFunction**)CALLOCATE(1, sizeof(CFCFunction*)); self->num_functions = 0; self->methods = (CFCMethod**)CALLOCATE(1, sizeof(CFCMethod*)); self->num_methods = 0; self->member_vars = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*)); self->num_member_vars = 0; self->inert_vars = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*)); self->num_inert_vars = 0; self->parent_class_name = CFCUtil_strdup(parent_class_name); self->docucomment = (CFCDocuComment*)CFCBase_incref((CFCBase*)docucomment); self->file_spec = (CFCFileSpec*)CFCBase_incref((CFCBase*)file_spec); // Cache several derived symbols. const char *last_colon = strrchr(class_name, ':'); self->struct_sym = last_colon ? CFCUtil_strdup(last_colon + 1) : CFCUtil_strdup(class_name); const char *prefix = CFCClass_get_prefix(self); size_t struct_sym_len = strlen(self->struct_sym); self->short_vtable_var = (char*)MALLOCATE(struct_sym_len + 1); size_t i; for (i = 0; i < struct_sym_len; i++) { self->short_vtable_var[i] = toupper(self->struct_sym[i]); } self->short_vtable_var[struct_sym_len] = '\0'; self->full_struct_sym = CFCUtil_sprintf("%s%s", prefix, self->struct_sym); self->ivars_struct = CFCUtil_sprintf("%sIVARS", self->struct_sym); self->full_ivars_struct = CFCUtil_sprintf("%sIVARS", self->full_struct_sym); self->ivars_func = CFCUtil_sprintf("%s_IVARS", CFCClass_get_cnick(self)); self->full_ivars_func = CFCUtil_sprintf("%s%s_IVARS", prefix, CFCClass_get_cnick(self)); self->full_ivars_offset = CFCUtil_sprintf("%s_OFFSET", self->full_ivars_func); size_t full_struct_sym_len = strlen(self->full_struct_sym); self->full_vtable_var = (char*)MALLOCATE(full_struct_sym_len + 1); for (i = 0; self->full_struct_sym[i] != '\0'; i++) { self->full_vtable_var[i] = toupper(self->full_struct_sym[i]); } self->full_vtable_var[i] = '\0'; self->privacy_symbol = CFCUtil_sprintf("C_%s", self->full_vtable_var); // Build the relative path to the autogenerated C header file. if (file_spec) { const char *path_part = CFCFileSpec_get_path_part(self->file_spec); self->include_h = CFCUtil_sprintf("%s.h", path_part); } else { self->include_h = CFCUtil_strdup("class.h"); } self->is_final = !!is_final; self->is_inert = !!is_inert; if (file_spec && CFCFileSpec_included(file_spec)) { if (!CFCParcel_included(parcel)) { CFCUtil_die("Class %s from include dir found in parcel %s from" " source dir", class_name, CFCParcel_get_name(parcel)); } } else { if (CFCParcel_included(parcel)) { CFCUtil_die("Class %s from source dir found in parcel %s from" " include dir", class_name, CFCParcel_get_name(parcel)); } } // Store in registry. S_register(self); return self; }
static void S_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); }
static void S_run_parcel_tests(CFCTest *test) { { CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, NULL); OK(test, parcel != NULL, "new"); OK(test, !CFCParcel_included(parcel), "not included"); CFCBase_decref((CFCBase*)parcel); } { CFCFileSpec *file_spec = CFCFileSpec_new(".", "Parcel", true); CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, file_spec); OK(test, CFCParcel_included(parcel), "included"); CFCBase_decref((CFCBase*)parcel); CFCBase_decref((CFCBase*)file_spec); } { const char *json = " {\n" " \"name\": \"Crustacean\",\n" " \"nickname\": \"Crust\",\n" " \"version\": \"v0.1.0\"\n" " }\n"; CFCParcel *parcel = CFCParcel_new_from_json(json, NULL); OK(test, parcel != NULL, "new_from_json"); CFCBase_decref((CFCBase*)parcel); } { const char *path = "t" CHY_DIR_SEP "cfbase" CHY_DIR_SEP "Animal.cfp"; CFCParcel *parcel = CFCParcel_new_from_file(path, NULL); OK(test, parcel != NULL, "new_from_file"); CFCBase_decref((CFCBase*)parcel); } { CFCParcel *parcel = CFCParcel_new("Crustacean", "Crust", NULL, NULL); CFCParcel_register(parcel); STR_EQ(test, CFCVersion_get_vstring(CFCParcel_get_version(parcel)), "v0", "get_version"); CFCBase_decref((CFCBase*)parcel); CFCParcel_reap_singletons(); } { const char *json = " {\n" " \"name\": \"Crustacean\",\n" " \"version\": \"v0.1.0\",\n" " \"prerequisites\": {\n" " \"Clownfish\": null,\n" " \"Arthropod\": \"v30.104.5\"\n" " }\n" " }\n"; CFCParcel *parcel = CFCParcel_new_from_json(json, NULL); CFCPrereq **prereqs = CFCParcel_get_prereqs(parcel); OK(test, prereqs != NULL, "prereqs"); CFCPrereq *cfish = prereqs[0]; OK(test, cfish != NULL, "prereqs[0]"); const char *cfish_name = CFCPrereq_get_name(cfish); STR_EQ(test, cfish_name, "Clownfish", "prereqs[0] name"); CFCVersion *v0 = CFCVersion_new("v0"); CFCVersion *cfish_version = CFCPrereq_get_version(cfish); INT_EQ(test, CFCVersion_compare_to(cfish_version, v0), 0, "prereqs[0] version"); CFCPrereq *apod = prereqs[1]; OK(test, apod != NULL, "prereqs[1]"); const char *apod_name = CFCPrereq_get_name(apod); STR_EQ(test, apod_name, "Arthropod", "prereqs[1] name"); CFCVersion *v30_104_5 = CFCVersion_new("v30.104.5"); CFCVersion *apod_version = CFCPrereq_get_version(apod); INT_EQ(test, CFCVersion_compare_to(apod_version, v30_104_5), 0, "prereqs[1] version"); OK(test, prereqs[2] == NULL, "prereqs[2]"); CFCBase_decref((CFCBase*)v30_104_5); CFCBase_decref((CFCBase*)v0); CFCBase_decref((CFCBase*)parcel); } { CFCFileSpec *foo_file_spec = CFCFileSpec_new(".", "Foo", true); CFCParcel *foo = CFCParcel_new("Foo", NULL, NULL, foo_file_spec); CFCParcel_register(foo); CFCVersion *cfish_version = CFCVersion_new("v0.8.7"); CFCFileSpec *cfish_file_spec = CFCFileSpec_new(".", "Clownfish", true); CFCParcel *cfish = CFCParcel_new("Clownfish", NULL, cfish_version, cfish_file_spec); CFCParcel_register(cfish); const char *crust_json = " {\n" " \"name\": \"Crustacean\",\n" " \"version\": \"v0.1.0\",\n" " \"prerequisites\": {\n" " \"Clownfish\": \"v0.8.5\",\n" " }\n" " }\n"; CFCParcel *crust = CFCParcel_new_from_json(crust_json, NULL); CFCParcel_register(crust); CFCParcel_check_prereqs(crust); INT_EQ(test, CFCParcel_required(foo), false, "parcel not required"); INT_EQ(test, CFCParcel_required(cfish), true, "prereq required"); INT_EQ(test, CFCParcel_required(crust), true, "self required"); CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(crust); OK(test, prereq_parcels[0] != NULL, "prereq_parcels[0]"); const char *name = CFCParcel_get_name(prereq_parcels[0]); STR_EQ(test, name, "Clownfish", "prereq_parcels[0] name"); OK(test, prereq_parcels[1] == NULL, "prereq_parcels[0]"); OK(test, CFCParcel_has_prereq(crust, cfish), "has_prereq"); OK(test, CFCParcel_has_prereq(crust, crust), "has_prereq self"); OK(test, !CFCParcel_has_prereq(crust, foo), "has_prereq false"); CFCParcel_add_struct_sym(cfish, "Swim"); CFCParcel_add_struct_sym(crust, "Pinch"); CFCParcel_add_struct_sym(foo, "Bar"); CFCParcel *found; found = CFCParcel_lookup_struct_sym(crust, "Swim"); OK(test, found == cfish, "lookup_struct_sym prereq"); found = CFCParcel_lookup_struct_sym(crust, "Pinch"); OK(test, found == crust, "lookup_struct_sym self"); found = CFCParcel_lookup_struct_sym(crust, "Bar"); OK(test, found == NULL, "lookup_struct_sym other"); FREEMEM(prereq_parcels); CFCBase_decref((CFCBase*)crust); CFCBase_decref((CFCBase*)cfish_version); CFCBase_decref((CFCBase*)cfish_file_spec); CFCBase_decref((CFCBase*)cfish); CFCBase_decref((CFCBase*)foo_file_spec); CFCBase_decref((CFCBase*)foo); CFCParcel_reap_singletons(); } }
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); }