Routines could use more support for reflection
My Trait::Traced module implements support for tracing routine calls. There are some problems with the output produced by traced routine calls though:
- Private methods and metamethods don't get traced accurately because there doesn't seem to be a simple way to determine which type of method a method was declared to be from a trait, since it happens before the method actually gets added to the type.
- Tokens, rules, and regexes all get traced as being declared to be regexes because there doesn't seem to be a simple way to determine what type of regex declaration they were created with.
- What type of declaration any type of routines was created with needs to be inferred from its type's name and the
is_dispatcherandmultimethods ofRoutine. This isn't entirely reliable since type names can be reset (this may be a case of DIHWIDT though).
Because tracing probably isn't the only scenario where reflection like this would be useful, I think more support for reflecting routines themselves, similarly to how you can reflect their signature, should exist. In Rakudo, this support could be used to optimize the gist and raku methods of the various routine types and make their output more accurate.
I propose adding the following methods:
-
Routine.multi_declarator
Returns the multi declarator the routine was declared with (or Nil if there is none).
-
Routine.declarator
Returns the routine declarator the routine was declared with.
-
Method.prefix
Returns '!' for private methods, '^' for metamethods, or Nil for regular methods.
-
Method.private
Returns whether or not the method was declared as a private method.
-
Method.meta
Returns whether or not the method was declared as a metamethod.
A couple of thoughts:
-
Routineobjects are already big enough. Not only are we paying the cost of their storage in compiled output, but also every closure clone of aRoutinepays for any extra state we add. I guessRoutine.multi_declaratorcan be inferred from existing state, but the others not. - Whether a method is a private or meta-method isn't a property of the method itself, but rather the method table it is installed into. It would be entirely possible (if unusual) for a method to be installed as both a private and a normal method. So I'm not really in favor of these ones.
- If we really want to know about regex vs. rule vs. token I'd rather model that with subtypes of
Regexinstead.
On 17 Jan 2020, at 00:48, Jonathan Worthington [email protected] wrote:
A couple of thoughts:
• Routine objects are already big enough. Not only are we paying the cost of their storage in compiled output, but also every closure clone of a Routine pays for any extra state we add. I guess Routine.multi_declarator can be inferred from existing state, but the others not.
If Routine objects are basically too fat for their own good, would it make sense to combine $!rw, $!yada and $!onlystar into a single bitmap, thereby reducing each Routine by 128 bytes? Or even more if it would make sense to make that bitmap an int8?
• If we really want to know about regex vs. rule vs. token I'd rather model that with subtypes of Regex instead.
This looks an easy way to do this. Has worked fine for ObjAt and ValueObjAt, and Iterator and PredictiveIterator :-)
If Routine objects are basically too fat for their own good, would it make sense to combine $!rw, $!yada and $!onlystar into a single bitmap, thereby reducing each Routine by 128 bytes?
Probably $!rw and $!yada you can, and it's worth it. But $!onlystar is, iirc, looked at by the multi cache mechanism inside of the VM, so it's rather harder to eliminate it.
Or even more if it would make sense to make that bitmap an int8?
I suspect alignment means that you'll not gain anything.
I've merged the $!rw and $!yada attributes into a single $!flags attribute. Added a set_flag and get_flag method, so that additional flag-like meta-information could be added easily. All available and spectest clean in https://github.com/rakudo/rakudo/commit/4366980681.
Routine.multi_declarator Returns the multi declarator the routine was declared with (or Nil if there is none).
You mean, whether the routine is a multi or not? Isn't that already available in the Routine.multi bool?
Routine.declarator Returns the routine declarator the routine was declared with.
You mean: either sub or method or role or regex etc?
If we really want to know about regex vs. rule vs. token I'd rather model that with subtypes of Regex instead.
It struck me that all dispatch related attributes of Routine are basically unused if the Routine is an only sub or an only method. Would it make sense to move all of those attributes to a subclass of Routine (tentatively called MultiRoutine) and thus save a lot of overhead on the non-multi cases?
You mean, whether the routine is a
multior not? Isn't that already available in theRoutine.multibool?
I mean if it was declared with proto, multi, or only. multi can be inferred with Routine.multi and proto with Routine.is_dispatcher, but how would you tell if a routine was declared with only or not? $*MULTINESS can be used from traits to determine this, but not in very many other cases.
You mean: either
subormethodorroleorregexetc?
sub, method, submethod, regex, token, or rule, mainly.
An update on this: I'm currently doing some early R&D on macro implementation, which along the way will involve giving us a spec'd DOM for Raku code. I expect that a trait applied to a method shall be able to obtain the node for the method it's being applied to (so long as it's called at compile time, which traits are). It will be able to use that to get hold of whatever it wishes - without us having to preserve any further information to runtime in the general case.