First off, general u3/vere docs are here. They feel pretty opaque, and Paul/~fodwyt, an experienced vere dev, has admitted to reading them every so often and still bringing back new understanding of the doc each time. I suggest to skim it, most of it isn't exactly relevant, but do read the sections on reference counts and reference protocols.
For a quick overview on what goes into adding a jet, see this commit as an example. (Matching Hoon change is here though only the use of ~%
and ~/
is of interest.)
To get your jet called whenever the matching Hoon code is called, that code needs to be hinted. Use ~/
and sometimes ~%
for this. hoon.hoon
and zuse.hoon
contain plenty examples on how to do this. Generally, for core jets, do whatever your neighboring arms are doing. Then, for gate jets, do whatever similar-looking cores are doing. ~/
is usually fine for the gates themselves.
Jets are registered in /jets/tree.c
. The general patter you'll observe is that a u3j_harm
is created for each jetted arm, and that those are then collected in a u3j_core
which matches the (jetted) core they're in in the Hoon code.
".2"
says what we're jetting lives at axis 2 (of what was hinted?). This is followed by the w
version of the jet (see below). Optionally, also put c3n
behind the function to indicate the jet needs to be tested. When this is done, the Hoon code will be run alongside the jet, and vere will crash if the results don't match. Turn this off before committing.
Remember how I mentioned reading up on reference counting above? When writing jet code, that comes into play.
To increment the reference count, we "keep" a u3_noun
or u3_atom
using u3k(the_thing)
.
To decrement the reference count, we "lose" a u3_noun
or u3_atom
using u3z(the_thing)
.
There's three versions of your jet functions you can write:
q
: These are gonna be the meat of your jet. Their function signature often closely resembles that of the Hoon code, and the logic performed within should produce an identical result. You should not lose any references to your arguments, but you should make sure to lose all nouns/atoms you create during the logic, except for the one you're returning. This also holds true for nouns you're not explicitly storing in a variable, but just pass directly into a function. The difference betweenq
andk
comes into play here.k
: These have the same function signature asq
, except they promise to lose a reference to all the arguments. These are implemented by calling theq
version, storing the result in au3_noun
, callingu3z()
on all arguments, and then returning the stored result.w
: These take a single argument,u3_noun cor
, and are tasked with unpacking that noun into individual arguments, using which it will callq
. For this, useu3r_mean()
for unpacking multiple values at once, oru3x_at()
for an individual value.xtract.h
might be useful in some cases, though it seemsu3r_mean()
is most common. For each version of the jet you write, be sure to add it to/include/jets/x.c
wherex
is the version you wrote. You will always be writingw
andq
versions.k
is entirely optional, and generally best to only add when it turns out you need it somewhere (for ref-counting convenience).
When dealing with u3_atom
s, they can either be pointers (in the case of bignums) or direct values (aka "cat"s, in the case of ints that fit in 31 bits). You may want to c3_assert(_(u3a_is_cat(my_atom)))
in cases where larger values are not expected and doing C-style arithmatic on the value would be easier. Don't even think about doing C-style addition etc on non-cat atoms.
For dealing with non-cat atoms, it's useful to know that you're free to call other jets from within yours. For example, to add two atoms, just call u3qa_add(a1, a2)
(or the k
version, depending).
To do printfs, use fprintf(stderr, "hello world")
.
When you think you're done, there's a couple things you can do to test for correctness. This is in addition to running your jet registered with c3n
as described above.
Before proceeding with the below, we need to make sure the jet runs under conditions that actually trigger the memory checker. In zuse.hoon
, find ++ sein
. Directly under the |=
, add a (or multiple) ~&
printf that calls the function you jetted. Put this modified zuse.hoon
into a ship's base desk, and run .pill +solid
from dojo to start compiling the system files into a pill. Shut the ship down, copy out ship/.urb/put.pill
and save it somewhere easy.
Next, to actually find out whether you're leaking memory or not, define U3_CPU_DEBUG
in trace.h
, define U3_MEMORY_DEBUG
in allocate.h
, and boot a new ship with the -g
flag set, using -B
to point to the special pill you just created.
If you're leaking memory, it will print the identified leaks for you and crash. If this information is too vague and you want to see the data that you're leaking, uncomment the u3m_p
call on line 1736 of allocate.c
. To test whether it's actually working, add a u3nc(0,0);
into your jet. This should be picked up as a memory leak.