Whenever an operation is requested to a reflective object, an implicit interaction with its meta-object takes place. An interception mechanism reifies the operation and invokes a method that the target object's meta-object must implement to handle intercepted operations.
This method is supplied with the reified operation, i.e., an object that allows the meta-object to find out which base-level object the operation refers to (because a meta-object might be associated with several objects), what kind of operation it is (method invocation, or attribute read or write), which method or attribute it operates upon, the argument list of a method invocation, or the value to be stored in an attribute.
Several reflective architectures assume that, when a meta-object is requested to handle an operation, it becomes responsible for providing a result for it, by producing the result itself or by requesting the base-level object to perform the intercepted operation.
Unfortunately, allowing a meta-object to deliver an operation to an object introduces two potential security holes. Whatever mechanism a meta-object would use to deliver the operation to its target object might be used by other objects to access an object, possibly bypassing interception. However, even if access to this mechanism was restricted so that only the meta-object associated with an object could deliver operations to it, the second hole remains: if an intruder manages to obtain a reference to a meta-object, it may pretend to be the interception mechanism and ask the meta-object to handle arbitrary operations, and the meta-object may end up delivering them to the base-level object.
In order to avoid these security holes, we propose that the only entity able to deliver operations to objects must be the interception mechanism, and the only way a meta-object can request it to do so is by arranging that the operation handling method returns a delivery request. Hence, a meta-object cannot be led into delivering an operation, since it is impossible to bypass the interception mechanism.
Thus, when a meta-object is requested to handle an operation, it may return a result for the presented operation or a request for an operation to be delivered. In the latter case, it may indicate that it wishes to view or modify the result of the operation, after it is performed. If it does not indicate it, the result can be returned directly to the requester of the operation, without reification. Otherwise, the interception mechanism will invoke a method that the meta-object must implement to handle results. This method may return a different result for the operation.
It is desirable for meta-objects to be able to modify operations they are requested to handle, as well as to create stand-alone operations and submit them for execution by the objects they control. Furthermore, allowing a meta-object to judiciously break encapsulation of a base-level object may ease the implementation of some typical meta-level features such as persistence and remote invocation, but this ability should be restricted to the meta-object associated with the object.
Unlike the case of operation delivery, this mechanism cannot be based upon returning from operation handling requests, because it should be possible to create operations autonomously. A publicly accessible method that would allow for creation of operations after verifying that its caller is the meta-object of the target object would probably work. However, it would limit the use of other meta-level objects as part of the implementation of meta-objects, because these other meta-level objects would be unable to create operations.
The mechanism we have designed is based on operation factories, meta-level objects that function as keys that allow their possessors to open the implementation of an object. For each reflective object an operation factory is created for its meta-object. The operation factory will only create operations targeted at that object. It is up to the meta-object to keep the operation factory secret. One interesting feature of this mechanism is that meta-objects can disclose operations factories to other meta-objects they collaborate with.
Operation factories provide methods to create stand-alone and replacement operations. A stand-alone operation can be submitted for execution by the invocation of a method it offers, but it must undergo interception, because only the interception mechanism can deliver an operation to its target object.
A replacement operation, on the other hand, can be returned by the operation handling method to the interception mechanism, that will deliver it instead of the operation it replaces. For this reason, the operation factory methods used to create replacement operations must ensure that a replacement operation is compatible, in terms of result type, with the replaced operation, and it carries a reference to the replaced operation, to prove that it has not been created to replace another operation.
The existence of a mechanism to create replacement operations obviates the need for mechanisms to modify existing operation objects, so we have simplified the interface of operation objects by making them immutable.