Ejemplo n.º 1
0
static void
S_run_parcel_tests(CFCTest *test) {
    {
        CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, NULL);
        OK(test, parcel != NULL, "new");
        OK(test, !CFCParcel_included(parcel), "not included");
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        CFCFileSpec *file_spec = CFCFileSpec_new(".", "Parcel", true);
        CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, file_spec);
        OK(test, CFCParcel_included(parcel), "included");
        CFCBase_decref((CFCBase*)parcel);
        CFCBase_decref((CFCBase*)file_spec);
    }

    {
        const char *json =
            "        {\n"
            "            \"name\": \"Crustacean\",\n"
            "            \"nickname\": \"Crust\",\n"
            "            \"version\": \"v0.1.0\"\n"
            "        }\n";
        CFCParcel *parcel = CFCParcel_new_from_json(json, NULL);
        OK(test, parcel != NULL, "new_from_json");
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        const char *path = "t" CHY_DIR_SEP "cfbase" CHY_DIR_SEP "Animal.cfp";
        CFCParcel *parcel = CFCParcel_new_from_file(path, NULL);
        OK(test, parcel != NULL, "new_from_file");
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        CFCParcel *parcel = CFCParcel_new("Crustacean", "Crust", NULL, NULL);
        CFCParcel_register(parcel);
        STR_EQ(test, CFCVersion_get_vstring(CFCParcel_get_version(parcel)),
               "v0", "get_version");

        CFCBase_decref((CFCBase*)parcel);
        CFCParcel_reap_singletons();
    }

    {
        const char *json =
            "        {\n"
            "            \"name\": \"Crustacean\",\n"
            "            \"version\": \"v0.1.0\",\n"
            "            \"prerequisites\": {\n"
            "                \"Clownfish\": null,\n"
            "                \"Arthropod\": \"v30.104.5\"\n"
            "            }\n"
            "        }\n";
        CFCParcel *parcel = CFCParcel_new_from_json(json, NULL);

        CFCPrereq **prereqs = CFCParcel_get_prereqs(parcel);
        OK(test, prereqs != NULL, "prereqs");

        CFCPrereq *cfish = prereqs[0];
        OK(test, cfish != NULL, "prereqs[0]");
        const char *cfish_name = CFCPrereq_get_name(cfish);
        STR_EQ(test, cfish_name, "Clownfish", "prereqs[0] name");
        CFCVersion *v0            = CFCVersion_new("v0");
        CFCVersion *cfish_version = CFCPrereq_get_version(cfish);
        INT_EQ(test, CFCVersion_compare_to(cfish_version, v0), 0,
               "prereqs[0] version");

        CFCPrereq *apod = prereqs[1];
        OK(test, apod != NULL, "prereqs[1]");
        const char *apod_name = CFCPrereq_get_name(apod);
        STR_EQ(test, apod_name, "Arthropod", "prereqs[1] name");
        CFCVersion *v30_104_5    = CFCVersion_new("v30.104.5");
        CFCVersion *apod_version = CFCPrereq_get_version(apod);
        INT_EQ(test, CFCVersion_compare_to(apod_version, v30_104_5), 0,
               "prereqs[1] version");

        OK(test, prereqs[2] == NULL, "prereqs[2]");

        CFCBase_decref((CFCBase*)v30_104_5);
        CFCBase_decref((CFCBase*)v0);
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        CFCFileSpec *foo_file_spec = CFCFileSpec_new(".", "Foo", true);
        CFCParcel *foo = CFCParcel_new("Foo", NULL, NULL, foo_file_spec);
        CFCParcel_register(foo);

        CFCVersion *cfish_version = CFCVersion_new("v0.8.7");
        CFCFileSpec *cfish_file_spec
            = CFCFileSpec_new(".", "Clownfish", true);
        CFCParcel *cfish
            = CFCParcel_new("Clownfish", NULL, cfish_version, cfish_file_spec);
        CFCParcel_register(cfish);

        const char *crust_json =
            "        {\n"
            "            \"name\": \"Crustacean\",\n"
            "            \"version\": \"v0.1.0\",\n"
            "            \"prerequisites\": {\n"
            "                \"Clownfish\": \"v0.8.5\",\n"
            "            }\n"
            "        }\n";
        CFCParcel *crust = CFCParcel_new_from_json(crust_json, NULL);
        CFCParcel_register(crust);

        CFCParcel_check_prereqs(crust);
        INT_EQ(test, CFCParcel_required(foo), false, "parcel not required");
        INT_EQ(test, CFCParcel_required(cfish), true, "prereq required");
        INT_EQ(test, CFCParcel_required(crust), true, "self required");

        CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(crust);
        OK(test, prereq_parcels[0] != NULL, "prereq_parcels[0]");
        const char *name = CFCParcel_get_name(prereq_parcels[0]);
        STR_EQ(test, name, "Clownfish", "prereq_parcels[0] name");
        OK(test, prereq_parcels[1] == NULL, "prereq_parcels[0]");

        OK(test, CFCParcel_has_prereq(crust, cfish), "has_prereq");
        OK(test, CFCParcel_has_prereq(crust, crust), "has_prereq self");
        OK(test, !CFCParcel_has_prereq(crust, foo), "has_prereq false");

        CFCParcel_add_struct_sym(cfish, "Swim");
        CFCParcel_add_struct_sym(crust, "Pinch");
        CFCParcel_add_struct_sym(foo, "Bar");
        CFCParcel *found;
        found = CFCParcel_lookup_struct_sym(crust, "Swim");
        OK(test, found == cfish, "lookup_struct_sym prereq");
        found = CFCParcel_lookup_struct_sym(crust, "Pinch");
        OK(test, found == crust, "lookup_struct_sym self");
        found = CFCParcel_lookup_struct_sym(crust, "Bar");
        OK(test, found == NULL, "lookup_struct_sym other");

        FREEMEM(prereq_parcels);
        CFCBase_decref((CFCBase*)crust);
        CFCBase_decref((CFCBase*)cfish_version);
        CFCBase_decref((CFCBase*)cfish_file_spec);
        CFCBase_decref((CFCBase*)cfish);
        CFCBase_decref((CFCBase*)foo_file_spec);
        CFCBase_decref((CFCBase*)foo);
        CFCParcel_reap_singletons();
    }
}
Ejemplo n.º 2
0
CFCClass*
CFCClass_do_create(CFCClass *self, struct CFCParcel *parcel,
                   const char *exposure, const char *name,
                   const char *nickname, CFCDocuComment *docucomment,
                   CFCFileSpec *file_spec, const char *parent_class_name,
                   int is_final, int is_inert, int is_abstract) {
    CFCUTIL_NULL_CHECK(parcel);
    CFCUTIL_NULL_CHECK(name);
    exposure = exposure ? exposure  : "parcel";

    // Validate.
    if (!S_validate_exposure(exposure)) {
        CFCBase_decref((CFCBase*)self);
        CFCUtil_die("Invalid exposure: '%s'", exposure);
    }
    if (!CFCClass_validate_class_name(name)) {
        CFCBase_decref((CFCBase*)self);
        CFCUtil_die("Invalid name: '%s'", name);
    }

    const char *last_colon = strrchr(name, ':');
    const char *struct_sym = last_colon ? last_colon + 1 : name;

    // Derive nickname if necessary, then validate.
    const char *real_nickname = NULL;
    if (nickname) {
        real_nickname = nickname;
    }
    else {
        real_nickname = struct_sym;
    }
    if (!S_validate_nickname(real_nickname)) {
        CFCBase_decref((CFCBase*)self);
        CFCUtil_die("Invalid nickname: '%s'", real_nickname);
    }

    // Default parent class name is "Clownfish::Obj".
    if (!is_inert
        && !parent_class_name
        && strcmp(name, "Clownfish::Obj") != 0
       ) {
        parent_class_name = "Clownfish::Obj";
    }

    // Assign.
    self->parcel          = (CFCParcel*)CFCBase_incref((CFCBase*)parcel);
    self->exposure        = CFCUtil_strdup(exposure);
    self->name            = CFCUtil_strdup(name);
    self->nickname        = CFCUtil_strdup(real_nickname);
    self->tree_grown      = false;
    self->parent          = NULL;
    self->children        = (CFCClass**)CALLOCATE(1, sizeof(CFCClass*));
    self->num_kids        = 0;
    self->functions       = (CFCFunction**)CALLOCATE(1, sizeof(CFCFunction*));
    self->num_functions   = 0;
    self->fresh_methods   = (CFCMethod**)CALLOCATE(1, sizeof(CFCMethod*));
    self->num_fresh_meths = 0;
    self->methods         = NULL;
    self->num_methods     = 0;
    self->fresh_vars      = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*));
    self->num_fresh_vars  = 0;
    self->member_vars     = NULL;
    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 *prefix = CFCClass_get_prefix(self);
    self->struct_sym        = CFCUtil_strdup(struct_sym);
    self->full_struct_sym   = CFCUtil_sprintf("%s%s", prefix, struct_sym);
    self->ivars_struct      = CFCUtil_sprintf("%sIVARS", struct_sym);
    self->full_ivars_struct = CFCUtil_sprintf("%s%s", prefix,
                                              self->ivars_struct);
    self->ivars_func        = CFCUtil_sprintf("%s_IVARS", self->nickname);
    self->full_ivars_func   = CFCUtil_sprintf("%s%s", prefix,
                                              self->ivars_func);
    self->full_ivars_offset = CFCUtil_sprintf("%s_OFFSET",
                                              self->full_ivars_func);

    const char *PREFIX = CFCClass_get_PREFIX(self);
    size_t struct_sym_len = strlen(struct_sym);
    char *short_class_var = (char*)MALLOCATE(struct_sym_len + 1);
    size_t i;
    for (i = 0; i < struct_sym_len; i++) {
        short_class_var[i] = toupper(struct_sym[i]);
    }
    short_class_var[struct_sym_len] = '\0';
    self->short_class_var = short_class_var;
    self->full_class_var  = CFCUtil_sprintf("%s%s", PREFIX, short_class_var);
    self->privacy_symbol  = CFCUtil_sprintf("C_%s", self->full_class_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;
    self->is_abstract = !!is_abstract;

    // Check for include flag mismatch.
    if (!CFCClass_included(self) && CFCParcel_included(parcel)) {
        CFCUtil_die("Class %s from source dir found in parcel %s from"
                    " include dir",
                    name, CFCParcel_get_name(parcel));
    }

    // Skip class if it's from an include dir and the parcel was already
    // processed in another source or include dir.
    const char *class_source_dir  = CFCClass_get_source_dir(self);
    const char *parcel_source_dir = CFCParcel_get_source_dir(parcel);
    if (!CFCClass_included(self)
        || !class_source_dir
        || !parcel_source_dir
        || strcmp(class_source_dir, parcel_source_dir) == 0
    ) {
        char *error;

        CFCUTIL_TRY {
            // Store in registry.
            S_register(self);
        }
        CFCUTIL_CATCH(error);

        if (error) {
            CFCBase_decref((CFCBase*)self);
            CFCUtil_rethrow(error);
        }

        CFCParcel_add_struct_sym(parcel, self->struct_sym);
    }