Skip to content

Exceptions Implementation

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

Exceptions-Implementation

How JNode implements Java exception handling at the VM and JIT levels.

Overview

JNode's exception handling spans two layers: bytecode interpretation and native code compilation. The JIT compiler (NativeCodeCompiler) transforms Java's exception table structure into a compact native representation, while the VM runtime (VmSystem.findThrowableHandler) performs stack unwinding to locate handlers.

Key Components

Class Purpose
VmCompiledExceptionHandler Native representation of a bytecode exception entry
VmCompiledCode Holds compiled native code + exception handler table
VmExceptions Declared exceptions from method's Exceptions attribute
AbstractExceptionHandler Base class; stores the catchType (VmConstClass)
NativeCodeCompiler Converts bytecode exception table to VmCompiledExceptionHandler[]

Source path: core/src/core/org/jnode/vm/classmgr/

How It Works

Exception Table Representation

The bytecode exception table is converted at JIT compile time into an array of VmCompiledExceptionHandler objects. Each handler stores raw native addresses:

public final class VmCompiledExceptionHandler extends AbstractExceptionHandler {
    private final VmAddress handler;   // Native address of catch block
    private final VmAddress startPtr;  // PC range start
    private final VmAddress endPtr;    // PC range end

    public boolean isInScope(Address address) {
        final Address start = Address.fromAddress(startPtr);
        final Address end = Address.fromAddress(endPtr);
        return address.GE(start) && address.LT(end);
    }
}

VmCompiledCode holds the handler table and a defaultExceptionHandler:

public final class VmCompiledCode extends AbstractCode {
    private final VmCompiledExceptionHandler[] eTable;
    private final VmAddress defaultExceptionHandler;
    // ...
}

Stack Unwinding

When an exception is thrown, VmSystem.findThrowableHandler() walks the call stack:

public static Address findThrowableHandler(Throwable ex, Address frame, Address address) {
    // 1. Get current method + compiled code
    final VmMethod method = reader.getMethod(frame);
    final VmCompiledCode cc = reader.getCompiledCode(frame);

    // 2. Iterate exception handlers
    for (int i = 0; i < cc.getNoExceptionHandlers(); i++) {
        final VmCompiledExceptionHandler ceh = cc.getExceptionHandler(i);

        if (ceh.isInScope(address)) {
            final VmConstClass catchType = eh.getCatchType();

            if (catchType == null) {
                // Catch-all (finally block)
                return Address.fromAddress(ceh.getHandler());
            } else {
                // Check assignability
                if (handlerClass.isAssignableFrom(exClass)) {
                    return Address.fromAddress(ceh.getHandler());
                }
            }
        }
    }

    // 3. If PC is in compiled code but no handler matched
    if (cc.contains(address)) {
        return Address.fromAddress(cc.getDefaultExceptionHandler());
    }
    return null;
}

The process repeats for each stack frame until a handler is found or the stack is exhausted.

Default Exception Handler

The defaultExceptionHandler is invoked when:

  • The exception PC is within the method's compiled code bounds
  • No explicit handler matched (wrong exception type)

It typically transfers control to an interpreter fallback or performs a fatal error dump.

@Uninterruptible Code Restrictions

Methods annotated with @Uninterruptible cannot throw exceptions because:

  1. They run without GC safepoints — throwing creates a safepoint
  2. Stack unwinding requires potentially blocking operations
  3. Thread switching is disabled, but handler dispatch may need it

Code marked @Uninterruptible must handle all error conditions inline. If an error occurs, the typical pattern is:

@Uninterruptible
public final void criticalOperation() {
    if (somethingWrong) {
        Unsafe.die("Fatal error in criticalOperation");
    }
}

Attempting to throw from uninterruptible code results in undefined behavior or a fatal VM halt.

Related Pages

Clone this wiki locally