Proxy implementation
This rounds out the proxy implementation by adding missing pieces to the
class linker, extending tests and fixing issues in the runtime support.
There are also some tweaks for performance and to clean up Method/Object
a little.
A unit test of the functionality is "art/test/run-test 044"
Change-Id: Id94102d10b81cd9b12b95ba8618f6187490204c4
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 9fd8f0b..53bcf7e 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -19,6 +19,7 @@
#include "oat_file.h"
#include "object.h"
#include "runtime.h"
+#include "runtime_support.h"
#include "ScopedLocalRef.h"
#include "space.h"
#include "stl_util.h"
@@ -127,6 +128,7 @@
"Ljava/lang/reflect/Constructor;",
"Ljava/lang/reflect/Field;",
"Ljava/lang/reflect/Method;",
+ "Ljava/lang/reflect/Proxy;",
"Ljava/lang/ClassLoader;",
"Ldalvik/system/BaseDexClassLoader;",
"Ldalvik/system/PathClassLoader;",
@@ -420,6 +422,12 @@
Class* Method_class = FindSystemClass("Ljava/lang/reflect/Method;");
CHECK_EQ(java_lang_reflect_Method, Method_class);
+ // End of special init trickery, subsequent classes may be loaded via FindSystemClass
+
+ // Create java.lang.reflect.Proxy root
+ Class* java_lang_reflect_Proxy = FindSystemClass("Ljava/lang/reflect/Proxy;");
+ SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy);
+
// java.lang.ref classes need to be specially flagged, but otherwise are normal classes
Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference);
@@ -1291,6 +1299,7 @@
}
const DexFile& ClassLinker::FindDexFile(const DexCache* dex_cache) const {
+ CHECK(dex_cache != NULL);
MutexLock mu(lock_);
for (size_t i = 0; i != dex_caches_.size(); ++i) {
if (dex_caches_[i] == dex_cache) {
@@ -1533,72 +1542,85 @@
}
Class* ClassLinker::CreateProxyClass(String* name, ObjectArray<Class>* interfaces,
- ClassLoader* loader, ObjectArray<Method>* methods, ObjectArray<Object>* throws) {
+ ClassLoader* loader, ObjectArray<Method>* methods, ObjectArray<ObjectArray<Class> >* throws) {
Class* klass = AllocClass(GetClassRoot(kJavaLangClass), sizeof(ProxyClass));
CHECK(klass != NULL);
klass->SetObjectSize(sizeof(Proxy));
- klass->SetDescriptor(intern_table_->InternStrong(name));
+ const char* descriptor = DotToDescriptor(name->ToModifiedUtf8().c_str()).c_str();;
+ klass->SetDescriptor(intern_table_->InternStrong(descriptor));
klass->SetAccessFlags(kAccPublic | kAccFinal);
klass->SetClassLoader(loader);
- klass->SetStatus(Class::kStatusInitialized);
- klass->SetInterfaces(interfaces);
+ klass->SetStatus(Class::kStatusInitialized); // no loading or initializing necessary
+ Class* proxy_class = GetClassRoot(kJavaLangReflectProxy);
+ klass->SetSuperClass(proxy_class); // The super class is java.lang.reflect.Proxy
+ klass->SetInterfaces(interfaces); // The interfaces are the array of interfaces specified
+ // Proxies have 1 direct method, the constructor
klass->SetDirectMethods(AllocObjectArray<Method>(1));
klass->SetDirectMethod(0, CreateProxyConstructor(klass));
+ // Create virtual method using specified prototypes
size_t num_virtual_methods = methods->GetLength();
klass->SetVirtualMethods(AllocObjectArray<Method>(num_virtual_methods));
for (size_t i = 0; i < num_virtual_methods; ++i) {
Method* prototype = methods->Get(i);
klass->SetVirtualMethod(i, CreateProxyMethod(klass, prototype, throws->Get(i)));
}
-
+ // Link the virtual methods, creating vtable and iftables
if (!LinkMethods(klass)) {
DCHECK(Thread::Current()->IsExceptionPending());
return NULL;
}
-
return klass;
}
Method* ClassLinker::CreateProxyConstructor(Class* klass) {
- Method* constructor = AllocMethod();
+ // Create constructor for Proxy that must initialize h
+ Class* proxy_class = GetClassRoot(kJavaLangReflectProxy);
+ ObjectArray<Method>* proxy_direct_methods = proxy_class->GetDirectMethods();
+ CHECK_EQ(proxy_direct_methods->GetLength(), 12);
+ Method* proxy_constructor = proxy_direct_methods->Get(2);
+ // Clone the existing constructor of Proxy (our constructor would just invoke it so steal its
+ // code_ too)
+ Method* constructor = down_cast<Method*>(proxy_constructor->Clone());
+ // Make this constructor public and fix the class to be our Proxy version
+ constructor->SetAccessFlags((constructor->GetAccessFlags() & ~kAccProtected) | kAccPublic);
constructor->SetDeclaringClass(klass);
- constructor->SetName(intern_table_->InternStrong("<init>"));
- constructor->SetSignature(intern_table_->InternStrong("(Ljava/lang/reflect/InvocationHandler;)V"));
- constructor->SetShorty(intern_table_->InternStrong("LV"));
- constructor->SetAccessFlags(kAccPublic | kAccNative);
-
- // TODO: return type
- // TODO: code block
-
+ // Sanity checks
+ CHECK(constructor->IsConstructor());
+ CHECK(constructor->GetName()->Equals("<init>"));
+ CHECK(constructor->GetSignature()->Equals("(Ljava/lang/reflect/InvocationHandler;)V"));
+ DCHECK(constructor->IsPublic());
return constructor;
}
-Method* ClassLinker::CreateProxyMethod(Class* klass, Method* prototype, Object* throws) {
- Method* method = AllocMethod();
+Method* ClassLinker::CreateProxyMethod(Class* klass, Method* prototype,
+ ObjectArray<Class>* throws) {
+ // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialise
+ // as necessary
+ Method* method = down_cast<Method*>(prototype->Clone());
+
+ // Set class to be the concrete proxy class and clear the abstract flag, modify exceptions to
+ // the intersection of throw exceptions as defined in Proxy
method->SetDeclaringClass(klass);
- method->SetName(const_cast<String*>(prototype->GetName()));
- method->SetSignature(const_cast<String*>(prototype->GetSignature()));
- method->SetShorty(prototype->GetShorty());
- method->SetAccessFlags(prototype->GetAccessFlags());
+ method->SetAccessFlags((method->GetAccessFlags() & ~kAccAbstract) | kAccFinal);
method->SetExceptionTypes(throws);
- // TODO: return type
- // method->SetReturnTypeIdx(dex_file.GetProtoId(method_id.proto_idx_).return_type_idx_);
+ // At runtime the method looks like a reference and argument saving method, clone the code
+ // related parameters from this method.
+ Method* refs_and_args = Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs);
+ method->SetCoreSpillMask(refs_and_args->GetCoreSpillMask());
+ method->SetFpSpillMask(refs_and_args->GetFpSpillMask());
+ method->SetFrameSizeInBytes(refs_and_args->GetFrameSizeInBytes());
+ method->SetCode(reinterpret_cast<void*>(art_proxy_invoke_handler));
- // TODO: code block
- // method->SetCodeItemOffset(src.code_off_);
- // method->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
- // method->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
- // method->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
- // method->SetDexCacheResolvedFields(klass->GetDexCache()->GetResolvedFields());
- // method->SetDexCacheCodeAndDirectMethods(klass->GetDexCache()->GetCodeAndDirectMethods());
- // method->SetDexCacheInitializedStaticStorage(klass->GetDexCache()->GetInitializedStaticStorage());
- // method->SetNumRegisters(code_item->registers_size_);
- // method->SetNumIns(code_item->ins_size_);
- // method->SetNumOuts(code_item->outs_size_);
- // LinkCode(method, oat_class.get(), method_index);
+ // Basic sanity
+ DCHECK(method->GetName()->Equals(prototype->GetName()));
+ DCHECK(method->GetSignature()->Equals(prototype->GetSignature()));
+ DCHECK(method->GetShorty()->Equals(prototype->GetShorty()));
+
+ // More complex sanity - via dex cache
+ CHECK_EQ(method->GetReturnType(), prototype->GetReturnType());
return method;
}
@@ -2092,7 +2114,7 @@
size_t j = 0;
for (; j < actual_count; ++j) {
Method* super_method = vtable->Get(j);
- if (local_method->HasSameNameAndDescriptor(super_method)) {
+ if (local_method->HasSameNameAndSignature(super_method)) {
// Verify
if (super_method->IsFinal()) {
ThrowLinkageError("Method %s.%s overrides final method in class %s",
@@ -2212,7 +2234,7 @@
// matter which direction we go. We walk it backward anyway.)
for (k = vtable->GetLength() - 1; k >= 0; --k) {
Method* vtable_method = vtable->Get(k);
- if (interface_method->HasSameNameAndDescriptor(vtable_method)) {
+ if (interface_method->HasSameNameAndSignature(vtable_method)) {
if (!vtable_method->IsPublic()) {
Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
"Implementation not public: %s", PrettyMethod(vtable_method).c_str());
@@ -2225,7 +2247,7 @@
if (k < 0) {
Method* miranda_method = NULL;
for (size_t mir = 0; mir < miranda_list.size(); mir++) {
- if (miranda_list[mir]->HasSameNameAndDescriptor(interface_method)) {
+ if (miranda_list[mir]->HasSameNameAndSignature(interface_method)) {
miranda_method = miranda_list[mir];
break;
}
diff --git a/src/class_linker.h b/src/class_linker.h
index 37802ae..91d6259 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -227,7 +227,7 @@
void VerifyClass(Class* klass);
Class* CreateProxyClass(String* name, ObjectArray<Class>* interfaces, ClassLoader* loader,
- ObjectArray<Method>* methods, ObjectArray<Object>* throws);
+ ObjectArray<Method>* methods, ObjectArray<ObjectArray<Class> >* throws);
private:
ClassLinker(InternTable*);
@@ -347,7 +347,7 @@
}
Method* CreateProxyConstructor(Class* klass);
- Method* CreateProxyMethod(Class* klass, Method* prototype, Object* throws);
+ Method* CreateProxyMethod(Class* klass, Method* prototype, ObjectArray<Class>* throws);
// lock to protect ClassLinker state
mutable Mutex lock_;
@@ -377,6 +377,7 @@
kJavaLangReflectConstructor,
kJavaLangReflectField,
kJavaLangReflectMethod,
+ kJavaLangReflectProxy,
kJavaLangClassLoader,
kDalvikSystemBaseDexClassLoader,
kDalvikSystemPathClassLoader,
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 34e02f0..e604ca0 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -864,8 +864,8 @@
Field* s3 = statics->FindStaticField("s3", class_linker_->FindClass("S", class_loader));
EXPECT_TRUE(s3->GetType()->IsPrimitiveShort());
- EXPECT_EQ(65000, s3->GetShort(NULL));
- s3->SetShort(NULL, 65001);
+ EXPECT_EQ(-536, s3->GetShort(NULL));
+ s3->SetShort(NULL, -535);
Field* s4 = statics->FindStaticField("s4", class_linker_->FindClass("I", class_loader));
EXPECT_TRUE(s4->GetType()->IsPrimitiveInt());
@@ -895,7 +895,7 @@
EXPECT_EQ(false, s0->GetBoolean(NULL));
EXPECT_EQ(6, s1->GetByte(NULL));
EXPECT_EQ('b', s2->GetChar(NULL));
- EXPECT_EQ(65001, s3->GetShort(NULL));
+ EXPECT_EQ(-535, s3->GetShort(NULL));
EXPECT_EQ(2000000001, s4->GetInt(NULL));
EXPECT_EQ(0x34567890abcdef12LL, s5->GetLong(NULL));
EXPECT_EQ(0.75, s6->GetFloat(NULL));
diff --git a/src/java_lang_reflect_Proxy.cc b/src/java_lang_reflect_Proxy.cc
index 821bdd8..55ecd08 100644
--- a/src/java_lang_reflect_Proxy.cc
+++ b/src/java_lang_reflect_Proxy.cc
@@ -29,7 +29,7 @@
ObjectArray<Class>* interfaces = Decode<ObjectArray<Class>*>(env, javaInterfaces);
ClassLoader* loader = Decode<ClassLoader*>(env, javaLoader);
ObjectArray<Method>* methods = Decode<ObjectArray<Method>*>(env, javaMethods);
- ObjectArray<Object>* throws = Decode<ObjectArray<Object>*>(env, javaThrows);
+ ObjectArray<ObjectArray<Class> >* throws = Decode<ObjectArray<ObjectArray<Class> >*>(env, javaThrows);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Class* result = class_linker->CreateProxyClass(name, interfaces, loader, methods, throws);
return AddLocalReference<jclass>(env, result);
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 7e75dff..7ce29d4 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -93,6 +93,7 @@
template ClassLoader* Decode<ClassLoader*>(JNIEnv*, jobject);
template Object* Decode<Object*>(JNIEnv*, jobject);
template ObjectArray<Class>* Decode<ObjectArray<Class>*>(JNIEnv*, jobject);
+template ObjectArray<ObjectArray<Class> >* Decode<ObjectArray<ObjectArray<Class> >*>(JNIEnv*, jobject);
template ObjectArray<Object>* Decode<ObjectArray<Object>*>(JNIEnv*, jobject);
template ObjectArray<StackTraceElement>* Decode<ObjectArray<StackTraceElement>*>(JNIEnv*, jobject);
template ObjectArray<Method>* Decode<ObjectArray<Method>*>(JNIEnv*, jobject);
diff --git a/src/object.cc b/src/object.cc
index aeaa19b..e8aeecd 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -201,12 +201,12 @@
Set32(object, c);
}
-uint16_t Field::GetShort(const Object* object) const {
+int16_t Field::GetShort(const Object* object) const {
DCHECK(GetType()->IsPrimitiveShort());
return Get32(object);
}
-void Field::SetShort(Object* object, uint16_t s) const {
+void Field::SetShort(Object* object, int16_t s) const {
DCHECK(GetType()->IsPrimitiveShort());
Set32(object, s);
}
@@ -560,9 +560,28 @@
return ShortyCharToSize(GetShorty()->CharAt(0));
}
-bool Method::HasSameNameAndDescriptor(const Method* that) const {
- return (this->GetName()->Equals(that->GetName()) &&
- this->GetSignature()->Equals(that->GetSignature()));
+Method* Method::FindOverriddenMethod() const {
+ if (IsStatic()) {
+ return NULL;
+ }
+ Class* declaring_class = GetDeclaringClass();
+ Class* super_class = declaring_class->GetSuperClass();
+ uint16_t method_index = GetMethodIndex();
+ ObjectArray<Method>* super_class_vtable = super_class->GetVTable();
+ Method* result = NULL;
+ if (super_class_vtable != NULL && method_index < super_class_vtable->GetLength()) {
+ result = super_class_vtable->Get(method_index);
+ } else {
+ ObjectArray<Class>* interfaces = declaring_class->GetInterfaces();
+ String* name = GetName();
+ String* signature = GetSignature();
+ for (int32_t i = 0; i < interfaces->GetLength() && result == NULL; i++) {
+ Class* interface = interfaces->Get(i);
+ result = interface->FindInterfaceMethod(name, signature);
+ }
+ }
+ DCHECK (result == NULL || HasSameNameAndSignature(result));
+ return result;
}
uint32_t Method::ToDexPC(const uintptr_t pc) const {
@@ -990,8 +1009,7 @@
return NULL;
}
-Method* Class::FindInterfaceMethod(const StringPiece& name,
- const StringPiece& signature) {
+Method* Class::FindInterfaceMethod(const StringPiece& name, const StringPiece& signature) const {
// Check the current class before checking the interfaces.
Method* method = FindVirtualMethod(name, signature);
if (method != NULL) {
@@ -1009,6 +1027,24 @@
return NULL;
}
+Method* Class::FindInterfaceMethod(String* name, String* signature) const {
+ // Check the current class before checking the interfaces.
+ Method* method = FindVirtualMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+ int32_t iftable_count = GetIfTableCount();
+ ObjectArray<InterfaceEntry>* iftable = GetIfTable();
+ for (int32_t i = 0; i < iftable_count; i++) {
+ Class* interface = iftable->Get(i)->GetInterface();
+ method = interface->FindVirtualMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
Method* Class::FindDeclaredDirectMethod(const StringPiece& name,
const StringPiece& signature) {
for (size_t i = 0; i < NumDirectMethods(); ++i) {
@@ -1033,20 +1069,41 @@
}
Method* Class::FindDeclaredVirtualMethod(const StringPiece& name,
- const StringPiece& signature) {
+ const StringPiece& signature) const {
for (size_t i = 0; i < NumVirtualMethods(); ++i) {
Method* method = GetVirtualMethod(i);
- if (method->GetName()->Equals(name) &&
- method->GetSignature()->Equals(signature)) {
+ if (method->GetName()->Equals(name) && method->GetSignature()->Equals(signature)) {
return method;
}
}
return NULL;
}
-Method* Class::FindVirtualMethod(const StringPiece& name,
- const StringPiece& signature) {
- for (Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
+Method* Class::FindDeclaredVirtualMethod(String* name, String* signature) const {
+ for (size_t i = 0; i < NumVirtualMethods(); ++i) {
+ Method* method = GetVirtualMethod(i);
+ if (method->GetName() == name && method->GetSignature() == signature) {
+ return method;
+ } else {
+ LOG(INFO) << "Find (" << name->ToModifiedUtf8() << ", " << signature->ToModifiedUtf8()
+ << ") != " << PrettyMethod(method);
+ }
+ }
+ return NULL;
+}
+
+Method* Class::FindVirtualMethod(const StringPiece& name, const StringPiece& signature) const {
+ for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
+ Method* method = klass->FindDeclaredVirtualMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
+Method* Class::FindVirtualMethod(String* name, String* signature) const {
+ for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
Method* method = klass->FindDeclaredVirtualMethod(name, signature);
if (method != NULL) {
return method;
@@ -1175,6 +1232,9 @@
template class PrimitiveArray<int64_t>; // LongArray
template class PrimitiveArray<int16_t>; // ShortArray
+// Explicitly instantiate Class[][]
+template class ObjectArray<ObjectArray<Class> >;
+
// TODO: get global references for these
Class* String::java_lang_String_ = NULL;
@@ -1350,6 +1410,15 @@
return result;
}
+bool Throwable::IsCheckedException() const {
+ Class* error = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Error;");
+ if (InstanceOf(error)) {
+ return false;
+ }
+ Class* jlre = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/RuntimeException;");
+ return !InstanceOf(jlre);
+}
+
Class* StackTraceElement::java_lang_StackTraceElement_ = NULL;
void StackTraceElement::SetClass(Class* java_lang_StackTraceElement) {
diff --git a/src/object.h b/src/object.h
index a4e9c7a..8f6f75d 100644
--- a/src/object.h
+++ b/src/object.h
@@ -447,8 +447,8 @@
void SetByte(Object* object, int8_t b) const;
uint16_t GetChar(const Object* object) const;
void SetChar(Object* object, uint16_t c) const;
- uint16_t GetShort(const Object* object) const;
- void SetShort(Object* object, uint16_t s) const;
+ int16_t GetShort(const Object* object) const;
+ void SetShort(Object* object, int16_t s) const;
int32_t GetInt(const Object* object) const;
void SetInt(Object* object, int32_t i) const;
int64_t GetLong(const Object* object) const;
@@ -534,7 +534,7 @@
}
// Returns the method name, e.g. "<init>" or "eatLunch"
- const String* GetName() const;
+ String* GetName() const;
void SetName(String* new_name);
@@ -550,11 +550,13 @@
void SetShorty(String* new_shorty);
- const String* GetSignature() const;
+ String* GetSignature() const;
void SetSignature(String* new_signature);
- bool HasSameNameAndDescriptor(const Method* that) const;
+ bool HasSameNameAndSignature(const Method* that) const {
+ return GetName() == that->GetName() && GetSignature() == that->GetSignature();
+ }
uint32_t GetAccessFlags() const;
@@ -704,6 +706,9 @@
ObjectArray<StaticStorageBase>* GetDexCacheInitializedStaticStorage() const;
void SetDexCacheInitializedStaticStorage(ObjectArray<StaticStorageBase>* new_value);
+ // Find the method that this method overrides
+ Method* FindOverriddenMethod() const;
+
void SetReturnTypeIdx(uint32_t new_return_type_idx);
Class* GetReturnType() const;
@@ -949,10 +954,13 @@
SetField32(OFFSET_OF_OBJECT_MEMBER(Method, fp_spill_mask_), fp_spill_mask, false);
}
- void SetExceptionTypes(Object* exception_types) {
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Method, java_exception_types_), exception_types, false);
+ ObjectArray<Class>* GetExceptionTypes() const {
+ return GetFieldObject<ObjectArray<Class>*>(
+ OFFSET_OF_OBJECT_MEMBER(Method, java_exception_types_), false);
}
+ void SetExceptionTypes(ObjectArray<Class>* exception_types);
+
ObjectArray<Class>* GetJavaParameterTypes() const {
return GetFieldObject<ObjectArray<Class>*>(
OFFSET_OF_OBJECT_MEMBER(Method, java_parameter_types_), false);
@@ -1083,11 +1091,10 @@
const uint32_t* mapping_table_;
- // For concrete virtual methods, this is the offset of the method
- // in Class::vtable_.
+ // For concrete virtual methods, this is the offset of the method in Class::vtable_.
//
- // For abstract methods in an interface class, this is the offset
- // of the method in "iftable_->Get(n)->GetMethodArray()".
+ // For abstract methods in an interface class, this is the offset of the method in
+ // "iftable_->Get(n)->GetMethodArray()".
uint32_t method_index_;
// The target native method registered with this method
@@ -1722,8 +1729,8 @@
// method for this class.
Method* FindVirtualMethodForInterface(Method* method);
- Method* FindInterfaceMethod(const StringPiece& name,
- const StringPiece& descriptor);
+ Method* FindInterfaceMethod(const StringPiece& name, const StringPiece& descriptor) const;
+ Method* FindInterfaceMethod(String* name, String* descriptor) const;
Method* FindVirtualMethodForVirtualOrInterface(Method* method) {
if (method->IsDirect()) {
@@ -1735,11 +1742,11 @@
return FindVirtualMethodForVirtual(method);
}
- Method* FindDeclaredVirtualMethod(const StringPiece& name,
- const StringPiece& signature);
+ Method* FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const;
+ Method* FindDeclaredVirtualMethod(String* name, String* signature) const;
- Method* FindVirtualMethod(const StringPiece& name,
- const StringPiece& descriptor);
+ Method* FindVirtualMethod(const StringPiece& name, const StringPiece& descriptor) const;
+ Method* FindVirtualMethod(String* name, String* descriptor) const;
Method* FindDeclaredDirectMethod(const StringPiece& name,
const StringPiece& signature);
@@ -2529,11 +2536,9 @@
GetField32(OFFSET_OF_OBJECT_MEMBER(Field, offset_), false));
}
-inline const String* Method::GetName() const {
+inline String* Method::GetName() const {
DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
- const String* result =
- GetFieldObject<const String*>(
- OFFSET_OF_OBJECT_MEMBER(Method, name_), false);
+ String* result = GetFieldObject<String*>(OFFSET_OF_OBJECT_MEMBER(Method, name_), false);
DCHECK(result != NULL);
return result;
}
@@ -2577,10 +2582,9 @@
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Method, shorty_), new_shorty, false);
}
-inline const String* Method::GetSignature() const {
+inline String* Method::GetSignature() const {
DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
- const String* result =
- GetFieldObject<const String*>(OFFSET_OF_OBJECT_MEMBER(Method, signature_), false);
+ String* result = GetFieldObject<String*>(OFFSET_OF_OBJECT_MEMBER(Method, signature_), false);
DCHECK(result != NULL);
return result;
}
@@ -2590,6 +2594,10 @@
new_signature, false);
}
+inline void Method::SetExceptionTypes(ObjectArray<Class>* exception_types) {
+ SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Method, java_exception_types_), exception_types, false);
+}
+
inline uint32_t Class::GetAccessFlags() const {
// Check class is loaded or this is java.lang.String that has a
// circularity issue during loading the names of its members
@@ -2656,6 +2664,7 @@
new_detail_message, false);
}
+ bool IsCheckedException() const;
private:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
Throwable* cause_;
diff --git a/src/object_test.cc b/src/object_test.cc
index a180d5e..e5af663 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -278,17 +278,17 @@
Method* m4_2 = klass2->GetVirtualMethod(3);
EXPECT_TRUE(m4_2->GetName()->Equals("m4"));
- EXPECT_TRUE(m1_1->HasSameNameAndDescriptor(m1_2));
- EXPECT_TRUE(m1_2->HasSameNameAndDescriptor(m1_1));
+ EXPECT_TRUE(m1_1->HasSameNameAndSignature(m1_2));
+ EXPECT_TRUE(m1_2->HasSameNameAndSignature(m1_1));
- EXPECT_TRUE(m2_1->HasSameNameAndDescriptor(m2_2));
- EXPECT_TRUE(m2_2->HasSameNameAndDescriptor(m2_1));
+ EXPECT_TRUE(m2_1->HasSameNameAndSignature(m2_2));
+ EXPECT_TRUE(m2_2->HasSameNameAndSignature(m2_1));
- EXPECT_TRUE(m3_1->HasSameNameAndDescriptor(m3_2));
- EXPECT_TRUE(m3_2->HasSameNameAndDescriptor(m3_1));
+ EXPECT_TRUE(m3_1->HasSameNameAndSignature(m3_2));
+ EXPECT_TRUE(m3_2->HasSameNameAndSignature(m3_1));
- EXPECT_TRUE(m4_1->HasSameNameAndDescriptor(m4_2));
- EXPECT_TRUE(m4_2->HasSameNameAndDescriptor(m4_1));
+ EXPECT_TRUE(m4_1->HasSameNameAndSignature(m4_2));
+ EXPECT_TRUE(m4_2->HasSameNameAndSignature(m4_1));
}
diff --git a/src/reflection.cc b/src/reflection.cc
index 1294c08..86b4b09 100644
--- a/src/reflection.cc
+++ b/src/reflection.cc
@@ -70,6 +70,7 @@
// Find the actual implementation of the virtual method.
m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m);
+ mid = reinterpret_cast<jmethodID>(m);
}
// Get our arrays of arguments and their types, and check they're the same size.
@@ -310,13 +311,13 @@
Field* primitive_field = o->GetClass()->GetIFields()->Get(0);
if (src_descriptor->Equals("Ljava/lang/Boolean;")) {
src_class = class_linker->FindPrimitiveClass('Z');
- boxed_value.z = primitive_field->GetBoolean(o);
+ boxed_value.i = primitive_field->GetBoolean(o); // and extend read value to 32bits
} else if (src_descriptor->Equals("Ljava/lang/Byte;")) {
src_class = class_linker->FindPrimitiveClass('B');
- boxed_value.b = primitive_field->GetByte(o);
+ boxed_value.i = primitive_field->GetByte(o); // and extend read value to 32bits
} else if (src_descriptor->Equals("Ljava/lang/Character;")) {
src_class = class_linker->FindPrimitiveClass('C');
- boxed_value.c = primitive_field->GetChar(o);
+ boxed_value.i = primitive_field->GetChar(o); // and extend read value to 32bits
} else if (src_descriptor->Equals("Ljava/lang/Float;")) {
src_class = class_linker->FindPrimitiveClass('F');
boxed_value.f = primitive_field->GetFloat(o);
@@ -331,7 +332,7 @@
boxed_value.j = primitive_field->GetLong(o);
} else if (src_descriptor->Equals("Ljava/lang/Short;")) {
src_class = class_linker->FindPrimitiveClass('S');
- boxed_value.s = primitive_field->GetShort(o);
+ boxed_value.i = primitive_field->GetShort(o); // and extend read value to 32bits
} else {
Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
"%s is not a boxed primitive type", PrettyDescriptor(src_descriptor).c_str());
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index ba3e1b4..ccb8dcc 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -901,14 +901,36 @@
return result;
}
+static void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception) {
+ ScopedLocalRef<jclass> jlr_UTE_class(env,
+ env->FindClass("java/lang/reflect/UndeclaredThrowableException"));
+ if (jlr_UTE_class.get() == NULL) {
+ LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\"";
+ } else {
+ jmethodID jlre_UTE_constructor = env->GetMethodID(jlr_UTE_class.get(), "<init>",
+ "(Ljava/lang/Throwable;)V");
+ jthrowable jexception = AddLocalReference<jthrowable>(env, exception);
+ ScopedLocalRef<jthrowable> jlr_UTE(env,
+ reinterpret_cast<jthrowable>(env->NewObject(jlr_UTE_class.get(), jlre_UTE_constructor,
+ jexception)));
+ int rc = env->Throw(jlr_UTE.get());
+ if (rc != JNI_OK) {
+ LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\"";
+ }
+ }
+ CHECK(self->IsExceptionPending());
+}
+
// Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method
// which is responsible for recording callee save registers. We explicitly handlerize incoming
// reference arguments (so they survive GC) and create a boxed argument array. Finally we invoke
// the invocation handler which is a field within the proxy object receiver.
extern "C" void artProxyInvokeHandler(Method* proxy_method, Object* receiver,
- byte* stack_args, Thread* self) {
+ Thread* self, byte* stack_args) {
// Register the top of the managed stack
- self->SetTopOfStack(reinterpret_cast<Method**>(stack_args + 8), 0);
+ Method** proxy_sp = reinterpret_cast<Method**>(stack_args - 12);
+ DCHECK_EQ(*proxy_sp, proxy_method);
+ self->SetTopOfStack(proxy_sp, 0);
// TODO: ARM specific
DCHECK_EQ(proxy_method->GetFrameSizeInBytes(), 48u);
// Start new JNI local reference state
@@ -941,7 +963,7 @@
param_index++;
}
// Placing into local references incoming arguments from the caller's stack arguments
- cur_arg += 5; // skip LR, Method* and spills for R1 to R3
+ cur_arg += 11; // skip callee saves, LR, Method* and out arg spills for R1 to R3
while (param_index < num_params) {
if (proxy_method->IsParamAReference(param_index)) {
Object* obj = *reinterpret_cast<Object**>(stack_args + (cur_arg * kPointerSize));
@@ -951,23 +973,31 @@
cur_arg = cur_arg + (proxy_method->IsParamALongOrDouble(param_index) ? 2 : 1);
param_index++;
}
- // Create args array
- ObjectArray<Object>* args =
- Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(num_params - 1);
- if(args == NULL) {
- CHECK(self->IsExceptionPending());
- return;
- }
// Set up arguments array and place in local IRT during boxing (which may allocate/GC)
jvalue args_jobj[3];
args_jobj[0].l = rcvr_jobj;
args_jobj[1].l = proxy_method_jobj;
- args_jobj[2].l = AddLocalReference<jobjectArray>(env, args);
+ // Args array, if no arguments then NULL (don't include receiver in argument count)
+ args_jobj[2].l = NULL;
+ ObjectArray<Object>* args = NULL;
+ if ((num_params - 1) > 0) {
+ args = Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(num_params - 1);
+ if(args == NULL) {
+ CHECK(self->IsExceptionPending());
+ return;
+ }
+ args_jobj[2].l = AddLocalReference<jobjectArray>(env, args);
+ }
+ // Convert proxy method into expected interface method
+ Method* interface_method = proxy_method->FindOverriddenMethod();
+ CHECK(interface_method != NULL);
+ args_jobj[1].l = AddLocalReference<jobject>(env, interface_method);
+ LOG(INFO) << "Interface method is " << PrettyMethod(interface_method, true);
// Box arguments
cur_arg = 0; // reset stack location to read to start
// reset index, will index into param type array which doesn't include the receiver
param_index = 0;
- ObjectArray<Class>* param_types = proxy_method->GetJavaParameterTypes();
+ ObjectArray<Class>* param_types = interface_method->GetJavaParameterTypes();
CHECK(param_types != NULL);
// Check number of parameter types agrees with number from the Method - less 1 for the receiver.
CHECK_EQ(static_cast<size_t>(param_types->GetLength()), num_params - 1);
@@ -980,8 +1010,7 @@
JValue val = *reinterpret_cast<JValue*>(stack_args + (cur_arg * kPointerSize));
if (cur_arg == 1 && (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble())) {
// long/double split over regs and stack, mask in high half from stack arguments
- // (7 = 2 reg args + LR + Method* + 3 arg reg spill slots)
- uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args + (7 * kPointerSize));
+ uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args + (13 * kPointerSize));
val.j = (val.j & 0xffffffffULL) | (high_half << 32);
}
BoxPrimitive(env, param_type, val);
@@ -995,8 +1024,8 @@
param_index++;
}
// Placing into local references incoming arguments from the caller's stack arguments
- cur_arg += 5; // skip LR, Method* and spills for R1 to R3
- while (param_index < num_params) {
+ cur_arg += 11; // skip callee saves, LR, Method* and out arg spills for R1 to R3
+ while (param_index < (num_params - 1)) {
Class* param_type = param_types->Get(param_index);
Object* obj;
if (!param_type->IsPrimitive()) {
@@ -1017,13 +1046,13 @@
static jmethodID inv_hand_invoke_mid = NULL;
static jfieldID proxy_inv_hand_fid = NULL;
if (proxy_inv_hand_fid == NULL) {
- ScopedLocalRef<jclass> proxy(env, env->FindClass("java.lang.reflect.Proxy"));
+ ScopedLocalRef<jclass> proxy(env, env->FindClass("java/lang/reflect/Proxy"));
proxy_inv_hand_fid = env->GetFieldID(proxy.get(), "h", "Ljava/lang/reflect/InvocationHandler;");
- ScopedLocalRef<jclass> inv_hand_class(env, env->FindClass("java.lang.reflect.InvocationHandler"));
+ ScopedLocalRef<jclass> inv_hand_class(env, env->FindClass("java/lang/reflect/InvocationHandler"));
inv_hand_invoke_mid = env->GetMethodID(inv_hand_class.get(), "invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
}
- DCHECK(env->IsInstanceOf(rcvr_jobj, env->FindClass("java.lang.reflect.Proxy")));
+ DCHECK(env->IsInstanceOf(rcvr_jobj, env->FindClass("java/lang/reflect/Proxy")));
jobject inv_hand = env->GetObjectField(rcvr_jobj, proxy_inv_hand_fid);
// Call InvocationHandler.invoke
jobject result = env->CallObjectMethodA(inv_hand, inv_hand_invoke_mid, args_jobj);
@@ -1032,11 +1061,32 @@
Object* result_ref = self->DecodeJObject(result);
if (result_ref != NULL) {
JValue result_unboxed;
- UnboxPrimitive(env, result_ref, proxy_method->GetReturnType(), result_unboxed);
+ UnboxPrimitive(env, result_ref, interface_method->GetReturnType(), result_unboxed);
*reinterpret_cast<JValue*>(stack_args) = result_unboxed;
} else {
*reinterpret_cast<jobject*>(stack_args) = NULL;
}
+ } else {
+ // In the case of checked exceptions that aren't declared, the exception must be wrapped by
+ // a UndeclaredThrowableException.
+ Throwable* exception = self->GetException();
+ self->ClearException();
+ if (!exception->IsCheckedException()) {
+ self->SetException(exception);
+ } else {
+ ObjectArray<Class>* declared_exceptions = proxy_method->GetExceptionTypes();
+ Class* exception_class = exception->GetClass();
+ bool declares_exception = false;
+ for (int i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) {
+ Class* declared_exception = declared_exceptions->Get(i);
+ declares_exception = declared_exception->IsAssignableFrom(exception_class);
+ }
+ if (declares_exception) {
+ self->SetException(exception);
+ } else {
+ ThrowNewUndeclaredThrowableException(self, env, exception);
+ }
+ }
}
}
diff --git a/src/runtime_support.h b/src/runtime_support.h
index de41f47..ba0b0f1 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -32,6 +32,8 @@
/* Helper for both JNI and regular compiled code */
extern "C" void art_deliver_exception_from_code(void*);
+extern "C" void art_proxy_invoke_handler();
+
#if defined(__arm__)
/* Compiler helpers */
extern "C" void* art_alloc_object_from_code(uint32_t type_idx, void* method);
diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S
index 2076fad..8ef3da8 100644
--- a/src/runtime_support_asm.S
+++ b/src/runtime_support_asm.S
@@ -485,8 +485,9 @@
add r3, sp, #12 @ pointer to r2/r3/LR/caller's Method**/out-args as second arg
blx artProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, args...)
ldr r12, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- ldrd r0, [sp, #12] @ load r0/r1 from r2/r3 that were overwritten with the out args
- RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+ ldr lr, [sp, #44] @ restore lr
+ ldrd r0, [sp, #12] @ load r0/r1 from r2/r3 that were overwritten with the out args
+ add sp, #48 @ pop frame
cmp r12, #0 @ success if no exception is pending
bxeq lr @ return on success
DELIVER_PENDING_EXCEPTION
@@ -588,4 +589,9 @@
call artDeliverExceptionFromCode // artDeliverExceptionFromCode(Throwable*, Thread*, SP)
int3
+ // TODO
+ .global art_proxy_invoke_handler
+art_proxy_invoke_handler:
+ int3
+
#endif
diff --git a/src/thread.cc b/src/thread.cc
index 62b3290..e143af5 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1091,15 +1091,17 @@
Method* method = down_cast<Method*>(method_trace->Get(i));
uint32_t native_pc = pc_trace->Get(i);
Class* klass = method->GetDeclaringClass();
- const DexFile& dex_file = class_linker->FindDexFile(klass->GetDexCache());
std::string class_name(PrettyDescriptor(klass->GetDescriptor()));
-
+ int32_t line_number = -1;
+ DexCache* dex_cache = klass->GetDexCache();
+ if (dex_cache != NULL) {
+ const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
+ line_number = dex_file.GetLineNumFromPC(method, method->ToDexPC(native_pc));
+ }
// Allocate element, potentially triggering GC
StackTraceElement* obj =
StackTraceElement::Alloc(String::AllocFromModifiedUtf8(class_name.c_str()),
- method->GetName(),
- klass->GetSourceFile(),
- dex_file.GetLineNumFromPC(method, method->ToDexPC(native_pc)));
+ method->GetName(), klass->GetSourceFile(), line_number);
if (obj == NULL) {
return NULL;
}