| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include "CFCBindMethod.h" |
| #include "CFCUtil.h" |
| #include "CFCMethod.h" |
| #include "CFCFunction.h" |
| #include "CFCParamList.h" |
| #include "CFCType.h" |
| #include "CFCVariable.h" |
| #include "CFCSymbol.h" |
| #include "CFCClass.h" |
| |
| /* Create a macro definition that aliases to a function name directly, since |
| * this method may not be overridden. */ |
| static char* |
| S_final_method_def(CFCMethod *method, CFCClass *klass); |
| |
| static char* |
| S_virtual_method_def(CFCMethod *method, CFCClass *klass); |
| |
| /* Take a NULL-terminated list of CFCVariables and build up a string of |
| * directives like: |
| * |
| * UNUSED_VAR(var1); |
| * UNUSED_VAR(var2); |
| */ |
| static char* |
| S_build_unused_vars(CFCVariable **vars); |
| |
| /* Create an unreachable return statement if necessary, in order to thwart |
| * compiler warnings. */ |
| static char* |
| S_maybe_unreachable(CFCType *return_type); |
| |
| /* Return a string which maps arguments to various arg wrappers conforming |
| * to Host's callback interface. For instance, (int32_t foo, Obj *bar) |
| * produces the following: |
| * |
| * CFISH_ARG_I32("foo", foo), |
| * CFISH_ARG_OBJ("bar", bar) |
| */ |
| static char* |
| S_callback_params(CFCMethod *method); |
| |
| /* Return a function which throws a runtime error indicating which variable |
| * couldn't be mapped. TODO: it would be better to resolve all these cases at |
| * compile-time. |
| */ |
| static char* |
| S_invalid_callback_def(CFCMethod *method); |
| |
| // Create a callback for a method which operates in a void context. |
| static char* |
| S_void_callback_def(CFCMethod *method, const char *callback_params); |
| |
| // Create a callback which returns a primitive type. |
| static char* |
| S_primitive_callback_def(CFCMethod *method, const char *callback_params); |
| |
| /* Create a callback which returns an object type -- either a generic object or |
| * a string. */ |
| static char* |
| S_obj_callback_def(CFCMethod *method, const char *callback_params); |
| |
| char* |
| CFCBindMeth_method_def(CFCMethod *method, CFCClass *klass) { |
| if (CFCMethod_final(method)) { |
| return S_final_method_def(method, klass); |
| } |
| else { |
| return S_virtual_method_def(method, klass); |
| } |
| } |
| |
| /* Create a macro definition that aliases to a function name directly, since |
| * this method may not be overridden. */ |
| static char* |
| S_final_method_def(CFCMethod *method, CFCClass *klass) { |
| const char *cnick = CFCClass_get_cnick(klass); |
| const char *self_type = CFCType_to_c(CFCMethod_self_type(method)); |
| size_t meth_sym_size = CFCMethod_full_method_sym(method, cnick, NULL, 0); |
| char *full_meth_sym = (char*)MALLOCATE(meth_sym_size); |
| CFCMethod_full_method_sym(method, cnick, full_meth_sym, meth_sym_size); |
| const char *full_func_sym = CFCMethod_implementing_func_sym(method); |
| const char *arg_names |
| = CFCParamList_name_list(CFCMethod_get_param_list(method)); |
| |
| char pattern[] = "#define %s(%s) \\\n %s((%s)%s)\n"; |
| size_t size = sizeof(pattern) |
| + strlen(full_meth_sym) |
| + strlen(arg_names) |
| + strlen(full_func_sym) |
| + strlen(self_type) |
| + strlen(arg_names) |
| + 20; |
| char *method_def = (char*)MALLOCATE(size); |
| sprintf(method_def, pattern, full_meth_sym, arg_names, full_func_sym, |
| self_type, arg_names); |
| |
| FREEMEM(full_meth_sym); |
| return method_def; |
| } |
| |
| static char* |
| S_virtual_method_def(CFCMethod *method, CFCClass *klass) { |
| const char *cnick = CFCClass_get_cnick(klass); |
| CFCParamList *param_list = CFCMethod_get_param_list(method); |
| const char *invoker_struct = CFCClass_full_struct_sym(klass); |
| const char *common_struct |
| = CFCType_get_specifier(CFCMethod_self_type(method)); |
| const char *typedef_str = CFCMethod_full_typedef(method); |
| |
| size_t meth_sym_size = CFCMethod_full_method_sym(method, cnick, NULL, 0); |
| char *full_meth_sym = (char*)MALLOCATE(meth_sym_size); |
| CFCMethod_full_method_sym(method, cnick, full_meth_sym, meth_sym_size); |
| |
| size_t offset_sym_size = CFCMethod_full_offset_sym(method, cnick, NULL, 0); |
| char *full_offset_sym = (char*)MALLOCATE(offset_sym_size); |
| CFCMethod_full_offset_sym(method, cnick, full_offset_sym, offset_sym_size); |
| |
| // Prepare parameter lists, minus invoker. The invoker gets forced to |
| // "self" later. |
| const char *arg_names_minus_invoker = CFCParamList_name_list(param_list); |
| const char *params_minus_invoker = CFCParamList_to_c(param_list); |
| while (*arg_names_minus_invoker && *arg_names_minus_invoker != ',') { |
| arg_names_minus_invoker++; |
| } |
| while (*params_minus_invoker && *params_minus_invoker != ',') { |
| params_minus_invoker++; |
| } |
| |
| // Prepare a return statement... or not. |
| CFCType *return_type = CFCMethod_get_return_type(method); |
| const char *ret_type_str = CFCType_to_c(return_type); |
| const char *maybe_return = CFCType_is_void(return_type) ? "" : "return "; |
| |
| const char pattern[] = |
| "extern size_t %s;\n" |
| "static CHY_INLINE %s\n" |
| "%s(const %s *self%s) {\n" |
| " char *const method_address = *(char**)self + %s;\n" |
| " const %s method = *((%s*)method_address);\n" |
| " %smethod((%s*)self%s);\n" |
| "}\n"; |
| |
| size_t size = sizeof(pattern) |
| + strlen(full_offset_sym) |
| + strlen(ret_type_str) |
| + strlen(full_meth_sym) |
| + strlen(invoker_struct) |
| + strlen(params_minus_invoker) |
| + strlen(full_offset_sym) |
| + strlen(typedef_str) |
| + strlen(typedef_str) |
| + strlen(maybe_return) |
| + strlen(common_struct) |
| + strlen(arg_names_minus_invoker) |
| + 40; |
| char *method_def = (char*)MALLOCATE(size); |
| sprintf(method_def, pattern, full_offset_sym, ret_type_str, |
| full_meth_sym, invoker_struct, params_minus_invoker, |
| full_offset_sym, typedef_str, typedef_str, maybe_return, |
| common_struct, arg_names_minus_invoker); |
| |
| FREEMEM(full_offset_sym); |
| FREEMEM(full_meth_sym); |
| return method_def; |
| } |
| |
| char* |
| CFCBindMeth_typdef_dec(struct CFCMethod *method) { |
| const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); |
| const char *ret_type = CFCType_to_c(CFCMethod_get_return_type(method)); |
| const char *full_typedef = CFCMethod_full_typedef(method); |
| size_t size = strlen(params) |
| + strlen(ret_type) |
| + strlen(full_typedef) |
| + 20 |
| + sizeof("\0"); |
| char *buf = (char*)MALLOCATE(size); |
| sprintf(buf, "typedef %s\n(*%s)(%s);\n", ret_type, full_typedef, params); |
| return buf; |
| } |
| |
| char* |
| CFCBindMeth_callback_dec(CFCMethod *method) { |
| const char *full_callback_sym = CFCMethod_full_callback_sym(method); |
| size_t size = strlen(full_callback_sym) + 50; |
| char *callback_dec = (char*)MALLOCATE(size); |
| sprintf(callback_dec, "extern cfish_Callback %s;\n", full_callback_sym); |
| return callback_dec; |
| } |
| |
| char* |
| CFCBindMeth_callback_obj_def(CFCMethod *method, const char *offset) { |
| const char *macro_sym = CFCMethod_get_macro_sym(method); |
| unsigned macro_sym_len = strlen(macro_sym); |
| const char *full_override_sym = CFCMethod_full_override_sym(method); |
| const char *full_callback_sym = CFCMethod_full_callback_sym(method); |
| char pattern[] = |
| "cfish_Callback %s = {\"%s\", %u, (cfish_method_t)%s, %s};\n"; |
| size_t size = sizeof(pattern) |
| + macro_sym_len |
| + strlen(full_override_sym) |
| + strlen(full_callback_sym) |
| + strlen(offset) |
| + 30; |
| char *def = (char*)MALLOCATE(size); |
| sprintf(def, pattern, full_callback_sym, macro_sym, macro_sym_len, |
| full_override_sym, offset); |
| return def; |
| } |
| |
| static char* |
| S_build_unused_vars(CFCVariable **vars) { |
| char *unused = CFCUtil_strdup(""); |
| |
| for (int i = 0; vars[i] != NULL; i++) { |
| const char *var_name = CFCVariable_micro_sym(vars[i]); |
| size_t size = strlen(unused) + strlen(var_name) + 80; |
| unused = (char*)REALLOCATE(unused, size); |
| strcat(unused, "\n CHY_UNUSED_VAR("); |
| strcat(unused, var_name); |
| strcat(unused, ");"); |
| } |
| |
| return unused; |
| } |
| |
| static char* |
| S_maybe_unreachable(CFCType *return_type) { |
| char *return_statement; |
| if (CFCType_is_void(return_type)) { |
| return_statement = CFCUtil_strdup(""); |
| } |
| else { |
| const char *ret_type_str = CFCType_to_c(return_type); |
| return_statement = (char*)MALLOCATE(strlen(ret_type_str) + 60); |
| sprintf(return_statement, "\n CHY_UNREACHABLE_RETURN(%s);", |
| ret_type_str); |
| } |
| return return_statement; |
| } |
| |
| char* |
| CFCBindMeth_abstract_method_def(CFCMethod *method) { |
| CFCParamList *param_list = CFCMethod_get_param_list(method); |
| const char *params = CFCParamList_to_c(param_list); |
| const char *full_func_sym = CFCMethod_implementing_func_sym(method); |
| const char *vtable_var |
| = CFCType_get_vtable_var(CFCMethod_self_type(method)); |
| CFCType *return_type = CFCMethod_get_return_type(method); |
| const char *ret_type_str = CFCType_to_c(return_type); |
| const char *macro_sym = CFCMethod_get_macro_sym(method); |
| |
| // Thwart compiler warnings. |
| CFCVariable **param_vars = CFCParamList_get_variables(param_list); |
| char *unused = S_build_unused_vars(param_vars + 1); |
| char *return_statement = S_maybe_unreachable(return_type); |
| |
| char pattern[] = |
| "%s\n" |
| "%s(%s) {\n" |
| " cfish_CharBuf *klass = self ? Cfish_Obj_Get_Class_Name((cfish_Obj*)self) : %s->name;%s\n" |
| " CFISH_THROW(CFISH_ERR, \"Abstract method '%s' not defined by %%o\", klass);%s\n" |
| "}\n"; |
| size_t needed = sizeof(pattern) |
| + strlen(ret_type_str) |
| + strlen(full_func_sym) |
| + strlen(params) |
| + strlen(vtable_var) |
| + strlen(unused) |
| + strlen(macro_sym) |
| + strlen(return_statement) |
| + 50; |
| char *abstract_def = (char*)MALLOCATE(needed); |
| sprintf(abstract_def, pattern, ret_type_str, full_func_sym, params, |
| vtable_var, unused, macro_sym, return_statement); |
| |
| FREEMEM(unused); |
| FREEMEM(return_statement); |
| return abstract_def; |
| } |
| |
| char* |
| CFCBindMeth_callback_def(CFCMethod *method) { |
| CFCType *return_type = CFCMethod_get_return_type(method); |
| char *params = S_callback_params(method); |
| char *callback_def = NULL; |
| |
| 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); |
| } |
| else if (CFCType_is_object(return_type)) { |
| callback_def = S_obj_callback_def(method, params); |
| } |
| else { |
| callback_def = S_primitive_callback_def(method, params); |
| } |
| |
| FREEMEM(params); |
| return callback_def; |
| } |
| |
| 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_invalid_callback_def(CFCMethod *method) { |
| const char *class_cnick = CFCMethod_get_class_cnick(method); |
| size_t meth_sym_size = CFCMethod_full_method_sym(method, class_cnick, NULL, 0); |
| char *full_method_sym = (char*)MALLOCATE(meth_sym_size); |
| CFCMethod_full_method_sym(method, class_cnick, full_method_sym, |
| meth_sym_size); |
| |
| const char *override_sym = CFCMethod_full_override_sym(method); |
| CFCParamList *param_list = CFCMethod_get_param_list(method); |
| const char *params = CFCParamList_to_c(param_list); |
| CFCVariable **param_vars = CFCParamList_get_variables(param_list); |
| |
| // Thwart compiler warnings. |
| CFCType *return_type = CFCMethod_get_return_type(method); |
| const char *ret_type_str = CFCType_to_c(return_type); |
| char *unused = S_build_unused_vars(param_vars); |
| char *unreachable = S_maybe_unreachable(return_type); |
| |
| char pattern[] = |
| "%s\n" |
| "%s(%s) {%s\n" |
| " CFISH_THROW(CFISH_ERR, \"Can't override %s via binding\");%s\n" |
| "}\n"; |
| size_t size = sizeof(pattern) |
| + strlen(ret_type_str) |
| + strlen(override_sym) |
| + strlen(params) |
| + strlen(unused) |
| + strlen(full_method_sym) |
| + strlen(unreachable) |
| + 20; |
| char *callback_def = (char*)MALLOCATE(size); |
| sprintf(callback_def, pattern, ret_type_str, override_sym, params, unused, |
| full_method_sym, unreachable); |
| |
| FREEMEM(full_method_sym); |
| FREEMEM(unreachable); |
| FREEMEM(unused); |
| return callback_def; |
| } |
| |
| static char* |
| S_void_callback_def(CFCMethod *method, const char *callback_params) { |
| const char *override_sym = CFCMethod_full_override_sym(method); |
| const char *params = CFCParamList_to_c(CFCMethod_get_param_list(method)); |
| const char pattern[] = |
| "void\n" |
| "%s(%s) {\n" |
| " cfish_Host_callback(%s);\n" |
| "}\n"; |
| size_t size = sizeof(pattern) |
| + strlen(override_sym) |
| + strlen(params) |
| + strlen(callback_params) |
| + 200; |
| char *callback_def = (char*)MALLOCATE(size); |
| sprintf(callback_def, pattern, override_sym, params, callback_params); |
| return callback_def; |
| } |
| |
| static char* |
| S_primitive_callback_def(CFCMethod *method, const char *callback_params) { |
| 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);\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) |
| + 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); |
| |
| return callback_def; |
| } |
| |
| static char* |
| S_obj_callback_def(CFCMethod *method, const char *callback_params) { |
| 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 *cb_func_name = CFCType_is_string_type(return_type) |
| ? "cfish_Host_callback_str" |
| : "cfish_Host_callback_obj"; |
| |
| char *nullable_check = CFCUtil_strdup(""); |
| if (!CFCType_nullable(return_type)) { |
| const char *macro_sym = CFCMethod_get_macro_sym(method); |
| char pattern[] = |
| "\n if (!retval) { CFISH_THROW(CFISH_ERR, " |
| "\"%s() for class '%%o' cannot return NULL\", " |
| "Cfish_Obj_Get_Class_Name((cfish_Obj*)self)); }"; |
| size_t size = sizeof(pattern) + strlen(macro_sym) + 30; |
| nullable_check = (char*)REALLOCATE(nullable_check, size); |
| sprintf(nullable_check, pattern, macro_sym); |
| } |
| |
| const char *decrement = CFCType_incremented(return_type) |
| ? "" |
| : "\n CFISH_DECREF(retval);"; |
| |
| char pattern[] = |
| "%s\n" |
| "%s(%s) {\n" |
| " %s retval = (%s)%s(%s);%s%s\n" |
| " return retval;\n" |
| "}\n"; |
| size_t size = sizeof(pattern) |
| + strlen(ret_type_str) |
| + strlen(override_sym) |
| + strlen(params) |
| + strlen(ret_type_str) |
| + strlen(ret_type_str) |
| + strlen(cb_func_name) |
| + strlen(callback_params) |
| + strlen(nullable_check) |
| + strlen(decrement) |
| + 30; |
| char *callback_def = (char*)MALLOCATE(size); |
| sprintf(callback_def, pattern, ret_type_str, override_sym, params, |
| ret_type_str, ret_type_str, cb_func_name, callback_params, |
| nullable_check, decrement); |
| |
| FREEMEM(nullable_check); |
| return callback_def; |
| } |
| |