Instead of writing functions where every class is handled as a branch (like in Listing 8.10.2), we create a function for every method. For every class, there is a table of functions (more specifically: function pointers). This table is called virtual method table, which is often abbreviated as VMT, vtable, or vtab. The VMT contains for each method of the class (and every super class) the address of the corresponding function. Since every class has its own VMT, the address of the VMT identifies the class uniquely. It corresponds to the “tag” op of the struct exp_t in Listing 8.10.2. Every object obtains during its initialization (where the concrete type is known) a pointer to the VMT of the class in a field that is hidden to the programmer (vtab in Listing 8.12.1). Since we need to access the VMT without knowing the concrete type, the pointer to the VMT is at the same position for every object (usually at offset 0). This allows us to identify the concrete type of any object at run time.
The address of the virtual method required at a given call site results from a two-step process: First, the right entry in the VMT needs to be determined. This information is given by the method name (and the signature, in case of method overloading) and can therefore be derived statically at compile time. In our example in Listing 8.12.1, the selected entry corresponds to a field in the ...vtab_t structs. Java's static semantics guarantees that there is an entry for the called method in every possible VMT. In the second step, the concrete VMT is determined. Since the VMT address has been written to the object's hidden vtab field during initialization, we only need to load this address at run time to obtain the right VMT.
One such call occurs in exp_add_eval in Listing 8.12.1 in the return statement. The expression l->vtab->eval determines the address of the overridden method eval via the VMT.