@@ -10,8 +10,9 @@ use std::str::FromStr;
1010
1111use super :: metadata_members:: { backing_field_references, MetadataCallable } ;
1212use crate :: frontend:: kotlin_metadata:: { KotlinMetadata , TypeReference , Visibility } ;
13- use crate :: frontend:: ClassNode ;
14- use crate :: ir:: { FieldReference , MethodDescriptor , MethodReference } ;
13+ use crate :: frontend:: { ClassNode , MethodNode } ;
14+ use crate :: ir:: generic_types:: { GenericSignatures , JvmTypeSignature , TypeArgument } ;
15+ use crate :: ir:: { ArgType , FieldReference , MethodDescriptor , MethodReference } ;
1516use crate :: language:: kotlin:: { KotlinDefaultCallContract , KotlinDefaultMask , KotlinIdentifier } ;
1617use std:: sync:: Arc ;
1718
@@ -571,6 +572,7 @@ impl KotlinDeclaredMembers {
571572 singletons : & mut std:: collections:: BTreeSet < crate :: ir:: ArgType > ,
572573 ) {
573574 let Some ( Ok ( metadata) ) = KotlinMetadata :: of ( & class. annotations ) else {
575+ self . collect_suspend_abi ( class) ;
574576 return ;
575577 } ;
576578 let declarations = metadata. declarations ( ) ;
@@ -726,6 +728,32 @@ impl KotlinDeclaredMembers {
726728 self . fields . insert ( reference, property. flags . visibility ( ) ) ;
727729 }
728730 }
731+ self . collect_suspend_abi ( class) ;
732+ }
733+
734+ fn collect_suspend_abi ( & mut self , class : & ClassNode ) {
735+ let continuation_impl = class
736+ . methods ( )
737+ . iter ( )
738+ . any ( |method| method. name ( ) == "invokeSuspend" ) ;
739+ for method in class. methods ( ) {
740+ if method. is_constructor ( ) || method. name ( ) == "<clinit>" {
741+ continue ;
742+ }
743+ if method. name ( ) == "create" || method. name ( ) == "invokeSuspend" {
744+ continue ;
745+ }
746+ if continuation_impl && method. name ( ) == "invoke" {
747+ continue ;
748+ }
749+ let Some ( declaration) = suspend_abi_declaration ( method) else {
750+ continue ;
751+ } ;
752+ let reference = method_reference ( class, method) ;
753+ self . suspend_functions
754+ . entry ( reference)
755+ . or_insert ( declaration) ;
756+ }
729757 }
730758
731759 fn index_default_calls (
@@ -931,6 +959,73 @@ impl KotlinDeclaredMembers {
931959 }
932960}
933961
962+ fn method_reference ( class : & ClassNode , method : & MethodNode ) -> MethodReference {
963+ MethodReference {
964+ owner : class. class_type ( ) . clone ( ) ,
965+ name : method. name ( ) . to_string ( ) ,
966+ descriptor : MethodDescriptor {
967+ parameters : method. param_types ( ) . to_vec ( ) ,
968+ return_type : method. return_type ( ) . clone ( ) ,
969+ } ,
970+ }
971+ }
972+
973+ fn suspend_abi_declaration ( method : & MethodNode ) -> Option < KotlinSuspendDeclaration > {
974+ if method. return_type ( ) != & ArgType :: object ( "java/lang/Object" ) {
975+ return None ;
976+ }
977+ let continuation_parameter = method. param_types ( ) . len ( ) . checked_sub ( 1 ) ?;
978+ if !is_continuation_type ( & method. param_types ( ) [ continuation_parameter] ) {
979+ return None ;
980+ }
981+ Some ( KotlinSuspendDeclaration {
982+ continuation_parameter,
983+ return_type : suspend_abi_return_type ( method) ,
984+ } )
985+ }
986+
987+ fn suspend_abi_return_type ( method : & MethodNode ) -> ArgType {
988+ continuation_result_type ( method) . unwrap_or_else ( || ArgType :: object ( "java/lang/Object" ) )
989+ }
990+
991+ fn continuation_result_type ( method : & MethodNode ) -> Option < ArgType > {
992+ let signature = method
993+ . signature
994+ . as_deref ( )
995+ . and_then ( |signature| GenericSignatures :: method ( signature) . ok ( ) )
996+ . or_else ( || {
997+ method
998+ . override_semantics
999+ . as_ref ( )
1000+ . and_then ( |semantics| semantics. inherited_signature . clone ( ) )
1001+ } ) ?;
1002+ let last = signature. parameter_types . last ( ) ?;
1003+ continuation_type_argument ( last) . map ( |ty| ty. erased ( ) )
1004+ }
1005+
1006+ fn continuation_type_argument ( ty : & JvmTypeSignature ) -> Option < & JvmTypeSignature > {
1007+ let JvmTypeSignature :: ClassType ( class) = ty else {
1008+ return None ;
1009+ } ;
1010+ if !is_continuation_class_name ( & class. erased_name ( ) ) {
1011+ return None ;
1012+ }
1013+ match class. type_arguments . first ( ) ? {
1014+ TypeArgument :: Super ( inner) | TypeArgument :: Exact ( inner) | TypeArgument :: Extends ( inner) => {
1015+ Some ( inner)
1016+ }
1017+ TypeArgument :: Unbounded => None ,
1018+ }
1019+ }
1020+
1021+ fn is_continuation_type ( ty : & ArgType ) -> bool {
1022+ ty. as_object ( ) . is_some_and ( is_continuation_class_name)
1023+ }
1024+
1025+ fn is_continuation_class_name ( name : & str ) -> bool {
1026+ name == "kotlin/coroutines/Continuation" || name. ends_with ( "/Continuation" )
1027+ }
1028+
9341029fn property_accessor_reference (
9351030 class : & ClassNode ,
9361031 signature : & crate :: frontend:: kotlin_metadata:: JvmSignature ,
@@ -941,3 +1036,112 @@ fn property_accessor_reference(
9411036 descriptor : MethodDescriptor :: from_str ( & signature. descriptor ) . ok ( ) ?,
9421037 } )
9431038}
1039+
1040+ #[ cfg( test) ]
1041+ mod tests {
1042+ use super :: * ;
1043+ use crate :: frontend:: { AccessInfo , ClassInfo , MethodInfo } ;
1044+
1045+ fn class_with_methods ( name : & str , methods : Vec < MethodNode > ) -> ClassNode {
1046+ let mut class = ClassNode :: new (
1047+ 0 ,
1048+ ClassInfo :: from_type_descriptor ( & format ! ( "L{name};" ) ) . expect ( "class descriptor" ) ,
1049+ AccessInfo :: for_class ( 0x1 ) ,
1050+ ) ;
1051+ for method in methods {
1052+ class. add_method ( method) ;
1053+ }
1054+ class
1055+ }
1056+
1057+ fn method ( name : & str , parameters : Vec < ArgType > , return_type : ArgType ) -> MethodNode {
1058+ MethodNode :: new (
1059+ 0 ,
1060+ MethodInfo :: new (
1061+ "Lsample/Host;" . to_string ( ) ,
1062+ name. to_string ( ) ,
1063+ parameters,
1064+ return_type,
1065+ ) ,
1066+ AccessInfo :: for_method ( 0x9 ) ,
1067+ )
1068+ }
1069+
1070+ #[ test]
1071+ fn continuation_type_accepts_obfuscated_package ( ) {
1072+ assert ! ( is_continuation_type( & ArgType :: object(
1073+ "kotlin/coroutines/Continuation"
1074+ ) ) ) ;
1075+ assert ! ( is_continuation_type( & ArgType :: object( "ef1/Continuation" ) ) ) ;
1076+ assert ! ( !is_continuation_type( & ArgType :: object( "java/lang/Object" ) ) ) ;
1077+ assert ! ( !is_continuation_type( & ArgType :: INT ) ) ;
1078+ }
1079+
1080+ #[ test]
1081+ fn suspend_abi_reads_continuation_result_type ( ) {
1082+ let method = method (
1083+ "await" ,
1084+ vec ! [
1085+ ArgType :: object( "java/lang/String" ) ,
1086+ ArgType :: object( "kotlin/coroutines/Continuation" ) ,
1087+ ] ,
1088+ ArgType :: object ( "java/lang/Object" ) ,
1089+ )
1090+ . with_signature ( Some (
1091+ "(Ljava/lang/String;Lkotlin/coroutines/Continuation<-Ljava/lang/Integer;>;)Ljava/lang/Object;"
1092+ . into ( ) ,
1093+ ) ) ;
1094+ let declaration = suspend_abi_declaration ( & method) . expect ( "suspend ABI" ) ;
1095+ assert_eq ! ( declaration. continuation_parameter, 1 ) ;
1096+ assert_eq ! (
1097+ declaration. return_type,
1098+ ArgType :: object( "java/lang/Integer" )
1099+ ) ;
1100+ }
1101+
1102+ #[ test]
1103+ fn collect_suspend_abi_skips_continuation_impl_invoke ( ) {
1104+ let class = class_with_methods (
1105+ "sample/StateMachine" ,
1106+ vec ! [
1107+ method(
1108+ "invokeSuspend" ,
1109+ vec![ ArgType :: object( "java/lang/Object" ) ] ,
1110+ ArgType :: object( "java/lang/Object" ) ,
1111+ ) ,
1112+ method(
1113+ "invoke" ,
1114+ vec![
1115+ ArgType :: object( "java/lang/String" ) ,
1116+ ArgType :: object( "kotlin/coroutines/Continuation" ) ,
1117+ ] ,
1118+ ArgType :: object( "java/lang/Object" ) ,
1119+ ) ,
1120+ method(
1121+ "await" ,
1122+ vec![ ArgType :: object( "kotlin/coroutines/Continuation" ) ] ,
1123+ ArgType :: object( "java/lang/Object" ) ,
1124+ ) ,
1125+ ] ,
1126+ ) ;
1127+ let declared = KotlinDeclaredMembers :: analyze ( & [ & class] , & |_, _| None ) ;
1128+ let invoke = method_reference (
1129+ & class,
1130+ class
1131+ . methods ( )
1132+ . iter ( )
1133+ . find ( |method| method. name ( ) == "invoke" )
1134+ . expect ( "invoke" ) ,
1135+ ) ;
1136+ let await_method = method_reference (
1137+ & class,
1138+ class
1139+ . methods ( )
1140+ . iter ( )
1141+ . find ( |method| method. name ( ) == "await" )
1142+ . expect ( "await" ) ,
1143+ ) ;
1144+ assert ! ( declared. suspend_declaration( & invoke) . is_none( ) ) ;
1145+ assert ! ( declared. suspend_declaration( & await_method) . is_some( ) ) ;
1146+ }
1147+ }
0 commit comments