Skip to content

Type System Internals

opencode-agent[bot] edited this page May 9, 2026 · 2 revisions

Type System Internals

The VM's class representation system, from VmType to TIB construction.

Overview

JNode's type system centers on VmType and its subclasses, which provide the runtime representation of Java classes. The type system manages class loading, linking (verification, preparation, compilation), field layout, and the Type Information Block (TIB) construction that enables object method dispatch.

Key Components

Class File Purpose
VmType classmgr/VmType.java Abstract base for all type representations
VmClassType classmgr/VmClassType.java Base for class and interface types
VmNormalClass classmgr/VmNormalClass.java Regular class (non-interface, non-array)
VmArrayClass classmgr/VmArrayClass.java Array type
VmInterfaceClass classmgr/VmInterfaceClass.java Interface type
VmPrimitiveClass classmgr/VmPrimitiveClass.java Primitive type (int, boolean, etc.)
TIBBuilder classmgr/TIBBuilder.java Constructs the Type Information Block
ObjectLayout classmgr/ObjectLayout.java Calculates field offsets and object size
VmClassLoader classmgr/VmClassLoader.java Class loading orchestration
TIBLayout classmgr/TIBLayout.java TIB array index constants

How It Works

VmType Hierarchy

VmType (abstract)
  ├── VmClassType
  │     ├── VmNormalClass
  │     └── VmInterfaceClass
  ├── VmArrayClass
  └── VmPrimitiveClass
  • VmType provides common fields (name, classloader, constant pool, state flags, method/field tables)
  • VmClassType extends VmType for class-like types (has superclass, modifiers)
  • VmNormalClass adds object size and reference offset tracking
  • VmArrayClass tracks component type and array-specific properties
  • VmPrimitiveClass represents built-in types (boolean, int, etc.)

Class Loading Pipeline

The loading pipeline proceeds through defined states (see VmTypeState):

  1. LOADED — Class bytes parsed, VmType created
  2. PREPARING — Superclass and interfaces loaded, field layout calculated
  3. PREPARED — Memory manager type created, TIB allocated
  4. VERIFYING — Bytecode verification
  5. VERIFIED — Verification complete
  6. COMPILING — JIT compilation of methods
  7. COMPILED — Native code ready
  8. LINKED — All preparation complete
  9. INITIALIZING/INITIALIZED<clinit> executed

The entry point is VmClassLoader.loadClass():

public Class<?> loadClass(String name, boolean resolve) {
    VmType result = loadClassImpl(name, resolve);
    // For reflection, convert to java.lang.Class
    return result.asClass();
}

Linking (Prepare, Verify, Compile)

The VmType.link() method orchestrates the three-phase linking:

public final VmType link() {
    if (!isLinked()) {
        prepare();    // Field layout, TIB creation
        verify();     // Bytecode verification
        compile();    // JIT compile methods
        // ...
        state |= VmTypeState.ST_LINKED;
    }
    return this;
}

Preparation (doPrepare in VmType):

  1. Load and prepare superclass
  2. Resolve implemented interfaces
  3. Calculate object size via ObjectLayout
  4. Assign field offsets (including alignment)
  5. Build allInterfaceTable (transitive interface closure)
  6. Construct TIB via TIBBuilder

Field Layout Calculation

ObjectLayout calculates instance field offsets:

public static int calculateObjectSize(VmNormalClass<?> clazz) {
    int size = OBJECT_HEADER_SIZE;  // Flags (4) + TIB reference (4) = 8 bytes
    
    // Walk superclass chain to get inherited field sizes
    VmType superClass = clazz.getSuperClass();
    if (superClass != null) {
        size += ((VmNormalClass)superClass).getObjectSize();
    }
    
    // Add this class's fields with 8-byte alignment
    for (VmField field : clazz.getDeclaredFields()) {
        if (!field.isStatic()) {
            size = align(size, field.getTypeSize());
            size += field.getTypeSize();
        }
    }
    return size;
}

Alignment rules:

  • Reference fields aligned to pointer size (4 or 8 bytes)
  • long/double fields aligned to 8 bytes
  • Object size padded to 8-byte multiple

Reference offsets are collected for GC during preparation.

TIB Construction

The Type Information Block (TIB) is a per-class Object array containing:

Index Content
0 VmType reference (class pointer)
1 IMT (Interface Method Table)
2 IMT collision flags array
3 Compiled IMT
4 Superclasses array
5+ vtable (virtual method pointers)

TIBBuilder.buildTIB() creates this structure:

public Object[] buildTIB(VmType type, VmInterfaceClass[] allInterfaces) {
    Object[] tib = new Object[calculateTIBSize()];
    
    // Index 0: VmType reference
    tib[TIBLayout.VMTYPE_INDEX] = type;
    
    // Index 1-4: IMT, collisions, compiled IMT, superclasses
    tib[TIBLayout.IMT_INDEX] = buildIMT(type, allInterfaces);
    tib[TIBLayout.IMTCOLLISIONS_INDEX] = new boolean[64];
    tib[TIBLayout.COMPILED_IMT_INDEX] = null;
    tib[TIBLayout.SUPERCLASSES_INDEX] = buildSuperclassesArray(type);
    
    // Index 5+: vtable methods
    int methodIndex = TIBLayout.FIRST_METHOD_INDEX;
    for (VmMethod m : type.getVirtualMethods()) {
        tib[methodIndex++] = getMethodAddress(m);
    }
    
    return tib;
}

The TIB is accessed at object header offset -1 (just before the Flags word).

Constant Pool Resolution

The constant pool (VmCP) contains symbolic references that are resolved on demand:

public Object resolveConstant(int index) {
    VmCPEntry entry = getEntry(index);
    switch (entry.getType()) {
        case CONSTANT_CLASS:
            return resolveClass((VmConstClass)entry);
        case CONSTANT_FIELD:
            return resolveField((VmConstFieldRef)entry);
        case CONSTANT_METHOD:
            return resolveMethod((VmConstMethodRef)entry);
        case CONSTANT_STRING:
            return resolveString((VmConstString)entry);
        // ...
    }
}

Resolution lazy-loads referenced classes and verifies method/field existence and accessibility.

Gotchas

  • Recursive preparation: Calling getSuperClass() during preparation triggers recursive load; the state machine prevents infinite recursion
  • TIB allocation: TIB is allocated in the boot image for system classes, dynamically for others
  • Interface method dispatch: Uses a 64-element hash table (IMT) with collision chains; the compiled IMT provides faster dispatch
  • Reference offsets: Collected during preparation for GC; must be recalculated when field layout changes
  • Statics isolation: Classes can have @SharedStatics (shared across isolates) or isolate-specific statics via separate allocation

Related Pages

Clone this wiki locally