Commit 799681b1 authored by Andreas Gampe's avatar Andreas Gampe
Browse files

ART: Refactor UnstartedRuntime for testing

Expose the UnstartedRuntime implementation functions as private static
methods of a class. Add a gtest that can invoke these functions. Add
sample tests for String and Memory.

Bug: 21173514
Change-Id: Ib5bde6347fafaf7607c642542ea7d5938ff4b1df
parent 9ba4e651
......@@ -163,6 +163,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \
runtime/instrumentation_test.cc \
runtime/intern_table_test.cc \
runtime/interpreter/safe_math_test.cc \
runtime/interpreter/unstarted_runtime_test.cc \
runtime/java_vm_ext_test.cc \
runtime/jit/jit_code_cache_test.cc \
runtime/leb128_test.cc \
......
......@@ -1594,7 +1594,7 @@ class Dex2Oat FINAL {
// Initialize maps for unstarted runtime. This needs to be here, as running clinits needs this
// set up.
interpreter::UnstartedRuntimeInitialize();
interpreter::UnstartedRuntime::Initialize();
runtime->GetClassLinker()->RunRootClinits();
runtime_ = runtime;
......
......@@ -327,7 +327,7 @@ void CommonRuntimeTest::SetUp() {
// Initialize maps for unstarted runtime. This needs to be here, as running clinits needs this
// set up.
if (!unstarted_initialized_) {
interpreter::UnstartedRuntimeInitialize();
interpreter::UnstartedRuntime::Initialize();
unstarted_initialized_ = true;
}
......
......@@ -386,7 +386,7 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive
// references pointers due to moving GC.
args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
if (!Runtime::Current()->IsStarted()) {
UnstartedRuntimeJni(self, method, receiver, args, result);
UnstartedRuntime::Jni(self, method, receiver, args, result);
} else {
InterpreterJni(self, method, shorty, receiver, args, result);
}
......@@ -474,7 +474,7 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, const DexFile::C
CHECK(!Runtime::Current()->IsStarted());
Object* receiver = is_static ? nullptr : shadow_frame->GetVRegReference(0);
uint32_t* args = shadow_frame->GetVRegArgs(is_static ? 0 : 1);
UnstartedRuntimeJni(self, shadow_frame->GetMethod(), receiver, args, result);
UnstartedRuntime::Jni(self, shadow_frame->GetMethod(), receiver, args, result);
}
self->PopShadowFrame();
......
......@@ -659,7 +659,7 @@ bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
}
entry(self, code_item, new_shadow_frame, result);
} else {
UnstartedRuntimeInvoke(self, code_item, new_shadow_frame, result, first_dest_reg);
UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
}
if (string_init && !self->IsExceptionPending()) {
......
This diff is collapsed.
......@@ -36,16 +36,69 @@ class Object;
namespace interpreter {
void UnstartedRuntimeInitialize();
// Support for an unstarted runtime. These are special handwritten implementations for select
// libcore native and non-native methods so we can compile-time initialize classes in the boot
// image.
//
// While it would technically be OK to only expose the public functions, a class was chosen to
// wrap this so the actual implementations are exposed for testing. This is also why the private
// methods are not documented here - they are not intended to be used directly except in
// testing.
void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame,
JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
class UnstartedRuntime {
public:
static void Initialize();
void UnstartedRuntimeJni(Thread* self, mirror::ArtMethod* method, mirror::Object* receiver,
uint32_t* args, JValue* result)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void Invoke(Thread* self,
const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame,
JValue* result,
size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void Jni(Thread* self,
mirror::ArtMethod* method,
mirror::Object* receiver,
uint32_t* args,
JValue* result)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
// Methods that intercept available libcore implementations.
#define UNSTARTED_DIRECT(ShortName, SigIgnored) \
static void Unstarted ## ShortName(Thread* self, \
ShadowFrame* shadow_frame, \
JValue* result, \
size_t arg_offset) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
#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(ShortName, SigIgnored) \
static void UnstartedJNI ## ShortName(Thread* self, \
mirror::ArtMethod* method, \
mirror::Object* receiver, \
uint32_t* args, \
JValue* result) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
#include "unstarted_runtime_list.h"
UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI)
#undef UNSTARTED_RUNTIME_DIRECT_LIST
#undef UNSTARTED_RUNTIME_JNI_LIST
#undef UNSTARTED_JNI
static void InitializeInvokeHandlers();
static void InitializeJNIHandlers();
friend class UnstartedRuntimeTest;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(UnstartedRuntime);
};
} // namespace interpreter
} // namespace art
......
/*
* 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.
*/
#ifndef ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_LIST_H_
#define ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_LIST_H_
// Methods that intercept available libcore implementations.
#define UNSTARTED_RUNTIME_DIRECT_LIST(V) \
V(ClassForName, "java.lang.Class java.lang.Class.forName(java.lang.String)") \
V(ClassForNameLong, "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)") \
V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \
V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \
V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \
V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \
V(SystemArraycopyChar, "void java.lang.System.arraycopy(char[], int, char[], int, int)") \
V(SystemArraycopyInt, "void java.lang.System.arraycopy(int[], int, int[], int, int)") \
V(ThreadLocalGet, "java.lang.Object java.lang.ThreadLocal.get()") \
V(MathCeil, "double java.lang.Math.ceil(double)") \
V(ArtMethodGetMethodName, "java.lang.String java.lang.reflect.ArtMethod.getMethodName(java.lang.reflect.ArtMethod)") \
V(ObjectHashCode, "int java.lang.Object.hashCode()") \
V(DoubleDoubleToRawLongBits, "long java.lang.Double.doubleToRawLongBits(double)") \
V(DexCacheGetDexNative, "com.android.dex.Dex java.lang.DexCache.getDexNative()") \
V(MemoryPeekByte, "byte libcore.io.Memory.peekByte(long)") \
V(MemoryPeekShort, "short libcore.io.Memory.peekShortNative(long)") \
V(MemoryPeekInt, "int libcore.io.Memory.peekIntNative(long)") \
V(MemoryPeekLong, "long libcore.io.Memory.peekLongNative(long)") \
V(MemoryPeekByteArray, "void libcore.io.Memory.peekByteArray(long, byte[], int, int)") \
V(SecurityGetSecurityPropertiesReader, "java.io.Reader java.security.Security.getSecurityPropertiesReader()") \
V(StringGetCharsNoCheck, "void java.lang.String.getCharsNoCheck(int, int, char[], int)") \
V(StringCharAt, "char java.lang.String.charAt(int)") \
V(StringSetCharAt, "void java.lang.String.setCharAt(int, char)") \
V(StringFactoryNewStringFromChars, "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])") \
V(StringFactoryNewStringFromString, "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)") \
V(StringFastSubstring, "java.lang.String java.lang.String.fastSubstring(int, int)") \
V(StringToCharArray, "char[] java.lang.String.toCharArray()")
// Methods that are native.
#define UNSTARTED_RUNTIME_JNI_LIST(V) \
V(VMRuntimeNewUnpaddedArray, "java.lang.Object dalvik.system.VMRuntime.newUnpaddedArray(java.lang.Class, int)") \
V(VMStackGetCallingClassLoader, "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") \
V(VMStackGetStackClass2, "java.lang.Class dalvik.system.VMStack.getStackClass2()") \
V(MathLog, "double java.lang.Math.log(double)") \
V(MathExp, "double java.lang.Math.exp(double)") \
V(ClassGetNameNative, "java.lang.String java.lang.Class.getNameNative()") \
V(FloatFloatToRawIntBits, "int java.lang.Float.floatToRawIntBits(float)") \
V(FloatIntBitsToFloat, "float java.lang.Float.intBitsToFloat(int)") \
V(ObjectInternalClone, "java.lang.Object java.lang.Object.internalClone()") \
V(ObjectNotifyAll, "void java.lang.Object.notifyAll()") \
V(StringCompareTo, "int java.lang.String.compareTo(java.lang.String)") \
V(StringIntern, "java.lang.String java.lang.String.intern()") \
V(StringFastIndexOf, "int java.lang.String.fastIndexOf(int, int)") \
V(ArrayCreateMultiArray, "java.lang.Object java.lang.reflect.Array.createMultiArray(java.lang.Class, int[])") \
V(ArrayCreateObjectArray, "java.lang.Object java.lang.reflect.Array.createObjectArray(java.lang.Class, int)") \
V(ThrowableNativeFillInStackTrace, "java.lang.Object java.lang.Throwable.nativeFillInStackTrace()") \
V(SystemIdentityHashCode, "int java.lang.System.identityHashCode(java.lang.Object)") \
V(ByteOrderIsLittleEndian, "boolean java.nio.ByteOrder.isLittleEndian()") \
V(UnsafeCompareAndSwapInt, "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") \
V(UnsafePutObject, "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") \
V(UnsafeGetArrayBaseOffsetForComponentType, "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)") \
V(UnsafeGetArrayIndexScaleForComponentType, "int sun.misc.Unsafe.getArrayIndexScaleForComponentType(java.lang.Class)")
#endif // ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_LIST_H_
// the guard in this file is just for cpplint
#undef ART_RUNTIME_INTERPRETER_UNSTARTED_RUNTIME_LIST_H_
/*
* 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 "handle.h"
#include "handle_scope-inl.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, \
mirror::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);
}
} // namespace interpreter
} // namespace art
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment