/* Binds the given value to the specified attribute. */ static void bind_attribute_boxed(PARROT_INTERP, STable *st, void *data, PMC *class_handle, STRING *name, INTVAL hint, PMC *value) { P6opaqueREPRData *repr_data = (P6opaqueREPRData *)st->REPR_data; INTVAL slot; /* Try the slot allocation first. */ slot = hint >= 0 && !(repr_data->mi) ? hint : try_get_slot(interp, repr_data, class_handle, name); if (slot >= 0) { STable *st = repr_data->flattened_stables[slot]; if (st) { if (value->vtable->base_type == smo_id && st == STABLE(value)) st->REPR->copy_to(interp, st, OBJECT_BODY(value), (char *)data + repr_data->attribute_offsets[slot]); else Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION, "Type mismatch when storing value to attribute '%Ss' on class '%Ss'", name, VTABLE_get_string(interp, introspection_call(interp, class_handle, STABLE(class_handle)->HOW, Parrot_str_new_constant(interp, "name"), 0))); } else { set_pmc_at_offset(data, repr_data->attribute_offsets[slot], value); } } else { /* Otherwise, complain that the attribute doesn't exist. */ no_such_attribute(interp, "bind", class_handle, name); } }
/* Helper for finding a slot number. */ static INTVAL try_get_slot(PARROT_INTERP, P6opaqueREPRData *repr_data, PMC *class_key, STRING *name) { INTVAL slot = -1; if (repr_data->name_to_index_mapping) { P6opaqueNameMap *cur_map_entry = repr_data->name_to_index_mapping; while (cur_map_entry->class_key != NULL) { if (cur_map_entry->class_key == class_key) { if (!PMC_IS_NULL(cur_map_entry->name_map)) { PMC *slot_pmc = VTABLE_get_pmc_keyed_str(interp, cur_map_entry->name_map, name); if (!PMC_IS_NULL(slot_pmc)) slot = VTABLE_get_integer(interp, slot_pmc); break; } else { Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION, "Null attribute map for P6opaque in class '%Ss'", VTABLE_get_string(interp, introspection_call(interp, class_key, STABLE(class_key)->HOW, Parrot_str_new_constant(interp, "name"), 0))); } } cur_map_entry++; } } return slot; }
/* Helper for complaining about attrbiute access errors. */ static void no_such_attribute(PARROT_INTERP, char *action, PMC *class_handle, STRING *name) { Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION, "Can not %s non-existant attribute '%Ss' on class '%Ss'", action, name, VTABLE_get_string(interp, introspection_call(interp, class_handle, STABLE(class_handle)->HOW, Parrot_str_new_constant(interp, "name"), 0))); }
/* Helper for complaining about attribute access errors. */ PARROT_DOES_NOT_RETURN static void no_such_attribute(PARROT_INTERP, const char *action, PMC *class_handle, STRING *name) { Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION, "Can not %s attribute '%Ss' declared in class '%Ss' with this object", action, name, VTABLE_get_string(interp, introspection_call(interp, class_handle, STABLE(class_handle)->HOW, Parrot_str_new_constant(interp, "name"), 0))); }
static void bind_attribute_ref(PARROT_INTERP, STable *st, void *data, PMC *class_handle, STRING *name, INTVAL hint, void *value) { P6opaqueREPRData *repr_data = (P6opaqueREPRData *)st->REPR_data; INTVAL slot; /* Try to find the slot. */ slot = hint >= 0 && !(repr_data->mi) ? hint : try_get_slot(interp, repr_data, class_handle, name); if (slot >= 0) { STable *st = repr_data->flattened_stables[slot]; if (st) st->REPR->copy_to(interp, st, value, (char *)data + repr_data->attribute_offsets[slot]); else Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION, "Can not bind by reference to non-flattened attribute '%Ss' on class '%Ss'", name, VTABLE_get_string(interp, introspection_call(interp, class_handle, STABLE(class_handle)->HOW, Parrot_str_new_constant(interp, "name"), 0))); } else { /* Otherwise, complain that the attribute doesn't exist. */ no_such_attribute(interp, "bind", class_handle, name); } }
/* Performs a change of type, where possible. */ static void change_type(PARROT_INTERP, PMC *obj, PMC *new_type) { P6opaqueInstance *instance = (P6opaqueInstance *)PMC_data(obj); P6opaqueREPRData *cur_repr_data = (P6opaqueREPRData *)STABLE(obj)->REPR_data; P6opaqueREPRData *new_repr_data = (P6opaqueREPRData *)STABLE(new_type)->REPR_data; STRING *mro_str = Parrot_str_new_constant(interp, "mro"); PMC *cur_mro, *new_mro; INTVAL cur_mro_elems, new_mro_elems, mro_is_suffix; /* Ensure we're not trying to change the type of a type object. */ if (PObj_flag_TEST(private0, obj)) Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION, "Cannot change the type of a type object"); /* Ensure that the destination type REPR is P6opaque also. */ if (REPR(obj) != REPR(new_type)) Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION, "P6opaque can only change type to another type with P6opaque REPR"); /* Ensure that MRO of new type has current type's MRO as a suffix. */ mro_is_suffix = 1; cur_mro = introspection_call(interp, STABLE(obj)->WHAT, STABLE(obj)->HOW, mro_str, 0); new_mro = introspection_call(interp, STABLE(new_type)->WHAT, STABLE(new_type)->HOW, mro_str, 0); cur_mro_elems = VTABLE_elements(interp, cur_mro); new_mro_elems = VTABLE_elements(interp, new_mro); if (new_mro_elems >= cur_mro_elems) { INTVAL start = new_mro_elems - cur_mro_elems; INTVAL i; for (i = 0; i < cur_mro_elems; i++) { PMC *cur_elem = VTABLE_get_pmc_keyed_int(interp, cur_mro, i); PMC *new_elem = VTABLE_get_pmc_keyed_int(interp, new_mro, i + start); if (decontainerize(interp, cur_elem) != decontainerize(interp, new_elem)) { mro_is_suffix = 0; break; } } } else { mro_is_suffix = 0; } if (!mro_is_suffix) Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION, "P6opaque only supports type changes where the MRO of the original type is a suffix of the MRO of the new type"); /* If the new REPR never calculated it's object layout, do so now. */ if (!new_repr_data->allocation_size) { compute_allocation_strategy(interp, new_type, new_repr_data); PARROT_GC_WRITE_BARRIER(interp, STABLE_PMC(new_type)); } /* Reallocate ourself to the new allocation size, if needed, and * ensure new chunk of the memory is zeroed. Note that we can't * really re-alloc, we need to go deal with the fixed size pool * allocator. */ if (new_repr_data->allocation_size > cur_repr_data->allocation_size) { P6opaqueInstance *new_body = (P6opaqueInstance *) Parrot_gc_allocate_fixed_size_storage(interp, new_repr_data->allocation_size); memset(new_body, 0, new_repr_data->allocation_size); memcpy(new_body, instance, cur_repr_data->allocation_size); PMC_data(obj) = new_body; Parrot_gc_free_fixed_size_storage(interp, cur_repr_data->allocation_size, instance); instance = new_body; } /* Finally, we're ready to switch the S-Table pointer. */ instance->common.stable = STABLE_PMC(new_type); PARROT_GC_WRITE_BARRIER(interp, obj); }
/* Locates all of the attributes. Puts them onto a flattened, ordered * list of attributes (populating the passed flat_list). Also builds * the index mapping for doing named lookups. Note index is not related * to the storage position. */ static PMC * index_mapping_and_flat_list(PARROT_INTERP, PMC *WHAT, P6opaqueREPRData *repr_data) { PMC *flat_list = Parrot_pmc_new(interp, enum_class_ResizablePMCArray); PMC *class_list = Parrot_pmc_new(interp, enum_class_ResizablePMCArray); PMC *attr_map_list = Parrot_pmc_new(interp, enum_class_ResizablePMCArray); STRING *attributes_str = Parrot_str_new_constant(interp, "attributes"); STRING *parents_str = Parrot_str_new_constant(interp, "parents"); STRING *name_str = Parrot_str_new_constant(interp, "name"); STRING *mro_str = Parrot_str_new_constant(interp, "mro"); INTVAL current_slot = 0; INTVAL num_classes, i; P6opaqueNameMap * result = NULL; /* Get the MRO. */ PMC *mro = introspection_call(interp, WHAT, STABLE(WHAT)->HOW, mro_str, 0); INTVAL mro_idx = VTABLE_elements(interp, mro); /* Walk through the parents list. */ while (mro_idx) { /* Get current class in MRO. */ PMC *current_class = decontainerize(interp, VTABLE_get_pmc_keyed_int(interp, mro, --mro_idx)); PMC *HOW = STABLE(current_class)->HOW; /* Get its local parents. */ PMC *parents = introspection_call(interp, current_class, HOW, parents_str, 1); INTVAL num_parents = VTABLE_elements(interp, parents); /* Get attributes and iterate over them. */ PMC *attributes = introspection_call(interp, current_class, HOW, attributes_str, 1); PMC *attr_map = PMCNULL; PMC *attr_iter = VTABLE_get_iter(interp, attributes); while (VTABLE_get_bool(interp, attr_iter)) { /* Get attribute. */ PMC * attr = VTABLE_shift_pmc(interp, attr_iter); /* Get its name. */ PMC *name_pmc = accessor_call(interp, attr, name_str); STRING *name = VTABLE_get_string(interp, name_pmc); /* Allocate a slot. */ if (PMC_IS_NULL(attr_map)) attr_map = Parrot_pmc_new(interp, enum_class_Hash); VTABLE_set_pmc_keyed_str(interp, attr_map, name, Parrot_pmc_new_init_int(interp, enum_class_Integer, current_slot)); current_slot++; /* Push attr onto the flat list. */ VTABLE_push_pmc(interp, flat_list, attr); } /* Add to class list and map list. */ VTABLE_push_pmc(interp, class_list, current_class); VTABLE_push_pmc(interp, attr_map_list, attr_map); /* If there's more than one parent, flag that we in an MI * situation. */ if (num_parents > 1) repr_data->mi = 1; } /* We can now form the name map. */ num_classes = VTABLE_elements(interp, class_list); result = (P6opaqueNameMap *) mem_sys_allocate_zeroed(sizeof(P6opaqueNameMap) * (1 + num_classes)); for (i = 0; i < num_classes; i++) { result[i].class_key = VTABLE_get_pmc_keyed_int(interp, class_list, i); result[i].name_map = VTABLE_get_pmc_keyed_int(interp, attr_map_list, i); } repr_data->name_to_index_mapping = result; return flat_list; }