/*
* 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 "class_linker.h"
#include "common_runtime_test.h"
#include "dex_instruction.h"
#include "handle.h"
#include "handle_scope-inl.h"
#include "interpreter/interpreter_common.h"
#include "mirror/class_loader.h"
#include "mirror/string-inl.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
namespace art {
namespace interpreter {
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, SigIgnored) \
static void Unstarted ## Name(Thread* self, \
ShadowFrame* shadow_frame, \
JValue* result, \
size_t arg_offset) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
interpreter::UnstartedRuntime::Unstarted ## Name(self, shadow_frame, result, arg_offset); \
}
#include "unstarted_runtime_list.h"
UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT)
#undef UNSTARTED_RUNTIME_DIRECT_LIST
#undef UNSTARTED_RUNTIME_JNI_LIST
#undef UNSTARTED_DIRECT
// Methods that are native.
#define UNSTARTED_JNI(Name, SigIgnored) \
static void UnstartedJNI ## Name(Thread* self, \
ArtMethod* method, \
mirror::Object* receiver, \
uint32_t* args, \
JValue* result) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
interpreter::UnstartedRuntime::UnstartedJNI ## Name(self, method, receiver, args, result); \
}
#include "unstarted_runtime_list.h"
UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI)
#undef UNSTARTED_RUNTIME_DIRECT_LIST
#undef UNSTARTED_RUNTIME_JNI_LIST
#undef UNSTARTED_JNI
};
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;
ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(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, &result, 0);
EXPECT_EQ(result.GetB(), static_cast<int8_t>(base_array[i]));
}
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
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;
ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(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, &result, 0);
typedef int16_t unaligned_short __attribute__ ((aligned (1)));
const unaligned_short* short_ptr = reinterpret_cast<const unaligned_short*>(base_ptr + i);
EXPECT_EQ(result.GetS(), *short_ptr);
}
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
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;
ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(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, &result, 0);
typedef int32_t unaligned_int __attribute__ ((aligned (1)));
const unaligned_int* int_ptr = reinterpret_cast<const unaligned_int*>(base_ptr + i);
EXPECT_EQ(result.GetI(), *int_ptr);
}
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
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;
ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(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, &result, 0);
typedef int64_t unaligned_long __attribute__ ((aligned (1)));
const unaligned_long* long_ptr = reinterpret_cast<const unaligned_long*>(base_ptr + i);
EXPECT_EQ(result.GetJ(), *long_ptr);
}
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
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;
ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(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, &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);
}
}
}
}
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
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));
mirror::String* test_string = mirror::String::AllocFromModifiedUtf8(self, base_string);
JValue result;
ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(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, &result, 0);
EXPECT_EQ(result.GetI(), base_string[i]);
}
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
TEST_F(UnstartedRuntimeTest, StringInit) {
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
mirror::Class* klass = mirror::String::GetJavaLangString();
ArtMethod* method = klass->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V",
sizeof(void*));
// create instruction data for invoke-direct {v0, v1} of method with fake index
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
const Instruction* inst = Instruction::At(inst_data);
JValue result;
ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, method, 0);
const char* base_string = "hello_world";
mirror::String* string_arg = mirror::String::AllocFromModifiedUtf8(self, base_string);
mirror::String* reference_empty_string = mirror::String::AllocFromModifiedUtf8(self, "");
shadow_frame->SetVRegReference(0, reference_empty_string);
shadow_frame->SetVRegReference(1, string_arg);
interpreter::DoCall<false, false>(method, self, *shadow_frame, inst, inst_data[0], &result);
mirror::String* string_result = reinterpret_cast<mirror::String*>(result.GetL());
EXPECT_EQ(string_arg->GetLength(), string_result->GetLength());
EXPECT_EQ(memcmp(string_arg->GetValue(), string_result->GetValue(),
string_arg->GetLength() * sizeof(uint16_t)), 0);
ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
}
} // namespace interpreter
} // namespace art