Skip to content
This repository was archived by the owner on Sep 5, 2025. It is now read-only.

Commit f8a578c

Browse files
author
Brian Pfretzschner
committed
WIP String encoding still broken
1 parent 2f24e8e commit f8a578c

File tree

4 files changed

+166
-61
lines changed

4 files changed

+166
-61
lines changed

out.txt

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
Compiling vm v0.1.0 (/home/brian/Code/rust-jvm/vm)
2+
Compiling rt v0.1.0 (/home/brian/Code/rust-jvm/rt)
3+
warning: unused variable: `fd`
4+
--> rt/src/native/java_io_fileoutputstream.rs:27:9
5+
|
6+
27 | let fd = {
7+
| ^^ help: if this is intentional, prefix it with an underscore: `_fd`
8+
|
9+
= note: `#[warn(unused_variables)]` on by default
10+
11+
warning: `rt` (lib) generated 1 warning
12+
Compiling cli v0.1.0 (/home/brian/Code/rust-jvm/cli)
13+
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.65s
14+
Running `target/debug/cli execute HelloWorld /home/brian/Downloads/helloworld.jar rt/jmods/java.base/classes`
15+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/java/lang/System.class...
16+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] Class java/lang/System not initialized and contains <clinit> -> executing now
17+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing java/lang/System.<clinit>()V in thread vm-init now...
18+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.<clinit>()V#0 = 184
19+
[2024-09-10T18:34:33Z DEBUG vm::eval::invokestatic] invokestatic: java/lang/System.registerNatives()V
20+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.<clinit>()V#3 = 1
21+
[2024-09-10T18:34:33Z TRACE vm::eval::aconst_null] aconst_null: Pushing Null to stack
22+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.<clinit>()V#4 = 179
23+
[2024-09-10T18:34:33Z TRACE vm::eval::putstatic] putstatic: Popped value from stack and store it in java/lang/System.in
24+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.<clinit>()V#7 = 1
25+
[2024-09-10T18:34:33Z TRACE vm::eval::aconst_null] aconst_null: Pushing Null to stack
26+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.<clinit>()V#8 = 179
27+
[2024-09-10T18:34:33Z TRACE vm::eval::putstatic] putstatic: Popped value from stack and store it in java/lang/System.out
28+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.<clinit>()V#11 = 1
29+
[2024-09-10T18:34:33Z TRACE vm::eval::aconst_null] aconst_null: Pushing Null to stack
30+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.<clinit>()V#12 = 179
31+
[2024-09-10T18:34:33Z TRACE vm::eval::putstatic] putstatic: Popped value from stack and store it in java/lang/System.err
32+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.<clinit>()V#15 = 177
33+
[2024-09-10T18:34:33Z TRACE vm::eval::return_] return
34+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] java/lang/System.<clinit> done
35+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing java/lang/System.initPhase1()V in thread vm-init now...
36+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/System.initPhase1()V#0 = 187
37+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/java/util/Properties.class...
38+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] Class java/util/Properties not initialized and contains <clinit> -> executing now
39+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing java/util/Properties.<clinit>()V in thread vm-init now...
40+
[2024-09-10T18:34:33Z TRACE vm::eval] java/util/Properties.<clinit>()V#0 = 184
41+
[2024-09-10T18:34:33Z DEBUG vm::eval::invokestatic] invokestatic: jdk/internal/misc/Unsafe.getUnsafe()Ljdk/internal/misc/Unsafe;
42+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/jdk/internal/misc/Unsafe.class...
43+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] Class jdk/internal/misc/Unsafe not initialized and contains <clinit> -> executing now
44+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing jdk/internal/misc/Unsafe.<clinit>()V in thread vm-init now...
45+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<clinit>()V#0 = 184
46+
[2024-09-10T18:34:33Z DEBUG vm::eval::invokestatic] invokestatic: jdk/internal/misc/Unsafe.registerNatives()V
47+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<clinit>()V#3 = 187
48+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/java/lang/Object.class...
49+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] Class java/lang/Object not initialized and contains <clinit> -> executing now
50+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing java/lang/Object.<clinit>()V in thread vm-init now...
51+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Object.<clinit>()V#0 = 184
52+
[2024-09-10T18:34:33Z DEBUG vm::eval::invokestatic] invokestatic: java/lang/Object.registerNatives()V
53+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Object.<clinit>()V#3 = 177
54+
[2024-09-10T18:34:33Z TRACE vm::eval::return_] return
55+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] java/lang/Object.<clinit> done
56+
[2024-09-10T18:34:33Z TRACE vm::eval::new] new: jdk/internal/misc/Unsafe -> Pushing reference to stack
57+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<clinit>()V#6 = 89
58+
[2024-09-10T18:34:33Z TRACE vm::eval::dup] dup: Duplicating last stack element
59+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<clinit>()V#7 = 183
60+
[2024-09-10T18:34:33Z DEBUG vm::eval::invokespecial] invokespecial: jdk/internal/misc/Unsafe.<init>()V
61+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing jdk/internal/misc/Unsafe.<init>()V in thread vm-init now...
62+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<init>()V#0 = 42
63+
[2024-09-10T18:34:33Z TRACE vm::eval::aload_x] aload_0: Pushed Reference to stack
64+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<init>()V#1 = 183
65+
[2024-09-10T18:34:33Z DEBUG vm::eval::invokespecial] invokespecial: java/lang/Object.<init>()V
66+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing java/lang/Object.<init>()V in thread vm-init now...
67+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Object.<init>()V#0 = 177
68+
[2024-09-10T18:34:33Z TRACE vm::eval::return_] return
69+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<init>()V#4 = 177
70+
[2024-09-10T18:34:33Z TRACE vm::eval::return_] return
71+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<clinit>()V#10 = 179
72+
[2024-09-10T18:34:33Z TRACE vm::eval::putstatic] putstatic: Popped value from stack and store it in jdk/internal/misc/Unsafe.theUnsafe
73+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<clinit>()V#13 = 178
74+
[2024-09-10T18:34:33Z TRACE vm::eval::getstatic] getstatic: jdk/internal/misc/Unsafe.theUnsafeLjdk/internal/misc/Unsafe; -> push value to stack
75+
[2024-09-10T18:34:33Z TRACE vm::eval] jdk/internal/misc/Unsafe.<clinit>()V#16 = 19
76+
[2024-09-10T18:34:33Z TRACE vm::eval::ldc_x] ldc_w: Found Class [Z
77+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/java/lang/Class.class...
78+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] Class java/lang/Class not initialized and contains <clinit> -> executing now
79+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing java/lang/Class.<clinit>()V in thread vm-init now...
80+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Class.<clinit>()V#0 = 184
81+
[2024-09-10T18:34:33Z DEBUG vm::eval::invokestatic] invokestatic: java/lang/Class.registerNatives()V
82+
[2024-09-10T18:34:33Z TRACE rt::native::java_lang_class] Execute native java/lang/Class.registerNatives()V
83+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Class.<clinit>()V#3 = 3
84+
[2024-09-10T18:34:33Z TRACE vm::eval::iconst_x] iconst_0: Pushing constant Int 0 to stack
85+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Class.<clinit>()V#4 = 189
86+
[2024-09-10T18:34:33Z TRACE vm::eval::anewarray] anewarray: Create new VmArray of length 0 and push Arrayref to stack
87+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Class.<clinit>()V#7 = 179
88+
[2024-09-10T18:34:33Z TRACE vm::eval::putstatic] putstatic: Popped value from stack and store it in java/lang/Class.EMPTY_CLASS_ARRAY
89+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Class.<clinit>()V#10 = 3
90+
[2024-09-10T18:34:33Z TRACE vm::eval::iconst_x] iconst_0: Pushing constant Int 0 to stack
91+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Class.<clinit>()V#11 = 189
92+
[2024-09-10T18:34:33Z TRACE vm::eval::anewarray] anewarray: Create new VmArray of length 0 and push Arrayref to stack
93+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Class.<clinit>()V#14 = 179
94+
[2024-09-10T18:34:33Z TRACE vm::eval::putstatic] putstatic: Popped value from stack and store it in java/lang/Class.serialPersistentFields
95+
[2024-09-10T18:34:33Z TRACE vm::eval] java/lang/Class.<clinit>()V#17 = 177
96+
[2024-09-10T18:34:33Z TRACE vm::eval::return_] return
97+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] java/lang/Class.<clinit> done
98+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/java/nio/charset/StandardCharsets.class...
99+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] Class java/nio/charset/StandardCharsets not initialized and contains <clinit> -> executing now
100+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing java/nio/charset/StandardCharsets.<clinit>()V in thread vm-init now...
101+
[2024-09-10T18:34:33Z TRACE vm::eval] java/nio/charset/StandardCharsets.<clinit>()V#0 = 178
102+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/sun/nio/cs/US_ASCII.class...
103+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] Class sun/nio/cs/US_ASCII not initialized and contains <clinit> -> executing now
104+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing sun/nio/cs/US_ASCII.<clinit>()V in thread vm-init now...
105+
[2024-09-10T18:34:33Z TRACE vm::eval] sun/nio/cs/US_ASCII.<clinit>()V#0 = 187
106+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/java/nio/charset/Charset.class...
107+
[2024-09-10T18:34:33Z DEBUG vm::vm_thread] Class java/nio/charset/Charset not initialized and contains <clinit> -> executing now
108+
[2024-09-10T18:34:33Z TRACE vm::vm_thread] Executing java/nio/charset/Charset.<clinit>()V in thread vm-init now...
109+
[2024-09-10T18:34:33Z TRACE vm::eval] java/nio/charset/Charset.<clinit>()V#0 = 187
110+
[2024-09-10T18:34:33Z DEBUG loader::classfile_loader] Parsing classfile /home/brian/Code/rust-jvm/rt/jmods/java.base/classes/sun/nio/cs/StandardCharsets.class...

