@@ -30,27 +30,56 @@ impl String {
3030 parent_class : Some ( "java/lang/Object" ) ,
3131 interfaces : vec ! [ ] ,
3232 methods : vec ! [
33+ JavaMethodProto :: new( "<init>" , "()V" , Self :: init_empty, Default :: default ( ) ) ,
3334 JavaMethodProto :: new( "<init>" , "([B)V" , Self :: init_with_byte_array, Default :: default ( ) ) ,
3435 JavaMethodProto :: new( "<init>" , "([C)V" , Self :: init_with_char_array, Default :: default ( ) ) ,
3536 JavaMethodProto :: new( "<init>" , "([CII)V" , Self :: init_with_partial_char_array, Default :: default ( ) ) ,
3637 JavaMethodProto :: new( "<init>" , "([BII)V" , Self :: init_with_partial_byte_array, Default :: default ( ) ) ,
38+ JavaMethodProto :: new(
39+ "<init>" ,
40+ "([BLjava/lang/String;)V" ,
41+ Self :: init_with_byte_array_charset,
42+ Default :: default ( ) ,
43+ ) ,
44+ JavaMethodProto :: new(
45+ "<init>" ,
46+ "([BIILjava/lang/String;)V" ,
47+ Self :: init_with_partial_byte_array_charset,
48+ Default :: default ( ) ,
49+ ) ,
3750 JavaMethodProto :: new( "<init>" , "(Ljava/lang/String;)V" , Self :: init_with_string, Default :: default ( ) ) ,
3851 JavaMethodProto :: new( "<init>" , "(Ljava/lang/StringBuffer;)V" , Self :: init_with_string_buffer, Default :: default ( ) ) ,
3952 JavaMethodProto :: new( "equals" , "(Ljava/lang/Object;)Z" , Self :: equals, Default :: default ( ) ) ,
53+ JavaMethodProto :: new( "equalsIgnoreCase" , "(Ljava/lang/String;)Z" , Self :: equals_ignore_case, Default :: default ( ) ) ,
4054 JavaMethodProto :: new( "compareTo" , "(Ljava/lang/String;)I" , Self :: compare_to, Default :: default ( ) ) ,
4155 JavaMethodProto :: new( "hashCode" , "()I" , Self :: hash_code, Default :: default ( ) ) ,
4256 JavaMethodProto :: new( "toString" , "()Ljava/lang/String;" , Self :: to_string, Default :: default ( ) ) ,
4357 JavaMethodProto :: new( "charAt" , "(I)C" , Self :: char_at, Default :: default ( ) ) ,
4458 JavaMethodProto :: new( "getBytes" , "()[B" , Self :: get_bytes, Default :: default ( ) ) ,
59+ JavaMethodProto :: new( "getBytes" , "(Ljava/lang/String;)[B" , Self :: get_bytes_charset, Default :: default ( ) ) ,
4560 JavaMethodProto :: new( "getChars" , "(II[CI)V" , Self :: get_chars, Default :: default ( ) ) ,
4661 JavaMethodProto :: new( "toCharArray" , "()[C" , Self :: to_char_array, Default :: default ( ) ) ,
4762 JavaMethodProto :: new( "toUpperCase" , "()Ljava/lang/String;" , Self :: to_upper_case, Default :: default ( ) ) ,
63+ JavaMethodProto :: new( "toLowerCase" , "()Ljava/lang/String;" , Self :: to_lower_case, Default :: default ( ) ) ,
4864 JavaMethodProto :: new( "length" , "()I" , Self :: length, Default :: default ( ) ) ,
4965 JavaMethodProto :: new( "concat" , "(Ljava/lang/String;)Ljava/lang/String;" , Self :: concat, Default :: default ( ) ) ,
5066 JavaMethodProto :: new( "substring" , "(I)Ljava/lang/String;" , Self :: substring, Default :: default ( ) ) ,
5167 JavaMethodProto :: new( "substring" , "(II)Ljava/lang/String;" , Self :: substring_with_end, Default :: default ( ) ) ,
68+ JavaMethodProto :: new( "replace" , "(CC)Ljava/lang/String;" , Self :: replace, Default :: default ( ) ) ,
69+ JavaMethodProto :: new( "regionMatches" , "(ZILjava/lang/String;II)Z" , Self :: region_matches, Default :: default ( ) ) ,
70+ JavaMethodProto :: new( "valueOf" , "(Z)Ljava/lang/String;" , Self :: value_of_boolean, MethodAccessFlags :: STATIC ) ,
5271 JavaMethodProto :: new( "valueOf" , "(C)Ljava/lang/String;" , Self :: value_of_char, MethodAccessFlags :: STATIC ) ,
5372 JavaMethodProto :: new( "valueOf" , "(I)Ljava/lang/String;" , Self :: value_of_integer, MethodAccessFlags :: STATIC ) ,
73+ JavaMethodProto :: new( "valueOf" , "(J)Ljava/lang/String;" , Self :: value_of_long, MethodAccessFlags :: STATIC ) ,
74+ JavaMethodProto :: new( "valueOf" , "(F)Ljava/lang/String;" , Self :: value_of_float, MethodAccessFlags :: STATIC ) ,
75+ JavaMethodProto :: new( "valueOf" , "(D)Ljava/lang/String;" , Self :: value_of_double, MethodAccessFlags :: STATIC ) ,
76+ JavaMethodProto :: new( "valueOf" , "([C)Ljava/lang/String;" , Self :: value_of_char_array, MethodAccessFlags :: STATIC ) ,
77+ JavaMethodProto :: new(
78+ "valueOf" ,
79+ "([CII)Ljava/lang/String;" ,
80+ Self :: value_of_partial_char_array,
81+ MethodAccessFlags :: STATIC ,
82+ ) ,
5483 JavaMethodProto :: new(
5584 "valueOf" ,
5685 "(Ljava/lang/Object;)Ljava/lang/String;" ,
@@ -62,9 +91,11 @@ impl String {
6291 JavaMethodProto :: new( "indexOf" , "(Ljava/lang/String;)I" , Self :: index_of_string, Default :: default ( ) ) ,
6392 JavaMethodProto :: new( "indexOf" , "(Ljava/lang/String;I)I" , Self :: index_of_string_from, Default :: default ( ) ) ,
6493 JavaMethodProto :: new( "lastIndexOf" , "(I)I" , Self :: last_index_of, Default :: default ( ) ) ,
94+ JavaMethodProto :: new( "lastIndexOf" , "(II)I" , Self :: last_index_of_from, Default :: default ( ) ) ,
6595 JavaMethodProto :: new( "trim" , "()Ljava/lang/String;" , Self :: trim, Default :: default ( ) ) ,
6696 JavaMethodProto :: new( "startsWith" , "(Ljava/lang/String;)Z" , Self :: starts_with, Default :: default ( ) ) ,
6797 JavaMethodProto :: new( "startsWith" , "(Ljava/lang/String;I)Z" , Self :: starts_with_offset, Default :: default ( ) ) ,
98+ JavaMethodProto :: new( "endsWith" , "(Ljava/lang/String;)Z" , Self :: ends_with, Default :: default ( ) ) ,
6899 ] ,
69100 fields : vec ! [ JavaFieldProto :: new( "value" , "[C" , Default :: default ( ) ) ] ,
70101 access_flags : Default :: default ( ) ,
@@ -463,6 +494,262 @@ impl String {
463494 Ok ( this_string. starts_with ( & prefix_string) )
464495 }
465496
497+ async fn init_empty ( jvm : & Jvm , _: & mut RuntimeContext , mut this : ClassInstanceRef < Self > ) -> Result < ( ) > {
498+ tracing:: debug!( "java.lang.String::<init>({:?})" , & this) ;
499+
500+ let _: ( ) = jvm. invoke_special ( & this, "java/lang/Object" , "<init>" , "()V" , ( ) ) . await ?;
501+
502+ let array = jvm. instantiate_array ( "C" , 0 ) . await ?;
503+ jvm. put_field ( & mut this, "value" , "[C" , array) . await ?;
504+
505+ Ok ( ( ) )
506+ }
507+
508+ async fn init_with_byte_array_charset (
509+ jvm : & Jvm ,
510+ _: & mut RuntimeContext ,
511+ this : ClassInstanceRef < Self > ,
512+ value : ClassInstanceRef < Array < i8 > > ,
513+ charset_name : ClassInstanceRef < Self > ,
514+ ) -> Result < ( ) > {
515+ tracing:: debug!( "java.lang.String::<init>({:?}, {:?}, {:?})" , & this, & value, & charset_name) ;
516+
517+ let count = jvm. array_length ( & value) . await ? as i32 ;
518+
519+ let _: ( ) = jvm
520+ . invoke_special (
521+ & this,
522+ "java/lang/String" ,
523+ "<init>" ,
524+ "([BIILjava/lang/String;)V" ,
525+ ( value, 0 , count, charset_name) ,
526+ )
527+ . await ?;
528+
529+ Ok ( ( ) )
530+ }
531+
532+ async fn init_with_partial_byte_array_charset (
533+ jvm : & Jvm ,
534+ _: & mut RuntimeContext ,
535+ this : ClassInstanceRef < Self > ,
536+ value : ClassInstanceRef < Array < i8 > > ,
537+ offset : i32 ,
538+ count : i32 ,
539+ charset_name : ClassInstanceRef < Self > ,
540+ ) -> Result < ( ) > {
541+ tracing:: debug!(
542+ "java.lang.String::<init>({:?}, {:?}, {}, {}, {:?})" ,
543+ & this,
544+ & value,
545+ offset,
546+ count,
547+ & charset_name
548+ ) ;
549+
550+ let bytes: Vec < i8 > = jvm. load_array ( & value, offset as _ , count as _ ) . await ?;
551+
552+ let charset = JavaLangString :: to_rust_string ( jvm, & charset_name) . await ?;
553+ let string = Self :: decode_str ( & charset, cast_slice ( & bytes) ) ;
554+
555+ let utf16 = string. encode_utf16 ( ) . collect :: < Vec < _ > > ( ) ;
556+
557+ let mut array = jvm. instantiate_array ( "C" , utf16. len ( ) ) . await ?;
558+ jvm. store_array ( & mut array, 0 , utf16) . await ?;
559+
560+ let _: ( ) = jvm. invoke_special ( & this, "java/lang/String" , "<init>" , "([C)V" , [ array. into ( ) ] ) . await ?;
561+
562+ Ok ( ( ) )
563+ }
564+
565+ async fn equals_ignore_case ( jvm : & Jvm , _: & mut RuntimeContext , this : ClassInstanceRef < Self > , other : ClassInstanceRef < Self > ) -> Result < bool > {
566+ tracing:: debug!( "java.lang.String::equalsIgnoreCase({:?}, {:?})" , & this, & other) ;
567+
568+ if other. is_null ( ) {
569+ return Ok ( false ) ;
570+ }
571+
572+ let this_string = JavaLangString :: to_rust_string ( jvm, & this) . await ?;
573+ let other_string = JavaLangString :: to_rust_string ( jvm, & other) . await ?;
574+
575+ Ok ( this_string. eq_ignore_ascii_case ( & other_string) || this_string. to_lowercase ( ) == other_string. to_lowercase ( ) )
576+ }
577+
578+ async fn get_bytes_charset (
579+ jvm : & Jvm ,
580+ _: & mut RuntimeContext ,
581+ this : ClassInstanceRef < Self > ,
582+ charset_name : ClassInstanceRef < Self > ,
583+ ) -> Result < ClassInstanceRef < Array < i8 > > > {
584+ tracing:: debug!( "java.lang.String::getBytes({:?}, {:?})" , & this, & charset_name) ;
585+
586+ let string = JavaLangString :: to_rust_string ( jvm, & this) . await ?;
587+ let charset = JavaLangString :: to_rust_string ( jvm, & charset_name) . await ?;
588+
589+ let bytes = cast_vec ( Self :: encode_str ( & charset, & string) ) ;
590+
591+ let mut byte_array = jvm. instantiate_array ( "B" , bytes. len ( ) ) . await ?;
592+ jvm. array_raw_buffer_mut ( & mut byte_array) . await ?. write ( 0 , & bytes) ?;
593+
594+ Ok ( byte_array. into ( ) )
595+ }
596+
597+ async fn to_lower_case ( jvm : & Jvm , _: & mut RuntimeContext , this : ClassInstanceRef < Self > ) -> Result < ClassInstanceRef < Self > > {
598+ tracing:: debug!( "java.lang.String::toLowerCase({:?})" , & this) ;
599+
600+ let string = JavaLangString :: to_rust_string ( jvm, & this) . await ?;
601+ let lower = string. to_lowercase ( ) ;
602+
603+ Ok ( JavaLangString :: from_rust_string ( jvm, & lower) . await ?. into ( ) )
604+ }
605+
606+ async fn replace (
607+ jvm : & Jvm ,
608+ _: & mut RuntimeContext ,
609+ this : ClassInstanceRef < Self > ,
610+ old_char : JavaChar ,
611+ new_char : JavaChar ,
612+ ) -> Result < ClassInstanceRef < Self > > {
613+ tracing:: debug!( "java.lang.String::replace({:?}, {}, {})" , & this, old_char, new_char) ;
614+
615+ let value = jvm. get_field ( & this, "value" , "[C" ) . await ?;
616+ let length = jvm. array_length ( & value) . await ?;
617+ let chars: Vec < JavaChar > = jvm. load_array ( & value, 0 , length) . await ?;
618+
619+ let replaced: Vec < JavaChar > = chars. into_iter ( ) . map ( |c| if c == old_char { new_char } else { c } ) . collect ( ) ;
620+
621+ let mut array = jvm. instantiate_array ( "C" , replaced. len ( ) ) . await ?;
622+ jvm. store_array ( & mut array, 0 , replaced) . await ?;
623+
624+ let new_string = jvm. new_class ( "java/lang/String" , "([C)V" , ( array, ) ) . await ?;
625+
626+ Ok ( new_string. into ( ) )
627+ }
628+
629+ #[ allow( clippy:: too_many_arguments) ]
630+ async fn region_matches (
631+ jvm : & Jvm ,
632+ _: & mut RuntimeContext ,
633+ this : ClassInstanceRef < Self > ,
634+ ignore_case : bool ,
635+ toffset : i32 ,
636+ other : ClassInstanceRef < Self > ,
637+ ooffset : i32 ,
638+ len : i32 ,
639+ ) -> Result < bool > {
640+ tracing:: debug!(
641+ "java.lang.String::regionMatches({:?}, {}, {}, {:?}, {}, {})" ,
642+ & this,
643+ ignore_case,
644+ toffset,
645+ & other,
646+ ooffset,
647+ len
648+ ) ;
649+
650+ if toffset < 0 || ooffset < 0 || len < 0 {
651+ return Ok ( false ) ;
652+ }
653+
654+ let this_string = JavaLangString :: to_rust_string ( jvm, & this) . await ?;
655+ let other_string = JavaLangString :: to_rust_string ( jvm, & other) . await ?;
656+
657+ let this_chars: Vec < u16 > = this_string. encode_utf16 ( ) . collect ( ) ;
658+ let other_chars: Vec < u16 > = other_string. encode_utf16 ( ) . collect ( ) ;
659+
660+ let end_t = toffset as usize + len as usize ;
661+ let end_o = ooffset as usize + len as usize ;
662+ if end_t > this_chars. len ( ) || end_o > other_chars. len ( ) {
663+ return Ok ( false ) ;
664+ }
665+
666+ let this_slice = & this_chars[ toffset as usize ..end_t] ;
667+ let other_slice = & other_chars[ ooffset as usize ..end_o] ;
668+
669+ if ignore_case {
670+ let to_lower = |c : u16 | -> u16 {
671+ char:: from_u32 ( c as u32 )
672+ . map ( |ch| ch. to_lowercase ( ) . next ( ) . unwrap_or ( ch) as u32 as u16 )
673+ . unwrap_or ( c)
674+ } ;
675+ Ok ( this_slice. iter ( ) . copied ( ) . map ( to_lower) . eq ( other_slice. iter ( ) . copied ( ) . map ( to_lower) ) )
676+ } else {
677+ Ok ( this_slice == other_slice)
678+ }
679+ }
680+
681+ async fn last_index_of_from ( jvm : & Jvm , _: & mut RuntimeContext , this : ClassInstanceRef < Self > , ch : i32 , from_index : i32 ) -> Result < i32 > {
682+ tracing:: debug!( "java.lang.String::lastIndexOf({:?}, {}, {})" , & this, ch, from_index) ;
683+
684+ if from_index < 0 {
685+ return Ok ( -1 ) ;
686+ }
687+
688+ let this_string = JavaLangString :: to_rust_string ( jvm, & this) . await ?;
689+ let chars: Vec < char > = this_string. chars ( ) . collect ( ) ;
690+ let end = ( from_index as usize + 1 ) . min ( chars. len ( ) ) ;
691+
692+ let index = chars[ ..end] . iter ( ) . rposition ( |& c| c as u32 == ch as u32 ) . map ( |x| x as i32 ) ;
693+
694+ Ok ( index. unwrap_or ( -1 ) )
695+ }
696+
697+ async fn ends_with ( jvm : & Jvm , _: & mut RuntimeContext , this : ClassInstanceRef < Self > , suffix : ClassInstanceRef < Self > ) -> Result < bool > {
698+ tracing:: debug!( "java.lang.String::endsWith({:?}, {:?})" , & this, & suffix) ;
699+
700+ let this_string = JavaLangString :: to_rust_string ( jvm, & this) . await ?;
701+ let suffix_string = JavaLangString :: to_rust_string ( jvm, & suffix) . await ?;
702+
703+ Ok ( this_string. ends_with ( & suffix_string) )
704+ }
705+
706+ async fn value_of_boolean ( jvm : & Jvm , _: & mut RuntimeContext , value : bool ) -> Result < ClassInstanceRef < Self > > {
707+ tracing:: debug!( "java.lang.String::valueOf({})" , value) ;
708+
709+ let string = if value { "true" } else { "false" } ;
710+ Ok ( JavaLangString :: from_rust_string ( jvm, string) . await ?. into ( ) )
711+ }
712+
713+ async fn value_of_long ( jvm : & Jvm , _: & mut RuntimeContext , value : i64 ) -> Result < ClassInstanceRef < Self > > {
714+ tracing:: debug!( "java.lang.String::valueOf({})" , value) ;
715+
716+ Ok ( JavaLangString :: from_rust_string ( jvm, & value. to_string ( ) ) . await ?. into ( ) )
717+ }
718+
719+ async fn value_of_float ( jvm : & Jvm , _: & mut RuntimeContext , value : f32 ) -> Result < ClassInstanceRef < Self > > {
720+ tracing:: debug!( "java.lang.String::valueOf({})" , value) ;
721+
722+ Ok ( JavaLangString :: from_rust_string ( jvm, & value. to_string ( ) ) . await ?. into ( ) )
723+ }
724+
725+ async fn value_of_double ( jvm : & Jvm , _: & mut RuntimeContext , value : f64 ) -> Result < ClassInstanceRef < Self > > {
726+ tracing:: debug!( "java.lang.String::valueOf({})" , value) ;
727+
728+ Ok ( JavaLangString :: from_rust_string ( jvm, & value. to_string ( ) ) . await ?. into ( ) )
729+ }
730+
731+ async fn value_of_char_array ( jvm : & Jvm , _: & mut RuntimeContext , value : ClassInstanceRef < Array < JavaChar > > ) -> Result < ClassInstanceRef < Self > > {
732+ tracing:: debug!( "java.lang.String::valueOf({:?})" , & value) ;
733+
734+ let new_string = jvm. new_class ( "java/lang/String" , "([C)V" , ( value, ) ) . await ?;
735+
736+ Ok ( new_string. into ( ) )
737+ }
738+
739+ async fn value_of_partial_char_array (
740+ jvm : & Jvm ,
741+ _: & mut RuntimeContext ,
742+ value : ClassInstanceRef < Array < JavaChar > > ,
743+ offset : i32 ,
744+ count : i32 ,
745+ ) -> Result < ClassInstanceRef < Self > > {
746+ tracing:: debug!( "java.lang.String::valueOf({:?}, {}, {})" , & value, offset, count) ;
747+
748+ let new_string = jvm. new_class ( "java/lang/String" , "([CII)V" , ( value, offset, count) ) . await ?;
749+
750+ Ok ( new_string. into ( ) )
751+ }
752+
466753 fn decode_str ( charset : & str , bytes : & [ u8 ] ) -> RustString {
467754 match charset {
468755 "UTF-8" => str:: from_utf8 ( bytes) . unwrap ( ) . to_string ( ) ,
0 commit comments