text: /* 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 "charmony.h"
  
  #include <string.h>
  #include <stdio.h>
  
  #define CFC_NEED_BASE_STRUCT_DEF
  #include "CFCBase.h"
  #include "CFCBindCore.h"
  #include "CFCBindClass.h"
  #include "CFCBindFile.h"
  #include "CFCBindSpecs.h"
  #include "CFCClass.h"
  #include "CFCFile.h"
  #include "CFCHierarchy.h"
  #include "CFCParcel.h"
  #include "CFCUtil.h"
  #include "CFCVersion.h"
  
  #define STRING(s)  #s
  #define XSTRING(s) STRING(s)
  
  struct CFCBindCore {
      CFCBase base;
      CFCHierarchy *hierarchy;
      char         *c_header;
      char         *c_footer;
  };
  
  /* Write the "parcel.h" header file, which contains common symbols needed by
   * all classes, plus typedefs for all class structs.
   */
  static void
  S_write_parcel_h(CFCBindCore *self, CFCParcel *parcel);
  
  /* Write the "parcel.c" file containing autogenerated implementation code.
   */
  static void
  S_write_parcel_c(CFCBindCore *self, CFCParcel *parcel);
  
  /* Write the "cfish_platform.h" header file, which contains platform-specific
   * definitions.
   */
  static void
  S_write_platform_h(CFCBindCore *self);
  
  static char*
  S_charmony_feature_defines();
  
  static char*
  S_charmony_string_defines();
  
  static char*
  S_charmony_stdbool_defines();
  
  static char*
  S_charmony_stdint_defines();
  
  static char*
  S_charmony_alloca_defines();
  
  static void
  S_write_host_data_json(CFCBindCore *self, CFCParcel *parcel,
                         const char *dest_dir, const char *host_lang);
  
  static const CFCMeta CFCBINDCORE_META = {
      "Clownfish::CFC::Binding::Core",
      sizeof(CFCBindCore),
      (CFCBase_destroy_t)CFCBindCore_destroy
  };
  
  CFCBindCore*
  CFCBindCore_new(CFCHierarchy *hierarchy, const char *header,
                  const char *footer) {
      CFCBindCore *self = (CFCBindCore*)CFCBase_allocate(&CFCBINDCORE_META);
      return CFCBindCore_init(self, hierarchy, header, footer);
  }
  
  CFCBindCore*
  CFCBindCore_init(CFCBindCore *self, CFCHierarchy *hierarchy,
                   const char *header, const char *footer) {
      CFCUTIL_NULL_CHECK(hierarchy);
      CFCUTIL_NULL_CHECK(header);
      CFCUTIL_NULL_CHECK(footer);
      self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy);
      self->c_header  = CFCUtil_make_c_comment(header);
      self->c_footer  = CFCUtil_make_c_comment(footer);
      return self;
  }
  
  void
  CFCBindCore_destroy(CFCBindCore *self) {
      CFCBase_decref((CFCBase*)self->hierarchy);
      FREEMEM(self->c_header);
      FREEMEM(self->c_footer);
      CFCBase_destroy((CFCBase*)self);
  }
  
  int
  CFCBindCore_write_all_modified(CFCBindCore *self, int modified) {
      CFCHierarchy *hierarchy = self->hierarchy;
      const char   *header    = self->c_header;
      const char   *footer    = self->c_footer;
  
      // Discover whether files need to be regenerated.
      modified = CFCHierarchy_propagate_modified(hierarchy, modified);
  
      // Iterate over all File objects, writing out those which don't have
      // up-to-date auto-generated files.
      const char *inc_dest = CFCHierarchy_get_include_dest(hierarchy);
      CFCFile **files = CFCHierarchy_files(hierarchy);
      for (int i = 0; files[i] != NULL; i++) {
          if (CFCFile_get_modified(files[i])) {
              CFCBindFile_write_h(files[i], inc_dest, header, footer);
          }
      }
  
      // If any class definition has changed, rewrite the parcel.h and parcel.c
      // files.
      if (modified) {
          S_write_platform_h(self);
  
          CFCParcel **parcels = CFCParcel_all_parcels();
          for (size_t i = 0; parcels[i]; ++i) {
              CFCParcel *parcel = parcels[i];
              S_write_parcel_h(self, parcel);
              if (!CFCParcel_included(parcel)) {
                  S_write_parcel_c(self, parcel);
              }
          }
      }
  
      return modified;
  }
  
  /* Write the "parcel.h" header file, which contains common symbols needed by
   * all classes, plus typedefs for all class structs.
   */
  static void
  S_write_parcel_h(CFCBindCore *self, CFCParcel *parcel) {
      CFCHierarchy *hierarchy   = self->hierarchy;
      const char   *prefix      = CFCParcel_get_prefix(parcel);
      const char   *PREFIX      = CFCParcel_get_PREFIX(parcel);
      const char   *privacy_sym = CFCParcel_get_privacy_sym(parcel);
  
      // Declare object structs and class singletons for all instantiable
      // classes.
      char *typedefs    = CFCUtil_strdup("");
      char *class_decls = CFCUtil_strdup("");
      CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy);
      for (int i = 0; ordered[i] != NULL; i++) {
          CFCClass *klass = ordered[i];
          const char *class_prefix = CFCClass_get_prefix(klass);
          if (strcmp(class_prefix, prefix) != 0) { continue; }
  
          if (!CFCClass_inert(klass)) {
              const char *full_struct = CFCClass_full_struct_sym(klass);
              typedefs = CFCUtil_cat(typedefs, "typedef struct ", full_struct,
                                     " ", full_struct, ";\n", NULL);
              const char *class_var = CFCClass_full_class_var(klass);
              class_decls = CFCUtil_cat(class_decls, "extern ", PREFIX,
                                        "VISIBLE cfish_Class *", class_var,
                                        ";\n", NULL);
          }
      }
      FREEMEM(ordered);
  
      // Special includes and macros for Clownfish parcel.
      const char *cfish_includes =
          "#include <stdarg.h>\n"
          "#include <stddef.h>\n"
          "\n"
          "#include \"cfish_platform.h\"\n"
          "#include \"cfish_hostdefs.h\"\n";
  
      // Special definitions for Clownfish parcel.
      const char *cfish_defs_1 =
          "#define CFISH_UNUSED_VAR(var) ((void)var)\n"
          "#define CFISH_UNREACHABLE_RETURN(type) return (type)0\n"
          "\n"
          "/* Generic method pointer.\n"
          " */\n"
          "typedef void\n"
          "(*cfish_method_t)(const void *vself);\n"
          "\n"
          "/* Access the function pointer for a given method from the class.\n"
          " */\n"
          "#define CFISH_METHOD_PTR(_class, _full_meth) \\\n"
          "     ((_full_meth ## _t)cfish_method(_class, _full_meth ## _OFFSET))\n"
          "\n"
          "static CFISH_INLINE cfish_method_t\n"
          "cfish_method(const void *klass, uint32_t offset) {\n"
          "    union { char *cptr; cfish_method_t *fptr; } ptr;\n"
          "    ptr.cptr = (char*)klass + offset;\n"
          "    return ptr.fptr[0];\n"
          "}\n"
          "\n"
          "typedef struct cfish_Dummy {\n"
          "   CFISH_OBJ_HEAD\n"
          "   void *klass;\n"
          "} cfish_Dummy;\n"
          "\n"
          "/* Access the function pointer for a given method from the object.\n"
          " */\n"
          "static CFISH_INLINE cfish_method_t\n"
          "cfish_obj_method(const void *object, uint32_t offset) {\n"
          "    cfish_Dummy *dummy = (cfish_Dummy*)object;\n"
          "    return cfish_method(dummy->klass, offset);\n"
          "}\n"
          "\n"
          "/* Access the function pointer for the given method in the\n"
          " * superclass. */\n"
          "#define CFISH_SUPER_METHOD_PTR(_class, _full_meth) \\\n"
          "     ((_full_meth ## _t)cfish_super_method(_class, \\\n"
          "                                           _full_meth ## _OFFSET))\n"
          "\n"
          "extern CFISH_VISIBLE uint32_t cfish_Class_offset_of_parent;\n"
          "static CFISH_INLINE cfish_method_t\n"
          "cfish_super_method(const void *klass, uint32_t offset) {\n"
          "    char *class_as_char = (char*)klass;\n"
          "    cfish_Class **parent_ptr\n"
          "        = (cfish_Class**)(__builtin_assume_aligned(\n"
          "class_as_char+cfish_Class_offset_of_parent, sizeof(cfish_Class*)));\n"
          "    return cfish_method(*parent_ptr, offset);\n"
          "}\n"
          "\n"
          "typedef void\n"
          "(*cfish_destroy_t)(void *vself);\n"
          "extern CFISH_VISIBLE uint32_t CFISH_Obj_Destroy_OFFSET;\n"
          "\n"
          "/** Invoke the [](.Destroy) method found in `klass` on\n"
          " * `self`.\n"
          " *\n"
          " * TODO: Eliminate this function if we can arrive at a proper SUPER syntax.\n"
          " */\n"
          "static CFISH_INLINE void\n"
          "cfish_super_destroy(void *vself, cfish_Class *klass) {\n"
          "    cfish_Obj *self = (cfish_Obj*)vself;\n"
          "    if (self != NULL) {\n"
          "        cfish_destroy_t super_destroy\n"
          "            = (cfish_destroy_t)cfish_super_method(klass, CFISH_Obj_Destroy_OFFSET);\n"
          "        super_destroy(self);\n"
          "    }\n"
          "}\n"
          "\n"
          "#define CFISH_SUPER_DESTROY(_self, _class) \\\n"
          "    cfish_super_destroy(_self, _class)\n"
          "\n"
          "extern CFISH_VISIBLE cfish_Obj*\n"
          "cfish_inc_refcount(void *vself);\n"
          "\n"
          "/** NULL-safe invocation invocation of `cfish_inc_refcount`.\n"
          " *\n"
          " * @return NULL if `self` is NULL, otherwise the return value\n"
          " * of `cfish_inc_refcount`.\n"
          " */\n"
          "static CFISH_INLINE cfish_Obj*\n"
          "cfish_incref(void *vself) {\n"
          "    if (vself != NULL) { return cfish_inc_refcount(vself); }\n"
          "    else { return NULL; }\n"
          "}\n"
          "\n"
          "#define CFISH_INCREF(_self) cfish_incref(_self)\n"
          "#define CFISH_INCREF_NN(_self) cfish_inc_refcount(_self)\n"
          "\n"
          "extern CFISH_VISIBLE uint32_t\n"
          "cfish_dec_refcount(void *vself);\n"
          "\n"
          "/** NULL-safe invocation of `cfish_dec_refcount`.\n"
          " *\n"
          " * @return NULL if `self` is NULL, otherwise the return value\n"
          " * of `cfish_dec_refcount`.\n"
          " */\n"
          "static CFISH_INLINE uint32_t\n"
          "cfish_decref(void *vself) {\n"
          "    if (vself != NULL) { return cfish_dec_refcount(vself); }\n"
          "    else { return 0; }\n"
          "}\n"
          "\n"
          "#define CFISH_DECREF(_self) cfish_decref(_self)\n"
          "#define CFISH_DECREF_NN(_self) cfish_dec_refcount(_self)\n"
          "\n"
          "extern CFISH_VISIBLE uint32_t\n"
          "cfish_get_refcount(void *vself);\n"
          "\n"
          "#define CFISH_REFCOUNT_NN(_self) \\\n"
          "    cfish_get_refcount(_self)\n"
          "\n"
          "/* Flags for internal use. */\n"
          "#define CFISH_fREFCOUNTSPECIAL 0x00000001\n"
          "#define CFISH_fFINAL           0x00000002\n"
          ;
      const char *cfish_defs_2 =
          "#ifdef CFISH_USE_SHORT_NAMES\n"
          "  #define UNUSED_VAR               CFISH_UNUSED_VAR\n"
          "  #define UNREACHABLE_RETURN       CFISH_UNREACHABLE_RETURN\n"
          "  #define METHOD_PTR               CFISH_METHOD_PTR\n"
          "  #define SUPER_METHOD_PTR         CFISH_SUPER_METHOD_PTR\n"
          "  #define SUPER_DESTROY(_self, _class) CFISH_SUPER_DESTROY(_self, _class)\n"
          "  #define INCREF(_self)                CFISH_INCREF(_self)\n"
          "  #define INCREF_NN(_self)             CFISH_INCREF_NN(_self)\n"
          "  #define DECREF(_self)                CFISH_DECREF(_self)\n"
          "  #define DECREF_NN(_self)             CFISH_DECREF_NN(_self)\n"
          "  #define REFCOUNT_NN(_self)           CFISH_REFCOUNT_NN(_self)\n"
          "#endif\n"
          "\n";
  
      char *extra_defs;
      char *extra_includes;
      if (CFCParcel_is_cfish(parcel)) {
          const char *spec_typedefs = CFCBindSpecs_get_typedefs();
          extra_defs = CFCUtil_sprintf("%s%s%s", cfish_defs_1, spec_typedefs,
                                       cfish_defs_2);
          extra_includes = CFCUtil_strdup(cfish_includes);
      }
      else {
          extra_defs = CFCUtil_strdup("");
          extra_includes = CFCUtil_strdup("");
  
          // Include parcel.h of prerequisite parcels.
          CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(parcel);
          for (size_t i = 0; prereq_parcels[i]; ++i) {
              const char *prereq_prefix
                  = CFCParcel_get_prefix(prereq_parcels[i]);
              extra_includes = CFCUtil_cat(extra_includes, "#include \"",
                                           prereq_prefix, "parcel.h\"\n", NULL);
          }
          FREEMEM(prereq_parcels);
      }
  
      const char pattern[] =
          "%s\n"
          "#ifndef CFISH_%sPARCEL_H\n"
          "#define CFISH_%sPARCEL_H 1\n"
          "\n"
          "#ifdef __cplusplus\n"
          "extern \"C\" {\n"
          "#endif\n"
          "\n"
          "%s" // Extra includes.
          "\n"
          "#ifdef %s\n"
          "  #define %sVISIBLE CFISH_EXPORT\n"
          "#else\n"
          "  #define %sVISIBLE CFISH_IMPORT\n"
          "#endif\n"
          "\n"
          "%s" // Typedefs.
          "\n"
          "%s" // Class singletons.
          "\n"
          "%s" // Extra definitions.
          "%sVISIBLE void\n"
          "%sbootstrap_internal(int force);\n"
          "\n"
          "%sVISIBLE void\n"
          "%sbootstrap_parcel(void);\n"
          "\n"
          "void\n"
          "%sinit_parcel(void);\n"
          "\n"
          "#ifdef __cplusplus\n"
          "}\n"
          "#endif\n"
          "\n"
          "#endif /* CFISH_%sPARCEL_H */\n"
          "\n"
          "%s\n"
          "\n";
      char *file_content
          = CFCUtil_sprintf(pattern, self->c_header, PREFIX, PREFIX,
                            extra_includes, privacy_sym, PREFIX, PREFIX,
                            typedefs, class_decls, extra_defs, PREFIX, prefix,
                            PREFIX, prefix, prefix, PREFIX, self->c_footer);
  
      // Unlink then write file.
      const char *inc_dest = CFCHierarchy_get_include_dest(hierarchy);
      char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sparcel.h", inc_dest,
                                       prefix);
      remove(filepath);
      CFCUtil_write_file(filepath, file_content, strlen(file_content));
      FREEMEM(filepath);
  
      FREEMEM(typedefs);
      FREEMEM(class_decls);
      FREEMEM(extra_defs);
      FREEMEM(extra_includes);
      FREEMEM(file_content);
  }
  
  static void
  S_write_parcel_c(CFCBindCore *self, CFCParcel *parcel) {
      CFCHierarchy *hierarchy = self->hierarchy;
      const char   *prefix    = CFCParcel_get_prefix(parcel);
  
      // Aggregate C code for the parcel.
      char *privacy_syms = CFCUtil_strdup("");
      char *includes     = CFCUtil_strdup("");
      char *c_data       = CFCUtil_strdup("");
      CFCBindSpecs *specs = CFCBindSpecs_new();
      CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy);
  
      for (int i = 0; ordered[i] != NULL; i++) {
          CFCClass *klass = ordered[i];
          const char *class_prefix = CFCClass_get_prefix(klass);
          if (strcmp(class_prefix, prefix) != 0) { continue; }
  
          const char *include_h = CFCClass_include_h(klass);
          includes = CFCUtil_cat(includes, "#include \"", include_h,
                                 "\"\n", NULL);
  
          CFCBindClass *class_binding = CFCBindClass_new(klass);
  
          char *class_c_data = CFCBindClass_to_c_data(class_binding);
          c_data = CFCUtil_cat(c_data, class_c_data, "\n", NULL);
          FREEMEM(class_c_data);
  
          CFCBindSpecs_add_class(specs, klass);
  
          const char *privacy_sym = CFCClass_privacy_symbol(klass);
          privacy_syms = CFCUtil_cat(privacy_syms, "#define ",
                                     privacy_sym, "\n", NULL);
  
          CFCBase_decref((CFCBase*)class_binding);
      }
  
      char *spec_defs      = CFCBindSpecs_defs(specs);
      char *spec_init_func = CFCBindSpecs_init_func_def(specs);
      FREEMEM(ordered);
  
      char *prereq_bootstrap = CFCUtil_strdup("");
      CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(parcel);
      for (size_t i = 0; prereq_parcels[i]; ++i) {
          const char *prereq_prefix = CFCParcel_get_prefix(prereq_parcels[i]);
          prereq_bootstrap = CFCUtil_cat(prereq_bootstrap, "    ", prereq_prefix,
                                         "bootstrap_internal(0);\n", NULL);
      }
      FREEMEM(prereq_parcels);
  
      char pattern[] =
          "%s\n"
          "\n"
          "#include <stdio.h>\n"
          "#include <stdlib.h>\n"
          "\n"
          "%s"
          "\n"
          "#include \"Clownfish/Class.h\"\n"   // Needed for bootstrap.
          "#include \"Clownfish/Err.h\"\n"     // Needed for abstract methods.
          "%s\n"
          "\n"
          "%s\n"
          "\n"
          "/* ClassSpec and MethSpec structs for initialization.\n"
          " */\n"
          "\n"
          "%s" // spec_defs
          "\n"
          "/* Code to initialize ClassSpec and MethSpec structs.\n"
          " */\n"
          "\n"
          "%s" // spec_init_func
          "\n"
          "void\n"
          "%sbootstrap_internal(int force) {\n"
          "    static int bootstrapped = 0;\n"
          "    if (bootstrapped && !force) { return; }\n"
          "    S_bootstrap_specs();\n"
          "    %sinit_parcel();\n"
          "    bootstrapped = 1;\n"
          "}\n"
          "\n"
          "void\n"
          "%sbootstrap_parcel() {\n"
          "%s" // Bootstrap prerequisite parcels.
          "    %sbootstrap_internal(0);\n"
          "}\n"
          "\n"
          "%s\n";
      char *file_content
          = CFCUtil_sprintf(pattern, self->c_header, privacy_syms, includes,
                            c_data, spec_defs, spec_init_func, prefix, prefix,
                            prefix, prereq_bootstrap, prefix, self->c_footer);
  
      // Unlink then open file.
      const char *src_dest = CFCHierarchy_get_source_dest(hierarchy);
      char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sparcel.c", src_dest,
                                       prefix);
      remove(filepath);
      CFCUtil_write_file(filepath, file_content, strlen(file_content));
      FREEMEM(filepath);
  
      CFCBase_decref((CFCBase*)specs);
      FREEMEM(privacy_syms);
      FREEMEM(includes);
      FREEMEM(c_data);
      FREEMEM(spec_defs);
      FREEMEM(spec_init_func);
      FREEMEM(prereq_bootstrap);
      FREEMEM(file_content);
  }
  
  /* Write the "cfish_platform.h" header file, which contains platform-specific
   * definitions.
   */
  static void
  S_write_platform_h(CFCBindCore *self) {
      char *feature_defs = S_charmony_feature_defines();
      char *string_defs  = S_charmony_string_defines();
      char *stdbool_defs = S_charmony_stdbool_defines();
      char *stdint_defs  = S_charmony_stdint_defines();
      char *alloca_defs  = S_charmony_alloca_defines();
  
      const char pattern[] =
          "%s"
          "\n"
          "#ifndef CFISH_PLATFORM_H\n"
          "#define CFISH_PLATFORM_H 1\n"
          "\n"
          "#ifdef __cplusplus\n"
          "extern \"C\" {\n"
          "#endif\n"
          "\n"
          "%s"
          "%s"
          "\n"
          "%s"
          "%s"
          "\n"
          "%s"
          "\n"
          "#ifdef __cplusplus\n"
          "}\n"
          "#endif\n"
          "\n"
          "#endif /* CFISH_PLATFORM_H */\n"
          "\n"
          "%s"
          "\n";
      char *file_content
          = CFCUtil_sprintf(pattern, self->c_header, feature_defs, string_defs,
                            stdbool_defs, stdint_defs, alloca_defs,
                            self->c_footer);
  
      // Unlink then write file.
      const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy);
      char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_platform.h",
                                       inc_dest);
      remove(filepath);
      CFCUtil_write_file(filepath, file_content, strlen(file_content));
      FREEMEM(filepath);
  
      FREEMEM(feature_defs);
      FREEMEM(string_defs);
      FREEMEM(stdbool_defs);
      FREEMEM(stdint_defs);
      FREEMEM(alloca_defs);
      FREEMEM(file_content);
  }
  
  static char*
  S_charmony_feature_defines() {
      char *defines = CFCUtil_strdup("");
  
  #ifdef CHY_LITTLE_END
      // Needed by NumberUtils.cfh.
      defines = CFCUtil_cat(defines, "#define CFISH_LITTLE_END\n", NULL);
  #endif
  #ifdef CHY_BIG_END
      // Needed by NumberUtils.cfh.
      defines = CFCUtil_cat(defines, "#define CFISH_BIG_END\n", NULL);
  #endif
  #ifdef CHY_HAS_FUNC_MACRO
      // Needed by Err.cfh.
      defines = CFCUtil_cat(defines, "#define CFISH_HAS_FUNC_MACRO\n", NULL);
  #endif
  #ifdef CHY_HAS_VARIADIC_MACROS
      // Needed by Err.cfh.
      defines = CFCUtil_cat(defines, "#define CFISH_HAS_VARIADIC_MACROS\n",
                            NULL);
  #endif
  #ifdef CHY_HAS_ISO_VARIADIC_MACROS
      // Needed by Err.cfh.
      defines = CFCUtil_cat(defines, "#define CFISH_HAS_ISO_VARIADIC_MACROS\n",
                            NULL);
  #endif
  #ifdef CHY_HAS_GNUC_VARIADIC_MACROS
      // Needed by Err.cfh.
      defines = CFCUtil_cat(defines, "#define CFISH_HAS_GNUC_VARIADIC_MACROS\n",
                            NULL);
  #endif
  
      return defines;
  }
  
  static char*
  S_charmony_string_defines() {
      const char *pattern =
          "#define CFISH_INLINE %s\n"
          "#define CFISH_EXPORT %s\n"
          "#define CFISH_IMPORT %s\n"
          "#define CFISH_SIZEOF_CHAR %s\n"
          "#define CFISH_SIZEOF_SHORT %s\n"
          "#define CFISH_SIZEOF_INT %s\n"
          "#define CFISH_SIZEOF_LONG %s\n"
          "#define CFISH_SIZEOF_SIZE_T %s\n"
          "#define CFISH_FUNC_MACRO %s\n"
          "#define CFISH_U64_TO_DOUBLE(x) %s\n";
      char *defines
          = CFCUtil_sprintf(pattern,
                            XSTRING(CHY_INLINE),
                            XSTRING(CHY_EXPORT),
                            XSTRING(CHY_IMPORT),
                            XSTRING(CHY_SIZEOF_CHAR),
                            XSTRING(CHY_SIZEOF_SHORT),
                            XSTRING(CHY_SIZEOF_INT),
                            XSTRING(CHY_SIZEOF_LONG),
                            XSTRING(CHY_SIZEOF_SIZE_T),
                            XSTRING(CHY_FUNC_MACRO),
                            XSTRING(CHY_U64_TO_DOUBLE(x)));
  
      return defines;
  }
  
  static char*
  S_charmony_stdbool_defines() {
  #ifdef CHY_HAS_STDBOOL_H
      const char *defines = "#include <stdbool.h>\n";
  #else
      const char *defines =
          "#if (!defined(__cplusplus) && !defined(CFISH_HAS_STDBOOL))\n"
          "  typedef int bool;\n"
          "  #ifndef true\n"
          "    #define true 1\n"
          "  #endif\n"
          "  #ifndef false\n"
          "    #define false 0\n"
          "  #endif\n"
          "#endif\n";
  #endif
  
      return CFCUtil_strdup(defines);
  }
  
  static char*
  S_charmony_stdint_defines() {
  #ifdef CHY_HAS_STDINT_H
      return CFCUtil_strdup("#include <stdint.h>\n");
  #else
      const char *pattern =
          "#ifndef CFISH_HAS_STDINT\n"
          "  typedef %s int8_t;\n"
          "  typedef %s uint8_t;\n"
          "  typedef %s int16_t;\n"
          "  typedef %s uint16_t;\n"
          "  typedef %s int32_t;\n"
          "  typedef %s uint32_t;\n"
          "  typedef %s int64_t;\n"
          "  typedef %s uint64_t;\n"
          "#endif\n";
      return CFCUtil_sprintf(pattern,
                             XSTRING(CHY_INT8_T),  XSTRING(CHY_UINT8_T),
                             XSTRING(CHY_INT16_T), XSTRING(CHY_UINT16_T),
                             XSTRING(CHY_INT32_T), XSTRING(CHY_UINT32_T),
                             XSTRING(CHY_INT64_T), XSTRING(CHY_UINT64_T));
  #endif
  }
  
  static char*
  S_charmony_alloca_defines() {
      char *defines = CFCUtil_strdup("");
  
  #if defined(CHY_HAS_ALLOCA_H)
      defines = CFCUtil_cat(defines, "#include <alloca.h>\n", NULL);
  #elif defined(CHY_HAS_MALLOC_H)
      defines = CFCUtil_cat(defines, "#include <malloc.h>\n", NULL);
  #elif defined(CHY_ALLOCA_IN_STDLIB_H)
      defines = CFCUtil_cat(defines, "#include <stdlib.h>\n", NULL);
  #endif
  
      defines = CFCUtil_cat(defines, "#define cfish_alloca ",
                            XSTRING(chy_alloca), "\n", NULL);
  
      return defines;
  }
  
  void
  CFCBindCore_copy_headers(CFCBindCore *self, const char *dest_dir) {
      char *default_dest = NULL;
  
      if (dest_dir == NULL || dest_dir[0] == '\0') {
          default_dest = CFCUtil_sprintf("%s" CHY_DIR_SEP "share" CHY_DIR_SEP
                                         "clownfish" CHY_DIR_SEP "include",
                                         CFCHierarchy_get_dest(self->hierarchy));
          dest_dir = default_dest;
      }
  
      /* Copy .cfp files. */
      CFCParcel **parcels = CFCParcel_all_parcels();
      for (size_t i = 0; parcels[i] != NULL; i++) {
          CFCParcel *parcel = parcels[i];
          if (CFCParcel_included(parcel) || !CFCParcel_is_installed(parcel)) {
              continue;
          }
  
          const char *source_path = CFCParcel_get_cfp_path(parcel);
          const char *parcel_name = CFCParcel_get_name(parcel);
          CFCVersion *version     = CFCParcel_get_version(parcel);
          const char *vstring     = CFCVersion_get_vstring(version);
  
          char *dest_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s" CHY_DIR_SEP
                                            "%s" CHY_DIR_SEP "parcel.json",
                                            dest_dir, parcel_name, vstring);
  
          size_t len = 0;
          char *content = CFCUtil_slurp_text(source_path, &len);
          CFCUtil_write_file(dest_path, content, len);
  
          FREEMEM(content);
          FREEMEM(dest_path);
      }
  
      /* Copy .cfh files. */
      CFCFile **files = CFCHierarchy_files(self->hierarchy);
      for (size_t i = 0; files[i] != NULL; i++) {
          CFCFile *file = files[i];
          if (CFCFile_included(file)) { continue; }
          CFCParcel *parcel = CFCFile_get_parcel(file);
          if (!CFCParcel_is_installed(parcel)) { continue; }
  
          const char *source_path = CFCFile_get_path(file);
          const char *parcel_name = CFCParcel_get_name(parcel);
          CFCVersion *version     = CFCParcel_get_version(parcel);
          const char *vstring     = CFCVersion_get_vstring(version);
          const char *path_part   = CFCFile_get_path_part(file);
  
          char *dest_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s" CHY_DIR_SEP
                                            "%s" CHY_DIR_SEP "%s.cfh", dest_dir,
                                            parcel_name, vstring, path_part);
  
          size_t len = 0;
          char *content = CFCUtil_slurp_text(source_path, &len);
          CFCUtil_write_file(dest_path, content, len);
  
          FREEMEM(content);
          FREEMEM(dest_path);
      }
  
      FREEMEM(default_dest);
  }
  
  void
  CFCBindCore_write_host_data_json(CFCBindCore *self, const char *dest_dir,
                                   const char *host_lang) {
      CFCParcel **parcels = CFCParcel_all_parcels();
  
      for (size_t i = 0; parcels[i] != NULL; i++) {
          CFCParcel *parcel = parcels[i];
          if (!CFCParcel_included(parcel) && CFCParcel_is_installed(parcel)) {
              S_write_host_data_json(self, parcel, dest_dir, host_lang);
          }
      }
  }
  
  static void
  S_write_host_data_json(CFCBindCore *self, CFCParcel *parcel,
                         const char *dest_dir, const char *host_lang) {
      const char *prefix      = CFCParcel_get_prefix(parcel);
      const char *parcel_name = CFCParcel_get_name(parcel);
      CFCVersion *version     = CFCParcel_get_version(parcel);
      const char *vstring     = CFCVersion_get_vstring(version);
      char       *json_pairs  = CFCUtil_strdup("");
  
      const char *host_module_name = CFCParcel_get_host_module_name(parcel);
      if (host_module_name != NULL) {
          const char *pattern = "    \"host_module\": \"%s\"";
          char *pair = CFCUtil_sprintf(pattern, host_module_name);
          json_pairs = CFCUtil_cat(json_pairs, pair, NULL);
          FREEMEM(pair);
      }
  
      char *classes_json = CFCUtil_strdup("");
      CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
  
      for (size_t i = 0; ordered[i] != NULL; i++) {
          CFCClass *klass = ordered[i];
          const char *class_prefix = CFCClass_get_prefix(klass);
          if (strcmp(class_prefix, prefix) != 0) { continue; }
  
          CFCBindClass *class_binding = CFCBindClass_new(klass);
  
          char *class_json = CFCBindClass_host_data_json(class_binding);
          if (class_json[0] != '\0') {
              const char *sep = classes_json[0] == '\0' ? "" : ",\n";
              classes_json = CFCUtil_cat(classes_json, sep, class_json, NULL);
          }
  
          FREEMEM(class_json);
          CFCBase_decref((CFCBase*)class_binding);
      }
      FREEMEM(ordered);
  
      if (classes_json[0] != '\0') {
          const char *pattern =
              "    \"classes\": {\n"
              "%s\n"
              "    }";
          char *pair = CFCUtil_sprintf(pattern, classes_json);
          const char *sep = json_pairs[0] == '\0' ? "" : ",\n";
          json_pairs = CFCUtil_cat(json_pairs, sep, pair, NULL);
          FREEMEM(pair);
      }
  
      char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s" CHY_DIR_SEP "%s"
                                       CHY_DIR_SEP "parcel_%s.json", dest_dir,
                                       parcel_name, vstring, host_lang);
      remove(filepath);
  
      if (json_pairs[0] != '\0') {
          const char *pattern =
              "{\n"
              "%s\n"
              "}\n";
          char *json = CFCUtil_sprintf(pattern, json_pairs);
          CFCUtil_write_file(filepath, json, strlen(json));
          FREEMEM(json);
      }
  
      FREEMEM(filepath);
      FREEMEM(classes_json);
      FREEMEM(json_pairs);
  }
  
  
  