Revert^2 "Remove finalizable restriction on structural redefinition"

This reverts commit 0b1afb7c63d99c88f90f17770ad94ccb0b0c57d3.

Daemon shutdown is known to be somewhat flaky. That seems to be the
cause of this failure.

Reason for revert: Removed Daemon threads from tests 2006 & 2007.
Bug: 134162467
Test: % ./art/test/run-test --create-runner --host --prebuild --compact-dex-level fast --jit --no-relocate --runtime-option -Xcheck:jni --debuggable --runtime-option -Xopaque-jni-ids:true --64 2007
      ...
      Runnable test script written to /tmp/allight/test-230585/runit.sh
      ...
      % ./art/tools/parallel_run.py -j80 /tmp/allight/test-230585/runit.sh --out failure.txt
Test: ./art/test/run-test --create-runner --host --prebuild --compact-dex-level fast --jit --no-relocate --runtime-option -Xcheck:jni --debuggable --runtime-option -Xopaque-jni-ids:true --64 2006

Change-Id: I392a2936995dd05b08feea36f11b616c1548ae46
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index c505933..dccc226 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1131,10 +1131,6 @@
   jvmtiError res;
   if (driver_->type_ == RedefinitionType::kStructural && this->IsStructuralRedefinition()) {
     res = Redefiner::GetClassRedefinitionError<RedefinitionType::kStructural>(h_klass, &err);
-    if (res == OK && HasVirtualMembers() && h_klass->IsFinalizable()) {
-      res = ERR(INTERNAL);
-      err = "Cannot redefine finalizable objects at this time.";
-    }
   } else {
     res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(h_klass, &err);
   }
@@ -1750,7 +1746,21 @@
                        [&](auto class_pair) REQUIRES_SHARED(art::Locks::mutator_lock_) {
                          return class_pair.first == hinstance->GetClass();
                        }));
-    art::ObjPtr<art::mirror::Object> new_instance(new_type->AllocObject(driver_->self_));
+    // Make sure when allocating the new instance we don't add it's finalizer since we will directly
+    // replace the old object in the finalizer reference. If we added it here to we would call
+    // finalize twice.
+    // NB If a type is changed from being non-finalizable to finalizable the finalizers on any
+    //    objects created before the redefine will never be called. This is (sort of) allowable by
+    //    the spec and greatly simplifies implementation.
+    // TODO Make it so we will always call all finalizers, even if the object when it was created
+    // wasn't finalizable. To do this we need to be careful of handling failure correctly and making
+    // sure that objects aren't finalized multiple times and that instances of failed redefinitions
+    // aren't finalized.
+    art::ObjPtr<art::mirror::Object> new_instance(
+        new_type->Alloc</*kIsInstrumented=*/true,
+                        art::mirror::Class::AddFinalizer::kNoAddFinalizer,
+                        /*kCheckAddFinalizer=*/false>(
+            driver_->self_, driver_->runtime_->GetHeap()->GetCurrentAllocator()));
     if (new_instance.IsNull()) {
       driver_->self_->AssertPendingOOMException();
       driver_->self_->ClearException();
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 338928e..1e42201 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -199,14 +199,15 @@
       return nullptr;
     }
     gc::Heap* heap = Runtime::Current()->GetHeap();
-    // Pass in false since the object cannot be finalizable.
+    // Pass in kNoAddFinalizer since the object cannot be finalizable.
     // CheckClassInitializedForObjectAlloc can cause thread suspension which means we may now be
     // instrumented.
-    return klass->Alloc</*kInstrumented=*/true, /*kCheckAddFinalizer=*/false>(
+    return klass->Alloc</*kInstrumented=*/true, mirror::Class::AddFinalizer::kNoAddFinalizer>(
         self, heap->GetCurrentAllocator());
   }
-  // Pass in false since the object cannot be finalizable.
-  return klass->Alloc<kInstrumented, /*kCheckAddFinalizer=*/false>(self, allocator_type);
+  // Pass in kNoAddFinalizer since the object cannot be finalizable.
+  return klass->Alloc<kInstrumented,
+                      mirror::Class::AddFinalizer::kNoAddFinalizer>(self, allocator_type);
 }
 
 // Given the context of a calling Method and an initialized class, create an instance.
