Ejemplo n.º 1
0
static char*
S_obj_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);
    const char *nullable  = CFCType_nullable(return_type) ? "true" : "false";

    char pattern[] =
        "%s\n"
        "%s(%s) {\n"
        "%s"
        "    %s retval = (%s)S_finish_callback_obj(self, \"%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,
                          micro_sym, nullable, refcount_mods);

    return callback_def;
}
Ejemplo n.º 2
0
static void
S_write_callbacks_c(CFCPerl *self) {
    CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
    static const char pattern[] =
        "%s"
        "\n"
        "#include \"XSBind.h\"\n"
        "#include \"callbacks.h\"\n"
        "\n"
        "static void\n"
        "S_finish_callback_void(const char *meth_name) {\n"
        "    int count = call_method(meth_name, G_VOID | G_DISCARD);\n"
        "    if (count != 0) {\n"
        "        CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n"
        "                    meth_name, (int32_t)count);\n"
        "    }\n"
        "    FREETMPS;\n"
        "    LEAVE;\n"
        "}\n"
        "\n"
        "static CFISH_INLINE SV*\n"
        "SI_do_callback_sv(const char *meth_name) {\n"
        "    int count = call_method(meth_name, G_SCALAR);\n"
        "    if (count != 1) {\n"
        "        CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n"
        "                    meth_name, (int32_t)count);\n"
        "    }\n"
        "    dSP;\n"
        "    SV *return_sv = POPs;\n"
        "    PUTBACK;\n"
        "    return return_sv;\n"
        "}\n"
        "\n"
        "static int64_t\n"
        "S_finish_callback_i64(const char *meth_name) {\n"
        "    SV *return_sv = SI_do_callback_sv(meth_name);\n"
        "    int64_t retval;\n"
        "    if (sizeof(IV) == 8) {\n"
        "        retval = (int64_t)SvIV(return_sv);\n"
        "    }\n"
        "    else {\n"
        "        if (SvIOK(return_sv)) {\n"
        "            // It's already no more than 32 bits, so don't convert.\n"
        "            retval = SvIV(return_sv);\n"
        "        }\n"
        "        else {\n"
        "            // Maybe lossy.\n"
        "            double temp = SvNV(return_sv);\n"
        "            retval = (int64_t)temp;\n"
        "        }\n"
        "    }\n"
        "    FREETMPS;\n"
        "    LEAVE;\n"
        "    return retval;\n"
        "}\n"
        "\n"
        "static double\n"
        "S_finish_callback_f64(const char *meth_name) {\n"
        "    SV *return_sv = SI_do_callback_sv(meth_name);\n"
        "    double retval = SvNV(return_sv);\n"
        "    FREETMPS;\n"
        "    LEAVE;\n"
        "    return retval;\n"
        "}\n"
        "\n"
        "static cfish_Obj*\n"
        "S_finish_callback_obj(void *vself, const char *meth_name,\n"
        "                      int nullable) {\n"
        "    SV *return_sv = SI_do_callback_sv(meth_name);\n"
        "    cfish_Obj *retval = XSBind_perl_to_cfish(return_sv);\n"
        "    FREETMPS;\n"
        "    LEAVE;\n"
        "    if (!nullable && !retval) {\n"
        "        CFISH_THROW(CFISH_ERR, \"%%o#%%s cannot return NULL\",\n"
        "                    CFISH_Obj_Get_Class_Name((cfish_Obj*)vself),\n"
        "                    meth_name);\n"
        "    }\n"
        "    return retval;\n"
        "}\n"
        "\n";
    char *content = CFCUtil_sprintf(pattern, self->header);

    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_inert(klass)) { continue; }

        CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
        for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
            CFCMethod *method = fresh_methods[meth_num];

            // Define callback.
            if (CFCMethod_novel(method) && !CFCMethod_final(method)) {
                char *cb_def = CFCPerlMethod_callback_def(method);
                content = CFCUtil_cat(content, cb_def, "\n", NULL);
                FREEMEM(cb_def);
            }
        }
        FREEMEM(fresh_methods);
    }

    content = CFCUtil_cat(content, self->footer, NULL);

    // Write if changed.
    const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy);
    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "callbacks.c",
                                     src_dest);
    CFCUtil_write_if_changed(filepath, content, strlen(content));

    FREEMEM(filepath);
    FREEMEM(content);
    FREEMEM(ordered);
}
Ejemplo n.º 3
0
static char*
S_method_def(CFCMethod *method, CFCClass *klass, int optimized_final_meth) {
    CFCParamList *param_list = CFCMethod_get_param_list(method);
    const char *PREFIX         = CFCClass_get_PREFIX(klass);
    const char *invoker_struct = CFCClass_full_struct_sym(klass);
    const char *self_name      = CFCParamList_param_name(param_list, 0);

    char *full_meth_sym   = CFCMethod_full_method_sym(method, klass);
    char *full_offset_sym = CFCMethod_full_offset_sym(method, klass);
    char *full_typedef    = CFCMethod_full_typedef(method, klass);
    char *full_imp_sym    = CFCMethod_imp_func(method, klass);

    // Prepare parameter lists, minus the type of the invoker.
    if (CFCParamList_variadic(param_list)) {
        CFCUtil_die("Variadic methods not supported");
    }
    const char *arg_names  = CFCParamList_name_list(param_list);
    const char *params_end = CFCParamList_to_c(param_list);
    while (*params_end && *params_end != '*') {
        params_end++;
    }

    // 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 innards_pattern[] =
        "    const %s method = (%s)cfish_obj_method(%s, %s);\n"
        "    %smethod(%s);\n"
        ;
    char *innards = CFCUtil_sprintf(innards_pattern, full_typedef,
                                    full_typedef, self_name, full_offset_sym,
                                    maybe_return, arg_names);
    if (optimized_final_meth) {
        CFCParcel  *parcel = CFCClass_get_parcel(klass);
        const char *privacy_sym = CFCParcel_get_privacy_sym(parcel);
        char *invoker_cast = CFCUtil_strdup("");
        if (!CFCMethod_is_fresh(method, klass)) {
            CFCType *self_type = CFCMethod_self_type(method);
            invoker_cast = CFCUtil_cat(invoker_cast, "(",
                                       CFCType_to_c(self_type), ")", NULL);
        }
        const char pattern[] =
            "#ifdef %s\n"
            "    %s%s(%s%s);\n"
            "#else\n"
            "%s"
            "#endif\n"
            ;
        char *temp = CFCUtil_sprintf(pattern, privacy_sym,
                                     maybe_return, full_imp_sym,
                                     invoker_cast, arg_names, innards);
        FREEMEM(innards);
        innards = temp;
        FREEMEM(invoker_cast);
    }

    const char pattern[] =
        "extern %sVISIBLE uint32_t %s;\n"
        "static CFISH_INLINE %s\n"
        "%s(%s%s) {\n"
        "%s"
        "}\n";
    char *method_def
        = CFCUtil_sprintf(pattern, PREFIX, full_offset_sym, ret_type_str,
                          full_meth_sym, invoker_struct, params_end, innards);

    FREEMEM(innards);
    FREEMEM(full_imp_sym);
    FREEMEM(full_offset_sym);
    FREEMEM(full_meth_sym);
    FREEMEM(full_typedef);
    return method_def;
}
Ejemplo n.º 4
0
static void
S_add_load_method(CFCClass *klass) {
    CFCMethod *method = S_make_method_obj(klass, "Load");
    CFCClass_add_method(klass, method);
    CFCBase_decref((CFCBase*)method);
    const char *full_func_sym = CFCMethod_implementing_func_sym(method);
    const char *full_struct   = CFCClass_full_struct_sym(klass);
    const char *vtable_var    = CFCClass_full_vtable_var(klass);
    CFCClass   *parent        = CFCClass_get_parent(klass);
    const char *prefix        = CFCClass_get_prefix(klass);
    const char *class_cnick   = CFCClass_get_cnick(klass);
    char buf[BUF_SIZE];

    if (parent && CFCClass_has_attribute(parent, "dumpable")) {
        char *full_typedef = CFCMethod_full_typedef(method, klass);
        char *full_meth    = CFCMethod_full_method_sym(method, klass);

        const char pattern[] =
            "cfish_Obj*\n"
            "%s(%s *self, cfish_Obj *dump)\n"
            "{\n"
            "    cfish_Hash *source = (cfish_Hash*)CFISH_CERTIFY(dump, CFISH_HASH);\n"
            "    %s super_load = CFISH_SUPER_METHOD_PTR(%s, %s);\n"
            "    %s *loaded = (%s*)super_load(self, dump);\n"
            "    %sIVARS *ivars = %s%s_IVARS(loaded);\n";
        char *autocode
            = CFCUtil_sprintf(pattern, full_func_sym, full_struct,
                              full_typedef, vtable_var, full_meth, full_struct,
                              full_struct, full_struct, prefix, class_cnick);
        CFCClass_append_autocode(klass, autocode);
        FREEMEM(full_meth);
        FREEMEM(full_typedef);
        FREEMEM(autocode);

        CFCVariable **fresh = CFCClass_fresh_member_vars(klass);
        for (size_t i = 0; fresh[i] != NULL; i++) {
            S_process_load_member(klass, fresh[i], buf, BUF_SIZE);
        }
        FREEMEM(fresh);
    }
    else {
        const char pattern[] =
            "cfish_Obj*\n"
            "%s(%s *self, cfish_Obj *dump)\n"
            "{\n"
            "    cfish_Hash *source = (cfish_Hash*)CFISH_CERTIFY(dump, CFISH_HASH);\n"
            "    cfish_CharBuf *class_name = (cfish_CharBuf*)CFISH_CERTIFY(\n"
            "        Cfish_Hash_Fetch_Str(source, \"_class\", 6), CFISH_CHARBUF);\n"
            "    cfish_VTable *vtable = cfish_VTable_singleton(class_name, NULL);\n"
            "    %s *loaded = (%s*)Cfish_VTable_Make_Obj(vtable);\n"
            "    %sIVARS *ivars = %s%s_IVARS(loaded);\n"
            "    CHY_UNUSED_VAR(self);\n";
        char *autocode
            = CFCUtil_sprintf(pattern, full_func_sym, full_struct, full_struct,
                              full_struct,
                              full_struct, prefix, class_cnick);
        CFCClass_append_autocode(klass, autocode);
        FREEMEM(autocode);
        CFCVariable **members = CFCClass_member_vars(klass);
        for (size_t i = 0; members[i] != NULL; i++) {
            S_process_load_member(klass, members[i], buf, BUF_SIZE);
        }
    }

    CFCClass_append_autocode(klass, "    return (cfish_Obj*)loaded;\n}\n\n");
}
Ejemplo n.º 5
0
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);

    // Bootstrapping code for prerequisite parcels.
    //
    // bootstrap_inheritance() first calls bootstrap_inheritance() for all
    // parcels from which classes are inherited. Then the Classes of the parcel
    // are initialized. It aborts on recursive invocation.
    //
    // bootstrap_parcel() first calls bootstrap_inheritance() of its own
    // parcel. Then it calls bootstrap_parcel() for all prerequisite parcels.
    // Finally, it calls init_parcel(). Recursive invocation is allowed.

    char *inh_bootstrap    = CFCUtil_strdup("");
    char *prereq_bootstrap = CFCUtil_strdup("");
    CFCParcel **inh_parcels = CFCParcel_inherited_parcels(parcel);
    for (size_t i = 0; inh_parcels[i]; ++i) {
        const char *inh_prefix = CFCParcel_get_prefix(inh_parcels[i]);
        inh_bootstrap = CFCUtil_cat(inh_bootstrap, "    ", inh_prefix,
                                    "bootstrap_inheritance();\n", NULL);
    }
    FREEMEM(inh_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]);
        prereq_bootstrap = CFCUtil_cat(prereq_bootstrap, "    ", prereq_prefix,
                                       "bootstrap_parcel();\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"
        "static int bootstrap_state = 0;\n"
        "\n"
        "void\n"
        "%sbootstrap_inheritance() {\n"
        "    if (bootstrap_state == 1) {\n"
        "        fprintf(stderr, \"Cycle in class inheritance between\"\n"
        "                        \" parcels detected.\\n\");\n"
        "        abort();\n"
        "    }\n"
        "    if (bootstrap_state >= 2) { return; }\n"
        "    bootstrap_state = 1;\n"
        "%s" // Bootstrap inherited parcels.
        "    S_bootstrap_specs();\n"
        "    bootstrap_state = 2;\n"
        "}\n"
        "\n"
        "void\n"
        "%sbootstrap_parcel() {\n"
        "    if (bootstrap_state >= 3) { return; }\n"
        "    %sbootstrap_inheritance();\n"
        "    bootstrap_state = 3;\n"
        "%s" // Finish bootstrapping of all prerequisite parcels.
        "    %sinit_parcel();\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,
                          inh_bootstrap, 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(inh_bootstrap);
    FREEMEM(prereq_bootstrap);
    FREEMEM(file_content);
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
static void
S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
    const char *parcel_name = CFCParcel_get_name(parcel);
    char *pymod_name = CFCUtil_strdup(parcel_name);
    // TODO: Stop lowercasing when parcels are restricted to lowercase.
    for (int i = 0; pymod_name[i] != '\0'; i++) {
        pymod_name[i] = tolower(pymod_name[i]);
    }
    const char *last_dot = strrchr(pymod_name, '.');
    const char *last_component = last_dot != NULL
                                 ? last_dot + 1
                                 : pymod_name;
    char *helper_mod_name = CFCUtil_sprintf("%s._%s", pymod_name, last_component);
    for (int i = 0; helper_mod_name[i] != '\0'; i++) {
        helper_mod_name[i] = tolower(helper_mod_name[i]);
    }

    CFCClass  **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
    CFCParcel **parcels = CFCParcel_all_parcels();
    char *callbacks          = S_gen_callbacks(self, parcel, ordered);
    char *type_linkups       = S_gen_type_linkups(self, parcel, ordered);
    char *pound_includes     = CFCUtil_strdup("");
    char *class_bindings     = S_gen_class_bindings(self, parcel, pymod_name, ordered);
    char *parcel_boots       = CFCUtil_strdup("");
    char *pytype_ready_calls = CFCUtil_strdup("");
    char *module_adds        = CFCUtil_strdup("");

    // Add parcel bootstrapping calls.
    for (size_t i = 0; parcels[i]; ++i) {
        if (!CFCParcel_included(parcels[i])) {
            const char *prefix = CFCParcel_get_prefix(parcels[i]);
            parcel_boots = CFCUtil_cat(parcel_boots, "    ", prefix,
                                       "bootstrap_parcel();\n", NULL);
        }
    }

    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_included(klass)) { continue; }
        const char *struct_sym = CFCClass_get_struct_sym(klass);

        const char *include_h  = CFCClass_include_h(klass);
        pound_includes = CFCUtil_cat(pound_includes, "#include \"",
                                     include_h, "\"\n", NULL);

        // The PyType_Ready invocations for instantiable classes are handled
        // via bootstrapping of Clownfish Class objects.  Since inert classes
        // do not at present have Class objects, we need to handle their
        // PyType_Ready calls independently.
        if (CFCClass_inert(klass)) {
            pytype_ready_calls = CFCUtil_cat(pytype_ready_calls,
                "    if (PyType_Ready(&", struct_sym,
                "_pytype_struct) < 0) { return NULL; }\n", NULL);
        }

        module_adds = CFCUtil_cat(module_adds, "    PyModule_AddObject(module, \"",
                                  struct_sym, "\", (PyObject*)&", struct_sym,
                                  "_pytype_struct);\n", NULL);
    }

    const char pattern[] =
        "%s\n"
        "\n"
        "#include \"Python.h\"\n"
        "#include \"cfish_parcel.h\"\n"
        "#include \"CFBind.h\"\n"
        "%s\n"
        "\n"
        "%s\n" // callbacks
        "\n"
        "static PyModuleDef module_def = {\n"
        "    PyModuleDef_HEAD_INIT,\n"
        "    \"%s\",\n" // module name
        "    NULL,\n" // docstring
        "    -1,\n"
        "    NULL, NULL, NULL, NULL, NULL\n"
        "};\n"
        "\n"
        "%s" // class bindings
        "\n"
        "%s" // S_link_py_types function
        "\n"
        "PyMODINIT_FUNC\n"
        "PyInit__%s(void) {\n"
        "    cfish_Class_bootstrap_hook1 = CFBind_class_bootstrap_hook1;\n"
        "\n"
        "%s\n" // PyType_Ready calls
        "\n"
        "    S_link_py_types();\n"
        "\n"
        "%s\n" // parcel boots
        "\n"
        "    PyObject *module = PyModule_Create(&module_def);\n"
        "%s\n" // Add types to module
        "\n"
        "    return module;\n"
        "}\n"
        "\n"
        "%s\n"
        "\n";

    char *content
        = CFCUtil_sprintf(pattern, self->header, pound_includes, callbacks,
                          helper_mod_name, class_bindings, type_linkups,
                          last_component, pytype_ready_calls, parcel_boots,
                          module_adds, self->footer);

    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "_%s.c", dest,
                                     last_component);
    CFCUtil_write_if_changed(filepath, content, strlen(content));
    FREEMEM(filepath);

    FREEMEM(content);
    FREEMEM(module_adds);
    FREEMEM(pytype_ready_calls);
    FREEMEM(parcel_boots);
    FREEMEM(class_bindings);
    FREEMEM(helper_mod_name);
    FREEMEM(pymod_name);
    FREEMEM(pound_includes);
    FREEMEM(type_linkups);
    FREEMEM(callbacks);
    FREEMEM(ordered);
}
Ejemplo n.º 8
0
CFCMethod*
CFCMethod_init(CFCMethod *self, CFCParcel *parcel, const char *exposure,
               const char *class_name, const char *class_cnick,
               const char *macro_sym, CFCType *return_type,
               CFCParamList *param_list, CFCDocuComment *docucomment,
               int is_final, int is_abstract) {
    // Validate macro_sym, derive micro_sym.
    if (!S_validate_macro_sym(macro_sym)) {
        CFCBase_decref((CFCBase*)self);
        CFCUtil_die("Invalid macro_sym: '%s'",
                    macro_sym ? macro_sym : "[NULL]");
    }
    char *micro_sym = CFCUtil_strdup(macro_sym);
    for (size_t i = 0; micro_sym[i] != '\0'; i++) {
        micro_sym[i] = tolower(micro_sym[i]);
    }

    // Super-init and clean up derived micro_sym.
    CFCFunction_init((CFCFunction*)self, parcel, exposure, class_name,
                     class_cnick, micro_sym, return_type, param_list,
                     docucomment, false);
    FREEMEM(micro_sym);

    // Verify that the first element in the arg list is a self.
    CFCVariable **args = CFCParamList_get_variables(param_list);
    if (!args[0]) { CFCUtil_die("Missing 'self' argument"); }
    CFCType *type = CFCVariable_get_type(args[0]);
    const char *specifier = CFCType_get_specifier(type);
    const char *prefix    = CFCMethod_get_prefix(self);
    const char *last_colon = strrchr(class_name, ':');
    const char *struct_sym = last_colon ? last_colon + 1 : class_name;
    if (strcmp(specifier, struct_sym) != 0) {
        char *wanted = CFCUtil_sprintf("%s%s", prefix, struct_sym);
        int mismatch = strcmp(wanted, specifier);
        FREEMEM(wanted);
        if (mismatch) {
            CFCUtil_die("First arg type doesn't match class: '%s' '%s'",
                        class_name, specifier);
        }
    }

    self->macro_sym         = CFCUtil_strdup(macro_sym);
    self->full_override_sym = NULL;
    self->host_alias        = NULL;
    self->is_final          = is_final;
    self->is_abstract       = is_abstract;
    self->is_excluded       = false;

    // Derive name of implementing function.
    self->short_imp_func
        = CFCUtil_sprintf("%s_%s_IMP", CFCMethod_get_class_cnick(self),
                          self->macro_sym);
    self->imp_func = CFCUtil_sprintf("%s%s", CFCMethod_get_PREFIX(self),
                                     self->short_imp_func);

    // Assume that this method is novel until we discover when applying
    // inheritance that it overrides another.
    self->is_novel = true;

    return self;
}
Ejemplo n.º 9
0
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;
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
0
static char*
S_xsub_def_positional_args(CFCPerlMethod *self) {
    CFCMethod *method = self->method;
    CFCParamList *param_list = CFCMethod_get_param_list(method);
    CFCVariable **arg_vars = CFCParamList_get_variables(param_list);
    const char **arg_inits = CFCParamList_get_initial_values(param_list);
    unsigned num_vars = (unsigned)CFCParamList_num_vars(param_list);
    char *body = S_xsub_body(self);

    // Determine how many args are truly required and build an error check.
    unsigned min_required = 0;
    for (unsigned i = 0; i < num_vars; i++) {
        if (arg_inits[i] == NULL) {
            min_required = i + 1;
        }
    }
    char *xs_name_list = num_vars > 0
                         ? CFCUtil_strdup(CFCVariable_micro_sym(arg_vars[0]))
                         : CFCUtil_strdup("");
    for (unsigned i = 1; i < num_vars; i++) {
        const char *var_name = CFCVariable_micro_sym(arg_vars[i]);
        if (i < min_required) {
            xs_name_list = CFCUtil_cat(xs_name_list, ", ", var_name, NULL);
        }
        else {
            xs_name_list = CFCUtil_cat(xs_name_list, ", [", var_name, "]",
                                       NULL);
        }
    }
    const char num_args_pattern[] =
        "if (items %s %u) { CFISH_THROW(CFISH_ERR, \"Usage: %%s(%s)\", GvNAME(CvGV(cv))); }";
    char *num_args_check;
    if (min_required < num_vars) {
        num_args_check = CFCUtil_sprintf(num_args_pattern, "<", min_required,
                                         xs_name_list);
    }
    else {
        num_args_check = CFCUtil_sprintf(num_args_pattern, "!=", num_vars,
                                         xs_name_list);
    }

    // Var assignments.
    char *var_assignments = CFCUtil_strdup("");
    for (unsigned i = 0; i < num_vars; i++) {
        CFCVariable *var = arg_vars[i];
        const char  *val = arg_inits[i];
        const char  *var_name = CFCVariable_micro_sym(var);
        CFCType     *var_type = CFCVariable_get_type(var);
        const char  *type_c   = CFCType_to_c(var_type);

        if (i == 0) {    // self
            const char *meth_micro_sym = CFCMethod_micro_sym(self->method);
            char *statement
                = S_self_assign_statement(self, var_type, meth_micro_sym);
            var_assignments = CFCUtil_cat(var_assignments, statement, NULL);
            FREEMEM(statement);
        }
        else {
            char perl_stack_var[30];
            sprintf(perl_stack_var, "ST(%u)", i);
            char *conversion
                = CFCPerlTypeMap_from_perl(var_type, perl_stack_var);
            if (!conversion) {
                CFCUtil_die("Can't map type '%s'", type_c);
            }
            if (val) {
                char pattern[] =
                    "\n    %s %s = ( items >= %u && XSBind_sv_defined(ST(%u)) )"
                    " ? %s : %s;";
                char *statement = CFCUtil_sprintf(pattern, type_c, var_name, i,
                                                  i, conversion, val);
                var_assignments
                    = CFCUtil_cat(var_assignments, statement, NULL);
                FREEMEM(statement);
            }
            else {
                var_assignments
                    = CFCUtil_cat(var_assignments, "\n    ", type_c, " ",
                                  var_name, " = ", conversion, ";", NULL);
            }
            FREEMEM(conversion);
        }
    }

    char pattern[] =
        "XS(%s);\n"
        "XS(%s) {\n"
        "    dXSARGS;\n"
        "    CHY_UNUSED_VAR(cv);\n"
        "    SP -= items;\n"
        "    %s;\n"
        "\n"
        "    /* Extract vars from Perl stack. */\n"
        "    %s\n"
        "\n"
        "    /* Execute */\n"
        "    %s\n"
        "}\n";
    char *xsub
        = CFCUtil_sprintf(pattern, self->sub.c_name, self->sub.c_name,
                          num_args_check, var_assignments, body);

    FREEMEM(num_args_check);
    FREEMEM(var_assignments);
    FREEMEM(body);
    return xsub;
}
Ejemplo n.º 12
0
char*
CFCPerlConstructor_xsub_def(CFCPerlConstructor *self) {
    const char *c_name = self->sub.c_name;
    CFCParamList *param_list = self->sub.param_list;
    char         *name_list  = CFCPerlSub_arg_name_list((CFCPerlSub*)self);
    CFCVariable **arg_vars   = CFCParamList_get_variables(param_list);
    const char   *func_sym   = CFCFunction_full_func_sym(self->init_func);
    char *arg_decls    = CFCPerlSub_arg_declarations((CFCPerlSub*)self);
    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(arg_", name,
                              ");", NULL);
        }
    }

    const char pattern[] =
        "XS(%s);\n"
        "XS(%s) {\n"
        "    dXSARGS;\n"
        "    %s arg_self;\n"
        "%s"
        "    bool args_ok;\n"
        "    %s retval;\n"
        "\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.
        "    arg_self = (%s)XSBind_new_blank_obj(ST(0));%s\n"
        "\n"
        "    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, self_type_str, arg_decls,
                          self_type_str, allot_params, self_type_str,
                          refcount_mods, func_sym, name_list);

    FREEMEM(refcount_mods);
    FREEMEM(arg_decls);
    FREEMEM(allot_params);
    FREEMEM(name_list);

    return xsub_def;
}
Ejemplo n.º 13
0
void
CFCBindFile_write_h(CFCFile *file, const char *dest, const char *header,
                    const char *footer) {
    CFCUTIL_NULL_CHECK(file);
    CFCUTIL_NULL_CHECK(dest);
    CFCUTIL_NULL_CHECK(header);
    CFCUTIL_NULL_CHECK(footer);

    // Make directories.
    char *h_path = CFCFile_h_path(file, dest);
    char *h_dir  = CFCUtil_strdup(h_path);
    for (size_t len = strlen(h_dir); len--;) {
        if (h_dir[len] == CHY_DIR_SEP_CHAR) {
            h_dir[len] = 0;
            break;
        }
    }
    if (!CFCUtil_is_dir(h_dir)) {
        CFCUtil_make_path(h_dir);
        if (!CFCUtil_is_dir(h_dir)) {
            CFCUtil_die("Can't make path %s", h_dir);
        }
    }
    FREEMEM(h_dir);

    // Create the include-guard strings.
    const char *include_guard_start = CFCFile_guard_start(file);
    const char *include_guard_close = CFCFile_guard_close(file);

    // Aggregate block content.
    char *content = CFCUtil_strdup("");
    CFCBase **blocks = CFCFile_blocks(file);
    for (int i = 0; blocks[i] != NULL; i++) {
        const char *cfc_class = CFCBase_get_cfc_class(blocks[i]);

        if (strcmp(cfc_class, "Clownfish::CFC::Model::Parcel") == 0) {
            CFCParcel *parcel = (CFCParcel*)blocks[i];
            const char *prefix = CFCParcel_get_prefix(parcel);
            content = CFCUtil_cat(content, "#include \"", prefix,
                                  "parcel.h\"\n\n", NULL);
        }
        else if (strcmp(cfc_class, "Clownfish::CFC::Model::Class") == 0) {
            CFCBindClass *class_binding
                = CFCBindClass_new((CFCClass*)blocks[i]);
            char *c_header = CFCBindClass_to_c_header(class_binding);
            content = CFCUtil_cat(content, c_header, "\n", NULL);
            FREEMEM(c_header);
            CFCBase_decref((CFCBase*)class_binding);
        }
        else if (strcmp(cfc_class, "Clownfish::CFC::Model::CBlock") == 0) {
            const char *block_contents 
                = CFCCBlock_get_contents((CFCCBlock*)blocks[i]);
            content = CFCUtil_cat(content, block_contents, "\n", NULL);
        }
        else {
            CFCUtil_die("Unexpected class: %s", cfc_class);
        }
    }

    char pattern[] =
        "%s\n"
        "\n"
        "%s\n"
        "\n"
        "#ifdef __cplusplus\n"
        "extern \"C\" {\n"
        "#endif\n"
        "\n"
        "%s\n"
        "\n"
        "#ifdef __cplusplus\n"
        "}\n"
        "#endif\n"
        "\n"
        "%s\n"
        "\n"
        "%s\n"
        "\n";
    char *file_content
        = CFCUtil_sprintf(pattern, header, include_guard_start, content,
                          include_guard_close, footer);

    // Unlink then write file.
    remove(h_path);
    CFCUtil_write_file(h_path, file_content, strlen(file_content));

    FREEMEM(content);
    FREEMEM(file_content);
    FREEMEM(h_path);
}
Ejemplo n.º 14
0
CFCClass*
CFCClass_do_create(CFCClass *self, struct CFCParcel *parcel,
                   const char *exposure, const char *class_name,
                   const char *cnick, const char *micro_sym,
                   CFCDocuComment *docucomment, CFCFileSpec *file_spec,
                   const char *parent_class_name, int is_final, int is_inert) {
    CFCUTIL_NULL_CHECK(class_name);
    exposure  = exposure  ? exposure  : "parcel";
    micro_sym = micro_sym ? micro_sym : "class";
    parcel    = parcel    ? parcel    : CFCParcel_default_parcel();
    CFCSymbol_init((CFCSymbol*)self, parcel, exposure, class_name, cnick,
                   micro_sym);
    if (!is_inert
        && !parent_class_name
        && strcmp(class_name, "Clownfish::Obj") != 0
       ) {
        parent_class_name = "Clownfish::Obj";
    }
    self->parent     = NULL;
    self->tree_grown = false;
    self->children        = (CFCClass**)CALLOCATE(1, sizeof(CFCClass*));
    self->num_kids        = 0;
    self->functions       = (CFCFunction**)CALLOCATE(1, sizeof(CFCFunction*));
    self->num_functions   = 0;
    self->methods         = (CFCMethod**)CALLOCATE(1, sizeof(CFCMethod*));
    self->num_methods     = 0;
    self->member_vars     = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*));
    self->num_member_vars = 0;
    self->inert_vars      = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*));
    self->num_inert_vars  = 0;
    self->parent_class_name = CFCUtil_strdup(parent_class_name);
    self->docucomment
        = (CFCDocuComment*)CFCBase_incref((CFCBase*)docucomment);
    self->file_spec = (CFCFileSpec*)CFCBase_incref((CFCBase*)file_spec);

    // Cache several derived symbols.
    const char *last_colon = strrchr(class_name, ':');
    self->struct_sym = last_colon
                       ? CFCUtil_strdup(last_colon + 1)
                       : CFCUtil_strdup(class_name);
    const char *prefix = CFCClass_get_prefix(self);
    size_t struct_sym_len = strlen(self->struct_sym);
    self->short_vtable_var = (char*)MALLOCATE(struct_sym_len + 1);
    size_t i;
    for (i = 0; i < struct_sym_len; i++) {
        self->short_vtable_var[i] = toupper(self->struct_sym[i]);
    }
    self->short_vtable_var[struct_sym_len] = '\0';
    self->full_struct_sym   = CFCUtil_sprintf("%s%s", prefix, self->struct_sym);
    self->ivars_struct      = CFCUtil_sprintf("%sIVARS", self->struct_sym);
    self->full_ivars_struct = CFCUtil_sprintf("%sIVARS", self->full_struct_sym);
    self->ivars_func        = CFCUtil_sprintf("%s_IVARS", CFCClass_get_cnick(self));
    self->full_ivars_func   = CFCUtil_sprintf("%s%s_IVARS", prefix,
                                              CFCClass_get_cnick(self));
    self->full_ivars_offset = CFCUtil_sprintf("%s_OFFSET", self->full_ivars_func);
    size_t full_struct_sym_len = strlen(self->full_struct_sym);
    self->full_vtable_var = (char*)MALLOCATE(full_struct_sym_len + 1);
    for (i = 0; self->full_struct_sym[i] != '\0'; i++) {
        self->full_vtable_var[i] = toupper(self->full_struct_sym[i]);
    }
    self->full_vtable_var[i] = '\0';
    self->privacy_symbol = CFCUtil_sprintf("C_%s", self->full_vtable_var);

    // Build the relative path to the autogenerated C header file.
    if (file_spec) {
        const char *path_part = CFCFileSpec_get_path_part(self->file_spec);
        self->include_h = CFCUtil_sprintf("%s.h", path_part);
    }
    else {
        self->include_h = CFCUtil_strdup("class.h");
    }

    self->is_final    = !!is_final;
    self->is_inert    = !!is_inert;

    if (file_spec && CFCFileSpec_included(file_spec)) {
        if (!CFCParcel_included(parcel)) {
            CFCUtil_die("Class %s from include dir found in parcel %s from"
                        " source dir",
                        class_name, CFCParcel_get_name(parcel));
        }
    }
    else {
        if (CFCParcel_included(parcel)) {
            CFCUtil_die("Class %s from source dir found in parcel %s from"
                        " include dir",
                        class_name, CFCParcel_get_name(parcel));
        }
    }

    // Store in registry.
    S_register(self);

    return self;
}
Ejemplo n.º 15
0
static void
S_find_prereq(CFCHierarchy *self, CFCParcel *parent, CFCPrereq *prereq) {
    const char *name        = CFCPrereq_get_name(prereq);
    CFCVersion *min_version = CFCPrereq_get_version(prereq);

    // Check whether prereq was processed already.
    CFCParcel **parcels = CFCParcel_all_parcels();
    for (int i = 0; parcels[i]; ++i) {
        CFCParcel *parcel = parcels[i];
        const char *other_name = CFCParcel_get_name(parcel);

        if (strcmp(other_name, name) == 0) {
            CFCVersion *other_version = CFCParcel_get_version(parcel);
            CFCVersion *major_version = CFCParcel_get_major_version(parcel);

            if (CFCVersion_compare_to(major_version, min_version) <= 0
                && CFCVersion_compare_to(min_version, other_version) <= 0
               ) {
                // Compatible version found.
                return;
            }
            else {
                CFCUtil_die("Parcel %s %s required by %s not compatible with"
                            " version %s required by %s",
                            name, other_version, "[TODO]",
                            CFCVersion_get_vstring(min_version),
                            CFCParcel_get_name(parent));
            }
        }
    }

    CFCParcel *parcel = NULL;

    // TODO: Decide whether to prefer higher versions from directories
    // that come later in the list of include dirs or stop processing once
    // a suitable version was found in a dir.
    for (size_t i = 0; self->includes[i] != NULL; i++) {
        char *name_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s",
                                         self->includes[i], name);

        if (CFCUtil_is_dir(name_dir)) {
            void *dirhandle = CFCUtil_opendir(name_dir);
            const char *entry = NULL;

            while (NULL != (entry = CFCUtil_dirnext(dirhandle))) {
                if (!CFCVersion_is_vstring(entry)) { continue; }

                char *version_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s",
                                                    name_dir, entry);

                if (CFCUtil_is_dir(version_dir)) {
                    parcel = S_audition_parcel(version_dir, entry, min_version,
                                               parcel);
                }

                FREEMEM(version_dir);
            }

            CFCUtil_closedir(dirhandle, name_dir);
        }

        FREEMEM(name_dir);
    }

    if (parcel == NULL) {
        CFCUtil_die("Parcel %s %s required by %s not found",
                    name, CFCVersion_get_vstring(min_version),
                    CFCParcel_get_name(parent));
    }

    // Make sure to register prereq parcels first, so that prereq
    // parent classes precede subclasses.
    S_find_prereqs(self, parcel);

    CFCParcel_register(parcel);

    CFCBase_decref((CFCBase*)parcel);
}
Ejemplo n.º 16
0
static void
S_resolve(CFCUri *self, const char *parcel, const char *struct_sym,
          const char *callable) {

    // Try to find a CFCClass.
    CFCClass *doc_class = self->doc_class;
    CFCClass *klass     = NULL;

    if (parcel) {
        char *full_struct_sym = CFCUtil_sprintf("%s_%s", parcel, struct_sym);
        klass = CFCClass_fetch_by_struct_sym(full_struct_sym);
        FREEMEM(full_struct_sym);
    }
    else if (struct_sym && doc_class) {
        const char *prefix = CFCClass_get_prefix(doc_class);
        char *full_struct_sym = CFCUtil_sprintf("%s%s", prefix, struct_sym);
        klass = CFCClass_fetch_by_struct_sym(full_struct_sym);
        FREEMEM(full_struct_sym);
    }
    else if (callable) {
        klass = doc_class;
    }

    if (klass) {
        if (!CFCClass_public(klass)) {
            CFCUtil_warn("Non-public class '%s' in Clownfish URI: %s",
                          CFCClass_get_struct_sym(klass), self->string);
        }

        self->type  = CFC_URI_CLASS;
        self->klass = klass;
        CFCBase_incref((CFCBase*)klass);

        if (callable) {
            if (islower(callable[0])) {
                CFCFunction *function = CFCClass_function(klass, callable);

                if (!function) {
                    CFCUtil_warn("Unknown function '%s' in Clownfish URI: %s",
                                 callable, self->string);
                }
                else if (!CFCFunction_public(function)) {
                    CFCUtil_warn("Non-public function '%s' in Clownfish URI:"
                                 " %s", callable, self->string);
                }

                self->type     = CFC_URI_FUNCTION;
                self->callable = CFCUtil_strdup(callable);
            }
            else {
                CFCMethod *method = CFCClass_method(klass, callable);

                if (!method) {
                    CFCUtil_warn("Unknown method '%s' in Clownfish URI: %s",
                                 callable, self->string);
                }
                else if (!CFCMethod_public(method)) {
                    CFCUtil_warn("Non-public method '%s' in Clownfish URI:"
                                 " %s", callable, self->string);
                }

                self->type     = CFC_URI_METHOD;
                self->callable = CFCUtil_strdup(callable);
            }
        }

        return;
    }

    // Try to find a CFCDocument.
    if (!parcel && struct_sym && !callable) {
        CFCDocument *doc = CFCDocument_fetch(struct_sym);

        if (doc) {
            self->type     = CFC_URI_DOCUMENT;
            self->document = doc;
            CFCBase_incref((CFCBase*)doc);
            return;
        }
    }

    S_set_error(self, "Couldn't resolve Clownfish URI");
}
Ejemplo n.º 17
0
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();
}
Ejemplo n.º 18
0
char*
CFCPerlPod_methods_pod(CFCPerlPod *self, CFCClass *klass) {
    const char *class_name = CFCClass_get_name(klass);
    char *abstract_pod = CFCUtil_strdup("");
    char *methods_pod  = CFCUtil_strdup("");

    // Start with methods that don't map to a Clownfish method.
    for (size_t i = 0; i < self->num_methods; i++) {
        NamePod meth_spec = self->methods[i];
        CFCMethod *method = CFCClass_method(klass, meth_spec.func);
        if (method) { continue; }
        if (!meth_spec.pod) {
            CFCUtil_die("No POD specified for method '%s' in class '%s'",
                        meth_spec.alias, CFCClass_get_name(klass));
        }
        methods_pod = CFCUtil_cat(methods_pod, meth_spec.pod, "\n", NULL);
    }

    CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
    for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
        CFCMethod *method = fresh_methods[meth_num];
        const char *name = CFCMethod_get_name(method);
        char *meth_pod = NULL;

        // Try to find custom POD for method.
        NamePod *meth_spec = NULL;
        for (size_t j = 0; j < self->num_methods; j++) {
            NamePod *candidate = &self->methods[j];
            const char *other_name = candidate->func;
            if (other_name && strcmp(other_name, name) == 0) {
                meth_spec = candidate;
                break;
            }
        }

        if (meth_spec) {
            // Found custom POD.
            if (meth_spec->pod) {
                meth_pod = CFCUtil_sprintf("%s\n", meth_spec->pod);
            }
            else {
                meth_pod
                    = CFCPerlPod_gen_subroutine_pod((CFCCallable*)method,
                                                    meth_spec->alias, klass,
                                                    meth_spec->sample,
                                                    class_name, false);
            }
        }
        else {
            // No custom POD found. Add POD for public methods with Perl
            // bindings.
            if (!CFCMethod_public(method)
                || CFCMethod_excluded_from_host(method)
                || !CFCMethod_can_be_bound(method)
               ) {
                continue;
            }

            // Only add POD for novel methods and the first implementation
            // of abstract methods.
            if (!CFCMethod_novel(method)) {
                if (CFCMethod_abstract(method)) { continue; }
                CFCClass *parent = CFCClass_get_parent(klass);
                CFCMethod *parent_method = CFCClass_method(parent, name);
                if (!CFCMethod_abstract(parent_method)) { continue; }
            }

            char *perl_name = CFCPerlMethod_perl_name(method);
            meth_pod
                = CFCPerlPod_gen_subroutine_pod((CFCCallable*)method,
                                                perl_name, klass, NULL,
                                                class_name, false);
            FREEMEM(perl_name);
        }

        if (CFCMethod_abstract(method)) {
            abstract_pod = CFCUtil_cat(abstract_pod, meth_pod, NULL);
        }
        else {
            methods_pod = CFCUtil_cat(methods_pod, meth_pod, NULL);
        }
        FREEMEM(meth_pod);
    }

    char *pod = CFCUtil_strdup("");
    if (strlen(abstract_pod)) {
        pod = CFCUtil_cat(pod, "=head1 ABSTRACT METHODS\n\n", abstract_pod, NULL);
    }
    FREEMEM(abstract_pod);
    if (strlen(methods_pod)) {
        pod = CFCUtil_cat(pod, "=head1 METHODS\n\n", methods_pod, NULL);
    }
    FREEMEM(methods_pod);

    return pod;
}
Ejemplo n.º 19
0
static char*
S_gen_callbacks(CFCPython *self, CFCParcel *parcel, CFCClass **ordered) {
    char *callbacks  = CFCUtil_strdup("");

    // Generate implementation files containing callback definitions.
    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_included(klass)
            || CFCClass_inert(klass)
            //|| CFCClass_get_parcel(klass) != parcel
           ) {
            continue;
        }

        CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
        for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
            CFCMethod *method = fresh_methods[meth_num];

            // Define callback.
            if (CFCMethod_novel(method) && !CFCMethod_final(method)) {
                char *cb_def = CFCPyMethod_callback_def(method, klass);
                callbacks = CFCUtil_cat(callbacks, cb_def, "\n", NULL);
                FREEMEM(cb_def);
            }
        }
    }

    static const char helpers[] =
        "static PyObject*\n"
        "S_pack_tuple(int num_args, ...) {\n"
        "    PyObject *tuple = PyTuple_New(num_args);\n"
        "    va_list args;\n"
        "    va_start(args, num_args);\n"
        "    for (int i = 0; i < num_args; i++) {\n"
        "        PyObject *arg = va_arg(args, PyObject*);\n"
        "        PyTuple_SET_ITEM(tuple, i, arg);\n"
        "    }\n"
        "    va_end(args);\n"
        "    return tuple;\n"
        "}\n"
        "#define CFBIND_TRY(routine) \\\n"
        "    do { \\\n"
        "        jmp_buf env; \\\n"
        "        jmp_buf *prev_env = CFBind_swap_env(&env); \\\n"
        "        if (!setjmp(env)) { \\\n"
        "            routine; \\\n"
        "        } \\\n"
        "        CFBind_swap_env(prev_env); \\\n"
        "    } while (0)\n"
        "\n"
        "static PyObject*\n"
        "S_call_pymeth(PyObject *self, const char *meth_name, PyObject *args,\n"
        "              const char *file, int line, const char *func) {\n"
        "    PyObject *callable = PyObject_GetAttrString(self, meth_name);\n"
        "    if (!PyCallable_Check(callable)) {\n"
        "        cfish_String *mess\n"
        "            = cfish_Err_make_mess(file, line, func, \"Attr '%s' not callable\",\n"
        "                                  meth_name);\n"
        "        cfish_Err_throw_mess(CFISH_ERR, mess);\n"
        "    }\n"
        "    PyObject *result = PyObject_CallObject(callable, args);\n"
        "    Py_DECREF(args);\n"
        "    if (result == NULL) {\n"
        "        cfish_String *mess\n"
        "            = cfish_Err_make_mess(file, line, func,\n"
        "                                  \"Callback to '%s' failed\", meth_name);\n"
        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
        "    }\n"
        "    return result;\n"
        "}\n"
        "\n"
        "#define CALL_PYMETH_VOID(self, meth_name, args) \\\n"
        "    S_call_pymeth_void(self, meth_name, args, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
        "\n"
        "static void\n"
        "S_call_pymeth_void(PyObject *self, const char *meth_name, PyObject *args,\n"
        "                   const char *file, int line, const char *func) {\n"
        "    PyObject *py_result\n"
        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
        "    if (py_result == NULL) {\n"
        "        cfish_String *mess\n"
        "            = cfish_Err_make_mess(file, line, func, \"Call to %s failed\",\n"
        "                                  meth_name);\n"
        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
        "    }\n"
        "    Py_DECREF(py_result);\n"
        "}\n"
        "\n"
        "#define CALL_PYMETH_BOOL(self, meth_name, args) \\\n"
        "    S_call_pymeth_bool(self, meth_name, args, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
        "\n"
        "static bool\n"
        "S_call_pymeth_bool(PyObject *self, const char *meth_name, PyObject *args,\n"
        "                   const char *file, int line, const char *func) {\n"
        "    PyObject *py_result\n"
        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
        "    int truthiness = py_result != NULL\n"
        "                     ? PyObject_IsTrue(py_result)\n"
        "                     : -1;\n"
        "    if (truthiness == -1) {\n"
        "        cfish_String *mess\n"
        "            = cfish_Err_make_mess(file, line, func, \"Call to %s failed\",\n"
        "                                  meth_name);\n"
        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
        "    }\n"
        "    Py_DECREF(py_result);\n"
        "    return !!truthiness;\n"
        "}\n"
        "\n"
        "#define CALL_PYMETH_OBJ(self, meth_name, args, ret_class, nullable) \\\n"
        "    S_call_pymeth_obj(self, meth_name, args, ret_class, nullable, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
        "\n"
        "static cfish_Obj*\n"
        "S_call_pymeth_obj(PyObject *self, const char *meth_name,\n"
        "                  PyObject *args, cfish_Class *ret_class, bool nullable,\n"
        "                  const char *file, int line, const char *func) {\n"
        "    PyObject *py_result\n"
        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
        "    cfish_Obj *result = CFBind_py_to_cfish(py_result, ret_class);\n"
        "    Py_DECREF(py_result);\n"
        "    if (!nullable && result == NULL) {\n"
        "        CFISH_THROW(CFISH_ERR, \"%s cannot return NULL\", meth_name);\n"
        "    }\n"
        "    else if (!cfish_Obj_is_a(result, ret_class)) {\n"
        "        cfish_Class *result_class = cfish_Obj_get_class(result);\n"
        "        CFISH_DECREF(result);\n"
        "        CFISH_THROW(CFISH_ERR, \"%s returned %o instead of %o\", meth_name,\n"
        "                    CFISH_Class_Get_Name(result_class),\n"
        "                    CFISH_Class_Get_Name(ret_class));\n"
        "    }\n"
        "    return result;\n"
        "}\n"
        "\n"
        "#define CALL_PYMETH_DOUBLE(self, meth_name, args) \\\n"
        "    S_call_pymeth_f64(self, meth_name, args, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
        "#define CALL_PYMETH_FLOAT(self, meth_name, args) \\\n"
        "    ((float)S_call_pymeth_f64(self, meth_name, args, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "\n"
        "static double\n"
        "S_call_pymeth_f64(PyObject *self, const char *meth_name, PyObject *args,\n"
        "                  const char *file, int line, const char *func) {\n"
        "    PyObject *py_result\n"
        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
        "    PyErr_Clear();\n"
        "    double result = PyFloat_AsDouble(py_result);\n"
        "    if (PyErr_Occurred()) {\n"
        "        cfish_String *mess\n"
        "            = cfish_Err_make_mess(file, line, func,\n"
        "                                  \"Converting result of '%s' to double failed\",\n"
        "                                  meth_name);\n"
        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
        "    }\n"
        "    Py_DECREF(py_result);\n"
        "    return result;\n"
        "}\n"
        "\n"
        "#define CALL_PYMETH_INT64_T(self, meth_name, args) \\\n"
        "    S_call_pymeth_i64(self, meth_name, args, INT64_MAX, INT64_MIN, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
        "#define CALL_PYMETH_INT32_T(self, meth_name, args) \\\n"
        "    ((int32_t)S_call_pymeth_i64(self, meth_name, args, INT32_MAX, INT32_MIN, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_INT16_T(self, meth_name, args) \\\n"
        "    ((int16_t)S_call_pymeth_i64(self, meth_name, args, INT16_MAX, INT16_MIN, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_INT8_T(self, meth_name, args) \\\n"
        "    ((int8_t)S_call_pymeth_i64(self, meth_name, args, INT8_MAX, INT8_MIN, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_CHAR(self, meth_name, args) \\\n"
        "    ((char)S_call_pymeth_i64(self, meth_name, args, CHAR_MAX, CHAR_MIN, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_SHORT(self, meth_name, args) \\\n"
        "    ((short)S_call_pymeth_i64(self, meth_name, args, SHRT_MAX, SHRT_MIN, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_INT(self, meth_name, args) \\\n"
        "    ((int16_t)S_call_pymeth_i64(self, meth_name, args, INT_MAX, INT_MIN, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_LONG(self, meth_name, args) \\\n"
        "    ((int16_t)S_call_pymeth_i64(self, meth_name, args, LONG_MAX, LONG_MIN, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "\n"
        "static int64_t\n"
        "S_call_pymeth_i64(PyObject *self, const char *meth_name, PyObject *args,\n"
        "                  int64_t max, int64_t min,\n"
        "                  const char *file, int line, const char *func) {\n"
        "    PyObject *py_result\n"
        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
        "    PyErr_Clear();\n"
        "    int64_t result = PyLong_AsLongLong(py_result);\n"
        "    if (PyErr_Occurred() || result > max || result < min) {\n"
        "        cfish_String *mess\n"
        "            = cfish_Err_make_mess(file, line, func,\n"
        "                                  \"Converting result of '%s' to int64_t failed\",\n"
        "                                  meth_name);\n"
        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
        "    }\n"
        "    Py_DECREF(py_result);\n"
        "    return result;\n"
        "}\n"
        "\n"
        "#define CALL_PYMETH_UINT64_T(self, meth_name, args) \\\n"
        "    S_call_pymeth_u64(self, meth_name, args, UINT64_MAX, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
        "#define CALL_PYMETH_UINT32_T(self, meth_name, args) \\\n"
        "    ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT32_MAX, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_UINT16_T(self, meth_name, args) \\\n"
        "    ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT16_MAX, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_UINT8_T(self, meth_name, args) \\\n"
        "    ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT8_MAX, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
        "#define CALL_PYMETH_SIZE_T(self, meth_name, args) \\\n"
        "    S_call_pymeth_u64(self, meth_name, args, SIZE_MAX, \\\n"
        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
        "\n"
        "static uint64_t\n"
        "S_call_pymeth_u64(PyObject *self, const char *meth_name, PyObject *args,\n"
        "                  uint64_t max,\n"
        "                  const char *file, int line, const char *func) {\n"
        "    PyObject *py_result\n"
        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
        "    PyErr_Clear();\n"
        "    uint64_t result = PyLong_AsUnsignedLongLong(py_result);\n"
        "    if (PyErr_Occurred()) {\n"
        "        cfish_String *mess\n"
        "            = cfish_Err_make_mess(file, line, func,\n"
        "                                  \"Converting result of '%s' to uint64_t failed\",\n"
        "                                  meth_name);\n"
        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
        "    }\n"
        "    Py_DECREF(py_result);\n"
        "    return result;\n"
        "}\n"
        ;

    static const char pattern[] =
        "%s\n"
        "\n"
        "%s"
        ;
    char *content = CFCUtil_sprintf(pattern, helpers, callbacks);

    FREEMEM(callbacks);
    return content;
}
Ejemplo n.º 20
0
char*
CFCPerlPod_gen_subroutine_pod(CFCCallable *func,
                              const char *alias, CFCClass *klass,
                              const char *code_sample,
                              const char *class_name, int is_constructor) {
    const char *func_name = CFCCallable_get_name(func);

    // Only allow "public" subs to be exposed as part of the public API.
    if (!CFCCallable_public(func)) {
        CFCUtil_die("%s#%s is not public", class_name, func_name);
    }

    char *pod = CFCUtil_sprintf("=head2 %s\n\n", alias);

    // Add code sample.
    if (!code_sample) {
        char *auto_sample
            = S_gen_code_sample(func, alias, klass, is_constructor);
        pod = CFCUtil_cat(pod, auto_sample, "\n", NULL);
        FREEMEM(auto_sample);
    }
    else {
        pod = CFCUtil_cat(pod, code_sample, "\n", NULL);
    }

    // Get documentation, which may be inherited.
    CFCDocuComment *docucomment = CFCCallable_get_docucomment(func);
    if (!docucomment) {
        CFCClass *parent = klass;
        while (NULL != (parent = CFCClass_get_parent(parent))) {
            CFCCallable *parent_func
                = (CFCCallable*)CFCClass_method(parent, func_name);
            if (!parent_func) { break; }
            docucomment = CFCCallable_get_docucomment(parent_func);
            if (docucomment) { break; }
        }
    }
    if (!docucomment) {
        return pod;
    }

    // Incorporate "description" text from DocuComment.
    const char *long_doc = CFCDocuComment_get_description(docucomment);
    if (long_doc && strlen(long_doc)) {
        char *perlified = CFCPerlPod_md_to_pod(long_doc, klass, 3);
        pod = CFCUtil_cat(pod, perlified, NULL);
        FREEMEM(perlified);
    }

    // Add params in a list.
    const char**param_names = CFCDocuComment_get_param_names(docucomment);
    const char**param_docs  = CFCDocuComment_get_param_docs(docucomment);
    if (param_names[0]) {
        pod = CFCUtil_cat(pod, "=over\n\n", NULL);
        for (size_t i = 0; param_names[i] != NULL; i++) {
            char *perlified = CFCPerlPod_md_to_pod(param_docs[i], klass, 3);
            pod = CFCUtil_cat(pod, "=item *\n\nB<", param_names[i], "> - ",
                              perlified, NULL);
            FREEMEM(perlified);
        }
        pod = CFCUtil_cat(pod, "=back\n\n", NULL);
    }

    // Add return value description, if any.
    const char *retval_doc = CFCDocuComment_get_retval(docucomment);
    if (retval_doc && strlen(retval_doc)) {
        char *perlified = CFCPerlPod_md_to_pod(retval_doc, klass, 3);
        pod = CFCUtil_cat(pod, "Returns: ", perlified, NULL);
        FREEMEM(perlified);
    }

    return pod;
}
Ejemplo n.º 21
0
char*
CFCPerlClass_create_pod(CFCPerlClass *self) {
    CFCPerlPod *pod_spec   = self->pod_spec;
    const char *class_name = self->class_name;
    CFCClass   *client     = self->client;
    if (!pod_spec) {
        return NULL;
    }
    if (!client) {
        CFCUtil_die("No client for %s", class_name);
    }
    CFCDocuComment *docucom = CFCClass_get_docucomment(client);
    if (!docucom) {
        CFCUtil_die("No DocuComment for %s", class_name);
    }

    // Get the class's brief description.
    const char *raw_brief = CFCDocuComment_get_brief(docucom);
    char *brief = CFCPerlPod_md_to_pod(pod_spec, client, raw_brief);

    // Get the class's long description.
    char *description;
    const char *pod_description = CFCPerlPod_get_description(pod_spec);
    if (pod_description && strlen(pod_description)) {
        description = CFCUtil_sprintf("%s\n", pod_description);
    }
    else {
        const char *raw_description = CFCDocuComment_get_long(docucom);
        description = CFCPerlPod_md_to_pod(pod_spec, client, raw_description);
    }

    // Create SYNOPSIS.
    const char *raw_synopsis = CFCPerlPod_get_synopsis(pod_spec);
    char *synopsis = CFCUtil_strdup("");
    if (raw_synopsis && strlen(raw_synopsis)) {
        synopsis = CFCUtil_cat(synopsis, "=head1 SYNOPSIS\n\n", raw_synopsis,
                               "\n", NULL);
    }

    // Create CONSTRUCTORS.
    char *constructor_pod = CFCPerlPod_constructors_pod(pod_spec, client);

    // Create METHODS, possibly including an ABSTRACT METHODS section.
    char *methods_pod = CFCPerlPod_methods_pod(pod_spec, client);

    // Build an INHERITANCE section describing class ancestry.
    char *inheritance = CFCUtil_strdup("");
    if (CFCClass_get_parent(client)) {
        inheritance = CFCUtil_cat(inheritance, "=head1 INHERITANCE\n\n",
                                  class_name, NULL);
        CFCClass *ancestor = client;
        while (NULL != (ancestor = CFCClass_get_parent(ancestor))) {
            const char *ancestor_klass = CFCClass_get_class_name(ancestor);
            if (CFCPerlClass_singleton(ancestor_klass)) {
                inheritance = CFCUtil_cat(inheritance, " isa L<",
                                          ancestor_klass, ">", NULL);
            }
            else {
                inheritance = CFCUtil_cat(inheritance, " isa ",
                                          ancestor_klass, NULL);
            }
        }
        inheritance = CFCUtil_cat(inheritance, ".\n\n", NULL);
    }

    // Put it all together.
    const char pattern[] =
        "=head1 NAME\n"
        "\n"
        "%s - %s"
        "%s"
        "=head1 DESCRIPTION\n"
        "\n"
        "%s"
        "%s"
        "%s"
        "%s"
        "=cut\n"
        "\n";
    char *pod
        = CFCUtil_sprintf(pattern, class_name, brief, synopsis, description,
                          constructor_pod, methods_pod, inheritance);

    FREEMEM(brief);
    FREEMEM(synopsis);
    FREEMEM(description);
    FREEMEM(constructor_pod);
    FREEMEM(methods_pod);
    FREEMEM(inheritance);

    return pod;
}
Ejemplo n.º 22
0
// Convert a single node.
static char*
S_node_to_pod(cmark_node *node, CFCClass *klass, int header_level) {
    char *result = CFCUtil_strdup("");
    if (node == NULL) {
        return result;
    }

    int found_matching_code_block = false;
    cmark_iter *iter = cmark_iter_new(node);
    cmark_event_type ev_type;

    while (CMARK_EVENT_DONE != (ev_type = cmark_iter_next(iter))) {
        cmark_node *node = cmark_iter_get_node(iter);
        cmark_node_type type = cmark_node_get_type(node);

        switch (type) {
            case CMARK_NODE_DOCUMENT:
                break;

            case CMARK_NODE_PARAGRAPH:
                if (ev_type == CMARK_EVENT_EXIT) {
                    result = CFCUtil_cat(result, "\n\n", NULL);
                }
                break;

            case CMARK_NODE_BLOCK_QUOTE:
            case CMARK_NODE_LIST:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "=over\n\n", NULL);
                }
                else {
                    result = CFCUtil_cat(result, "=back\n\n", NULL);
                }
                break;

            case CMARK_NODE_ITEM:
                // TODO: Ordered lists.
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "=item *\n\n", NULL);
                }
                break;

            case CMARK_NODE_HEADER:
                if (ev_type == CMARK_EVENT_ENTER) {
                    int extra_level = cmark_node_get_header_level(node) - 1;
                    char *header = CFCUtil_sprintf("=head%d ",
                                                   header_level + extra_level);
                    result = CFCUtil_cat(result, header, NULL);
                    FREEMEM(header);
                }
                else {
                    result = CFCUtil_cat(result, "\n\n", NULL);
                }
                break;

            case CMARK_NODE_CODE_BLOCK: {
                int is_host = CFCMarkdown_code_block_is_host(node, "perl");

                if (is_host) {
                    found_matching_code_block = true;

                    const char *content = cmark_node_get_literal(node);
                    char *copy = CFCUtil_strdup(content);
                    // Chomp trailing newline.
                    size_t len = strlen(copy);
                    if (len > 0 && copy[len-1] == '\n') {
                        copy[len-1] = '\0';
                    }
                    char *indented
                        = CFCUtil_global_replace(copy, "\n", "\n    ");
                    result
                        = CFCUtil_cat(result, "    ", indented, "\n\n", NULL);
                    FREEMEM(indented);
                    FREEMEM(copy);
                }

                if (CFCMarkdown_code_block_is_last(node)) {
                    if (!found_matching_code_block) {
                        result = CFCUtil_cat(result,
                            "    Code example for Perl is missing\n\n");
                    }
                    else {
                        // Reset.
                        found_matching_code_block = false;
                    }
                }

                break;
            }

            case CMARK_NODE_HTML: {
                const char *html = cmark_node_get_literal(node);
                result = CFCUtil_cat(result, "=begin html\n\n", html,
                                     "\n=end\n\n", NULL);
                break;
            }

            case CMARK_NODE_HRULE:
                break;

            case CMARK_NODE_TEXT: {
                const char *content = cmark_node_get_literal(node);
                char *escaped = S_pod_escape(content);
                result = CFCUtil_cat(result, escaped, NULL);
                FREEMEM(escaped);
                break;
            }

            case CMARK_NODE_LINEBREAK:
                // POD doesn't support line breaks. Start a new paragraph.
                result = CFCUtil_cat(result, "\n\n", NULL);
                break;

            case CMARK_NODE_SOFTBREAK:
                result = CFCUtil_cat(result, "\n", NULL);
                break;

            case CMARK_NODE_CODE: {
                const char *content = cmark_node_get_literal(node);
                char *escaped = S_pod_escape(content);
                result = CFCUtil_cat(result, "C<", escaped, ">", NULL);
                FREEMEM(escaped);
                break;
            }

            case CMARK_NODE_INLINE_HTML: {
                const char *html = cmark_node_get_literal(node);
                CFCUtil_warn("Inline HTML not supported in POD: %s", html);
                break;
            }

            case CMARK_NODE_LINK:
                if (ev_type == CMARK_EVENT_ENTER) {
                    char *pod = S_convert_link(node, klass, header_level);
                    result = CFCUtil_cat(result, pod, NULL);
                    FREEMEM(pod);
                    cmark_iter_reset(iter, node, CMARK_EVENT_EXIT);
                }
                break;

            case CMARK_NODE_IMAGE:
                CFCUtil_warn("Images not supported in POD");
                break;

            case CMARK_NODE_STRONG:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "B<", NULL);
                }
                else {
                    result = CFCUtil_cat(result, ">", NULL);
                }
                break;

            case CMARK_NODE_EMPH:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "I<", NULL);
                }
                else {
                    result = CFCUtil_cat(result, ">", NULL);
                }
                break;

            default:
                CFCUtil_die("Invalid cmark node type: %d", (int)type);
                break;
        }
    }

    cmark_iter_free(iter);
    return result;
}
Ejemplo n.º 23
0
/* 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**)(class_as_char + cfish_Class_offset_of_parent);\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_inheritance();\n"
        "\n"
        "%sVISIBLE void\n"
        "%sbootstrap_parcel();\n"
        "\n"
        "void\n"
        "%sinit_parcel();\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);
}
Ejemplo n.º 24
0
static char*
S_convert_link(cmark_node *link, CFCClass *doc_class, int header_level) {
    cmark_node *child = cmark_node_first_child(link);
    const char *uri   = cmark_node_get_url(link);
    char       *text  = S_nodes_to_pod(child, doc_class, header_level);
    char       *retval;

    if (!CFCUri_is_clownfish_uri(uri)) {
        retval = S_pod_link(text, uri);
        FREEMEM(text);
        return retval;
    }

    char       *new_uri  = NULL;
    char       *new_text = NULL;
    CFCUri     *uri_obj  = CFCUri_new(uri, doc_class);
    CFCUriType  type     = CFCUri_get_type(uri_obj);

    switch (type) {
        case CFC_URI_ERROR: {
            const char *error = CFCUri_get_error(uri_obj);
            new_text = CFCUtil_sprintf("[%s]", error);
            break;
        }

        case CFC_URI_NULL:
            // Change all instances of NULL to 'undef'
            new_text = CFCUtil_strdup("undef");
            break;

        case CFC_URI_CLASS: {
            CFCClass *klass = CFCUri_get_class(uri_obj);

            if (klass != doc_class) {
                const char *class_name = CFCClass_get_name(klass);
                new_uri = CFCUtil_strdup(class_name);
            }

            if (text[0] == '\0') {
                const char *src = CFCClass_included(klass)
                                  ? CFCClass_get_name(klass)
                                  : CFCClass_get_struct_sym(klass);
                new_text = CFCUtil_strdup(src);
            }

            break;
        }

        case CFC_URI_FUNCTION:
        case CFC_URI_METHOD: {
            CFCClass   *klass = CFCUri_get_class(uri_obj);
            const char *name  = CFCUri_get_callable_name(uri_obj);

            // Convert "Err_get_error" to "Clownfish->error".
            if (strcmp(CFCClass_full_struct_sym(klass), "cfish_Err") == 0
                && strcmp(name, "get_error") == 0
            ) {
                new_text = CFCUtil_strdup("Clownfish->error");
                break;
            }

            char *perl_name = CFCUtil_strdup(name);
            for (size_t i = 0; perl_name[i] != '\0'; ++i) {
                perl_name[i] = CFCUtil_tolower(perl_name[i]);
            }

            // The Perl POD only contains sections for novel methods. Link
            // to the class where the method is declared first.
            if (type == CFC_URI_METHOD) {
                CFCClass *parent = CFCClass_get_parent(klass);
                while (parent && CFCClass_method(parent, name)) {
                    klass = parent;
                    parent = CFCClass_get_parent(klass);
                }
            }

            if (klass == doc_class) {
                new_uri = CFCUtil_sprintf("/%s", perl_name);
            }
            else {
                const char *class_name = CFCClass_get_name(klass);
                new_uri = CFCUtil_sprintf("%s/%s", class_name, perl_name);
            }

            if (text[0] == '\0') {
                new_text = CFCUtil_sprintf("%s()", perl_name);
            }

            FREEMEM(perl_name);
            break;
        }

        case CFC_URI_DOCUMENT: {
            CFCDocument *doc = CFCUri_get_document(uri_obj);

            const char *path_part = CFCDocument_get_path_part(doc);
            new_uri = CFCUtil_global_replace(path_part, CHY_DIR_SEP, "::");

            if (text[0] == '\0') {
                const char *name = CFCDocument_get_name(doc);
                new_text = CFCUtil_strdup(name);
            }

            break;
        }
    }

    if (new_text) {
        FREEMEM(text);
        text = new_text;
    }

    if (new_uri) {
        retval = S_pod_link(text, new_uri);
        FREEMEM(new_uri);
        FREEMEM(text);
    }
    else {
        retval = text;
    }

    CFCBase_decref((CFCBase*)uri_obj);

    return retval;
}
Ejemplo n.º 25
0
static void
S_add_dump_method(CFCClass *klass) {
    CFCMethod *method = S_make_method_obj(klass, "Dump");
    CFCClass_add_method(klass, method);
    CFCBase_decref((CFCBase*)method);
    const char *full_func_sym = CFCMethod_implementing_func_sym(method);
    const char *full_struct   = CFCClass_full_struct_sym(klass);
    const char *vtable_var    = CFCClass_full_vtable_var(klass);
    const char *prefix        = CFCClass_get_prefix(klass);
    const char *class_cnick   = CFCClass_get_cnick(klass);
    CFCClass   *parent        = CFCClass_get_parent(klass);
    char buf[BUF_SIZE];

    if (parent && CFCClass_has_attribute(parent, "dumpable")) {
        char *full_typedef = CFCMethod_full_typedef(method, klass);
        char *full_meth    = CFCMethod_full_method_sym(method, klass);

        const char pattern[] =
            "cfish_Obj*\n"
            "%s(%s *self)\n"
            "{\n"
            "    %sIVARS *ivars = %s%s_IVARS(self);\n"
            "    %s super_dump = CFISH_SUPER_METHOD_PTR(%s, %s);\n"
            "    cfish_Hash *dump = (cfish_Hash*)super_dump(self);\n";
        char *autocode
            = CFCUtil_sprintf(pattern, full_func_sym, full_struct,
                              full_struct, prefix, class_cnick,
                              full_typedef, vtable_var, full_meth);
        CFCClass_append_autocode(klass, autocode);
        FREEMEM(full_meth);
        FREEMEM(full_typedef);
        FREEMEM(autocode);

        CFCVariable **fresh = CFCClass_fresh_member_vars(klass);
        for (size_t i = 0; fresh[i] != NULL; i++) {
            S_process_dump_member(klass, fresh[i], buf, BUF_SIZE);
        }
        FREEMEM(fresh);
    }
    else {
        const char pattern[] =
            "cfish_Obj*\n"
            "%s(%s *self)\n"
            "{\n"
            "    %sIVARS *ivars = %s%s_IVARS(self);\n"
            "    cfish_Hash *dump = cfish_Hash_new(0);\n"
            "    Cfish_Hash_Store_Str(dump, \"_class\", 6,\n"
            "        (cfish_Obj*)Cfish_CB_Clone(Cfish_Obj_Get_Class_Name((cfish_Obj*)self)));\n";
        char *autocode
            = CFCUtil_sprintf(pattern, full_func_sym, full_struct,
                              full_struct, prefix, class_cnick);
        CFCClass_append_autocode(klass, autocode);
        FREEMEM(autocode);
        CFCVariable **members = CFCClass_member_vars(klass);
        for (size_t i = 0; members[i] != NULL; i++) {
            S_process_dump_member(klass, members[i], buf, BUF_SIZE);
        }
    }

    CFCClass_append_autocode(klass, "    return (cfish_Obj*)dump;\n}\n\n");
}
Ejemplo n.º 26
0
static void
S_write_host_c(CFCPerl *self, CFCParcel *parcel) {
    CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
    const char  *prefix      = CFCParcel_get_prefix(parcel);
    const char  *privacy_sym = CFCParcel_get_privacy_sym(parcel);
    char        *includes    = CFCUtil_strdup("");
    char        *cb_defs     = CFCUtil_strdup("");
    char        *alias_adds  = CFCUtil_strdup("");

    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_inert(klass)) { continue; }
        const char *class_prefix = CFCClass_get_prefix(klass);
        if (strcmp(class_prefix, prefix) != 0) { continue; }

        const char *class_name = CFCClass_get_name(klass);

        const char *include_h = CFCClass_include_h(klass);
        includes = CFCUtil_cat(includes, "#include \"", include_h,
                               "\"\n", NULL);

        // Callbacks.
        CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
        for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
            CFCMethod *method = fresh_methods[meth_num];

            // Define callback.
            if (CFCMethod_novel(method) && !CFCMethod_final(method)) {
                char *cb_def = CFCPerlMethod_callback_def(method, klass);
                cb_defs = CFCUtil_cat(cb_defs, cb_def, "\n", NULL);
                FREEMEM(cb_def);
            }
        }

        // Add class aliases.
        CFCPerlClass *class_binding = CFCPerlClass_singleton(class_name);
        if (class_binding) {
            const char *class_var = CFCClass_full_class_var(klass);
            const char **aliases
                = CFCPerlClass_get_class_aliases(class_binding);
            for (size_t j = 0; aliases[j] != NULL; j++) {
                const char *alias = aliases[j];
                int alias_len  = (int)strlen(alias);
                const char pattern[] =
                    "    cfish_Class_add_alias_to_registry("
                    "%s, \"%s\", %d);\n";
                char *alias_add
                    = CFCUtil_sprintf(pattern, class_var, alias, alias_len);
                alias_adds = CFCUtil_cat(alias_adds, alias_add, NULL);
                FREEMEM(alias_add);
            }

            char *metadata_code
                = CFCPerlClass_method_metadata_code(class_binding);
            alias_adds = CFCUtil_cat(alias_adds, metadata_code, NULL);
            FREEMEM(metadata_code);
        }
    }

    const char pattern[] =
        "%s"
        "\n"
        "#define %s\n"  // privacy_sym
        "\n"
        "#include \"%sperl.h\"\n"
        "#include \"XSBind.h\"\n"
        "#include \"Clownfish/Class.h\"\n"
        "#include \"Clownfish/Err.h\"\n"
        "#include \"Clownfish/Obj.h\"\n"
        "%s"
        "\n"
        "/* Avoid conflicts with Clownfish bool type. */\n"
        "#define HAS_BOOL\n"
        "#define PERL_NO_GET_CONTEXT\n"
        "#include \"EXTERN.h\"\n"
        "#include \"perl.h\"\n"
        "#include \"XSUB.h\"\n"
        "\n"
        "static void\n"
        "S_finish_callback_void(pTHX_ const char *meth_name) {\n"
        "    int count = call_method(meth_name, G_VOID | G_DISCARD);\n"
        "    if (count != 0) {\n"
        "        CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n"
        "                    meth_name, (int32_t)count);\n"
        "    }\n"
        "    FREETMPS;\n"
        "    LEAVE;\n"
        "}\n"
        "\n"
        "static CFISH_INLINE SV*\n"
        "SI_do_callback_sv(pTHX_ const char *meth_name) {\n"
        "    int count = call_method(meth_name, G_SCALAR);\n"
        "    if (count != 1) {\n"
        "        CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n"
        "                    meth_name, (int32_t)count);\n"
        "    }\n"
        "    dSP;\n"
        "    SV *return_sv = POPs;\n"
        "    PUTBACK;\n"
        "    return return_sv;\n"
        "}\n"
        "\n"
        "static int64_t\n"
        "S_finish_callback_i64(pTHX_ const char *meth_name) {\n"
        "    SV *return_sv = SI_do_callback_sv(aTHX_ meth_name);\n"
        "    int64_t retval;\n"
        "    if (sizeof(IV) == 8) {\n"
        "        retval = (int64_t)SvIV(return_sv);\n"
        "    }\n"
        "    else {\n"
        "        if (SvIOK(return_sv)) {\n"
        "            // It's already no more than 32 bits, so don't convert.\n"
        "            retval = SvIV(return_sv);\n"
        "        }\n"
        "        else {\n"
        "            // Maybe lossy.\n"
        "            double temp = SvNV(return_sv);\n"
        "            retval = (int64_t)temp;\n"
        "        }\n"
        "    }\n"
        "    FREETMPS;\n"
        "    LEAVE;\n"
        "    return retval;\n"
        "}\n"
        "\n"
        "static double\n"
        "S_finish_callback_f64(pTHX_ const char *meth_name) {\n"
        "    SV *return_sv = SI_do_callback_sv(aTHX_ meth_name);\n"
        "    double retval = SvNV(return_sv);\n"
        "    FREETMPS;\n"
        "    LEAVE;\n"
        "    return retval;\n"
        "}\n"
        "\n"
        "static cfish_Obj*\n"
        "S_finish_callback_obj(pTHX_ void *vself, const char *meth_name,\n"
        "                      int nullable) {\n"
        "    SV *return_sv = SI_do_callback_sv(aTHX_ meth_name);\n"
        "    cfish_Obj *retval\n"
        "        = XSBind_perl_to_cfish_nullable(aTHX_ return_sv, CFISH_OBJ);\n"
        "    FREETMPS;\n"
        "    LEAVE;\n"
        "    if (!nullable && !retval) {\n"
        "        CFISH_THROW(CFISH_ERR, \"%%o#%%s cannot return NULL\",\n"
        "                    cfish_Obj_get_class_name((cfish_Obj*)vself),\n"
        "                    meth_name);\n"
        "    }\n"
        "    return retval;\n"
        "}\n"
	"\n"
        "%s"
        "\n"
        "void\n"
        "%sbootstrap_perl() {\n"
        "    dTHX;\n"
        "    %sbootstrap_parcel();\n"
        "\n"
        "%s"
        "}\n"
        "\n"
        "%s";
    char *content
        = CFCUtil_sprintf(pattern, self->c_header, privacy_sym, prefix,
                          includes, cb_defs, prefix, prefix, alias_adds,
                          self->c_footer);

    const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy);
    char *host_c_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sperl.c", src_dest,
                                        prefix);
    CFCUtil_write_file(host_c_path, content, strlen(content));
    FREEMEM(host_c_path);

    FREEMEM(content);
    FREEMEM(alias_adds);
    FREEMEM(cb_defs);
    FREEMEM(includes);
    FREEMEM(ordered);
}
Ejemplo n.º 27
0
/* Generate the code which parses arguments passed from Python and converts
 * them to Clownfish-flavored C values.
 */
