Skip to content
opencode-agent[bot] edited this page May 10, 2026 · 1 revision

vtable (Virtual Method Table)

Method dispatch table for virtual method calls in class hierarchy.

Overview

The vtable (virtual method table) is a per-class array of method pointers stored in the TIB (Type Information Block) starting at index 5 (TIBLayout.FIRST_METHOD_INDEX). It enables efficient O(1) virtual method dispatch by indexing directly into the table at a known slot, rather than searching the class hierarchy at runtime. Each class in the inheritance chain contributes its own methods to the vtable, with overrides replacing the parent's entry at the same index. The JIT compilers use the vtable index as a direct load offset, generating a single memory access to resolve any virtual call.

Key Components

Class / File Role
core/src/core/org/jnode/vm/classmgr/TIBBuilder.java Builds the vtable from a superclass's TIB and declared methods
core/src/core/org/jnode/vm/classmgr/VmClassType.java Implements prepareTIB(), constructing the vtable via TIBBuilder
core/src/core/org/jnode/vm/classmgr/TIBLayout.java Defines FIRST_METHOD_INDEX = 5 as the vtable start offset
core/src/core/org/jnode/vm/classmgr/VmType.java Abstract prepareTIB() declaration, TIB field storage
core/src/core/org/jnode/vm/classmgr/VmNormalClass.java Concrete class representation used in vtable inheritance
core/src/core/org/jnode/vm/classmgr/VmInstanceMethod.java Virtual method representation with setTibOffset()

How It Works

TIB Layout

The TIB is an Object[] array. Indices 0–4 are reserved for metadata; the vtable begins at index 5:

TIB[index] Layout:
  [0] VmType reference
  [1] IMT (Interface Method Table)
  [2] IMT collision flags
  [3] CompiledIMT
  [4] Superclasses array
  [5..N] vtable (VmInstanceMethod references)

See TIBLayout for constants. The vtable size is class-specific and grows as subclasses add new virtual methods.

vtable Construction

The vtable is built by VmClassType.prepareTIB() using TIBBuilder:

protected Object[] prepareTIB(HashSet<VmInterfaceClass<?>> allInterfaces) {
    final VmNormalClass superClass = getSuperClass();
    final TIBBuilder vmt;

    if (superClass != null) {
        // Copy parent vtable slots
        vmt = new TIBBuilder(this, superClass.getTIB(), tc_mtable_length);
    } else {
        // Root class (e.g., Object) starts with empty table
        vmt = new TIBBuilder(this, tc_mtable_length);
    }

    // Add or override methods from this class
    for (int i = 0; i < tc_mtable_length; i++) {
        final VmMethod mts = getDeclaredMethod(i);
        if (!(mts.isStatic() || mts.isSpecial())) {
            final VmInstanceMethod method = (VmInstanceMethod) mts;
            final int index = vmt.indexOf(name, signature);
            if (index >= 0) {
                // Override existing entry if visibility allows
                if (vmt.overrides(index, method)) {
                    vmt.set(index, method);
                } else {
                    vmt.add(method); // Package-private: add as new slot
                }
            } else {
                // New method: append to vtable
                vmt.add(method);
            }
        }
    }

    this.tib = vmt.toArray();
    return tib;
}

TIBBuilder Internals

TIBBuilder maintains two structures:

  1. tibAsListArrayList<Object> holding the growing vtable
  2. nameSignature2IndexHashMap<String, Integer> for O(1) name+signature lookup
public void add(VmInstanceMethod method) {
    final int idx = tibAsList.size();
    tibAsList.add(method);
    nameSignature2Index.put(getNameSignature(method), idx);
    method.setTibOffset(idx);
}

The setTibOffset() call records the vtable slot in the method object itself, allowing compiled code to emit direct load instructions.

Inheritance Rules

When a subclass extends a parent:

  • Slot preservation: Methods from parent that are not overridden retain their vtable index in the child
  • Override replacement: When the child declares a method with identical name+signature, it replaces the parent's entry at the same index
  • New methods: Appended after the parent's last slot
  • Static methods: Excluded from the vtable entirely (dispatch uses class reference, not object TIB)
  • Private methods: Cannot be overridden; added as new slots if declared
  • Abstract interface methods: Added as placeholder entries for abstract classes

JIT Compilation

The JIT compilers resolve virtual calls using the vtable index:

// L1 compiler (X86BytecodeVisitor.java:3522)
public final void visit_invokevirtual(VmConstMethodRef methodRef) {
    // Do a virtual method table invocation
    final int vtableOffset = methodRef.getVtableOffset();
    // Loads method pointer from [TIB + vtableOffset * wordSize]
}

The vtable offset is a precomputed constant known at compile time. This allows the JIT to emit a single indexed load: mov reg, [object_ref + vtableOffset], followed by an indirect call.

Gotchas & Non-Obvious Behavior

  • Slot reuse vs. append: A subclass overriding a package-private method does not replace the parent's slot — it adds a new one at the end. This is intentional to preserve binary compatibility. See TIBBuilder.overrides() for the visibility check.
  • Vtable depth varies per class: Each class has its own vtable size based on the number of virtual methods it declares or inherits. The JIT must resolve the vtable offset relative to the object's actual class, not a static declaration.
  • Synthetic abstract method cloning: Abstract classes implementing interfaces clone each unimplemented interface method into the vtable. The clone ensures the interface method's vtable offset doesn't conflict with multiple abstract classes. See VmClassType.prepareTIB() lines 188–193.
  • TIB is shared at object creation: Every object instance carries a reference to its class's TIB (object header offset -1). All instances of Foo share the same vtable — method overrides affect all instances simultaneously.
  • JIT vtable index stability: Once a method is compiled, its vtable slot is fixed. This means method overrides after first compilation require deoptimization or inline cache invalidation.
  • No vtable for interfaces: Interfaces use the IMT, not the vtable. The vtable only covers class inheritance hierarchy.

Related Pages

  • Virtual-Methods — Overview of virtual method dispatch including IMT and CompiledIMT
  • Object-Layout — Object header structure, TIB array layout, and field alignment
  • Type-System-Internals — VmType hierarchy, class loading, and TIB construction
  • JIT-Compilers — How vtable offsets are resolved during compilation
  • IMT — Interface method dispatch table

Clone this wiki locally