Skip to content

Commit eaf45f4

Browse files
committed
feat: add jimage support
1 parent 3c545bc commit eaf45f4

File tree

20 files changed

+1839
-70
lines changed

20 files changed

+1839
-70
lines changed

Cargo.lock

Lines changed: 105 additions & 61 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ default-members = [
55
"ristretto_cli",
66
"ristretto_gc",
77
"ristretto_jit",
8+
"ristretto_jimage",
89
"ristretto_macros",
910
"ristretto_vm",
1011
]
@@ -14,6 +15,7 @@ members = [
1415
"ristretto_classloader",
1516
"ristretto_cli",
1617
"ristretto_gc",
18+
"ristretto_jimage",
1719
"ristretto_jit",
1820
"ristretto_macros",
1921
"ristretto_vm",
@@ -32,12 +34,12 @@ version = "0.26.0"
3234

3335
[workspace.dependencies]
3436
anstyle = "1.0.11"
35-
anyhow = "1.0.99"
37+
anyhow = "1.0.100"
3638
async-recursion = "1.1.1"
3739
bitflags = "2.9.4"
3840
byteorder = "1.5.0"
3941
byte-unit = "5.1.6"
40-
clap = "4.5.47"
42+
clap = "4.5.48"
4143
console = "0.16.1"
4244
cranelift = "0.123.2"
4345
criterion = { version = "0.7.0", default-features = false }
@@ -47,7 +49,9 @@ filetime = "0.2.26"
4749
flate2 = "1.1.2"
4850
getrandom = "0.3.3"
4951
indoc = "2.0.6"
50-
indexmap = "2.11.1"
52+
indexmap = "2.11.4"
53+
memchr = "2.7.5"
54+
memmap2 = "0.9.8"
5155
os_info = "3.12.0"
5256
parking_lot = "0.12.4"
5357
phf = "0.13.1"
@@ -56,14 +60,14 @@ proc-macro2 = "1.0.101"
5660
quote = "1.0.40"
5761
rayon = "1.11.0"
5862
reqwest = { version = "0.12.23", default-features = false }
59-
serde = "1.0.224"
63+
serde = "1.0.226"
6064
serde_plain = "1.0.2"
6165
stacker = "0.1.21"
6266
syn = "2.0.106"
6367
sysinfo = "0.37.0"
6468
sys-locale = "0.3.2"
6569
tar = "0.4.44"
66-
tempfile = "3.22.0"
70+
tempfile = "3.23.0"
6771
test-log = "0.2.18"
6872
thiserror = "2.0.16"
6973
thread-priority = "3.0.0"
@@ -73,7 +77,7 @@ tracing-subscriber = "0.3.20"
7377
walkdir = "2.5.0"
7478
whoami = "1.6.0"
7579
zerocopy = "0.8.26"
76-
zip = { version = "5.1.1", default-features = false, features = ["deflate"] }
80+
zip = { version = "5.1.1", default-features = false }
7781

7882
[workspace.metadata.release]
7983
shared-version = true

ristretto_classfile/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ indoc = { workspace = true }
2424
reqwest = { workspace = true, features = ["rustls-tls-native-roots"] }
2525
tar = { workspace = true }
2626
tokio = { workspace = true }
27-
zip = { workspace = true }
27+
zip = { workspace = true, features = ["deflate"] }
2828

2929
[[bench]]
3030
harness = false

ristretto_classfile/src/attributes/instruction.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,7 @@ impl Instruction {
13701370
///
13711371
/// If an instruction cannot be serialized to bytes.
13721372
#[expect(clippy::too_many_lines)]
1373+
#[expect(clippy::match_same_arms)]
13731374
pub fn to_bytes(&self, bytes: &mut Cursor<Vec<u8>>) -> Result<()> {
13741375
bytes.write_u8(self.code())?;
13751376

ristretto_classfile/src/attributes/stack_frame.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ impl StackFrame {
423423
/// - If the number of locals or stack items exceeds `u16::MAX`.
424424
/// - If a stack frame fails to serialize.
425425
/// - If writing to the byte stream fails.
426+
#[expect(clippy::match_same_arms)]
426427
pub fn to_bytes(&self, bytes: &mut Vec<u8>) -> Result<()> {
427428
match self {
428429
StackFrame::SameFrame { frame_type } => {

ristretto_classfile/src/constant.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ impl Constant {
301301
/// Returns an error if:
302302
/// - A UTF-8 string is more than 65535 bytes long
303303
/// - Writing to the buffer fails
304+
#[expect(clippy::match_same_arms)]
304305
pub fn to_bytes(&self, bytes: &mut Vec<u8>) -> Result<()> {
305306
bytes.write_u8(self.tag())?;
306307

ristretto_classloader/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ tokio = { workspace = true }
2626
tracing = { workspace = true }
2727
walkdir = { workspace = true }
2828
zerocopy = { workspace = true }
29-
zip = { workspace = true }
29+
zip = { workspace = true, features = ["deflate"] }
3030

3131
[target.'cfg(target_family = "wasm")'.dependencies]
3232
tokio = { workspace = true }

ristretto_jimage/Cargo.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
authors.workspace = true
3+
categories.workspace = true
4+
description = "JVM JImage Reader/Writer"
5+
edition.workspace = true
6+
keywords = ["java", "jimage"]
7+
license.workspace = true
8+
name = "ristretto_jimage"
9+
repository.workspace = true
10+
rust-version.workspace = true
11+
version.workspace = true
12+
13+
[dependencies]
14+
byteorder = { workspace = true }
15+
memchr = { workspace = true }
16+
thiserror = { workspace = true }
17+
18+
[target.'cfg(not(target_family = "wasm"))'.dependencies]
19+
memmap2 = { workspace = true }
20+
21+
[dev-dependencies]
22+
criterion = { workspace = true }
23+
ristretto_classfile = { path = "../ristretto_classfile", version = "0.26.0" }
24+
ristretto_classloader = { path = "../ristretto_classloader", version = "0.26.0" }
25+
tempfile = { workspace = true }
26+
tokio = { workspace = true, features = ["rt-multi-thread"] }
27+
28+
[[bench]]
29+
harness = false
30+
name = "jimage"

ristretto_jimage/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Ristretto JImage
2+
3+
[![ci](https://github.com/theseus-rs/ristretto/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/theseus-rs/ristretto/actions/workflows/ci.yml)
4+
[![Documentation](https://docs.rs/ristretto_jimage/badge.svg)](https://docs.rs/ristretto_jimage)
5+
[![Code Coverage](https://codecov.io/gh/theseus-rs/ristretto/branch/main/graph/badge.svg)](https://codecov.io/gh/theseus-rs/ristretto)
6+
[![Benchmarks](https://img.shields.io/badge/%F0%9F%90%B0_bencher-enabled-6ec241)](https://bencher.dev/perf/theseus-rs-ristretto)
7+
[![Latest version](https://img.shields.io/crates/v/ristretto_jimage.svg)](https://crates.io/crates/ristretto_jimage)
8+
[![License](https://img.shields.io/crates/l/ristretto_jimage)](https://github.com/theseus-rs/ristretto#license)
9+
[![Semantic Versioning](https://img.shields.io/badge/%E2%9A%99%EF%B8%8F_SemVer-2.0.0-blue)](https://semver.org/spec/v2.0.0.html)
10+
11+
## Overview
12+
13+
Ristretto JImage reads Java Image (JImage) files, which are used in Java 9 and later to store Java class files and
14+
resources in a compact format. This crate provides functionality to parse JImage files, extract class files and
15+
resources, and access metadata about the contents of the JImage.
16+
17+
## License
18+
19+
Licensed under either of
20+
21+
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
22+
* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
23+
24+
## Contribution
25+
26+
Unless you explicitly state otherwise, any contribution intentionally submitted
27+
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
28+
additional terms or conditions.

ristretto_jimage/benches/jimage.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use criterion::{Criterion, criterion_group, criterion_main};
2+
use ristretto_classfile::ClassFile;
3+
use ristretto_classloader::runtime::default_class_loader;
4+
use ristretto_jimage::{Image, Result};
5+
use std::io::Cursor;
6+
use std::sync::Arc;
7+
use tokio::runtime::Runtime;
8+
9+
fn benchmarks(criterion: &mut Criterion) {
10+
bench_lifecycle(criterion).ok();
11+
}
12+
13+
fn bench_lifecycle(criterion: &mut Criterion) -> Result<()> {
14+
let runtime = Runtime::new()?;
15+
let image = runtime.block_on(async { get_image().await })?;
16+
let image = Arc::new(image);
17+
18+
criterion.bench_function("jimage_load_hash_map", |bencher| {
19+
bencher.iter(|| {
20+
runtime.block_on(async {
21+
let _ = load_class(&image, "/java.base/java/util/HashMap.class")
22+
.await
23+
.ok();
24+
});
25+
});
26+
});
27+
28+
Ok(())
29+
}
30+
31+
async fn get_image() -> Result<Image> {
32+
let (java_home, _java_version, _class_loader) =
33+
default_class_loader().await.expect("java home");
34+
let path = java_home.join("lib").join("modules");
35+
let image = Image::from_file(&path)?;
36+
Ok(image)
37+
}
38+
39+
async fn load_class(image: &Image, class_name: &str) -> Result<()> {
40+
let resource = image.get_resource(class_name)?;
41+
let mut bytes = Cursor::new(resource.data().to_vec());
42+
let _class_file = ClassFile::from_bytes(&mut bytes).expect("read classfile");
43+
Ok(())
44+
}
45+
46+
criterion_group!(
47+
name = benches;
48+
config = Criterion::default();
49+
targets = benchmarks
50+
);
51+
criterion_main!(benches);

0 commit comments

Comments
 (0)