Metaobjects

    A metaobject is an object that controls the behavior of another object. If an object has an attached metaobject and a message is sent to the object, this message is packed in an object and passed as parameter to a method called methodCall of the metaobject. Then the metaobject can call the original object method or do anything it wants to.

    Metaobjects in Green are called shells and are a little bit different from the previous description. A shell is a pseudo-object with methods and instance variables that can be attached to an object. Any message sent to the object is first searched in the shell and then in the object. As an example, we will use shell class Border and class Window.

class Window
    proc init( ... )
      begin
      ...
      end
  public:
    proc draw()
      ...

    ...
end

shell class Border(Window)
    proc init()
      begin
      end
  public:
    proc draw()
      begin
      self.drawBorder();
        { call object method }
      super.draw();
      end
    private:
      proc drawBorder()
        ...
end

    As indicated by the syntax "shell class Border(Window)", Border shells can be attached to objects of subtypes of Window, which includes Window itself. A shell of Border can be attached to a window object through the syntax
     Meta.attachShell(window, Border.new());
Since Border defines a method draw, if a message "draw()" is sent to an object with a Border shell the method draw of Border will be called. Study the following example.

window = Window.new("My Window", 30, 20, 120, 300);
window.draw();  // call Window::draw()
Meta.attachShell( window, Border.new() );
window.draw();  // call Borfer::draw

A shell can be removed from an object by

     Meta.removeShell(window)

    The full functionality of metaobjects is got by declaring a method interceptAll in a shell class:

shell class Border(Window)
  public:
    proc interceptAll( mi : ObjectMethodInfo;
                       vetArg : array(Any)[] )
      begin
      mi.invoke(vetArg);
      end
end

    Class ObjectMethodInfo belongs to the introspective reflection library and is used to describe a method of a specific object. Method invoke of this class calls the method with the parameters given by the array vetArg.

    After a Border shell is attached to an object, any message sent to the object will be packed in parameters mi and vetArg and method interceptAll of Border will be called. For example, in the code

     window = Window.new(...);
     window.draw();
     Meta.attachShell(window, Border.new());
     window.draw();

the last statement will call Border::interceptAll and parameter mi will describe method Window::draw of window (the object). vetArg will have zero elements since draw has no parameter. The statement
     mi.invoke(vetArg);
will call Window::draw using object window as the message receiver.

    Shells may be attached to class objects as well. Although class objects are classless objects, they do have types. A shell class defining one or more new methods may be attached to a class object to control the creation of objects of that class.

    There are some more examples with shells that show the C code generated by a compiler of a subset of Green.
 

What is new ?

    Shells are much easier to understand than metaobjects of other languages such as Open C++ or Meta Java. Shells are much more efficient too. If method interceptAll is not used, there is no overhead: a message send to an object with a shell is as fast as a normal message send. If interceptAll is used, there is an overhead small when compared with the use of metaobjects. To compare, suppose method interceptAll of a shell class just forwards the message to the object as in the example. Then if a shell of this class is attached to an object, a message send to it will be between 2.7 and 4.6 slower than a normal message send. We considered that the object method corresponding to the message sent has an empty body and no parameters or return value. If metaobjects of other languages were used, the overhead would be between 10 and 30 times. See more in the ECOOP'98 article.

Return