[e-lang] Callback-less primitives (was Re: Debugging?)
Kevin Reid
kpreid at mac.com
Fri Oct 5 00:10:45 EDT 2007
On Oct 1, 2007, at 20:54, Kevin Reid wrote:
> A vat is created whose implementation includes debugging support --
> most likely, it is implemented using objects living in a normal
> vat. The "host" vat can therefore run code which examines the
> "debug-vat" objects and execution state to examine/step/modify them.
> ...
> This hasn't been done yet because it requires what I'll call a
> "full VM" implementation of E: there must be no immediate callbacks
> from primitive code to E objects, as that would make interruption
> impossible.
MarkM and I discussed this just now. We could not think of any very-
hard cases of immediate callbacks; here are some primitives which
call back that we thought about:
1. anything which coerces
2. ConstMap#iterate/1
3. __loop
1. Standard guard coercion calls __conformTo/1 on any object which is
not of the required primitive type. This can be handled by the debug-
vat executing, for example, 3#add/1 as if it were:
def 3 { to add(other :int) { return __primitiveAdd(3, other) } }
That is, the invocation of int#coerce/2 is an explicit operation
visible in the debugger, and is followed by a call of
__primitiveAdd#run/2. __primitiveAdd does not coerce; it does a
primitive type check (semantically equivalent to __approvedBy) which
may fail but never invokes its argument's __conformTo.
2. ConstMap#iterate/1, for example, can be implemented as if it is:
to iterate(f) {
def keys := map.getKeys()
var i := 0
while (i < keys.size()) {
f(i, map[keys[i]])
i += 1
}
}
The primitive map operations for this purpose are then getKeys/0 and
get/1, which have no reason to call back.
3. __loop is harder; its entire purpose is to call back. However,
suppose it is implemented in E as:
def __loop(f) {
if (f()) {
__loop(f)
}
}
This is a tail-recursive procedure, so it's fine as long as the
evaluator does tail-call elimination. Now, doing so is undesirable in
general for debugging, but in this particular case, every call frame
is *identical*: __loop(<some-thunk>). Therefore, the debugger can,
without discarding information, condense
- __loop.run(<x>)
- __loop.run(<x>)
- __loop.run(<x>)
into
- __loop.run(<x>) [x3]
Furthermore, this may be used to break upon certain numbers of loop
iterations, or notice some kinds of apparently-infinite recursion.
--
Kevin Reid <http://homepage.mac.com/kpreid/>
More information about the e-lang
mailing list