Merge "Make our CHECK logging more useful on the target..." into dalvik-dev
diff --git a/Android.mk b/Android.mk
index abdcc06..ccf9b50 100644
--- a/Android.mk
+++ b/Android.mk
@@ -88,7 +88,7 @@
adb shell touch /sdcard/test-art-target-gtest
adb shell rm /sdcard/test-art-target-gtest
adb shell sh -c "$(foreach file,$(sort $(ART_TARGET_TEST_EXECUTABLES)), /system/bin/$(notdir $(file)) &&) touch /sdcard/test-art-target-gtest"
- (adb pull /sdcard/test-art-target-gtest /tmp/ && echo test-art-target-gtest PASSED) || echo test-art-target-gtest FAILED
+ $(hide) (adb pull /sdcard/test-art-target-gtest /tmp/ && echo test-art-target-gtest PASSED) || echo test-art-target-gtest FAILED
$(hide) rm /tmp/test-art-target-gtest
.PHONY: test-art-target-oat
@@ -99,7 +99,7 @@
adb shell touch /sdcard/test-art-target-oat-HelloWorld
adb shell rm /sdcard/test-art-target-oat-HelloWorld
adb shell sh -c "oatexecd -Xbootclasspath:/system/framework/core.jar -Xbootimage:/system/framework/boot.oat -classpath /system/framework/art-test-dex-HelloWorld.jar -Ximage:/system/framework/art-test-dex-HelloWorld.oat HelloWorld && touch /sdcard/test-art-target-oat-HelloWorld"
- (adb pull /sdcard/test-art-target-oat-HelloWorld /tmp/ && echo test-art-target-oat-HelloWorld PASSED) || (echo test-art-target-oat-HelloWorld FAILED && exit 1)
+ $(hide) (adb pull /sdcard/test-art-target-oat-HelloWorld /tmp/ && echo test-art-target-oat-HelloWorld PASSED) || (echo test-art-target-oat-HelloWorld FAILED && exit 1)
$(hide) rm /tmp/test-art-target-oat-HelloWorld
.PHONY: test-art-target-oat-Fibonacci
@@ -107,7 +107,7 @@
adb shell touch /sdcard/test-art-target-oat-Fibonacci
adb shell rm /sdcard/test-art-target-oat-Fibonacci
adb shell sh -c "oatexecd -Xbootclasspath:/system/framework/core.jar -Xbootimage:/system/framework/boot.oat -classpath /system/framework/art-test-dex-Fibonacci.jar -Ximage:/system/framework/art-test-dex-Fibonacci.oat Fibonacci 10 && touch /sdcard/test-art-target-oat-Fibonacci"
- (adb pull /sdcard/test-art-target-oat-Fibonacci /tmp/ && echo test-art-target-oat-Fibonacci PASSED) || (echo test-art-target-oat-Fibonacci FAILED && exit 1)
+ $(hide) (adb pull /sdcard/test-art-target-oat-Fibonacci /tmp/ && echo test-art-target-oat-Fibonacci PASSED) || (echo test-art-target-oat-Fibonacci FAILED && exit 1)
$(hide) rm /tmp/test-art-target-oat-Fibonacci
.PHONY: dump-boot-oat
diff --git a/build/Android.common.mk b/build/Android.common.mk
index b99a876..ae96dab 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -106,6 +106,8 @@
src/java_lang_System.cc \
src/java_lang_Thread.cc \
src/java_lang_Throwable.cc \
+ src/java_lang_reflect_Field.cc \
+ src/java_lang_reflect_Method.cc \
src/java_util_concurrent_atomic_AtomicLong.cc \
src/jni_compiler.cc \
src/jni_internal.cc \
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index c4dbbba..93d08b2 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -1810,28 +1810,27 @@
void ArmAssembler::ExceptionPoll(ManagedRegister mscratch) {
ArmManagedRegister scratch = mscratch.AsArm();
- ArmExceptionSlowPath* slow = new ArmExceptionSlowPath();
+ ArmExceptionSlowPath* slow = new ArmExceptionSlowPath(scratch);
buffer_.EnqueueSlowPath(slow);
LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
TR, Thread::ExceptionOffset().Int32Value());
cmp(scratch.AsCoreRegister(), ShifterOperand(0));
b(slow->Entry(), NE);
- Bind(slow->Continuation());
}
void ArmExceptionSlowPath::Emit(Assembler* sasm) {
ArmAssembler* sp_asm = down_cast<ArmAssembler*>(sasm);
#define __ sp_asm->
__ Bind(&entry_);
- // Pass top of stack as argument
- __ mov(R0, ShifterOperand(SP));
- __ LoadFromOffset(kLoadWord, R12, TR,
- Thread::ExceptionEntryPointOffset().Int32Value());
- // Note: assume that link register will be spilled/filled on method entry/exit
+
+ // Pass exception object as argument
+ // Don't care about preserving R0 as this call won't return
+ __ mov(R0, ShifterOperand(scratch_.AsCoreRegister()));
+ // Set up call to Thread::Current()->pDeliverException
+ __ LoadFromOffset(kLoadWord, R12, TR, OFFSETOF_MEMBER(Thread, pDeliverException));
__ blx(R12);
- // TODO: this call should never return as it should make a long jump to
- // the appropriate catch block
- __ b(&continuation_);
+ // Call never returns
+ __ bkpt(0);
#undef __
}
diff --git a/src/assembler_arm.h b/src/assembler_arm.h
index e58eb92..1a722f7 100644
--- a/src/assembler_arm.h
+++ b/src/assembler_arm.h
@@ -609,8 +609,10 @@
// Slowpath entered when Thread::Current()->_exception is non-null
class ArmExceptionSlowPath : public SlowPath {
public:
- ArmExceptionSlowPath() {}
+ ArmExceptionSlowPath(ArmManagedRegister scratch) : scratch_(scratch) {}
virtual void Emit(Assembler *sp_asm);
+ private:
+ const ArmManagedRegister scratch_;
};
// Slowpath entered when Thread::Current()->_suspend_count is non-zero
diff --git a/src/assembler_x86.cc b/src/assembler_x86.cc
index 892bf76..e126d88 100644
--- a/src/assembler_x86.cc
+++ b/src/assembler_x86.cc
@@ -1658,8 +1658,10 @@
// TODO: place reference map on call
}
-void X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister) {
- UNIMPLEMENTED(FATAL);
+void X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
+ Register scratch = mscratch.AsX86().AsCpuRegister();
+ movl(scratch, Address(ESP, base));
+ call(Address(scratch, offset));
}
void X86Assembler::Call(ThreadOffset offset, ManagedRegister mscratch) {
@@ -1713,7 +1715,6 @@
buffer_.EnqueueSlowPath(slow);
fs()->cmpl(Address::Absolute(Thread::ExceptionOffset()), Immediate(0));
j(kNotEqual, slow->Entry());
- Bind(slow->Continuation());
}
void X86ExceptionSlowPath::Emit(Assembler *sasm) {
@@ -1721,14 +1722,11 @@
#define __ sp_asm->
__ Bind(&entry_);
// NB the return value is dead
- // Pass top of stack as argument
- __ pushl(ESP);
- __ fs()->call(Address::Absolute(Thread::ExceptionEntryPointOffset()));
- // TODO: this call should never return as it should make a long jump to
- // the appropriate catch block
- // Release argument
- __ addl(ESP, Immediate(kPointerSize));
- __ jmp(&continuation_);
+ // Pass exception as argument in EAX
+ __ fs()->movl(EAX, Address::Absolute(Thread::ExceptionOffset()));
+ __ fs()->call(Address::Absolute(OFFSETOF_MEMBER(Thread, pDeliverException)));
+ // this call should never return
+ __ int3();
#undef __
}
diff --git a/src/calling_convention.h b/src/calling_convention.h
index 4464609..a1d1b32 100644
--- a/src/calling_convention.h
+++ b/src/calling_convention.h
@@ -139,13 +139,13 @@
virtual uint32_t CoreSpillMask() const = 0;
virtual uint32_t FpSpillMask() const = 0;
- // Returns true if the register will be clobbered by an outgoing
- // argument value.
- virtual bool IsOutArgRegister(ManagedRegister reg) = 0;
+ // Returns true if the method register will have been clobbered during argument
+ // set up
+ virtual bool IsMethodRegisterCrushedPreCall() = 0;
// Iterator interface
bool HasNext();
- void Next();
+ virtual void Next();
bool IsCurrentParamAReference();
size_t CurrentParamSize();
virtual bool IsCurrentParamInRegister() = 0;
diff --git a/src/calling_convention_arm.cc b/src/calling_convention_arm.cc
index f8e8f3d..6e664ca 100644
--- a/src/calling_convention_arm.cc
+++ b/src/calling_convention_arm.cc
@@ -100,13 +100,54 @@
// JNI calling convention
ArmJniCallingConvention::ArmJniCallingConvention(Method* method) : JniCallingConvention(method) {
- for (int i = R4; i < R12; i++) {
- callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(static_cast<Register>(i)));
+ // Compute padding to ensure longs and doubles are not split in AAPCS
+ // TODO: in terms of outgoing argument size this may be overly generous
+ // due to padding appearing in the registers
+ size_t padding = 0;
+ size_t check = method->IsStatic() ? 1 : 0;
+ for(size_t i = 0; i < method->NumArgs(); i++) {
+ if(((i & 1) == check) && method->IsParamALongOrDouble(i)) {
+ padding += 4;
+ }
}
- // TODO: VFP
- // for (SRegister i = S16; i <= S31; i++) {
- // callee_save_regs_.push_back(ArmManagedRegister::FromSRegister(i));
- // }
+ padding_ = padding;
+ if (method->IsSynchronized()) {
+ // Preserve callee saves that may be clobbered during monitor enter where
+ // we copy across R0 to R3
+ if (method->NumArgs() > 0) {
+ callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R4));
+ if (method->NumArgs() > 1) {
+ callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R5));
+ if (method->NumArgs() > 2) {
+ callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R6));
+ if (method->NumArgs() > 3) {
+ callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R7));
+ }
+ }
+ }
+ }
+ }
+}
+
+uint32_t ArmJniCallingConvention::CoreSpillMask() const {
+ // Compute spill mask to agree with callee saves initialized in the constructor
+ uint32_t result = 0;
+ Method* method = GetMethod();
+ if (method->IsSynchronized()) {
+ if (method->NumArgs() > 0) {
+ result |= 1 << R4;
+ if (method->NumArgs() > 1) {
+ result |= 1 << R5;
+ if (method->NumArgs() > 2) {
+ result |= 1 << R6;
+ if (method->NumArgs() > 3) {
+ result |= 1 << R7;
+ }
+ }
+ }
+ }
+ }
+ return result;
}
size_t ArmJniCallingConvention::FrameSize() {
@@ -120,16 +161,7 @@
}
size_t ArmJniCallingConvention::OutArgSize() {
- const Method* method = GetMethod();
- size_t padding; // padding to ensure longs and doubles are not split in AAPCS
- if (method->IsStatic()) {
- padding = (method->NumArgs() > 1) && !method->IsParamALongOrDouble(0) &&
- method->IsParamALongOrDouble(1) ? 4 : 0;
- } else {
- padding = (method->NumArgs() > 2) && !method->IsParamALongOrDouble(1) &&
- method->IsParamALongOrDouble(2) ? 4 : 0;
- }
- return RoundUp(NumberOfOutgoingStackArgs() * kPointerSize + padding,
+ return RoundUp(NumberOfOutgoingStackArgs() * kPointerSize + padding_,
kStackAlignment);
}
@@ -139,28 +171,27 @@
}
// Will reg be crushed by an outgoing argument?
-bool ArmJniCallingConvention::IsOutArgRegister(ManagedRegister mreg) {
- Register reg = mreg.AsArm().AsCoreRegister();
- return reg >= R0 && reg <= R3;
+bool ArmJniCallingConvention::IsMethodRegisterCrushedPreCall() {
+ return true; // The method register R0 is always clobbered by the JNIEnv
}
-// JniCallingConvention ABI follows AAPCS
-//
-// In processing each parameter, we know that IsCurrentParamInRegister()
-// or IsCurrentParamOnStack() will be called first.
-// Both functions will ensure that we conform to AAPCS.
-//
-bool ArmJniCallingConvention::IsCurrentParamInRegister() {
- // AAPCS processing
+// JniCallingConvention ABI follows AAPCS where longs and doubles must occur
+// in even register numbers and stack slots
+void ArmJniCallingConvention::Next() {
+ JniCallingConvention::Next();
Method* method = GetMethod();
- int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
- if ((itr_args_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
+ size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
+ if ((itr_args_ >= 2) &&
+ (arg_pos < GetMethod()->NumArgs()) &&
+ method->IsParamALongOrDouble(arg_pos)) {
// itr_slots_ needs to be an even number, according to AAPCS.
if ((itr_slots_ & 0x1u) != 0) {
itr_slots_++;
}
}
+}
+bool ArmJniCallingConvention::IsCurrentParamInRegister() {
return itr_slots_ < 4;
}
@@ -187,7 +218,7 @@
FrameOffset ArmJniCallingConvention::CurrentParamStackOffset() {
CHECK_GE(itr_slots_, 4u);
return FrameOffset(displacement_.Int32Value() - OutArgSize()
- + ((itr_slots_ - 4) * kPointerSize));
+ + ((itr_slots_ - 4) * kPointerSize));
}
size_t ArmJniCallingConvention::NumberOfOutgoingStackArgs() {
diff --git a/src/calling_convention_arm.h b/src/calling_convention_arm.h
index 8e5d4c2..4415254 100644
--- a/src/calling_convention_arm.h
+++ b/src/calling_convention_arm.h
@@ -35,19 +35,18 @@
virtual ManagedRegister ReturnRegister();
virtual ManagedRegister InterproceduralScratchRegister();
// JNI calling convention
+ virtual void Next(); // Override default behavior for AAPCS
virtual size_t FrameSize();
virtual size_t ReturnPcOffset();
virtual size_t OutArgSize();
virtual const std::vector<ManagedRegister>& CalleeSaveRegisters() const {
return callee_save_regs_;
}
- virtual uint32_t CoreSpillMask() const {
- return 0x0FF0; // R4 to R12
- }
+ virtual uint32_t CoreSpillMask() const;
virtual uint32_t FpSpillMask() const {
- return 0;
+ return 0; // Floats aren't spilled in JNI down call
}
- virtual bool IsOutArgRegister(ManagedRegister reg);
+ virtual bool IsMethodRegisterCrushedPreCall();
virtual bool IsCurrentParamInRegister();
virtual bool IsCurrentParamOnStack();
virtual ManagedRegister CurrentParamRegister();
@@ -60,6 +59,9 @@
// TODO: these values aren't unique and can be shared amongst instances
std::vector<ManagedRegister> callee_save_regs_;
+ // Padding to ensure longs and doubles are not split in AAPCS
+ size_t padding_;
+
DISALLOW_COPY_AND_ASSIGN(ArmJniCallingConvention);
};
diff --git a/src/calling_convention_x86.cc b/src/calling_convention_x86.cc
index af464af..a8f5778 100644
--- a/src/calling_convention_x86.cc
+++ b/src/calling_convention_x86.cc
@@ -41,7 +41,7 @@
// Managed runtime calling convention
ManagedRegister X86ManagedRuntimeCallingConvention::MethodRegister() {
- return X86ManagedRegister::FromCpuRegister(EDI);
+ return X86ManagedRegister::FromCpuRegister(EAX);
}
bool X86ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
@@ -86,8 +86,8 @@
return FrameSize() - kPointerSize;
}
-bool X86JniCallingConvention::IsOutArgRegister(ManagedRegister) {
- return false; // Everything is passed by stack
+bool X86JniCallingConvention::IsMethodRegisterCrushedPreCall() {
+ return GetMethod()->IsSynchronized(); // Monitor enter crushes the method register
}
bool X86JniCallingConvention::IsCurrentParamInRegister() {
diff --git a/src/calling_convention_x86.h b/src/calling_convention_x86.h
index 8230754..8c55472 100644
--- a/src/calling_convention_x86.h
+++ b/src/calling_convention_x86.h
@@ -49,7 +49,7 @@
virtual uint32_t FpSpillMask() const {
return 0;
}
- virtual bool IsOutArgRegister(ManagedRegister reg);
+ virtual bool IsMethodRegisterCrushedPreCall();
virtual bool IsCurrentParamInRegister();
virtual bool IsCurrentParamOnStack();
virtual ManagedRegister CurrentParamRegister();
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index 652899c..e729b51 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -683,9 +683,9 @@
void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
{
loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pThrowException), rLR);
+ OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
- callNoUnwindHelper(cUnit, rLR); // art_throw_exception(exception);
+ callNoUnwindHelper(cUnit, rLR); // art_deliver_exception(exception);
}
static void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
diff --git a/src/context_arm.cc b/src/context_arm.cc
index 387b71d..85ada9d 100644
--- a/src/context_arm.cc
+++ b/src/context_arm.cc
@@ -8,9 +8,11 @@
namespace arm {
ArmContext::ArmContext() {
+#ifndef NDEBUG
for (int i=0; i < 16; i++) {
- gprs_[i] = 0xEBAD6070;
+ gprs_[i] = 0xEBAD6070+i;
}
+#endif
memset(fprs_, 0, sizeof(fprs_));
}
diff --git a/src/context_x86.cc b/src/context_x86.cc
index 43de4ba..04976a5 100644
--- a/src/context_x86.cc
+++ b/src/context_x86.cc
@@ -2,21 +2,58 @@
#include "context_x86.h"
-#include "logging.h"
+#include "object.h"
namespace art {
namespace x86 {
+X86Context::X86Context() {
+ for (int i=0; i < 8; i++) {
+ gprs_[i] = 0xEBAD6070+i;
+ }
+}
+
+void X86Context::FillCalleeSaves(const Frame& fr) {
+ Method* method = fr.GetMethod();
+ uint32_t core_spills = method->GetCoreSpillMask();
+ size_t spill_count = __builtin_popcount(core_spills);
+ CHECK_EQ(method->GetFpSpillMask(), 0u);
+ if (spill_count > 0) {
+ // Lowest number spill is furthest away, walk registers and fill into context
+ int j = 1;
+ for(int i = 0; i < 8; i++) {
+ if (((core_spills >> i) & 1) != 0) {
+ gprs_[i] = fr.LoadCalleeSave(spill_count - j);
+ j++;
+ }
+ }
+ }
+}
+
void X86Context::DoLongJump() {
#if defined(__i386__)
// Load ESP and EIP
- asm volatile ( "movl %%esp, %0\n"
- "jmp *%1"
- : // output
- : "m"(esp_), "r"(&eip_) // input
+ gprs_[ESP] -= 4; // push EIP for return
+ *((uintptr_t*)(gprs_[ESP])) = eip_;
+ asm volatile (
+ "pushl %4\n\t"
+ "pushl %0\n\t"
+ "pushl %1\n\t"
+ "pushl %2\n\t"
+ "pushl %3\n\t"
+ "pushl %4\n\t"
+ "pushl %5\n\t"
+ "pushl %6\n\t"
+ "pushl %7\n\t"
+ "popal\n\t"
+ "popl %%esp\n\t"
+ "ret\n\t"
+ : //output
+ : "g"(gprs_[EAX]), "g"(gprs_[ECX]), "g"(gprs_[EDX]), "g"(gprs_[EBX]),
+ "g"(gprs_[ESP]), "g"(gprs_[EBP]), "g"(gprs_[ESI]), "g"(gprs_[EDI])
:); // clobber
#else
- UNIMPLEMENTED(FATAL);
+ UNIMPLEMENTED(FATAL);
#endif
}
diff --git a/src/context_x86.h b/src/context_x86.h
index 0e31b25..10dcbb4 100644
--- a/src/context_x86.h
+++ b/src/context_x86.h
@@ -5,19 +5,21 @@
#include "context.h"
+#include "constants_x86.h"
+
namespace art {
namespace x86 {
class X86Context : public Context {
public:
- X86Context() : esp_(0), eip_(0) {}
+ X86Context();
virtual ~X86Context() {}
// No callee saves on X86
- virtual void FillCalleeSaves(const Frame& fr) {}
+ virtual void FillCalleeSaves(const Frame& fr);
virtual void SetSP(uintptr_t new_sp) {
- esp_ = new_sp;
+ gprs_[ESP] = new_sp;
}
virtual void SetPC(uintptr_t new_pc) {
@@ -27,8 +29,7 @@
virtual void DoLongJump();
private:
- // Currently just ESP and EIP are used
- uintptr_t esp_;
+ uintptr_t gprs_[8];
uintptr_t eip_;
};
} // namespace x86
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index b91410d..4bc1caa 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -24,6 +24,16 @@
namespace {
+jboolean Class_desiredAssertionStatus(JNIEnv* env, jobject javaThis) {
+ return JNI_FALSE;
+}
+
+jobject Class_getClassLoader(JNIEnv* env, jclass, jobject javaClass) {
+ Class* c = Decode<Class*>(env, javaClass);
+ Object* result = reinterpret_cast<Object*>(const_cast<ClassLoader*>(c->GetClassLoader()));
+ return AddLocalReference<jobject>(env, result);
+}
+
jclass Class_getComponentType(JNIEnv* env, jobject javaThis) {
Class* c = Decode<Class*>(env, javaThis);
if (!c->IsArrayClass()) {
@@ -46,39 +56,138 @@
}
jobjectArray Class_getDeclaredClasses(JNIEnv* env, jclass java_lang_Class_class, jclass c, jboolean publicOnly) {
- UNIMPLEMENTED(WARNING);
+ UNIMPLEMENTED(WARNING) << "needs annotations";
return env->NewObjectArray(0, java_lang_Class_class, NULL);
}
+jobject Class_getDeclaredField(JNIEnv* env, jclass java_lang_Class_class, jclass jklass, jobject jname) {
+ Class* klass = Decode<Class*>(env, jklass);
+ DCHECK(klass->IsClass());
+ String* name = Decode<String*>(env, jname);
+ DCHECK(name->IsString());
+
+ for (size_t i = 0; i < klass->NumInstanceFields(); ++i) {
+ Field* f = klass->GetInstanceField(i);
+ if (f->GetName()->Equals(name)) {
+ return AddLocalReference<jclass>(env, f);
+ }
+ }
+ for (size_t i = 0; i < klass->NumStaticFields(); ++i) {
+ Field* f = klass->GetStaticField(i);
+ if (f->GetName()->Equals(name)) {
+ return AddLocalReference<jclass>(env, f);
+ }
+ }
+ return NULL;
+}
+
+jclass Class_getDeclaringClass(JNIEnv* env, jobject javaThis) {
+ UNIMPLEMENTED(WARNING) << "needs annotations";
+ return NULL;
+}
+
+jobject Class_getEnclosingConstructor(JNIEnv* env, jobject javaThis) {
+ UNIMPLEMENTED(WARNING) << "needs annotations";
+ return NULL;
+}
+
+jobject Class_getEnclosingMethod(JNIEnv* env, jobject javaThis) {
+ UNIMPLEMENTED(WARNING) << "needs annotations";
+ return NULL;
+}
+
+/*
+ * private native String getNameNative()
+ *
+ * Return the class' name. The exact format is bizarre, but it's the specified
+ * behavior: keywords for primitive types, regular "[I" form for primitive
+ * arrays (so "int" but "[I"), and arrays of reference types written
+ * between "L" and ";" but with dots rather than slashes (so "java.lang.String"
+ * but "[Ljava.lang.String;"). Madness.
+ */
+jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
+ Class* c = Decode<Class*>(env, javaThis);
+ std::string descriptor(c->GetDescriptor()->ToModifiedUtf8());
+ if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+ // The descriptor indicates that this is the class for
+ // a primitive type; special-case the return value.
+ const char* name = NULL;
+ switch (descriptor[0]) {
+ case 'Z': name = "boolean"; break;
+ case 'B': name = "byte"; break;
+ case 'C': name = "char"; break;
+ case 'S': name = "short"; break;
+ case 'I': name = "int"; break;
+ case 'J': name = "long"; break;
+ case 'F': name = "float"; break;
+ case 'D': name = "double"; break;
+ case 'V': name = "void"; break;
+ default:
+ LOG(FATAL) << "Unknown primitive type: " << PrintableChar(descriptor[0]);
+ }
+ return env->NewStringUTF(name);
+ }
+
+ // Convert the UTF-8 name to a java.lang.String. The
+ // name must use '.' to separate package components.
+ if (descriptor.size() > 2 && descriptor[0] == 'L' && descriptor[descriptor.size() - 1] == ';') {
+ descriptor.erase(0, 1);
+ descriptor.erase(descriptor.size() - 1);
+ }
+ std::replace(descriptor.begin(), descriptor.end(), '/', '.');
+ return env->NewStringUTF(descriptor.c_str());
+}
+
+jclass Class_getSuperclass(JNIEnv* env, jobject javaThis) {
+ Class* c = Decode<Class*>(env, javaThis);
+ Class* result = c->GetSuperClass();
+ return AddLocalReference<jclass>(env, result);
+}
+
+jboolean Class_isAnonymousClass(JNIEnv* env, jobject javaThis) {
+ UNIMPLEMENTED(WARNING) << "needs annotations";
+ return JNI_FALSE;
+}
+
+jboolean Class_isInterface(JNIEnv* env, jobject javaThis) {
+ Class* c = Decode<Class*>(env, javaThis);
+ return c->IsInterface();
+}
+
+jboolean Class_isPrimitive(JNIEnv* env, jobject javaThis) {
+ Class* c = Decode<Class*>(env, javaThis);
+ return c->IsPrimitive();
+}
+
static JNINativeMethod gMethods[] = {
//NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
- //NATIVE_METHOD(Class, desiredAssertionStatus, "()Z"),
- //NATIVE_METHOD(Class, getClassLoader, "(Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
+ NATIVE_METHOD(Class, desiredAssertionStatus, "()Z"),
+ NATIVE_METHOD(Class, getClassLoader, "(Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
NATIVE_METHOD(Class, getComponentType, "()Ljava/lang/Class;"),
//NATIVE_METHOD(Class, getDeclaredAnnotation, "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
//NATIVE_METHOD(Class, getDeclaredAnnotations, "()[Ljava/lang/annotation/Annotation;"),
NATIVE_METHOD(Class, getDeclaredClasses, "(Ljava/lang/Class;Z)[Ljava/lang/Class;"),
//NATIVE_METHOD(Class, getDeclaredConstructorOrMethod, "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Member;"),
//NATIVE_METHOD(Class, getDeclaredConstructors, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Constructor;"),
- //NATIVE_METHOD(Class, getDeclaredField, "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;"),
+ NATIVE_METHOD(Class, getDeclaredField, "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;"),
//NATIVE_METHOD(Class, getDeclaredFields, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;"),
//NATIVE_METHOD(Class, getDeclaredMethods, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Method;"),
- //NATIVE_METHOD(Class, getDeclaringClass, "()Ljava/lang/Class;"),
+ NATIVE_METHOD(Class, getDeclaringClass, "()Ljava/lang/Class;"),
//NATIVE_METHOD(Class, getEnclosingClass, "()Ljava/lang/Class;"),
- //NATIVE_METHOD(Class, getEnclosingConstructor, "()Ljava/lang/reflect/Constructor;"),
- //NATIVE_METHOD(Class, getEnclosingMethod, "()Ljava/lang/reflect/Method;"),
+ NATIVE_METHOD(Class, getEnclosingConstructor, "()Ljava/lang/reflect/Constructor;"),
+ NATIVE_METHOD(Class, getEnclosingMethod, "()Ljava/lang/reflect/Method;"),
//NATIVE_METHOD(Class, getInnerClassName, "()Ljava/lang/String;"),
//NATIVE_METHOD(Class, getInterfaces, "()[Ljava/lang/Class;"),
//NATIVE_METHOD(Class, getModifiers, "(Ljava/lang/Class;Z)I"),
- //NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
+ NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
//NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/Object;"),
- //NATIVE_METHOD(Class, getSuperclass, "()Ljava/lang/Class;"),
- //NATIVE_METHOD(Class, isAnonymousClass, "()Z"),
+ NATIVE_METHOD(Class, getSuperclass, "()Ljava/lang/Class;"),
+ NATIVE_METHOD(Class, isAnonymousClass, "()Z"),
//NATIVE_METHOD(Class, isAssignableFrom, "(Ljava/lang/Class;)Z"),
//NATIVE_METHOD(Class, isDeclaredAnnotationPresent, "(Ljava/lang/Class;)Z"),
//NATIVE_METHOD(Class, isInstance, "(Ljava/lang/Object;)Z"),
- //NATIVE_METHOD(Class, isInterface, "()Z"),
- //NATIVE_METHOD(Class, isPrimitive, "()Z"),
+ NATIVE_METHOD(Class, isInterface, "()Z"),
+ NATIVE_METHOD(Class, isPrimitive, "()Z"),
//NATIVE_METHOD(Class, newInstanceImpl, "()Ljava/lang/Object;"),
};
diff --git a/src/java_lang_reflect_Field.cc b/src/java_lang_reflect_Field.cc
new file mode 100644
index 0000000..528c09f
--- /dev/null
+++ b/src/java_lang_reflect_Field.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 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_internal.h"
+#include "class_linker.h"
+#include "object.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+namespace art {
+
+namespace {
+
+jint Field_getFieldModifiers(JNIEnv* env, jobject jfield, jclass javaDeclaringClass, jint slot) {
+ return Decode<Object*>(env, jfield)->AsField()->GetAccessFlags() & kAccFieldFlagsMask;
+}
+
+static JNINativeMethod gMethods[] = {
+ //NATIVE_METHOD(Field, getAnnotation, "(Ljava/lang/Class;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;"),
+ //NATIVE_METHOD(Field, getBField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)B"),
+ //NATIVE_METHOD(Field, getCField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)C"),
+ //NATIVE_METHOD(Field, getDField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)D"),
+ //NATIVE_METHOD(Field, getDeclaredAnnotations, "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;"),
+ //NATIVE_METHOD(Field, getFField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)F"),
+ //NATIVE_METHOD(Field, getField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;"),
+ NATIVE_METHOD(Field, getFieldModifiers, "(Ljava/lang/Class;I)I"),
+ //NATIVE_METHOD(Field, getIField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)I"),
+ //NATIVE_METHOD(Field, getJField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)J"),
+ //NATIVE_METHOD(Field, getSField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)S"),
+ //NATIVE_METHOD(Field, getSignatureAnnotation, "(Ljava/lang/Class;I)[Ljava/lang/Object;"),
+ //NATIVE_METHOD(Field, getZField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)Z"),
+ //NATIVE_METHOD(Field, isAnnotationPresent, "(Ljava/lang/Class;ILjava/lang/Class;)Z"),
+ //NATIVE_METHOD(Field, setBField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCB)V"),
+ //NATIVE_METHOD(Field, setCField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCC)V"),
+ //NATIVE_METHOD(Field, setDField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCD)V"),
+ //NATIVE_METHOD(Field, setFField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCF)V"),
+ //NATIVE_METHOD(Field, setField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZLjava/lang/Object;)V"),
+ //NATIVE_METHOD(Field, setIField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCI)V"),
+ //NATIVE_METHOD(Field, setJField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCJ)V"),
+ //NATIVE_METHOD(Field, setSField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCS)V"),
+ //NATIVE_METHOD(Field, setZField, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCZ)V"),
+};
+
+} // namespace
+
+void register_java_lang_reflect_Field(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "java/lang/reflect/Field", gMethods, NELEM(gMethods));
+}
+
+} // namespace art
diff --git a/src/java_lang_reflect_Method.cc b/src/java_lang_reflect_Method.cc
new file mode 100644
index 0000000..c886f59
--- /dev/null
+++ b/src/java_lang_reflect_Method.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 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_internal.h"
+#include "class_linker.h"
+#include "object.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+namespace art {
+
+namespace {
+
+
+// We move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+// position, because the callers of this function are trying to convey
+// the "traditional" meaning of the flags to their callers.
+uint32_t FixupMethodFlags(uint32_t access_flags) {
+ access_flags &= ~kAccSynchronized;
+ if ((access_flags & kAccDeclaredSynchronized) != 0) {
+ access_flags |= kAccSynchronized;
+ }
+ return access_flags & kAccMethodFlagsMask;
+}
+
+jint Method_getMethodModifiers(JNIEnv* env, jclass, jclass javaDeclaringClass, jobject jmethod, jint slot) {
+ return FixupMethodFlags(Decode<Object*>(env, jmethod)->AsMethod()->GetAccessFlags());
+}
+
+static JNINativeMethod gMethods[] = {
+ //NATIVE_METHOD(Method, getAnnotation, "(Ljava/lang/Class;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;"),
+ //NATIVE_METHOD(Method, getDeclaredAnnotations, "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;"),
+ //NATIVE_METHOD(Method, getDefaultValue, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
+ NATIVE_METHOD(Method, getMethodModifiers, "(Ljava/lang/Class;Ljava/lang/reflect/AccessibleObject;I)I"),
+ //NATIVE_METHOD(Method, getParameterAnnotations, "(Ljava/lang/Class;I)[[Ljava/lang/annotation/Annotation;"),
+ //NATIVE_METHOD(Method, getSignatureAnnotation, "(Ljava/lang/Class;I)[Ljava/lang/Object;"),
+ //NATIVE_METHOD(Method, invokeNative, "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;"),
+ //NATIVE_METHOD(Method, isAnnotationPresent, "(Ljava/lang/Class;ILjava/lang/Class;)Z"),
+};
+
+} // namespace
+
+void register_java_lang_reflect_Method(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "java/lang/reflect/Method", gMethods, NELEM(gMethods));
+}
+
+} // namespace art
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 2088f7d..9624246 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -273,7 +273,7 @@
}
// 9. Plant call to native code associated with method
- if (!jni_conv->IsOutArgRegister(mr_conv->MethodRegister())) {
+ if (!jni_conv->IsMethodRegisterCrushedPreCall()) {
// Method register shouldn't have been crushed by setting up outgoing
// arguments
__ Call(mr_conv->MethodRegister(), Method::NativeMethodOffset(),
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 9328e85..35a76c9 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -448,42 +448,34 @@
EXPECT_EQ(1, gSuspendCounterHandler_calls);
}
-int gExceptionHandler_calls;
-void ExceptionHandler(Method** frame) {
- // Check we came here in the native state then transition to runnable to work
- // on the Object*
- EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
- ScopedJniThreadState ts(Thread::Current()->GetJniEnv());
-
- EXPECT_TRUE((*frame)->GetName()->Equals("throwException"));
- gExceptionHandler_calls++;
- Thread::Current()->ClearException();
-}
-
void Java_MyClass_throwException(JNIEnv* env, jobject) {
jclass c = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(c, "hello");
}
TEST_F(JniCompilerTest, ExceptionHandling) {
- Thread::Current()->RegisterExceptionEntryPoint(&ExceptionHandler);
- gExceptionHandler_calls = 0;
gJava_MyClass_foo_calls = 0;
+ // Check a single call of a JNI method is ok
SetupForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClass_foo));
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
EXPECT_EQ(1, gJava_MyClass_foo_calls);
- EXPECT_EQ(0, gExceptionHandler_calls);
+ EXPECT_FALSE(Thread::Current()->IsExceptionPending());
+ // Get class for exception we expect to be thrown
+ Class* jlre = class_linker_->FindClass("Ljava/lang/RuntimeException;", class_loader_);
SetupForTest(false, "throwException", "()V", reinterpret_cast<void*>(&Java_MyClass_throwException));
+ // Call Java_MyClass_throwException (JNI method that throws exception)
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
EXPECT_EQ(1, gJava_MyClass_foo_calls);
- EXPECT_EQ(1, gExceptionHandler_calls);
+ EXPECT_TRUE(Thread::Current()->IsExceptionPending());
+ EXPECT_TRUE(Thread::Current()->GetException()->InstanceOf(jlre));
+ Thread::Current()->ClearException();
+ // Check a single call of a JNI method is ok
SetupForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClass_foo));
env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
EXPECT_EQ(2, gJava_MyClass_foo_calls);
- EXPECT_EQ(1, gExceptionHandler_calls);
}
jint Java_MyClass_nativeUpCall(JNIEnv* env, jobject thisObj, jint i) {
@@ -558,4 +550,20 @@
env_->CallStaticVoidMethod(jklass_, jmethod_, jobj_, 1234, jklass_, 5678, 9876);
}
+jboolean my_casi(JNIEnv* env, jobject unsafe, jobject obj, jlong offset, jint expected, jint newval) {
+ EXPECT_TRUE(env->IsSameObject(JniCompilerTest::jobj_, unsafe));
+ EXPECT_TRUE(env->IsSameObject(JniCompilerTest::jobj_, obj));
+ EXPECT_EQ(0x12345678ABCDEF88ll, offset);
+ EXPECT_EQ(static_cast<jint>(0xCAFEF00D), expected);
+ EXPECT_EQ(static_cast<jint>(0xEBADF00D), newval);
+ return JNI_TRUE;
+}
+
+TEST_F(JniCompilerTest, CompareAndSwapInt) {
+ SetupForTest(false, "compareAndSwapInt", "(Ljava/lang/Object;JII)Z",
+ reinterpret_cast<void*>(&my_casi));
+ jboolean result = env_->CallBooleanMethod(jobj_, jmethod_, jobj_, 0x12345678ABCDEF88ll, 0xCAFEF00D, 0xEBADF00D);
+ EXPECT_EQ(result, JNI_TRUE);
+}
+
} // namespace art
diff --git a/src/jni_internal_x86.cc b/src/jni_internal_x86.cc
index a096da0..7128c6e 100644
--- a/src/jni_internal_x86.cc
+++ b/src/jni_internal_x86.cc
@@ -28,16 +28,15 @@
UniquePtr<X86Assembler> assembler(
down_cast<X86Assembler*>(Assembler::Create(kX86)));
#define __ assembler->
- // Size of frame - spill of EDI + Method* + possible receiver + arg array
+ // Size of frame - return address + Method* + possible receiver + arg array
size_t frame_size = (2 * kPointerSize) +
(method->IsStatic() ? 0 : kPointerSize) +
method->NumArgArrayBytes();
size_t pad_size = RoundUp(frame_size, kStackAlignment) - frame_size;
- __ pushl(EDI); // preserve EDI
- __ movl(EDI, Address(ESP, 8)); // EDI = method
- __ movl(EAX, Address(ESP, 12)); // EAX = receiver
- __ movl(EDX, Address(ESP, 20)); // EDX = arg array
+ __ movl(EAX, Address(ESP, 4)); // EAX = method
+ __ movl(ECX, Address(ESP, 8)); // ECX = receiver
+ __ movl(EDX, Address(ESP, 16)); // EDX = arg array
// Push padding
if (pad_size != 0) {
@@ -49,35 +48,35 @@
__ pushl(Address(EDX, off - kPointerSize));
}
if (!method->IsStatic()) {
- __ pushl(EAX);
+ __ pushl(ECX);
}
// Push 0 as NULL Method* thereby terminating managed stack crawls
__ pushl(Immediate(0));
- __ call(Address(EDI, method->GetCodeOffset())); // Call code off of method
- // pop arguments and padding up to saved EDI
+ __ call(Address(EAX, method->GetCodeOffset())); // Call code off of method
+
+ // pop arguments up to the return address
__ addl(ESP, Immediate(frame_size + pad_size - kPointerSize));
char ch = method->GetShorty()->CharAt(0);
if (ch != 'V') {
// Load the result JValue pointer.
- __ movl(EDI, Address(ESP, 24));
+ __ movl(ECX, Address(ESP, 20));
switch (ch) {
case 'D':
- __ fstpl(Address(EDI, 0));
+ __ fstpl(Address(ECX, 0));
break;
case 'F':
- __ fstps(Address(EDI, 0));
+ __ fstps(Address(ECX, 0));
break;
case 'J':
- __ movl(Address(EDI, 0), EAX);
- __ movl(Address(EDI, 4), EDX);
+ __ movl(Address(ECX, 0), EAX);
+ __ movl(Address(ECX, 4), EDX);
break;
default:
- __ movl(Address(EDI, 0), EAX);
+ __ movl(Address(ECX, 0), EAX);
break;
}
}
- __ popl(EDI); // restore EDI
__ ret();
// TODO: store native_entry in the stub table
ByteArray* code = ByteArray::Alloc(assembler->CodeSize());
diff --git a/src/oatexec.cc b/src/oatexec.cc
index 68c67e1..3730b43 100644
--- a/src/oatexec.cc
+++ b/src/oatexec.cc
@@ -30,7 +30,6 @@
return false;
}
static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC
-#if 0 // reflect.Method.getModifiers not yet implemented
jmethodID get_modifiers = env->GetMethodID(method.get(),
"getModifiers",
"()I");
@@ -39,10 +38,6 @@
return false;
}
int modifiers = env->CallIntMethod(reflected.get(), get_modifiers);
-#else
- int modifiers = PUBLIC;
- UNIMPLEMENTED(WARNING) << "assuming main is public...";
-#endif
if ((modifiers & PUBLIC) == 0) {
return false;
}
diff --git a/src/object.cc b/src/object.cc
index 8d3a94f..7f247c7 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -488,8 +488,8 @@
uint32_t Method::ToDexPC(const uintptr_t pc) const {
IntArray* mapping_table = GetMappingTable();
if (mapping_table == NULL) {
- DCHECK(pc == 0);
- return DexFile::kDexNoIndex; // Special no mapping/pc == -1 case
+ DCHECK(IsNative());
+ return DexFile::kDexNoIndex; // Special no mapping case
}
size_t mapping_table_length = mapping_table->GetLength();
uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(GetCode());
@@ -611,7 +611,7 @@
if (have_executable_code && stub != NULL) {
LOG(INFO) << "invoking " << PrettyMethod(this) << " code=" << (void*) GetCode() << " stub=" << (void*) stub;
(*stub)(this, receiver, self, args, result);
- LOG(INFO) << "returning " << PrettyMethod(this) << " code=" << (void*) GetCode() << " stub=" << (void*) stub;
+ LOG(INFO) << "returned " << PrettyMethod(this) << " code=" << (void*) GetCode() << " stub=" << (void*) stub;
} else {
LOG(WARNING) << "Not invoking method with no associated code: " << PrettyMethod(this);
if (result != NULL) {
diff --git a/src/runtime.cc b/src/runtime.cc
index e0a0b20..b9120fa 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -361,9 +361,20 @@
// Finish attaching the main thread.
Thread::Current()->CreatePeer("main", false);
+ RunImageClinits();
+
StartDaemonThreads();
}
+// initialize classes that have instances in the image but that have
+// <clinit> methods so they could not be initialized by the compiler.
+void Runtime::RunImageClinits() {
+ Class* Field_class = class_linker_->FindSystemClass("Ljava/lang/reflect/Field;");
+ CHECK(Field_class->FindDeclaredDirectMethod("<clinit>", "()V") != NULL);
+ class_linker_->EnsureInitialized(Field_class);
+ CHECK(!Thread::Current()->IsExceptionPending());
+}
+
void Runtime::StartDaemonThreads() {
signal_catcher_ = new SignalCatcher;
@@ -372,8 +383,6 @@
Method* m = c->FindDirectMethod("start", "()V");
CHECK(m != NULL);
// m->Invoke(Thread::Current(), NULL, NULL, NULL);
-
- signal_catcher_->HandleSigQuit();
}
bool Runtime::IsStarted() {
@@ -458,8 +467,8 @@
//REGISTER(register_java_lang_reflect_AccessibleObject);
//REGISTER(register_java_lang_reflect_Array);
//REGISTER(register_java_lang_reflect_Constructor);
- //REGISTER(register_java_lang_reflect_Field);
- //REGISTER(register_java_lang_reflect_Method);
+ REGISTER(register_java_lang_reflect_Field);
+ REGISTER(register_java_lang_reflect_Method);
//REGISTER(register_java_lang_reflect_Proxy);
REGISTER(register_java_util_concurrent_atomic_AtomicLong);
//REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmServer);
diff --git a/src/runtime.h b/src/runtime.h
index 35f8ef7..6d11cf3 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -163,6 +163,7 @@
bool Init(const Options& options, bool ignore_unrecognized);
void InitLibraries();
void RegisterRuntimeNativeMethods(JNIEnv*);
+ void RunImageClinits();
void StartDaemonThreads();
std::string boot_class_path_;
diff --git a/src/runtime_support.S b/src/runtime_support.S
index 458102d..8b904b8 100644
--- a/src/runtime_support.S
+++ b/src/runtime_support.S
@@ -2,19 +2,19 @@
.balign 4
- .global art_throw_exception
- .extern artThrowExceptionHelper
+ .global art_deliver_exception
+ .extern artDeliverExceptionHelper
/*
- * Called by managed code, saves all registers (forms basis of long jump context).
+ * Called by managed code, saves mosts registers (forms basis of long jump context).
* artThrowExceptionHelper will place a mock Method* at the bottom of the thread.
* r0 holds Throwable
*/
-art_throw_exception:
+art_deliver_exception:
stmdb sp!, {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}
- sub sp, #16 @ 4 words of space, bottom word will hold Method*
+ sub sp, #16 @ 4 words of space, bottom word will hold Method*
mov r1, r9
mov r2, sp
- b artThrowExceptionHelper @ artThrowExceptionHelper(Throwable*, SP)
+ b artDeliverExceptionHelper @ artThrowExceptionHelper(Throwable*, SP)
.global art_invoke_interface_trampoline
.extern artFindInterfaceMethodInCache
@@ -122,3 +122,33 @@
bx lr
#endif
+
+#if defined(__i386__)
+
+ .global art_deliver_exception
+ .extern artDeliverExceptionHelper
+ .extern _ZN3art6Thread5self_E
+ /*
+ * Called by managed code, saves callee saves and then calls artThrowExceptionHelper
+ * that will place a mock Method* at the bottom of the stack.
+ * EAX holds the exception.
+ */
+art_deliver_exception:
+ // Create frame
+ pushl %edi // Save callee saves
+ pushl %esi
+ pushl %ebp
+ pushl %ebx
+ pushl $0
+ pushl $0
+ pushl $0 // Will be clobbered to be Method*
+ mov %esp, %ecx
+ // Outgoing argument set up
+ pushl $0 // Alignment padding
+ pushl %ecx
+ pushl $0 // TODO: pass fs:offsetof(Thread,self_) - for now this is computed in the helper
+ pushl %eax
+ call artDeliverExceptionHelper // artThrowExceptionHelper(Throwable*, Thread*, SP)
+ int3
+
+#endif
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 671d7dc..4915314 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -9,7 +9,7 @@
extern "C" uint64_t art_shr_long(uint64_t, uint32_t);
extern "C" uint64_t art_ushr_long(uint64_t, uint32_t);
extern "C" void art_invoke_interface_trampoline(void*, void*, void*, void*);
- extern "C" void art_throw_exception(void*);
+ extern "C" void art_deliver_exception(void*);
/* Conversions */
extern "C" float __aeabi_i2f(int op1); // OP_INT_TO_FLOAT
@@ -45,4 +45,8 @@
#endif
+#if defined(__i386__)
+extern "C" void art_deliver_exception(void*);
+#endif
+
#endif // ART_SRC_RUNTIME_SUPPORT_H_
diff --git a/src/stub_x86.cc b/src/stub_x86.cc
index c23c751..13f9c07 100644
--- a/src/stub_x86.cc
+++ b/src/stub_x86.cc
@@ -16,9 +16,8 @@
// Pad stack to ensure 16-byte alignment
__ pushl(Immediate(0));
- __ pushl(Immediate(0));
__ fs()->pushl(Address::Absolute(Thread::SelfOffset())); // Thread*
- __ pushl(EDI); // Method*
+ __ pushl(EAX); // Method*
// Call to throw AbstractMethodError
__ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pThrowAbstractMethodErrorFromCode)),
@@ -45,14 +44,12 @@
// Pad stack to ensure 16-byte alignment
__ pushl(Immediate(0));
__ pushl(Immediate(0));
- __ pushl(Immediate(0));
- __ fs()->movl(ECX, Address::Absolute(Thread::SelfOffset()));
- __ pushl(ECX); // Thread*
+ __ fs()->pushl(Address::Absolute(Thread::SelfOffset())); // Thread*
__ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pFindNativeMethod)),
X86ManagedRegister::FromCpuRegister(ECX));
- __ addl(ESP, Immediate(16));
+ __ addl(ESP, Immediate(12));
Label no_native_code_found; // forward declaration
__ cmpl(EAX, Immediate(0));
diff --git a/src/thread.cc b/src/thread.cc
index 83c1b81..86994cf 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -52,9 +52,9 @@
} // namespace art
// Called by generated call to throw an exception
-extern "C" void artThrowExceptionHelper(art::Throwable* exception,
- art::Thread* thread,
- art::Method** sp) {
+extern "C" void artDeliverExceptionHelper(art::Throwable* exception,
+ art::Thread* thread,
+ art::Method** sp) {
/*
* exception may be NULL, in which case this routine should
* throw NPE. NOTE: this is a convenience for generated code,
@@ -62,6 +62,10 @@
* and threw a NPE if NULL. This routine responsible for setting
* exception_ in thread and delivering the exception.
*/
+#if defined(__i386__)
+ thread = art::Thread::Current(); // TODO: fix passing this in as an argument
+#endif
+ // Place a special frame at the TOS that will save all callee saves
*sp = thread->CalleeSaveMethod();
thread->SetTopOfStack(sp, 0);
thread->DeliverException(exception);
@@ -306,7 +310,10 @@
pLdivmod = __aeabi_ldivmod;
pLmul = __aeabi_lmul;
pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
- pThrowException = art_throw_exception;
+ pDeliverException = art_deliver_exception;
+#endif
+#if defined(__i386__)
+ pDeliverException = art_deliver_exception;
#endif
pF2l = F2L;
pD2l = D2L;
@@ -347,9 +354,14 @@
}
void Frame::Next() {
+ size_t frame_size = GetMethod()->GetFrameSizeInBytes();
+ DCHECK_NE(frame_size, 0u);
+ DCHECK_LT(frame_size, 1024u);
byte* next_sp = reinterpret_cast<byte*>(sp_) +
- GetMethod()->GetFrameSizeInBytes();
+ frame_size;
sp_ = reinterpret_cast<Method**>(next_sp);
+ DCHECK(*sp_ == NULL ||
+ (*sp_)->GetClass()->GetDescriptor()->Equals("Ljava/lang/reflect/Method;"));
}
uintptr_t Frame::GetReturnPC() const {
@@ -365,6 +377,9 @@
size_t frame_size = method->GetFrameSizeInBytes();
byte* save_addr = reinterpret_cast<byte*>(sp_) + frame_size -
((num + 1) * kPointerSize);
+#if defined(__i386__)
+ save_addr -= kPointerSize; // account for return address
+#endif
return *reinterpret_cast<uintptr_t*>(save_addr);
}
@@ -927,6 +942,11 @@
return false;
}
+void Thread::PopSirt() {
+ CHECK(top_sirt_ != NULL);
+ top_sirt_ = top_sirt_->Link();
+}
+
Object* Thread::DecodeJObject(jobject obj) {
DCHECK(CanAccessDirectReferences());
if (obj == NULL) {
@@ -1069,15 +1089,19 @@
}
}
-void Thread::WalkStackUntilUpCall(StackVisitor* visitor) const {
+void Thread::WalkStackUntilUpCall(StackVisitor* visitor, bool include_upcall) const {
Frame frame = GetTopOfStack();
uintptr_t pc = top_of_managed_stack_pc_;
if (frame.GetSP() != 0) {
for ( ; frame.GetMethod() != 0; frame.Next()) {
+ DCHECK(frame.GetMethod()->IsWithinCode(pc));
visitor->VisitFrame(frame, pc);
pc = frame.GetReturnPC();
}
+ if (include_upcall) {
+ visitor->VisitFrame(frame, pc);
+ }
}
}
@@ -1167,39 +1191,78 @@
Method* Thread::CalleeSaveMethod() const {
// TODO: we should only allocate this once
- // TODO: this code is ARM specific
Method* method = Runtime::Current()->GetClassLinker()->AllocMethod();
+#if defined(__arm__)
method->SetCode(NULL, art::kThumb2, NULL);
method->SetFrameSizeInBytes(64);
method->SetReturnPcOffsetInBytes(60);
- method->SetCoreSpillMask(0x4FFE);
+ method->SetCoreSpillMask((1 << art::arm::R1) |
+ (1 << art::arm::R2) |
+ (1 << art::arm::R3) |
+ (1 << art::arm::R4) |
+ (1 << art::arm::R5) |
+ (1 << art::arm::R6) |
+ (1 << art::arm::R7) |
+ (1 << art::arm::R8) |
+ (1 << art::arm::R9) |
+ (1 << art::arm::R10) |
+ (1 << art::arm::R11) |
+ (1 << art::arm::LR));
method->SetFpSpillMask(0);
+#elif defined(__i386__)
+ method->SetCode(NULL, art::kX86, NULL);
+ method->SetFrameSizeInBytes(32);
+ method->SetReturnPcOffsetInBytes(28);
+ method->SetCoreSpillMask((1 << art::x86::EBX) |
+ (1 << art::x86::EBP) |
+ (1 << art::x86::ESI) |
+ (1 << art::x86::EDI));
+ method->SetFpSpillMask(0);
+#else
+ UNIMPLEMENTED(FATAL);
+#endif
return method;
}
class CatchBlockStackVisitor : public Thread::StackVisitor {
public:
CatchBlockStackVisitor(Class* to_find, Context* ljc)
- : found_(false), to_find_(to_find), long_jump_context_(ljc) {}
+ : found_(false), to_find_(to_find), long_jump_context_(ljc), native_method_count_(0) {
+#ifndef NDEBUG
+ handler_pc_ = 0xEBADC0DE;
+ handler_frame_.SetSP(reinterpret_cast<Method**>(0xEBADF00D));
+#endif
+ }
virtual void VisitFrame(const Frame& fr, uintptr_t pc) {
if (!found_) {
- last_pc_ = pc;
- handler_frame_ = fr;
Method* method = fr.GetMethod();
- if (pc > 0) {
- // Move the PC back 2 bytes as a call will frequently terminate the
- // decoding of a particular instruction and we want to make sure we
- // get the Dex PC of the instruction with the call and not the
- // instruction following.
- pc -= 2;
+ if (method == NULL) {
+ // This is the upcall, we remember the frame and last_pc so that we may
+ // long jump to them
+ handler_pc_ = pc;
+ handler_frame_ = fr;
+ return;
}
- uint32_t dex_pc = method->ToDexPC(pc);
+ uint32_t dex_pc = DexFile::kDexNoIndex;
+ if (pc > 0) {
+ if (method->IsNative()) {
+ native_method_count_++;
+ } else {
+ // Move the PC back 2 bytes as a call will frequently terminate the
+ // decoding of a particular instruction and we want to make sure we
+ // get the Dex PC of the instruction with the call and not the
+ // instruction following.
+ pc -= 2;
+ dex_pc = method->ToDexPC(pc);
+ }
+ }
if (dex_pc != DexFile::kDexNoIndex) {
uint32_t found_dex_pc = method->FindCatchBlock(to_find_, dex_pc);
if (found_dex_pc != DexFile::kDexNoIndex) {
found_ = true;
- handler_dex_pc_ = found_dex_pc;
+ handler_pc_ = method->ToNativePC(found_dex_pc);
+ handler_frame_ = fr;
}
}
if (!found_) {
@@ -1215,11 +1278,12 @@
Class* to_find_;
// Frame with found handler or last frame if no handler found
Frame handler_frame_;
- // Found dex PC of the handler block
- uint32_t handler_dex_pc_;
+ // PC to branch to for the handler
+ uintptr_t handler_pc_;
// Context that will be the target of the long jump
Context* long_jump_context_;
- uintptr_t last_pc_;
+ // Number of native methods passed in crawl (equates to number of SIRTs to pop)
+ uint32_t native_method_count_;
};
void Thread::DeliverException(Throwable* exception) {
@@ -1227,16 +1291,18 @@
Context* long_jump_context = GetLongJumpContext();
CatchBlockStackVisitor catch_finder(exception->GetClass(), long_jump_context);
- WalkStackUntilUpCall(&catch_finder);
+ WalkStackUntilUpCall(&catch_finder, true);
- long_jump_context->SetSP(reinterpret_cast<intptr_t>(catch_finder.handler_frame_.GetSP()));
- uintptr_t long_jump_pc;
- if (catch_finder.found_) {
- long_jump_pc = catch_finder.handler_frame_.GetMethod()->ToNativePC(catch_finder.handler_dex_pc_);
+ // Pop any SIRT
+ if (catch_finder.native_method_count_ == 1) {
+ PopSirt();
} else {
- long_jump_pc = catch_finder.last_pc_;
+ // We only expect the stack crawl to have passed 1 native method as its terminated
+ // by a up call
+ DCHECK_EQ(catch_finder.native_method_count_, 0u);
}
- long_jump_context->SetPC(long_jump_pc);
+ long_jump_context->SetSP(reinterpret_cast<intptr_t>(catch_finder.handler_frame_.GetSP()));
+ long_jump_context->SetPC(catch_finder.handler_pc_);
long_jump_context->DoLongJump();
}
diff --git a/src/thread.h b/src/thread.h
index 860a185..f4cc747 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -212,7 +212,7 @@
Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
void (*pUnlockObjectFromCode)(Thread*, Object*);
void (*pLockObjectFromCode)(Thread*, Object*);
- void (*pThrowException)(void*);
+ void (*pDeliverException)(void*);
void (*pHandleFillArrayDataFromCode)(Array*, const uint16_t*);
Class* (*pInitializeTypeFromCode)(uint32_t, Method*);
void (*pResolveMethodFromCode)(Method*, uint32_t);
@@ -385,6 +385,9 @@
// Is the given obj in this thread's stack indirect reference table?
bool SirtContains(jobject obj);
+ // Pop the top SIRT
+ void PopSirt();
+
// Convert a jobject into a Object*
Object* DecodeJObject(jobject obj);
@@ -416,10 +419,6 @@
NotifyLocked();
}
- void RegisterExceptionEntryPoint(void (*handler)(Method**)) {
- exception_entry_point_ = handler;
- }
-
void RegisterSuspendCountEntryPoint(void (*handler)(Method**)) {
suspend_count_entry_point_ = handler;
}
@@ -507,10 +506,6 @@
return ThreadOffset(OFFSETOF_MEMBER(Thread, top_sirt_));
}
- static ThreadOffset ExceptionEntryPointOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_entry_point_));
- }
-
static ThreadOffset SuspendCountEntryPointOffset() {
return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_entry_point_));
}
@@ -543,7 +538,7 @@
void WalkStack(StackVisitor* visitor) const;
- void WalkStackUntilUpCall(StackVisitor* visitor) const;
+ void WalkStackUntilUpCall(StackVisitor* visitor, bool include_upcall) const;
// Thin lock thread id. This is a small integer used by the thin lock implementation.
// This is not to be confused with the native thread's tid, nor is it the value returned
@@ -624,9 +619,6 @@
// TLS key used to retrieve the VM thread object.
static pthread_key_t pthread_key_self_;
- // Entry point called when exception_ is set
- void (*exception_entry_point_)(Method** frame);
-
// Entry point called when suspend_count_ is non-zero
void (*suspend_count_entry_point_)(Method** frame);
diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java
index 3ce18d3..0c040b3 100644
--- a/test/MyClassNatives/MyClassNatives.java
+++ b/test/MyClassNatives/MyClassNatives.java
@@ -15,4 +15,5 @@
static native double fooSDD(double x, double y);
static synchronized native Object fooSSIOO(int x, Object y, Object z);
static native void arraycopy(Object src, int src_pos, Object dst, int dst_pos, int length);
+ native boolean compareAndSwapInt(Object obj, long offset, int expected, int newval);
}