Skip to content

VM Class Manager

Levente Santha edited this page May 15, 2026 · 1 revision

VM Class Manager

JNode's class management system distributed across VmType, ClassDecoder, and VmClassLoader — handles bytecode decoding, type loading, linking pipeline, TIB construction, and JIT compilation orchestration.

Overview

JNode does not have a single "VmClassManager" or "VmTypeManager" class. Instead, class management functionality is distributed across three core components:

  1. VmType (2508 lines) — The central type representation. An abstract class that manages the entire class lifecycle: loading, verification, preparation, compilation, linking, and initialization. It holds all type metadata including the constant pool, method table, field table, interface tables, and state flags.

  2. ClassDecoder (1503 lines) — The bytecode parser that decodes .class files into VmType instances. It parses the JVM class file format (magic number, constant pool, fields, methods, attributes) and creates VmNormalClass, VmInterfaceClass, VmArrayClass, or VmPrimitiveClass instances.

  3. VmClassLoader (150 lines) — The abstract classloader contract defining loadClass(), defineClass(), findLoadedClass(), and JIT compilation hooks. Concrete implementations (VmSystemClassLoader, VmJavaClassLoader, PluginClassLoaderImpl) provide the actual class loading logic.

Together, these three components implement the full JVM class loading pipeline: bytecode decoding → type creation → linking (prepare, verify, compile) → initialization.

Key Components

Class / File Role
core/src/core/org/jnode/vm/classmgr/VmType.java Central type representation (2508 lines): lifecycle management, constant pool, method/field tables, state machine
core/src/core/org/jnode/vm/classmgr/ClassDecoder.java Bytecode parser (1503 lines): decodes .class files into VmType instances
core/src/core/org/jnode/vm/classmgr/VmClassLoader.java Abstract classloader contract (150 lines): loadClass, defineClass, JIT hooks
core/src/core/org/jnode/vm/classmgr/VmClassType.java Abstract base for TIB-bearing types (277 lines): prepareTIB(), prepareIMT()
core/src/core/org/jnode/vm/classmgr/VmNormalClass.java Regular class representation (168 lines): object size, reference offsets for GC
core/src/core/org/jnode/vm/classmgr/VmArrayClass.java Array type metadata (182 lines): component type, total/maxLength
core/src/core/org/jnode/vm/classmgr/VmInterfaceClass.java Interface representation (96 lines): no TIB, no IMT
core/src/core/org/jnode/vm/classmgr/VmPrimitiveClass.java Primitive type representation (92 lines): jvmType, floatingPoint, wide
core/src/core/org/jnode/vm/classmgr/VmTypeState.java State flag bitmasks: LOADED, PREPARED, VERIFIED, COMPILED, LINKED, INITIALIZED
core/src/core/org/jnode/vm/classmgr/TIBBuilder.java Vtable builder (180 lines): inheritance-copy, method append/override
core/src/core/org/jnode/vm/classmgr/IMTBuilder.java Interface Method Table builder (148 lines): 64-slot hash table with collision chaining
core/src/core/org/jnode/vm/classmgr/ObjectLayout.java Object memory layout constants: FLAGS_SLOT=-2, TIB_SLOT=-1, IMT_LENGTH=64
core/src/core/org/jnode/vm/classmgr/TIBLayout.java TIB array index constants: VMTYPE_INDEX=0, IMT_INDEX=1, FIRST_METHOD_INDEX=5
core/src/core/org/jnode/vm/classmgr/VmCP.java Runtime constant pool representation (256 lines): 12 JVM CP entry types

How It Works

Class Loading Pipeline

The complete class loading and linking pipeline:

1. ClassDecoder.decodeClass(byte[]) 
   → parses .class file format
   → creates VmNormalClass, VmInterfaceClass, or VmArrayClass
   → populates constant pool, fields, methods, interfaces

2. VmType.link()
   → doPrepare()   — build TIB (vtable), IMT, superclasses array, fix field offsets
   → doVerify()    — verify superclass chain (TODO: full bytecode verification)
   → doCompile()   — compile all methods via L1/L2 JIT compilers
   → sets ST_LINKED state flag

