class StubTest : public CommonRuntimeTest { protected: // We need callee-save methods set up in the Runtime for exceptions. void SetUp() override { // Do the normal setup.
CommonRuntimeTest::SetUp();
{ // Create callee-save methods
ScopedObjectAccess soa(Thread::Current());
runtime_->SetInstructionSet(kRuntimeISA); for (uint32_t i = 0; i < static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType); ++i) {
CalleeSaveType type = CalleeSaveType(i); if (!runtime_->HasCalleeSaveMethod(type)) {
runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(), type);
}
}
}
}
void SetUpRuntimeOptions(RuntimeOptions *options) override { // Use a smaller heap for (std::pair<std::string, constvoid*>& pair : *options) { if (pair.first.find("-Xmx") == 0) {
pair.first = "-Xmx4M"; // Smallest we can go.
}
}
options->push_back(std::make_pair("-Xint", nullptr));
}
// Helper function needed since TEST_F makes a new class.
Thread::tls_ptr_sized_values* GetTlsPtr(Thread* self) { return &self->tlsPtr_;
}
// TODO: Set up a frame according to referrer's specs.
size_t Invoke3WithReferrer(size_t arg0, size_t arg1, size_t arg2, uintptr_t code, Thread* self,
ArtMethod* referrer) { return Invoke3WithReferrerAndHidden(arg0, arg1, arg2, code, self, referrer, 0);
}
// TODO: Set up a frame according to referrer's specs.
size_t Invoke3WithReferrerAndHidden(size_t arg0, size_t arg1, size_t arg2, uintptr_t code,
Thread* self, ArtMethod* referrer, size_t hidden) { // Push a transition back into managed code onto the linked list in thread.
ManagedStack fragment;
ScopedManagedStackFragment smsf(self, &fragment);
size_t result;
size_t fpr_result = 0; #ifdefined(__i386__) // TODO: Set the thread? #define PUSH(reg) "push "# reg "\n\t .cfi_adjust_cfa_offset 4\n\t" #define POP(reg) "pop "# reg "\n\t .cfi_adjust_cfa_offset -4\n\t"
__asm__ __volatile__( "movd %[hidden], %%xmm7\n\t"// This is a memory op, so do this early. If it is off of // esp, then we won't be able to access it after spilling.
// Store the inputs to the stack, but keep the referrer up top, less work.
PUSH(%[referrer]) // Align stack.
PUSH(%[referrer]) // Store referrer
PUSH(%[arg0])
PUSH(%[arg1])
PUSH(%[arg2])
PUSH(%[code]) // Now read them back into the required registers.
POP(%%edi)
POP(%%edx)
POP(%%ecx)
POP(%%eax) // Call is prepared now.
"call *%%edi\n\t"// Call the stub "addl $8, %%esp\n\t"// Pop referrer and padding. ".cfi_adjust_cfa_offset -8\n\t"
: "=a" (result) // Use the result from eax
: [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code),
[referrer]"r"(referrer), [hidden]"m"(hidden) // This places code into edi, arg0 into eax, arg1 into ecx, and arg2 into edx
: "memory", "xmm7"); // clobber. #undef PUSH #undef POP #elifdefined(__arm__)
__asm__ __volatile__( "push {r1-r12, lr}\n\t"// Save state, 13*4B = 52B ".cfi_adjust_cfa_offset 52\n\t" "push {r9}\n\t" ".cfi_adjust_cfa_offset 4\n\t" "mov r9, %[referrer]\n\n" "str r9, [sp, #-8]!\n\t"// Push referrer, +8B padding so 16B aligned ".cfi_adjust_cfa_offset 8\n\t" "ldr r9, [sp, #8]\n\t"
// Push everything on the stack, so we don't rely on the order. What a mess. :-( "sub sp, sp, #24\n\t" "str %[arg0], [sp]\n\t" "str %[arg1], [sp, #4]\n\t" "str %[arg2], [sp, #8]\n\t" "str %[code], [sp, #12]\n\t" "str %[self], [sp, #16]\n\t" "str %[hidden], [sp, #20]\n\t" "ldr r0, [sp]\n\t" "ldr r1, [sp, #4]\n\t" "ldr r2, [sp, #8]\n\t" "ldr r3, [sp, #12]\n\t" "ldr r9, [sp, #16]\n\t" "ldr r12, [sp, #20]\n\t" "add sp, sp, #24\n\t"
"blx r3\n\t"// Call the stub "add sp, sp, #12\n\t"// Pop null and padding ".cfi_adjust_cfa_offset -12\n\t" "pop {r1-r12, lr}\n\t"// Restore state ".cfi_adjust_cfa_offset -52\n\t" "mov %[result], r0\n\t"// Save the result
: [result] "=r" (result) // Use the result from r0
: [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self),
[referrer] "r"(referrer), [hidden] "r"(hidden)
: "r0", "memory"); // clobber. #elifdefined(__aarch64__)
__asm__ __volatile__( // Spill x0-x7 which we say we don't clobber. May contain args. "sub sp, sp, #80\n\t" ".cfi_adjust_cfa_offset 80\n\t" "stp x0, x1, [sp]\n\t" "stp x2, x3, [sp, #16]\n\t" "stp x4, x5, [sp, #32]\n\t" "stp x6, x7, [sp, #48]\n\t" // To be extra defensive, store x20,x21. We do this because some of the stubs might make a // transition into the runtime via the blr instruction below and *not* save x20. "stp x20, x21, [sp, #64]\n\t"
// Push everything on the stack, so we don't rely on the order. What a mess. :-( "sub sp, sp, #48\n\t" ".cfi_adjust_cfa_offset 48\n\t" // All things are "r" constraints, so direct str/stp should work. "stp %[arg0], %[arg1], [sp]\n\t" "stp %[arg2], %[code], [sp, #16]\n\t" "stp %[self], %[hidden], [sp, #32]\n\t"
// End "3:\n\t"
: [result] "=r" (result) // Use the result from r0
: [arg0] "0"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self),
[referrer] "r"(referrer), [hidden] "r"(hidden), [fpr_result] "m" (fpr_result) // X18 is a reserved register, cannot be clobbered. // Leave one register unclobbered, which is needed for compiling with // -fstack-protector-strong. According to AAPCS64 registers x9-x15 are caller-saved, // which means we should unclobber one of the callee-saved registers that are unused. // Here we use x20. // http://b/72613441, Clang 7.0 asks for one more register, so we do not reserve x21.
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x19", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x30", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "memory"); #elifdefined(__x86_64__) && !defined(__APPLE__) #define PUSH(reg) "pushq "# reg "\n\t .cfi_adjust_cfa_offset 8\n\t" #define POP(reg) "popq "# reg "\n\t .cfi_adjust_cfa_offset -8\n\t" // Note: Uses the native convention. We do a callee-save regimen by manually spilling and // restoring almost all registers. // TODO: Set the thread?
__asm__ __volatile__( // Spill almost everything (except rax, rsp). 14 registers.
PUSH(%%rbx)
PUSH(%%rcx)
PUSH(%%rdx)
PUSH(%%rsi)
PUSH(%%rdi)
PUSH(%%rbp)
PUSH(%%r8)
PUSH(%%r9)
PUSH(%%r10)
PUSH(%%r11)
PUSH(%%r12)
PUSH(%%r13)
PUSH(%%r14)
PUSH(%%r15)
: "=a" (result) // Use the result from rax
: [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code),
[referrer] "r"(referrer), [hidden] "r"(hidden) // This places arg0 into rdi, arg1 into rsi, arg2 into rdx, and code into some other // register. We can't use "b" (rbx), as ASAN uses this for the frame pointer.
: "memory"); // We spill and restore (almost) all registers, so only mention memory here. #undef PUSH #undef POP #else
UNUSED(arg0, arg1, arg2, code, referrer, hidden);
LOG(WARNING) << "Was asked to invoke for an architecture I do not understand.";
result = 0; #endif
for (size_t i = 1; i < 4; ++i) {
EXPECT_NE(orig[i], trg[i]);
}
for (size_t i = 4; i < 14; ++i) {
EXPECT_EQ(orig[i], trg[i]);
}
for (size_t i = 14; i < 20; ++i) {
EXPECT_NE(orig[i], trg[i]);
}
// TODO: Test overlapping?
#else
LOG(INFO) << "Skipping memcpy as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping memcpy as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// Force a fat lock by running identity hashcode to fill up lock word.
Handle<mirror::String> obj2(hs.NewHandle(
mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
// Test done. #else
LOG(INFO) << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
class RandGen { public: explicit RandGen(uint32_t seed) : val_(seed) {}
// NO_THREAD_SAFETY_ANALYSIS as we do not want to grab exclusive mutator lock for MonitorInfo. staticvoid TestUnlockObject(StubTest* test) NO_THREAD_SAFETY_ANALYSIS { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__)) static constexpr size_t kThinLockLoops = 100;
Thread* self = Thread::Current();
const uintptr_t art_quick_lock_object = StubTest::GetEntrypoint(self, kQuickLockObject); const uintptr_t art_quick_unlock_object = StubTest::GetEntrypoint(self, kQuickUnlockObject); // Create an object
ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init static constexpr size_t kNumberOfLocks = 10; // Number of objects = lock
StackHandleScope<kNumberOfLocks + 1> hs(self);
Handle<mirror::String> obj(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
LockWord lock = obj->GetLockWord(false);
LockWord::LockState old_state = lock.GetState();
EXPECT_EQ(LockWord::LockState::kUnlocked, old_state);
test->Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U, art_quick_unlock_object, self); // This should be an illegal monitor state.
EXPECT_TRUE(self->IsExceptionPending());
self->ClearException();
// Unlock the remaining count times and then check it's unlocked. Then deallocate. // Go reverse order to correctly handle Handles. for (size_t i = 0; i < kNumberOfLocks; ++i) {
size_t index = kNumberOfLocks - 1 - i;
size_t count = counts[index]; while (count > 0) {
test->Invoke3(reinterpret_cast<size_t>(objects[index].Get()), 0U, 0U, art_quick_unlock_object,
self);
count--;
}
// Test done. #else
UNUSED(test);
LOG(INFO) << "Skipping unlock_object as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping unlock_object as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
TEST_F(StubTest, UnlockObject) { // This will lead to monitor error messages in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
#else
LOG(INFO) << "Skipping check_cast as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping check_cast as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
TEST_F(StubTest, AllocObject) { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__)) // This will lead to OOM error messages in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
// TODO: Check the "Unresolved" allocation stubs
Thread* self = Thread::Current(); // Create an object
ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init
// Array helps to fill memory faster.
Handle<mirror::Class> ca(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")));
// Use arbitrary large amount for now. staticconst size_t kMaxHandles = 1000000;
std::unique_ptr<StackHandleScope<kMaxHandles>> hsp(new StackHandleScope<kMaxHandles>(self));
std::vector<Handle<mirror::Object>> handles; // Start allocating with 128K
size_t length = 128 * KB / 4; while (length > 10) {
Handle<mirror::Object> h(hsp->NewHandle<mirror::Object>(
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), length / 4))); if (self->IsExceptionPending() || h == nullptr) {
self->ClearException();
// Try a smaller length
length = length / 8; // Use at most half the reported free space.
size_t mem = Runtime::Current()->GetHeap()->GetFreeMemory(); if (length * 8 > mem) {
length = mem / 8;
}
} else {
handles.push_back(h);
}
}
LOG(INFO) << "Used " << handles.size() << " arrays to fill space.";
// Allocate simple objects till it fails. while (!self->IsExceptionPending()) {
Handle<mirror::Object> h = hsp->NewHandle(c->AllocObject(soa.Self())); if (!self->IsExceptionPending() && h != nullptr) {
handles.push_back(h);
}
}
self->ClearException();
// Tests done. #else
LOG(INFO) << "Skipping alloc_object as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping alloc_object as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
{ // We can use null in the second argument as we do not need a method here (not used in // resolved/initialized cases)
size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 10U, reinterpret_cast<size_t>(nullptr),
StubTest::GetEntrypoint(self, kQuickAllocArrayResolved32),
self);
EXPECT_FALSE(self->IsExceptionPending()) << mirror::Object::PrettyTypeOf(self->GetException());
EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
ObjPtr<mirror::Object> obj = reinterpret_cast<mirror::Object*>(result);
EXPECT_TRUE(obj->IsArrayInstance());
EXPECT_TRUE(obj->IsObjectArray());
EXPECT_OBJ_PTR_EQ(c.Get(), obj->GetClass());
VerifyObject(obj);
ObjPtr<mirror::Array> array = reinterpret_cast<mirror::Array*>(result);
EXPECT_EQ(array->GetLength(), 10);
}
// Failure tests.
// Out-of-memory.
{
size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()),
GB, // that should fail... reinterpret_cast<size_t>(nullptr),
StubTest::GetEntrypoint(self, kQuickAllocArrayResolved32),
self);
// Tests done. #else
LOG(INFO) << "Skipping alloc_array as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping alloc_array as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
TEST_F(StubTest, StringCompareTo) { // There is no StringCompareTo runtime entrypoint for __arm__ or __aarch64__. #ifdefined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs
ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init
// Create some strings // Use array so we can index into it and use a matrix for expected results // Setup: The first half is standard. The second half uses a non-zero offset. // TODO: Shared backing arrays. constchar* c[] = { "", "", "a", "aa", "ab", "aacaacaacaacaacaac", // This one's under the default limit to go to __memcmp16. "aacaacaacaacaacaacaacaacaacaacaacaac", // This one's over. "aacaacaacaacaacaacaacaacaacaacaacaaca" }; // As is this one. We need a separate one to // defeat object-equal optimizations. static constexpr size_t kStringCount = arraysize(c);
for (size_t i = 0; i < kStringCount; ++i) {
s[i] = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), c[i]));
}
// TODO: wide characters
// Matrix of expectations. First component is first parameter. Note we only check against the // sign, not the value. As we are testing random offsets, we need to compute this and need to // rely on String::CompareTo being correct.
int32_t expected[kStringCount][kStringCount]; for (size_t x = 0; x < kStringCount; ++x) { for (size_t y = 0; y < kStringCount; ++y) {
expected[x][y] = s[x]->CompareTo(s[y].Get());
}
}
// Play with it...
for (size_t x = 0; x < kStringCount; ++x) { for (size_t y = 0; y < kStringCount; ++y) { // Test string_compareto x y
size_t result = Invoke3(reinterpret_cast<size_t>(s[x].Get()), reinterpret_cast<size_t>(s[y].Get()), 0U,
art_quick_string_compareto, self);
// Tests done. #else
LOG(INFO) << "Skipping string_compareto as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping string_compareto as I don't know how to do that on " << kRuntimeISA <<
std::endl; #endif
}
for (size_t i = 0; i < num_values; ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet8Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGetBooleanStatic),
self,
referrer); // Boolean currently stores bools as uint8_t, be more zealous about asserting correct writes/gets.
EXPECT_EQ(values[i], static_cast<uint8_t>(res)) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set_boolean_static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_boolean_static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
} staticvoid GetSetByteStatic(ArtField* f, Thread* self, ArtMethod* referrer,
StubTest* test)
REQUIRES_SHARED(Locks::mutator_lock_) { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__))
int8_t values[] = { -128, -64, 0, 64, 127 };
for (size_t i = 0; i < arraysize(values); ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet8Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGetByteStatic),
self,
referrer);
EXPECT_EQ(values[i], static_cast<int8_t>(res)) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set_byte_static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_byte_static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), 0U,
StubTest::GetEntrypoint(self, kQuickGetByteInstance),
self,
referrer);
EXPECT_EQ(res, static_cast<int8_t>(res2));
} #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping set_byte_instance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_byte_instance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
for (size_t i = 0; i < arraysize(values); ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet16Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGetCharStatic),
self,
referrer);
EXPECT_EQ(values[i], static_cast<uint16_t>(res)) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set_char_static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_char_static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
} staticvoid GetSetShortStatic(ArtField* f, Thread* self,
ArtMethod* referrer, StubTest* test)
REQUIRES_SHARED(Locks::mutator_lock_) { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__))
int16_t values[] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };
for (size_t i = 0; i < arraysize(values); ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet16Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGetShortStatic),
self,
referrer);
EXPECT_EQ(static_cast<int16_t>(res), values[i]) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set_short_static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_short_static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), 0U,
StubTest::GetEntrypoint(self, kQuickGetShortInstance),
self,
referrer);
EXPECT_EQ(res, static_cast<int16_t>(res2));
} #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping set_short_instance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_short_instance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
for (size_t i = 0; i < arraysize(values); ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet32Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGet32Static),
self,
referrer);
EXPECT_EQ(res, values[i]) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set32static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set32static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), 0U,
StubTest::GetEntrypoint(self, kQuickGet32Instance),
self,
referrer);
EXPECT_EQ(res, static_cast<int32_t>(res2));
} #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping set32instance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set32instance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// Allocate a string object for simplicity.
ObjPtr<mirror::String> str = mirror::String::AllocFromModifiedUtf8(self, "Test");
set_and_check_static(f->GetDexFieldIndex(), str, self, referrer, test);
set_and_check_static(f->GetDexFieldIndex(), nullptr, self, referrer, test); #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping setObjstatic as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping setObjstatic as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// Allocate a string object for simplicity.
ObjPtr<mirror::String> str = mirror::String::AllocFromModifiedUtf8(self, "Test");
set_and_check_instance(f, obj->Get(), str, self, referrer, test);
set_and_check_instance(f, obj->Get(), nullptr, self, referrer, test); #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping setObjinstance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping setObjinstance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// TODO: Complete these tests for 32b architectures
for (size_t i = 0; i < arraysize(values); ++i) { // 64 bit FieldSet stores the set value in the second register.
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()),
values[i], 0U,
StubTest::GetEntrypoint(self, kQuickSet64Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGet64Static),
self,
referrer);
EXPECT_EQ(res, values[i]) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set64static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set64static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), 0U,
StubTest::GetEntrypoint(self, kQuickGet64Instance),
self,
referrer);
EXPECT_EQ(res, static_cast<int64_t>(res2));
} #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping set64instance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set64instance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
staticvoid TestFields(Thread* self, StubTest* test, Primitive::Type test_type) { // garbage is created during ClassLinker::Init
ScopedObjectAccess soa(self);
StackHandleScope<3> hs(self);
Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object>(o)));
Handle<mirror::Class> c(hs.NewHandle(obj->GetClass())); // Need a method as a referrer
ArtMethod* m = &c->GetMethods(kRuntimePointerSize)[0];
// Play with it...
// Static fields. for (ArtField& f : c->GetFields()) { if (!f.IsStatic()) { continue;
}
Primitive::Type type = f.GetTypeAsPrimitiveType(); if (test_type != type) { continue;
} switch (type) { case Primitive::Type::kPrimBoolean:
GetSetBooleanStatic(&f, self, m, test); break; case Primitive::Type::kPrimByte:
GetSetByteStatic(&f, self, m, test); break; case Primitive::Type::kPrimChar:
GetSetCharStatic(&f, self, m, test); break; case Primitive::Type::kPrimShort:
GetSetShortStatic(&f, self, m, test); break; case Primitive::Type::kPrimInt:
GetSet32Static(&f, self, m, test); break; case Primitive::Type::kPrimLong:
GetSet64Static(&f, self, m, test); break; case Primitive::Type::kPrimNot: // Don't try array. if (f.GetTypeDescriptor()[0] != '[') {
GetSetObjStatic(&f, self, m, test);
} break; default: break; // Skip.
}
}
// Instance fields. for (ArtField& f : c->GetFields()) { if (f.IsStatic()) { continue;
}
Primitive::Type type = f.GetTypeAsPrimitiveType(); if (test_type != type) { continue;
} switch (type) { case Primitive::Type::kPrimBoolean:
GetSetBooleanInstance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimByte:
GetSetByteInstance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimChar:
GetSetCharInstance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimShort:
GetSetShortInstance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimInt:
GetSet32Instance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimLong:
GetSet64Instance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimNot: // Don't try array. if (f.GetTypeDescriptor()[0] != '[') {
GetSetObjInstance(&obj, &f, self, m, test);
} break; default: break; // Skip.
}
}
// Disabled, b/27991555 . // FIXME: Hacking the entry point to point to art_quick_to_interpreter_bridge is broken. // The bridge calls through to GetCalleeSaveMethodCaller() which looks up the pre-header // and gets a bogus OatQuickMethodHeader* pointing into our assembly code just before // the bridge and uses that to check for inlined frames, crashing in the process.
TEST_F(StubTest, DISABLED_IMT) { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__))
Thread* self = Thread::Current();
// We construct the ImtConflictTable ourselves, as we cannot go into the runtime stub // that will create it: the runtime stub expects to be called by compiled code.
LinearAlloc* linear_alloc = Runtime::Current()->GetLinearAlloc();
ArtMethod* conflict_method = Runtime::Current()->CreateImtConflictMethod(linear_alloc);
ImtConflictTable* empty_conflict_table =
Runtime::Current()->GetClassLinker()->CreateImtConflictTable(/*count=*/0u, linear_alloc); void* data = linear_alloc->Alloc(
self,
ImtConflictTable::ComputeSizeWithOneMoreEntry(empty_conflict_table, kRuntimePointerSize),
LinearAllocKind::kNoGCRoots);
ImtConflictTable* new_table = new (data) ImtConflictTable(
empty_conflict_table, inf_contains, contains_amethod, kRuntimePointerSize);
conflict_method->SetImtConflictTable(new_table, kRuntimePointerSize);
result = Invoke3WithReferrer( static_cast<size_t>(inf_contains->GetDexMethodIndex()), reinterpret_cast<size_t>(array_list.Get()), reinterpret_cast<size_t>(array_list.Get()),
StubTest::GetEntrypoint(self, kQuickInvokeInterfaceTrampolineWithAccessCheck), self,
contains_amethod);
ASSERT_FALSE(self->IsExceptionPending());
EXPECT_EQ(static_cast<size_t>(JNI_FALSE), result); #else
LOG(INFO) << "Skipping imt as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping imt as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
TEST_F(StubTest, StringIndexOf) { #ifdefined(__arm__) || defined(__aarch64__)
Thread* self = Thread::Current();
ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init
// Create some strings // Use array so we can index into it and use a matrix for expected results // Setup: The first half is standard. The second half uses a non-zero offset. // TODO: Shared backing arrays. constchar* c_str[] = { "", "a", "ba", "cba", "dcba", "edcba", "asdfghjkl" }; static constexpr size_t kStringCount = arraysize(c_str); constchar c_char[] = { 'a', 'b', 'c', 'd', 'e' }; static constexpr size_t kCharCount = arraysize(c_char);
for (size_t i = 0; i < kStringCount; ++i) {
s[i] = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), c_str[i]));
}
// Matrix of expectations. First component is first parameter. Note we only check against the // sign, not the value. As we are testing random offsets, we need to compute this and need to // rely on String::CompareTo being correct. static constexpr size_t kMaxLen = 9;
DCHECK_LE(strlen(c_str[kStringCount-1]), kMaxLen) << "Please fix the indexof test.";
// Last dimension: start, offset by 1.
int32_t expected[kStringCount][kCharCount][kMaxLen + 3]; for (size_t x = 0; x < kStringCount; ++x) { for (size_t y = 0; y < kCharCount; ++y) { for (size_t z = 0; z <= kMaxLen + 2; ++z) {
expected[x][y][z] = s[x]->FastIndexOf(c_char[y], static_cast<int32_t>(z) - 1);
}
}
}
// Play with it...
for (size_t x = 0; x < kStringCount; ++x) { for (size_t y = 0; y < kCharCount; ++y) { for (size_t z = 0; z <= kMaxLen + 2; ++z) {
int32_t start = static_cast<int32_t>(z) - 1;
// Test string_compareto x y
size_t result = Invoke3(reinterpret_cast<size_t>(s[x].Get()), c_char[y], start,
StubTest::GetEntrypoint(self, kQuickIndexOf), self);
EXPECT_FALSE(self->IsExceptionPending());
// The result is a 32b signed integer union {
size_t r;
int32_t i;
} conv;
conv.r = result;
// Tests done. #else
LOG(INFO) << "Skipping indexof as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping indexof as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// TODO: Exercise the ReadBarrierMarkRegX entry points.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.