示例#1
0
static int
S_compare_class_name(const void *va, const void *vb) {
    const char *a = CFCClass_get_name(*(CFCClass**)va);
    const char *b = CFCClass_get_name(*(CFCClass**)vb);

    return strcmp(a, b);
}
示例#2
0
char*
CFCPerlPod_constructors_pod(CFCPerlPod *self, CFCClass *klass) {
    if (!self->num_constructors) {
        return CFCUtil_strdup("");
    }
    const char *class_name = CFCClass_get_name(klass);
    char *pod = CFCUtil_strdup("=head1 CONSTRUCTORS\n\n");
    for (size_t i = 0; i < self->num_constructors; i++) {
        NamePod slot = self->constructors[i];
        if (slot.pod) {
            pod = CFCUtil_cat(pod, slot.pod, "\n", NULL);
        }
        else {
            const char *func_name = slot.func ? slot.func : slot.alias;
            CFCFunction *pod_func = CFCClass_function(klass, func_name);
            if (!pod_func) {
                CFCUtil_die("Can't find constructor '%s' in class '%s'",
                            func_name, CFCClass_get_name(klass));
            }
            char *sub_pod
                = CFCPerlPod_gen_subroutine_pod((CFCCallable*)pod_func,
                                                slot.alias, klass, slot.sample,
                                                class_name, true);
            pod = CFCUtil_cat(pod, sub_pod, NULL);
            FREEMEM(sub_pod);
        }
    }
    return pod;
}
示例#3
0
int
S_do_propagate_modified(CFCHierarchy *self, CFCClass *klass, int modified) {
    const char *path_part = CFCClass_get_path_part(klass);
    CFCUTIL_NULL_CHECK(path_part);
    CFCFile *file = S_fetch_file(self, path_part);
    CFCUTIL_NULL_CHECK(file);
    const char *source_path = CFCFile_get_path(file);
    char *h_path = CFCFile_h_path(file, self->inc_dest);

    if (!CFCUtil_current(source_path, h_path)) {
        modified = true;
    }
    FREEMEM(h_path);
    if (modified) {
        CFCFile_set_modified(file, modified);
    }

    // Proceed to the next generation.
    int somebody_is_modified = modified;
    CFCClass **children = CFCClass_children(klass);
    for (size_t i = 0; children[i] != NULL; i++) {
        CFCClass *kid = children[i];
        if (CFCClass_final(klass)) {
            CFCUtil_die("Attempt to inherit from final class '%s' by '%s'",
                        CFCClass_get_name(klass),
                        CFCClass_get_name(kid));
        }
        if (S_do_propagate_modified(self, kid, modified)) {
            somebody_is_modified = 1;
        }
    }

    return somebody_is_modified;
}
示例#4
0
void
CFCParcel_add_class(CFCParcel *self, CFCClass *klass) {
    // Ensure unique class name.
    const char *class_name = CFCClass_get_name(klass);
    CFCClass *other = S_fetch_class(self, class_name, 2);
    if (other) {
        CFCUtil_die("Two classes with name %s", class_name);
    }

    const char *struct_sym = CFCClass_get_struct_sym(klass);
    const char *nickname   = CFCClass_get_nickname(klass);

    for (size_t i = 0; self->classes[i]; ++i) {
        CFCClass *other = self->classes[i];

        // Ensure unique struct symbol and nickname in parcel.
        if (strcmp(struct_sym, CFCClass_get_struct_sym(other)) == 0) {
            CFCUtil_die("Class name conflict between %s and %s",
                        CFCClass_get_name(klass), CFCClass_get_name(other));
        }
        if (strcmp(nickname, CFCClass_get_nickname(other)) == 0) {
            CFCUtil_die("Class nickname conflict between %s and %s",
                        CFCClass_get_name(klass), CFCClass_get_name(other));
        }
    }

    size_t num_classes = self->num_classes;
    size_t size = (num_classes + 2) * sizeof(CFCClass*);
    CFCClass **classes = (CFCClass**)REALLOCATE(self->classes, size);
    classes[num_classes]   = (CFCClass*)CFCBase_incref((CFCBase*)klass);
    classes[num_classes+1] = NULL;
    self->classes     = classes;
    self->num_classes = num_classes + 1;
}
示例#5
0
char*
CFCPyMethod_constructor_wrapper(CFCFunction *init_func, CFCClass *invoker) {
    CFCParamList *param_list  = CFCFunction_get_param_list(init_func);
    const char *self_type
        = CFCType_to_c(CFCFunction_get_return_type(init_func));
    char *func_sym   = CFCFunction_full_func_sym(init_func, invoker);
    char *decs       = S_gen_decs(param_list, 1);
    char *increfs    = S_gen_arg_increfs(param_list, 1);
    char *decrefs    = S_gen_decrefs(param_list, 1);
    const char *class_var  = CFCClass_full_class_var(invoker);
    const char *struct_sym = CFCClass_full_struct_sym(invoker);
    char *error = NULL;
    char *arg_parsing = S_gen_arg_parsing(param_list, 1, &error);
    if (error) {
        CFCUtil_die("%s in constructor for %s", error,
                    CFCClass_get_name(invoker));
    }
    if (!arg_parsing) {
        CFCUtil_die("Unexpected arg parsing error for %s",
                    CFCClass_get_name(invoker));
    }
    char *first_arg = CFCUtil_sprintf("(%s)CFISH_Class_Make_Obj(%s)",
                                      self_type, class_var);
    char *arg_list = S_gen_arg_list(param_list, first_arg);

    char pattern[] =
        "static PyObject*\n"
        "S_%s_PY_NEW(PyTypeObject *type, PyObject *args, PyObject *kwargs) {\n"
        "%s" // decs
        "%s" // arg_parsing
        "%s" // increfs
        "    %s self = NULL;\n"
        "    CFBIND_TRY(self = %s(%s));\n"
        "%s" // decrefs
        "    if (CFBind_migrate_cferr()) {\n"
        "        return NULL;\n"
        "    }\n"
        "    return (PyObject*)self;\n"
        "}\n"
        ;
    char *wrapper = CFCUtil_sprintf(pattern, struct_sym, decs,
                                    arg_parsing, increfs, self_type,
                                    func_sym, arg_list, decrefs);

    FREEMEM(arg_list);
    FREEMEM(first_arg);
    FREEMEM(func_sym);
    FREEMEM(decrefs);
    FREEMEM(increfs);
    FREEMEM(decs);
    FREEMEM(arg_parsing);
    return wrapper;
}
示例#6
0
static CFCClass*
S_fetch_class(CFCParcel *self, const char *class_name, int level) {
    // level == 0: Only search parcel.
    // level == 1: Search parcel and direct prereqs.
    // level == 2: Search parcel an indirect prereqs.

    for (size_t i = 0; self->classes[i]; ++i) {
        CFCClass *klass = self->classes[i];
        if (strcmp(CFCClass_get_name(klass), class_name) == 0) {
            return klass;
        }
    }

    if (level == 0) { return NULL; }
    if (level == 1) { level = 0; }

    for (size_t i = 0; self->prereqs[i]; ++i) {
        const char *prereq_name   = CFCPrereq_get_name(self->prereqs[i]);
        CFCParcel  *prereq_parcel = CFCParcel_fetch(prereq_name);

        CFCClass *klass = S_fetch_class(prereq_parcel, class_name, level);
        if (klass) { return klass; }
    }

    return NULL;
}
示例#7
0
static char*
S_gen_code_sample(CFCFunction *func, const char *alias, CFCClass *klass,
                  int is_constructor) {
    char *invocant = NULL;

    if (is_constructor) {
        invocant = CFCUtil_strdup(CFCClass_get_name(klass));
    }
    else {
        char *lower = S_camel_to_lower(CFCClass_get_struct_sym(klass));
        invocant = CFCUtil_sprintf("$%s", lower);
        FREEMEM(lower);
    }

    CFCParamList *param_list = CFCFunction_get_param_list(func);
    size_t        num_vars   = CFCParamList_num_vars(param_list);
    size_t        start      = is_constructor ? 0 : 1;
    char         *sample     = NULL;

    if (start == num_vars) {
        sample = CFCUtil_sprintf("    %s->%s();\n", invocant, alias);
    }
    else if (is_constructor || num_vars - start >= 2) {
        sample = S_gen_labeled_sample(invocant, alias, param_list, start);
    }
    else {
        sample = S_gen_positional_sample(invocant, alias, param_list, start);
    }

    return sample;
}
示例#8
0
static char*
S_gen_code_sample(CFCCallable *func, const char *alias, CFCClass *klass,
                  int is_constructor) {
    char *prologue       = CFCUtil_sprintf("");
    char *class_var_name = S_camel_to_lower(CFCClass_get_struct_sym(klass));

    CFCType *ret_type = CFCCallable_get_return_type(func);
    if (!CFCType_is_void(ret_type)) {
        char *ret_name = S_perl_var_name(ret_type, is_constructor);

        if (!is_constructor && strcmp(ret_name, class_var_name) == 0) {
            // Return type equals `klass`. Use a generic variable name
            // to avoid confusing code samples like
            // `my $string = $string->trim`.
            prologue = CFCUtil_cat(prologue, "my $result = ", NULL);
        }
        else {
            prologue = CFCUtil_cat(prologue, "my $", ret_name, " = ", NULL);
        }

        FREEMEM(ret_name);
    }

    if (is_constructor) {
        const char *invocant = CFCClass_get_name(klass);
        prologue = CFCUtil_cat(prologue, invocant, NULL);
    }
    else {
        prologue = CFCUtil_cat(prologue, "$", class_var_name, NULL);
    }

    prologue = CFCUtil_cat(prologue, "->", alias, NULL);

    CFCParamList *param_list = CFCCallable_get_param_list(func);
    int           num_vars   = CFCParamList_num_vars(param_list);
    int           start      = is_constructor ? 0 : 1;
    char         *sample     = NULL;

    if (start == num_vars) {
        sample = CFCUtil_sprintf("    %s();\n", prologue);
    }
    else if (is_constructor || num_vars - start >= 2) {
        sample = S_gen_labeled_sample(prologue, param_list, start);
    }
    else {
        sample = S_gen_positional_sample(prologue, param_list, start);
    }

    FREEMEM(class_var_name);
    FREEMEM(prologue);
    return sample;
}
示例#9
0
char*
CFCC_link_text(CFCUri *uri_obj) {
    char *link_text = NULL;
    CFCUriType type = CFCUri_get_type(uri_obj);

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

        case CFC_URI_NULL:
            link_text = CFCUtil_strdup("NULL");
            break;

        case CFC_URI_CLASS: {
            CFCClass *klass = CFCUri_get_class(uri_obj);
            const char *src = CFCClass_included(klass)
                              ? CFCClass_get_name(klass)
                              : CFCClass_get_struct_sym(klass);
            link_text = CFCUtil_strdup(src);
            break;
        }

        case CFC_URI_FUNCTION:
        case CFC_URI_METHOD: {
            const char *name = CFCUri_get_callable_name(uri_obj);
            link_text = CFCUtil_sprintf("%s()", name);
            break;
        }

        case CFC_URI_DOCUMENT: {
            CFCDocument *doc = CFCUri_get_document(uri_obj);
            const char *name = CFCDocument_get_name(doc);
            link_text = CFCUtil_strdup(name);
            break;
        }

        default:
            CFCUtil_die("Unsupported node type: %d", type);
            break;
    }

    return link_text;
}
示例#10
0
static char*
S_gen_class_bindings(CFCPython *self, CFCParcel *parcel,
                     const char *pymod_name, CFCClass **ordered) {
    char *bindings = CFCUtil_strdup("");
    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_included(klass)) {
            continue;
        }
        const char *class_name = CFCClass_get_name(klass);
        CFCPyClass *class_binding = CFCPyClass_singleton(class_name);
        if (!class_binding) {
            // No binding spec'd out, so create one using defaults.
            class_binding = CFCPyClass_new(klass);
            CFCPyClass_add_to_registry(class_binding);
        }

        char *code = CFCPyClass_gen_binding_code(class_binding);
        bindings = CFCUtil_cat(bindings, code, NULL);
        FREEMEM(code);
    }
    return bindings;
}
示例#11
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;
}
示例#12
0
static void
S_run_basic_tests(CFCTest *test) {
    CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST);
    STR_EQ(test, CFCHierarchy_get_dest(hierarchy), T_CFDEST, "get_dest");
    STR_EQ(test, CFCHierarchy_get_include_dest(hierarchy), T_CFDEST_INCLUDE,
           "get_include_dest");
    STR_EQ(test, CFCHierarchy_get_source_dest(hierarchy), T_CFDEST_SOURCE,
           "get_source_dest");

    CFCHierarchy_add_source_dir(hierarchy, T_CFBASE);
    const char **source_dirs = CFCHierarchy_get_source_dirs(hierarchy);
    STR_EQ(test, source_dirs[0], T_CFBASE, "source_dirs[0]");
    OK(test, source_dirs[1] == NULL, "source_dirs[1]");

    CFCHierarchy_build(hierarchy);

    CFCFile **files  = CFCHierarchy_files(hierarchy);
    CFCFile  *animal = NULL;
    CFCFile  *dog    = NULL;
    CFCFile  *util   = NULL;
    for (int i = 0; i < 3; ++i) {
        CFCFile *file = files[i];
        OK(test, file != NULL, "files[%d]", i);
        OK(test, !CFCFile_get_modified(file), "start off not modified");

        CFCBase **blocks = CFCFile_blocks(file);
        for (int j = 0; blocks[j]; ++j) {
            CFCBase *block = blocks[j];
            const char *cfc_class_name = CFCBase_get_cfc_class(block);
            if (strcmp(cfc_class_name, "Clownfish::CFC::Model::Class") == 0) {
                CFCClass *klass = (CFCClass*)block;
                const char *class_name = CFCClass_get_name(klass);
                if (strcmp(class_name, "Animal") == 0) {
                    animal = file;
                }
                else if (strcmp(class_name, "Animal::Dog") == 0) {
                    dog = file;
                }
                else if (strcmp(class_name, "Animal::Util") == 0) {
                    util = file;
                }
            }
        }
    }
    OK(test, files[3] == NULL, "recursed and found all three files");

    {
        CFCClass **ordered_classes = CFCHierarchy_ordered_classes(hierarchy);
        OK(test, ordered_classes[0] != NULL, "ordered_classes[0]");
        OK(test, ordered_classes[1] != NULL, "ordered_classes[1]");
        OK(test, ordered_classes[2] != NULL, "ordered_classes[2]");
        OK(test, ordered_classes[3] != NULL, "ordered_classes[3]");
        OK(test, ordered_classes[4] == NULL, "all classes");
        FREEMEM(ordered_classes);
    }

    // Generate fake C files, with times set to two seconds ago.
    time_t now       = time(NULL);
    time_t past_time = now - 2;
    static const char *const h_paths[] = {
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal.h",
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal" CHY_DIR_SEP "Dog.h",
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal" CHY_DIR_SEP "Util.h"
    };
    OK(test, CFCUtil_make_path(T_CFDEST_INCLUDE CHY_DIR_SEP "Animal"),
       "make_path");
    for (int i = 0; i < 3; ++i) {
        const char *h_path  = h_paths[i];
        const char *content = "#include <stdio.h>\n";
        CFCUtil_write_file(h_path, content, strlen(content));
        CFCTest_set_file_times(h_path, past_time);
    }

    char *cfh_path = CFCFile_cfh_path(animal, T_CFBASE);
    CFCTest_set_file_times(cfh_path, now);
    FREEMEM(cfh_path);

    CFCHierarchy_propagate_modified(hierarchy, 0);

    OK(test, CFCFile_get_modified(animal), "Animal modified");
    OK(test, CFCFile_get_modified(dog),
       "Parent's modification propagates to child's file");
    OK(test, !CFCFile_get_modified(util),
       "Modification doesn't propagate to inert class");

    for (int i = 0; i < 3; ++i) {
        remove(h_paths[i]);
    }
    rmdir(T_CFDEST_INCLUDE CHY_DIR_SEP "Animal");
    rmdir(T_CFDEST_INCLUDE);
    rmdir(T_CFDEST_SOURCE);
    rmdir(T_CFDEST);

    CFCBase_decref((CFCBase*)hierarchy);
    CFCClass_clear_registry();
    CFCParcel_reap_singletons();
}
示例#13
0
static void
S_run_include_tests(CFCTest *test) {
    {
        CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST);
        CFCHierarchy_add_source_dir(hierarchy, T_CFEXT);
        CFCHierarchy_add_include_dir(hierarchy, T_CFBASE);
        const char **include_dirs = CFCHierarchy_get_include_dirs(hierarchy);
        STR_EQ(test, include_dirs[0], T_CFBASE, "include_dirs[0]");
        OK(test, include_dirs[1] == NULL, "include_dirs[1]");

        CFCHierarchy_build(hierarchy);

        CFCClass **classes    = CFCHierarchy_ordered_classes(hierarchy);
        CFCClass  *rottweiler = NULL;;
        int num_classes;
        int num_source_classes = 0;
        for (num_classes = 0; classes[num_classes]; ++num_classes) {
            CFCClass *klass = classes[num_classes];
            int expect_included = 1;
            const char *class_name = CFCClass_get_name(klass);
            if (strcmp(class_name, "Animal::Rottweiler") == 0) {
                rottweiler      = klass;
                expect_included = 0;
                ++num_source_classes;
            }
            INT_EQ(test, CFCClass_included(klass), expect_included,
                   "included");
        }
        INT_EQ(test, num_classes, 5, "class count");
        INT_EQ(test, num_source_classes, 1, "source class count");
        STR_EQ(test, CFCClass_get_name(CFCClass_get_parent(rottweiler)),
               "Animal::Dog", "parent of included class");

        FREEMEM(classes);
        CFCBase_decref((CFCBase*)hierarchy);
        CFCClass_clear_registry();
        CFCParcel_reap_singletons();
    }

    {
        CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST);
        CFCHierarchy_add_source_dir(hierarchy, T_CFBASE);
        CFCHierarchy_add_source_dir(hierarchy, T_CFEXT);

        CFCHierarchy_build(hierarchy);

        CFCClass **classes    = CFCHierarchy_ordered_classes(hierarchy);
        CFCClass  *rottweiler = NULL;;
        int num_classes;
        for (num_classes = 0; classes[num_classes]; ++num_classes) {
            CFCClass *klass = classes[num_classes];
            const char *class_name = CFCClass_get_name(klass);
            if (strcmp(class_name, "Animal::Rottweiler") == 0) {
                rottweiler = klass;
            }
            OK(test, !CFCClass_included(klass), "not included");
        }
        INT_EQ(test, num_classes, 5, "class count");
        OK(test, rottweiler != NULL, "found rottweiler");
        STR_EQ(test, CFCClass_get_name(CFCClass_get_parent(rottweiler)),
               "Animal::Dog", "parent of class from second source");

        FREEMEM(classes);
        CFCBase_decref((CFCBase*)hierarchy);
        CFCClass_clear_registry();
        CFCParcel_reap_singletons();
    }

    {
        CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST);
        CFCHierarchy_add_include_dir(hierarchy, T_CFBASE);
        CFCHierarchy_add_include_dir(hierarchy, T_CFEXT);
        CFCHierarchy_add_prereq(hierarchy, "AnimalExtension");

        CFCHierarchy_build(hierarchy);

        CFCParcel *animal = CFCParcel_fetch("Animal");
        OK(test, animal != NULL, "parcel Animal registered");
        OK(test, CFCParcel_required(animal), "parcel Animal required");
        CFCParcel *animal_ext = CFCParcel_fetch("AnimalExtension");
        OK(test, animal_ext != NULL, "parcel AnimalExtension registered");
        OK(test, CFCParcel_required(animal_ext),
           "parcel AnimalExtension required");

        CFCClass **classes = CFCHierarchy_ordered_classes(hierarchy);
        int num_classes = 0;
        while (classes[num_classes]) {
            ++num_classes;
        }
        INT_EQ(test, num_classes, 5, "class count");

        FREEMEM(classes);
        CFCBase_decref((CFCBase*)hierarchy);
        CFCClass_clear_registry();
        CFCParcel_reap_singletons();
    }

    rmdir(T_CFDEST_INCLUDE);
    rmdir(T_CFDEST_SOURCE);
    rmdir(T_CFDEST);
}
示例#14
0
void
CFCParcel_connect_and_sort_classes(CFCParcel *self) {
    size_t num_classes = self->num_classes;
    size_t alloc_size  = (num_classes + 1) * sizeof(CFCClass*);
    CFCClass **classes = self->classes;
    CFCClass **sorted  = (CFCClass**)MALLOCATE(alloc_size);

    // Perform a depth-first search of the classes in the parcel, and store
    // the classes in the order that they were visited. This makes sure
    // that subclasses are sorted after their parents.
    //
    // To avoid a recursive algorithm, the end of the sorted array is used
    // as a stack for classes that have yet to be visited.
    //
    // Root and child classes are sorted by name to get a deterministic
    // order.

    // Set up parent/child relationship of classes and find subtree roots
    // in parcel.
    size_t todo = num_classes;
    for (size_t i = 0; i < num_classes; i++) {
        CFCClass *klass  = classes[i];
        CFCClass *parent = NULL;

        const char *parent_name = CFCClass_get_parent_class_name(klass);
        if (parent_name) {
            parent = S_fetch_class(self, parent_name, 1);
            if (!parent) {
                CFCUtil_die("Parent class '%s' of '%s' not found in parcel"
                            " '%s' or its prerequisites", parent_name,
                            CFCClass_get_name(klass), self->name);
            }
            CFCClass_add_child(parent, klass);
        }

        if (!parent || !CFCClass_in_parcel(parent, self)) {
            // Subtree root.
            sorted[--todo] = klass;
        }

        // Resolve types.
        CFCClass_resolve_types(klass);
    }

    qsort(&sorted[todo], num_classes - todo, sizeof(sorted[0]),
          S_compare_class_name);

    size_t num_sorted = 0;
    while (todo < num_classes) {
        CFCClass *klass = sorted[todo++];
        sorted[num_sorted++] = klass;

        // Push children on stack. Since this function is called first for
        // prereq parcels, we can be sure that the class doesn't have
        // children from another parcel yet.
        CFCClass **children = CFCClass_children(klass);
        size_t prev_todo = todo;
        for (size_t i = 0; children[i]; i++) {
            if (todo <= num_sorted) {
                CFCUtil_die("Internal error in CFCParcel_sort_classes");
            }
            sorted[--todo] = children[i];
        }

        qsort(&sorted[todo], prev_todo - todo, sizeof(sorted[0]),
              S_compare_class_name);
    }

    if (num_sorted != num_classes) {
        CFCUtil_die("Internal error in CFCParcel_sort_classes");
    }

    sorted[num_classes] = NULL;

    FREEMEM(self->classes);
    self->classes = sorted;
}
示例#15
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);
}
示例#16
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);
}
示例#17
0
void
CFCBindSpecs_add_class(CFCBindSpecs *self, CFCClass *klass) {
    if (CFCClass_inert(klass)) { return; }

    const char *class_name        = CFCClass_get_name(klass);
    const char *class_var         = CFCClass_full_class_var(klass);
    const char *ivars_offset_name = CFCClass_full_ivars_offset(klass);

    const char *flags = CFCClass_final(klass) ? "cfish_ClassSpec_FINAL" : "0";

    char *ivars_size = S_ivars_size(klass);

    char *parent_ptr = NULL;
    CFCClass *parent = CFCClass_get_parent(klass);
    if (!parent) {
        parent_ptr = CFCUtil_strdup("NULL");
    }
    else {
        if (CFCClass_in_same_parcel(klass, parent)) {
            parent_ptr
                = CFCUtil_sprintf("&%s", CFCClass_full_class_var(parent));
        }
        else {
            parent_ptr = CFCUtil_strdup("NULL");

            const char *class_name = CFCClass_get_name(klass);
            const char *parent_var = CFCClass_full_class_var(parent);
            const char *pattern =
                "    /* %s */\n"
                "    class_specs[%d].parent = &%s;\n";
            char *init_code = CFCUtil_sprintf(pattern, class_name,
                                              self->num_specs, parent_var);
            self->init_code = CFCUtil_cat(self->init_code, init_code, NULL);
            FREEMEM(init_code);
        }
    }

    int num_new_novel      = 0;
    int num_new_overridden = 0;
    int num_new_inherited  = 0;
    CFCMethod **methods = CFCClass_methods(klass);

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

        if (CFCMethod_is_fresh(method, klass)) {
            if (CFCMethod_novel(method)) {
                int meth_index = self->num_novel + num_new_novel;
                S_add_novel_meth(self, method, klass, meth_index);
                ++num_new_novel;
            }
            else {
                int meth_index = self->num_overridden + num_new_overridden;
                S_add_overridden_meth(self, method, klass, meth_index);
                ++num_new_overridden;
            }
        }
        else {
            int meth_index = self->num_inherited + num_new_inherited;
            S_add_inherited_meth(self, method, klass, meth_index);
            ++num_new_inherited;
        }
    }

    char pattern[] =
        "    {\n"
        "        &%s, /* class */\n"
        "        %s, /* parent */\n"
        "        \"%s\", /* name */\n"
        "        %s, /* ivars_size */\n"
        "        &%s, /* ivars_offset_ptr */\n"
        "        %d, /* num_novel */\n"
        "        %d, /* num_overridden */\n"
        "        %d, /* num_inherited */\n"
        "        %s /* flags */\n"
        "    }";
    char *class_spec
        = CFCUtil_sprintf(pattern, class_var, parent_ptr, class_name,
                          ivars_size, ivars_offset_name, num_new_novel,
                          num_new_overridden, num_new_inherited, flags);

    const char *sep = self->num_specs == 0 ? "" : ",\n";
    self->class_specs = CFCUtil_cat(self->class_specs, sep, class_spec, NULL);

    self->num_novel      += num_new_novel;
    self->num_overridden += num_new_overridden;
    self->num_inherited  += num_new_inherited;
    self->num_specs      += 1;

    FREEMEM(class_spec);
    FREEMEM(parent_ptr);
    FREEMEM(ivars_size);
}
示例#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;
}