Add Class::IsFinalizable and Object::AddFinalizerReference.
Also correctly set the special bit in Class' flags.
We need compiler support before I can go further.
Change-Id: Ib7a637d7140a6f8c416635738d4d0b57c17ad628
diff --git a/src/assembler_x86.cc b/src/assembler_x86.cc
index 5df8704..c7b2bb5 100644
--- a/src/assembler_x86.cc
+++ b/src/assembler_x86.cc
@@ -1797,7 +1797,7 @@
X86Assembler* sp_asm = down_cast<X86Assembler*>(sasm);
#define __ sp_asm->
__ Bind(&entry_);
- // NB the return value is dead
+ // Note: the return value is dead
// Pass exception as argument in EAX
__ fs()->movl(EAX, Address::Absolute(Thread::ExceptionOffset()));
__ fs()->call(Address::Absolute(OFFSETOF_MEMBER(Thread, pDeliverException)));
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 1049107..6ebc638 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -470,7 +470,7 @@
}
// Let the heap know some key offsets into java.lang.ref instances
- // NB we hard code the field indexes here rather than using FindInstanceField
+ // Note: we hard code the field indexes here rather than using FindInstanceField
// as the types of the field can't be resolved prior to the runtime being
// fully initialized
Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
@@ -1053,16 +1053,32 @@
Method* dst) {
const DexFile::MethodId& method_id = dex_file.GetMethodId(src.method_idx_);
dst->SetDeclaringClass(klass);
+
String* method_name = ResolveString(dex_file, method_id.name_idx_, klass->GetDexCache());
dst->SetName(method_name);
if (method_name->Equals("<init>")) {
dst->SetClass(GetClassRoot(kJavaLangReflectConstructor));
}
- {
- int32_t utf16_length;
- std::string utf8(dex_file.CreateMethodDescriptor(method_id.proto_idx_, &utf16_length));
- dst->SetSignature(intern_table_->InternStrong(utf16_length, utf8.c_str()));
+
+ int32_t utf16_length;
+ std::string signature(dex_file.CreateMethodDescriptor(method_id.proto_idx_, &utf16_length));
+ dst->SetSignature(intern_table_->InternStrong(utf16_length, signature.c_str()));
+
+ if (method_name->Equals("finalize") && signature == "()V") {
+ /*
+ * The Enum class declares a "final" finalize() method to prevent subclasses from introducing
+ * a finalizer. We don't want to set the finalizable flag for Enum or its subclasses, so we
+ * exclude it here.
+ *
+ * We also want to avoid setting the flag on Object, where we know that finalize() is empty.
+ */
+ if (klass->GetClassLoader() != NULL ||
+ (!klass->GetDescriptor()->Equals("Ljava/lang/Object;") &&
+ !klass->GetDescriptor()->Equals("Ljava/lang/Enum;"))) {
+ klass->SetFinalizable();
+ }
}
+
dst->SetProtoIdx(method_id.proto_idx_);
dst->SetCodeItemOffset(src.code_off_);
const char* shorty = dex_file.GetShorty(method_id.proto_idx_);
@@ -1771,7 +1787,6 @@
"java.lang.Object must not have a superclass");
return false;
}
- // TODO: clear finalize attribute
return true;
}
if (super == NULL) {
@@ -1795,6 +1810,12 @@
PrettyDescriptor(klass->GetDescriptor()).c_str());
return false;
}
+
+ // Inherit kAccClassIsFinalizable from the superclass in case this class doesn't override finalize.
+ if (super->IsFinalizable()) {
+ klass->SetFinalizable();
+ }
+
#ifndef NDEBUG
// Ensure super classes are fully resolved prior to resolving fields..
while (super != NULL) {
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 18a3f92..8be1b90 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -969,4 +969,37 @@
EXPECT_EQ(init, clinit->GetDexCacheInitializedStaticStorage()->Get(type_idx));
}
+TEST_F(ClassLinkerTest, FinalizableBit) {
+ Class* c;
+
+ // Object has a finalize method, but we know it's empty.
+ c = class_linker_->FindSystemClass("Ljava/lang/Object;");
+ EXPECT_FALSE(c->IsFinalizable());
+
+ // Enum has a finalize method to prevent its subclasses from implementing one.
+ c = class_linker_->FindSystemClass("Ljava/lang/Enum;");
+ EXPECT_FALSE(c->IsFinalizable());
+
+ // RoundingMode is an enum.
+ c = class_linker_->FindSystemClass("Ljava/math/RoundingMode;");
+ EXPECT_FALSE(c->IsFinalizable());
+
+ // RandomAccessFile extends Object and overrides finalize.
+ c = class_linker_->FindSystemClass("Ljava/io/RandomAccessFile;");
+ EXPECT_TRUE(c->IsFinalizable());
+
+ // FileInputStream is finalizable and extends InputStream which isn't.
+ c = class_linker_->FindSystemClass("Ljava/io/InputStream;");
+ EXPECT_FALSE(c->IsFinalizable());
+ c = class_linker_->FindSystemClass("Ljava/io/FileInputStream;");
+ EXPECT_TRUE(c->IsFinalizable());
+
+ // ScheduledThreadPoolExecutor doesn't have a finalize method but
+ // extends ThreadPoolExecutor which does.
+ c = class_linker_->FindSystemClass("Ljava/util/concurrent/ThreadPoolExecutor;");
+ EXPECT_TRUE(c->IsFinalizable());
+ c = class_linker_->FindSystemClass("Ljava/util/concurrent/ScheduledThreadPoolExecutor;");
+ EXPECT_TRUE(c->IsFinalizable());
+}
+
} // namespace art
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index c8cfc66..83be88a 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -219,7 +219,7 @@
// to the convention required for a native call (shuffling). For references
// place an index/pointer to the reference after checking whether it is
// NULL (which must be encoded as NULL).
- // NB. we do this prior to materializing the JNIEnv* and static's jclass to
+ // Note: we do this prior to materializing the JNIEnv* and static's jclass to
// give as many free registers for the shuffle as possible
mr_conv->ResetIterator(FrameOffset(frame_size+out_arg_size));
uint32_t args_count = 0;
diff --git a/src/object.cc b/src/object.cc
index 49d689c..4950077 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -22,6 +22,20 @@
namespace art {
+void Object::AddFinalizerReference() {
+ Thread* self = Thread::Current();
+
+ // TODO: cache these somewhere.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* java_lang_ref_FinalizerReference = class_linker->FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+ CHECK(java_lang_ref_FinalizerReference != NULL);
+ Method* m = java_lang_ref_FinalizerReference->FindDirectMethod("add", "(Ljava/lang/Object;)V");
+ CHECK(m != NULL);
+
+ LOG(INFO) << "Object::AddFinalizerReference invoking FinalizerReference.add for " << (void*) this;
+ m->Invoke(self, NULL, reinterpret_cast<byte*>(this), NULL);
+}
+
Object* Object::Clone() {
Class* c = GetClass();
DCHECK(!c->IsClassClass());
@@ -41,10 +55,9 @@
size_t offset = sizeof(Object);
memcpy(dst_bytes + offset, src_bytes + offset, num_bytes - offset);
- // TODO: Mark the clone as finalizable if appropriate.
-// if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) {
-// dvmSetFinalizable(copy);
-// }
+ if (c->IsFinalizable()) {
+ copy->AddFinalizerReference();
+ }
return copy;
}
@@ -1384,7 +1397,7 @@
// Quick length inequality test
return false;
} else {
- // NB don't short circuit on hash code as we're presumably here as the
+ // Note: don't short circuit on hash code as we're presumably here as the
// hash code was already equal
for (int32_t i = 0; i < that->GetLength(); ++i) {
if (this->CharAt(i) != that->CharAt(i)) {
diff --git a/src/object.h b/src/object.h
index 205d7c0..ce1d144 100644
--- a/src/object.h
+++ b/src/object.h
@@ -133,11 +133,13 @@
| kAccConstructor
| kAccDeclaredSynchronized);
-// if only kAccClassIsReference is set, we have a soft reference
-static const uint32_t kAccClassIsReference = 0x8000000; // class is a soft/weak/phantom ref
-static const uint32_t kAccClassIsWeakReference = 0x4000000; // class is a weak reference
-static const uint32_t kAccClassIsFinalizerReference = 0x2000000; // class is a finalizer reference
-static const uint32_t kAccClassIsPhantomReference = 0x1000000; // class is a phantom reference
+// Special runtime-only flags.
+// Note: if only kAccClassIsReference is set, we have a soft reference.
+static const uint32_t kAccClassIsFinalizable = 0x80000000; // class/ancestor overrides finalize()
+static const uint32_t kAccClassIsReference = 0x08000000; // class is a soft/weak/phantom ref
+static const uint32_t kAccClassIsWeakReference = 0x04000000; // class is a weak reference
+static const uint32_t kAccClassIsFinalizerReference = 0x02000000; // class is a finalizer reference
+static const uint32_t kAccClassIsPhantomReference = 0x01000000; // class is a phantom reference
static const uint32_t kAccReferenceFlagsMask = (kAccClassIsReference
| kAccClassIsWeakReference
@@ -296,6 +298,9 @@
return down_cast<const Field*>(this);
}
+ // If you're looking for SetFinalizable, this is the moral equivalent.
+ void AddFinalizerReference();
+
bool IsReferenceInstance() const;
bool IsWeakReferenceInstance() const;
@@ -425,8 +430,7 @@
uint32_t GetAccessFlags() const;
void SetAccessFlags(uint32_t new_access_flags) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Field, access_flags_), new_access_flags,
- false);
+ SetField32(OFFSET_OF_OBJECT_MEMBER(Field, access_flags_), new_access_flags, false);
}
bool IsPublic() const {
@@ -602,8 +606,7 @@
uint32_t GetAccessFlags() const;
void SetAccessFlags(uint32_t new_access_flags) {
- SetField32(OFFSET_OF_OBJECT_MEMBER(Method, access_flags_), new_access_flags,
- false);
+ SetField32(OFFSET_OF_OBJECT_MEMBER(Method, access_flags_), new_access_flags, false);
}
// Returns true if the method is declared public.
@@ -1327,6 +1330,15 @@
return (GetAccessFlags() & kAccFinal) != 0;
}
+ bool IsFinalizable() const {
+ return (GetAccessFlags() & kAccClassIsFinalizable) != 0;
+ }
+
+ void SetFinalizable() {
+ uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), false);
+ SetAccessFlags(flags | kAccClassIsFinalizable);
+ }
+
// Returns true if the class is abstract.
bool IsAbstract() const {
return (GetAccessFlags() & kAccAbstract) != 0;