static char*
S_gen_arg_parsing(CFCParamList *param_list, int first_tick, char **error) {
    char *content = NULL;

    CFCVariable **vars = CFCParamList_get_variables(param_list);
    const char **vals = CFCParamList_get_initial_values(param_list);
    int num_vars = CFCParamList_num_vars(param_list);

    char *declarations = CFCUtil_strdup("");
    char *keywords     = CFCUtil_strdup("");
    char *format_str   = CFCUtil_strdup("");
    char *targets      = CFCUtil_strdup("");
    int optional_started = 0;

    for (int i = first_tick; i < num_vars; i++) {
        CFCVariable *var  = vars[i];
        const char  *val  = vals[i];

        const char *var_name = CFCVariable_get_name(var);
        keywords = CFCUtil_cat(keywords, "\"", var_name, "\", ", NULL);

        // Build up ParseTuple format string.
        if (val == NULL) {
            if (optional_started) { // problem!
                *error = "Required after optional param";
                goto CLEAN_UP_AND_RETURN;
            }
        }
        else {
            if (!optional_started) {
                optional_started = 1;
                format_str = CFCUtil_cat(format_str, "|", NULL);
            }
        }
        format_str = CFCUtil_cat(format_str, "O&", NULL);

        char *declaration = S_gen_declaration(var, val);
        declarations = CFCUtil_cat(declarations, declaration, NULL);
        FREEMEM(declaration);

        char *target = S_gen_target(var, val);
        targets = CFCUtil_cat(targets, target, NULL);
        FREEMEM(target);
    }

    char parse_pattern[] =
        "%s"
        "    char *keywords[] = {%sNULL};\n"
        "    char *fmt = \"%s\";\n"
        "    int ok = PyArg_ParseTupleAndKeywords(args, kwargs, fmt,\n"
        "        keywords%s);\n"
        "    if (!ok) { return NULL; }\n"
        ;
    content = CFCUtil_sprintf(parse_pattern, declarations, keywords,
                              format_str, targets);

CLEAN_UP_AND_RETURN:
    FREEMEM(declarations);
    FREEMEM(keywords);
    FREEMEM(format_str);
    FREEMEM(targets);
    return content;
}
Ejemplo n.º 28
0
void
CFCPerl_write_bindings(CFCPerl *self, const char *boot_class,
                       CFCParcel **parcels) {
    CFCUTIL_NULL_CHECK(boot_class);
    CFCUTIL_NULL_CHECK(parcels);

    CFCClass     **ordered  = CFCHierarchy_ordered_classes(self->hierarchy);
    CFCPerlClass **registry = CFCPerlClass_registry();
    char *privacy_syms    = CFCUtil_strdup("");
    char *includes        = CFCUtil_strdup("");
    char *generated_xs    = CFCUtil_strdup("");
    char *class_specs     = CFCUtil_strdup("");
    char *xsub_specs      = CFCUtil_strdup("");
    char *bootstrap_calls = CFCUtil_strdup("");
    char *hand_rolled_xs  = CFCUtil_strdup("");

    for (size_t i = 0; parcels[i]; ++i) {
        CFCParcel *parcel = parcels[i];

        // Set host_module_name for parcel.
        if (!CFCParcel_included(parcel) && CFCParcel_is_installed(parcel)) {
            CFCParcel_set_host_module_name(parcel, boot_class);
        }

        // Bake the parcel privacy defines into the XS, so it can be compiled
        // without any extra compiler flags.
        const char *privacy_sym = CFCParcel_get_privacy_sym(parcel);
        privacy_syms = CFCUtil_cat(privacy_syms, "#define ", privacy_sym,
                                   "\n", NULL);

        // Bootstrap calls.
        const char *prefix = CFCParcel_get_prefix(parcel);
        includes = CFCUtil_cat(includes, "#include \"", prefix, "perl.h\"\n",
                               NULL);
        bootstrap_calls = CFCUtil_cat(bootstrap_calls, "    ", prefix,
                                      "bootstrap_perl();\n", NULL);
    }

    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];

        CFCParcel *parcel = CFCClass_get_parcel(klass);
        int found = false;
        for (size_t j = 0; parcels[j]; j++) {
            if (parcel == parcels[j]) {
                found = true;
                break;
            }
        }
        if (!found) { continue; }

        // Pound-includes for generated headers.
        const char *include_h = CFCClass_include_h(klass);
        includes = CFCUtil_cat(includes, "#include \"", include_h, "\"\n",
                               NULL);

        if (CFCClass_inert(klass)) { continue; }
        int num_xsubs = 0;

        // Constructors.
        CFCPerlConstructor **constructors
            = CFCPerlClass_constructor_bindings(klass);
        for (size_t j = 0; constructors[j] != NULL; j++) {
            CFCPerlSub *xsub = (CFCPerlSub*)constructors[j];

            // Add the XSUB function definition.
            char *xsub_def
                = CFCPerlConstructor_xsub_def(constructors[j], klass);
            generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n",
                                       NULL);
            FREEMEM(xsub_def);

            // Add XSUB initialization at boot.
            xsub_specs = S_add_xsub_spec(xsub_specs, xsub);
            num_xsubs += 1;

            CFCBase_decref((CFCBase*)constructors[j]);
        }
        FREEMEM(constructors);

        // Methods.
        CFCPerlMethod **methods = CFCPerlClass_method_bindings(klass);
        for (size_t j = 0; methods[j] != NULL; j++) {
            CFCPerlSub *xsub = (CFCPerlSub*)methods[j];

            // Add the XSUB function definition.
            char *xsub_def = CFCPerlMethod_xsub_def(methods[j], klass);
            generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n",
                                       NULL);
            FREEMEM(xsub_def);

            // Add XSUB initialization at boot.
            xsub_specs = S_add_xsub_spec(xsub_specs, xsub);
            num_xsubs += 1;

            CFCBase_decref((CFCBase*)methods[j]);
        }
        FREEMEM(methods);

        // Append XSBind_ClassSpec entry.
        const char *class_name = CFCClass_get_name(klass);
        CFCClass *parent = CFCClass_get_parent(klass);
        char *parent_name;
        if (parent) {
            parent_name = CFCUtil_sprintf("\"%s\"", CFCClass_get_name(parent));
        }
        else {
            parent_name = CFCUtil_strdup("NULL");
        }
        char *class_spec = CFCUtil_sprintf("{ \"%s\", %s, %d }", class_name,
                                           parent_name, num_xsubs);
        const char *sep = class_specs[0] == '\0' ? "" : ",\n";
        class_specs = CFCUtil_cat(class_specs, sep, "        ", class_spec,
                                  NULL);
        FREEMEM(class_spec);
        FREEMEM(parent_name);
    }

    // Hand-rolled XS.
    for (size_t i = 0; registry[i] != NULL; i++) {
        CFCPerlClass *perl_class = registry[i];

        CFCParcel *parcel = CFCPerlClass_get_parcel(perl_class);
        int found = false;
        for (size_t j = 0; parcels[j]; j++) {
            if (parcel == parcels[j]) {
                found = true;
                break;
            }
        }
        if (!found) { continue; }

        const char *xs = CFCPerlClass_get_xs_code(perl_class);
        hand_rolled_xs = CFCUtil_cat(hand_rolled_xs, xs, "\n", NULL);
    }

    const char pattern[] =
        "%s" // Header.
        "\n"
        "%s" // Privacy syms.
        "\n"
        "#include \"XSBind.h\"\n"
        "%s" // Includes.
        "\n"
        "#ifndef XS_INTERNAL\n"
        "  #define XS_INTERNAL XS\n"
        "#endif\n"
        "\n"
        "%s" // Generated XS.
        "\n"
        "MODULE = %s   PACKAGE = %s\n" // Boot class.
        "\n"
        "BOOT:\n"
        "{\n"
        "    static const cfish_XSBind_ClassSpec class_specs[] = {\n"
        "%s\n" // Class specs.
        "    };\n"
        "    static const cfish_XSBind_XSubSpec xsub_specs[] = {\n"
        "%s\n" // XSUB specs.
        "    };\n"
        "    size_t num_classes\n"
        "        = sizeof(class_specs) / sizeof(class_specs[0]);\n"
        "    const char* file = __FILE__;\n"
        "\n"
        "%s" // Bootstrap calls.
        "\n"
        "    cfish_XSBind_bootstrap(aTHX_ num_classes, class_specs,\n"
        "                           xsub_specs, file);\n"
        "}\n"
        "\n"
        "%s" // Hand-rolled XS.
        "\n"
        "%s"; // Footer
    char *contents
        = CFCUtil_sprintf(pattern, self->c_header, privacy_syms, includes,
                          generated_xs, boot_class, boot_class, class_specs,
                          xsub_specs, bootstrap_calls, hand_rolled_xs,
                          self->c_footer);

    // Derive path to generated .xs file.
    char *xs_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.xs", self->lib_dir,
                                    boot_class);
    S_replace_double_colons(xs_path, CHY_DIR_SEP_CHAR);

    // Write out if there have been any changes.
    CFCUtil_write_if_changed(xs_path, contents, strlen(contents));

    FREEMEM(xs_path);
    FREEMEM(contents);
    FREEMEM(hand_rolled_xs);
    FREEMEM(bootstrap_calls);
    FREEMEM(xsub_specs);
    FREEMEM(class_specs);
    FREEMEM(generated_xs);
    FREEMEM(includes);
    FREEMEM(privacy_syms);
    FREEMEM(ordered);
}
Ejemplo n.º 29
0
char*
CFCGoClass_go_typing(CFCGoClass *self) {
    char *content = NULL;
    if (!self->client) {
        CFCUtil_die("Can't find class for %s", self->class_name);
    }
    else if (CFCClass_inert(self->client)) {
        content = CFCUtil_strdup("");
    } else {
        const char *short_struct = CFCClass_get_struct_sym(self->client);

        CFCClass *parent = CFCClass_get_parent(self->client);
        char *parent_type_str = NULL;
        if (parent) {
            const char *parent_struct = CFCClass_get_struct_sym(parent);
            CFCParcel *parent_parcel = CFCClass_get_parcel(parent);
            if (parent_parcel == self->parcel) {
                parent_type_str = CFCUtil_strdup(parent_struct);
            }
            else {
                char *parent_package
                    = CFCGoTypeMap_go_short_package(parent_parcel);
                parent_type_str = CFCUtil_sprintf("%s.%s", parent_package,
                                                  parent_struct);
                FREEMEM(parent_package);
            }
        }

        char *go_struct_def;
        if (parent && !self->suppress_struct) {
            go_struct_def
                = CFCUtil_sprintf("type %sIMP struct {\n\t%sIMP\n}\n",
                                  short_struct, parent_type_str);
        }
        else {
            go_struct_def = CFCUtil_strdup("");
        }

        char *parent_iface;
        if (parent) {
            parent_iface = CFCUtil_sprintf("\t%s\n", parent_type_str);
        }
        else {
            parent_iface = CFCUtil_strdup("");
        }

        char *novel_iface = CFCUtil_strdup("");
        S_lazy_init_method_bindings(self);
        for (int i = 0; self->method_bindings[i] != NULL; i++) {
            CFCGoMethod *meth_binding = self->method_bindings[i];
            CFCMethod *method = CFCGoMethod_get_client(meth_binding);
            if (method) {
                if (!CFCMethod_novel(method)) {
                    continue;
                }
                const char *sym = CFCMethod_get_name(method);
                if (!CFCClass_fresh_method(self->client, sym)) {
                    continue;
                }
            }

            const char *sig = CFCGoMethod_get_sig(meth_binding, self->client);
            novel_iface = CFCUtil_cat(novel_iface, "\t", sig, "\n", NULL);
        }

        char pattern[] =
            "type %s interface {\n"
            "%s"
            "%s"
            "}\n"
            "\n"
            "%s"
            ;
        content = CFCUtil_sprintf(pattern, short_struct, parent_iface,
                                  novel_iface, go_struct_def);
        FREEMEM(parent_type_str);
        FREEMEM(go_struct_def);
        FREEMEM(parent_iface);
    }
    return content;
}
Ejemplo n.º 30
0
static void
S_write_boot_c(CFCPerl *self) {
    CFCClass  **ordered   = CFCHierarchy_ordered_classes(self->hierarchy);
    CFCParcel **parcels   = CFCParcel_all_parcels();
    char *pound_includes  = CFCUtil_strdup("");
    char *bootstrap_code  = CFCUtil_strdup("");
    char *alias_adds      = CFCUtil_strdup("");
    char *isa_pushes      = CFCUtil_strdup("");

    for (size_t i = 0; parcels[i]; ++i) {
        if (!CFCParcel_included(parcels[i])) {
            const char *prefix = CFCParcel_get_prefix(parcels[i]);
            bootstrap_code = CFCUtil_cat(bootstrap_code, "    ", prefix,
                                         "bootstrap_parcel();\n", NULL);
        }
    }

    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_included(klass)) { continue; }

        const char *class_name = CFCClass_get_class_name(klass);
        const char *include_h  = CFCClass_include_h(klass);
        pound_includes = CFCUtil_cat(pound_includes, "#include \"",
                                     include_h, "\"\n", NULL);

        if (CFCClass_inert(klass)) { continue; }

        // Add aliases for selected KinoSearch classes which allow old indexes
        // to be read.
        CFCPerlClass *class_binding = CFCPerlClass_singleton(class_name);
        if (class_binding) {
            const char *vtable_var = CFCClass_full_vtable_var(klass);
            const char **aliases
                = CFCPerlClass_get_class_aliases(class_binding);
            for (size_t j = 0; aliases[j] != NULL; j++) {
                const char *alias = aliases[j];
                size_t alias_len  = strlen(alias);
                const char pattern[] =
                    "    cfish_VTable_add_alias_to_registry("
                    "%s, \"%s\", %u);\n";
                char *alias_add
                    = CFCUtil_sprintf(pattern, vtable_var, alias,
                                      (unsigned)alias_len);
                alias_adds = CFCUtil_cat(alias_adds, alias_add, NULL);
                FREEMEM(alias_add);
            }

            char *metadata_code
                = CFCPerlClass_method_metadata_code(class_binding);
            alias_adds = CFCUtil_cat(alias_adds, metadata_code, NULL);
            FREEMEM(metadata_code);
        }

        CFCClass *parent = CFCClass_get_parent(klass);
        if (parent) {
            const char *parent_class_name = CFCClass_get_class_name(parent);
            isa_pushes
                = CFCUtil_cat(isa_pushes, "    isa = get_av(\"",
                              class_name, "::ISA\", 1);\n", NULL);
            isa_pushes
                = CFCUtil_cat(isa_pushes, "    av_push(isa, newSVpv(\"", 
                              parent_class_name, "\", 0));\n", NULL);
        }
    }

    const char pattern[] =
        "%s\n"
        "\n"
        "#include \"cfish_parcel.h\"\n"
        "#include \"EXTERN.h\"\n"
        "#include \"perl.h\"\n"
        "#include \"XSUB.h\"\n"
        "#include \"boot.h\"\n"
        "#include \"Clownfish/String.h\"\n"
        "#include \"Clownfish/VTable.h\"\n"
        "%s\n"
        "\n"
        "void\n"
        "%s() {\n"
        "%s"
        "\n"
        "%s"
        "\n"
        "    AV *isa;\n"
        "%s"
        "}\n"
        "\n"
        "%s\n"
        "\n";
    char *content
        = CFCUtil_sprintf(pattern, self->header, pound_includes,
                          self->boot_func, bootstrap_code, alias_adds,
                          isa_pushes, self->footer);

    const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy);
    char *boot_c_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "boot.c", src_dest);
    CFCUtil_write_file(boot_c_path, content, strlen(content));
    FREEMEM(boot_c_path);

    FREEMEM(content);
    FREEMEM(isa_pushes);
    FREEMEM(alias_adds);
    FREEMEM(bootstrap_code);
    FREEMEM(pound_includes);
    FREEMEM(parcels);
    FREEMEM(ordered);
}