static void S_write_parcel_c(CFCBindCore *self, CFCParcel *parcel) { CFCHierarchy *hierarchy = self->hierarchy; const char *prefix = CFCParcel_get_prefix(parcel); // Aggregate C code for the parcel. char *privacy_syms = CFCUtil_strdup(""); char *includes = CFCUtil_strdup(""); char *c_data = CFCUtil_strdup(""); CFCBindSpecs *specs = CFCBindSpecs_new(); CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); for (int i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; const char *class_prefix = CFCClass_get_prefix(klass); if (strcmp(class_prefix, prefix) != 0) { continue; } const char *include_h = CFCClass_include_h(klass); includes = CFCUtil_cat(includes, "#include \"", include_h, "\"\n", NULL); CFCBindClass *class_binding = CFCBindClass_new(klass); char *class_c_data = CFCBindClass_to_c_data(class_binding); c_data = CFCUtil_cat(c_data, class_c_data, "\n", NULL); FREEMEM(class_c_data); CFCBindSpecs_add_class(specs, klass); const char *privacy_sym = CFCClass_privacy_symbol(klass); privacy_syms = CFCUtil_cat(privacy_syms, "#define ", privacy_sym, "\n", NULL); CFCBase_decref((CFCBase*)class_binding); } char *spec_defs = CFCBindSpecs_defs(specs); char *spec_init_func = CFCBindSpecs_init_func_def(specs); FREEMEM(ordered); // Bootstrapping code for prerequisite parcels. // // bootstrap_inheritance() first calls bootstrap_inheritance() for all // parcels from which classes are inherited. Then the Classes of the parcel // are initialized. It aborts on recursive invocation. // // bootstrap_parcel() first calls bootstrap_inheritance() of its own // parcel. Then it calls bootstrap_parcel() for all prerequisite parcels. // Finally, it calls init_parcel(). Recursive invocation is allowed. char *inh_bootstrap = CFCUtil_strdup(""); char *prereq_bootstrap = CFCUtil_strdup(""); CFCParcel **inh_parcels = CFCParcel_inherited_parcels(parcel); for (size_t i = 0; inh_parcels[i]; ++i) { const char *inh_prefix = CFCParcel_get_prefix(inh_parcels[i]); inh_bootstrap = CFCUtil_cat(inh_bootstrap, " ", inh_prefix, "bootstrap_inheritance();\n", NULL); } FREEMEM(inh_parcels); CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(parcel); for (size_t i = 0; prereq_parcels[i]; ++i) { const char *prereq_prefix = CFCParcel_get_prefix(prereq_parcels[i]); prereq_bootstrap = CFCUtil_cat(prereq_bootstrap, " ", prereq_prefix, "bootstrap_parcel();\n", NULL); } FREEMEM(prereq_parcels); char pattern[] = "%s\n" "\n" "#include <stdio.h>\n" "#include <stdlib.h>\n" "\n" "%s" "\n" "#include \"Clownfish/Class.h\"\n" // Needed for bootstrap. "#include \"Clownfish/Err.h\"\n" // Needed for abstract methods. "%s\n" "\n" "%s\n" "\n" "/* ClassSpec and MethSpec structs for initialization.\n" " */\n" "\n" "%s" // spec_defs "\n" "/* Code to initialize ClassSpec and MethSpec structs.\n" " */\n" "\n" "%s" // spec_init_func "\n" "static int bootstrap_state = 0;\n" "\n" "void\n" "%sbootstrap_inheritance() {\n" " if (bootstrap_state == 1) {\n" " fprintf(stderr, \"Cycle in class inheritance between\"\n" " \" parcels detected.\\n\");\n" " abort();\n" " }\n" " if (bootstrap_state >= 2) { return; }\n" " bootstrap_state = 1;\n" "%s" // Bootstrap inherited parcels. " S_bootstrap_specs();\n" " bootstrap_state = 2;\n" "}\n" "\n" "void\n" "%sbootstrap_parcel() {\n" " if (bootstrap_state >= 3) { return; }\n" " %sbootstrap_inheritance();\n" " bootstrap_state = 3;\n" "%s" // Finish bootstrapping of all prerequisite parcels. " %sinit_parcel();\n" "}\n" "\n" "%s\n"; char *file_content = CFCUtil_sprintf(pattern, self->c_header, privacy_syms, includes, c_data, spec_defs, spec_init_func, prefix, inh_bootstrap, prefix, prefix, prereq_bootstrap, prefix, self->c_footer); // Unlink then open file. const char *src_dest = CFCHierarchy_get_source_dest(hierarchy); char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sparcel.c", src_dest, prefix); remove(filepath); CFCUtil_write_file(filepath, file_content, strlen(file_content)); FREEMEM(filepath); CFCBase_decref((CFCBase*)specs); FREEMEM(privacy_syms); FREEMEM(includes); FREEMEM(c_data); FREEMEM(spec_defs); FREEMEM(spec_init_func); FREEMEM(inh_bootstrap); FREEMEM(prereq_bootstrap); FREEMEM(file_content); }
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_write_callbacks_c(CFCPerl *self) { CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); static const char pattern[] = "%s" "\n" "#include \"XSBind.h\"\n" "#include \"callbacks.h\"\n" "\n" "static void\n" "S_finish_callback_void(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(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(const char *meth_name) {\n" " SV *return_sv = SI_do_callback_sv(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(const char *meth_name) {\n" " SV *return_sv = SI_do_callback_sv(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(void *vself, const char *meth_name,\n" " int nullable) {\n" " SV *return_sv = SI_do_callback_sv(meth_name);\n" " cfish_Obj *retval = XSBind_perl_to_cfish(return_sv);\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"; char *content = CFCUtil_sprintf(pattern, self->header); for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_inert(klass)) { continue; } 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); content = CFCUtil_cat(content, cb_def, "\n", NULL); FREEMEM(cb_def); } } FREEMEM(fresh_methods); } content = CFCUtil_cat(content, self->footer, NULL); // Write if changed. const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy); char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "callbacks.c", src_dest); CFCUtil_write_if_changed(filepath, content, strlen(content)); FREEMEM(filepath); FREEMEM(content); 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); }
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); }