jvm-rs/docs/class-loading.md

134 lines
3.8 KiB
Markdown

# Class Loading and Boot Image
**Location**: `crates/core/src/class_loader.rs`, `crates/core/src/bimage.rs`
## Boot Image (Bimage)
A 7z archive containing precompiled Java standard library classes.
### Structure
```rust
pub struct Bimage {
image: ArchiveReader<File>, // 7z archive reader
modules: Vec<String>, // Available modules
packages: HashMap<String, String>, // Package -> Module mapping
pub total_access_time: Duration, // Performance tracking
}
```
- **Default Location**: `./lib/modules`
- **Format**: 7z compressed archive
- **Structure**: `<module>/classes/<class>.class`
- **Default Module**: `java.base` (used when no module is specified)
## Class Loading Flow
The `ClassLoader` manages class resolution with a two-tier fallback mechanism:
### Process
1. **Check Cache**: Look in `DashMap<(String, LoaderId), Arc<RuntimeClass>>` for already-loaded classes
2. **Try Bimage**: Attempt to load from boot image via `bimage.get_class(module, class_fqn)`
3. **Fallback to Disk**: If not in bimage, load from filesystem at `{CLASSPATH}/{class_name}.class`
4. **Parse & Cache**: Parse ClassFile using deku, create RuntimeClass, store in cache
### Key Method
```rust
pub fn load_class(&mut self, what: &str, loader: LoaderId) -> Result<Arc<RuntimeClass>, VmError> {
let bytes = self.bimage
.and_then(|b| b.get_class("", what).ok())
.or_else(|_| Self::load_class_from_disk(what))
.map_err(|_| VmError::LoaderError(...))?;
let (_, cf) = ClassFile::from_bytes(bytes.as_ref())?;
let runtime = self.runtime_class(cf);
// Store with loader ID for multi-loader support
self.classes.insert((class_fqn, loader), Arc::new(runtime));
}
```
## Classpath Handling
### Resolution Priority
1. **Bimage (boot image)** - Primary source for standard library
2. **Command-line argument (arg[1])** - User-provided classpath
3. **Default `./data` directory** - Fallback location
### Implementation
```rust
fn load_class_from_disk(what: &str) -> Result<Vec<u8>, String> {
let class_path = std::env::args()
.nth(1)
.unwrap_or("./data".to_string())
.replace("\\", "/");
let path = format!("{class_path}/{what}.class");
// Load file from disk
}
```
## Bootstrap Process
**Location**: `crates/core/src/vm.rs` - `boot_strap()` method
### Steps
1. **Create VM**: `ClassLoader::with_bimage()` - initializes with boot image
2. **Load Core Classes**: Preloads essential VM classes
3. **Create Primitive Classes**: Synthetic class objects for primitive types
4. **Initialize Classes**: Run static initializers (`<clinit>`)
### Core Classes Loaded
```rust
let classes = vec![
"java/lang/String",
"java/lang/System",
"java/lang/Class",
"java/lang/Object",
"java/lang/Thread",
"java/lang/ThreadGroup",
"java/lang/Module",
"java/lang/reflect/Method",
// ...
];
```
## RuntimeClass
Runtime representation of a loaded class:
### Cached Data
- **Superclass Chain**: `super_classes: Vec<Arc<RuntimeClass>>`
- **Interface Hierarchy**: `super_interfaces: Vec<Arc<RuntimeClass>>`
- **Component Type**: For array classes, reference to element type
- **Initialization State**: Thread-safe `InitState` enum
### Initialization States
```rust
pub enum InitState {
NotInitialized,
Initializing(ThreadId), // Track which thread is initializing
Initialized,
}
```
### Method/Field Resolution
- `find_method()` - Searches class then walks up superclass chain
- `find_field()` - Same recursive behavior for fields
- `is_assignable_into()` - Checks type compatibility with array covariance
## Multi-Loader Support
Classes are keyed by `(class_name, LoaderId)` tuple to support:
- Different class loaders loading same-named classes
- Isolation between class loader namespaces
- Proper class identity checks