@@ -216,8 +217,9 @@
                                                              Thread* self,
                                                              gc::AllocatorType allocator_type) {
   DCHECK(klass != nullptr);
-  // Pass in false since the object cannot be finalizable.
-  return klass->Alloc<kInstrumented, /*kCheckAddFinalizer=*/false>(self, allocator_type);
+  // Pass in kNoAddFinalizer since the object cannot be finalizable.
+  return klass->Alloc<kInstrumented,
+                      mirror::Class::AddFinalizer::kNoAddFinalizer>(self, allocator_type);
 }
 
 
diff --git a/runtime/mirror/class-alloc-inl.h b/runtime/mirror/class-alloc-inl.h
index 2861244..5627b49 100644
--- a/runtime/mirror/class-alloc-inl.h
+++ b/runtime/mirror/class-alloc-inl.h
@@ -46,13 +46,19 @@
   DCHECK_GE(this->object_size_, sizeof(Object));
 }
 
-template<bool kIsInstrumented, bool kCheckAddFinalizer>
+template<bool kIsInstrumented, Class::AddFinalizer kAddFinalizer, bool kCheckAddFinalizer>
 inline ObjPtr<Object> Class::Alloc(Thread* self, gc::AllocatorType allocator_type) {
   CheckObjectAlloc();
   gc::Heap* heap = Runtime::Current()->GetHeap();
-  const bool add_finalizer = kCheckAddFinalizer && IsFinalizable();
-  if (!kCheckAddFinalizer) {
-    DCHECK(!IsFinalizable());
+  bool add_finalizer;
+  switch (kAddFinalizer) {
+    case Class::AddFinalizer::kUseClassTag:
+      add_finalizer = IsFinalizable();
+      break;
+    case Class::AddFinalizer::kNoAddFinalizer:
+      add_finalizer = false;
+      DCHECK(!kCheckAddFinalizer || !IsFinalizable());
+      break;
   }
   // Note that the `this` pointer may be invalidated after the allocation.
   ObjPtr<Object> obj =
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index b600c43..3e24346 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -498,8 +498,21 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsPrimitiveArray() REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Enum used to control whether we try to add a finalizer-reference for object alloc or not.
+  enum class AddFinalizer {
+    // Don't create a finalizer reference regardless of what the class-flags say.
+    kNoAddFinalizer,
+    // Use the class-flags to figure out if we should make a finalizer reference.
+    kUseClassTag,
+  };
+
   // Creates a raw object instance but does not invoke the default constructor.
-  template<bool kIsInstrumented = true, bool kCheckAddFinalizer = true>
+  // kCheckAddFinalizer controls whether we use a DCHECK to sanity check that we create a
+  // finalizer-reference if needed. This should only be disabled when doing structural class
+  // redefinition.
+  template <bool kIsInstrumented = true,
+            AddFinalizer kAddFinalizer = AddFinalizer::kUseClassTag,
+            bool kCheckAddFinalizer = true>
   ALWAYS_INLINE ObjPtr<Object> Alloc(Thread* self, gc::AllocatorType allocator_type)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
 
diff --git a/test/2006-virtual-structural-finalizing/expected.txt b/test/2006-virtual-structural-finalizing/expected.txt
new file mode 100644
index 0000000..e965357
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/expected.txt
@@ -0,0 +1,3 @@
+Finalizing
+start_counter: 1
+Finish_counter: 1
diff --git a/test/2006-virtual-structural-finalizing/info.txt b/test/2006-virtual-structural-finalizing/info.txt
new file mode 100644
index 0000000..3e5291d
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/info.txt
@@ -0,0 +1,4 @@
+Tests structural redefinition with multiple threads.
+
+Tests that using the structural redefinition while concurrently loading and using a subtype of
+the class being redefined doesn't cause any unexpected problems.
diff --git a/test/2006-virtual-structural-finalizing/run b/test/2006-virtual-structural-finalizing/run
new file mode 100755
index 0000000..03e41a5
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2006-virtual-structural-finalizing/src-art/Main.java b/test/2006-virtual-structural-finalizing/src-art/Main.java
new file mode 100644
index 0000000..11f9aa7
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/src-art/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test2006.run();
+  }
+}
diff --git a/test/2006-virtual-structural-finalizing/src-art/art/Redefinition.java b/test/2006-virtual-structural-finalizing/src-art/art/Redefinition.java
new file mode 120000
index 0000000..81eaf31
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/src-art/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java
\ No newline at end of file
diff --git a/test/2006-virtual-structural-finalizing/src-art/art/Test2006.java b/test/2006-virtual-structural-finalizing/src-art/art/Test2006.java
new file mode 100644
index 0000000..510d13d
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/src-art/art/Test2006.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package art;
+
+import dalvik.system.InMemoryDexClassLoader;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Supplier;
+import java.util.concurrent.atomic.*;
+import java.lang.ref.*;
+
+public class Test2006 {
+  public static final CountDownLatch start_latch = new CountDownLatch(1);
+  public static final CountDownLatch redefine_latch = new CountDownLatch(1);
+  public static final CountDownLatch finish_latch = new CountDownLatch(1);
+  public static volatile int start_counter = 0;
+  public static volatile int finish_counter = 0;
+  public static class Transform {
+    public Transform() { }
+    protected void finalize() throws Throwable {
+      System.out.println("Finalizing");
+      start_counter++;
+      start_latch.countDown();
+      redefine_latch.await();
+      finish_counter++;
+      finish_latch.countDown();
+    }
+  }
+
+  /**
+   * base64 encoded class/dex file for
+   * public static class Transform {
+   *   public String greeting;
+   *
+   *   public Transform() {
+   *     greeting = "Hello";
+   *   }
+   *   protected void finalize() {
+   *     System.out.println("NOTHING HERE!");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES =
+      Base64.getDecoder()
+          .decode(
+"ZGV4CjAzNQDtxu0Tsy2rLn9iTZHx3r+yuY0IuN+y1el4BAAAcAAAAHhWNBIAAAAAAAAAALQDAAAX" +
+"AAAAcAAAAAkAAADMAAAAAgAAAPAAAAACAAAACAEAAAQAAAAYAQAAAQAAADgBAAAgAwAAWAEAAKoB" +
+"AACyAQAAuQEAANMBAADjAQAABwIAACcCAAA+AgAAUgIAAGYCAAB6AgAAiQIAAJgCAACjAgAApgIA" +
+"AKoCAAC3AgAAwQIAAMsCAADRAgAA1gIAAN8CAADmAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAA" +
+"CAAAAAkAAAANAAAADQAAAAgAAAAAAAAADgAAAAgAAACkAQAAAAAGABEAAAAHAAQAEwAAAAAAAAAA" +
+"AAAAAAAAABAAAAAEAAEAFAAAAAUAAAAAAAAAAAAAAAEAAAAFAAAAAAAAAAsAAACkAwAAhAMAAAAA" +
+"AAACAAEAAQAAAJgBAAAIAAAAcBADAAEAGgABAFsQAAAOAAMAAQACAAAAngEAAAgAAABiAAEAGgEK" +
+"AG4gAgAQAA4ABgAOPEsACgAOeAAAAQAAAAYABjxpbml0PgAFSGVsbG8AGExhcnQvVGVzdDIwMDYk" +
+"VHJhbnNmb3JtOwAOTGFydC9UZXN0MjAwNjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdD" +
+"bGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8vUHJpbnRTdHJl" +
+"YW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9T" +
+"eXN0ZW07AA1OT1RISU5HIEhFUkUhAA1UZXN0MjAwNi5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZMAAth" +
+"Y2Nlc3NGbGFncwAIZmluYWxpemUACGdyZWV0aW5nAARuYW1lAANvdXQAB3ByaW50bG4ABXZhbHVl" +
+"AIwBfn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwiaGFzLWNoZWNrc3VtcyI6ZmFsc2Us" +
+"Im1pbi1hcGkiOjEsInNoYS0xIjoiMTI5ZWU5ZjY3NTZjMzlkZjU3ZmYwNzg1ZDI1NmIyMzc3MjY0" +
+"MmI3YyIsInZlcnNpb24iOiIyLjAuMTAtZGV2In0AAgIBFRgBAgMCDwQJEhcMAAEBAQABAIGABNgC" +
+"AQT4AgAAAAACAAAAdQMAAHsDAACYAwAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAX" +
+"AAAAcAAAAAIAAAAJAAAAzAAAAAMAAAACAAAA8AAAAAQAAAACAAAACAEAAAUAAAAEAAAAGAEAAAYA" +
+"AAABAAAAOAEAAAEgAAACAAAAWAEAAAMgAAACAAAAmAEAAAEQAAABAAAApAEAAAIgAAAXAAAAqgEA" +
+"AAQgAAACAAAAdQMAAAAgAAABAAAAhAMAAAMQAAACAAAAlAMAAAYgAAABAAAApAMAAAAQAAABAAAA" +
+"tAMAAA==");
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest();
+  }
+
+  public static final class GcThread extends Thread {
+    public volatile boolean finished = false;
+    public void run() {
+      while (!finished) {
+        Runtime.getRuntime().gc();
+        System.runFinalization();
+      }
+    }
+  }
+
+  public static void doTest() throws Exception {
+    GcThread gc_thr = new GcThread();
+    gc_thr.start();
+    mktransform();
+    start_latch.await();
+    System.out.println("start_counter: " + start_counter);
+    Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+    redefine_latch.countDown();
+    finish_latch.await();
+    System.out.println("Finish_counter: " + finish_counter);
+    gc_thr.finished = true;
+    gc_thr.join();
+  }
+  public static void mktransform() throws Exception {
+    Transform tr = new Transform();
+  }
+}
diff --git a/test/2006-virtual-structural-finalizing/src/Main.java b/test/2006-virtual-structural-finalizing/src/Main.java
new file mode 100644
index 0000000..89b8557
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.out.println("FAIL: Test is only for art!");
+  }
+}
diff --git a/test/2007-virtual-structural-finalizable/expected.txt b/test/2007-virtual-structural-finalizable/expected.txt
new file mode 100644
index 0000000..781fc9a
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/expected.txt
@@ -0,0 +1,2 @@
+Finalizing
+counter: 1
diff --git a/test/2007-virtual-structural-finalizable/info.txt b/test/2007-virtual-structural-finalizable/info.txt
new file mode 100644
index 0000000..3e5291d
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/info.txt
@@ -0,0 +1,4 @@
+Tests structural redefinition with multiple threads.
+
+Tests that using the structural redefinition while concurrently loading and using a subtype of
+the class being redefined doesn't cause any unexpected problems.
diff --git a/test/2007-virtual-structural-finalizable/run b/test/2007-virtual-structural-finalizable/run
new file mode 100755
index 0000000..03e41a5
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2007-virtual-structural-finalizable/src-art/Main.java b/test/2007-virtual-structural-finalizable/src-art/Main.java
new file mode 100644
index 0000000..ab8daea
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/src-art/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test2007.run();
+  }
+}
diff --git a/test/2007-virtual-structural-finalizable/src-art/art/Redefinition.java b/test/2007-virtual-structural-finalizable/src-art/art/Redefinition.java
new file mode 120000
index 0000000..81eaf31
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/src-art/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java
\ No newline at end of file
diff --git a/test/2007-virtual-structural-finalizable/src-art/art/Test2007.java b/test/2007-virtual-structural-finalizable/src-art/art/Test2007.java
new file mode 100644
index 0000000..77284eb
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/src-art/art/Test2007.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package art;
+
+import dalvik.system.InMemoryDexClassLoader;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Supplier;
+import java.util.concurrent.atomic.*;
+import java.lang.ref.*;
+
+public class Test2007 {
+  public static final CountDownLatch finish_latch = new CountDownLatch(1);
+  public static volatile int counter = 0;
+  public static Object theObject = null;
+  public static class Transform {
+    public Transform() { }
+    protected void finalize() throws Throwable {
+      System.out.println("Should never be called!");
+      // Do nothing.
+    }
+  }
+
+  /**
+   * base64 encoded class/dex file for
+   * public static class Transform {
+   *   public String greeting;
+   *
+   *   public Transform() {
+   *     greeting = "Hello";
+   *   }
+   *   protected void finalize() throws Throwable {
+   *     System.out.println("Finalizing");
+   *     counter++;
+   *     finish_latch.countDown();
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES =
+      Base64.getDecoder()
+          .decode(
+"ZGV4CjAzNQCC9DECxo2lTpw7FCCSqZArgZe8ab49ywRoBQAAcAAAAHhWNBIAAAAAAAAAAKQEAAAe" +
+"AAAAcAAAAA0AAADoAAAAAgAAABwBAAAEAAAANAEAAAUAAABUAQAAAQAAAHwBAADMAwAAnAEAAAYC" +
+"AAAOAgAAGgIAACECAAAkAgAAPgIAAE4CAAByAgAAkgIAAK4CAADFAgAA2QIAAO0CAAABAwAAGAMA" +
+"AD8DAABOAwAAWQMAAFwDAABgAwAAbQMAAHgDAACBAwAAiwMAAJkDAACjAwAAqQMAAK4DAAC3AwAA" +
+"vgMAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAABEAAAAR" +
+"AAAADAAAAAAAAAASAAAADAAAAAACAAABAAgAGAAAAAIAAAAVAAAAAgALABcAAAAJAAYAGgAAAAEA" +
+"AAAAAAAAAQAAABYAAAAGAAEAGwAAAAcAAAAAAAAACwAAABQAAAABAAAAAQAAAAcAAAAAAAAADwAA" +
+"AIwEAABkBAAAAAAAAAIAAQABAAAA8gEAAAgAAABwEAMAAQAaAAIAWxAAAA4AAwABAAIAAAD4AQAA" +
+"EwAAAGIAAwAaAQEAbiACABAAYAABANgAAAFnAAEAYgACAG4QBAAAAA4ADAAOPEsAEAAOeGlaAAAB" +
+"AAAACAAGPGluaXQ+AApGaW5hbGl6aW5nAAVIZWxsbwABSQAYTGFydC9UZXN0MjAwNyRUcmFuc2Zv" +
+"cm07AA5MYXJ0L1Rlc3QyMDA3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAe" +
+"TGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABpMZGFsdmlrL2Fubm90YXRpb24vVGhyb3dz" +
+"OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcv" +
+"U3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABVMamF2YS9sYW5nL1Rocm93YWJsZTsAJUxqYXZh" +
+"L3V0aWwvY29uY3VycmVudC9Db3VudERvd25MYXRjaDsADVRlc3QyMDA3LmphdmEACVRyYW5zZm9y" +
+"bQABVgACVkwAC2FjY2Vzc0ZsYWdzAAljb3VudERvd24AB2NvdW50ZXIACGZpbmFsaXplAAxmaW5p" +
+"c2hfbGF0Y2gACGdyZWV0aW5nAARuYW1lAANvdXQAB3ByaW50bG4ABXZhbHVlAIwBfn5EOHsiY29t" +
+"cGlsYXRpb24tbW9kZSI6ImRlYnVnIiwiaGFzLWNoZWNrc3VtcyI6ZmFsc2UsIm1pbi1hcGkiOjEs" +
+"InNoYS0xIjoiMTI5ZWU5ZjY3NTZjMzlkZjU3ZmYwNzg1ZDI1NmIyMzc3MjY0MmI3YyIsInZlcnNp" +
+"b24iOiIyLjAuMTAtZGV2In0AAgUBHBwBGAoCAwEcGAICBAITBAkZFxAAAQEBAAEAgYAEnAMBBLwD" +
+"AAAAAAEAAABNBAAAAgAAAFUEAABbBAAAgAQAAAAAAAABAAAAAAAAAAEAAAB4BAAAEAAAAAAAAAAB" +
+"AAAAAAAAAAEAAAAeAAAAcAAAAAIAAAANAAAA6AAAAAMAAAACAAAAHAEAAAQAAAAEAAAANAEAAAUA" +
+"AAAFAAAAVAEAAAYAAAABAAAAfAEAAAEgAAACAAAAnAEAAAMgAAACAAAA8gEAAAEQAAABAAAAAAIA" +
+"AAIgAAAeAAAABgIAAAQgAAADAAAATQQAAAAgAAABAAAAZAQAAAMQAAADAAAAdAQAAAYgAAABAAAA" +
+"jAQAAAAQAAABAAAApAQAAA==");
+
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest();
+  }
+
+  public static final class GcThread extends Thread {
+    public volatile boolean finished = false;
+    public void run() {
+      while (!finished) {
+        Runtime.getRuntime().gc();
+        System.runFinalization();
+      }
+    }
+  }
+
+  public static void doTest() throws Exception {
+    // Try GC forever
+    GcThread gc_thr = new GcThread();
+    gc_thr.start();
+    // Make a transform
+    mktransform();
+    Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+    theObject = null;
+    finish_latch.await();
+    System.out.println("counter: " + counter);
+    // Make sure we don't have any remaining things to finalize, eg obsolete objects or something.
+    Runtime.getRuntime().gc();
+    System.runFinalization();
+    gc_thr.finished = true;
+    gc_thr.join();
+  }
+
+  // Make sure there is never a transform in the frame of doTest.
+  public static void mktransform() throws Exception {
+    theObject = new Transform();
+  }
+}
diff --git a/test/2007-virtual-structural-finalizable/src/Main.java b/test/2007-virtual-structural-finalizable/src/Main.java
new file mode 100644
index 0000000..89b8557
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.out.println("FAIL: Test is only for art!");
+  }
+}