Skip to main content.

Archives

This is the archive for April 2010

Thursday, April 08, 2010

Ok, the problem with the "opaque object* and class handler" model I just posted about is inheritance. But it's a problem even now, as pure CoreObject derived entities cannot be inherited at all, while more complex FalconObject based entities can inherit only from ONE non-pure falcon base class.

This should fix the problem and allow interface-based classing to work.

The system is based on 3 classes: the dear old CoreClass implementing pure language-level classes, a base abstract class for manipulation of foreign data (UserClass) and a class glueing the two, called HyperClass. A new CoreClass can be derived from any count of base CoreClass parents; it knows that its opaque "object" is actually an array of items parallel to its property table (and there's a good news about property tables too). The CoreClass is meant to be final (or nearly so).

UserClass doesn't provide any default way to access its properties and makes no assumption on the nature of the opaque data used as object to be manipulated.

The HyperClass is created when linking a CoreClass with a UserClass-derived entity. As the new model doesn't require anymore a VM to be there to link a final class, nor the class structure declaration to be unmodifiable, the CoreClass itself can perform its own link (up to the constructor call). If all its parents are CoreClasses, it will return itself modified so that it has the union of properties provided by the parents (using the current logic). If one or more parents are not CoreClasses (if they are user or hyper classes), it creates an HyperClass that will take its place.

The Hyperclass knows its opaque structure to be an HyperObject, that is, an array of opaque pointers where the element 0 MAY be occupied by an item array storing data coming from the CoreClasses, while the others are the opaque pointers known and manipulated by each non-CoreClass parent.

The HyperClass handles its properties differently from CoreClasses: it holds a list of HyperProperties each indicating the owning class (after link resolution), the position of the owning class in the HyperObject array (0 if the parent is a CoreClass) and, in case the property is coming from the CoreClass hierarcy, the position to access the property.

In this way, if willing to access a property provided by a UserClass (of which Falcon knows nothing about the internal object manipulation logic), it will just hand down to the right class the request to get that property on an opaque data of the type that the UserClass is expecting to manipulate.

In case of explicit subclass access (i.e. HyperClass.BaseClass.prop), the process is even simpler. If the BaseClass is a CoreClass, we simply ask for the default value of that property (as defined by our standard); if it's a UserClass, we simply hand down the request for that property to the UserClass, feeding it with its own data (Note to me: a flag should indicate if we're taking the default property via the baseclass access).

The HyperClass fits also the interface object picture.
Interfaces are inherited exactly like objects. When forming an HyperClass, if a subclass provides an interface object (i.e. the sequence interface for hooking into for/in, or the array subscript interface), the winning interface gets wrapped into a HyperInterface for the same task, which records that interface and the position in the HyperObject that must be passed to that interface if invoked.

This makes possible to turn all the basic structures into UserClasses, with the ability to be even derived. The special callback hooks used for operator overload can force language-based CoreClasses to push in the hierarcy a standard InterfaceObject, whose action is that of calling the required callback. This allows, for example to override the language-defined action for an Array (declared as a UserClass) providing a __getIndex callback on a language level Array subclass (implemented as a CoreClass at module level, and becoming a HyperClass during the link step).

... cool, huh?


Talking with the people in #falcon IRC channel (at irc.freenode.org), we worked out the concept a bit, and we found out that the best place where to store the interface objects is the Falcon "CoreClass" instance.

The CoreClass has the meaning to describe what Falcon can do with the data you pass. Rather than storing the knowledge of how to handle the object in the CoreObject hierarcy, we can think of the object as a totally opaque entity, and store all the knowledge about it in the CoreClass.

This allows embedders and modules to provide totally opaque data (their data) and separate it from the code that Falcon should use to handle it.

The way to associate CoreClasses with the opaque data they should handle is still under investigation. One option is that of just storing the class and its opaque data in the item, but then we must make sure that a method creation (obj.mth) has a way to refer the class. Shouldn't be too hard in the new model we're developing, as the methods may now have a back-reference to the classes from which they come from.

Another option may that of having a pool of pre-allocated small buffers where the opaque-data / handler-class pair can be stored, and then store that pointer in the items. This has the advantage that we're sure that the opaque data can never be disconnected from the class, but it requires extra allocation, memory management and dereferences.

The current coupling of methods and objects on which they operate solves a problem similar to this, and has been proven one of our most solid assets since first alpha release; so, keeping the pair class/opaque-object or method(class)/opaque-object directly in the item structure seems a very smooth and viable solution.