///////////////////////////// // is assignable from ///////////////////////////// 1. Available data a. object class_id, obj_type b. inherited from class_id, obj_type, hard-coded in constructor 0. does inheritor have to be same obj-type as parent? a. no - i can inherit from something and be a different thing 1. how do we go multi-level? hard-code it all? or read object data. 2. seems like a path to explore - constants describing inheritance 2. Rules a. x.isAssignableFrom(y) means I can assign an object of class y to one of class x b. x.isAssignableFrom(y) if y inherits from x c. if y IS_A(x) then x.isAssignableFrom(y) d. x.isAssignableFrom(y) means I can do: xobj = yobj; e. true: Object.class.isAssignableFrom( Integer.class); f. false: int.class.isAssignableFrom( short.class)); 1. note above that isAssignableFrom does not imply isConvertibleFrom g. false: short.class.isAssignableFrom( int.class); h. false: Integer.class.isAssignableFrom( Short.class); i. false: Integer.class.isAssignableFrom( int.class); j. false: Integer.class.isAssignableFrom( short.class); k. false: Integer.class.isAssignableFrom( short.class); l. false: Object.class.isAssignableFrom ( short.class); 1. note above that a class must derive from object to assign to object 2. however note that in c binding we never encounter such an assignment possibility, since the right side would be wrapped, and the assignment therefore OK. m. true: Object.class.isAssignableFrom ( Short.class); n. true: Integer[].class.isAssignableFrom( Integer[].class) o. false: Object[].class.isAssignableFrom( Integer.class); p. true: Object[].class.isAssignableFrom( Integer[].class); q. true: Object.class.isAssignableFrom( Integer[].class); r. true: short[].class.isAssignableFrom( short[].class) true Integer.class.isAssignableFrom( Integer.class) true String.class.isAssignableFrom( String.class) s. Integer[] IntObj1 = new Integer[4]; Integer[] IntObj2 = new Integer[8]; IntObj1 = IntObj2; Object[] x = IntObj1; t. 1. consider using vtab to get at inheritance hierarchy a. in this way we don't duplicate an inheritance list for all objects of same type b. we would have to make a vtable another etch_object type, such that it could use the etch_object value* to point to an inheritance list. the vtable object could have a short count field to indicate the length of the inheritance list, or the first entry in the list could be the count, with a null list implying zero, or we could just walk a linked list. 1. we would change all i_xxxx objects to conform. we must ensure that all vtables conform to this naming convention also. they seem to do so. 2. we would grep all new_xxxx to find ctors and examine each such object for inheritance. b. for objects which don't currently use the vtable we could allocate one to use just for this purpose. c. however this entails changing many constructors to allocate a vtable. this may be worth it however since the list is infrequently accessed and so we don't want to use up an object slot. d. however if there is no inheritance we don't neeed a vtable, so this would save some ctor work. this assumes that the final object parent is not explicit in the list, but rather exists implicitly for objects deriving from Object. typedef struct etchparentinfo { union { unsigned short obj_type; unsigned short list_size; /* entry[0] in any inheritance list */ }; union { unsigned short class_id; unsigned short parent_count; /* entry[0] in any inheritance list */ }; } etchparentinfo; /** * vtabmask * mask over any vtable */ struct vtabmask { unsigned int hashkey; unsigned short obj_type; unsigned short class_id; objmask* vtab; int (*destroy)(void*); void*(*clone) (void*); struct objmask* parent; etchresult* result; unsigned int refcount; unsigned int length; unsigned char is_null; unsigned char is_copy; unsigned char is_static; unsigned char reserved; etchparentinfo* inherits_from; /* function pointers start here */ }; u. true: etch_id_name.class.isAssignableFrom( etch_field.class) true: etch_object.class.isAssignableFrom( etch_field.class) 1. In the first case we need a method of assigning a field to an id_name. In the second case we would wrap the field in an object. 2. How do we assign extended object to base object, in general? a. do we somehow manipulate the object header so it becomes a different type? b. do we assume that content is ordered, and memcpy child to parent? c. we need to formalize what is means to assign an object to another, 1. does each object now need an assign_from() method? a. such a method would have to be able to cast the extended to the base, meaning the base portion of extended must be positionally the same. 3. Let's postpone implementation of assignment methods until we determine apecifically what sort of assignments will be required by the compiler. 3. Representation of inheritance at run time a. Note that we need to interpret isAssignableFrom same as java, and implement the assignment. 1. problematic will be assignments such as int[] to int[], since we must re-construct a target array, due to the fact that our native arrays are fixed size. b. To solve this problem, begin by representing each of the objects in the examples above by their etch-c internal type codes. 1. Determine how the isAssignableFrom() logic would handle each such isAssignableFrom test 2. Determine how an assignemt would be made for each isAssignableFrom instance. e. Object.class.isAssignableFrom( Integer.class) (Note that assignments must implicitly destroy content of the target object) 1. Object.class a. obj_type: ETCHTYPEB_ETCHOBJECT b. class_id: CLASSID_OBJECT c. byte typecode none d. value (if obj.is_value_object) 1. value obj_type 2. value class_id 2. Integer.class a. obj_type: ETCHTYPEB_PRIMITIVE b. class_id: CLASSID_PRIMITIVE_INT32 3. Determine if assignment is legal a. if (is_etch_object(targetobj)) true 4. Assignment a. Prior to assignment, system must destroy target object content. 1. if (obj.value) if (obj.is_value_object) obj.value->destroy(); else etch_free(obj.value); b. obj.value = intobj; obj.is_value_object = TRUE; obj.is_value_owned = TRUE; // assignment to object always implies object owns value f. int.class.isAssignableFrom( short.class)); 1. int.class a. obj_type: ETCHTYPEB_PRIMITIVE b. class_id: CLASSID_PRIMITIVE_INT32 2. short.class a. obj_type: ETCHTYPEB_PRIMITIVE b. class_id: CLASSID_PRIMITIVE_INT16 3. Determine if assignment is legal a. if (is_etch_primitive(targetobj)) false h. Integer.class.isAssignableFrom( Short.class); (note that in c binding, native primitives are never used where class comes into play; therefore in the context of isAssignableFrom rules, int and Integer are the same.) 1. int.class a. obj_type: ETCHTYPEB_PRIMITIVE b. class_id: CLASSID_PRIMITIVE_INT32 2. short.class a. obj_type: ETCHTYPEB_PRIMITIVE b. class_id: CLASSID_PRIMITIVE_INT16 3. Determine if assignment is legal a. if (is_etch_primitive(targetobj)) false l. Object.class.isAssignableFrom ( short.class); ** Note again that in c binding we never encounter a situation of Object x = (short) n; For java, this assignment is not legal. For c, the right hand side would always be a wrapped short, and therefore the assignment is legal. 1. Object.class a. obj_type: ETCHTYPEB_ETCHOBJECT b. class_id: CLASSID_OBJECT 2. short.class a. obj_type: ETCHTYPEB_PRIMITIVE b. class_id: CLASSID_PRIMITIVE_INT16 3. Determine if assignment is legal a. if (is_etch_object(targetobj)) true 4. Assignment a. if (obj.value) if (obj.is_value_object) obj.value->destroy(); else etch_free(obj.value); b. obj.value = intobj; obj.is_value_object = TRUE; obj.is_value_owned = TRUE; // assignment to object always implies object owns value n. Integer[].class.isAssignableFrom( Integer[].class note that it would seem that an assignment to a etch nativearray array should necessitate creating a new target array, however since the nativearray is a wrapped object, we can instead simply destroy the left side content and assign the right side content and counts. 1. leftobj a. obj_type: ETCHTYPEB_NATIVEARRAY b. class_id: CLASSID_ARRAY_INT32 c. numdims 1 d. content_obj_type ETCHTYPEB_PRIMITIVE e. content_class_id CLASSID_PRIMITIVE_INT32 2. rightobj a. obj_type: ETCHTYPEB_NATIVEARRAY b. class_id: CLASSID_ARRAY_INT32 c. numdims 1 d. content_obj_type ETCHTYPEB_PRIMITIVE e. content_class_id CLASSID_PRIMITIVE_INT32 3. Determine if assignment is legal and do the assignment if so if (is_etch_nativearray(leftobj)) if (is_etch_nativearray(rightobj)) if ((leftobj.class_id == rightobj.class_id) || (leftobj.class_id == CLASSID_ARRAY_OBJECT)) if (leftobj.numdims == rightobj.numdims) { destroy_nativearray_content(leftobj); // do the assignment leftobj.values = rightobj.values; leftobj.bytecount = rightobj.bytecount; leftobj.content_obj_type = rightobj.content_obj_type; // in case assigning to object[] leftobj.content_class_id = rightobj.content_class_id; // in case assigning to object[] memcpy(leftobj.dimension, rightobj.dimension, sizeof(leftobj.dimension)); memcpy(leftobj.dimsize, rightobj.dimsize, sizeof(leftobj.dimsize)); memcpy(leftobj.counts, rightobj.counts, sizeof(leftobj.counts)); ) else false; /* array dimensions not the same */ else false; /* both sides not array of int32 */ else false; /* both sides not nativearray */ o. Object[].class.isAssignableFrom( Integer.class); ** assignment of a scalar object to an array object is not valid 1. Object[].class a. obj_type: ETCHTYPEB_ETCHOBJECT b. class_id: CLASSID_ARRAY_OBJECT c. numdims 1 d. content_obj_type ETCHTYPEB_ETCHOBJECT e. content_class_id CLASSID_OBJECT 2. Integer.class a. obj_type: ETCHTYPEB_PRIMITIVE b. class_id: CLASSID_PRIMITIVE_INT32 3. Determine if assignment is legal if (is_etch_nativearray(leftobj)) if (is_etch_nativearray(rightobj)); else false p. Object[].class.isAssignableFrom( Integer[].class); note that an array of any type can be assigned to an array of objects, assuming like dimensionality. 1. leftobj a. obj_type: ETCHTYPEB_NATIVEARRAY b. class_id: CLASSID_ARRAY_OBJECT c. numdims 1 d. content_obj_type ETCHTYPEB_PRIMITIVE e. content_class_id CLASSID_PRIMITIVE_INT32 2. rightobj a. obj_type: ETCHTYPEB_NATIVEARRAY b. class_id: CLASSID_ARRAY_INT32 c. numdims 1 d. content_obj_type ETCHTYPEB_PRIMITIVE e. content_class_id CLASSID_PRIMITIVE_INT32 3. Determine if assignment is legal and do the assignment if so if (is_etch_nativearray(leftobj)) if (is_etch_nativearray(rightobj)) if ((leftobj.class_id == rightobj.class_id) || (leftobj.class_id == CLASSID_ARRAY_OBJECT)) if (leftobj.numdims == rightobj.numdims) { destroy_nativearray_content(leftobj); // do the assignment leftobj.values = rightobj.values; leftobj.bytecount = rightobj.bytecount; leftobj.content_obj_type = rightobj.content_obj_type; // in case assigning to object[] leftobj.content_class_id = rightobj.content_class_id; // in case assigning to object[] memcpy(leftobj.dimension, rightobj.dimension, sizeof(leftobj.dimension)); memcpy(leftobj.dimsize, rightobj.dimsize, sizeof(leftobj.dimsize)); memcpy(leftobj.counts, rightobj.counts, sizeof(leftobj.counts)); ) else false; /* array dimensions not the same */ else false; /* both sides not array of int32 */ else false; /* both sides not nativearray */ q. Object.class.isAssignableFrom( Integer[].class); in this case the object content becomes the nativearray object 1. leftobj a. obj_type: ETCHTYPEB_OBJECT b. class_id: CLASSID_OBJECT 2. rightobj a. obj_type: ETCHTYPEB_NATIVEARRAY b. class_id: CLASSID_ARRAY_INT32 c. numdims 1 d. content_obj_type ETCHTYPEB_PRIMITIVE e. content_class_id CLASSID_PRIMITIVE_INT32 3. Determine if assignment is legal and do the assignment if so if (is_etch_object(leftobj)) if (is_etch_object(rightobj)) { destroy_etch_object_value(leftobj.value); /* not relevant to this example */ leftobj.value = rightobj.value; leftobj.is_value_object = rightobj.is_value_object; leftobj.is_value_owned = rightobj.is_value_owned; } else /* this example: assigning a non-wrapper object to a wrapper object */ { destroy_etch_object_value(leftobj.value); leftobj->value = rightobj; // wrap right side inside left side leftobj->is_value_object = TRUE; leftobj->is_value_owned = TRUE; } u. etch_id_name.class.isAssignableFrom(etch_field.class) 1. leftobj a. obj_type: ETCHTYPEB_ID_NAME b. class_id: CLASSID_ID_NAME c. inherits_from = etch_id_name, Object 2. rightobj a. obj_type: ETCHTYPEB_ID_NAME b. class_id: ETCHTYPEB_ID_FIELD 3. Determine if assignment is legal and do the assignment if so if (is_etch_object(leftobj)) { ... } else if (is_etch_object(rightobj)) { ... } else if (is_assignable_from(leftobj, rightobj) // true { // now what do we do with the string? // it would seem we need a clone_content virtual method on the object // or possibly clone the object, copy it and delete the object frame but not its owned content // here's how: copy content but mark it not owned: memcpy(leftobj, rightobj, sizeof(leftobj)); leftobj->is_copy = TRUE; // now the dtor will not destroy the name } a. how to specify static object content 1. we don't have a means of specifying that an object's content should not be destroyed with the object. a. when content is objmask-based, the objmask destructor handles this, however when content is anonymous pointer, it gets etch_freed with the object, except in cases where we have added an extra owned flag, such as for nativearray content. 2. we can rename the is_copy flag which we do not enforce, and have used only once. 3. possible names a. is_cloned_content b. is_static_content c. is_foreign_content d. is_copy e. can we avoid current problems by re-purposing the is_static and is_copy flags to mean is_static_shell and is_static_content? 1. would we miss the is_copy meaning in that case? i.e. does is_static_content have the same effect as is_copy? a. maybe we need to know that an object is a copy of another, in addition to whether or not content is not owned 2. possible names: a. is_statcon, is_statobj b. is_owned derivatives are not good because default should be owned and we don't want to have to explicitly flag default conditions. c. is_static_cont, is_static_shell 3. can we instead use the is_static flag as quad-state? e.g., 1 indicates shell, 2 content, and 3 both. i like this idea. it buys us all needed functionality without sacrificing meaning. we could rename is_static to not appear boolean. b. how to copy one object to another, 1. need to handle memory for the abandoned left side content 2. need to ensure that new left side content becomes imuutable 3. if we enforce content inheritance order (base class first, child 1 next, etc.) we should be able to memcpy right side to left side, and then mark left side content immutable with the is_copy flag. 4. how do we destroy left side content. we almost certainly want to use the destructor since it already handles content, however it would need to know to not destroy the object shell. 5. how to invoke destructor without destroying shell a. another byte flag? if we had reserved one byte for bitflags we could do internal stuff like this much more easily. check the objects which have used this flag and determine if we can put the flag somewhere else. b. use an unused but existing field in the object. 5. HOW DO WE HANDLE REFCOUNTED OBJECTS ON EITHER SIDE? a. if an obj is refcounted it means that it is "owned" by multiple code paths. b. example 1: objB recounted 2 copied to objA not refcounted. 1. the refcounts should remain the same on both sides, i.e. after copy, objA refcount is set to zero. c. example 2: objB not refcounted copied to objA refcounted 2. 1. again, objA recfount should not change. d. so the rule is, the target object retains its refcount. 6. is the etchresult* considered to be a part of the content? a. i.e. if I copy an object, can the object include a result? if so, the result is part of the content. b. however if I return an object with a result, that object might include static content, but the result must still be destroyed. c. so the rule must be that if we copy an object, the result does not go with it, i.e. we null out the result address after copy. 7. so then, what is the required housekeeping before and after a copy a. before copy, save left side refcount b. destroy left side content 1. mark left side static shell and invoke its destructor c. memcpy right side to left side d. set left side refcount to saved refcount e. set left side result to null