#include "moar.h" /* This representation's function pointer table. */ static const MVMREPROps CUnion_this_repr; static void free_repr_data(MVMCUnionREPRData *repr_data); /* 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 MVMObject * index_mapping_and_flat_list(MVMThreadContext *tc, MVMObject *mro, MVMCUnionREPRData *repr_data) { MVMInstance *instance = tc->instance; MVMObject *flat_list, *class_list, *attr_map_list; MVMint32 num_classes, i, current_slot = 0; MVMCUnionNameMap *result; MVMint32 mro_idx = MVM_repr_elems(tc, mro); flat_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type); class_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type); attr_map_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type); /* Walk through the parents list. */ while (mro_idx) { /* Get current class in MRO. */ MVMObject *type_info = MVM_repr_at_pos_o(tc, mro, --mro_idx); MVMObject *current_class = MVM_repr_at_pos_o(tc, type_info, 0); /* Get its local parents; make sure we're not doing MI. */ MVMObject *parents = MVM_repr_at_pos_o(tc, type_info, 2); MVMint32 num_parents = MVM_repr_elems(tc, parents); if (num_parents <= 1) { /* Get attributes and iterate over them. */ MVMObject *attributes = MVM_repr_at_pos_o(tc, type_info, 1); MVMIter * const attr_iter = (MVMIter *)MVM_iter(tc, attributes); MVMObject *attr_map = NULL; if (MVM_iter_istrue(tc, attr_iter)) attr_map = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type); while (MVM_iter_istrue(tc, attr_iter)) { MVMObject *current_slot_obj = MVM_repr_box_int(tc, MVM_hll_current(tc)->int_box_type, current_slot); MVMObject *attr, *name_obj; MVMString *name; MVM_repr_shift_o(tc, (MVMObject *)attr_iter); /* Get attribute. */ attr = MVM_iterval(tc, attr_iter); /* Get its name. */ name_obj = MVM_repr_at_key_o(tc, attr, instance->str_consts.name); name = MVM_repr_get_str(tc, name_obj); MVM_repr_bind_key_o(tc, attr_map, name, current_slot_obj); current_slot++; /* Push attr onto the flat list. */ MVM_repr_push_o(tc, flat_list, attr); } /* Add to class list and map list. */ MVM_repr_push_o(tc, class_list, current_class); MVM_repr_push_o(tc, attr_map_list, attr_map); } else { MVM_gc_allocate_gen2_default_clear(tc); MVM_exception_throw_adhoc(tc, "CUnion representation does not support multiple inheritance"); } } /* We can now form the name map. */ num_classes = MVM_repr_elems(tc, class_list); result = (MVMCUnionNameMap *) MVM_malloc(sizeof(MVMCUnionNameMap) * (1 + num_classes)); for (i = 0; i < num_classes; i++) { result[i].class_key = MVM_repr_at_pos_o(tc, class_list, i); result[i].name_map = MVM_repr_at_pos_o(tc, attr_map_list, i); } /* set the end to be NULL, it's useful for iteration. */ result[i].class_key = NULL; repr_data->name_to_index_mapping = result; return flat_list; } /* This works out an allocation strategy for the object. It takes care of * "inlining" storage of attributes that are natively typed, as well as * noting unbox targets. */ static void compute_allocation_strategy(MVMThreadContext *tc, MVMObject *repr_info, MVMCUnionREPRData *repr_data) { /* Compute index mapping table and get flat list of attributes. */ MVMObject *flat_list = index_mapping_and_flat_list(tc, repr_info, repr_data); /* If we have no attributes in the index mapping, then just the header. */ if (repr_data->name_to_index_mapping[0].class_key == NULL) { repr_data->struct_size = 1; /* avoid 0-byte malloc */ repr_data->struct_align = ALIGNOF(void *); } /* Otherwise, we need to compute the allocation strategy. */ else { /* The structure itself will as big as the biggest element. * So we keep track of that biggest element. */ MVMint32 total_size = 0; /* Get number of attributes and set up various counters. */ MVMint32 num_attrs = MVM_repr_elems(tc, flat_list); MVMint32 info_alloc = num_attrs == 0 ? 1 : num_attrs; MVMint32 cur_obj_attr = 0; MVMint32 cur_init_slot = 0; MVMint32 i; /* Allocate location/offset arrays and GC mark info arrays. */ repr_data->num_attributes = num_attrs; repr_data->attribute_locations = (MVMint32 *) MVM_malloc(info_alloc * sizeof(MVMint32)); repr_data->struct_offsets = (MVMint32 *) MVM_malloc(info_alloc * sizeof(MVMint32)); repr_data->flattened_stables = (MVMSTable **) MVM_calloc(info_alloc, sizeof(MVMObject *)); repr_data->member_types = (MVMObject **) MVM_calloc(info_alloc, sizeof(MVMObject *)); repr_data->struct_align = 0; /* Go over the attributes and arrange their allocation. */ for (i = 0; i < num_attrs; i++) { /* Fetch its type; see if it's some kind of unboxed type. */ MVMObject *attr = MVM_repr_at_pos_o(tc, flat_list, i); MVMObject *type = MVM_repr_at_key_o(tc, attr, tc->instance->str_consts.type); MVMObject *inlined_val = MVM_repr_at_key_o(tc, attr, tc->instance->str_consts.inlined); MVMint64 inlined = !MVM_is_null(tc, inlined_val) && MVM_repr_get_int(tc, inlined_val); MVMint32 bits = sizeof(void *) * 8; MVMint32 align = ALIGNOF(void *); if (!MVM_is_null(tc, type)) { /* See if it's a type that we know how to handle in a C struct. */ const MVMStorageSpec *spec = REPR(type)->get_storage_spec(tc, STABLE(type)); MVMint32 type_id = REPR(type)->ID; if (spec->inlineable == MVM_STORAGE_SPEC_INLINED && (spec->boxed_primitive == MVM_STORAGE_SPEC_BP_INT || spec->boxed_primitive == MVM_STORAGE_SPEC_BP_UINT64 || spec->boxed_primitive == MVM_STORAGE_SPEC_BP_NUM)) { /* It's a boxed int or num; pretty easy. It'll just live in the * body of the struct. Instead of masking in i here (which * would be the parallel to how we handle boxed types) we * repurpose it to store the bit-width of the type, so * that get_attribute_ref can find it later. */ bits = spec->bits; align = spec->align; repr_data->attribute_locations[i] = (bits << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_IN_STRUCT; repr_data->flattened_stables[i] = STABLE(type); if (REPR(type)->initialize) { if (!repr_data->initialize_slots) repr_data->initialize_slots = (MVMint32 *) MVM_calloc(info_alloc + 1, sizeof(MVMint32)); repr_data->initialize_slots[cur_init_slot] = i; cur_init_slot++; } } else if (spec->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) { /* It's a string of some kind. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_STRING; repr_data->member_types[i] = type; repr_data->flattened_stables[i] = STABLE(type); if (REPR(type)->initialize) { if (!repr_data->initialize_slots) repr_data->initialize_slots = (MVMint32 *) MVM_calloc(info_alloc + 1, sizeof(MVMint32)); repr_data->initialize_slots[cur_init_slot] = i; cur_init_slot++; } } else if (type_id == MVM_REPR_ID_MVMCArray) { /* It's a CArray of some kind. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CARRAY; repr_data->member_types[i] = type; } else if (type_id == MVM_REPR_ID_MVMCStruct) { /* It's a CStruct. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CSTRUCT; repr_data->member_types[i] = type; if (inlined) { MVMCStructREPRData *cstruct_repr_data = (MVMCStructREPRData *)STABLE(type)->REPR_data; if (!cstruct_repr_data) { MVM_gc_allocate_gen2_default_clear(tc); free_repr_data(repr_data); MVM_exception_throw_adhoc(tc, "CUnion: can't inline a CStruct attribute before its type's definition"); } bits = cstruct_repr_data->struct_size * 8; align = cstruct_repr_data->struct_align; repr_data->attribute_locations[i] |= MVM_CUNION_ATTR_INLINED; } } else if (type_id == MVM_REPR_ID_MVMCPPStruct) { /* It's a CPPStruct. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CPPSTRUCT; repr_data->member_types[i] = type; if (inlined) { MVMCPPStructREPRData *cppstruct_repr_data = (MVMCPPStructREPRData *)STABLE(type)->REPR_data; if (!cppstruct_repr_data) { MVM_gc_allocate_gen2_default_clear(tc); free_repr_data(repr_data); MVM_exception_throw_adhoc(tc, "CUnion: can't inline a CPPStruct attribute before its type's definition"); } bits = cppstruct_repr_data->struct_size * 8; align = cppstruct_repr_data->struct_align; repr_data->attribute_locations[i] |= MVM_CUNION_ATTR_INLINED; } } else if (type_id == MVM_REPR_ID_MVMCUnion) { /* It's a CUnion. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CUNION; repr_data->member_types[i] = type; if (inlined) { MVMCUnionREPRData *cunion_repr_data = (MVMCUnionREPRData *)STABLE(type)->REPR_data; if (!cunion_repr_data) { MVM_gc_allocate_gen2_default_clear(tc); free_repr_data(repr_data); MVM_exception_throw_adhoc(tc, "CUnion: can't inline a CUnion attribute before its type's definition"); } bits = cunion_repr_data->struct_size * 8; align = cunion_repr_data->struct_align; repr_data->attribute_locations[i] |= MVM_CUNION_ATTR_INLINED; } } else if (type_id == MVM_REPR_ID_MVMCPointer) { /* It's a CPointer. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CUNION_ATTR_SHIFT) | MVM_CUNION_ATTR_CPTR; repr_data->member_types[i] = type; } else { MVM_gc_allocate_gen2_default_clear(tc); free_repr_data(repr_data); MVM_exception_throw_adhoc(tc, "CUnion representation only handles attributes of type:\n" " (u)int8, (u)int16, (u)int32, (u)int64, (u)long, (u)longlong, num32, num64, (s)size_t, bool, Str\n" " and types with representation: CArray, CPointer, CStruct, CPPStruct and CUnion"); } } else { MVM_gc_allocate_gen2_default_clear(tc); free_repr_data(repr_data); MVM_exception_throw_adhoc(tc, "CUnion representation requires the types of all attributes to be specified"); } if (bits % 8) { MVM_gc_allocate_gen2_default_clear(tc); free_repr_data(repr_data); MVM_exception_throw_adhoc(tc, "CUnion only supports native types that are a multiple of 8 bits wide (was passed: %"PRId32")", bits); } if (align > repr_data->struct_align) repr_data->struct_align = align; repr_data->struct_offsets[i] = 0; if (bits / 8 > total_size) total_size = bits / 8; } /* Finally, put computed allocation size in place; it's body size plus * header size. Also number of markables and sentinels. */ repr_data->struct_size = total_size; if (repr_data->initialize_slots) repr_data->initialize_slots[cur_init_slot] = -1; } } /* Helper for reading an int at the specified offset. */ static MVMint32 get_int_at_offset(void *data, MVMint32 offset) { void *location = (char *)data + offset; return *((MVMint32 *)location); } /* Helper for writing an int at the specified offset. */ static void set_int_at_offset(void *data, MVMint32 offset, MVMint32 value) { void *location = (char *)data + offset; *((MVMint32 *)location) = value; } /* Helper for reading a num at the specified offset. */ static MVMnum32 get_num_at_offset(void *data, MVMint32 offset) { void *location = (char *)data + offset; return *((MVMnum32 *)location); } /* Helper for writing a num at the specified offset. */ static void set_num_at_offset(void *data, MVMint32 offset, MVMnum32 value) { void *location = (char *)data + offset; *((MVMnum32 *)location) = value; } /* Helper for reading a pointer at the specified offset. */ static void * get_ptr_at_offset(void *data, MVMint32 offset) { void *location = (char *)data + offset; return *((void **)location); } /* Helper for writing a pointer at the specified offset. */ static void set_ptr_at_offset(void *data, MVMint32 offset, void *value) { void *location = (char *)data + offset; *((void **)location) = value; } /* Helper for finding a slot number. */ static MVMint32 try_get_slot(MVMThreadContext *tc, MVMCUnionREPRData *repr_data, MVMObject *class_key, MVMString *name) { if (repr_data->name_to_index_mapping) { MVMCUnionNameMap *cur_map_entry = repr_data->name_to_index_mapping; while (cur_map_entry->class_key != NULL) { if (cur_map_entry->class_key == class_key) { MVMObject *slot_obj = MVM_repr_at_key_o(tc, cur_map_entry->name_map, name); if (IS_CONCRETE(slot_obj)) return MVM_repr_get_int(tc, slot_obj); break; } cur_map_entry++; } } return -1; } /* Creates a new type object of this representation, and associates it with * the given HOW. */ static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) { MVMSTable *st = MVM_gc_allocate_stable(tc, &CUnion_this_repr, HOW); MVMROOT(tc, st, { MVMObject *obj = MVM_gc_allocate_type_object(tc, st); MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj); st->size = sizeof(MVMCUnion); }); return st->WHAT; } /* Composes the representation. */ static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *repr_info) { /* Compute allocation strategy. */ MVMObject *attr_info = MVM_repr_at_key_o(tc, repr_info, tc->instance->str_consts.attribute); MVMCUnionREPRData *repr_data = MVM_calloc(1, sizeof(MVMCUnionREPRData)); MVM_gc_allocate_gen2_default_set(tc); compute_allocation_strategy(tc, attr_info, repr_data); MVM_gc_allocate_gen2_default_clear(tc); st->REPR_data = repr_data; } /* Initialize a new instance. */ static void initialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) { MVMCUnionREPRData * repr_data = (MVMCUnionREPRData *)st->REPR_data; /* Allocate object body. */ MVMCUnionBody *body = (MVMCUnionBody *)data; body->cunion = calloc(1, repr_data->struct_size > 0 ? repr_data->struct_size : 1); /* Allocate child obj array. */ if (repr_data->num_child_objs > 0) body->child_objs = (MVMObject **)MVM_calloc(repr_data->num_child_objs, sizeof(MVMObject *)); /* Initialize the slots. */ if (repr_data->initialize_slots) { MVMint32 i; for (i = 0; repr_data->initialize_slots[i] >= 0; i++) { MVMint32 offset = repr_data->struct_offsets[repr_data->initialize_slots[i]]; MVMSTable *st = repr_data->flattened_stables[repr_data->initialize_slots[i]]; st->REPR->initialize(tc, st, root, (char *)body->cunion + offset); } } } /* Copies to the body of one object to another. */ static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) { MVM_exception_throw_adhoc(tc, "cloning a CUnion is NYI"); } /* Helper for complaining about attribute access errors. */ MVM_NO_RETURN static void no_such_attribute(MVMThreadContext *tc, const char *action, MVMObject *class_handle, MVMString *name) MVM_NO_RETURN_ATTRIBUTE; static void no_such_attribute(MVMThreadContext *tc, const char *action, MVMObject *class_handle, MVMString *name) { char *c_name = MVM_string_utf8_encode_C_string(tc, name); char *waste[] = { c_name, NULL }; MVM_exception_throw_adhoc_free(tc, waste, "Can not %s non-existent attribute '%s'", action, c_name); } /* Helper to die because this type doesn't support attributes. */ MVM_NO_RETURN static void die_no_attrs(MVMThreadContext *tc) MVM_NO_RETURN_ATTRIBUTE; static void die_no_attrs(MVMThreadContext *tc) { MVM_exception_throw_adhoc(tc, "CUnion representation attribute not yet fully implemented"); } static void get_attribute(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint, MVMRegister *result_reg, MVMuint16 kind) { MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data; MVMCUnionBody *body = (MVMCUnionBody *)data; MVMint64 slot; if (!repr_data) MVM_exception_throw_adhoc(tc, "CUnion: must compose before using get_attribute"); slot = hint >= 0 ? hint : try_get_slot(tc, repr_data, class_handle, name); if (slot >= 0) { MVMSTable *attr_st = repr_data->flattened_stables[slot]; switch (kind) { case MVM_reg_obj: { MVMint32 type = repr_data->attribute_locations[slot] & MVM_CUNION_ATTR_MASK; MVMint32 real_slot = repr_data->attribute_locations[slot] >> MVM_CUNION_ATTR_SHIFT; if (type == MVM_CUNION_ATTR_IN_STRUCT) { MVM_exception_throw_adhoc(tc, "CUnion can't perform boxed get on flattened attributes yet"); } else { MVMObject *typeobj = repr_data->member_types[slot]; MVMObject *obj = body->child_objs[real_slot]; if (!obj) { /* No cached object. */ MVMROOT(tc, root, { if (repr_data->attribute_locations[slot] & MVM_CUNION_ATTR_INLINED) { if (type == MVM_CUNION_ATTR_CSTRUCT) { obj = MVM_nativecall_make_cstruct(tc, typeobj, (char *)body->cunion + repr_data->struct_offsets[slot]); } else if (type == MVM_CUNION_ATTR_CPPSTRUCT) { obj = MVM_nativecall_make_cppstruct(tc, typeobj, (char *)body->cunion + repr_data->struct_offsets[slot]); } else if (type == MVM_CUNION_ATTR_CUNION) { obj = MVM_nativecall_make_cunion(tc, typeobj, (char *)body->cunion + repr_data->struct_offsets[slot]); } } else { void *cobj = get_ptr_at_offset(body->cunion, repr_data->struct_offsets[slot]); if (cobj) { if (type == MVM_CUNION_ATTR_CARRAY) { obj = MVM_nativecall_make_carray(tc, typeobj, cobj); } else if(type == MVM_CUNION_ATTR_CSTRUCT) { obj = MVM_nativecall_make_cstruct(tc, typeobj, cobj); } else if(type == MVM_CUNION_ATTR_CPPSTRUCT) { obj = MVM_nativecall_make_cppstruct(tc, typeobj, cobj); } else if(type == MVM_CUNION_ATTR_CUNION) { obj = MVM_nativecall_make_cunion(tc, typeobj, cobj); } else if(type == MVM_CUNION_ATTR_CPTR) { obj = MVM_nativecall_make_cpointer(tc, typeobj, cobj); } else if(type == MVM_CUNION_ATTR_STRING) { MVMROOT(tc, typeobj, { MVMString *str = MVM_string_utf8_decode(tc, tc->instance->VMString, cobj, strlen(cobj)); obj = MVM_repr_box_str(tc, typeobj, str); }); } } else { obj = typeobj; } } }); MVM_ASSIGN_REF(tc, &(root->header), body->child_objs[real_slot], obj); } result_reg->o = obj; } break; } case MVM_reg_int64: { if (attr_st) result_reg->i64 = attr_st->REPR->box_funcs.get_int(tc, attr_st, root, ((char *)body->cunion) + repr_data->struct_offsets[slot]); else MVM_exception_throw_adhoc(tc, "CUnion: invalid native get of object attribute"); break; } case MVM_reg_uint64: { if (attr_st) result_reg->u64 = attr_st->REPR->box_funcs.get_uint(tc, attr_st, root, ((char *)body->cunion) + repr_data->struct_offsets[slot]); else MVM_exception_throw_adhoc(tc, "CUnion: invalid native get of object attribute"); break; } case MVM_reg_num64: { if (attr_st) result_reg->n64 = attr_st->REPR->box_funcs.get_num(tc, attr_st, root, ((char *)body->cunion) + repr_data->struct_offsets[slot]); else MVM_exception_throw_adhoc(tc, "CUnion: invalid native get of object attribute"); break; } case MVM_reg_str: { if (attr_st) result_reg->s = attr_st->REPR->box_funcs.get_str(tc, attr_st, root, ((char *)body->cunion) + repr_data->struct_offsets[slot]); else MVM_exception_throw_adhoc(tc, "CUnion: invalid native get of object attribute"); if (!result_reg->s) result_reg->s = tc->instance->str_consts.empty; break; } default: MVM_exception_throw_adhoc(tc, "CUnion: invalid kind in attribute get"); } } else { no_such_attribute(tc, "bind", class_handle, name); } } /* Binds the given value to the specified attribute. */ static void bind_attribute(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint, MVMRegister value_reg, MVMuint16 kind) { MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data; MVMCUnionBody *body = (MVMCUnionBody *)data; MVMint64 slot; if (!repr_data) MVM_exception_throw_adhoc(tc, "CUnion: must compose before using bind_attribute"); slot = hint >= 0 ? hint : try_get_slot(tc, repr_data, class_handle, name); if (slot >= 0) { MVMSTable *attr_st = repr_data->flattened_stables[slot]; switch (kind) { case MVM_reg_obj: { MVMObject *value = value_reg.o; MVMint32 type = repr_data->attribute_locations[slot] & MVM_CUNION_ATTR_MASK; if (type == MVM_CUNION_ATTR_IN_STRUCT) { MVM_exception_throw_adhoc(tc, "CUnion can't perform boxed bind on flattened attributes yet"); } else { MVMint32 real_slot = repr_data->attribute_locations[slot] >> MVM_CUNION_ATTR_SHIFT; if (IS_CONCRETE(value)) { void *cobj = NULL; MVM_ASSIGN_REF(tc, &(root->header), body->child_objs[real_slot], value); /* Set cobj to correct pointer based on type of value. */ if (type == MVM_CUNION_ATTR_CARRAY) { if (REPR(value)->ID != MVM_REPR_ID_MVMCArray) MVM_exception_throw_adhoc(tc, "Can only store CArray attribute in CArray slot in CUnion"); cobj = ((MVMCArray *)value)->body.storage; } else if (type == MVM_CUNION_ATTR_CSTRUCT) { if (REPR(value)->ID != MVM_REPR_ID_MVMCStruct) MVM_exception_throw_adhoc(tc, "Can only store CStruct attribute in CStruct slot in CUnion"); cobj = ((MVMCStruct *)value)->body.cstruct; } else if (type == MVM_CUNION_ATTR_CPTR) { if (REPR(value)->ID != MVM_REPR_ID_MVMCPointer) MVM_exception_throw_adhoc(tc, "Can only store CPointer attribute in CPointer slot in CUnion"); cobj = ((MVMCPointer *)value)->body.ptr; } else if (type == MVM_CUNION_ATTR_STRING) { MVMString *str = MVM_repr_get_str(tc, value); cobj = MVM_string_utf8_encode_C_string(tc, str); } set_ptr_at_offset(body->cunion, repr_data->struct_offsets[slot], cobj); } else { body->child_objs[real_slot] = NULL; set_ptr_at_offset(body->cunion, repr_data->struct_offsets[slot], NULL); } } break; } case MVM_reg_int64: { if (attr_st) attr_st->REPR->box_funcs.set_int(tc, attr_st, root, ((char *)body->cunion) + repr_data->struct_offsets[slot], value_reg.i64); else MVM_exception_throw_adhoc(tc, "CUnion: invalid native binding to object attribute"); break; } case MVM_reg_uint64: { if (attr_st) attr_st->REPR->box_funcs.set_uint(tc, attr_st, root, ((char *)body->cunion) + repr_data->struct_offsets[slot], value_reg.u64); else MVM_exception_throw_adhoc(tc, "CUnion: invalid native binding to object attribute"); break; } case MVM_reg_num64: { if (attr_st) attr_st->REPR->box_funcs.set_num(tc, attr_st, root, ((char *)body->cunion) + repr_data->struct_offsets[slot], value_reg.n64); else MVM_exception_throw_adhoc(tc, "CUnion: invalid native binding to object attribute"); break; } case MVM_reg_str: { if (attr_st) attr_st->REPR->box_funcs.set_str(tc, attr_st, root, ((char *)body->cunion) + repr_data->struct_offsets[slot], value_reg.s); else MVM_exception_throw_adhoc(tc, "CUnion: invalid native binding to object attribute"); break; } default: MVM_exception_throw_adhoc(tc, "CUnion: invalid kind in attribute bind"); } } else { no_such_attribute(tc, "bind", class_handle, name); } } /* Checks if an attribute has been initialized. */ static MVMint64 is_attribute_initialized(MVMThreadContext *tc, MVMSTable *st, void *data, MVMObject *class_handle, MVMString *name, MVMint64 hint) { die_no_attrs(tc); } /* Gets the hint for the given attribute ID. */ static MVMint64 hint_for(MVMThreadContext *tc, MVMSTable *st, MVMObject *class_handle, MVMString *name) { return MVM_NO_HINT; } /* Adds held objects to the GC worklist. */ static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) { MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *) st->REPR_data; MVMCUnionBody *body = (MVMCUnionBody *)data; MVMint32 i; for (i = 0; i < repr_data->num_child_objs; i++) MVM_gc_worklist_add(tc, worklist, &body->child_objs[i]); } /* Marks the representation data in an STable.*/ static void gc_mark_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMGCWorklist *worklist) { MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data; if (repr_data) { MVMint32 i; if (repr_data->name_to_index_mapping) { MVMCUnionNameMap *map = repr_data->name_to_index_mapping; for (i = 0; map[i].class_key; i++) { MVM_gc_worklist_add(tc, worklist, &map[i].class_key); MVM_gc_worklist_add(tc, worklist, &map[i].name_map); } } if (repr_data->flattened_stables) { MVMSTable **flattened_stables = repr_data->flattened_stables; for (i = 0; i < repr_data->num_attributes; i++) MVM_gc_worklist_add(tc, worklist, &flattened_stables[i]); } if (repr_data->member_types) { MVMObject **member_types = repr_data->member_types; for (i = 0; i < repr_data->num_attributes; i++) MVM_gc_worklist_add(tc, worklist, &member_types[i]); } } } static void free_repr_data(MVMCUnionREPRData *repr_data) { /* May not have survived to composition. */ if (repr_data == NULL) return; if (repr_data->name_to_index_mapping) { MVM_free(repr_data->name_to_index_mapping); MVM_free(repr_data->attribute_locations); MVM_free(repr_data->struct_offsets); MVM_free(repr_data->flattened_stables); MVM_free(repr_data->member_types); MVM_free(repr_data->initialize_slots); } MVM_free(repr_data); } /* Free representation data. */ static void gc_free_repr_data(MVMThreadContext *tc, MVMSTable *st) { MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data; free_repr_data(repr_data); } /* This is called to do any cleanup of resources when an object gets * embedded inside another one. Never called on a top-level object. */ static void gc_cleanup(MVMThreadContext *tc, MVMSTable *st, void *data) { MVMCUnionBody *body = (MVMCUnionBody *)data; if (body->child_objs) MVM_free(body->child_objs); /* XXX For some reason, this causes crashes at the moment. Need to * work out why. */ /*if (body->cunion) free(body->cunion);*/ } /* Called by the VM in order to free memory associated with this object. */ static void gc_free(MVMThreadContext *tc, MVMObject *obj) { gc_cleanup(tc, STABLE(obj), OBJECT_BODY(obj)); } static const MVMStorageSpec storage_spec = { MVM_STORAGE_SPEC_REFERENCE, /* inlineable */ sizeof(void*) * 8, /* bits */ ALIGNOF(void*), /* align */ MVM_STORAGE_SPEC_BP_NONE, /* boxed_primitive */ 0, /* can_box */ 0, /* is_unsigned */ }; /* Gets the storage specification for this representation. */ static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) { return &storage_spec; } /* Serializes the REPR data. */ static void serialize_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMSerializationWriter *writer) { MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)st->REPR_data; MVMint32 i, num_classes, num_slots; MVM_serialization_write_int(tc, writer, repr_data->struct_size); MVM_serialization_write_int(tc, writer, repr_data->struct_align); MVM_serialization_write_int(tc, writer, repr_data->num_attributes); MVM_serialization_write_int(tc, writer, repr_data->num_child_objs); for(i = 0; i < repr_data->num_attributes; i++){ MVM_serialization_write_int(tc, writer, repr_data->attribute_locations[i]); MVM_serialization_write_int(tc, writer, repr_data->struct_offsets[i]); MVM_serialization_write_int(tc, writer, repr_data->flattened_stables[i] != NULL); if (repr_data->flattened_stables[i]) MVM_serialization_write_stable_ref(tc, writer, repr_data->flattened_stables[i]); MVM_serialization_write_ref(tc, writer, repr_data->member_types[i]); } i=0; while (repr_data->name_to_index_mapping[i].class_key) i++; num_classes = i; MVM_serialization_write_int(tc, writer, num_classes); for(i = 0; i < num_classes; i++){ MVM_serialization_write_ref(tc, writer, repr_data->name_to_index_mapping[i].class_key); MVM_serialization_write_ref(tc, writer, repr_data->name_to_index_mapping[i].name_map); } i=0; while(repr_data->initialize_slots && repr_data->initialize_slots[i] != -1) i++; num_slots = i; MVM_serialization_write_int(tc, writer, num_slots); for(i = 0; i < num_slots; i++){ MVM_serialization_write_int(tc, writer, repr_data->initialize_slots[i]); } } /* Deserializes the REPR data. */ static void deserialize_repr_data(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) { MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *) MVM_malloc(sizeof(MVMCUnionREPRData)); MVMint32 i, num_classes, num_slots; repr_data->struct_size = MVM_serialization_read_int(tc, reader); repr_data->struct_align = MVM_serialization_read_int(tc, reader); repr_data->num_attributes = MVM_serialization_read_int(tc, reader); repr_data->num_child_objs = MVM_serialization_read_int(tc, reader); repr_data->attribute_locations = (MVMint32 *)MVM_malloc(sizeof(MVMint32) * repr_data->num_attributes); repr_data->struct_offsets = (MVMint32 *)MVM_malloc(sizeof(MVMint32) * repr_data->num_attributes); repr_data->flattened_stables = (MVMSTable **)MVM_malloc(repr_data->num_attributes * sizeof(MVMSTable *)); repr_data->member_types = (MVMObject **)MVM_malloc(repr_data->num_attributes * sizeof(MVMObject *)); for(i = 0; i < repr_data->num_attributes; i++) { repr_data->attribute_locations[i] = MVM_serialization_read_int(tc, reader); repr_data->struct_offsets[i] = MVM_serialization_read_int(tc, reader); if(MVM_serialization_read_int(tc, reader)){ MVM_ASSIGN_REF(tc, &(st->header), repr_data->flattened_stables[i], MVM_serialization_read_stable_ref(tc, reader)); } else { repr_data->flattened_stables[i] = NULL; } repr_data->member_types[i] = MVM_serialization_read_ref(tc, reader); } num_classes = MVM_serialization_read_int(tc, reader); repr_data->name_to_index_mapping = (MVMCUnionNameMap *)MVM_malloc(sizeof(MVMCUnionNameMap) * (1 + num_classes)); for(i = 0; i < num_classes; i++){ repr_data->name_to_index_mapping[i].class_key = MVM_serialization_read_ref(tc, reader); repr_data->name_to_index_mapping[i].name_map = MVM_serialization_read_ref(tc, reader); } repr_data->name_to_index_mapping[i].class_key = NULL; repr_data->name_to_index_mapping[i].name_map = NULL; num_slots = MVM_serialization_read_int(tc, reader); repr_data->initialize_slots = (MVMint32 *)MVM_malloc(sizeof(MVMint32) * (1 + num_slots)); for(i = 0; i < num_slots; i++){ repr_data->initialize_slots[i] = MVM_serialization_read_int(tc, reader); } repr_data->initialize_slots[i] = -1; st->REPR_data = repr_data; } static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) { st->size = sizeof(MVMCUnion); } /* Initializes the representation. */ const MVMREPROps * MVMCUnion_initialize(MVMThreadContext *tc) { return &CUnion_this_repr; } static const MVMREPROps CUnion_this_repr = { type_object_for, MVM_gc_allocate_object, initialize, copy_to, { get_attribute, bind_attribute, hint_for, is_attribute_initialized, MVM_REPR_DEFAULT_ATTRIBUTE_AS_ATOMIC }, /* attr_funcs */ MVM_REPR_DEFAULT_BOX_FUNCS, MVM_REPR_DEFAULT_POS_FUNCS, MVM_REPR_DEFAULT_ASS_FUNCS, MVM_REPR_DEFAULT_ELEMS, get_storage_spec, NULL, /* change_type */ NULL, /* serialize */ NULL, /* deserialize */ serialize_repr_data, deserialize_repr_data, deserialize_stable_size, gc_mark, gc_free, gc_cleanup, gc_mark_repr_data, gc_free_repr_data, compose, NULL, /* spesh */ "CUnion", /* name */ MVM_REPR_ID_MVMCUnion, NULL, /* unmanaged_size */ NULL, /* describe_refs */ };