/* Some of the ParseTuple conversion routines provided by the Python-flavored * CFBind module accept a CFBindArg instead of just a pointer to the value * itself. This routine generates the declarations for those CFBindArg * variables, as well as handling some default values. */ static char* S_gen_declaration(CFCVariable *var, const char *val) { CFCType *type = CFCVariable_get_type(var); const char *var_name = CFCVariable_get_name(var); const char *type_str = CFCType_to_c(type); char *result = NULL; if (CFCType_is_object(type)) { const char *specifier = CFCType_get_specifier(type); if (strcmp(specifier, "cfish_String") == 0) { if (val && strcmp(val, "NULL") != 0) { const char pattern[] = " const char arg_%s_DEFAULT[] = %s;\n" " %s_ARG = CFISH_SSTR_WRAP_UTF8(\n" " arg_%s_DEFAULT, sizeof(arg_%s_DEFAULT) - 1);\n" ; result = CFCUtil_sprintf(pattern, var_name, val, var_name, var_name, var_name); } } else { if (val && strcmp(val, "NULL") != 0) { CFCUtil_die("Can't assign a default of '%s' to a %s", val, type_str); } if (strcmp(specifier, "cfish_Hash") != 0 && strcmp(specifier, "cfish_Vector") != 0 ) { const char *class_var = CFCType_get_class_var(type); char pattern[] = " CFBindArg wrap_arg_%s = {%s, &%s_ARG};\n" ; result = CFCUtil_sprintf(pattern, var_name, class_var, var_name); } } } else if (CFCType_is_primitive(type)) { if (val) { char pattern[] = " %s_ARG = %s;\n"; result = CFCUtil_sprintf(pattern, var_name, val); } } else { CFCUtil_die("Unexpected type, can't gen declaration: %s", type_str); } return result; }
static char* S_gen_target(CFCVariable *var, const char *value) { CFCType *type = CFCVariable_get_type(var); const char *specifier = CFCType_get_specifier(type); const char *micro_sym = CFCVariable_get_name(var); const char *maybe_maybe = ""; const char *dest_name; char *var_name = NULL; if (CFCType_is_primitive(type)) { dest_name = CFCType_get_specifier(type); if (value != NULL) { maybe_maybe = "maybe_"; } var_name = CFCUtil_sprintf("%s_ARG", CFCVariable_get_name(var)); } else if (CFCType_is_object(type)) { if (CFCType_nullable(type) || (value && strcmp(value, "NULL") == 0) ) { maybe_maybe = "maybe_"; } if (strcmp(specifier, "cfish_String") == 0) { dest_name = "string"; var_name = CFCUtil_sprintf("%s_ARG", CFCVariable_get_name(var)); } else if (strcmp(specifier, "cfish_Hash") == 0) { dest_name = "hash"; var_name = CFCUtil_sprintf("%s_ARG", CFCVariable_get_name(var)); } else if (strcmp(specifier, "cfish_Vector") == 0) { dest_name = "vec"; var_name = CFCUtil_sprintf("%s_ARG", CFCVariable_get_name(var)); } else { dest_name = "obj"; var_name = CFCUtil_sprintf("wrap_arg_%s", micro_sym); } } else { dest_name = "INVALID"; } char *content = CFCUtil_sprintf(", CFBind_%sconvert_%s, &%s", maybe_maybe, dest_name, var_name); FREEMEM(var_name); return content; }
static char* S_build_py_args(CFCParamList *param_list) { int num_vars = CFCParamList_num_vars(param_list); CFCVariable **vars = CFCParamList_get_variables(param_list); char pattern[] = " PyObject *cfcb_ARGS = S_pack_tuple(%d"; char *py_args = CFCUtil_sprintf(pattern, num_vars - 1); for (int i = 1; vars[i] != NULL; i++) { const char *var_name = CFCVariable_get_name(vars[i]); CFCType *type = CFCVariable_get_type(vars[i]); char *conversion = CFCPyTypeMap_c_to_py(type, var_name); py_args = CFCUtil_cat(py_args, ",\n ", conversion, NULL); FREEMEM(conversion); } py_args = CFCUtil_cat(py_args, ");", NULL); return py_args; }
char* CFCPerlSub_arg_declarations(CFCPerlSub *self) { CFCParamList *param_list = self->param_list; CFCVariable **arg_vars = CFCParamList_get_variables(param_list); size_t num_vars = CFCParamList_num_vars(param_list); char *decls = CFCUtil_strdup(""); // Declare variables. for (size_t i = 1; i < num_vars; i++) { CFCVariable *arg_var = arg_vars[i]; CFCType *type = CFCVariable_get_type(arg_var); const char *type_str = CFCType_to_c(type); const char *var_name = CFCVariable_micro_sym(arg_var); decls = CFCUtil_cat(decls, " ", type_str, " arg_", var_name, ";\n", NULL); } return decls; }
static char* S_gen_arg_increfs(CFCParamList *param_list, int first_tick) { CFCVariable **vars = CFCParamList_get_variables(param_list); int num_vars = CFCParamList_num_vars(param_list); char *content = CFCUtil_strdup(""); for (int i = first_tick;i < num_vars; i++) { CFCType *type = CFCVariable_get_type(vars[i]); if (CFCType_decremented(type)) { const char *name = CFCVariable_get_name(vars[i]); const char *specifier = CFCType_get_specifier(type); char pattern[] = " %s_ARG = (%s*)CFISH_INCREF(%s_ARG);\n"; char *incref = CFCUtil_sprintf(pattern, name, specifier, name); content = CFCUtil_cat(content, incref, NULL); FREEMEM(incref); } } return content; }
int CFCCallable_can_be_bound(CFCCallable *self) { // Test whether parameters can be mapped automatically. CFCVariable **arg_vars = CFCParamList_get_variables(self->param_list); for (size_t i = 0; arg_vars[i] != NULL; i++) { CFCType *type = CFCVariable_get_type(arg_vars[i]); if (!CFCType_is_object(type) && !CFCType_is_primitive(type)) { return false; } } // Test whether return type can be mapped automatically. if (!CFCType_is_void(self->return_type) && !CFCType_is_object(self->return_type) && !CFCType_is_primitive(self->return_type) ) { return false; } return true; }
static char* S_xsub_def_labeled_params(CFCPerlMethod *self) { const char *c_name = self->sub.c_name; CFCParamList *param_list = self->sub.param_list; CFCVariable **arg_vars = CFCParamList_get_variables(param_list); CFCVariable *self_var = arg_vars[0]; CFCType *self_type = CFCVariable_get_type(self_var); const char *self_micro_sym = CFCVariable_micro_sym(self_var); const char *micro_sym = CFCMethod_micro_sym(self->method); char *self_assign = S_self_assign_statement(self, self_type, micro_sym); char *allot_params = CFCPerlSub_build_allot_params((CFCPerlSub*)self); char *body = S_xsub_body(self); char pattern[] = "XS(%s);\n" "XS(%s) {\n" " dXSARGS;\n" " CHY_UNUSED_VAR(cv);\n" " if (items < 1) { CFISH_THROW(CFISH_ERR, \"Usage: %%s(%s, ...)\", GvNAME(CvGV(cv))); }\n" " SP -= items;\n" "\n" " /* Extract vars from Perl stack. */\n" " %s\n" " %s\n" "\n" " /* Execute */\n" " %s\n" "}\n"; char *xsub_def = CFCUtil_sprintf(pattern, c_name, c_name, self_micro_sym, allot_params, self_assign, body); FREEMEM(self_assign); FREEMEM(allot_params); FREEMEM(body); return xsub_def; }
static char* S_perl_var_name(CFCVariable *var) { CFCType *type = CFCVariable_get_type(var); const char *specifier = CFCType_get_specifier(type); char *perl_name = NULL; if (CFCType_is_object(type)) { // Skip parcel prefix. if (islower(*specifier)) { for (specifier++; *specifier; specifier++) { if (*specifier == '_') { specifier++; break; } } } perl_name = S_camel_to_lower(specifier); } else if (CFCType_is_integer(type)) { if (strcmp(specifier, "bool") == 0) { perl_name = CFCUtil_strdup("bool"); } else { perl_name = CFCUtil_strdup("int"); } } else if (CFCType_is_floating(type)) { perl_name = CFCUtil_strdup("float"); } else { CFCUtil_die("Don't know how to create code sample for type '%s'", specifier); } return perl_name; }
static char* S_callback_refcount_mods(CFCMethod *method) { char *refcount_mods = CFCUtil_strdup(""); CFCType *return_type = CFCMethod_get_return_type(method); CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); // Host_callback_obj returns an incremented object. If this method does // not return an incremented object, we must cancel out that refcount. // (No function can return a decremented object.) if (CFCType_is_object(return_type) && !CFCType_incremented(return_type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_DECREF(retval);", NULL); } // The Host_callback_xxx functions have no effect on the refcounts of // arguments, so we need to adjust them after the fact. for (int i = 0; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; CFCType *type = CFCVariable_get_type(var); const char *name = CFCVariable_micro_sym(var); if (!CFCType_is_object(type)) { continue; } else if (CFCType_incremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_INCREF(", name, ");", NULL); } else if (CFCType_decremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_DECREF(", name, ");", NULL); } } return refcount_mods; }
static char* S_callback_refcount_mods(CFCMethod *method) { char *refcount_mods = CFCUtil_strdup(""); CFCType *return_type = CFCMethod_get_return_type(method); CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); // `XSBind_perl_to_cfish()` returns an incremented object. If this method // does not return an incremented object, we must cancel out that // refcount. (No function can return a decremented object.) if (CFCType_is_object(return_type) && !CFCType_incremented(return_type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_DECREF(retval);", NULL); } // Adjust refcounts of arguments per method signature, so that Perl code // does not have to. for (int i = 0; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; CFCType *type = CFCVariable_get_type(var); const char *name = CFCVariable_micro_sym(var); if (!CFCType_is_object(type)) { continue; } else if (CFCType_incremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_INCREF(", name, ");", NULL); } else if (CFCType_decremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_DECREF(", name, ");", NULL); } } return refcount_mods; }
// Prep refcount decrement calls to follow the Clownfish subroutine // invocation. static char* S_gen_decrefs(CFCParamList *param_list, int first_tick) { CFCVariable **vars = CFCParamList_get_variables(param_list); int num_vars = CFCParamList_num_vars(param_list); char *decrefs = CFCUtil_strdup(""); for (int i = first_tick; i < num_vars; i++) { CFCVariable *var = vars[i]; CFCType *type = CFCVariable_get_type(var); const char *micro_sym = CFCVariable_get_name(var); const char *specifier = CFCType_get_specifier(type); if (strcmp(specifier, "cfish_Obj") == 0 || strcmp(specifier, "cfish_String") == 0 || strcmp(specifier, "cfish_Vector") == 0 || strcmp(specifier, "cfish_Hash") == 0 ) { decrefs = CFCUtil_cat(decrefs, " CFISH_DECREF(", micro_sym, "_ARG);\n", NULL); } } return decrefs; }
CFCType* CFCMethod_self_type(CFCMethod *self) { CFCVariable **vars = CFCParamList_get_variables(self->function.param_list); return CFCVariable_get_type(vars[0]); }
char* CFCPerlConstructor_xsub_def(CFCPerlConstructor *self) { const char *c_name = self->sub.c_name; CFCParamList *param_list = self->sub.param_list; char *name_list = CFCPerlSub_arg_name_list((CFCPerlSub*)self); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char *func_sym = CFCFunction_full_func_sym(self->init_func); char *arg_decls = CFCPerlSub_arg_declarations((CFCPerlSub*)self); char *allot_params = CFCPerlSub_build_allot_params((CFCPerlSub*)self); CFCVariable *self_var = arg_vars[0]; CFCType *self_type = CFCVariable_get_type(self_var); const char *self_type_str = CFCType_to_c(self_type); // Compensate for swallowed refcounts. char *refcount_mods = CFCUtil_strdup(""); for (size_t i = 0; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; CFCType *type = CFCVariable_get_type(var); if (CFCType_is_object(type) && CFCType_decremented(type)) { const char *name = CFCVariable_micro_sym(var); refcount_mods = CFCUtil_cat(refcount_mods, "\n CFISH_INCREF(arg_", name, ");", NULL); } } const char pattern[] = "XS(%s);\n" "XS(%s) {\n" " dXSARGS;\n" " %s arg_self;\n" "%s" " bool args_ok;\n" " %s retval;\n" "\n" " CFISH_UNUSED_VAR(cv);\n" " if (items < 1) { CFISH_THROW(CFISH_ERR, \"Usage: %%s(class_name, ...)\", GvNAME(CvGV(cv))); }\n" " SP -= items;\n" "\n" " %s\n" // Create "self" last, so that earlier exceptions while fetching // params don't trigger a bad invocation of DESTROY. " arg_self = (%s)XSBind_new_blank_obj(ST(0));%s\n" "\n" " retval = %s(%s);\n" " if (retval) {\n" " ST(0) = (SV*)CFISH_Obj_To_Host((cfish_Obj*)retval);\n" " CFISH_Obj_Dec_RefCount((cfish_Obj*)retval);\n" " }\n" " else {\n" " ST(0) = newSV(0);\n" " }\n" " sv_2mortal(ST(0));\n" " XSRETURN(1);\n" "}\n\n"; char *xsub_def = CFCUtil_sprintf(pattern, c_name, c_name, self_type_str, arg_decls, self_type_str, allot_params, self_type_str, refcount_mods, func_sym, name_list); FREEMEM(refcount_mods); FREEMEM(arg_decls); FREEMEM(allot_params); FREEMEM(name_list); return xsub_def; }
static char* S_xsub_body(CFCPerlMethod *self) { CFCMethod *method = self->method; CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char *name_list = CFCParamList_name_list(param_list); char *body = CFCUtil_strdup(""); CFCParcel *parcel = CFCMethod_get_parcel(method); const char *class_name = CFCMethod_get_class_name(method); CFCClass *klass = CFCClass_fetch_singleton(parcel, class_name); if (!klass) { CFCUtil_die("Can't find a CFCClass for '%s'", class_name); } // Extract the method function pointer. char *full_typedef = CFCMethod_full_typedef(method, klass); char *full_meth = CFCMethod_full_method_sym(method, klass); char *method_ptr = CFCUtil_sprintf("%s method = CFISH_METHOD_PTR(%s, %s);\n ", full_typedef, CFCClass_full_vtable_var(klass), full_meth); body = CFCUtil_cat(body, method_ptr, NULL); FREEMEM(full_typedef); FREEMEM(full_meth); FREEMEM(method_ptr); // Compensate for functions which eat refcounts. for (int i = 0; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; CFCType *type = CFCVariable_get_type(var); if (CFCType_is_object(type) && CFCType_decremented(type)) { body = CFCUtil_cat(body, "CFISH_INCREF(", CFCVariable_micro_sym(var), ");\n ", NULL); } } if (CFCType_is_void(CFCMethod_get_return_type(method))) { // Invoke method in void context. body = CFCUtil_cat(body, "method(", name_list, ");\n XSRETURN(0);", NULL); } else { // Return a value for method invoked in a scalar context. CFCType *return_type = CFCMethod_get_return_type(method); const char *type_str = CFCType_to_c(return_type); char *assignment = CFCPerlTypeMap_to_perl(return_type, "retval"); if (!assignment) { CFCUtil_die("Can't find typemap for '%s'", type_str); } body = CFCUtil_cat(body, type_str, " retval = method(", name_list, ");\n ST(0) = ", assignment, ";", NULL); if (CFCType_is_object(return_type) && CFCType_incremented(return_type) ) { body = CFCUtil_cat(body, "\n CFISH_DECREF(retval);", NULL); } body = CFCUtil_cat(body, "\n sv_2mortal( ST(0) );\n XSRETURN(1);", NULL); FREEMEM(assignment); } return body; }
static char* S_xsub_def_positional_args(CFCPerlMethod *self) { CFCMethod *method = self->method; CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char **arg_inits = CFCParamList_get_initial_values(param_list); unsigned num_vars = (unsigned)CFCParamList_num_vars(param_list); char *body = S_xsub_body(self); // Determine how many args are truly required and build an error check. unsigned min_required = 0; for (unsigned i = 0; i < num_vars; i++) { if (arg_inits[i] == NULL) { min_required = i + 1; } } char *xs_name_list = num_vars > 0 ? CFCUtil_strdup(CFCVariable_micro_sym(arg_vars[0])) : CFCUtil_strdup(""); for (unsigned i = 1; i < num_vars; i++) { const char *var_name = CFCVariable_micro_sym(arg_vars[i]); if (i < min_required) { xs_name_list = CFCUtil_cat(xs_name_list, ", ", var_name, NULL); } else { xs_name_list = CFCUtil_cat(xs_name_list, ", [", var_name, "]", NULL); } } const char num_args_pattern[] = "if (items %s %u) { CFISH_THROW(CFISH_ERR, \"Usage: %%s(%s)\", GvNAME(CvGV(cv))); }"; char *num_args_check; if (min_required < num_vars) { num_args_check = CFCUtil_sprintf(num_args_pattern, "<", min_required, xs_name_list); } else { num_args_check = CFCUtil_sprintf(num_args_pattern, "!=", num_vars, xs_name_list); } // Var assignments. char *var_assignments = CFCUtil_strdup(""); for (unsigned i = 0; i < num_vars; i++) { CFCVariable *var = arg_vars[i]; const char *val = arg_inits[i]; const char *var_name = CFCVariable_micro_sym(var); CFCType *var_type = CFCVariable_get_type(var); const char *type_c = CFCType_to_c(var_type); if (i == 0) { // self const char *meth_micro_sym = CFCMethod_micro_sym(self->method); char *statement = S_self_assign_statement(self, var_type, meth_micro_sym); var_assignments = CFCUtil_cat(var_assignments, statement, NULL); FREEMEM(statement); } else { char perl_stack_var[30]; sprintf(perl_stack_var, "ST(%u)", i); char *conversion = CFCPerlTypeMap_from_perl(var_type, perl_stack_var); if (!conversion) { CFCUtil_die("Can't map type '%s'", type_c); } if (val) { char pattern[] = "\n %s %s = ( items >= %u && XSBind_sv_defined(ST(%u)) )" " ? %s : %s;"; char *statement = CFCUtil_sprintf(pattern, type_c, var_name, i, i, conversion, val); var_assignments = CFCUtil_cat(var_assignments, statement, NULL); FREEMEM(statement); } else { var_assignments = CFCUtil_cat(var_assignments, "\n ", type_c, " ", var_name, " = ", conversion, ";", NULL); } FREEMEM(conversion); } } char pattern[] = "XS(%s);\n" "XS(%s) {\n" " dXSARGS;\n" " CHY_UNUSED_VAR(cv);\n" " SP -= items;\n" " %s;\n" "\n" " /* Extract vars from Perl stack. */\n" " %s\n" "\n" " /* Execute */\n" " %s\n" "}\n"; char *xsub = CFCUtil_sprintf(pattern, self->sub.c_name, self->sub.c_name, num_args_check, var_assignments, body); FREEMEM(num_args_check); FREEMEM(var_assignments); FREEMEM(body); return xsub; }
static char* S_callback_start(CFCMethod *method) { CFCParamList *param_list = CFCMethod_get_param_list(method); static const char pattern[] = " dSP;\n" " EXTEND(SP, %d);\n" " ENTER;\n" " SAVETMPS;\n" " PUSHMARK(SP);\n" " mPUSHs((SV*)Cfish_Obj_To_Host((cfish_Obj*)self));\n"; int num_args = (int)CFCParamList_num_vars(param_list) - 1; int num_to_extend = num_args == 0 ? 1 : num_args == 1 ? 2 : 1 + (num_args * 2); char *params = CFCUtil_sprintf(pattern, num_to_extend); // Iterate over arguments, mapping them to Perl scalars. CFCVariable **arg_vars = CFCParamList_get_variables(param_list); for (int i = 1; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; const char *name = CFCVariable_micro_sym(var); CFCType *type = CFCVariable_get_type(var); const char *c_type = CFCType_to_c(type); // Add labels when there are two or more parameters. if (num_args > 1) { char num_buf[20]; sprintf(num_buf, "%d", (int)strlen(name)); params = CFCUtil_cat(params, " mPUSHp(\"", name, "\", ", num_buf, ");\n", NULL); } if (CFCType_is_string_type(type)) { // Convert Clownfish string type to UTF-8 Perl string scalars. params = CFCUtil_cat(params, " mPUSHs(XSBind_cb_to_sv(", "(cfish_CharBuf*)", name, "));\n", NULL); } else if (CFCType_is_object(type)) { // Wrap other Clownfish object types in Perl objects. params = CFCUtil_cat(params, " mPUSHs(XSBind_cfish_to_perl(", "(cfish_Obj*)", name, "));\n", NULL); } else if (CFCType_is_integer(type)) { // Convert primitive integer types to IV Perl scalars. int width = (int)CFCType_get_width(type); if (width != 0 && width <= 4) { params = CFCUtil_cat(params, " mPUSHi(", name, ");\n", NULL); } else { // If the Perl IV integer type is not wide enough, use // doubles. This may be lossy if the value is above 2**52, // but practically speaking, it's important to handle numbers // between 2**32 and 2**52 cleanly. params = CFCUtil_cat(params, " if (sizeof(IV) >= sizeof(", c_type, ")) { mPUSHi(", name, "); }\n", " else { mPUSHn((double)", name, "); } // lossy \n", NULL); } } else if (CFCType_is_floating(type)) { // Convert primitive floating point types to NV Perl scalars. params = CFCUtil_cat(params, " mPUSHn(", name, ");\n", NULL); } else { // Can't map variable type. Signal to caller. FREEMEM(params); return NULL; } } // Restore the Perl stack pointer. params = CFCUtil_cat(params, " PUTBACK;\n", NULL); return params; }
CFCMethod* CFCMethod_init(CFCMethod *self, CFCParcel *parcel, const char *exposure, const char *class_name, const char *class_cnick, const char *macro_sym, CFCType *return_type, CFCParamList *param_list, CFCDocuComment *docucomment, int is_final, int is_abstract) { // Validate macro_sym, derive micro_sym. if (!S_validate_macro_sym(macro_sym)) { CFCBase_decref((CFCBase*)self); CFCUtil_die("Invalid macro_sym: '%s'", macro_sym ? macro_sym : "[NULL]"); } char *micro_sym = CFCUtil_strdup(macro_sym); for (size_t i = 0; micro_sym[i] != '\0'; i++) { micro_sym[i] = tolower(micro_sym[i]); } // Super-init and clean up derived micro_sym. CFCFunction_init((CFCFunction*)self, parcel, exposure, class_name, class_cnick, micro_sym, return_type, param_list, docucomment, false); FREEMEM(micro_sym); // Verify that the first element in the arg list is a self. CFCVariable **args = CFCParamList_get_variables(param_list); if (!args[0]) { CFCUtil_die("Missing 'self' argument"); } CFCType *type = CFCVariable_get_type(args[0]); const char *specifier = CFCType_get_specifier(type); const char *prefix = CFCMethod_get_prefix(self); const char *last_colon = strrchr(class_name, ':'); const char *struct_sym = last_colon ? last_colon + 1 : class_name; if (strcmp(specifier, struct_sym) != 0) { char *wanted = CFCUtil_sprintf("%s%s", prefix, struct_sym); int mismatch = strcmp(wanted, specifier); FREEMEM(wanted); if (mismatch) { CFCUtil_die("First arg type doesn't match class: '%s' '%s'", class_name, specifier); } } self->macro_sym = CFCUtil_strdup(macro_sym); self->full_override_sym = NULL; self->host_alias = NULL; self->is_final = is_final; self->is_abstract = is_abstract; self->is_excluded = false; // Derive name of implementing function. self->short_imp_func = CFCUtil_sprintf("%s_%s_IMP", CFCMethod_get_class_cnick(self), self->macro_sym); self->imp_func = CFCUtil_sprintf("%s%s", CFCMethod_get_PREFIX(self), self->short_imp_func); // Assume that this method is novel until we discover when applying // inheritance that it overrides another. self->is_novel = true; return self; }
static void S_run_tests(CFCTest *test) { #define STUFF_THING "Stuff" CHY_DIR_SEP "Thing" CFCParser *parser = CFCParser_new(); CFCFileSpec *file_spec = CFCFileSpec_new(".", STUFF_THING, 0); { const char *string = "parcel Stuff;\n" "class Stuff::Thing {\n" " Foo *foo;\n" " Bar *bar;\n" "}\n" "class Foo {}\n" "class Bar {}\n" "__C__\n" "int foo;\n" "__END_C__\n"; CFCFile *file = CFCParser_parse_file(parser, string, file_spec); STR_EQ(test, CFCFile_get_source_dir(file), ".", "get_source_dir"); STR_EQ(test, CFCFile_get_path_part(file), STUFF_THING, "get_path_part"); OK(test, !CFCFile_included(file), "included"); STR_EQ(test, CFCFile_guard_name(file), "H_STUFF_THING", "guard_name"); STR_EQ(test, CFCFile_guard_start(file), "#ifndef H_STUFF_THING\n#define H_STUFF_THING 1\n", "guard_start"); STR_EQ(test, CFCFile_guard_close(file), "#endif /* H_STUFF_THING */\n", "guard_close"); OK(test, !CFCFile_get_modified(file), "modified false at start"); CFCFile_set_modified(file, 1); OK(test, CFCFile_get_modified(file), "set_modified, get_modified"); #define PATH_TO_STUFF_THING \ "path" CHY_DIR_SEP \ "to" CHY_DIR_SEP \ "Stuff" CHY_DIR_SEP \ "Thing" char *cfh_path = CFCFile_cfh_path(file, "path/to"); STR_EQ(test, cfh_path, PATH_TO_STUFF_THING ".cfh", "cfh_path"); FREEMEM(cfh_path); char *c_path = CFCFile_c_path(file, "path/to"); STR_EQ(test, c_path, PATH_TO_STUFF_THING ".c", "c_path"); FREEMEM(c_path); char *h_path = CFCFile_h_path(file, "path/to"); STR_EQ(test, h_path, PATH_TO_STUFF_THING ".h", "h_path"); FREEMEM(h_path); CFCClass **classes = CFCFile_classes(file); OK(test, classes[0] != NULL && classes[1] != NULL && classes[2] != NULL && classes[3] == NULL, "classes() filters blocks"); CFCVariable **member_vars = CFCClass_fresh_member_vars(classes[0]); CFCType *foo_type = CFCVariable_get_type(member_vars[0]); CFCType_resolve(foo_type); STR_EQ(test, CFCType_get_specifier(foo_type), "stuff_Foo", "file production picked up parcel def"); CFCType *bar_type = CFCVariable_get_type(member_vars[1]); CFCType_resolve(bar_type); STR_EQ(test, CFCType_get_specifier(bar_type), "stuff_Bar", "parcel def is sticky"); CFCParcel *parcel = CFCFile_get_parcel(file); STR_EQ(test, CFCParcel_get_name(parcel), "Stuff", "get_parcel"); CFCBase **blocks = CFCFile_blocks(file); STR_EQ(test, CFCBase_get_cfc_class(blocks[0]), "Clownfish::CFC::Model::Class", "blocks[0]"); STR_EQ(test, CFCBase_get_cfc_class(blocks[1]), "Clownfish::CFC::Model::Class", "blocks[1]"); STR_EQ(test, CFCBase_get_cfc_class(blocks[2]), "Clownfish::CFC::Model::Class", "blocks[2]"); STR_EQ(test, CFCBase_get_cfc_class(blocks[3]), "Clownfish::CFC::Model::CBlock", "blocks[3]"); OK(test, blocks[4] == NULL, "blocks[4]"); CFCBase_decref((CFCBase*)file); CFCClass_clear_registry(); } CFCBase_decref((CFCBase*)file_spec); CFCBase_decref((CFCBase*)parser); CFCParcel_reap_singletons(); }
static char* S_callback_params(CFCMethod *method) { const char *micro_sym = CFCSymbol_micro_sym((CFCSymbol*)method); CFCParamList *param_list = CFCMethod_get_param_list(method); unsigned num_params = CFCParamList_num_vars(param_list) - 1; size_t needed = strlen(micro_sym) + 30; char *params = (char*)MALLOCATE(needed); // TODO: use something other than micro_sym here. sprintf(params, "self, \"%s\", %u", micro_sym, num_params); // Iterate over arguments, mapping them to various arg wrappers which // conform to Host's callback interface. CFCVariable **arg_vars = CFCParamList_get_variables(param_list); for (int i = 1; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; const char *name = CFCVariable_micro_sym(var); size_t name_len = strlen(name); CFCType *type = CFCVariable_get_type(var); const char *c_type = CFCType_to_c(type); size_t size = strlen(params) + strlen(c_type) + name_len * 2 + 30; char *new_buf = (char*)MALLOCATE(size); if (CFCType_is_string_type(type)) { sprintf(new_buf, "%s, CFISH_ARG_STR(\"%s\", %s)", params, name, name); } else if (CFCType_is_object(type)) { sprintf(new_buf, "%s, CFISH_ARG_OBJ(\"%s\", %s)", params, name, name); } else if (CFCType_is_integer(type)) { int width = CFCType_get_width(type); if (width) { if (width <= 4) { sprintf(new_buf, "%s, CFISH_ARG_I32(\"%s\", %s)", params, name, name); } else { sprintf(new_buf, "%s, CFISH_ARG_I64(\"%s\", %s)", params, name, name); } } else { sprintf(new_buf, "%s, CFISH_ARG_I(%s, \"%s\", %s)", params, c_type, name, name); } } else if (CFCType_is_floating(type)) { sprintf(new_buf, "%s, CFISH_ARG_F64(\"%s\", %s)", params, name, name); } else { // Can't map variable type. Signal to caller. FREEMEM(params); FREEMEM(new_buf); return NULL; } FREEMEM(params); params = new_buf; } return params; }