| ///////////////////////////// | |
| // 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 | |