int CFCType_equals(CFCType *self, CFCType *other) { if ((CFCType_const(self) ^ CFCType_const(other)) || (CFCType_nullable(self) ^ CFCType_nullable(other)) || (CFCType_is_void(self) ^ CFCType_is_void(other)) || (CFCType_is_object(self) ^ CFCType_is_object(other)) || (CFCType_is_primitive(self) ^ CFCType_is_primitive(other)) || (CFCType_is_integer(self) ^ CFCType_is_integer(other)) || (CFCType_is_floating(self) ^ CFCType_is_floating(other)) || (CFCType_is_va_list(self) ^ CFCType_is_va_list(other)) || (CFCType_is_arbitrary(self) ^ CFCType_is_arbitrary(other)) || (CFCType_is_composite(self) ^ CFCType_is_composite(other)) || (CFCType_incremented(self) ^ CFCType_incremented(other)) || (CFCType_decremented(self) ^ CFCType_decremented(other)) || !!self->child ^ !!other->child || !!self->array ^ !!other->array ) { return false; } if (self->indirection != other->indirection) { return false; } if (strcmp(self->specifier, other->specifier) != 0) { return false; } if (self->child) { if (!CFCType_equals(self->child, other->child)) { return false; } } if (self->array) { if (strcmp(self->array, other->array) != 0) { return false; } } return true; }
int CFCType_similar(CFCType *self, CFCType *other) { if (!CFCType_is_object(self)) { CFCUtil_die("Attempt to call 'similar' on a non-object type"); } if ((CFCType_const(self) ^ CFCType_const(other)) || (CFCType_nullable(self) ^ CFCType_nullable(other)) || (CFCType_incremented(self) ^ CFCType_incremented(other)) || (CFCType_decremented(self) ^ CFCType_decremented(other)) || (CFCType_is_object(self) ^ CFCType_is_object(other)) ) { return false; } return true; }
char* CFCBindMeth_callback_def(CFCMethod *method) { CFCType *return_type = CFCMethod_get_return_type(method); char *params = S_callback_params(method); char *callback_def = NULL; char *refcount_mods = S_callback_refcount_mods(method); if (!params) { // Can't map vars, because there's at least one type in the argument // list we don't yet support. Return a callback wrapper that throws // an error error. callback_def = S_invalid_callback_def(method); } else if (CFCType_is_void(return_type)) { callback_def = S_void_callback_def(method, params, refcount_mods); } else if (CFCType_is_object(return_type)) { callback_def = S_obj_callback_def(method, params, refcount_mods); } else { callback_def = S_primitive_callback_def(method, params, refcount_mods); } FREEMEM(params); FREEMEM(refcount_mods); return callback_def; }
static char* S_callback_refcount_mods(CFCParamList *param_list) { char *refcount_mods = CFCUtil_strdup(""); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); // 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_get_name(var); if (!CFCType_is_object(type)) { continue; } else if (CFCType_incremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, " CFISH_INCREF(", name, ");\n", NULL); } else if (CFCType_decremented(type)) { refcount_mods = CFCUtil_cat(refcount_mods, " CFISH_DECREF(", name, ");\n", NULL); } } return refcount_mods; }
char* CFCPerlMethod_callback_def(CFCMethod *method) { CFCType *return_type = CFCMethod_get_return_type(method); char *start = S_callback_start(method); char *callback_def = NULL; char *refcount_mods = S_callback_refcount_mods(method); if (!start) { // Can't map vars, because there's at least one type in the argument // list we don't yet support. Return a callback wrapper that throws // an error error. callback_def = S_invalid_callback_def(method); } else if (CFCType_is_void(return_type)) { callback_def = S_void_callback_def(method, start, refcount_mods); } else if (CFCType_is_object(return_type)) { callback_def = S_obj_callback_def(method, start, refcount_mods); } else if (CFCType_is_integer(return_type) || CFCType_is_floating(return_type) ) { callback_def = S_primitive_callback_def(method, start, refcount_mods); } else { // Can't map return type. callback_def = S_invalid_callback_def(method); } FREEMEM(start); FREEMEM(refcount_mods); return callback_def; }
char* CFCPerlConstructor_xsub_def(CFCPerlConstructor *self) { const char *c_name = self->sub.c_name; CFCParamList *param_list = self->sub.param_list; const char *name_list = CFCParamList_name_list(param_list); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char *func_sym = CFCFunction_full_func_sym(self->init_func); 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(", name, ");", NULL); } } const char pattern[] = "XS(%s);\n" "XS(%s) {\n" " dXSARGS;\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. " %s self = (%s)XSBind_new_blank_obj(ST(0));%s\n" "\n" " %s 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, allot_params, self_type_str, self_type_str, refcount_mods, self_type_str, func_sym, name_list); FREEMEM(refcount_mods); FREEMEM(allot_params); return xsub_def; }
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); }
int CFCMethod_compatible(CFCMethod *self, CFCMethod *other) { if (!other) { return false; } if (strcmp(self->macro_sym, other->macro_sym)) { return false; } int my_public = CFCMethod_public(self); int other_public = CFCMethod_public(other); if (!!my_public != !!other_public) { return false; } // Check arguments and initial values. CFCParamList *my_param_list = self->function.param_list; CFCParamList *other_param_list = other->function.param_list; CFCVariable **my_args = CFCParamList_get_variables(my_param_list); CFCVariable **other_args = CFCParamList_get_variables(other_param_list); const char **my_vals = CFCParamList_get_initial_values(my_param_list); const char **other_vals = CFCParamList_get_initial_values(other_param_list); for (size_t i = 1; ; i++) { // start at 1, skipping self if (!!my_args[i] != !!other_args[i]) { return false; } if (!!my_vals[i] != !!other_vals[i]) { return false; } if (my_vals[i]) { if (strcmp(my_vals[i], other_vals[i])) { return false; } } if (my_args[i]) { if (!CFCVariable_equals(my_args[i], other_args[i])) { return false; } } else { break; } } // Check return types. CFCType *type = CFCMethod_get_return_type(self); CFCType *other_type = CFCMethod_get_return_type(other); if (CFCType_is_object(type)) { // Weak validation to allow covariant object return types. if (!CFCType_is_object(other_type)) { return false; } if (!CFCType_similar(type, other_type)) { return false; } } else { if (!CFCType_equals(type, other_type)) { return false; } } return true; }
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); }
void CFCType_resolve(CFCType *self, CFCClass **classes) { if (CFCType_is_composite(self)) { CFCType_resolve(self->child, classes); return; } if (!CFCType_is_object(self)) { return; } CFCClass *klass = NULL; char *specifier = self->specifier; if (isupper(self->specifier[0])) { // Try to find class from class list. for (size_t i = 0; classes[i]; ++i) { CFCClass *maybe_class = classes[i]; const char *struct_sym = CFCClass_get_struct_sym(maybe_class); if (strcmp(specifier, struct_sym) == 0) { if (klass) { CFCUtil_die("Type '%s' is ambigious", specifier); } klass = maybe_class; } } if (!klass) { CFCUtil_die("No class found for type '%s'", specifier); } // Create actual specifier with prefix. const char *prefix = CFCClass_get_prefix(klass); self->specifier = CFCUtil_sprintf("%s%s", prefix, specifier); FREEMEM(specifier); } else { // Try to find class from class list. for (size_t i = 0; classes[i]; ++i) { CFCClass *maybe_class = classes[i]; const char *full_struct_sym = CFCClass_full_struct_sym(maybe_class); if (strcmp(specifier, full_struct_sym) == 0) { klass = maybe_class; break; } } } // Add parcel dependency. if (klass) { CFCParcel *class_parcel = CFCClass_get_parcel(klass); CFCParcel_add_dependent_parcel(self->parcel, class_parcel); } }
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; }
// Create an assignment statement for extracting $self from the Perl stack. static char* S_self_assign_statement(CFCPerlMethod *self, CFCType *type, const char *method_name) { (void)self; // unused const char *type_c = CFCType_to_c(type); if (!CFCType_is_object(type)) { CFCUtil_die("Not an object type: %s", type_c); } const char *vtable_var = CFCType_get_vtable_var(type); char pattern[] = "%s self = (%s)XSBind_sv_to_cfish_obj(ST(0), %s, NULL);"; char *statement = CFCUtil_sprintf(pattern, type_c, type_c, vtable_var); return statement; }
/* 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_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; }
char* CFCPerlSub_build_allot_params(CFCPerlSub *self) { CFCParamList *param_list = self->param_list; CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char **arg_inits = CFCParamList_get_initial_values(param_list); size_t num_vars = CFCParamList_num_vars(param_list); char *allot_params = CFCUtil_strdup(""); // Declare variables and assign default values. for (size_t i = 1; i < num_vars; i++) { CFCVariable *arg_var = arg_vars[i]; const char *val = arg_inits[i]; const char *var_name = CFCVariable_micro_sym(arg_var); if (val == NULL) { CFCType *arg_type = CFCVariable_get_type(arg_var); val = CFCType_is_object(arg_type) ? "NULL" : "0"; } allot_params = CFCUtil_cat(allot_params, "arg_", var_name, " = ", val, ";\n ", NULL); } // Iterate over args in param list. allot_params = CFCUtil_cat(allot_params, "args_ok = XSBind_allot_params(\n" " &(ST(0)), 1, items,\n", NULL); for (size_t i = 1; i < num_vars; i++) { CFCVariable *var = arg_vars[i]; const char *val = arg_inits[i]; int required = val ? 0 : 1; const char *name = CFCVariable_micro_sym(var); CFCType *type = CFCVariable_get_type(var); char *arg = S_allot_params_arg(type, name, required); allot_params = CFCUtil_cat(allot_params, " ", arg, ",\n", NULL); FREEMEM(arg); } allot_params = CFCUtil_cat(allot_params, " NULL);\n", " if (!args_ok) {\n" " CFISH_RETHROW(CFISH_INCREF(cfish_Err_get_error()));\n" " }", NULL); return allot_params; }
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_perl_var_name(CFCType *type, int is_ctor_retval) { const char *specifier = CFCType_get_specifier(type); char *perl_name = NULL; if (CFCType_is_object(type)) { if (!is_ctor_retval && strcmp(specifier, "cfish_Vector") == 0) { perl_name = CFCUtil_strdup("arrayref"); } else if (!is_ctor_retval && strcmp(specifier, "cfish_Hash") == 0) { perl_name = CFCUtil_strdup("hashref"); } else { // Skip parcel prefix. if (CFCUtil_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_allot_params_arg(CFCType *type, const char *label, int required) { const char *type_c_string = CFCType_to_c(type); unsigned label_len = (unsigned)strlen(label); const char *req_string = required ? "true" : "false"; if (CFCType_is_object(type)) { const char *struct_sym = CFCType_get_specifier(type); const char *class_var = CFCType_get_class_var(type); // Share buffers rather than copy between Perl scalars and Clownfish // string types. int use_sv_buffer = false; if (strcmp(struct_sym, "cfish_String") == 0 || strcmp(struct_sym, "cfish_Obj") == 0 ) { use_sv_buffer = true; } const char *allocation = use_sv_buffer ? "alloca(cfish_SStr_size())" : "NULL"; const char pattern[] = "ALLOT_OBJ(&arg_%s, \"%s\", %u, %s, %s, %s)"; char *arg = CFCUtil_sprintf(pattern, label, label, label_len, req_string, class_var, allocation); return arg; } else if (CFCType_is_primitive(type)) { for (int i = 0; prim_type_to_allot_macro[i].prim_type != NULL; i++) { const char *prim_type = prim_type_to_allot_macro[i].prim_type; if (strcmp(prim_type, type_c_string) == 0) { const char *allot = prim_type_to_allot_macro[i].allot_macro; char pattern[] = "%s(&arg_%s, \"%s\", %u, %s)"; char *arg = CFCUtil_sprintf(pattern, allot, label, label, label_len, req_string); return arg; } } } CFCUtil_die("Missing typemap for %s", type_c_string); return NULL; // unreachable }
static char* S_build_pymeth_invocation(CFCMethod *method) { CFCType *return_type = CFCMethod_get_return_type(method); const char *micro_sym = CFCSymbol_get_name((CFCSymbol*)method); char *invocation = NULL; const char *ret_type_str = CFCType_to_c(return_type); if (CFCType_is_void(return_type)) { const char pattern[] = " CALL_PYMETH_VOID((PyObject*)self, \"%s\", cfcb_ARGS);"; invocation = CFCUtil_sprintf(pattern, micro_sym); } else if (CFCType_is_object(return_type)) { const char *nullable = CFCType_nullable(return_type) ? "true" : "false"; const char *ret_class = CFCType_get_class_var(return_type); const char pattern[] = " %s cfcb_RESULT = (%s)CALL_PYMETH_OBJ((PyObject*)self, \"%s\", cfcb_ARGS, %s, %s);"; invocation = CFCUtil_sprintf(pattern, ret_type_str, ret_type_str, micro_sym, ret_class, nullable); } else if (CFCType_is_primitive(return_type)) { char type_upcase[64]; if (strlen(ret_type_str) > 63) { CFCUtil_die("Unexpectedly long type name: %s", ret_type_str); } for (int i = 0, max = strlen(ret_type_str) + 1; i < max; i++) { type_upcase[i] = toupper(ret_type_str[i]); } const char pattern[] = " %s cfcb_RESULT = CALL_PYMETH_%s((PyObject*)self, \"%s\", cfcb_ARGS);"; invocation = CFCUtil_sprintf(pattern, ret_type_str, type_upcase, micro_sym); } else { CFCUtil_die("Unexpected return type: %s", CFCType_to_c(return_type)); } return invocation; }
const char* CFCType_to_c(CFCType *self) { char *c_string = self->c_string; if (c_string) { return c_string; } if (CFCType_is_composite(self)) { // NOTE: Array postfixes are NOT included. const char *child_c_string = CFCType_to_c(self->child); size_t child_c_len = strlen(child_c_string); size_t amount = child_c_len + self->indirection; c_string = (char*)MALLOCATE(amount + 1); strcpy(c_string, child_c_string); for (int i = 0; i < self->indirection; i++) { strncat(c_string, "*", 1); } } else if (CFCType_is_object(self)) { if (CFCType_const(self)) { c_string = CFCUtil_sprintf("const %s*", self->specifier); } else { c_string = CFCUtil_sprintf("%s*", self->specifier); } } else { if (CFCType_const(self)) { c_string = CFCUtil_sprintf("const %s", self->specifier); } else { c_string = CFCUtil_strdup(self->specifier); } } self->c_string = c_string; return c_string; }
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 void S_run_tests(CFCTest *test) { CFCParser *parser = CFCParser_new(); OK(test, parser != NULL, "new"); { CFCParcel *fish = CFCTest_parse_parcel(test, parser, "parcel Fish;"); CFCParcel *registered = CFCParcel_new("Crustacean", "Crust", NULL, false); CFCParcel_register(registered); CFCParcel *parcel = CFCTest_parse_parcel(test, parser, "parcel Crustacean;"); OK(test, parcel == registered, "Fetch registered parcel"); OK(test, CFCParser_get_parcel(parser) == parcel, "parcel_definition sets internal var"); CFCBase_decref((CFCBase*)fish); CFCBase_decref((CFCBase*)registered); CFCBase_decref((CFCBase*)parcel); } { static const char *const specifiers[8] = { "foo", "_foo", "foo_yoo", "FOO", "Foo", "fOO", "f00", "foo_foo_foo" }; for (int i = 0; i < 8; ++i) { const char *specifier = specifiers[i]; char *src = CFCUtil_sprintf("int32_t %s;", specifier); CFCVariable *var = CFCTest_parse_variable(test, parser, src); STR_EQ(test, CFCVariable_micro_sym(var), specifier, "identifier/declarator: %s", specifier); FREEMEM(src); CFCBase_decref((CFCBase*)var); } } { static const char *const specifiers[6] = { "void", "float", "uint32_t", "int64_t", "uint8_t", "bool" }; for (int i = 0; i < 6; ++i) { const char *specifier = specifiers[i]; char *src = CFCUtil_sprintf("int32_t %s;", specifier); CFCBase *result = CFCParser_parse(parser, src); OK(test, result == NULL, "reserved word not parsed as identifier: %s", specifier); FREEMEM(src); CFCBase_decref(result); } } { static const char *const type_strings[7] = { "bool", "const char *", "Obj*", "i32_t", "char[]", "long[1]", "i64_t[30]" }; for (int i = 0; i < 7; ++i) { const char *type_string = type_strings[i]; CFCType *type = CFCTest_parse_type(test, parser, type_string); CFCBase_decref((CFCBase*)type); } } { static const char *const class_names[7] = { "ByteBuf", "Obj", "ANDMatcher", "Foo", "FooJr", "FooIII", "Foo4th" }; CFCClass *class_list[8]; for (int i = 0; i < 7; ++i) { char *class_code = CFCUtil_sprintf("class %s {}", class_names[i]); CFCClass *klass = CFCTest_parse_class(test, parser, class_code); class_list[i] = klass; FREEMEM(class_code); } class_list[7] = NULL; for (int i = 0; i < 7; ++i) { const char *class_name = class_names[i]; char *src = CFCUtil_sprintf("%s*", class_name); char *expected = CFCUtil_sprintf("crust_%s", class_name); CFCType *type = CFCTest_parse_type(test, parser, src); CFCType_resolve(type, class_list); STR_EQ(test, CFCType_get_specifier(type), expected, "object_type_specifier: %s", class_name); FREEMEM(src); FREEMEM(expected); CFCBase_decref((CFCBase*)type); } for (int i = 0; i < 7; ++i) { CFCBase_decref((CFCBase*)class_list[i]); } CFCClass_clear_registry(); } { CFCType *type = CFCTest_parse_type(test, parser, "const char"); OK(test, CFCType_const(type), "type_qualifier const"); CFCBase_decref((CFCBase*)type); } { static const char *const exposures[2] = { "public", "" }; static int (*const accessors[2])(CFCSymbol *sym) = { CFCSymbol_public, CFCSymbol_parcel }; for (int i = 0; i < 2; ++i) { const char *exposure = exposures[i]; char *src = CFCUtil_sprintf("%s inert int32_t foo;", exposure); CFCVariable *var = CFCTest_parse_variable(test, parser, src); OK(test, accessors[i]((CFCSymbol*)var), "exposure_specifier %s", exposure); FREEMEM(src); CFCBase_decref((CFCBase*)var); } } { static const char *const hex_constants[] = { "0x1", "0x0a", "0xFFFFFFFF", "-0xFC", NULL }; S_test_initial_value(test, parser, hex_constants, "int32_t", "hex_constant:"); } { static const char *const integer_constants[] = { "1", "-9999", "0", "10000", NULL }; S_test_initial_value(test, parser, integer_constants, "int32_t", "integer_constant:"); } { static const char *const float_constants[] = { "1.0", "-9999.999", "0.1", "0.0", NULL }; S_test_initial_value(test, parser, float_constants, "double", "float_constant:"); } { static const char *const string_literals[] = { "\"blah\"", "\"blah blah\"", "\"\\\"blah\\\" \\\"blah\\\"\"", NULL }; S_test_initial_value(test, parser, string_literals, "String*", "string_literal:"); } { static const char *const composites[5] = { "int[]", "i32_t **", "Foo **", "Foo ***", "const void *" }; for (int i = 0; i < 5; ++i) { const char *composite = composites[i]; CFCType *type = CFCTest_parse_type(test, parser, composite); OK(test, CFCType_is_composite(type), "composite_type: %s", composite); CFCBase_decref((CFCBase*)type); } } { static const char *const object_types[5] = { "Obj *", "incremented Foo*", "decremented String *" }; for (int i = 0; i < 3; ++i) { const char *object_type = object_types[i]; CFCType *type = CFCTest_parse_type(test, parser, object_type); OK(test, CFCType_is_object(type), "object_type: %s", object_type); CFCBase_decref((CFCBase*)type); } } { static const char *const param_list_strings[3] = { "()", "(int foo)", "(Obj *foo, Foo **foo_ptr)" }; for (int i = 0; i < 3; ++i) { const char *param_list_string = param_list_strings[i]; CFCParamList *param_list = CFCTest_parse_param_list(test, parser, param_list_string); INT_EQ(test, CFCParamList_num_vars(param_list), i, "param list num_vars: %d", i); CFCBase_decref((CFCBase*)param_list); } } { CFCParamList *param_list = CFCTest_parse_param_list(test, parser, "(int foo, ...)"); OK(test, CFCParamList_variadic(param_list), "variadic param list"); CFCBase_decref((CFCBase*)param_list); } { const char *param_list_string = "(int foo = 0xFF, char *bar =\"blah\")"; CFCParamList *param_list = CFCTest_parse_param_list(test, parser, param_list_string); const char **initial_values = CFCParamList_get_initial_values(param_list); STR_EQ(test, initial_values[0], "0xFF", "param list initial_values[0]"); STR_EQ(test, initial_values[1], "\"blah\"", "param list initial_values[1]"); OK(test, initial_values[2] == NULL, "param list initial_values[2]"); CFCBase_decref((CFCBase*)param_list); } { CFCParser_set_class_name(parser, "Stuff::Obj"); CFCParser_set_class_cnick(parser, "Obj"); const char *method_string = "public Foo* Spew_Foo(Obj *self, uint32_t *how_many);"; CFCMethod *method = CFCTest_parse_method(test, parser, method_string); CFCBase_decref((CFCBase*)method); const char *var_string = "public inert Hash *hash;"; CFCVariable *var = CFCTest_parse_variable(test, parser, var_string); CFCBase_decref((CFCBase*)var); } { static const char *const class_names[4] = { "Foo", "Foo::FooJr", "Foo::FooJr::FooIII", "Foo::FooJr::FooIII::Foo4th" }; for (int i = 0; i < 4; ++i) { const char *class_name = class_names[i]; char *class_string = CFCUtil_sprintf("class %s { }", class_name); CFCClass *klass = CFCTest_parse_class(test, parser, class_string); STR_EQ(test, CFCClass_get_class_name(klass), class_name, "class_name: %s", class_name); FREEMEM(class_string); CFCBase_decref((CFCBase*)klass); } } { static const char *const cnicks[2] = { "Food", "FF" }; for (int i = 0; i < 2; ++i) { const char *cnick = cnicks[i]; char *class_string = CFCUtil_sprintf("class Foodie%s cnick %s { }", cnick, cnick); CFCClass *klass = CFCTest_parse_class(test, parser, class_string); STR_EQ(test, CFCClass_get_cnick(klass), cnick, "cnick: %s", cnick); FREEMEM(class_string); CFCBase_decref((CFCBase*)klass); } } CFCBase_decref((CFCBase*)parser); CFCClass_clear_registry(); CFCParcel_reap_singletons(); }
char* CFCPerlTypeMap_from_perl(CFCType *type, const char *xs_var) { char *result = NULL; if (CFCType_is_object(type)) { const char *struct_sym = CFCType_get_specifier(type); const char *vtable_var = CFCType_get_vtable_var(type); if (strcmp(struct_sym, "lucy_CharBuf") == 0 || strcmp(struct_sym, "cfish_CharBuf") == 0 || strcmp(struct_sym, "lucy_Obj") == 0 || strcmp(struct_sym, "cfish_Obj") == 0 ) { // Share buffers rather than copy between Perl scalars and // Clownfish string types. result = CFCUtil_cat(CFCUtil_strdup(""), "(", struct_sym, "*)XSBind_sv_to_cfish_obj(", xs_var, ", ", vtable_var, ", alloca(cfish_ZCB_size()))", NULL); } else { result = CFCUtil_cat(CFCUtil_strdup(""), "(", struct_sym, "*)XSBind_sv_to_cfish_obj(", xs_var, ", ", vtable_var, ", NULL)", NULL); } } else if (CFCType_is_primitive(type)) { const char *specifier = CFCType_get_specifier(type); size_t size = 80 + strlen(xs_var) * 2; result = (char*)MALLOCATE(size); if (strcmp(specifier, "double") == 0) { sprintf(result, "SvNV(%s)", xs_var); } else if (strcmp(specifier, "float") == 0) { sprintf(result, "(float)SvNV(%s)", xs_var); } else if (strcmp(specifier, "int") == 0) { sprintf(result, "(int)SvIV(%s)", xs_var); } else if (strcmp(specifier, "short") == 0) { sprintf(result, "(short)SvIV(%s)", xs_var); } else if (strcmp(specifier, "long") == 0) { const char pattern[] = "((sizeof(long) <= sizeof(IV)) ? (long)SvIV(%s) " ": (long)SvNV(%s))"; sprintf(result, pattern, xs_var, xs_var); } else if (strcmp(specifier, "size_t") == 0) { sprintf(result, "(size_t)SvIV(%s)", xs_var); } else if (strcmp(specifier, "uint64_t") == 0) { sprintf(result, "(uint64_t)SvNV(%s)", xs_var); } else if (strcmp(specifier, "uint32_t") == 0) { sprintf(result, "(uint32_t)SvUV(%s)", xs_var); } else if (strcmp(specifier, "uint16_t") == 0) { sprintf(result, "(uint16_t)SvUV(%s)", xs_var); } else if (strcmp(specifier, "uint8_t") == 0) { sprintf(result, "(uint8_t)SvUV(%s)", xs_var); } else if (strcmp(specifier, "int64_t") == 0) { sprintf(result, "(int64_t)SvNV(%s)", xs_var); } else if (strcmp(specifier, "int32_t") == 0) { sprintf(result, "(int32_t)SvIV(%s)", xs_var); } else if (strcmp(specifier, "int16_t") == 0) { sprintf(result, "(int16_t)SvIV(%s)", xs_var); } else if (strcmp(specifier, "int8_t") == 0) { sprintf(result, "(int8_t)SvIV(%s)", xs_var); } else if (strcmp(specifier, "chy_bool_t") == 0) { sprintf(result, "SvTRUE(%s) ? 1 : 0", xs_var); } else { FREEMEM(result); result = NULL; } } return result; }
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; }
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_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; }
static void S_run_object_tests(CFCTest *test) { static const char *modifiers[4] = { "const", "incremented", "decremented", "nullable" }; static int flags[4] = { CFCTYPE_CONST, CFCTYPE_INCREMENTED, CFCTYPE_DECREMENTED, CFCTYPE_NULLABLE }; static int (*accessors[4])(CFCType *type) = { CFCType_const, CFCType_incremented, CFCType_decremented, CFCType_nullable }; { CFCParser *parser = CFCParser_new(); CFCParcel *neato_parcel = CFCTest_parse_parcel(test, parser, "parcel Neato;"); static const char *specifiers[4] = { "Foo", "FooJr", "FooIII", "Foo4th" }; for (int i = 0; i < 4; ++i) { const char *specifier = specifiers[i]; char *class_code = CFCUtil_sprintf("class %s {}", specifier); CFCClass *klass = CFCTest_parse_class(test, parser, class_code); FREEMEM(class_code); static const char *prefixes[2] = { "", "neato_" }; char *expect = CFCUtil_sprintf("neato_%s", specifier); for (int j = 0; j < 2; ++j) { char *src = CFCUtil_sprintf("%s%s*", prefixes[j], specifier); CFCType *type = CFCTest_parse_type(test, parser, src); CFCType_resolve(type); STR_EQ(test, CFCType_get_specifier(type), expect, "object_type_specifier: %s", src); OK(test, CFCType_is_object(type), "%s is_object", src); INT_EQ(test, CFCType_get_indirection(type), 1, "%s indirection", src); FREEMEM(src); CFCBase_decref((CFCBase*)type); } FREEMEM(expect); for (int j = 0; j < 4; ++j) { char *src = CFCUtil_sprintf("%s %s*", modifiers[j], specifier); CFCType *type = CFCTest_parse_type(test, parser, src); OK(test, CFCType_is_object(type), "%s is_object", src); OK(test, accessors[j](type), "%s accessor", src); FREEMEM(src); CFCBase_decref((CFCBase*)type); } CFCBase_decref((CFCBase*)klass); CFCClass_clear_registry(); } CFCBase_decref((CFCBase*)neato_parcel); CFCBase_decref((CFCBase*)parser); } CFCParcel *neato_parcel = CFCParcel_new("Neato", NULL, NULL, NULL); CFCClass *foo_class = CFCClass_create(neato_parcel, NULL, "Foo", NULL, NULL, NULL, NULL, NULL, false, false, false); CFCType *foo = CFCType_new_object(0, neato_parcel, "Foo", 1); CFCType_resolve(foo); { CFCType *another_foo = CFCType_new_object(0, neato_parcel, "Foo", 1); CFCType_resolve(another_foo); OK(test, CFCType_equals(foo, another_foo), "equals"); CFCBase_decref((CFCBase*)another_foo); } { CFCClass *bar_class = CFCClass_create(neato_parcel, NULL, "Bar", NULL, NULL, NULL, NULL, NULL, false, false, false); CFCType *bar = CFCType_new_object(0, neato_parcel, "Bar", 1); CFCType_resolve(bar); OK(test, !CFCType_equals(foo, bar), "different specifier spoils equals"); CFCBase_decref((CFCBase*)bar); CFCBase_decref((CFCBase*)bar_class); } { CFCParcel *foreign_parcel = CFCParcel_new("Foreign", NULL, NULL, NULL); CFCClass *foreign_foo_class = CFCClass_create(foreign_parcel, NULL, "Foreign::Foo", NULL, NULL, NULL, NULL, NULL, false, false, false); CFCType *foreign_foo = CFCType_new_object(0, foreign_parcel, "Foo", 1); CFCType_resolve(foreign_foo); OK(test, !CFCType_equals(foo, foreign_foo), "different parcel spoils equals"); STR_EQ(test, CFCType_get_specifier(foreign_foo), "foreign_Foo", "prepend parcel prefix to specifier"); CFCBase_decref((CFCBase*)foreign_parcel); CFCBase_decref((CFCBase*)foreign_foo_class); CFCBase_decref((CFCBase*)foreign_foo); } { for (int i = 0; i < 4; ++i) { CFCType *modified_foo = CFCType_new_object(flags[i], neato_parcel, "Foo", 1); CFCType_resolve(modified_foo); OK(test, accessors[i](modified_foo), "%s", modifiers[i]); OK(test, !accessors[i](foo), "not %s", modifiers[i]); OK(test, !CFCType_equals(foo, modified_foo), "different %s spoils equals", modifiers[i]); OK(test, !CFCType_similar(foo, modified_foo), "different %s spoils similar", modifiers[i]); CFCBase_decref((CFCBase*)modified_foo); } } { CFCType *string_type = CFCType_new_object(0, neato_parcel, "String", 1); OK(test, CFCType_is_string_type(string_type), "%s", "is_string_type"); OK(test, !CFCType_is_string_type(foo), "not %s", "not is_string_type"); CFCBase_decref((CFCBase*)string_type); } CFCBase_decref((CFCBase*)neato_parcel); CFCBase_decref((CFCBase*)foo_class); CFCBase_decref((CFCBase*)foo); CFCClass_clear_registry(); CFCParcel_reap_singletons(); }
char* CFCGoTypeMap_go_type_name(CFCType *type, CFCParcel *current_parcel) { if (CFCType_cfish_obj(type)) { return CFCUtil_strdup("interface{}"); } else if (CFCType_cfish_string(type)) { return CFCUtil_strdup("string"); } else if (CFCType_cfish_blob(type)) { return CFCUtil_strdup("[]byte"); } else if (CFCType_cfish_vector(type)) { return CFCUtil_strdup("[]interface{}"); } else if (CFCType_cfish_hash(type)) { return CFCUtil_strdup("map[string]interface{}"); } else if (CFCType_is_object(type)) { // Divide the specifier into prefix and struct name. const char *specifier = CFCType_get_specifier(type); size_t prefix_len = 0; for (size_t max = strlen(specifier); prefix_len < max; prefix_len++) { if (isupper(specifier[prefix_len])) { break; } } if (!prefix_len) { CFCUtil_die("Can't convert object type name '%s'", specifier); } const char *struct_sym = specifier + prefix_len; // Find the parcel that the type lives in. CFCParcel** all_parcels = CFCParcel_all_parcels(); CFCParcel *parcel = NULL; for (int i = 0; all_parcels[i] != NULL; i++) { const char *candidate = CFCParcel_get_prefix(all_parcels[i]); if (strncmp(candidate, specifier, prefix_len) == 0 && strlen(candidate) == prefix_len ) { parcel = all_parcels[i]; break; } } if (!parcel) { CFCUtil_die("Can't find parcel for type '%s'", specifier); } // If the type lives in this parcel, return only the struct sym // without a go package prefix. if (parcel == current_parcel) { return CFCUtil_strdup(struct_sym); } // The type lives in another parcel, so prefix its Go package name. // TODO: Stop downcasing once Clownfish parcel names are constrained // to lower case. const char *package_name = CFCParcel_get_name(parcel); if (strrchr(package_name, '.')) { package_name = strrchr(package_name, '.') + 1; } char *result = CFCUtil_sprintf("%s.%s", package_name, struct_sym); for (int i = 0; result[i] != '.'; i++) { result[i] = tolower(result[i]); } return result; } else if (CFCType_is_primitive(type)) { const char *specifier = CFCType_get_specifier(type); for (int i = 0; i < num_conversions; i++) { if (strcmp(specifier, conversions[i].c) == 0) { return CFCUtil_strdup(conversions[i].go); } } } return NULL; }
char* CFCPerlTypeMap_to_perl(CFCType *type, const char *cf_var) { const char *type_str = CFCType_to_c(type); char *result = NULL; if (CFCType_is_object(type)) { result = CFCUtil_cat(CFCUtil_strdup(""), "(", cf_var, " == NULL ? newSV(0) : " "XSBind_cfish_to_perl((cfish_Obj*)", cf_var, "))", NULL); } else if (CFCType_is_primitive(type)) { // Convert from a primitive type to a Perl scalar. const char *specifier = CFCType_get_specifier(type); size_t size = 80 + strlen(cf_var) * 2; result = (char*)MALLOCATE(size); if (strcmp(specifier, "double") == 0) { sprintf(result, "newSVnv(%s)", cf_var); } else if (strcmp(specifier, "float") == 0) { sprintf(result, "newSVnv(%s)", cf_var); } else if (strcmp(specifier, "int") == 0) { sprintf(result, "newSViv(%s)", cf_var); } else if (strcmp(specifier, "short") == 0) { sprintf(result, "newSViv(%s)", cf_var); } else if (strcmp(specifier, "long") == 0) { char pattern[] = "((sizeof(long) <= sizeof(IV)) ? " "newSViv((IV)%s) : newSVnv((NV)%s))"; sprintf(result, pattern, cf_var, cf_var); } else if (strcmp(specifier, "size_t") == 0) { sprintf(result, "newSViv(%s)", cf_var); } else if (strcmp(specifier, "uint64_t") == 0) { char pattern[] = "sizeof(UV) == 8 ? newSVuv((UV)%s) : newSVnv((NV)%s)"; sprintf(result, pattern, cf_var, cf_var); } else if (strcmp(specifier, "uint32_t") == 0) { sprintf(result, "newSVuv(%s)", cf_var); } else if (strcmp(specifier, "uint16_t") == 0) { sprintf(result, "newSVuv(%s)", cf_var); } else if (strcmp(specifier, "uint8_t") == 0) { sprintf(result, "newSVuv(%s)", cf_var); } else if (strcmp(specifier, "int64_t") == 0) { char pattern[] = "sizeof(IV) == 8 ? newSViv((IV)%s) : newSVnv((NV)%s)"; sprintf(result, pattern, cf_var, cf_var); } else if (strcmp(specifier, "int32_t") == 0) { sprintf(result, "newSViv(%s)", cf_var); } else if (strcmp(specifier, "int16_t") == 0) { sprintf(result, "newSViv(%s)", cf_var); } else if (strcmp(specifier, "int8_t") == 0) { sprintf(result, "newSViv(%s)", cf_var); } else if (strcmp(specifier, "chy_bool_t") == 0) { sprintf(result, "newSViv(%s)", cf_var); } else { FREEMEM(result); result = NULL; } } else if (CFCType_is_composite(type)) { if (strcmp(type_str, "void*") == 0) { // Assume that void* is a reference SV -- either a hashref or an // arrayref. result = CFCUtil_cat(CFCUtil_strdup(""), "newRV_inc((SV*)", cf_var, ")", NULL); } } return result; }