Efficient C implementation of expanded (Draft 1)

Structure

Expanded structure

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.

Composite 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

Updating a reference from an expanded object

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.

Calling feature on expanded object

When you do:

a: A
...
a.f (x)

If a is a local

struct Tex_A a;
struct ex_A *a_call;

a_call = &(a.item)
f(NULL, a_call, x);

If a is an attribute

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'.

Assigning expanded attributes or locals

From a local to an attribute

struct Tex_A a;
EIF_REFERENCE obj;

(memcpy (obj + offset_to_attribute, &(a.item), sizeof(struct ex_A));

From an attribute to a local

struct Tex_A a;
EIF_REFERENCE obj;

(memcpy (&(a.item), obj + offset_to_attribute, sizeof(struct ex_A));