summaryrefslogtreecommitdiff
path: root/runtime/string_builder_append.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/string_builder_append.cc')
-rw-r--r--runtime/string_builder_append.cc162
1 files changed, 158 insertions, 4 deletions
diff --git a/runtime/string_builder_append.cc b/runtime/string_builder_append.cc
index 85b70eb12b..ef6969d4d0 100644
--- a/runtime/string_builder_append.cc
+++ b/runtime/string_builder_append.cc
@@ -20,9 +20,11 @@
#include "base/logging.h"
#include "common_throws.h"
#include "gc/heap.h"
+#include "mirror/array-inl.h"
#include "mirror/string-alloc-inl.h"
#include "obj_ptr-inl.h"
#include "runtime.h"
+#include "well_known_classes.h"
namespace art {
@@ -60,6 +62,11 @@ class StringBuilderAppend::Builder {
return new_string->GetLength() - (data - new_string->GetValue());
}
+ template <typename CharType>
+ CharType* AppendFpArg(ObjPtr<mirror::String> new_string,
+ CharType* data,
+ size_t fp_arg_index) const REQUIRES_SHARED(Locks::mutator_lock_);
+
template <typename CharType, size_t size>
static CharType* AppendLiteral(ObjPtr<mirror::String> new_string,
CharType* data,
@@ -75,6 +82,8 @@ class StringBuilderAppend::Builder {
CharType* data,
int64_t value) REQUIRES_SHARED(Locks::mutator_lock_);
+ int32_t ConvertFpArgs() REQUIRES_SHARED(Locks::mutator_lock_);
+
template <typename CharType>
void StoreData(ObjPtr<mirror::String> new_string, CharType* data) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -93,6 +102,12 @@ class StringBuilderAppend::Builder {
// References are moved to the handle scope during CalculateLengthWithFlag().
StackHandleScope<kMaxArgs> hs_;
+ // We convert float/double values using jdk.internal.math.FloatingDecimal which uses
+ // a thread-local converter under the hood. As we may have more than one
+ // float/double argument, we need to copy the data out of the converter.
+ uint8_t converted_fp_args_[kMaxArgs][26]; // 26 is the maximum number of characters.
+ int32_t converted_fp_arg_lengths_[kMaxArgs];
+
// The length and flag to store when the AppendBuilder is used as a pre-fence visitor.
int32_t length_with_flag_ = 0u;
};
@@ -142,6 +157,18 @@ inline size_t StringBuilderAppend::Builder::Uint64Length(uint64_t value) {
return log10_value_estimate + adjustment;
}
+template <typename CharType>
+inline CharType* StringBuilderAppend::Builder::AppendFpArg(ObjPtr<mirror::String> new_string,
+ CharType* data,
+ size_t fp_arg_index) const {
+ DCHECK_LE(fp_arg_index, std::size(converted_fp_args_));
+ const uint8_t* src = converted_fp_args_[fp_arg_index];
+ size_t length = converted_fp_arg_lengths_[fp_arg_index];
+ DCHECK_LE(length, std::size(converted_fp_args_[0]));
+ DCHECK_LE(length, RemainingSpace(new_string, data));
+ return std::copy_n(src, length, data);
+}
+
template <typename CharType, size_t size>
inline CharType* StringBuilderAppend::Builder::AppendLiteral(ObjPtr<mirror::String> new_string,
CharType* data,
@@ -204,10 +231,111 @@ inline CharType* StringBuilderAppend::Builder::AppendInt64(ObjPtr<mirror::String
return data + length;
}
+int32_t StringBuilderAppend::Builder::ConvertFpArgs() {
+ int32_t fp_args_length = 0u;
+ const uint32_t* current_arg = args_;
+ size_t fp_arg_index = 0u;
+ for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
+ DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
+ bool fp_arg = false;
+ ObjPtr<mirror::Object> converter;
+ switch (static_cast<Argument>(f & kArgMask)) {
+ case Argument::kString:
+ case Argument::kBoolean:
+ case Argument::kChar:
+ case Argument::kInt:
+ break;
+ case Argument::kLong: {
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ break;
+ }
+ case Argument::kFloat: {
+ fp_arg = true;
+ float arg = bit_cast<float>(*current_arg);
+ converter = WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F
+ ->InvokeStatic<'L', 'F'>(hs_.Self(), arg);
+ break;
+ }
+ case Argument::kDouble: {
+ fp_arg = true;
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ double arg = bit_cast<double>(
+ static_cast<uint64_t>(current_arg[0]) + (static_cast<uint64_t>(current_arg[1]) << 32));
+ converter = WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D
+ ->InvokeStatic<'L', 'D'>(hs_.Self(), arg);
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ break;
+ }
+ case Argument::kStringBuilder:
+ case Argument::kCharArray:
+ case Argument::kObject:
+ LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
+ << (f & kArgMask) << " full format: 0x" << std::hex << format_;
+ UNREACHABLE();
+ default:
+ LOG(FATAL) << "Unexpected arg format: 0x" << std::hex
+ << (f & kArgMask) << " full format: 0x" << std::hex << format_;
+ UNREACHABLE();
+ }
+ if (fp_arg) {
+ // If we see an exception (presumably OOME or SOE), keep it as is, even
+ // though it may be confusing to see the stack trace for FP argument
+ // conversion continue at the StringBuilder.toString() invoke location.
+ DCHECK_EQ(converter == nullptr, hs_.Self()->IsExceptionPending());
+ if (UNLIKELY(converter == nullptr)) {
+ return -1;
+ }
+ ArtField* btab_buffer_field =
+ WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
+ int32_t length;
+ if (converter->GetClass() == btab_buffer_field->GetDeclaringClass()) {
+ // Call `converter.getChars(converter.buffer)`.
+ StackHandleScope<1u> hs2(hs_.Self());
+ Handle<mirror::CharArray> buffer =
+ hs2.NewHandle(btab_buffer_field->GetObj<mirror::CharArray>(converter));
+ DCHECK(buffer != nullptr);
+ length = WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars
+ ->InvokeInstance<'I', 'L'>(hs_.Self(), converter, buffer.Get());
+ if (UNLIKELY(hs_.Self()->IsExceptionPending())) {
+ return -1;
+ }
+ // The converted string is now at the front of the buffer.
+ DCHECK_GT(length, 0);
+ DCHECK_LE(length, buffer->GetLength());
+ DCHECK_LE(static_cast<size_t>(length), std::size(converted_fp_args_[0]));
+ DCHECK(mirror::String::AllASCII(buffer->GetData(), length));
+ std::copy_n(buffer->GetData(), length, converted_fp_args_[fp_arg_index]);
+ } else {
+ ArtField* ebtab_image_field = WellKnownClasses::
+ jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
+ DCHECK(converter->GetClass() == ebtab_image_field->GetDeclaringClass());
+ ObjPtr<mirror::String> converted = ebtab_image_field->GetObj<mirror::String>(converter);
+ DCHECK(converted != nullptr);
+ length = converted->GetLength();
+ if (mirror::kUseStringCompression) {
+ DCHECK(converted->IsCompressed());
+ memcpy(converted_fp_args_[fp_arg_index], converted->GetValueCompressed(), length);
+ } else {
+ DCHECK(mirror::String::AllASCII(converted->GetValue(), length));
+ std::copy_n(converted->GetValue(), length, converted_fp_args_[fp_arg_index]);
+ }
+ }
+ converted_fp_arg_lengths_[fp_arg_index] = length;
+ fp_args_length += length;
+ ++fp_arg_index;
+ }
+ ++current_arg;
+ DCHECK_LE(fp_arg_index, kMaxArgs);
+ }
+ return fp_args_length;
+}
+
inline int32_t StringBuilderAppend::Builder::CalculateLengthWithFlag() {
static_assert(static_cast<size_t>(Argument::kEnd) == 0u, "kEnd must be 0.");
bool compressible = mirror::kUseStringCompression;
uint64_t length = 0u;
+ bool has_fp_args = false;
const uint32_t* current_arg = args_;
for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
@@ -243,12 +371,19 @@ inline int32_t StringBuilderAppend::Builder::CalculateLengthWithFlag() {
++current_arg; // Skip the low word, let the common code skip the high word.
break;
}
+ case Argument::kDouble:
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ FALLTHROUGH_INTENDED;
+ case Argument::kFloat:
+ // Conversion shall be performed in a separate pass because it calls back to
+ // managed code and we need to convert reference arguments to `Handle<>`s first.
+ has_fp_args = true;
+ break;
case Argument::kStringBuilder:
case Argument::kCharArray:
case Argument::kObject:
- case Argument::kFloat:
- case Argument::kDouble:
LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
<< (f & kArgMask) << " full format: 0x" << std::hex << format_;
UNREACHABLE();
@@ -261,6 +396,16 @@ inline int32_t StringBuilderAppend::Builder::CalculateLengthWithFlag() {
DCHECK_LE(hs_.NumberOfReferences(), kMaxArgs);
}
+ if (UNLIKELY(has_fp_args)) {
+ // Call Java helpers to convert FP args.
+ int32_t fp_args_length = ConvertFpArgs();
+ if (fp_args_length == -1) {
+ return -1;
+ }
+ DCHECK_GT(fp_args_length, 0);
+ length += fp_args_length;
+ }
+
if (length > std::numeric_limits<int32_t>::max()) {
// We cannot allocate memory for the entire result.
hs_.Self()->ThrowNewException("Ljava/lang/OutOfMemoryError;",
@@ -276,6 +421,7 @@ template <typename CharType>
inline void StringBuilderAppend::Builder::StoreData(ObjPtr<mirror::String> new_string,
CharType* data) const {
size_t handle_index = 0u;
+ size_t fp_arg_index = 0u;
const uint32_t* current_arg = args_;
for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
@@ -315,11 +461,18 @@ inline void StringBuilderAppend::Builder::StoreData(ObjPtr<mirror::String> new_s
++current_arg; // Skip the low word, let the common code skip the high word.
break;
}
+ case Argument::kDouble:
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ FALLTHROUGH_INTENDED;
+ case Argument::kFloat: {
+ data = AppendFpArg(new_string, data, fp_arg_index);
+ ++fp_arg_index;
+ break;
+ }
case Argument::kStringBuilder:
case Argument::kCharArray:
- case Argument::kFloat:
- case Argument::kDouble:
LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
<< (f & kArgMask) << " full format: 0x" << std::hex << format_;
UNREACHABLE();
@@ -330,6 +483,7 @@ inline void StringBuilderAppend::Builder::StoreData(ObjPtr<mirror::String> new_s
}
++current_arg;
DCHECK_LE(handle_index, hs_.NumberOfReferences());
+ DCHECK_LE(fp_arg_index, std::size(converted_fp_args_));
}
DCHECK_EQ(RemainingSpace(new_string, data), 0u) << std::hex << format_;
}