rt/src/native/java_io_fileoutputstream.rs

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use log::error;
12
use model::prelude::*;
23
use vm::frame::VmFrameImpl;
34

@@ -39,30 +40,24 @@ fn write_bytes(vm_thread: &mut VmThread) {
3940
}
4041
};
4142

42-
print!("write: ");
43-
byte_array.elements[offset..offset+length].iter().map(|a| match a {
44-
VmPrimitive::Byte(b) => *b,
45-
_ => todo!()
46-
}).for_each(|b| {
47-
print!("{} ", b);
48-
});
49-
println!();
43+
// Write raw bytes to the file descriptor without attempting string decoding
44+
use std::io::{self, Write};
5045

51-
// let v = &byte_array.elements[offset..offset+length].iter().map(|a| match a {
52-
// VmPrimitive::Byte(b) => *b,
53-
// _ => todo!()
54-
// }).collect::<Vec<_>>();
55-
// let t = String::from_utf8_lossy(v.as_ref());
56-
// print!("t: {:x}", t);
57-
58-
// for c in &byte_array.elements[offset..offset+length].iter().map(|a| match a {
59-
// VmPrimitive::Byte(b) => *b as char,
60-
// _ => todo!()
61-
// }).collect::<Vec<_>>() {
62-
// match fd {
63-
// 1 => print!("{}", c),
64-
// 2 => eprint!("{}", c),
65-
// _ => panic!("Unexpected fd = {}", fd),
66-
// };
67-
// }
68-
}
46+
let slice = &byte_array.elements[offset..offset + length];
47+
let mut buf: Vec<u8> = Vec::with_capacity(slice.len());
48+
for prim in slice {
49+
match prim {
50+
VmPrimitive::Byte(b) => buf.push(*b),
51+
_ => panic!("Unexpected primitive in byte array: {:?}", prim),
52+
}
53+
}
54+
55+
// Write raw bytes to stdout or stderr
56+
let mut handle: Box<dyn Write> = match fd {
57+
1 => Box::new(io::stdout()),
58+
2 => Box::new(io::stderr()),
59+
_ => panic!("Unexpected fd = {}", fd),
60+
};
61+
handle.write_all(&buf).expect("Failed to write to output");
62+
handle.flush().expect("Failed to flush output");
63+
}

