Skip to content

Commit f1b0a62

Browse files
author
Brian Pfretzschner
committed
Printing to stdout works!
1 parent 7ecb5e1 commit f1b0a62

File tree

6 files changed

+60
-106
lines changed

6 files changed

+60
-106
lines changed

model/src/vm_instance_impl.rs

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,11 @@ use crate::prelude::*;
66

77
impl Debug for VmInstance {
88
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9-
let info = if self.class_path == "java/lang/String" {
10-
format!("\"{}\"", get_java_string_value(self))
11-
} else { self.class_path.to_owned() };
12-
write!(f, "VmInstance({})", info)
9+
let fields = self
10+
.fields
11+
.iter()
12+
.map(|(k, v)| format!("{}={:?}", k, v))
13+
.join(", ");
14+
write!(f, "VmInstance({}, {})", self.class_path, fields)
1315
}
1416
}
15-
16-
fn get_java_byte_array_string_value(value_array: &VmArray) -> String {
17-
let element_values: Vec<u16> = value_array
18-
.elements
19-
.iter()
20-
.tuples()
21-
.map(|(h, l)| match (h, l) {
22-
(VmPrimitive::Byte(ref hb), VmPrimitive::Byte(ref lb)) => (*hb as u16) << 8 | *lb as u16,
23-
p => panic!("Unexpected primitives: {:?}", p),
24-
})
25-
.collect();
26-
27-
String::from_utf16_lossy(element_values.as_slice())
28-
}
29-
30-
fn get_java_string_value(string_instance: &VmInstance) -> String {
31-
match string_instance.fields.get("value").unwrap() {
32-
&VmPrimitive::Arrayref(ref rc_value_array) => {
33-
get_java_byte_array_string_value(&*rc_value_array.borrow())
34-
}
35-
p => panic!("Unexpected primitive: {:?}", p),
36-
}
37-
}

rt/Cargo.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ loader = { path = "../loader" }
99
vm = { path = "../vm" }
1010

1111
log = "0.4.17"
12-
anyhow = "1.0"
1312
dirs = "5.0"
14-
tracing = "0.1.41"
1513

1614
[dev-dependencies]
1715
parser = { path = "../parser" }
1816

19-
rstest = "0.22.0"
20-
pretty_assertions = "1.4.0"
17+
rstest = "0.26"
18+
pretty_assertions = "1.4"
2119
env_logger = "0.11"
22-
ctor = "0.2.8"
20+
ctor = "0.5.0"

rt/src/native/java_io_fileoutputstream.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use log::error;
21
use model::prelude::*;
32
use vm::frame::VmFrameImpl;
43

rt/tests/java_nio_charset_charset.rs

Lines changed: 0 additions & 34 deletions
This file was deleted.

