From d04e3dc7b8d567b0ac810dc5fbf676fc6a4ebb0f Mon Sep 17 00:00:00 2001 From: Inseok Lee Date: Mon, 26 Oct 2026 17:43:16 +0900 Subject: [PATCH 1/4] Fix superclass method lookup for invokespecial --- jvm/src/jvm.rs | 15 ++++++- test-data/Abstract$Base.class | Bin 281 -> 331 bytes test-data/Abstract$Derived.class | Bin 476 -> 476 bytes test-data/Abstract.class | Bin 426 -> 426 bytes test-data/SuperClass$InnerClass.class | Bin 566 -> 566 bytes test-data/SuperClass$InnerDerivedClass.class | Bin 426 -> 426 bytes test-data/SuperClass$InnerLeafClass.class | Bin 0 -> 649 bytes test-data/SuperClass.class | Bin 981 -> 1100 bytes test-data/SuperClass.txt | 2 + test-data/src/Abstract.java | 4 ++ test-data/src/SuperClass.java | 15 +++++++ tests/test_class.rs | 41 ++++++++++++++++++- 12 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 test-data/SuperClass$InnerLeafClass.class diff --git a/jvm/src/jvm.rs b/jvm/src/jvm.rs index fb154d2b..19616c2a 100644 --- a/jvm/src/jvm.rs +++ b/jvm/src/jvm.rs @@ -435,9 +435,14 @@ impl Jvm { tracing::trace!("Invoke special {class_name}.{name}:{descriptor}({args:?})"); let class = self.resolve_class(class_name).await?; - let method = class.definition.method(name, descriptor, false); + let method = if name == "" { + // Constructors are not inherited (JVMS 6.5 invokespecial). + class.definition.method(name, descriptor, false).map(|method| (class, method)) + } else { + self.resolve_method(&class, name, descriptor, false) + }; - if let Some(method) = method { + if let Some((class, method)) = method { let args = iter::once(JavaValue::Object(Some(clone_box(&**instance)))) .chain(args.into_vec()) .collect::>(); @@ -448,6 +453,12 @@ impl Jvm { .await); } + if method.access_flags().contains(MethodAccessFlags::ABSTRACT) { + return Err(self + .exception("java/lang/AbstractMethodError", &format!("{class_name}.{name}:{descriptor}")) + .await); + } + Ok(self .execute_method(&class, Some(instance.clone()), &method, args.into_boxed_slice()) .await? diff --git a/test-data/Abstract$Base.class b/test-data/Abstract$Base.class index a19098178c053fcc08ce69a1b505d9ab0a2e577f..7c65ea16f46042de8100d4baeef964db79ba0b28 100644 GIT binary patch delta 123 zcmbQqbef6l)W2Q(7#J9w7(^y=#j$93YKBcrHDnZ=I9-R4dEybRFczRBD+4zJBLgRp zB@3jPKq6Y(85lPL8H@~EK#~ne14*D-9w3{6fti69NHQ_-G4O+F0R|x;%>`7&&LF@b H%)$WxIMfaF delta 75 zcmX@jG?R(z)W2Q(7#J9w7=$Ns#ZAoDV-%P;Plu6d;%O~yW)=npZU#1>EHeWSkYr-u YW#9wT{0xFXnu~#vft`V$L5PI|0DdqE^Z)<= diff --git a/test-data/Abstract$Derived.class b/test-data/Abstract$Derived.class index 376e8843733371b8ce4c3465090a8ec65c425fb1..28034ff5583d1e33bce3bcbaf4ee01386e3af379 100644 GIT binary patch delta 23 ecmcb^e1~~M9U~+64Sm~et;h( z&Zg3W7)Z|9$;_E^&VGG=d;&PYMgb`}F47({$O_CiyAMhq1~QIUPs32@Bc=UE)lLwB z?4BR`W?!IKYYdpXo2@)@$h#IgVd*9c5Bl0SY_ztDbY znno+x+09`@ARQ=qQ$Pu$F3KLpFfK6m4_m-}u-YJGxmvGY&&d$JWixiBNe@$)W>uqN z`Z~4EmWx?|nL*pV0ULDvoi2SCwH0S;_@O%QcCM7Zlve?f(wQ=MQTsw_*-=L6SRilf zCba|#&8Vxd)v<56#eu<&?Oq^vPU=Y%8y72<+~2T(m+UyPS1daE&&o#wx z#e-o_Y(!|Bn@9`O5Haj4I?oP=+RS=16%iQly1m=@^!#IhG1aS=vTMJS033#)Mh;1-e8*4*&oF delta 304 zcmYL^y-q?w5QV?x2GE5>#i%zo{D?|W6K$^|UJ+Cps7y@6%EHdVK&*WTdSAebhMQ<& zVM%O#9l}`(DR$=DvomvcewY5zb~63^1}Q9;=_UGu}R&Pn zsklXeFWZ0}@>FGXOoy3keB;TRa2a6=8Nz?=5$PLVPxkU>|;6L b$kXCTXwp(niP99g66fX|axE@u?HiZ@WB?%A diff --git a/test-data/SuperClass.txt b/test-data/SuperClass.txt index bd767ccc..625d467b 100644 --- a/test-data/SuperClass.txt +++ b/test-data/SuperClass.txt @@ -3,3 +3,5 @@ 2 test test +leaf +test diff --git a/test-data/src/Abstract.java b/test-data/src/Abstract.java index 0b2a7905..6a8c5c96 100644 --- a/test-data/src/Abstract.java +++ b/test-data/src/Abstract.java @@ -1,6 +1,10 @@ class Abstract { static abstract class Base { + Base() {} + + Base(int value) {} + abstract void test(); } diff --git a/test-data/src/SuperClass.java b/test-data/src/SuperClass.java index 5c237bfe..fb060d23 100644 --- a/test-data/src/SuperClass.java +++ b/test-data/src/SuperClass.java @@ -13,6 +13,17 @@ class InnerDerivedClass extends InnerClass { String field4; } + class InnerLeafClass extends InnerDerivedClass { + @Override + void test() { + System.out.println("leaf"); + } + + void testSuper() { + super.test(); + } + } + void run() { InnerDerivedClass derived = null; @@ -34,6 +45,10 @@ void run() { System.out.println(derived.field4); derived.test(); + + InnerLeafClass leaf = new InnerLeafClass(); + leaf.test(); + leaf.testSuper(); } public static void main(String[] args) { diff --git a/tests/test_class.rs b/tests/test_class.rs index f039eae4..faa33803 100644 --- a/tests/test_class.rs +++ b/tests/test_class.rs @@ -2,7 +2,7 @@ mod test_helper; use std::{fs, path::Path}; -use jvm::Result; +use jvm::{JavaError, JavaValue, Result}; use test_helper::{run_class, run_jar}; @@ -47,3 +47,42 @@ async fn test_class() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn test_special_resolution_errors() -> Result<()> { + let jvm = test_utils::test_jvm().await?; + for name in ["Abstract$Base", "Abstract$Derived", "StaticInherit$Base", "StaticInherit$Sub"] { + let class = jvm_bytecode::ClassDefinitionImpl::from_classfile(&fs::read(format!("test-data/{name}.class")).unwrap()).unwrap(); + jvm.register_class(Box::new(class), None).await?; + } + let derived = jvm.new_class("Abstract$Derived", "()V", ()).await?; + let static_sub = jvm.new_class("StaticInherit$Sub", "()V", ()).await?; + + for (instance, owner, name, descriptor, args, expected) in [ + ( + &derived, + "Abstract$Derived", + "", + "(I)V", + vec![JavaValue::Int(1)], + "java/lang/NoSuchMethodError", + ), + (&derived, "Abstract$Base", "test", "()V", vec![], "java/lang/AbstractMethodError"), + ( + &static_sub, + "StaticInherit$Sub", + "method", + "()I", + vec![], + "java/lang/IncompatibleClassChangeError", + ), + ] { + let result: Result = jvm.invoke_special(instance, owner, name, descriptor, args).await; + match result { + Err(JavaError::JavaException(exception)) => assert_eq!(exception.class_definition().name(), expected), + other => panic!("Expected {expected} for {owner}.{name}{descriptor}, got {other:?}"), + } + } + + Ok(()) +} From c51a61a88db6272ce784977e81426509f8f5b9f8 Mon Sep 17 00:00:00 2001 From: Inseok Lee Date: Mon, 26 Oct 2026 18:32:52 +0900 Subject: [PATCH 2/4] Exercise special resolution errors through Java fixtures --- AGENTS.md | 1 + test-data/Abstract$Base.class | Bin 331 -> 281 bytes test-data/Abstract$Derived.class | Bin 476 -> 476 bytes test-data/Abstract.class | Bin 426 -> 426 bytes test-data/SpecialResolutionErrors.jar | Bin 0 -> 3651 bytes test-data/SpecialResolutionErrors.txt | 3 + test-data/src/Abstract.java | 4 -- .../special-resolution/RuntimeTargets.java | 14 +++++ .../SpecialResolutionErrors.java | 55 ++++++++++++++++++ tests/test_class.rs | 41 +------------ 10 files changed, 74 insertions(+), 44 deletions(-) create mode 100644 test-data/SpecialResolutionErrors.jar create mode 100644 test-data/SpecialResolutionErrors.txt create mode 100644 test-data/src/special-resolution/RuntimeTargets.java create mode 100644 test-data/src/special-resolution/SpecialResolutionErrors.java diff --git a/AGENTS.md b/AGENTS.md index 7185a1b0..a4cffaa7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,7 @@ ## Testing Boundaries - Keep `rustjava-runtime/tests/classes` limited to Java standard library class and API behavior. - Test JVM and interpreter semantics, including class initialization, bytecode execution, and monitor behavior, with compiled Java fixtures under `test-data/src` and expected output under `test-data`, executed by `tests/test_class.rs`. +- Add or extend fixtures consumed by the existing `test_class` runner rather than adding Rust tests that directly invoke JVM APIs. For linkage errors that Java source cannot express directly, compile the caller against one class version and replace the target class in the fixture; keep the source variants and reproducible build instructions alongside it. - Do not place JVM core behavior tests in the `rustjava-runtime` standard library test tree. ## Compatibility Sources diff --git a/test-data/Abstract$Base.class b/test-data/Abstract$Base.class index 7c65ea16f46042de8100d4baeef964db79ba0b28..a19098178c053fcc08ce69a1b505d9ab0a2e577f 100644 GIT binary patch delta 75 zcmX@jG?R(z)W2Q(7#J9w7=$Ns#ZAoDV-%P;Plu6d;%O~yW)=npZU#1>EHeWSkYr-u YW#9wT{0xFXnu~#vft`V$L5PI|0DdqE^Z)<= delta 123 zcmbQqbef6l)W2Q(7#J9w7(^y=#j$93YKBcrHDnZ=I9-R4dEybRFczRBD+4zJBLgRp zB@3jPKq6Y(85lPL8H@~EK#~ne14*D-9w3{6fti69NHQ_-G4O+F0R|x;%>`7&&LF@b H%)$WxIMfaF diff --git a/test-data/Abstract$Derived.class b/test-data/Abstract$Derived.class index 28034ff5583d1e33bce3bcbaf4ee01386e3af379..376e8843733371b8ce4c3465090a8ec65c425fb1 100644 GIT binary patch delta 23 ecmcb^e1~~M9U~+AHD~Co49Yj$? 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 Result<()> { Ok(()) } - -#[tokio::test] -async fn test_special_resolution_errors() -> Result<()> { - let jvm = test_utils::test_jvm().await?; - for name in ["Abstract$Base", "Abstract$Derived", "StaticInherit$Base", "StaticInherit$Sub"] { - let class = jvm_bytecode::ClassDefinitionImpl::from_classfile(&fs::read(format!("test-data/{name}.class")).unwrap()).unwrap(); - jvm.register_class(Box::new(class), None).await?; - } - let derived = jvm.new_class("Abstract$Derived", "()V", ()).await?; - let static_sub = jvm.new_class("StaticInherit$Sub", "()V", ()).await?; - - for (instance, owner, name, descriptor, args, expected) in [ - ( - &derived, - "Abstract$Derived", - "", - "(I)V", - vec![JavaValue::Int(1)], - "java/lang/NoSuchMethodError", - ), - (&derived, "Abstract$Base", "test", "()V", vec![], "java/lang/AbstractMethodError"), - ( - &static_sub, - "StaticInherit$Sub", - "method", - "()I", - vec![], - "java/lang/IncompatibleClassChangeError", - ), - ] { - let result: Result = jvm.invoke_special(instance, owner, name, descriptor, args).await; - match result { - Err(JavaError::JavaException(exception)) => assert_eq!(exception.class_definition().name(), expected), - other => panic!("Expected {expected} for {owner}.{name}{descriptor}, got {other:?}"), - } - } - - Ok(()) -} From 048f06c4f79f5a0e6b8fd84ec1b948b37b4cdd48 Mon Sep 17 00:00:00 2001 From: Inseok Lee Date: Mon, 26 Oct 2026 18:39:44 +0900 Subject: [PATCH 3/4] Use nested class fixtures for special resolution errors --- ...SpecialResolutionErrors$AbstractBase.class | Bin 0 -> 341 bytes ...pecialResolutionErrors$AbstractChild.class | Bin 0 -> 491 bytes ...ecialResolutionErrors$AbstractParent.class | Bin 0 -> 374 bytes ...cialResolutionErrors$ConstructorBase.class | Bin 0 -> 383 bytes ...ialResolutionErrors$ConstructorChild.class | Bin 0 -> 375 bytes .../SpecialResolutionErrors$StaticBase.class | Bin 0 -> 369 bytes .../SpecialResolutionErrors$StaticChild.class | Bin 0 -> 443 bytes ...SpecialResolutionErrors$StaticParent.class | Bin 0 -> 366 bytes test-data/SpecialResolutionErrors.class | Bin 0 -> 1484 bytes test-data/SpecialResolutionErrors.jar | Bin 3651 -> 0 bytes .../special-resolution/RuntimeTargets.java | 23 +++++--- .../SpecialResolutionErrors.java | 53 +++++++++--------- 12 files changed, 42 insertions(+), 34 deletions(-) create mode 100644 test-data/SpecialResolutionErrors$AbstractBase.class create mode 100644 test-data/SpecialResolutionErrors$AbstractChild.class create mode 100644 test-data/SpecialResolutionErrors$AbstractParent.class create mode 100644 test-data/SpecialResolutionErrors$ConstructorBase.class create mode 100644 test-data/SpecialResolutionErrors$ConstructorChild.class create mode 100644 test-data/SpecialResolutionErrors$StaticBase.class create mode 100644 test-data/SpecialResolutionErrors$StaticChild.class create mode 100644 test-data/SpecialResolutionErrors$StaticParent.class create mode 100644 test-data/SpecialResolutionErrors.class delete mode 100644 test-data/SpecialResolutionErrors.jar diff --git a/test-data/SpecialResolutionErrors$AbstractBase.class b/test-data/SpecialResolutionErrors$AbstractBase.class new file mode 100644 index 0000000000000000000000000000000000000000..9989b2127593ccab8aed36640a1f633482f84246 GIT binary patch literal 341 zcmZ`!O-sW-5PjRkB*tiCD|iw-^q?N>ACQ7AiUq0A7Vn#Ms9Vxq+1>cRJP98B0sbg) zwkI#n;k`E>%)I&j`TPPf#z~G2q7-p~JtP9t>Vt(>>A_G9TtAfA{NQ}{3<@SCTFH7qi-J zoC@R%)7qNuRp|BSt@dh5OKI2S-F5IIkj=>X2jhH-V}a9u?h6!Ct;tRr=^Qyu{~C(V zk@B-40`j{M*k^UdEJEe#1LBZQlT}W+BdmP<+Tdf)*R8JC-EW(aZfsbW1Nq$N@&d?6OwM0N!!uFIq3*X zvt!nNfk}_p!6J_uPLYi_211RqZH^R>!*|ddCoIK2l!0S5UJd7gy2&x1h4L%=Y2FFn l>wmmy$!L~(%KtA15v-swiA}6=Y%o$l6YGe%(FfgM^Z3P0Ust`r&PPl_nu#xTby_k>~cmN&> zaTCPAP+`gbe9pCx?ECrk{sCZu-3$@p1ic(<=o5yE7p}Cb@7UYA3EG-V=dAO?ap{9o zRhTHxg#Lv#I*bVM@ySDilyLOl#ardr1VV3WA0=Th(}w5GbII;rm9;q8!ZxnrE8U9y z6>(=zYN-h6oPD^qJ|q|r_EsDw?DeXgZ@5VelNXf|FF z@`Y_(Bd=BL4K}PjkC_PBT>5ZheMr$K9BnWYikUIeRa$s2J^%eD6poSd&GE&`SA($4 xY{*ElbM_8;W5V&cMj04H0Xxipt?S|ctV1Ly*xtb|_SRTppE2j*6eSLj{Qy-YQ1Ji& literal 0 HcmV?d00001 diff --git a/test-data/SpecialResolutionErrors$ConstructorChild.class b/test-data/SpecialResolutionErrors$ConstructorChild.class new file mode 100644 index 0000000000000000000000000000000000000000..46b288b6f57be53fda671da3b3f6fb5e202b4ee5 GIT binary patch literal 375 zcma)2%}T>S5dNmAO^h+E_TWkIAXq*41mdAVQIH3BmJ(;*1f^Rs literal 0 HcmV?d00001 diff --git a/test-data/SpecialResolutionErrors$StaticBase.class b/test-data/SpecialResolutionErrors$StaticBase.class new file mode 100644 index 0000000000000000000000000000000000000000..860ab64d1247d71dade0f07ac8932f207843abd5 GIT binary patch literal 369 zcmZ`!!AiqG5PjR!B-Uu66$C*L@t_{;4~T~r#e!65iuY|g)Gf)b>~8!jFP;Pset;h( z&i3Fza1ZkyZ|1$7`TG9&1Te&Y4;@4ax*0YQ3luB)A_uk9%fZ8ZMU@wbuas8)S|B<* ze@u`HoRrU0DOpd+nY#7L=o@Q|b*H75UR6WsNT5413x@n&X_~anoa{`_YgXch{F7O% z^Tqb2%bxv({lpzQzK3 Mj6HTFD9}gx121Gr@Bjb+ literal 0 HcmV?d00001 diff --git a/test-data/SpecialResolutionErrors$StaticChild.class b/test-data/SpecialResolutionErrors$StaticChild.class new file mode 100644 index 0000000000000000000000000000000000000000..89a3030930111aad6a06f939ecf32e3e9fe2a1c3 GIT binary patch literal 443 zcmaJ-!Ab)$5Pj3F-F3TK?W*;lAlQSg;1`Goi()~FSn-~21C68`Nm9ScizmT@AK*uc zll5ew24?2Xo5y1&U*8{}04}kgqJ}6&J;NFrgzorR7E0)Q>5N`@Wy&jSjdk6z7hV;2 z!phPU8t1B1{(=yl^d5snl3^Vw59ZR(%(NN|V`PNm{~ZT2rCDAdn5iVRZdEBqi}^&_ z2Qksi)C-|CAsw5AE#$Qd+`$UVvnTN)2+2q~e`B1Fu|w#r5EHUtSxP(5!a3=<{d;2b zx2^WW5t@8tcp>vofv~~okShhI{|tlxz*B+`X;kWA@=a94Jx!p^z; j1buYjOx89Gj1gl`7$L`g#{vh!BXJ@WI7BLC3(@Hx%hy*8 literal 0 HcmV?d00001 diff --git a/test-data/SpecialResolutionErrors.class b/test-data/SpecialResolutionErrors.class new file mode 100644 index 0000000000000000000000000000000000000000..3390226be375303cfec4692dcb87a87dbd3cce04 GIT binary patch literal 1484 zcmah}YflqF6g>mTl(OY5pn?x5uz-m8R#b!vT5SuVMnn9xT_&)&-6gwQ6MvDP^+QP{ z@dxmujAyoOS|inNGN&_h?zv~~-PyBWKYjw(z)lh!=#=O(Z~@%{14q_pYuUBDgXQhg zk+LI!?x&9DM9&2}&H48d2?4WstZc_}cU9=SmB{hE%^>iDFrD$eFbXPmLpy3xME-elLGxsPxT zrqP*18m57HEU=ok<+^c8vJy81W}E)Ap6!>98Cr5xe3r~1+o|I6O575dX_s)l#O_&k zR8P62Q!c@jxFayxOwgdUv`ylkz*rkyy3P3eEM$Mq@l>HwE~((1MR(jNTMnx;YJNPg zJOU}!!2XoCj_dqn(XRxy+H!R8M4NoKv{ixLf(oO&(%C`@zQ40=`02)hcC0{ok;Dsu z^#8aUR;VO4{=uxZuc7%;V5*&FjagIN5}0epYq?&UEMR0kPX%$472orL7DC)Z7NMqH zpQRcnt6D!Fk8jkd3#IT zTdv~v-+SLEtib4>B+BXf}45weGP?}o`4Wy~c; zjbRhx*d^mJqXyBPlQ2?wiA6oEq?qmrH#)G2r^F#7c$J=Ejdyq*8GaWx@rvskT$e1| hYhs$1jd^$RhWL#0x5PDKj<`#BWQGc|8CC literal 0 HcmV?d00001 diff --git a/test-data/SpecialResolutionErrors.jar b/test-data/SpecialResolutionErrors.jar deleted file mode 100644 index 2dee97c6d125e9d2dec9f0c01fa4428532df566e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3651 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 Date: Mon, 26 Oct 2026 18:47:05 +0900 Subject: [PATCH 4/4] Resolve maximally specific inherited interface methods --- jvm/src/jvm.rs | 33 +++++++++++++++--- test-data/SuperClass$BaseDefault.class | Bin 0 -> 272 bytes test-data/SuperClass$DefaultChild.class | Bin 0 -> 394 bytes test-data/SuperClass$DefaultParent.class | Bin 0 -> 460 bytes test-data/SuperClass$InnerClass.class | Bin 566 -> 566 bytes test-data/SuperClass$InnerDerivedClass.class | Bin 426 -> 426 bytes test-data/SuperClass$InnerLeafClass.class | Bin 649 -> 649 bytes test-data/SuperClass$PrivateDefault.class | Bin 0 -> 278 bytes .../SuperClass$ReverseDefaultChild.class | Bin 0 -> 422 bytes .../SuperClass$ReverseDefaultParent.class | Bin 0 -> 474 bytes test-data/SuperClass$SpecificDefault.class | Bin 0 -> 332 bytes test-data/SuperClass.class | Bin 1100 -> 1596 bytes test-data/SuperClass.txt | 2 ++ test-data/src/SuperClass.java | 27 ++++++++++++++ 14 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 test-data/SuperClass$BaseDefault.class create mode 100644 test-data/SuperClass$DefaultChild.class create mode 100644 test-data/SuperClass$DefaultParent.class create mode 100644 test-data/SuperClass$PrivateDefault.class create mode 100644 test-data/SuperClass$ReverseDefaultChild.class create mode 100644 test-data/SuperClass$ReverseDefaultParent.class create mode 100644 test-data/SuperClass$SpecificDefault.class diff --git a/jvm/src/jvm.rs b/jvm/src/jvm.rs index 19616c2a..b7bf4f30 100644 --- a/jvm/src/jvm.rs +++ b/jvm/src/jvm.rs @@ -1306,15 +1306,40 @@ impl Jvm { .and_then(|super_class| self.get_class(&super_class)); } + let mut visited = HashSet::new(); + let mut candidates: Vec<(Class, Box)> = Vec::new(); while let Some(interface_name) = interfaces.pop() { - let interface = self.get_class(&interface_name)?; - if let Some(method) = interface.definition.method(name, descriptor, false) { - return Some((interface, method)); + if !visited.insert(interface_name.clone()) { + continue; } + let interface = self.get_class(&interface_name)?; interfaces.extend(interface.definition.interface_names()); + if let Some(method) = interface.definition.method(name, descriptor, false) + && !method.access_flags().contains(MethodAccessFlags::PRIVATE) + { + if candidates + .iter() + .any(|(candidate, _)| self.is_inherited_from(&*candidate.definition, &interface_name)) + { + continue; + } + // Keep only declarations not overridden by a more specific interface. + candidates.retain(|(candidate, _)| !self.is_inherited_from(&*interface.definition, &candidate.definition.name())); + candidates.push((interface, method)); + } + } + + let mut concrete = candidates + .iter() + .enumerate() + .filter(|(_, (_, method))| !method.access_flags().contains(MethodAccessFlags::ABSTRACT)); + if let Some((index, _)) = concrete.next() + && concrete.next().is_none() + { + return Some(candidates.swap_remove(index)); } - None + candidates.into_iter().next() } pub(crate) fn find_field(&self, class: &dyn ClassDefinition, name: &str, descriptor: &str) -> Result>> { diff --git a/test-data/SuperClass$BaseDefault.class b/test-data/SuperClass$BaseDefault.class new file mode 100644 index 0000000000000000000000000000000000000000..d9ff20dd92c11ad1fbb88e339b11f0da6407a5ea GIT binary patch literal 272 zcmYk0O%B0O6ot>LYO6|(42%t!um?m)gaI3Pn)=6IFP|5T#TqP&k76R3)RE7j9z5ZOl z9cMc>@#2`$(wq~@H!@M&3D=p)w=_Lk6aOLx_Mx;_Y|^jt;z{t}2l!Fq zY$ytGU}xUEc{9BI`u_L?aDh>Z0AY+spo1=9G^<}!nH%Zc(WQEjwW)4pscc2)o@uMA zb3%B0a_`p?feoaD&PtkEHT)!oAPoN>$se^@5~93VvNF5TR!!^Yxhn7E+%OX@q%nka zR@CJ}U1`r{e`m%|awQ4LRJrQ9a8-<5FaGsP5EES6ot=iYZB9+RE@Q*2qG@jg?#~W(TXTYEkxW;<5Z@^Nl7MsFIR#KAHata zZ%VDnV&=@ucjs{K{r>s<0x-m(iwdeXY7K0`BDCjXDF%_yuY8HeNTfD4TtdG$A;r-^OU(d6+GFP+ix7!)RpFew25SMCMS delta 23 fcmdnSvW;cKRYpdp$=4VyS=kvl7&s=2Few25RW=2P diff --git a/test-data/SuperClass$InnerDerivedClass.class b/test-data/SuperClass$InnerDerivedClass.class index 5d7f9c9dedb2603a2d612f25eb3da5e6f0b03cbc..e970b9517423722edd08524f09c6c5d4fe08423e 100644 GIT binary patch delta 13 UcmZ3*yoz~42_vKOY~T(BA&@Fs2`=EkZNxZFWz2{}_MEaL9buSX44Vz|F8Hwq+;YSNX zvM$SeB{qcq|5^bc+42KN4;=);P}GZ%gkEsId6BHn1D<8R>m0@(jfW; D&C)vw literal 0 HcmV?d00001 diff --git a/test-data/SuperClass$ReverseDefaultChild.class b/test-data/SuperClass$ReverseDefaultChild.class new file mode 100644 index 0000000000000000000000000000000000000000..3121def047718388da32bb8c95794c14e133f1c9 GIT binary patch literal 422 zcmZ{g&q~8U5XQer(k8}ew5isMe-0khgHIqHjEDs(V#WKm9Wk;dl1=hdUOWjNd;lLx zoDHH_bYW+P-+nWEv+p0TZvalP5yFA%!;8>Cm#{sqAGu7mGG_OR8!io>@~x_Mb*W0u zD?;}u%d_g3;O_0;*zO?00zyKkQM%@q4}C<0t$$~wcbT3MytJ4}Lwu3tJgOfiTwbe* z7UE5n)`W0e)a8`VGRxwhX@~c!QG{T`raCW7<)d%K-_8lqFwei%U_>c!$ r(@nWE8gQj-L$HKRY~|>pC3plwL|yE_7Fid&rY+(w+JZH~j%oh@pO9~G literal 0 HcmV?d00001 diff --git a/test-data/SuperClass$SpecificDefault.class b/test-data/SuperClass$SpecificDefault.class new file mode 100644 index 0000000000000000000000000000000000000000..40174421224ba0a47fdc72dcad92af30597f2ed5 GIT binary patch literal 332 zcmY*U%WlFj5S$If36Ca}N*p-%&|brQ#=Il2;;am~xN_WsXI=-uQ|fJBk&%a2J;fKX~j*tp?<=O3E4u2dx& cq^E_Rpoc#08jLUy9Ew7SaIZp{7h$Nv3rHnM(EtDd literal 0 HcmV?d00001 diff --git a/test-data/SuperClass.class b/test-data/SuperClass.class index fa7e6c86fb737e2bbb8ed1390abacff03048dfac..70b539737c9be72c6ce14157011d237b359c326f 100644 GIT binary patch delta 830 zcmY+C+fEZv6o$XuB2%aJkcg-xwnRt}FT5~DP-BH2#G!z-RRj;{Sf)_h5YiTIyyyey zjp>u%MM=c?0LF*$35@?#p)(h=|F!ozeQVGD75kpBfByOQ1ISQ6uuY_Msl2!AHM0%3 z)tbzCRd=t^&TiKln>J~p{*pv^dFqzObFI+ zHjHOD7X%WIAs!%LyrBDv=(j;s-zkPMJdt%(YW$NMhnNEyhN4#A?c1<&68c-ILpu4~ zO-1XSuS^_~UO!~6$9#|J{ywk$LXXGV4{DVdQPyp>zeAi^&U2TG++&RUjPrnNJYaAqRv7{fr$^tKiU15lmzI{Fs0HyYWxUC0EHe)gYI^J z>=HI*@s|}A;Tg}RqdJ?xBH8biW=-8*v%x$0{CG>fUD-+Ld+AJ{1wCnp&0EBj=HN+J ZrB&&gbW7?(Zx6>*%%LXTkbW4B{RPq$xZ(f+ delta 330 zcmYL^OG`pQ7>1uY$I?a@5eOkET?oU>=mN);q^Gh>YY_ys3y~H|MAF)Q+XLb3+C+yeH`v*|tVrY%GXfK~0I*(!Rw%@l@@ZD2O5k}A6ee~j2 zTP(}*lmWi(46_&x#j--b>EId_#gVI)R>%J^cw39rm+nc^d{l} diff --git a/test-data/SuperClass.txt b/test-data/SuperClass.txt index 625d467b..e8b67ef4 100644 --- a/test-data/SuperClass.txt +++ b/test-data/SuperClass.txt @@ -5,3 +5,5 @@ test test leaf test +2 +2 diff --git a/test-data/src/SuperClass.java b/test-data/src/SuperClass.java index fb060d23..9d295a0d 100644 --- a/test-data/src/SuperClass.java +++ b/test-data/src/SuperClass.java @@ -1,4 +1,28 @@ class SuperClass { + interface BaseDefault { + default int value() { return 1; } + } + + interface SpecificDefault extends BaseDefault { + default int value() { return 2; } + } + + interface PrivateDefault { + private int value() { return 3; } + } + + static class DefaultParent implements SpecificDefault, BaseDefault, PrivateDefault {} + + static class DefaultChild extends DefaultParent { + int call() { return super.value(); } + } + + static class ReverseDefaultParent implements BaseDefault, SpecificDefault, PrivateDefault {} + + static class ReverseDefaultChild extends ReverseDefaultParent { + int call() { return super.value(); } + } + class InnerClass { int field1; long field2; @@ -49,6 +73,9 @@ void run() { InnerLeafClass leaf = new InnerLeafClass(); leaf.test(); leaf.testSuper(); + + System.out.println(new DefaultChild().call()); + System.out.println(new ReverseDefaultChild().call()); } public static void main(String[] args) {