3. VmType.initialize()
   → invokes <clinit> via VmReflection.invokeStatic()
   → sets SST_INITIALIZED / IST_INITIALIZED state flag

VmType State Machine

VmType tracks class state through bit flags in VmTypeState:

ST_LOADED (0x0001)     → Class bytes have been loaded
ST_DEFINED (0x0002)    → Type has been defined in the VM
ST_PREPARING (0x0010)  → TIB/IMT construction in progress
ST_PREPARED (0x0020)   → TIB/IMT construction complete
ST_VERIFYING (0x0004)  → Bytecode verification in progress
ST_VERIFIED (0x0008)   → Bytecode verification complete
ST_COMPILING (0x0080)  → JIT compilation in progress
ST_COMPILED (0x0040)   → JIT compilation complete
ST_LINKED (0x2000)     → prepare + verify + compile all done
ST_INITIALIZING        → <clinit> execution in progress
ST_INITIALIZED         → <clinit> execution complete
ST_INVALID (0x8000)    → Class loading failed

State transitions are enforced by doPrepare(), doVerify(), doCompile(), and doInitialize() methods, each of which checks preconditions and sets the appropriate flags.

VmType Hierarchy

VmAnnotatedElement
  └── VmType<T> (abstract, 2508 lines)
        ├── VmClassType<T> (abstract, 277 lines) — has TIB/vtable
        │     ├── VmNormalClass<T> (168 lines) — regular classes
        │     │     └── VmPrimitiveClass<T> (92 lines) — primitives
        │     └── VmArrayClass<T> (182 lines) — array types
        └── VmInterfaceClass<T> (96 lines) — interfaces (NO TIB)

See VmType-Hierarchy for detailed coverage of each class.

ClassDecoder Bytecode Parsing

ClassDecoder parses the JVM class file format:

Class File Structure:
  Magic Number (0xCAFEBABE)
  Minor/Major Version
  Constant Pool (12 entry types: UTF8, INT, FLOAT, LONG, DOUBLE,
                 CLASSREF, STRING, FIELDREF, METHODREF, IMETHODREF,
                 NAMEANDTYPE)
  Access Flags
  This Class / Super Class
  Interfaces Table
  Fields Table (with attributes: annotations, constant value)
  Methods Table (with attributes: Code, Exceptions, Annotations)
  Class Attributes (annotations, source file, signature, pragma flags)

Key parsing methods:

  • decodeClass() — main entry point, parses the entire class file
  • readCode() — parses the Code attribute (max stack, locals, bytecode, exception table)
  • readFields() — parses the field table, creates VmStaticField or VmInstanceField
  • createFields() — allocates statics space, aligns fields, calculates offsets
  • readMethods() — parses the method table, creates VmStaticMethod, VmSpecialMethod, or VmInstanceMethod
  • readInterfaces() — parses the implemented interfaces table
  • readRuntimeAnnotations() — parses annotation attributes
  • getNativeCodeReplacement() — finds native method replacements (JNode's native method substitution mechanism)

TIB Construction (prepareTIB)

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

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

    if (superClass != null) {
        // Inherit from superclass vtable
        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);
    }

    // Process each declared instance method
    for (VmInstanceMethod method : declaredMethods) {
        if (!method.isStatic() && !method.isSpecial()) {
            final int index = vmt.indexOf(name, signature);
            if (index >= 0) {
                if (vmt.overrides(index, method)) {
                    vmt.set(index, method);  // Override
                } else {
                    vmt.add(method);  // Package-private: new slot
                }
            } else {
                vmt.add(method);  // New method: append
            }
        }
    }

    // Abstract classes: clone unimplemented interface methods
    if (isAbstract()) {
        for (VmInterfaceClass<?> icls : allInterfaces) {
            for (VmMethod intfMethod : icls.getDeclaredMethods()) {
                if (!intfMethod.isStatic()) {
                    final int index = vmt.indexOf(intfMethod.getName(), intfMethod.getSignature());
                    if (index < 0) {
                        vmt.add(new VmInstanceMethod(intfMethod));  // Clone
                    }
                }
            }
        }
    }

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

See Virtual-Methods-Dispatch for detailed coverage of vtable/IMT dispatch.

VmClassLoader Contract

VmClassLoader defines the abstract classloader interface:

