| /* 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. |
| */ |
| |
| #define CFC_NEED_BASE_STRUCT_DEF |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include "CFCBindClass.h" |
| #include "CFCBindFunction.h" |
| #include "CFCBindMethod.h" |
| #include "CFCBase.h" |
| #include "CFCClass.h" |
| #include "CFCFunction.h" |
| #include "CFCMethod.h" |
| #include "CFCParamList.h" |
| #include "CFCParcel.h" |
| #include "CFCType.h" |
| #include "CFCVariable.h" |
| #include "CFCUtil.h" |
| |
| struct CFCBindClass { |
| CFCBase base; |
| CFCClass *client; |
| char *short_names_macro; |
| }; |
| |
| // Generate C header for an inert class. |
| static char* |
| S_to_c_header_inert(CFCBindClass *self); |
| |
| // Generate C header for a dynamic class. |
| static char* |
| S_to_c_header_dynamic(CFCBindClass *self); |
| |
| // Create the definition for the instantiable object struct. |
| static char* |
| S_struct_definition(CFCBindClass *self); |
| |
| // Declare typedefs for fresh methods, to ease casting. |
| static char* |
| S_method_typedefs(CFCBindClass *self); |
| |
| // If class inherits from something, include the parent class's header. |
| static char* |
| S_parent_include(CFCBindClass *self); |
| |
| // Add a C function definition for each method and each function. |
| static char* |
| S_sub_declarations(CFCBindClass *self); |
| |
| // Declare class (a.k.a. "inert") variables. |
| static char* |
| S_inert_var_declarations(CFCBindClass *self); |
| |
| // Define type-safe wrappers for Obj functions. |
| static char* |
| S_wrapper_defs(CFCBindClass *self); |
| |
| // Define method invocation inline functions. |
| static char* |
| S_method_defs(CFCBindClass *self); |
| |
| // Declare override symbols for functions which wrap host callbacks. |
| static char* |
| S_override_decs(CFCBindClass *self); |
| |
| // Define short names for all of the symbols associated with this class. |
| static char* |
| S_short_names(CFCBindClass *self); |
| |
| static const CFCMeta CFCBINDCLASS_META = { |
| "Clownfish::CFC::Binding::Core::Class", |
| sizeof(CFCBindClass), |
| (CFCBase_destroy_t)CFCBindClass_destroy |
| }; |
| |
| CFCBindClass* |
| CFCBindClass_new(CFCClass *client) { |
| CFCBindClass *self = (CFCBindClass*)CFCBase_allocate(&CFCBINDCLASS_META); |
| return CFCBindClass_init(self, client); |
| } |
| |
| CFCBindClass* |
| CFCBindClass_init(CFCBindClass *self, CFCClass *client) { |
| CFCUTIL_NULL_CHECK(client); |
| self->client = (CFCClass*)CFCBase_incref((CFCBase*)client); |
| |
| const char *PREFIX = CFCClass_get_PREFIX(client); |
| self->short_names_macro = CFCUtil_sprintf("%sUSE_SHORT_NAMES", PREFIX); |
| |
| return self; |
| } |
| |
| void |
| CFCBindClass_destroy(CFCBindClass *self) { |
| FREEMEM(self->short_names_macro); |
| CFCBase_decref((CFCBase*)self->client); |
| CFCBase_destroy((CFCBase*)self); |
| } |
| |
| char* |
| CFCBindClass_to_c_header(CFCBindClass *self) { |
| if (CFCClass_inert(self->client)) { |
| // Inert classes only output inert functions and vars. |
| return S_to_c_header_inert(self); |
| } |
| else { |
| return S_to_c_header_dynamic(self); |
| } |
| } |
| |
| static char* |
| S_to_c_header_inert(CFCBindClass *self) { |
| char *inert_func_decs = S_sub_declarations(self); |
| char *inert_var_defs = S_inert_var_declarations(self); |
| char *short_names = S_short_names(self); |
| |
| char pattern[] = |
| "/* Declare this class's inert variables.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Declare this class's inert functions.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Define \"short names\" for this class's symbols.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n"; |
| char *content = CFCUtil_sprintf(pattern, inert_var_defs, inert_func_decs, |
| short_names); |
| |
| FREEMEM(inert_var_defs); |
| FREEMEM(inert_func_decs); |
| FREEMEM(short_names); |
| return content; |
| } |
| |
| static char* |
| S_ivars_func(CFCBindClass *self) { |
| CFCClass *client = self->client; |
| const char *full_type = CFCClass_full_struct_sym(client); |
| const char *full_func = CFCClass_full_ivars_func(client); |
| const char *short_func = CFCClass_short_ivars_func(client); |
| const char *full_struct = CFCClass_full_ivars_struct(client); |
| const char *short_struct = CFCClass_short_ivars_struct(client); |
| const char *full_offset = CFCClass_full_ivars_offset(client); |
| const char *PREFIX = CFCClass_get_PREFIX(client); |
| char pattern[] = |
| "extern uint32_t %s;\n" |
| "typedef struct %s %s;\n" |
| "static CFISH_INLINE %s*\n" |
| "%s(%s *self) {\n" |
| " char *ptr = (char*)self + %s;\n" |
| " return (%s*)ptr;\n" |
| "}\n" |
| "#ifdef %sUSE_SHORT_NAMES\n" |
| " #define %s %s\n" |
| " #define %s %s\n" |
| "#endif\n"; |
| char *content = CFCUtil_sprintf(pattern, |
| full_offset, |
| full_struct, full_struct, |
| full_struct, |
| full_func, full_type, |
| full_offset, |
| full_struct, |
| PREFIX, |
| short_struct, full_struct, |
| short_func, full_func); |
| return content; |
| } |
| |
| static char* |
| S_to_c_header_dynamic(CFCBindClass *self) { |
| const char *privacy_symbol = CFCClass_privacy_symbol(self->client); |
| char *ivars = S_ivars_func(self); |
| char *struct_def = S_struct_definition(self); |
| char *parent_include = S_parent_include(self); |
| char *sub_declarations = S_sub_declarations(self); |
| char *inert_var_defs = S_inert_var_declarations(self); |
| char *method_typedefs = S_method_typedefs(self); |
| char *wrapper_defs = S_wrapper_defs(self); |
| char *method_defs = S_method_defs(self); |
| char *override_decs = S_override_decs(self); |
| char *short_names = S_short_names(self); |
| |
| char pattern[] = |
| "/* Include the header for this class's parent. \n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Define the struct layout for instances of this class.\n" |
| " */\n" |
| "\n" |
| "#ifdef %s\n" |
| "%s\n" |
| "%s\n" |
| "#endif /* %s */\n" |
| "\n" |
| "/* Declare this class's inert variables.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Declare both this class's inert functions and the C functions which\n" |
| " * implement this class's dynamic methods.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Define typedefs for each dynamic method, allowing us to cast generic\n" |
| " * pointers to the appropriate function pointer type more cleanly.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Define type-safe wrappers for inert functions of Obj.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Define the inline functions which implement this class's virtual methods.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Declare callbacks for wrapping host overrides.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Define \"short names\" for this class's symbols.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n"; |
| char *content |
| = CFCUtil_sprintf(pattern, parent_include, privacy_symbol, ivars, |
| struct_def, privacy_symbol, inert_var_defs, |
| sub_declarations, method_typedefs, wrapper_defs, |
| method_defs, override_decs, short_names); |
| |
| FREEMEM(ivars); |
| FREEMEM(struct_def); |
| FREEMEM(parent_include); |
| FREEMEM(sub_declarations); |
| FREEMEM(inert_var_defs); |
| FREEMEM(method_typedefs); |
| FREEMEM(wrapper_defs); |
| FREEMEM(method_defs); |
| FREEMEM(override_decs); |
| FREEMEM(short_names); |
| return content; |
| } |
| |
| char* |
| CFCBindClass_to_c_data(CFCBindClass *self) { |
| CFCClass *client = self->client; |
| |
| if (CFCClass_inert(client)) { |
| return CFCUtil_strdup(""); |
| } |
| |
| const char *ivars_offset = CFCClass_full_ivars_offset(client); |
| const char *class_var = CFCClass_full_class_var(client); |
| |
| CFCMethod **methods = CFCClass_methods(client); |
| |
| char *offsets = CFCUtil_strdup(""); |
| char *method_defs = CFCUtil_strdup(""); |
| |
| for (int meth_num = 0; methods[meth_num] != NULL; meth_num++) { |
| CFCMethod *method = methods[meth_num]; |
| |
| // Define method offset variable. |
| char *full_offset_sym = CFCMethod_full_offset_sym(method, client); |
| offsets = CFCUtil_cat(offsets, "uint32_t ", full_offset_sym, ";\n", |
| NULL); |
| FREEMEM(full_offset_sym); |
| |
| int is_fresh = CFCMethod_is_fresh(method, client); |
| |
| // Create a default implementation for abstract methods. |
| if (is_fresh && CFCMethod_abstract(method)) { |
| char *method_def = CFCBindMeth_abstract_method_def(method, client); |
| method_defs = CFCUtil_cat(method_defs, method_def, "\n", NULL); |
| FREEMEM(method_def); |
| } |
| } |
| |
| const char pattern[] = |
| "/* Offset from the top of the object at which the IVARS struct\n" |
| " * can be found.\n" |
| " */\n" |
| "\n" |
| "uint32_t %s;\n" |
| "\n" |
| "/* Offsets for method pointers, measured in bytes, from the top\n" |
| " * of this class's singleton object.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Define abstract methods of this class.\n" |
| " */\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* Define the pointer to the Class singleton object.\n" |
| " */\n" |
| "\n" |
| "cfish_Class *%s;\n" |
| "\n"; |
| char *code |
| = CFCUtil_sprintf(pattern, ivars_offset, offsets, method_defs, |
| class_var); |
| |
| FREEMEM(offsets); |
| FREEMEM(method_defs); |
| return code; |
| } |
| |
| // Create the definition for the instantiable object struct. |
| static char* |
| S_struct_definition(CFCBindClass *self) { |
| CFCClass *const client = self->client; |
| const char *struct_sym; |
| char *member_decs = CFCUtil_strdup(""); |
| |
| CFCParcel *parcel = CFCClass_get_parcel(client); |
| if (CFCParcel_is_cfish(parcel)) { |
| struct_sym = CFCClass_full_struct_sym(client); |
| member_decs = CFCUtil_cat(member_decs, "\n CFISH_OBJ_HEAD", NULL); |
| } |
| else { |
| struct_sym = CFCClass_full_ivars_struct(client); |
| } |
| |
| // Add all member variables declared by classes in this package. |
| CFCVariable **member_vars = CFCClass_member_vars(client); |
| size_t num_non_package_members = CFCClass_num_non_package_ivars(client); |
| for (size_t i = num_non_package_members; member_vars[i] != NULL; i++) { |
| const char *member_dec = CFCVariable_local_declaration(member_vars[i]); |
| member_decs = CFCUtil_cat(member_decs, "\n ", member_dec, NULL); |
| } |
| |
| char *struct_def; |
| |
| if (member_decs[0] == '\0') { |
| // Don't define empty struct. |
| struct_def = CFCUtil_strdup(""); |
| } |
| else { |
| char pattern[] = "struct %s {%s\n};\n"; |
| struct_def = CFCUtil_sprintf(pattern, struct_sym, member_decs); |
| } |
| |
| FREEMEM(member_decs); |
| return struct_def; |
| } |
| |
| // Declare typedefs for every method, to ease casting. |
| static char* |
| S_method_typedefs(CFCBindClass *self) { |
| CFCMethod** methods = CFCClass_methods(self->client); |
| char *typedefs = CFCUtil_strdup(""); |
| for (int i = 0; methods[i] != NULL; i++) { |
| CFCMethod *method = methods[i]; |
| char *typedef_str = CFCBindMeth_typedef_dec(method, self->client); |
| typedefs = CFCUtil_cat(typedefs, typedef_str, "\n", NULL); |
| FREEMEM(typedef_str); |
| } |
| return typedefs; |
| } |
| |
| // If class inherits from something, include the parent class's header. |
| static char* |
| S_parent_include(CFCBindClass *self) { |
| char *parent_include = CFCUtil_strdup(""); |
| CFCClass *parent = CFCClass_get_parent(self->client); |
| if (parent) { |
| parent_include = CFCUtil_cat(parent_include, "#include \"", |
| CFCClass_include_h(parent), "\"", NULL); |
| } |
| return parent_include; |
| } |
| |
| // Add a C function definition for each method and each function. |
| static char* |
| S_sub_declarations(CFCBindClass *self) { |
| const char *PREFIX = CFCClass_get_PREFIX(self->client); |
| CFCFunction **functions = CFCClass_functions(self->client); |
| CFCMethod** fresh_methods = CFCClass_fresh_methods(self->client); |
| char *declarations = CFCUtil_strdup(""); |
| for (int i = 0; functions[i] != NULL; i++) { |
| CFCFunction *func = functions[i]; |
| char *dec = CFCBindFunc_func_declaration(func, self->client); |
| if (!CFCFunction_inline(func)) { |
| declarations = CFCUtil_cat(declarations, PREFIX, "VISIBLE ", NULL); |
| } |
| declarations = CFCUtil_cat(declarations, dec, "\n\n", NULL); |
| FREEMEM(dec); |
| } |
| for (int i = 0; fresh_methods[i] != NULL; i++) { |
| CFCMethod *method = fresh_methods[i]; |
| char *dec = CFCBindMeth_imp_declaration(method, self->client); |
| declarations = CFCUtil_cat(declarations, dec, "\n\n", NULL); |
| FREEMEM(dec); |
| } |
| return declarations; |
| } |
| |
| // Declare class (a.k.a. "inert") variables. |
| static char* |
| S_inert_var_declarations(CFCBindClass *self) { |
| const char *PREFIX = CFCClass_get_PREFIX(self->client); |
| CFCVariable **inert_vars = CFCClass_inert_vars(self->client); |
| char *declarations = CFCUtil_strdup(""); |
| for (int i = 0; inert_vars[i] != NULL; i++) { |
| char *global_c = CFCVariable_global_c(inert_vars[i], self->client); |
| declarations = CFCUtil_cat(declarations, "extern ", PREFIX, "VISIBLE ", |
| global_c, ";\n", NULL); |
| FREEMEM(global_c); |
| } |
| return declarations; |
| } |
| |
| // Define type-safe wrappers for Obj functions. |
| static char* |
| S_wrapper_defs(CFCBindClass *self) { |
| CFCClass *client = self->client; |
| |
| if (strcmp(CFCClass_get_name(client), "Clownfish::Obj") == 0) { |
| return CFCUtil_strdup(""); |
| } |
| |
| const char *prefix = CFCClass_get_prefix(client); |
| const char *nickname = CFCClass_get_nickname(client); |
| const char *struct_sym = CFCClass_full_struct_sym(client); |
| |
| const char *pattern = |
| "static CFISH_INLINE cfish_Class*\n" |
| "%s%s_get_class(%s *self) {\n" |
| " return cfish_Obj_get_class((cfish_Obj*)self);\n" |
| "}\n" |
| "\n" |
| "static CFISH_INLINE cfish_String*\n" |
| "%s%s_get_class_name(%s *self) {\n" |
| " return cfish_Obj_get_class_name((cfish_Obj*)self);\n" |
| "}\n" |
| "\n" |
| "static CFISH_INLINE bool\n" |
| "%s%s_is_a(%s *self, cfish_Class *ancestor) {\n" |
| " return cfish_Obj_is_a((cfish_Obj*)self, ancestor);\n" |
| "}\n"; |
| |
| return CFCUtil_sprintf(pattern, |
| prefix, nickname, struct_sym, |
| prefix, nickname, struct_sym, |
| prefix, nickname, struct_sym); |
| } |
| |
| // Define method invocation inline functions. |
| static char* |
| S_method_defs(CFCBindClass *self) { |
| CFCMethod **methods = CFCClass_methods(self->client); |
| char *method_defs = CFCUtil_strdup(""); |
| for (int i = 0; methods[i] != NULL; i++) { |
| CFCMethod *method = methods[i]; |
| char *def = CFCBindMeth_method_def(method, self->client); |
| method_defs = CFCUtil_cat(method_defs, def, "\n", NULL); |
| FREEMEM(def); |
| } |
| return method_defs; |
| } |
| |
| static char* |
| S_override_decs(CFCBindClass *self) { |
| CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client); |
| char *decs = CFCUtil_strdup(""); |
| char *nulls = CFCUtil_strdup(""); |
| |
| for (int i = 0; fresh_methods[i] != NULL; i++) { |
| CFCMethod *method = fresh_methods[i]; |
| if (CFCMethod_final(method) || !CFCMethod_novel(method)) { |
| continue; |
| } |
| char *override_sym = CFCMethod_full_override_sym(method, self->client); |
| CFCType *return_type = CFCMethod_get_return_type(method); |
| CFCParamList *param_list = CFCMethod_get_param_list(method); |
| const char *ret_type_str = CFCType_to_c(return_type); |
| const char *params = CFCParamList_to_c(param_list); |
| char pattern[] = |
| "%s\n" |
| "%s(%s);\n"; |
| char *callback_dec |
| = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params); |
| decs = CFCUtil_cat(decs, callback_dec, NULL); |
| FREEMEM(callback_dec); |
| |
| nulls = CFCUtil_cat(nulls, "#define ", override_sym, " NULL\n", NULL); |
| FREEMEM(override_sym); |
| } |
| |
| char pattern[] = |
| "#ifdef CFISH_NO_DYNAMIC_OVERRIDES\n" |
| "%s" |
| "#else\n" |
| "%s" |
| "#endif\n" |
| ; |
| char *content = CFCUtil_sprintf(pattern, nulls, decs); |
| |
| FREEMEM(nulls); |
| FREEMEM(decs); |
| return content; |
| } |
| |
| // Define short names for all of the symbols associated with this class. |
| static char* |
| S_short_names(CFCBindClass *self) { |
| CFCClass *client = self->client; |
| char *short_names = CFCUtil_strdup(""); |
| short_names = CFCUtil_cat(short_names, "#ifdef ", self->short_names_macro, |
| "\n", NULL); |
| |
| if (!CFCClass_inert(client)) { |
| const char *short_struct = CFCClass_get_struct_sym(client); |
| const char *full_struct = CFCClass_full_struct_sym(client); |
| const char *short_class_var = CFCClass_short_class_var(client); |
| const char *full_class_var = CFCClass_full_class_var(client); |
| short_names = CFCUtil_cat(short_names, " #define ", |
| short_struct, " ", full_struct, "\n", |
| " #define ", short_class_var, " ", |
| full_class_var, "\n", NULL); |
| } |
| |
| CFCFunction **functions = CFCClass_functions(client); |
| for (int i = 0; functions[i] != NULL; i++) { |
| CFCFunction *func = functions[i]; |
| char *short_sym = CFCFunction_short_func_sym(func, client); |
| char *full_sym = CFCFunction_full_func_sym(func, client); |
| short_names = CFCUtil_cat(short_names, " #define ", short_sym, " ", |
| full_sym, "\n", NULL); |
| FREEMEM(short_sym); |
| FREEMEM(full_sym); |
| } |
| |
| CFCVariable **inert_vars = CFCClass_inert_vars(client); |
| for (int i = 0; inert_vars[i] != NULL; i++) { |
| CFCVariable *var = inert_vars[i]; |
| char *short_sym = CFCVariable_short_sym(var, client); |
| char *full_sym = CFCVariable_full_sym(var, client); |
| short_names = CFCUtil_cat(short_names, " #define ", short_sym, " ", |
| full_sym, "\n", NULL); |
| FREEMEM(short_sym); |
| FREEMEM(full_sym); |
| } |
| |
| // Wrappers. |
| if (!CFCClass_inert(client) |
| && strcmp(CFCClass_get_name(client), "Clownfish::Obj") != 0 |
| ) { |
| static const char *wrapped_funcs[] = { |
| "get_class", |
| "get_class_name", |
| "is_a" |
| }; |
| static int num_wrapped_funcs |
| = sizeof(wrapped_funcs) / sizeof(wrapped_funcs[0]); |
| const char *prefix = CFCClass_get_prefix(client); |
| const char *nickname = CFCClass_get_nickname(client); |
| for (int i = 0; i < num_wrapped_funcs; i++) { |
| const char *func = wrapped_funcs[i]; |
| short_names |
| = CFCUtil_cat(short_names, " #define ", nickname, "_", func, |
| " ", prefix, nickname, "_", func, "\n", NULL); |
| } |
| } |
| |
| if (!CFCClass_inert(client)) { |
| CFCMethod **fresh_methods = CFCClass_fresh_methods(client); |
| for (int i = 0; fresh_methods[i] != NULL; i++) { |
| CFCMethod *meth = fresh_methods[i]; |
| |
| // Implementing functions. |
| char *short_imp = CFCMethod_short_imp_func(meth, client); |
| char *full_imp = CFCMethod_imp_func(meth, client); |
| short_names = CFCUtil_cat(short_names, " #define ", short_imp, |
| " ", full_imp, "\n", NULL); |
| FREEMEM(short_imp); |
| FREEMEM(full_imp); |
| } |
| |
| CFCMethod **methods = CFCClass_methods(client); |
| for (int i = 0; methods[i] != NULL; i++) { |
| CFCMethod *meth = methods[i]; |
| static const char pattern[] = " #define %s %s\n"; |
| |
| // Method invocation symbols. |
| char *short_sym = CFCMethod_short_method_sym(meth, client); |
| char *full_sym = CFCMethod_full_method_sym(meth, client); |
| char *define_sym = CFCUtil_sprintf(pattern, short_sym, full_sym); |
| short_names = CFCUtil_cat(short_names, define_sym, NULL); |
| FREEMEM(short_sym); |
| FREEMEM(full_sym); |
| FREEMEM(define_sym); |
| |
| // Method typedefs. |
| char *short_typedef = CFCMethod_short_typedef(meth, client); |
| char *full_typedef = CFCMethod_full_typedef(meth, client); |
| char *define_typedef = CFCUtil_sprintf(pattern, short_typedef, |
| full_typedef); |
| short_names = CFCUtil_cat(short_names, define_typedef, NULL); |
| FREEMEM(short_typedef); |
| FREEMEM(full_typedef); |
| FREEMEM(define_typedef); |
| } |
| } |
| short_names = CFCUtil_cat(short_names, "#endif /* ", |
| self->short_names_macro, " */\n", NULL); |
| |
| return short_names; |
| } |
| |
| char* |
| CFCBindClass_host_data_json(CFCBindClass *self) { |
| if (CFCClass_final(self->client)) { return CFCUtil_strdup(""); } |
| |
| CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client); |
| char *methods_json = CFCUtil_strdup(""); |
| |
| for (int i = 0; fresh_methods[i] != NULL; i++) { |
| CFCMethod *method = fresh_methods[i]; |
| char *method_json = CFCBindMeth_host_data_json(method); |
| if (method_json[0] != '\0') { |
| const char *sep = methods_json[0] == '\0' ? "" : ",\n"; |
| methods_json = CFCUtil_cat(methods_json, sep, method_json, NULL); |
| } |
| FREEMEM(method_json); |
| } |
| |
| char *json; |
| |
| if (methods_json[0] == '\0') { |
| json = CFCUtil_strdup(""); |
| } |
| else { |
| const char *class_name = CFCClass_get_name(self->client); |
| |
| const char *pattern = |
| " \"%s\": {\n" |
| " \"methods\": {\n" |
| "%s\n" |
| " }\n" |
| " }"; |
| json = CFCUtil_sprintf(pattern, class_name, methods_json); |
| } |
| |
| FREEMEM(methods_json); |
| return json; |
| } |
| |