#include "unstarted_runtime.h"
+#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <cmath>
#include <limits>
+#include <locale>
#include <unordered_map>
#include "ScopedLocalRef.h"
}
}
+// Restricted support for character upper case / lower case. Only support ASCII, where
+// it's easy. Abort the transaction otherwise.
+static void CharacterLowerUpper(Thread* self,
+ ShadowFrame* shadow_frame,
+ JValue* result,
+ size_t arg_offset,
+ bool to_lower_case) SHARED_REQUIRES(Locks::mutator_lock_) {
+ uint32_t int_value = static_cast<uint32_t>(shadow_frame->GetVReg(arg_offset));
+
+ // Only ASCII (7-bit).
+ if (!isascii(int_value)) {
+ AbortTransactionOrFail(self,
+ "Only support ASCII characters for toLowerCase/toUpperCase: %u",
+ int_value);
+ return;
+ }
+
+ std::locale c_locale("C");
+ char char_value = static_cast<char>(int_value);
+
+ if (to_lower_case) {
+ result->SetI(std::tolower(char_value, c_locale));
+ } else {
+ result->SetI(std::toupper(char_value, c_locale));
+ }
+}
+
+void UnstartedRuntime::UnstartedCharacterToLowerCase(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ CharacterLowerUpper(self, shadow_frame, result, arg_offset, true);
+}
+
+void UnstartedRuntime::UnstartedCharacterToUpperCase(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ CharacterLowerUpper(self, shadow_frame, result, arg_offset, false);
+}
+
// Helper function to deal with class loading in an unstarted runtime.
static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> className,
Handle<mirror::ClassLoader> class_loader, JValue* result,
#include "unstarted_runtime.h"
#include <limits>
+#include <locale>
#include "base/casts.h"
#include "class_linker.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
+#include "transaction.h"
namespace art {
namespace interpreter {
EXPECT_EQ(expect_int64t, result_int64t) << result.GetD() << " vs " << test_pairs[i][1];
}
}
+
+ // Prepare for aborts. Aborts assume that the exception class is already resolved, as the
+ // loading code doesn't work under transactions.
+ void PrepareForAborts() SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* result = Runtime::Current()->GetClassLinker()->FindClass(
+ Thread::Current(),
+ Transaction::kAbortExceptionSignature,
+ ScopedNullHandle<mirror::ClassLoader>());
+ CHECK(result != nullptr);
+ }
};
TEST_F(UnstartedRuntimeTest, MemoryPeekByte) {
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
+TEST_F(UnstartedRuntimeTest, ToLowerUpper) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ std::locale c_locale("C");
+
+ // Check ASCII.
+ for (uint32_t i = 0; i < 128; ++i) {
+ bool c_upper = std::isupper(static_cast<char>(i), c_locale);
+ bool c_lower = std::islower(static_cast<char>(i), c_locale);
+ EXPECT_FALSE(c_upper && c_lower) << i;
+
+ // Check toLowerCase.
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+ ASSERT_FALSE(self->IsExceptionPending());
+ uint32_t lower_result = static_cast<uint32_t>(result.GetI());
+ if (c_lower) {
+ EXPECT_EQ(i, lower_result);
+ } else if (c_upper) {
+ EXPECT_EQ(static_cast<uint32_t>(std::tolower(static_cast<char>(i), c_locale)),
+ lower_result);
+ } else {
+ EXPECT_EQ(i, lower_result);
+ }
+ }
+
+ // Check toUpperCase.
+ {
+ JValue result2;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ UnstartedCharacterToUpperCase(self, tmp, &result2, 0);
+ ASSERT_FALSE(self->IsExceptionPending());
+ uint32_t upper_result = static_cast<uint32_t>(result2.GetI());
+ if (c_upper) {
+ EXPECT_EQ(i, upper_result);
+ } else if (c_lower) {
+ EXPECT_EQ(static_cast<uint32_t>(std::toupper(static_cast<char>(i), c_locale)),
+ upper_result);
+ } else {
+ EXPECT_EQ(i, upper_result);
+ }
+ }
+ }
+
+ // Check abort for other things. Can't test all.
+
+ PrepareForAborts();
+
+ for (uint32_t i = 128; i < 256; ++i) {
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedCharacterToUpperCase(self, tmp, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ }
+ for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) {
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedCharacterToUpperCase(self, tmp, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ }
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
} // namespace interpreter
} // namespace art