Skip to content

Commit f545507

Browse files
committed
Express array subtyping with JavaType instead of string slicing
is_assignable now parses names into JavaType and recurses structurally (Array/Class) rather than manipulating descriptor strings, which makes the array-covariance and Object/Cloneable/Serializable rules clearer. Behavior is unchanged.
1 parent d6a2624 commit f545507

1 file changed

Lines changed: 23 additions & 25 deletions

File tree

jvm/src/jvm.rs

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -589,36 +589,38 @@ impl Jvm {
589589
self.is_assignable(&instance.class_definition().name(), class_name)
590590
}
591591

592-
// aastore: whether value may be stored into array (JVMS 6.5 aastore); array component is always a reference type
592+
// aastore: whether value may be stored into array (JVMS 6.5 aastore)
593593
pub fn array_store_allowed(&self, array: &dyn ClassInstance, value: &dyn ClassInstance) -> bool {
594-
let component = component_name(&array.class_definition().name()[1..]);
594+
let JavaType::Array(component) = JavaType::parse(&array.class_definition().name()) else {
595+
return true;
596+
};
595597

596-
self.is_assignable(&value.class_definition().name(), &component)
598+
self.is_type_assignable(&name_to_type(&value.class_definition().name()), &component)
597599
}
598600

599601
// JVMS 4.10.3 subtyping, including array covariance. Names are in internal form: classes as
600602
// binary names (java/lang/String), arrays as descriptors ([Ljava/lang/String;, [I).
601603
pub fn is_assignable(&self, source: &str, target: &str) -> bool {
604+
self.is_type_assignable(&name_to_type(source), &name_to_type(target))
605+
}
606+
607+
fn is_type_assignable(&self, source: &JavaType, target: &JavaType) -> bool {
602608
if source == target {
603609
return true;
604610
}
605611

606-
// a primitive array component only matches its exact type
607-
if is_primitive_descriptor(source) {
608-
return false;
609-
}
610-
611-
if let Some(source_component) = source.strip_prefix('[') {
612-
if target == "java/lang/Object" || target == "java/lang/Cloneable" || target == "java/io/Serializable" {
613-
return true;
612+
match (source, target) {
613+
(JavaType::Array(source_component), JavaType::Array(target_component)) => self.is_type_assignable(source_component, target_component),
614+
// every array type is a subtype of Object, Cloneable, and java.io.Serializable (JLS 4.10.3)
615+
(JavaType::Array(_), JavaType::Class(name)) => {
616+
name == "java/lang/Object" || name == "java/lang/Cloneable" || name == "java/io/Serializable"
614617
}
615-
616-
return match target.strip_prefix('[') {
617-
Some(target_component) => self.is_assignable(&component_name(source_component), &component_name(target_component)),
618-
None => false,
619-
};
618+
(JavaType::Class(source), JavaType::Class(target)) => self.is_class_assignable(source, target),
619+
_ => false,
620620
}
621+
}
621622

623+
fn is_class_assignable(&self, source: &str, target: &str) -> bool {
622624
match self.get_class(source) {
623625
Some(class) => self.is_inherited_from(&*class.definition, target),
624626
None => false,
@@ -949,15 +951,11 @@ impl Jvm {
949951
}
950952
}
951953

952-
fn is_primitive_descriptor(name: &str) -> bool {
953-
matches!(name, "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z")
954-
}
955-
956-
// converts an array component descriptor to internal name form: Lfoo/Bar; -> foo/Bar, [X and primitives unchanged
957-
fn component_name(descriptor: &str) -> String {
958-
if let Some(class) = descriptor.strip_prefix('L').and_then(|x| x.strip_suffix(';')) {
959-
class.to_owned()
954+
// a class binary name (java/lang/String) or an array descriptor ([Ljava/lang/String;, [I) as a JavaType
955+
fn name_to_type(name: &str) -> JavaType {
956+
if name.starts_with('[') {
957+
JavaType::parse(name)
960958
} else {
961-
descriptor.to_owned()
959+
JavaType::Class(name.to_owned())
962960
}
963961
}

0 commit comments

Comments
 (0)