We generate for every expanded types two structures. For example if you have:
expanded class A feature i: INTEGER s: STRING b: expanded class B end expanded class B feature i: INTEGER end
For class A we generate two structures:
struct ex_A { EIF_INTEGER i; EIF_REFERENCE s; struct ex_B b; }
struct Tex_A { EIF_TYPE_ID type_id; struct ex_A item; }
The first structure `ex_A' describes object content. It is used when A is used as an attribute (or if you have a SPECIAL [A]). The second `Tex_A' is a typed version of the first one. It is used for declaring the types for locals or arguments of type A.
This comes from the fact that an expanded object exists only by itself if and only if it is declared as a local, otherwise it always included in a normal object. So when it is a local, then the GC is not smart enough to find out which expanded we are storing, therefore the added `type_id' to help the GC to mark the structure.
When A does not contain any references, and recursively no expanded with references, then we only need one structure as the GC does not need to keep track of those structure.
For a composite object, we first put the normal attributes of the current object at the top, then we add the expanded attributes. Because it does not map nicely with our generated code for struct `cnode' there is an initialization phase to find out how big are our expanded and where exactly they are located:
class C feature b: expanded class B end expanded class B feature i: INTEGER end
For class C we generate the following cnode struct like we did before except that the following fields will be updated as
struct cnode * cnode_of_C; #ifdef WORKBENCH cnode_of_c->size = normal_size + sizeof (struct ex_B); #else cnode_of_c->cn_offsets #endif
Let's say we have in class A (above) the following code:
set_s (a_s: like s) is do s := a_s end
And somewhere we have:
a: A a.set_s ("Fdsfds")
Here is what would be generated:
set_s (EIF_REFERENCE parent, struct ex_A * Current, EIF_REFERENCE a_s) { Current->s = a_s; RTAR(parent, a_s); }
As you notice we generate an extra argument compared to the usual case. This is the reference to the object, if any. that contains Current. When `Current' is actually a local variable, then it is NULL, otherwise it is a reference to the parent object.
When you do:
a: A ... a.f (x)
struct Tex_A a; struct ex_A *a_call; a_call = &(a.item) f(NULL, a_call, x);
EIF_REFERENCE Current; struct ex_A *a_call; a_call = (struct ex_A *) (Current + offsets (Current, a)); f(Current, a_call, x);
Note that when `a' is an attribute of an object, we pass this object as first argument of `f'.
struct Tex_A a; EIF_REFERENCE obj; (memcpy (obj + offset_to_attribute, &(a.item), sizeof(struct ex_A));
struct Tex_A a; EIF_REFERENCE obj; (memcpy (&(a.item), obj + offset_to_attribute, sizeof(struct ex_A));