vm/src/eval/ldc_x.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,8 @@ pub fn eval(
2121

2222
match jvm_class.constants.get(index).unwrap() {
2323
&ClassConstant::String(ref value) => {
24-
error!("{}: Pushing String \"{}\" to stack", instr_name, value);
24+
trace!("{}: Pushing String \"{}\" to stack", instr_name, value);
2525
let rc_instance = create_java_string(vm_thread, value.clone());
26-
27-
if value == "Hello world from HelloWorld.jar!" {
28-
error!("{:?}", rc_instance.borrow().fields["value"]);
29-
}
30-
3126
vm_thread
3227
.frame_stack
3328
.last_mut()

vm/src/utils.rs

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -99,49 +99,54 @@ pub fn get_java_string_value(string_instance: &VmInstance) -> String {
9999
}
100100

101101
pub fn create_java_string(vm_thread: &mut VmThread, string: String) -> Rc<RefCell<VmInstance>> {
102+
// Java 9+ uses compact strings: a byte[] `value` field and a `coder` field.
103+
// `coder == 0` → UTF‑8, `coder == 1` → UTF‑16 (big‑endian).
104+
// This implementation stores the string as UTF‑16 bytes (big‑endian) and sets coder = 1.
105+
// This matches the expectations of `get_java_string_value`, which decodes UTF‑16.
106+
102107
trace!("Creating Java String: {}", string);
103-
let count: usize = string.encode_utf16().count();
104-
let mut array = VmArray::new_primitive(count*2, 8);
105108

106-
for (i, c) in string.encode_utf16().enumerate() {
107-
array.elements[i*2] = VmPrimitive::Byte((c >> 8) as u8);
108-
array.elements[i*2+1] = VmPrimitive::Byte(c as u8);
109+
// Encode as UTF‑16BE bytes (big‑endian)
110+
let utf16_iter = string.encode_utf16();
111+
let mut bytes: Vec<u8> = Vec::with_capacity(utf16_iter.clone().count() * 2);
112+
for code_unit in utf16_iter {
113+
bytes.push((code_unit >> 8) as u8); // high byte
114+
bytes.push((code_unit & 0xFF) as u8); // low byte
115+
}
116+
let count = bytes.len();
117+
118+
// Allocate a byte[] array (atype 8 = byte) with the exact length
119+
let mut array = VmArray::new_primitive(count, 8);
120+
for (i, b) in bytes.iter().enumerate() {
121+
array.elements[i] = VmPrimitive::Byte(*b);
109122
}
110123
let rc_array = Rc::new(RefCell::new(array));
111-
112-
// array.elements.iter().map(|a| match a {
113-
// VmPrimitive::Byte(b) => *b,
114-
// _ => todo!()
115-
// }).for_each(|b| {
116-
// print!("{} ", b);
117-
// });
118-
// println!();
119124

125+
// Load java/lang/String class (triggers <clinit> if not already done)
120126
let jvm_class = vm_thread.load_and_clinit_class(&"java/lang/String".to_string());
127+
128+
// Create a new instance of java/lang/String
121129
let mut instance = VmInstance::new(vm_thread, &jvm_class);
130+
131+
// Set the `value` field to the byte[] we just created
122132
instance
123133
.fields
124134
.insert("value".to_string(), VmPrimitive::Arrayref(rc_array));
135+
136+
// Set the `coder` field to 1 (UTF‑16). Use 0 for UTF‑8.
125137
instance
126138
.fields
127-
.insert("coder".to_string(), VmPrimitive::Byte(1)); // coder = 1 which means UTF16 encoded string
139+
.insert("coder".to_string(), VmPrimitive::Byte(1));
140+
141+
// Some JVM implementations also have a cached `hash` field; initialise it to 0.
142+
// This is optional but mirrors the reference implementation.
143+
if instance.fields.contains_key("hash") {
144+
instance
145+
.fields
146+
.insert("hash".to_string(), VmPrimitive::Int(0));
147+
}
128148

129149
Rc::new(RefCell::new(instance))
130-
131-
// let rc_charset = find_static_field_value(vm_thread, &"java/nio/charset/StandardCharsets".to_string(), &"UTF_16".to_string());
132-
133-
// let jvm_class = vm_thread.load_and_clinit_class(&"java/lang/String".to_string());
134-
// let instance = VmInstance::new(vm_thread, &jvm_class);
135-
// let rc_instance = Rc::new(RefCell::new(instance));
136-
137-
// let frame = vm_thread.frame_stack.last_mut().unwrap();
138-
// frame.stack_push(rc_charset);
139-
// frame.stack_push(VmPrimitive::Arrayref(rc_array));
140-
// frame.stack_push(VmPrimitive::Objectref(rc_instance.clone()));
141-
142-
// vm_thread.invoke_method(&"java/lang/String".to_string(), &"<init>".to_string(), &"([BLjava/nio/charset/Charset;)V".to_string(), true);
143-
144-
// rc_instance
145150
}
146151

147152
pub fn find_static_field_value(

0 commit comments

Comments
 (0)