Skip to content

Commit f7a7609

Browse files
authored
Fix instance field resolution and storage across inheritance (#207)
* Fix instance field resolution and storage across inheritance * Require explicit class names in instance field APIs
1 parent 21f54ff commit f7a7609

191 files changed

Lines changed: 5405 additions & 3114 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎jvm-bytecode/src/class_definition.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ impl ClassDefinitionImpl {
8383
.map(|x| MethodImpl::from_method_proto(x, context.clone()))
8484
.collect::<Vec<_>>();
8585

86-
let fields = proto.fields.into_iter().map(FieldImpl::from_field_proto).collect::<Vec<_>>();
86+
let fields = proto
87+
.fields
88+
.into_iter()
89+
.map(|field| FieldImpl::from_field_proto(proto.name, field))
90+
.collect::<Vec<_>>();
8791

8892
let interfaces = proto.interfaces.into_iter().map(|x| x.to_string()).collect();
8993

@@ -111,7 +115,7 @@ impl ClassDefinitionImpl {
111115
_ => None,
112116
});
113117

114-
let field = FieldImpl::from_field_info(field_info);
118+
let field = FieldImpl::from_field_info(&class.this_class, field_info);
115119
if let Some(x) = constant
116120
&& field.access_flags().contains(FieldAccessFlags::STATIC)
117121
{

‎jvm-bytecode/src/field.rs‎

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use jvm_types::FieldAccessFlags;
1111

1212
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
1313
struct FieldInner {
14+
declaring_class: String,
1415
name: String,
1516
descriptor: String,
1617
access_flags: FieldAccessFlags,
@@ -22,28 +23,23 @@ pub struct FieldImpl {
2223
}
2324

2425
impl FieldImpl {
25-
pub fn new(name: &str, descriptor: &str, access_flags: FieldAccessFlags) -> Self {
26+
pub fn new(declaring_class: &str, name: &str, descriptor: &str, access_flags: FieldAccessFlags) -> Self {
2627
Self {
2728
inner: Arc::new(FieldInner {
29+
declaring_class: declaring_class.to_string(),
2830
name: name.to_string(),
2931
descriptor: descriptor.to_string(),
3032
access_flags,
3133
}),
3234
}
3335
}
3436

35-
pub fn from_field_proto(proto: JavaFieldProto) -> Self {
36-
Self::new(&proto.name, &proto.descriptor, proto.access_flags)
37+
pub fn from_field_proto(declaring_class: &str, proto: JavaFieldProto) -> Self {
38+
Self::new(declaring_class, &proto.name, &proto.descriptor, proto.access_flags)
3739
}
3840

39-
pub fn from_field_info(field_info: FieldInfo) -> Self {
40-
Self {
41-
inner: Arc::new(FieldInner {
42-
name: field_info.name.to_string(),
43-
descriptor: field_info.descriptor.to_string(),
44-
access_flags: field_info.access_flags,
45-
}),
46-
}
41+
pub fn from_field_info(declaring_class: &str, field_info: FieldInfo) -> Self {
42+
Self::new(declaring_class, &field_info.name, &field_info.descriptor, field_info.access_flags)
4743
}
4844
}
4945

‎jvm-bytecode/src/interpreter.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ impl Interpreter {
445445
return Err(jvm.exception("java/lang/NullPointerException", "null").await);
446446
}
447447

448-
let value = jvm.get_field(&instance.unwrap(), &x.name, &x.descriptor).await?;
448+
let value = jvm.get_field(&instance.unwrap(), &x.class, &x.name, &x.descriptor).await?;
449449

450450
stack_frame.operand_stack.push(Self::to_stack_frame_type(value));
451451
}
@@ -921,7 +921,7 @@ impl Interpreter {
921921

922922
let value = Self::to_field_type(&x.descriptor, value);
923923

924-
jvm.put_field(instance.as_mut().unwrap(), &x.name, &x.descriptor, value).await?;
924+
jvm.put_field(instance.as_mut().unwrap(), &x.class, &x.name, &x.descriptor, value).await?;
925925
}
926926
Opcode::Putstatic(x) => {
927927
let x = x.as_field_ref();

‎jvm/src/jvm.rs‎

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -261,15 +261,19 @@ impl Jvm {
261261
}
262262
}
263263

264-
pub async fn get_field<T>(&self, instance: &Box<dyn ClassInstance>, name: &str, descriptor: &str) -> Result<T>
264+
pub async fn get_field<T>(&self, instance: &Box<dyn ClassInstance>, class_name: &str, name: &str, descriptor: &str) -> Result<T>
265265
where
266266
T: From<JavaValue>,
267267
{
268-
tracing::trace!("Get field {}.{name}:{descriptor}", instance.class_definition().name());
268+
tracing::trace!("Get field {class_name}.{name}:{descriptor}");
269269

270-
let field = self.find_field(&*instance.class_definition(), name, descriptor)?;
271-
272-
if let Some(field) = field {
270+
let class = self.resolve_class(class_name).await?;
271+
if let Some((_, field)) = self.resolve_field(&class, name, descriptor) {
272+
if field.access_flags().contains(FieldAccessFlags::STATIC) {
273+
return Err(self
274+
.exception("java/lang/IncompatibleClassChangeError", &format!("{class_name}.{name}:{descriptor}"))
275+
.await);
276+
}
273277
let value = instance.get_field(&*field)?;
274278
if let JavaValue::Object(Some(instance)) = &value {
275279
let thread_id = (self.inner.get_current_thread_id)();
@@ -285,30 +289,28 @@ impl Jvm {
285289
Ok(value.into())
286290
} else {
287291
Err(self
288-
.exception(
289-
"java/lang/NoSuchFieldError",
290-
&format!("{}.{}:{}", instance.class_definition().name(), name, descriptor),
291-
)
292+
.exception("java/lang/NoSuchFieldError", &format!("{class_name}.{name}:{descriptor}"))
292293
.await)
293294
}
294295
}
295296

296-
pub async fn put_field<T>(&self, instance: &mut Box<dyn ClassInstance>, name: &str, descriptor: &str, value: T) -> Result<()>
297+
pub async fn put_field<T>(&self, instance: &mut Box<dyn ClassInstance>, class_name: &str, name: &str, descriptor: &str, value: T) -> Result<()>
297298
where
298299
T: Into<JavaValue> + Debug,
299300
{
300-
tracing::trace!("Put field {}.{name}:{descriptor} = {value:?}", instance.class_definition().name());
301+
tracing::trace!("Put field {class_name}.{name}:{descriptor} = {value:?}");
301302

302-
let field = self.find_field(&*instance.class_definition(), name, descriptor)?;
303-
304-
if let Some(field) = field {
303+
let class = self.resolve_class(class_name).await?;
304+
if let Some((_, field)) = self.resolve_field(&class, name, descriptor) {
305+
if field.access_flags().contains(FieldAccessFlags::STATIC) {
306+
return Err(self
307+
.exception("java/lang/IncompatibleClassChangeError", &format!("{class_name}.{name}:{descriptor}"))
308+
.await);
309+
}
305310
instance.put_field(&*field, value.into())
306311
} else {
307312
Err(self
308-
.exception(
309-
"java/lang/NoSuchFieldError",
310-
&format!("{}.{}:{}", instance.class_definition().name(), name, descriptor),
311-
)
313+
.exception("java/lang/NoSuchFieldError", &format!("{class_name}.{name}:{descriptor}"))
312314
.await)
313315
}
314316
}
@@ -1099,7 +1101,7 @@ impl Jvm {
10991101
.get_mut(&thread_id)
11001102
.unwrap()
11011103
.set_java_thread(java_thread.clone());
1102-
let interrupted: bool = self.get_field(&java_thread, "interrupted", "Z").await?;
1104+
let interrupted: bool = self.get_field(&java_thread, "java/lang/Thread", "interrupted", "Z").await?;
11031105
self.inner.threads.write().get_mut(&thread_id).unwrap().interrupted |= interrupted;
11041106

11051107
Ok(())

‎jvm/src/runtime/java_lang_class.rs‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ impl JavaLangClass {
1212
let mut name_bytes = jvm.instantiate_array("B", name.len()).await?;
1313
let bytes: Vec<i8> = cast_vec(name.as_bytes().to_vec());
1414
jvm.store_array(&mut name_bytes, 0, bytes).await?;
15-
jvm.put_field(&mut java_class, "nameBytes", "[B", name_bytes).await?;
15+
jvm.put_field(&mut java_class, "java/lang/Class", "nameBytes", "[B", name_bytes).await?;
1616

1717
Ok(java_class)
1818
}
1919

2020
#[allow(clippy::borrowed_box)]
2121
pub async fn name(jvm: &Jvm, this: &Box<dyn ClassInstance>) -> Result<String> {
22-
let name_bytes: ClassInstanceRef<Array<i8>> = jvm.get_field(this, "nameBytes", "[B").await?;
22+
let name_bytes: ClassInstanceRef<Array<i8>> = jvm.get_field(this, "java/lang/Class", "nameBytes", "[B").await?;
2323
let len = jvm.array_length(&name_bytes).await?;
2424
let name_bytes_vec: Vec<i8> = jvm.load_array(&name_bytes, 0, len).await?;
2525
match String::from_utf8(cast_vec(name_bytes_vec)) {
@@ -49,16 +49,16 @@ impl JavaLangClass {
4949
let mut name_bytes = jvm.instantiate_array("B", class_name.len()).await?;
5050
let bytes: Vec<i8> = cast_vec(class_name.into_owned().into_bytes());
5151
jvm.store_array(&mut name_bytes, 0, bytes).await?;
52-
jvm.put_field(&mut java_class, "nameBytes", "[B", name_bytes).await?;
52+
jvm.put_field(&mut java_class, "java/lang/Class", "nameBytes", "[B", name_bytes).await?;
5353

54-
jvm.put_field(&mut java_class, "classLoader", "Ljava/lang/ClassLoader;", class_loader)
54+
jvm.put_field(&mut java_class, "java/lang/Class", "classLoader", "Ljava/lang/ClassLoader;", class_loader)
5555
.await?;
5656

5757
Ok(java_class)
5858
}
5959

6060
#[allow(clippy::borrowed_box)]
6161
pub async fn class_loader(jvm: &Jvm, this: &Box<dyn ClassInstance>) -> Result<Option<Box<dyn ClassInstance>>> {
62-
jvm.get_field(this, "classLoader", "Ljava/lang/ClassLoader;").await
62+
jvm.get_field(this, "java/lang/Class", "classLoader", "Ljava/lang/ClassLoader;").await
6363
}
6464
}

‎jvm/src/runtime/java_lang_string.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ pub struct JavaLangString;
77
impl JavaLangString {
88
#[allow(clippy::borrowed_box)]
99
pub async fn to_utf16(jvm: &Jvm, this: &Box<dyn ClassInstance>) -> Result<Vec<JavaChar>> {
10-
let value = jvm.get_field(this, "value", "[C").await?;
11-
let offset: i32 = jvm.get_field(this, "offset", "I").await?;
12-
let count: i32 = jvm.get_field(this, "count", "I").await?;
10+
let value = jvm.get_field(this, "java/lang/String", "value", "[C").await?;
11+
let offset: i32 = jvm.get_field(this, "java/lang/String", "offset", "I").await?;
12+
let count: i32 = jvm.get_field(this, "java/lang/String", "count", "I").await?;
1313

1414
// access flags are not enforced, so bytecode can leave a negative here, which would widen into a huge usize
1515
if offset < 0 || count < 0 {

‎jvm/tests/test_garbage_collection.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,11 @@ async fn field_and_method_results_are_local_references() -> JvmResult<()> {
230230
assert_eq!(jvm.collect_garbage()?, 0);
231231

232232
jvm.push_native_frame();
233-
let _: ClassInstanceRef<Array<Object>> = jvm.get_field(&vector, "elementData", "[Ljava/lang/Object;").await?;
233+
let _: ClassInstanceRef<Array<Object>> = jvm.get_field(&vector, "java/util/Vector", "elementData", "[Ljava/lang/Object;").await?;
234234
let mut mutable_vector = (*vector).clone();
235235
jvm.put_field(
236236
&mut mutable_vector,
237+
"java/util/Vector",
237238
"elementData",
238239
"[Ljava/lang/Object;",
239240
ClassInstanceRef::<Array<Object>>::new(None),

0 commit comments

Comments
 (0)