Example #1
0
// Generate C code which initializes method metadata.
char*
CFCPerlClass_method_metadata_code(CFCPerlClass *self) {
    const char *class_var = CFCClass_full_class_var(self->client);
    CFCMethod **fresh_methods = CFCClass_fresh_methods(self->client);
    char *code = CFCUtil_strdup("");

    for (int i = 0; fresh_methods[i] != NULL; i++) {
        CFCMethod *method = fresh_methods[i];
        if (!CFCMethod_novel(method)) { continue; }

        const char *macro_sym = CFCMethod_get_macro_sym(method);
        const char *alias     = CFCMethod_get_host_alias(method);
        if (alias) {
            code = CFCUtil_cat(code, "    CFISH_Class_Add_Host_Method_Alias(",
                               class_var, ", \"", alias, "\", \"", macro_sym,
                               "\");\n", NULL);
        }
        if (CFCMethod_excluded_from_host(method)) {
            code = CFCUtil_cat(code, "    CFISH_Class_Exclude_Host_Method(",
                               class_var, ", \"", macro_sym, "\");\n", NULL);
        }
    }

    return code;
}
Example #2
0
static void
S_add_novel_meth(CFCBindSpecs *self, CFCMethod *method, CFCClass *klass,
                 int meth_index) {
    const char *meth_name = CFCMethod_get_name(method);
    const char *sep = meth_index == 0 ? "" : ",\n";

    char *full_override_sym;
    if (!CFCMethod_final(method) && !CFCMethod_excluded_from_host(method)) {
        full_override_sym = CFCMethod_full_override_sym(method, klass);
    }
    else {
        full_override_sym = CFCUtil_strdup("NULL");
    }

    char *imp_func        = CFCMethod_imp_func(method, klass);
    char *full_offset_sym = CFCMethod_full_offset_sym(method, klass);

    char pattern[] =
        "    {\n"
        "        &%s, /* offset */\n"
        "        \"%s\", /* name */\n"
        "        (cfish_method_t)%s, /* func */\n"
        "        (cfish_method_t)%s /* callback_func */\n"
        "    }";
    char *def
        = CFCUtil_sprintf(pattern, full_offset_sym, meth_name, imp_func,
                          full_override_sym);
    self->novel_specs = CFCUtil_cat(self->novel_specs, sep, def, NULL);

    FREEMEM(def);
    FREEMEM(full_offset_sym);
    FREEMEM(imp_func);
    FREEMEM(full_override_sym);
}
Example #3
0
static void
S_lazy_init_method_bindings(CFCGoClass *self) {
    if (self->method_bindings) {
        return;
    }
    CFCUTIL_NULL_CHECK(self->client);
    size_t        num_bound     = 0;
    CFCMethod   **fresh_methods = CFCClass_fresh_methods(self->client);
    CFCGoMethod **bound
        = (CFCGoMethod**)CALLOCATE(1, sizeof(CFCGoMethod*));

     // Iterate over the class's fresh methods.
    for (size_t i = 0; fresh_methods[i] != NULL; i++) {
        CFCMethod *method = fresh_methods[i];

        // Skip methods which have been explicitly excluded.
        if (CFCMethod_excluded_from_host(method)) {
            continue;
        }

        // Skip methods that shouldn't be bound.
        if (!CFCMethod_can_be_bound(method)) {
            continue;
        }

        // Only include novel methods.
        if (!CFCMethod_novel(method)) {
            continue;
        }
        const char *sym = CFCMethod_get_name(method);
        if (!CFCClass_fresh_method(self->client, sym)) {
            continue;
        }

        /* Create the binding, add it to the array.
         */
        CFCGoMethod *meth_binding = CFCGoMethod_new(method);
        size_t size = (num_bound + 2) * sizeof(CFCGoMethod*);
        bound = (CFCGoMethod**)REALLOCATE(bound, size);
        bound[num_bound] = meth_binding;
        num_bound++;
        bound[num_bound] = NULL;
    }

    self->method_bindings = bound;
    self->num_bound       = num_bound;
}
Example #4
0
static void
S_run_basic_tests(CFCTest *test) {
    CFCParser *parser = CFCParser_new();
    CFCParcel *neato_parcel
        = CFCTest_parse_parcel(test, parser, "parcel Neato;");

    CFCType *return_type = CFCTest_parse_type(test, parser, "Obj*");
    CFCParamList *param_list
        = CFCTest_parse_param_list(test, parser,
                                   "(Foo *self, int32_t count = 0)");
    CFCMethod *method
        = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                        "Return_An_Obj", return_type, param_list,
                        NULL, 0, 0);
    OK(test, method != NULL, "new");
    OK(test, CFCSymbol_parcel((CFCSymbol*)method),
       "parcel exposure by default");

    {
        CFCMethod *dupe
            = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                            "Return_An_Obj", return_type, param_list,
                            NULL, 0, 0);
        OK(test, CFCMethod_compatible(method, dupe), "compatible");
        CFCBase_decref((CFCBase*)dupe);
    }

    {
        CFCMethod *macro_sym_differs
            = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo", "Eat",
                            return_type, param_list, NULL, 0, 0);
        OK(test, !CFCMethod_compatible(method, macro_sym_differs),
           "different macro_sym spoils compatible");
        OK(test, !CFCMethod_compatible(macro_sym_differs, method),
           "... reversed");
        CFCBase_decref((CFCBase*)macro_sym_differs);
    }

    {
        static const char *param_strings[5] = {
            "(Foo *self, int32_t count = 0, int b)",
            "(Foo *self, int32_t count = 1)",
            "(Foo *self, int32_t count)",
            "(Foo *self, int32_t countess = 0)",
            "(Foo *self, uint32_t count = 0)"
        };
        static const char *test_names[5] = {
            "extra param",
            "different initial_value",
            "missing initial_value",
            "different param name",
            "different param type"
        };
        for (int i = 0; i < 5; ++i) {
            CFCParamList *other_param_list
                = CFCTest_parse_param_list(test, parser, param_strings[i]);
            CFCMethod *other
                = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                                "Return_An_Obj", return_type, other_param_list,
                                NULL, 0, 0);
            OK(test, !CFCMethod_compatible(method, other),
               "%s spoils compatible", test_names[i]);
            OK(test, !CFCMethod_compatible(other, method),
               "... reversed");
            CFCBase_decref((CFCBase*)other_param_list);
            CFCBase_decref((CFCBase*)other);
        }
    }

    {
        CFCParamList *self_differs_list
            = CFCTest_parse_param_list(test, parser,
                                       "(Bar *self, int32_t count = 0)");
        CFCMethod *self_differs
            = CFCMethod_new(neato_parcel, NULL, "Neato::Bar", "Bar",
                            "Return_An_Obj", return_type, self_differs_list,
                            NULL, 0, 0);
        OK(test, CFCMethod_compatible(method, self_differs),
           "different self type still compatible(),"
           " since can't test inheritance");
        OK(test, CFCMethod_compatible(self_differs, method),
           "... reversed");
        CFCBase_decref((CFCBase*)self_differs_list);
        CFCBase_decref((CFCBase*)self_differs);
    }

    {
        CFCMethod *aliased
            = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                            "Aliased", return_type, param_list,
                            NULL, 0, 0);
        OK(test, !CFCMethod_get_host_alias(aliased),
           "no host alias by default");
        CFCMethod_set_host_alias(aliased, "Host_Alias");
        STR_EQ(test, CFCMethod_get_host_alias(aliased), "Host_Alias",
               "set/get host alias");
        CFCBase_decref((CFCBase*)aliased);
    }

    {
        CFCMethod *excluded
            = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                            "Excluded", return_type, param_list,
                            NULL, 0, 0);
        OK(test, !CFCMethod_excluded_from_host(excluded),
           "not excluded by default");
        CFCMethod_exclude_from_host(excluded);
        OK(test, CFCMethod_excluded_from_host(excluded), "exclude from host");
        CFCBase_decref((CFCBase*)excluded);
    }

    CFCBase_decref((CFCBase*)parser);
    CFCBase_decref((CFCBase*)neato_parcel);
    CFCBase_decref((CFCBase*)return_type);
    CFCBase_decref((CFCBase*)param_list);
    CFCBase_decref((CFCBase*)method);

    CFCParcel_reap_singletons();
}
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;
}