rt/tests/java_util_properties.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ fn set_and_get() {
1717
let vm = new_test_vm();
1818
let mut vm_thread: VmThread = VmThread::new(&vm, "test".to_string());
1919
let rc_properties_instance = new_instance(&mut vm_thread, "java/util/Properties");
20-
20+
21+
let expected = create_java_string(&mut vm_thread, "val".to_string());
22+
2123
{
2224
let key = create_java_string(&mut vm_thread, "k".to_string());
23-
let val = create_java_string(&mut vm_thread, "val".to_string());
24-
2525
let frame = vm_thread.frame_stack.last_mut().unwrap();
2626
frame.stack_push(VmPrimitive::Objectref(rc_properties_instance.clone()));
2727
frame.stack_push(VmPrimitive::Objectref(key));
28-
frame.stack_push(VmPrimitive::Objectref(val));
28+
frame.stack_push(VmPrimitive::Objectref(expected.clone()));
2929

3030
}
3131
vm_thread.invoke_method(
@@ -49,7 +49,7 @@ fn set_and_get() {
4949
true,
5050
);
5151

52-
assert_eq!(vm_thread.frame_stack.last().unwrap().stack.last().unwrap(), &VmPrimitive::Int(1));
52+
assert_eq!(vm_thread.frame_stack.last().unwrap().stack.last().unwrap(), &VmPrimitive::Objectref(expected));
5353
}
5454

5555
fn new_test_vm() -> Vm {

vm/src/utils.rs

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pub fn read_i32_code(code: &Vec<u8>, pc: u16, offset: u16) -> i32 {
7777
((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4) as i32
7878
}
7979

80-
pub fn get_java_bytes_as_string_value(bytes: &[VmPrimitive]) -> String {
80+
fn get_java_bytes_utf16_string_value(bytes: &[VmPrimitive]) -> String {
8181
let element_values: Vec<u16> = bytes.iter()
8282
.tuples()
8383
.map(|(h, l)| match (h, l) {
@@ -88,64 +88,76 @@ pub fn get_java_bytes_as_string_value(bytes: &[VmPrimitive]) -> String {
8888

8989
String::from_utf16_lossy(element_values.as_slice())
9090
}
91+
fn get_java_bytes_latin1_string_value(bytes: &[VmPrimitive]) -> String {
92+
let element_values: Vec<u8> = bytes.iter()
93+
.map(|h| match h {
94+
VmPrimitive::Byte(ref b) => *b,
95+
p => panic!("Unexpected primitives: {:?}", p),
96+
})
97+
.collect();
98+
99+
String::from_utf8(element_values)
100+
.expect("Failed to convert Latin-1 bytes to String")
101+
}
91102

92103
pub fn get_java_string_value(string_instance: &VmInstance) -> String {
104+
let is_latin1 = if let &VmPrimitive::Byte(ref coder_value) = string_instance.fields.get("coder").unwrap() {
105+
*coder_value == 0
106+
} else {
107+
panic!("Unexpected coder field type in string instance: {:?}", string_instance.fields.get("coder"));
108+
};
109+
93110
match string_instance.fields.get("value").unwrap() {
94111
&VmPrimitive::Arrayref(ref rc_value_array) => {
95-
get_java_bytes_as_string_value(&*rc_value_array.borrow().elements)
112+
if is_latin1 {
113+
get_java_bytes_latin1_string_value(&*rc_value_array.borrow().elements)
114+
} else {
115+
// Handle UTF-16 case
116+
get_java_bytes_utf16_string_value(&*rc_value_array.borrow().elements)
117+
}
96118
}
97119
p => panic!("Unexpected primitive: {:?}", p),
98120
}
99121
}
100122

101123
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-
107-
trace!("Creating Java String: {}", string);
108-
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();
124+
let (bytes, is_latin1) = if string.is_ascii() {
125+
// Encoding as individual bytes is sufficient for ASCII strings
126+
(string.into_bytes(), true)
127+
} else {
128+
// Non-ASCII strings need to be encoded as UTF-16
129+
let utf16_iter = string.encode_utf16();
130+
let mut bytes: Vec<u8> = Vec::with_capacity(utf16_iter.clone().count() * 2);
131+
for code_unit in utf16_iter {
132+
bytes.push((code_unit & 0xFF) as u8); // low byte
133+
bytes.push((code_unit >> 8) as u8); // high byte
134+
}
135+
136+
(bytes, false)
137+
};
117138

118139
// Allocate a byte[] array (atype 8 = byte) with the exact length
119-
let mut array = VmArray::new_primitive(count, 8);
140+
let mut array = VmArray::new_primitive(bytes.len(), 8);
120141
for (i, b) in bytes.iter().enumerate() {
121142
array.elements[i] = VmPrimitive::Byte(*b);
122143
}
123144
let rc_array = Rc::new(RefCell::new(array));
124-
125-
// Load java/lang/String class (triggers <clinit> if not already done)
126-
let jvm_class = vm_thread.load_and_clinit_class(&"java/lang/String".to_string());
127-
145+
128146
// Create a new instance of java/lang/String
147+
let jvm_class = vm_thread.load_and_clinit_class(&"java/lang/String".to_string());
129148
let mut instance = VmInstance::new(vm_thread, &jvm_class);
130149

131150
// Set the `value` field to the byte[] we just created
132151
instance
133152
.fields
134-
.insert("value".to_string(), VmPrimitive::Arrayref(rc_array));
153+
.insert("value".to_string(), VmPrimitive::Arrayref(rc_array.clone()));
135154

136-
// Set the `coder` field to 1 (UTF‑16). Use 0 for UTF‑8.
155+
// Set the `coder` field to 1 for UTF‑16 or 0 for ASCII/Latin-1
156+
let coder_value = if is_latin1 { 0 } else { 1 };
137157
instance
138158
.fields
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-
}
148-
159+
.insert("coder".to_string(), VmPrimitive::Byte(coder_value));
160+
149161
Rc::new(RefCell::new(instance))
150162
}
151163

0 commit comments

Comments
 (0)