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;