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; }
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); }
static void S_run_integer_tests(CFCTest *test) { { CFCType *type = CFCType_new_integer(CFCTYPE_CONST, "int32_t"); OK(test, CFCType_const(type), "const"); STR_EQ(test, CFCType_get_specifier(type), "int32_t", "get_specifier"); STR_EQ(test, CFCType_to_c(type), "const int32_t", "'const' in C representation"); CFCBase_decref((CFCBase*)type); } { CFCParser *parser = CFCParser_new(); static const char *specifiers[14] = { "bool", "char", "short", "int", "long", "size_t", "int8_t", "int16_t", "int32_t", "int64_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t" }; for (int i = 0; i < 14; ++i) { const char *specifier = specifiers[i]; CFCType *type; type = CFCTest_parse_type(test, parser, specifier); OK(test, CFCType_is_integer(type), "%s is_integer", specifier); CFCBase_decref((CFCBase*)type); char *const_specifier = CFCUtil_sprintf("const %s", specifier); type = CFCTest_parse_type(test, parser, const_specifier); OK(test, CFCType_is_integer(type), "%s is_integer", const_specifier); OK(test, CFCType_const(type), "%s is const", const_specifier); FREEMEM(const_specifier); CFCBase_decref((CFCBase*)type); } CFCBase_decref((CFCBase*)parser); } }
static char* S_primitive_callback_def(CFCMethod *method, const char *callback_start, const char *refcount_mods) { const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); const char *micro_sym = CFCMethod_micro_sym(method); char callback_func[50]; if (CFCType_is_integer(return_type)) { strcpy(callback_func, "S_finish_callback_i64"); } else if (CFCType_is_floating(return_type)) { strcpy(callback_func, "S_finish_callback_f64"); } else { CFCUtil_die("Unexpected type: %s", ret_type_str); } char pattern[] = "%s\n" "%s(%s) {\n" "%s" " %s retval = (%s)%s(\"%s\");%s\n" " return retval;\n" "}\n"; char *callback_def = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params, callback_start, ret_type_str, ret_type_str, callback_func, micro_sym, refcount_mods); return callback_def; }
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; }
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); }
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_primitive_callback_def(CFCMethod *method, const char *callback_params, const char *refcount_mods) { const char *override_sym = CFCMethod_full_override_sym(method); const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); CFCType *return_type = CFCMethod_get_return_type(method); const char *ret_type_str = CFCType_to_c(return_type); char cb_func_name[40]; if (CFCType_is_floating(return_type)) { strcpy(cb_func_name, "cfish_Host_callback_f64"); } else if (CFCType_is_integer(return_type)) { strcpy(cb_func_name, "cfish_Host_callback_i64"); } else if (strcmp(ret_type_str, "void*") == 0) { strcpy(cb_func_name, "cfish_Host_callback_host"); } else { CFCUtil_die("unrecognized type: %s", ret_type_str); } char pattern[] = "%s\n" "%s(%s) {\n" " return (%s)%s(%s);%s\n" "}\n"; size_t size = sizeof(pattern) + strlen(ret_type_str) + strlen(override_sym) + strlen(params) + strlen(ret_type_str) + strlen(cb_func_name) + strlen(callback_params) + strlen(refcount_mods) + 20; char *callback_def = (char*)MALLOCATE(size); sprintf(callback_def, pattern, ret_type_str, override_sym, params, ret_type_str, cb_func_name, callback_params, refcount_mods); return callback_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_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 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; }