You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1375 lines
50 KiB
1375 lines
50 KiB
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "unstarted_runtime.h"
|
|
|
|
#include <limits>
|
|
#include <locale>
|
|
|
|
#include "base/casts.h"
|
|
#include "base/enums.h"
|
|
#include "base/memory_tool.h"
|
|
#include "class_linker.h"
|
|
#include "class_root-inl.h"
|
|
#include "common_runtime_test.h"
|
|
#include "dex/descriptors_names.h"
|
|
#include "dex/dex_instruction.h"
|
|
#include "handle.h"
|
|
#include "handle_scope-inl.h"
|
|
#include "interpreter/interpreter_common.h"
|
|
#include "mirror/array-alloc-inl.h"
|
|
#include "mirror/class-alloc-inl.h"
|
|
#include "mirror/class_loader.h"
|
|
#include "mirror/object-inl.h"
|
|
#include "mirror/object_array-alloc-inl.h"
|
|
#include "mirror/object_array-inl.h"
|
|
#include "mirror/string-inl.h"
|
|
#include "runtime.h"
|
|
#include "scoped_thread_state_change-inl.h"
|
|
#include "shadow_frame-inl.h"
|
|
#include "thread.h"
|
|
#include "transaction.h"
|
|
#include "unstarted_runtime_list.h"
|
|
|
|
namespace art {
|
|
namespace interpreter {
|
|
|
|
// Deleter to be used with ShadowFrame::CreateDeoptimizedFrame objects.
|
|
struct DeoptShadowFrameDelete {
|
|
// NOTE: Deleting a const object is valid but free() takes a non-const pointer.
|
|
void operator()(ShadowFrame* ptr) const {
|
|
if (ptr != nullptr) {
|
|
ShadowFrame::DeleteDeoptimizedFrame(ptr);
|
|
}
|
|
}
|
|
};
|
|
// Alias for std::unique_ptr<> that uses the above deleter.
|
|
using UniqueDeoptShadowFramePtr = std::unique_ptr<ShadowFrame, DeoptShadowFrameDelete>;
|
|
|
|
class UnstartedRuntimeTest : public CommonRuntimeTest {
|
|
protected:
|
|
// Re-expose all UnstartedRuntime implementations so we don't need to declare a million
|
|
// test friends.
|
|
|
|
// Methods that intercept available libcore implementations.
|
|
#define UNSTARTED_DIRECT(Name, DescriptorIgnored, NameIgnored, SignatureIgnored) \
|
|
static void Unstarted ## Name(Thread* self, \
|
|
ShadowFrame* shadow_frame, \
|
|
JValue* result, \
|
|
size_t arg_offset) \
|
|
REQUIRES_SHARED(Locks::mutator_lock_) { \
|
|
interpreter::UnstartedRuntime::Unstarted ## Name(self, shadow_frame, result, arg_offset); \
|
|
}
|
|
UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT)
|
|
#undef UNSTARTED_DIRECT
|
|
|
|
// Methods that are native.
|
|
#define UNSTARTED_JNI(Name, DescriptorIgnored, NameIgnored, SignatureIgnored) \
|
|
static void UnstartedJNI ## Name(Thread* self, \
|
|
ArtMethod* method, \
|
|
mirror::Object* receiver, \
|
|
uint32_t* args, \
|
|
JValue* result) \
|
|
REQUIRES_SHARED(Locks::mutator_lock_) { \
|
|
interpreter::UnstartedRuntime::UnstartedJNI ## Name(self, method, receiver, args, result); \
|
|
}
|
|
UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI)
|
|
#undef UNSTARTED_JNI
|
|
|
|
UniqueDeoptShadowFramePtr CreateShadowFrame(uint32_t num_vregs,
|
|
ShadowFrame* link,
|
|
ArtMethod* method,
|
|
uint32_t dex_pc) {
|
|
return UniqueDeoptShadowFramePtr(
|
|
ShadowFrame::CreateDeoptimizedFrame(num_vregs, link, method, dex_pc));
|
|
}
|
|
|
|
// Helpers for ArrayCopy.
|
|
//
|
|
// Note: as we have to use handles, we use StackHandleScope to transfer data. Hardcode a size
|
|
// of three everywhere. That is enough to test all cases.
|
|
|
|
static ObjPtr<mirror::ObjectArray<mirror::Object>> CreateObjectArray(
|
|
Thread* self,
|
|
ObjPtr<mirror::Class> component_type,
|
|
const StackHandleScope<3>& data)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
Runtime* runtime = Runtime::Current();
|
|
ObjPtr<mirror::Class> array_type =
|
|
runtime->GetClassLinker()->FindArrayClass(self, component_type);
|
|
CHECK(array_type != nullptr);
|
|
ObjPtr<mirror::ObjectArray<mirror::Object>> result =
|
|
mirror::ObjectArray<mirror::Object>::Alloc(self, array_type, 3);
|
|
CHECK(result != nullptr);
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
result->Set(static_cast<int32_t>(i), data.GetReference(i));
|
|
CHECK(!self->IsExceptionPending());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void CheckObjectArray(ObjPtr<mirror::ObjectArray<mirror::Object>> array,
|
|
const StackHandleScope<3>& data)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
CHECK_EQ(array->GetLength(), 3);
|
|
CHECK_EQ(data.NumberOfReferences(), 3U);
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
EXPECT_OBJ_PTR_EQ(data.GetReference(i), array->Get(static_cast<int32_t>(i))) << i;
|
|
}
|
|
}
|
|
|
|
void RunArrayCopy(Thread* self,
|
|
ShadowFrame* tmp,
|
|
bool expect_exception,
|
|
ObjPtr<mirror::ObjectArray<mirror::Object>> src,
|
|
int32_t src_pos,
|
|
ObjPtr<mirror::ObjectArray<mirror::Object>> dst,
|
|
int32_t dst_pos,
|
|
int32_t length)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
JValue result;
|
|
tmp->SetVRegReference(0, src);
|
|
tmp->SetVReg(1, src_pos);
|
|
tmp->SetVRegReference(2, dst);
|
|
tmp->SetVReg(3, dst_pos);
|
|
tmp->SetVReg(4, length);
|
|
UnstartedSystemArraycopy(self, tmp, &result, 0);
|
|
bool exception_pending = self->IsExceptionPending();
|
|
EXPECT_EQ(exception_pending, expect_exception);
|
|
if (exception_pending) {
|
|
self->ClearException();
|
|
}
|
|
}
|
|
|
|
void RunArrayCopy(Thread* self,
|
|
ShadowFrame* tmp,
|
|
bool expect_exception,
|
|
ObjPtr<mirror::Class> src_component_class,
|
|
ObjPtr<mirror::Class> dst_component_class,
|
|
const StackHandleScope<3>& src_data,
|
|
int32_t src_pos,
|
|
const StackHandleScope<3>& dst_data,
|
|
int32_t dst_pos,
|
|
int32_t length,
|
|
const StackHandleScope<3>& expected_result)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
StackHandleScope<3> hs_misc(self);
|
|
Handle<mirror::Class> dst_component_handle(hs_misc.NewHandle(dst_component_class));
|
|
|
|
Handle<mirror::ObjectArray<mirror::Object>> src_handle(
|
|
hs_misc.NewHandle(CreateObjectArray(self, src_component_class, src_data)));
|
|
|
|
Handle<mirror::ObjectArray<mirror::Object>> dst_handle(
|
|
hs_misc.NewHandle(CreateObjectArray(self, dst_component_handle.Get(), dst_data)));
|
|
|
|
RunArrayCopy(self,
|
|
tmp,
|
|
expect_exception,
|
|
src_handle.Get(),
|
|
src_pos,
|
|
dst_handle.Get(),
|
|
dst_pos,
|
|
length);
|
|
CheckObjectArray(dst_handle.Get(), expected_result);
|
|
}
|
|
|
|
void TestCeilFloor(bool ceil,
|
|
Thread* self,
|
|
ShadowFrame* tmp,
|
|
double const test_pairs[][2],
|
|
size_t num_pairs)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
for (size_t i = 0; i < num_pairs; ++i) {
|
|
tmp->SetVRegDouble(0, test_pairs[i][0]);
|
|
|
|
JValue result;
|
|
if (ceil) {
|
|
UnstartedMathCeil(self, tmp, &result, 0);
|
|
} else {
|
|
UnstartedMathFloor(self, tmp, &result, 0);
|
|
}
|
|
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
|
|
// We want precise results.
|
|
int64_t result_int64t = bit_cast<int64_t, double>(result.GetD());
|
|
int64_t expect_int64t = bit_cast<int64_t, double>(test_pairs[i][1]);
|
|
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() REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
ObjPtr<mirror::Object> result = Runtime::Current()->GetClassLinker()->FindClass(
|
|
Thread::Current(),
|
|
Transaction::kAbortExceptionDescriptor,
|
|
ScopedNullHandle<mirror::ClassLoader>());
|
|
CHECK(result != nullptr);
|
|
}
|
|
};
|
|
|
|
TEST_F(UnstartedRuntimeTest, MemoryPeekByte) {
|
|
Thread* self = Thread::Current();
|
|
|
|
ScopedObjectAccess soa(self);
|
|
constexpr const uint8_t base_array[] = "abcdefghijklmnop";
|
|
constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
|
|
const uint8_t* base_ptr = base_array;
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
for (int32_t i = 0; i < kBaseLen; ++i) {
|
|
tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
|
|
|
|
UnstartedMemoryPeekByte(self, tmp.get(), &result, 0);
|
|
|
|
EXPECT_EQ(result.GetB(), static_cast<int8_t>(base_array[i]));
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, MemoryPeekShort) {
|
|
Thread* self = Thread::Current();
|
|
|
|
ScopedObjectAccess soa(self);
|
|
constexpr const uint8_t base_array[] = "abcdefghijklmnop";
|
|
constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
|
|
const uint8_t* base_ptr = base_array;
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
int32_t adjusted_length = kBaseLen - sizeof(int16_t);
|
|
for (int32_t i = 0; i < adjusted_length; ++i) {
|
|
tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
|
|
|
|
UnstartedMemoryPeekShort(self, tmp.get(), &result, 0);
|
|
|
|
using unaligned_short __attribute__((__aligned__(1))) = int16_t;
|
|
const unaligned_short* short_ptr = reinterpret_cast<const unaligned_short*>(base_ptr + i);
|
|
EXPECT_EQ(result.GetS(), *short_ptr);
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, MemoryPeekInt) {
|
|
Thread* self = Thread::Current();
|
|
|
|
ScopedObjectAccess soa(self);
|
|
constexpr const uint8_t base_array[] = "abcdefghijklmnop";
|
|
constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
|
|
const uint8_t* base_ptr = base_array;
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
int32_t adjusted_length = kBaseLen - sizeof(int32_t);
|
|
for (int32_t i = 0; i < adjusted_length; ++i) {
|
|
tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
|
|
|
|
UnstartedMemoryPeekInt(self, tmp.get(), &result, 0);
|
|
|
|
using unaligned_int __attribute__((__aligned__(1))) = int32_t;
|
|
const unaligned_int* int_ptr = reinterpret_cast<const unaligned_int*>(base_ptr + i);
|
|
EXPECT_EQ(result.GetI(), *int_ptr);
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, MemoryPeekLong) {
|
|
Thread* self = Thread::Current();
|
|
|
|
ScopedObjectAccess soa(self);
|
|
constexpr const uint8_t base_array[] = "abcdefghijklmnop";
|
|
constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
|
|
const uint8_t* base_ptr = base_array;
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
int32_t adjusted_length = kBaseLen - sizeof(int64_t);
|
|
for (int32_t i = 0; i < adjusted_length; ++i) {
|
|
tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
|
|
|
|
UnstartedMemoryPeekLong(self, tmp.get(), &result, 0);
|
|
|
|
using unaligned_long __attribute__((__aligned__(1))) = int64_t;
|
|
const unaligned_long* long_ptr = reinterpret_cast<const unaligned_long*>(base_ptr + i);
|
|
EXPECT_EQ(result.GetJ(), *long_ptr);
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, StringGetCharsNoCheck) {
|
|
Thread* self = Thread::Current();
|
|
|
|
ScopedObjectAccess soa(self);
|
|
StackHandleScope<2> hs(self);
|
|
// TODO: Actual UTF.
|
|
constexpr const char base_string[] = "abcdefghijklmnop";
|
|
Handle<mirror::String> h_test_string(hs.NewHandle(
|
|
mirror::String::AllocFromModifiedUtf8(self, base_string)));
|
|
constexpr int32_t kBaseLen = sizeof(base_string) / sizeof(char) - 1;
|
|
Handle<mirror::CharArray> h_char_array(hs.NewHandle(
|
|
mirror::CharArray::Alloc(self, kBaseLen)));
|
|
// A buffer so we can make sure we only modify the elements targetted.
|
|
uint16_t buf[kBaseLen];
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
for (int32_t start_index = 0; start_index < kBaseLen; ++start_index) {
|
|
for (int32_t count = 0; count <= kBaseLen; ++count) {
|
|
for (int32_t trg_offset = 0; trg_offset < kBaseLen; ++trg_offset) {
|
|
// Only do it when in bounds.
|
|
if (start_index + count <= kBaseLen && trg_offset + count <= kBaseLen) {
|
|
tmp->SetVRegReference(0, h_test_string.Get());
|
|
tmp->SetVReg(1, start_index);
|
|
tmp->SetVReg(2, count);
|
|
tmp->SetVRegReference(3, h_char_array.Get());
|
|
tmp->SetVReg(3, trg_offset);
|
|
|
|
// Copy the char_array into buf.
|
|
memcpy(buf, h_char_array->GetData(), kBaseLen * sizeof(uint16_t));
|
|
|
|
UnstartedStringCharAt(self, tmp.get(), &result, 0);
|
|
|
|
uint16_t* data = h_char_array->GetData();
|
|
|
|
bool success = true;
|
|
|
|
// First segment should be unchanged.
|
|
for (int32_t i = 0; i < trg_offset; ++i) {
|
|
success = success && (data[i] == buf[i]);
|
|
}
|
|
// Second segment should be a copy.
|
|
for (int32_t i = trg_offset; i < trg_offset + count; ++i) {
|
|
success = success && (data[i] == buf[i - trg_offset + start_index]);
|
|
}
|
|
// Third segment should be unchanged.
|
|
for (int32_t i = trg_offset + count; i < kBaseLen; ++i) {
|
|
success = success && (data[i] == buf[i]);
|
|
}
|
|
|
|
EXPECT_TRUE(success);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, StringCharAt) {
|
|
Thread* self = Thread::Current();
|
|
|
|
ScopedObjectAccess soa(self);
|
|
// TODO: Actual UTF.
|
|
constexpr const char* base_string = "abcdefghijklmnop";
|
|
int32_t base_len = static_cast<int32_t>(strlen(base_string));
|
|
ObjPtr<mirror::String> test_string = mirror::String::AllocFromModifiedUtf8(self, base_string);
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
for (int32_t i = 0; i < base_len; ++i) {
|
|
tmp->SetVRegReference(0, test_string);
|
|
tmp->SetVReg(1, i);
|
|
|
|
UnstartedStringCharAt(self, tmp.get(), &result, 0);
|
|
|
|
EXPECT_EQ(result.GetI(), base_string[i]);
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, StringInit) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
ObjPtr<mirror::Class> klass = GetClassRoot<mirror::String>();
|
|
ArtMethod* method =
|
|
klass->FindConstructor("(Ljava/lang/String;)V",
|
|
Runtime::Current()->GetClassLinker()->GetImagePointerSize());
|
|
ASSERT_TRUE(method != nullptr);
|
|
|
|
// create instruction data for invoke-direct {v0, v1} of method with fake index
|
|
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, method, 0);
|
|
const char* base_string = "hello_world";
|
|
StackHandleScope<2> hs(self);
|
|
Handle<mirror::String> string_arg =
|
|
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, base_string));
|
|
Handle<mirror::String> reference_empty_string =
|
|
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, ""));
|
|
shadow_frame->SetVRegReference(0, reference_empty_string.Get());
|
|
shadow_frame->SetVRegReference(1, string_arg.Get());
|
|
|
|
interpreter::DoCall<false, false>(method,
|
|
self,
|
|
*shadow_frame,
|
|
Instruction::At(inst_data),
|
|
inst_data[0],
|
|
&result);
|
|
ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL());
|
|
EXPECT_EQ(string_arg->GetLength(), string_result->GetLength());
|
|
|
|
if (string_arg->IsCompressed() && string_result->IsCompressed()) {
|
|
EXPECT_EQ(memcmp(string_arg->GetValueCompressed(), string_result->GetValueCompressed(),
|
|
string_arg->GetLength() * sizeof(uint8_t)), 0);
|
|
} else if (!string_arg->IsCompressed() && !string_result->IsCompressed()) {
|
|
EXPECT_EQ(memcmp(string_arg->GetValue(), string_result->GetValue(),
|
|
string_arg->GetLength() * sizeof(uint16_t)), 0);
|
|
} else {
|
|
bool equal = true;
|
|
for (int i = 0; i < string_arg->GetLength(); ++i) {
|
|
if (string_arg->CharAt(i) != string_result->CharAt(i)) {
|
|
equal = false;
|
|
break;
|
|
}
|
|
}
|
|
EXPECT_EQ(equal, true);
|
|
}
|
|
}
|
|
|
|
// Tests the exceptions that should be checked before modifying the destination.
|
|
// (Doesn't check the object vs primitive case ATM.)
|
|
TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTestExceptions) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
// Note: all tests are not GC safe. Assume there's no GC running here with the few objects we
|
|
// allocate.
|
|
StackHandleScope<3> hs_misc(self);
|
|
Handle<mirror::Class> object_class(hs_misc.NewHandle(GetClassRoot<mirror::Object>()));
|
|
|
|
StackHandleScope<3> hs_data(self);
|
|
hs_data.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1"));
|
|
hs_data.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "2"));
|
|
hs_data.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "3"));
|
|
|
|
Handle<mirror::ObjectArray<mirror::Object>> array(
|
|
hs_misc.NewHandle(CreateObjectArray(self, object_class.Get(), hs_data)));
|
|
|
|
RunArrayCopy(self, tmp.get(), true, array.Get(), -1, array.Get(), 0, 0);
|
|
RunArrayCopy(self, tmp.get(), true, array.Get(), 0, array.Get(), -1, 0);
|
|
RunArrayCopy(self, tmp.get(), true, array.Get(), 0, array.Get(), 0, -1);
|
|
RunArrayCopy(self, tmp.get(), true, array.Get(), 0, array.Get(), 0, 4);
|
|
RunArrayCopy(self, tmp.get(), true, array.Get(), 0, array.Get(), 1, 3);
|
|
RunArrayCopy(self, tmp.get(), true, array.Get(), 1, array.Get(), 0, 3);
|
|
|
|
Handle<mirror::ObjectArray<mirror::Object>> class_as_array =
|
|
hs_misc.NewHandle(reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(object_class.Get()));
|
|
RunArrayCopy(self, tmp.get(), true, class_as_array.Get(), 0, array.Get(), 0, 0);
|
|
RunArrayCopy(self, tmp.get(), true, array.Get(), 0, class_as_array.Get(), 0, 0);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, SystemArrayCopyObjectArrayTest) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
StackHandleScope<1> hs_object(self);
|
|
Handle<mirror::Class> object_class(hs_object.NewHandle(GetClassRoot<mirror::Object>()));
|
|
|
|
// Simple test:
|
|
// [1,2,3]{1 @ 2} into [4,5,6] = [4,2,6]
|
|
{
|
|
StackHandleScope<3> hs_src(self);
|
|
hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1"));
|
|
hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "2"));
|
|
hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "3"));
|
|
|
|
StackHandleScope<3> hs_dst(self);
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "4"));
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "5"));
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "6"));
|
|
|
|
StackHandleScope<3> hs_expected(self);
|
|
hs_expected.NewHandle(hs_dst.GetReference(0));
|
|
hs_expected.NewHandle(hs_dst.GetReference(1));
|
|
hs_expected.NewHandle(hs_src.GetReference(1));
|
|
|
|
RunArrayCopy(self,
|
|
tmp.get(),
|
|
false,
|
|
object_class.Get(),
|
|
object_class.Get(),
|
|
hs_src,
|
|
1,
|
|
hs_dst,
|
|
2,
|
|
1,
|
|
hs_expected);
|
|
}
|
|
|
|
// Simple test:
|
|
// [1,2,3]{1 @ 1} into [4,5,6] = [4,2,6] (with dst String[])
|
|
{
|
|
StackHandleScope<3> hs_src(self);
|
|
hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1"));
|
|
hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "2"));
|
|
hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "3"));
|
|
|
|
StackHandleScope<3> hs_dst(self);
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "4"));
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "5"));
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "6"));
|
|
|
|
StackHandleScope<3> hs_expected(self);
|
|
hs_expected.NewHandle(hs_dst.GetReference(0));
|
|
hs_expected.NewHandle(hs_src.GetReference(1));
|
|
hs_expected.NewHandle(hs_dst.GetReference(2));
|
|
|
|
RunArrayCopy(self,
|
|
tmp.get(),
|
|
false,
|
|
object_class.Get(),
|
|
GetClassRoot<mirror::String>(),
|
|
hs_src,
|
|
1,
|
|
hs_dst,
|
|
1,
|
|
1,
|
|
hs_expected);
|
|
}
|
|
|
|
// Simple test:
|
|
// [1,*,3] into [4,5,6] = [1,5,6] + exc
|
|
{
|
|
StackHandleScope<3> hs_src(self);
|
|
hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1"));
|
|
hs_src.NewHandle(GetClassRoot<mirror::String>());
|
|
hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "3"));
|
|
|
|
StackHandleScope<3> hs_dst(self);
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "4"));
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "5"));
|
|
hs_dst.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "6"));
|
|
|
|
StackHandleScope<3> hs_expected(self);
|
|
hs_expected.NewHandle(hs_src.GetReference(0));
|
|
hs_expected.NewHandle(hs_dst.GetReference(1));
|
|
hs_expected.NewHandle(hs_dst.GetReference(2));
|
|
|
|
RunArrayCopy(self,
|
|
tmp.get(),
|
|
true,
|
|
object_class.Get(),
|
|
GetClassRoot<mirror::String>(),
|
|
hs_src,
|
|
0,
|
|
hs_dst,
|
|
0,
|
|
3,
|
|
hs_expected);
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, IntegerParseIntTest) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
// Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
|
|
// suffixes).
|
|
constexpr const char* test_string = "-2147483646";
|
|
constexpr int32_t test_values[] = {
|
|
6,
|
|
46,
|
|
646,
|
|
3646,
|
|
83646,
|
|
483646,
|
|
7483646,
|
|
47483646,
|
|
147483646,
|
|
2147483646,
|
|
-2147483646
|
|
};
|
|
|
|
static_assert(arraysize(test_values) == 11U, "test_values");
|
|
CHECK_EQ(strlen(test_string), 11U);
|
|
|
|
for (size_t i = 0; i <= 10; ++i) {
|
|
const char* test_value = &test_string[10 - i];
|
|
|
|
StackHandleScope<1> hs_str(self);
|
|
Handle<mirror::String> h_str(
|
|
hs_str.NewHandle(mirror::String::AllocFromModifiedUtf8(self, test_value)));
|
|
ASSERT_NE(h_str.Get(), nullptr);
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
|
|
tmp->SetVRegReference(0, h_str.Get());
|
|
|
|
JValue result;
|
|
UnstartedIntegerParseInt(self, tmp.get(), &result, 0);
|
|
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
EXPECT_EQ(result.GetI(), test_values[i]);
|
|
}
|
|
}
|
|
|
|
// Right now the same as Integer.Parse
|
|
TEST_F(UnstartedRuntimeTest, LongParseLongTest) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
// Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
|
|
// suffixes).
|
|
constexpr const char* test_string = "-2147483646";
|
|
constexpr int64_t test_values[] = {
|
|
6,
|
|
46,
|
|
646,
|
|
3646,
|
|
83646,
|
|
483646,
|
|
7483646,
|
|
47483646,
|
|
147483646,
|
|
2147483646,
|
|
-2147483646
|
|
};
|
|
|
|
static_assert(arraysize(test_values) == 11U, "test_values");
|
|
CHECK_EQ(strlen(test_string), 11U);
|
|
|
|
for (size_t i = 0; i <= 10; ++i) {
|
|
const char* test_value = &test_string[10 - i];
|
|
|
|
StackHandleScope<1> hs_str(self);
|
|
Handle<mirror::String> h_str(
|
|
hs_str.NewHandle(mirror::String::AllocFromModifiedUtf8(self, test_value)));
|
|
ASSERT_NE(h_str.Get(), nullptr);
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
|
|
tmp->SetVRegReference(0, h_str.Get());
|
|
|
|
JValue result;
|
|
UnstartedLongParseLong(self, tmp.get(), &result, 0);
|
|
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
EXPECT_EQ(result.GetJ(), test_values[i]);
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, Ceil) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
|
constexpr double inf = std::numeric_limits<double>::infinity();
|
|
constexpr double ld1 = static_cast<double>((UINT64_C(1) << 53) - 1);
|
|
constexpr double ld2 = static_cast<double>(UINT64_C(1) << 55);
|
|
constexpr double test_pairs[][2] = {
|
|
{ -0.0, -0.0 },
|
|
{ 0.0, 0.0 },
|
|
{ -0.5, -0.0 },
|
|
{ -1.0, -1.0 },
|
|
{ 0.5, 1.0 },
|
|
{ 1.0, 1.0 },
|
|
{ nan, nan },
|
|
{ inf, inf },
|
|
{ -inf, -inf },
|
|
{ ld1, ld1 },
|
|
{ ld2, ld2 }
|
|
};
|
|
|
|
TestCeilFloor(/* ceil= */ true, self, tmp.get(), test_pairs, arraysize(test_pairs));
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, Floor) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
|
constexpr double inf = std::numeric_limits<double>::infinity();
|
|
constexpr double ld1 = static_cast<double>((UINT64_C(1) << 53) - 1);
|
|
constexpr double ld2 = static_cast<double>(UINT64_C(1) << 55);
|
|
constexpr double test_pairs[][2] = {
|
|
{ -0.0, -0.0 },
|
|
{ 0.0, 0.0 },
|
|
{ -0.5, -1.0 },
|
|
{ -1.0, -1.0 },
|
|
{ 0.5, 0.0 },
|
|
{ 1.0, 1.0 },
|
|
{ nan, nan },
|
|
{ inf, inf },
|
|
{ -inf, -inf },
|
|
{ ld1, ld1 },
|
|
{ ld2, ld2 }
|
|
};
|
|
|
|
TestCeilFloor(/* ceil= */ false, self, tmp.get(), test_pairs, arraysize(test_pairs));
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, ToLowerUpper) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(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.get(), &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.get(), &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));
|
|
EnterTransactionMode();
|
|
UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0);
|
|
ASSERT_TRUE(IsTransactionAborted());
|
|
ExitTransactionMode();
|
|
ASSERT_TRUE(self->IsExceptionPending());
|
|
}
|
|
{
|
|
JValue result;
|
|
tmp->SetVReg(0, static_cast<int32_t>(i));
|
|
EnterTransactionMode();
|
|
UnstartedCharacterToUpperCase(self, tmp.get(), &result, 0);
|
|
ASSERT_TRUE(IsTransactionAborted());
|
|
ExitTransactionMode();
|
|
ASSERT_TRUE(self->IsExceptionPending());
|
|
}
|
|
}
|
|
for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) {
|
|
{
|
|
JValue result;
|
|
tmp->SetVReg(0, static_cast<int32_t>(i));
|
|
EnterTransactionMode();
|
|
UnstartedCharacterToLowerCase(self, tmp.get(), &result, 0);
|
|
ASSERT_TRUE(IsTransactionAborted());
|
|
ExitTransactionMode();
|
|
ASSERT_TRUE(self->IsExceptionPending());
|
|
}
|
|
{
|
|
JValue result;
|
|
tmp->SetVReg(0, static_cast<int32_t>(i));
|
|
EnterTransactionMode();
|
|
UnstartedCharacterToUpperCase(self, tmp.get(), &result, 0);
|
|
ASSERT_TRUE(IsTransactionAborted());
|
|
ExitTransactionMode();
|
|
ASSERT_TRUE(self->IsExceptionPending());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, Sin) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
// Test an important value, PI/6. That's the one we see in practice.
|
|
constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365);
|
|
tmp->SetVRegLong(0, static_cast<int64_t>(lvalue));
|
|
|
|
JValue result;
|
|
UnstartedMathSin(self, tmp.get(), &result, 0);
|
|
|
|
const uint64_t lresult = static_cast<uint64_t>(result.GetJ());
|
|
EXPECT_EQ(UINT64_C(0x3fdfffffffffffff), lresult);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, Cos) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
// Test an important value, PI/6. That's the one we see in practice.
|
|
constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365);
|
|
tmp->SetVRegLong(0, static_cast<int64_t>(lvalue));
|
|
|
|
JValue result;
|
|
UnstartedMathCos(self, tmp.get(), &result, 0);
|
|
|
|
const uint64_t lresult = static_cast<uint64_t>(result.GetJ());
|
|
EXPECT_EQ(UINT64_C(0x3febb67ae8584cab), lresult);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, Pow) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
// Test an important pair.
|
|
constexpr uint64_t lvalue1 = UINT64_C(0x4079000000000000);
|
|
constexpr uint64_t lvalue2 = UINT64_C(0xbfe6db6dc0000000);
|
|
|
|
tmp->SetVRegLong(0, static_cast<int64_t>(lvalue1));
|
|
tmp->SetVRegLong(2, static_cast<int64_t>(lvalue2));
|
|
|
|
JValue result;
|
|
UnstartedMathPow(self, tmp.get(), &result, 0);
|
|
|
|
const uint64_t lresult = static_cast<uint64_t>(result.GetJ());
|
|
EXPECT_EQ(UINT64_C(0x3f8c5c51326aa7ee), lresult);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, IsAnonymousClass) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
ObjPtr<mirror::Class> class_klass = GetClassRoot<mirror::Class>();
|
|
shadow_frame->SetVRegReference(0, class_klass);
|
|
UnstartedClassIsAnonymousClass(self, shadow_frame.get(), &result, 0);
|
|
EXPECT_EQ(result.GetZ(), 0);
|
|
|
|
jobject class_loader = LoadDex("Nested");
|
|
StackHandleScope<1> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> loader(
|
|
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
ObjPtr<mirror::Class> c = class_linker_->FindClass(soa.Self(), "LNested$1;", loader);
|
|
ASSERT_TRUE(c != nullptr);
|
|
shadow_frame->SetVRegReference(0, c);
|
|
UnstartedClassIsAnonymousClass(self, shadow_frame.get(), &result, 0);
|
|
EXPECT_EQ(result.GetZ(), 1);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, GetDeclaringClass) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
jobject class_loader = LoadDex("Nested");
|
|
StackHandleScope<4> hs(self);
|
|
Handle<mirror::ClassLoader> loader(
|
|
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
|
|
Handle<mirror::Class> nested_klass(hs.NewHandle(
|
|
class_linker_->FindClass(soa.Self(), "LNested;", loader)));
|
|
Handle<mirror::Class> inner_klass(hs.NewHandle(
|
|
class_linker_->FindClass(soa.Self(), "LNested$Inner;", loader)));
|
|
Handle<mirror::Class> anon_klass(hs.NewHandle(
|
|
class_linker_->FindClass(soa.Self(), "LNested$1;", loader)));
|
|
|
|
shadow_frame->SetVRegReference(0, nested_klass.Get());
|
|
UnstartedClassGetDeclaringClass(self, shadow_frame.get(), &result, 0);
|
|
EXPECT_EQ(result.GetL(), nullptr);
|
|
|
|
shadow_frame->SetVRegReference(0, inner_klass.Get());
|
|
UnstartedClassGetDeclaringClass(self, shadow_frame.get(), &result, 0);
|
|
EXPECT_EQ(result.GetL(), nested_klass.Get());
|
|
|
|
shadow_frame->SetVRegReference(0, anon_klass.Get());
|
|
UnstartedClassGetDeclaringClass(self, shadow_frame.get(), &result, 0);
|
|
EXPECT_EQ(result.GetL(), nullptr);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, ThreadLocalGet) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
StackHandleScope<1> hs(self);
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
|
|
// Positive test. See that We get something for float conversion.
|
|
{
|
|
Handle<mirror::Class> floating_decimal = hs.NewHandle(
|
|
class_linker->FindClass(self,
|
|
"Lsun/misc/FloatingDecimal;",
|
|
ScopedNullHandle<mirror::ClassLoader>()));
|
|
ASSERT_TRUE(floating_decimal != nullptr);
|
|
ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true));
|
|
|
|
ArtMethod* caller_method = floating_decimal->FindClassMethod(
|
|
"getBinaryToASCIIBuffer",
|
|
"()Lsun/misc/FloatingDecimal$BinaryToASCIIBuffer;",
|
|
class_linker->GetImagePointerSize());
|
|
// floating_decimal->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
|
|
ASSERT_TRUE(caller_method != nullptr);
|
|
ASSERT_TRUE(caller_method->IsDirect());
|
|
ASSERT_TRUE(caller_method->GetDeclaringClass() == floating_decimal.Get());
|
|
UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, nullptr, caller_method, 0);
|
|
shadow_frame->SetLink(caller_frame.get());
|
|
|
|
UnstartedThreadLocalGet(self, shadow_frame.get(), &result, 0);
|
|
EXPECT_TRUE(result.GetL() != nullptr);
|
|
EXPECT_FALSE(self->IsExceptionPending());
|
|
|
|
shadow_frame->SetLink(nullptr);
|
|
}
|
|
|
|
// Negative test.
|
|
PrepareForAborts();
|
|
|
|
{
|
|
// Just use a method in Class.
|
|
ObjPtr<mirror::Class> class_class = GetClassRoot<mirror::Class>();
|
|
ArtMethod* caller_method =
|
|
&*class_class->GetDeclaredMethods(class_linker->GetImagePointerSize()).begin();
|
|
UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, nullptr, caller_method, 0);
|
|
shadow_frame->SetLink(caller_frame.get());
|
|
|
|
EnterTransactionMode();
|
|
UnstartedThreadLocalGet(self, shadow_frame.get(), &result, 0);
|
|
ASSERT_TRUE(IsTransactionAborted());
|
|
ExitTransactionMode();
|
|
ASSERT_TRUE(self->IsExceptionPending());
|
|
self->ClearException();
|
|
|
|
shadow_frame->SetLink(nullptr);
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, FloatConversion) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
StackHandleScope<1> hs(self);
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
Handle<mirror::Class> double_class = hs.NewHandle(
|
|
class_linker->FindClass(self,
|
|
"Ljava/lang/Double;",
|
|
ScopedNullHandle<mirror::ClassLoader>()));
|
|
ASSERT_TRUE(double_class != nullptr);
|
|
ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true));
|
|
|
|
ArtMethod* method = double_class->FindClassMethod("toString",
|
|
"(D)Ljava/lang/String;",
|
|
class_linker->GetImagePointerSize());
|
|
ASSERT_TRUE(method != nullptr);
|
|
ASSERT_TRUE(method->IsDirect());
|
|
ASSERT_TRUE(method->GetDeclaringClass() == double_class.Get());
|
|
|
|
// create instruction data for invoke-direct {v0, v1} of method with fake index
|
|
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, method, 0);
|
|
|
|
shadow_frame->SetVRegDouble(0, 1.23);
|
|
interpreter::DoCall<false, false>(method,
|
|
self,
|
|
*shadow_frame,
|
|
Instruction::At(inst_data),
|
|
inst_data[0],
|
|
&result);
|
|
ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL());
|
|
ASSERT_TRUE(string_result != nullptr);
|
|
|
|
std::string mod_utf = string_result->ToModifiedUtf8();
|
|
EXPECT_EQ("1.23", mod_utf);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, ThreadCurrentThread) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
StackHandleScope<1> hs(self);
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
Handle<mirror::Class> thread_class = hs.NewHandle(
|
|
class_linker->FindClass(self, "Ljava/lang/Thread;", ScopedNullHandle<mirror::ClassLoader>()));
|
|
ASSERT_TRUE(thread_class.Get() != nullptr);
|
|
ASSERT_TRUE(class_linker->EnsureInitialized(self, thread_class, true, true));
|
|
|
|
// Negative test. In general, currentThread should fail (as we should not leak a peer that will
|
|
// be recreated at runtime).
|
|
PrepareForAborts();
|
|
|
|
{
|
|
EnterTransactionMode();
|
|
UnstartedThreadCurrentThread(self, shadow_frame.get(), &result, 0);
|
|
ASSERT_TRUE(IsTransactionAborted());
|
|
ExitTransactionMode();
|
|
ASSERT_TRUE(self->IsExceptionPending());
|
|
self->ClearException();
|
|
}
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, LogManager) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
StackHandleScope<1> hs(self);
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
Handle<mirror::Class> log_manager_class = hs.NewHandle(
|
|
class_linker->FindClass(self,
|
|
"Ljava/util/logging/LogManager;",
|
|
ScopedNullHandle<mirror::ClassLoader>()));
|
|
ASSERT_TRUE(log_manager_class.Get() != nullptr);
|
|
ASSERT_TRUE(class_linker->EnsureInitialized(self, log_manager_class, true, true));
|
|
}
|
|
|
|
class UnstartedClassForNameTest : public UnstartedRuntimeTest {
|
|
public:
|
|
template <typename T>
|
|
void RunTest(T& runner, bool in_transaction, bool should_succeed) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
// Ensure that Class is initialized.
|
|
{
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
StackHandleScope<1> hs(self);
|
|
Handle<mirror::Class> h_class = hs.NewHandle(GetClassRoot<mirror::Class>());
|
|
CHECK(class_linker->EnsureInitialized(self, h_class, true, true));
|
|
}
|
|
|
|
// A selection of classes from different core classpath components.
|
|
constexpr const char* kTestCases[] = {
|
|
"java.net.CookieManager", // From libcore.
|
|
"dalvik.system.ClassExt", // From libart.
|
|
};
|
|
|
|
if (in_transaction) {
|
|
// For transaction mode, we cannot load any classes, as the pre-fence initialization of
|
|
// classes isn't transactional. Load them ahead of time.
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
for (const char* name : kTestCases) {
|
|
class_linker->FindClass(self,
|
|
DotToDescriptor(name).c_str(),
|
|
ScopedNullHandle<mirror::ClassLoader>());
|
|
CHECK(!self->IsExceptionPending()) << self->GetException()->Dump();
|
|
}
|
|
}
|
|
|
|
if (!should_succeed) {
|
|
// Negative test. In general, currentThread should fail (as we should not leak a peer that will
|
|
// be recreated at runtime).
|
|
PrepareForAborts();
|
|
}
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
for (const char* name : kTestCases) {
|
|
ObjPtr<mirror::String> name_string = mirror::String::AllocFromModifiedUtf8(self, name);
|
|
CHECK(name_string != nullptr);
|
|
|
|
if (in_transaction) {
|
|
EnterTransactionMode();
|
|
}
|
|
CHECK(!self->IsExceptionPending());
|
|
|
|
runner(self, shadow_frame.get(), name_string, &result);
|
|
|
|
if (should_succeed) {
|
|
CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump();
|
|
CHECK(result.GetL() != nullptr) << name;
|
|
} else {
|
|
CHECK(self->IsExceptionPending()) << name;
|
|
if (in_transaction) {
|
|
ASSERT_TRUE(IsTransactionAborted());
|
|
}
|
|
self->ClearException();
|
|
}
|
|
|
|
if (in_transaction) {
|
|
ExitTransactionMode();
|
|
}
|
|
}
|
|
}
|
|
|
|
mirror::ClassLoader* GetBootClassLoader() REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
Thread* self = Thread::Current();
|
|
StackHandleScope<2> hs(self);
|
|
MutableHandle<mirror::ClassLoader> boot_cp = hs.NewHandle<mirror::ClassLoader>(nullptr);
|
|
|
|
{
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
|
|
// Create the fake boot classloader. Any instance is fine, they are technically interchangeable.
|
|
Handle<mirror::Class> boot_cp_class = hs.NewHandle(
|
|
class_linker->FindClass(self,
|
|
"Ljava/lang/BootClassLoader;",
|
|
ScopedNullHandle<mirror::ClassLoader>()));
|
|
CHECK(boot_cp_class != nullptr);
|
|
CHECK(class_linker->EnsureInitialized(self, boot_cp_class, true, true));
|
|
|
|
boot_cp.Assign(boot_cp_class->AllocObject(self)->AsClassLoader());
|
|
CHECK(boot_cp != nullptr);
|
|
|
|
ArtMethod* boot_cp_init = boot_cp_class->FindConstructor(
|
|
"()V", class_linker->GetImagePointerSize());
|
|
CHECK(boot_cp_init != nullptr);
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, boot_cp_init, 0);
|
|
shadow_frame->SetVRegReference(0, boot_cp.Get());
|
|
|
|
// create instruction data for invoke-direct {v0} of method with fake index
|
|
uint16_t inst_data[3] = { 0x1070, 0x0000, 0x0010 };
|
|
|
|
interpreter::DoCall<false, false>(boot_cp_init,
|
|
self,
|
|
*shadow_frame,
|
|
Instruction::At(inst_data),
|
|
inst_data[0],
|
|
&result);
|
|
CHECK(!self->IsExceptionPending());
|
|
}
|
|
|
|
return boot_cp.Get();
|
|
}
|
|
};
|
|
|
|
TEST_F(UnstartedClassForNameTest, ClassForName) {
|
|
auto runner = [](Thread* self,
|
|
ShadowFrame* shadow_frame,
|
|
ObjPtr<mirror::String> name,
|
|
JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
shadow_frame->SetVRegReference(0, name);
|
|
UnstartedClassForName(self, shadow_frame, result, 0);
|
|
};
|
|
RunTest(runner, false, true);
|
|
}
|
|
|
|
TEST_F(UnstartedClassForNameTest, ClassForNameLong) {
|
|
auto runner = [](Thread* self,
|
|
ShadowFrame* shadow_frame,
|
|
ObjPtr<mirror::String> name,
|
|
JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
shadow_frame->SetVRegReference(0, name);
|
|
shadow_frame->SetVReg(1, 0);
|
|
shadow_frame->SetVRegReference(2, nullptr);
|
|
UnstartedClassForNameLong(self, shadow_frame, result, 0);
|
|
};
|
|
RunTest(runner, false, true);
|
|
}
|
|
|
|
TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoader) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
StackHandleScope<1> hs(self);
|
|
Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader());
|
|
|
|
auto runner = [&](Thread* th,
|
|
ShadowFrame* shadow_frame,
|
|
ObjPtr<mirror::String> name,
|
|
JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
shadow_frame->SetVRegReference(0, name);
|
|
shadow_frame->SetVReg(1, 0);
|
|
shadow_frame->SetVRegReference(2, boot_cp.Get());
|
|
UnstartedClassForNameLong(th, shadow_frame, result, 0);
|
|
};
|
|
RunTest(runner, false, true);
|
|
}
|
|
|
|
TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderTransaction) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
StackHandleScope<1> hs(self);
|
|
Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader());
|
|
|
|
auto runner = [&](Thread* th,
|
|
ShadowFrame* shadow_frame,
|
|
ObjPtr<mirror::String> name,
|
|
JValue* result)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
shadow_frame->SetVRegReference(0, name);
|
|
shadow_frame->SetVReg(1, 0);
|
|
shadow_frame->SetVRegReference(2, boot_cp.Get());
|
|
UnstartedClassForNameLong(th, shadow_frame, result, 0);
|
|
};
|
|
RunTest(runner, true, true);
|
|
}
|
|
|
|
TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderFail) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
StackHandleScope<2> hs(self);
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
jobject path_jobj = class_linker->CreatePathClassLoader(self, {});
|
|
ASSERT_TRUE(path_jobj != nullptr);
|
|
Handle<mirror::ClassLoader> path_cp = hs.NewHandle<mirror::ClassLoader>(
|
|
self->DecodeJObject(path_jobj)->AsClassLoader());
|
|
|
|
auto runner = [&](Thread* th,
|
|
ShadowFrame* shadow_frame,
|
|
ObjPtr<mirror::String> name,
|
|
JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
shadow_frame->SetVRegReference(0, name);
|
|
shadow_frame->SetVReg(1, 0);
|
|
shadow_frame->SetVRegReference(2, path_cp.Get());
|
|
UnstartedClassForNameLong(th, shadow_frame, result, 0);
|
|
};
|
|
RunTest(runner, true, false);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, ClassGetSignatureAnnotation) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
StackHandleScope<1> hs(self);
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
Handle<mirror::Class> list_class = hs.NewHandle(
|
|
class_linker->FindClass(self,
|
|
"Ljava/util/List;",
|
|
ScopedNullHandle<mirror::ClassLoader>()));
|
|
ASSERT_TRUE(list_class.Get() != nullptr);
|
|
ASSERT_TRUE(class_linker->EnsureInitialized(self, list_class, true, true));
|
|
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
shadow_frame->SetVRegReference(0, list_class.Get());
|
|
UnstartedClassGetSignatureAnnotation(self, shadow_frame.get(), &result, 0);
|
|
ASSERT_TRUE(result.GetL() != nullptr);
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
|
|
ASSERT_TRUE(result.GetL()->IsObjectArray());
|
|
ObjPtr<mirror::ObjectArray<mirror::Object>> array =
|
|
result.GetL()->AsObjectArray<mirror::Object>();
|
|
std::ostringstream oss;
|
|
for (int32_t i = 0; i != array->GetLength(); ++i) {
|
|
ObjPtr<mirror::Object> elem = array->Get(i);
|
|
ASSERT_TRUE(elem != nullptr);
|
|
ASSERT_TRUE(elem->IsString());
|
|
oss << elem->AsString()->ToModifiedUtf8();
|
|
}
|
|
std::string output_string = oss.str();
|
|
ASSERT_EQ(output_string, "<E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;");
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
|
|
StackHandleScope<4> hs(self);
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
|
|
|
// Get Throwable.
|
|
Handle<mirror::Class> throw_class = hs.NewHandle(GetClassRoot<mirror::Throwable>());
|
|
ASSERT_TRUE(class_linker->EnsureInitialized(self, throw_class, true, true));
|
|
|
|
// Get an input object.
|
|
Handle<mirror::String> input = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "abd"));
|
|
|
|
// Find the constructor.
|
|
PointerSize pointer_size = class_linker->GetImagePointerSize();
|
|
ArtMethod* throw_cons = throw_class->FindConstructor("(Ljava/lang/String;)V", pointer_size);
|
|
ASSERT_TRUE(throw_cons != nullptr);
|
|
Handle<mirror::Constructor> cons = hs.NewHandle((pointer_size == PointerSize::k64)
|
|
? mirror::Constructor::CreateFromArtMethod<PointerSize::k64>(self, throw_cons)
|
|
: mirror::Constructor::CreateFromArtMethod<PointerSize::k32>(self, throw_cons));
|
|
ASSERT_TRUE(cons != nullptr);
|
|
|
|
Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle(
|
|
mirror::ObjectArray<mirror::Object>::Alloc(
|
|
self, GetClassRoot<mirror::ObjectArray<mirror::Object>>(class_linker_), 1));
|
|
ASSERT_TRUE(args != nullptr);
|
|
args->Set(0, input.Get());
|
|
|
|
// OK, we're ready now.
|
|
JValue result;
|
|
UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
shadow_frame->SetVRegReference(0, cons.Get());
|
|
shadow_frame->SetVRegReference(1, args.Get());
|
|
UnstartedConstructorNewInstance0(self, shadow_frame.get(), &result, 0);
|
|
|
|
ASSERT_TRUE(result.GetL() != nullptr);
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
|
|
// Should be a new object.
|
|
ASSERT_NE(result.GetL(), input.Get());
|
|
// Should be of type Throwable.
|
|
ASSERT_OBJ_PTR_EQ(GetClassRoot<mirror::Throwable>(), result.GetL()->GetClass());
|
|
// Should have the right string.
|
|
ObjPtr<mirror::String> result_msg =
|
|
reinterpret_cast<mirror::Throwable*>(result.GetL())->GetDetailMessage();
|
|
EXPECT_OBJ_PTR_EQ(input.Get(), result_msg);
|
|
}
|
|
|
|
TEST_F(UnstartedRuntimeTest, IdentityHashCode) {
|
|
Thread* self = Thread::Current();
|
|
ScopedObjectAccess soa(self);
|
|
UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
|
|
|
|
JValue result;
|
|
UnstartedSystemIdentityHashCode(self, tmp.get(), &result, 0);
|
|
|
|
EXPECT_EQ(0, result.GetI());
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
|
|
ObjPtr<mirror::String> str = mirror::String::AllocFromModifiedUtf8(self, "abd");
|
|
tmp->SetVRegReference(0, str);
|
|
UnstartedSystemIdentityHashCode(self, tmp.get(), &result, 0);
|
|
EXPECT_NE(0, result.GetI());
|
|
EXPECT_EQ(str->IdentityHashCode(), result.GetI());
|
|
ASSERT_FALSE(self->IsExceptionPending());
|
|
}
|
|
|
|
} // namespace interpreter
|
|
} // namespace art
|