Merge "Introduce a NearLabel in thumb2."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5d4f1d3..bfc8956 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -163,6 +163,7 @@
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 \
@@ -417,7 +418,7 @@
LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
LOCAL_SRC_FILES := $$(art_gtest_filename)
LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes)
- LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest libart-disassembler
+ LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest libartd-disassembler
LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 47288b5..7cb7489 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -74,8 +74,8 @@
static constexpr bool kTimeCompileMethod = !kIsDebugBuild;
-// Whether to produce 64-bit ELF files for 64-bit targets. Leave this off for now.
-static constexpr bool kProduce64BitELFFiles = false;
+// Whether to produce 64-bit ELF files for 64-bit targets.
+static constexpr bool kProduce64BitELFFiles = true;
// Whether classes-to-compile and methods-to-compile are only applied to the boot image, or, when
// given, too all compilations.
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index be28755..7da4f2d 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -265,6 +265,21 @@
StartAttributeStream("kind") << barrier->GetBarrierKind();
}
+ void VisitLoadClass(HLoadClass* load_cass) OVERRIDE {
+ StartAttributeStream("gen_clinit_check") << std::boolalpha
+ << load_cass->MustGenerateClinitCheck() << std::noboolalpha;
+ }
+
+ void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
+ StartAttributeStream("must_do_null_check") << std::boolalpha
+ << check_cast->MustDoNullCheck() << std::noboolalpha;
+ }
+
+ void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE {
+ StartAttributeStream("must_do_null_check") << std::boolalpha
+ << instance_of->MustDoNullCheck() << std::noboolalpha;
+ }
+
bool IsPass(const char* name) {
return strcmp(pass_name_, name) == 0;
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 680fb47..b712e5e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3444,8 +3444,8 @@
return generate_clinit_check_;
}
- void SetMustGenerateClinitCheck() {
- generate_clinit_check_ = true;
+ void SetMustGenerateClinitCheck(bool generate_clinit_check) {
+ generate_clinit_check_ = generate_clinit_check;
}
bool CanCallRuntime() const {
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 78d1185..538736b 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -53,7 +53,7 @@
if (check->GetPrevious() == cls) {
// Pass the initialization duty to the `HLoadClass` instruction,
// and remove the instruction from the graph.
- cls->SetMustGenerateClinitCheck();
+ cls->SetMustGenerateClinitCheck(true);
check->GetBlock()->RemoveInstruction(check);
}
}
@@ -82,8 +82,15 @@
void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
if (invoke->IsStaticWithExplicitClinitCheck()) {
size_t last_input_index = invoke->InputCount() - 1;
- HInstruction* last_input = invoke->InputAt(last_input_index);
- DCHECK(last_input->IsLoadClass()) << last_input->DebugName();
+ HLoadClass* last_input = invoke->InputAt(last_input_index)->AsLoadClass();
+ DCHECK(last_input != nullptr)
+ << "Last input is not HLoadClass. It is " << last_input->DebugName();
+
+ // The static call will initialize the class so there's no need for a clinit check if
+ // it's the first user.
+ if (last_input == invoke->GetPrevious()) {
+ last_input->SetMustGenerateClinitCheck(false);
+ }
// Remove a load class instruction as last input of a static
// invoke, which has been added (along with a clinit check,
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 3cf458a..43bec37 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1594,7 +1594,7 @@
// 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;
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 7a711cc..de3a29b 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -327,7 +327,7 @@
// 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;
}
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 852ba49..0eb7f2b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3974,7 +3974,8 @@
CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
- JValue result = InvokeWithJValues(soa, pReq->receiver.Read(), soa.EncodeMethod(m.Get()),
+ ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(pReq->receiver.Read()));
+ JValue result = InvokeWithJValues(soa, ref.get(), soa.EncodeMethod(m.Get()),
reinterpret_cast<jvalue*>(pReq->arg_values));
pReq->result_tag = BasicTagFromDescriptor(m.Get()->GetShorty());
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 639be51..39d850f 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -82,6 +82,15 @@
return obj;
}
+inline void IndirectReferenceTable::Update(IndirectRef iref, mirror::Object* obj) {
+ if (!GetChecked(iref)) {
+ LOG(WARNING) << "IndirectReferenceTable Update failed to find reference " << iref;
+ return;
+ }
+ uint32_t idx = ExtractIndex(iref);
+ table_[idx].SetReference(obj);
+}
+
} // namespace art
#endif // ART_RUNTIME_INDIRECT_REFERENCE_TABLE_INL_H_
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index a0e53af..dea5dfd 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -213,6 +213,10 @@
uint32_t GetSerial() const {
return serial_;
}
+ void SetReference(mirror::Object* obj) {
+ DCHECK_LT(serial_, kIRTPrevCount);
+ references_[serial_] = GcRoot<mirror::Object>(obj);
+ }
private:
uint32_t serial_;
@@ -294,6 +298,13 @@
}
/*
+ * Update an existing entry.
+ *
+ * Updates an existing indirect reference to point to a new object.
+ */
+ void Update(IndirectRef iref, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
* Remove an existing entry.
*
* If the entry is not between the current top index and the bottom index
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index a37aee5..26860e7 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -386,7 +386,7 @@
// 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 @@
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();
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 59d3008..363c65a 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -659,7 +659,7 @@
}
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()) {
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 317106b..738e52b 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -120,7 +120,7 @@
return param->AsString();
}
-static void UnstartedClassForName(
+void UnstartedRuntime::UnstartedClassForName(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
@@ -134,7 +134,7 @@
CheckExceptionGenerateClassNotFound(self);
}
-static void UnstartedClassForNameLong(
+void UnstartedRuntime::UnstartedClassForNameLong(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
@@ -152,7 +152,7 @@
CheckExceptionGenerateClassNotFound(self);
}
-static void UnstartedClassClassForName(
+void UnstartedRuntime::UnstartedClassClassForName(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
@@ -170,7 +170,7 @@
CheckExceptionGenerateClassNotFound(self);
}
-static void UnstartedClassNewInstance(
+void UnstartedRuntime::UnstartedClassNewInstance(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
StackHandleScope<3> hs(self); // Class, constructor, object.
@@ -226,7 +226,7 @@
}
}
-static void UnstartedClassGetDeclaredField(
+void UnstartedRuntime::UnstartedClassGetDeclaredField(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
@@ -265,7 +265,7 @@
}
}
-static void UnstartedVmClassLoaderFindLoadedClass(
+void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
@@ -286,7 +286,7 @@
}
}
-static void UnstartedVoidLookupType(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedVoidLookupType(Thread* self ATTRIBUTE_UNUSED,
ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
JValue* result,
size_t arg_offset ATTRIBUTE_UNUSED)
@@ -323,7 +323,7 @@
}
}
-static void UnstartedSystemArraycopy(
+void UnstartedRuntime::UnstartedSystemArraycopy(
Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Special case array copying without initializing System.
@@ -409,7 +409,21 @@
}
}
-static void UnstartedThreadLocalGet(
+void UnstartedRuntime::UnstartedSystemArraycopyChar(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Just forward.
+ UnstartedRuntime::UnstartedSystemArraycopy(self, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedSystemArraycopyInt(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Just forward.
+ UnstartedRuntime::UnstartedSystemArraycopy(self, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedThreadLocalGet(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::string caller(PrettyMethod(shadow_frame->GetLink()->GetMethod()));
@@ -459,7 +473,7 @@
}
}
-static void UnstartedMathCeil(
+void UnstartedRuntime::UnstartedMathCeil(
Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
double in = shadow_frame->GetVRegDouble(arg_offset);
double out;
@@ -474,21 +488,21 @@
result->SetD(out);
}
-static void UnstartedArtMethodGetMethodName(
+void UnstartedRuntime::UnstartedArtMethodGetMethodName(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtMethod* method = shadow_frame->GetVRegReference(arg_offset)->AsArtMethod();
result->SetL(method->GetNameAsString(self));
}
-static void UnstartedObjectHashCode(
+void UnstartedRuntime::UnstartedObjectHashCode(
Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
result->SetI(obj->IdentityHashCode());
}
-static void UnstartedDoubleDoubleToRawLongBits(
+void UnstartedRuntime::UnstartedDoubleDoubleToRawLongBits(
Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
double in = shadow_frame->GetVRegDouble(arg_offset);
result->SetJ(bit_cast<int64_t, double>(in));
@@ -522,7 +536,7 @@
return self->DecodeJObject(dex.get());
}
-static void UnstartedDexCacheGetDexNative(
+void UnstartedRuntime::UnstartedDexCacheGetDexNative(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// We will create the Dex object, but the image writer will release it before creating the
@@ -555,17 +569,20 @@
}
case Primitive::kPrimShort: {
- result->SetS(*reinterpret_cast<int16_t*>(static_cast<intptr_t>(address)));
+ typedef int16_t unaligned_short __attribute__ ((aligned (1)));
+ result->SetS(*reinterpret_cast<unaligned_short*>(static_cast<intptr_t>(address)));
return;
}
case Primitive::kPrimInt: {
- result->SetI(*reinterpret_cast<int32_t*>(static_cast<intptr_t>(address)));
+ typedef int32_t unaligned_int __attribute__ ((aligned (1)));
+ result->SetI(*reinterpret_cast<unaligned_int*>(static_cast<intptr_t>(address)));
return;
}
case Primitive::kPrimLong: {
- result->SetJ(*reinterpret_cast<int64_t*>(static_cast<intptr_t>(address)));
+ typedef int64_t unaligned_long __attribute__ ((aligned (1)));
+ result->SetJ(*reinterpret_cast<unaligned_long*>(static_cast<intptr_t>(address)));
return;
}
@@ -582,22 +599,28 @@
UNREACHABLE();
}
-static void UnstartedMemoryPeekEntry(
+void UnstartedRuntime::UnstartedMemoryPeekByte(
Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- std::string name(PrettyMethod(shadow_frame->GetMethod()));
- if (name == "byte libcore.io.Memory.peekByte(long)") {
- UnstartedMemoryPeek(Primitive::kPrimByte, shadow_frame, result, arg_offset);
- } else if (name == "short libcore.io.Memory.peekShortNative(long)") {
- UnstartedMemoryPeek(Primitive::kPrimShort, shadow_frame, result, arg_offset);
- } else if (name == "int libcore.io.Memory.peekIntNative(long)") {
- UnstartedMemoryPeek(Primitive::kPrimInt, shadow_frame, result, arg_offset);
- } else if (name == "long libcore.io.Memory.peekLongNative(long)") {
- UnstartedMemoryPeek(Primitive::kPrimLong, shadow_frame, result, arg_offset);
- } else {
- LOG(FATAL) << "Unsupported Memory.peek entry: " << name;
- UNREACHABLE();
- }
+ UnstartedMemoryPeek(Primitive::kPrimByte, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedMemoryPeekShort(
+ Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ UnstartedMemoryPeek(Primitive::kPrimShort, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedMemoryPeekInt(
+ Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ UnstartedMemoryPeek(Primitive::kPrimInt, shadow_frame, result, arg_offset);
+}
+
+void UnstartedRuntime::UnstartedMemoryPeekLong(
+ Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ UnstartedMemoryPeek(Primitive::kPrimLong, shadow_frame, result, arg_offset);
}
static void UnstartedMemoryPeekArray(
@@ -649,20 +672,14 @@
UNREACHABLE();
}
-static void UnstartedMemoryPeekArrayEntry(
+void UnstartedRuntime::UnstartedMemoryPeekByteArray(
Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- std::string name(PrettyMethod(shadow_frame->GetMethod()));
- if (name == "void libcore.io.Memory.peekByteArray(long, byte[], int, int)") {
- UnstartedMemoryPeekArray(Primitive::kPrimByte, self, shadow_frame, arg_offset);
- } else {
- LOG(FATAL) << "Unsupported Memory.peekArray entry: " << name;
- UNREACHABLE();
- }
+ UnstartedMemoryPeekArray(Primitive::kPrimByte, self, shadow_frame, arg_offset);
}
// This allows reading security.properties in an unstarted runtime and initialize Security.
-static void UnstartedSecurityGetSecurityPropertiesReader(
+void UnstartedRuntime::UnstartedSecurityGetSecurityPropertiesReader(
Thread* self,
ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
JValue* result,
@@ -756,7 +773,7 @@
}
// This allows reading the new style of String objects during compilation.
-static void UnstartedStringGetCharsNoCheck(
+void UnstartedRuntime::UnstartedStringGetCharsNoCheck(
Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
jint start = shadow_frame->GetVReg(arg_offset + 1);
@@ -777,7 +794,7 @@
}
// This allows reading chars from the new style of String objects during compilation.
-static void UnstartedStringCharAt(
+void UnstartedRuntime::UnstartedStringCharAt(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
jint index = shadow_frame->GetVReg(arg_offset + 1);
@@ -790,7 +807,7 @@
}
// This allows setting chars from the new style of String objects during compilation.
-static void UnstartedStringSetCharAt(
+void UnstartedRuntime::UnstartedStringSetCharAt(
Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
jint index = shadow_frame->GetVReg(arg_offset + 1);
@@ -804,7 +821,7 @@
}
// This allows creating the new style of String objects during compilation.
-static void UnstartedStringFactoryNewStringFromChars(
+void UnstartedRuntime::UnstartedStringFactoryNewStringFromChars(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
jint offset = shadow_frame->GetVReg(arg_offset);
@@ -818,7 +835,7 @@
}
// This allows creating the new style of String objects during compilation.
-static void UnstartedStringFactoryNewStringFromString(
+void UnstartedRuntime::UnstartedStringFactoryNewStringFromString(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::String* to_copy = shadow_frame->GetVRegReference(arg_offset)->AsString();
@@ -834,8 +851,7 @@
allocator));
}
-// This allows creating the new style of String objects during compilation.
-static void UnstartedStringFastSubstring(
+void UnstartedRuntime::UnstartedStringFastSubstring(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
jint start = shadow_frame->GetVReg(arg_offset + 1);
@@ -852,7 +868,7 @@
}
// This allows getting the char array for new style of String objects during compilation.
-static void UnstartedStringToCharArray(
+void UnstartedRuntime::UnstartedStringToCharArray(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::String* string = shadow_frame->GetVRegReference(arg_offset)->AsString();
@@ -863,7 +879,7 @@
result->SetL(string->ToCharArray(self));
}
-static void UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self,
+void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -880,7 +896,7 @@
array_class->GetComponentSizeShift(), allocator));
}
-static void UnstartedJNIVMStackGetCallingClassLoader(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIVMStackGetCallingClassLoader(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -888,7 +904,7 @@
result->SetL(nullptr);
}
-static void UnstartedJNIVMStackGetStackClass2(Thread* self,
+void UnstartedRuntime::UnstartedJNIVMStackGetStackClass2(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -901,7 +917,7 @@
}
}
-static void UnstartedJNIMathLog(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIMathLog(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -911,7 +927,7 @@
result->SetD(log(value.GetD()));
}
-static void UnstartedJNIMathExp(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIMathExp(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -921,7 +937,7 @@
result->SetD(exp(value.GetD()));
}
-static void UnstartedJNIClassGetNameNative(Thread* self,
+void UnstartedRuntime::UnstartedJNIClassGetNameNative(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -931,7 +947,7 @@
result->SetL(mirror::Class::ComputeName(hs.NewHandle(receiver->AsClass())));
}
-static void UnstartedJNIFloatFloatToRawIntBits(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIFloatFloatToRawIntBits(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -939,7 +955,7 @@
result->SetI(args[0]);
}
-static void UnstartedJNIFloatIntBitsToFloat(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIFloatIntBitsToFloat(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -947,7 +963,7 @@
result->SetI(args[0]);
}
-static void UnstartedJNIObjectInternalClone(Thread* self,
+void UnstartedRuntime::UnstartedJNIObjectInternalClone(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -956,7 +972,7 @@
result->SetL(receiver->Clone(self));
}
-static void UnstartedJNIObjectNotifyAll(Thread* self,
+void UnstartedRuntime::UnstartedJNIObjectNotifyAll(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -965,7 +981,7 @@
receiver->NotifyAll(self);
}
-static void UnstartedJNIStringCompareTo(Thread* self,
+void UnstartedRuntime::UnstartedJNIStringCompareTo(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver,
uint32_t* args,
@@ -978,7 +994,7 @@
result->SetI(receiver->AsString()->CompareTo(rhs));
}
-static void UnstartedJNIStringIntern(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIStringIntern(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -987,7 +1003,7 @@
result->SetL(receiver->AsString()->Intern());
}
-static void UnstartedJNIStringFastIndexOf(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIStringFastIndexOf(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver,
uint32_t* args,
@@ -996,7 +1012,7 @@
result->SetI(receiver->AsString()->FastIndexOf(args[0], args[1]));
}
-static void UnstartedJNIArrayCreateMultiArray(Thread* self,
+void UnstartedRuntime::UnstartedJNIArrayCreateMultiArray(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -1008,7 +1024,7 @@
result->SetL(mirror::Array::CreateMultiArray(self, h_class, h_dimensions));
}
-static void UnstartedJNIArrayCreateObjectArray(Thread* self,
+void UnstartedRuntime::UnstartedJNIArrayCreateObjectArray(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -1033,7 +1049,7 @@
result->SetL(new_array);
}
-static void UnstartedJNIThrowableNativeFillInStackTrace(Thread* self,
+void UnstartedRuntime::UnstartedJNIThrowableNativeFillInStackTrace(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -1047,7 +1063,7 @@
}
}
-static void UnstartedJNISystemIdentityHashCode(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNISystemIdentityHashCode(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -1057,7 +1073,7 @@
result->SetI((obj != nullptr) ? obj->IdentityHashCode() : 0);
}
-static void UnstartedJNIByteOrderIsLittleEndian(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIByteOrderIsLittleEndian(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -1065,7 +1081,7 @@
result->SetZ(JNI_TRUE);
}
-static void UnstartedJNIUnsafeCompareAndSwapInt(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIUnsafeCompareAndSwapInt(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -1086,7 +1102,7 @@
result->SetZ(success ? JNI_TRUE : JNI_FALSE);
}
-static void UnstartedJNIUnsafePutObject(Thread* self ATTRIBUTE_UNUSED,
+void UnstartedRuntime::UnstartedJNIUnsafePutObject(Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
uint32_t* args,
@@ -1102,7 +1118,7 @@
}
}
-static void UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType(
+void UnstartedRuntime::UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType(
Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
@@ -1114,7 +1130,7 @@
result->SetI(mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value());
}
-static void UnstartedJNIUnsafeGetArrayIndexScaleForComponentType(
+void UnstartedRuntime::UnstartedJNIUnsafeGetArrayIndexScaleForComponentType(
Thread* self ATTRIBUTE_UNUSED,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
@@ -1136,147 +1152,37 @@
static std::unordered_map<std::string, InvokeHandler> invoke_handlers_;
static std::unordered_map<std::string, JNIHandler> jni_handlers_;
-static void UnstartedRuntimeInitializeInvokeHandlers() {
- struct InvokeHandlerDef {
- std::string name;
- InvokeHandler function;
- };
-
- InvokeHandlerDef defs[] {
- { "java.lang.Class java.lang.Class.forName(java.lang.String)",
- &UnstartedClassForName },
- { "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)",
- &UnstartedClassForNameLong },
- { "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)",
- &UnstartedClassClassForName },
- { "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)",
- &UnstartedVmClassLoaderFindLoadedClass },
- { "java.lang.Class java.lang.Void.lookupType()",
- &UnstartedVoidLookupType },
- { "java.lang.Object java.lang.Class.newInstance()",
- &UnstartedClassNewInstance },
- { "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)",
- &UnstartedClassGetDeclaredField },
- { "int java.lang.Object.hashCode()",
- &UnstartedObjectHashCode },
- { "java.lang.String java.lang.reflect.ArtMethod.getMethodName(java.lang.reflect.ArtMethod)",
- &UnstartedArtMethodGetMethodName },
- { "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)",
- &UnstartedSystemArraycopy},
- { "void java.lang.System.arraycopy(char[], int, char[], int, int)",
- &UnstartedSystemArraycopy },
- { "void java.lang.System.arraycopy(int[], int, int[], int, int)",
- &UnstartedSystemArraycopy },
- { "long java.lang.Double.doubleToRawLongBits(double)",
- &UnstartedDoubleDoubleToRawLongBits },
- { "double java.lang.Math.ceil(double)",
- &UnstartedMathCeil },
- { "java.lang.Object java.lang.ThreadLocal.get()",
- &UnstartedThreadLocalGet },
- { "com.android.dex.Dex java.lang.DexCache.getDexNative()",
- &UnstartedDexCacheGetDexNative },
- { "byte libcore.io.Memory.peekByte(long)",
- &UnstartedMemoryPeekEntry },
- { "short libcore.io.Memory.peekShortNative(long)",
- &UnstartedMemoryPeekEntry },
- { "int libcore.io.Memory.peekIntNative(long)",
- &UnstartedMemoryPeekEntry },
- { "long libcore.io.Memory.peekLongNative(long)",
- &UnstartedMemoryPeekEntry },
- { "void libcore.io.Memory.peekByteArray(long, byte[], int, int)",
- &UnstartedMemoryPeekArrayEntry },
- { "java.io.Reader java.security.Security.getSecurityPropertiesReader()",
- &UnstartedSecurityGetSecurityPropertiesReader },
- { "void java.lang.String.getCharsNoCheck(int, int, char[], int)",
- &UnstartedStringGetCharsNoCheck },
- { "char java.lang.String.charAt(int)",
- &UnstartedStringCharAt },
- { "void java.lang.String.setCharAt(int, char)",
- &UnstartedStringSetCharAt },
- { "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])",
- &UnstartedStringFactoryNewStringFromChars },
- { "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)",
- &UnstartedStringFactoryNewStringFromString },
- { "java.lang.String java.lang.String.fastSubstring(int, int)",
- &UnstartedStringFastSubstring },
- { "char[] java.lang.String.toCharArray()",
- &UnstartedStringToCharArray },
- };
-
- for (auto& def : defs) {
- invoke_handlers_.insert(std::make_pair(def.name, def.function));
- }
+void UnstartedRuntime::InitializeInvokeHandlers() {
+#define UNSTARTED_DIRECT(ShortName, Sig) \
+ invoke_handlers_.insert(std::make_pair(Sig, & UnstartedRuntime::Unstarted ## ShortName));
+#include "unstarted_runtime_list.h"
+ UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_DIRECT
}
-static void UnstartedRuntimeInitializeJNIHandlers() {
- struct JNIHandlerDef {
- std::string name;
- JNIHandler function;
- };
-
- JNIHandlerDef defs[] {
- { "java.lang.Object dalvik.system.VMRuntime.newUnpaddedArray(java.lang.Class, int)",
- &UnstartedJNIVMRuntimeNewUnpaddedArray },
- { "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()",
- &UnstartedJNIVMStackGetCallingClassLoader },
- { "java.lang.Class dalvik.system.VMStack.getStackClass2()",
- &UnstartedJNIVMStackGetStackClass2 },
- { "double java.lang.Math.log(double)",
- &UnstartedJNIMathLog },
- { "java.lang.String java.lang.Class.getNameNative()",
- &UnstartedJNIClassGetNameNative },
- { "int java.lang.Float.floatToRawIntBits(float)",
- &UnstartedJNIFloatFloatToRawIntBits },
- { "float java.lang.Float.intBitsToFloat(int)",
- &UnstartedJNIFloatIntBitsToFloat },
- { "double java.lang.Math.exp(double)",
- &UnstartedJNIMathExp },
- { "java.lang.Object java.lang.Object.internalClone()",
- &UnstartedJNIObjectInternalClone },
- { "void java.lang.Object.notifyAll()",
- &UnstartedJNIObjectNotifyAll},
- { "int java.lang.String.compareTo(java.lang.String)",
- &UnstartedJNIStringCompareTo },
- { "java.lang.String java.lang.String.intern()",
- &UnstartedJNIStringIntern },
- { "int java.lang.String.fastIndexOf(int, int)",
- &UnstartedJNIStringFastIndexOf },
- { "java.lang.Object java.lang.reflect.Array.createMultiArray(java.lang.Class, int[])",
- &UnstartedJNIArrayCreateMultiArray },
- { "java.lang.Object java.lang.reflect.Array.createObjectArray(java.lang.Class, int)",
- &UnstartedJNIArrayCreateObjectArray },
- { "java.lang.Object java.lang.Throwable.nativeFillInStackTrace()",
- &UnstartedJNIThrowableNativeFillInStackTrace },
- { "int java.lang.System.identityHashCode(java.lang.Object)",
- &UnstartedJNISystemIdentityHashCode },
- { "boolean java.nio.ByteOrder.isLittleEndian()",
- &UnstartedJNIByteOrderIsLittleEndian },
- { "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)",
- &UnstartedJNIUnsafeCompareAndSwapInt },
- { "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)",
- &UnstartedJNIUnsafePutObject },
- { "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)",
- &UnstartedJNIUnsafeGetArrayBaseOffsetForComponentType },
- { "int sun.misc.Unsafe.getArrayIndexScaleForComponentType(java.lang.Class)",
- &UnstartedJNIUnsafeGetArrayIndexScaleForComponentType },
- };
-
- for (auto& def : defs) {
- jni_handlers_.insert(std::make_pair(def.name, def.function));
- }
+void UnstartedRuntime::InitializeJNIHandlers() {
+#define UNSTARTED_JNI(ShortName, Sig) \
+ jni_handlers_.insert(std::make_pair(Sig, & UnstartedRuntime::UnstartedJNI ## ShortName));
+#include "unstarted_runtime_list.h"
+ UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_JNI
}
-void UnstartedRuntimeInitialize() {
+void UnstartedRuntime::Initialize() {
CHECK(!tables_initialized_);
- UnstartedRuntimeInitializeInvokeHandlers();
- UnstartedRuntimeInitializeJNIHandlers();
+ InitializeInvokeHandlers();
+ InitializeJNIHandlers();
tables_initialized_ = true;
}
-void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_item,
- ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+void UnstartedRuntime::Invoke(Thread* self, const DexFile::CodeItem* code_item,
+ ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
// In a runtime that's not started we intercept certain methods to avoid complicated dependency
// problems in core libraries.
CHECK(tables_initialized_);
@@ -1294,8 +1200,8 @@
}
// Hand select a number of methods to be run in a not yet started runtime without using JNI.
-void UnstartedRuntimeJni(Thread* self, mirror::ArtMethod* method, mirror::Object* receiver,
- uint32_t* args, JValue* result) {
+void UnstartedRuntime::Jni(Thread* self, mirror::ArtMethod* method, mirror::Object* receiver,
+ uint32_t* args, JValue* result) {
std::string name(PrettyMethod(method));
const auto& iter = jni_handlers_.find(name);
if (iter != jni_handlers_.end()) {
diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h
index 2d7d380..a361af0 100644
--- a/runtime/interpreter/unstarted_runtime.h
+++ b/runtime/interpreter/unstarted_runtime.h
@@ -36,16 +36,69 @@
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
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
new file mode 100644
index 0000000..8f6014c
--- /dev/null
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -0,0 +1,79 @@
+/*
+ * 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_
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
new file mode 100644
index 0000000..34ab277
--- /dev/null
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -0,0 +1,251 @@
+/*
+ * 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
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index fd386d7..f435467 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -311,6 +311,30 @@
return; \
}
+template <bool kNative>
+static mirror::ArtMethod* FindMethod(mirror::Class* c,
+ const StringPiece& name,
+ const StringPiece& sig)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
+ mirror::ArtMethod* method = c->GetDirectMethod(i);
+ if (kNative == method->IsNative() &&
+ name == method->GetName() && method->GetSignature() == sig) {
+ return method;
+ }
+ }
+
+ for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
+ mirror::ArtMethod* method = c->GetVirtualMethod(i);
+ if (kNative == method->IsNative() &&
+ name == method->GetName() && method->GetSignature() == sig) {
+ return method;
+ }
+ }
+
+ return nullptr;
+}
+
class JNI {
public:
static jint GetVersion(JNIEnv*) {
@@ -682,8 +706,7 @@
CHECK_NON_NULL_ARGUMENT(obj);
CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
- JValue result(InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args));
+ JValue result(InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args));
return soa.AddLocalReference<jobject>(result.GetL());
}
@@ -709,8 +732,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args).GetZ();
+ return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetZ();
}
static jbyte CallByteMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -735,8 +757,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args).GetB();
+ return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetB();
}
static jchar CallCharMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -761,8 +782,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args).GetC();
+ return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetC();
}
static jdouble CallDoubleMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -787,8 +807,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args).GetD();
+ return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetD();
}
static jfloat CallFloatMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -813,8 +832,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args).GetF();
+ return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetF();
}
static jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -839,8 +857,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args).GetI();
+ return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetI();
}
static jlong CallLongMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -865,8 +882,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args).GetJ();
+ return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetJ();
}
static jshort CallShortMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -891,8 +907,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
- args).GetS();
+ return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetS();
}
static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
@@ -916,7 +931,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);
ScopedObjectAccess soa(env);
- InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args);
+ InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args);
}
static jobject CallNonvirtualObjectMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -945,7 +960,7 @@
CHECK_NON_NULL_ARGUMENT(obj);
CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
- JValue result(InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args));
+ JValue result(InvokeWithJValues(soa, obj, mid, args));
return soa.AddLocalReference<jobject>(result.GetL());
}
@@ -974,7 +989,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetZ();
+ return InvokeWithJValues(soa, obj, mid, args).GetZ();
}
static jbyte CallNonvirtualByteMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1001,7 +1016,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetB();
+ return InvokeWithJValues(soa, obj, mid, args).GetB();
}
static jchar CallNonvirtualCharMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1028,7 +1043,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetC();
+ return InvokeWithJValues(soa, obj, mid, args).GetC();
}
static jshort CallNonvirtualShortMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1055,7 +1070,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetS();
+ return InvokeWithJValues(soa, obj, mid, args).GetS();
}
static jint CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1082,7 +1097,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetI();
+ return InvokeWithJValues(soa, obj, mid, args).GetI();
}
static jlong CallNonvirtualLongMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1109,7 +1124,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetJ();
+ return InvokeWithJValues(soa, obj, mid, args).GetJ();
}
static jfloat CallNonvirtualFloatMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1136,7 +1151,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetF();
+ return InvokeWithJValues(soa, obj, mid, args).GetF();
}
static jdouble CallNonvirtualDoubleMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1163,7 +1178,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid);
ScopedObjectAccess soa(env);
- return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetD();
+ return InvokeWithJValues(soa, obj, mid, args).GetD();
}
static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
@@ -1189,7 +1204,7 @@
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj);
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);
ScopedObjectAccess soa(env);
- InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args);
+ InvokeWithJValues(soa, obj, mid, args);
}
static jfieldID GetFieldID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
@@ -2133,10 +2148,33 @@
++sig;
}
- mirror::ArtMethod* m = c->FindDirectMethod(name, sig);
- if (m == nullptr) {
- m = c->FindVirtualMethod(name, sig);
+ // Note: the right order is to try to find the method locally
+ // first, either as a direct or a virtual method. Then move to
+ // the parent.
+ mirror::ArtMethod* m = nullptr;
+ bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled();
+ for (mirror::Class* current_class = c;
+ current_class != nullptr;
+ current_class = current_class->GetSuperClass()) {
+ // Search first only comparing methods which are native.
+ m = FindMethod<true>(current_class, name, sig);
+ if (m != nullptr) {
+ break;
+ }
+
+ // Search again comparing to all methods, to find non-native methods that match.
+ m = FindMethod<false>(current_class, name, sig);
+ if (m != nullptr) {
+ break;
+ }
+
+ if (warn_on_going_to_parent) {
+ LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. "
+ << "This is slow, consider changing your RegisterNatives calls.";
+ warn_on_going_to_parent = false;
+ }
}
+
if (m == nullptr) {
LOG(return_errors ? ERROR : INTERNAL_FATAL) << "Failed to register native method "
<< PrettyDescriptor(c) << "." << name << sig << " in "
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 3d14a4e..581ef0e 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -858,8 +858,7 @@
jstring s = reinterpret_cast<jstring>(env_->AllocObject(c));
ASSERT_NE(s, nullptr);
env_->CallVoidMethod(s, mid2);
- // With the string change, this should now throw an UnsupportedOperationException.
- ASSERT_EQ(JNI_TRUE, env_->ExceptionCheck());
+ ASSERT_EQ(JNI_FALSE, env_->ExceptionCheck());
env_->ExceptionClear();
mid = env_->GetMethodID(c, "length", "()I");
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index 9820db7..388921b 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -30,10 +30,11 @@
template <bool kTransactionActive>
inline mirror::Field* Field::CreateFromArtField(Thread* self, ArtField* field,
bool force_resolve) {
+ StackHandleScope<2> hs(self);
// Try to resolve type before allocating since this is a thread suspension point.
- mirror::Class* type = field->GetType<true>();
+ Handle<mirror::Class> type = hs.NewHandle(field->GetType<true>());
- if (type == nullptr) {
+ if (type.Get() == nullptr) {
if (force_resolve) {
if (kIsDebugBuild) {
self->AssertPendingException();
@@ -48,7 +49,6 @@
self->ClearException();
}
}
- StackHandleScope<1> hs(self);
auto ret = hs.NewHandle(static_cast<Field*>(StaticClass()->AllocObject(self)));
if (ret.Get() == nullptr) {
if (kIsDebugBuild) {
@@ -58,14 +58,22 @@
}
auto dex_field_index = field->GetDexFieldIndex();
auto* resolved_field = field->GetDexCache()->GetResolvedField(dex_field_index, sizeof(void*));
- if (resolved_field != nullptr) {
- DCHECK_EQ(resolved_field, field);
+ if (field->GetDeclaringClass()->IsProxyClass()) {
+ DCHECK(field->IsStatic());
+ DCHECK_LT(dex_field_index, 2U);
+ // The two static fields (interfaces, throws) of all proxy classes
+ // share the same dex file indices 0 and 1. So, we can't resolve
+ // them in the dex cache.
} else {
- // We rely on the field being resolved so that we can back to the ArtField
- // (i.e. FromReflectedMethod).
- field->GetDexCache()->SetResolvedField(dex_field_index, field, sizeof(void*));
+ if (resolved_field != nullptr) {
+ DCHECK_EQ(resolved_field, field);
+ } else {
+ // We rely on the field being resolved so that we can back to the ArtField
+ // (i.e. FromReflectedMethod).
+ field->GetDexCache()->SetResolvedField(dex_field_index, field, sizeof(void*));
+ }
}
- ret->SetType<kTransactionActive>(type);
+ ret->SetType<kTransactionActive>(type.Get());
ret->SetDeclaringClass<kTransactionActive>(field->GetDeclaringClass());
ret->SetAccessFlags<kTransactionActive>(field->GetAccessFlags());
ret->SetDexFieldIndex<kTransactionActive>(dex_field_index);
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index 70311bb..933784e 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -54,7 +54,19 @@
}
ArtField* Field::GetArtField() {
- mirror::DexCache* const dex_cache = GetDeclaringClass()->GetDexCache();
+ mirror::Class* declaring_class = GetDeclaringClass();
+ if (UNLIKELY(declaring_class->IsProxyClass())) {
+ DCHECK(IsStatic());
+ DCHECK_EQ(declaring_class->NumStaticFields(), 2U);
+ // 0 == Class[] interfaces; 1 == Class[][] throws;
+ if (GetDexFieldIndex() == 0) {
+ return &declaring_class->GetSFields()[0];
+ } else {
+ DCHECK_EQ(GetDexFieldIndex(), 1U);
+ return &declaring_class->GetSFields()[1];
+ }
+ }
+ mirror::DexCache* const dex_cache = declaring_class->GetDexCache();
ArtField* const art_field = dex_cache->GetResolvedField(GetDexFieldIndex(), sizeof(void*));
CHECK(art_field != nullptr);
return art_field;
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index b471293..93d1f66 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -20,6 +20,7 @@
#include "art_field-inl.h"
#include "class_linker-inl.h"
#include "common_compiler_test.h"
+#include "mirror/field-inl.h"
#include "mirror/method.h"
#include "scoped_thread_state_change.h"
@@ -191,4 +192,53 @@
EXPECT_FALSE(field->IsPrimitiveType());
}
+// Creates two proxy classes and check the art/mirror fields of their static fields.
+TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject jclass_loader = LoadDex("Interfaces");
+ StackHandleScope<7> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+
+ Handle<mirror::Class> proxyClass0;
+ Handle<mirror::Class> proxyClass1;
+ {
+ std::vector<mirror::Class*> interfaces;
+ proxyClass0 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy0", interfaces));
+ proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces));
+ }
+
+ ASSERT_TRUE(proxyClass0.Get() != nullptr);
+ ASSERT_TRUE(proxyClass0->IsProxyClass());
+ ASSERT_TRUE(proxyClass0->IsInitialized());
+ ASSERT_TRUE(proxyClass1.Get() != nullptr);
+ ASSERT_TRUE(proxyClass1->IsProxyClass());
+ ASSERT_TRUE(proxyClass1->IsInitialized());
+
+ ArtField* static_fields0 = proxyClass0->GetSFields();
+ ASSERT_TRUE(static_fields0 != nullptr);
+ ASSERT_EQ(2u, proxyClass0->NumStaticFields());
+ ArtField* static_fields1 = proxyClass1->GetSFields();
+ ASSERT_TRUE(static_fields1 != nullptr);
+ ASSERT_EQ(2u, proxyClass1->NumStaticFields());
+
+ EXPECT_EQ(static_fields0[0].GetDeclaringClass(), proxyClass0.Get());
+ EXPECT_EQ(static_fields0[1].GetDeclaringClass(), proxyClass0.Get());
+ EXPECT_EQ(static_fields1[0].GetDeclaringClass(), proxyClass1.Get());
+ EXPECT_EQ(static_fields1[1].GetDeclaringClass(), proxyClass1.Get());
+
+ Handle<mirror::Field> field00 =
+ hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0[0], true));
+ Handle<mirror::Field> field01 =
+ hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields0[1], true));
+ Handle<mirror::Field> field10 =
+ hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1[0], true));
+ Handle<mirror::Field> field11 =
+ hs.NewHandle(mirror::Field::CreateFromArtField(soa.Self(), &static_fields1[1], true));
+ EXPECT_EQ(field00->GetArtField(), &static_fields0[0]);
+ EXPECT_EQ(field01->GetArtField(), &static_fields0[1]);
+ EXPECT_EQ(field10->GetArtField(), &static_fields1[0]);
+ EXPECT_EQ(field11->GetArtField(), &static_fields1[1]);
+}
+
} // namespace art
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 49e1b8e..d321d27 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -21,6 +21,7 @@
#include "common_throws.h"
#include "dex_file-inl.h"
#include "entrypoints/entrypoint_utils.h"
+#include "indirect_reference_table-inl.h"
#include "jni_internal.h"
#include "mirror/abstract_method.h"
#include "mirror/art_method-inl.h"
@@ -449,6 +450,11 @@
}
mirror::ArtMethod* method = soa.DecodeMethod(mid);
+ bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ if (is_string_init) {
+ // Replace calls to String.<init> with equivalent StringFactory call.
+ method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+ }
mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
@@ -456,11 +462,15 @@
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
+ if (is_string_init) {
+ // For string init, remap original receiver to StringFactory result.
+ soa.Self()->GetJniEnv()->locals.Update(obj, result.GetL());
+ }
return result;
}
-JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver,
- jmethodID mid, jvalue* args) {
+JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+ jvalue* args) {
// We want to make sure that the stack is not within a small distance from the
// protected region in case we are calling into a leaf function whose stack
// check has been elided.
@@ -470,17 +480,27 @@
}
mirror::ArtMethod* method = soa.DecodeMethod(mid);
+ bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ if (is_string_init) {
+ // Replace calls to String.<init> with equivalent StringFactory call.
+ method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+ }
+ mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromJValues(soa, receiver, args);
InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
+ if (is_string_init) {
+ // For string init, remap original receiver to StringFactory result.
+ soa.Self()->GetJniEnv()->locals.Update(obj, result.GetL());
+ }
return result;
}
JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
- mirror::Object* receiver, jmethodID mid, jvalue* args) {
+ jobject obj, jmethodID mid, jvalue* args) {
// We want to make sure that the stack is not within a small distance from the
// protected region in case we are calling into a leaf function whose stack
// check has been elided.
@@ -489,13 +509,24 @@
return JValue();
}
+ mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
+ bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ if (is_string_init) {
+ // Replace calls to String.<init> with equivalent StringFactory call.
+ method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+ receiver = nullptr;
+ }
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromJValues(soa, receiver, args);
InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
+ if (is_string_init) {
+ // For string init, remap original receiver to StringFactory result.
+ soa.Self()->GetJniEnv()->locals.Update(obj, result.GetL());
+ }
return result;
}
@@ -511,12 +542,22 @@
mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
+ bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ if (is_string_init) {
+ // Replace calls to String.<init> with equivalent StringFactory call.
+ method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+ receiver = nullptr;
+ }
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
+ if (is_string_init) {
+ // For string init, remap original receiver to StringFactory result.
+ soa.Self()->GetJniEnv()->locals.Update(obj, result.GetL());
+ }
return result;
}
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 37f8a6a..6b5ffc7 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -49,12 +49,12 @@
va_list args)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver,
- jmethodID mid, jvalue* args)
+JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+ jvalue* args)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
- mirror::Object* receiver, jmethodID mid, jvalue* args)
+ jobject obj, jmethodID mid, jvalue* args)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index a62bc5e..36e444a 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -133,7 +133,8 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "nop", "()V");
- InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), nullptr);
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
+ InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), nullptr);
}
void InvokeIdentityByteMethod(bool is_static) {
@@ -141,22 +142,23 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[1];
args[0].b = 0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(0, result.GetB());
args[0].b = -1;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(-1, result.GetB());
args[0].b = SCHAR_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(SCHAR_MAX, result.GetB());
args[0].b = (SCHAR_MIN << 24) >> 24;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(SCHAR_MIN, result.GetB());
}
@@ -165,22 +167,23 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[1];
args[0].i = 0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(0, result.GetI());
args[0].i = -1;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(-1, result.GetI());
args[0].i = INT_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(INT_MAX, result.GetI());
args[0].i = INT_MIN;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(INT_MIN, result.GetI());
}
@@ -189,22 +192,23 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[1];
args[0].d = 0.0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(0.0, result.GetD());
args[0].d = -1.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(-1.0, result.GetD());
args[0].d = DBL_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(DBL_MAX, result.GetD());
args[0].d = DBL_MIN;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(DBL_MIN, result.GetD());
}
@@ -213,26 +217,27 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[2];
args[0].i = 1;
args[1].i = 2;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(3, result.GetI());
args[0].i = -2;
args[1].i = 5;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(3, result.GetI());
args[0].i = INT_MAX;
args[1].i = INT_MIN;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(-1, result.GetI());
args[0].i = INT_MAX;
args[1].i = INT_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(-2, result.GetI());
}
@@ -241,36 +246,37 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[3];
args[0].i = 0;
args[1].i = 0;
args[2].i = 0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(0, result.GetI());
args[0].i = 1;
args[1].i = 2;
args[2].i = 3;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(6, result.GetI());
args[0].i = -1;
args[1].i = 2;
args[2].i = -3;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(-2, result.GetI());
args[0].i = INT_MAX;
args[1].i = INT_MIN;
args[2].i = INT_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(2147483646, result.GetI());
args[0].i = INT_MAX;
args[1].i = INT_MAX;
args[2].i = INT_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(2147483645, result.GetI());
}
@@ -279,41 +285,42 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[4];
args[0].i = 0;
args[1].i = 0;
args[2].i = 0;
args[3].i = 0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(0, result.GetI());
args[0].i = 1;
args[1].i = 2;
args[2].i = 3;
args[3].i = 4;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(10, result.GetI());
args[0].i = -1;
args[1].i = 2;
args[2].i = -3;
args[3].i = 4;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(2, result.GetI());
args[0].i = INT_MAX;
args[1].i = INT_MIN;
args[2].i = INT_MAX;
args[3].i = INT_MIN;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(-2, result.GetI());
args[0].i = INT_MAX;
args[1].i = INT_MAX;
args[2].i = INT_MAX;
args[3].i = INT_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(-4, result.GetI());
}
@@ -322,6 +329,7 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[5];
args[0].i = 0;
@@ -329,7 +337,7 @@
args[2].i = 0;
args[3].i = 0;
args[4].i = 0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(0, result.GetI());
args[0].i = 1;
@@ -337,7 +345,7 @@
args[2].i = 3;
args[3].i = 4;
args[4].i = 5;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(15, result.GetI());
args[0].i = -1;
@@ -345,7 +353,7 @@
args[2].i = -3;
args[3].i = 4;
args[4].i = -5;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(-3, result.GetI());
args[0].i = INT_MAX;
@@ -353,7 +361,7 @@
args[2].i = INT_MAX;
args[3].i = INT_MIN;
args[4].i = INT_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(2147483645, result.GetI());
args[0].i = INT_MAX;
@@ -361,7 +369,7 @@
args[2].i = INT_MAX;
args[3].i = INT_MAX;
args[4].i = INT_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_EQ(2147483643, result.GetI());
}
@@ -370,31 +378,32 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[2];
args[0].d = 0.0;
args[1].d = 0.0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(0.0, result.GetD());
args[0].d = 1.0;
args[1].d = 2.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(3.0, result.GetD());
args[0].d = 1.0;
args[1].d = -2.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(-1.0, result.GetD());
args[0].d = DBL_MAX;
args[1].d = DBL_MIN;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(1.7976931348623157e308, result.GetD());
args[0].d = DBL_MAX;
args[1].d = DBL_MAX;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(INFINITY, result.GetD());
}
@@ -403,24 +412,25 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[3];
args[0].d = 0.0;
args[1].d = 0.0;
args[2].d = 0.0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(0.0, result.GetD());
args[0].d = 1.0;
args[1].d = 2.0;
args[2].d = 3.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(6.0, result.GetD());
args[0].d = 1.0;
args[1].d = -2.0;
args[2].d = 3.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(2.0, result.GetD());
}
@@ -429,27 +439,28 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[4];
args[0].d = 0.0;
args[1].d = 0.0;
args[2].d = 0.0;
args[3].d = 0.0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(0.0, result.GetD());
args[0].d = 1.0;
args[1].d = 2.0;
args[2].d = 3.0;
args[3].d = 4.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(10.0, result.GetD());
args[0].d = 1.0;
args[1].d = -2.0;
args[2].d = 3.0;
args[3].d = -4.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(-2.0, result.GetD());
}
@@ -458,6 +469,7 @@
mirror::ArtMethod* method;
mirror::Object* receiver;
ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D");
+ ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
jvalue args[5];
args[0].d = 0.0;
@@ -465,7 +477,7 @@
args[2].d = 0.0;
args[3].d = 0.0;
args[4].d = 0.0;
- JValue result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ JValue result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(0.0, result.GetD());
args[0].d = 1.0;
@@ -473,7 +485,7 @@
args[2].d = 3.0;
args[3].d = 4.0;
args[4].d = 5.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(15.0, result.GetD());
args[0].d = 1.0;
@@ -481,7 +493,7 @@
args[2].d = 3.0;
args[3].d = -4.0;
args[4].d = 5.0;
- result = InvokeWithJValues(soa, receiver, soa.EncodeMethod(method), args);
+ result = InvokeWithJValues(soa, receiver_ref.get(), soa.EncodeMethod(method), args);
EXPECT_DOUBLE_EQ(3.0, result.GetD());
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 148bb6d..2145c9c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -216,7 +216,8 @@
// Invoke the 'run' method of our java.lang.Thread.
mirror::Object* receiver = self->tlsPtr_.opeer;
jmethodID mid = WellKnownClasses::java_lang_Thread_run;
- InvokeVirtualOrInterfaceWithJValues(soa, receiver, mid, nullptr);
+ ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
+ InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
}
// Detach and delete self.
Runtime::Current()->GetThreadList()->Unregister(self);
@@ -1886,7 +1887,8 @@
jv_args[i].l = cause.get();
++i;
}
- InvokeWithJValues(soa, exception.Get(), soa.EncodeMethod(exception_init_method), jv_args);
+ ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(exception.Get()));
+ InvokeWithJValues(soa, ref.get(), soa.EncodeMethod(exception_init_method), jv_args);
if (LIKELY(!IsExceptionPending())) {
SetException(exception.Get());
}
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index cdc5461..1ec0cf2 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -550,21 +550,58 @@
}
extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jclass) {
- const char* string = "Test";
- int length = strlen(string);
jclass c = env->FindClass("java/lang/String");
- assert(c != NULL);
- jmethodID method = env->GetMethodID(c, "<init>", "([B)V");
- assert(method != NULL);
+ assert(c != nullptr);
+
+ jmethodID mid1 = env->GetMethodID(c, "<init>", "()V");
+ assert(mid1 != nullptr);
assert(!env->ExceptionCheck());
- jbyteArray array = env->NewByteArray(length);
- env->SetByteArrayRegion(array, 0, length, reinterpret_cast<const jbyte*>(string));
- jobject o = env->NewObject(c, method, array);
- assert(o != NULL);
- jstring s = reinterpret_cast<jstring>(o);
- assert(env->GetStringLength(s) == length);
- assert(env->GetStringUTFLength(s) == length);
+ jmethodID mid2 = env->GetMethodID(c, "<init>", "([B)V");
+ assert(mid2 != nullptr);
+ assert(!env->ExceptionCheck());
+ jmethodID mid3 = env->GetMethodID(c, "<init>", "([C)V");
+ assert(mid3 != nullptr);
+ assert(!env->ExceptionCheck());
+ jmethodID mid4 = env->GetMethodID(c, "<init>", "(Ljava/lang/String;)V");
+ assert(mid4 != nullptr);
+ assert(!env->ExceptionCheck());
+
+ const char* test_array = "Test";
+ int byte_array_length = strlen(test_array);
+ jbyteArray byte_array = env->NewByteArray(byte_array_length);
+ env->SetByteArrayRegion(byte_array, 0, byte_array_length, reinterpret_cast<const jbyte*>(test_array));
+
+ // Test NewObject
+ jstring s = reinterpret_cast<jstring>(env->NewObject(c, mid2, byte_array));
+ assert(s != nullptr);
+ assert(env->GetStringLength(s) == byte_array_length);
+ assert(env->GetStringUTFLength(s) == byte_array_length);
const char* chars = env->GetStringUTFChars(s, nullptr);
- assert(strcmp(string, chars) == 0);
+ assert(strcmp(test_array, chars) == 0);
env->ReleaseStringUTFChars(s, chars);
+
+ // Test AllocObject and Call(Nonvirtual)VoidMethod
+ jstring s1 = reinterpret_cast<jstring>(env->AllocObject(c));
+ assert(s1 != nullptr);
+ jstring s2 = reinterpret_cast<jstring>(env->AllocObject(c));
+ assert(s2 != nullptr);
+ jstring s3 = reinterpret_cast<jstring>(env->AllocObject(c));
+ assert(s3 != nullptr);
+ jstring s4 = reinterpret_cast<jstring>(env->AllocObject(c));
+ assert(s4 != nullptr);
+
+ jcharArray char_array = env->NewCharArray(5);
+ jstring string_arg = env->NewStringUTF("helloworld");
+
+ // With Var Args
+ env->CallVoidMethod(s1, mid1);
+ env->CallNonvirtualVoidMethod(s2, c, mid2, byte_array);
+
+ // With JValues
+ jvalue args3[1];
+ args3[0].l = char_array;
+ jvalue args4[1];
+ args4[0].l = string_arg;
+ env->CallVoidMethodA(s3, mid3, args3);
+ env->CallNonvirtualVoidMethodA(s4, c, mid4, args4);
}
diff --git a/test/020-string/src/Main.java b/test/020-string/src/Main.java
index bb8ce1f..b876e6a 100644
--- a/test/020-string/src/Main.java
+++ b/test/020-string/src/Main.java
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+import java.nio.charset.Charset;
+import java.io.UnsupportedEncodingException;
+
/**
* Simple string test.
*/
@@ -21,6 +24,7 @@
public static void main(String args[]) {
basicTest();
indexTest();
+ constructorTest();
}
public static void basicTest() {
@@ -81,4 +85,36 @@
subStr.indexOf('&') + ":" +
baseStr.indexOf(0x12341234));
}
+
+ public static void constructorTest() {
+ byte[] byteArray = "byteArray".getBytes();
+ char[] charArray = new char[] { 'c', 'h', 'a', 'r', 'A', 'r', 'r', 'a', 'y' };
+ String charsetName = "US-ASCII";
+ Charset charset = Charset.forName("UTF-8");
+ String string = "string";
+ StringBuffer stringBuffer = new StringBuffer("stringBuffer");
+ int [] codePoints = new int[] { 65, 66, 67, 68, 69 };
+ StringBuilder stringBuilder = new StringBuilder("stringBuilder");
+
+ String s1 = new String();
+ String s2 = new String(byteArray);
+ String s3 = new String(byteArray, 1);
+ String s4 = new String(byteArray, 0, 4);
+ String s5 = new String(byteArray, 2, 4, 5);
+
+ try {
+ String s6 = new String(byteArray, 2, 4, charsetName);
+ String s7 = new String(byteArray, charsetName);
+ } catch (UnsupportedEncodingException e) {
+ System.out.println("Got unexpected UnsupportedEncodingException");
+ }
+ String s8 = new String(byteArray, 3, 3, charset);
+ String s9 = new String(byteArray, charset);
+ String s10 = new String(charArray);
+ String s11 = new String(charArray, 0, 4);
+ String s12 = new String(string);
+ String s13 = new String(stringBuffer);
+ String s14 = new String(codePoints, 1, 3);
+ String s15 = new String(stringBuilder);
+ }
}
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 24e9600..db2fc9b 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -327,5 +327,7 @@
.loadLibrary = &native_bridge_loadLibrary,
.getTrampoline = &native_bridge_getTrampoline,
.isSupported = &native_bridge_isSupported,
- .getAppEnv = &native_bridge_getAppEnv
+ .getAppEnv = &native_bridge_getAppEnv,
+ .isCompatibleWith = nullptr,
+ .getSignalHandler = nullptr
};
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
new file mode 100644
index 0000000..b2d7e55
--- /dev/null
+++ b/test/137-cfi/cfi.cc
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#if __linux__
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#endif
+
+#include "jni.h"
+
+#include <backtrace/Backtrace.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "utils.h"
+
+namespace art {
+
+// For testing debuggerd. We do not have expected-death tests, so can't test this by default.
+// Code for this is copied from SignalTest.
+static constexpr bool kCauseSegfault = false;
+char* go_away_compiler_cfi = nullptr;
+
+static void CauseSegfault() {
+#if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
+ // On supported architectures we cause a real SEGV.
+ *go_away_compiler_cfi = 'a';
+#else
+ // On other architectures we simulate SEGV.
+ kill(getpid(), SIGSEGV);
+#endif
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) {
+ // Keep pausing.
+ for (;;) {
+ pause();
+ }
+}
+
+// Helper to look for a sequence in the stack trace.
+#if __linux__
+static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) {
+ size_t cur_search_index = 0; // The currently active index in seq.
+ CHECK_GT(seq.size(), 0U);
+
+ for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
+ if (BacktraceMap::IsValid(it->map)) {
+ LOG(INFO) << "Got " << it->func_name << ", looking for " << seq[cur_search_index];
+ if (it->func_name == seq[cur_search_index]) {
+ cur_search_index++;
+ if (cur_search_index == seq.size()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) {
+#if __linux__
+ // TODO: What to do on Valgrind?
+
+ std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
+ if (!bt->Unwind(0, nullptr)) {
+ return JNI_FALSE;
+ } else if (bt->NumFrames() == 0) {
+ return JNI_FALSE;
+ }
+
+ // We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
+ // This is also risky, as deduping might play a trick on us, so the test needs to make sure that
+ // only unique functions are being expected.
+ std::vector<std::string> seq = {
+ "Java_Main_unwindInProcess", // This function.
+ "boolean Main.unwindInProcess(int, boolean)", // The corresponding Java native method frame.
+ "void Main.main(java.lang.String[])" // The Java entry method.
+ };
+
+ bool result = CheckStack(bt.get(), seq);
+ if (!kCauseSegfault) {
+ return result ? JNI_TRUE : JNI_FALSE;
+ } else {
+ LOG(INFO) << "Result of check-stack: " << result;
+ }
+#endif
+
+ if (kCauseSegfault) {
+ CauseSegfault();
+ }
+
+ return JNI_FALSE;
+}
+
+#if __linux__
+static constexpr int kSleepTimeMicroseconds = 50000; // 0.05 seconds
+static constexpr int kMaxTotalSleepTimeMicroseconds = 1000000; // 1 second
+
+// Wait for a sigstop. This code is copied from libbacktrace.
+int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed ATTRIBUTE_UNUSED) {
+ for (;;) {
+ int status;
+ pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
+ if (n == -1) {
+ PLOG(WARNING) << "waitpid failed: tid " << tid;
+ break;
+ } else if (n == tid) {
+ if (WIFSTOPPED(status)) {
+ return WSTOPSIG(status);
+ } else {
+ PLOG(ERROR) << "unexpected waitpid response: n=" << n << ", status=" << std::hex << status;
+ break;
+ }
+ }
+
+ if (*total_sleep_time_usec > kMaxTotalSleepTimeMicroseconds) {
+ PLOG(WARNING) << "timed out waiting for stop signal: tid=" << tid;
+ break;
+ }
+
+ usleep(kSleepTimeMicroseconds);
+ *total_sleep_time_usec += kSleepTimeMicroseconds;
+ }
+
+ return -1;
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobject, jint pid_int) {
+#if __linux__
+ // TODO: What to do on Valgrind?
+ pid_t pid = static_cast<pid_t>(pid_int);
+
+ // OK, this is painful. debuggerd uses ptrace to unwind other processes.
+
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0)) {
+ // Were not able to attach, bad.
+ PLOG(ERROR) << "Failed to attach.";
+ kill(pid, SIGCONT);
+ return JNI_FALSE;
+ }
+
+ kill(pid, SIGSTOP);
+
+ bool detach_failed = false;
+ int total_sleep_time_usec = 0;
+ int signal = wait_for_sigstop(pid, &total_sleep_time_usec, &detach_failed);
+ if (signal == -1) {
+ LOG(WARNING) << "wait_for_sigstop failed.";
+ }
+
+ std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+ bool result = true;
+ if (!bt->Unwind(0, nullptr)) {
+ result = false;
+ } else if (bt->NumFrames() == 0) {
+ result = false;
+ }
+
+ if (result) {
+ // See comment in unwindInProcess for non-exact stack matching.
+ std::vector<std::string> seq = {
+ // "Java_Main_sleep", // The sleep function being executed in the
+ // other runtime.
+ // Note: For some reason, the name isn't
+ // resolved, so don't look for it right now.
+ "boolean Main.sleep(int, boolean, double)", // The corresponding Java native method frame.
+ "void Main.main(java.lang.String[])" // The Java entry method.
+ };
+
+ result = CheckStack(bt.get(), seq);
+ }
+
+ if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
+ PLOG(ERROR) << "Detach failed";
+ }
+
+ // Continue the process so we can kill it on the Java side.
+ kill(pid, SIGCONT);
+
+ return result ? JNI_TRUE : JNI_FALSE;
+#else
+ return JNI_FALSE;
+#endif
+}
+
+} // namespace art
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/137-cfi/expected.txt
diff --git a/test/137-cfi/info.txt b/test/137-cfi/info.txt
new file mode 100644
index 0000000..7d59605
--- /dev/null
+++ b/test/137-cfi/info.txt
@@ -0,0 +1 @@
+Test that unwinding with CFI info works.
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
new file mode 100644
index 0000000..e184e66
--- /dev/null
+++ b/test/137-cfi/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+ // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do
+ // not dlopen at the moment, this doesn't work, so keep it off for now.
+ public final static boolean TEST_LOCAL_UNWINDING = false;
+
+ // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work
+ // no matter whether we're using dlopen or not.
+ public final static boolean TEST_REMOTE_UNWINDING = true;
+
+ private boolean secondary;
+
+ public Main(boolean secondary) {
+ this.secondary = secondary;
+ }
+
+ public static void main(String[] args) throws Exception {
+ boolean secondary = false;
+ if (args.length > 0 && args[args.length - 1].equals("--secondary")) {
+ secondary = true;
+ }
+ new Main(secondary).run();
+ }
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ private void run() {
+ if (secondary) {
+ if (!TEST_REMOTE_UNWINDING) {
+ throw new RuntimeException("Should not be running secondary!");
+ }
+ runSecondary();
+ } else {
+ runPrimary();
+ }
+ }
+
+ private void runSecondary() {
+ foo(true);
+ throw new RuntimeException("Didn't expect to get back...");
+ }
+
+ private void runPrimary() {
+ // First do the in-process unwinding.
+ if (TEST_LOCAL_UNWINDING && !foo(false)) {
+ System.out.println("Unwinding self failed.");
+ }
+
+ if (!TEST_REMOTE_UNWINDING) {
+ // Skip the remote step.
+ return;
+ }
+
+ // Fork the secondary.
+ String[] cmdline = getCmdLine();
+ String[] secCmdLine = new String[cmdline.length + 1];
+ System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
+ secCmdLine[secCmdLine.length - 1] = "--secondary";
+ Process p = exec(secCmdLine);
+
+ try {
+ int pid = getPid(p);
+ if (pid <= 0) {
+ throw new RuntimeException("Couldn't parse process");
+ }
+
+ // Wait a bit, so the forked process has time to run until its sleep phase.
+ try {
+ Thread.sleep(5000);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ if (!unwindOtherProcess(pid)) {
+ System.out.println("Unwinding other process failed.");
+ }
+ } finally {
+ // Kill the forked process.
+ p.destroy();
+ }
+ }
+
+ private static Process exec(String[] args) {
+ try {
+ return Runtime.getRuntime().exec(args);
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+
+ private static int getPid(Process p) {
+ // Could do reflection for the private pid field, but String parsing is easier.
+ String s = p.toString();
+ if (s.startsWith("Process[pid=")) {
+ return Integer.parseInt(s.substring("Process[pid=".length(), s.length() - 1));
+ } else {
+ return -1;
+ }
+ }
+
+ // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
+ private static String[] getCmdLine() {
+ try {
+ BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
+ String s = in.readLine();
+ in.close();
+ return s.split("\0");
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+
+ public boolean foo(boolean b) {
+ return bar(b);
+ }
+
+ public boolean bar(boolean b) {
+ if (b) {
+ return sleep(2, b, 1.0);
+ } else {
+ return unwindInProcess(1, b);
+ }
+ }
+
+ // Native functions. Note: to avoid deduping, they must all have different signatures.
+
+ public native boolean sleep(int i, boolean b, double dummy);
+
+ public native boolean unwindInProcess(int i, boolean b);
+ public native boolean unwindOtherProcess(int pid);
+}
diff --git a/test/139-register-natives/check b/test/139-register-natives/check
new file mode 100755
index 0000000..265ad85
--- /dev/null
+++ b/test/139-register-natives/check
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+# Strip any JNI registration error messages
+sed -e '/java_vm_ext/d' -e '/jni_internal.cc/d' "$2" > "$2.tmp"
+
+diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/139-register-natives/expected.txt b/test/139-register-natives/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/139-register-natives/expected.txt
diff --git a/test/139-register-natives/info.txt b/test/139-register-natives/info.txt
new file mode 100644
index 0000000..48e08a4
--- /dev/null
+++ b/test/139-register-natives/info.txt
@@ -0,0 +1 @@
+Tests the correct order of RegisterNatives.
diff --git a/test/139-register-natives/regnative.cc b/test/139-register-natives/regnative.cc
new file mode 100644
index 0000000..d9c8b31
--- /dev/null
+++ b/test/139-register-natives/regnative.cc
@@ -0,0 +1,33 @@
+/*
+ * 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 "jni.h"
+
+namespace art {
+
+// Simple empty method. We will check for correct registration with UnsatisfiedLinkError.
+static void foo(JNIEnv*, jclass) {
+}
+
+static JNINativeMethod gMethods[] = {
+ { "foo", "()V", reinterpret_cast<void*>(foo) }
+};
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_registerNatives(JNIEnv* env, jclass, jclass trg) {
+ return env->RegisterNatives(trg, gMethods, 1);
+}
+
+} // namespace art
diff --git a/test/139-register-natives/src/Main.java b/test/139-register-natives/src/Main.java
new file mode 100644
index 0000000..35b2f9c
--- /dev/null
+++ b/test/139-register-natives/src/Main.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ testRegistration1();
+ testRegistration2();
+ testRegistration3();
+ }
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ // Test that a subclass' method is registered instead of a superclass' method.
+ private static void testRegistration1() {
+ registerNatives(TestSub.class);
+
+ expectNotThrows(new TestSub());
+ expectThrows(new TestSuper());
+ }
+
+ // Test that a superclass' method is registered if the subclass doesn't have a matching method.
+ private static void testRegistration2() {
+ registerNatives(TestSub2.class);
+
+ expectNotThrows(new TestSub2());
+ expectNotThrows(new TestSuper2());
+ }
+
+ // Test that registration fails if the subclass has a matching non-native method.
+ private static void testRegistration3() {
+ try {
+ registerNatives(TestSub3.class);
+ System.out.println("Expected exception for registerNatives(TestSub3.class)");
+ } catch (NoSuchMethodError ignored) {
+ }
+ }
+
+ private native static int registerNatives(Class c);
+
+ private static void expectThrows(Base b) {
+ try {
+ b.callMyFoo();
+ System.out.println("Expected exception for " + b.getClass().getName());
+ } catch (Throwable ignored) {
+ }
+ }
+
+ private static void expectNotThrows(Base b) {
+ try {
+ b.callMyFoo();
+ } catch (Throwable t) {
+ System.out.println("Did not expect an exception for " + b.getClass().getName());
+ t.printStackTrace(System.out);
+ }
+ }
+}
+
+abstract class Base {
+ public abstract void callMyFoo();
+}
+
+class TestSuper extends Base {
+ private native void foo();
+
+ @Override
+ public void callMyFoo() {
+ foo();
+ }
+}
+
+class TestSub extends TestSuper {
+ public native void foo();
+
+ @Override
+ public void callMyFoo() {
+ foo();
+ }
+}
+
+class TestSuper2 extends Base{
+ public native void foo();
+
+ @Override
+ public void callMyFoo() {
+ foo();
+ }
+}
+
+class TestSub2 extends TestSuper2 {
+}
+
+class TestSuper3 extends Base {
+ public native void foo();
+
+ @Override
+ public void callMyFoo() {
+ foo();
+ }
+}
+
+class TestSub3 extends TestSuper3 {
+ public void foo() {
+ System.out.println("TestSub3.foo()");
+ }
+}
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 61199a7..e8739b8 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -24,12 +24,12 @@
*/
// CHECK-START: void Main.invokeStaticInlined() builder (after)
- // CHECK-DAG: <<LoadClass:l\d+>> LoadClass
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
// CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>]
// CHECK-START: void Main.invokeStaticInlined() inliner (after)
- // CHECK-DAG: <<LoadClass:l\d+>> LoadClass
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
// CHECK-START: void Main.invokeStaticInlined() inliner (after)
@@ -42,7 +42,7 @@
// CFG as it is before the next pass (liveness analysis) instead.
// CHECK-START: void Main.invokeStaticInlined() liveness (before)
- // CHECK-DAG: LoadClass
+ // CHECK-DAG: LoadClass gen_clinit_check:true
// CHECK-START: void Main.invokeStaticInlined() liveness (before)
// CHECK-NOT: ClinitCheck
@@ -67,12 +67,12 @@
*/
// CHECK-START: void Main.invokeStaticNotInlined() builder (after)
- // CHECK-DAG: <<LoadClass:l\d+>> LoadClass
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
// CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>]
// CHECK-START: void Main.invokeStaticNotInlined() inliner (after)
- // CHECK-DAG: <<LoadClass:l\d+>> LoadClass
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
// CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>]
@@ -260,6 +260,44 @@
}
}
+
+ /*
+ * Verify that if we have a static call immediately after the load class
+ * we don't do generate a clinit check.
+ */
+
+ // CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
+ // CHECK-DAG: <<IntConstant:i\d+>> IntConstant 0
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ // CHECK-DAG: InvokeStaticOrDirect
+ // CHECK-DAG: StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
+
+ // CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
+ // CHECK-NOT: ClinitCheck
+
+ static void noClinitBecauseOfInvokeStatic() {
+ ClassWithClinit2.staticMethod();
+ ClassWithClinit2.doThrow = false;
+ }
+
+ /*
+ * Verify that if the static call is after a field access, the load class
+ * will generate a clinit check.
+ */
+
+ // CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
+ // CHECK-DAG: <<IntConstant:i\d+>> IntConstant 0
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:true
+ // CHECK-DAG: StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
+ // CHECK-DAG: InvokeStaticOrDirect
+
+ // CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
+ // CHECK-NOT: ClinitCheck
+ static void clinitBecauseOfFieldAccess() {
+ ClassWithClinit2.doThrow = false;
+ ClassWithClinit2.staticMethod();
+ }
+
// TODO: Add a test for the case of a static method whose declaring
// class type index is not available (i.e. when `storage_index`
// equals `DexFile::kDexNoIndex` in
diff --git a/test/486-checker-must-do-null-check/expected.txt b/test/486-checker-must-do-null-check/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/486-checker-must-do-null-check/expected.txt
diff --git a/test/486-checker-must-do-null-check/info.txt b/test/486-checker-must-do-null-check/info.txt
new file mode 100644
index 0000000..494ff1c
--- /dev/null
+++ b/test/486-checker-must-do-null-check/info.txt
@@ -0,0 +1 @@
+Verifies MustDoNullCheck() on InstanceOf and CheckCast
diff --git a/test/486-checker-must-do-null-check/src/Main.java b/test/486-checker-must-do-null-check/src/Main.java
new file mode 100644
index 0000000..f285566
--- /dev/null
+++ b/test/486-checker-must-do-null-check/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ // CHECK-START: void Main.InstanceOfPreChecked(java.lang.Object) instruction_simplifier (after)
+ // CHECK: InstanceOf must_do_null_check:false
+ public void InstanceOfPreChecked(Object o) throws Exception {
+ o.toString();
+ if (o instanceof Main) {
+ throw new Exception();
+ }
+ }
+
+ // CHECK-START: void Main.InstanceOf(java.lang.Object) instruction_simplifier (after)
+ // CHECK: InstanceOf must_do_null_check:true
+ public void InstanceOf(Object o) throws Exception {
+ if (o instanceof Main) {
+ throw new Exception();
+ }
+ }
+
+ // CHECK-START: void Main.CheckCastPreChecked(java.lang.Object) instruction_simplifier (after)
+ // CHECK: CheckCast must_do_null_check:false
+ public void CheckCastPreChecked(Object o) {
+ o.toString();
+ ((Main)o).Bar();
+ }
+
+ // CHECK-START: void Main.CheckCast(java.lang.Object) instruction_simplifier (after)
+ // CHECK: CheckCast must_do_null_check:true
+ public void CheckCast(Object o) {
+ ((Main)o).Bar();
+ }
+
+ void Bar() {throw new RuntimeException();}
+
+ public static void main(String[] sa) {
+ Main t = new Main();
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 5e768ee..57d06c4 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -28,6 +28,8 @@
116-nodex2oat/nodex2oat.cc \
117-nopatchoat/nopatchoat.cc \
118-noimage-dex2oat/noimage-dex2oat.cc \
+ 137-cfi/cfi.cc \
+ 139-register-natives/regnative.cc \
454-get-vreg/get_vreg_jni.cc \
455-set-vreg/set_vreg_jni.cc \
457-regs/regs_jni.cc \
@@ -56,7 +58,7 @@
LOCAL_MODULE_TAGS := tests
endif
LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES)
- LOCAL_SHARED_LIBRARIES += libartd
+ LOCAL_SHARED_LIBRARIES += libartd libbacktrace
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 07e7620..a3a639b 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -264,10 +264,12 @@
117-nopatchoat \
118-noimage-dex2oat \
119-noimage-patchoat \
+ 137-cfi \
138-duplicate-classes-check2
# This test fails without an image.
TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
+ 137-cfi \
138-duplicate-classes-check
ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
@@ -294,9 +296,13 @@
TEST_ART_BROKEN_FALLBACK_RUN_TESTS :=
+# 137:
+# This test unrolls and expects managed frames, but tracing means we run the interpreter.
+# 802:
# This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
# when already tracing, and writes an error message that we do not want to check for.
TEST_ART_BROKEN_TRACING_RUN_TESTS := \
+ 137-cfi \
802-deoptimization
ifneq (,$(filter trace,$(TRACE_TYPES)))
@@ -323,6 +329,8 @@
118-noimage-dex2oat \
119-noimage-patchoat \
131-structural-change \
+ 137-cfi \
+ 139-register-natives \
454-get-vreg \
455-set-vreg \
457-regs \
@@ -337,6 +345,33 @@
TEST_ART_BROKEN_NDEBUG_TESTS :=
+# Known broken tests for the interpreter.
+# CFI unwinding expects managed frames.
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
+ 137-cfi
+
+ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS :=
+
+# Known broken tests for the JIT.
+# CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT
+# also uses Generic JNI instead of the JNI compiler.
+TEST_ART_BROKEN_JIT_RUN_TESTS := \
+ 137-cfi
+
+ifneq (,$(filter jit,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ jit,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_JIT_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_JIT_RUN_TESTS :=
+
# Known broken tests for the default compiler (Quick).
TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \
457-regs
@@ -428,7 +463,9 @@
TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS :=
# Tests that should fail in the read barrier configuration.
-TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=
+# 137: Read barrier forces interpreter. Cannot run this with the interpreter.
+TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \
+ 137-cfi
ifeq ($(ART_USE_READ_BARRIER),true)
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -438,6 +475,11 @@
TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=
+# Test 137-cfi works in 32-bit only until we enable 64-bit ELF files.
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),137-cfi,64)
+
# Clear variables ahead of appending to them when defining tests.
$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
$(foreach target, $(TARGET_TYPES), \
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 1c44958..240ed41 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -226,7 +226,11 @@
if [ "$USE_JVM" = "y" ]; then
# Xmx is necessary since we don't pass down the ART flags to JVM.
- ${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes $MAIN "$@"
+ cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@"
+ if [ "$DEV_MODE" = "y" ]; then
+ echo $cmdline
+ fi
+ $cmdline
exit
fi
diff --git a/test/run-test b/test/run-test
index 54c6bbd..12b743d 100755
--- a/test/run-test
+++ b/test/run-test
@@ -368,6 +368,9 @@
else
run_args="${run_args} --no-relocate"
fi
+elif [ "$runtime" = "jvm" ]; then
+ # TODO: Detect whether the host is 32-bit or 64-bit.
+ run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib64"
fi
if [ "$have_image" = "no" ]; then
diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py
index 116fe9a..6cff2bf 100644
--- a/tools/checker/match/file.py
+++ b/tools/checker/match/file.py
@@ -12,127 +12,138 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from collections import namedtuple
from common.immutables import ImmutableDict
from common.logger import Logger
from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
from file_format.checker.struct import CheckerFile, TestCase, TestAssertion
from match.line import MatchLines
-def __headAndTail(list):
- return list[0], list[1:]
+MatchScope = namedtuple("MatchScope", ["start", "end"])
+MatchInfo = namedtuple("MatchInfo", ["scope", "variables"])
-def __splitByVariant(lines, variant):
- """ Splits a list of check lines at index 'i' such that lines[i] is the first
- element whose variant is not equal to the given parameter.
+class MatchFailedException(Exception):
+ def __init__(self, assertion, lineNo):
+ self.assertion = assertion
+ self.lineNo = lineNo
+
+def splitIntoGroups(assertions):
+ """ Breaks up a list of assertions, grouping instructions which should be
+ tested in the same scope (consecutive DAG and NOT instructions).
+ """
+ splitAssertions = []
+ lastVariant = None
+ for assertion in assertions:
+ if (assertion.variant == lastVariant and
+ assertion.variant in [TestAssertion.Variant.DAG, TestAssertion.Variant.Not]):
+ splitAssertions[-1].append(assertion)
+ else:
+ splitAssertions.append([assertion])
+ lastVariant = assertion.variant
+ return splitAssertions
+
+def findMatchingLine(assertion, c1Pass, scope, variables, excludeLines=[]):
+ """ Finds the first line in `c1Pass` which matches `assertion`.
+
+ Scan only lines numbered between `scope.start` and `scope.end` and not on the
+ `excludeLines` list.
+
+ Returns the index of the `c1Pass` line matching the assertion and variables
+ values after the match.
+
+ Raises MatchFailedException if no such `c1Pass` line can be found.
"""
- i = 0
- while i < len(lines) and lines[i].variant == variant:
- i += 1
- return lines[:i], lines[i:]
+ for i in range(scope.start, scope.end):
+ if i in excludeLines: continue
+ newVariables = MatchLines(assertion, c1Pass.body[i], variables)
+ if newVariables is not None:
+ return MatchInfo(MatchScope(i, i), newVariables)
+ raise MatchFailedException(assertion, scope.start)
-def __nextIndependentChecks(checkLines):
- """ Extracts the first sequence of check lines which are independent of each
- other's match location, i.e. either consecutive DAG lines or a single
- InOrder line. Any Not lines preceeding this sequence are also extracted.
+def matchDagGroup(assertions, c1Pass, scope, variables):
+ """ Attempts to find matching `c1Pass` lines for a group of DAG assertions.
+
+ Assertions are matched in the list order and variable values propagated. Only
+ lines in `scope` are scanned and each line can only match one assertion.
+
+ Returns the range of `c1Pass` lines covered by this group (min/max of matching
+ line numbers) and the variable values after the match of the last assertion.
+
+ Raises MatchFailedException when an assertion cannot be satisfied.
"""
- notChecks, checkLines = __splitByVariant(checkLines, TestAssertion.Variant.Not)
- if not checkLines:
- return notChecks, [], []
-
- head, tail = __headAndTail(checkLines)
- if head.variant == TestAssertion.Variant.InOrder:
- return notChecks, [head], tail
- else:
- assert head.variant == TestAssertion.Variant.DAG
- independentChecks, checkLines = __splitByVariant(checkLines, TestAssertion.Variant.DAG)
- return notChecks, independentChecks, checkLines
-
-def __findFirstMatch(checkLine, outputLines, startLineNo, lineFilter, varState):
- """ If successful, returns the line number of the first output line matching
- the check line and the updated variable state. Otherwise returns -1 and
- None, respectively. The 'lineFilter' parameter can be used to supply a
- list of line numbers (counting from 1) which should be skipped.
- """
- matchLineNo = startLineNo
- for outputLine in outputLines:
- if matchLineNo not in lineFilter:
- newVarState = MatchLines(checkLine, outputLine, varState)
- if newVarState is not None:
- return matchLineNo, newVarState
- matchLineNo += 1
- return -1, None
-
-def __matchIndependentChecks(checkLines, outputLines, startLineNo, varState):
- """ Matches the given positive check lines against the output in order of
- appearance. Variable state is propagated but the scope of the search
- remains the same for all checks. Each output line can only be matched
- once. If all check lines are matched, the resulting variable state is
- returned together with the remaining output. The function also returns
- output lines which appear before either of the matched lines so they can
- be tested against Not checks.
- """
- # If no checks are provided, skip over the entire output.
- if not checkLines:
- return outputLines, [], startLineNo + len(outputLines), varState
-
- # Keep track of which lines have been matched.
matchedLines = []
+ for assertion in assertions:
+ assert assertion.variant == TestAssertion.Variant.DAG
+ match = findMatchingLine(assertion, c1Pass, scope, variables, matchedLines)
+ variables = match.variables
+ assert match.scope.start == match.scope.end
+ assert match.scope.start not in matchedLines
+ matchedLines.append(match.scope.start)
+ return MatchInfo(MatchScope(min(matchedLines), max(matchedLines)), variables)
- # Find first unused output line which matches each check line.
- for checkLine in checkLines:
- matchLineNo, varState = \
- __findFirstMatch(checkLine, outputLines, startLineNo, matchedLines, varState)
- if varState is None:
- Logger.testFailed("Could not match check line \"" + checkLine.originalText + "\" " +
- "starting from output line " + str(startLineNo),
- checkLine.fileName, checkLine.lineNo)
- matchedLines.append(matchLineNo)
+def testNotGroup(assertions, c1Pass, scope, variables):
+ """ Verifies that none of the given NOT assertions matches a line inside
+ the given `scope` of `c1Pass` lines.
- # Return new variable state and the output lines which lie outside the
- # match locations of this independent group.
- minMatchLineNo = min(matchedLines)
- maxMatchLineNo = max(matchedLines)
- preceedingLines = outputLines[:minMatchLineNo - startLineNo]
- remainingLines = outputLines[maxMatchLineNo - startLineNo + 1:]
- return preceedingLines, remainingLines, maxMatchLineNo + 1, varState
-
-def __matchNotLines(checkLines, outputLines, startLineNo, varState):
- """ Makes sure that the given check lines do not match any of the given output
- lines. Variable state does not change.
+ Raises MatchFailedException if an assertion matches a line in the scope.
"""
- for checkLine in checkLines:
- assert checkLine.variant == TestAssertion.Variant.Not
- matchLineNo, matchVarState = \
- __findFirstMatch(checkLine, outputLines, startLineNo, [], varState)
- if matchVarState is not None:
- Logger.testFailed("CHECK-NOT line \"" + checkLine.originalText + "\" matches output line " + \
- str(matchLineNo), checkLine.fileName, checkLine.lineNo)
+ for i in range(scope.start, scope.end):
+ line = c1Pass.body[i]
+ for assertion in assertions:
+ assert assertion.variant == TestAssertion.Variant.Not
+ if MatchLines(assertion, line, variables) is not None:
+ raise MatchFailedException(assertion, i)
-def __matchGroups(checkGroup, outputGroup):
- """ Matches the check lines in this group against an output group. It is
- responsible for running the checks in the right order and scope, and
- for propagating the variable state between the check lines.
+def MatchTestCase(testCase, c1Pass):
+ """ Runs a test case against a C1visualizer graph dump.
+
+ Raises MatchFailedException when an assertion cannot be satisfied.
"""
- varState = ImmutableDict()
- checkLines = checkGroup.assertions
- outputLines = outputGroup.body
- startLineNo = outputGroup.startLineNo
+ assert testCase.name == c1Pass.name
- while checkLines:
- # Extract the next sequence of location-independent checks to be matched.
- notChecks, independentChecks, checkLines = __nextIndependentChecks(checkLines)
+ matchFrom = 0
+ variables = ImmutableDict()
+ c1Length = len(c1Pass.body)
- # Match the independent checks.
- notOutput, outputLines, newStartLineNo, newVarState = \
- __matchIndependentChecks(independentChecks, outputLines, startLineNo, varState)
+ # NOT assertions are verified retrospectively, once the scope is known.
+ pendingNotAssertions = None
- # Run the Not checks against the output lines which lie between the last
- # two independent groups or the bounds of the output.
- __matchNotLines(notChecks, notOutput, startLineNo, varState)
+ # Prepare assertions by grouping those that are verified in the same scope.
+ # We also add None as an EOF assertion that will set scope for NOTs.
+ assertionGroups = splitIntoGroups(testCase.assertions)
+ assertionGroups.append(None)
- # Update variable state.
- startLineNo = newStartLineNo
- varState = newVarState
+ for assertionGroup in assertionGroups:
+ if assertionGroup is None:
+ # EOF marker always matches the last+1 line of c1Pass.
+ match = MatchInfo(MatchScope(c1Length, c1Length), None)
+ elif assertionGroup[0].variant == TestAssertion.Variant.Not:
+ # NOT assertions will be tested together with the next group.
+ assert not pendingNotAssertions
+ pendingNotAssertions = assertionGroup
+ continue
+ elif assertionGroup[0].variant == TestAssertion.Variant.InOrder:
+ # Single in-order assertion. Find the first line that matches.
+ assert len(assertionGroup) == 1
+ scope = MatchScope(matchFrom, c1Length)
+ match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
+ else:
+ # A group of DAG assertions. Match them all starting from the same point.
+ assert assertionGroup[0].variant == TestAssertion.Variant.DAG
+ scope = MatchScope(matchFrom, c1Length)
+ match = matchDagGroup(assertionGroup, c1Pass, scope, variables)
+
+ if pendingNotAssertions:
+ # Previous group were NOT assertions. Make sure they don't match any lines
+ # in the [matchFrom, match.start) scope.
+ scope = MatchScope(matchFrom, match.scope.start)
+ testNotGroup(pendingNotAssertions, c1Pass, scope, variables)
+ pendingNotAssertions = None
+
+ # Update state.
+ assert matchFrom <= match.scope.end
+ matchFrom = match.scope.end + 1
+ variables = match.variables
def MatchFiles(checkerFile, c1File):
for testCase in checkerFile.testCases:
@@ -141,8 +152,18 @@
# match a check group against the first output group of the same name.
c1Pass = c1File.findPass(testCase.name)
if c1Pass is None:
- Logger.fail("Test case \"" + testCase.name + "\" not found in the C1visualizer output",
+ Logger.fail("Test case \"{}\" not found in the CFG file".format(testCase.name),
testCase.fileName, testCase.startLineNo)
+
Logger.startTest(testCase.name)
- __matchGroups(testCase, c1Pass)
- Logger.testPassed()
+ try:
+ MatchTestCase(testCase, c1Pass)
+ Logger.testPassed()
+ except MatchFailedException as e:
+ lineNo = c1Pass.startLineNo + e.lineNo
+ if e.assertion.variant == TestAssertion.Variant.Not:
+ Logger.testFailed("NOT assertion matched line {}".format(lineNo),
+ e.assertion.fileName, e.assertion.lineNo)
+ else:
+ Logger.testFailed("Assertion could not be matched starting from line {}".format(lineNo),
+ e.assertion.fileName, e.assertion.lineNo)
diff --git a/tools/checker/match/test.py b/tools/checker/match/test.py
index 97725ad..e4dd784 100644
--- a/tools/checker/match/test.py
+++ b/tools/checker/match/test.py
@@ -18,7 +18,7 @@
from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
from file_format.checker.parser import ParseCheckerStream, ParseCheckerAssertion
from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
-from match.file import MatchFiles
+from match.file import MatchTestCase, MatchFailedException
from match.line import MatchLines
import io
@@ -38,68 +38,71 @@
ToUnicode(c1String),
ImmutableDict(varState))
- def matches(self, checkerString, c1String, varState={}):
- return self.tryMatch(checkerString, c1String, varState) is not None
+ def assertMatches(self, checkerString, c1String, varState={}):
+ self.assertIsNotNone(self.tryMatch(checkerString, c1String, varState))
+
+ def assertDoesNotMatch(self, checkerString, c1String, varState={}):
+ self.assertIsNone(self.tryMatch(checkerString, c1String, varState))
def test_TextAndWhitespace(self):
- self.assertTrue(self.matches("foo", "foo"))
- self.assertTrue(self.matches("foo", " foo "))
- self.assertTrue(self.matches("foo", "foo bar"))
- self.assertFalse(self.matches("foo", "XfooX"))
- self.assertFalse(self.matches("foo", "zoo"))
+ self.assertMatches("foo", "foo")
+ self.assertMatches("foo", " foo ")
+ self.assertMatches("foo", "foo bar")
+ self.assertDoesNotMatch("foo", "XfooX")
+ self.assertDoesNotMatch("foo", "zoo")
- self.assertTrue(self.matches("foo bar", "foo bar"))
- self.assertTrue(self.matches("foo bar", "abc foo bar def"))
- self.assertTrue(self.matches("foo bar", "foo foo bar bar"))
+ self.assertMatches("foo bar", "foo bar")
+ self.assertMatches("foo bar", "abc foo bar def")
+ self.assertMatches("foo bar", "foo foo bar bar")
- self.assertTrue(self.matches("foo bar", "foo X bar"))
- self.assertFalse(self.matches("foo bar", "foo Xbar"))
+ self.assertMatches("foo bar", "foo X bar")
+ self.assertDoesNotMatch("foo bar", "foo Xbar")
def test_Pattern(self):
- self.assertTrue(self.matches("foo{{A|B}}bar", "fooAbar"))
- self.assertTrue(self.matches("foo{{A|B}}bar", "fooBbar"))
- self.assertFalse(self.matches("foo{{A|B}}bar", "fooCbar"))
+ self.assertMatches("foo{{A|B}}bar", "fooAbar")
+ self.assertMatches("foo{{A|B}}bar", "fooBbar")
+ self.assertDoesNotMatch("foo{{A|B}}bar", "fooCbar")
def test_VariableReference(self):
- self.assertTrue(self.matches("foo<<X>>bar", "foobar", {"X": ""}))
- self.assertTrue(self.matches("foo<<X>>bar", "fooAbar", {"X": "A"}))
- self.assertTrue(self.matches("foo<<X>>bar", "fooBbar", {"X": "B"}))
- self.assertFalse(self.matches("foo<<X>>bar", "foobar", {"X": "A"}))
- self.assertFalse(self.matches("foo<<X>>bar", "foo bar", {"X": "A"}))
+ self.assertMatches("foo<<X>>bar", "foobar", {"X": ""})
+ self.assertMatches("foo<<X>>bar", "fooAbar", {"X": "A"})
+ self.assertMatches("foo<<X>>bar", "fooBbar", {"X": "B"})
+ self.assertDoesNotMatch("foo<<X>>bar", "foobar", {"X": "A"})
+ self.assertDoesNotMatch("foo<<X>>bar", "foo bar", {"X": "A"})
with self.assertRaises(CheckerException):
- self.assertTrue(self.matches("foo<<X>>bar", "foobar", {}))
+ self.tryMatch("foo<<X>>bar", "foobar", {})
def test_VariableDefinition(self):
- self.assertTrue(self.matches("foo<<X:A|B>>bar", "fooAbar"))
- self.assertTrue(self.matches("foo<<X:A|B>>bar", "fooBbar"))
- self.assertFalse(self.matches("foo<<X:A|B>>bar", "fooCbar"))
+ self.assertMatches("foo<<X:A|B>>bar", "fooAbar")
+ self.assertMatches("foo<<X:A|B>>bar", "fooBbar")
+ self.assertDoesNotMatch("foo<<X:A|B>>bar", "fooCbar")
env = self.tryMatch("foo<<X:A.*B>>bar", "fooABbar", {})
self.assertEqual(env, {"X": "AB"})
env = self.tryMatch("foo<<X:A.*B>>bar", "fooAxxBbar", {})
self.assertEqual(env, {"X": "AxxB"})
- self.assertTrue(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooAbarAbaz"))
- self.assertTrue(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooBbarBbaz"))
- self.assertFalse(self.matches("foo<<X:A|B>>bar<<X>>baz", "fooAbarBbaz"))
+ self.assertMatches("foo<<X:A|B>>bar<<X>>baz", "fooAbarAbaz")
+ self.assertMatches("foo<<X:A|B>>bar<<X>>baz", "fooBbarBbaz")
+ self.assertDoesNotMatch("foo<<X:A|B>>bar<<X>>baz", "fooAbarBbaz")
def test_NoVariableRedefinition(self):
with self.assertRaises(CheckerException):
- self.matches("<<X:...>><<X>><<X:...>><<X>>", "foofoobarbar")
+ self.tryMatch("<<X:...>><<X>><<X:...>><<X>>", "foofoobarbar")
def test_EnvNotChangedOnPartialMatch(self):
env = {"Y": "foo"}
- self.assertFalse(self.matches("<<X:A>>bar", "Abaz", env))
+ self.assertDoesNotMatch("<<X:A>>bar", "Abaz", env)
self.assertFalse("X" in env.keys())
def test_VariableContentEscaped(self):
- self.assertTrue(self.matches("<<X:..>>foo<<X>>", ".*foo.*"))
- self.assertFalse(self.matches("<<X:..>>foo<<X>>", ".*fooAAAA"))
+ self.assertMatches("<<X:..>>foo<<X>>", ".*foo.*")
+ self.assertDoesNotMatch("<<X:..>>foo<<X>>", ".*fooAAAA")
class MatchFiles_Test(unittest.TestCase):
- def matches(self, checkerString, c1String):
+ def assertMatches(self, checkerString, c1String):
checkerString = \
"""
// CHECK-START: MyMethod MyPass
@@ -119,22 +122,24 @@
"""
checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(ToUnicode(checkerString)))
c1File = ParseC1visualizerStream("<c1-file>", io.StringIO(ToUnicode(c1String)))
- try:
- MatchFiles(checkerFile, c1File)
- return True
- except CheckerException:
- return False
+ assert len(checkerFile.testCases) == 1
+ assert len(c1File.passes) == 1
+ MatchTestCase(checkerFile.testCases[0], c1File.passes[0])
+
+ def assertDoesNotMatch(self, checkerString, c1String):
+ with self.assertRaises(MatchFailedException):
+ self.assertMatches(checkerString, c1String)
def test_Text(self):
- self.assertTrue(self.matches( "// CHECK: foo bar", "foo bar"))
- self.assertFalse(self.matches("// CHECK: foo bar", "abc def"))
+ self.assertMatches("// CHECK: foo bar", "foo bar")
+ self.assertDoesNotMatch("// CHECK: foo bar", "abc def")
def test_Pattern(self):
- self.assertTrue(self.matches( "// CHECK: abc {{de.}}", "abc de#"))
- self.assertFalse(self.matches("// CHECK: abc {{de.}}", "abc d#f"))
+ self.assertMatches("// CHECK: abc {{de.}}", "abc de#")
+ self.assertDoesNotMatch("// CHECK: abc {{de.}}", "abc d#f")
def test_Variables(self):
- self.assertTrue(self.matches(
+ self.assertMatches(
"""
// CHECK: foo<<X:.>>bar
// CHECK: abc<<X>>def
@@ -142,8 +147,8 @@
"""
foo0bar
abc0def
- """))
- self.assertTrue(self.matches(
+ """)
+ self.assertMatches(
"""
// CHECK: foo<<X:([0-9]+)>>bar
// CHECK: abc<<X>>def
@@ -153,8 +158,8 @@
foo1234bar
abc1234def
### 1234 ###
- """))
- self.assertFalse(self.matches(
+ """)
+ self.assertDoesNotMatch(
"""
// CHECK: foo<<X:([0-9]+)>>bar
// CHECK: abc<<X>>def
@@ -162,16 +167,16 @@
"""
foo1234bar
abc1235def
- """))
+ """)
def test_WholeWordMustMatch(self):
- self.assertTrue(self.matches( "// CHECK: b{{.}}r", "abc bar def"))
- self.assertFalse(self.matches( "// CHECK: b{{.}}r", "abc Xbar def"))
- self.assertFalse(self.matches( "// CHECK: b{{.}}r", "abc barX def"))
- self.assertFalse(self.matches( "// CHECK: b{{.}}r", "abc b r def"))
+ self.assertMatches("// CHECK: b{{.}}r", "abc bar def")
+ self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc Xbar def")
+ self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc barX def")
+ self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc b r def")
def test_InOrderAssertions(self):
- self.assertTrue(self.matches(
+ self.assertMatches(
"""
// CHECK: foo
// CHECK: bar
@@ -179,8 +184,8 @@
"""
foo
bar
- """))
- self.assertFalse(self.matches(
+ """)
+ self.assertDoesNotMatch(
"""
// CHECK: foo
// CHECK: bar
@@ -188,10 +193,10 @@
"""
bar
foo
- """))
+ """)
def test_DagAssertions(self):
- self.assertTrue(self.matches(
+ self.assertMatches(
"""
// CHECK-DAG: foo
// CHECK-DAG: bar
@@ -199,8 +204,8 @@
"""
foo
bar
- """))
- self.assertTrue(self.matches(
+ """)
+ self.assertMatches(
"""
// CHECK-DAG: foo
// CHECK-DAG: bar
@@ -208,10 +213,10 @@
"""
bar
foo
- """))
+ """)
def test_DagAssertionsScope(self):
- self.assertTrue(self.matches(
+ self.assertMatches(
"""
// CHECK: foo
// CHECK-DAG: abc
@@ -223,8 +228,8 @@
def
abc
bar
- """))
- self.assertFalse(self.matches(
+ """)
+ self.assertDoesNotMatch(
"""
// CHECK: foo
// CHECK-DAG: abc
@@ -236,8 +241,8 @@
abc
bar
def
- """))
- self.assertFalse(self.matches(
+ """)
+ self.assertDoesNotMatch(
"""
// CHECK: foo
// CHECK-DAG: abc
@@ -249,26 +254,26 @@
def
bar
abc
- """))
+ """)
def test_NotAssertions(self):
- self.assertTrue(self.matches(
+ self.assertMatches(
"""
// CHECK-NOT: foo
""",
"""
abc
def
- """))
- self.assertFalse(self.matches(
+ """)
+ self.assertDoesNotMatch(
"""
// CHECK-NOT: foo
""",
"""
abc foo
def
- """))
- self.assertFalse(self.matches(
+ """)
+ self.assertDoesNotMatch(
"""
// CHECK-NOT: foo
// CHECK-NOT: bar
@@ -276,10 +281,10 @@
"""
abc
def bar
- """))
+ """)
def test_NotAssertionsScope(self):
- self.assertTrue(self.matches(
+ self.assertMatches(
"""
// CHECK: abc
// CHECK-NOT: foo
@@ -288,8 +293,8 @@
"""
abc
def
- """))
- self.assertTrue(self.matches(
+ """)
+ self.assertMatches(
"""
// CHECK: abc
// CHECK-NOT: foo
@@ -299,8 +304,8 @@
abc
def
foo
- """))
- self.assertFalse(self.matches(
+ """)
+ self.assertDoesNotMatch(
"""
// CHECK: abc
// CHECK-NOT: foo
@@ -310,10 +315,10 @@
abc
foo
def
- """))
+ """)
def test_LineOnlyMatchesOnce(self):
- self.assertTrue(self.matches(
+ self.assertMatches(
"""
// CHECK-DAG: foo
// CHECK-DAG: foo
@@ -322,8 +327,8 @@
foo
abc
foo
- """))
- self.assertFalse(self.matches(
+ """)
+ self.assertDoesNotMatch(
"""
// CHECK-DAG: foo
// CHECK-DAG: foo
@@ -332,4 +337,4 @@
foo
abc
bar
- """))
+ """)