Fix String::DoReplace() using obsolete `this`.

Change it to a static function taking a Handle<>.

Test: testrunner.py --host --interp-ac --gcstress -t 021-string2
Bug: 36335996
Change-Id: I5ab3e7adc59d6a9095290e57d5ce5d46b79f089b
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index eb0a9d1..fa2af9f 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1336,12 +1336,14 @@
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   jchar old_c = shadow_frame->GetVReg(arg_offset + 1);
   jchar new_c = shadow_frame->GetVReg(arg_offset + 2);
-  ObjPtr<mirror::String> string = shadow_frame->GetVRegReference(arg_offset)->AsString();
+  StackHandleScope<1> hs(self);
+  Handle<mirror::String> string =
+      hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsString());
   if (string == nullptr) {
     AbortTransactionOrFail(self, "String.replaceWithMatch with null object");
     return;
   }
-  result->SetL(string->DoReplace(self, old_c, new_c));
+  result->SetL(mirror::String::DoReplace(self, string, old_c, new_c));
 }
 
 // This allows creating the new style of String objects during compilation.
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 884b88a..de0e75b 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -89,16 +89,17 @@
   return true;
 }
 
-ObjPtr<String> String::DoReplace(Thread* self, uint16_t old_c, uint16_t new_c) {
-  DCHECK(IsCompressed() ? ContainsElement(ArrayRef<uint8_t>(value_compressed_, GetLength()), old_c)
-                        : ContainsElement(ArrayRef<uint16_t>(value_, GetLength()), old_c));
-  int32_t length = GetLength();
+ObjPtr<String> String::DoReplace(Thread* self, Handle<String> src, uint16_t old_c, uint16_t new_c) {
+  int32_t length = src->GetLength();
+  DCHECK(src->IsCompressed()
+             ? ContainsElement(ArrayRef<uint8_t>(src->value_compressed_, length), old_c)
+             : ContainsElement(ArrayRef<uint16_t>(src->value_, length), old_c));
   bool compressible =
       kUseStringCompression &&
       IsASCII(new_c) &&
-      (IsCompressed() || (!IsASCII(old_c) && AllASCIIExcept(value_, length, old_c)));
+      (src->IsCompressed() || (!IsASCII(old_c) && AllASCIIExcept(src->value_, length, old_c)));
   gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
-  const int32_t length_with_flag = String::GetFlaggedCount(GetLength(), compressible);
+  const int32_t length_with_flag = String::GetFlaggedCount(length, compressible);
   SetStringCountVisitor visitor(length_with_flag);
   ObjPtr<String> string = Alloc<true>(self, length_with_flag, allocator_type, visitor);
   if (UNLIKELY(string == nullptr)) {
@@ -109,10 +110,10 @@
       return dchecked_integral_cast<uint8_t>((old_c != c) ? c : new_c);
     };
     uint8_t* out = string->value_compressed_;
-    if (LIKELY(IsCompressed())) {  // LIKELY(compressible == IsCompressed())
-      std::transform(value_compressed_, value_compressed_ + length, out, replace);
+    if (LIKELY(src->IsCompressed())) {  // LIKELY(compressible == src->IsCompressed())
+      std::transform(src->value_compressed_, src->value_compressed_ + length, out, replace);
     } else {
-      std::transform(value_, value_ + length, out, replace);
+      std::transform(src->value_, src->value_ + length, out, replace);
     }
     DCHECK(kUseStringCompression && AllASCII(out, length));
   } else {
@@ -120,10 +121,10 @@
       return (old_c != c) ? c : new_c;
     };
     uint16_t* out = string->value_;
-    if (UNLIKELY(IsCompressed())) {  // LIKELY(compressible == IsCompressed())
-      std::transform(value_compressed_, value_compressed_ + length, out, replace);
+    if (UNLIKELY(src->IsCompressed())) {  // LIKELY(compressible == src->IsCompressed())
+      std::transform(src->value_compressed_, src->value_compressed_ + length, out, replace);
     } else {
-      std::transform(value_, value_ + length, out, replace);
+      std::transform(src->value_, src->value_ + length, out, replace);
     }
     DCHECK(!kUseStringCompression || !AllASCII(out, length));
   }
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index dbb5a4c..b59bbfb 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -96,7 +96,7 @@
 
   // Create a new string where all occurences of `old_c` are replaced with `new_c`.
   // String.doReplace(char, char) is called from String.replace(char, char) when there is a match.
-  ObjPtr<String> DoReplace(Thread* self, uint16_t old_c, uint16_t new_c)
+  static ObjPtr<String> DoReplace(Thread* self, Handle<String> src, uint16_t old_c, uint16_t new_c)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   ObjPtr<String> Intern() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 2e561ff..bf33bf2 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -101,8 +101,9 @@
 
 static jstring String_doReplace(JNIEnv* env, jobject java_this, jchar old_c, jchar new_c) {
   ScopedFastNativeObjectAccess soa(env);
-  ObjPtr<mirror::String> result =
-      soa.Decode<mirror::String>(java_this)->DoReplace(soa.Self(), old_c, new_c);
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::String> string = hs.NewHandle(soa.Decode<mirror::String>(java_this));
+  ObjPtr<mirror::String> result = mirror::String::DoReplace(soa.Self(), string, old_c, new_c);
   return soa.AddLocalReference<jstring>(result);
 }