public abstract class VmClassLoader extends VmSystemObject {
    public abstract VmType<?> loadClass(String className, boolean resolve);
    public abstract VmType<?> findLoadedClass(String className);
    public abstract VmType<?> defineClass(String name, byte[] data, ...);
    public abstract VmType<?> defineClass(String name, ByteBuffer data, ...);
    public abstract VmType<?> defineClass(VmType<?> createdType);
    public abstract ClassLoader asClassLoader();
    public abstract void disassemble(VmMethod vmMethod, int optLevel, ...);
    public abstract CompiledIMT compileIMT(IMTBuilder builder);
    public abstract VmArchitecture getArchitecture();
    public abstract boolean isCompileRequired();
    public abstract boolean isSystemClassLoader();
    public abstract VmSharedStatics getSharedStatics();
    public abstract VmIsolatedStatics getIsolatedStatics();
}

Concrete implementations:

  • VmSystemClassLoader — loads bootstrap classes from the boot image
  • VmJavaClassLoader — loads application classes from the classpath
  • PluginClassLoaderImpl — loads plugin classes with plugin-level delegation

See VM-Classloader for detailed coverage of each implementation.

Bootstrap vs. Runtime Compilation

VmType supports two compilation modes:

  • compileBootstrap() — called during boot image creation. Compiles all methods using the L1 compiler (fast, non-optimizing). Results are baked into the boot image.
  • compileRuntime() — called at runtime via LoadCompileService. Uses the L2 compiler (optimizing, SSA-based) for hot methods.

Primitive Class Singletons

VmType maintains static singleton references for all 9 primitive classes and their array classes:

// Initialized during VM bootstrap via initializeForBootImage()
static VmPrimitiveClass BooleanClass, ByteClass, CharClass, ShortClass,
    IntClass, LongClass, FloatClass, DoubleClass, VoidClass;

// Array classes for each primitive
static VmArrayClass BooleanArrayClass, ByteArrayClass, CharArrayClass, ...;

These are reconstructed from the boot image via loadFromBootClassArray() when the VM starts.

Gotchas & Non-Obvious Behavior

  • No single "manager" class: The issue #75 referenced VmTypeManager.java and VmClassManager.java — these files do not exist. Class management is distributed across VmType, ClassDecoder, and VmClassLoader.
  • Verification is a TODO: VmType.doVerify() only checks the superclass chain. Full bytecode verification is not implemented — JNode trusts the class files it loads.
  • Array classes delegate to component type: VmArrayClass.prepare(), compile(), and verify() all delegate to the component type first, ensuring the component class is fully linked before the array class.
  • Interfaces have no TIB: VmInterfaceClass.prepareTIB() and prepareIMT() both return null. Interface method dispatch uses the IMT in the implementing class, not the interface itself.
  • Pragma annotations control compilation: JNode uses custom @Pragma annotations to control JIT compilation behavior (e.g., @NoInline, @Uninterruptible). ClassDecoder reads these during bytecode parsing.
  • Native method replacement: ClassDecoder.getNativeCodeReplacement() allows JNode to substitute native method implementations with Java code at class load time.
  • Duplicate ID detection in IMT only logs: The IMT builder logs duplicate selectors but does not prevent them — collision chains handle the resolution.
  • isAssignableFrom uses superClassesArray: The fast path for isAssignableFrom() uses the precomputed superClassesArray rather than walking the superclass chain at runtime.

Related Pages

  • Type-System-Internals — VmType hierarchy, class loading pipeline, TIB construction
  • VmType-Hierarchy — Detailed coverage of VmType, VmClassType, VmNormalClass, VmArrayClass, VmInterfaceClass, VmPrimitiveClass
  • VM-Classloader — VmClassLoader contract and concrete implementations
  • Virtual-Methods-Dispatch — vtable, IMT, CompiledIMT dispatch mechanisms
  • JIT-Compilers — L1/L2 compiler pipeline, register allocation
  • TIB — Type Information Block structure and construction
  • vtable — Virtual method table construction and slot assignment
  • IMT — Interface Method Table with 64-element hash table
  • Object-Layout — Object header structure, TIB embedding, field alignment

Clone this wiki locally