OK, good point; however, I was trying to find a way to eliminate the c-lists and handles, and let the capabilities be represented as the code doing the access itself. It seems that I didn't state that part.
I came up with it indirectly, while considering ways to eliminate the system call in a
quaject's callentries.
I am pretty sure Brendan is familiar with this, but I expect there are others reading this who aren't, so let me explain. A quaject can be seen loosely as an object which can have vtable slots (er, not exactly, but close enough for now) for callentries (methods), callbacks, and callouts (continuations, usually to call finalizers or similar things) which are specific to the instance, where the 'default' vtable entries can be replaced with entries pointing to specialized code generated by the system, either when constructed or over the course of their lifetime, and can even have the entries themselves replaced by code for very small snippets. The purpose is to allow general-purpose code to be replaced by code generated at run time for a specific operation, based on one or more templates which are created when the 'class' is defined.
Most quajects are not used for data,
per se, but as placeholders for operations such as reading from or writing to disk, where the overhead of the generating the specialized code is less than that of using the general-purpose driver; the 'driver' would be a template which could be used to generate the callentries.
In Synthesis, access to callentries/callbacks/callouts is done using handles that are given to it by the kernel, in a way similar to c-lists (the original dissertation explicitly compares the quaject handles to c-lists, in fact). As I understand it,the synthesized code is stored in memory shared by the kernel and the specific process, which the process has execute permissions for but not read or write permissions (and with most applications also getting their own virtualized 's-machine' for further separation), but in order to access the right code, you need a system call, each and every time. While system calls in Synthesis are usually a good deal faster than in most other OSes (relatively speaking, as the two systems it ran on are rather dated today, being based on the 68000 CPU), it still seemed like a good idea to find a way to eliminate even that overhead. I thought of public-key encryption as a way to do this, but concluded that the overhead would probably be higher than that of the system call would be.
(In any case, a lot of the things which a callentry might need to do would require the CPU to be in system mode anyway. That didn't occur to me until just now, for some reason. I think I need to re-read those papers again.)
That's when I thought about capabilities. They are implemented in similar ways, as already stated, and are invoked much less often than callentries would be. It seemed to me that this approach would be acceptable as a way of implementing capabilities in terms of quajects - each callentry/back/out would in effect also be a capability.
Still, I can't see any way to do this that isn't either slower than c-lists, or less secure, or both. I still want to play around with the idea, both regarding this and for something a little different (I think it might have applications regarding DRM, specifically as 'gatekeeper' which could be done as an a pair of add-ons for HTTP servers and browsers, but I need to think more about it), but at this point I don't know if it will go anywhere.